From e97f344f00e1442f304630d81aadb9f82432bdea Mon Sep 17 00:00:00 2001 From: "." <.@.> Date: Tue, 3 May 2016 19:57:02 +1200 Subject: [PATCH] wip --HG-- branch : nmdc-ircfrontend --- client.go | 1 + server.go | 136 ++++++++++++++++++++++-------------------------------- 2 files changed, 55 insertions(+), 82 deletions(-) diff --git a/client.go b/client.go index 8d74458..b70d84b 100644 --- a/client.go +++ b/client.go @@ -58,6 +58,7 @@ func (c *Client) joinChannel(channelName string) { func (c *Client) disconnect() { c.connected = false + c.connection.Close() } func (c *Client) sendGlobalMessage(motd string) { diff --git a/server.go b/server.go index 4a6d3ad..41fae31 100644 --- a/server.go +++ b/server.go @@ -7,6 +7,7 @@ import ( "libnmdc" "net" "strings" + "time" ) type Server struct { @@ -16,18 +17,21 @@ type Server struct { client *Client channel Channel // Single blessed channel upstreamLauncher libnmdc.HubConnectionOptions - upstream libnmdc.HubConnection + upstream *libnmdc.HubConnection motd string } func NewServer(name string, upstream libnmdc.HubAddress) *Server { + self := libnmdc.NewUserInfo("") + self.ClientTag = APP_DESCRIPTION + return &Server{eventChan: make(chan Event), name: name, client: nil, motd: "Connected to " + name, upstreamLauncher: libnmdc.HubConnectionOptions{ Address: upstream, - Self: *libnmdc.NewUserInfo(""), + Self: *self, }, channel: Channel{ clientMap: make(map[string]*Client), @@ -48,59 +52,37 @@ func (s *Server) RunClient(conn net.Conn) { s.client.sendGlobalMessage(s.motd) // Can't connect to the upstream server yet, until we've recieved a nick. - // So we can't have a conjoined select statement - // Need a separate goroutine for both IRC and NMDC protocols, and - // synchronisation to ensure simultaneous cleanup - closeChan := make(chan struct{}, 2) - go s.ProtocolReadLoop_IRC(closeChan) - // FIXME - -} - -func (s *Server) ProtocolReadLoop_IRC(closeChan chan struct{}) { - - defer func() { - closeChan <- struct{}{} - }() - - // Read loop for { buf := make([]byte, CLIENT_READ_BUFFSIZE) - // s.client.connection.SetReadDeadline(time.Now().Add(time.Second * CLIENT_READ_TIMEOUT_SEC)) - - select { - case <-closeChan: - return - - case ln, err := s.client.connection.Read(buf): - if err != nil { - if err == io.EOF { - s.client.disconnect() - return // FIXME cleanup - } - continue + s.client.connection.SetReadDeadline(time.Now().Add(time.Second * CLIENT_READ_TIMEOUT_SEC)) + ln, err := s.client.connection.Read(buf) + if err != nil { + if err == io.EOF { + s.client.disconnect() + return // FIXME cleanup } + continue + } - rawLines := buf[:ln] - rawLines = bytes.Replace(rawLines, []byte("\r\n"), []byte("\n"), -1) - rawLines = bytes.Replace(rawLines, []byte("\r"), []byte("\n"), -1) - lines := bytes.Split(rawLines, []byte("\n")) - for _, line := range lines { - if len(line) > 0 { + rawLines := buf[:ln] + rawLines = bytes.Replace(rawLines, []byte("\r\n"), []byte("\n"), -1) + rawLines = bytes.Replace(rawLines, []byte("\r"), []byte("\n"), -1) + lines := bytes.Split(rawLines, []byte("\n")) + for _, line := range lines { + if len(line) > 0 { - // Client sent a command - fields := strings.Fields(string(line)) - if len(fields) < 1 { - return - } - - if strings.HasPrefix(fields[0], ":") { - fields = fields[1:] - } - - s.handleCommand(strings.ToUpper(fields[0]), fields[1:]) + // Client sent a command + fields := strings.Fields(string(line)) + if len(fields) < 1 { + return } + + if strings.HasPrefix(fields[0], ":") { + fields = fields[1:] + } + + s.handleCommand(strings.ToUpper(fields[0]), fields[1:]) } } } @@ -108,14 +90,15 @@ func (s *Server) ProtocolReadLoop_IRC(closeChan chan struct{}) { } func (s *Server) ProtocolReadLoop_NMDC(closeChan chan struct{}) { - defer func() { - closeChan <- struct{}{} - }() + + // Initiate connection + s.upstream = s.upstreamLauncher.Connect() // Read loop for { select { case <-closeChan: + // Need some way of deliberately shutting down a libnmdc connection... return case hubEvent := <-s.upstream.OnEvent: @@ -186,22 +169,30 @@ func (s *Server) handleCommand(command string, args []string) { } case "USER": + // This command sets altname, realname, ... none of which we use + // It's the final step in a PASS/NICK/USER login handshake. + if s.client.registered == true { s.client.reply(rplKill, "You're already registered.", "") s.client.disconnect() + return } if s.client.nick == "" { s.client.reply(rplKill, "Your nickname is already being used", "") s.client.disconnect() - - } else { - s.client.reply(rplWelcome) - s.client.registered = true - - // Spawn upstream connection + return } + s.client.reply(rplWelcome) + s.client.registered = true + + // Spawn upstream connection + go s.ProtocolReadLoop_NMDC(nil) // FIXME need shutdown synchronisation + + // Tell the user that they themselves joined the chat channel + s.client.reply(rplJoin, s.client.nick, BLESSED_CHANNEL) + case "JOIN": if s.client.registered == false { s.client.reply(errNotReg) @@ -247,22 +238,12 @@ func (s *Server) handleCommand(command string, args []string) { // IRC is case-insensitive case-preserving. We can respect that for the // channel name, but not really for user nicks - recipient := strings.ToLower(args[0]) - if recipient == BLESSED_CHANNEL { + if strings.ToLower(args[0]) == BLESSED_CHANNEL { s.upstream.SayPublic(message) - /* - for _, c := range s.channel.clientMap { - if c != client { - c.reply(rplMsg, s.client.nick, args[0], message) - } - } - */ - } else if nmdcUser, clientExists := s.upstream.Users[args[0]]; clientExists { - - s.upstream.SayPrivate(recipient, message) - // client2.reply(rplMsg, s.client.nick, client2.nick, message) + } else if _, clientExists := s.upstream.Users[args[0]]; clientExists { + s.upstream.SayPrivate(args[0], message) } else { s.client.reply(errNoSuchNick, args[0]) @@ -325,17 +306,8 @@ func (s *Server) handleCommand(command string, args []string) { return } - //username := args[0] - //password := args[1] - - if false { // op the user - s.client.operator = true - s.client.reply(rplOper) - return - } else { - s.client.reply(errPassword) - - } + // Can't use this command. + s.client.reply(errPassword) case "KILL": if s.client.registered == false { @@ -372,7 +344,7 @@ func (s *Server) handleCommand(command string, args []string) { } if len(args) == 1 { - //No more args, they just want the mode + // No more args, they just want the mode s.client.reply(rplChannelModeIs, args[0], BLESSED_CHANNEL_MODE, "") } else { // Setting modes is disallowed