Persist realname for always-on clients

This commit is contained in:
Conrad Lukawski 2020-07-06 04:08:04 -04:00
parent 7d596add87
commit 6f8711da3b
5 changed files with 51 additions and 10 deletions

View File

@ -40,6 +40,7 @@ const (
keyAccountJoinedChannels = "account.joinedto %s" // channels a persistent client has joined keyAccountJoinedChannels = "account.joinedto %s" // channels a persistent client has joined
keyAccountLastSeen = "account.lastseen %s" keyAccountLastSeen = "account.lastseen %s"
keyAccountModes = "account.modes %s" // user modes for the always-on client as a string keyAccountModes = "account.modes %s" // user modes for the always-on client as a string
keyAccountRealname = "account.realname %s" // client realname stored as string
keyVHostQueueAcctToId = "vhostQueue %s" keyVHostQueueAcctToId = "vhostQueue %s"
vhostRequestIdx = "vhostQueue" vhostRequestIdx = "vhostQueue"
@ -127,7 +128,13 @@ func (am *AccountManager) createAlwaysOnClients(config *Config) {
account, err := am.LoadAccount(accountName) account, err := am.LoadAccount(accountName)
if err == nil && account.Verified && if err == nil && account.Verified &&
persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, account.Settings.AlwaysOn) { persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, account.Settings.AlwaysOn) {
am.server.AddAlwaysOnClient(account, am.loadChannels(accountName), am.loadLastSeen(accountName), am.loadModes(accountName)) am.server.AddAlwaysOnClient(
account,
am.loadChannels(accountName),
am.loadLastSeen(accountName),
am.loadModes(accountName),
am.loadRealname(accountName),
)
} }
} }
} }
@ -650,6 +657,30 @@ func (am *AccountManager) loadLastSeen(account string) (lastSeen map[string]time
return return
} }
func (am *AccountManager) saveRealname(account string, realname string) {
key := fmt.Sprintf(keyAccountRealname, account)
am.server.store.Update(func(tx *buntdb.Tx) error {
if realname != "" {
tx.Set(key, realname, nil)
} else {
tx.Delete(key)
}
return nil
})
}
func (am *AccountManager) loadRealname(account string) (realname string) {
key := fmt.Sprintf(keyAccountRealname, account)
am.server.store.Update(func(tx *buntdb.Tx) error {
realname, _ = tx.Get(key)
return nil
})
if realname == "" {
return ""
}
return
}
func (am *AccountManager) addRemoveCertfp(account, certfp string, add bool, hasPrivs bool) (err error) { func (am *AccountManager) addRemoveCertfp(account, certfp string, add bool, hasPrivs bool) (err error) {
certfp, err = utils.NormalizeCertfp(certfp) certfp, err = utils.NormalizeCertfp(certfp)
if err != nil { if err != nil {
@ -874,6 +905,9 @@ func (am *AccountManager) Verify(client *Client, account string, code string) er
am.server.RandomlyRename(currentClient) am.server.RandomlyRename(currentClient)
} }
} }
if client.AlwaysOn() {
client.markDirty(IncludeRealname)
}
return nil return nil
} }

View File

@ -365,7 +365,7 @@ func (server *Server) RunClient(conn IRCConn) {
client.run(session) client.run(session)
} }
func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, lastSeen map[string]time.Time, uModes modes.Modes) { func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, lastSeen map[string]time.Time, uModes modes.Modes, realname string) {
now := time.Now().UTC() now := time.Now().UTC()
config := server.Config() config := server.Config()
if lastSeen == nil && account.Settings.AutoreplayMissed { if lastSeen == nil && account.Settings.AutoreplayMissed {
@ -386,6 +386,7 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string,
realIP: utils.IPv4LoopbackAddress, realIP: utils.IPv4LoopbackAddress,
alwaysOn: true, alwaysOn: true,
realname: realname,
} }
client.SetMode(modes.TLS, true) client.SetMode(modes.TLS, true)
@ -1707,6 +1708,7 @@ const (
IncludeChannels uint = 1 << iota IncludeChannels uint = 1 << iota
IncludeLastSeen IncludeLastSeen
IncludeUserModes IncludeUserModes
IncludeRealname
) )
func (client *Client) markDirty(dirtyBits uint) { func (client *Client) markDirty(dirtyBits uint) {
@ -1778,6 +1780,9 @@ func (client *Client) performWrite(additionalDirtyBits uint) {
} }
client.server.accounts.saveModes(account, uModes) client.server.accounts.saveModes(account, uModes)
} }
if (dirtyBits & IncludeRealname) != 0 {
client.server.accounts.saveRealname(account, client.realname)
}
} }
// Blocking store; see Channel.Store and Socket.BlockingWrite // Blocking store; see Channel.Store and Socket.BlockingWrite

View File

@ -122,7 +122,6 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
accountName := client.accountName accountName := client.accountName
settings := client.accountSettings settings := client.accountSettings
registered := client.registered registered := client.registered
realname := client.realname
client.stateMutex.RUnlock() client.stateMutex.RUnlock()
// recompute always-on status, because client.alwaysOn is not set for unregistered clients // recompute always-on status, because client.alwaysOn is not set for unregistered clients
@ -225,12 +224,6 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
client.server.stats.AddRegistered(invisible, operator) client.server.stats.AddRegistered(invisible, operator)
} }
session.autoreplayMissedSince = lastSeen session.autoreplayMissedSince = lastSeen
// XXX SetNames only changes names if they are unset, so the realname change only
// takes effect on first attach to an always-on client (good), but the user/ident
// change is always a no-op (bad). we could make user/ident act the same way as
// realname, but then we'd have to send CHGHOST and i don't want to deal with that
// for performance reasons
currentClient.SetNames("user", realname, true)
// successful reattach! // successful reattach!
return newNick, nil, back return newNick, nil, back
} else if currentClient == client && currentClient.Nick() == newNick { } else if currentClient == client && currentClient.Nick() == newNick {

View File

@ -373,7 +373,11 @@ func (client *Client) SetMode(mode modes.Mode, on bool) bool {
func (client *Client) SetRealname(realname string) { func (client *Client) SetRealname(realname string) {
client.stateMutex.Lock() client.stateMutex.Lock()
client.realname = realname client.realname = realname
alwaysOn := client.alwaysOn
client.stateMutex.Unlock() client.stateMutex.Unlock()
if alwaysOn {
client.markDirty(IncludeRealname)
}
} }
func (client *Client) Channels() (result []*Channel) { func (client *Client) Channels() (result []*Channel) {

View File

@ -2514,6 +2514,11 @@ func sceneHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
// SETNAME <realname> // SETNAME <realname>
func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
realname := msg.Params[0] realname := msg.Params[0]
if realname == "" {
rb.Add(nil, server.name, "FAIL", "SETNAME", "INVALID_REALNAME", client.t("Realname is not valid"))
return false
}
client.SetRealname(realname) client.SetRealname(realname)
details := client.Details() details := client.Details()