diff --git a/irc/client.go b/irc/client.go index 78b5452c..29e1cddb 100644 --- a/irc/client.go +++ b/irc/client.go @@ -88,15 +88,13 @@ func (client *Client) HasUsername() bool { return client.username != "" } -func (fromClient *Client) InterestedClients() ClientSet { +func (client *Client) InterestedClients() ClientSet { clients := make(ClientSet) - clients[fromClient] = true - for channel := range fromClient.channels { - for client := range channel.members { - clients[client] = true + for channel := range client.channels { + for member := range channel.members { + clients[member] = true } } - return clients } diff --git a/irc/commands.go b/irc/commands.go index cc85065a..5366fbfd 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -35,6 +35,7 @@ var ( "QUIT": NewQuitCommand, "TOPIC": NewTopicCommand, "USER": NewUserMsgCommand, + "WHOIS": NewWhoisCommand, } ) @@ -451,3 +452,30 @@ func NewModeCommand(args []string) (EditableCommand, error) { return cmd, nil } + +type WhoisCommand struct { + BaseCommand + target string + masks []string +} + +func NewWhoisCommand(args []string) (EditableCommand, error) { + if len(args) < 1 { + return nil, NotEnoughArgsError + } + + var masks string + var target string + + if len(args) > 1 { + target = args[0] + masks = args[1] + } else { + masks = args[0] + } + + return &WhoisCommand{ + target: target, + masks: strings.Split(masks, ","), + }, nil +} diff --git a/irc/net.go b/irc/net.go index 5f30538f..23f780a7 100644 --- a/irc/net.go +++ b/irc/net.go @@ -25,11 +25,11 @@ func StringReadChan(conn net.Conn) <-chan string { for { line, err := readTrimmedLine(reader) if err != nil { - log.Print("net: ", err) + log.Printf("%s → %s error: %s", conn.RemoteAddr(), conn.LocalAddr(), err) break } if DEBUG_NET { - log.Printf("%s → %s : %s", conn.RemoteAddr(), conn.LocalAddr(), line) + log.Printf("%s → %s %s", conn.RemoteAddr(), conn.LocalAddr(), line) } ch <- line } @@ -45,10 +45,10 @@ func StringWriteChan(conn net.Conn) chan<- string { defer close(ch) for str := range ch { if DEBUG_NET { - log.Printf("%s ← %s : %s", conn.RemoteAddr(), conn.LocalAddr(), str) + log.Printf("%s ← %s %s", conn.RemoteAddr(), conn.LocalAddr(), str) } if _, err := writer.WriteString(str + "\r\n"); err != nil { - log.Print("net: ", err) + log.Printf("%s ← %s error: %s", conn.RemoteAddr(), conn.LocalAddr(), err) break } writer.Flush() diff --git a/irc/reply.go b/irc/reply.go index 6a7a8d74..90fa4112 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -6,6 +6,18 @@ import ( "time" ) +const ( + MAX_REPLY_LEN = 510 // 512 - CRLF +) + +func joinedLen(names []string) int { + var l = len(names) - 1 // " " between names + for _, name := range names { + l += len(name) + } + return l +} + type Identifier interface { Id() string Nick() string @@ -96,18 +108,6 @@ func NewNamesReply(channel *Channel) Reply { } } -const ( - MAX_REPLY_LEN = 510 // 512 - CRLF -) - -func joinedLen(names []string) int { - var l = len(names) - 1 // " " between names - for _, name := range names { - l += len(name) - } - return l -} - func (reply *NamesReply) Format(client *Client, write chan<- string) { base := RplNamReply(reply.channel, []string{}) baseLen := len(base.FormatString(client)) @@ -225,6 +225,15 @@ func RplYoureOper(server *Server) Reply { ":You are now an IRC operator") } +func RplWhoisUser(server *Server, client *Client) Reply { + return NewNumericReply(server, RPL_WHOISUSER, "%s %s %s * :%s", + client.nick, client.username, client.hostname, client.realname) +} + +func RplEndOfWhois(server *Server) Reply { + return NewNumericReply(server, RPL_ENDOFWHOIS, ":End of WHOIS list") +} + // errors (also numeric) func ErrAlreadyRegistered(source Identifier) Reply { @@ -298,3 +307,7 @@ func ErrNoPrivileges(server *Server) Reply { func ErrRestricted(server *Server) Reply { return NewNumericReply(server, ERR_RESTRICTED, ":Your connection is restricted!") } + +func ErrNoSuchServer(server *Server, target string) Reply { + return NewNumericReply(server, ERR_NOSUCHSERVER, "%s :No such server", target) +} diff --git a/irc/server.go b/irc/server.go index e807f3b4..f0fd6067 100644 --- a/irc/server.go +++ b/irc/server.go @@ -165,6 +165,7 @@ func (m *NickCommand) HandleServer(s *Server) { } reply := RplNick(c, m.nickname) + c.replies <- reply for iclient := range c.InterestedClients() { iclient.replies <- reply } @@ -201,7 +202,6 @@ func (m *QuitCommand) HandleServer(s *Server) { reply := RplQuit(c, m.message) for client := range c.InterestedClients() { client.replies <- reply - } } @@ -280,3 +280,22 @@ func (m *ModeCommand) HandleServer(s *Server) { client.replies <- ErrUsersDontMatch(client) } + +func (m *WhoisCommand) HandleServer(server *Server) { + client := m.Client() + + // TODO implement target query + if m.target != "" { + client.replies <- ErrNoSuchServer(server, m.target) + return + } + + for _, mask := range m.masks { + // TODO implement wildcard matching + mclient := server.clients[mask] + if mclient != nil { + client.replies <- RplWhoisUser(server, mclient) + } + } + client.replies <- RplEndOfWhois(server) +}