2018-05-31 21:24:59 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/khlieng/dispatch/pkg/irc"
|
|
|
|
"github.com/khlieng/dispatch/pkg/session"
|
|
|
|
"github.com/khlieng/dispatch/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2019-01-23 06:34:39 +00:00
|
|
|
// AnonymousUserExpiration is the time to wait before removing an anonymous
|
|
|
|
// user that has no irc or websocket connections
|
2018-05-31 21:24:59 +00:00
|
|
|
AnonymousUserExpiration = 1 * time.Minute
|
|
|
|
)
|
|
|
|
|
2019-01-23 06:34:39 +00:00
|
|
|
// State is the live state of a single user
|
2018-05-31 21:24:59 +00:00
|
|
|
type State struct {
|
2019-01-23 06:34:39 +00:00
|
|
|
stateData
|
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
networks map[string]*storage.Network
|
2020-05-20 05:21:12 +00:00
|
|
|
pendingDCCSends map[string]*irc.DCCSend
|
2018-05-31 21:24:59 +00:00
|
|
|
|
|
|
|
ws map[string]*wsConn
|
|
|
|
broadcast chan WSResponse
|
|
|
|
|
|
|
|
srv *Dispatch
|
|
|
|
user *storage.User
|
|
|
|
expiration *time.Timer
|
|
|
|
reset chan time.Duration
|
2020-06-15 08:58:51 +00:00
|
|
|
lock sync.Mutex
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewState(user *storage.User, srv *Dispatch) *State {
|
|
|
|
return &State{
|
2019-01-23 06:34:39 +00:00
|
|
|
stateData: stateData{m: map[string]interface{}{}},
|
2020-06-15 08:58:51 +00:00
|
|
|
networks: make(map[string]*storage.Network),
|
2020-05-20 05:21:12 +00:00
|
|
|
pendingDCCSends: make(map[string]*irc.DCCSend),
|
2018-05-31 21:24:59 +00:00
|
|
|
ws: make(map[string]*wsConn),
|
|
|
|
broadcast: make(chan WSResponse, 32),
|
|
|
|
srv: srv,
|
|
|
|
user: user,
|
|
|
|
expiration: time.NewTimer(AnonymousUserExpiration),
|
|
|
|
reset: make(chan time.Duration, 1),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
func (s *State) network(host string) (*storage.Network, bool) {
|
|
|
|
s.lock.Lock()
|
|
|
|
n, ok := s.networks[host]
|
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
return n, ok
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
func (s *State) client(host string) (*irc.Client, bool) {
|
|
|
|
if network, ok := s.network(host); ok {
|
|
|
|
return network.Client(), true
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|
2020-06-15 08:58:51 +00:00
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) setNetwork(host string, network *storage.Network) {
|
|
|
|
s.lock.Lock()
|
|
|
|
s.networks[host] = network
|
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
|
|
|
|
s.reset <- 0
|
|
|
|
}
|
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
func (s *State) deleteNetwork(host string) {
|
|
|
|
s.lock.Lock()
|
|
|
|
delete(s.networks, host)
|
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
|
|
|
|
s.resetExpirationIfEmpty()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) numIRC() int {
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Lock()
|
|
|
|
n := len(s.networks)
|
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
func (s *State) pendingDCC(filename string) (*irc.DCCSend, bool) {
|
|
|
|
s.lock.Lock()
|
2020-05-20 05:21:12 +00:00
|
|
|
pack, ok := s.pendingDCCSends[filename]
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Unlock()
|
2020-05-20 05:21:12 +00:00
|
|
|
return pack, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) setPendingDCC(filename string, pack *irc.DCCSend) {
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Lock()
|
2020-05-20 05:21:12 +00:00
|
|
|
s.pendingDCCSends[filename] = pack
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Unlock()
|
2020-05-20 05:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) deletePendingDCC(filename string) {
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Lock()
|
2020-05-20 05:21:12 +00:00
|
|
|
delete(s.pendingDCCSends, filename)
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Unlock()
|
2020-05-20 05:21:12 +00:00
|
|
|
}
|
|
|
|
|
2018-05-31 21:24:59 +00:00
|
|
|
func (s *State) setWS(addr string, w *wsConn) {
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Lock()
|
2018-05-31 21:24:59 +00:00
|
|
|
s.ws[addr] = w
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
|
|
|
|
s.reset <- 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) deleteWS(addr string) {
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Lock()
|
2018-05-31 21:24:59 +00:00
|
|
|
delete(s.ws, addr)
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
|
|
|
|
s.resetExpirationIfEmpty()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) numWS() int {
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Lock()
|
2018-05-31 21:24:59 +00:00
|
|
|
n := len(s.ws)
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) sendJSON(t string, v interface{}) {
|
|
|
|
s.broadcast <- WSResponse{t, v}
|
|
|
|
}
|
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
func (s *State) sendLastMessages(network, channel string, count int) {
|
|
|
|
messages, hasMore, err := s.user.LastMessages(network, channel, count)
|
2018-05-31 21:24:59 +00:00
|
|
|
if err == nil && len(messages) > 0 {
|
|
|
|
res := Messages{
|
2020-06-15 08:58:51 +00:00
|
|
|
Network: network,
|
2018-05-31 21:24:59 +00:00
|
|
|
To: channel,
|
|
|
|
Messages: messages,
|
|
|
|
}
|
|
|
|
|
|
|
|
if hasMore {
|
|
|
|
res.Next = messages[0].ID
|
|
|
|
}
|
|
|
|
|
|
|
|
s.sendJSON("messages", res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
func (s *State) sendMessages(network, channel string, count int, fromID string) {
|
|
|
|
messages, hasMore, err := s.user.Messages(network, channel, count, fromID)
|
2018-05-31 21:24:59 +00:00
|
|
|
if err == nil && len(messages) > 0 {
|
|
|
|
res := Messages{
|
2020-06-15 08:58:51 +00:00
|
|
|
Network: network,
|
2018-05-31 21:24:59 +00:00
|
|
|
To: channel,
|
|
|
|
Messages: messages,
|
|
|
|
Prepend: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
if hasMore {
|
|
|
|
res.Next = messages[0].ID
|
|
|
|
}
|
|
|
|
|
|
|
|
s.sendJSON("messages", res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) resetExpirationIfEmpty() {
|
|
|
|
if s.numIRC() == 0 && s.numWS() == 0 {
|
|
|
|
s.reset <- AnonymousUserExpiration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) kill() {
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Lock()
|
2018-05-31 21:24:59 +00:00
|
|
|
for _, ws := range s.ws {
|
|
|
|
ws.conn.Close()
|
|
|
|
}
|
2020-06-15 08:58:51 +00:00
|
|
|
for _, network := range s.networks {
|
|
|
|
network.Client().Quit()
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *State) run() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case res := <-s.broadcast:
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Lock()
|
2018-05-31 21:24:59 +00:00
|
|
|
for _, ws := range s.ws {
|
|
|
|
ws.out <- res
|
|
|
|
}
|
2020-06-15 08:58:51 +00:00
|
|
|
s.lock.Unlock()
|
2018-05-31 21:24:59 +00:00
|
|
|
|
|
|
|
case <-s.expiration.C:
|
|
|
|
s.srv.states.delete(s.user.ID)
|
|
|
|
s.user.Remove()
|
|
|
|
return
|
|
|
|
|
|
|
|
case duration := <-s.reset:
|
|
|
|
if duration == 0 {
|
|
|
|
s.expiration.Stop()
|
|
|
|
} else {
|
|
|
|
s.expiration.Reset(duration)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-23 06:34:39 +00:00
|
|
|
type stateData struct {
|
|
|
|
m map[string]interface{}
|
|
|
|
lock sync.Mutex
|
|
|
|
}
|
|
|
|
|
2019-01-23 07:20:16 +00:00
|
|
|
func (s *stateData) Get(key string) interface{} {
|
2019-01-23 06:34:39 +00:00
|
|
|
s.lock.Lock()
|
|
|
|
v := s.m[key]
|
|
|
|
s.lock.Unlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2019-01-23 07:20:16 +00:00
|
|
|
func (s *stateData) Set(key string, value interface{}) {
|
2019-01-23 06:34:39 +00:00
|
|
|
s.lock.Lock()
|
|
|
|
s.m[key] = value
|
|
|
|
s.lock.Unlock()
|
|
|
|
}
|
|
|
|
|
2019-01-23 07:20:16 +00:00
|
|
|
func (s *stateData) String(key string) string {
|
2020-05-16 00:33:38 +00:00
|
|
|
if v, ok := s.Get(key).(string); ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
return ""
|
2019-01-23 06:34:39 +00:00
|
|
|
}
|
|
|
|
|
2019-01-23 07:20:16 +00:00
|
|
|
func (s *stateData) Int(key string) int {
|
2020-05-16 00:33:38 +00:00
|
|
|
if v, ok := s.Get(key).(int); ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
return 0
|
2019-01-23 06:34:39 +00:00
|
|
|
}
|
|
|
|
|
2019-01-23 07:20:16 +00:00
|
|
|
func (s *stateData) Bool(key string) bool {
|
2020-05-16 00:33:38 +00:00
|
|
|
if v, ok := s.Get(key).(bool); ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
return false
|
2019-01-23 06:34:39 +00:00
|
|
|
}
|
|
|
|
|
2018-05-31 21:24:59 +00:00
|
|
|
type stateStore struct {
|
|
|
|
states map[uint64]*State
|
|
|
|
sessions map[string]*session.Session
|
|
|
|
sessionStore storage.SessionStore
|
|
|
|
lock sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func newStateStore(sessionStore storage.SessionStore) *stateStore {
|
|
|
|
store := &stateStore{
|
|
|
|
states: make(map[uint64]*State),
|
|
|
|
sessions: make(map[string]*session.Session),
|
|
|
|
sessionStore: sessionStore,
|
|
|
|
}
|
|
|
|
|
2020-06-15 08:58:51 +00:00
|
|
|
sessions, err := sessionStore.Sessions()
|
2018-05-31 21:24:59 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2018-06-01 03:40:12 +00:00
|
|
|
log.Printf("[Init] %d sessions", len(sessions))
|
|
|
|
|
2018-05-31 21:24:59 +00:00
|
|
|
for _, session := range sessions {
|
|
|
|
if !session.Expired() {
|
2018-06-01 02:16:38 +00:00
|
|
|
store.sessions[session.Key()] = session
|
2018-05-31 21:24:59 +00:00
|
|
|
} else {
|
|
|
|
go sessionStore.DeleteSession(session.Key())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return store
|
|
|
|
}
|
|
|
|
|
2018-06-01 03:40:12 +00:00
|
|
|
func (s *stateStore) run() {
|
|
|
|
pruneSessions := time.Tick(time.Minute * 5)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-pruneSessions:
|
|
|
|
s.lock.Lock()
|
|
|
|
for key, session := range s.sessions {
|
|
|
|
if session.Expired() {
|
|
|
|
s.internalDeleteSession(key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.lock.Unlock()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-31 21:24:59 +00:00
|
|
|
func (s *stateStore) get(id uint64) *State {
|
|
|
|
s.lock.Lock()
|
|
|
|
state := s.states[id]
|
|
|
|
s.lock.Unlock()
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stateStore) set(state *State) {
|
|
|
|
s.lock.Lock()
|
|
|
|
s.states[state.user.ID] = state
|
|
|
|
s.lock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stateStore) delete(id uint64) {
|
|
|
|
s.lock.Lock()
|
|
|
|
delete(s.states, id)
|
|
|
|
for key, session := range s.sessions {
|
|
|
|
if session.UserID == id {
|
|
|
|
delete(s.sessions, key)
|
|
|
|
go s.sessionStore.DeleteSession(key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.lock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stateStore) getSession(key string) *session.Session {
|
|
|
|
s.lock.Lock()
|
|
|
|
session := s.sessions[key]
|
|
|
|
s.lock.Unlock()
|
|
|
|
return session
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stateStore) setSession(session *session.Session) {
|
|
|
|
s.lock.Lock()
|
|
|
|
s.sessions[session.Key()] = session
|
|
|
|
s.lock.Unlock()
|
2018-06-01 02:47:11 +00:00
|
|
|
go s.sessionStore.SaveSession(session)
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stateStore) deleteSession(key string) {
|
|
|
|
s.lock.Lock()
|
2018-06-01 03:40:12 +00:00
|
|
|
s.internalDeleteSession(key)
|
|
|
|
s.lock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stateStore) internalDeleteSession(key string) {
|
2018-05-31 21:24:59 +00:00
|
|
|
id := s.sessions[key].UserID
|
|
|
|
delete(s.sessions, key)
|
2018-06-01 03:40:12 +00:00
|
|
|
|
2018-05-31 21:24:59 +00:00
|
|
|
n := 0
|
|
|
|
for _, session := range s.sessions {
|
|
|
|
if session.UserID == id {
|
|
|
|
n++
|
|
|
|
}
|
|
|
|
}
|
2018-06-01 03:40:12 +00:00
|
|
|
|
2018-05-31 21:24:59 +00:00
|
|
|
state := s.states[id]
|
|
|
|
if n == 0 {
|
|
|
|
delete(s.states, id)
|
|
|
|
}
|
|
|
|
|
2018-06-01 03:40:12 +00:00
|
|
|
go func() {
|
|
|
|
if n == 0 {
|
|
|
|
// This anonymous user is not reachable anymore since all sessions have
|
|
|
|
// expired, so we clean it up
|
|
|
|
state.kill()
|
|
|
|
state.user.Remove()
|
|
|
|
}
|
2018-05-31 21:24:59 +00:00
|
|
|
|
2018-06-01 03:40:12 +00:00
|
|
|
s.sessionStore.DeleteSession(key)
|
|
|
|
}()
|
2018-05-31 21:24:59 +00:00
|
|
|
}
|