feedbag.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package handler
  2. import (
  3. "context"
  4. "time"
  5. "github.com/mkaminski/goaim/oscar"
  6. "github.com/mkaminski/goaim/state"
  7. )
  8. func NewFeedbagService(messageRelayer MessageRelayer, feedbagManager FeedbagManager) *FeedbagService {
  9. return &FeedbagService{messageRelayer: messageRelayer, feedbagManager: feedbagManager}
  10. }
  11. type FeedbagService struct {
  12. messageRelayer MessageRelayer
  13. feedbagManager FeedbagManager
  14. }
  15. func (s FeedbagService) RightsQueryHandler(_ context.Context, inFrame oscar.SNACFrame) oscar.SNACMessage {
  16. return oscar.SNACMessage{
  17. Frame: oscar.SNACFrame{
  18. FoodGroup: oscar.Feedbag,
  19. SubGroup: oscar.FeedbagRightsReply,
  20. RequestID: inFrame.RequestID,
  21. },
  22. Body: oscar.SNAC_0x13_0x03_FeedbagRightsReply{
  23. TLVRestBlock: oscar.TLVRestBlock{
  24. TLVList: oscar.TLVList{
  25. oscar.NewTLV(0x03, uint16(200)),
  26. oscar.NewTLV(0x04, []uint16{
  27. 0x3D,
  28. 0x3D,
  29. 0x64,
  30. 0x64,
  31. 0x01,
  32. 0x01,
  33. 0x32,
  34. 0x00,
  35. 0x00,
  36. 0x03,
  37. 0x00,
  38. 0x00,
  39. 0x00,
  40. 0x80,
  41. 0xFF,
  42. 0x14,
  43. 0xC8,
  44. 0x01,
  45. 0x00,
  46. 0x01,
  47. 0x00,
  48. }),
  49. oscar.NewTLV(0x05, uint16(200)),
  50. oscar.NewTLV(0x06, uint16(200)),
  51. oscar.NewTLV(0x07, uint16(200)),
  52. oscar.NewTLV(0x08, uint16(200)),
  53. oscar.NewTLV(0x09, uint16(200)),
  54. oscar.NewTLV(0x0A, uint16(200)),
  55. oscar.NewTLV(0x0C, uint16(200)),
  56. oscar.NewTLV(0x0D, uint16(200)),
  57. oscar.NewTLV(0x0E, uint16(100)),
  58. },
  59. },
  60. },
  61. }
  62. }
  63. func (s FeedbagService) QueryHandler(_ context.Context, sess *state.Session, inFrame oscar.SNACFrame) (oscar.SNACMessage, error) {
  64. fb, err := s.feedbagManager.Retrieve(sess.ScreenName())
  65. if err != nil {
  66. return oscar.SNACMessage{}, err
  67. }
  68. lm := time.UnixMilli(0)
  69. if len(fb) > 0 {
  70. lm, err = s.feedbagManager.LastModified(sess.ScreenName())
  71. if err != nil {
  72. return oscar.SNACMessage{}, err
  73. }
  74. }
  75. return oscar.SNACMessage{
  76. Frame: oscar.SNACFrame{
  77. FoodGroup: oscar.Feedbag,
  78. SubGroup: oscar.FeedbagReply,
  79. RequestID: inFrame.RequestID,
  80. },
  81. Body: oscar.SNAC_0x13_0x06_FeedbagReply{
  82. Version: 0,
  83. Items: fb,
  84. LastUpdate: uint32(lm.Unix()),
  85. },
  86. }, nil
  87. }
  88. func (s FeedbagService) QueryIfModifiedHandler(_ context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x13_0x05_FeedbagQueryIfModified) (oscar.SNACMessage, error) {
  89. fb, err := s.feedbagManager.Retrieve(sess.ScreenName())
  90. if err != nil {
  91. return oscar.SNACMessage{}, err
  92. }
  93. lm := time.UnixMilli(0)
  94. if len(fb) > 0 {
  95. lm, err = s.feedbagManager.LastModified(sess.ScreenName())
  96. if err != nil {
  97. return oscar.SNACMessage{}, err
  98. }
  99. if lm.Before(time.Unix(int64(inBody.LastUpdate), 0)) {
  100. return oscar.SNACMessage{
  101. Frame: oscar.SNACFrame{
  102. FoodGroup: oscar.Feedbag,
  103. SubGroup: oscar.FeedbagReplyNotModified,
  104. RequestID: inFrame.RequestID,
  105. },
  106. Body: oscar.SNAC_0x13_0x05_FeedbagQueryIfModified{
  107. LastUpdate: uint32(lm.Unix()),
  108. Count: uint8(len(fb)),
  109. },
  110. }, nil
  111. }
  112. }
  113. return oscar.SNACMessage{
  114. Frame: oscar.SNACFrame{
  115. FoodGroup: oscar.Feedbag,
  116. SubGroup: oscar.FeedbagReply,
  117. RequestID: inFrame.RequestID,
  118. },
  119. Body: oscar.SNAC_0x13_0x06_FeedbagReply{
  120. Version: 0,
  121. Items: fb,
  122. LastUpdate: uint32(lm.Unix()),
  123. },
  124. }, nil
  125. }
  126. func (s FeedbagService) InsertItemHandler(ctx context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x13_0x08_FeedbagInsertItem) (oscar.SNACMessage, error) {
  127. for _, item := range inBody.Items {
  128. // don't let users block themselves, it causes the AIM client to go
  129. // into a weird state.
  130. if item.ClassID == 3 && item.Name == sess.ScreenName() {
  131. return oscar.SNACMessage{
  132. Frame: oscar.SNACFrame{
  133. FoodGroup: oscar.Feedbag,
  134. SubGroup: oscar.FeedbagErr,
  135. RequestID: inFrame.RequestID,
  136. },
  137. Body: oscar.SNACError{
  138. Code: oscar.ErrorCodeNotSupportedByHost,
  139. },
  140. }, nil
  141. }
  142. }
  143. if err := s.feedbagManager.Upsert(sess.ScreenName(), inBody.Items); err != nil {
  144. return oscar.SNACMessage{}, nil
  145. }
  146. for _, item := range inBody.Items {
  147. switch item.ClassID {
  148. case oscar.FeedbagClassIdBuddy, oscar.FeedbagClassIDPermit: // add new buddy
  149. unicastArrival(ctx, item.Name, sess.ScreenName(), s.messageRelayer)
  150. case oscar.FeedbagClassIDDeny: // block buddy
  151. // notify this user that buddy is offline
  152. unicastDeparture(ctx, item.Name, sess.ScreenName(), s.messageRelayer)
  153. // notify former buddy that this user is offline
  154. unicastDeparture(ctx, sess.ScreenName(), item.Name, s.messageRelayer)
  155. }
  156. }
  157. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  158. for range inBody.Items {
  159. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000)
  160. }
  161. return oscar.SNACMessage{
  162. Frame: oscar.SNACFrame{
  163. FoodGroup: oscar.Feedbag,
  164. SubGroup: oscar.FeedbagStatus,
  165. RequestID: inFrame.RequestID,
  166. },
  167. Body: snacPayloadOut,
  168. }, nil
  169. }
  170. func (s FeedbagService) UpdateItemHandler(ctx context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x13_0x09_FeedbagUpdateItem) (oscar.SNACMessage, error) {
  171. if err := s.feedbagManager.Upsert(sess.ScreenName(), inBody.Items); err != nil {
  172. return oscar.SNACMessage{}, nil
  173. }
  174. for _, item := range inBody.Items {
  175. switch item.ClassID {
  176. case oscar.FeedbagClassIdBuddy, oscar.FeedbagClassIDPermit:
  177. unicastArrival(ctx, item.Name, sess.ScreenName(), s.messageRelayer)
  178. }
  179. }
  180. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  181. for range inBody.Items {
  182. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000)
  183. }
  184. return oscar.SNACMessage{
  185. Frame: oscar.SNACFrame{
  186. FoodGroup: oscar.Feedbag,
  187. SubGroup: oscar.FeedbagStatus,
  188. RequestID: inFrame.RequestID,
  189. },
  190. Body: snacPayloadOut,
  191. }, nil
  192. }
  193. func (s FeedbagService) DeleteItemHandler(ctx context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x13_0x0A_FeedbagDeleteItem) (oscar.SNACMessage, error) {
  194. if err := s.feedbagManager.Delete(sess.ScreenName(), inBody.Items); err != nil {
  195. return oscar.SNACMessage{}, err
  196. }
  197. for _, item := range inBody.Items {
  198. if item.ClassID == oscar.FeedbagClassIDDeny {
  199. unicastArrival(ctx, item.Name, sess.ScreenName(), s.messageRelayer)
  200. unicastArrival(ctx, sess.ScreenName(), item.Name, s.messageRelayer)
  201. }
  202. }
  203. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  204. for range inBody.Items {
  205. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000) // success by default
  206. }
  207. return oscar.SNACMessage{
  208. Frame: oscar.SNACFrame{
  209. FoodGroup: oscar.Feedbag,
  210. SubGroup: oscar.FeedbagStatus,
  211. RequestID: inFrame.RequestID,
  212. },
  213. Body: snacPayloadOut,
  214. }, nil
  215. }
  216. // StartClusterHandler exists to capture the SNAC input in unit tests to verify
  217. // it's correctly unmarshalled.
  218. func (s FeedbagService) StartClusterHandler(context.Context, oscar.SNACFrame, oscar.SNAC_0x13_0x11_FeedbagStartCluster) {
  219. }