]> git.r.bdr.sh - rbdr/mobius/blobdiff - hotline/server.go
Fix regression in 1.5 login behavior
[rbdr/mobius] / hotline / server.go
index ec835228607d8f3052d858b24a2c9b8b1320a914..3e4ba2f08f90d1f9db317085c0873f82930292ac 100644 (file)
@@ -2,7 +2,6 @@ package hotline
 
 import (
        "bufio"
-       "bytes"
        "context"
        "encoding/binary"
        "errors"
@@ -33,8 +32,6 @@ type requestCtx struct {
        name       string
 }
 
-var nostalgiaVersion = []byte{0, 0, 2, 0x2c} // version ID used by the Nostalgia client
-
 type Server struct {
        Port          int
        Accounts      map[string]*Account
@@ -152,18 +149,18 @@ func (s *Server) sendTransaction(t Transaction) error {
 
        s.mux.Lock()
        client := s.Clients[uint16(clientID)]
+       s.mux.Unlock()
        if client == nil {
                return fmt.Errorf("invalid client id %v", *t.clientID)
        }
 
-       s.mux.Unlock()
-
        b, err := t.MarshalBinary()
        if err != nil {
                return err
        }
 
-       if _, err := client.Connection.Write(b); err != nil {
+       _, err = client.Connection.Write(b)
+       if err != nil {
                return err
        }
 
@@ -380,7 +377,6 @@ func (s *Server) NewClientConn(conn io.ReadWriteCloser, remoteAddr string) *Clie
                Version:    []byte{},
                AutoReply:  []byte{},
                transfers:  map[int]map[[4]byte]*FileTransfer{},
-               Agreed:     false,
                RemoteAddr: remoteAddr,
        }
        clientConn.transfers = map[int]map[[4]byte]*FileTransfer{
@@ -467,9 +463,6 @@ func (s *Server) connectedUsers() []Field {
 
        var connectedUsers []Field
        for _, c := range sortedClients(s.Clients) {
-               if !c.Agreed {
-                       continue
-               }
                user := User{
                        ID:    *c.ID,
                        Icon:  c.Icon,
@@ -564,8 +557,13 @@ func (s *Server) handleNewConnection(ctx context.Context, rwc io.ReadWriteCloser
 
        scanner.Scan()
 
+       // Make a new []byte slice and copy the scanner bytes to it.  This is critical to avoid a data race as the
+       // scanner re-uses the buffer for subsequent scans.
+       buf := make([]byte, len(scanner.Bytes()))
+       copy(buf, scanner.Bytes())
+
        var clientLogin Transaction
-       if _, err := clientLogin.Write(scanner.Bytes()); err != nil {
+       if _, err := clientLogin.Write(buf); err != nil {
                return err
        }
 
@@ -666,12 +664,17 @@ func (s *Server) handleNewConnection(ctx context.Context, rwc io.ReadWriteCloser
                c.Server.outbox <- *NewTransaction(tranShowAgreement, c.ID, NewField(fieldData, s.Agreement))
        }
 
-       // Used simplified hotline v1.2.3 login flow for clients that do not send login info in tranAgreed
-       if c.Version == nil || bytes.Equal(c.Version, nostalgiaVersion) {
-               c.Agreed = true
+       // If the client has provided a username as part of the login, we can infer that it is using the 1.2.3 login
+       // flow and not the 1.5+ flow.
+       if len(c.UserName) != 0 {
+               // Add the client username to the logger.  For 1.5+ clients, we don't have this information yet as it comes as
+               // part of tranAgreed
                c.logger = c.logger.With("name", string(c.UserName))
-               c.logger.Infow("Login successful", "clientVersion", fmt.Sprintf("%v", func() int { i, _ := byteToInt(c.Version); return i }()))
 
+               c.logger.Infow("Login successful", "clientVersion", "Not sent (probably 1.2.3)")
+
+               // Notify other clients on the server that the new user has logged in.  For 1.5+ clients we don't have this
+               // information yet, so we do it in tranAgreed instead
                for _, t := range c.notifyOthers(
                        *NewTransaction(
                                tranNotifyChangeUser, nil,
@@ -748,6 +751,10 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro
                delete(s.fileTransfers, t.ReferenceNumber)
                s.mux.Unlock()
 
+               // Wait a few seconds before closing the connection: this is a workaround for problems
+               // observed with Windows clients where the client must initiate close of the TCP connection before
+               // the server does.  This is gross and seems unnecessary.  TODO: Revisit?
+               time.Sleep(3 * time.Second)
        }()
 
        s.mux.Lock()
@@ -782,7 +789,9 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro
        case FileDownload:
                s.Stats.DownloadCounter += 1
                s.Stats.DownloadsInProgress += 1
-               defer func() { s.Stats.DownloadsInProgress -= 1 }()
+               defer func() {
+                       s.Stats.DownloadsInProgress -= 1
+               }()
 
                var dataOffset int64
                if fileTransfer.fileResumeData != nil {