mirror of
				https://github.com/fluffle/goirc
				synced 2025-11-03 19:48:04 +00:00 
			
		
		
		
	Merge branch 'fluffle'
Conflicts: README.md client.go irc/commands.go irc/connection.go irc/handlers.go
This commit is contained in:
		
						commit
						ff21e678d4
					
				
					 5 changed files with 488 additions and 490 deletions
				
			
		| 
						 | 
					@ -1,9 +1,10 @@
 | 
				
			||||||
package irc
 | 
					package irc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// this file contains the various commands you can
 | 
					// this file contains the various commands you can
 | 
				
			||||||
// send to the server using an Conn connection
 | 
					// send to the server using an Conn connection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
// This could be a lot less ugly with the ability to manipulate
 | 
					// This could be a lot less ugly with the ability to manipulate
 | 
				
			||||||
// the symbol table and add methods/functions on the fly
 | 
					// the symbol table and add methods/functions on the fly
 | 
				
			||||||
// [ CMD, FMT, FMTARGS ] etc.
 | 
					// [ CMD, FMT, FMTARGS ] etc.
 | 
				
			||||||
| 
						 | 
					@ -27,8 +28,8 @@ func (conn *Conn) User(ident, name string) {
 | 
				
			||||||
func (conn *Conn) Join(channel string) { conn.out <- "JOIN "+channel }
 | 
					func (conn *Conn) Join(channel string) { conn.out <- "JOIN "+channel }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Part() sends a PART command to the server with an optional part message
 | 
					// Part() sends a PART command to the server with an optional part message
 | 
				
			||||||
func (conn *Conn) Part(channel string, message string) {
 | 
					func (conn *Conn) Part(channel string, message ...string) {
 | 
				
			||||||
	msg := message
 | 
						msg := strings.Join(message, " ")
 | 
				
			||||||
	if msg != "" {
 | 
						if msg != "" {
 | 
				
			||||||
		msg = " :" + msg
 | 
							msg = " :" + msg
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -36,8 +37,8 @@ func (conn *Conn) Part(channel string, message string) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Kick() sends a KICK command to remove a nick from a channel
 | 
					// Kick() sends a KICK command to remove a nick from a channel
 | 
				
			||||||
func (conn *Conn) Kick(channel, nick string, message string) {
 | 
					func (conn *Conn) Kick(channel, nick string, message ...string) {
 | 
				
			||||||
	msg := message
 | 
						msg := strings.Join(message, " ")
 | 
				
			||||||
	if msg != "" {
 | 
						if msg != "" {
 | 
				
			||||||
		msg = " :" + msg
 | 
							msg = " :" + msg
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -45,8 +46,8 @@ func (conn *Conn) Kick(channel, nick string, message string) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Quit() sends a QUIT command to the server with an optional quit message
 | 
					// Quit() sends a QUIT command to the server with an optional quit message
 | 
				
			||||||
func (conn *Conn) Quit(message string) {
 | 
					func (conn *Conn) Quit(message ...string) {
 | 
				
			||||||
	msg := message
 | 
						msg := strings.Join(message, " ")
 | 
				
			||||||
	if msg == "" {
 | 
						if msg == "" {
 | 
				
			||||||
		msg = "GoBye!"
 | 
							msg = "GoBye!"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -67,8 +68,8 @@ func (conn *Conn) Notice(t, msg string) { conn.out <- "NOTICE "+t+" :"+msg }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Ctcp() sends a (generic) CTCP message to the target t
 | 
					// Ctcp() sends a (generic) CTCP message to the target t
 | 
				
			||||||
// with an optional argument
 | 
					// with an optional argument
 | 
				
			||||||
func (conn *Conn) Ctcp(t, ctcp,arg string) {
 | 
					func (conn *Conn) Ctcp(t, ctcp string, arg ...string) {
 | 
				
			||||||
	msg := arg
 | 
						msg := strings.Join(arg, " ")
 | 
				
			||||||
	if msg != "" {
 | 
						if msg != "" {
 | 
				
			||||||
		msg = " " + msg
 | 
							msg = " " + msg
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -77,8 +78,8 @@ func (conn *Conn) Ctcp(t, ctcp,arg string) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CtcpReply() sends a generic CTCP reply to the target t
 | 
					// CtcpReply() sends a generic CTCP reply to the target t
 | 
				
			||||||
// with an optional argument
 | 
					// with an optional argument
 | 
				
			||||||
func (conn *Conn) CtcpReply(t, ctcp string, arg string) {
 | 
					func (conn *Conn) CtcpReply(t, ctcp string, arg ...string) {
 | 
				
			||||||
	msg := arg
 | 
						msg := strings.Join(arg, " ")
 | 
				
			||||||
	if msg != "" {
 | 
						if msg != "" {
 | 
				
			||||||
		msg = " " + msg
 | 
							msg = " " + msg
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -86,7 +87,7 @@ func (conn *Conn) CtcpReply(t, ctcp string, arg string) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Version() sends a CTCP "VERSION" to the target t
 | 
					// Version() sends a CTCP "VERSION" to the target t
 | 
				
			||||||
func (conn *Conn) Version(t string) { conn.Ctcp(t, "VERSION","") }
 | 
					func (conn *Conn) Version(t string) { conn.Ctcp(t, "VERSION") }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Action() sends a CTCP "ACTION" to the target t
 | 
					// Action() sends a CTCP "ACTION" to the target t
 | 
				
			||||||
func (conn *Conn) Action(t, msg string) { conn.Ctcp(t, "ACTION", msg) }
 | 
					func (conn *Conn) Action(t, msg string) { conn.Ctcp(t, "ACTION", msg) }
 | 
				
			||||||
| 
						 | 
					@ -94,8 +95,8 @@ func (conn *Conn) Action(t, msg string) { conn.Ctcp(t, "ACTION", msg) }
 | 
				
			||||||
// Topic() sends a TOPIC command to the channel
 | 
					// Topic() sends a TOPIC command to the channel
 | 
				
			||||||
//   Topic(channel) retrieves the current channel topic (see "332" handler)
 | 
					//   Topic(channel) retrieves the current channel topic (see "332" handler)
 | 
				
			||||||
//   Topic(channel, topic) sets the topic for the channel
 | 
					//   Topic(channel, topic) sets the topic for the channel
 | 
				
			||||||
func (conn *Conn) Topic(channel string, topic string) {
 | 
					func (conn *Conn) Topic(channel string, topic ...string) {
 | 
				
			||||||
	t := topic
 | 
						t := strings.Join(topic, " ")
 | 
				
			||||||
	if t != "" {
 | 
						if t != "" {
 | 
				
			||||||
		t = " :" + t
 | 
							t = " :" + t
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -109,8 +110,8 @@ func (conn *Conn) Topic(channel string, topic string) {
 | 
				
			||||||
//     modestring == e.g. "+o <nick>" or "+ntk <key>" or "-is"
 | 
					//     modestring == e.g. "+o <nick>" or "+ntk <key>" or "-is"
 | 
				
			||||||
// This means you'll need to do your own mode work. It may be linked in with
 | 
					// This means you'll need to do your own mode work. It may be linked in with
 | 
				
			||||||
// the state tracking and ChanMode/NickMode/ChanPrivs objects later...
 | 
					// the state tracking and ChanMode/NickMode/ChanPrivs objects later...
 | 
				
			||||||
func (conn *Conn) Mode(t string, modestring string) {
 | 
					func (conn *Conn) Mode(t string, modestring ...string) {
 | 
				
			||||||
	mode := modestring
 | 
						mode := strings.Join(modestring, " ")
 | 
				
			||||||
	if mode != "" {
 | 
						if mode != "" {
 | 
				
			||||||
		mode = " " + mode
 | 
							mode = " " + mode
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -120,10 +121,10 @@ func (conn *Conn) Mode(t string, modestring string) {
 | 
				
			||||||
// Away() sends an AWAY command to the server
 | 
					// Away() sends an AWAY command to the server
 | 
				
			||||||
//   Away() resets away status
 | 
					//   Away() resets away status
 | 
				
			||||||
//   Away(message) sets away with the given message
 | 
					//   Away(message) sets away with the given message
 | 
				
			||||||
func (conn *Conn) Away(message string) {
 | 
					func (conn *Conn) Away(message ...string) {
 | 
				
			||||||
	msg := message
 | 
						msg := strings.Join(message, " ")
 | 
				
			||||||
	if msg != "" {
 | 
						if msg != "" {
 | 
				
			||||||
		msg = " :"+msg
 | 
							msg = " :" + msg
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	conn.out <- "AWAY"+msg
 | 
						conn.out <- "AWAY"+msg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -137,4 +138,3 @@ func (conn *Conn) Invite(nick, channel string) {
 | 
				
			||||||
func (conn *Conn) Oper(user, pass string) {
 | 
					func (conn *Conn) Oper(user, pass string) {
 | 
				
			||||||
	conn.out <- "OPER "+user+" "+pass
 | 
						conn.out <- "OPER "+user+" "+pass
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ import (
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"crypto/rand"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
| 
						 | 
					@ -26,8 +25,9 @@ type Conn struct {
 | 
				
			||||||
	out       chan string
 | 
						out       chan string
 | 
				
			||||||
	connected bool
 | 
						connected bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Are we connecting via SSL?
 | 
						// Are we connecting via SSL? Do we care about certificate validity?
 | 
				
			||||||
	SSL       bool
 | 
						SSL       bool
 | 
				
			||||||
 | 
						SSLConfig *tls.Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Error channel to transmit any fail back to the user
 | 
						// Error channel to transmit any fail back to the user
 | 
				
			||||||
	Err chan os.Error
 | 
						Err chan os.Error
 | 
				
			||||||
| 
						 | 
					@ -75,6 +75,8 @@ func (conn *Conn) initialise() {
 | 
				
			||||||
	conn.in = make(chan *Line, 32)
 | 
						conn.in = make(chan *Line, 32)
 | 
				
			||||||
	conn.out = make(chan string, 32)
 | 
						conn.out = make(chan string, 32)
 | 
				
			||||||
	conn.Err = make(chan os.Error, 4)
 | 
						conn.Err = make(chan os.Error, 4)
 | 
				
			||||||
 | 
						conn.SSL = false
 | 
				
			||||||
 | 
						conn.SSLConfig = nil
 | 
				
			||||||
	conn.io = nil
 | 
						conn.io = nil
 | 
				
			||||||
	conn.sock = nil
 | 
						conn.sock = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,41 +89,55 @@ func (conn *Conn) initialise() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Connect the IRC connection object to "host[:port]" which should be either
 | 
					// Connect the IRC connection object to "host[:port]" which should be either
 | 
				
			||||||
// a hostname or an IP address, with an optional port. To enable explicit SSL
 | 
					// a hostname or an IP address, with an optional port. To enable explicit SSL
 | 
				
			||||||
// on the connection to the IRC server, set ssl to true. The port will default
 | 
					// on the connection to the IRC server, set Conn.SSL to true before calling
 | 
				
			||||||
// to 6697 if ssl is enabled, and 6667 otherwise. You can also provide an
 | 
					// Connect(). The port will default to 6697 if ssl is enabled, and 6667
 | 
				
			||||||
// optional connect password.
 | 
					// otherwise. You can also provide an optional connect password.
 | 
				
			||||||
func (conn *Conn) Connect(host string, ssl bool, pass string) os.Error {
 | 
					func (conn *Conn) Connect(host string, pass ...string) os.Error {
 | 
				
			||||||
	if conn.connected {
 | 
						if conn.connected {
 | 
				
			||||||
		return os.NewError(fmt.Sprintf("irc.Connect(): already connected to %s, cannot connect to %s", conn.Host, host))
 | 
							return os.NewError(fmt.Sprintf(
 | 
				
			||||||
	}
 | 
								"irc.Connect(): already connected to %s, cannot connect to %s",
 | 
				
			||||||
	if !hasPort(host) {
 | 
								conn.Host, host))
 | 
				
			||||||
		if ssl {
 | 
					 | 
				
			||||||
			host += ":6697"
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			host += ":6667"
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sock, err := net.Dial("tcp", "", host)
 | 
						if conn.SSL {
 | 
				
			||||||
	if err != nil {
 | 
							if !hasPort(host) {
 | 
				
			||||||
 | 
								host += ":6697"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// It's unfortunate that tls.Dial doesn't allow a tls.Config arg,
 | 
				
			||||||
 | 
							// so we simply replicate it here with the correct Config.
 | 
				
			||||||
 | 
							// http://codereview.appspot.com/2883041
 | 
				
			||||||
 | 
							if s, err := net.Dial("tcp", "", host); err == nil {
 | 
				
			||||||
 | 
								// Passing nil config => certs are validated.
 | 
				
			||||||
 | 
								c := tls.Client(s, conn.SSLConfig)
 | 
				
			||||||
 | 
								if err = c.Handshake(); err == nil {
 | 
				
			||||||
 | 
									conn.sock = c
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									s.Close()
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if !hasPort(host) {
 | 
				
			||||||
 | 
								host += ":6667"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if s, err := net.Dial("tcp", "", host); err == nil {
 | 
				
			||||||
 | 
								conn.sock = s
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	if ssl {
 | 
					 | 
				
			||||||
		sock = tls.Client(sock, &tls.Config{Rand: rand.Reader, Time: time.Nanoseconds})
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	conn.Host = host
 | 
						conn.Host = host
 | 
				
			||||||
	conn.SSL = ssl
 | 
					 | 
				
			||||||
	conn.sock = sock
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	conn.io = bufio.NewReadWriter(
 | 
						conn.io = bufio.NewReadWriter(
 | 
				
			||||||
		bufio.NewReader(conn.sock),
 | 
							bufio.NewReader(conn.sock),
 | 
				
			||||||
		bufio.NewWriter(conn.sock))
 | 
							bufio.NewWriter(conn.sock))
 | 
				
			||||||
	go conn.send()
 | 
						go conn.send()
 | 
				
			||||||
	go conn.recv()
 | 
						go conn.recv()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pass != "" {
 | 
						if len(pass) > 0 {
 | 
				
			||||||
		conn.Pass(pass)
 | 
							conn.Pass(pass[0])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	conn.Nick(conn.Me.Nick)
 | 
						conn.Nick(conn.Me.Nick)
 | 
				
			||||||
	conn.User(conn.Me.Ident, conn.Me.Name)
 | 
						conn.User(conn.Me.Ident, conn.Me.Name)
 | 
				
			||||||
| 
						 | 
					@ -136,13 +152,15 @@ func (conn *Conn) error(s string, a ...interface{}) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// copied from http.client for great justice
 | 
					// copied from http.client for great justice
 | 
				
			||||||
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
 | 
					func hasPort(s string) bool {
 | 
				
			||||||
 | 
						return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dispatch input from channel as \r\n terminated line to peer
 | 
					// dispatch input from channel as \r\n terminated line to peer
 | 
				
			||||||
// flood controlled using hybrid's algorithm if conn.Flood is true
 | 
					// flood controlled using hybrid's algorithm if conn.Flood is true
 | 
				
			||||||
func (conn *Conn) send() {
 | 
					func (conn *Conn) send() {
 | 
				
			||||||
	lastsent := time.Nanoseconds()
 | 
						lastsent := time.Nanoseconds()
 | 
				
			||||||
	var badness, linetime, second int64 = 0, 0, 1000000000;
 | 
						var badness, linetime, second int64 = 0, 0, 1000000000
 | 
				
			||||||
	for line := range conn.out {
 | 
						for line := range conn.out {
 | 
				
			||||||
		// Hybrid's algorithm allows for 2 seconds per line and an additional
 | 
							// Hybrid's algorithm allows for 2 seconds per line and an additional
 | 
				
			||||||
		// 1/120 of a second per character on that line.
 | 
							// 1/120 of a second per character on that line.
 | 
				
			||||||
| 
						 | 
					@ -162,7 +180,7 @@ func (conn *Conn) send() {
 | 
				
			||||||
			// so sleep for the current line's time value before sending it
 | 
								// so sleep for the current line's time value before sending it
 | 
				
			||||||
			time.Sleep(linetime)
 | 
								time.Sleep(linetime)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if _,err := conn.io.WriteString(line + "\r\n"); err != nil {
 | 
							if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
 | 
				
			||||||
			conn.error("irc.send(): %s", err.String())
 | 
								conn.error("irc.send(): %s", err.String())
 | 
				
			||||||
			conn.shutdown()
 | 
								conn.shutdown()
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
| 
						 | 
					@ -183,8 +201,7 @@ func (conn *Conn) recv() {
 | 
				
			||||||
			conn.shutdown()
 | 
								conn.shutdown()
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// chop off \r\n
 | 
							s = strings.Trim(s, "\r\n")
 | 
				
			||||||
		s = s[0 : len(s)-2]
 | 
					 | 
				
			||||||
		if conn.Debug {
 | 
							if conn.Debug {
 | 
				
			||||||
			fmt.Println("<- " + s)
 | 
								fmt.Println("<- " + s)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										335
									
								
								irc/handlers.go
									
										
									
									
									
								
							
							
						
						
									
										335
									
								
								irc/handlers.go
									
										
									
									
									
								
							| 
						 | 
					@ -3,10 +3,7 @@ package irc
 | 
				
			||||||
// this file contains the basic set of event handlers
 | 
					// this file contains the basic set of event handlers
 | 
				
			||||||
// to manage tracking an irc connection etc.
 | 
					// to manage tracking an irc connection etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import "strings"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddHandler() adds an event handler for a specific IRC command.
 | 
					// AddHandler() adds an event handler for a specific IRC command.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
| 
						 | 
					@ -24,17 +21,7 @@ import (
 | 
				
			||||||
func (conn *Conn) AddHandler(name string, f func(*Conn, *Line)) {
 | 
					func (conn *Conn) AddHandler(name string, f func(*Conn, *Line)) {
 | 
				
			||||||
	n := strings.ToUpper(name)
 | 
						n := strings.ToUpper(name)
 | 
				
			||||||
	if e, ok := conn.events[n]; ok {
 | 
						if e, ok := conn.events[n]; ok {
 | 
				
			||||||
		if len(e) == cap(e) {
 | 
							conn.events[n] = append(e, f)
 | 
				
			||||||
			// crap, we're full. expand e by another 10 handler slots
 | 
					 | 
				
			||||||
			ne := make([]func(*Conn, *Line), len(e), len(e)+10)
 | 
					 | 
				
			||||||
			for i := 0; i < len(e); i++ {
 | 
					 | 
				
			||||||
				ne[i] = e[i]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			e = ne
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		e = e[0 : len(e)+1]
 | 
					 | 
				
			||||||
		e[len(e)-1] = f
 | 
					 | 
				
			||||||
		conn.events[n] = e
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		e := make([]func(*Conn, *Line), 1, 10)
 | 
							e := make([]func(*Conn, *Line), 1, 10)
 | 
				
			||||||
		e[0] = f
 | 
							e[0] = f
 | 
				
			||||||
| 
						 | 
					@ -85,24 +72,13 @@ func (conn *Conn) dispatchEvent(line *Line) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// sets up the internal event handlers to do useful things with lines
 | 
					// Basic ping/pong handler
 | 
				
			||||||
// XXX: is there a better way of doing this?
 | 
					func (conn *Conn) h_PING(line *Line) {
 | 
				
			||||||
// Turns out there may be but it's not actually implemented in the language yet
 | 
						conn.Raw("PONG :" + line.Text)
 | 
				
			||||||
// according to the people on freenode/#go-nuts ... :-(
 | 
					}
 | 
				
			||||||
// see: http://golang.org/doc/go_spec.html#Method_expressions for details
 | 
					 | 
				
			||||||
// I think this means we should be able to do something along the lines of:
 | 
					 | 
				
			||||||
//   conn.AddHandler("event", (*Conn).h_handler);
 | 
					 | 
				
			||||||
// where h_handler is declared in the irc package as:
 | 
					 | 
				
			||||||
//   func (conn *Conn) h_handler(line *Line) {}
 | 
					 | 
				
			||||||
// in the future, but for now the compiler throws a hissy fit.
 | 
					 | 
				
			||||||
func (conn *Conn) setupEvents() {
 | 
					 | 
				
			||||||
	conn.events = make(map[string][]func(*Conn, *Line))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Basic ping/pong handler
 | 
					// Handler to trigger a "CONNECTED" event on receipt of numeric 001
 | 
				
			||||||
	conn.AddHandler("PING", func(conn *Conn, line *Line) { conn.Raw("PONG :" + line.Text) })
 | 
					func (conn *Conn) h_001(line *Line) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Handler to trigger a "CONNECTED" event on receipt of numeric 001
 | 
					 | 
				
			||||||
	conn.AddHandler("001", func(conn *Conn, line *Line) {
 | 
					 | 
				
			||||||
	// we're connected!
 | 
						// we're connected!
 | 
				
			||||||
	conn.connected = true
 | 
						conn.connected = true
 | 
				
			||||||
	conn.dispatchEvent(&Line{Cmd: "CONNECTED"})
 | 
						conn.dispatchEvent(&Line{Cmd: "CONNECTED"})
 | 
				
			||||||
| 
						 | 
					@ -113,18 +89,18 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
			conn.Me.Host = h[idx+1 : len(h)]
 | 
								conn.Me.Host = h[idx+1 : len(h)]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// XXX: do we need 005 protocol support message parsing here?
 | 
					// XXX: do we need 005 protocol support message parsing here?
 | 
				
			||||||
	// probably in the future, but I can't quite be arsed yet.
 | 
					// probably in the future, but I can't quite be arsed yet.
 | 
				
			||||||
	/*
 | 
					/*
 | 
				
			||||||
	:irc.pl0rt.org 005 GoTest CMDS=KNOCK,MAP,DCCALLOW,USERIP UHNAMES NAMESX SAFELIST HCN MAXCHANNELS=20 CHANLIMIT=#:20 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 :are supported by this server
 | 
						:irc.pl0rt.org 005 GoTest CMDS=KNOCK,MAP,DCCALLOW,USERIP UHNAMES NAMESX SAFELIST HCN MAXCHANNELS=20 CHANLIMIT=#:20 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 :are supported by this server
 | 
				
			||||||
	:irc.pl0rt.org 005 GoTest MAXTARGETS=20 WALLCHOPS WATCH=128 WATCHOPTS=A SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(qaohv)~&@%+ CHANMODES=beI,kfL,lj,psmntirRcOAQKVCuzNSMT NETWORK=bb101.net CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT :are supported by this server
 | 
						:irc.pl0rt.org 005 GoTest MAXTARGETS=20 WALLCHOPS WATCH=128 WATCHOPTS=A SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(qaohv)~&@%+ CHANMODES=beI,kfL,lj,psmntirRcOAQKVCuzNSMT NETWORK=bb101.net CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT :are supported by this server
 | 
				
			||||||
	:irc.pl0rt.org 005 GoTest STATUSMSG=~&@%+ EXCEPTS INVEX :are supported by this server
 | 
						:irc.pl0rt.org 005 GoTest STATUSMSG=~&@%+ EXCEPTS INVEX :are supported by this server
 | 
				
			||||||
	*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handler to deal with "433 :Nickname already in use"
 | 
					// Handler to deal with "433 :Nickname already in use"
 | 
				
			||||||
	conn.AddHandler("433", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_433(line *Line) {
 | 
				
			||||||
	// Args[1] is the new nick we were attempting to acquire
 | 
						// Args[1] is the new nick we were attempting to acquire
 | 
				
			||||||
	conn.Nick(line.Args[1] + "_")
 | 
						conn.Nick(line.Args[1] + "_")
 | 
				
			||||||
	// if this is happening before we're properly connected (i.e. the nick
 | 
						// if this is happening before we're properly connected (i.e. the nick
 | 
				
			||||||
| 
						 | 
					@ -133,30 +109,39 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
	if !conn.connected && line.Args[1] == conn.Me.Nick {
 | 
						if !conn.connected && line.Args[1] == conn.Me.Nick {
 | 
				
			||||||
		conn.Me.ReNick(line.Args[1] + "_")
 | 
							conn.Me.ReNick(line.Args[1] + "_")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handler NICK messages to inform us about nick changes
 | 
					// Handler NICK messages to inform us about nick changes
 | 
				
			||||||
	conn.AddHandler("NICK", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_NICK(line *Line) {
 | 
				
			||||||
	// all nicks should be handled the same way, our own included
 | 
						// all nicks should be handled the same way, our own included
 | 
				
			||||||
	if n := conn.GetNick(line.Nick); n != nil {
 | 
						if n := conn.GetNick(line.Nick); n != nil {
 | 
				
			||||||
		n.ReNick(line.Text)
 | 
							n.ReNick(line.Text)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.NICK(): buh? unknown nick %s.", line.Nick)
 | 
							conn.error("irc.NICK(): buh? unknown nick %s.", line.Nick)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle VERSION requests and CTCP PING
 | 
					// Handle VERSION requests and CTCP PING
 | 
				
			||||||
	conn.AddHandler("CTCP", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_CTCP(line *Line) {
 | 
				
			||||||
	if line.Args[0] == "VERSION" {
 | 
						if line.Args[0] == "VERSION" {
 | 
				
			||||||
		conn.CtcpReply(line.Nick, "VERSION", "powered by goirc...")
 | 
							conn.CtcpReply(line.Nick, "VERSION", "powered by goirc...")
 | 
				
			||||||
	} else if line.Args[0] == "PING" {
 | 
						} else if line.Args[0] == "PING" {
 | 
				
			||||||
		conn.CtcpReply(line.Nick, "PING", line.Text)
 | 
							conn.CtcpReply(line.Nick, "PING", line.Text)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle JOINs to channels to maintain state
 | 
					// Handle JOINs to channels to maintain state
 | 
				
			||||||
	conn.AddHandler("JOIN", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_JOIN(line *Line) {
 | 
				
			||||||
		ch := conn.GetChannel(line.Text)
 | 
						// Some IRCds (ircu) send ':n!u@h JOIN #chan' not ':n!u@h JOIN :#chan'
 | 
				
			||||||
 | 
						// Unfortunately the RFCs aren't specific about this. In fact the
 | 
				
			||||||
 | 
						// examples indicate no colon should be sent, but it's unusual.
 | 
				
			||||||
 | 
						var chname string
 | 
				
			||||||
 | 
						if len(line.Text) > 0 {
 | 
				
			||||||
 | 
							chname = line.Text
 | 
				
			||||||
 | 
						} else if len(line.Args) > 0 {
 | 
				
			||||||
 | 
							chname = line.Args[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ch := conn.GetChannel(chname)
 | 
				
			||||||
	n := conn.GetNick(line.Nick)
 | 
						n := conn.GetNick(line.Nick)
 | 
				
			||||||
	if ch == nil {
 | 
						if ch == nil {
 | 
				
			||||||
		// first we've seen of this channel, so should be us joining it
 | 
							// first we've seen of this channel, so should be us joining it
 | 
				
			||||||
| 
						 | 
					@ -165,11 +150,11 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
			conn.error("irc.JOIN(): buh? JOIN to unknown channel %s recieved from (non-me) nick %s", line.Text, line.Nick)
 | 
								conn.error("irc.JOIN(): buh? JOIN to unknown channel %s recieved from (non-me) nick %s", line.Text, line.Nick)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
			ch = conn.NewChannel(line.Text)
 | 
							ch = conn.NewChannel(chname)
 | 
				
			||||||
		// since we don't know much about this channel, ask server for info
 | 
							// since we don't know much about this channel, ask server for info
 | 
				
			||||||
		// we get the channel users automatically in 353 and the channel
 | 
							// we get the channel users automatically in 353 and the channel
 | 
				
			||||||
		// topic in 332 on join, so we just need to get the modes
 | 
							// topic in 332 on join, so we just need to get the modes
 | 
				
			||||||
			conn.Mode(ch.Name,"")
 | 
							conn.Mode(ch.Name)
 | 
				
			||||||
		// sending a WHO for the channel is MUCH more efficient than
 | 
							// sending a WHO for the channel is MUCH more efficient than
 | 
				
			||||||
		// triggering a WHOIS on every nick from the 353 handler
 | 
							// triggering a WHOIS on every nick from the 353 handler
 | 
				
			||||||
		conn.Who(ch.Name)
 | 
							conn.Who(ch.Name)
 | 
				
			||||||
| 
						 | 
					@ -182,24 +167,29 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// this takes care of both nick and channel linking \o/
 | 
						// this takes care of both nick and channel linking \o/
 | 
				
			||||||
	ch.AddNick(n)
 | 
						ch.AddNick(n)
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle PARTs from channels to maintain state
 | 
					// Handle PARTs from channels to maintain state
 | 
				
			||||||
	conn.AddHandler("PART", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_PART(line *Line) {
 | 
				
			||||||
		var ch *Channel
 | 
						// Some IRCds (ircu) send 'PART :#chan' when there's no part message
 | 
				
			||||||
 | 
						// instead of 'PART #chan'. This is *questionable* behaviour...
 | 
				
			||||||
 | 
						var chname string
 | 
				
			||||||
	if len(line.Args) > 0 {
 | 
						if len(line.Args) > 0 {
 | 
				
			||||||
			ch = conn.GetChannel(line.Args[0])
 | 
							chname = line.Args[0]
 | 
				
			||||||
 | 
						} else if len(line.Text) > 0 {
 | 
				
			||||||
 | 
							chname = line.Text
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						ch := conn.GetChannel(chname)
 | 
				
			||||||
	n := conn.GetNick(line.Nick)
 | 
						n := conn.GetNick(line.Nick)
 | 
				
			||||||
	if ch != nil && n != nil {
 | 
						if ch != nil && n != nil {
 | 
				
			||||||
		ch.DelNick(n)
 | 
							ch.DelNick(n)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
			conn.error("irc.PART(): buh? %s", line.Raw)
 | 
							conn.error("irc.PART(): buh? PART of channel %s by nick %s", chname, line.Nick)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle KICKs from channels to maintain state
 | 
					// Handle KICKs from channels to maintain state
 | 
				
			||||||
	conn.AddHandler("KICK", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_KICK(line *Line) {
 | 
				
			||||||
	// XXX: this won't handle autorejoining channels on KICK
 | 
						// XXX: this won't handle autorejoining channels on KICK
 | 
				
			||||||
	// it's trivial to do this in a seperate handler...
 | 
						// it's trivial to do this in a seperate handler...
 | 
				
			||||||
	ch := conn.GetChannel(line.Args[0])
 | 
						ch := conn.GetChannel(line.Args[0])
 | 
				
			||||||
| 
						 | 
					@ -209,128 +199,29 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.KICK(): buh? KICK from channel %s of nick %s", line.Args[0], line.Args[1])
 | 
							conn.error("irc.KICK(): buh? KICK from channel %s of nick %s", line.Args[0], line.Args[1])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle other people's QUITs
 | 
					// Handle other people's QUITs
 | 
				
			||||||
	conn.AddHandler("QUIT", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_QUIT(line *Line) {
 | 
				
			||||||
	if n := conn.GetNick(line.Nick); n != nil {
 | 
						if n := conn.GetNick(line.Nick); n != nil {
 | 
				
			||||||
		n.Delete()
 | 
							n.Delete()
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.QUIT(): buh? QUIT from unknown nick %s", line.Nick)
 | 
							conn.error("irc.QUIT(): buh? QUIT from unknown nick %s", line.Nick)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle MODE changes for channels we know about (and our nick personally)
 | 
					// Handle MODE changes for channels we know about (and our nick personally)
 | 
				
			||||||
	// this is moderately ugly. suggestions for improvement welcome
 | 
					func (conn *Conn) h_MODE(line *Line) {
 | 
				
			||||||
	conn.AddHandler("MODE", func(conn *Conn, line *Line) {
 | 
					 | 
				
			||||||
	// channel modes first
 | 
						// channel modes first
 | 
				
			||||||
	if ch := conn.GetChannel(line.Args[0]); ch != nil {
 | 
						if ch := conn.GetChannel(line.Args[0]); ch != nil {
 | 
				
			||||||
			modeargs := line.Args[2:len(line.Args)]
 | 
							conn.ParseChannelModes(ch, line.Args[1], line.Args[2:len(line.Args)])
 | 
				
			||||||
			var modeop bool // true => add mode, false => remove mode
 | 
					 | 
				
			||||||
			var modestr string
 | 
					 | 
				
			||||||
			for i := 0; i < len(line.Args[1]); i++ {
 | 
					 | 
				
			||||||
				switch m := line.Args[1][i]; m {
 | 
					 | 
				
			||||||
				case '+':
 | 
					 | 
				
			||||||
					modeop = true
 | 
					 | 
				
			||||||
					modestr = string(m)
 | 
					 | 
				
			||||||
				case '-':
 | 
					 | 
				
			||||||
					modeop = false
 | 
					 | 
				
			||||||
					modestr = string(m)
 | 
					 | 
				
			||||||
				case 'i':
 | 
					 | 
				
			||||||
					ch.Modes.InviteOnly = modeop
 | 
					 | 
				
			||||||
				case 'm':
 | 
					 | 
				
			||||||
					ch.Modes.Moderated = modeop
 | 
					 | 
				
			||||||
				case 'n':
 | 
					 | 
				
			||||||
					ch.Modes.NoExternalMsg = modeop
 | 
					 | 
				
			||||||
				case 'p':
 | 
					 | 
				
			||||||
					ch.Modes.Private = modeop
 | 
					 | 
				
			||||||
				case 's':
 | 
					 | 
				
			||||||
					ch.Modes.Secret = modeop
 | 
					 | 
				
			||||||
				case 't':
 | 
					 | 
				
			||||||
					ch.Modes.ProtectedTopic = modeop
 | 
					 | 
				
			||||||
				case 'z':
 | 
					 | 
				
			||||||
					ch.Modes.SSLOnly = modeop
 | 
					 | 
				
			||||||
				case 'O':
 | 
					 | 
				
			||||||
					ch.Modes.OperOnly = modeop
 | 
					 | 
				
			||||||
				case 'k':
 | 
					 | 
				
			||||||
					if len(modeargs) != 0 {
 | 
					 | 
				
			||||||
						ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)]
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				case 'l':
 | 
					 | 
				
			||||||
					if len(modeargs) != 0 {
 | 
					 | 
				
			||||||
						ch.Modes.Limit, _ = strconv.Atoi(modeargs[0])
 | 
					 | 
				
			||||||
						modeargs = modeargs[1:len(modeargs)]
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				case 'q', 'a', 'o', 'h', 'v':
 | 
					 | 
				
			||||||
					if len(modeargs) != 0 {
 | 
					 | 
				
			||||||
						n := conn.GetNick(modeargs[0])
 | 
					 | 
				
			||||||
						if p, ok := ch.Nicks[n]; ok && n != nil {
 | 
					 | 
				
			||||||
							switch m {
 | 
					 | 
				
			||||||
							case 'q':
 | 
					 | 
				
			||||||
								p.Owner = modeop
 | 
					 | 
				
			||||||
							case 'a':
 | 
					 | 
				
			||||||
								p.Admin = modeop
 | 
					 | 
				
			||||||
							case 'o':
 | 
					 | 
				
			||||||
								p.Op = modeop
 | 
					 | 
				
			||||||
							case 'h':
 | 
					 | 
				
			||||||
								p.HalfOp = modeop
 | 
					 | 
				
			||||||
							case 'v':
 | 
					 | 
				
			||||||
								p.Voice = modeop
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
							modeargs = modeargs[1:len(modeargs)]
 | 
					 | 
				
			||||||
						} else {
 | 
					 | 
				
			||||||
							conn.error("irc.MODE(): MODE %s %s%s %s: buh? state tracking failure.", ch.Name, modestr, m, modeargs[0])
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				case 'b':
 | 
					 | 
				
			||||||
					if len(modeargs) != 0 {
 | 
					 | 
				
			||||||
						// we only care about host bans
 | 
					 | 
				
			||||||
						if modeop && strings.HasPrefix(modeargs[0], "*!*@") {
 | 
					 | 
				
			||||||
							for n, _ := range ch.Nicks {
 | 
					 | 
				
			||||||
								if modeargs[0][4:] == n.Host {
 | 
					 | 
				
			||||||
									ch.AddBan(n.Nick, modeargs[0])
 | 
					 | 
				
			||||||
								}
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
						} else if !modeop {
 | 
					 | 
				
			||||||
							ch.DeleteBan(modeargs[0])
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						modeargs = modeargs[1:len(modeargs)]
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
	} else if n := conn.GetNick(line.Args[0]); n != nil {
 | 
						} else if n := conn.GetNick(line.Args[0]); n != nil {
 | 
				
			||||||
		// nick mode change, should be us
 | 
							// nick mode change, should be us
 | 
				
			||||||
		if n != conn.Me {
 | 
							if n != conn.Me {
 | 
				
			||||||
			conn.error("irc.MODE(): buh? recieved MODE %s for (non-me) nick %s", line.Text, n.Nick)
 | 
								conn.error("irc.MODE(): buh? recieved MODE %s for (non-me) nick %s", line.Text, n.Nick)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
			var modeop bool // true => add mode, false => remove mode
 | 
							conn.ParseNickModes(n, line.Text)
 | 
				
			||||||
			for i := 0; i < len(line.Text); i++ {
 | 
					 | 
				
			||||||
				switch m := line.Text[i]; m {
 | 
					 | 
				
			||||||
				case '+':
 | 
					 | 
				
			||||||
					modeop = true
 | 
					 | 
				
			||||||
				case '-':
 | 
					 | 
				
			||||||
					modeop = false
 | 
					 | 
				
			||||||
				case 'i':
 | 
					 | 
				
			||||||
					n.Modes.Invisible = modeop
 | 
					 | 
				
			||||||
				case 'o':
 | 
					 | 
				
			||||||
					n.Modes.Oper = modeop
 | 
					 | 
				
			||||||
				case 'w':
 | 
					 | 
				
			||||||
					n.Modes.WallOps = modeop
 | 
					 | 
				
			||||||
				case 'x':
 | 
					 | 
				
			||||||
					n.Modes.HiddenHost = modeop
 | 
					 | 
				
			||||||
				case 'z':
 | 
					 | 
				
			||||||
					n.Modes.SSL = modeop
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if line.Text != "" {
 | 
							if line.Text != "" {
 | 
				
			||||||
			conn.error("irc.MODE(): buh? not sure what to do with nick MODE %s %s", line.Args[0], line.Text)
 | 
								conn.error("irc.MODE(): buh? not sure what to do with nick MODE %s %s", line.Args[0], line.Text)
 | 
				
			||||||
| 
						 | 
					@ -338,19 +229,19 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
			conn.error("irc.MODE(): buh? not sure what to do with chan MODE %s", strings.Join(line.Args, " "))
 | 
								conn.error("irc.MODE(): buh? not sure what to do with chan MODE %s", strings.Join(line.Args, " "))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle TOPIC changes for channels
 | 
					// Handle TOPIC changes for channels
 | 
				
			||||||
	conn.AddHandler("TOPIC", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_TOPIC(line *Line) {
 | 
				
			||||||
	if ch := conn.GetChannel(line.Args[0]); ch != nil {
 | 
						if ch := conn.GetChannel(line.Args[0]); ch != nil {
 | 
				
			||||||
		ch.Topic = line.Text
 | 
							ch.Topic = line.Text
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.TOPIC(): buh? topic change on unknown channel %s", line.Args[0])
 | 
							conn.error("irc.TOPIC(): buh? topic change on unknown channel %s", line.Args[0])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle 311 whois reply
 | 
					// Handle 311 whois reply
 | 
				
			||||||
	conn.AddHandler("311", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_311(line *Line) {
 | 
				
			||||||
	if n := conn.GetNick(line.Args[1]); n != nil {
 | 
						if n := conn.GetNick(line.Args[1]); n != nil {
 | 
				
			||||||
		n.Ident = line.Args[2]
 | 
							n.Ident = line.Args[2]
 | 
				
			||||||
		n.Host = line.Args[3]
 | 
							n.Host = line.Args[3]
 | 
				
			||||||
| 
						 | 
					@ -358,70 +249,29 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.311(): buh? received WHOIS info for unknown nick %s", line.Args[1])
 | 
							conn.error("irc.311(): buh? received WHOIS info for unknown nick %s", line.Args[1])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle 324 mode reply
 | 
					// Handle 324 mode reply
 | 
				
			||||||
	conn.AddHandler("324", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_324(line *Line) {
 | 
				
			||||||
	// XXX: copypasta from MODE, needs tidying.
 | 
						// XXX: copypasta from MODE, needs tidying.
 | 
				
			||||||
	if ch := conn.GetChannel(line.Args[1]); ch != nil {
 | 
						if ch := conn.GetChannel(line.Args[1]); ch != nil {
 | 
				
			||||||
			modeargs := line.Args[3:len(line.Args)]
 | 
							conn.ParseChannelModes(ch, line.Args[2], line.Args[3:len(line.Args)])
 | 
				
			||||||
			var modeop bool // true => add mode, false => remove mode
 | 
					 | 
				
			||||||
			var modestr string
 | 
					 | 
				
			||||||
			for i := 0; i < len(line.Args[2]); i++ {
 | 
					 | 
				
			||||||
				switch m := line.Args[2][i]; m {
 | 
					 | 
				
			||||||
				case '+':
 | 
					 | 
				
			||||||
					modeop = true
 | 
					 | 
				
			||||||
					modestr = string(m)
 | 
					 | 
				
			||||||
				case '-':
 | 
					 | 
				
			||||||
					modeop = false
 | 
					 | 
				
			||||||
					modestr = string(m)
 | 
					 | 
				
			||||||
				case 'i':
 | 
					 | 
				
			||||||
					ch.Modes.InviteOnly = modeop
 | 
					 | 
				
			||||||
				case 'm':
 | 
					 | 
				
			||||||
					ch.Modes.Moderated = modeop
 | 
					 | 
				
			||||||
				case 'n':
 | 
					 | 
				
			||||||
					ch.Modes.NoExternalMsg = modeop
 | 
					 | 
				
			||||||
				case 'p':
 | 
					 | 
				
			||||||
					ch.Modes.Private = modeop
 | 
					 | 
				
			||||||
				case 's':
 | 
					 | 
				
			||||||
					ch.Modes.Secret = modeop
 | 
					 | 
				
			||||||
				case 't':
 | 
					 | 
				
			||||||
					ch.Modes.ProtectedTopic = modeop
 | 
					 | 
				
			||||||
				case 'z':
 | 
					 | 
				
			||||||
					ch.Modes.SSLOnly = modeop
 | 
					 | 
				
			||||||
				case 'O':
 | 
					 | 
				
			||||||
					ch.Modes.OperOnly = modeop
 | 
					 | 
				
			||||||
				case 'k':
 | 
					 | 
				
			||||||
					if len(modeargs) != 0 {
 | 
					 | 
				
			||||||
						ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)]
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						conn.error("irc.324(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				case 'l':
 | 
					 | 
				
			||||||
					if len(modeargs) != 0 {
 | 
					 | 
				
			||||||
						ch.Modes.Limit, _ = strconv.Atoi(modeargs[0])
 | 
					 | 
				
			||||||
						modeargs = modeargs[1:len(modeargs)]
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						conn.error("irc.324(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.324(): buh? received MODE settings for unknown channel %s", line.Args[1])
 | 
							conn.error("irc.324(): buh? received MODE settings for unknown channel %s", line.Args[1])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle 332 topic reply on join to channel
 | 
					// Handle 332 topic reply on join to channel
 | 
				
			||||||
	conn.AddHandler("332", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_332(line *Line) {
 | 
				
			||||||
	if ch := conn.GetChannel(line.Args[1]); ch != nil {
 | 
						if ch := conn.GetChannel(line.Args[1]); ch != nil {
 | 
				
			||||||
		ch.Topic = line.Text
 | 
							ch.Topic = line.Text
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.332(): buh? received TOPIC value for unknown channel %s", line.Args[1])
 | 
							conn.error("irc.332(): buh? received TOPIC value for unknown channel %s", line.Args[1])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle 352 who reply
 | 
					// Handle 352 who reply
 | 
				
			||||||
	conn.AddHandler("352", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_352(line *Line) {
 | 
				
			||||||
	if n := conn.GetNick(line.Args[5]); n != nil {
 | 
						if n := conn.GetNick(line.Args[5]); n != nil {
 | 
				
			||||||
		n.Ident = line.Args[2]
 | 
							n.Ident = line.Args[2]
 | 
				
			||||||
		n.Host = line.Args[3]
 | 
							n.Host = line.Args[3]
 | 
				
			||||||
| 
						 | 
					@ -439,10 +289,10 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.352(): buh? got WHO reply for unknown nick %s", line.Args[5])
 | 
							conn.error("irc.352(): buh? got WHO reply for unknown nick %s", line.Args[5])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle 353 names reply
 | 
					// Handle 353 names reply
 | 
				
			||||||
	conn.AddHandler("353", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_353(line *Line) {
 | 
				
			||||||
	if ch := conn.GetChannel(line.Args[2]); ch != nil {
 | 
						if ch := conn.GetChannel(line.Args[2]); ch != nil {
 | 
				
			||||||
		nicks := strings.Split(line.Text, " ", -1)
 | 
							nicks := strings.Split(line.Text, " ", -1)
 | 
				
			||||||
		for _, nick := range nicks {
 | 
							for _, nick := range nicks {
 | 
				
			||||||
| 
						 | 
					@ -483,14 +333,37 @@ func (conn *Conn) setupEvents() {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.353(): buh? received NAMES list for unknown channel %s", line.Args[2])
 | 
							conn.error("irc.353(): buh? received NAMES list for unknown channel %s", line.Args[2])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle 671 whois reply (nick connected via SSL)
 | 
					// Handle 671 whois reply (nick connected via SSL)
 | 
				
			||||||
	conn.AddHandler("671", func(conn *Conn, line *Line) {
 | 
					func (conn *Conn) h_671(line *Line) {
 | 
				
			||||||
	if n := conn.GetNick(line.Args[1]); n != nil {
 | 
						if n := conn.GetNick(line.Args[1]); n != nil {
 | 
				
			||||||
		n.Modes.SSL = true
 | 
							n.Modes.SSL = true
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		conn.error("irc.671(): buh? received WHOIS SSL info for unknown nick %s", line.Args[1])
 | 
							conn.error("irc.671(): buh? received WHOIS SSL info for unknown nick %s", line.Args[1])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// sets up the internal event handlers to do useful things with lines
 | 
				
			||||||
 | 
					func (conn *Conn) setupEvents() {
 | 
				
			||||||
 | 
						conn.events = make(map[string][]func(*Conn, *Line))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn.AddHandler("CTCP", (*Conn).h_CTCP)
 | 
				
			||||||
 | 
						conn.AddHandler("JOIN", (*Conn).h_JOIN)
 | 
				
			||||||
 | 
						conn.AddHandler("KICK", (*Conn).h_KICK)
 | 
				
			||||||
 | 
						conn.AddHandler("MODE", (*Conn).h_MODE)
 | 
				
			||||||
 | 
						conn.AddHandler("NICK", (*Conn).h_NICK)
 | 
				
			||||||
 | 
						conn.AddHandler("PART", (*Conn).h_PART)
 | 
				
			||||||
 | 
						conn.AddHandler("PING", (*Conn).h_PING)
 | 
				
			||||||
 | 
						conn.AddHandler("QUIT", (*Conn).h_QUIT)
 | 
				
			||||||
 | 
						conn.AddHandler("TOPIC", (*Conn).h_TOPIC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn.AddHandler("001", (*Conn).h_001)
 | 
				
			||||||
 | 
						conn.AddHandler("311", (*Conn).h_311)
 | 
				
			||||||
 | 
						conn.AddHandler("324", (*Conn).h_324)
 | 
				
			||||||
 | 
						conn.AddHandler("332", (*Conn).h_332)
 | 
				
			||||||
 | 
						conn.AddHandler("352", (*Conn).h_352)
 | 
				
			||||||
 | 
						conn.AddHandler("353", (*Conn).h_353)
 | 
				
			||||||
 | 
						conn.AddHandler("433", (*Conn).h_433)
 | 
				
			||||||
 | 
						conn.AddHandler("671", (*Conn).h_671)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,4 +12,3 @@ func TestIRC(t *testing.T) {
 | 
				
			||||||
		t.FailNow()
 | 
							t.FailNow()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										111
									
								
								irc/nickchan.go
									
										
									
									
									
								
							
							
						
						
									
										111
									
								
								irc/nickchan.go
									
										
									
									
									
								
							| 
						 | 
					@ -6,6 +6,8 @@ package irc
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A struct representing an IRC channel
 | 
					// A struct representing an IRC channel
 | 
				
			||||||
| 
						 | 
					@ -97,6 +99,113 @@ func (conn *Conn) GetChannel(c string) *Channel {
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parses mode strings for a channel
 | 
				
			||||||
 | 
					func (conn *Conn) ParseChannelModes(ch *Channel, modes string, modeargs []string) {
 | 
				
			||||||
 | 
						var modeop bool // true => add mode, false => remove mode
 | 
				
			||||||
 | 
						var modestr string
 | 
				
			||||||
 | 
						for i := 0; i < len(modes); i++ {
 | 
				
			||||||
 | 
							switch m := modes[i]; m {
 | 
				
			||||||
 | 
							case '+':
 | 
				
			||||||
 | 
								modeop = true
 | 
				
			||||||
 | 
								modestr = string(m)
 | 
				
			||||||
 | 
							case '-':
 | 
				
			||||||
 | 
								modeop = false
 | 
				
			||||||
 | 
								modestr = string(m)
 | 
				
			||||||
 | 
							case 'i':
 | 
				
			||||||
 | 
								ch.Modes.InviteOnly = modeop
 | 
				
			||||||
 | 
							case 'm':
 | 
				
			||||||
 | 
								ch.Modes.Moderated = modeop
 | 
				
			||||||
 | 
							case 'n':
 | 
				
			||||||
 | 
								ch.Modes.NoExternalMsg = modeop
 | 
				
			||||||
 | 
							case 'p':
 | 
				
			||||||
 | 
								ch.Modes.Private = modeop
 | 
				
			||||||
 | 
							case 's':
 | 
				
			||||||
 | 
								ch.Modes.Secret = modeop
 | 
				
			||||||
 | 
							case 't':
 | 
				
			||||||
 | 
								ch.Modes.ProtectedTopic = modeop
 | 
				
			||||||
 | 
							case 'z':
 | 
				
			||||||
 | 
								ch.Modes.SSLOnly = modeop
 | 
				
			||||||
 | 
							case 'O':
 | 
				
			||||||
 | 
								ch.Modes.OperOnly = modeop
 | 
				
			||||||
 | 
							case 'k':
 | 
				
			||||||
 | 
								if len(modeargs) != 0 {
 | 
				
			||||||
 | 
									ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)]
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									conn.error("irc.ParseChanModes(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case 'l':
 | 
				
			||||||
 | 
								if len(modeargs) != 0 {
 | 
				
			||||||
 | 
									ch.Modes.Limit, _ = strconv.Atoi(modeargs[0])
 | 
				
			||||||
 | 
									modeargs = modeargs[1:len(modeargs)]
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									conn.error("irc.ParseChanModes(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case 'q', 'a', 'o', 'h', 'v':
 | 
				
			||||||
 | 
								if len(modeargs) != 0 {
 | 
				
			||||||
 | 
									n := conn.GetNick(modeargs[0])
 | 
				
			||||||
 | 
									if p, ok := ch.Nicks[n]; ok && n != nil {
 | 
				
			||||||
 | 
										switch m {
 | 
				
			||||||
 | 
										case 'q':
 | 
				
			||||||
 | 
											p.Owner = modeop
 | 
				
			||||||
 | 
										case 'a':
 | 
				
			||||||
 | 
											p.Admin = modeop
 | 
				
			||||||
 | 
										case 'o':
 | 
				
			||||||
 | 
											p.Op = modeop
 | 
				
			||||||
 | 
										case 'h':
 | 
				
			||||||
 | 
											p.HalfOp = modeop
 | 
				
			||||||
 | 
										case 'v':
 | 
				
			||||||
 | 
											p.Voice = modeop
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										modeargs = modeargs[1:len(modeargs)]
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										conn.error("irc.ParseChanModes(): MODE %s %s%s %s: buh? state tracking failure.", ch.Name, modestr, m, modeargs[0])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									conn.error("irc.ParseChanModes(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case 'b':
 | 
				
			||||||
 | 
								if len(modeargs) != 0 {
 | 
				
			||||||
 | 
									// we only care about host bans
 | 
				
			||||||
 | 
									if modeop && strings.HasPrefix(modeargs[0], "*!*@") {
 | 
				
			||||||
 | 
										for n, _ := range ch.Nicks {
 | 
				
			||||||
 | 
											if modeargs[0][4:] == n.Host {
 | 
				
			||||||
 | 
												ch.AddBan(n.Nick, modeargs[0])
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else if !modeop {
 | 
				
			||||||
 | 
										ch.DeleteBan(modeargs[0])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									modeargs = modeargs[1:len(modeargs)]
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parse mode strings for a nick 
 | 
				
			||||||
 | 
					func (conn *Conn) ParseNickModes(n *Nick, modes string) {
 | 
				
			||||||
 | 
						var modeop bool // true => add mode, false => remove mode
 | 
				
			||||||
 | 
						for i := 0; i < len(modes); i++ {
 | 
				
			||||||
 | 
							switch m := modes[i]; m {
 | 
				
			||||||
 | 
							case '+':
 | 
				
			||||||
 | 
								modeop = true
 | 
				
			||||||
 | 
							case '-':
 | 
				
			||||||
 | 
								modeop = false
 | 
				
			||||||
 | 
							case 'i':
 | 
				
			||||||
 | 
								n.Modes.Invisible = modeop
 | 
				
			||||||
 | 
							case 'o':
 | 
				
			||||||
 | 
								n.Modes.Oper = modeop
 | 
				
			||||||
 | 
							case 'w':
 | 
				
			||||||
 | 
								n.Modes.WallOps = modeop
 | 
				
			||||||
 | 
							case 'x':
 | 
				
			||||||
 | 
								n.Modes.HiddenHost = modeop
 | 
				
			||||||
 | 
							case 'z':
 | 
				
			||||||
 | 
								n.Modes.SSL = modeop
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/******************************************************************************\
 | 
					/******************************************************************************\
 | 
				
			||||||
 * Channel methods for state management
 | 
					 * Channel methods for state management
 | 
				
			||||||
\******************************************************************************/
 | 
					\******************************************************************************/
 | 
				
			||||||
| 
						 | 
					@ -340,7 +449,7 @@ func (cm *ChanMode) String() string {
 | 
				
			||||||
		case *reflect.IntValue:
 | 
							case *reflect.IntValue:
 | 
				
			||||||
			if f.Get() != 0 {
 | 
								if f.Get() != 0 {
 | 
				
			||||||
				str += ChanModeToString[t.Field(i).Name]
 | 
									str += ChanModeToString[t.Field(i).Name]
 | 
				
			||||||
				a[1] = fmt.Sprintf("%d", cm.Limit)
 | 
									a[1] = fmt.Sprintf("%d", f.Get())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue