4
0

oservice.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. package server
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "github.com/mkaminski/goaim/oscar"
  7. "io"
  8. "time"
  9. )
  10. type OServiceHandler interface {
  11. WriteOServiceHostOnline(w io.Writer, sequence *uint32) error
  12. ClientOnlineHandler(snacPayloadIn oscar.SNAC_0x01_0x02_OServiceClientOnline, sess *Session, sm SessionManager, fm FeedbagManager, room ChatRoom) error
  13. ClientVersionsHandler(snacPayloadIn oscar.SNAC_0x01_0x17_OServiceClientVersions) XMessage
  14. IdleNotificationHandler(sess *Session, sm SessionManager, fm *FeedbagStore, snacPayloadIn oscar.SNAC_0x01_0x11_OServiceIdleNotification) error
  15. RateParamsQueryHandler() XMessage
  16. RateParamsSubAddHandler(oscar.SNAC_0x01_0x08_OServiceRateParamsSubAdd)
  17. ServiceRequestHandler(cfg Config, cr *ChatRegistry, sess *Session, snacPayloadIn oscar.SNAC_0x01_0x04_OServiceServiceRequest) (XMessage, error)
  18. SetUserInfoFieldsHandler(sess *Session, sm SessionManager, fm *FeedbagStore, snacPayloadIn oscar.SNAC_0x01_0x1E_OServiceSetUserInfoFields) (XMessage, error)
  19. UserInfoQueryHandler(sess *Session) XMessage
  20. }
  21. func NewOServiceRouter() OServiceRouter {
  22. return OServiceRouter{
  23. OServiceHandler: OServiceService{},
  24. }
  25. }
  26. type OServiceRouter struct {
  27. OServiceHandler
  28. }
  29. func (rt OServiceRouter) RouteOService(cfg Config, cr *ChatRegistry, sm SessionManager, fm *FeedbagStore, sess *Session, room ChatRoom, SNACFrame oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  30. switch SNACFrame.SubGroup {
  31. case oscar.OServiceClientOnline:
  32. inSNAC := oscar.SNAC_0x01_0x02_OServiceClientOnline{}
  33. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  34. return err
  35. }
  36. return rt.ClientOnlineHandler(inSNAC, sess, sm, fm, room)
  37. case oscar.OServiceServiceRequest:
  38. inSNAC := oscar.SNAC_0x01_0x04_OServiceServiceRequest{}
  39. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  40. return err
  41. }
  42. outSNAC, err := rt.ServiceRequestHandler(cfg, cr, sess, inSNAC)
  43. switch {
  44. case errors.Is(err, ErrUnsupportedSubGroup):
  45. return sendInvalidSNACErr(SNACFrame, w, sequence)
  46. case err != nil:
  47. return err
  48. }
  49. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  50. case oscar.OServiceRateParamsQuery:
  51. outSNAC := rt.RateParamsQueryHandler()
  52. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  53. case oscar.OServiceRateParamsSubAdd:
  54. inSNAC := oscar.SNAC_0x01_0x08_OServiceRateParamsSubAdd{}
  55. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  56. return err
  57. }
  58. rt.RateParamsSubAddHandler(inSNAC)
  59. return oscar.Unmarshal(&inSNAC, r)
  60. case oscar.OServiceUserInfoQuery:
  61. outSNAC := rt.UserInfoQueryHandler(sess)
  62. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  63. case oscar.OServiceIdleNotification:
  64. inSNAC := oscar.SNAC_0x01_0x11_OServiceIdleNotification{}
  65. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  66. return err
  67. }
  68. return rt.IdleNotificationHandler(sess, sm, fm, inSNAC)
  69. case oscar.OServiceClientVersions:
  70. inSNAC := oscar.SNAC_0x01_0x17_OServiceClientVersions{}
  71. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  72. return err
  73. }
  74. outSNAC := rt.ClientVersionsHandler(inSNAC)
  75. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  76. case oscar.OServiceSetUserInfoFields:
  77. inSNAC := oscar.SNAC_0x01_0x1E_OServiceSetUserInfoFields{}
  78. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  79. return err
  80. }
  81. outSNAC, err := rt.SetUserInfoFieldsHandler(sess, sm, fm, inSNAC)
  82. if err != nil {
  83. return err
  84. }
  85. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  86. default:
  87. return ErrUnsupportedSubGroup
  88. }
  89. }
  90. type OServiceService struct {
  91. }
  92. func (s OServiceService) WriteOServiceHostOnline(w io.Writer, sequence *uint32) error {
  93. fmt.Println("writeOServiceHostOnline...")
  94. snacFrameOut := oscar.SnacFrame{
  95. FoodGroup: oscar.OSERVICE,
  96. SubGroup: oscar.OServiceHostOnline,
  97. }
  98. snacPayloadOut := oscar.SNAC_0x01_0x03_OServiceHostOnline{
  99. FoodGroups: []uint16{oscar.OSERVICE, oscar.LOCATE, oscar.BUDDY, oscar.ICBM, oscar.FEEDBAG, oscar.CHAT_NAV},
  100. }
  101. return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
  102. }
  103. func (s OServiceService) ClientVersionsHandler(snacPayloadIn oscar.SNAC_0x01_0x17_OServiceClientVersions) XMessage {
  104. return XMessage{
  105. snacFrame: oscar.SnacFrame{
  106. FoodGroup: oscar.OSERVICE,
  107. SubGroup: oscar.OServiceHostVersions,
  108. },
  109. snacOut: oscar.SNAC_0x01_0x18_OServiceHostVersions{
  110. Versions: snacPayloadIn.Versions,
  111. },
  112. }
  113. }
  114. func (s OServiceService) RateParamsQueryHandler() XMessage {
  115. snacFrameOut := oscar.SnacFrame{
  116. FoodGroup: oscar.OSERVICE,
  117. SubGroup: oscar.OServiceRateParamsReply,
  118. }
  119. snacPayloadOut := oscar.SNAC_0x01_0x07_OServiceRateParamsReply{
  120. RateClasses: []struct {
  121. ID uint16
  122. WindowSize uint32
  123. ClearLevel uint32
  124. AlertLevel uint32
  125. LimitLevel uint32
  126. DisconnectLevel uint32
  127. CurrentLevel uint32
  128. MaxLevel uint32
  129. LastTime uint32 // protocol v2 only
  130. CurrentState uint8 // protocol v2 only
  131. }{
  132. {
  133. ID: 0x0001,
  134. WindowSize: 0x00000050,
  135. ClearLevel: 0x000009C4,
  136. AlertLevel: 0x000007D0,
  137. LimitLevel: 0x000005DC,
  138. DisconnectLevel: 0x00000320,
  139. CurrentLevel: 0x00000D69,
  140. MaxLevel: 0x00001770,
  141. LastTime: 0x00000000,
  142. CurrentState: 0x00,
  143. },
  144. },
  145. RateGroups: []struct {
  146. ID uint16
  147. Pairs []struct {
  148. FoodGroup uint16
  149. SubGroup uint16
  150. } `count_prefix:"uint16"`
  151. }{
  152. {
  153. ID: 1,
  154. Pairs: []struct {
  155. FoodGroup uint16
  156. SubGroup uint16
  157. }{},
  158. },
  159. },
  160. }
  161. for i := uint16(0); i < 24; i++ { // for each food group
  162. for j := uint16(0); j < 32; j++ { // for each subgroup
  163. snacPayloadOut.RateGroups[0].Pairs = append(snacPayloadOut.RateGroups[0].Pairs,
  164. struct {
  165. FoodGroup uint16
  166. SubGroup uint16
  167. }{
  168. FoodGroup: i,
  169. SubGroup: j,
  170. })
  171. }
  172. }
  173. return XMessage{
  174. snacFrame: snacFrameOut,
  175. snacOut: snacPayloadOut,
  176. }
  177. }
  178. func (s OServiceService) UserInfoQueryHandler(sess *Session) XMessage {
  179. return XMessage{
  180. snacFrame: oscar.SnacFrame{
  181. FoodGroup: oscar.OSERVICE,
  182. SubGroup: oscar.OServiceUserInfoUpdate,
  183. },
  184. snacOut: oscar.SNAC_0x01_0x0F_OServiceUserInfoUpdate{
  185. TLVUserInfo: sess.GetTLVUserInfo(),
  186. },
  187. }
  188. }
  189. func (s OServiceService) ClientOnlineHandler(snacPayloadIn oscar.SNAC_0x01_0x02_OServiceClientOnline, sess *Session, sm SessionManager, fm FeedbagManager, room ChatRoom) error {
  190. for _, version := range snacPayloadIn.GroupVersions {
  191. fmt.Printf("hahaha ClientOnlineHandler read SNAC client messageType: %+v\n", version)
  192. }
  193. if err := BroadcastArrival(sess, sm, fm); err != nil {
  194. return err
  195. }
  196. buddies, err := fm.Buddies(sess.ScreenName)
  197. if err != nil {
  198. return err
  199. }
  200. for _, buddy := range buddies {
  201. err := UnicastArrival(buddy, sess.ScreenName, sm)
  202. switch {
  203. case errors.Is(err, ErrSessNotFound):
  204. continue
  205. case err != nil:
  206. return err
  207. }
  208. }
  209. return nil
  210. }
  211. func (s OServiceService) SetUserInfoFieldsHandler(sess *Session, sm SessionManager, fm *FeedbagStore, snacPayloadIn oscar.SNAC_0x01_0x1E_OServiceSetUserInfoFields) (XMessage, error) {
  212. if status, hasStatus := snacPayloadIn.GetUint32(0x06); hasStatus {
  213. switch status {
  214. case 0x000:
  215. sess.SetInvisible(false)
  216. if err := BroadcastArrival(sess, sm, fm); err != nil {
  217. return XMessage{}, err
  218. }
  219. case 0x100:
  220. sess.SetInvisible(true)
  221. if err := BroadcastDeparture(sess, sm, fm); err != nil {
  222. return XMessage{}, err
  223. }
  224. default:
  225. return XMessage{}, fmt.Errorf("don't know what to do with status %d", status)
  226. }
  227. }
  228. return XMessage{
  229. snacFrame: oscar.SnacFrame{
  230. FoodGroup: oscar.OSERVICE,
  231. SubGroup: oscar.OServiceUserInfoUpdate,
  232. },
  233. snacOut: oscar.SNAC_0x01_0x0F_OServiceUserInfoUpdate{
  234. TLVUserInfo: sess.GetTLVUserInfo(),
  235. },
  236. }, nil
  237. }
  238. func (s OServiceService) IdleNotificationHandler(sess *Session, sm SessionManager, fm *FeedbagStore, snacPayloadIn oscar.SNAC_0x01_0x11_OServiceIdleNotification) error {
  239. if snacPayloadIn.IdleTime == 0 {
  240. sess.SetActive()
  241. } else {
  242. sess.SetIdle(time.Duration(snacPayloadIn.IdleTime) * time.Second)
  243. }
  244. return BroadcastArrival(sess, sm, fm)
  245. }
  246. func (s OServiceService) ServiceRequestHandler(cfg Config, cr *ChatRegistry, sess *Session, snacPayloadIn oscar.SNAC_0x01_0x04_OServiceServiceRequest) (XMessage, error) {
  247. if snacPayloadIn.FoodGroup != oscar.CHAT {
  248. return XMessage{}, ErrUnsupportedSubGroup
  249. }
  250. roomMeta, ok := snacPayloadIn.GetSlice(0x01)
  251. if !ok {
  252. return XMessage{}, errors.New("missing room info")
  253. }
  254. roomSnac := oscar.SNAC_0x01_0x04_TLVRoomInfo{}
  255. if err := oscar.Unmarshal(&roomSnac, bytes.NewBuffer(roomMeta)); err != nil {
  256. return XMessage{}, err
  257. }
  258. room, err := cr.Retrieve(string(roomSnac.Cookie))
  259. if err != nil {
  260. return XMessage{}, ErrUnsupportedSubGroup
  261. }
  262. room.NewSessionWithSN(sess.ID, sess.ScreenName)
  263. return XMessage{
  264. snacFrame: oscar.SnacFrame{
  265. FoodGroup: oscar.OSERVICE,
  266. SubGroup: oscar.OServiceServiceResponse,
  267. },
  268. snacOut: oscar.SNAC_0x01_0x05_OServiceServiceResponse{
  269. TLVRestBlock: oscar.TLVRestBlock{
  270. TLVList: oscar.TLVList{
  271. {
  272. TType: oscar.OServiceTLVTagsReconnectHere,
  273. Val: Address(cfg.OSCARHost, cfg.ChatPort),
  274. },
  275. {
  276. TType: oscar.OServiceTLVTagsLoginCookie,
  277. Val: ChatCookie{
  278. Cookie: []byte(room.Cookie),
  279. SessID: sess.ID,
  280. },
  281. },
  282. {
  283. TType: oscar.OServiceTLVTagsGroupID,
  284. Val: oscar.CHAT,
  285. },
  286. {
  287. TType: oscar.OServiceTLVTagsSSLCertName,
  288. Val: "",
  289. },
  290. {
  291. TType: oscar.OServiceTLVTagsSSLState,
  292. Val: uint8(0x00),
  293. },
  294. },
  295. },
  296. },
  297. }, nil
  298. }
  299. // RateParamsSubAddHandler exists to capture the SNAC input in unit tests to
  300. // verify it's correctly unmarshalled.
  301. func (s OServiceService) RateParamsSubAddHandler(oscar.SNAC_0x01_0x08_OServiceRateParamsSubAdd) {
  302. }
  303. func NewOServiceRouterForChat() OServiceRouter {
  304. return OServiceRouter{
  305. OServiceHandler: OServiceServiceForChat{},
  306. }
  307. }
  308. type OServiceServiceForChat struct {
  309. OServiceService
  310. }
  311. func (s OServiceServiceForChat) WriteOServiceHostOnline(w io.Writer, sequence *uint32) error {
  312. fmt.Println("writeOServiceHostOnline...")
  313. snacFrameOut := oscar.SnacFrame{
  314. FoodGroup: oscar.OSERVICE,
  315. SubGroup: oscar.OServiceHostOnline,
  316. }
  317. snacPayloadOut := oscar.SNAC_0x01_0x03_OServiceHostOnline{
  318. FoodGroups: []uint16{oscar.OSERVICE, oscar.CHAT},
  319. }
  320. return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
  321. }
  322. func (s OServiceServiceForChat) ClientOnlineHandler(snacPayloadIn oscar.SNAC_0x01_0x02_OServiceClientOnline, sess *Session, sm SessionManager, fm FeedbagManager, room ChatRoom) error {
  323. for _, version := range snacPayloadIn.GroupVersions {
  324. fmt.Printf("ClientOnlineHandler read SNAC client messageType: %+v\n", version)
  325. }
  326. SendChatRoomInfoUpdate(sess, sm, room)
  327. AlertUserJoined(sess, sm)
  328. SetOnlineChatUsers(sess, sm)
  329. return nil
  330. }