session.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. chatRoomCookie string
  23. closed bool
  24. id string
  25. idle bool
  26. idleTime time.Time
  27. invisible bool
  28. msgCh chan wire.SNACMessage
  29. mutex sync.RWMutex
  30. nowFn func() time.Time
  31. signonComplete bool
  32. screenName string
  33. signonTime time.Time
  34. stopCh chan struct{}
  35. warning uint16
  36. caps [][16]byte
  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. // SetScreenName sets the user's screen name.
  69. func (s *Session) SetScreenName(screenName string) {
  70. s.mutex.Lock()
  71. defer s.mutex.Unlock()
  72. s.screenName = screenName
  73. }
  74. // ScreenName returns the user's screen name.
  75. func (s *Session) ScreenName() string {
  76. s.mutex.RLock()
  77. defer s.mutex.RUnlock()
  78. return s.screenName
  79. }
  80. // SetID sets the user's session ID.
  81. func (s *Session) SetID(ID string) {
  82. s.mutex.Lock()
  83. defer s.mutex.Unlock()
  84. s.id = ID
  85. }
  86. // ID returns the user's session ID.
  87. func (s *Session) ID() string {
  88. s.mutex.RLock()
  89. defer s.mutex.RUnlock()
  90. return s.id
  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: s.screenName,
  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, uint16(wire.OServiceUserFlagOSCARFree|wire.OServiceUserFlagUnavailable)))
  168. } else {
  169. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(wire.OServiceUserFlagOSCARFree)))
  170. }
  171. // invisibility status
  172. if s.invisible {
  173. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoStatus, uint16(wire.OServiceUserFlagInvisible)))
  174. } else {
  175. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoStatus, uint16(0)))
  176. }
  177. // idle status
  178. if s.idle {
  179. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(s.idleTime).Seconds())))
  180. } else {
  181. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoIdleTime, uint16(0)))
  182. }
  183. // capabilities (buddy icon, chat, etc...)
  184. if len(s.caps) > 0 {
  185. tlvs.Append(wire.NewTLV(wire.OServiceUserInfoOscarCaps, s.caps))
  186. }
  187. return tlvs
  188. }
  189. // SetCaps sets capability UUIDs that represent the features the client
  190. // supports. If set, capability metadata appears in the user info TLV list.
  191. func (s *Session) SetCaps(caps [][16]byte) {
  192. s.mutex.Lock()
  193. defer s.mutex.Unlock()
  194. s.caps = caps
  195. }
  196. // Caps retrieves user capabilities.
  197. func (s *Session) Caps() [][16]byte {
  198. s.mutex.RLock()
  199. defer s.mutex.RUnlock()
  200. return s.caps
  201. }
  202. func (s *Session) Warning() uint16 {
  203. s.mutex.RLock()
  204. defer s.mutex.RUnlock()
  205. var w uint16
  206. w = s.warning
  207. return w
  208. }
  209. // ReceiveMessage returns a channel of messages relayed via this session. It
  210. // may only be read by one consumer. The channel never closes; call this method
  211. // in a select block along with Closed in order to detect session closure.
  212. func (s *Session) ReceiveMessage() chan wire.SNACMessage {
  213. return s.msgCh
  214. }
  215. // RelayMessage receives a SNAC message from a user and passes it on
  216. // asynchronously to the consumer of this session's messages. It returns
  217. // SessSendStatus to indicate whether the message was successfully sent or
  218. // not. This method is non-blocking.
  219. func (s *Session) RelayMessage(msg wire.SNACMessage) SessSendStatus {
  220. s.mutex.RLock()
  221. defer s.mutex.RUnlock()
  222. if s.closed {
  223. return SessSendClosed
  224. }
  225. select {
  226. case s.msgCh <- msg:
  227. return SessSendOK
  228. case <-s.stopCh:
  229. return SessSendClosed
  230. default:
  231. return SessQueueFull
  232. }
  233. }
  234. // Close shuts down the session's ability to relay messages. Once invoked,
  235. // RelayMessage returns SessQueueFull and Closed returns a closed channel.
  236. // It is not possible to re-open message relaying once closed. It is safe to
  237. // call from multiple go routines.
  238. func (s *Session) Close() {
  239. s.mutex.Lock()
  240. defer s.mutex.Unlock()
  241. if s.closed {
  242. return
  243. }
  244. close(s.stopCh)
  245. s.closed = true
  246. }
  247. // Closed blocks until the session is closed.
  248. func (s *Session) Closed() <-chan struct{} {
  249. return s.stopCh
  250. }