session.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. package state
  2. import (
  3. "net/netip"
  4. "sync"
  5. "time"
  6. "github.com/mk6i/retro-aim-server/wire"
  7. )
  8. // SessSendStatus is the result of sending a message to a user.
  9. type SessSendStatus int
  10. // RateClassState tracks the rate limiting state for a specific rate class
  11. // within a user's session.
  12. //
  13. // It embeds the static wire.RateClass configuration and maintains dynamic,
  14. // per-session state used to evaluate rate limits in real time.
  15. type RateClassState struct {
  16. // static rate limit configuration for this class
  17. wire.RateClass
  18. // CurrentLevel is the current exponential moving average for this rate
  19. // class.
  20. CurrentLevel int32
  21. // LastTime represents the last time a SNAC message was sent for this rate
  22. // class.
  23. LastTime time.Time
  24. // CurrentStatus is the last recorded rate limit status for this rate class.
  25. CurrentStatus wire.RateLimitStatus
  26. // Subscribed indicates whether the user wants to receive rate limit
  27. // parameter updates for this rate class.
  28. Subscribed bool
  29. // LimitedNow indicates whether the user is currently rate limited for this
  30. // rate class; the user is blocked from sending SNACs in this rate class
  31. // until the clear threshold is met.
  32. LimitedNow bool
  33. }
  34. const (
  35. // SessSendOK indicates message was sent to recipient
  36. SessSendOK SessSendStatus = iota
  37. // SessSendClosed indicates send did not complete because session is closed
  38. SessSendClosed
  39. // SessQueueFull indicates send failed due to full queue -- client is likely
  40. // dead
  41. SessQueueFull
  42. )
  43. // Session represents a user's current session. Unless stated otherwise, all
  44. // methods may be safely accessed by multiple goroutines.
  45. type Session struct {
  46. awayMessage string
  47. caps [][16]byte
  48. chatRoomCookie string
  49. closed bool
  50. displayScreenName DisplayScreenName
  51. identScreenName IdentScreenName
  52. idle bool
  53. idleTime time.Time
  54. msgCh chan wire.SNACMessage
  55. mutex sync.RWMutex
  56. nowFn func() time.Time
  57. signonComplete bool
  58. signonTime time.Time
  59. stopCh chan struct{}
  60. uin uint32
  61. warning uint16
  62. userInfoBitmask uint16
  63. userStatusBitmask uint32
  64. clientID string
  65. remoteAddr *netip.AddrPort
  66. lastObservedStates [5]RateClassState
  67. rateByClassID [5]RateClassState
  68. foodGroupVersions [wire.MDir + 1]uint16
  69. typingEventsEnabled bool
  70. }
  71. // NewSession returns a new instance of Session. By default, the user may have
  72. // up to 1000 pending messages before blocking.
  73. func NewSession() *Session {
  74. now := time.Now()
  75. return &Session{
  76. msgCh: make(chan wire.SNACMessage, 1000),
  77. nowFn: time.Now,
  78. stopCh: make(chan struct{}),
  79. signonTime: now,
  80. caps: make([][16]byte, 0),
  81. userInfoBitmask: wire.OServiceUserFlagOSCARFree,
  82. userStatusBitmask: wire.OServiceUserStatusAvailable,
  83. foodGroupVersions: func() [wire.MDir + 1]uint16 {
  84. // initialize default food groups versions to 1.0
  85. vals := [wire.MDir + 1]uint16{}
  86. vals[wire.OService] = 1
  87. vals[wire.Locate] = 1
  88. vals[wire.Buddy] = 1
  89. vals[wire.ICBM] = 1
  90. vals[wire.Advert] = 1
  91. vals[wire.Invite] = 1
  92. vals[wire.Admin] = 1
  93. vals[wire.Popup] = 1
  94. vals[wire.PermitDeny] = 1
  95. vals[wire.UserLookup] = 1
  96. vals[wire.Stats] = 1
  97. vals[wire.Translate] = 1
  98. vals[wire.ChatNav] = 1
  99. vals[wire.Chat] = 1
  100. vals[wire.ODir] = 1
  101. vals[wire.BART] = 1
  102. vals[wire.Feedbag] = 1
  103. vals[wire.ICQ] = 1
  104. vals[wire.BUCP] = 1
  105. vals[wire.Alert] = 1
  106. vals[wire.Plugin] = 1
  107. vals[wire.UnnamedFG24] = 1
  108. vals[wire.MDir] = 1
  109. return vals
  110. }(),
  111. }
  112. }
  113. func (s *Session) SetRateClasses(now time.Time, classes wire.RateLimitClasses) {
  114. s.mutex.Lock()
  115. defer s.mutex.Unlock()
  116. var newStates [5]RateClassState
  117. for i, class := range classes.All() {
  118. newStates[i] = RateClassState{
  119. CurrentLevel: class.MaxLevel,
  120. CurrentStatus: wire.RateLimitStatusClear,
  121. LastTime: now,
  122. RateClass: class,
  123. Subscribed: s.lastObservedStates[i].Subscribed,
  124. }
  125. }
  126. if s.lastObservedStates[0].ID == 0 {
  127. s.lastObservedStates = newStates
  128. } else {
  129. s.lastObservedStates = s.rateByClassID
  130. }
  131. s.rateByClassID = newStates
  132. }
  133. // SetRemoteAddr sets the user's remote IP address
  134. func (s *Session) SetRemoteAddr(remoteAddr *netip.AddrPort) {
  135. s.mutex.Lock()
  136. defer s.mutex.Unlock()
  137. s.remoteAddr = remoteAddr
  138. }
  139. // RemoteAddrs returns user's remote IP address
  140. func (s *Session) RemoteAddr() (remoteAddr *netip.AddrPort) {
  141. s.mutex.RLock()
  142. defer s.mutex.RUnlock()
  143. return s.remoteAddr
  144. }
  145. // SetUserInfoFlag sets a flag to and returns UserInfoBitmask
  146. func (s *Session) SetUserInfoFlag(flag uint16) (flags uint16) {
  147. s.mutex.Lock()
  148. defer s.mutex.Unlock()
  149. s.userInfoBitmask |= flag
  150. return s.userInfoBitmask
  151. }
  152. // ClearUserInfoFlag clear a flag from and returns UserInfoBitmask
  153. func (s *Session) ClearUserInfoFlag(flag uint16) (flags uint16) {
  154. s.mutex.Lock()
  155. defer s.mutex.Unlock()
  156. s.userInfoBitmask &^= flag
  157. return s.userInfoBitmask
  158. }
  159. // UserInfoBitmask returns UserInfoBitmask
  160. func (s *Session) UserInfoBitmask() (flags uint16) {
  161. s.mutex.RLock()
  162. defer s.mutex.RUnlock()
  163. return s.userInfoBitmask
  164. }
  165. // SetUserStatusBitmask sets the user status bitmask from the client.
  166. func (s *Session) SetUserStatusBitmask(bitmask uint32) {
  167. s.mutex.Lock()
  168. defer s.mutex.Unlock()
  169. s.userStatusBitmask = bitmask
  170. }
  171. // IncrementWarning increments the user's warning level. To decrease, pass a
  172. // negative increment value.
  173. func (s *Session) IncrementWarning(incr uint16) {
  174. s.mutex.Lock()
  175. defer s.mutex.Unlock()
  176. s.warning += incr
  177. }
  178. // Invisible returns true if the user is idle.
  179. func (s *Session) Invisible() bool {
  180. s.mutex.RLock()
  181. defer s.mutex.RUnlock()
  182. return s.userStatusBitmask&wire.OServiceUserStatusInvisible == wire.OServiceUserStatusInvisible
  183. }
  184. // SetIdentScreenName sets the user's screen name.
  185. func (s *Session) SetIdentScreenName(screenName IdentScreenName) {
  186. s.mutex.Lock()
  187. defer s.mutex.Unlock()
  188. s.identScreenName = screenName
  189. }
  190. // IdentScreenName returns the user's screen name.
  191. func (s *Session) IdentScreenName() IdentScreenName {
  192. s.mutex.RLock()
  193. defer s.mutex.RUnlock()
  194. return s.identScreenName
  195. }
  196. // SetDisplayScreenName sets the user's screen name.
  197. func (s *Session) SetDisplayScreenName(displayScreenName DisplayScreenName) {
  198. s.mutex.Lock()
  199. defer s.mutex.Unlock()
  200. s.displayScreenName = displayScreenName
  201. }
  202. // DisplayScreenName returns the user's screen name.
  203. func (s *Session) DisplayScreenName() DisplayScreenName {
  204. s.mutex.RLock()
  205. defer s.mutex.RUnlock()
  206. return s.displayScreenName
  207. }
  208. // SetSignonTime sets the user's sign-ontime.
  209. func (s *Session) SetSignonTime(t time.Time) {
  210. s.mutex.Lock()
  211. defer s.mutex.Unlock()
  212. s.signonTime = t
  213. }
  214. // SignonTime reports when the user signed on
  215. func (s *Session) SignonTime() time.Time {
  216. s.mutex.RLock()
  217. defer s.mutex.RUnlock()
  218. return s.signonTime
  219. }
  220. // Idle reports the user's idle state.
  221. func (s *Session) Idle() bool {
  222. s.mutex.RLock()
  223. defer s.mutex.RUnlock()
  224. return s.idle
  225. }
  226. // IdleTime reports when the user went idle
  227. func (s *Session) IdleTime() time.Time {
  228. s.mutex.RLock()
  229. defer s.mutex.RUnlock()
  230. return s.idleTime
  231. }
  232. // SetIdle sets the user's idle state.
  233. func (s *Session) SetIdle(dur time.Duration) {
  234. s.mutex.Lock()
  235. defer s.mutex.Unlock()
  236. s.idle = true
  237. // set the time the user became idle
  238. s.idleTime = s.nowFn().Add(-dur)
  239. }
  240. // UnsetIdle removes the user's idle state.
  241. func (s *Session) UnsetIdle() {
  242. s.mutex.Lock()
  243. defer s.mutex.Unlock()
  244. s.idle = false
  245. }
  246. // SetAwayMessage sets the user's away message.
  247. func (s *Session) SetAwayMessage(awayMessage string) {
  248. s.mutex.Lock()
  249. defer s.mutex.Unlock()
  250. s.awayMessage = awayMessage
  251. }
  252. // AwayMessage returns the user's away message.
  253. func (s *Session) AwayMessage() string {
  254. s.mutex.RLock()
  255. defer s.mutex.RUnlock()
  256. return s.awayMessage
  257. }
  258. // SetChatRoomCookie sets the chatRoomCookie for the chat room the user is currently in.
  259. func (s *Session) SetChatRoomCookie(cookie string) {
  260. s.mutex.Lock()
  261. defer s.mutex.Unlock()
  262. s.chatRoomCookie = cookie
  263. }
  264. // ChatRoomCookie gets the chatRoomCookie for the chat room the user is currently in.
  265. func (s *Session) ChatRoomCookie() string {
  266. s.mutex.RLock()
  267. defer s.mutex.RUnlock()
  268. return s.chatRoomCookie
  269. }
  270. // SignonComplete indicates whether the client has completed the sign-on sequence.
  271. func (s *Session) SignonComplete() bool {
  272. s.mutex.RLock()
  273. defer s.mutex.RUnlock()
  274. return s.signonComplete
  275. }
  276. // SetSignonComplete indicates that the client has completed the sign-on sequence.
  277. func (s *Session) SetSignonComplete() {
  278. s.mutex.Lock()
  279. defer s.mutex.Unlock()
  280. s.signonComplete = true
  281. }
  282. // UIN returns the user's ICQ number.
  283. func (s *Session) UIN() uint32 {
  284. s.mutex.RLock()
  285. defer s.mutex.RUnlock()
  286. return s.uin
  287. }
  288. // SetUIN sets the user's ICQ number.
  289. func (s *Session) SetUIN(uin uint32) {
  290. s.mutex.Lock()
  291. defer s.mutex.Unlock()
  292. s.uin = uin
  293. }
  294. // TLVUserInfo returns a TLV list containing session information required by
  295. // multiple SNAC message types that convey user information.
  296. func (s *Session) TLVUserInfo() wire.TLVUserInfo {
  297. s.mutex.RLock()
  298. defer s.mutex.RUnlock()
  299. return wire.TLVUserInfo{
  300. ScreenName: string(s.displayScreenName),
  301. WarningLevel: s.warning,
  302. TLVBlock: wire.TLVBlock{
  303. TLVList: s.userInfo(),
  304. },
  305. }
  306. }
  307. func (s *Session) userInfo() wire.TLVList {
  308. tlvs := wire.TLVList{}
  309. // sign-in timestamp
  310. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(s.signonTime.Unix())))
  311. // user info flags
  312. uFlags := s.userInfoBitmask
  313. if s.awayMessage != "" {
  314. uFlags |= wire.OServiceUserFlagUnavailable
  315. }
  316. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uFlags))
  317. // user status flags
  318. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoStatus, s.userStatusBitmask))
  319. // idle status
  320. if s.idle {
  321. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(s.idleTime).Minutes())))
  322. }
  323. // ICQ direct-connect info. The TLV is required for buddy arrival events to
  324. // work in ICQ, even if the values are set to default.
  325. if s.userInfoBitmask&wire.OServiceUserFlagICQ == wire.OServiceUserFlagICQ {
  326. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}))
  327. }
  328. // capabilities (buddy icon, chat, etc...)
  329. if len(s.caps) > 0 {
  330. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, s.caps))
  331. }
  332. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)))
  333. return tlvs
  334. }
  335. // SetCaps sets capability UUIDs that represent the features the client
  336. // supports. If set, capability metadata appears in the user info TLV list.
  337. func (s *Session) SetCaps(caps [][16]byte) {
  338. s.mutex.Lock()
  339. defer s.mutex.Unlock()
  340. s.caps = caps
  341. }
  342. // Caps retrieves user capabilities.
  343. func (s *Session) Caps() [][16]byte {
  344. s.mutex.RLock()
  345. defer s.mutex.RUnlock()
  346. return s.caps
  347. }
  348. func (s *Session) Warning() uint16 {
  349. s.mutex.RLock()
  350. defer s.mutex.RUnlock()
  351. var w uint16
  352. w = s.warning
  353. return w
  354. }
  355. // ReceiveMessage returns a channel of messages relayed via this session. It
  356. // may only be read by one consumer. The channel never closes; call this method
  357. // in a select block along with Closed in order to detect session closure.
  358. func (s *Session) ReceiveMessage() chan wire.SNACMessage {
  359. return s.msgCh
  360. }
  361. // RelayMessage receives a SNAC message from a user and passes it on
  362. // asynchronously to the consumer of this session's messages. It returns
  363. // SessSendStatus to indicate whether the message was successfully sent or
  364. // not. This method is non-blocking.
  365. func (s *Session) RelayMessage(msg wire.SNACMessage) SessSendStatus {
  366. s.mutex.RLock()
  367. defer s.mutex.RUnlock()
  368. if s.closed {
  369. return SessSendClosed
  370. }
  371. select {
  372. case s.msgCh <- msg:
  373. return SessSendOK
  374. case <-s.stopCh:
  375. return SessSendClosed
  376. default:
  377. return SessQueueFull
  378. }
  379. }
  380. // Close shuts down the session's ability to relay messages. Once invoked,
  381. // RelayMessage returns SessQueueFull and Closed returns a closed channel.
  382. // It is not possible to re-open message relaying once closed. It is safe to
  383. // call from multiple go routines.
  384. func (s *Session) Close() {
  385. s.mutex.Lock()
  386. defer s.mutex.Unlock()
  387. s.close()
  388. }
  389. func (s *Session) close() {
  390. if s.closed {
  391. return
  392. }
  393. close(s.stopCh)
  394. s.closed = true
  395. }
  396. // Closed blocks until the session is closed.
  397. func (s *Session) Closed() <-chan struct{} {
  398. return s.stopCh
  399. }
  400. // SetClientID sets the client ID.
  401. func (s *Session) SetClientID(clientID string) {
  402. s.mutex.Lock()
  403. defer s.mutex.Unlock()
  404. s.clientID = clientID
  405. }
  406. // ClientID retrieves the client ID.
  407. func (s *Session) ClientID() string {
  408. s.mutex.RLock()
  409. defer s.mutex.RUnlock()
  410. return s.clientID
  411. }
  412. // SubscribeRateLimits subscribes the Session to updates for the specified
  413. // rate limit classes. Future calls to ObserveRateChanges will report changes
  414. // for these classes.
  415. func (s *Session) SubscribeRateLimits(classes []wire.RateLimitClassID) {
  416. s.mutex.Lock()
  417. defer s.mutex.Unlock()
  418. for _, classID := range classes {
  419. s.rateByClassID[classID-1].Subscribed = true
  420. }
  421. }
  422. // ObserveRateChanges updates rate limit states for all known classes and returns
  423. // any classes and class states that have changed since the previous observation.
  424. func (s *Session) ObserveRateChanges(now time.Time) (classDelta []RateClassState, stateDelta []RateClassState) {
  425. s.mutex.Lock()
  426. defer s.mutex.Unlock()
  427. for i, params := range s.rateByClassID {
  428. if !params.Subscribed {
  429. continue
  430. }
  431. state, level := wire.CheckRateLimit(params.LastTime, now, params.RateClass, params.CurrentLevel, params.LimitedNow)
  432. s.rateByClassID[i].CurrentStatus = state
  433. // clear limited now flag if passing from limited state to clear state
  434. if s.rateByClassID[i].LimitedNow && state == wire.RateLimitStatusClear {
  435. s.rateByClassID[i].LimitedNow = false
  436. s.rateByClassID[i].CurrentLevel = level
  437. }
  438. // did rate class change?
  439. if params.RateClass != s.lastObservedStates[i].RateClass {
  440. classDelta = append(classDelta, s.rateByClassID[i])
  441. }
  442. // did rate limit status change?
  443. if s.lastObservedStates[i].CurrentStatus != s.rateByClassID[i].CurrentStatus {
  444. stateDelta = append(stateDelta, s.rateByClassID[i])
  445. }
  446. // save it for next time
  447. s.lastObservedStates[i] = s.rateByClassID[i]
  448. }
  449. return classDelta, stateDelta
  450. }
  451. // EvaluateRateLimit checks and updates the session’s rate limit state
  452. // for the given rate class ID. If the rate status reaches 'disconnect',
  453. // the session is closed.
  454. func (s *Session) EvaluateRateLimit(now time.Time, rateClassID wire.RateLimitClassID) wire.RateLimitStatus {
  455. s.mutex.Lock()
  456. defer s.mutex.Unlock()
  457. rateClass := &s.rateByClassID[rateClassID-1]
  458. status, newLevel := wire.CheckRateLimit(rateClass.LastTime, now, rateClass.RateClass, rateClass.CurrentLevel, rateClass.LimitedNow)
  459. rateClass.CurrentLevel = newLevel
  460. rateClass.CurrentStatus = status
  461. rateClass.LastTime = now
  462. rateClass.LimitedNow = status == wire.RateLimitStatusLimited
  463. if status == wire.RateLimitStatusDisconnect {
  464. s.close()
  465. }
  466. return status
  467. }
  468. // SetFoodGroupVersions sets the client's supported food group versions
  469. func (s *Session) SetFoodGroupVersions(versions [wire.MDir + 1]uint16) {
  470. s.mutex.Lock()
  471. defer s.mutex.Unlock()
  472. s.foodGroupVersions = versions
  473. }
  474. // FoodGroupVersions retrieves the client's supported food group versions.
  475. func (s *Session) FoodGroupVersions() [wire.MDir + 1]uint16 {
  476. s.mutex.RLock()
  477. defer s.mutex.RUnlock()
  478. return s.foodGroupVersions
  479. }
  480. // TypingEventsEnabled indicates whether the client wants to send and receive
  481. // typing events.
  482. func (s *Session) TypingEventsEnabled() bool {
  483. s.mutex.RLock()
  484. defer s.mutex.RUnlock()
  485. return s.typingEventsEnabled
  486. }
  487. // SetTypingEventsEnabled sets whether the client wants to send and receive
  488. // typing events.
  489. func (s *Session) SetTypingEventsEnabled(enabled bool) {
  490. s.mutex.Lock()
  491. defer s.mutex.Unlock()
  492. s.typingEventsEnabled = enabled
  493. }