// vim:ts=4:sts=4:sw=4:noet:tw=72 package ircd import ( "fmt" "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, ""}, "WHO": {handleCmdWho, 1, false, ""}, "WHOIS": {handleCmdWhois, 0, false, ""}, "PING": {handleCmdPing, 1, false, ""}, "PONG": {handleCmdPong, 0, 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 !strings.HasPrefix(chid, "#") { sv.sendReply(msg.Pre, ERR_NOSUCHCHANNEL, msg.Args[0], "No such channel") return } 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) // dont proceed further if message was sent by remote origin if !sv.localOrigin(msg) { return } sv.cluster.Subscribe(chid, sv.remoteq) sv.sendReply(msg.Pre, RPL_TOPIC, msg.Args[0], sv.chTopics[msg.Args[0]]) sv.channelNames(msg.Pre, msg.Args[0]) m, isoper := sv.opers[clid] if isoper { mode := "o " + clid sv.chModes[chid][mode] = true sv.sendMsg(irc.M(sv.host, "MODE", chid+" +o "+clid, "")) if m == "a" { sv.sendMsg(irc.M(sv.host, "PRIVMSG", chid, clid+" is a server administrator")) } if m == "o" { 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) sv.channelRemoveUser(chid, clid) } 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) sv.channelRemoveUser(chid, 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 handleCmdWho(sv *Server, msg *irc.Message) { clid := strings.ToLower(msg.Args[0]) if _, exists := sv.clients[clid]; exists { // :irc.dnix.de 352 Anydin #northbrigade webuser369 -anon/guest irc.dnix.de webuser36968 H :0 webuser36968 sv.sendReply(msg.Pre, RPL_WHOREPLY, fmt.Sprintf("* %s %s %s %s %s%s", clid, sv.clHosts[clid], sv.host, clid, "H", ""), "0 "+msg.Args[0]) } sv.sendReply(msg.Pre, RPL_ENDOFWHO, clid, "End of /WHO list.") } func handleCmdWhois(sv *Server, msg *irc.Message) { clid := strings.ToLower(msg.Args[0]) if _, exists := sv.clients[clid]; !exists { sv.sendReply(msg.Pre, ERR_NOSUCHNICK, clid, "No such nick") return } sv.sendReply(msg.Pre, RPL_WHOISUSER, fmt.Sprintf("%s %s %s *", clid, clid, sv.clHosts[clid]), msg.Args[0]) m, isoper := sv.opers[clid] if isoper { if m == "a" { sv.sendReply(msg.Pre, RPL_WHOISOPERATOR, clid, "is a server administrator") } if m == "o" { sv.sendReply(msg.Pre, RPL_WHOISOPERATOR, clid, "is a server operator") } } sv.sendReply(msg.Pre, RPL_ENDOFWHOIS, "", "End of /WHOIS list") } func handleCmdPing(sv *Server, msg *irc.Message) { sv.sendReply(msg.Pre, "PONG", msg.Args[0], "") } func handleCmdPong(sv *Server, msg *irc.Message) { } 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) { }