session.go 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453
  1. package state
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/netip"
  6. "slices"
  7. "sync"
  8. "time"
  9. "github.com/mk6i/open-oscar-server/wire"
  10. )
  11. // SessSendStatus is the result of sending a message to a user.
  12. type SessSendStatus int
  13. // RateClassState tracks the rate limiting state for a specific rate class
  14. // within a user's session.
  15. //
  16. // It embeds the static wire.RateClass configuration and maintains dynamic,
  17. // per-session state used to evaluate rate limits in real time.
  18. type RateClassState struct {
  19. // static rate limit configuration for this class
  20. wire.RateClass
  21. // CurrentLevel is the current exponential moving average for this rate
  22. // class.
  23. CurrentLevel int32
  24. // LastTime represents the last time a SNAC message was sent for this rate
  25. // class.
  26. LastTime time.Time
  27. // CurrentStatus is the last recorded rate limit status for this rate class.
  28. CurrentStatus wire.RateLimitStatus
  29. // Subscribed indicates whether the user wants to receive rate limit
  30. // parameter updates for this rate class.
  31. Subscribed bool
  32. // LimitedNow indicates whether the user is currently rate limited for this
  33. // rate class; the user is blocked from sending SNACs in this rate class
  34. // until the clear threshold is met.
  35. LimitedNow bool
  36. }
  37. const (
  38. // SessSendOK indicates message was sent to recipient
  39. SessSendOK SessSendStatus = iota
  40. // SessSendClosed indicates send did not complete because session is closed
  41. SessSendClosed
  42. // SessQueueFull indicates send failed due to full queue -- client is likely
  43. // dead
  44. SessQueueFull
  45. )
  46. // maxNotifyTxnNames is the maximum number of distinct screen names accumulated in
  47. // a single notify transaction.
  48. const maxNotifyTxnNames = 1000
  49. var (
  50. errNotifyTxnNotActive = errors.New("notify transaction is not active")
  51. errNotifyTxnTooManyNames = errors.New("notify transaction exceeds maximum screen names")
  52. )
  53. // Session represents shared user-level state that persists across all concurrent
  54. // connections for a single user account.
  55. //
  56. // Session maintains client identity information, preferences, rate limiting state,
  57. // and other shared data that should be consistent across all of a user's active
  58. // connections. Individual connection-specific state (like remote address, sign-on
  59. // status, or per-connection capabilities) is stored in SessionInstance instead.
  60. //
  61. // All methods on Session are safe for concurrent use.
  62. type Session struct {
  63. mutex sync.RWMutex
  64. // User identity (shared across all sessions)
  65. displayScreenName DisplayScreenName
  66. identScreenName IdentScreenName
  67. uin uint32
  68. memberSince time.Time
  69. signonTime time.Time
  70. // User-level settings and profile (shared)
  71. warning uint16
  72. warningCh chan uint16
  73. offlineMsgCount int
  74. chatRoomCookie string
  75. buddyIcon wire.BARTID
  76. typingEventsEnabled bool
  77. // usesFeedbag is true after FeedbagService.Use completes this sign-on (server-side buddy list / SSI).
  78. usesFeedbag bool
  79. // Rate limiting (shared across all sessions per user)
  80. rateLimitStates [5]RateClassState
  81. rateLimitStatesOriginal [5]RateClassState
  82. lastObservedStates [5]RateClassState
  83. instances map[uint8]*SessionInstance
  84. instancesOrdered []*SessionInstance
  85. initOnce sync.Once
  86. onSessCloseFn func()
  87. nowFn func() time.Time
  88. }
  89. // NewSession creates a new Session for a user.
  90. func NewSession() *Session {
  91. return &Session{
  92. warningCh: make(chan uint16, 1),
  93. instances: make(map[uint8]*SessionInstance),
  94. instancesOrdered: make([]*SessionInstance, 0),
  95. onSessCloseFn: func() {},
  96. nowFn: time.Now,
  97. }
  98. }
  99. //
  100. // Instance Management
  101. //
  102. // AddInstance creates and adds a new connection instance to the session.
  103. // Returns the newly created SessionInstance with a unique instance number.
  104. func (s *Session) AddInstance() *SessionInstance {
  105. s.mutex.Lock()
  106. defer s.mutex.Unlock()
  107. instance := &SessionInstance{
  108. session: s,
  109. instanceNum: s.generateInstanceNum(),
  110. msgCh: make(chan wire.SNACMessage, 1000),
  111. stopCh: make(chan struct{}),
  112. capabilities: make([][16]byte, 0),
  113. foodGroupVersions: defaultFoodGroupVersions(),
  114. userInfoBitmask: wire.OServiceUserFlagOSCARFree,
  115. userStatusBitmask: wire.OServiceUserStatusAvailable,
  116. onInstanceCloseFn: func() {},
  117. }
  118. s.instances[instance.instanceNum] = instance
  119. s.instancesOrdered = append(s.instancesOrdered, instance)
  120. return instance
  121. }
  122. // HasLiveInstances returns true if the session has at least one live instance.
  123. // A live instance is one that is not closed and has completed the sign-on sequence.
  124. func (s *Session) HasLiveInstances() bool {
  125. s.mutex.RLock()
  126. defer s.mutex.RUnlock()
  127. for _, instance := range s.instances {
  128. if instance.live() {
  129. return true
  130. }
  131. }
  132. return false
  133. }
  134. // InstanceCount returns the number of total instances in the session group.
  135. func (s *Session) InstanceCount() int {
  136. s.mutex.RLock()
  137. defer s.mutex.RUnlock()
  138. return len(s.instances)
  139. }
  140. // Instances returns all instances in the order they were added.
  141. func (s *Session) Instances() []*SessionInstance {
  142. s.mutex.RLock()
  143. defer s.mutex.RUnlock()
  144. instances := make([]*SessionInstance, len(s.instancesOrdered))
  145. copy(instances, s.instancesOrdered)
  146. return instances
  147. }
  148. // RemoveInstance removes an instance from the session group.
  149. func (s *Session) RemoveInstance(instance *SessionInstance) {
  150. s.mutex.Lock()
  151. defer s.mutex.Unlock()
  152. delete(s.instances, instance.instanceNum)
  153. for i, inst := range s.instancesOrdered {
  154. if inst == instance {
  155. s.instancesOrdered = append(s.instancesOrdered[:i], s.instancesOrdered[i+1:]...)
  156. break
  157. }
  158. }
  159. }
  160. // generateInstanceNum generates the next available instance number for this session group.
  161. // It finds the next number that is not currently in use by iterating over the possible key range.
  162. func (s *Session) generateInstanceNum() uint8 {
  163. // if num reaches 0, all number have been taken
  164. for num := uint8(1); num != 0; num++ {
  165. if _, exists := s.instances[num]; !exists {
  166. return num
  167. }
  168. }
  169. // the caller should ensure there are no more than 255 instances per session
  170. panic("all instance numbers are taken (max 255 instances per session)")
  171. }
  172. // defaultFoodGroupVersions returns default version numbers for all food groups.
  173. func defaultFoodGroupVersions() [wire.MDir + 1]uint16 {
  174. vals := [wire.MDir + 1]uint16{}
  175. vals[wire.OService] = 1
  176. vals[wire.Locate] = 1
  177. vals[wire.Buddy] = 1
  178. vals[wire.ICBM] = 1
  179. vals[wire.Advert] = 1
  180. vals[wire.Invite] = 1
  181. vals[wire.Admin] = 1
  182. vals[wire.Popup] = 1
  183. vals[wire.PermitDeny] = 1
  184. vals[wire.UserLookup] = 1
  185. vals[wire.Stats] = 1
  186. vals[wire.Translate] = 1
  187. vals[wire.ChatNav] = 1
  188. vals[wire.Chat] = 1
  189. vals[wire.ODir] = 1
  190. vals[wire.BART] = 1
  191. vals[wire.Feedbag] = 1
  192. vals[wire.ICQ] = 1
  193. vals[wire.BUCP] = 1
  194. vals[wire.Alert] = 1
  195. vals[wire.Plugin] = 1
  196. vals[wire.UnnamedFG24] = 1
  197. vals[wire.MDir] = 1
  198. return vals
  199. }
  200. //
  201. // Identity
  202. //
  203. // ChatRoomCookie returns the chat room cookie.
  204. func (s *Session) ChatRoomCookie() string {
  205. s.mutex.RLock()
  206. defer s.mutex.RUnlock()
  207. return s.chatRoomCookie
  208. }
  209. // DisplayScreenName returns the user's display screen name.
  210. func (s *Session) DisplayScreenName() DisplayScreenName {
  211. s.mutex.RLock()
  212. defer s.mutex.RUnlock()
  213. return s.displayScreenName
  214. }
  215. // IdentScreenName returns the user's identity screen name.
  216. func (s *Session) IdentScreenName() IdentScreenName {
  217. s.mutex.RLock()
  218. defer s.mutex.RUnlock()
  219. return s.identScreenName
  220. }
  221. // SetDisplayScreenName sets the user's display screen name (shared across all sessions).
  222. func (s *Session) SetDisplayScreenName(displayScreenName DisplayScreenName) {
  223. s.mutex.Lock()
  224. defer s.mutex.Unlock()
  225. s.displayScreenName = displayScreenName
  226. }
  227. // SetIdentScreenName sets the user's identity screen name (shared across all sessions).
  228. func (s *Session) SetIdentScreenName(screenName IdentScreenName) {
  229. s.mutex.Lock()
  230. defer s.mutex.Unlock()
  231. s.identScreenName = screenName
  232. }
  233. // SetUIN sets the user's ICQ number (shared across all sessions).
  234. func (s *Session) SetUIN(uin uint32) {
  235. s.mutex.Lock()
  236. defer s.mutex.Unlock()
  237. s.uin = uin
  238. }
  239. // UIN returns the user's ICQ number.
  240. func (s *Session) UIN() uint32 {
  241. s.mutex.RLock()
  242. defer s.mutex.RUnlock()
  243. return s.uin
  244. }
  245. //
  246. // Status / Availability
  247. //
  248. // Away returns true if all instances are away.
  249. func (s *Session) Away() bool {
  250. instances := s.Instances()
  251. if len(instances) == 0 {
  252. return false
  253. }
  254. for _, instance := range instances {
  255. if instance.UserInfoBitmask()&wire.OServiceUserFlagUnavailable == 0 &&
  256. instance.UserStatusBitmask()&wire.OServiceUserStatusAway == 0 {
  257. return false
  258. }
  259. }
  260. return true
  261. }
  262. // AwayMessage returns the away message from the last instance to set an away message.
  263. func (s *Session) AwayMessage() string {
  264. s.mutex.RLock()
  265. defer s.mutex.RUnlock()
  266. var latest *SessionInstance
  267. var latestTime time.Time
  268. for _, instance := range s.instances {
  269. // Only consider instances that are away
  270. if !instance.Away() {
  271. continue
  272. }
  273. _, awayTime := instance.AwayMessage()
  274. if latest == nil || awayTime.After(latestTime) {
  275. latest = instance
  276. latestTime = awayTime
  277. }
  278. }
  279. if latest == nil {
  280. return ""
  281. }
  282. awayMsg, _ := latest.AwayMessage()
  283. return awayMsg
  284. }
  285. // Idle returns true if all instances are idle.
  286. func (s *Session) Idle() bool {
  287. instances := s.Instances()
  288. if len(instances) == 0 {
  289. return false
  290. }
  291. for _, instance := range instances {
  292. if !instance.Idle() {
  293. return false
  294. }
  295. }
  296. return true
  297. }
  298. // IdleTime returns the latest idle time if all instances are idle. If not all
  299. // instances are idle, it returns a zero time.
  300. func (s *Session) IdleTime() time.Time {
  301. if !s.Idle() {
  302. return time.Time{}
  303. }
  304. return s.mostRecentIdleTime()
  305. }
  306. // Inactive returns true if all instances are not active.
  307. func (s *Session) Inactive() bool {
  308. for _, instance := range s.Instances() {
  309. if instance.active() {
  310. return false
  311. }
  312. }
  313. return true
  314. }
  315. // Instance returns the SessionInstance with the given instance number, or nil if not found.
  316. func (s *Session) Instance(num uint8) *SessionInstance {
  317. s.mutex.RLock()
  318. defer s.mutex.RUnlock()
  319. return s.instances[num]
  320. }
  321. // Invisible returns true if all instances are invisible.
  322. func (s *Session) Invisible() bool {
  323. s.mutex.RLock()
  324. defer s.mutex.RUnlock()
  325. for _, instance := range s.instances {
  326. if !instance.Invisible() {
  327. return false
  328. }
  329. }
  330. return true
  331. }
  332. // mostRecentIdleTime returns the most recent idle time from all instances.
  333. func (s *Session) mostRecentIdleTime() time.Time {
  334. var mostRecent time.Time
  335. for _, instance := range s.Instances() {
  336. if mostRecent.IsZero() || (instance.Idle() && instance.IdleTime().After(mostRecent)) {
  337. mostRecent = instance.IdleTime()
  338. }
  339. }
  340. return mostRecent
  341. }
  342. //
  343. // Rate Limiting / Warning
  344. //
  345. // EvaluateRateLimit checks and updates the rate limit state.
  346. func (s *Session) EvaluateRateLimit(now time.Time, rateClassID wire.RateLimitClassID) wire.RateLimitStatus {
  347. if s.AllUserInfoBitmask(wire.OServiceUserFlagBot) {
  348. return wire.RateLimitStatusClear // don't rate limit bots
  349. }
  350. s.mutex.Lock()
  351. rateClass := &s.rateLimitStates[rateClassID-1]
  352. status, newLevel := wire.CheckRateLimit(rateClass.LastTime, now, rateClass.RateClass, rateClass.CurrentLevel, rateClass.LimitedNow)
  353. rateClass.CurrentLevel = newLevel
  354. rateClass.CurrentStatus = status
  355. rateClass.LastTime = now
  356. rateClass.LimitedNow = status == wire.RateLimitStatusLimited
  357. s.mutex.Unlock()
  358. if status == wire.RateLimitStatusDisconnect {
  359. s.CloseSession()
  360. }
  361. return status
  362. }
  363. // ObserveRateChanges updates rate limit states and returns changes.
  364. func (s *Session) ObserveRateChanges(now time.Time) (classDelta []RateClassState, stateDelta []RateClassState) {
  365. s.mutex.Lock()
  366. defer s.mutex.Unlock()
  367. for i, params := range s.rateLimitStates {
  368. if !params.Subscribed {
  369. continue
  370. }
  371. state, level := wire.CheckRateLimit(params.LastTime, now, params.RateClass, params.CurrentLevel, params.LimitedNow)
  372. s.rateLimitStates[i].CurrentStatus = state
  373. // clear limited now flag if passing from limited state to clear state
  374. if s.rateLimitStates[i].LimitedNow && state == wire.RateLimitStatusClear {
  375. s.rateLimitStates[i].LimitedNow = false
  376. s.rateLimitStates[i].CurrentLevel = level
  377. }
  378. // did rate class change?
  379. if params.RateClass != s.lastObservedStates[i].RateClass {
  380. classDelta = append(classDelta, s.rateLimitStates[i])
  381. }
  382. // did rate limit status change?
  383. if s.lastObservedStates[i].CurrentStatus != s.rateLimitStates[i].CurrentStatus {
  384. stateDelta = append(stateDelta, s.rateLimitStates[i])
  385. }
  386. // save it for next time
  387. s.lastObservedStates[i] = s.rateLimitStates[i]
  388. }
  389. return classDelta, stateDelta
  390. }
  391. // RateLimitStates returns the current rate limit states (shared across all sessions).
  392. func (s *Session) RateLimitStates() [5]RateClassState {
  393. s.mutex.RLock()
  394. defer s.mutex.RUnlock()
  395. return s.rateLimitStates
  396. }
  397. // SetRateClasses sets the rate limit classes (shared across all sessions).
  398. func (s *Session) SetRateClasses(now time.Time, classes wire.RateLimitClasses) {
  399. s.mutex.Lock()
  400. defer s.mutex.Unlock()
  401. var newStates [5]RateClassState
  402. for i, class := range classes.All() {
  403. newStates[i] = RateClassState{
  404. CurrentLevel: class.MaxLevel,
  405. CurrentStatus: wire.RateLimitStatusClear,
  406. LastTime: now,
  407. RateClass: class,
  408. Subscribed: s.lastObservedStates[i].Subscribed,
  409. }
  410. }
  411. if s.lastObservedStates[0].ID == 0 {
  412. s.lastObservedStates = newStates
  413. } else {
  414. s.lastObservedStates = s.rateLimitStates
  415. }
  416. s.rateLimitStates = newStates
  417. s.rateLimitStatesOriginal = newStates
  418. }
  419. // SetWarning sets the user's warning level (shared across all sessions).
  420. func (s *Session) SetWarning(warning uint16) {
  421. s.mutex.Lock()
  422. defer s.mutex.Unlock()
  423. s.warning = warning
  424. }
  425. // ScaleWarningAndRateLimit increments the user's warning level and scales rate limits.
  426. func (s *Session) ScaleWarningAndRateLimit(incr int16, classID wire.RateLimitClassID) (bool, uint16) {
  427. s.mutex.Lock()
  428. defer s.mutex.Unlock()
  429. // Handle warning level increment
  430. newWarning := int32(s.warning) + int32(incr)
  431. if newWarning > 1000 {
  432. return false, 0
  433. }
  434. if newWarning < 0 {
  435. s.warning = 0 // clamp min at 0
  436. } else {
  437. s.warning = uint16(newWarning)
  438. }
  439. pct := float32(incr) / 1000.0
  440. // create reference variables for better readability
  441. rateClass := &s.rateLimitStates[classID-1]
  442. originalRateClass := &s.rateLimitStatesOriginal[classID-1]
  443. // clamp function to constrain values between min and max
  444. clamp := func(value, min, max int32) int32 {
  445. if value < min {
  446. return min
  447. }
  448. if value > max {
  449. return max
  450. }
  451. return value
  452. }
  453. // Apply a buffer to limit/clear/alert levels so that they never approach
  454. // too close to the maximum level. Otherwise, AIM 4.8 exhibits instability
  455. // (client crashes, IM window glitches) when the warning level reaches 90-100%.
  456. maxLevel := originalRateClass.MaxLevel - 150
  457. // scale the rate limit parameters
  458. newLimitLevel := rateClass.LimitLevel + int32(float32(maxLevel-originalRateClass.LimitLevel)*pct)
  459. rateClass.LimitLevel = clamp(newLimitLevel, originalRateClass.LimitLevel, originalRateClass.MaxLevel)
  460. newLimitLevel = rateClass.ClearLevel + int32(float32(maxLevel-originalRateClass.ClearLevel)*pct)
  461. rateClass.ClearLevel = clamp(newLimitLevel, originalRateClass.ClearLevel, originalRateClass.MaxLevel)
  462. newLimitLevel = rateClass.AlertLevel + int32(float32(maxLevel-originalRateClass.AlertLevel)*pct)
  463. rateClass.AlertLevel = clamp(newLimitLevel, originalRateClass.AlertLevel, originalRateClass.MaxLevel)
  464. s.warningCh <- s.warning
  465. return true, s.warning
  466. }
  467. // SubscribeRateLimits subscribes to rate limit updates.
  468. func (s *Session) SubscribeRateLimits(classes []wire.RateLimitClassID) {
  469. s.mutex.Lock()
  470. defer s.mutex.Unlock()
  471. for _, classID := range classes {
  472. s.rateLimitStates[classID-1].Subscribed = true
  473. }
  474. }
  475. // Warning returns the user's current warning level.
  476. func (s *Session) Warning() uint16 {
  477. s.mutex.RLock()
  478. defer s.mutex.RUnlock()
  479. return s.warning
  480. }
  481. // WarningCh returns the warning notification channel.
  482. func (s *Session) WarningCh() chan uint16 {
  483. return s.warningCh
  484. }
  485. //
  486. // Lifecycle
  487. //
  488. // CloseSession closes all instances in the session.
  489. func (s *Session) CloseSession() {
  490. s.mutex.RLock()
  491. instances := make([]*SessionInstance, 0, len(s.instances))
  492. for _, instance := range s.instances {
  493. instances = append(instances, instance)
  494. }
  495. s.mutex.RUnlock()
  496. for _, instance := range instances {
  497. instance.closeOnly()
  498. }
  499. }
  500. // OnSessionClose registers a function to be called once all instances have closed.
  501. func (s *Session) OnSessionClose(fn func()) {
  502. s.mutex.Lock()
  503. defer s.mutex.Unlock()
  504. s.onSessCloseFn = fn
  505. }
  506. // RunOnce executes the given function once across all invocations. Used to
  507. // run arbitrary code that must only run once when the first session instance
  508. // connects. The function must not block.
  509. func (s *Session) RunOnce(fn func() error) error {
  510. var err error
  511. s.initOnce.Do(func() {
  512. err = fn()
  513. })
  514. return err
  515. }
  516. // SetNowFn sets the function used to get the current time. This is useful for testing.
  517. func (s *Session) SetNowFn(fn func() time.Time) {
  518. s.mutex.Lock()
  519. defer s.mutex.Unlock()
  520. s.nowFn = fn
  521. }
  522. //
  523. // User Settings / Attributes
  524. //
  525. // AllUserInfoBitmask returns whether all instances have user info flag set.
  526. func (s *Session) AllUserInfoBitmask(flag uint16) bool {
  527. for _, instance := range s.Instances() {
  528. if instance.UserInfoBitmask()&flag != flag {
  529. return false
  530. }
  531. }
  532. return true
  533. }
  534. // AllUserStatusBitmask returns whether all instances have user status flag set.
  535. func (s *Session) AllUserStatusBitmask(flag uint32) bool {
  536. for _, instance := range s.Instances() {
  537. if instance.UserStatusBitmask()&flag != flag {
  538. return false
  539. }
  540. }
  541. return true
  542. }
  543. // BuddyIcon returns the session's buddy icon metadata and reports whether it
  544. // has been set.
  545. func (s *Session) BuddyIcon() (wire.BARTID, bool) {
  546. s.mutex.RLock()
  547. defer s.mutex.RUnlock()
  548. icon := s.buddyIcon
  549. return icon, icon.Type != 0
  550. }
  551. // Caps returns the union of all capability UUIDs from all instances in the session.
  552. func (s *Session) Caps() [][16]byte {
  553. s.mutex.RLock()
  554. defer s.mutex.RUnlock()
  555. caps := make(map[[16]byte]bool)
  556. for _, instance := range s.instances {
  557. for _, c := range instance.caps() {
  558. caps[c] = true
  559. }
  560. }
  561. ret := make([][16]byte, 0, len(caps))
  562. for c := range caps {
  563. ret = append(ret, c)
  564. }
  565. // Sort capabilities to ensure deterministic order
  566. slices.SortFunc(ret, func(a, b [16]byte) int {
  567. for i := 0; i < 16; i++ {
  568. if a[i] != b[i] {
  569. return int(a[i]) - int(b[i])
  570. }
  571. }
  572. return 0
  573. })
  574. return ret
  575. }
  576. // HasCap returns true if any instance in the session has the given capability UUID.
  577. func (s *Session) HasCap(cap [16]byte) bool {
  578. for _, c := range s.Caps() {
  579. if c == cap {
  580. return true
  581. }
  582. }
  583. return false
  584. }
  585. // MemberSince reports when the user became a member.
  586. func (s *Session) MemberSince() time.Time {
  587. s.mutex.RLock()
  588. defer s.mutex.RUnlock()
  589. return s.memberSince
  590. }
  591. // OfflineMsgCount returns the offline message count.
  592. func (s *Session) OfflineMsgCount() int {
  593. s.mutex.RLock()
  594. defer s.mutex.RUnlock()
  595. return s.offlineMsgCount
  596. }
  597. // Profile returns the most recently updated non-empty profile from all instances.
  598. func (s *Session) Profile() UserProfile {
  599. var latest UserProfile
  600. for _, instance := range s.Instances() {
  601. profile := instance.Profile()
  602. if profile.IsEmpty() {
  603. continue
  604. }
  605. if latest.IsEmpty() || profile.UpdateTime.After(latest.UpdateTime) {
  606. latest = profile
  607. }
  608. }
  609. return latest
  610. }
  611. // SetBuddyIcon stores the session's buddy icon metadata.
  612. func (s *Session) SetBuddyIcon(icon wire.BARTID) {
  613. s.mutex.Lock()
  614. defer s.mutex.Unlock()
  615. s.buddyIcon = icon
  616. }
  617. // SetChatRoomCookie sets the chat room cookie.
  618. func (s *Session) SetChatRoomCookie(cookie string) {
  619. s.mutex.Lock()
  620. defer s.mutex.Unlock()
  621. s.chatRoomCookie = cookie
  622. }
  623. // SetMemberSince sets the member since timestamp.
  624. func (s *Session) SetMemberSince(t time.Time) {
  625. s.mutex.Lock()
  626. defer s.mutex.Unlock()
  627. s.memberSince = t
  628. }
  629. // SetOfflineMsgCount sets the offline message count.
  630. func (s *Session) SetOfflineMsgCount(count int) {
  631. s.mutex.Lock()
  632. defer s.mutex.Unlock()
  633. s.offlineMsgCount = count
  634. }
  635. // SetSignonTime sets the session's sign-on time.
  636. func (s *Session) SetSignonTime(t time.Time) {
  637. s.mutex.Lock()
  638. defer s.mutex.Unlock()
  639. s.signonTime = t
  640. }
  641. // SetTypingEventsEnabled sets whether the session wants to send and receive typing events.
  642. func (s *Session) SetTypingEventsEnabled(enabled bool) {
  643. s.mutex.Lock()
  644. defer s.mutex.Unlock()
  645. s.typingEventsEnabled = enabled
  646. }
  647. // SignonTime returns the session's sign-on time.
  648. func (s *Session) SignonTime() time.Time {
  649. s.mutex.RLock()
  650. defer s.mutex.RUnlock()
  651. return s.signonTime
  652. }
  653. // TLVUserInfo returns a TLV list containing session information aggregated from all instances.
  654. func (s *Session) TLVUserInfo() wire.TLVUserInfo {
  655. return wire.TLVUserInfo{
  656. ScreenName: s.DisplayScreenName().String(),
  657. WarningLevel: s.Warning(),
  658. TLVBlock: wire.TLVBlock{
  659. TLVList: s.userInfo(),
  660. },
  661. }
  662. }
  663. // TypingEventsEnabled indicates whether the session wants to send and receive typing events.
  664. func (s *Session) TypingEventsEnabled() bool {
  665. s.mutex.RLock()
  666. defer s.mutex.RUnlock()
  667. return s.typingEventsEnabled
  668. }
  669. // SetUsesFeedbag records that this session uses the feedbag (server-side) buddy list for this sign-on.
  670. func (s *Session) SetUsesFeedbag() {
  671. s.mutex.Lock()
  672. defer s.mutex.Unlock()
  673. s.usesFeedbag = true
  674. }
  675. // UsesFeedbag reports whether the client completed the feedbag Use handshake this sign-on.
  676. func (s *Session) UsesFeedbag() bool {
  677. s.mutex.RLock()
  678. defer s.mutex.RUnlock()
  679. return s.usesFeedbag
  680. }
  681. func (s *Session) userInfo() wire.TLVList {
  682. tlvs := wire.TLVList{}
  683. // sign-in timestamp
  684. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(s.SignonTime().Unix())))
  685. instances := s.Instances()
  686. // Use the first instance as a template for user flags. Most flags are static
  687. // and should be consistent across all instances; only the "away" flag may vary.
  688. // If instances differ in protocol type (ICQ vs AIM), that indicates an error.
  689. var baseUserFlags uint16
  690. if len(instances) > 0 {
  691. baseUserFlags = instances[0].UserInfoBitmask()
  692. }
  693. if s.Away() {
  694. baseUserFlags |= wire.OServiceUserFlagUnavailable
  695. } else {
  696. baseUserFlags &^= wire.OServiceUserFlagUnavailable
  697. }
  698. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoUserFlags, baseUserFlags))
  699. // user status flags - user-level (shared)
  700. var statusBitmask uint32
  701. if len(instances) > 0 {
  702. statusBitmask = instances[0].UserStatusBitmask()
  703. for _, instance := range instances {
  704. statusBitmask &= instance.UserStatusBitmask()
  705. }
  706. }
  707. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoStatus, statusBitmask))
  708. // idle status - use most recent idle time if all instances are idle
  709. if s.Idle() {
  710. mostRecentIdleTime := s.mostRecentIdleTime()
  711. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(mostRecentIdleTime).Minutes())))
  712. }
  713. // set buddy icon metadata, if user has buddy icon
  714. if icon, hasIcon := s.BuddyIcon(); hasIcon {
  715. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoBARTInfo, icon))
  716. }
  717. // ICQ direct-connect info. The TLV is required for buddy arrival events to
  718. // work in ICQ, even if the values are set to default.
  719. if baseUserFlags&wire.OServiceUserFlagICQ == wire.OServiceUserFlagICQ {
  720. if len(instances) > 0 {
  721. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoICQDC, instances[0].ICQDCInfo()))
  722. }
  723. }
  724. caps := s.Caps()
  725. if len(caps) > 0 {
  726. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, caps))
  727. }
  728. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)))
  729. return tlvs
  730. }
  731. // SessionInstance represents a single client connection instance within a user's
  732. // session. Multiple SessionInstance objects can belong to the same Session,
  733. // allowing a user to maintain concurrent connections from different clients or
  734. // devices.
  735. //
  736. // SessionInstance stores connection-specific state such as the remote address,
  737. // sign-on completion status, client capabilities, idle state, and per-connection
  738. // profile data. It holds a reference to its parent Session to access shared
  739. // user-level data like identity, warning levels, and rate limiting state.
  740. //
  741. // All methods on SessionInstance are safe for concurrent use.
  742. type SessionInstance struct {
  743. session *Session
  744. mutex sync.RWMutex
  745. // Unique instance identifier
  746. instanceNum uint8
  747. // Per-session connection state
  748. remoteAddr *netip.AddrPort
  749. signonComplete bool
  750. closed bool
  751. stopCh chan struct{}
  752. msgCh chan wire.SNACMessage
  753. kerberosAuth bool
  754. // Per-session client information
  755. clientID string
  756. capabilities [][16]byte
  757. foodGroupVersions [wire.MDir + 1]uint16
  758. multiConnFlag wire.MultiConnFlag
  759. toc2 bool
  760. toc2MsgEnc bool
  761. icqDCInfo wire.ICQDCInfo
  762. // Per-session state
  763. idle bool
  764. idleTime time.Time
  765. awayMsg string
  766. userInfoBitmask uint16
  767. userStatusBitmask uint32
  768. // contactsInit indicates whether the client-side buddy list or feedbag has been initialized
  769. contactsInit bool
  770. // notify transaction: accumulate visibility notification targets during feedbag batches
  771. notifyTxnActive bool
  772. notifyTxnShouldSend bool
  773. notifyTxnNames map[IdentScreenName]struct{}
  774. // Per-session profile
  775. profile UserProfile
  776. awayTime time.Time
  777. onInstanceCloseFn func()
  778. }
  779. // Session returns the parent Session for this instance.
  780. func (s *SessionInstance) Session() *Session {
  781. return s.session
  782. }
  783. //
  784. // Identity
  785. //
  786. // ChatRoomCookie returns the chat room cookie from the parent session.
  787. func (s *SessionInstance) ChatRoomCookie() string {
  788. return s.session.ChatRoomCookie()
  789. }
  790. // ClientID retrieves the instance's client ID.
  791. func (s *SessionInstance) ClientID() string {
  792. s.mutex.RLock()
  793. defer s.mutex.RUnlock()
  794. return s.clientID
  795. }
  796. // DisplayScreenName returns the user's display screen name.
  797. func (s *SessionInstance) DisplayScreenName() DisplayScreenName {
  798. return s.session.DisplayScreenName()
  799. }
  800. // IdentScreenName returns the user's identity screen name.
  801. func (s *SessionInstance) IdentScreenName() IdentScreenName {
  802. return s.session.IdentScreenName()
  803. }
  804. // Num returns the unique instance identifier.
  805. func (s *SessionInstance) Num() uint8 {
  806. return s.instanceNum
  807. }
  808. // UIN returns the user's ICQ number.
  809. func (s *SessionInstance) UIN() uint32 {
  810. return s.session.UIN()
  811. }
  812. // SetClientID sets the instance's client ID.
  813. func (s *SessionInstance) SetClientID(clientID string) {
  814. s.mutex.Lock()
  815. defer s.mutex.Unlock()
  816. s.clientID = clientID
  817. }
  818. // SetTOC2 sets this instance to TOC2. supportsTOC2MsgEnc is true for toc2_login (encoded messaging), false for toc2_signon.
  819. func (s *SessionInstance) SetTOC2(supportsTOC2MsgEnc bool) {
  820. s.mutex.Lock()
  821. defer s.mutex.Unlock()
  822. s.toc2 = true
  823. s.toc2MsgEnc = supportsTOC2MsgEnc
  824. }
  825. // IsTOC2 returns true when the client is TOC2 (with or without encoded messaging).
  826. func (s *SessionInstance) IsTOC2() bool {
  827. s.mutex.RLock()
  828. defer s.mutex.RUnlock()
  829. return s.toc2
  830. }
  831. // SupportsTOC2MsgEnc returns true only when TOC2 with encoded messaging (toc2_login).
  832. func (s *SessionInstance) SupportsTOC2MsgEnc() bool {
  833. s.mutex.RLock()
  834. defer s.mutex.RUnlock()
  835. return s.toc2MsgEnc
  836. }
  837. //
  838. // Status / Availability
  839. //
  840. // Away returns true if the instance is away.
  841. func (s *SessionInstance) Away() bool {
  842. s.mutex.RLock()
  843. defer s.mutex.RUnlock()
  844. return s.away()
  845. }
  846. // AwayMessage returns the instance's away message and the time it was set.
  847. func (s *SessionInstance) AwayMessage() (string, time.Time) {
  848. s.mutex.RLock()
  849. defer s.mutex.RUnlock()
  850. return s.awayMsg, s.awayTime
  851. }
  852. // Idle reports the instance's idle state.
  853. func (s *SessionInstance) Idle() bool {
  854. s.mutex.RLock()
  855. defer s.mutex.RUnlock()
  856. return s.idle
  857. }
  858. // IdleTime reports when the instance went idle.
  859. func (s *SessionInstance) IdleTime() time.Time {
  860. s.mutex.RLock()
  861. defer s.mutex.RUnlock()
  862. return s.idleTime
  863. }
  864. // Invisible returns true if the user is invisible.
  865. func (s *SessionInstance) Invisible() bool {
  866. s.mutex.RLock()
  867. defer s.mutex.RUnlock()
  868. return s.userStatusBitmask&wire.OServiceUserStatusInvisible == wire.OServiceUserStatusInvisible
  869. }
  870. // SetIdle sets the instance's idle state.
  871. func (s *SessionInstance) SetIdle(dur time.Duration) {
  872. s.mutex.Lock()
  873. defer s.mutex.Unlock()
  874. s.idle = true
  875. // set the time the instance became idle
  876. s.idleTime = s.session.nowFn().Add(-dur)
  877. }
  878. // SetSignonComplete indicates that the instance has completed the sign-on sequence.
  879. func (s *SessionInstance) SetSignonComplete() {
  880. s.mutex.Lock()
  881. defer s.mutex.Unlock()
  882. s.signonComplete = true
  883. }
  884. // SignonComplete indicates whether the instance has completed the sign-on sequence.
  885. func (s *SessionInstance) SignonComplete() bool {
  886. s.mutex.RLock()
  887. defer s.mutex.RUnlock()
  888. return s.signonComplete
  889. }
  890. // UnsetIdle removes the instance's idle state.
  891. func (s *SessionInstance) UnsetIdle() {
  892. s.mutex.Lock()
  893. defer s.mutex.Unlock()
  894. s.idle = false
  895. }
  896. // active returns true if the instance is active. An instance is considered active if:
  897. // - it is not closed
  898. // - it has completed the sign-on sequence
  899. // - it is not idle
  900. // - it is not away
  901. func (s *SessionInstance) active() bool {
  902. s.mutex.RLock()
  903. defer s.mutex.RUnlock()
  904. return !s.closed && s.signonComplete && !s.idle && !s.away()
  905. }
  906. // away checks if the instance is away based on bitmask flags.
  907. // This method must be called while holding the mutex lock.
  908. func (s *SessionInstance) away() bool {
  909. return s.userInfoBitmask&wire.OServiceUserFlagUnavailable != 0 ||
  910. s.userStatusBitmask&wire.OServiceUserStatusAway != 0
  911. }
  912. // live returns whether the instance is ready to receive messages.
  913. func (s *SessionInstance) live() bool {
  914. s.mutex.RLock()
  915. defer s.mutex.RUnlock()
  916. return !s.closed && s.signonComplete
  917. }
  918. //
  919. // Rate Limiting / Warning
  920. //
  921. // RateLimitStates returns the current rate limit states.
  922. func (s *SessionInstance) RateLimitStates() [5]RateClassState {
  923. return s.session.RateLimitStates()
  924. }
  925. // Warning returns the user's current warning level.
  926. func (s *SessionInstance) Warning() uint16 {
  927. return s.session.Warning()
  928. }
  929. // WarningCh returns the warning notification channel.
  930. func (s *SessionInstance) WarningCh() chan uint16 {
  931. return s.session.WarningCh()
  932. }
  933. //
  934. // Lifecycle
  935. //
  936. // Closed blocks until the instance is closed.
  937. func (s *SessionInstance) Closed() <-chan struct{} {
  938. return s.stopCh
  939. }
  940. // CloseInstance shuts down the instance's ability to relay messages and removes it from the session.
  941. func (s *SessionInstance) CloseInstance() {
  942. s.mutex.Lock()
  943. if s.closed {
  944. s.mutex.Unlock()
  945. return
  946. }
  947. close(s.stopCh)
  948. s.closed = true
  949. onInstanceCloseFn := s.onInstanceCloseFn
  950. s.mutex.Unlock()
  951. // remove the instance now so that the function has an updated view of the world
  952. s.session.RemoveInstance(s)
  953. count := s.session.InstanceCount()
  954. if count == 0 {
  955. s.session.mutex.RLock()
  956. onSessCloseFn := s.session.onSessCloseFn
  957. s.session.mutex.RUnlock()
  958. onSessCloseFn()
  959. } else {
  960. onInstanceCloseFn()
  961. }
  962. }
  963. // OnClose registers a function to be called when the instance closes,
  964. // but only if other instances remain in the session. If this is the last instance
  965. // to close, OnSessionClose will be called instead.
  966. func (s *SessionInstance) OnClose(fn func()) {
  967. s.mutex.Lock()
  968. defer s.mutex.Unlock()
  969. s.onInstanceCloseFn = fn
  970. }
  971. // ContactsInit returns whether the client-side buddy list has been loaded or
  972. // the feedbag has been initialized.
  973. func (s *SessionInstance) ContactsInit() bool {
  974. s.mutex.RLock()
  975. defer s.mutex.RUnlock()
  976. return s.contactsInit
  977. }
  978. // SetContactsInit indicates that the client-side buddy list has been loaded or
  979. // the feedbag has been initialized.
  980. func (s *SessionInstance) SetContactsInit() {
  981. s.mutex.Lock()
  982. defer s.mutex.Unlock()
  983. s.contactsInit = true
  984. }
  985. // BeginNotifyTxn starts accumulating notification targets during feedbag transactions.
  986. func (s *SessionInstance) BeginNotifyTxn() {
  987. s.mutex.Lock()
  988. defer s.mutex.Unlock()
  989. s.notifyTxnShouldSend = false
  990. s.notifyTxnNames = nil
  991. s.notifyTxnActive = true
  992. }
  993. // InNotifyTxn reports whether a notification transaction is open.
  994. func (s *SessionInstance) InNotifyTxn() bool {
  995. s.mutex.RLock()
  996. defer s.mutex.RUnlock()
  997. return s.notifyTxnActive
  998. }
  999. // NotifyTxn records that a visibility notification should be sent when the transaction
  1000. // ends, optionally limiting it to the given ident screen names. If no names are provided,
  1001. // all buddies will be notified.
  1002. func (s *SessionInstance) NotifyTxn(names ...IdentScreenName) error {
  1003. s.mutex.Lock()
  1004. defer s.mutex.Unlock()
  1005. if !s.notifyTxnActive {
  1006. return fmt.Errorf("notify transaction: %w", errNotifyTxnNotActive)
  1007. }
  1008. if len(s.notifyTxnNames)+len(names) > maxNotifyTxnNames {
  1009. return fmt.Errorf("notify transaction: %w", errNotifyTxnTooManyNames)
  1010. }
  1011. s.notifyTxnShouldSend = true
  1012. if s.notifyTxnNames == nil {
  1013. s.notifyTxnNames = make(map[IdentScreenName]struct{})
  1014. }
  1015. for _, name := range names {
  1016. s.notifyTxnNames[name] = struct{}{}
  1017. }
  1018. return nil
  1019. }
  1020. // EndNotifyTxn returns whether to send a notification and accumulated notification targets,
  1021. // then clears transaction state.
  1022. func (s *SessionInstance) EndNotifyTxn() (shouldNotify bool, screenNames []IdentScreenName) {
  1023. s.mutex.Lock()
  1024. defer func() {
  1025. s.notifyTxnActive = false
  1026. s.notifyTxnShouldSend = false
  1027. s.notifyTxnNames = nil
  1028. s.mutex.Unlock()
  1029. }()
  1030. if !s.notifyTxnActive {
  1031. return false, nil
  1032. }
  1033. shouldNotify = s.notifyTxnShouldSend
  1034. if len(s.notifyTxnNames) > 0 {
  1035. screenNames = make([]IdentScreenName, 0, len(s.notifyTxnNames))
  1036. for name := range s.notifyTxnNames {
  1037. screenNames = append(screenNames, name)
  1038. }
  1039. }
  1040. return shouldNotify, screenNames
  1041. }
  1042. // CloseInstance shuts down the instance's ability to relay messages.
  1043. func (s *SessionInstance) closeOnly() {
  1044. s.mutex.Lock()
  1045. if s.closed {
  1046. s.mutex.Unlock()
  1047. return
  1048. }
  1049. close(s.stopCh)
  1050. s.closed = true
  1051. s.mutex.Unlock()
  1052. s.session.RemoveInstance(s)
  1053. if s.session.InstanceCount() == 0 {
  1054. s.session.mutex.RLock()
  1055. onSessCloseFn := s.session.onSessCloseFn
  1056. s.session.mutex.RUnlock()
  1057. onSessCloseFn()
  1058. }
  1059. }
  1060. //
  1061. // User Settings / Attributes
  1062. //
  1063. // ClearUserInfoFlag clears a flag from the user info bitmask.
  1064. func (s *SessionInstance) ClearUserInfoFlag(flag uint16) (flags uint16) {
  1065. s.mutex.Lock()
  1066. defer s.mutex.Unlock()
  1067. s.userInfoBitmask &^= flag
  1068. return s.userInfoBitmask
  1069. }
  1070. // FoodGroupVersions retrieves the instance's supported food group versions.
  1071. func (s *SessionInstance) FoodGroupVersions() [wire.MDir + 1]uint16 {
  1072. s.mutex.RLock()
  1073. defer s.mutex.RUnlock()
  1074. return s.foodGroupVersions
  1075. }
  1076. // KerberosAuth indicates whether Kerberos authentication was used for this instance.
  1077. func (s *SessionInstance) KerberosAuth() bool {
  1078. s.mutex.RLock()
  1079. defer s.mutex.RUnlock()
  1080. return s.kerberosAuth
  1081. }
  1082. // MultiConnFlag retrieves the multi-connection flag for this instance.
  1083. func (s *SessionInstance) MultiConnFlag() wire.MultiConnFlag {
  1084. s.mutex.RLock()
  1085. defer s.mutex.RUnlock()
  1086. return s.multiConnFlag
  1087. }
  1088. // OfflineMsgCount returns the offline message count.
  1089. func (s *SessionInstance) OfflineMsgCount() int {
  1090. return s.session.OfflineMsgCount()
  1091. }
  1092. // Profile returns the user's profile information.
  1093. func (s *SessionInstance) Profile() UserProfile {
  1094. s.mutex.RLock()
  1095. defer s.mutex.RUnlock()
  1096. return s.profile
  1097. }
  1098. // RemoteAddr returns the instance's remote IP address.
  1099. func (s *SessionInstance) RemoteAddr() (remoteAddr *netip.AddrPort) {
  1100. s.mutex.RLock()
  1101. defer s.mutex.RUnlock()
  1102. return s.remoteAddr
  1103. }
  1104. // SetAwayMessage sets the instance's away message.
  1105. func (s *SessionInstance) SetAwayMessage(awayMessage string) {
  1106. s.mutex.Lock()
  1107. defer s.mutex.Unlock()
  1108. s.awayMsg = awayMessage
  1109. }
  1110. // SetCaps sets capability UUIDs for the instance.
  1111. func (s *SessionInstance) SetCaps(caps [][16]byte) {
  1112. s.mutex.Lock()
  1113. defer s.mutex.Unlock()
  1114. s.capabilities = caps
  1115. }
  1116. // SetFoodGroupVersions sets the instance's supported food group versions.
  1117. func (s *SessionInstance) SetFoodGroupVersions(versions [wire.MDir + 1]uint16) {
  1118. s.mutex.Lock()
  1119. defer s.mutex.Unlock()
  1120. s.foodGroupVersions = versions
  1121. }
  1122. // SetKerberosAuth sets whether Kerberos authentication was used for this instance.
  1123. func (s *SessionInstance) SetKerberosAuth(enabled bool) {
  1124. s.mutex.Lock()
  1125. defer s.mutex.Unlock()
  1126. s.kerberosAuth = enabled
  1127. }
  1128. // SetMultiConnFlag sets the multi-connection flag for this instance.
  1129. func (s *SessionInstance) SetMultiConnFlag(flag wire.MultiConnFlag) {
  1130. s.mutex.Lock()
  1131. defer s.mutex.Unlock()
  1132. s.multiConnFlag = flag
  1133. }
  1134. // SetProfile sets the user's profile information.
  1135. func (s *SessionInstance) SetProfile(profile UserProfile) {
  1136. s.mutex.Lock()
  1137. defer s.mutex.Unlock()
  1138. s.profile = profile
  1139. }
  1140. // SetRemoteAddr sets the instance's remote IP address.
  1141. func (s *SessionInstance) SetRemoteAddr(remoteAddr *netip.AddrPort) {
  1142. s.mutex.Lock()
  1143. defer s.mutex.Unlock()
  1144. s.remoteAddr = remoteAddr
  1145. }
  1146. // SetUserInfoFlag sets a flag on the user info bitmask.
  1147. func (s *SessionInstance) SetUserInfoFlag(flag uint16) {
  1148. s.mutex.Lock()
  1149. defer s.mutex.Unlock()
  1150. if flag == wire.OServiceUserFlagUnavailable {
  1151. s.awayTime = s.session.nowFn()
  1152. }
  1153. s.userInfoBitmask |= flag
  1154. }
  1155. // SetUserStatusBitmask sets the user status bitmask.
  1156. func (s *SessionInstance) SetUserStatusBitmask(bitmask uint32) {
  1157. s.mutex.Lock()
  1158. defer s.mutex.Unlock()
  1159. if bitmask&wire.OServiceUserStatusAway == wire.OServiceUserStatusAway {
  1160. if !s.away() {
  1161. s.awayTime = s.session.nowFn()
  1162. }
  1163. }
  1164. s.userStatusBitmask = bitmask
  1165. }
  1166. // SignonTime returns the session's sign-on time.
  1167. func (s *SessionInstance) SignonTime() time.Time {
  1168. return s.session.SignonTime()
  1169. }
  1170. // TypingEventsEnabled indicates whether the session wants to send and receive typing events.
  1171. func (s *SessionInstance) TypingEventsEnabled() bool {
  1172. return s.session.TypingEventsEnabled()
  1173. }
  1174. // UserInfoBitmask returns the user info bitmask.
  1175. func (s *SessionInstance) UserInfoBitmask() uint16 {
  1176. s.mutex.RLock()
  1177. defer s.mutex.RUnlock()
  1178. return s.userInfoBitmask
  1179. }
  1180. // UserStatusBitmask returns the user status bitmask.
  1181. func (s *SessionInstance) UserStatusBitmask() uint32 {
  1182. s.mutex.RLock()
  1183. defer s.mutex.RUnlock()
  1184. return s.userStatusBitmask
  1185. }
  1186. // SetICQDCInfo stores ICQ direct-connect settings from TLV 0x0C on SNAC(01,1E).
  1187. func (s *SessionInstance) SetICQDCInfo(info wire.ICQDCInfo) {
  1188. s.mutex.Lock()
  1189. defer s.mutex.Unlock()
  1190. s.icqDCInfo = info
  1191. }
  1192. // ICQDCInfo returns stored ICQ direct-connect settings.
  1193. func (s *SessionInstance) ICQDCInfo() wire.ICQDCInfo {
  1194. s.mutex.RLock()
  1195. defer s.mutex.RUnlock()
  1196. return s.icqDCInfo
  1197. }
  1198. // caps retrieves instance capabilities.
  1199. func (s *SessionInstance) caps() [][16]byte {
  1200. s.mutex.RLock()
  1201. defer s.mutex.RUnlock()
  1202. return s.capabilities
  1203. }
  1204. //
  1205. // Message Sending
  1206. //
  1207. // ReceiveMessage returns a channel of messages relayed via this instance.
  1208. func (s *SessionInstance) ReceiveMessage() chan wire.SNACMessage {
  1209. return s.msgCh
  1210. }
  1211. // RelayMessageToInstance receives a SNAC message and passes it to the instance's message channel.
  1212. func (s *SessionInstance) RelayMessageToInstance(msg wire.SNACMessage) SessSendStatus {
  1213. s.mutex.RLock()
  1214. defer s.mutex.RUnlock()
  1215. if s.closed {
  1216. return SessSendClosed
  1217. }
  1218. select {
  1219. case s.msgCh <- msg:
  1220. return SessSendOK
  1221. case <-s.stopCh:
  1222. return SessSendClosed
  1223. default:
  1224. return SessQueueFull
  1225. }
  1226. }