session.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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) GetTLVUserInfo() oscar.TLVUserInfo {
  78. return oscar.TLVUserInfo{
  79. ScreenName: s.ScreenName,
  80. WarningLevel: s.GetWarning(),
  81. TLVBlock: oscar.TLVBlock{
  82. TLVList: s.GetUserInfo(),
  83. },
  84. }
  85. }
  86. func (s *Session) GetUserInfo() []oscar.TLV {
  87. s.Mutex.RLock()
  88. defer s.Mutex.RUnlock()
  89. // sign-in timestamp
  90. tlvs := []oscar.TLV{
  91. {
  92. TType: 0x03,
  93. Val: uint32(s.SignonTime.Unix()),
  94. },
  95. }
  96. // away message status
  97. userFlags := oscar.TLV{
  98. TType: 0x01,
  99. Val: uint16(0x0010), // AIM client
  100. }
  101. if s.AwayMessage != "" {
  102. userFlags.Val = userFlags.Val.(uint16) | uint16(0x0020)
  103. }
  104. tlvs = append(tlvs, userFlags)
  105. // invisibility status
  106. status := oscar.TLV{
  107. TType: 0x06,
  108. Val: uint16(0x0000),
  109. }
  110. if s.invisible {
  111. status.Val = status.Val.(uint16) | uint16(0x0100)
  112. }
  113. tlvs = append(tlvs, status)
  114. // idle status
  115. idle := oscar.TLV{
  116. TType: 0x04,
  117. Val: uint16(0),
  118. }
  119. if s.idle {
  120. idle.Val = uint16(time.Now().Sub(s.idleTime).Seconds())
  121. }
  122. tlvs = append(tlvs, idle)
  123. // capabilities
  124. caps := oscar.TLV{
  125. TType: 0x0D,
  126. Val: []byte{},
  127. }
  128. // chat capability
  129. caps.Val = append(caps.Val.([]byte), CapChat...)
  130. tlvs = append(tlvs, caps)
  131. return tlvs
  132. }
  133. func (s *Session) GetWarning() uint16 {
  134. var w uint16
  135. s.Mutex.RLock()
  136. w = s.Warning
  137. s.Mutex.RUnlock()
  138. return w
  139. }
  140. func (s *Session) RecvMessage() chan XMessage {
  141. return s.msgCh
  142. }
  143. func (s *Session) SendMessage(msg XMessage) SessSendStatus {
  144. select {
  145. case s.msgCh <- msg:
  146. return SessSendOK
  147. case <-s.stopCh:
  148. return SessSendClosed
  149. case <-time.After(s.sendTimeout):
  150. return SessSendTimeout
  151. }
  152. }
  153. func (s *Session) Close() {
  154. s.Mutex.Lock()
  155. defer s.Mutex.Unlock()
  156. if s.closed {
  157. return
  158. }
  159. close(s.stopCh)
  160. s.closed = true
  161. }
  162. func (s *Session) Closed() <-chan struct{} {
  163. return s.stopCh
  164. }
  165. type InMemorySessionManager struct {
  166. store map[string]*Session
  167. mapMutex sync.RWMutex
  168. }
  169. func NewSessionManager() *InMemorySessionManager {
  170. return &InMemorySessionManager{
  171. store: make(map[string]*Session),
  172. }
  173. }
  174. func (s *InMemorySessionManager) Broadcast(msg XMessage) {
  175. s.mapMutex.RLock()
  176. defer s.mapMutex.RUnlock()
  177. for _, sess := range s.store {
  178. go s.maybeSendMessage(msg, sess)
  179. }
  180. }
  181. func (s *InMemorySessionManager) maybeSendMessage(msg XMessage, sess *Session) {
  182. switch sess.SendMessage(msg) {
  183. case SessSendClosed:
  184. fmt.Printf("message to %s was blocked, removing session\n", sess.ScreenName)
  185. case SessSendTimeout:
  186. fmt.Printf("message to %s timed out\n", sess.ScreenName)
  187. sess.Close()
  188. }
  189. }
  190. func (s *InMemorySessionManager) Empty() bool {
  191. s.mapMutex.RLock()
  192. defer s.mapMutex.RUnlock()
  193. return len(s.store) == 0
  194. }
  195. func (s *InMemorySessionManager) All() []*Session {
  196. s.mapMutex.RLock()
  197. defer s.mapMutex.RUnlock()
  198. var sessions []*Session
  199. for _, sess := range s.store {
  200. sessions = append(sessions, sess)
  201. }
  202. return sessions
  203. }
  204. func (s *InMemorySessionManager) BroadcastExcept(except *Session, msg XMessage) {
  205. s.mapMutex.RLock()
  206. defer s.mapMutex.RUnlock()
  207. for _, sess := range s.store {
  208. if sess == except {
  209. continue
  210. }
  211. go s.maybeSendMessage(msg, sess)
  212. }
  213. }
  214. func (s *InMemorySessionManager) Retrieve(ID string) (*Session, bool) {
  215. s.mapMutex.RLock()
  216. defer s.mapMutex.RUnlock()
  217. sess, found := s.store[ID]
  218. return sess, found
  219. }
  220. func (s *InMemorySessionManager) RetrieveByScreenName(screenName string) (*Session, error) {
  221. s.mapMutex.RLock()
  222. defer s.mapMutex.RUnlock()
  223. for _, sess := range s.store {
  224. if screenName == sess.ScreenName {
  225. return sess, nil
  226. }
  227. }
  228. return nil, fmt.Errorf("%w: %s", errSessNotFound, screenName)
  229. }
  230. func (s *InMemorySessionManager) retrieveByScreenNames(screenNames []string) []*Session {
  231. s.mapMutex.RLock()
  232. defer s.mapMutex.RUnlock()
  233. var ret []*Session
  234. for _, sn := range screenNames {
  235. for _, sess := range s.store {
  236. if sn == sess.ScreenName {
  237. ret = append(ret, sess)
  238. }
  239. }
  240. }
  241. return ret
  242. }
  243. func (s *InMemorySessionManager) SendToScreenName(screenName string, msg XMessage) {
  244. sess, err := s.RetrieveByScreenName(screenName)
  245. if err != nil {
  246. fmt.Printf("error sending to screen name: %s\n", screenName)
  247. return
  248. }
  249. go s.maybeSendMessage(msg, sess)
  250. }
  251. func (s *InMemorySessionManager) BroadcastToScreenNames(screenNames []string, msg XMessage) {
  252. for _, sess := range s.retrieveByScreenNames(screenNames) {
  253. go s.maybeSendMessage(msg, sess)
  254. }
  255. }
  256. func makeSession() *Session {
  257. return &Session{
  258. msgCh: make(chan XMessage, 1),
  259. stopCh: make(chan struct{}),
  260. sendTimeout: sendTimeout,
  261. SignonTime: time.Now(),
  262. }
  263. }
  264. func (s *InMemorySessionManager) NewSessionWithSN(sessID string, screenName string) *Session {
  265. s.mapMutex.Lock()
  266. defer s.mapMutex.Unlock()
  267. sess := makeSession()
  268. sess.ID = sessID
  269. sess.ScreenName = screenName
  270. s.store[sess.ID] = sess
  271. return sess
  272. }
  273. func (s *InMemorySessionManager) Remove(sess *Session) {
  274. s.mapMutex.Lock()
  275. defer s.mapMutex.Unlock()
  276. delete(s.store, sess.ID)
  277. }
  278. type ChatRoom struct {
  279. ID string
  280. SessionManager *InMemorySessionManager
  281. CreateTime time.Time
  282. Name string
  283. }
  284. func (c ChatRoom) TLVList() []oscar.TLV {
  285. return []oscar.TLV{
  286. {
  287. TType: 0x00c9,
  288. Val: uint16(15),
  289. },
  290. {
  291. TType: 0x00ca,
  292. Val: uint32(c.CreateTime.Unix()),
  293. },
  294. {
  295. TType: 0x00d1,
  296. Val: uint16(1024),
  297. },
  298. {
  299. TType: 0x00d2,
  300. Val: uint16(100),
  301. },
  302. {
  303. TType: 0x00d5,
  304. Val: uint8(2),
  305. },
  306. {
  307. TType: 0x006a,
  308. Val: c.Name,
  309. },
  310. {
  311. TType: 0x00d3,
  312. Val: c.Name,
  313. },
  314. }
  315. }
  316. type ChatRegistry struct {
  317. store map[string]ChatRoom
  318. mapMutex sync.RWMutex
  319. }
  320. func NewChatRegistry() *ChatRegistry {
  321. return &ChatRegistry{
  322. store: make(map[string]ChatRoom),
  323. }
  324. }
  325. func (c *ChatRegistry) Register(room ChatRoom) {
  326. c.mapMutex.Lock()
  327. defer c.mapMutex.Unlock()
  328. c.store[room.ID] = room
  329. }
  330. func (c *ChatRegistry) Retrieve(chatID string) (ChatRoom, error) {
  331. c.mapMutex.RLock()
  332. defer c.mapMutex.RUnlock()
  333. sm, found := c.store[chatID]
  334. if !found {
  335. return sm, errors.New("unable to find session manager for chat")
  336. }
  337. return sm, nil
  338. }
  339. func (c *ChatRegistry) MaybeRemoveRoom(chatID string) {
  340. c.mapMutex.Lock()
  341. defer c.mapMutex.Unlock()
  342. room, found := c.store[chatID]
  343. if found && room.SessionManager.Empty() {
  344. delete(c.store, chatID)
  345. }
  346. }