diff --git a/irc/accounts.go b/irc/accounts.go index b0d0ee29..3603ac87 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -39,7 +39,8 @@ const ( keyAccountChannels = "account.channels %s" // channels registered to the account keyAccountJoinedChannels = "account.joinedto %s" // channels a persistent client has joined 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" vhostRequestIdx = "vhostQueue" @@ -127,7 +128,13 @@ func (am *AccountManager) createAlwaysOnClients(config *Config) { account, err := am.LoadAccount(accountName) if err == nil && account.Verified && 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,27 @@ func (am *AccountManager) loadLastSeen(account string) (lastSeen map[string]time 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 + }) + return +} + func (am *AccountManager) addRemoveCertfp(account, certfp string, add bool, hasPrivs bool) (err error) { certfp, err = utils.NormalizeCertfp(certfp) if err != nil { @@ -874,6 +902,9 @@ func (am *AccountManager) Verify(client *Client, account string, code string) er am.server.RandomlyRename(currentClient) } } + if client.AlwaysOn() { + client.markDirty(IncludeRealname) + } return nil } diff --git a/irc/client.go b/irc/client.go index a26a3915..59dbc036 100644 --- a/irc/client.go +++ b/irc/client.go @@ -364,7 +364,7 @@ func (server *Server) RunClient(conn IRCConn) { 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() config := server.Config() if lastSeen == nil && account.Settings.AutoreplayMissed { @@ -385,6 +385,7 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, realIP: utils.IPv4LoopbackAddress, alwaysOn: true, + realname: realname, } client.SetMode(modes.TLS, true) @@ -1706,6 +1707,7 @@ const ( IncludeChannels uint = 1 << iota IncludeLastSeen IncludeUserModes + IncludeRealname ) func (client *Client) markDirty(dirtyBits uint) { @@ -1777,6 +1779,9 @@ func (client *Client) performWrite(additionalDirtyBits uint) { } 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 diff --git a/irc/client_lookup_set.go b/irc/client_lookup_set.go index 26544453..45737b90 100644 --- a/irc/client_lookup_set.go +++ b/irc/client_lookup_set.go @@ -225,12 +225,10 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick client.server.stats.AddRegistered(invisible, operator) } 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) + // TODO: transition mechanism for #1065, clean this up eventually: + if currentClient.Realname() == "" { + currentClient.SetRealname(realname) + } // successful reattach! return newNick, nil, back } else if currentClient == client && currentClient.Nick() == newNick { diff --git a/irc/getters.go b/irc/getters.go index a5762f69..2f9cb848 100644 --- a/irc/getters.go +++ b/irc/getters.go @@ -375,7 +375,11 @@ func (client *Client) SetMode(mode modes.Mode, on bool) bool { func (client *Client) SetRealname(realname string) { client.stateMutex.Lock() client.realname = realname + alwaysOn := client.alwaysOn client.stateMutex.Unlock() + if alwaysOn { + client.markDirty(IncludeRealname) + } } func (client *Client) Channels() (result []*Channel) { @@ -428,6 +432,13 @@ func (client *Client) UpdateActive(session *Session) { session.lastActive = now } +func (client *Client) Realname() string { + client.stateMutex.RLock() + result := client.realname + client.stateMutex.RUnlock() + return result +} + func (channel *Channel) Name() string { channel.stateMutex.RLock() defer channel.stateMutex.RUnlock() diff --git a/irc/handlers.go b/irc/handlers.go index 95fd396c..773fb253 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2565,6 +2565,11 @@ func sceneHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res // SETNAME func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { 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) details := client.Details()