session.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. package state
  2. import (
  3. "sync"
  4. "time"
  5. "github.com/mk6i/retro-aim-server/wire"
  6. )
  7. // SessSendStatus is the result of sending a message to a user.
  8. type SessSendStatus int
  9. const (
  10. // SessSendOK indicates message was sent to recipient
  11. SessSendOK SessSendStatus = iota
  12. // SessSendClosed indicates send did not complete because session is closed
  13. SessSendClosed
  14. // SessQueueFull indicates send failed due to full queue -- client is likely
  15. // dead
  16. SessQueueFull
  17. )
  18. // Session represents a user's current session. Unless stated otherwise, all
  19. // methods may be safely accessed by multiple goroutines.
  20. type Session struct {
  21. awayMessage string
  22. caps [][16]byte
  23. chatRoomCookie string
  24. closed bool
  25. displayScreenName DisplayScreenName
  26. identScreenName IdentScreenName
  27. idle bool
  28. idleTime time.Time
  29. msgCh chan wire.SNACMessage
  30. mutex sync.RWMutex
  31. nowFn func() time.Time
  32. signonComplete bool
  33. signonTime time.Time
  34. stopCh chan struct{}
  35. uin uint32
  36. warning uint16
  37. userInfoBitmask uint16
  38. userStatusBitmask uint32
  39. clientID string
  40. }
  41. // NewSession returns a new instance of Session. By default, the user may have
  42. // up to 1000 pending messages before blocking.
  43. func NewSession() *Session {
  44. return &Session{
  45. msgCh: make(chan wire.SNACMessage, 1000),
  46. nowFn: time.Now,
  47. stopCh: make(chan struct{}),
  48. signonTime: time.Now(),
  49. caps: make([][16]byte, 0),
  50. userInfoBitmask: wire.OServiceUserFlagOSCARFree,
  51. userStatusBitmask: wire.OServiceUserStatusAvailable,
  52. }
  53. }
  54. // SetUserInfoFlag sets a flag to and returns UserInfoBitmask
  55. func (s *Session) SetUserInfoFlag(flag uint16) (flags uint16) {
  56. s.mutex.Lock()
  57. defer s.mutex.Unlock()
  58. s.userInfoBitmask |= flag
  59. return s.userInfoBitmask
  60. }
  61. // ClearUserInfoFlag clear a flag from and returns UserInfoBitmask
  62. func (s *Session) ClearUserInfoFlag(flag uint16) (flags uint16) {
  63. s.mutex.Lock()
  64. defer s.mutex.Unlock()
  65. s.userInfoBitmask &^= flag
  66. return s.userInfoBitmask
  67. }
  68. // UserInfoBitmask returns UserInfoBitmask
  69. func (s *Session) UserInfoBitmask() (flags uint16) {
  70. s.mutex.RLock()
  71. defer s.mutex.RUnlock()
  72. return s.userInfoBitmask
  73. }
  74. // SetUserStatusBitmask sets the user status bitmask from the client.
  75. func (s *Session) SetUserStatusBitmask(bitmask uint32) {
  76. s.mutex.Lock()
  77. defer s.mutex.Unlock()
  78. s.userStatusBitmask = bitmask
  79. }
  80. // IncrementWarning increments the user's warning level. To decrease, pass a
  81. // negative increment value.
  82. func (s *Session) IncrementWarning(incr uint16) {
  83. s.mutex.Lock()
  84. defer s.mutex.Unlock()
  85. s.warning += incr
  86. }
  87. // Invisible returns true if the user is idle.
  88. func (s *Session) Invisible() bool {
  89. s.mutex.RLock()
  90. defer s.mutex.RUnlock()
  91. return s.userStatusBitmask&wire.OServiceUserStatusInvisible == wire.OServiceUserStatusInvisible
  92. }
  93. // SetIdentScreenName sets the user's screen name.
  94. func (s *Session) SetIdentScreenName(screenName IdentScreenName) {
  95. s.mutex.Lock()
  96. defer s.mutex.Unlock()
  97. s.identScreenName = screenName
  98. }
  99. // IdentScreenName returns the user's screen name.
  100. func (s *Session) IdentScreenName() IdentScreenName {
  101. s.mutex.RLock()
  102. defer s.mutex.RUnlock()
  103. return s.identScreenName
  104. }
  105. // SetDisplayScreenName sets the user's screen name.
  106. func (s *Session) SetDisplayScreenName(displayScreenName DisplayScreenName) {
  107. s.mutex.Lock()
  108. defer s.mutex.Unlock()
  109. s.displayScreenName = displayScreenName
  110. }
  111. // DisplayScreenName returns the user's screen name.
  112. func (s *Session) DisplayScreenName() DisplayScreenName {
  113. s.mutex.RLock()
  114. defer s.mutex.RUnlock()
  115. return s.displayScreenName
  116. }
  117. // SetSignonTime sets the user's sign-ontime.
  118. func (s *Session) SetSignonTime(t time.Time) {
  119. s.mutex.Lock()
  120. defer s.mutex.Unlock()
  121. s.signonTime = t
  122. }
  123. // SignonTime reports when the user signed on
  124. func (s *Session) SignonTime() time.Time {
  125. s.mutex.RLock()
  126. defer s.mutex.RUnlock()
  127. return s.signonTime
  128. }
  129. // Idle reports the user's idle state.
  130. func (s *Session) Idle() bool {
  131. s.mutex.RLock()
  132. defer s.mutex.RUnlock()
  133. return s.idle
  134. }
  135. // IdleTime reports when the user went idle
  136. func (s *Session) IdleTime() time.Time {
  137. s.mutex.RLock()
  138. defer s.mutex.RUnlock()
  139. return s.idleTime
  140. }
  141. // SetIdle sets the user's idle state.
  142. func (s *Session) SetIdle(dur time.Duration) {
  143. s.mutex.Lock()
  144. defer s.mutex.Unlock()
  145. s.idle = true
  146. // set the time the user became idle
  147. s.idleTime = s.nowFn().Add(-dur)
  148. }
  149. // UnsetIdle removes the user's idle state.
  150. func (s *Session) UnsetIdle() {
  151. s.mutex.Lock()
  152. defer s.mutex.Unlock()
  153. s.idle = false
  154. }
  155. // SetAwayMessage sets the user's away message.
  156. func (s *Session) SetAwayMessage(awayMessage string) {
  157. s.mutex.Lock()
  158. defer s.mutex.Unlock()
  159. s.awayMessage = awayMessage
  160. }
  161. // AwayMessage returns the user's away message.
  162. func (s *Session) AwayMessage() string {
  163. s.mutex.RLock()
  164. defer s.mutex.RUnlock()
  165. return s.awayMessage
  166. }
  167. // SetChatRoomCookie sets the chatRoomCookie for the chat room the user is currently in.
  168. func (s *Session) SetChatRoomCookie(cookie string) {
  169. s.mutex.Lock()
  170. defer s.mutex.Unlock()
  171. s.chatRoomCookie = cookie
  172. }
  173. // ChatRoomCookie gets the chatRoomCookie for the chat room the user is currently in.
  174. func (s *Session) ChatRoomCookie() string {
  175. s.mutex.RLock()
  176. defer s.mutex.RUnlock()
  177. return s.chatRoomCookie
  178. }
  179. // SignonComplete indicates whether the client has completed the sign-on sequence.
  180. func (s *Session) SignonComplete() bool {
  181. s.mutex.RLock()
  182. defer s.mutex.RUnlock()
  183. return s.signonComplete
  184. }
  185. // SetSignonComplete indicates that the client has completed the sign-on sequence.
  186. func (s *Session) SetSignonComplete() {
  187. s.mutex.Lock()
  188. defer s.mutex.Unlock()
  189. s.signonComplete = true
  190. }
  191. // UIN returns the user's ICQ number.
  192. func (s *Session) UIN() uint32 {
  193. s.mutex.RLock()
  194. defer s.mutex.RUnlock()
  195. return s.uin
  196. }
  197. // SetUIN sets the user's ICQ number.
  198. func (s *Session) SetUIN(uin uint32) {
  199. s.mutex.Lock()
  200. defer s.mutex.Unlock()
  201. s.uin = uin
  202. }
  203. // TLVUserInfo returns a TLV list containing session information required by
  204. // multiple SNAC message types that convey user information.
  205. func (s *Session) TLVUserInfo() wire.TLVUserInfo {
  206. s.mutex.RLock()
  207. defer s.mutex.RUnlock()
  208. return wire.TLVUserInfo{
  209. ScreenName: string(s.displayScreenName),
  210. WarningLevel: s.warning,
  211. TLVBlock: wire.TLVBlock{
  212. TLVList: s.userInfo(),
  213. },
  214. }
  215. }
  216. func (s *Session) userInfo() wire.TLVList {
  217. tlvs := wire.TLVList{}
  218. // sign-in timestamp
  219. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(s.signonTime.Unix())))
  220. // user info flags
  221. uFlags := s.userInfoBitmask
  222. if s.awayMessage != "" {
  223. uFlags |= wire.OServiceUserFlagUnavailable
  224. }
  225. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uFlags))
  226. // user status flags
  227. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoStatus, s.userStatusBitmask))
  228. // idle status
  229. if s.idle {
  230. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(s.idleTime).Minutes())))
  231. }
  232. // ICQ direct-connect info. The TLV is required for buddy arrival events to
  233. // work in ICQ, even if the values are set to default.
  234. if s.userInfoBitmask&wire.OServiceUserFlagICQ == wire.OServiceUserFlagICQ {
  235. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}))
  236. }
  237. // capabilities (buddy icon, chat, etc...)
  238. if len(s.caps) > 0 {
  239. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, s.caps))
  240. }
  241. return tlvs
  242. }
  243. // SetCaps sets capability UUIDs that represent the features the client
  244. // supports. If set, capability metadata appears in the user info TLV list.
  245. func (s *Session) SetCaps(caps [][16]byte) {
  246. s.mutex.Lock()
  247. defer s.mutex.Unlock()
  248. s.caps = caps
  249. }
  250. // Caps retrieves user capabilities.
  251. func (s *Session) Caps() [][16]byte {
  252. s.mutex.RLock()
  253. defer s.mutex.RUnlock()
  254. return s.caps
  255. }
  256. func (s *Session) Warning() uint16 {
  257. s.mutex.RLock()
  258. defer s.mutex.RUnlock()
  259. var w uint16
  260. w = s.warning
  261. return w
  262. }
  263. // ReceiveMessage returns a channel of messages relayed via this session. It
  264. // may only be read by one consumer. The channel never closes; call this method
  265. // in a select block along with Closed in order to detect session closure.
  266. func (s *Session) ReceiveMessage() chan wire.SNACMessage {
  267. return s.msgCh
  268. }
  269. // RelayMessage receives a SNAC message from a user and passes it on
  270. // asynchronously to the consumer of this session's messages. It returns
  271. // SessSendStatus to indicate whether the message was successfully sent or
  272. // not. This method is non-blocking.
  273. func (s *Session) RelayMessage(msg wire.SNACMessage) SessSendStatus {
  274. s.mutex.RLock()
  275. defer s.mutex.RUnlock()
  276. if s.closed {
  277. return SessSendClosed
  278. }
  279. select {
  280. case s.msgCh <- msg:
  281. return SessSendOK
  282. case <-s.stopCh:
  283. return SessSendClosed
  284. default:
  285. return SessQueueFull
  286. }
  287. }
  288. // Close shuts down the session's ability to relay messages. Once invoked,
  289. // RelayMessage returns SessQueueFull and Closed returns a closed channel.
  290. // It is not possible to re-open message relaying once closed. It is safe to
  291. // call from multiple go routines.
  292. func (s *Session) Close() {
  293. s.mutex.Lock()
  294. defer s.mutex.Unlock()
  295. if s.closed {
  296. return
  297. }
  298. close(s.stopCh)
  299. s.closed = true
  300. }
  301. // Closed blocks until the session is closed.
  302. func (s *Session) Closed() <-chan struct{} {
  303. return s.stopCh
  304. }
  305. // SetClientID sets the client ID.
  306. func (s *Session) SetClientID(clientID string) {
  307. s.mutex.Lock()
  308. defer s.mutex.Unlock()
  309. s.clientID = clientID
  310. }
  311. // ClientID retrieves the client ID.
  312. func (s *Session) ClientID() string {
  313. s.mutex.RLock()
  314. defer s.mutex.RUnlock()
  315. return s.clientID
  316. }