diff options
author | Wim <wim@42.be> | 2017-07-28 14:26:26 +0200 |
---|---|---|
committer | Wim <wim@42.be> | 2017-07-28 14:26:26 +0200 |
commit | 38d09dba2e02a3136ea202cea8b6df58dea19d1d (patch) | |
tree | 9108e0e1ad5db19c1d9cdb3eb1bbfc412b3fd91a /vendor/github.com/thoj/go-ircevent/irc.go | |
parent | fafd0c68e9d0b0db601465da2ab52ee8a47302e5 (diff) | |
download | matterbridge-msglm-38d09dba2e02a3136ea202cea8b6df58dea19d1d.tar.gz matterbridge-msglm-38d09dba2e02a3136ea202cea8b6df58dea19d1d.tar.bz2 matterbridge-msglm-38d09dba2e02a3136ea202cea8b6df58dea19d1d.zip |
Update vendor (go-irc)
Diffstat (limited to 'vendor/github.com/thoj/go-ircevent/irc.go')
-rw-r--r-- | vendor/github.com/thoj/go-ircevent/irc.go | 483 |
1 files changed, 0 insertions, 483 deletions
diff --git a/vendor/github.com/thoj/go-ircevent/irc.go b/vendor/github.com/thoj/go-ircevent/irc.go deleted file mode 100644 index eb9174d3..00000000 --- a/vendor/github.com/thoj/go-ircevent/irc.go +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright 2009 Thomas Jager <mail@jager.no> All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -This package provides an event based IRC client library. It allows to -register callbacks for the events you need to handle. Its features -include handling standard CTCP, reconnecting on errors and detecting -stones servers. -Details of the IRC protocol can be found in the following RFCs: -https://tools.ietf.org/html/rfc1459 -https://tools.ietf.org/html/rfc2810 -https://tools.ietf.org/html/rfc2811 -https://tools.ietf.org/html/rfc2812 -https://tools.ietf.org/html/rfc2813 -The details of the client-to-client protocol (CTCP) can be found here: http://www.irchelp.org/irchelp/rfc/ctcpspec.html -*/ - -package irc - -import ( - "bufio" - "bytes" - "crypto/tls" - "errors" - "fmt" - "log" - "net" - "os" - "strconv" - "strings" - "time" -) - -const ( - VERSION = "go-ircevent v2.1" -) - -var ErrDisconnected = errors.New("Disconnect Called") - -// Read data from a connection. To be used as a goroutine. -func (irc *Connection) readLoop() { - defer irc.Done() - br := bufio.NewReaderSize(irc.socket, 512) - - errChan := irc.ErrorChan() - - for { - select { - case <-irc.end: - return - default: - // Set a read deadline based on the combined timeout and ping frequency - // We should ALWAYS have received a response from the server within the timeout - // after our own pings - if irc.socket != nil { - irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq)) - } - - msg, err := br.ReadString('\n') - - // We got past our blocking read, so bin timeout - if irc.socket != nil { - var zero time.Time - irc.socket.SetReadDeadline(zero) - } - - if err != nil { - errChan <- err - return - } - - if irc.Debug { - irc.Log.Printf("<-- %s\n", strings.TrimSpace(msg)) - } - - irc.Lock() - irc.lastMessage = time.Now() - irc.Unlock() - event, err := parseToEvent(msg) - event.Connection = irc - if err == nil { - /* XXX: len(args) == 0: args should be empty */ - irc.RunCallbacks(event) - } - } - } -} - -//Parse raw irc messages -func parseToEvent(msg string) (*Event, error) { - msg = strings.TrimSuffix(msg, "\n") //Remove \r\n - msg = strings.TrimSuffix(msg, "\r") - event := &Event{Raw: msg} - if len(msg) < 5 { - return nil, errors.New("Malformed msg from server") - } - if msg[0] == ':' { - if i := strings.Index(msg, " "); i > -1 { - event.Source = msg[1:i] - msg = msg[i+1 : len(msg)] - - } else { - return nil, errors.New("Malformed msg from server") - } - - if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 && i < j { - event.Nick = event.Source[0:i] - event.User = event.Source[i+1 : j] - event.Host = event.Source[j+1 : len(event.Source)] - } - } - - split := strings.SplitN(msg, " :", 2) - args := strings.Split(split[0], " ") - event.Code = strings.ToUpper(args[0]) - event.Arguments = args[1:] - if len(split) > 1 { - event.Arguments = append(event.Arguments, split[1]) - } - return event, nil - -} - -// Loop to write to a connection. To be used as a goroutine. -func (irc *Connection) writeLoop() { - defer irc.Done() - errChan := irc.ErrorChan() - for { - select { - case <-irc.end: - return - case b, ok := <-irc.pwrite: - if !ok || b == "" || irc.socket == nil { - return - } - - if irc.Debug { - irc.Log.Printf("--> %s\n", strings.TrimSpace(b)) - } - - // Set a write deadline based on the time out - irc.socket.SetWriteDeadline(time.Now().Add(irc.Timeout)) - - _, err := irc.socket.Write([]byte(b)) - - // Past blocking write, bin timeout - var zero time.Time - irc.socket.SetWriteDeadline(zero) - - if err != nil { - errChan <- err - return - } - } - } -} - -// Pings the server if we have not received any messages for 5 minutes -// to keep the connection alive. To be used as a goroutine. -func (irc *Connection) pingLoop() { - defer irc.Done() - ticker := time.NewTicker(1 * time.Minute) // Tick every minute for monitoring - ticker2 := time.NewTicker(irc.PingFreq) // Tick at the ping frequency. - for { - select { - case <-ticker.C: - //Ping if we haven't received anything from the server within the keep alive period - if time.Since(irc.lastMessage) >= irc.KeepAlive { - irc.SendRawf("PING %d", time.Now().UnixNano()) - } - case <-ticker2.C: - //Ping at the ping frequency - irc.SendRawf("PING %d", time.Now().UnixNano()) - //Try to recapture nickname if it's not as configured. - irc.Lock() - if irc.nick != irc.nickcurrent { - irc.nickcurrent = irc.nick - irc.SendRawf("NICK %s", irc.nick) - } - irc.Unlock() - case <-irc.end: - ticker.Stop() - ticker2.Stop() - return - } - } -} - -func (irc *Connection) isQuitting() bool { - irc.Lock() - defer irc.Unlock() - return irc.quit -} - -// Main loop to control the connection. -func (irc *Connection) Loop() { - errChan := irc.ErrorChan() - for !irc.isQuitting() { - err := <-errChan - close(irc.end) - irc.Wait() - for !irc.isQuitting() { - irc.Log.Printf("Error, disconnected: %s\n", err) - if err = irc.Reconnect(); err != nil { - irc.Log.Printf("Error while reconnecting: %s\n", err) - time.Sleep(60 * time.Second) - } else { - errChan = irc.ErrorChan() - break - } - } - } -} - -// Quit the current connection and disconnect from the server -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1.6 -func (irc *Connection) Quit() { - quit := "QUIT" - - if irc.QuitMessage != "" { - quit = fmt.Sprintf("QUIT :%s", irc.QuitMessage) - } - - irc.SendRaw(quit) - irc.Lock() - irc.stopped = true - irc.quit = true - irc.Unlock() -} - -// Use the connection to join a given channel. -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.1 -func (irc *Connection) Join(channel string) { - irc.pwrite <- fmt.Sprintf("JOIN %s\r\n", channel) -} - -// Leave a given channel. -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.2 -func (irc *Connection) Part(channel string) { - irc.pwrite <- fmt.Sprintf("PART %s\r\n", channel) -} - -// Send a notification to a nickname. This is similar to Privmsg but must not receive replies. -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.2 -func (irc *Connection) Notice(target, message string) { - irc.pwrite <- fmt.Sprintf("NOTICE %s :%s\r\n", target, message) -} - -// Send a formated notification to a nickname. -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.2 -func (irc *Connection) Noticef(target, format string, a ...interface{}) { - irc.Notice(target, fmt.Sprintf(format, a...)) -} - -// Send (action) message to a target (channel or nickname). -// No clear RFC on this one... -func (irc *Connection) Action(target, message string) { - irc.pwrite <- fmt.Sprintf("PRIVMSG %s :\001ACTION %s\001\r\n", target, message) -} - -// Send formatted (action) message to a target (channel or nickname). -func (irc *Connection) Actionf(target, format string, a ...interface{}) { - irc.Action(target, fmt.Sprintf(format, a...)) -} - -// Send (private) message to a target (channel or nickname). -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.4.1 -func (irc *Connection) Privmsg(target, message string) { - irc.pwrite <- fmt.Sprintf("PRIVMSG %s :%s\r\n", target, message) -} - -// Send formated string to specified target (channel or nickname). -func (irc *Connection) Privmsgf(target, format string, a ...interface{}) { - irc.Privmsg(target, fmt.Sprintf(format, a...)) -} - -// Kick <user> from <channel> with <msg>. For no message, pass empty string ("") -func (irc *Connection) Kick(user, channel, msg string) { - var cmd bytes.Buffer - cmd.WriteString(fmt.Sprintf("KICK %s %s", channel, user)) - if msg != "" { - cmd.WriteString(fmt.Sprintf(" :%s", msg)) - } - cmd.WriteString("\r\n") - irc.pwrite <- cmd.String() -} - -// Kick all <users> from <channel> with <msg>. For no message, pass -// empty string ("") -func (irc *Connection) MultiKick(users []string, channel string, msg string) { - var cmd bytes.Buffer - cmd.WriteString(fmt.Sprintf("KICK %s %s", channel, strings.Join(users, ","))) - if msg != "" { - cmd.WriteString(fmt.Sprintf(" :%s", msg)) - } - cmd.WriteString("\r\n") - irc.pwrite <- cmd.String() -} - -// Send raw string. -func (irc *Connection) SendRaw(message string) { - irc.pwrite <- message + "\r\n" -} - -// Send raw formated string. -func (irc *Connection) SendRawf(format string, a ...interface{}) { - irc.SendRaw(fmt.Sprintf(format, a...)) -} - -// Set (new) nickname. -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1.2 -func (irc *Connection) Nick(n string) { - irc.nick = n - irc.SendRawf("NICK %s", n) -} - -// Determine nick currently used with the connection. -func (irc *Connection) GetNick() string { - return irc.nickcurrent -} - -// Query information about a particular nickname. -// RFC 1459: https://tools.ietf.org/html/rfc1459#section-4.5.2 -func (irc *Connection) Whois(nick string) { - irc.SendRawf("WHOIS %s", nick) -} - -// Query information about a given nickname in the server. -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.5.1 -func (irc *Connection) Who(nick string) { - irc.SendRawf("WHO %s", nick) -} - -// Set different modes for a target (channel or nickname). -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.2.3 -func (irc *Connection) Mode(target string, modestring ...string) { - if len(modestring) > 0 { - mode := strings.Join(modestring, " ") - irc.SendRawf("MODE %s %s", target, mode) - return - } - irc.SendRawf("MODE %s", target) -} - -func (irc *Connection) ErrorChan() chan error { - return irc.Error -} - -// Returns true if the connection is connected to an IRC server. -func (irc *Connection) Connected() bool { - return !irc.stopped -} - -// A disconnect sends all buffered messages (if possible), -// stops all goroutines and then closes the socket. -func (irc *Connection) Disconnect() { - if irc.socket != nil { - irc.socket.Close() - } - irc.ErrorChan() <- ErrDisconnected -} - -// Reconnect to a server using the current connection. -func (irc *Connection) Reconnect() error { - irc.end = make(chan struct{}) - return irc.Connect(irc.Server) -} - -// Connect to a given server using the current connection configuration. -// This function also takes care of identification if a password is provided. -// RFC 1459 details: https://tools.ietf.org/html/rfc1459#section-4.1 -func (irc *Connection) Connect(server string) error { - irc.Server = server - // mark Server as stopped since there can be an error during connect - irc.stopped = true - - // make sure everything is ready for connection - if len(irc.Server) == 0 { - return errors.New("empty 'server'") - } - if strings.Count(irc.Server, ":") != 1 { - return errors.New("wrong number of ':' in address") - } - if strings.Index(irc.Server, ":") == 0 { - return errors.New("hostname is missing") - } - if strings.Index(irc.Server, ":") == len(irc.Server)-1 { - return errors.New("port missing") - } - // check for valid range - ports := strings.Split(irc.Server, ":")[1] - port, err := strconv.Atoi(ports) - if err != nil { - return errors.New("extracting port failed") - } - if !((port >= 0) && (port <= 65535)) { - return errors.New("port number outside valid range") - } - if irc.Log == nil { - return errors.New("'Log' points to nil") - } - if len(irc.nick) == 0 { - return errors.New("empty 'nick'") - } - if len(irc.user) == 0 { - return errors.New("empty 'user'") - } - - if irc.UseTLS { - dialer := &net.Dialer{Timeout: irc.Timeout} - irc.socket, err = tls.DialWithDialer(dialer, "tcp", irc.Server, irc.TLSConfig) - } else { - irc.socket, err = net.DialTimeout("tcp", irc.Server, irc.Timeout) - } - if err != nil { - return err - } - - irc.stopped = false - irc.Log.Printf("Connected to %s (%s)\n", irc.Server, irc.socket.RemoteAddr()) - - irc.pwrite = make(chan string, 10) - irc.Error = make(chan error, 2) - irc.Add(3) - go irc.readLoop() - go irc.writeLoop() - go irc.pingLoop() - if len(irc.Password) > 0 { - irc.pwrite <- fmt.Sprintf("PASS %s\r\n", irc.Password) - } - - resChan := make(chan *SASLResult) - if irc.UseSASL { - irc.setupSASLCallbacks(resChan) - irc.pwrite <- fmt.Sprintf("CAP LS\r\n") - // request SASL - irc.pwrite <- fmt.Sprintf("CAP REQ :sasl\r\n") - // if sasl request doesn't complete in 15 seconds, close chan and timeout - select { - case res := <-resChan: - if res.Failed { - close(resChan) - return res.Err - } - case <-time.After(time.Second * 15): - close(resChan) - return errors.New("SASL setup timed out. This shouldn't happen.") - } - } - irc.pwrite <- fmt.Sprintf("NICK %s\r\n", irc.nick) - irc.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user) - return nil -} - -// Create a connection with the (publicly visible) nickname and username. -// The nickname is later used to address the user. Returns nil if nick -// or user are empty. -func IRC(nick, user string) *Connection { - // catch invalid values - if len(nick) == 0 { - return nil - } - if len(user) == 0 { - return nil - } - - irc := &Connection{ - nick: nick, - nickcurrent: nick, - user: user, - Log: log.New(os.Stdout, "", log.LstdFlags), - end: make(chan struct{}), - Version: VERSION, - KeepAlive: 4 * time.Minute, - Timeout: 1 * time.Minute, - PingFreq: 15 * time.Minute, - SASLMech: "PLAIN", - QuitMessage: "", - } - irc.setupCallbacks() - return irc -} |