icq.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. package handler
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "log/slog"
  10. "github.com/mk6i/retro-aim-server/server/oscar"
  11. "github.com/mk6i/retro-aim-server/server/oscar/middleware"
  12. "github.com/mk6i/retro-aim-server/state"
  13. "github.com/mk6i/retro-aim-server/wire"
  14. )
  15. type ICQService interface {
  16. DeleteMsgReq(ctx context.Context, sess *state.Session, seq uint16) error
  17. FindByICQName(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails, seq uint16) error
  18. FindByICQEmail(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail, seq uint16) error
  19. FindByEmail3(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16) error
  20. FindByICQInterests(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages, seq uint16) error
  21. FindByUIN(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error
  22. FindByUIN2(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16) error
  23. FindByWhitePages2(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16) error
  24. FullUserInfo(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error
  25. OfflineMsgReq(ctx context.Context, sess *state.Session, seq uint16) error
  26. SetAffiliations(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations, seq uint16) error
  27. SetBasicInfo(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo, seq uint16) error
  28. SetEmails(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x040B_DBQueryMetaReqSetEmails, seq uint16) error
  29. SetInterests(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests, seq uint16) error
  30. SetMoreInfo(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo, seq uint16) error
  31. SetPermissions(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions, seq uint16) error
  32. SetUserNotes(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes, seq uint16) error
  33. SetWorkInfo(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo, seq uint16) error
  34. ShortUserInfo(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x04BA_DBQueryMetaReqShortInfo, seq uint16) error
  35. XMLReqData(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq, seq uint16) error
  36. }
  37. var (
  38. errUnknownICQMetaReqType = errors.New("unknown ICQ request type")
  39. errUnknownICQMetaReqSubType = errors.New("unknown ICQ metadata request subtype")
  40. )
  41. func NewICQHandler(logger *slog.Logger, ICQService ICQService) ICQHandler {
  42. return ICQHandler{
  43. RouteLogger: middleware.RouteLogger{
  44. Logger: logger,
  45. },
  46. ICQService: ICQService,
  47. }
  48. }
  49. type ICQHandler struct {
  50. ICQService
  51. middleware.RouteLogger
  52. }
  53. func (rt ICQHandler) DBQuery(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, r io.Reader, rw oscar.ResponseWriter) error {
  54. inBody := wire.SNAC_0x15_0x02_BQuery{}
  55. if err := wire.UnmarshalBE(&inBody, r); err != nil {
  56. return err
  57. }
  58. md, ok := inBody.Bytes(wire.ICQTLVTagsMetadata)
  59. if !ok {
  60. return errors.New("invalid ICQ frame")
  61. }
  62. icqChunk := wire.ICQMessageRequestEnvelope{}
  63. if err := wire.UnmarshalLE(&icqChunk, bytes.NewBuffer(md)); err != nil {
  64. return err
  65. }
  66. buf := bytes.NewBuffer(icqChunk.Body)
  67. icqMD := wire.ICQMetadataWithSubType{}
  68. if err := wire.UnmarshalLE(&icqMD, buf); err != nil {
  69. return err
  70. }
  71. switch icqMD.ReqType {
  72. case wire.ICQDBQueryOfflineMsgReq:
  73. return rt.ICQService.OfflineMsgReq(ctx, sess, icqMD.Seq)
  74. case wire.ICQDBQueryDeleteMsgReq:
  75. return rt.ICQService.DeleteMsgReq(ctx, sess, icqMD.Seq)
  76. case wire.ICQDBQueryMetaReq:
  77. if icqMD.Optional == nil {
  78. return errors.New("got req without subtype")
  79. }
  80. rt.Logger.Debug("ICQ client request",
  81. "query_name", wire.ICQDBQueryName(icqMD.ReqType),
  82. "query_type", wire.ICQDBQueryMetaName(icqMD.Optional.ReqSubType),
  83. "uin", sess.UIN())
  84. switch icqMD.Optional.ReqSubType {
  85. case wire.ICQDBQueryMetaReqShortInfo:
  86. userInfo := wire.ICQ_0x07D0_0x04BA_DBQueryMetaReqShortInfo{}
  87. if err := binary.Read(buf, binary.LittleEndian, &userInfo); err != nil {
  88. return nil
  89. }
  90. return rt.ICQService.ShortUserInfo(ctx, sess, userInfo, icqMD.Seq)
  91. case wire.ICQDBQueryMetaReqFullInfo, wire.ICQDBQueryMetaReqFullInfo2:
  92. userInfo := wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{}
  93. if err := binary.Read(buf, binary.LittleEndian, &userInfo); err != nil {
  94. return nil
  95. }
  96. return rt.ICQService.FullUserInfo(ctx, sess, userInfo, icqMD.Seq)
  97. case wire.ICQDBQueryMetaReqXMLReq:
  98. req := wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq{}
  99. if err := wire.UnmarshalLE(&req, buf); err != nil {
  100. return err
  101. }
  102. if err := rt.ICQService.XMLReqData(ctx, sess, req, icqMD.Seq); err != nil {
  103. return err
  104. }
  105. case wire.ICQDBQueryMetaReqSetPermissions:
  106. req := wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions{}
  107. if err := wire.UnmarshalLE(&req, buf); err != nil {
  108. return err
  109. }
  110. if err := rt.ICQService.SetPermissions(ctx, sess, req, icqMD.Seq); err != nil {
  111. return err
  112. }
  113. case wire.ICQDBQueryMetaReqSearchByUIN:
  114. req := wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{}
  115. if err := wire.UnmarshalLE(&req, buf); err != nil {
  116. return err
  117. }
  118. if err := rt.ICQService.FindByUIN(ctx, sess, req, icqMD.Seq); err != nil {
  119. return err
  120. }
  121. case wire.ICQDBQueryMetaReqSearchByUIN2:
  122. rest := buf.Bytes()
  123. if bytes.HasPrefix(rest, []byte{0x36, 0x01, 0x06, 0x00}) && len(rest) == 8 {
  124. // fix incorrect TLV len set by QIP 2005. it specifies len=6
  125. // for a 4-byte value, causing the unmarshaler to return EOF.
  126. rest[2] = 4
  127. }
  128. req := wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{}
  129. if err := wire.UnmarshalLE(&req, bytes.NewReader(rest)); err != nil {
  130. return err
  131. }
  132. if err := rt.ICQService.FindByUIN2(ctx, sess, req, icqMD.Seq); err != nil {
  133. return err
  134. }
  135. case wire.ICQDBQueryMetaReqSearchByEmail:
  136. req := wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail{}
  137. if err := wire.UnmarshalLE(&req, buf); err != nil {
  138. return err
  139. }
  140. if err := rt.ICQService.FindByICQEmail(ctx, sess, req, icqMD.Seq); err != nil {
  141. return err
  142. }
  143. case wire.ICQDBQueryMetaReqSearchByEmail3:
  144. req := wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3{}
  145. if err := wire.UnmarshalLE(&req, buf); err != nil {
  146. return err
  147. }
  148. if err := rt.ICQService.FindByEmail3(ctx, sess, req, icqMD.Seq); err != nil {
  149. return err
  150. }
  151. case wire.ICQDBQueryMetaReqSearchByDetails:
  152. req := wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails{}
  153. if err := wire.UnmarshalLE(&req, buf); err != nil {
  154. return err
  155. }
  156. if err := rt.ICQService.FindByICQName(ctx, sess, req, icqMD.Seq); err != nil {
  157. return err
  158. }
  159. case wire.ICQDBQueryMetaReqSearchWhitePages:
  160. req := wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages{}
  161. if err := wire.UnmarshalLE(&req, buf); err != nil {
  162. return err
  163. }
  164. if err := rt.ICQService.FindByICQInterests(ctx, sess, req, icqMD.Seq); err != nil {
  165. return err
  166. }
  167. case wire.ICQDBQueryMetaReqSearchWhitePages2:
  168. req := wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2{}
  169. if err := wire.UnmarshalLE(&req, buf); err != nil {
  170. return err
  171. }
  172. if err := rt.ICQService.FindByWhitePages2(ctx, sess, req, icqMD.Seq); err != nil {
  173. return err
  174. }
  175. case wire.ICQDBQueryMetaReqSetBasicInfo:
  176. req := wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo{}
  177. if err := wire.UnmarshalLE(&req, buf); err != nil {
  178. return err
  179. }
  180. if err := rt.ICQService.SetBasicInfo(ctx, sess, req, icqMD.Seq); err != nil {
  181. return err
  182. }
  183. case wire.ICQDBQueryMetaReqSetWorkInfo:
  184. req := wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo{}
  185. if err := wire.UnmarshalLE(&req, buf); err != nil {
  186. return err
  187. }
  188. if err := rt.ICQService.SetWorkInfo(ctx, sess, req, icqMD.Seq); err != nil {
  189. return err
  190. }
  191. case wire.ICQDBQueryMetaReqSetMoreInfo:
  192. req := wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo{}
  193. if err := wire.UnmarshalLE(&req, buf); err != nil {
  194. return err
  195. }
  196. if err := rt.ICQService.SetMoreInfo(ctx, sess, req, icqMD.Seq); err != nil {
  197. return err
  198. }
  199. case wire.ICQDBQueryMetaReqSetNotes:
  200. req := wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes{}
  201. if err := wire.UnmarshalLE(&req, buf); err != nil {
  202. return err
  203. }
  204. if err := rt.ICQService.SetUserNotes(ctx, sess, req, icqMD.Seq); err != nil {
  205. return err
  206. }
  207. case wire.ICQDBQueryMetaReqSetEmails:
  208. req := wire.ICQ_0x07D0_0x040B_DBQueryMetaReqSetEmails{}
  209. if err := wire.UnmarshalLE(&req, buf); err != nil {
  210. return err
  211. }
  212. if err := rt.ICQService.SetEmails(ctx, sess, req, icqMD.Seq); err != nil {
  213. return err
  214. }
  215. case wire.ICQDBQueryMetaReqSetInterests:
  216. req := wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests{}
  217. if err := wire.UnmarshalLE(&req, buf); err != nil {
  218. return err
  219. }
  220. if err := rt.ICQService.SetInterests(ctx, sess, req, icqMD.Seq); err != nil {
  221. return err
  222. }
  223. case wire.ICQDBQueryMetaReqSetAffiliations:
  224. req := wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{}
  225. if err := wire.UnmarshalLE(&req, buf); err != nil {
  226. return err
  227. }
  228. if err := rt.ICQService.SetAffiliations(ctx, sess, req, icqMD.Seq); err != nil {
  229. return err
  230. }
  231. case wire.ICQDBQueryMetaReqStat0a8c,
  232. wire.ICQDBQueryMetaReqStat0a96,
  233. wire.ICQDBQueryMetaReqStat0aaa,
  234. wire.ICQDBQueryMetaReqStat0ab4,
  235. wire.ICQDBQueryMetaReqStat0ab9,
  236. wire.ICQDBQueryMetaReqStat0abe,
  237. wire.ICQDBQueryMetaReqStat0ac8,
  238. wire.ICQDBQueryMetaReqStat0acd,
  239. wire.ICQDBQueryMetaReqStat0ad2,
  240. wire.ICQDBQueryMetaReqStat0ad7,
  241. wire.ICQDBQueryMetaReqStat0758:
  242. rt.Logger.Debug("got a request for stats, not doing anything right now")
  243. default:
  244. return fmt.Errorf("%w: %X", errUnknownICQMetaReqSubType, icqMD.Optional.ReqSubType)
  245. }
  246. default:
  247. return fmt.Errorf("%w: %X", errUnknownICQMetaReqType, icqMD.ReqType)
  248. }
  249. return nil
  250. }