session.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  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. clientID string
  50. closed bool
  51. displayScreenName DisplayScreenName
  52. foodGroupVersions [wire.MDir + 1]uint16
  53. identScreenName IdentScreenName
  54. idle bool
  55. idleTime time.Time
  56. lastObservedStates [5]RateClassState
  57. msgCh chan wire.SNACMessage
  58. multiConnFlag wire.MultiConnFlag
  59. mutex sync.RWMutex
  60. nowFn func() time.Time
  61. rateByClassID [5]RateClassState
  62. rateByClassIDOriginal [5]RateClassState
  63. remoteAddr *netip.AddrPort
  64. signonComplete bool
  65. signonTime time.Time
  66. stopCh chan struct{}
  67. typingEventsEnabled bool
  68. uin uint32
  69. userInfoBitmask uint16
  70. userStatusBitmask uint32
  71. warning uint16
  72. warningCh chan uint16
  73. lastWarnUpdate time.Time
  74. }
  75. // NewSession returns a new instance of Session. By default, the user may have
  76. // up to 1000 pending messages before blocking.
  77. func NewSession() *Session {
  78. now := time.Now()
  79. return &Session{
  80. msgCh: make(chan wire.SNACMessage, 1000),
  81. nowFn: time.Now,
  82. stopCh: make(chan struct{}),
  83. signonTime: now,
  84. caps: make([][16]byte, 0),
  85. userInfoBitmask: wire.OServiceUserFlagOSCARFree,
  86. userStatusBitmask: wire.OServiceUserStatusAvailable,
  87. foodGroupVersions: func() [wire.MDir + 1]uint16 {
  88. // initialize default food groups versions to 1.0
  89. vals := [wire.MDir + 1]uint16{}
  90. vals[wire.OService] = 1
  91. vals[wire.Locate] = 1
  92. vals[wire.Buddy] = 1
  93. vals[wire.ICBM] = 1
  94. vals[wire.Advert] = 1
  95. vals[wire.Invite] = 1
  96. vals[wire.Admin] = 1
  97. vals[wire.Popup] = 1
  98. vals[wire.PermitDeny] = 1
  99. vals[wire.UserLookup] = 1
  100. vals[wire.Stats] = 1
  101. vals[wire.Translate] = 1
  102. vals[wire.ChatNav] = 1
  103. vals[wire.Chat] = 1
  104. vals[wire.ODir] = 1
  105. vals[wire.BART] = 1
  106. vals[wire.Feedbag] = 1
  107. vals[wire.ICQ] = 1
  108. vals[wire.BUCP] = 1
  109. vals[wire.Alert] = 1
  110. vals[wire.Plugin] = 1
  111. vals[wire.UnnamedFG24] = 1
  112. vals[wire.MDir] = 1
  113. return vals
  114. }(),
  115. warningCh: make(chan uint16, 1),
  116. }
  117. }
  118. func (s *Session) SetRateClasses(now time.Time, classes wire.RateLimitClasses) {
  119. s.mutex.Lock()
  120. defer s.mutex.Unlock()
  121. var newStates [5]RateClassState
  122. for i, class := range classes.All() {
  123. newStates[i] = RateClassState{
  124. CurrentLevel: class.MaxLevel,
  125. CurrentStatus: wire.RateLimitStatusClear,
  126. LastTime: now,
  127. RateClass: class,
  128. Subscribed: s.lastObservedStates[i].Subscribed,
  129. }
  130. }
  131. if s.lastObservedStates[0].ID == 0 {
  132. s.lastObservedStates = newStates
  133. } else {
  134. s.lastObservedStates = s.rateByClassID
  135. }
  136. s.rateByClassID = newStates
  137. s.rateByClassIDOriginal = newStates
  138. }
  139. // SetRemoteAddr sets the user's remote IP address
  140. func (s *Session) SetRemoteAddr(remoteAddr *netip.AddrPort) {
  141. s.mutex.Lock()
  142. defer s.mutex.Unlock()
  143. s.remoteAddr = remoteAddr
  144. }
  145. // RemoteAddrs returns user's remote IP address
  146. func (s *Session) RemoteAddr() (remoteAddr *netip.AddrPort) {
  147. s.mutex.RLock()
  148. defer s.mutex.RUnlock()
  149. return s.remoteAddr
  150. }
  151. // SetUserInfoFlag sets a flag to and returns UserInfoBitmask
  152. func (s *Session) SetUserInfoFlag(flag uint16) (flags uint16) {
  153. s.mutex.Lock()
  154. defer s.mutex.Unlock()
  155. s.userInfoBitmask |= flag
  156. return s.userInfoBitmask
  157. }
  158. // ClearUserInfoFlag clear a flag from and returns UserInfoBitmask
  159. func (s *Session) ClearUserInfoFlag(flag uint16) (flags uint16) {
  160. s.mutex.Lock()
  161. defer s.mutex.Unlock()
  162. s.userInfoBitmask &^= flag
  163. return s.userInfoBitmask
  164. }
  165. // UserInfoBitmask returns UserInfoBitmask
  166. func (s *Session) UserInfoBitmask() (flags uint16) {
  167. s.mutex.RLock()
  168. defer s.mutex.RUnlock()
  169. return s.userInfoBitmask
  170. }
  171. // SetUserStatusBitmask sets the user status bitmask from the client.
  172. func (s *Session) SetUserStatusBitmask(bitmask uint32) {
  173. s.mutex.Lock()
  174. defer s.mutex.Unlock()
  175. s.userStatusBitmask = bitmask
  176. }
  177. // UserStatusBitmask returns the user status bitmask.
  178. func (s *Session) UserStatusBitmask() uint32 {
  179. s.mutex.RLock()
  180. defer s.mutex.RUnlock()
  181. return s.userStatusBitmask
  182. }
  183. // IncrementWarning increments the user's warning level and scales rate limits accordingly.
  184. // The incr parameter is the warning increment (negative to decrease), and classID specifies
  185. // which rate limit class to scale. The incr param is a percentage represented as an integer
  186. // where 30 = 3.0%, 100 = 10.0%, etc.
  187. func (s *Session) IncrementWarning(incr int16, classID wire.RateLimitClassID) (bool, uint16) {
  188. s.mutex.Lock()
  189. defer s.mutex.Unlock()
  190. // Handle warning level increment
  191. newWarning := int32(s.warning) + int32(incr)
  192. if newWarning > 1000 {
  193. return false, 0
  194. }
  195. if newWarning < 0 {
  196. s.warning = 0 // clamp min at 0
  197. } else {
  198. s.warning = uint16(newWarning)
  199. }
  200. pct := float32(incr) / 1000.0
  201. // create reference variables for better readability
  202. rateClass := &s.rateByClassID[classID-1]
  203. originalRateClass := &s.rateByClassIDOriginal[classID-1]
  204. // clamp function to constrain values between min and max
  205. clamp := func(value, min, max int32) int32 {
  206. if value < min {
  207. return min
  208. }
  209. if value > max {
  210. return max
  211. }
  212. return value
  213. }
  214. // scale the rate limit parameters
  215. newLimitLevel := rateClass.LimitLevel + int32(float32(originalRateClass.MaxLevel-originalRateClass.LimitLevel)*pct)
  216. rateClass.LimitLevel = clamp(newLimitLevel, originalRateClass.LimitLevel, originalRateClass.MaxLevel)
  217. newLimitLevel = rateClass.ClearLevel + int32(float32(originalRateClass.MaxLevel-originalRateClass.ClearLevel)*pct)
  218. rateClass.ClearLevel = clamp(newLimitLevel, originalRateClass.ClearLevel, originalRateClass.MaxLevel)
  219. newLimitLevel = rateClass.AlertLevel + int32(float32(originalRateClass.MaxLevel-originalRateClass.AlertLevel)*pct)
  220. rateClass.AlertLevel = clamp(newLimitLevel, originalRateClass.AlertLevel, originalRateClass.MaxLevel)
  221. s.warningCh <- s.warning
  222. return true, s.warning
  223. }
  224. // SetWarning sets the user's last warning level.
  225. func (s *Session) SetWarning(warning uint16) {
  226. s.mutex.Lock()
  227. defer s.mutex.Unlock()
  228. s.warning = warning
  229. }
  230. // Warning returns the user's current warning level as a percentage.
  231. // The warning level is stored as an integer representation of a percentage
  232. // where 30 = 3.0%, 100 = 10.0%, 1000 = 100.0%, etc.
  233. // This is how the OSCAR protocol represents warning percentages.
  234. func (s *Session) Warning() uint16 {
  235. s.mutex.RLock()
  236. defer s.mutex.RUnlock()
  237. return s.warning
  238. }
  239. // WarningCh returns the warning notification channel.
  240. // Listeners can receive from this channel to be notified when warnings occur.
  241. func (s *Session) WarningCh() chan uint16 {
  242. return s.warningCh
  243. }
  244. // Invisible returns true if the user is idle.
  245. func (s *Session) Invisible() bool {
  246. s.mutex.RLock()
  247. defer s.mutex.RUnlock()
  248. return s.userStatusBitmask&wire.OServiceUserStatusInvisible == wire.OServiceUserStatusInvisible
  249. }
  250. // SetIdentScreenName sets the user's screen name.
  251. func (s *Session) SetIdentScreenName(screenName IdentScreenName) {
  252. s.mutex.Lock()
  253. defer s.mutex.Unlock()
  254. s.identScreenName = screenName
  255. }
  256. // IdentScreenName returns the user's screen name.
  257. func (s *Session) IdentScreenName() IdentScreenName {
  258. s.mutex.RLock()
  259. defer s.mutex.RUnlock()
  260. return s.identScreenName
  261. }
  262. // SetDisplayScreenName sets the user's screen name.
  263. func (s *Session) SetDisplayScreenName(displayScreenName DisplayScreenName) {
  264. s.mutex.Lock()
  265. defer s.mutex.Unlock()
  266. s.displayScreenName = displayScreenName
  267. }
  268. // DisplayScreenName returns the user's screen name.
  269. func (s *Session) DisplayScreenName() DisplayScreenName {
  270. s.mutex.RLock()
  271. defer s.mutex.RUnlock()
  272. return s.displayScreenName
  273. }
  274. // SetSignonTime sets the user's sign-ontime.
  275. func (s *Session) SetSignonTime(t time.Time) {
  276. s.mutex.Lock()
  277. defer s.mutex.Unlock()
  278. s.signonTime = t
  279. }
  280. // SignonTime reports when the user signed on
  281. func (s *Session) SignonTime() time.Time {
  282. s.mutex.RLock()
  283. defer s.mutex.RUnlock()
  284. return s.signonTime
  285. }
  286. // Idle reports the user's idle state.
  287. func (s *Session) Idle() bool {
  288. s.mutex.RLock()
  289. defer s.mutex.RUnlock()
  290. return s.idle
  291. }
  292. // IdleTime reports when the user went idle
  293. func (s *Session) IdleTime() time.Time {
  294. s.mutex.RLock()
  295. defer s.mutex.RUnlock()
  296. return s.idleTime
  297. }
  298. // SetIdle sets the user's idle state.
  299. func (s *Session) SetIdle(dur time.Duration) {
  300. s.mutex.Lock()
  301. defer s.mutex.Unlock()
  302. s.idle = true
  303. // set the time the user became idle
  304. s.idleTime = s.nowFn().Add(-dur)
  305. }
  306. // UnsetIdle removes the user's idle state.
  307. func (s *Session) UnsetIdle() {
  308. s.mutex.Lock()
  309. defer s.mutex.Unlock()
  310. s.idle = false
  311. }
  312. // SetAwayMessage sets the user's away message.
  313. func (s *Session) SetAwayMessage(awayMessage string) {
  314. s.mutex.Lock()
  315. defer s.mutex.Unlock()
  316. s.awayMessage = awayMessage
  317. }
  318. // AwayMessage returns the user's away message.
  319. func (s *Session) AwayMessage() string {
  320. s.mutex.RLock()
  321. defer s.mutex.RUnlock()
  322. return s.awayMessage
  323. }
  324. // SetChatRoomCookie sets the chatRoomCookie for the chat room the user is currently in.
  325. func (s *Session) SetChatRoomCookie(cookie string) {
  326. s.mutex.Lock()
  327. defer s.mutex.Unlock()
  328. s.chatRoomCookie = cookie
  329. }
  330. // ChatRoomCookie gets the chatRoomCookie for the chat room the user is currently in.
  331. func (s *Session) ChatRoomCookie() string {
  332. s.mutex.RLock()
  333. defer s.mutex.RUnlock()
  334. return s.chatRoomCookie
  335. }
  336. // SignonComplete indicates whether the client has completed the sign-on sequence.
  337. func (s *Session) SignonComplete() bool {
  338. s.mutex.RLock()
  339. defer s.mutex.RUnlock()
  340. return s.signonComplete
  341. }
  342. // SetSignonComplete indicates that the client has completed the sign-on sequence.
  343. func (s *Session) SetSignonComplete() {
  344. s.mutex.Lock()
  345. defer s.mutex.Unlock()
  346. s.signonComplete = true
  347. }
  348. // UIN returns the user's ICQ number.
  349. func (s *Session) UIN() uint32 {
  350. s.mutex.RLock()
  351. defer s.mutex.RUnlock()
  352. return s.uin
  353. }
  354. // SetUIN sets the user's ICQ number.
  355. func (s *Session) SetUIN(uin uint32) {
  356. s.mutex.Lock()
  357. defer s.mutex.Unlock()
  358. s.uin = uin
  359. }
  360. // TLVUserInfo returns a TLV list containing session information required by
  361. // multiple SNAC message types that convey user information.
  362. func (s *Session) TLVUserInfo() wire.TLVUserInfo {
  363. s.mutex.RLock()
  364. defer s.mutex.RUnlock()
  365. return wire.TLVUserInfo{
  366. ScreenName: string(s.displayScreenName),
  367. WarningLevel: uint16(s.warning),
  368. TLVBlock: wire.TLVBlock{
  369. TLVList: s.userInfo(),
  370. },
  371. }
  372. }
  373. func (s *Session) userInfo() wire.TLVList {
  374. tlvs := wire.TLVList{}
  375. // sign-in timestamp
  376. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(s.signonTime.Unix())))
  377. // user info flags
  378. uFlags := s.userInfoBitmask
  379. if s.awayMessage != "" {
  380. uFlags |= wire.OServiceUserFlagUnavailable
  381. }
  382. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uFlags))
  383. // user status flags
  384. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoStatus, s.userStatusBitmask))
  385. // idle status
  386. if s.idle {
  387. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(s.idleTime).Minutes())))
  388. }
  389. // ICQ direct-connect info. The TLV is required for buddy arrival events to
  390. // work in ICQ, even if the values are set to default.
  391. if s.userInfoBitmask&wire.OServiceUserFlagICQ == wire.OServiceUserFlagICQ {
  392. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}))
  393. }
  394. // capabilities (buddy icon, chat, etc...)
  395. if len(s.caps) > 0 {
  396. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, s.caps))
  397. }
  398. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)))
  399. return tlvs
  400. }
  401. // SetCaps sets capability UUIDs that represent the features the client
  402. // supports. If set, capability metadata appears in the user info TLV list.
  403. func (s *Session) SetCaps(caps [][16]byte) {
  404. s.mutex.Lock()
  405. defer s.mutex.Unlock()
  406. s.caps = caps
  407. }
  408. // Caps retrieves user capabilities.
  409. func (s *Session) Caps() [][16]byte {
  410. s.mutex.RLock()
  411. defer s.mutex.RUnlock()
  412. return s.caps
  413. }
  414. // ReceiveMessage returns a channel of messages relayed via this session. It
  415. // may only be read by one consumer. The channel never closes; call this method
  416. // in a select block along with Closed in order to detect session closure.
  417. func (s *Session) ReceiveMessage() chan wire.SNACMessage {
  418. return s.msgCh
  419. }
  420. // RelayMessage receives a SNAC message from a user and passes it on
  421. // asynchronously to the consumer of this session's messages. It returns
  422. // SessSendStatus to indicate whether the message was successfully sent or
  423. // not. This method is non-blocking.
  424. func (s *Session) RelayMessage(msg wire.SNACMessage) SessSendStatus {
  425. s.mutex.RLock()
  426. defer s.mutex.RUnlock()
  427. if s.closed {
  428. return SessSendClosed
  429. }
  430. select {
  431. case s.msgCh <- msg:
  432. return SessSendOK
  433. case <-s.stopCh:
  434. return SessSendClosed
  435. default:
  436. return SessQueueFull
  437. }
  438. }
  439. // Close shuts down the session's ability to relay messages. Once invoked,
  440. // RelayMessage returns SessQueueFull and Closed returns a closed channel.
  441. // It is not possible to re-open message relaying once closed. It is safe to
  442. // call from multiple go routines.
  443. func (s *Session) Close() {
  444. s.mutex.Lock()
  445. defer s.mutex.Unlock()
  446. s.close()
  447. }
  448. func (s *Session) close() {
  449. if s.closed {
  450. return
  451. }
  452. close(s.stopCh)
  453. s.closed = true
  454. }
  455. // Closed blocks until the session is closed.
  456. func (s *Session) Closed() <-chan struct{} {
  457. return s.stopCh
  458. }
  459. // SetClientID sets the client ID.
  460. func (s *Session) SetClientID(clientID string) {
  461. s.mutex.Lock()
  462. defer s.mutex.Unlock()
  463. s.clientID = clientID
  464. }
  465. // ClientID retrieves the client ID.
  466. func (s *Session) ClientID() string {
  467. s.mutex.RLock()
  468. defer s.mutex.RUnlock()
  469. return s.clientID
  470. }
  471. // SubscribeRateLimits subscribes the Session to updates for the specified
  472. // rate limit classes. Future calls to ObserveRateChanges will report changes
  473. // for these classes.
  474. func (s *Session) SubscribeRateLimits(classes []wire.RateLimitClassID) {
  475. s.mutex.Lock()
  476. defer s.mutex.Unlock()
  477. for _, classID := range classes {
  478. s.rateByClassID[classID-1].Subscribed = true
  479. }
  480. }
  481. // ObserveRateChanges updates rate limit states for all known classes and returns
  482. // any classes and class states that have changed since the previous observation.
  483. func (s *Session) ObserveRateChanges(now time.Time) (classDelta []RateClassState, stateDelta []RateClassState) {
  484. s.mutex.Lock()
  485. defer s.mutex.Unlock()
  486. for i, params := range s.rateByClassID {
  487. if !params.Subscribed {
  488. continue
  489. }
  490. state, level := wire.CheckRateLimit(params.LastTime, now, params.RateClass, params.CurrentLevel, params.LimitedNow)
  491. s.rateByClassID[i].CurrentStatus = state
  492. // clear limited now flag if passing from limited state to clear state
  493. if s.rateByClassID[i].LimitedNow && state == wire.RateLimitStatusClear {
  494. s.rateByClassID[i].LimitedNow = false
  495. s.rateByClassID[i].CurrentLevel = level
  496. }
  497. // did rate class change?
  498. if params.RateClass != s.lastObservedStates[i].RateClass {
  499. classDelta = append(classDelta, s.rateByClassID[i])
  500. }
  501. // did rate limit status change?
  502. if s.lastObservedStates[i].CurrentStatus != s.rateByClassID[i].CurrentStatus {
  503. stateDelta = append(stateDelta, s.rateByClassID[i])
  504. }
  505. // save it for next time
  506. s.lastObservedStates[i] = s.rateByClassID[i]
  507. }
  508. return classDelta, stateDelta
  509. }
  510. // EvaluateRateLimit checks and updates the session’s rate limit state
  511. // for the given rate class ID. If the rate status reaches 'disconnect',
  512. // the session is closed. Rate limits are not enforced if the user is a bot
  513. // (has wire.OServiceUserFlagBot set in their user info bitmask).
  514. func (s *Session) EvaluateRateLimit(now time.Time, rateClassID wire.RateLimitClassID) wire.RateLimitStatus {
  515. s.mutex.Lock()
  516. defer s.mutex.Unlock()
  517. if s.userInfoBitmask&wire.OServiceUserFlagBot == wire.OServiceUserFlagBot {
  518. return wire.RateLimitStatusClear // don't rate limit bots
  519. }
  520. rateClass := &s.rateByClassID[rateClassID-1]
  521. status, newLevel := wire.CheckRateLimit(rateClass.LastTime, now, rateClass.RateClass, rateClass.CurrentLevel, rateClass.LimitedNow)
  522. rateClass.CurrentLevel = newLevel
  523. rateClass.CurrentStatus = status
  524. rateClass.LastTime = now
  525. rateClass.LimitedNow = status == wire.RateLimitStatusLimited
  526. if status == wire.RateLimitStatusDisconnect {
  527. s.close()
  528. }
  529. return status
  530. }
  531. // SetFoodGroupVersions sets the client's supported food group versions
  532. func (s *Session) SetFoodGroupVersions(versions [wire.MDir + 1]uint16) {
  533. s.mutex.Lock()
  534. defer s.mutex.Unlock()
  535. s.foodGroupVersions = versions
  536. }
  537. // FoodGroupVersions retrieves the client's supported food group versions.
  538. func (s *Session) FoodGroupVersions() [wire.MDir + 1]uint16 {
  539. s.mutex.RLock()
  540. defer s.mutex.RUnlock()
  541. return s.foodGroupVersions
  542. }
  543. // TypingEventsEnabled indicates whether the client wants to send and receive
  544. // typing events.
  545. func (s *Session) TypingEventsEnabled() bool {
  546. s.mutex.RLock()
  547. defer s.mutex.RUnlock()
  548. return s.typingEventsEnabled
  549. }
  550. // SetTypingEventsEnabled sets whether the client wants to send and receive
  551. // typing events.
  552. func (s *Session) SetTypingEventsEnabled(enabled bool) {
  553. s.mutex.Lock()
  554. defer s.mutex.Unlock()
  555. s.typingEventsEnabled = enabled
  556. }
  557. // SetMultiConnFlag sets the multi-connection flag for this session.
  558. func (s *Session) SetMultiConnFlag(flag wire.MultiConnFlag) {
  559. s.mutex.Lock()
  560. defer s.mutex.Unlock()
  561. s.multiConnFlag = flag
  562. }
  563. // MultiConnFlag retrieves the multi-connection flag for this session.
  564. func (s *Session) MultiConnFlag() wire.MultiConnFlag {
  565. s.mutex.RLock()
  566. defer s.mutex.RUnlock()
  567. return s.multiConnFlag
  568. }