From 479fbf104bf97cffa2de0f604945fc181c80a82c Mon Sep 17 00:00:00 2001 From: "." <.@.> Date: Sat, 7 May 2016 13:55:29 +1200 Subject: [PATCH] verbose support, WHO support, global messages in blessed channel, prevent leaving blessed channel --HG-- branch : nmdc-ircfrontend --- TODO.txt | 15 ++++++++--- main.go | 11 ++++---- server.go | 72 ++++++++++++++++++++++++++++++++++++++++++++--------- typedefs.go | 2 ++ 4 files changed, 79 insertions(+), 21 deletions(-) diff --git a/TODO.txt b/TODO.txt index 74d9e60..e0ce56e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -4,10 +4,10 @@ PRE-RELEASE - nick list -- expose unprefixed system messages in chat channel - - part all nicks on server disconnection +- test the current password auth + WISHLIST ======== @@ -20,10 +20,19 @@ WISHLIST - expose upstream op status -- support WHO, WHOIS, USERIP/KILL/KICK (if opped) +- support WHO + +- support WHOIS + +- support USERIP/KILL/KICK for ops - use CTCP chat to support irc-special characters in chat messages (colon, newline) +- respond to CAP LS 302 (hexchat) in some way that'll kick us into a modern protocol mode + +- support SASL PLAIN authentication as well as just classic PASS + +- threadsafe access to libnmdc hub options diff --git a/main.go b/main.go index 1012fc9..93d1da6 100644 --- a/main.go +++ b/main.go @@ -25,14 +25,12 @@ import ( "net" ) -var ( - ircAddress = flag.String("bind", ":6697", "The address:port to bind to and listen for clients on") - dcAddress = flag.String("upstream", "127.0.0.1:411", "Upstream NMDC server") - serverName = flag.String("servername", "nmdc-ircfrontend", "Server name displayed to clients") -) - func main() { + ircAddress := flag.String("bind", ":6697", "The address:port to bind to and listen for clients on") + dcAddress := flag.String("upstream", "127.0.0.1:411", "Upstream NMDC server") + serverName := flag.String("servername", "nmdc-ircfrontend", "Server name displayed to clients") + verbose := flag.Bool("verbose", false, "Display debugging information") flag.Parse() log.Printf("Listening on '%s'...", *ircAddress) @@ -52,6 +50,7 @@ func main() { // Spin up a worker for the new user. server := NewServer(*serverName, libnmdc.HubAddress(*dcAddress), conn) + server.verbose = *verbose go server.RunWorker() } } diff --git a/server.go b/server.go index 11d6a5b..5223182 100644 --- a/server.go +++ b/server.go @@ -40,6 +40,8 @@ type Server struct { upstreamCloser chan struct{} upstream *libnmdc.HubConnection + verbose bool + lastMessage string // FIXME racey } @@ -59,6 +61,18 @@ func NewServer(name string, upstream libnmdc.HubAddress, conn net.Conn) *Server } } +func (s *Server) verboseln(line string) { + if s.verbose { + log.Println(line) + } +} + +func (s *Server) verbosef(fmt string, args ...interface{}) { + if s.verbose { + log.Printf(fmt, args...) + } +} + func (s *Server) RunWorker() { // Send the connection handshake. @@ -72,13 +86,12 @@ func (s *Server) RunWorker() { buf := make([]byte, CLIENT_READ_BUFFSIZE) - //s.clientConn.SetReadDeadline(time.Now().Add(5 * time.Second)) ln, err := s.clientConn.Read(buf) if err != nil { if err == io.EOF { break // abandon thread } - //log.Println(err.Error()) + s.verboseln(err.Error()) continue } @@ -104,7 +117,7 @@ func (s *Server) RunWorker() { } } - log.Println("Broken loop.") + s.verboseln("Broken loop.") // Cleanup upstream if s.clientRegistered { @@ -123,7 +136,7 @@ func (s *Server) upstreamWorker() { select { case <-s.upstreamCloser: // Abandon the upstream connection - log.Println("Abandoning upstream connection...") + s.verboseln("Abandoning upstream connection...") s.upstream.Disconnect() return @@ -155,7 +168,7 @@ func (s *Server) upstreamWorker() { } case libnmdc.EVENT_SYSTEM_MESSAGE_FROM_CONN, libnmdc.EVENT_SYSTEM_MESSAGE_FROM_HUB: - s.reply(rplMsg, BLESSED_CHANNEL, BLESSED_CHANNEL, hubEvent.Message) // experimental + s.reply(rplMsg, "", BLESSED_CHANNEL, hubEvent.Message) // experimental // s.sendClientGlobalMessage(hubEvent.Message) } @@ -165,6 +178,8 @@ func (s *Server) upstreamWorker() { func (s *Server) handleCommand(command string, args []string) { + s.verbosef(" >>> '%s' %v", command, args) + switch command { case "PING": s.reply(rplPong) @@ -261,7 +276,15 @@ func (s *Server) handleRegisteredCommand(command string, args []string) { } case "PART": - // You can check out any time you like, but you can never leave + if len(args) < 1 { + s.reply(errMoreArgs) + return + } + + if args[0] == BLESSED_CHANNEL { + // You can check out any time you like, but you can never leave + s.reply(rplJoin, s.upstreamLauncher.Self.Nick, BLESSED_CHANNEL) + } case "PRIVMSG": if len(args) < 2 { @@ -325,18 +348,34 @@ func (s *Server) handleRegisteredCommand(command string, args []string) { // Can't use this command. s.reply(errPassword) - case "KILL": + case "KILL", "KICK": s.reply(errNoPriv) return - case "KICK": - if s.clientRegistered == false { - s.reply(errNotReg) + case "WHO": + if len(args) < 1 { + s.reply(errMoreArgs) return } - s.reply(errNoPriv) - return + if args[0] == BLESSED_CHANNEL { + // always include ourselves + s.reply(rplWho, s.upstreamLauncher.Self.Nick) + + for nick, _ := range s.upstream.Users { + if nick != s.upstreamLauncher.Self.Nick { // but don't repeat ourselves + s.reply(rplWho, nick) + } + } + } else { + // argument is a filter + for nick, _ := range s.upstream.Users { + if strings.Contains(nick, args[0]) { + s.reply(rplWho, nick) + } + } + } + s.reply(rplEndOfWho) case "MODE": if len(args) < 1 { @@ -409,10 +448,17 @@ func (s *Server) reply(code replyCode, args ...string) { s.writeClient(fmt.Sprintf(":%s 332 %s %s :%s", s.name, s.upstreamLauncher.Self.Nick, args[0], args[1])) case rplNoTopic: s.writeClient(fmt.Sprintf(":%s 331 %s %s :No topic is set", s.name, s.upstreamLauncher.Self.Nick, args[0])) + case rplNames: s.writeClient(fmt.Sprintf(":%s 353 %s = %s :%s", s.name, s.upstreamLauncher.Self.Nick, args[0], args[1])) case rplEndOfNames: s.writeClient(fmt.Sprintf(":%s 366 %s %s :End of NAMES list", s.name, s.upstreamLauncher.Self.Nick, args[0])) + + case rplWho: + s.writeClient(fmt.Sprintf(":%s 352 %s %s %s %s %s %s H :0 %s", s.name, s.upstreamLauncher.Self.Nick, BLESSED_CHANNEL, args[0], args[0], s.name, args[0], args[0])) + case rplEndOfWho: + s.writeClient(fmt.Sprintf(":%s 315 :End of WHO list", s.name)) + case rplNickChange: s.writeClient(fmt.Sprintf(":%s NICK %s", args[0], args[1])) case rplKill: @@ -473,6 +519,8 @@ func (s *Server) writeClient(output string) { return } + s.verbosef(" <<< %s", output) + s.clientConn.SetWriteDeadline(time.Now().Add(time.Second * 30)) if _, err := fmt.Fprintf(s.clientConn, "%s\r\n", output); err != nil { s.DisconnectClient() diff --git a/typedefs.go b/typedefs.go index d035158..6e78807 100644 --- a/typedefs.go +++ b/typedefs.go @@ -39,6 +39,8 @@ const ( rplNoTopic rplNames rplEndOfNames + rplWho + rplEndOfWho rplNickChange rplKill rplMsg