chat.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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. CookieCracker
  22. }
  23. // Start creates a TCP server that implements that chat flow.
  24. func (rt ChatServer) Start() {
  25. addr := net.JoinHostPort("", rt.Config.ChatPort)
  26. listener, err := net.Listen("tcp", addr)
  27. if err != nil {
  28. rt.Logger.Error("unable to bind server address", "host", addr, "err", err.Error())
  29. os.Exit(1)
  30. }
  31. defer listener.Close()
  32. rt.Logger.Info("starting server", "listen_host", addr, "oscar_host", rt.Config.OSCARHost)
  33. for {
  34. conn, err := listener.Accept()
  35. if err != nil {
  36. rt.Logger.Error(err.Error())
  37. continue
  38. }
  39. ctx := context.Background()
  40. ctx = context.WithValue(ctx, "ip", conn.RemoteAddr().String())
  41. rt.Logger.DebugContext(ctx, "accepted connection")
  42. go func() {
  43. if err := rt.handleNewConnection(ctx, conn); err != nil {
  44. rt.Logger.Info("user session failed", "err", err.Error())
  45. }
  46. }()
  47. }
  48. }
  49. func (rt ChatServer) handleNewConnection(ctx context.Context, rwc io.ReadWriteCloser) error {
  50. flapc := wire.NewFlapClient(100, rwc, rwc)
  51. if err := flapc.SendSignonFrame(nil); err != nil {
  52. return err
  53. }
  54. flap, err := flapc.ReceiveSignonFrame()
  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. token, err := rt.CookieCracker.Crack(loginCookie)
  64. if err != nil {
  65. return err
  66. }
  67. chatSess, err := rt.RegisterChatSession(token)
  68. if err != nil {
  69. return err
  70. }
  71. if chatSess == nil {
  72. return errors.New("session not found")
  73. }
  74. defer func() {
  75. chatSess.Close()
  76. rwc.Close()
  77. if err := rt.SignoutChat(ctx, chatSess); err != nil {
  78. rt.Logger.ErrorContext(ctx, "unable to sign out user", "err", err.Error())
  79. }
  80. }()
  81. msg := rt.HostOnline()
  82. if err := flapc.SendSNAC(msg.Frame, msg.Body); err != nil {
  83. return err
  84. }
  85. ctx = context.WithValue(ctx, "screenName", chatSess.ScreenName())
  86. return dispatchIncomingMessages(ctx, chatSess, flapc, rwc, rt.Logger, rt.Handler, rt.Config)
  87. }