X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/blobdiff_plain/7cd900d61edbd6d322db3cecb913adf574389320..dd3acf5eaf12f9419950b7ee7221d99e98a1d9a1:/hotline/server.go diff --git a/hotline/server.go b/hotline/server.go index 9df89f2..21b1316 100644 --- a/hotline/server.go +++ b/hotline/server.go @@ -17,7 +17,6 @@ import ( "math/rand" "net" "os" - "path" "path/filepath" "runtime/debug" "strings" @@ -168,25 +167,31 @@ func (s *Server) sendTransaction(t Transaction) error { return nil } +func (s *Server) processOutbox() { + for { + t := <-s.outbox + go func() { + if err := s.sendTransaction(t); err != nil { + s.Logger.Errorw("error sending transaction", "err", err) + } + }() + } +} + func (s *Server) Serve(ctx context.Context, ln net.Listener) error { + go s.processOutbox() + for { conn, err := ln.Accept() if err != nil { s.Logger.Errorw("error accepting connection", "err", err) } + connCtx := context.WithValue(ctx, contextKeyReq, requestCtx{ + remoteAddr: conn.RemoteAddr().String(), + }) go func() { - for { - t := <-s.outbox - go func() { - if err := s.sendTransaction(t); err != nil { - s.Logger.Errorw("error sending transaction", "err", err) - } - }() - } - }() - go func() { - if err := s.handleNewConnection(ctx, conn, conn.RemoteAddr().String()); err != nil { + if err := s.handleNewConnection(connCtx, conn, conn.RemoteAddr().String()); err != nil { s.Logger.Infow("New client connection established", "RemoteAddr", conn.RemoteAddr()) if err == io.EOF { s.Logger.Infow("Client disconnected", "RemoteAddr", conn.RemoteAddr()) @@ -227,28 +232,28 @@ func NewServer(configDir string, netPort int, logger *zap.SugaredLogger, FS File return nil, err } - server.Agreement, err = os.ReadFile(configDir + agreementFile) + server.Agreement, err = os.ReadFile(filepath.Join(configDir, agreementFile)) if err != nil { return nil, err } - if server.FlatNews, err = os.ReadFile(configDir + "MessageBoard.txt"); err != nil { + if server.FlatNews, err = os.ReadFile(filepath.Join(configDir, "MessageBoard.txt")); err != nil { return nil, err } - if err := server.loadThreadedNews(configDir + "ThreadedNews.yaml"); err != nil { + if err := server.loadThreadedNews(filepath.Join(configDir, "ThreadedNews.yaml")); err != nil { return nil, err } - if err := server.loadConfig(configDir + "config.yaml"); err != nil { + if err := server.loadConfig(filepath.Join(configDir, "config.yaml")); err != nil { return nil, err } - if err := server.loadAccounts(configDir + "Users/"); err != nil { + if err := server.loadAccounts(filepath.Join(configDir, "Users/")); err != nil { return nil, err } - server.Config.FileRoot = configDir + "Files/" + server.Config.FileRoot = filepath.Join(configDir, "Files") *server.NextGuestID = 1 @@ -329,14 +334,14 @@ func (s *Server) writeThreadedNews() error { return err } err = ioutil.WriteFile( - s.ConfigDir+"ThreadedNews.yaml", + filepath.Join(s.ConfigDir, "ThreadedNews.yaml"), out, 0666, ) return err } -func (s *Server) NewClientConn(conn net.Conn, remoteAddr string) *ClientConn { +func (s *Server) NewClientConn(conn io.ReadWriteCloser, remoteAddr string) *ClientConn { s.mux.Lock() defer s.mux.Unlock() @@ -379,7 +384,7 @@ func (s *Server) NewUser(login, name, password string, access []byte) error { } s.Accounts[login] = &account - return s.FS.WriteFile(s.ConfigDir+"Users/"+login+".yaml", out, 0666) + return s.FS.WriteFile(filepath.Join(s.ConfigDir, "Users", login+".yaml"), out, 0666) } func (s *Server) UpdateUser(login, newLogin, name, password string, access []byte) error { @@ -388,7 +393,7 @@ func (s *Server) UpdateUser(login, newLogin, name, password string, access []byt // update renames the user login if login != newLogin { - err := os.Rename(s.ConfigDir+"Users/"+login+".yaml", s.ConfigDir+"Users/"+newLogin+".yaml") + err := os.Rename(filepath.Join(s.ConfigDir, "Users", login+".yaml"), filepath.Join(s.ConfigDir, "Users", newLogin+".yaml")) if err != nil { return err } @@ -405,7 +410,8 @@ func (s *Server) UpdateUser(login, newLogin, name, password string, access []byt if err != nil { return err } - if err := os.WriteFile(s.ConfigDir+"Users/"+newLogin+".yaml", out, 0666); err != nil { + + if err := os.WriteFile(filepath.Join(s.ConfigDir, "Users", newLogin+".yaml"), out, 0666); err != nil { return err } @@ -419,7 +425,7 @@ func (s *Server) DeleteUser(login string) error { delete(s.Accounts, login) - return s.FS.Remove(s.ConfigDir + "Users/" + login + ".yaml") + return s.FS.Remove(filepath.Join(s.ConfigDir, "Users", login+".yaml")) } func (s *Server) connectedUsers() []Field { @@ -455,7 +461,7 @@ func (s *Server) loadThreadedNews(threadedNewsPath string) error { // loadAccounts loads account data from disk func (s *Server) loadAccounts(userDir string) error { - matches, err := filepath.Glob(path.Join(userDir, "*.yaml")) + matches, err := filepath.Glob(filepath.Join(userDir, "*.yaml")) if err != nil { return err } @@ -515,7 +521,7 @@ func dontPanic(logger *zap.SugaredLogger) { } // handleNewConnection takes a new net.Conn and performs the initial login sequence -func (s *Server) handleNewConnection(ctx context.Context, conn net.Conn, remoteAddr string) error { +func (s *Server) handleNewConnection(ctx context.Context, conn io.ReadWriteCloser, remoteAddr string) error { defer dontPanic(s.Logger) if err := Handshake(conn); err != nil { @@ -579,11 +585,13 @@ func (s *Server) handleNewConnection(ctx context.Context, conn net.Conn, remoteA *c.Flags = []byte{0, 2} } - s.Logger.Infow("Client connection received", "login", login, "version", *c.Version, "RemoteAddr", remoteAddr) + c.logger = s.Logger.With("remoteAddr", remoteAddr, "login", login) + + c.logger.Infow("Client connection received", "version", fmt.Sprintf("%x", *c.Version)) s.outbox <- c.NewReply(clientLogin, NewField(fieldVersion, []byte{0x00, 0xbe}), - NewField(fieldCommunityBannerID, []byte{0x00, 0x01}), + NewField(fieldCommunityBannerID, []byte{0, 0}), NewField(fieldServerName, []byte(s.Config.Name)), ) @@ -596,8 +604,9 @@ func (s *Server) handleNewConnection(ctx context.Context, conn net.Conn, remoteA // 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 + c.logger = c.logger.With("name", string(c.UserName)) - c.notifyOthers( + for _, t := range c.notifyOthers( *NewTransaction( tranNotifyChangeUser, nil, NewField(fieldUserName, c.UserName), @@ -605,7 +614,9 @@ func (s *Server) handleNewConnection(ctx context.Context, conn net.Conn, remoteA NewField(fieldUserIconID, *c.Icon), NewField(fieldUserFlags, *c.Flags), ), - ) + ) { + c.Server.outbox <- t + } } c.Server.Stats.LoginCount += 1 @@ -628,13 +639,13 @@ func (s *Server) handleNewConnection(ctx context.Context, conn net.Conn, remoteA // into a slice of transactions var transactions []Transaction if transactions, tReadlen, err = readTransactions(tranBuff); err != nil { - c.Server.Logger.Errorw("Error handling transaction", "err", err) + c.logger.Errorw("Error handling transaction", "err", err) } // iterate over all the transactions that were parsed from the byte slice and handle them for _, t := range transactions { if err := c.handleTransaction(&t); err != nil { - c.Server.Logger.Errorw("Error handling transaction", "err", err) + c.logger.Errorw("Error handling transaction", "err", err) } } } @@ -705,6 +716,10 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro ) switch fileTransfer.Type { + case bannerDownload: + if err := s.bannerDownload(rwc); err != nil { + return err + } case FileDownload: s.Stats.DownloadCounter += 1 @@ -765,6 +780,9 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro } err = sendFile(wr, rFile, int(dataOffset)) + if err != nil { + return err + } if err := wr.Flush(); err != nil { return err @@ -808,8 +826,6 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro return err } - defer func() { _ = file.Close() }() - s.Logger.Infow("File upload started", "transactionRef", fileTransfer.ReferenceNumber, "dstFile", destinationFile) rForkWriter := io.Discard @@ -830,6 +846,10 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro return err } + if err := file.Close(); err != nil { + return err + } + if err := s.FS.Rename(destinationFile+".incomplete", destinationFile); err != nil { return err } @@ -1002,6 +1022,10 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro return nil }) + if err != nil { + return err + } + case FolderUpload: dstPath, err := readPath(s.Config.FileRoot, fileTransfer.FilePath, fileTransfer.FileName) if err != nil { @@ -1059,8 +1083,8 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro ) if fu.IsFolder == [2]byte{0, 1} { - if _, err := os.Stat(dstPath + "/" + fu.FormattedPath()); os.IsNotExist(err) { - if err := os.Mkdir(dstPath+"/"+fu.FormattedPath(), 0777); err != nil { + if _, err := os.Stat(filepath.Join(dstPath, fu.FormattedPath())); os.IsNotExist(err) { + if err := os.Mkdir(filepath.Join(dstPath, fu.FormattedPath()), 0777); err != nil { return err } } @@ -1073,7 +1097,7 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro nextAction := dlFldrActionSendFile // Check if we have the full file already. If so, send dlFldrAction_NextFile to client to skip. - _, err = os.Stat(dstPath + "/" + fu.FormattedPath()) + _, err = os.Stat(filepath.Join(dstPath, fu.FormattedPath())) if err != nil && !errors.Is(err, fs.ErrNotExist) { return err } @@ -1082,7 +1106,7 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro } // Check if we have a partial file already. If so, send dlFldrAction_ResumeFile to client to resume upload. - incompleteFile, err := os.Stat(dstPath + "/" + fu.FormattedPath() + incompleteFileSuffix) + incompleteFile, err := os.Stat(filepath.Join(dstPath, fu.FormattedPath()+incompleteFileSuffix)) if err != nil && !errors.Is(err, fs.ErrNotExist) { return err } @@ -1135,7 +1159,7 @@ func (s *Server) handleFileTransfer(ctx context.Context, rwc io.ReadWriter) erro return err } - filePath := dstPath + "/" + fu.FormattedPath() + filePath := filepath.Join(dstPath, fu.FormattedPath()) hlFile, err := newFileWrapper(s.FS, filePath, 0) if err != nil {