chat.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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 := config.Address("", rt.Config.ChatPort)
  25. listener, err := net.Listen("tcp", addr)
  26. if err != nil {
  27. rt.Logger.Error("unable to bind chat server address", "err", err.Error())
  28. os.Exit(1)
  29. }
  30. defer listener.Close()
  31. rt.Logger.Info("starting chat service", "addr", addr)
  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 := &flapClient{
  50. r: rwc,
  51. sequence: 100,
  52. w: rwc,
  53. }
  54. flap, err := flapc.SignonHandshake()
  55. if err != nil {
  56. return err
  57. }
  58. var ok bool
  59. loginCookie, ok := flap.Slice(wire.OServiceTLVTagsLoginCookie)
  60. if !ok {
  61. return errors.New("unable to get login cookie from payload")
  62. }
  63. chatSess, err := rt.RetrieveChatSession(loginCookie)
  64. if err != nil {
  65. return err
  66. }
  67. if chatSess == nil {
  68. return errors.New("session not found")
  69. }
  70. defer func() {
  71. chatSess.Close()
  72. rwc.Close()
  73. if err := rt.SignoutChat(ctx, chatSess); err != nil {
  74. rt.Logger.ErrorContext(ctx, "unable to sign out user", "err", err.Error())
  75. }
  76. }()
  77. msg := rt.HostOnline()
  78. if err := flapc.SendSNAC(msg.Frame, msg.Body); err != nil {
  79. return err
  80. }
  81. ctx = context.WithValue(ctx, "screenName", chatSess.ScreenName())
  82. return dispatchIncomingMessages(ctx, chatSess, flapc, rwc, rt.Logger, rt.Handler, rt.Config)
  83. }