session.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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. invisible bool
  30. msgCh chan wire.SNACMessage
  31. mutex sync.RWMutex
  32. nowFn func() time.Time
  33. signonComplete bool
  34. signonTime time.Time
  35. stopCh chan struct{}
  36. warning uint16
  37. }
  38. // NewSession returns a new instance of Session. By default, the user may have
  39. // up to 1000 pending messages before blocking.
  40. func NewSession() *Session {
  41. return &Session{
  42. msgCh: make(chan wire.SNACMessage, 1000),
  43. nowFn: time.Now,
  44. stopCh: make(chan struct{}),
  45. signonTime: time.Now(),
  46. caps: make([][16]byte, 0),
  47. }
  48. }
  49. // IncrementWarning increments the user's warning level. To decrease, pass a
  50. // negative increment value.
  51. func (s *Session) IncrementWarning(incr uint16) {
  52. s.mutex.Lock()
  53. defer s.mutex.Unlock()
  54. s.warning += incr
  55. }
  56. // SetInvisible toggles the user's invisibility status.
  57. func (s *Session) SetInvisible(invisible bool) {
  58. s.mutex.Lock()
  59. defer s.mutex.Unlock()
  60. s.invisible = invisible
  61. }
  62. // Invisible returns true if the user is idle.
  63. func (s *Session) Invisible() bool {
  64. s.mutex.RLock()
  65. defer s.mutex.RUnlock()
  66. return s.invisible
  67. }
  68. // SetIdentScreenName sets the user's screen name.
  69. func (s *Session) SetIdentScreenName(screenName IdentScreenName) {
  70. s.mutex.Lock()
  71. defer s.mutex.Unlock()
  72. s.identScreenName = screenName
  73. }
  74. // IdentScreenName returns the user's screen name.
  75. func (s *Session) IdentScreenName() IdentScreenName {
  76. s.mutex.RLock()
  77. defer s.mutex.RUnlock()
  78. return s.identScreenName
  79. }
  80. // SetDisplayScreenName sets the user's screen name.
  81. func (s *Session) SetDisplayScreenName(displayScreenName DisplayScreenName) {
  82. s.mutex.Lock()
  83. defer s.mutex.Unlock()
  84. s.displayScreenName = displayScreenName
  85. }
  86. // DisplayScreenName returns the user's screen name.
  87. func (s *Session) DisplayScreenName() DisplayScreenName {
  88. s.mutex.RLock()
  89. defer s.mutex.RUnlock()
  90. return s.displayScreenName
  91. }
  92. // SetSignonTime sets the user's sign-ontime.
  93. func (s *Session) SetSignonTime(t time.Time) {
  94. s.mutex.Lock()
  95. defer s.mutex.Unlock()
  96. s.signonTime = t
  97. }
  98. // SetIdle sets the user's idle state.
  99. func (s *Session) SetIdle(dur time.Duration) {
  100. s.mutex.Lock()
  101. defer s.mutex.Unlock()
  102. s.idle = true
  103. // set the time the user became idle
  104. s.idleTime = s.nowFn().Add(-dur)
  105. }
  106. // UnsetIdle removes the user's idle state.
  107. func (s *Session) UnsetIdle() {
  108. s.mutex.Lock()
  109. defer s.mutex.Unlock()
  110. s.idle = false
  111. }
  112. // SetAwayMessage sets the user's away message.
  113. func (s *Session) SetAwayMessage(awayMessage string) {
  114. s.mutex.Lock()
  115. defer s.mutex.Unlock()
  116. s.awayMessage = awayMessage
  117. }
  118. // AwayMessage returns the user's away message.
  119. func (s *Session) AwayMessage() string {
  120. s.mutex.RLock()
  121. defer s.mutex.RUnlock()
  122. return s.awayMessage
  123. }
  124. // SetChatRoomCookie sets the chatRoomCookie for the chat room the user is currently in.
  125. func (s *Session) SetChatRoomCookie(cookie string) {
  126. s.mutex.Lock()
  127. defer s.mutex.Unlock()
  128. s.chatRoomCookie = cookie
  129. }
  130. // ChatRoomCookie gets the chatRoomCookie for the chat room the user is currently in.
  131. func (s *Session) ChatRoomCookie() string {
  132. s.mutex.RLock()
  133. defer s.mutex.RUnlock()
  134. return s.chatRoomCookie
  135. }
  136. // SignonComplete indicates whether the client has completed the sign-on sequence.
  137. func (s *Session) SignonComplete() bool {
  138. s.mutex.RLock()
  139. defer s.mutex.RUnlock()
  140. return s.signonComplete
  141. }
  142. // SetSignonComplete indicates that the client has completed the sign-on sequence.
  143. func (s *Session) SetSignonComplete() {
  144. s.mutex.Lock()
  145. defer s.mutex.Unlock()
  146. s.signonComplete = true
  147. }
  148. // TLVUserInfo returns a TLV list containing session information required by
  149. // multiple SNAC message types that convey user information.
  150. func (s *Session) TLVUserInfo() wire.TLVUserInfo {
  151. s.mutex.RLock()
  152. defer s.mutex.RUnlock()
  153. return wire.TLVUserInfo{
  154. ScreenName: string(s.displayScreenName),
  155. WarningLevel: s.warning,
  156. TLVBlock: wire.TLVBlock{
  157. TLVList: s.userInfo(),
  158. },
  159. }
  160. }
  161. func (s *Session) userInfo() wire.TLVList {
  162. tlvs := wire.TLVList{}
  163. // sign-in timestamp
  164. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(s.signonTime.Unix())))
  165. // away message status
  166. if s.awayMessage != "" {
  167. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoUserFlags, wire.OServiceUserFlagOSCARFree|wire.OServiceUserFlagUnavailable))
  168. } else {
  169. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoUserFlags, wire.OServiceUserFlagOSCARFree))
  170. }
  171. // reflects invisibility toggle status back to toggling client
  172. if s.invisible {
  173. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoStatus, wire.OServiceUserStatusInvisible))
  174. } else {
  175. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0)))
  176. }
  177. // idle status
  178. if s.idle {
  179. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(s.idleTime).Minutes())))
  180. }
  181. // capabilities (buddy icon, chat, etc...)
  182. if len(s.caps) > 0 {
  183. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoOscarCaps, s.caps))
  184. }
  185. return tlvs
  186. }
  187. // SetCaps sets capability UUIDs that represent the features the client
  188. // supports. If set, capability metadata appears in the user info TLV list.
  189. func (s *Session) SetCaps(caps [][16]byte) {
  190. s.mutex.Lock()
  191. defer s.mutex.Unlock()
  192. s.caps = caps
  193. }
  194. // Caps retrieves user capabilities.
  195. func (s *Session) Caps() [][16]byte {
  196. s.mutex.RLock()
  197. defer s.mutex.RUnlock()
  198. return s.caps
  199. }
  200. func (s *Session) Warning() uint16 {
  201. s.mutex.RLock()
  202. defer s.mutex.RUnlock()
  203. var w uint16
  204. w = s.warning
  205. return w
  206. }
  207. // ReceiveMessage returns a channel of messages relayed via this session. It
  208. // may only be read by one consumer. The channel never closes; call this method
  209. // in a select block along with Closed in order to detect session closure.
  210. func (s *Session) ReceiveMessage() chan wire.SNACMessage {
  211. return s.msgCh
  212. }
  213. // RelayMessage receives a SNAC message from a user and passes it on
  214. // asynchronously to the consumer of this session's messages. It returns
  215. // SessSendStatus to indicate whether the message was successfully sent or
  216. // not. This method is non-blocking.
  217. func (s *Session) RelayMessage(msg wire.SNACMessage) SessSendStatus {
  218. s.mutex.RLock()
  219. defer s.mutex.RUnlock()
  220. if s.closed {
  221. return SessSendClosed
  222. }
  223. select {
  224. case s.msgCh <- msg:
  225. return SessSendOK
  226. case <-s.stopCh:
  227. return SessSendClosed
  228. default:
  229. return SessQueueFull
  230. }
  231. }
  232. // Close shuts down the session's ability to relay messages. Once invoked,
  233. // RelayMessage returns SessQueueFull and Closed returns a closed channel.
  234. // It is not possible to re-open message relaying once closed. It is safe to
  235. // call from multiple go routines.
  236. func (s *Session) Close() {
  237. s.mutex.Lock()
  238. defer s.mutex.Unlock()
  239. if s.closed {
  240. return
  241. }
  242. close(s.stopCh)
  243. s.closed = true
  244. }
  245. // Closed blocks until the session is closed.
  246. func (s *Session) Closed() <-chan struct{} {
  247. return s.stopCh
  248. }