session.go 7.5 KB

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