chat.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package oscar
  2. import (
  3. "context"
  4. "errors"
  5. "io"
  6. "log/slog"
  7. "net"
  8. "os"
  9. "github.com/mk6i/retro-aim-server/config"
  10. "github.com/mk6i/retro-aim-server/wire"
  11. )
  12. // ChatServer represents a service that implements a chat room session.
  13. // Clients connect to this service upon creating a chat room or being invited
  14. // to a chat room.
  15. type ChatServer struct {
  16. AuthService
  17. Handler
  18. Logger *slog.Logger
  19. OnlineNotifier
  20. config.Config
  21. }
  22. // Start creates a TCP server that implements that chat flow.
  23. func (rt ChatServer) Start() {
  24. addr := net.JoinHostPort("", rt.Config.ChatPort)
  25. listener, err := net.Listen("tcp", addr)
  26. if err != nil {
  27. rt.Logger.Error("unable to bind server address", "host", addr, "err", err.Error())
  28. os.Exit(1)
  29. }
  30. defer listener.Close()
  31. rt.Logger.Info("starting server", "listen_host", addr, "oscar_host", rt.Config.OSCARHost)
  32. for {
  33. conn, err := listener.Accept()
  34. if err != nil {
  35. rt.Logger.Error(err.Error())
  36. continue
  37. }
  38. ctx := context.Background()
  39. ctx = context.WithValue(ctx, "ip", conn.RemoteAddr().String())
  40. rt.Logger.DebugContext(ctx, "accepted connection")
  41. go func() {
  42. if err := rt.handleNewConnection(ctx, conn); err != nil {
  43. rt.Logger.Info("user session failed", "err", err.Error())
  44. }
  45. }()
  46. }
  47. }
  48. func (rt ChatServer) handleNewConnection(ctx context.Context, rwc io.ReadWriteCloser) error {
  49. flapc := wire.NewFlapClient(100, rwc, rwc)
  50. if err := flapc.SendSignonFrame(nil); err != nil {
  51. return err
  52. }
  53. flap, err := flapc.ReceiveSignonFrame()
  54. if err != nil {
  55. return err
  56. }
  57. authCookie, ok := flap.Slice(wire.OServiceTLVTagsLoginCookie)
  58. if !ok {
  59. return errors.New("unable to get login cookie from payload")
  60. }
  61. chatSess, err := rt.RegisterChatSession(authCookie)
  62. if err != nil {
  63. return err
  64. }
  65. if chatSess == nil {
  66. return errors.New("session not found")
  67. }
  68. defer func() {
  69. chatSess.Close()
  70. rwc.Close()
  71. rt.SignoutChat(ctx, chatSess)
  72. }()
  73. msg := rt.HostOnline()
  74. if err := flapc.SendSNAC(msg.Frame, msg.Body); err != nil {
  75. return err
  76. }
  77. ctx = context.WithValue(ctx, "screenName", chatSess.IdentScreenName())
  78. return dispatchIncomingMessages(ctx, chatSess, flapc, rwc, rt.Logger, rt.Handler, rt.Config)
  79. }