mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 10:42:52 +01:00
Add NickServ "CLIENTS LIST" and "CLIENTS LOGOUT".
CLIENTS LIST shows information about clients attached to a nick. CLIENTS LOGOUT allows individual (or all) sessions to be logged out. SESSIONS is now an alias for CLIENTS LIST. Fixes #1072.
This commit is contained in:
parent
536c21a874
commit
ca2132ff09
@ -90,6 +90,7 @@ type Client struct {
|
||||
lastSeen map[string]time.Time // maps device ID (including "") to time of last received command
|
||||
lastSeenLastWrite time.Time // last time `lastSeen` was written to the datastore
|
||||
loginThrottle connection_limits.GenericThrottle
|
||||
nextSessionID int64 // Incremented when a new session is established
|
||||
nick string
|
||||
nickCasefolded string
|
||||
nickMaskCasefolded string
|
||||
@ -150,6 +151,7 @@ type Session struct {
|
||||
idleTimer *time.Timer
|
||||
pingSent bool // we sent PING to a putatively idle connection and we're waiting for PONG
|
||||
|
||||
sessionID int64
|
||||
socket *Socket
|
||||
realIP net.IP
|
||||
proxiedIP net.IP
|
||||
@ -353,6 +355,7 @@ func (server *Server) RunClient(conn IRCConn) {
|
||||
realIP: realIP,
|
||||
proxiedIP: proxiedIP,
|
||||
requireSASL: requireSASL,
|
||||
nextSessionID: 1,
|
||||
}
|
||||
if requireSASL {
|
||||
client.requireSASLMessage = banMsg
|
||||
@ -420,6 +423,8 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string,
|
||||
|
||||
alwaysOn: true,
|
||||
realname: realname,
|
||||
|
||||
nextSessionID: 1,
|
||||
}
|
||||
|
||||
client.SetMode(modes.TLS, true)
|
||||
|
@ -65,12 +65,13 @@ func (client *Client) GetSessionByResumeID(resumeID string) (result *Session) {
|
||||
}
|
||||
|
||||
type SessionData struct {
|
||||
ctime time.Time
|
||||
atime time.Time
|
||||
ip net.IP
|
||||
hostname string
|
||||
certfp string
|
||||
deviceID string
|
||||
ctime time.Time
|
||||
atime time.Time
|
||||
ip net.IP
|
||||
hostname string
|
||||
certfp string
|
||||
deviceID string
|
||||
sessionID int64
|
||||
}
|
||||
|
||||
func (client *Client) AllSessionData(currentSession *Session) (data []SessionData, currentIndex int) {
|
||||
@ -84,11 +85,12 @@ func (client *Client) AllSessionData(currentSession *Session) (data []SessionDat
|
||||
currentIndex = i
|
||||
}
|
||||
data[i] = SessionData{
|
||||
atime: session.lastActive,
|
||||
ctime: session.ctime,
|
||||
hostname: session.rawHostname,
|
||||
certfp: session.certfp,
|
||||
deviceID: session.deviceID,
|
||||
atime: session.lastActive,
|
||||
ctime: session.ctime,
|
||||
hostname: session.rawHostname,
|
||||
certfp: session.certfp,
|
||||
deviceID: session.deviceID,
|
||||
sessionID: session.sessionID,
|
||||
}
|
||||
if session.proxiedIP != nil {
|
||||
data[i].ip = session.proxiedIP
|
||||
@ -109,6 +111,8 @@ func (client *Client) AddSession(session *Session) (success bool, numSessions in
|
||||
}
|
||||
// success, attach the new session to the client
|
||||
session.client = client
|
||||
session.sessionID = client.nextSessionID
|
||||
client.nextSessionID++
|
||||
newSessions := make([]*Session, len(client.sessions)+1)
|
||||
copy(newSessions, client.sessions)
|
||||
newSessions[len(newSessions)-1] = session
|
||||
|
114
irc/nickserv.go
114
irc/nickserv.go
@ -43,6 +43,23 @@ const nickservHelp = `NickServ lets you register, log in to, and manage an accou
|
||||
|
||||
var (
|
||||
nickservCommands = map[string]*serviceCommand{
|
||||
"clients": {
|
||||
handler: nsClientsHandler,
|
||||
help: `Syntax: $bCLIENTS LIST [nickname]$b
|
||||
|
||||
CLIENTS LIST shows information about the clients currently attached, via
|
||||
the server's multiclient functionality, to your nickname. An administrator
|
||||
can use this command to list another user's clients.
|
||||
|
||||
Syntax: $bCLIENTS LOGOUT [nickname] [client_id/all]$b
|
||||
|
||||
CLIENTS LOGOUT detaches a single client, or all other clients currently
|
||||
attached, via the server's multiclient functionality, to your nickname. An
|
||||
administrator can use this command to logout another user's clients.`,
|
||||
helpShort: `$bCLIENTS$b can list and logout the sessions attached to a nickname.`,
|
||||
enabled: servCmdRequiresBouncerEnabled,
|
||||
minParams: 1,
|
||||
},
|
||||
"drop": {
|
||||
handler: nsDropHandler,
|
||||
help: `Syntax: $bDROP [nickname]$b
|
||||
@ -150,14 +167,13 @@ an administrator can set use this command to set up user accounts.`,
|
||||
minParams: 1,
|
||||
},
|
||||
"sessions": {
|
||||
handler: nsSessionsHandler,
|
||||
hidden: true,
|
||||
handler: nsClientsHandler,
|
||||
help: `Syntax: $bSESSIONS [nickname]$b
|
||||
|
||||
SESSIONS lists information about the sessions currently attached, via
|
||||
the server's multiclient functionality, to your nickname. An administrator
|
||||
can use this command to list another user's sessions.`,
|
||||
helpShort: `$bSESSIONS$b lists the sessions attached to a nickname.`,
|
||||
enabled: servCmdRequiresBouncerEnabled,
|
||||
SESSIONS is an alias for $bCLIENTS LIST$b. See the help entry for $bCLIENTS$b
|
||||
for more information.`,
|
||||
enabled: servCmdRequiresBouncerEnabled,
|
||||
},
|
||||
"unregister": {
|
||||
handler: nsUnregisterHandler,
|
||||
@ -1065,9 +1081,30 @@ func nsEnforceHandler(server *Server, client *Client, command string, params []s
|
||||
}
|
||||
}
|
||||
|
||||
func nsSessionsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
||||
target := client
|
||||
func nsClientsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
||||
var verb string
|
||||
|
||||
if command == "sessions" {
|
||||
// Legacy "SESSIONS" command is an alias for CLIENTS LIST.
|
||||
verb = "list"
|
||||
} else if len(params) > 0 {
|
||||
verb = strings.ToLower(params[0])
|
||||
params = params[1:]
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case "list":
|
||||
nsClientsListHandler(server, client, params, rb)
|
||||
case "logout":
|
||||
nsClientsLogoutHandler(server, client, params, rb)
|
||||
default:
|
||||
nsNotice(rb, client.t("Invalid parameters"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func nsClientsListHandler(server *Server, client *Client, params []string, rb *ResponseBuffer) {
|
||||
target := client
|
||||
if 0 < len(params) {
|
||||
target = server.clients.Get(params[0])
|
||||
if target == nil {
|
||||
@ -1082,12 +1119,12 @@ func nsSessionsHandler(server *Server, client *Client, command string, params []
|
||||
}
|
||||
|
||||
sessionData, currentIndex := target.AllSessionData(rb.session)
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached session(s)"), target.Nick(), len(sessionData)))
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached clients(s)"), target.Nick(), len(sessionData)))
|
||||
for i, session := range sessionData {
|
||||
if currentIndex == i {
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Session %d (currently attached session):"), i+1))
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Client %d (currently attached client):"), session.sessionID))
|
||||
} else {
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Session %d:"), i+1))
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID))
|
||||
}
|
||||
if session.deviceID != "" {
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Device ID: %s"), session.deviceID))
|
||||
@ -1102,6 +1139,61 @@ func nsSessionsHandler(server *Server, client *Client, command string, params []
|
||||
}
|
||||
}
|
||||
|
||||
func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb *ResponseBuffer) {
|
||||
if len(params) < 1 {
|
||||
nsNotice(rb, client.t("Missing client ID to logout (or \"all\")"))
|
||||
return
|
||||
}
|
||||
|
||||
target := client
|
||||
if len(params) >= 2 {
|
||||
// CLIENTS LOGOUT [nickname] [client ID]
|
||||
target = server.clients.Get(params[0])
|
||||
if target == nil {
|
||||
nsNotice(rb, client.t("No such nick"))
|
||||
return
|
||||
}
|
||||
// User must have "local_kill" privileges to logout other user sessions.
|
||||
if target != client {
|
||||
oper := client.Oper()
|
||||
if oper == nil || !oper.Class.Capabilities.Has("local_kill") {
|
||||
nsNotice(rb, client.t("Insufficient oper privs"))
|
||||
return
|
||||
}
|
||||
}
|
||||
params = params[1:]
|
||||
}
|
||||
|
||||
var sessionToDestroy *Session // target.destroy(nil) will logout all sessions
|
||||
if strings.ToLower(params[0]) != "all" {
|
||||
sessionID, err := strconv.ParseInt(params[0], 10, 64)
|
||||
if err != nil {
|
||||
nsNotice(rb, client.t("Client ID to logout should be an integer (or \"all\")"))
|
||||
return
|
||||
}
|
||||
// Find the client ID that the user requested to logout.
|
||||
sessions := target.Sessions()
|
||||
for _, session := range sessions {
|
||||
if session.sessionID == sessionID {
|
||||
sessionToDestroy = session
|
||||
}
|
||||
}
|
||||
if sessionToDestroy == nil {
|
||||
nsNotice(rb, client.t("Specified client ID does not exist"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
target.destroy(sessionToDestroy)
|
||||
if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target {
|
||||
if sessionToDestroy != nil {
|
||||
nsNotice(rb, client.t("Successfully logged out session"))
|
||||
} else {
|
||||
nsNotice(rb, client.t("Successfully logged out all sessions"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func nsCertHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
||||
verb := strings.ToLower(params[0])
|
||||
params = params[1:]
|
||||
|
Loading…
Reference in New Issue
Block a user