From c2e3bbe494c4c5d5e1f662d3e1689d8d10565b3a Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Thu, 27 Mar 2014 16:48:13 -0700 Subject: [PATCH 1/8] fix whois; closes #32 --- irc/client_lookup_set.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/irc/client_lookup_set.go b/irc/client_lookup_set.go index 108de80b..c9147a07 100644 --- a/irc/client_lookup_set.go +++ b/irc/client_lookup_set.go @@ -92,12 +92,13 @@ func (clients *ClientLookupSet) FindAll(userhost Name) (set ClientSet) { return } for rows.Next() { - var nickname Name - err := rows.Scan(&nickname) + var sqlNickname string + err := rows.Scan(&sqlNickname) if err != nil { Log.error.Println("ClientLookupSet.FindAll.Scan:", err) return } + nickname := Name(sqlNickname) client := clients.Get(nickname) if client == nil { Log.error.Println("ClientLookupSet.FindAll: missing client:", nickname) From d696f2313ef523a1ee5438a0bccea17222b42bb8 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Thu, 27 Mar 2014 16:49:22 -0700 Subject: [PATCH 2/8] stop leaking client goroutines; closes #34 --- irc/client.go | 21 ++++++++++++++++----- irc/socket.go | 8 +------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/irc/client.go b/irc/client.go index 4e076b54..e6f95787 100644 --- a/irc/client.go +++ b/irc/client.go @@ -60,10 +60,17 @@ func NewClient(server *Server, conn net.Conn) *Client { // command goroutine // -func (client *Client) run() { - for command := range client.commands { - command.SetClient(client) +func (client *Client) send(command Command) { + command.SetClient(client) + client.server.commands <- command +} +func (client *Client) run() { + client.send(&ProxyCommand{ + hostname: AddrLookupHostname(client.socket.conn.RemoteAddr()), + }) + + for command := range client.commands { checkPass, ok := command.(checkPasswordCommand) if ok { checkPass.LoadPassword(client.server) @@ -75,8 +82,12 @@ func (client *Client) run() { checkPass.CheckPassword() } - client.server.commands <- command + client.send(command) } + + client.send(&QuitCommand{ + message: "connection closed", + }) } func (client *Client) connectionTimeout() { @@ -259,8 +270,8 @@ func (client *Client) Quit(message Text) { return } - client.Reply(RplError("connection closed")) client.hasQuit = true + client.Reply(RplError("quit")) client.server.whoWas.Append(client) friends := client.Friends() friends.Remove(client) diff --git a/irc/socket.go b/irc/socket.go index 84e90313..2b075ea0 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -41,10 +41,6 @@ func (socket *Socket) Close() { } func (socket *Socket) readLines(commands chan<- Command) { - commands <- &ProxyCommand{ - hostname: AddrLookupHostname(socket.conn.RemoteAddr()), - } - for { line, err := socket.reader.ReadString('\n') if socket.isError(err, R) { @@ -64,9 +60,7 @@ func (socket *Socket) readLines(commands chan<- Command) { commands <- msg } - commands <- &QuitCommand{ - message: "connection closed", - } + close(commands) } func (socket *Socket) Write(line string) (err error) { From 2dc69c7e3d32c9c99ed2dfc1b0c508d9e96e8b1c Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Thu, 27 Mar 2014 17:51:29 -0700 Subject: [PATCH 3/8] use a Scanner instead of ReadString --- irc/socket.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/irc/socket.go b/irc/socket.go index 2b075ea0..aa9be493 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -4,7 +4,6 @@ import ( "bufio" "io" "net" - "strings" ) const ( @@ -15,14 +14,12 @@ const ( type Socket struct { conn net.Conn - reader *bufio.Reader writer *bufio.Writer } func NewSocket(conn net.Conn, commands chan<- Command) *Socket { socket := &Socket{ conn: conn, - reader: bufio.NewReader(conn), writer: bufio.NewWriter(conn), } @@ -41,12 +38,9 @@ func (socket *Socket) Close() { } func (socket *Socket) readLines(commands chan<- Command) { - for { - line, err := socket.reader.ReadString('\n') - if socket.isError(err, R) { - break - } - line = strings.TrimRight(line, CRLF) + scanner := bufio.NewScanner(socket.conn) + for scanner.Scan() { + line := scanner.Text() if len(line) == 0 { continue } @@ -60,6 +54,10 @@ func (socket *Socket) readLines(commands chan<- Command) { commands <- msg } + if err := scanner.Err(); err != nil { + Log.debug.Printf("%s error: %s", socket, err) + } + close(commands) } From 3b12dec207cfac0e8695708a6427fdc504c23b00 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Thu, 27 Mar 2014 18:58:12 -0700 Subject: [PATCH 4/8] cleanup --- irc/client.go | 20 +++----------------- irc/socket.go | 8 ++++++++ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/irc/client.go b/irc/client.go index e6f95787..15e1c23a 100644 --- a/irc/client.go +++ b/irc/client.go @@ -60,19 +60,9 @@ func NewClient(server *Server, conn net.Conn) *Client { // command goroutine // -func (client *Client) send(command Command) { - command.SetClient(client) - client.server.commands <- command -} - func (client *Client) run() { - client.send(&ProxyCommand{ - hostname: AddrLookupHostname(client.socket.conn.RemoteAddr()), - }) - for command := range client.commands { - checkPass, ok := command.(checkPasswordCommand) - if ok { + if checkPass, ok := command.(checkPasswordCommand); ok { checkPass.LoadPassword(client.server) // Block the client thread while handling a potentially expensive // password bcrypt operation. Since the server is single-threaded @@ -81,13 +71,9 @@ func (client *Client) run() { // completes. This could be a form of DoS if handled naively. checkPass.CheckPassword() } - - client.send(command) + command.SetClient(client) + client.server.commands <- command } - - client.send(&QuitCommand{ - message: "connection closed", - }) } func (client *Client) connectionTimeout() { diff --git a/irc/socket.go b/irc/socket.go index aa9be493..f3eaa16e 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -38,6 +38,10 @@ func (socket *Socket) Close() { } func (socket *Socket) readLines(commands chan<- Command) { + commands <- &ProxyCommand{ + hostname: AddrLookupHostname(socket.conn.RemoteAddr()), + } + scanner := bufio.NewScanner(socket.conn) for scanner.Scan() { line := scanner.Text() @@ -58,6 +62,10 @@ func (socket *Socket) readLines(commands chan<- Command) { Log.debug.Printf("%s error: %s", socket, err) } + commands <- &QuitCommand{ + message: "connection closed", + } + close(commands) } From 63a46498ce9c4373a35ad3c9cc07fe9360480570 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Thu, 27 Mar 2014 19:04:34 -0700 Subject: [PATCH 5/8] reuse replies where possible --- irc/channel.go | 6 ++++-- irc/theater.go | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index 05a96459..a8308a4b 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -244,11 +244,12 @@ func (channel *Channel) PrivMsg(client *Client, message Text) { client.ErrCannotSendToChan(channel) return } + reply := RplPrivMsg(client, channel, message) for member := range channel.members { if member == client { continue } - member.Reply(RplPrivMsg(client, channel, message)) + member.Reply(reply) } } @@ -458,11 +459,12 @@ func (channel *Channel) Notice(client *Client, message Text) { client.ErrCannotSendToChan(channel) return } + reply := RplNotice(client, channel, message) for member := range channel.members { if member == client { continue } - member.Reply(RplNotice(client, channel, message)) + member.Reply(reply) } } diff --git a/irc/theater.go b/irc/theater.go index 36525048..4fa6a2f4 100644 --- a/irc/theater.go +++ b/irc/theater.go @@ -89,8 +89,9 @@ func (m *TheaterPrivMsgCommand) HandleServer(s *Server) { return } + reply := RplPrivMsg(TheaterClient(m.asNick), channel, m.message) for member := range channel.members { - member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, m.message)) + member.Reply(reply) } } @@ -124,7 +125,8 @@ func (m *TheaterActionCommand) HandleServer(s *Server) { return } + reply := RplCTCPAction(TheaterClient(m.asNick), channel, m.action) for member := range channel.members { - member.Reply(RplCTCPAction(TheaterClient(m.asNick), channel, m.action)) + member.Reply(reply) } } From f32df20a83e45bd432ea323847c274e9e1648858 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Fri, 28 Mar 2014 12:11:01 -0700 Subject: [PATCH 6/8] get rid of useless assignment --- irc/reply.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc/reply.go b/irc/reply.go index 2c11bea2..b4304bfb 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -81,7 +81,7 @@ func (target *Client) MultilineReply(names []string, code NumericCode, format st for to < len(names) { if (from < (to - 1)) && tooLong(names[from:to]) { target.NumericReply(code, format, argsAndNames(names[from:to-1])...) - from, to = to-1, to + from = to - 1 } else { to += 1 } From 21a86c3216369c5711636f60b0b8396da2b5482a Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Fri, 28 Mar 2014 12:35:05 -0700 Subject: [PATCH 7/8] fix gcstats debugging command --- irc/server.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/irc/server.go b/irc/server.go index 57f53d13..9701f4f3 100644 --- a/irc/server.go +++ b/irc/server.go @@ -654,9 +654,11 @@ func (msg *DebugCommand) HandleServer(server *Server) { server.Reply(client, "OK") case "GCSTATS": - stats := &debug.GCStats{ + stats := debug.GCStats{ + Pause: make([]time.Duration, 10), PauseQuantiles: make([]time.Duration, 5), } + debug.ReadGCStats(&stats) server.Reply(client, "last GC: %s", stats.LastGC.Format(time.RFC1123)) server.Reply(client, "num GC: %d", stats.NumGC) server.Reply(client, "pause total: %s", stats.PauseTotal) @@ -671,14 +673,15 @@ func (msg *DebugCommand) HandleServer(server *Server) { server.Reply(client, "num goroutines: %d", count) case "PROFILEHEAP": - file, err := os.Create("ergonomadic.heap.prof") + profFile := "ergonomadic-heap.prof" + file, err := os.Create(profFile) if err != nil { log.Printf("error: %s", err) break } defer file.Close() pprof.Lookup("heap").WriteTo(file, 0) - server.Reply(client, "written to ergonomadic-heap.prof") + server.Reply(client, "written to %s", profFile) } } From b0bb0faadcdf61afa7d133348684471b9ecc05d7 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Fri, 28 Mar 2014 13:47:49 -0700 Subject: [PATCH 8/8] remove unnecessary String()s; rename parse functions --- irc/client.go | 4 +- irc/commands.go | 231 +++++++++++++++++------------------------------- irc/nickname.go | 17 ++-- irc/server.go | 2 +- irc/socket.go | 8 +- irc/theater.go | 16 ---- 6 files changed, 93 insertions(+), 185 deletions(-) diff --git a/irc/client.go b/irc/client.go index 15e1c23a..ccadbfe6 100644 --- a/irc/client.go +++ b/irc/client.go @@ -77,9 +77,7 @@ func (client *Client) run() { } func (client *Client) connectionTimeout() { - client.commands <- &QuitCommand{ - message: "connection timeout", - } + client.commands <- NewQuitCommand("connection timeout") } // diff --git a/irc/commands.go b/irc/commands.go index 22153041..544367f6 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -26,37 +26,37 @@ var ( NotEnoughArgsError = errors.New("not enough arguments") ErrParseCommand = errors.New("failed to parse message") parseCommandFuncs = map[StringCode]parseCommandFunc{ - AWAY: NewAwayCommand, - CAP: NewCapCommand, - DEBUG: NewDebugCommand, - INVITE: NewInviteCommand, - ISON: NewIsOnCommand, - JOIN: NewJoinCommand, - KICK: NewKickCommand, - KILL: NewKillCommand, - LIST: NewListCommand, - MODE: NewModeCommand, - MOTD: NewMOTDCommand, - NAMES: NewNamesCommand, - NICK: NewNickCommand, - NOTICE: NewNoticeCommand, - ONICK: NewOperNickCommand, - OPER: NewOperCommand, - PART: NewPartCommand, - PASS: NewPassCommand, - PING: NewPingCommand, - PONG: NewPongCommand, - PRIVMSG: NewPrivMsgCommand, - PROXY: NewProxyCommand, - QUIT: NewQuitCommand, - THEATER: NewTheaterCommand, // nonstandard - TIME: NewTimeCommand, - TOPIC: NewTopicCommand, - USER: NewUserCommand, - VERSION: NewVersionCommand, - WHO: NewWhoCommand, - WHOIS: NewWhoisCommand, - WHOWAS: NewWhoWasCommand, + AWAY: ParseAwayCommand, + CAP: ParseCapCommand, + DEBUG: ParseDebugCommand, + INVITE: ParseInviteCommand, + ISON: ParseIsOnCommand, + JOIN: ParseJoinCommand, + KICK: ParseKickCommand, + KILL: ParseKillCommand, + LIST: ParseListCommand, + MODE: ParseModeCommand, + MOTD: ParseMOTDCommand, + NAMES: ParseNamesCommand, + NICK: ParseNickCommand, + NOTICE: ParseNoticeCommand, + ONICK: ParseOperNickCommand, + OPER: ParseOperCommand, + PART: ParsePartCommand, + PASS: ParsePassCommand, + PING: ParsePingCommand, + PONG: ParsePongCommand, + PRIVMSG: ParsePrivMsgCommand, + PROXY: ParseProxyCommand, + QUIT: ParseQuitCommand, + THEATER: ParseTheaterCommand, // nonstandard + TIME: ParseTimeCommand, + TOPIC: ParseTopicCommand, + USER: ParseUserCommand, + VERSION: ParseVersionCommand, + WHO: ParseWhoCommand, + WHOIS: ParseWhoisCommand, + WHOWAS: ParseWhoWasCommand, } ) @@ -85,7 +85,7 @@ func ParseCommand(line string) (cmd Command, err error) { code, args := ParseLine(line) constructor := parseCommandFuncs[code] if constructor == nil { - cmd = NewUnknownCommand(args) + cmd = ParseUnknownCommand(args) } else { cmd, err = constructor(args) } @@ -135,11 +135,7 @@ type UnknownCommand struct { args []string } -func (cmd *UnknownCommand) String() string { - return fmt.Sprintf("UNKNOWN(command=%s, args=%s)", cmd.Code(), cmd.args) -} - -func NewUnknownCommand(args []string) *UnknownCommand { +func ParseUnknownCommand(args []string) *UnknownCommand { return &UnknownCommand{ args: args, } @@ -153,11 +149,7 @@ type PingCommand struct { server2 Name } -func (cmd *PingCommand) String() string { - return fmt.Sprintf("PING(server=%s, server2=%s)", cmd.server, cmd.server2) -} - -func NewPingCommand(args []string) (Command, error) { +func ParsePingCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } @@ -178,11 +170,7 @@ type PongCommand struct { server2 Name } -func (cmd *PongCommand) String() string { - return fmt.Sprintf("PONG(server1=%s, server2=%s)", cmd.server1, cmd.server2) -} - -func NewPongCommand(args []string) (Command, error) { +func ParsePongCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } @@ -204,10 +192,6 @@ type PassCommand struct { err error } -func (cmd *PassCommand) String() string { - return fmt.Sprintf("PASS(password=%s)", cmd.password) -} - func (cmd *PassCommand) LoadPassword(server *Server) { cmd.hash = server.password } @@ -219,7 +203,7 @@ func (cmd *PassCommand) CheckPassword() { cmd.err = ComparePassword(cmd.hash, cmd.password) } -func NewPassCommand(args []string) (Command, error) { +func ParsePassCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } @@ -230,7 +214,7 @@ func NewPassCommand(args []string) (Command, error) { // NICK -func NewNickCommand(args []string) (Command, error) { +func ParseNickCommand(args []string) (Command, error) { if len(args) != 1 { return nil, NotEnoughArgsError } @@ -252,11 +236,6 @@ type RFC1459UserCommand struct { servername Name } -func (cmd *RFC1459UserCommand) String() string { - return fmt.Sprintf("USER(username=%s, hostname=%s, servername=%s, realname=%s)", - cmd.username, cmd.hostname, cmd.servername, cmd.realname) -} - // USER type RFC2812UserCommand struct { UserCommand @@ -264,11 +243,6 @@ type RFC2812UserCommand struct { unused string } -func (cmd *RFC2812UserCommand) String() string { - return fmt.Sprintf("USER(username=%s, mode=%d, unused=%s, realname=%s)", - cmd.username, cmd.mode, cmd.unused, cmd.realname) -} - func (cmd *RFC2812UserCommand) Flags() []UserMode { flags := make([]UserMode, 0) if (cmd.mode & 4) == 4 { @@ -280,7 +254,7 @@ func (cmd *RFC2812UserCommand) Flags() []UserMode { return flags } -func NewUserCommand(args []string) (Command, error) { +func ParseUserCommand(args []string) (Command, error) { if len(args) != 4 { return nil, NotEnoughArgsError } @@ -311,11 +285,15 @@ type QuitCommand struct { message Text } -func (cmd *QuitCommand) String() string { - return fmt.Sprintf("QUIT(message=%s)", cmd.message) +func NewQuitCommand(message Text) *QuitCommand { + cmd := &QuitCommand{ + message: message, + } + cmd.code = QUIT + return cmd } -func NewQuitCommand(args []string) (Command, error) { +func ParseQuitCommand(args []string) (Command, error) { msg := &QuitCommand{} if len(args) > 0 { msg.message = NewText(args[0]) @@ -331,11 +309,7 @@ type JoinCommand struct { zero bool } -func (cmd *JoinCommand) String() string { - return fmt.Sprintf("JOIN(channels=%s, zero=%t)", cmd.channels, cmd.zero) -} - -func NewJoinCommand(args []string) (Command, error) { +func ParseJoinCommand(args []string) (Command, error) { msg := &JoinCommand{ channels: make(map[Name]Text), } @@ -378,11 +352,7 @@ func (cmd *PartCommand) Message() Text { return cmd.message } -func (cmd *PartCommand) String() string { - return fmt.Sprintf("PART(channels=%s, message=%s)", cmd.channels, cmd.message) -} - -func NewPartCommand(args []string) (Command, error) { +func ParsePartCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } @@ -403,11 +373,7 @@ type PrivMsgCommand struct { message Text } -func (cmd *PrivMsgCommand) String() string { - return fmt.Sprintf("PRIVMSG(target=%s, message=%s)", cmd.target, cmd.message) -} - -func NewPrivMsgCommand(args []string) (Command, error) { +func ParsePrivMsgCommand(args []string) (Command, error) { if len(args) < 2 { return nil, NotEnoughArgsError } @@ -426,11 +392,7 @@ type TopicCommand struct { topic Text } -func (cmd *TopicCommand) String() string { - return fmt.Sprintf("TOPIC(channel=%s, topic=%s)", cmd.channel, cmd.topic) -} - -func NewTopicCommand(args []string) (Command, error) { +func ParseTopicCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } @@ -480,7 +442,7 @@ type ModeCommand struct { } // MODE *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) ) -func NewUserModeCommand(nickname Name, args []string) (Command, error) { +func ParseUserModeCommand(nickname Name, args []string) (Command, error) { cmd := &ModeCommand{ nickname: nickname, changes: make(ModeChanges, 0), @@ -506,10 +468,6 @@ func NewUserModeCommand(nickname Name, args []string) (Command, error) { return cmd, nil } -func (cmd *ModeCommand) String() string { - return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes) -} - type ChannelModeChange struct { mode ChannelMode op ModeOp @@ -557,7 +515,7 @@ type ChannelModeCommand struct { } // MODE *( ( "-" / "+" ) * * ) -func NewChannelModeCommand(channel Name, args []string) (Command, error) { +func ParseChannelModeCommand(channel Name, args []string) (Command, error) { cmd := &ChannelModeCommand{ channel: channel, changes: make(ChannelModeChanges, 0), @@ -599,20 +557,16 @@ func NewChannelModeCommand(channel Name, args []string) (Command, error) { return cmd, nil } -func (msg *ChannelModeCommand) String() string { - return fmt.Sprintf("MODE(channel=%s, changes=%s)", msg.channel, msg.changes) -} - -func NewModeCommand(args []string) (Command, error) { +func ParseModeCommand(args []string) (Command, error) { if len(args) == 0 { return nil, NotEnoughArgsError } name := NewName(args[0]) if name.IsChannel() { - return NewChannelModeCommand(name, args[1:]) + return ParseChannelModeCommand(name, args[1:]) } else { - return NewUserModeCommand(name, args[1:]) + return ParseUserModeCommand(name, args[1:]) } } @@ -623,7 +577,7 @@ type WhoisCommand struct { } // WHOIS [ ] *( "," ) -func NewWhoisCommand(args []string) (Command, error) { +func ParseWhoisCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } @@ -644,10 +598,6 @@ func NewWhoisCommand(args []string) (Command, error) { }, nil } -func (msg *WhoisCommand) String() string { - return fmt.Sprintf("WHOIS(target=%s, masks=%s)", msg.target, msg.masks) -} - type WhoCommand struct { BaseCommand mask Name @@ -655,7 +605,7 @@ type WhoCommand struct { } // WHO [ [ "o" ] ] -func NewWhoCommand(args []string) (Command, error) { +func ParseWhoCommand(args []string) (Command, error) { cmd := &WhoCommand{} if len(args) > 0 { @@ -669,25 +619,17 @@ func NewWhoCommand(args []string) (Command, error) { return cmd, nil } -func (msg *WhoCommand) String() string { - return fmt.Sprintf("WHO(mask=%s, operatorOnly=%t)", msg.mask, msg.operatorOnly) -} - type OperCommand struct { PassCommand name Name } -func (msg *OperCommand) String() string { - return fmt.Sprintf("OPER(name=%s, password=%s)", msg.name, msg.password) -} - func (msg *OperCommand) LoadPassword(server *Server) { msg.hash = server.operators[msg.name] } // OPER -func NewOperCommand(args []string) (Command, error) { +func ParseOperCommand(args []string) (Command, error) { if len(args) < 2 { return nil, NotEnoughArgsError } @@ -705,12 +647,7 @@ type CapCommand struct { capabilities CapabilitySet } -func (msg *CapCommand) String() string { - return fmt.Sprintf("CAP(subCommand=%s, capabilities=%s)", - msg.subCommand, msg.capabilities) -} - -func NewCapCommand(args []string) (Command, error) { +func ParseCapCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } @@ -740,11 +677,15 @@ type ProxyCommand struct { hostname Name // looked up in socket thread } -func (msg *ProxyCommand) String() string { - return fmt.Sprintf("PROXY(sourceIP=%s, sourcePort=%s)", msg.sourceIP, msg.sourcePort) +func NewProxyCommand(hostname Name) *ProxyCommand { + cmd := &ProxyCommand{ + hostname: hostname, + } + cmd.code = PROXY + return cmd } -func NewProxyCommand(args []string) (Command, error) { +func ParseProxyCommand(args []string) (Command, error) { if len(args) < 5 { return nil, NotEnoughArgsError } @@ -764,11 +705,7 @@ type AwayCommand struct { away bool } -func (msg *AwayCommand) String() string { - return fmt.Sprintf("AWAY(%s)", msg.text) -} - -func NewAwayCommand(args []string) (Command, error) { +func ParseAwayCommand(args []string) (Command, error) { cmd := &AwayCommand{} if len(args) > 0 { @@ -784,11 +721,7 @@ type IsOnCommand struct { nicks []Name } -func (msg *IsOnCommand) String() string { - return fmt.Sprintf("ISON(nicks=%s)", msg.nicks) -} - -func NewIsOnCommand(args []string) (Command, error) { +func ParseIsOnCommand(args []string) (Command, error) { if len(args) == 0 { return nil, NotEnoughArgsError } @@ -803,7 +736,7 @@ type MOTDCommand struct { target Name } -func NewMOTDCommand(args []string) (Command, error) { +func ParseMOTDCommand(args []string) (Command, error) { cmd := &MOTDCommand{} if len(args) > 0 { cmd.target = NewName(args[0]) @@ -817,11 +750,7 @@ type NoticeCommand struct { message Text } -func (cmd *NoticeCommand) String() string { - return fmt.Sprintf("NOTICE(target=%s, message=%s)", cmd.target, cmd.message) -} - -func NewNoticeCommand(args []string) (Command, error) { +func ParseNoticeCommand(args []string) (Command, error) { if len(args) < 2 { return nil, NotEnoughArgsError } @@ -844,7 +773,7 @@ func (msg *KickCommand) Comment() Text { return msg.comment } -func NewKickCommand(args []string) (Command, error) { +func ParseKickCommand(args []string) (Command, error) { if len(args) < 2 { return nil, NotEnoughArgsError } @@ -875,7 +804,7 @@ type ListCommand struct { target Name } -func NewListCommand(args []string) (Command, error) { +func ParseListCommand(args []string) (Command, error) { cmd := &ListCommand{} if len(args) > 0 { cmd.channels = NewNames(strings.Split(args[0], ",")) @@ -892,7 +821,7 @@ type NamesCommand struct { target Name } -func NewNamesCommand(args []string) (Command, error) { +func ParseNamesCommand(args []string) (Command, error) { cmd := &NamesCommand{} if len(args) > 0 { cmd.channels = NewNames(strings.Split(args[0], ",")) @@ -908,7 +837,7 @@ type DebugCommand struct { subCommand Name } -func NewDebugCommand(args []string) (Command, error) { +func ParseDebugCommand(args []string) (Command, error) { if len(args) == 0 { return nil, NotEnoughArgsError } @@ -923,7 +852,7 @@ type VersionCommand struct { target Name } -func NewVersionCommand(args []string) (Command, error) { +func ParseVersionCommand(args []string) (Command, error) { cmd := &VersionCommand{} if len(args) > 0 { cmd.target = NewName(args[0]) @@ -937,7 +866,7 @@ type InviteCommand struct { channel Name } -func NewInviteCommand(args []string) (Command, error) { +func ParseInviteCommand(args []string) (Command, error) { if len(args) < 2 { return nil, NotEnoughArgsError } @@ -948,7 +877,7 @@ func NewInviteCommand(args []string) (Command, error) { }, nil } -func NewTheaterCommand(args []string) (Command, error) { +func ParseTheaterCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } else if upperSubCmd := strings.ToUpper(args[0]); upperSubCmd == "IDENTIFY" && len(args) == 3 { @@ -978,7 +907,7 @@ type TimeCommand struct { target Name } -func NewTimeCommand(args []string) (Command, error) { +func ParseTimeCommand(args []string) (Command, error) { cmd := &TimeCommand{} if len(args) > 0 { cmd.target = NewName(args[0]) @@ -992,7 +921,7 @@ type KillCommand struct { comment Text } -func NewKillCommand(args []string) (Command, error) { +func ParseKillCommand(args []string) (Command, error) { if len(args) < 2 { return nil, NotEnoughArgsError } @@ -1009,7 +938,7 @@ type WhoWasCommand struct { target Name } -func NewWhoWasCommand(args []string) (Command, error) { +func ParseWhoWasCommand(args []string) (Command, error) { if len(args) < 1 { return nil, NotEnoughArgsError } @@ -1025,7 +954,7 @@ func NewWhoWasCommand(args []string) (Command, error) { return cmd, nil } -func NewOperNickCommand(args []string) (Command, error) { +func ParseOperNickCommand(args []string) (Command, error) { if len(args) < 2 { return nil, NotEnoughArgsError } diff --git a/irc/nickname.go b/irc/nickname.go index 00a27c17..5931f244 100644 --- a/irc/nickname.go +++ b/irc/nickname.go @@ -1,18 +1,10 @@ package irc -import ( - "fmt" -) - type NickCommand struct { BaseCommand nickname Name } -func (m *NickCommand) String() string { - return fmt.Sprintf("NICK(nickname=%s)", m.nickname) -} - func (m *NickCommand) HandleRegServer(s *Server) { client := m.Client() if !client.authorized { @@ -89,11 +81,20 @@ func (msg *OperNickCommand) HandleServer(server *Server) { return } + if msg.nick == client.nick { + return + } + target := server.clients.Get(msg.target) if target == nil { client.ErrNoSuchNick(msg.target) return } + if server.clients.Get(msg.nick) != nil { + client.ErrNickNameInUse(msg.nick) + return + } + target.ChangeNickname(msg.nick) } diff --git a/irc/server.go b/irc/server.go index 9701f4f3..af0f6c96 100644 --- a/irc/server.go +++ b/irc/server.go @@ -121,7 +121,7 @@ func (server *Server) loadChannels() { func (server *Server) processCommand(cmd Command) { client := cmd.Client() - Log.debug.Printf("%s → %s %s", client, server, cmd) + Log.debug.Printf("%s → %+v", client, cmd) if !client.registered { regCmd, ok := cmd.(RegServerCommand) diff --git a/irc/socket.go b/irc/socket.go index f3eaa16e..06a2be5a 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -38,9 +38,7 @@ func (socket *Socket) Close() { } func (socket *Socket) readLines(commands chan<- Command) { - commands <- &ProxyCommand{ - hostname: AddrLookupHostname(socket.conn.RemoteAddr()), - } + commands <- NewProxyCommand(AddrLookupHostname(socket.conn.RemoteAddr())) scanner := bufio.NewScanner(socket.conn) for scanner.Scan() { @@ -62,9 +60,7 @@ func (socket *Socket) readLines(commands chan<- Command) { Log.debug.Printf("%s error: %s", socket, err) } - commands <- &QuitCommand{ - message: "connection closed", - } + commands <- NewQuitCommand("connection closed") close(commands) } diff --git a/irc/theater.go b/irc/theater.go index 4fa6a2f4..e2eacc1a 100644 --- a/irc/theater.go +++ b/irc/theater.go @@ -1,9 +1,5 @@ package irc -import ( - "fmt" -) - type TheaterClient Name func (c TheaterClient) Id() Name { @@ -29,10 +25,6 @@ func (m *TheaterIdentifyCommand) LoadPassword(s *Server) { m.hash = s.theaters[m.channel] } -func (cmd *TheaterIdentifyCommand) String() string { - return fmt.Sprintf("THEATER_IDENTIFY(channel=%s)", cmd.channel) -} - func (m *TheaterIdentifyCommand) HandleServer(s *Server) { client := m.Client() if !m.channel.IsChannel() { @@ -66,10 +58,6 @@ type TheaterPrivMsgCommand struct { message Text } -func (cmd *TheaterPrivMsgCommand) String() string { - return fmt.Sprintf("THEATER_PRIVMSG(channel=%s, asNick=%s, message=%s)", cmd.channel, cmd.asNick, cmd.message) - -} func (m *TheaterPrivMsgCommand) HandleServer(s *Server) { client := m.Client() @@ -102,10 +90,6 @@ type TheaterActionCommand struct { action CTCPText } -func (cmd *TheaterActionCommand) String() string { - return fmt.Sprintf("THEATER_ACTION(channel=%s, asNick=%s, action=%s)", cmd.channel, cmd.asNick, cmd.action) -} - func (m *TheaterActionCommand) HandleServer(s *Server) { client := m.Client()