session.go 41 KB

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