buddy.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package server
  2. import (
  3. "context"
  4. "io"
  5. "log/slog"
  6. "github.com/mkaminski/goaim/oscar"
  7. )
  8. type BuddyHandler interface {
  9. RightsQueryHandler(ctx context.Context) XMessage
  10. }
  11. func NewBuddyRouter(logger *slog.Logger) BuddyRouter {
  12. return BuddyRouter{
  13. BuddyHandler: BuddyService{},
  14. RouteLogger: RouteLogger{
  15. Logger: logger,
  16. },
  17. }
  18. }
  19. type BuddyRouter struct {
  20. BuddyHandler
  21. RouteLogger
  22. }
  23. func (rt *BuddyRouter) RouteBuddy(ctx context.Context, SNACFrame oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  24. switch SNACFrame.SubGroup {
  25. case oscar.BuddyRightsQuery:
  26. inSNAC := oscar.SNAC_0x03_0x02_BuddyRightsQuery{}
  27. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  28. return err
  29. }
  30. outSNAC := rt.RightsQueryHandler(ctx)
  31. rt.logRequestAndResponse(ctx, SNACFrame, inSNAC, outSNAC.snacFrame, outSNAC.snacOut)
  32. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  33. default:
  34. return ErrUnsupportedSubGroup
  35. }
  36. }
  37. type BuddyService struct {
  38. }
  39. func (s BuddyService) RightsQueryHandler(context.Context) XMessage {
  40. return XMessage{
  41. snacFrame: oscar.SnacFrame{
  42. FoodGroup: oscar.BUDDY,
  43. SubGroup: oscar.BuddyRightsReply,
  44. },
  45. snacOut: oscar.SNAC_0x03_0x03_BuddyRightsReply{
  46. TLVRestBlock: oscar.TLVRestBlock{
  47. TLVList: oscar.TLVList{
  48. oscar.NewTLV(0x01, uint16(100)),
  49. oscar.NewTLV(0x02, uint16(100)),
  50. oscar.NewTLV(0x03, uint16(100)),
  51. oscar.NewTLV(0x04, uint16(100)),
  52. },
  53. },
  54. },
  55. }
  56. }
  57. func BroadcastArrival(ctx context.Context, sess *Session, sm SessionManager, fm FeedbagManager) error {
  58. screenNames, err := fm.InterestedUsers(sess.ScreenName)
  59. if err != nil {
  60. return err
  61. }
  62. sm.BroadcastToScreenNames(ctx, screenNames, XMessage{
  63. snacFrame: oscar.SnacFrame{
  64. FoodGroup: oscar.BUDDY,
  65. SubGroup: oscar.BuddyArrived,
  66. },
  67. snacOut: oscar.SNAC_0x03_0x0A_BuddyArrived{
  68. TLVUserInfo: oscar.TLVUserInfo{
  69. ScreenName: sess.ScreenName,
  70. WarningLevel: sess.GetWarning(),
  71. TLVBlock: oscar.TLVBlock{
  72. TLVList: sess.GetUserInfo(),
  73. },
  74. },
  75. },
  76. })
  77. return nil
  78. }
  79. func BroadcastDeparture(ctx context.Context, sess *Session, sm SessionManager, fm *FeedbagStore) error {
  80. screenNames, err := fm.InterestedUsers(sess.ScreenName)
  81. if err != nil {
  82. return err
  83. }
  84. sm.BroadcastToScreenNames(ctx, screenNames, XMessage{
  85. snacFrame: oscar.SnacFrame{
  86. FoodGroup: oscar.BUDDY,
  87. SubGroup: oscar.BuddyDeparted,
  88. },
  89. snacOut: oscar.SNAC_0x03_0x0B_BuddyDeparted{
  90. TLVUserInfo: oscar.TLVUserInfo{
  91. ScreenName: sess.ScreenName,
  92. WarningLevel: sess.GetWarning(),
  93. },
  94. },
  95. })
  96. return nil
  97. }
  98. func UnicastArrival(ctx context.Context, srcScreenName, destScreenName string, sm SessionManager) error {
  99. sess, err := sm.RetrieveByScreenName(srcScreenName)
  100. switch {
  101. case err != nil:
  102. return err
  103. case sess.Invisible(): // don't tell user this buddy is online
  104. return nil
  105. }
  106. sm.SendToScreenName(ctx, destScreenName, XMessage{
  107. snacFrame: oscar.SnacFrame{
  108. FoodGroup: oscar.BUDDY,
  109. SubGroup: oscar.BuddyArrived,
  110. },
  111. snacOut: oscar.SNAC_0x03_0x0A_BuddyArrived{
  112. TLVUserInfo: sess.GetTLVUserInfo(),
  113. },
  114. })
  115. return nil
  116. }
  117. func UnicastDeparture(ctx context.Context, srcScreenName, destScreenName string, sm SessionManager) error {
  118. sess, err := sm.RetrieveByScreenName(srcScreenName)
  119. switch {
  120. case err != nil:
  121. return err
  122. case sess.Invisible(): // don't tell user this buddy is online
  123. return nil
  124. }
  125. sm.SendToScreenName(ctx, destScreenName, XMessage{
  126. snacFrame: oscar.SnacFrame{
  127. FoodGroup: oscar.BUDDY,
  128. SubGroup: oscar.BuddyDeparted,
  129. },
  130. snacOut: oscar.SNAC_0x03_0x0B_BuddyDeparted{
  131. TLVUserInfo: oscar.TLVUserInfo{
  132. // don't include the TLV block, otherwise the AIM client fails
  133. // to process the block event
  134. ScreenName: sess.ScreenName,
  135. WarningLevel: sess.GetWarning(),
  136. },
  137. },
  138. })
  139. return nil
  140. }