session.go 42 KB

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