session.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. package server
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/mkaminski/goaim/oscar"
  6. "sync"
  7. "time"
  8. )
  9. var (
  10. ErrSessNotFound = errors.New("session was not found")
  11. ErrSignedOff = errors.New("user signed off")
  12. )
  13. type SessSendStatus int
  14. const (
  15. // SessSendOK indicates message was sent to recipient
  16. SessSendOK SessSendStatus = iota
  17. // SessSendClosed indicates send did not complete because session is closed
  18. SessSendClosed
  19. // SessSendTimeout indicates send timed out due to blocked recipient
  20. SessSendTimeout
  21. )
  22. const sendTimeout = 10 * time.Second
  23. type Session struct {
  24. ID string
  25. ScreenName string
  26. msgCh chan XMessage
  27. stopCh chan struct{}
  28. Mutex sync.RWMutex
  29. Warning uint16
  30. AwayMessage string
  31. SignonTime time.Time
  32. invisible bool
  33. idle bool
  34. idleTime time.Time
  35. sendTimeout time.Duration
  36. closed bool
  37. }
  38. func (s *Session) IncreaseWarning(incr uint16) {
  39. s.Mutex.RLock()
  40. defer s.Mutex.RUnlock()
  41. s.Warning += incr
  42. }
  43. func (s *Session) SetInvisible(invisible bool) {
  44. s.Mutex.RLock()
  45. defer s.Mutex.RUnlock()
  46. s.invisible = invisible
  47. }
  48. func (s *Session) Invisible() bool {
  49. s.Mutex.RLock()
  50. defer s.Mutex.RUnlock()
  51. return s.invisible
  52. }
  53. func (s *Session) SetIdle(dur time.Duration) {
  54. s.Mutex.RLock()
  55. defer s.Mutex.RUnlock()
  56. s.idle = true
  57. // set the time the user became idle
  58. s.idleTime = time.Now().Add(-dur)
  59. }
  60. func (s *Session) SetActive() {
  61. s.Mutex.RLock()
  62. defer s.Mutex.RUnlock()
  63. s.idle = false
  64. }
  65. func (s *Session) Idle() bool {
  66. s.Mutex.RLock()
  67. defer s.Mutex.RUnlock()
  68. return s.idle
  69. }
  70. func (s *Session) SetAwayMessage(awayMessage string) {
  71. s.Mutex.RLock()
  72. defer s.Mutex.RUnlock()
  73. s.AwayMessage = awayMessage
  74. }
  75. func (s *Session) GetAwayMessage() string {
  76. s.Mutex.RLock()
  77. defer s.Mutex.RUnlock()
  78. return s.AwayMessage
  79. }
  80. func (s *Session) GetTLVUserInfo() oscar.TLVUserInfo {
  81. return oscar.TLVUserInfo{
  82. ScreenName: s.ScreenName,
  83. WarningLevel: s.GetWarning(),
  84. TLVBlock: oscar.TLVBlock{
  85. TLVList: s.GetUserInfo(),
  86. },
  87. }
  88. }
  89. func (s *Session) GetUserInfo() oscar.TLVList {
  90. s.Mutex.RLock()
  91. defer s.Mutex.RUnlock()
  92. // sign-in timestamp
  93. tlvs := oscar.TLVList{}
  94. tlvs.AddTLV(oscar.NewTLV(0x03, uint32(s.SignonTime.Unix())))
  95. // away message status
  96. if s.AwayMessage != "" {
  97. tlvs.AddTLV(oscar.NewTLV(0x01, uint16(0x0010)|uint16(0x0020)))
  98. } else {
  99. tlvs.AddTLV(oscar.NewTLV(0x01, uint16(0x0010)))
  100. }
  101. // invisibility status
  102. if s.invisible {
  103. tlvs.AddTLV(oscar.NewTLV(0x06, uint16(0x0100)))
  104. } else {
  105. tlvs.AddTLV(oscar.NewTLV(0x06, uint16(0x0000)))
  106. }
  107. // idle status
  108. if s.idle {
  109. tlvs.AddTLV(oscar.NewTLV(0x04, uint16(time.Now().Sub(s.idleTime).Seconds())))
  110. } else {
  111. tlvs.AddTLV(oscar.NewTLV(0x04, uint16(0)))
  112. }
  113. // capabilities
  114. var caps []byte
  115. // chat capability
  116. caps = append(caps, CapChat...)
  117. tlvs.AddTLV(oscar.NewTLV(0x0D, caps))
  118. return tlvs
  119. }
  120. func (s *Session) GetWarning() uint16 {
  121. var w uint16
  122. s.Mutex.RLock()
  123. w = s.Warning
  124. s.Mutex.RUnlock()
  125. return w
  126. }
  127. func (s *Session) RecvMessage() chan XMessage {
  128. return s.msgCh
  129. }
  130. func (s *Session) SendMessage(msg XMessage) SessSendStatus {
  131. select {
  132. case s.msgCh <- msg:
  133. return SessSendOK
  134. case <-s.stopCh:
  135. return SessSendClosed
  136. case <-time.After(s.sendTimeout):
  137. return SessSendTimeout
  138. }
  139. }
  140. func (s *Session) Close() {
  141. s.Mutex.Lock()
  142. defer s.Mutex.Unlock()
  143. if s.closed {
  144. return
  145. }
  146. close(s.stopCh)
  147. s.closed = true
  148. }
  149. func (s *Session) Closed() <-chan struct{} {
  150. return s.stopCh
  151. }
  152. type InMemorySessionManager struct {
  153. store map[string]*Session
  154. mapMutex sync.RWMutex
  155. }
  156. func NewSessionManager() *InMemorySessionManager {
  157. return &InMemorySessionManager{
  158. store: make(map[string]*Session),
  159. }
  160. }
  161. func (s *InMemorySessionManager) Broadcast(msg XMessage) {
  162. s.mapMutex.RLock()
  163. defer s.mapMutex.RUnlock()
  164. for _, sess := range s.store {
  165. go s.maybeSendMessage(msg, sess)
  166. }
  167. }
  168. func (s *InMemorySessionManager) maybeSendMessage(msg XMessage, sess *Session) {
  169. switch sess.SendMessage(msg) {
  170. case SessSendClosed:
  171. fmt.Printf("can't send message to %s because the session was closed: %+v\n", sess.ScreenName, msg)
  172. case SessSendTimeout:
  173. fmt.Printf("message to %s timed out\n", sess.ScreenName)
  174. sess.Close()
  175. }
  176. }
  177. func (s *InMemorySessionManager) Empty() bool {
  178. s.mapMutex.RLock()
  179. defer s.mapMutex.RUnlock()
  180. return len(s.store) == 0
  181. }
  182. func (s *InMemorySessionManager) Participants() []*Session {
  183. s.mapMutex.RLock()
  184. defer s.mapMutex.RUnlock()
  185. var sessions []*Session
  186. for _, sess := range s.store {
  187. sessions = append(sessions, sess)
  188. }
  189. return sessions
  190. }
  191. func (s *InMemorySessionManager) BroadcastExcept(except *Session, msg XMessage) {
  192. s.mapMutex.RLock()
  193. defer s.mapMutex.RUnlock()
  194. for _, sess := range s.store {
  195. if sess == except {
  196. continue
  197. }
  198. go s.maybeSendMessage(msg, sess)
  199. }
  200. }
  201. func (s *InMemorySessionManager) Retrieve(ID string) (*Session, bool) {
  202. s.mapMutex.RLock()
  203. defer s.mapMutex.RUnlock()
  204. sess, found := s.store[ID]
  205. return sess, found
  206. }
  207. func (s *InMemorySessionManager) RetrieveByScreenName(screenName string) (*Session, error) {
  208. s.mapMutex.RLock()
  209. defer s.mapMutex.RUnlock()
  210. for _, sess := range s.store {
  211. if screenName == sess.ScreenName {
  212. return sess, nil
  213. }
  214. }
  215. return nil, fmt.Errorf("%w: %s", ErrSessNotFound, screenName)
  216. }
  217. func (s *InMemorySessionManager) retrieveByScreenNames(screenNames []string) []*Session {
  218. s.mapMutex.RLock()
  219. defer s.mapMutex.RUnlock()
  220. var ret []*Session
  221. for _, sn := range screenNames {
  222. for _, sess := range s.store {
  223. if sn == sess.ScreenName {
  224. ret = append(ret, sess)
  225. }
  226. }
  227. }
  228. return ret
  229. }
  230. func (s *InMemorySessionManager) SendToScreenName(screenName string, msg XMessage) {
  231. sess, err := s.RetrieveByScreenName(screenName)
  232. if err != nil {
  233. fmt.Printf("error sending to screen name: %s\n", screenName)
  234. return
  235. }
  236. go s.maybeSendMessage(msg, sess)
  237. }
  238. func (s *InMemorySessionManager) BroadcastToScreenNames(screenNames []string, msg XMessage) {
  239. for _, sess := range s.retrieveByScreenNames(screenNames) {
  240. go s.maybeSendMessage(msg, sess)
  241. }
  242. }
  243. func makeSession() *Session {
  244. return &Session{
  245. msgCh: make(chan XMessage, 1),
  246. stopCh: make(chan struct{}),
  247. sendTimeout: sendTimeout,
  248. SignonTime: time.Now(),
  249. }
  250. }
  251. func (s *InMemorySessionManager) NewSessionWithSN(sessID string, screenName string) *Session {
  252. s.mapMutex.Lock()
  253. defer s.mapMutex.Unlock()
  254. sess := makeSession()
  255. sess.ID = sessID
  256. sess.ScreenName = screenName
  257. s.store[sess.ID] = sess
  258. return sess
  259. }
  260. func (s *InMemorySessionManager) Remove(sess *Session) {
  261. s.mapMutex.Lock()
  262. defer s.mapMutex.Unlock()
  263. delete(s.store, sess.ID)
  264. }
  265. type ChatRoom struct {
  266. CreateTime time.Time
  267. DetailLevel uint8
  268. Exchange uint16
  269. Cookie string
  270. InstanceNumber uint16
  271. Name string
  272. SessionManager
  273. }
  274. func (c ChatRoom) TLVList() []oscar.TLV {
  275. return []oscar.TLV{
  276. oscar.NewTLV(0x00c9, uint16(15)),
  277. oscar.NewTLV(0x00ca, uint32(c.CreateTime.Unix())),
  278. oscar.NewTLV(0x00d1, uint16(1024)),
  279. oscar.NewTLV(0x00d2, uint16(100)),
  280. oscar.NewTLV(0x00d5, uint8(2)),
  281. oscar.NewTLV(0x006a, c.Name),
  282. oscar.NewTLV(0x00d3, c.Name),
  283. }
  284. }
  285. type ChatRegistry struct {
  286. store map[string]ChatRoom
  287. mapMutex sync.RWMutex
  288. }
  289. func NewChatRegistry() *ChatRegistry {
  290. return &ChatRegistry{
  291. store: make(map[string]ChatRoom),
  292. }
  293. }
  294. func (c *ChatRegistry) Register(room ChatRoom) {
  295. c.mapMutex.Lock()
  296. defer c.mapMutex.Unlock()
  297. c.store[room.Cookie] = room
  298. }
  299. func (c *ChatRegistry) Retrieve(chatID string) (ChatRoom, error) {
  300. c.mapMutex.RLock()
  301. defer c.mapMutex.RUnlock()
  302. sm, found := c.store[chatID]
  303. if !found {
  304. return sm, errors.New("unable to find session manager for chat")
  305. }
  306. return sm, nil
  307. }
  308. func (c *ChatRegistry) MaybeRemoveRoom(chatID string) {
  309. c.mapMutex.Lock()
  310. defer c.mapMutex.Unlock()
  311. room, found := c.store[chatID]
  312. if found && room.Empty() {
  313. delete(c.store, chatID)
  314. }
  315. }