diff --git a/client.go b/client.go index 99c3211..db1cdb6 100644 --- a/client.go +++ b/client.go @@ -168,6 +168,7 @@ func (cl *RemoteClient) connReader() { func (cl *RemoteClient) connWriter() { for line := range cl.writeq { written := 0 + line = strings.Replace(line, "%", "%%", 0) bytes := []byte(line + "\r\n") for written < len(line) { n, err := cl.conn.Write(bytes[written:]) diff --git a/handlers.go b/handlers.go index dfaeb52..640fff9 100644 --- a/handlers.go +++ b/handlers.go @@ -55,7 +55,7 @@ func handleCmdPrivmsg(sv *Server, msg *irc.Message) { clid := strings.ToLower(msg.Pre) chid := strings.ToLower(msg.Args[0]) if sv.channelCheckMode(chid, "m") && - !sv.channelCheckPerm(chid, clid, "ohv") { + !sv.channelCheckPerm(chid, clid, "qaohv") { sv.sendReply(clid, ERR_CANNOTSENDTOCHAN, chid, "Cannot send to channel") return } @@ -84,15 +84,27 @@ 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]) + _, 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 first || isop { + 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, "")) } - 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 @@ -108,6 +120,8 @@ func handleCmdPart(sv *Server, msg *irc.Message) { } 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) @@ -144,27 +158,27 @@ func handleCmdMode(sv *Server, msg *irc.Message) { switch modeSwitch[1] { // maybe this can be done more elegant case 'o': - if !sv.channelCheckPerm(chid, clid, "o") { + if !sv.channelCheckPerm(chid, clid, "qao") { goto noPerm } case 'h': - if !sv.channelCheckPerm(chid, clid, "o") { + if !sv.channelCheckPerm(chid, clid, "qao") { goto noPerm } case 'b': - if !sv.channelCheckPerm(chid, clid, "oh") { + if !sv.channelCheckPerm(chid, clid, "qaoh") { goto noPerm } case 'v': - if !sv.channelCheckPerm(chid, clid, "oh") { + if !sv.channelCheckPerm(chid, clid, "qaoh") { goto noPerm } case 'm': - if !sv.channelCheckPerm(chid, clid, "oh") { + if !sv.channelCheckPerm(chid, clid, "qaoh") { goto noPerm } case 't': - if !sv.channelCheckPerm(chid, clid, "oh") { + if !sv.channelCheckPerm(chid, clid, "qaoh") { goto noPerm } default: @@ -196,7 +210,7 @@ func handleCmdTopic(sv *Server, msg *irc.Message) { sv.sendReply(msg.Pre, RPL_TOPIC, chid, sv.chTopics[chid]) } else { if sv.channelCheckMode(chid, "t") && - !sv.channelCheckPerm(chid, clid, "oh") { + !sv.channelCheckPerm(chid, clid, "qaoh") { sv.sendReply(clid, ERR_CHANOPRIVSNEEDED, chid, "You're not channel operator") return } @@ -210,11 +224,13 @@ 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") { + 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) diff --git a/server.go b/server.go index 1c23a23..3fba1c6 100644 --- a/server.go +++ b/server.go @@ -22,7 +22,7 @@ const ( ) var myinfo string = "%s %s/%s * *" -var isupport string = "CASEMAPPING=rfc1459 CHANTYPES=# NICKLEN=32 MODES=1 PREFIX=(ohv)@%%+" +var isupport string = "CASEMAPPING=rfc1459 CHANTYPES=# NICKLEN=32 MODES=1 PREFIX=(qaohv)~&@%+" type Server struct { queue chan *irc.Message @@ -36,7 +36,8 @@ type Server struct { created string motd string - opers map[string]bool + admins map[string]bool + opers map[string]bool clients map[string]Client clModes map[string]string @@ -65,6 +66,7 @@ func NewServer(configPath, software, version string) *Server { sv.addq = make(chan Client, 128) sv.delq = make(chan Client, 128) + sv.admins = make(map[string]bool) sv.opers = make(map[string]bool) sv.clients = make(map[string]Client) @@ -84,9 +86,13 @@ func NewServer(configPath, software, version string) *Server { sv.info, _ = sv.config.GetString("server", "info") sv.motd, _ = sv.config.GetString("server", "motd") - opers, _ := sv.config.GetString("control", "o") - for _, oper := range strings.Split(opers, ",") { - sv.opers[oper] = true + nicks, _ := sv.config.GetString("control", "a") + for _, nick := range strings.Split(nicks, ",") { + sv.admins[nick] = true + } + nicks, _ = sv.config.GetString("control", "o") + for _, nick := range strings.Split(nicks, ",") { + sv.opers[nick] = true } sv.packetsTransferred = 0 @@ -326,8 +332,8 @@ func (sv *Server) channelNames(nick, ch string) { if names != "" { names += " " } - modeFlags := "ohv" - modeChars := "@%%+" + modeFlags := "qaohv" + modeChars := "~&@%+" for i, mf := range modeFlags { if _, exists := sv.chModes[chid][string(mf)+" "+clid]; exists { name = string(modeChars[i]) + name