diff --git a/main.go b/main.go index d12b68d..8a7c77a 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ type Client struct { conn net.Conn host string channels map[string]*Channel + monitorTargets map[string]bool registered bool saslRequired bool saslComplete bool @@ -153,10 +154,11 @@ func handleConn(conn net.Conn) { reader := bufio.NewReader(conn) client := &Client{ - conn: conn, - host: host, - channels: make(map[string]*Channel), - registered: false, + conn: conn, + host: host, + channels: make(map[string]*Channel), + monitorTargets: make(map[string]bool), + registered: false, } defer client.cleanup() for { @@ -186,6 +188,10 @@ func handleConn(conn net.Conn) { } clients[newNick] = client cliMu.Unlock() + if oldNick != "" { + notifyMonitorsOffline(oldNick) + } + notifyMonitorsOnline(newNick) for _, ch := range client.channels { ch.mu.Lock() if oldNick != "" { @@ -461,6 +467,22 @@ func handleConn(conn net.Conn) { } else { fmt.Fprintf(client.conn, ":server 461 %s NAMES :Not enough parameters\r\n", client.nick) } + case "ISON": + if len(msg.Params) < 1 { + client.fwrite(":server 461 %s ISON :Not enough parameters\r\n", client.nick) + continue + } + requested := msg.Params + var online []string + + cliMu.RLock() + for _, nick := range requested { + if _, ok := clients[nick]; ok { + online = append(online, nick) + } + } + cliMu.RUnlock() + client.fwrite(":server 303 %s :%s\r\n", client.nick, strings.Join(online, " ")) // ircv3 stuff case "CHATHISTORY": if len(msg.Params) < 4 { @@ -475,6 +497,38 @@ func handleConn(conn net.Conn) { continue } client.serveHistory(subcmd, target, count, msg.Params) + case "MONITOR": + if len(msg.Params) < 1 { + client.fwrite(":server 461 %s MONITOR :Not enough parameters\r\n", client.nick) + continue + } + subcmd := strings.ToUpper(msg.Params[0]) + switch subcmd { + case "+": + for _, nick := range msg.Params[1:] { + client.monitorTargets[nick] = true + cliMu.RLock() + if _, ok := clients[nick]; ok { + client.fwrite(":server 730 %s %s :is online\r\n", client.nick, nick) + } + cliMu.RUnlock() + } + case "-": + for _, nick := range msg.Params[1:] { + delete(client.monitorTargets, nick) + } + case "C": + client.monitorTargets = make(map[string]bool) + case "L": + var nicks []string + for nick := range client.monitorTargets { + nicks = append(nicks, nick) + } + client.fwrite(":server 732 %s :%s\r\n", client.nick, strings.Join(nicks, " ")) + client.fwrite(":server 733 %s :End of MONITOR list\r\n", client.nick) + default: + client.fwrite(":server 461 %s MONITOR :Invalid subcommand\r\n", client.nick) + } default: if client.nick != "" { fmt.Fprintf(conn, ":server 421 %s %s :Unknown command or command is not implemented\r\n", client.nick, msg.Command) @@ -884,6 +938,7 @@ func (c *Client) checkRegistration() { return } c.registered = true + notifyMonitorsOnline(c.nick) c.fwrite(":server 001 %s :Welcome to Gocirc, %s!\r\n", c.nick, c.nick) c.fwrite(":server 002 %s :Your host is server\r\n", c.nick) c.fwrite(":server 003 %s :This server has %d users right now\r\n", c.nick, len(clients)) @@ -1010,6 +1065,7 @@ func (c *Client) cleanup() { cliMu.Lock() delete(clients, c.nick) cliMu.Unlock() + notifyMonitorsOffline(c.nick) for name, ch := range c.channels { ch.mu.Lock() delete(ch.members, c.nick) @@ -1139,3 +1195,24 @@ func scanRows(rows *sql.Rows, channel string) []HistoryMessage { } return msgs } + +// IRC MONITOR command stuff +func notifyMonitorsOnline(nick string) { + cliMu.RLock() + defer cliMu.RUnlock() + for _, c := range clients { + if c.monitorTargets[nick] { + c.fwrite(":server 730 %s %s :is online\r\n", c.nick, nick) + } + } +} + +func notifyMonitorsOffline(nick string) { + cliMu.RLock() + defer cliMu.RUnlock() + for _, c := range clients { + if c.monitorTargets[nick] { + c.fwrite(":server 731 %s %s :is offline\r\n", c.nick, nick) + } + } +}