Files
Michael 425e14305b Move portable dictionaries to -dictionaryValueForTarget:
Gives dictionaries greater context of which operation is in progress

As part of this, during copies, defaults are not stripped. Copy objects whole. Only strip defaults when we want to save to disk or cloud.
2025-01-11 15:48:19 -05:00

1418 lines
47 KiB
Objective-C
Executable File

/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2008 - 2010 Satoshi Nakagawa <psychs AT limechat DOT net>
* Copyright (c) 2010 - 2018 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "NSObjectHelperPrivate.h"
#import "NSStringHelper.h"
#import "IRC.h"
#import "IRCAddressBook.h"
#import "IRCChannelConfig.h"
#import "IRCHighlightMatchCondition.h"
#import "IRCNetworkList.h"
#import "IRCServerPrivate.h"
#import "TPCPreferencesLocal.h"
#import "TLOLocalization.h"
#import "IRCClientConfigInternal.h"
NS_ASSUME_NONNULL_BEGIN
#define IRCClientConfigDictionaryVersionLatest 710
#define IRCClientConfigFloodControlDefaultDelayIntervalLimited 2
#define IRCClientConfigFloodControlDefaultMessageCountLimited 2 // freenode gets a special case 'cause they are strict about flood control
@interface IRCClientConfig ()
@property (readonly) BOOL prefersSecuredConnection_;
@property (readonly) uint16_t serverPort_;
@property (readonly, copy, nullable) NSString *serverAddress_;
@property (readonly) BOOL connectionPrefersModernCiphers_;
@end
@implementation IRCClientConfig
#pragma mark -
#pragma mark Defaults
- (void)populateDefaultsPreflight
{
if (self.initializedAsCopy) {
return;
}
/* Even if a value is NO, include it as a default. */
/* This allows NO values to be stripped from output dictionary. */
NSMutableDictionary<NSString *, id> *defaults = [NSMutableDictionary dictionary];
defaults[@"addressType"] = @(IRCConnectionAddressTypeDefault);
defaults[@"autoConnect"] = @(NO);
defaults[@"autoReconnect"] = @(NO);
defaults[@"autoSleepModeDisconnect"] = @(YES);
defaults[@"autojoinWaitsForNickServ"] = @(NO);
defaults[@"cachedLastServerTimeCapabilityReceivedAtTimestamp"] = @(0);
defaults[@"cipherSuites"] = @(RCMCipherSuiteCollectionDefault);
defaults[@"connectionName"] = TXTLS(@"BasicLanguage[vfu-c0]");
defaults[@"fallbackEncoding"] = @(TXDefaultFallbackStringEncoding);
defaults[@"floodControlDelayTimerInterval"] = @(IRCConnectionConfigFloodControlDefaultDelayInterval);
defaults[@"floodControlMaximumMessages"] = @(IRCConnectionConfigFloodControlDefaultMessageCount);
defaults[@"hideAutojoinDelayedWarnings"] = @(NO);
defaults[@"hideNetworkUnavailabilityNotices"] = @(NO);
defaults[@"normalLeavingComment"] = TXTLS(@"BasicLanguage[1dd-0f]");
defaults[@"performDisconnectOnPongTimer"] = @(NO);
defaults[@"performDisconnectOnReachabilityChange"] = @(YES);
defaults[@"performPongTimer"] = @(YES);
defaults[@"prefersSecuredConnection"] = @(NO);
defaults[@"primaryEncoding"] = @(TXDefaultPrimaryStringEncoding);
defaults[@"proxyPort"] = @(IRCConnectionDefaultProxyPort);
defaults[@"proxyType"] = @(IRCConnectionProxyTypeAutomatic);
defaults[@"saslAuthenticationDisableExternalMechanism"] = @(NO);
defaults[@"sendAuthenticationRequestsToUserServ"] = @(NO);
defaults[@"sendWhoCommandRequestsToChannels"] = @(YES);
defaults[@"serverPort"] = @(IRCConnectionDefaultServerPort);
defaults[@"setInvisibleModeOnConnect"] = @(NO);
defaults[@"sidebarItemExpanded"] = @(YES);
defaults[@"sleepModeLeavingComment"] = TXTLS(@"BasicLanguage[qi7-5y]");
defaults[@"validateServerCertificateChain"] = @(YES);
defaults[@"zncIgnoreConfiguredAutojoin"] = @(NO);
defaults[@"zncIgnorePlaybackNotifications"] = @(YES);
defaults[@"zncIgnoreUserNotifications"] = @(NO);
defaults[@"zncOnlyPlaybackLatest"] = @(YES);
self->_defaults = [defaults copy];
}
- (void)populateDefaultsPostflight
{
if (self.initializedAsCopy) {
return;
}
SetVariableIfNil(self->_uniqueIdentifier, [NSString stringWithUUID])
SetVariableIfNil(self->_nickname, [TPCPreferences defaultNickname])
SetVariableIfNil(self->_awayNickname, [TPCPreferences defaultAwayNickname])
SetVariableIfNil(self->_username, [TPCPreferences defaultUsername])
SetVariableIfNil(self->_realName, [TPCPreferences defaultRealName])
SetVariableIfNil(self->_ignoreList, @[])
SetVariableIfNil(self->_channelList, @[])
SetVariableIfNil(self->_highlightList, @[])
SetVariableIfNil(self->_serverList, @[])
SetVariableIfNil(self->_alternateNicknames, @[])
SetVariableIfNil(self->_loginCommands, @[])
[self modifyFloodControlDefaults];
}
- (void)populateDefaultsByAppendingDictionary:(NSDictionary<NSString *, id> *)defaultsToAppend
{
NSParameterAssert(defaultsToAppend != nil);
self->_defaults = [self->_defaults dictionaryByAddingEntries:defaultsToAppend];
}
- (void)modifyFloodControlDefaults
{
if (self.floodControlDelayTimerInterval != IRCConnectionConfigFloodControlDefaultDelayInterval ||
self.floodControlMaximumMessages != IRCConnectionConfigFloodControlDefaultMessageCount)
{
return;
}
BOOL haveLimitedServer = NO;
for (IRCServer *server in self.serverList) {
if ([server.serverAddress hasSuffix:@".freenode.net"] == NO) {
continue;
}
haveLimitedServer = YES;
break;
}
if (haveLimitedServer == NO) {
return;
}
NSUInteger floodControlDelayTimerInterval = IRCClientConfigFloodControlDefaultDelayIntervalLimited;
NSUInteger floodControlMaximumMessages = IRCClientConfigFloodControlDefaultMessageCountLimited;
[self populateDefaultsByAppendingDictionary:@{
@"floodControlDelayTimerInterval" : @(floodControlDelayTimerInterval),
@"floodControlMaximumMessages" : @(floodControlMaximumMessages)
}];
self->_floodControlDelayTimerInterval = floodControlDelayTimerInterval;
self->_floodControlMaximumMessages = floodControlMaximumMessages;
}
#pragma mark -
#pragma mark Server Configuration
- (instancetype)init
{
return [self initWithDictionary:@{}];
}
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dic
{
return [self initWithDictionary:dic ignorePrivateMessages:NO];
}
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dic ignorePrivateMessages:(BOOL)ignorePrivateMessages
{
if ((self = [super init])) {
[self populateDefaultsPreflight];
[self populateDictionaryValue:dic
ignorePrivateMessages:ignorePrivateMessages
applyDefaults:YES
bypassIsCopyCheck:NO];
[self populateDefaultsPostflight];
[self initializedClassHealthCheck];
return self;
}
return nil;
}
- (void)initializedClassHealthCheck
{
if (self.initializedAsCopy) {
return;
}
if (self->_proxyPort == 0) {
self->_proxyPort = IRCConnectionDefaultProxyPort;
}
if ([self isMutable]) {
return;
}
NSParameterAssert(self->_connectionName.length > 0);
}
+ (instancetype)newConfigByMerging:(IRCClientConfig *)config1 with:(IRCClientConfig *)config2
{
NSParameterAssert(config1 != nil);
NSParameterAssert(config2 != nil);
IRCClientConfigMutable *config1Mutable = [config1 mutableCopy];
[config1Mutable populateDictionaryValue:config2.dictionaryValue
ignorePrivateMessages:NO
applyDefaults:NO
bypassIsCopyCheck:YES];
if ([self isMutable]) {
return config1Mutable;
}
return [config1Mutable copy];
}
+ (instancetype)newConfigWithNetwork:(IRCNetwork *)network
{
NSParameterAssert(network != nil);
IRCClientConfigMutable *configMutable = [IRCClientConfigMutable new];
configMutable.connectionName = network.networkName;
IRCServerMutable *server = [IRCServerMutable new];
server.serverAddress = network.serverAddress;
server.serverPort = network.serverPort;
server.prefersSecuredConnection = network.prefersSecuredConnection;
configMutable.serverList = @[[server copy]];
if ([self isMutable]) {
return configMutable;
}
return [configMutable copy];
}
- (void)populateDictionaryValue:(NSDictionary<NSString *, id> *)dic ignorePrivateMessages:(BOOL)ignorePrivateMessages applyDefaults:(BOOL)applyDefaults bypassIsCopyCheck:(BOOL)bypassIsCopyCheck
{
NSParameterAssert(dic != nil);
NSMutableDictionary<NSString *, id> *defaultsMutable = nil;
if (applyDefaults) {
defaultsMutable = [self->_defaults mutableCopy];
[defaultsMutable addEntriesFromDictionary:dic];
} else {
defaultsMutable = [dic mutableCopy];
}
/* Load the newest set of keys. */
[defaultsMutable assignUnsignedIntegerTo:&self->_dictionaryVersion forKey:@"dictionaryVersion"];
[defaultsMutable assignArrayTo:&self->_alternateNicknames forKey:@"alternateNicknames"];
[defaultsMutable assignArrayTo:&self->_loginCommands forKey:@"onConnectCommands"];
[defaultsMutable assignBoolTo:&self->_autoConnect forKey:@"autoConnect"];
[defaultsMutable assignBoolTo:&self->_autoReconnect forKey:@"autoReconnect"];
[defaultsMutable assignBoolTo:&self->_autoSleepModeDisconnect forKey:@"autoSleepModeDisconnect"];
[defaultsMutable assignBoolTo:&self->_autojoinWaitsForNickServ forKey:@"autojoinWaitsForNickServ"];
[defaultsMutable assignBoolTo:&self->_hideAutojoinDelayedWarnings forKey:@"hideAutojoinDelayedWarnings"];
[defaultsMutable assignBoolTo:&self->_hideNetworkUnavailabilityNotices forKey:@"hideNetworkUnavailabilityNotices"];
[defaultsMutable assignBoolTo:&self->_performDisconnectOnPongTimer forKey:@"performDisconnectOnPongTimer"];
[defaultsMutable assignBoolTo:&self->_performDisconnectOnReachabilityChange forKey:@"performDisconnectOnReachabilityChange"];
[defaultsMutable assignBoolTo:&self->_performPongTimer forKey:@"performPongTimer"];
[defaultsMutable assignBoolTo:&self->_prefersSecuredConnection forKey:@"prefersSecuredConnection"];
[defaultsMutable assignBoolTo:&self->_saslAuthenticationDisableExternalMechanism forKey:@"saslAuthenticationDisableExternalMechanism"];
[defaultsMutable assignBoolTo:&self->_sendAuthenticationRequestsToUserServ forKey:@"sendAuthenticationRequestsToUserServ"];
[defaultsMutable assignBoolTo:&self->_sendWhoCommandRequestsToChannels forKey:@"sendWhoCommandRequestsToChannels"];
[defaultsMutable assignBoolTo:&self->_setInvisibleModeOnConnect forKey:@"setInvisibleModeOnConnect"];
[defaultsMutable assignBoolTo:&self->_sidebarItemExpanded forKey:@"sidebarItemExpanded"];
[defaultsMutable assignBoolTo:&self->_validateServerCertificateChain forKey:@"validateServerCertificateChain"];
[defaultsMutable assignBoolTo:&self->_zncIgnoreConfiguredAutojoin forKey:@"zncIgnoreConfiguredAutojoin"];
[defaultsMutable assignBoolTo:&self->_zncIgnorePlaybackNotifications forKey:@"zncIgnorePlaybackNotifications"];
[defaultsMutable assignBoolTo:&self->_zncIgnoreUserNotifications forKey:@"zncIgnoreUserNotifications"];
[defaultsMutable assignBoolTo:&self->_zncOnlyPlaybackLatest forKey:@"zncOnlyPlaybackLatest"];
[defaultsMutable assignDoubleTo:&self->_lastMessageServerTime forKey:@"cachedLastServerTimeCapabilityReceivedAtTimestamp"];
[defaultsMutable assignObjectTo:&self->_identityClientSideCertificate forKey:@"identityClientSideCertificate"];
[defaultsMutable assignStringTo:&self->_awayNickname forKey:@"awayNickname"];
[defaultsMutable assignStringTo:&self->_connectionName forKey:@"connectionName"];
[defaultsMutable assignStringTo:&self->_nickname forKey:@"nickname"];
[defaultsMutable assignStringTo:&self->_normalLeavingComment forKey:@"normalLeavingComment"];
[defaultsMutable assignStringTo:&self->_proxyAddress forKey:@"proxyAddress"];
[defaultsMutable assignStringTo:&self->_proxyUsername forKey:@"proxyUsername"];
[defaultsMutable assignStringTo:&self->_realName forKey:@"realName"];
[defaultsMutable assignStringTo:&self->_serverAddress forKey:@"serverAddress"];
[defaultsMutable assignStringTo:&self->_sleepModeLeavingComment forKey:@"sleepModeLeavingComment"];
[defaultsMutable assignStringTo:&self->_uniqueIdentifier forKey:@"uniqueIdentifier"];
[defaultsMutable assignStringTo:&self->_username forKey:@"username"];
[defaultsMutable assignUnsignedIntegerTo:&self->_addressType forKey:@"addressType"];
[defaultsMutable assignUnsignedIntegerTo:&self->_cipherSuites forKey:@"cipherSuites"];
[defaultsMutable assignUnsignedIntegerTo:&self->_fallbackEncoding forKey:@"fallbackEncoding"];
[defaultsMutable assignUnsignedIntegerTo:&self->_floodControlDelayTimerInterval forKey:@"floodControlDelayTimerInterval"];
[defaultsMutable assignUnsignedIntegerTo:&self->_floodControlMaximumMessages forKey:@"floodControlMaximumMessages"];
[defaultsMutable assignUnsignedIntegerTo:&self->_primaryEncoding forKey:@"primaryEncoding"];
[defaultsMutable assignUnsignedIntegerTo:&self->_proxyType forKey:@"proxyType"];
[defaultsMutable assignUnsignedShortTo:&self->_proxyPort forKey:@"proxyPort"];
[defaultsMutable assignUnsignedShortTo:&self->_serverPort forKey:@"serverPort"];
/* -connectionPrefersIPv4 is a special exception to legacy support.
We still load its value, regardless of dictionary version, so that
we can show a user a warning when -connectionPrefersIPv4 == YES
and -addressType == IPv6 (which is automatically migrates to).
We then change value to NO when user modifies -addressType. */
[defaultsMutable assignBoolTo:&self->_connectionPrefersIPv4 forKey:@"connectionPrefersIPv4"];
/* If this is a copy operation, then we can just stop here. The rest of the data processed below,
such as other configurations and backwards keys are already taken care of. */
if (self.initializedAsCopy && bypassIsCopyCheck == NO) {
return;
}
/* Channel list */
NSMutableArray<IRCChannelConfig *> *channelListOut = [NSMutableArray array];
NSArray<NSDictionary *> *channelListIn = [defaultsMutable arrayForKey:@"channelList"];
for (NSDictionary<NSString *, id> *e in channelListIn) {
IRCChannelConfig *c = [[IRCChannelConfig alloc] initWithDictionary:e];
if (c.type == IRCChannelTypePrivateMessage) {
if (ignorePrivateMessages == NO) {
[channelListOut addObject:c];
}
} else {
[channelListOut addObject:c];
}
}
self->_channelList = [channelListOut copy];
/* Ignore list */
NSMutableArray<IRCAddressBookEntry *> *ignoreListOut = [NSMutableArray array];
NSArray<NSDictionary *> *ignoreListIn = [defaultsMutable arrayForKey:@"ignoreList"];
for (NSDictionary<NSString *, id> *e in ignoreListIn) {
IRCAddressBookEntry *c = [[IRCAddressBookEntry alloc] initWithDictionary:e];
[ignoreListOut addObject:c];
}
self->_ignoreList = [ignoreListOut copy];
/* Highlight list */
NSMutableArray<IRCHighlightMatchCondition *> *highlightListOut = [NSMutableArray array];
NSArray<NSDictionary *> *highlightListIn = [defaultsMutable arrayForKey:@"highlightList"];
for (NSDictionary<NSString *, id> *e in highlightListIn) {
IRCHighlightMatchCondition *c = [[IRCHighlightMatchCondition alloc] initWithDictionary:e];
[highlightListOut addObject:c];
}
self->_highlightList = [highlightListOut copy];
/* Server List */
NSMutableArray<IRCServer *> *serverListOut = [NSMutableArray array];
NSArray<NSDictionary *> *serverListIn = [defaultsMutable arrayForKey:@"serverList"];
for (NSDictionary<NSString *, id> *e in serverListIn) {
IRCServer *c = [[IRCServer alloc] initWithDictionary:e];
[serverListOut addObject:c];
}
self->_serverList = [serverListOut copy];
/* Perform migration */
/* If legacy keys were assigned before new keys, then a transition would not occur properly. */
/* Since the new keys will read from -defaults if they are not present in /dic/, then those
would override legacy keys when performing a first pass. */
/* Is everything up to date? */
NSUInteger dictionaryVersion = self->_dictionaryVersion;
if (dictionaryVersion == IRCClientConfigDictionaryVersionLatest) {
return;
}
/* 710 is latest version which means we migrate it at all times */
[self _migrate710Dictionary:dic withDefaults:defaultsMutable];
/* 704 is no longer the latest version but there was no version before
it which means we only have to migrate it when the version is unknown. */
if (dictionaryVersion == 0) {
[self _migrate704Dictionary:dic withDefaults:defaultsMutable];
}
/* Update version */
self->_dictionaryVersion = IRCClientConfigDictionaryVersionLatest;
}
- (void)_migrate710Dictionary:(NSDictionary *)dic withDefaults:(NSMutableDictionary *)defaultsMutable
{
NSParameterAssert(dic != nil);
NSParameterAssert(defaultsMutable != nil);
TEXTUAL_IGNORE_DEPRECATION_BEGIN
if (self.connectionPrefersIPv4) {
TEXTUAL_IGNORE_DEPRECATION_END
self->_addressType = IRCConnectionAddressTypeIPv4;
}
}
- (void)_migrate704Dictionary:(NSDictionary *)dic withDefaults:(NSMutableDictionary *)defaultsMutable
{
NSParameterAssert(dic != nil);
NSParameterAssert(defaultsMutable != nil);
[defaultsMutable assignArrayTo:&self->_alternateNicknames forKey:@"identityAlternateNicknames"];
[defaultsMutable assignBoolTo:&self->_autoConnect forKey:@"connectOnLaunch"];
[defaultsMutable assignBoolTo:&self->_autoReconnect forKey:@"connectOnDisconnect"];
[defaultsMutable assignBoolTo:&self->_autoSleepModeDisconnect forKey:@"disconnectOnSleepMode"];
[defaultsMutable assignBoolTo:&self->_autojoinWaitsForNickServ forKey:@"autojoinWaitsForNickServIdentification"];
[defaultsMutable assignBoolTo:&self->_prefersSecuredConnection forKey:@"connectUsingSSL"];
[defaultsMutable assignBoolTo:&self->_setInvisibleModeOnConnect forKey:@"setInvisibleOnConnect"];
[defaultsMutable assignBoolTo:&self->_sidebarItemExpanded forKey:@"serverListItemIsExpanded"];
[defaultsMutable assignBoolTo:&self->_validateServerCertificateChain forKey:@"validateServerSideSSLCertificate"];
[defaultsMutable assignObjectTo:&self->_identityClientSideCertificate forKey:@"IdentitySSLCertificate"];
[defaultsMutable assignStringTo:&self->_awayNickname forKey:@"identityAwayNickname"];
[defaultsMutable assignStringTo:&self->_nickname forKey:@"identityNickname"];
[defaultsMutable assignStringTo:&self->_normalLeavingComment forKey:@"connectionDisconnectDefaultMessage"];
[defaultsMutable assignStringTo:&self->_proxyAddress forKey:@"proxyServerAddress"];
[defaultsMutable assignStringTo:&self->_proxyUsername forKey:@"proxyServerUsername"];
[defaultsMutable assignStringTo:&self->_realName forKey:@"identityRealname"];
[defaultsMutable assignStringTo:&self->_sleepModeLeavingComment forKey:@"connectionDisconnectSleepModeMessage"];
[defaultsMutable assignStringTo:&self->_username forKey:@"identityUsername"];
[defaultsMutable assignUnsignedIntegerTo:&self->_primaryEncoding forKey:@"characterEncodingDefault"];
[defaultsMutable assignUnsignedIntegerTo:&self->_fallbackEncoding forKey:@"characterEncodingFallback"];
[defaultsMutable assignUnsignedIntegerTo:&self->_proxyType forKey:@"proxyServerType"];
[defaultsMutable assignUnsignedShortTo:&self->_proxyPort forKey:@"proxyServerPort"];
[defaultsMutable assignDoubleTo:&self->_lastMessageServerTime forKey:@"cachedLastServerTimeCapacityReceivedAtTimestamp"];
/* Flood control */
/* This is here to migrate to the new properties. Saving these values
in this dictionary key is no longer preferred. */
BOOL floodControlSetToDisabled = NO;
NSDictionary *floodControlDic = [defaultsMutable dictionaryForKey:@"floodControl"];
if (floodControlDic) {
NSNumber *serviceEnabled = floodControlDic[@"serviceEnabled"];
if (serviceEnabled && serviceEnabled.boolValue == NO) {
floodControlSetToDisabled = YES;
}
[floodControlDic assignUnsignedIntegerTo:&self->_floodControlDelayTimerInterval forKey:@"delayTimerInterval"];
[floodControlDic assignUnsignedIntegerTo:&self->_floodControlMaximumMessages forKey:@"maximumMessageCount"];
}
if (floodControlSetToDisabled == NO) {
NSNumber *floodControlEnabled = defaultsMutable[@"isOutgoingFloodControlEnabled"];
if (floodControlEnabled && floodControlEnabled.boolValue == NO) {
floodControlSetToDisabled = YES;
}
}
/* An option to disable flood control no longer exists.
If the user had flood control disabled when the option did exist,
then set the the current values to appear disabled. */
if (floodControlSetToDisabled) {
self->_floodControlDelayTimerInterval = IRCConnectionConfigFloodControlMinimumDelayInterval;
self->_floodControlMaximumMessages = IRCConnectionConfigFloodControlMaximumMessageCount;
}
/* Migrate to keychain. */
NSString *proxyPassword = [defaultsMutable stringForKey:@"proxyServerPassword"];
if (proxyPassword) {
self->_proxyPassword = [proxyPassword copy];
[self writeProxyPasswordToKeychain];
}
/* Cipher suites */
/* The dictionary excludes defaults which means we need to be cautious
about reading the value of dic when performing migration. */
if (dic[@"cipherSuites"] == nil) {
NSNumber *connectionPrefersModernCiphers = dic[@"connectionPrefersModernCiphers"];
if (connectionPrefersModernCiphers && connectionPrefersModernCiphers.boolValue == NO) {
self->_cipherSuites = RCMCipherSuiteCollectionNone;
}
}
/* Migrate servers */
[self _migrateDictionaryToServerListV1Layout:defaultsMutable];
}
- (void)_migrateDictionaryToServerListV1Layout:(NSDictionary *)dic
{
NSParameterAssert(dic != nil);
/* This key is no longer assigned. We still check it so that
clients that did not set dictionaryVersion but did set this
key wont trigger migration again. */
id migratedToServerListV1Layout = dic[@"migratedToServerListV1Layout"];
if (migratedToServerListV1Layout && [migratedToServerListV1Layout boolValue]) {
return;
}
/* Do not perform migration if already one server exists. */
/* IRCClientConfig inserts these values back into the exported dictionary
for backwards compatibility which means once we imported them and have
at least one server, then importing again will not help. */
if (self.serverList.count > 0) {
return;
}
/* Perform migration */
NSString *serverAddress = [dic stringForKey:@"serverAddress"];
if (serverAddress.isValidInternetAddress == NO) {
LogToConsoleDebug("Migration cancelled because of bad server address");
return;
}
uint16_t serverPort = [dic unsignedShortForKey:@"serverPort"];
if (serverPort == 0 || serverPort > TXMaximumTCPPort) {
LogToConsoleDebug("Migration cancelled because of bad server port");
return;
}
BOOL prefersSecuredConnection = [dic boolForKey:@"prefersSecuredConnection"];
NSString *serverPasswordServiceName = [NSString stringWithFormat:@"textual.server.%@", self.uniqueIdentifier];
NSString *serverPassword = [XRKeychain getPasswordFromKeychainItem:@"Textual (Server Password)"
withItemKind:@"application password"
forUsername:nil
serviceName:serverPasswordServiceName];
IRCServerMutable *server = [IRCServerMutable new];
server.serverAddress = serverAddress;
server.serverPort = serverPort;
server.serverPassword = serverPassword;
server.prefersSecuredConnection = prefersSecuredConnection;
[server writeServerPasswordToKeychain];
self->_serverList = @[[server copy]];
self->_migratedServerPasswordPendingDestroy = YES;
}
- (BOOL)isEqual:(id)object
{
if (object == nil) {
return NO;
}
if (object == self) {
return YES;
}
if ([object isKindOfClass:[IRCClientConfig class]] == NO) {
return NO;
}
IRCClientConfig *objectCast = (IRCClientConfig *)object;
NSDictionary *s1 = self.dictionaryValue;
NSDictionary *s2 = objectCast.dictionaryValue;
return ([s1 isEqualToDictionary:s2] &&
((self->_nicknamePassword == nil && objectCast->_nicknamePassword == nil) ||
[self->_nicknamePassword isEqualToString:objectCast->_nicknamePassword]) &&
((self->_proxyPassword == nil && objectCast->_proxyPassword == nil) ||
[self->_proxyPassword isEqualToString:objectCast->_proxyPassword]));
}
- (NSUInteger)hash
{
return self.uniqueIdentifier.hash;
}
- (id)copyAsMutable:(BOOL)mutableCopy uniquing:(BOOL)uniquing
{
IRCClientConfig *config = [self allocForCopyAsMutable:mutableCopy];
config->_nicknamePassword = self->_nicknamePassword;
config->_proxyPassword = self->_proxyPassword;
config->_defaults = self->_defaults;
config->_migratedServerPasswordPendingDestroy = self->_migratedServerPasswordPendingDestroy;
if (uniquing) {
NSMutableArray *channelList = [self.channelList mutableCopy];
NSMutableArray *highlightList = [self.highlightList mutableCopy];
NSMutableArray *ignoreList = [self.ignoreList mutableCopy];
NSMutableArray *serverList = [self.serverList mutableCopy];
[channelList performSelectorOnObjectValueAndReplace:@selector(uniqueCopy)];
[highlightList performSelectorOnObjectValueAndReplace:@selector(uniqueCopy)];
[ignoreList performSelectorOnObjectValueAndReplace:@selector(uniqueCopy)];
[serverList performSelectorOnObjectValueAndReplace:@selector(uniqueCopy)];
config->_channelList = [channelList copy];
config->_highlightList = [highlightList copy];
config->_ignoreList = [ignoreList copy];
config->_serverList = [serverList copy];
config->_uniqueIdentifier = [NSString stringWithUUID];
}
else // uniquing
{
config->_channelList = self->_channelList;
config->_highlightList = self->_highlightList;
config->_ignoreList = self->_ignoreList;
config->_serverList = self->_serverList;
}
return [config initWithDictionary:self.dictionaryValueForCopy ignorePrivateMessages:NO];
}
- (__kindof XRPortablePropertyDict *)mutableClass
{
return [IRCClientConfigMutable self];
}
- (NSDictionary<NSString *, id> *)dictionaryValueForTarget:(XRPortablePropertyDictTarget)target
{
NSMutableDictionary<NSString *, id> *dic = [NSMutableDictionary dictionary];
[dic setUnsignedInteger:self->_dictionaryVersion forKey:@"dictionaryVersion"];
[dic maybeSetObject:self.alternateNicknames forKey:@"alternateNicknames"];
[dic maybeSetObject:self.awayNickname forKey:@"awayNickname"];
[dic maybeSetObject:self.connectionName forKey:@"connectionName"];
[dic maybeSetObject:self.loginCommands forKey:@"onConnectCommands"];
[dic maybeSetObject:self.nickname forKey:@"nickname"];
[dic maybeSetObject:self.normalLeavingComment forKey:@"normalLeavingComment"];
[dic maybeSetObject:self.proxyAddress forKey:@"proxyAddress"];
[dic maybeSetObject:self.proxyUsername forKey:@"proxyUsername"];
[dic maybeSetObject:self.realName forKey:@"realName"];
[dic maybeSetObject:self.sleepModeLeavingComment forKey:@"sleepModeLeavingComment"];
[dic maybeSetObject:self.uniqueIdentifier forKey:@"uniqueIdentifier"];
[dic maybeSetObject:self.username forKey:@"username"];
[dic setBool:self.autoConnect forKey:@"autoConnect"];
[dic setBool:self.autoReconnect forKey:@"autoReconnect"];
[dic setBool:self.autoSleepModeDisconnect forKey:@"autoSleepModeDisconnect"];
[dic setBool:self.autojoinWaitsForNickServ forKey:@"autojoinWaitsForNickServ"];
[dic setBool:self.hideAutojoinDelayedWarnings forKey:@"hideAutojoinDelayedWarnings"];
[dic setBool:self.hideNetworkUnavailabilityNotices forKey:@"hideNetworkUnavailabilityNotices"];
[dic setBool:self.performDisconnectOnPongTimer forKey:@"performDisconnectOnPongTimer"];
[dic setBool:self.performDisconnectOnReachabilityChange forKey:@"performDisconnectOnReachabilityChange"];
[dic setBool:self.performPongTimer forKey:@"performPongTimer"];
[dic setBool:self.saslAuthenticationDisableExternalMechanism forKey:@"saslAuthenticationDisableExternalMechanism"];
[dic setBool:self.sendAuthenticationRequestsToUserServ forKey:@"sendAuthenticationRequestsToUserServ"];
[dic setBool:self.sendWhoCommandRequestsToChannels forKey:@"sendWhoCommandRequestsToChannels"];
[dic setBool:self.setInvisibleModeOnConnect forKey:@"setInvisibleModeOnConnect"];
[dic setBool:self.validateServerCertificateChain forKey:@"validateServerCertificateChain"];
[dic setBool:self.zncIgnoreConfiguredAutojoin forKey:@"zncIgnoreConfiguredAutojoin"];
[dic setBool:self.zncIgnorePlaybackNotifications forKey:@"zncIgnorePlaybackNotifications"];
[dic setBool:self.zncIgnoreUserNotifications forKey:@"zncIgnoreUserNotifications"];
[dic setBool:self.zncOnlyPlaybackLatest forKey:@"zncOnlyPlaybackLatest"];
[dic setUnsignedInteger:self.addressType forKey:@"addressType"];
[dic setUnsignedInteger:self.cipherSuites forKey:@"cipherSuites"];
[dic setUnsignedInteger:self.fallbackEncoding forKey:@"fallbackEncoding"];
[dic setUnsignedInteger:self.floodControlDelayTimerInterval forKey:@"floodControlDelayTimerInterval"];
[dic setUnsignedInteger:self.floodControlMaximumMessages forKey:@"floodControlMaximumMessages"];
[dic setUnsignedInteger:self.primaryEncoding forKey:@"primaryEncoding"];
[dic setUnsignedInteger:self.proxyType forKey:@"proxyType"];
[dic setUnsignedShort:self.proxyPort forKey:@"proxyPort"];
/* These are items that cannot be synced over iCloud because they access data specific to
this device or only contain state information which is not useful to other devices. */
[dic maybeSetObject:self.identityClientSideCertificate forKey:@"identityClientSideCertificate"];
[dic setBool:self.sidebarItemExpanded forKey:@"sidebarItemExpanded"];
[dic setDouble:self.lastMessageServerTime forKey:@"cachedLastServerTimeCapabilityReceivedAtTimestamp"];
/* Deprecated */
/* These values are inserted here for backwards compatibility
with earlier versions of Textual */
TEXTUAL_IGNORE_DEPRECATION_BEGIN
[dic setBool:self.connectionPrefersIPv4 forKey:@"connectionPrefersIPv4"];
TEXTUAL_IGNORE_DEPRECATION_END
[dic setBool:self.legacyConnectionPrefersModernCiphers forKey:@"connectionPrefersModernCiphers"];
[dic maybeSetObject:self.legacyServerAddress forKey:@"serverAddress"];
[dic setBool:self.legacyPrefersSecuredConnection forKey:@"prefersSecuredConnection"];
[dic setUnsignedShort:self.legacyServerPort forKey:@"serverPort"];
/* During a copy operation, it is faster to copy these arrays as a whole.
It also preserves -secretKey value in IRCChannelConfig since that will
be lost when reconstructing from dictionary value. */
if (target == XRPortablePropertyDictTargetCopy ||
target == XRPortablePropertyDictTargetMutableCopy)
{
return [dic copy];
}
/* Channel List */
NSMutableArray<NSDictionary *> *channelListOut = [NSMutableArray array];
for (IRCChannelConfig *e in self.channelList) {
NSDictionary *d = e.dictionaryValue;
[channelListOut addObject:d];
}
if (channelListOut.count > 0) {
dic[@"channelList"] = [channelListOut copy];
}
/* Highlight list */
NSMutableArray<NSDictionary *> *highlightListOut = [NSMutableArray array];
for (IRCHighlightMatchCondition *e in self.highlightList) {
NSDictionary *d = e.dictionaryValue;
[highlightListOut addObject:d];
}
if (highlightListOut.count > 0) {
dic[@"highlightList"] = [highlightListOut copy];
}
/* Ignore list */
NSMutableArray<NSDictionary *> *ignoreListOut = [NSMutableArray array];
for (IRCAddressBookEntry *e in self.ignoreList) {
NSDictionary *d = e.dictionaryValue;
[ignoreListOut addObject:d];
}
if (ignoreListOut.count > 0) {
dic[@"ignoreList"] = [ignoreListOut copy];
}
/* Servers */
NSMutableArray<NSDictionary *> *serverListOut = [NSMutableArray array];
for (IRCServer *e in self.serverList) {
NSDictionary *d = e.dictionaryValue;
[serverListOut addObject:d];
}
if (serverListOut.count > 0) {
dic[@"serverList"] = [serverListOut copy];
}
return [dic dictionaryByRemovingDefaults:self->_defaults allowEmptyValues:YES];
}
#pragma mark -
#pragma mark Keychain Management
- (nullable NSString *)nicknamePassword
{
if (self->_nicknamePassword) {
return self->_nicknamePassword;
}
return self.nicknamePasswordFromKeychain;
}
- (nullable NSString *)nicknamePasswordFromKeychain
{
NSString *nicknamePasswordServiceName = [NSString stringWithFormat:@"textual.nickserv.%@", self.uniqueIdentifier];
NSString *kcPassword = [XRKeychain getPasswordFromKeychainItem:@"Textual (NickServ)"
withItemKind:@"application password"
forUsername:nil
serviceName:nicknamePasswordServiceName];
return kcPassword;
}
- (nullable NSString *)proxyPassword
{
if (self->_proxyPassword) {
return self->_proxyPassword;
}
return self.proxyPasswordFromKeychain;
}
- (nullable NSString *)proxyPasswordFromKeychain
{
NSString *proxyPasswordServiceName = [NSString stringWithFormat:@"textual.proxy-server.%@", self.uniqueIdentifier];
NSString *kcPassword = [XRKeychain getPasswordFromKeychainItem:@"Textual (Proxy Server Password)"
withItemKind:@"application password"
forUsername:nil
serviceName:proxyPasswordServiceName];
return kcPassword;
}
- (void)writeNicknamePasswordToKeychain
{
if (self->_nicknamePassword == nil) {
return;
}
NSString *nicknamePasswordServiceName = [NSString stringWithFormat:@"textual.nickserv.%@", self.uniqueIdentifier];
[XRKeychain modifyOrAddKeychainItem:@"Textual (NickServ)"
withItemKind:@"application password"
forUsername:nil
withNewPassword:self->_nicknamePassword
serviceName:nicknamePasswordServiceName];
self->_nicknamePassword = nil;
}
- (void)writeProxyPasswordToKeychain
{
if (self->_proxyPassword == nil) {
return;
}
NSString *proxyPasswordServiceName = [NSString stringWithFormat:@"textual.proxy-server.%@", self.uniqueIdentifier];
[XRKeychain modifyOrAddKeychainItem:@"Textual (Proxy Server Password)"
withItemKind:@"application password"
forUsername:nil
withNewPassword:self->_proxyPassword
serviceName:proxyPasswordServiceName];
self->_proxyPassword = nil;
}
- (void)destroyNicknamePasswordKeychainItem
{
NSString *nicknamePasswordServiceName = [NSString stringWithFormat:@"textual.nickserv.%@", self.uniqueIdentifier];
[XRKeychain deleteKeychainItem:@"Textual (NickServ)"
withItemKind:@"application password"
forUsername:nil
serviceName:nicknamePasswordServiceName];
self->_nicknamePassword = nil;
}
- (void)destroyProxyPasswordKeychainItem
{
NSString *proxyPasswordServiceName = [NSString stringWithFormat:@"textual.proxy-server.%@", self.uniqueIdentifier];
[XRKeychain deleteKeychainItem:@"Textual (Proxy Server Password)"
withItemKind:@"application password"
forUsername:nil
serviceName:proxyPasswordServiceName];
self->_proxyPassword = nil;
}
- (void)destroyServerPasswordKeychainItemAfterMigration
{
if (self->_migratedServerPasswordPendingDestroy == NO) {
return;
}
self->_migratedServerPasswordPendingDestroy = NO;
NSString *serverPasswordServiceName = [NSString stringWithFormat:@"textual.server.%@", self.uniqueIdentifier];
[XRKeychain deleteKeychainItem:@"Textual (Server Password)"
withItemKind:@"application password"
forUsername:nil
serviceName:serverPasswordServiceName];
}
#pragma mark -
#pragma mark Deprecated Properties
- (nullable NSString *)legacyServerAddress
{
IRCServer *server = self.serverList.firstObject;
if (server == nil) {
return self->_serverAddress;
}
return server.serverAddress;
}
- (uint16_t)legacyServerPort
{
IRCServer *server = self.serverList.firstObject;
if (server == nil) {
return self->_serverPort;
}
return server.serverPort;
}
- (BOOL)legacyPrefersSecuredConnection
{
IRCServer *server = self.serverList.firstObject;
if (server == nil) {
return self->_prefersSecuredConnection;
}
return server.prefersSecuredConnection;
}
- (BOOL)legacyConnectionPrefersModernCiphers
{
return (self.cipherSuites != RCMCipherSuiteCollectionNone);
}
- (BOOL)showConnectionPrefersIPv4Warning
{
TEXTUAL_IGNORE_DEPRECATION_BEGIN
return (self.addressType == IRCConnectionAddressTypeIPv4 && self.connectionPrefersIPv4);
TEXTUAL_IGNORE_DEPRECATION_END
}
@end
#pragma mark -
@implementation IRCClientConfigMutable
@dynamic addressType;
@dynamic alternateNicknames;
@dynamic autoConnect;
@dynamic autoReconnect;
@dynamic autoSleepModeDisconnect;
@dynamic autojoinWaitsForNickServ;
@dynamic awayNickname;
@dynamic channelList;
@dynamic cipherSuites;
@dynamic connectionName;
@dynamic connectionPrefersIPv4;
@dynamic fallbackEncoding;
@dynamic floodControlDelayTimerInterval;
@dynamic floodControlMaximumMessages;
@dynamic hideAutojoinDelayedWarnings;
@dynamic hideNetworkUnavailabilityNotices;
@dynamic highlightList;
@dynamic identityClientSideCertificate;
@dynamic ignoreList;
@dynamic lastMessageServerTime;
@dynamic loginCommands;
@dynamic nickname;
@dynamic nicknamePassword;
@dynamic normalLeavingComment;
@dynamic performDisconnectOnPongTimer;
@dynamic performDisconnectOnReachabilityChange;
@dynamic performPongTimer;
@dynamic primaryEncoding;
@dynamic proxyAddress;
@dynamic proxyPassword;
@dynamic proxyPort;
@dynamic proxyType;
@dynamic proxyUsername;
@dynamic realName;
@dynamic saslAuthenticationDisableExternalMechanism;
@dynamic sendAuthenticationRequestsToUserServ;
@dynamic sendWhoCommandRequestsToChannels;
@dynamic serverList;
@dynamic setInvisibleModeOnConnect;
@dynamic sidebarItemExpanded;
@dynamic sleepModeLeavingComment;
@dynamic username;
@dynamic validateServerCertificateChain;
@dynamic zncIgnoreConfiguredAutojoin;
@dynamic zncIgnorePlaybackNotifications;
@dynamic zncIgnoreUserNotifications;
@dynamic zncOnlyPlaybackLatest;
+ (BOOL)isMutable
{
return YES;
}
- (__kindof XRPortablePropertyDict *)immutableClass
{
return [IRCClientConfig self];
}
- (void)setAutoConnect:(BOOL)autoConnect
{
if (self->_autoConnect != autoConnect) {
self->_autoConnect = autoConnect;
}
}
- (void)setAutoReconnect:(BOOL)autoReconnect
{
if (self->_autoReconnect != autoReconnect) {
self->_autoReconnect = autoReconnect;
}
}
- (void)setAutoSleepModeDisconnect:(BOOL)autoSleepModeDisconnect
{
if (self->_autoSleepModeDisconnect != autoSleepModeDisconnect) {
self->_autoSleepModeDisconnect = autoSleepModeDisconnect;
}
}
- (void)setAutojoinWaitsForNickServ:(BOOL)autojoinWaitsForNickServ
{
if (self->_autojoinWaitsForNickServ != autojoinWaitsForNickServ) {
self->_autojoinWaitsForNickServ = autojoinWaitsForNickServ;
}
}
- (void)setConnectionPrefersIPv4:(BOOL)connectionPrefersIPv4
{
TEXTUAL_DEPRECATED_WARNING
if (self->_connectionPrefersIPv4 != connectionPrefersIPv4) {
self->_connectionPrefersIPv4 = connectionPrefersIPv4;
}
}
- (void)setHideAutojoinDelayedWarnings:(BOOL)hideAutojoinDelayedWarnings
{
if (self->_hideAutojoinDelayedWarnings != hideAutojoinDelayedWarnings) {
self->_hideAutojoinDelayedWarnings = hideAutojoinDelayedWarnings;
}
}
- (void)setHideNetworkUnavailabilityNotices:(BOOL)hideNetworkUnavailabilityNotices
{
if (self->_hideNetworkUnavailabilityNotices != hideNetworkUnavailabilityNotices) {
self->_hideNetworkUnavailabilityNotices = hideNetworkUnavailabilityNotices;
}
}
- (void)setPerformDisconnectOnPongTimer:(BOOL)performDisconnectOnPongTimer
{
if (self->_performDisconnectOnPongTimer != performDisconnectOnPongTimer) {
self->_performDisconnectOnPongTimer = performDisconnectOnPongTimer;
}
}
- (void)setPerformDisconnectOnReachabilityChange:(BOOL)performDisconnectOnReachabilityChange
{
if (self->_performDisconnectOnReachabilityChange != performDisconnectOnReachabilityChange) {
self->_performDisconnectOnReachabilityChange = performDisconnectOnReachabilityChange;
}
}
- (void)setPerformPongTimer:(BOOL)performPongTimer
{
if (self->_performPongTimer != performPongTimer) {
self->_performPongTimer = performPongTimer;
}
}
- (void)setSaslAuthenticationDisableExternalMechanism:(BOOL)saslAuthenticationDisableExternalMechanism
{
if (self->_saslAuthenticationDisableExternalMechanism != saslAuthenticationDisableExternalMechanism) {
self->_saslAuthenticationDisableExternalMechanism = saslAuthenticationDisableExternalMechanism;
}
}
- (void)setSendAuthenticationRequestsToUserServ:(BOOL)sendAuthenticationRequestsToUserServ
{
if (self->_sendAuthenticationRequestsToUserServ != sendAuthenticationRequestsToUserServ) {
self->_sendAuthenticationRequestsToUserServ = sendAuthenticationRequestsToUserServ;
}
}
- (void)setSendWhoCommandRequestsToChannels:(BOOL)sendWhoCommandRequestsToChannels
{
if (self->_sendWhoCommandRequestsToChannels != sendWhoCommandRequestsToChannels) {
self->_sendWhoCommandRequestsToChannels = sendWhoCommandRequestsToChannels;
}
}
- (void)setSetInvisibleModeOnConnect:(BOOL)setInvisibleModeOnConnect
{
if (self->_setInvisibleModeOnConnect != setInvisibleModeOnConnect) {
self->_setInvisibleModeOnConnect = setInvisibleModeOnConnect;
}
}
- (void)setSidebarItemExpanded:(BOOL)sidebarItemExpanded
{
if (self->_sidebarItemExpanded != sidebarItemExpanded) {
self->_sidebarItemExpanded = sidebarItemExpanded;
}
}
- (void)setValidateServerCertificateChain:(BOOL)validateServerCertificateChain
{
if (self->_validateServerCertificateChain != validateServerCertificateChain) {
self->_validateServerCertificateChain = validateServerCertificateChain;
}
}
- (void)setZncIgnoreConfiguredAutojoin:(BOOL)zncIgnoreConfiguredAutojoin
{
if (self->_zncIgnoreConfiguredAutojoin != zncIgnoreConfiguredAutojoin) {
self->_zncIgnoreConfiguredAutojoin = zncIgnoreConfiguredAutojoin;
}
}
- (void)setZncIgnorePlaybackNotifications:(BOOL)zncIgnorePlaybackNotifications
{
if (self->_zncIgnorePlaybackNotifications != zncIgnorePlaybackNotifications) {
self->_zncIgnorePlaybackNotifications = zncIgnorePlaybackNotifications;
}
}
- (void)setZncIgnoreUserNotifications:(BOOL)zncIgnoreUserNotifications
{
if (self->_zncIgnoreUserNotifications != zncIgnoreUserNotifications) {
self->_zncIgnoreUserNotifications = zncIgnoreUserNotifications;
}
}
- (void)setZncOnlyPlaybackLatest:(BOOL)zncOnlyPlaybackLatest
{
if (self->_zncOnlyPlaybackLatest != zncOnlyPlaybackLatest) {
self->_zncOnlyPlaybackLatest = zncOnlyPlaybackLatest;
}
}
- (void)setAddressType:(IRCConnectionAddressType)addressType
{
if (self->_addressType != addressType) {
self->_addressType = addressType;
}
}
- (void)setProxyType:(IRCConnectionProxyType)proxyType
{
if (self->_proxyType != proxyType) {
self->_proxyType = proxyType;
}
}
- (void)setIgnoreList:(NSArray<IRCAddressBookEntry *> *)ignoreList
{
NSParameterAssert(ignoreList != nil);
if (self->_ignoreList != ignoreList) {
self->_ignoreList = [ignoreList copy];
}
}
- (void)setChannelList:(NSArray<IRCChannelConfig *> *)channelList
{
NSParameterAssert(channelList != nil);
if (self->_channelList != channelList) {
self->_channelList = [channelList copy];
}
}
- (void)setHighlightList:(NSArray<IRCHighlightMatchCondition *> *)highlightList
{
NSParameterAssert(highlightList != nil);
if (self->_highlightList != highlightList) {
self->_highlightList = [highlightList copy];
}
}
- (void)setAlternateNicknames:(NSArray<NSString *> *)alternateNicknames
{
NSParameterAssert(alternateNicknames != nil);
if (self->_alternateNicknames != alternateNicknames) {
self->_alternateNicknames = [alternateNicknames copy];
}
}
- (void)setLoginCommands:(NSArray<NSString *> *)loginCommands
{
NSParameterAssert(loginCommands != nil);
if (self->_loginCommands != loginCommands) {
self->_loginCommands = [loginCommands copy];
}
}
- (void)setServerList:(NSArray<IRCServer *> *)serverList
{
NSParameterAssert(serverList != nil);
if (self->_serverList != serverList) {
self->_serverList = [serverList copy];
}
}
- (void)setIdentityClientSideCertificate:(nullable NSData *)identityClientSideCertificate
{
if (self->_identityClientSideCertificate != identityClientSideCertificate) {
self->_identityClientSideCertificate = [identityClientSideCertificate copy];
}
}
- (void)setAwayNickname:(nullable NSString *)awayNickname
{
if (self->_awayNickname != awayNickname) {
self->_awayNickname = [awayNickname copy];
}
}
- (void)setConnectionName:(NSString *)connectionName
{
NSParameterAssert(connectionName != nil);
if (self->_connectionName != connectionName) {
self->_connectionName = [connectionName copy];
}
}
- (void)setNickname:(NSString *)nickname
{
NSParameterAssert(nickname != nil);
if (self->_nickname != nickname) {
self->_nickname = [nickname copy];
}
}
- (void)setNicknamePassword:(nullable NSString *)nicknamePassword
{
if (self->_nicknamePassword != nicknamePassword) {
self->_nicknamePassword = [nicknamePassword copy];
}
}
- (void)setNormalLeavingComment:(NSString *)normalLeavingComment
{
NSParameterAssert(normalLeavingComment != nil);
if (self->_normalLeavingComment != normalLeavingComment) {
self->_normalLeavingComment = [normalLeavingComment copy];
}
}
- (void)setProxyAddress:(nullable NSString *)proxyAddress
{
if (self->_proxyAddress != proxyAddress) {
self->_proxyAddress = [proxyAddress copy];
}
}
- (void)setProxyPassword:(nullable NSString *)proxyPassword
{
if (self->_proxyPassword != proxyPassword) {
self->_proxyPassword = [proxyPassword copy];
}
}
- (void)setProxyUsername:(nullable NSString *)proxyUsername
{
if (self->_proxyUsername != proxyUsername) {
self->_proxyUsername = [proxyUsername copy];
}
}
- (void)setRealName:(NSString *)realName
{
NSParameterAssert(realName != nil);
if (self->_realName != realName) {
self->_realName = [realName copy];
}
}
- (void)setSleepModeLeavingComment:(NSString *)sleepModeLeavingComment
{
NSParameterAssert(sleepModeLeavingComment != nil);
if (self->_sleepModeLeavingComment != sleepModeLeavingComment) {
self->_sleepModeLeavingComment = [sleepModeLeavingComment copy];
}
}
- (void)setUsername:(NSString *)username
{
NSParameterAssert(username != nil);
if (self->_username != username) {
self->_username = [username copy];
}
}
- (void)setFallbackEncoding:(NSStringEncoding)fallbackEncoding
{
if (self->_fallbackEncoding != fallbackEncoding) {
self->_fallbackEncoding = fallbackEncoding;
}
}
- (void)setPrimaryEncoding:(NSStringEncoding)primaryEncoding
{
if (self->_primaryEncoding != primaryEncoding) {
self->_primaryEncoding = primaryEncoding;
}
}
- (void)setLastMessageServerTime:(NSTimeInterval)lastMessageServerTime
{
if (self->_lastMessageServerTime != lastMessageServerTime) {
self->_lastMessageServerTime = lastMessageServerTime;
}
}
- (void)setFloodControlDelayTimerInterval:(NSUInteger)floodControlDelayTimerInterval
{
NSParameterAssert(floodControlDelayTimerInterval >= IRCConnectionConfigFloodControlMinimumDelayInterval &&
floodControlDelayTimerInterval <= IRCConnectionConfigFloodControlMaximumDelayInterval);
if (self->_floodControlDelayTimerInterval != floodControlDelayTimerInterval) {
self->_floodControlDelayTimerInterval = floodControlDelayTimerInterval;
}
}
- (void)setFloodControlMaximumMessages:(NSUInteger)floodControlMaximumMessages
{
NSParameterAssert(floodControlMaximumMessages >= IRCConnectionConfigFloodControlMinimumMessageCount &&
floodControlMaximumMessages <= IRCConnectionConfigFloodControlMaximumMessageCount);
if (self->_floodControlMaximumMessages != floodControlMaximumMessages) {
self->_floodControlMaximumMessages = floodControlMaximumMessages;
}
}
- (void)setProxyPort:(uint16_t)proxyPort
{
if (self->_proxyPort != proxyPort) {
self->_proxyPort = proxyPort;
}
}
- (void)setCipherSuites:(RCMCipherSuiteCollection)cipherSuites
{
if (self->_cipherSuites != cipherSuites) {
self->_cipherSuites = cipherSuites;
}
}
@end
NS_ASSUME_NONNULL_END