// 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, "qaohv") { 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]) _, isadm := sv.admins[clid] if isadm { mode := "q " + clid sv.chModes[chid][mode] = true sv.sendMsg(irc.M(sv.host, "MODE", chid+" +q "+clid, "")) sv.sendMsg(irc.M(sv.host, "PRIVMSG", chid, clid+" is a server administrator")) return } _, isop := sv.opers[clid] if isop { mode := "a " + clid sv.chModes[chid][mode] = true sv.sendMsg(irc.M(sv.host, "MODE", chid+" +a "+clid, "")) sv.sendMsg(irc.M(sv.host, "PRIVMSG", chid, clid+" is a server operator")) return } if first { mode := "o " + clid sv.chModes[chid][mode] = true sv.sendMsg(irc.M(sv.host, "MODE", chid+" +o "+clid, "")) } } // 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], "q "+clid) delete(sv.chModes[chid], "a "+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, "qao") { goto noPerm } case 'h': if !sv.channelCheckPerm(chid, clid, "qao") { goto noPerm } case 'b': if !sv.channelCheckPerm(chid, clid, "qaoh") { goto noPerm } case 'v': if !sv.channelCheckPerm(chid, clid, "qaoh") { goto noPerm } case 'm': if !sv.channelCheckPerm(chid, clid, "qaoh") { goto noPerm } case 't': if !sv.channelCheckPerm(chid, clid, "qaoh") { 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, "qaoh") { 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, "qaoh") { sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") } sv.sendMsg(msg) delete(sv.chUsers[chid], target) delete(sv.chModes[chid], "q "+target) delete(sv.chModes[chid], "a "+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) { }