2017-10-02 10:42:50 +02:00
|
|
|
// Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
|
|
|
|
// released under the MIT license
|
|
|
|
|
|
|
|
package irc
|
|
|
|
|
2018-02-03 11:21:32 +01:00
|
|
|
import (
|
2019-05-08 10:11:54 +02:00
|
|
|
"net"
|
2019-05-09 20:18:30 +02:00
|
|
|
"sync/atomic"
|
2019-03-12 00:24:45 +01:00
|
|
|
"time"
|
2019-05-09 20:18:30 +02:00
|
|
|
"unsafe"
|
2019-03-12 00:24:45 +01:00
|
|
|
|
2019-02-19 08:54:57 +01:00
|
|
|
"github.com/oragono/oragono/irc/languages"
|
2018-02-03 11:21:32 +01:00
|
|
|
"github.com/oragono/oragono/irc/modes"
|
2020-09-24 08:44:12 +02:00
|
|
|
"github.com/oragono/oragono/irc/utils"
|
2018-02-03 11:21:32 +01:00
|
|
|
)
|
2017-10-05 15:39:57 +02:00
|
|
|
|
2019-02-19 08:54:57 +01:00
|
|
|
func (server *Server) Config() (config *Config) {
|
2019-05-09 20:18:30 +02:00
|
|
|
return (*Config)(atomic.LoadPointer(&server.config))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (server *Server) SetConfig(config *Config) {
|
|
|
|
atomic.StorePointer(&server.config, unsafe.Pointer(config))
|
2018-03-18 02:32:12 +01:00
|
|
|
}
|
|
|
|
|
2017-11-09 04:19:50 +01:00
|
|
|
func (server *Server) ChannelRegistrationEnabled() bool {
|
2018-07-16 09:46:40 +02:00
|
|
|
return server.Config().Channels.Registration.Enabled
|
2017-11-09 04:19:50 +01:00
|
|
|
}
|
|
|
|
|
2018-04-19 08:48:19 +02:00
|
|
|
func (server *Server) GetOperator(name string) (oper *Oper) {
|
|
|
|
name, err := CasefoldName(name)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2019-05-09 20:18:30 +02:00
|
|
|
return server.Config().operators[name]
|
2018-04-19 08:48:19 +02:00
|
|
|
}
|
|
|
|
|
2019-02-19 08:54:57 +01:00
|
|
|
func (server *Server) Languages() (lm *languages.Manager) {
|
|
|
|
return server.Config().languageManager
|
|
|
|
}
|
|
|
|
|
2020-07-08 11:32:14 +02:00
|
|
|
func (server *Server) Defcon() uint32 {
|
|
|
|
return atomic.LoadUint32(&server.defcon)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (server *Server) SetDefcon(defcon uint32) {
|
|
|
|
atomic.StoreUint32(&server.defcon, defcon)
|
|
|
|
}
|
|
|
|
|
2019-04-12 06:08:46 +02:00
|
|
|
func (client *Client) Sessions() (sessions []*Session) {
|
|
|
|
client.stateMutex.RLock()
|
2019-08-02 06:20:31 +02:00
|
|
|
sessions = client.sessions
|
2019-04-12 06:08:46 +02:00
|
|
|
client.stateMutex.RUnlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-22 03:40:25 +02:00
|
|
|
func (client *Client) GetSessionByResumeID(resumeID string) (result *Session) {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
|
|
|
|
for _, session := range client.sessions {
|
|
|
|
if session.resumeID == resumeID {
|
|
|
|
return session
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-08 10:11:54 +02:00
|
|
|
type SessionData struct {
|
2020-09-19 19:19:41 +02:00
|
|
|
ctime time.Time
|
|
|
|
atime time.Time
|
|
|
|
ip net.IP
|
|
|
|
hostname string
|
|
|
|
certfp string
|
|
|
|
deviceID string
|
2020-09-24 08:44:12 +02:00
|
|
|
connInfo string
|
2020-09-19 19:19:41 +02:00
|
|
|
sessionID int64
|
2019-05-08 10:11:54 +02:00
|
|
|
}
|
|
|
|
|
2020-09-24 14:35:03 +02:00
|
|
|
func (client *Client) AllSessionData(currentSession *Session, hasPrivs bool) (data []SessionData, currentIndex int) {
|
2019-05-08 10:11:54 +02:00
|
|
|
currentIndex = -1
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
|
|
|
|
data = make([]SessionData, len(client.sessions))
|
|
|
|
for i, session := range client.sessions {
|
|
|
|
if session == currentSession {
|
|
|
|
currentIndex = i
|
|
|
|
}
|
|
|
|
data[i] = SessionData{
|
2020-09-19 19:19:41 +02:00
|
|
|
atime: session.lastActive,
|
|
|
|
ctime: session.ctime,
|
|
|
|
hostname: session.rawHostname,
|
|
|
|
certfp: session.certfp,
|
|
|
|
deviceID: session.deviceID,
|
|
|
|
sessionID: session.sessionID,
|
2019-05-08 10:11:54 +02:00
|
|
|
}
|
|
|
|
if session.proxiedIP != nil {
|
|
|
|
data[i].ip = session.proxiedIP
|
|
|
|
} else {
|
|
|
|
data[i].ip = session.realIP
|
|
|
|
}
|
2020-09-24 14:35:03 +02:00
|
|
|
if hasPrivs {
|
|
|
|
data[i].connInfo = utils.DescribeConn(session.socket.conn.UnderlyingConn().Conn)
|
|
|
|
}
|
2019-05-08 10:11:54 +02:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-19 20:12:20 +02:00
|
|
|
func (client *Client) AddSession(session *Session) (success bool, numSessions int, lastSeen time.Time, back bool) {
|
2021-03-18 07:53:18 +01:00
|
|
|
config := client.server.Config()
|
2019-04-12 06:08:46 +02:00
|
|
|
client.stateMutex.Lock()
|
|
|
|
defer client.stateMutex.Unlock()
|
|
|
|
|
2019-05-22 03:40:25 +02:00
|
|
|
// client may be dying and ineligible to receive another session
|
2019-05-28 10:56:56 +02:00
|
|
|
if client.destroyed {
|
2020-02-19 01:38:42 +01:00
|
|
|
return
|
2019-04-12 06:08:46 +02:00
|
|
|
}
|
2019-05-22 03:40:25 +02:00
|
|
|
// success, attach the new session to the client
|
2019-04-12 06:08:46 +02:00
|
|
|
session.client = client
|
2020-09-19 19:19:41 +02:00
|
|
|
session.sessionID = client.nextSessionID
|
|
|
|
client.nextSessionID++
|
2019-08-02 06:20:31 +02:00
|
|
|
newSessions := make([]*Session, len(client.sessions)+1)
|
|
|
|
copy(newSessions, client.sessions)
|
|
|
|
newSessions[len(newSessions)-1] = session
|
2020-06-18 09:38:00 +02:00
|
|
|
if client.accountSettings.AutoreplayMissed || session.deviceID != "" {
|
2020-06-12 21:51:48 +02:00
|
|
|
lastSeen = client.lastSeen[session.deviceID]
|
2020-06-27 00:02:22 +02:00
|
|
|
client.setLastSeen(time.Now().UTC(), session.deviceID)
|
2020-02-19 01:38:42 +01:00
|
|
|
}
|
2019-08-02 06:20:31 +02:00
|
|
|
client.sessions = newSessions
|
2021-03-18 07:53:18 +01:00
|
|
|
// TODO(#1551) there should be a cap to opt out of this behavior on a session
|
|
|
|
if persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) {
|
2020-05-19 20:12:20 +02:00
|
|
|
client.awayMessage = ""
|
2021-03-18 07:53:18 +01:00
|
|
|
if len(client.sessions) == 1 {
|
|
|
|
back = true
|
|
|
|
}
|
2020-05-19 20:12:20 +02:00
|
|
|
}
|
|
|
|
return true, len(client.sessions), lastSeen, back
|
2019-04-12 06:08:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) removeSession(session *Session) (success bool, length int) {
|
|
|
|
if len(client.sessions) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sessions := make([]*Session, 0, len(client.sessions)-1)
|
|
|
|
for _, currentSession := range client.sessions {
|
|
|
|
if session == currentSession {
|
|
|
|
success = true
|
|
|
|
} else {
|
|
|
|
sessions = append(sessions, currentSession)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
client.sessions = sessions
|
|
|
|
length = len(sessions)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-22 03:40:25 +02:00
|
|
|
func (session *Session) SetResumeID(resumeID string) {
|
|
|
|
session.client.stateMutex.Lock()
|
|
|
|
session.resumeID = resumeID
|
|
|
|
session.client.stateMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
2017-11-03 07:36:55 +01:00
|
|
|
func (client *Client) Nick() string {
|
2017-10-04 06:57:03 +02:00
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.nick
|
|
|
|
}
|
|
|
|
|
2017-11-03 07:36:55 +01:00
|
|
|
func (client *Client) NickMaskString() string {
|
2017-10-04 06:57:03 +02:00
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.nickMaskString
|
|
|
|
}
|
|
|
|
|
2017-11-03 07:36:55 +01:00
|
|
|
func (client *Client) NickCasefolded() string {
|
2017-10-04 06:57:03 +02:00
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.nickCasefolded
|
|
|
|
}
|
2017-10-15 18:24:28 +02:00
|
|
|
|
2018-12-23 19:25:02 +01:00
|
|
|
func (client *Client) NickMaskCasefolded() string {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.nickMaskCasefolded
|
|
|
|
}
|
|
|
|
|
2017-10-23 01:50:16 +02:00
|
|
|
func (client *Client) Username() string {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.username
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) Hostname() string {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.hostname
|
|
|
|
}
|
|
|
|
|
2020-07-17 07:55:13 +02:00
|
|
|
func (client *Client) Away() (result bool, message string) {
|
2019-04-28 21:10:03 +02:00
|
|
|
client.stateMutex.Lock()
|
2021-03-18 07:53:18 +01:00
|
|
|
message = client.awayMessage
|
2019-04-28 21:10:03 +02:00
|
|
|
client.stateMutex.Unlock()
|
2021-03-18 07:53:18 +01:00
|
|
|
result = client.awayMessage != ""
|
2019-04-28 21:10:03 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-03-18 07:53:18 +01:00
|
|
|
func (session *Session) SetAway(awayMessage string) {
|
|
|
|
client := session.client
|
|
|
|
config := client.server.Config()
|
|
|
|
|
2019-04-28 21:10:03 +02:00
|
|
|
client.stateMutex.Lock()
|
2021-03-18 07:53:18 +01:00
|
|
|
defer client.stateMutex.Unlock()
|
|
|
|
|
|
|
|
session.awayMessage = awayMessage
|
|
|
|
session.awayAt = time.Now().UTC()
|
|
|
|
|
|
|
|
autoAway := client.registered && client.alwaysOn && persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway)
|
|
|
|
if autoAway {
|
|
|
|
client.setAutoAwayNoMutex(config)
|
|
|
|
} else {
|
|
|
|
client.awayMessage = awayMessage
|
|
|
|
}
|
2019-04-28 21:10:03 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-03-18 07:53:18 +01:00
|
|
|
func (client *Client) setAutoAwayNoMutex(config *Config) {
|
|
|
|
// aggregate the away statuses of the individual sessions:
|
|
|
|
var globalAwayState string
|
|
|
|
var awaySetAt time.Time
|
|
|
|
for _, cSession := range client.sessions {
|
|
|
|
if cSession.awayMessage == "" {
|
|
|
|
// a session is active, we are not auto-away
|
|
|
|
client.awayMessage = ""
|
|
|
|
return
|
|
|
|
} else if cSession.awayAt.After(awaySetAt) {
|
|
|
|
// choose the latest available away message from any session
|
|
|
|
globalAwayState = cSession.awayMessage
|
|
|
|
awaySetAt = cSession.awayAt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if awaySetAt.IsZero() {
|
|
|
|
// no sessions, enable auto-away
|
|
|
|
client.awayMessage = config.languageManager.Translate(client.languages, `User is currently disconnected`)
|
|
|
|
} else {
|
|
|
|
client.awayMessage = globalAwayState
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-19 01:38:42 +01:00
|
|
|
func (client *Client) AlwaysOn() (alwaysOn bool) {
|
2020-12-02 09:56:00 +01:00
|
|
|
client.stateMutex.RLock()
|
2020-07-26 21:51:33 +02:00
|
|
|
alwaysOn = client.registered && client.alwaysOn
|
2020-12-02 09:56:00 +01:00
|
|
|
client.stateMutex.RUnlock()
|
2020-02-19 01:38:42 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-31 00:59:49 +01:00
|
|
|
// uniqueIdentifiers returns the strings for which the server enforces per-client
|
|
|
|
// uniqueness/ownership; no two clients can have colliding casefolded nicks or
|
|
|
|
// skeletons.
|
|
|
|
func (client *Client) uniqueIdentifiers() (nickCasefolded string, skeleton string) {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.nickCasefolded, client.skeleton
|
|
|
|
}
|
|
|
|
|
2019-02-12 06:27:57 +01:00
|
|
|
func (client *Client) ResumeID() string {
|
2018-11-26 11:23:27 +01:00
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
2019-02-12 06:27:57 +01:00
|
|
|
return client.resumeID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) SetResumeID(id string) {
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
defer client.stateMutex.Unlock()
|
|
|
|
client.resumeID = id
|
2018-11-26 11:23:27 +01:00
|
|
|
}
|
|
|
|
|
2018-04-19 08:48:19 +02:00
|
|
|
func (client *Client) Oper() *Oper {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.oper
|
|
|
|
}
|
|
|
|
|
2020-08-06 09:16:58 +02:00
|
|
|
func (client *Client) Registered() (result bool) {
|
2019-02-10 02:01:47 +01:00
|
|
|
// `registered` is only written from the client's own goroutine, but may be
|
|
|
|
// read from other goroutines; therefore, the client's own goroutine may read
|
|
|
|
// the value without synchronization, but must write it with synchronization,
|
|
|
|
// and other goroutines must read it with synchronization
|
2020-08-06 09:16:58 +02:00
|
|
|
client.stateMutex.RLock()
|
|
|
|
result = client.registered
|
|
|
|
client.stateMutex.RUnlock()
|
|
|
|
return
|
2019-02-10 02:01:47 +01:00
|
|
|
}
|
|
|
|
|
2019-02-26 03:50:43 +01:00
|
|
|
func (client *Client) RawHostname() (result string) {
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
result = client.rawHostname
|
|
|
|
client.stateMutex.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-02-17 20:29:04 +01:00
|
|
|
func (client *Client) AwayMessage() (result string) {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
result = client.awayMessage
|
|
|
|
client.stateMutex.RUnlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-02-11 11:30:40 +01:00
|
|
|
func (client *Client) Account() string {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return client.account
|
|
|
|
}
|
|
|
|
|
2017-11-09 04:19:50 +01:00
|
|
|
func (client *Client) AccountName() string {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
2018-02-11 11:30:40 +01:00
|
|
|
return client.accountName
|
|
|
|
}
|
|
|
|
|
2020-02-19 01:38:42 +01:00
|
|
|
func (client *Client) Login(account ClientAccount) {
|
2020-02-21 05:55:42 +01:00
|
|
|
alwaysOn := persistenceEnabled(client.server.Config().Accounts.Multiclient.AlwaysOn, account.Settings.AlwaysOn)
|
2020-02-19 01:38:42 +01:00
|
|
|
client.stateMutex.Lock()
|
|
|
|
defer client.stateMutex.Unlock()
|
|
|
|
client.account = account.NameCasefolded
|
|
|
|
client.accountName = account.Name
|
|
|
|
client.accountSettings = account.Settings
|
2020-07-26 21:51:33 +02:00
|
|
|
// mark always-on here: it will not be respected until the client is registered
|
|
|
|
client.alwaysOn = alwaysOn
|
2020-02-19 01:38:42 +01:00
|
|
|
client.accountRegDate = account.RegisteredAt
|
|
|
|
return
|
|
|
|
}
|
2018-02-20 10:50:46 +01:00
|
|
|
|
2020-11-11 01:59:12 +01:00
|
|
|
func (client *Client) setAccountName(name string) {
|
|
|
|
// XXX this assumes validation elsewhere
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
defer client.stateMutex.Unlock()
|
|
|
|
client.accountName = name
|
|
|
|
}
|
|
|
|
|
2021-01-12 14:40:13 +01:00
|
|
|
func (client *Client) setCloakedHostname(cloak string) {
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
defer client.stateMutex.Unlock()
|
|
|
|
client.cloakedHostname = cloak
|
|
|
|
client.updateNickMaskNoMutex()
|
|
|
|
}
|
|
|
|
|
2021-01-19 14:49:45 +01:00
|
|
|
func (client *Client) CloakedHostname() string {
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
defer client.stateMutex.Unlock()
|
|
|
|
return client.cloakedHostname
|
|
|
|
}
|
|
|
|
|
2020-02-19 01:38:42 +01:00
|
|
|
func (client *Client) historyCutoff() (cutoff time.Time) {
|
2018-02-11 11:30:40 +01:00
|
|
|
client.stateMutex.Lock()
|
2020-02-19 01:38:42 +01:00
|
|
|
if client.account != "" {
|
|
|
|
cutoff = client.accountRegDate
|
|
|
|
} else {
|
|
|
|
cutoff = client.ctime
|
|
|
|
}
|
|
|
|
client.stateMutex.Unlock()
|
2018-02-20 10:50:46 +01:00
|
|
|
return
|
2017-11-09 04:19:50 +01:00
|
|
|
}
|
|
|
|
|
2020-02-19 01:38:42 +01:00
|
|
|
func (client *Client) Logout() {
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
client.account = ""
|
2020-04-06 19:10:38 +02:00
|
|
|
client.accountName = "*"
|
2020-02-19 01:38:42 +01:00
|
|
|
client.alwaysOn = false
|
2020-02-21 12:36:32 +01:00
|
|
|
client.accountRegDate = time.Time{}
|
|
|
|
client.accountSettings = AccountSettings{}
|
2020-02-19 01:38:42 +01:00
|
|
|
client.stateMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
2019-05-19 10:27:44 +02:00
|
|
|
func (client *Client) AccountSettings() (result AccountSettings) {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
result = client.accountSettings
|
|
|
|
client.stateMutex.RUnlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) SetAccountSettings(settings AccountSettings) {
|
2020-03-02 07:53:02 +01:00
|
|
|
// we mark dirty if the client is transitioning to always-on
|
2020-12-21 11:11:50 +01:00
|
|
|
var becameAlwaysOn bool
|
2020-02-21 05:55:42 +01:00
|
|
|
alwaysOn := persistenceEnabled(client.server.Config().Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
|
2019-05-19 10:27:44 +02:00
|
|
|
client.stateMutex.Lock()
|
2020-02-19 01:38:42 +01:00
|
|
|
if client.registered {
|
2020-07-27 12:08:06 +02:00
|
|
|
// only allow the client to become always-on if their nick equals their account name
|
|
|
|
alwaysOn = alwaysOn && client.nick == client.accountName
|
2020-06-12 21:51:48 +02:00
|
|
|
becameAlwaysOn = (!client.alwaysOn && alwaysOn)
|
2020-02-19 01:38:42 +01:00
|
|
|
client.alwaysOn = alwaysOn
|
|
|
|
}
|
2020-06-12 21:51:48 +02:00
|
|
|
client.accountSettings = settings
|
2019-05-19 10:27:44 +02:00
|
|
|
client.stateMutex.Unlock()
|
2020-06-12 21:51:48 +02:00
|
|
|
if becameAlwaysOn {
|
2020-03-02 07:53:02 +01:00
|
|
|
client.markDirty(IncludeAllAttrs)
|
|
|
|
}
|
2019-05-19 10:27:44 +02:00
|
|
|
}
|
|
|
|
|
2019-02-19 08:54:57 +01:00
|
|
|
func (client *Client) Languages() (languages []string) {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
languages = client.languages
|
|
|
|
client.stateMutex.RUnlock()
|
|
|
|
return languages
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) SetLanguages(languages []string) {
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
client.languages = languages
|
|
|
|
client.stateMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
2018-02-03 11:21:32 +01:00
|
|
|
func (client *Client) HasMode(mode modes.Mode) bool {
|
2018-04-23 00:47:10 +02:00
|
|
|
// client.flags has its own synch
|
2020-02-19 01:38:42 +01:00
|
|
|
return client.modes.HasMode(mode)
|
2018-04-23 00:47:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) SetMode(mode modes.Mode, on bool) bool {
|
2020-02-19 01:38:42 +01:00
|
|
|
return client.modes.SetMode(mode, on)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) SetRealname(realname string) {
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
client.realname = realname
|
2020-07-26 21:51:33 +02:00
|
|
|
alwaysOn := client.registered && client.alwaysOn
|
2020-02-19 01:38:42 +01:00
|
|
|
client.stateMutex.Unlock()
|
2020-07-06 10:08:04 +02:00
|
|
|
if alwaysOn {
|
|
|
|
client.markDirty(IncludeRealname)
|
|
|
|
}
|
2017-10-23 01:50:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) Channels() (result []*Channel) {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
length := len(client.channels)
|
|
|
|
result = make([]*Channel, length)
|
|
|
|
i := 0
|
|
|
|
for channel := range client.channels {
|
|
|
|
result[i] = channel
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-02-06 10:55:05 +01:00
|
|
|
func (client *Client) NumChannels() int {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
|
|
|
return len(client.channels)
|
|
|
|
}
|
|
|
|
|
2018-05-04 06:24:54 +02:00
|
|
|
func (client *Client) WhoWas() (result WhoWas) {
|
2019-01-01 19:00:16 +01:00
|
|
|
return client.Details().WhoWas
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *Client) Details() (result ClientDetails) {
|
2018-05-04 06:24:54 +02:00
|
|
|
client.stateMutex.RLock()
|
|
|
|
defer client.stateMutex.RUnlock()
|
2019-05-09 00:14:49 +02:00
|
|
|
return client.detailsNoMutex()
|
|
|
|
}
|
2018-05-04 06:24:54 +02:00
|
|
|
|
2019-05-09 00:14:49 +02:00
|
|
|
func (client *Client) detailsNoMutex() (result ClientDetails) {
|
2019-01-01 19:00:16 +01:00
|
|
|
result.nick = client.nick
|
|
|
|
result.nickCasefolded = client.nickCasefolded
|
2018-05-04 06:24:54 +02:00
|
|
|
result.username = client.username
|
2019-02-06 20:14:32 +01:00
|
|
|
result.hostname = client.hostname
|
2018-05-04 06:24:54 +02:00
|
|
|
result.realname = client.realname
|
2019-01-01 19:00:16 +01:00
|
|
|
result.nickMask = client.nickMaskString
|
|
|
|
result.nickMaskCasefolded = client.nickMaskCasefolded
|
|
|
|
result.account = client.account
|
|
|
|
result.accountName = client.accountName
|
2018-05-04 06:24:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-29 10:32:39 +02:00
|
|
|
func (client *Client) UpdateActive(session *Session) {
|
|
|
|
now := time.Now().UTC()
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
defer client.stateMutex.Unlock()
|
|
|
|
client.lastActive = now
|
|
|
|
session.lastActive = now
|
|
|
|
}
|
|
|
|
|
2020-07-12 22:31:51 +02:00
|
|
|
func (client *Client) Realname() string {
|
|
|
|
client.stateMutex.RLock()
|
|
|
|
result := client.realname
|
|
|
|
client.stateMutex.RUnlock()
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-12-21 11:11:50 +01:00
|
|
|
func (client *Client) IsExpiredAlwaysOn(config *Config) (result bool) {
|
|
|
|
client.stateMutex.Lock()
|
|
|
|
defer client.stateMutex.Unlock()
|
2021-01-15 12:50:35 +01:00
|
|
|
return client.checkAlwaysOnExpirationNoMutex(config, false)
|
2020-12-21 11:11:50 +01:00
|
|
|
}
|
|
|
|
|
2021-01-15 12:50:35 +01:00
|
|
|
func (client *Client) checkAlwaysOnExpirationNoMutex(config *Config, ignoreRegistration bool) (result bool) {
|
|
|
|
if !((client.registered || ignoreRegistration) && client.alwaysOn) {
|
2020-12-21 11:11:50 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
deadline := time.Duration(config.Accounts.Multiclient.AlwaysOnExpiration)
|
|
|
|
if deadline == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
now := time.Now()
|
|
|
|
for _, ts := range client.lastSeen {
|
|
|
|
if now.Sub(ts) < deadline {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2017-10-23 01:50:16 +02:00
|
|
|
func (channel *Channel) Name() string {
|
|
|
|
channel.stateMutex.RLock()
|
|
|
|
defer channel.stateMutex.RUnlock()
|
|
|
|
return channel.name
|
|
|
|
}
|
|
|
|
|
2017-10-30 10:21:47 +01:00
|
|
|
func (channel *Channel) NameCasefolded() string {
|
|
|
|
channel.stateMutex.RLock()
|
|
|
|
defer channel.stateMutex.RUnlock()
|
|
|
|
return channel.nameCasefolded
|
|
|
|
}
|
|
|
|
|
2019-03-12 00:24:45 +01:00
|
|
|
func (channel *Channel) Rename(name, nameCasefolded string) {
|
2017-10-30 10:21:47 +01:00
|
|
|
channel.stateMutex.Lock()
|
2019-03-12 00:24:45 +01:00
|
|
|
channel.name = name
|
2020-08-04 16:13:29 +02:00
|
|
|
if channel.nameCasefolded != nameCasefolded {
|
|
|
|
channel.nameCasefolded = nameCasefolded
|
|
|
|
if channel.registeredFounder != "" {
|
|
|
|
channel.registeredTime = time.Now().UTC()
|
|
|
|
}
|
2019-03-12 00:24:45 +01:00
|
|
|
}
|
|
|
|
channel.stateMutex.Unlock()
|
2017-10-30 10:21:47 +01:00
|
|
|
}
|
|
|
|
|
2017-10-23 01:50:16 +02:00
|
|
|
func (channel *Channel) Members() (result []*Client) {
|
|
|
|
channel.stateMutex.RLock()
|
|
|
|
defer channel.stateMutex.RUnlock()
|
|
|
|
return channel.membersCache
|
|
|
|
}
|
|
|
|
|
2018-12-28 19:45:55 +01:00
|
|
|
func (channel *Channel) setUserLimit(limit int) {
|
2017-10-23 01:50:16 +02:00
|
|
|
channel.stateMutex.Lock()
|
|
|
|
channel.userLimit = limit
|
|
|
|
channel.stateMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (channel *Channel) setKey(key string) {
|
|
|
|
channel.stateMutex.Lock()
|
2018-04-04 03:49:40 +02:00
|
|
|
defer channel.stateMutex.Unlock()
|
2017-10-23 01:50:16 +02:00
|
|
|
channel.key = key
|
|
|
|
}
|
|
|
|
|
2017-11-09 04:19:50 +01:00
|
|
|
func (channel *Channel) Founder() string {
|
|
|
|
channel.stateMutex.RLock()
|
|
|
|
defer channel.stateMutex.RUnlock()
|
|
|
|
return channel.registeredFounder
|
|
|
|
}
|
2019-03-12 00:24:45 +01:00
|
|
|
|
2019-04-23 06:05:12 +02:00
|
|
|
func (channel *Channel) HighestUserMode(client *Client) (result modes.Mode) {
|
|
|
|
channel.stateMutex.RLock()
|
2021-01-21 03:13:18 +01:00
|
|
|
clientModes := channel.members[client].modes
|
2019-04-23 06:05:12 +02:00
|
|
|
channel.stateMutex.RUnlock()
|
|
|
|
return clientModes.HighestChannelUserMode()
|
|
|
|
}
|
2020-02-19 01:38:42 +01:00
|
|
|
|
|
|
|
func (channel *Channel) Settings() (result ChannelSettings) {
|
|
|
|
channel.stateMutex.RLock()
|
|
|
|
result = channel.settings
|
|
|
|
channel.stateMutex.RUnlock()
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func (channel *Channel) SetSettings(settings ChannelSettings) {
|
|
|
|
channel.stateMutex.Lock()
|
|
|
|
channel.settings = settings
|
|
|
|
channel.stateMutex.Unlock()
|
|
|
|
channel.MarkDirty(IncludeSettings)
|
|
|
|
}
|
2020-12-14 11:00:21 +01:00
|
|
|
|
|
|
|
func (channel *Channel) setForward(forward string) {
|
|
|
|
channel.stateMutex.Lock()
|
|
|
|
channel.forward = forward
|
|
|
|
channel.stateMutex.Unlock()
|
|
|
|
}
|
2020-12-15 10:00:44 +01:00
|
|
|
|
|
|
|
func (channel *Channel) Ctime() (ctime time.Time) {
|
|
|
|
channel.stateMutex.RLock()
|
|
|
|
ctime = channel.createdTime
|
|
|
|
channel.stateMutex.RUnlock()
|
|
|
|
return
|
|
|
|
}
|
2021-01-19 14:49:45 +01:00
|
|
|
|
|
|
|
func (channel *Channel) getAmode(cfaccount string) (result modes.Mode) {
|
|
|
|
channel.stateMutex.RLock()
|
|
|
|
defer channel.stateMutex.RUnlock()
|
|
|
|
return channel.accountToUMode[cfaccount]
|
|
|
|
}
|