feedbag.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. package server
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/mkaminski/goaim/oscar"
  6. "io"
  7. "time"
  8. )
  9. const (
  10. FeedbagErr uint16 = 0x0001
  11. FeedbagRightsQuery = 0x0002
  12. FeedbagQuery = 0x0004
  13. FeedbagQueryIfModified = 0x0005
  14. FeedbagReply = 0x0006
  15. FeedbagUse = 0x0007
  16. FeedbagInsertItem = 0x0008
  17. FeedbagUpdateItem = 0x0009
  18. FeedbagDeleteItem = 0x000A
  19. FeedbagInsertClass = 0x000B
  20. FeedbagUpdateClass = 0x000C
  21. FeedbagDeleteClass = 0x000D
  22. FeedbagStatus = 0x000E
  23. FeedbagReplyNotModified = 0x000F
  24. FeedbagDeleteUser = 0x0010
  25. FeedbagStartCluster = 0x0011
  26. FeedbagEndCluster = 0x0012
  27. FeedbagAuthorizeBuddy = 0x0013
  28. FeedbagPreAuthorizeBuddy = 0x0014
  29. FeedbagPreAuthorizedBuddy = 0x0015
  30. FeedbagRemoveMe = 0x0016
  31. FeedbagRemoveMe2 = 0x0017
  32. FeedbagRequestAuthorizeToHost = 0x0018
  33. FeedbagRequestAuthorizeToClient = 0x0019
  34. FeedbagRespondAuthorizeToHost = 0x001A
  35. FeedbagRespondAuthorizeToClient = 0x001B
  36. FeedbagBuddyAdded = 0x001C
  37. FeedbagRequestAuthorizeToBadog = 0x001D
  38. FeedbagRespondAuthorizeToBadog = 0x001E
  39. FeedbagBuddyAddedToBadog = 0x001F
  40. FeedbagTestSnac = 0x0021
  41. FeedbagForwardMsg = 0x0022
  42. FeedbagIsAuthRequiredQuery = 0x0023
  43. FeedbagIsAuthRequiredReply = 0x0024
  44. FeedbagRecentBuddyUpdate = 0x0025
  45. )
  46. const (
  47. FeedbagAttributesShared uint16 = 0x0064
  48. FeedbagAttributesInvited = 0x0065
  49. FeedbagAttributesPending = 0x0066
  50. FeedbagAttributesTimeT = 0x0067
  51. FeedbagAttributesDenied = 0x0068
  52. FeedbagAttributesSwimIndex = 0x0069
  53. FeedbagAttributesRecentBuddy = 0x006A
  54. FeedbagAttributesAutoBot = 0x006B
  55. FeedbagAttributesInteraction = 0x006D
  56. FeedbagAttributesMegaBot = 0x006F
  57. FeedbagAttributesOrder = 0x00C8
  58. FeedbagAttributesBuddyPrefs = 0x00C9
  59. FeedbagAttributesPdMode = 0x00CA
  60. FeedbagAttributesPdMask = 0x00CB
  61. FeedbagAttributesPdFlags = 0x00CC
  62. FeedbagAttributesClientPrefs = 0x00CD
  63. FeedbagAttributesLanguage = 0x00CE
  64. FeedbagAttributesFishUri = 0x00CF
  65. FeedbagAttributesWirelessPdMode = 0x00D0
  66. FeedbagAttributesWirelessIgnoreMode = 0x00D1
  67. FeedbagAttributesFishPdMode = 0x00D2
  68. FeedbagAttributesFishIgnoreMode = 0x00D3
  69. FeedbagAttributesCreateTime = 0x00D4
  70. FeedbagAttributesBartInfo = 0x00D5
  71. FeedbagAttributesBuddyPrefsValid = 0x00D6
  72. FeedbagAttributesBuddyPrefs2 = 0x00D7
  73. FeedbagAttributesBuddyPrefs2Valid = 0x00D8
  74. FeedbagAttributesBartList = 0x00D9
  75. FeedbagAttributesArriveSound = 0x012C
  76. FeedbagAttributesLeaveSound = 0x012D
  77. FeedbagAttributesImage = 0x012E
  78. FeedbagAttributesColorBg = 0x012F
  79. FeedbagAttributesColorFg = 0x0130
  80. FeedbagAttributesAlias = 0x0131
  81. FeedbagAttributesPassword = 0x0132
  82. FeedbagAttributesDisabled = 0x0133
  83. FeedbagAttributesCollapsed = 0x0134
  84. FeedbagAttributesUrl = 0x0135
  85. FeedbagAttributesActiveList = 0x0136
  86. FeedbagAttributesEmailAddr = 0x0137
  87. FeedbagAttributesPhoneNumber = 0x0138
  88. FeedbagAttributesCellPhoneNumber = 0x0139
  89. FeedbagAttributesSmsPhoneNumber = 0x013A
  90. FeedbagAttributesWireless = 0x013B
  91. FeedbagAttributesNote = 0x013C
  92. FeedbagAttributesAlertPrefs = 0x013D
  93. FeedbagAttributesBudalertSound = 0x013E
  94. FeedbagAttributesStockalertValue = 0x013F
  95. FeedbagAttributesTpalertEditUrl = 0x0140
  96. FeedbagAttributesTpalertDeleteUrl = 0x0141
  97. FeedbagAttributesTpprovMorealertsUrl = 0x0142
  98. FeedbagAttributesFish = 0x0143
  99. FeedbagAttributesXunconfirmedxLastAccess = 0x0145
  100. FeedbagAttributesImSent = 0x0150
  101. FeedbagAttributesOnlineTime = 0x0151
  102. FeedbagAttributesAwayMsg = 0x0152
  103. FeedbagAttributesImReceived = 0x0153
  104. FeedbagAttributesBuddyfeedView = 0x0154
  105. FeedbagAttributesWorkPhoneNumber = 0x0158
  106. FeedbagAttributesOtherPhoneNumber = 0x0159
  107. FeedbagAttributesWebPdMode = 0x015F
  108. FeedbagAttributesFirstCreationTimeXc = 0x0167
  109. FeedbagAttributesPdModeXc = 0x016E
  110. )
  111. const (
  112. FeedbagClassIdBuddy uint16 = 0x0000
  113. FeedbagClassIdGroup = 0x0001
  114. FeedbagClassIdPermit = 0x0002
  115. FeedbagClassIdDeny = 0x0003
  116. FeedbagClassIdPdinfo = 0x0004
  117. FeedbagClassIdBuddyPrefs = 0x0005
  118. FeedbagClassIdNonbuddy = 0x0006
  119. FeedbagClassIdTpaProvider = 0x0007
  120. FeedbagClassIdTpaSubscription = 0x0008
  121. FeedbagClassIdClientPrefs = 0x0009
  122. FeedbagClassIdStock = 0x000A
  123. FeedbagClassIdWeather = 0x000B
  124. FeedbagClassIdWatchList = 0x000D
  125. FeedbagClassIdIgnoreList = 0x000E
  126. FeedbagClassIdDateTime = 0x000F
  127. FeedbagClassIdExternalUser = 0x0010
  128. FeedbagClassIdRootCreator = 0x0011
  129. FeedbagClassIdFish = 0x0012
  130. FeedbagClassIdImportTimestamp = 0x0013
  131. FeedbagClassIdBart = 0x0014
  132. FeedbagClassIdRbOrder = 0x0015
  133. FeedbagClassIdPersonality = 0x0016
  134. FeedbagClassIdAlProf = 0x0017
  135. FeedbagClassIdAlInfo = 0x0018
  136. FeedbagClassIdInteraction = 0x0019
  137. FeedbagClassIdVanityInfo = 0x001D
  138. FeedbagClassIdFavoriteLocation = 0x001E
  139. FeedbagClassIdBartPdinfo = 0x001F
  140. FeedbagClassIdCustomEmoticons = 0x0024
  141. FeedbagClassIdMaxPredefined = 0x0024
  142. FeedbagClassIdXIcqStatusNote = 0x015C
  143. FeedbagClassIdMin = 0x0400
  144. )
  145. func routeFeedbag(sm SessionManager, sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  146. switch snac.SubGroup {
  147. case FeedbagRightsQuery:
  148. return SendAndReceiveFeedbagRightsQuery(snac, r, w, sequence)
  149. case FeedbagQuery:
  150. return ReceiveAndSendFeedbagQuery(sess, fm, snac, w, sequence)
  151. case FeedbagQueryIfModified:
  152. return ReceiveAndSendFeedbagQueryIfModified(sess, fm, snac, r, w, sequence)
  153. case FeedbagUse:
  154. return ReceiveUse(snac, r, w, sequence)
  155. case FeedbagInsertItem:
  156. return ReceiveInsertItem(sm, sess, fm, snac, r, w, sequence)
  157. case FeedbagUpdateItem:
  158. return ReceiveUpdateItem(sm, sess, fm, snac, r, w, sequence)
  159. case FeedbagDeleteItem:
  160. return ReceiveDeleteItem(sm, sess, fm, snac, r, w, sequence)
  161. case FeedbagStartCluster:
  162. return ReceiveFeedbagStartCluster(snac, r, w, sequence)
  163. case FeedbagEndCluster:
  164. return ReceiveFeedbagEndCluster(snac, r, w, sequence)
  165. default:
  166. return ErrUnsupportedSubGroup
  167. }
  168. }
  169. func SendAndReceiveFeedbagRightsQuery(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  170. fmt.Printf("sendAndReceiveFeedbagRightsQuery read SNAC frame: %+v\n", snac)
  171. snacPayloadIn := oscar.SNAC_0x13_0x02_FeedbagRightsQuery{}
  172. if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
  173. return err
  174. }
  175. fmt.Printf("sendAndReceiveFeedbagRightsQuery read SNAC payload: %+v\n", snacPayloadIn)
  176. snacFrameOut := oscar.SnacFrame{
  177. FoodGroup: 0x13,
  178. SubGroup: 0x03,
  179. }
  180. snacPayloadOut := oscar.SNAC_0x13_0x03_FeedbagRightsReply{
  181. TLVRestBlock: oscar.TLVRestBlock{
  182. TLVList: oscar.TLVList{
  183. {
  184. TType: 0x03,
  185. Val: uint16(200),
  186. },
  187. {
  188. TType: 0x04,
  189. Val: []uint16{
  190. 0x3D,
  191. 0x3D,
  192. 0x64,
  193. 0x64,
  194. 0x01,
  195. 0x01,
  196. 0x32,
  197. 0x00,
  198. 0x00,
  199. 0x03,
  200. 0x00,
  201. 0x00,
  202. 0x00,
  203. 0x80,
  204. 0xFF,
  205. 0x14,
  206. 0xC8,
  207. 0x01,
  208. 0x00,
  209. 0x01,
  210. 0x00,
  211. },
  212. },
  213. {
  214. TType: 0x05,
  215. Val: uint16(200),
  216. },
  217. {
  218. TType: 0x06,
  219. Val: uint16(200),
  220. },
  221. {
  222. TType: 0x07,
  223. Val: uint16(200),
  224. },
  225. {
  226. TType: 0x08,
  227. Val: uint16(200),
  228. },
  229. {
  230. TType: 0x09,
  231. Val: uint16(200),
  232. },
  233. {
  234. TType: 0x0A,
  235. Val: uint16(200),
  236. },
  237. {
  238. TType: 0x0C,
  239. Val: uint16(200),
  240. },
  241. {
  242. TType: 0x0D,
  243. Val: uint16(200),
  244. },
  245. {
  246. TType: 0x0E,
  247. Val: uint16(100),
  248. },
  249. },
  250. },
  251. }
  252. return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
  253. }
  254. func ReceiveAndSendFeedbagQuery(sess *Session, fm FeedbagManager, snac oscar.SnacFrame, w io.Writer, sequence *uint32) error {
  255. fb, err := fm.Retrieve(sess.ScreenName)
  256. if err != nil {
  257. return err
  258. }
  259. lm := time.UnixMilli(0)
  260. if len(fb) > 0 {
  261. lm, err = fm.LastModified(sess.ScreenName)
  262. if err != nil {
  263. return err
  264. }
  265. }
  266. snacFrameOut := oscar.SnacFrame{
  267. FoodGroup: FEEDBAG,
  268. SubGroup: FeedbagReply,
  269. }
  270. snacPayloadOut := oscar.SNAC_0x13_0x06_FeedbagReply{
  271. Version: 0,
  272. Items: fb,
  273. LastUpdate: uint32(lm.Unix()),
  274. }
  275. return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
  276. }
  277. func ReceiveAndSendFeedbagQueryIfModified(sess *Session, fm FeedbagManager, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  278. snacPayloadIn := oscar.SNAC_0x13_0x05_FeedbagQueryIfModified{}
  279. if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
  280. return err
  281. }
  282. fb, err := fm.Retrieve(sess.ScreenName)
  283. if err != nil {
  284. return err
  285. }
  286. lm := time.UnixMilli(0)
  287. if len(fb) > 0 {
  288. lm, err = fm.LastModified(sess.ScreenName)
  289. if err != nil {
  290. return err
  291. }
  292. if lm.Before(time.Unix(int64(snacPayloadIn.LastUpdate), 0)) {
  293. snacFrameOut := oscar.SnacFrame{
  294. FoodGroup: FEEDBAG,
  295. SubGroup: FeedbagReplyNotModified,
  296. }
  297. snacPayloadOut := oscar.SNAC_0x13_0x05_FeedbagQueryIfModified{
  298. LastUpdate: uint32(lm.Unix()),
  299. Count: uint8(len(fb)),
  300. }
  301. return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
  302. }
  303. }
  304. snacFrameOut := oscar.SnacFrame{
  305. FoodGroup: FEEDBAG,
  306. SubGroup: FeedbagReply,
  307. }
  308. snacPayloadOut := oscar.SNAC_0x13_0x06_FeedbagReply{
  309. Version: 0,
  310. Items: fb,
  311. LastUpdate: uint32(lm.Unix()),
  312. }
  313. return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
  314. }
  315. func ReceiveInsertItem(sm SessionManager, sess *Session, fm FeedbagManager, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  316. snacPayloadIn := oscar.SNAC_0x13_0x08_FeedbagInsertItem{}
  317. if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
  318. return err
  319. }
  320. for _, item := range snacPayloadIn.Items {
  321. // don't let users block themselves, it causes the AIM client to go
  322. // into a weird state.
  323. if item.ClassID == 3 && item.Name == sess.ScreenName {
  324. snacFrameOut := oscar.SnacFrame{
  325. FoodGroup: FEEDBAG,
  326. SubGroup: FeedbagErr,
  327. }
  328. snacPayloadOut := oscar.SnacError{
  329. Code: ErrorCodeNotSupportedByHost,
  330. }
  331. return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
  332. }
  333. }
  334. if err := fm.Upsert(sess.ScreenName, snacPayloadIn.Items); err != nil {
  335. return err
  336. }
  337. snacFrameOut := oscar.SnacFrame{
  338. FoodGroup: FEEDBAG,
  339. SubGroup: FeedbagStatus,
  340. }
  341. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  342. for range snacPayloadIn.Items {
  343. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000)
  344. }
  345. if err := writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w); err != nil {
  346. return err
  347. }
  348. for _, item := range snacPayloadIn.Items {
  349. switch item.ClassID {
  350. case 2:
  351. // notify that added buddy is online
  352. if err := NotifyBuddyOnline(w, item.Name, sm, sequence); err != nil {
  353. return err
  354. }
  355. case 3:
  356. // DENY, block buddy
  357. if err := blockBuddy(sm, sess, item.Name, sequence, w); err != nil {
  358. return err
  359. }
  360. }
  361. }
  362. return nil
  363. }
  364. func blockBuddy(sm SessionManager, sess *Session, screenName string, sequence *uint32, w io.Writer) error {
  365. buddySess, err := sm.RetrieveByScreenName(screenName)
  366. if err != nil {
  367. if errors.Is(err, errSessNotFound) {
  368. // former buddy is offline
  369. return nil
  370. }
  371. return err
  372. }
  373. // tell the blocked buddy you've signed off
  374. sm.SendToScreenName(screenName, XMessage{
  375. snacFrame: oscar.SnacFrame{
  376. FoodGroup: BUDDY,
  377. SubGroup: BuddyDeparted,
  378. },
  379. snacOut: oscar.SNAC_0x03_0x0A_BuddyArrived{
  380. TLVUserInfo: oscar.TLVUserInfo{
  381. ScreenName: sess.ScreenName,
  382. WarningLevel: sess.GetWarning(),
  383. },
  384. },
  385. })
  386. // tell yourself blocked buddy has signed off
  387. snacFrameOut := oscar.SnacFrame{
  388. FoodGroup: BUDDY,
  389. SubGroup: BuddyDeparted,
  390. }
  391. snacPayloadOut := oscar.SNAC_0x03_0x0A_BuddyArrived{
  392. TLVUserInfo: oscar.TLVUserInfo{
  393. ScreenName: buddySess.ScreenName,
  394. WarningLevel: buddySess.GetWarning(),
  395. },
  396. }
  397. return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
  398. }
  399. func ReceiveUpdateItem(sm SessionManager, sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  400. fmt.Printf("ReceiveUpdateItem read SNAC frame: %+v\n", snac)
  401. snacPayloadIn := oscar.SNAC_0x13_0x09_FeedbagUpdateItem{}
  402. if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
  403. return err
  404. }
  405. if err := fm.Upsert(sess.ScreenName, snacPayloadIn.Items); err != nil {
  406. return err
  407. }
  408. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  409. for _, item := range snacPayloadIn.Items {
  410. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000) // success by default
  411. fmt.Printf("ReceiveUpdateItem read SNAC feedbag item: %+v\n", item)
  412. }
  413. snacFrameOut := oscar.SnacFrame{
  414. FoodGroup: FEEDBAG,
  415. SubGroup: FeedbagStatus,
  416. }
  417. if err := writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w); err != nil {
  418. return err
  419. }
  420. return GetAllOnlineBuddies(w, sess, sm, fm, sequence)
  421. }
  422. func ReceiveDeleteItem(sm SessionManager, sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  423. fmt.Printf("ReceiveUpdateItem read SNAC frame: %+v\n", snac)
  424. snacPayloadIn := oscar.SNAC_0x13_0x0A_FeedbagDeleteItem{}
  425. if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
  426. return err
  427. }
  428. if err := fm.Delete(sess.ScreenName, snacPayloadIn.Items); err != nil {
  429. return err
  430. }
  431. snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
  432. hasUnblock := false
  433. for _, item := range snacPayloadIn.Items {
  434. if item.ClassID == 3 {
  435. hasUnblock = true
  436. }
  437. snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000) // success by default
  438. fmt.Printf("ReceiveDeleteItem read SNAC feedbag item: %+v\n", item)
  439. }
  440. if hasUnblock {
  441. // notify previously blocked users that user is back online
  442. if err := NotifyArrival(sess, sm, fm); err != nil {
  443. return err
  444. }
  445. }
  446. snacFrameOut := oscar.SnacFrame{
  447. FoodGroup: FEEDBAG,
  448. SubGroup: FeedbagStatus,
  449. }
  450. return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
  451. }
  452. func ReceiveFeedbagStartCluster(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  453. fmt.Printf("ReceiveFeedbagStartCluster read SNAC frame: %+v\n", snac)
  454. tlv := oscar.TLVRestBlock{}
  455. return tlv.Read(r)
  456. }
  457. func ReceiveFeedbagEndCluster(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  458. fmt.Printf("receiveFeedbagEndCluster read SNAC frame: %+v\n", snac)
  459. return nil
  460. }
  461. func ReceiveUse(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  462. fmt.Printf("ReceiveUse read SNAC frame: %+v\n", snac)
  463. return nil
  464. }