feedbag.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. package server
  2. import (
  3. "errors"
  4. "io"
  5. "time"
  6. "github.com/mkaminski/goaim/oscar"
  7. )
  8. type FeedbagHandler interface {
  9. DeleteItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x0A_FeedbagDeleteItem) (XMessage, error)
  10. InsertItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x08_FeedbagInsertItem) (XMessage, error)
  11. QueryHandler(sess *Session, fm FeedbagManager) (XMessage, error)
  12. QueryIfModifiedHandler(sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x05_FeedbagQueryIfModified) (XMessage, error)
  13. RightsQueryHandler() XMessage
  14. StartClusterHandler(oscar.SNAC_0x13_0x11_FeedbagStartCluster)
  15. UpdateItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x09_FeedbagUpdateItem) (XMessage, error)
  16. }
  17. func NewFeedbagRouter() FeedbagRouter {
  18. return FeedbagRouter{
  19. FeedbagHandler: FeedbagService{},
  20. }
  21. }
  22. type FeedbagRouter struct {
  23. FeedbagHandler
  24. }
  25. func (rt FeedbagRouter) RouteFeedbag(sm SessionManager, sess *Session, fm FeedbagManager, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  26. switch snac.SubGroup {
  27. case oscar.FeedbagRightsQuery:
  28. inSNAC := oscar.SNAC_0x13_0x02_FeedbagRightsQuery{}
  29. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  30. return err
  31. }
  32. outSNAC := rt.RightsQueryHandler()
  33. return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  34. case oscar.FeedbagQuery:
  35. inSNAC, err := rt.QueryHandler(sess, fm)
  36. if err != nil {
  37. return err
  38. }
  39. return writeOutSNAC(snac, inSNAC.snacFrame, inSNAC.snacOut, sequence, w)
  40. case oscar.FeedbagQueryIfModified:
  41. inSNAC := oscar.SNAC_0x13_0x05_FeedbagQueryIfModified{}
  42. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  43. return err
  44. }
  45. outSNAC, err := rt.QueryIfModifiedHandler(sess, fm, inSNAC)
  46. if err != nil {
  47. return err
  48. }
  49. return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  50. case oscar.FeedbagUse:
  51. return nil
  52. case oscar.FeedbagInsertItem:
  53. inSNAC := oscar.SNAC_0x13_0x08_FeedbagInsertItem{}
  54. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  55. return err
  56. }
  57. outSNAC, err := rt.InsertItemHandler(sm, sess, fm, inSNAC)
  58. if err != nil {
  59. return err
  60. }
  61. return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  62. case oscar.FeedbagUpdateItem:
  63. inSNAC := oscar.SNAC_0x13_0x09_FeedbagUpdateItem{}
  64. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  65. return err
  66. }
  67. outSNAC, err := rt.UpdateItemHandler(sm, sess, fm, inSNAC)
  68. if err != nil {
  69. return err
  70. }
  71. return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  72. case oscar.FeedbagDeleteItem:
  73. inSNAC := oscar.SNAC_0x13_0x0A_FeedbagDeleteItem{}
  74. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  75. return err
  76. }
  77. outSNAC, err := rt.DeleteItemHandler(sm, sess, fm, inSNAC)
  78. if err != nil {
  79. return err
  80. }
  81. return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  82. case oscar.FeedbagStartCluster:
  83. inSNAC := oscar.SNAC_0x13_0x11_FeedbagStartCluster{}
  84. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  85. return err
  86. }
  87. rt.StartClusterHandler(inSNAC)
  88. return nil
  89. case oscar.FeedbagEndCluster:
  90. return nil
  91. default:
  92. return ErrUnsupportedSubGroup
  93. }
  94. }
  95. type FeedbagService struct {
  96. }
  97. func (s FeedbagService) RightsQueryHandler() XMessage {
  98. return XMessage{
  99. snacFrame: oscar.SnacFrame{
  100. FoodGroup: oscar.FEEDBAG,
  101. SubGroup: oscar.FeedbagRightsReply,
  102. },
  103. snacOut: oscar.SNAC_0x13_0x03_FeedbagRightsReply{
  104. TLVRestBlock: oscar.TLVRestBlock{
  105. TLVList: oscar.TLVList{
  106. {
  107. TType: 0x03,
  108. Val: uint16(200),
  109. },
  110. {
  111. TType: 0x04,
  112. Val: []uint16{
  113. 0x3D,
  114. 0x3D,
  115. 0x64,
  116. 0x64,
  117. 0x01,
  118. 0x01,
  119. 0x32,
  120. 0x00,
  121. 0x00,
  122. 0x03,
  123. 0x00,
  124. 0x00,
  125. 0x00,
  126. 0x80,
  127. 0xFF,
  128. 0x14,
  129. 0xC8,
  130. 0x01,
  131. 0x00,
  132. 0x01,
  133. 0x00,
  134. },
  135. },
  136. {
  137. TType: 0x05,
  138. Val: uint16(200),
  139. },
  140. {
  141. TType: 0x06,
  142. Val: uint16(200),
  143. },
  144. {
  145. TType: 0x07,
  146. Val: uint16(200),
  147. },
  148. {
  149. TType: 0x08,
  150. Val: uint16(200),
  151. },
  152. {
  153. TType: 0x09,
  154. Val: uint16(200),
  155. },
  156. {
  157. TType: 0x0A,
  158. Val: uint16(200),
  159. },
  160. {
  161. TType: 0x0C,
  162. Val: uint16(200),
  163. },
  164. {
  165. TType: 0x0D,
  166. Val: uint16(200),
  167. },
  168. {
  169. TType: 0x0E,
  170. Val: uint16(100),
  171. },
  172. },
  173. },
  174. },
  175. }
  176. }
  177. func (s FeedbagService) QueryHandler(sess *Session, fm FeedbagManager) (XMessage, error) {
  178. fb, err := fm.Retrieve(sess.ScreenName)
  179. if err != nil {
  180. return XMessage{}, err
  181. }
  182. lm := time.UnixMilli(0)
  183. if len(fb) > 0 {
  184. lm, err = fm.LastModified(sess.ScreenName)
  185. if err != nil {
  186. return XMessage{}, err
  187. }
  188. }
  189. return XMessage{
  190. snacFrame: oscar.SnacFrame{
  191. FoodGroup: oscar.FEEDBAG,
  192. SubGroup: oscar.FeedbagReply,
  193. },
  194. snacOut: oscar.SNAC_0x13_0x06_FeedbagReply{
  195. Version: 0,
  196. Items: fb,
  197. LastUpdate: uint32(lm.Unix()),
  198. },
  199. }, nil
  200. }
  201. func (s FeedbagService) QueryIfModifiedHandler(sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x05_FeedbagQueryIfModified) (XMessage, error) {
  202. fb, err := fm.Retrieve(sess.ScreenName)
  203. if err != nil {
  204. return XMessage{}, err
  205. }
  206. lm := time.UnixMilli(0)
  207. if len(fb) > 0 {
  208. lm, err = fm.LastModified(sess.ScreenName)
  209. if err != nil {
  210. return XMessage{}, err
  211. }
  212. if lm.Before(time.Unix(int64(snacPayloadIn.LastUpdate), 0)) {
  213. return XMessage{
  214. snacFrame: oscar.SnacFrame{
  215. FoodGroup: oscar.FEEDBAG,
  216. SubGroup: oscar.FeedbagReplyNotModified,
  217. },
  218. snacOut: oscar.SNAC_0x13_0x05_FeedbagQueryIfModified{
  219. LastUpdate: uint32(lm.Unix()),
  220. Count: uint8(len(fb)),
  221. },
  222. }, nil
  223. }
  224. }
  225. return XMessage{
  226. snacFrame: oscar.SnacFrame{
  227. FoodGroup: oscar.FEEDBAG,
  228. SubGroup: oscar.FeedbagReply,
  229. },
  230. snacOut: oscar.SNAC_0x13_0x06_FeedbagReply{
  231. Version: 0,
  232. Items: fb,
  233. LastUpdate: uint32(lm.Unix()),
  234. },
  235. }, nil
  236. }
  237. func (s FeedbagService) InsertItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x08_FeedbagInsertItem) (XMessage, error) {
  238. for _, item := range snacPayloadIn.Items {
  239. // don't let users block themselves, it causes the AIM client to go
  240. // into a weird state.
  241. if item.ClassID == 3 && item.Name == sess.ScreenName {
  242. return XMessage{
  243. snacFrame: oscar.SnacFrame{
  244. FoodGroup: oscar.FEEDBAG,
  245. SubGroup: oscar.FeedbagErr,
  246. },
  247. snacOut: oscar.SnacError{
  248. Code: ErrorCodeNotSupportedByHost,
  249. },
  250. }, nil
  251. }
  252. }
  253. if err := fm.Upsert(sess.ScreenName, snacPayloadIn.Items); err != nil {
  254. return XMessage{}, nil
  255. }
  256. for _, item := range snacPayloadIn.Items {
  257. switch item.ClassID {
  258. case oscar.FeedbagClassIdBuddy, oscar.FeedbagClassIDPermit: // add new buddy
  259. err := UnicastArrival(item.Name, sess.ScreenName, sm)
  260. switch {
  261. case errors.Is(err, ErrSessNotFound):
  262. continue
  263. case err != nil:
  264. return XMessage{}, err
  265. }
  266. case oscar.FeedbagClassIDDeny: // block buddy
  267. // notify this user that buddy is offline
  268. err := UnicastDeparture(item.Name, sess.ScreenName, sm)
  269. switch {
  270. case errors.Is(err, ErrSessNotFound):
  271. continue
  272. case err != nil:
  273. return XMessage{}, err
  274. }
  275. // notify former buddy that this user is offline
  276. if err := UnicastDeparture(sess.ScreenName, item.Name, sm); err != nil {
  277. return XMessage{}, err
  278. }
  279. }
  280. }
  281. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  282. for range snacPayloadIn.Items {
  283. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000)
  284. }
  285. return XMessage{
  286. snacFrame: oscar.SnacFrame{
  287. FoodGroup: oscar.FEEDBAG,
  288. SubGroup: oscar.FeedbagStatus,
  289. },
  290. snacOut: snacPayloadOut,
  291. }, nil
  292. }
  293. func (s FeedbagService) UpdateItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x09_FeedbagUpdateItem) (XMessage, error) {
  294. if err := fm.Upsert(sess.ScreenName, snacPayloadIn.Items); err != nil {
  295. return XMessage{}, nil
  296. }
  297. for _, item := range snacPayloadIn.Items {
  298. switch item.ClassID {
  299. case oscar.FeedbagClassIdBuddy, oscar.FeedbagClassIDPermit:
  300. err := UnicastArrival(item.Name, sess.ScreenName, sm)
  301. switch {
  302. case errors.Is(err, ErrSessNotFound):
  303. continue
  304. case err != nil:
  305. return XMessage{}, err
  306. }
  307. }
  308. }
  309. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  310. for range snacPayloadIn.Items {
  311. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000)
  312. }
  313. return XMessage{
  314. snacFrame: oscar.SnacFrame{
  315. FoodGroup: oscar.FEEDBAG,
  316. SubGroup: oscar.FeedbagStatus,
  317. },
  318. snacOut: snacPayloadOut,
  319. }, nil
  320. }
  321. func (s FeedbagService) DeleteItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x0A_FeedbagDeleteItem) (XMessage, error) {
  322. if err := fm.Delete(sess.ScreenName, snacPayloadIn.Items); err != nil {
  323. return XMessage{}, err
  324. }
  325. for _, item := range snacPayloadIn.Items {
  326. if item.ClassID == oscar.FeedbagClassIDDeny {
  327. err := UnicastArrival(item.Name, sess.ScreenName, sm)
  328. switch {
  329. case errors.Is(err, ErrSessNotFound):
  330. continue
  331. case err != nil:
  332. return XMessage{}, err
  333. }
  334. err = UnicastArrival(sess.ScreenName, item.Name, sm)
  335. switch {
  336. case errors.Is(err, ErrSessNotFound):
  337. continue
  338. case err != nil:
  339. return XMessage{}, err
  340. }
  341. }
  342. }
  343. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  344. for range snacPayloadIn.Items {
  345. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000) // success by default
  346. }
  347. return XMessage{
  348. snacFrame: oscar.SnacFrame{
  349. FoodGroup: oscar.FEEDBAG,
  350. SubGroup: oscar.FeedbagStatus,
  351. },
  352. snacOut: snacPayloadOut,
  353. }, nil
  354. }
  355. // StartClusterHandler exists to capture the SNAC input in unit tests to verify
  356. // it's correctly unmarshalled.
  357. func (s FeedbagService) StartClusterHandler(oscar.SNAC_0x13_0x11_FeedbagStartCluster) {
  358. }