mirror of
				https://github.com/ergochat/ergo.git
				synced 2025-10-31 05:47:22 +01:00 
			
		
		
		
	fix #1345
Store the channel-user modes of always-on clients along with their channel memberships, restore them on server startup. This will coexist alongside /CS AMODE, which autoapplies modes to clients on join regardless of their always-on status.
This commit is contained in:
		
							parent
							
								
									3aeac42978
								
							
						
					
					
						commit
						51f279289d
					
				| @ -38,11 +38,13 @@ const ( | ||||
| 	keyAccountVHost            = "account.vhost %s" | ||||
| 	keyCertToAccount           = "account.creds.certfp %s" | ||||
| 	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 | ||||
| 	keyAccountRealname         = "account.realname %s"  // client realname stored as string | ||||
| 	keyAccountSuspended        = "account.suspended %s" // client realname stored as string | ||||
| 	// for an always-on client, a map of channel names they're in to their current modes | ||||
| 	// (not to be confused with their amodes, which a non-always-on client can have): | ||||
| 	keyAccountChannelToModes = "account.channeltomodes %s" | ||||
| 
 | ||||
| 	maxCertfpsPerAccount = 5 | ||||
| ) | ||||
| @ -542,24 +544,34 @@ func (am *AccountManager) setPassword(account string, password string, hasPrivs | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (am *AccountManager) saveChannels(account string, channels []string) { | ||||
| 	channelsStr := strings.Join(channels, ",") | ||||
| 	key := fmt.Sprintf(keyAccountJoinedChannels, account) | ||||
| func (am *AccountManager) saveChannels(account string, channelToModes map[string]string) { | ||||
| 	j, err := json.Marshal(channelToModes) | ||||
| 	if err != nil { | ||||
| 		am.server.logger.Error("internal", "couldn't marshal channel-to-modes", account, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	jStr := string(j) | ||||
| 	key := fmt.Sprintf(keyAccountChannelToModes, account) | ||||
| 	am.server.store.Update(func(tx *buntdb.Tx) error { | ||||
| 		tx.Set(key, channelsStr, nil) | ||||
| 		tx.Set(key, jStr, nil) | ||||
| 		return nil | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func (am *AccountManager) loadChannels(account string) (channels []string) { | ||||
| 	key := fmt.Sprintf(keyAccountJoinedChannels, account) | ||||
| func (am *AccountManager) loadChannels(account string) (channelToModes map[string]string) { | ||||
| 	key := fmt.Sprintf(keyAccountChannelToModes, account) | ||||
| 	var channelsStr string | ||||
| 	am.server.store.View(func(tx *buntdb.Tx) error { | ||||
| 		channelsStr, _ = tx.Get(key) | ||||
| 		return nil | ||||
| 	}) | ||||
| 	if channelsStr != "" { | ||||
| 		return strings.Split(channelsStr, ",") | ||||
| 	if channelsStr == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	err := json.Unmarshal([]byte(channelsStr), &channelToModes) | ||||
| 	if err != nil { | ||||
| 		am.server.logger.Error("internal", "couldn't marshal channel-to-modes", account, err.Error()) | ||||
| 		return nil | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| @ -1454,7 +1466,7 @@ func (am *AccountManager) Unregister(account string, erase bool) error { | ||||
| 	settingsKey := fmt.Sprintf(keyAccountSettings, casefoldedAccount) | ||||
| 	vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount) | ||||
| 	channelsKey := fmt.Sprintf(keyAccountChannels, casefoldedAccount) | ||||
| 	joinedChannelsKey := fmt.Sprintf(keyAccountJoinedChannels, casefoldedAccount) | ||||
| 	joinedChannelsKey := fmt.Sprintf(keyAccountChannelToModes, casefoldedAccount) | ||||
| 	lastSeenKey := fmt.Sprintf(keyAccountLastSeen, casefoldedAccount) | ||||
| 	unregisteredKey := fmt.Sprintf(keyAccountUnregistered, casefoldedAccount) | ||||
| 	modesKey := fmt.Sprintf(keyAccountModes, casefoldedAccount) | ||||
|  | ||||
| @ -553,6 +553,30 @@ func (channel *Channel) ClientStatus(client *Client) (present bool, cModes modes | ||||
| 	return present, modes.AllModes() | ||||
| } | ||||
| 
 | ||||
| // helper for persisting channel-user modes for always-on clients; | ||||
| // return the channel name and all channel-user modes for a client | ||||
| func (channel *Channel) nameAndModes(client *Client) (chname string, modeStr string) { | ||||
| 	channel.stateMutex.RLock() | ||||
| 	defer channel.stateMutex.RUnlock() | ||||
| 	chname = channel.name | ||||
| 	modeStr = channel.members[client].String() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // overwrite any existing channel-user modes with the stored ones | ||||
| func (channel *Channel) setModesForClient(client *Client, modeStr string) { | ||||
| 	newModes := modes.NewModeSet() | ||||
| 	for _, mode := range modeStr { | ||||
| 		newModes.SetMode(modes.Mode(mode), true) | ||||
| 	} | ||||
| 	channel.stateMutex.Lock() | ||||
| 	defer channel.stateMutex.Unlock() | ||||
| 	if _, ok := channel.members[client]; !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	channel.members[client] = newModes | ||||
| } | ||||
| 
 | ||||
| func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool { | ||||
| 	channel.stateMutex.RLock() | ||||
| 	founder := channel.registeredFounder | ||||
| @ -1383,6 +1407,9 @@ func (channel *Channel) applyModeToMember(client *Client, change modes.ModeChang | ||||
| 	if !exists { | ||||
| 		rb.Add(nil, client.server.name, ERR_USERNOTINCHANNEL, client.Nick(), channel.Name(), client.t("They aren't on that channel")) | ||||
| 	} | ||||
| 	if applied { | ||||
| 		target.markDirty(IncludeChannels) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -404,7 +404,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, realname string) { | ||||
| func (server *Server) AddAlwaysOnClient(account ClientAccount, channelToModes map[string]string, lastSeen map[string]time.Time, uModes modes.Modes, realname string) { | ||||
| 	now := time.Now().UTC() | ||||
| 	config := server.Config() | ||||
| 	if lastSeen == nil && account.Settings.AutoreplayMissed { | ||||
| @ -463,10 +463,15 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, | ||||
| 	// XXX set this last to avoid confusing SetNick: | ||||
| 	client.registered = true | ||||
| 
 | ||||
| 	for _, chname := range chnames { | ||||
| 	for chname, modeStr := range channelToModes { | ||||
| 		// XXX we're using isSajoin=true, to make these joins succeed even without channel key | ||||
| 		// this is *probably* ok as long as the persisted memberships are accurate | ||||
| 		server.channels.Join(client, chname, "", true, nil) | ||||
| 		if channel := server.channels.Get(chname); channel != nil { | ||||
| 			channel.setModesForClient(client, modeStr) | ||||
| 		} else { | ||||
| 			server.logger.Error("internal", "could not create channel", chname) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) { | ||||
| @ -1967,11 +1972,12 @@ func (client *Client) performWrite(additionalDirtyBits uint) { | ||||
| 
 | ||||
| 	if (dirtyBits & IncludeChannels) != 0 { | ||||
| 		channels := client.Channels() | ||||
| 		channelNames := make([]string, len(channels)) | ||||
| 		for i, channel := range channels { | ||||
| 			channelNames[i] = channel.Name() | ||||
| 		channelToModes := make(map[string]string, len(channels)) | ||||
| 		for _, channel := range channels { | ||||
| 			chname, modes := channel.nameAndModes(client) | ||||
| 			channelToModes[chname] = modes | ||||
| 		} | ||||
| 		client.server.accounts.saveChannels(account, channelNames) | ||||
| 		client.server.accounts.saveChannels(account, channelToModes) | ||||
| 	} | ||||
| 	if (dirtyBits & IncludeLastSeen) != 0 { | ||||
| 		client.server.accounts.saveLastSeen(account, client.copyLastSeen()) | ||||
|  | ||||
| @ -211,9 +211,9 @@ func (client *Client) SetAway(away bool, awayMessage string) (changed bool) { | ||||
| } | ||||
| 
 | ||||
| func (client *Client) AlwaysOn() (alwaysOn bool) { | ||||
| 	client.stateMutex.Lock() | ||||
| 	client.stateMutex.RLock() | ||||
| 	alwaysOn = client.registered && client.alwaysOn | ||||
| 	client.stateMutex.Unlock() | ||||
| 	client.stateMutex.RUnlock() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Shivaram Lingamneni
						Shivaram Lingamneni