diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..dfaeb52 --- /dev/null +++ b/handlers.go @@ -0,0 +1,246 @@ +// vim:ts=4:sts=4:sw=4:noet:tw=72 + +package ircd + +import ( + "strings" + + "code.dnix.de/an/irc" + "code.dnix.de/an/xlog" +) + +type commandHook struct { + HookFn func(sv *Server, msg *irc.Message) + MinArgs int + NeedTrail bool + NeedMode string +} + +var svCommandHooks = map[string]commandHook{ + "PRIVMSG": {handleCmdPrivmsg, 1, true, ""}, + "JOIN": {handleCmdJoin, 1, false, ""}, + "PART": {handleCmdPart, 1, false, ""}, + "QUIT": {handleCmdQuit, 0, false, ""}, + "MODE": {handleCmdMode, 1, false, ""}, + "TOPIC": {handleCmdTopic, 1, false, ""}, + "KICK": {handleCmdKick, 2, false, ""}, + "NAMES": {handleCmdNames, 1, false, ""}, + "WHOIS": {handleCmdWhois, 0, false, ""}, + "PING": {handleCmdPing, 1, false, ""}, + "REHASH": {handleCmdRehash, 0, false, "ao"}, + "KILL": {handleCmdKill, 0, false, "ao"}, + /* + "LIST": {handleCmdList, 0, false, false}, + "VERSION": {handleCmdVersion, 0, false, false}, + "STATS": {handleCmdStats, 0, false, false}, + "TIME": {handleCmdTime, 0, false, false}, + "OPER": {handleCmdOper, 1, false, false}, + "ADMIN": {handleCmdAdmin, 0, false, false}, + "INFO": {handleCmdInfo, 0, false, false}, + "WHO": {handleCmdWho, 0, false, false}, + "WHOWAS": {handleCmdWhowas, 0, false, false}, + "PONG": {handleCmdPong, 0, false, false}, + "ERROR": {handleCmdError, 0, false, false}, + "AWAY": {handleCmdAway, 0, false, false}, + "RESTART": {handleCmdRestart, 0, false, false}, + "SUMMON": {handleCmdSummon, 0, false, false}, + "USERS": {handleCmdUsers, 0, false, false}, + "USERHOST": {handleCmdUserhost, 0, false, false}, + "ISON": {handleCmdIson, 0, false, false}, + */ +} + +func handleCmdPrivmsg(sv *Server, msg *irc.Message) { + if strings.HasPrefix(msg.Args[0], "#") { + clid := strings.ToLower(msg.Pre) + chid := strings.ToLower(msg.Args[0]) + if sv.channelCheckMode(chid, "m") && + !sv.channelCheckPerm(chid, clid, "ohv") { + sv.sendReply(clid, ERR_CANNOTSENDTOCHAN, chid, "Cannot send to channel") + return + } + if _, exists := sv.chUsers[chid][clid]; !exists { + sv.sendReply(clid, ERR_CANNOTSENDTOCHAN, chid, "Cannot send to channel") + return + } + } + sv.sendMsg(msg) +} + +func handleCmdJoin(sv *Server, msg *irc.Message) { + first := false + clid := strings.ToLower(msg.Pre) + chid := strings.ToLower(msg.Args[0]) + if _, exists := sv.chUsers[chid]; !exists { + sv.chUsers[chid] = make(map[string]string) + sv.chTopics[chid] = "" + sv.chModes[chid] = make(map[string]bool) + first = true + } + if _, exists := sv.chUsers[chid][clid]; exists { + return + } + sv.chUsers[chid][clid] = "" + sv.sendMsg(msg) + sv.sendReply(msg.Pre, RPL_TOPIC, msg.Args[0], sv.chTopics[msg.Args[0]]) + sv.channelNames(msg.Pre, msg.Args[0]) + _, isop := sv.opers[clid] + if first || isop { + mode := "o " + clid + sv.chModes[chid][mode] = true + sv.sendMsg(irc.M(sv.host, "MODE", chid+" +o "+clid, "")) + } + if isop { + sv.sendMsg(irc.M(sv.host, "PRIVMSG", chid, clid+" is a server operator")) + } +} + +// Handle user parting the channel and delete channel if last user has +// left +func handleCmdPart(sv *Server, msg *irc.Message) { + clid := strings.ToLower(msg.Pre) + chid := strings.ToLower(msg.Args[0]) + if _, exists := sv.chUsers[chid]; !exists { + return + } + if _, exists := sv.chUsers[chid][clid]; !exists { + return + } + sv.sendMsg(msg) + delete(sv.chUsers[chid], clid) + delete(sv.chModes[chid], "o "+clid) + delete(sv.chModes[chid], "h "+clid) + delete(sv.chModes[chid], "v "+clid) + if len(sv.chUsers[chid]) == 0 { + delete(sv.chUsers, chid) + delete(sv.chTopics, chid) + delete(sv.chModes, chid) + } +} + +func handleCmdQuit(sv *Server, msg *irc.Message) { + +} + +func handleCmdMode(sv *Server, msg *irc.Message) { + if strings.HasPrefix(msg.Args[0], "#") { + chid := strings.ToLower(msg.Args[0]) + clid := strings.ToLower(msg.Pre) + if _, exists := sv.chUsers[chid]; !exists { + return + } + if len(msg.Args) < 2 { + return + } + mode := "" + modeSwitch := msg.Args[1] + modeTarget := "" + if len(modeSwitch) < 2 { + return + } + if len(msg.Args) > 2 { + modeTarget = strings.ToLower(msg.Args[2]) + } + switch modeSwitch[1] { + // maybe this can be done more elegant + case 'o': + if !sv.channelCheckPerm(chid, clid, "o") { + goto noPerm + } + case 'h': + if !sv.channelCheckPerm(chid, clid, "o") { + goto noPerm + } + case 'b': + if !sv.channelCheckPerm(chid, clid, "oh") { + goto noPerm + } + case 'v': + if !sv.channelCheckPerm(chid, clid, "oh") { + goto noPerm + } + case 'm': + if !sv.channelCheckPerm(chid, clid, "oh") { + goto noPerm + } + case 't': + if !sv.channelCheckPerm(chid, clid, "oh") { + goto noPerm + } + default: + return + } + mode = string(modeSwitch[1]) + if modeTarget != "" { + mode = mode + " " + modeTarget + } + if modeSwitch[0] == '+' { + sv.chModes[chid][mode] = true + } else { + delete(sv.chModes[chid], mode) + } + sv.sendMsg(msg) + return + noPerm: + sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") + } +} + +func handleCmdTopic(sv *Server, msg *irc.Message) { + chid := strings.ToLower(msg.Args[0]) + clid := strings.ToLower(msg.Pre) + if _, exists := sv.chUsers[chid]; !exists { + sv.sendReply(msg.Pre, ERR_NOSUCHCHANNEL, chid, "No such channel") + } + if msg.Trail == "" { + sv.sendReply(msg.Pre, RPL_TOPIC, chid, sv.chTopics[chid]) + } else { + if sv.channelCheckMode(chid, "t") && + !sv.channelCheckPerm(chid, clid, "oh") { + sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") + return + } + sv.chTopics[chid] = msg.Trail + sv.sendMsg(msg) + //sv.sendReply(msg.Pre, RPL_TOPIC, ch, msg.Trail) + } +} + +func handleCmdKick(sv *Server, msg *irc.Message) { + chid := strings.ToLower(msg.Args[0]) + clid := strings.ToLower(msg.Pre) + target := strings.ToLower(msg.Args[1]) + if !sv.channelCheckPerm(chid, clid, "oh") { + sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") + } + sv.sendMsg(msg) + delete(sv.chUsers[chid], target) + delete(sv.chModes[chid], "o "+target) + delete(sv.chModes[chid], "h "+target) + delete(sv.chModes[chid], "v "+target) +} + +func handleCmdNames(sv *Server, msg *irc.Message) { + chid := strings.ToLower(msg.Args[0]) + if _, exists := sv.chUsers[chid]; !exists { + sv.sendReply(msg.Pre, ERR_NOSUCHCHANNEL, chid, "No such channel") + } + sv.channelNames(msg.Pre, chid) +} + +func handleCmdWhois(sv *Server, msg *irc.Message) { + sv.sendReply(msg.Pre, RPL_WHOISUSER, "nick user host *", "real name") +} + +func handleCmdPing(sv *Server, msg *irc.Message) { + sv.sendReply(msg.Pre, "PONG", msg.Args[0], "") +} + +func handleCmdRehash(sv *Server, msg *irc.Message) { + sv.loadConfig() + sv.sendReply(msg.Pre, RPL_REHASHING, "", "Rehashing.") + xlog.Info("Rehashing") +} + +func handleCmdKill(sv *Server, msg *irc.Message) { +} diff --git a/server.go b/server.go index fe0467d..9af5fc1 100644 --- a/server.go +++ b/server.go @@ -355,239 +355,3 @@ func (sv *Server) channelCheckPerm(chid, clid, modes string) bool { } return false } - -type commandHook struct { - HookFn func(sv *Server, msg *irc.Message) - MinArgs int - NeedTrail bool - NeedMode string -} - -var svCommandHooks = map[string]commandHook{ - "PRIVMSG": {handleCmdPrivmsg, 1, true, ""}, - "JOIN": {handleCmdJoin, 1, false, ""}, - "PART": {handleCmdPart, 1, false, ""}, - "QUIT": {handleCmdQuit, 0, false, ""}, - "MODE": {handleCmdMode, 1, false, ""}, - "TOPIC": {handleCmdTopic, 1, false, ""}, - "KICK": {handleCmdKick, 2, false, ""}, - "NAMES": {handleCmdNames, 1, false, ""}, - "WHOIS": {handleCmdWhois, 0, false, ""}, - "PING": {handleCmdPing, 1, false, ""}, - "REHASH": {handleCmdRehash, 0, false, "ao"}, - "KILL": {handleCmdKill, 0, false, "ao"}, - /* - "LIST": {handleCmdList, 0, false, false}, - "VERSION": {handleCmdVersion, 0, false, false}, - "STATS": {handleCmdStats, 0, false, false}, - "TIME": {handleCmdTime, 0, false, false}, - "OPER": {handleCmdOper, 1, false, false}, - "ADMIN": {handleCmdAdmin, 0, false, false}, - "INFO": {handleCmdInfo, 0, false, false}, - "WHO": {handleCmdWho, 0, false, false}, - "WHOWAS": {handleCmdWhowas, 0, false, false}, - "PONG": {handleCmdPong, 0, false, false}, - "ERROR": {handleCmdError, 0, false, false}, - "AWAY": {handleCmdAway, 0, false, false}, - "RESTART": {handleCmdRestart, 0, false, false}, - "SUMMON": {handleCmdSummon, 0, false, false}, - "USERS": {handleCmdUsers, 0, false, false}, - "USERHOST": {handleCmdUserhost, 0, false, false}, - "ISON": {handleCmdIson, 0, false, false}, - */ -} - -func handleCmdPrivmsg(sv *Server, msg *irc.Message) { - if strings.HasPrefix(msg.Args[0], "#") { - clid := strings.ToLower(msg.Pre) - chid := strings.ToLower(msg.Args[0]) - if sv.channelCheckMode(chid, "m") && - !sv.channelCheckPerm(chid, clid, "ohv") { - sv.sendReply(clid, ERR_CANNOTSENDTOCHAN, chid, "Cannot send to channel") - return - } - if _, exists := sv.chUsers[chid][clid]; !exists { - sv.sendReply(clid, ERR_CANNOTSENDTOCHAN, chid, "Cannot send to channel") - return - } - } - sv.sendMsg(msg) -} - -func handleCmdJoin(sv *Server, msg *irc.Message) { - first := false - clid := strings.ToLower(msg.Pre) - chid := strings.ToLower(msg.Args[0]) - if _, exists := sv.chUsers[chid]; !exists { - sv.chUsers[chid] = make(map[string]string) - sv.chTopics[chid] = "" - sv.chModes[chid] = make(map[string]bool) - first = true - } - if _, exists := sv.chUsers[chid][clid]; exists { - return - } - sv.chUsers[chid][clid] = "" - sv.sendMsg(msg) - sv.sendReply(msg.Pre, RPL_TOPIC, msg.Args[0], sv.chTopics[msg.Args[0]]) - sv.channelNames(msg.Pre, msg.Args[0]) - _, isop := sv.opers[clid] - if first || isop { - mode := "o " + clid - sv.chModes[chid][mode] = true - sv.sendMsg(irc.M(sv.host, "MODE", chid+" +o "+clid, "")) - } - if isop { - sv.sendMsg(irc.M(sv.host, "PRIVMSG", chid, clid+" is a server operator")) - } -} - -// Handle user parting the channel and delete channel if last user has -// left -func handleCmdPart(sv *Server, msg *irc.Message) { - clid := strings.ToLower(msg.Pre) - chid := strings.ToLower(msg.Args[0]) - if _, exists := sv.chUsers[chid]; !exists { - return - } - if _, exists := sv.chUsers[chid][clid]; !exists { - return - } - sv.sendMsg(msg) - delete(sv.chUsers[chid], clid) - delete(sv.chModes[chid], "o "+clid) - delete(sv.chModes[chid], "h "+clid) - delete(sv.chModes[chid], "v "+clid) - if len(sv.chUsers[chid]) == 0 { - delete(sv.chUsers, chid) - delete(sv.chTopics, chid) - delete(sv.chModes, chid) - } -} - -func handleCmdQuit(sv *Server, msg *irc.Message) { - -} - -func handleCmdMode(sv *Server, msg *irc.Message) { - if strings.HasPrefix(msg.Args[0], "#") { - chid := strings.ToLower(msg.Args[0]) - clid := strings.ToLower(msg.Pre) - if _, exists := sv.chUsers[chid]; !exists { - return - } - if len(msg.Args) < 2 { - return - } - mode := "" - modeSwitch := msg.Args[1] - modeTarget := "" - if len(modeSwitch) < 2 { - return - } - if len(msg.Args) > 2 { - modeTarget = strings.ToLower(msg.Args[2]) - } - switch modeSwitch[1] { - // maybe this can be done more elegant - case 'o': - if !sv.channelCheckPerm(chid, clid, "o") { - goto noPerm - } - case 'h': - if !sv.channelCheckPerm(chid, clid, "o") { - goto noPerm - } - case 'b': - if !sv.channelCheckPerm(chid, clid, "oh") { - goto noPerm - } - case 'v': - if !sv.channelCheckPerm(chid, clid, "oh") { - goto noPerm - } - case 'm': - if !sv.channelCheckPerm(chid, clid, "oh") { - goto noPerm - } - case 't': - if !sv.channelCheckPerm(chid, clid, "oh") { - goto noPerm - } - default: - return - } - mode = string(modeSwitch[1]) - if modeTarget != "" { - mode = mode + " " + modeTarget - } - if modeSwitch[0] == '+' { - sv.chModes[chid][mode] = true - } else { - delete(sv.chModes[chid], mode) - } - sv.sendMsg(msg) - return - noPerm: - sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") - } -} - -func handleCmdTopic(sv *Server, msg *irc.Message) { - chid := strings.ToLower(msg.Args[0]) - clid := strings.ToLower(msg.Pre) - if _, exists := sv.chUsers[chid]; !exists { - sv.sendReply(msg.Pre, ERR_NOSUCHCHANNEL, chid, "No such channel") - } - if msg.Trail == "" { - sv.sendReply(msg.Pre, RPL_TOPIC, chid, sv.chTopics[chid]) - } else { - if sv.channelCheckMode(chid, "t") && - !sv.channelCheckPerm(chid, clid, "oh") { - sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") - return - } - sv.chTopics[chid] = msg.Trail - sv.sendMsg(msg) - //sv.sendReply(msg.Pre, RPL_TOPIC, ch, msg.Trail) - } -} - -func handleCmdKick(sv *Server, msg *irc.Message) { - chid := strings.ToLower(msg.Args[0]) - clid := strings.ToLower(msg.Pre) - target := strings.ToLower(msg.Args[1]) - if !sv.channelCheckPerm(chid, clid, "oh") { - sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") - } - sv.sendMsg(msg) - delete(sv.chUsers[chid], target) - delete(sv.chModes[chid], "o "+target) - delete(sv.chModes[chid], "h "+target) - delete(sv.chModes[chid], "v "+target) -} - -func handleCmdNames(sv *Server, msg *irc.Message) { - chid := strings.ToLower(msg.Args[0]) - if _, exists := sv.chUsers[chid]; !exists { - sv.sendReply(msg.Pre, ERR_NOSUCHCHANNEL, chid, "No such channel") - } - sv.channelNames(msg.Pre, chid) -} - -func handleCmdWhois(sv *Server, msg *irc.Message) { - sv.sendReply(msg.Pre, RPL_WHOISUSER, "nick user host *", "real name") -} - -func handleCmdPing(sv *Server, msg *irc.Message) { - sv.sendReply(msg.Pre, "PONG", msg.Args[0], "") -} - -func handleCmdRehash(sv *Server, msg *irc.Message) { - sv.loadConfig() - sv.sendReply(msg.Pre, RPL_REHASHING, "", "Rehashing.") - xlog.Info("Rehashing") -} - -func handleCmdKill(sv *Server, msg *irc.Message) { -}