// Copyright (c) 2016-2017 Daniel Oaks // released under the MIT license package irc import ( "strconv" "strings" "github.com/goshuirc/irc-go/ircmsg" ) // alertMonitors alerts everyone monitoring us that we're online. func (client *Client) alertMonitors() { // get monitors client.server.monitoringMutex.RLock() monitors := client.server.monitoring[client.nickCasefolded] client.server.monitoringMutex.RUnlock() // alert monitors for _, mClient := range monitors { // don't have to notify ourselves if mClient != client { mClient.SendFromClient("", client, nil, RPL_MONONLINE, mClient.nick, client.nickMaskString) } } } // clearMonitorList clears our MONITOR list. func (client *Client) clearMonitorList() { // lockin' everything client.monitoringMutex.Lock() defer client.monitoringMutex.Unlock() client.server.monitoringMutex.Lock() defer client.server.monitoringMutex.Unlock() for name := range client.monitoring { // just removes current client from the list orig := client.server.monitoring[name] var index int for i, cli := range orig { if cli == client { index = i break } } client.server.monitoring[name] = append(orig[:index], orig[index+1:]...) } client.monitoring = make(map[string]bool) } var ( metadataSubcommands = map[string]func(server *Server, client *Client, msg ircmsg.IrcMessage) bool{ "-": monitorRemoveHandler, "+": monitorAddHandler, "c": monitorClearHandler, "l": monitorListHandler, "s": monitorStatusHandler, } ) func monitorHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { handler, exists := metadataSubcommands[strings.ToLower(msg.Params[0])] if !exists { client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "MONITOR", msg.Params[0], "Unknown subcommand") return false } return handler(server, client, msg) } func monitorRemoveHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { if len(msg.Params) < 2 { client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, "Not enough parameters") return false } targets := strings.Split(msg.Params[1], ",") for len(targets) > 0 { // check name length if len(targets[0]) < 1 { targets = targets[1:] continue } // remove target casefoldedTarget, err := CasefoldName(targets[0]) if err != nil { // skip silently I guess targets = targets[1:] continue } client.monitoringMutex.Lock() client.server.monitoringMutex.Lock() if client.monitoring[casefoldedTarget] { // just removes current client from the list orig := server.monitoring[casefoldedTarget] var index int for i, cli := range orig { if cli == client { index = i break } } server.monitoring[casefoldedTarget] = append(orig[:index], orig[index+1:]...) delete(client.monitoring, casefoldedTarget) } client.monitoringMutex.Unlock() client.server.monitoringMutex.Unlock() // remove first element of targets list targets = targets[1:] } return false } func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { if len(msg.Params) < 2 { client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, "Not enough parameters") return false } var online []string var offline []string targets := strings.Split(msg.Params[1], ",") for len(targets) > 0 { // check name length if len(targets[0]) < 1 || len(targets[0]) > server.limits.NickLen { targets = targets[1:] continue } // check the monitor list length if len(client.monitoring) >= server.limits.MonitorEntries { client.Send(nil, server.name, ERR_MONLISTFULL, client.nick, strconv.Itoa(server.limits.MonitorEntries), strings.Join(targets, ",")) break } // add target casefoldedTarget, err := CasefoldName(targets[0]) if err != nil { // skip silently I guess targets = targets[1:] continue } client.monitoringMutex.Lock() client.server.monitoringMutex.Lock() if !client.monitoring[casefoldedTarget] { client.monitoring[casefoldedTarget] = true orig := server.monitoring[casefoldedTarget] server.monitoring[casefoldedTarget] = append(orig, client) } client.monitoringMutex.Unlock() client.server.monitoringMutex.Unlock() // add to online / offline lists target := server.clients.Get(casefoldedTarget) if target == nil { offline = append(offline, targets[0]) } else { online = append(online, target.nickMaskString) } // remove first element of targets list targets = targets[1:] } if len(online) > 0 { client.Send(nil, server.name, RPL_MONONLINE, client.nick, strings.Join(online, ",")) } if len(offline) > 0 { client.Send(nil, server.name, RPL_MONOFFLINE, client.nick, strings.Join(offline, ",")) } return false } func monitorClearHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { client.clearMonitorList() return false } func monitorListHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { var monitorList []string client.monitoringMutex.RLock() for name := range client.monitoring { monitorList = append(monitorList, name) } client.monitoringMutex.RUnlock() for _, line := range argsToStrings(maxLastArgLength, monitorList, ",") { client.Send(nil, server.name, RPL_MONLIST, client.nick, line) } return false } func monitorStatusHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { var online []string var offline []string client.monitoringMutex.RLock() monitoring := client.monitoring client.monitoringMutex.RUnlock() for name := range monitoring { target := server.clients.Get(name) if target == nil { offline = append(offline, name) } else { online = append(online, target.nickMaskString) } } if len(online) > 0 { for _, line := range argsToStrings(maxLastArgLength, online, ",") { client.Send(nil, server.name, RPL_MONONLINE, client.nick, line) } } if len(offline) > 0 { for _, line := range argsToStrings(maxLastArgLength, offline, ",") { client.Send(nil, server.name, RPL_MONOFFLINE, client.nick, line) } } return false }