diff --git a/server.go b/server.go index 30d6083..5ffe018 100644 --- a/server.go +++ b/server.go @@ -37,6 +37,7 @@ type Server struct { motd string clients map[string]Client + clModes map[string]string chUsers map[string]map[string]string chTopics map[string]string @@ -63,6 +64,7 @@ func NewServer(configPath, software, version string) *Server { sv.delq = make(chan Client, 128) sv.clients = make(map[string]Client) + sv.clModes = make(map[string]string) sv.chUsers = make(map[string]map[string]string) sv.chTopics = make(map[string]string) @@ -295,42 +297,65 @@ func (sv *Server) sendLogon(nick string) { sv.sendReply(nick, RPL_ENDOFMOTD, "", "End of MOTD command") } +// Send channel name list to client func (sv *Server) channelNames(nick, ch string) { chid := strings.ToLower(ch) if _, exists := sv.chUsers[chid]; !exists { return } names := "" - for clid, mode := range sv.chUsers[chid] { + for clid, _ := range sv.chUsers[chid] { name := sv.clients[clid].Name() if names != "" { names += " " } - names = names + mode + name + modeFlags := "ohv" + modeChars := "@%+" + for i, mf := range modeFlags { + if _, exists := sv.chModes[chid][string(mf)+" "+clid]; exists { + name = string(modeChars[i]) + name + break + } + } + names = names + name + } + chunks := len(strings.Split(names, " "))/8 + 1 + nameList := strings.SplitN(names, " ", chunks) + for _, nameSlice := range nameList { + sv.sendReply(nick, RPL_NAMEREPLY, "= "+ch, nameSlice) } - sv.sendReply(nick, RPL_NAMEREPLY, "= "+ch, names) sv.sendReply(nick, RPL_ENDOFNAMES, ch, "End of /NAMES list") } +// Check if mode exists for the specified channel +func (sv *Server) channelCheckMode(chid, mode string) bool { + if _, exists := sv.chModes[chid]; !exists { + return false + } + if _, exists := sv.chModes[chid][mode]; !exists { + return false + } + return true +} + type commandHook struct { HookFn func(sv *Server, msg *irc.Message) MinArgs int NeedTrail bool - NeedOper bool - NeedAuth bool + NeedMode string } var svCommandHooks = map[string]commandHook{ - "PRIVMSG": {handleCmdPrivmsg, 1, true, false, false}, - "JOIN": {handleCmdJoin, 1, false, false, false}, - "PART": {handleCmdPart, 1, false, false, false}, - "QUIT": {handleCmdQuit, 0, false, false, false}, - "MODE": {handleCmdMode, 1, false, false, false}, - "TOPIC": {handleCmdTopic, 1, false, false, false}, - "NAMES": {handleCmdNames, 1, false, false, false}, - "WHOIS": {handleCmdWhois, 0, false, false, false}, - "PING": {handleCmdPing, 1, false, false, false}, - "REHASH": {handleCmdRehash, 0, false, false, false}, + "PRIVMSG": {handleCmdPrivmsg, 1, true, ""}, + "JOIN": {handleCmdJoin, 1, false, ""}, + "PART": {handleCmdPart, 1, false, ""}, + "QUIT": {handleCmdQuit, 0, false, ""}, + "MODE": {handleCmdMode, 1, false, ""}, + "TOPIC": {handleCmdTopic, 1, false, ""}, + "NAMES": {handleCmdNames, 1, false, ""}, + "WHOIS": {handleCmdWhois, 0, false, ""}, + "PING": {handleCmdPing, 1, false, ""}, + "REHASH": {handleCmdRehash, 0, false, "ao"}, /* "LIST": {handleCmdList, 0, false, false}, "VERSION": {handleCmdVersion, 0, false, false}, @@ -354,16 +379,26 @@ var svCommandHooks = map[string]commandHook{ } 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.channelCheckMode(chid, "v "+clid) { + 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 @@ -372,8 +407,16 @@ func handleCmdJoin(sv *Server, msg *irc.Message) { sv.sendMsg(msg) sv.sendReply(msg.Pre, RPL_TOPIC, msg.Args[0], sv.chTopics[msg.Args[0]]) sv.channelNames(msg.Pre, msg.Args[0]) + if !first { + return + } + 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]) @@ -385,7 +428,14 @@ func handleCmdPart(sv *Server, msg *irc.Message) { } 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) { @@ -395,29 +445,70 @@ 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 } - modeFlag := strings.ToLower(msg.Args[1]) + modeFlag := msg.Args[1] if len(msg.Args) < 3 { //modeTar := "" } else { //modeTar := strings.ToLower(msg.Args[2]) } switch modeFlag { + // maybe this can be done more elegant case "+o": + if !sv.channelCheckMode(chid, "o "+clid) { + goto noPerm + } case "-o": + if !sv.channelCheckMode(chid, "o "+clid) { + goto noPerm + } case "+h": + if !sv.channelCheckMode(chid, "o "+clid) { + goto noPerm + } case "-h": + if !sv.channelCheckMode(chid, "o "+clid) { + goto noPerm + } case "+b": + if !sv.channelCheckMode(chid, "o "+clid) && + !sv.channelCheckMode(chid, "h "+clid) { + goto noPerm + } case "-b": + if !sv.channelCheckMode(chid, "o "+clid) && + !sv.channelCheckMode(chid, "h "+clid) { + goto noPerm + } case "+v": + if !sv.channelCheckMode(chid, "o "+clid) && + !sv.channelCheckMode(chid, "h "+clid) { + goto noPerm + } case "-v": + if !sv.channelCheckMode(chid, "o "+clid) && + !sv.channelCheckMode(chid, "h "+clid) { + goto noPerm + } + case "+m": + if !sv.channelCheckMode(chid, "o "+clid) { + goto noPerm + } + case "-m": + if !sv.channelCheckMode(chid, "o "+clid) { + goto noPerm + } } sv.sendMsg(msg) + return + noPerm: + sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") } }