diff --git a/irc/accounts.go b/irc/accounts.go index 1b5e28c7..44b94bcc 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -19,6 +19,7 @@ import ( "github.com/oragono/oragono/irc/connection_limits" "github.com/oragono/oragono/irc/email" "github.com/oragono/oragono/irc/ldap" + "github.com/oragono/oragono/irc/modes" "github.com/oragono/oragono/irc/passwd" "github.com/oragono/oragono/irc/utils" "github.com/tidwall/buntdb" @@ -40,6 +41,7 @@ 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 keyVHostQueueAcctToId = "vhostQueue %s" vhostRequestIdx = "vhostQueue" @@ -127,7 +129,7 @@ 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.server.AddAlwaysOnClient(account, am.loadChannels(accountName), am.loadLastSeen(accountName), am.loadModes(accountName)) } } } @@ -594,6 +596,28 @@ func (am *AccountManager) loadChannels(account string) (channels []string) { return } +func (am *AccountManager) saveModes(account string, uModes modes.Modes) { + modeStr := uModes.String() + key := fmt.Sprintf(keyAccountModes, account) + am.server.store.Update(func(tx *buntdb.Tx) error { + tx.Set(key, modeStr, nil) + return nil + }) +} + +func (am *AccountManager) loadModes(account string) (uModes modes.Modes) { + key := fmt.Sprintf(keyAccountModes, account) + var modeStr string + am.server.store.View(func(tx *buntdb.Tx) error { + modeStr, _ = tx.Get(key) + return nil + }) + for _, m := range modeStr { + uModes = append(uModes, modes.Mode(m)) + } + return +} + func (am *AccountManager) saveLastSeen(account string, lastSeen time.Time) { key := fmt.Sprintf(keyAccountLastSeen, account) var val string diff --git a/irc/client.go b/irc/client.go index 927f460c..bf8d2664 100644 --- a/irc/client.go +++ b/irc/client.go @@ -360,7 +360,7 @@ func (server *Server) RunClient(conn IRCConn) { client.run(session) } -func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, lastSeen time.Time) { +func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, lastSeen time.Time, uModes modes.Modes) { now := time.Now().UTC() config := server.Config() if lastSeen.IsZero() { @@ -383,9 +383,10 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, alwaysOn: true, } - ApplyUserModeChanges(client, config.Accounts.defaultUserModes, false, nil) - client.SetMode(modes.TLS, true) + for _, m := range uModes { + client.SetMode(m, true) + } client.writerSemaphore.Initialize(1) client.history.Initialize(0, 0) client.brbTimer.Initialize(client) @@ -1633,6 +1634,7 @@ func (client *Client) historyStatus(config *Config) (status HistoryStatus, targe const ( IncludeChannels uint = 1 << iota IncludeLastSeen + IncludeUserModes ) func (client *Client) markDirty(dirtyBits uint) { @@ -1691,4 +1693,18 @@ func (client *Client) performWrite() { if (dirtyBits & IncludeLastSeen) != 0 { client.server.accounts.saveLastSeen(account, lastSeen) } + if (dirtyBits & IncludeUserModes) != 0 { + uModes := make(modes.Modes, 0, len(modes.SupportedUserModes)) + for _, m := range modes.SupportedUserModes { + switch m { + case modes.Operator, modes.ServerNotice: + // these can't be persisted because they depend on the operator block + default: + if client.HasMode(m) { + uModes = append(uModes, m) + } + } + } + client.server.accounts.saveModes(account, uModes) + } } diff --git a/irc/modes.go b/irc/modes.go index 11912544..06dd8e51 100644 --- a/irc/modes.go +++ b/irc/modes.go @@ -102,6 +102,10 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool, // can't do anything to TLS mode } + if len(applied) != 0 { + client.markDirty(IncludeUserModes) + } + // return the changes we could actually apply return applied }