/* SOGoSystemDefaults.m - this file is part of SOGo * * Copyright (C) 2009-2021 Inverse inc. * Copyright (C) 2012 Jeroen Dekkers * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #import #import #import #import #import #import #import #import "NSArray+Utilities.h" #import "NSString+Crypto.h" #import "NSDictionary+Utilities.h" #import "SOGoStartupLogger.h" #import "SOGoSystemDefaults.h" #import "SOGoConstants.h" @implementation SOGoSystemDefaults #if defined(LDAP_CONFIG) #endif typedef void (*NSUserDefaultsInitFunction) (); #define DIR_SEP "/" #ifndef NSUIntegerMax #define NSUIntegerMax UINTPTR_MAX #endif static void BootstrapNSUserDefaults () { char *filename; NSUserDefaultsInitFunction SOGoNSUserDefaultsBootstrap; void *handle; filename = SOGO_LIBDIR DIR_SEP "libSOGoNSUserDefaults.so.1"; handle = dlopen (filename, RTLD_NOW | RTLD_GLOBAL); if (handle) { SOGoNSUserDefaultsBootstrap = dlsym (handle, "SOGoNSUserDefaultsBootstrap"); if (SOGoNSUserDefaultsBootstrap) SOGoNSUserDefaultsBootstrap (); } } static void _injectConfigurationFromFile (NSMutableDictionary *defaultsDict, NSString *filename, NSObject *logger) { NSDictionary *newConfig, *fileAttrs; NSFileManager *fm; fm = [NSFileManager defaultManager]; if ([fm fileExistsAtPath: filename]) { fileAttrs = [fm fileAttributesAtPath: filename traverseLink: YES]; if (![fileAttrs objectForKey: @"NSFileSize"]) { [logger errorWithFormat: @"Can't get file attributes from '%@'", filename]; exit(1); } if ([[fileAttrs objectForKey: @"NSFileSize"] intValue] == 0 ) { [logger warnWithFormat: @"Empty file: '%@'. Skipping", filename]; } else { newConfig = [NSDictionary dictionaryWithContentsOfFile: filename]; if (newConfig) [defaultsDict addEntriesFromDictionary: newConfig]; else { [logger errorWithFormat: @"Cannot read configuration from '%@'. Aborting", filename]; exit(1); } } } } + (void) prepareUserDefaults { /* Load settings from configuration files and * enforce the following order of precedence. * First match wins * 1. Command line arguments * 2. .GNUstepDefaults * 3. /etc/sogo/{debconf,sogo}.conf * 4. SOGoDefaults.plist * * The default standardUserDefaults search list is as follows: * GSPrimaryDomain * NSArgumentDomain (command line arguments) * applicationDomain (sogod) * NSGlobalDomain * GSConfigDomain * (languages) * NSRegistrationDomain * * We'll end up with this search list: * NSArgumentDomain (command line arguments) * sogodRuntimeDomain (config from all config files) * GSPrimaryDomain * NSGlobalDomain * GSConfigDomain * (languages) * NSRegistrationDomain (SOPE loads its defaults in this one) */ NSDictionary *sogodDomain; NSMutableDictionary *configFromFiles; NSUserDefaults *ud; SOGoStartupLogger *logger; NSBundle *bundle; NSString *confFiles[] = {@"/etc/sogo/debconf.conf", @"/etc/sogo/sogo.conf"}; NSString *filename, *redirectURL; NSUInteger count; logger = [SOGoStartupLogger sharedLogger]; /* Load the configuration from the standard user default files */ ud = [NSUserDefaults standardUserDefaults]; /* Populate configFromFiles with default values from SOGoDefaults.plist */ configFromFiles = [NSMutableDictionary dictionaryWithCapacity:0]; bundle = [NSBundle bundleForClass: self]; filename = [bundle pathForResource: @"SOGoDefaults" ofType: @"plist"]; if (filename) _injectConfigurationFromFile (configFromFiles, filename, logger); /* Fill/Override configFromFiles values with configuration stored * in "/etc" */ for (count = 0; count < sizeof(confFiles)/sizeof(confFiles[0]); count++) _injectConfigurationFromFile (configFromFiles, confFiles[count], logger); /* This dance is required to let other appplications (sogo-tool) use * options from the sogod domain while preserving the order of precedence * - remove the 'sogod' domain from the user defaults search list * - Load the content of the sogod domain into configFromFiles * Thereby overriding values from the config files loaded above */ [ud removeSuiteNamed: @"sogod"]; sogodDomain = [ud persistentDomainForName: @"sogod"]; if ([sogodDomain count]) [configFromFiles addEntriesFromDictionary: sogodDomain]; /* Add a volatile domain containing the config to the search list. * The domain is added at the very front of the search list */ [ud setVolatileDomain: configFromFiles forName: @"sogodRuntimeDomain"]; [ud addSuiteNamed: @"sogodRuntimeDomain"]; /* NSArgumentsDomain goes back in front of the search list */ [ud addSuiteNamed: @"NSArgumentDomain"]; /* issue a warning if WOApplicationRedirectURL is used */ redirectURL = [ud stringForKey: @"WOApplicationRedirectURL"]; if (redirectURL) { [logger warnWithFormat: @"Using obsolete 'WOApplicationRedirectURL' user default."]; [logger warnWithFormat: @" Please configure the use of the x-webobjects-XXX headers" @" with your webserver (see sample files)."]; if ([redirectURL hasSuffix: @"/"]) [ud setObject: [redirectURL substringToIndex: [redirectURL length] - 1] forKey: @"WOApplicationRedirectURL"]; } } + (void) initialize { BootstrapNSUserDefaults (); [self prepareUserDefaults]; } + (SOGoSystemDefaults *) sharedSystemDefaults { static SOGoSystemDefaults *sharedSystemDefaults = nil; NSUserDefaults *ud; if (!sharedSystemDefaults) { ud = [NSUserDefaults standardUserDefaults]; sharedSystemDefaults = [self defaultsSourceWithSource: ud andParentSource: nil]; [sharedSystemDefaults retain]; } return sharedSystemDefaults; } - (id) init { if ((self = [super init])) { loginDomains = nil; } return self; } - (void) dealloc { [loginDomains release]; [super dealloc]; } - (BOOL) migrate { static NSDictionary *migratedKeys = nil; if (!migratedKeys) { migratedKeys = [NSDictionary dictionaryWithObjectsAndKeys: @"SOGoProfileURL", @"AgenorProfileURL", @"SOGoTimeZone", @"SOGoServerTimeZone", nil]; [migratedKeys retain]; } return ([self migrateOldDefaultsWithDictionary: migratedKeys] | [super migrate]); } - (NSArray *) domainIds { NSDictionary *domains = [self dictionaryForKey: @"domains"]; return [domains allKeys]; } - (BOOL) doesLoginTypeByDomain { return ([self dictionaryForKey: @"SOGoLoginTypeByDomain"] != nil); } - (NSString *) getLoginTypeForDomain: (NSString*) _domain { NSDictionary *domains, *config; NSString *type; if(![self doesLoginTypeByDomain]) return nil; domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"]; if([domains objectForKey: _domain]) { config = [domains objectForKey: _domain]; } else if([domains objectForKey: @"login_default"]) { config = [domains objectForKey: @"login_default"]; } else return nil; if((type = [config objectForKey: @"type"])) { return type; } else return nil; } - (NSString *) getImapAuthMechForDomain: (NSString*) _domain { NSDictionary *domains, *config; NSString *type; if(![self doesLoginTypeByDomain]) return nil; domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"]; if([domains objectForKey: _domain]) { config = [domains objectForKey: _domain]; } else if([domains objectForKey: @"login_default"]) { config = [domains objectForKey: @"login_default"]; } else return nil; if((type = [config objectForKey: @"imapAuthMech"])) { return type; } else return nil; } - (NSString *) getSmtpAuthMechForDomain: (NSString*) _domain { NSDictionary *domains, *config; NSString *type; if(![self doesLoginTypeByDomain]) return nil; domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"]; if([domains objectForKey: _domain]) { config = [domains objectForKey: _domain]; } else if([domains objectForKey: @"login_default"]) { config = [domains objectForKey: @"login_default"]; } else return nil; if((type = [config objectForKey: @"smtpAuthMech"])) { return type; } else return nil; } - (NSString *) getLoginConfigForDomain: (NSDictionary*) _domain { NSDictionary *domains, *config; if(![self doesLoginTypeByDomain]) return nil; domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"]; if([domains objectForKey: _domain]) { config = [domains objectForKey: _domain]; } else if([domains objectForKey: @"login_default"]) { config = [domains objectForKey: @"login_default"]; } if(config) return config; else return nil; } - (BOOL) hasOpenIdType { if([self doesLoginTypeByDomain]) { NSDictionary *domainsConfig; NSEnumerator *e; NSString *domain, *type; if(![self doesLoginTypeByDomain]) return NO; domainsConfig = [self dictionaryForKey: @"SOGoLoginTypeByDomain"]; e = [domainsConfig keyEnumerator]; while((domain = [e nextObject])) { if((type = [[domainsConfig objectForKey: domain] objectForKey: @"type"])) { if([type isEqualToString: @"openid"]) return YES; } } return NO; } else return [[self authenticationType] isEqualToString: @"openid"]; } - (BOOL) enableDomainBasedUID { return [self boolForKey: @"SOGoEnableDomainBasedUID"]; } - (BOOL) forbidUnknownDomainsAuth { return [self boolForKey: @"SOGoForbidUnknownDomainsAuth"]; } - (NSArray *) domainsAllowed { return [NSMutableArray arrayWithArray: [self stringArrayForKey: @"SOGoDomainAllowed"]]; } - (NSArray *) loginDomains { NSMutableArray *filteredLoginDomains; NSArray *domains; id currentObject; int count; if (self->loginDomains == nil) { filteredLoginDomains = [NSMutableArray arrayWithArray: [self stringArrayForKey: @"SOGoLoginDomains"]]; domains = [self domainIds]; count = [filteredLoginDomains count]; while (count > 0) { count--; currentObject = [filteredLoginDomains objectAtIndex: count]; if (![domains containsObject: currentObject]) { [filteredLoginDomains removeObject: currentObject]; [self warnWithFormat: @"SOGoLoginDomains contains an invalid domain : %@", currentObject]; } } ASSIGN (self->loginDomains, filteredLoginDomains); } return self->loginDomains; } - (NSArray *) visibleDomainsForDomain: (NSString *) domain { NSMutableArray *domains; NSArray *definedDomains, *visibleDomains, *currentGroup; NSEnumerator *groups; NSString *currentDomain; definedDomains = [self domainIds]; visibleDomains = [self arrayForKey: @"SOGoDomainsVisibility"]; domains = [NSMutableArray array]; groups = [visibleDomains objectEnumerator]; while ((currentGroup = (NSArray *)[groups nextObject])) { if ([currentGroup containsObject: domain]) [domains addObjectsFromArray: currentGroup]; } // Remove lookup domain and invalid domains groups = [domains objectEnumerator]; while ((currentDomain = [groups nextObject])) { if ([currentDomain isEqualToString: domain] || ![definedDomains containsObject: currentDomain]) [domains removeObject: currentDomain]; } return [domains uniqueObjects]; } /* System-level only */ - (BOOL) crashOnSessionCreate { return [self boolForKey: @"SOGoCrashOnSessionCreate"]; } - (BOOL) debugRequests { return [self boolForKey: @"SOGoDebugRequests"]; } - (BOOL) debugLeaks; { return [self boolForKey: @"SOGoDebugLeaks"]; } - (int) vmemLimit { return [self integerForKey: @"SxVMemLimit"]; } - (BOOL) trustProxyAuthentication; { return [self boolForKey: @"SOGoTrustProxyAuthentication"]; } - (NSString *) encryptionKey; { return [self stringForKey: @"SOGoEncryptionKey"]; } - (BOOL) isSogoSecretSet { NSString *type; type = [self stringForKey: @"SOGoSecretType"]; if(!type || [type isEqualToString:@"none"]) return NO; else return YES; } - (NSString *) sogoSecretValue { NSString *value, *type; NSDictionary *env; type = [self stringForKey: @"SOGoSecretType"]; if(!type) type = @"none"; if ([type isEqualToString:@"plain"]) { value = [self stringForKey: @"SOGoSecretValue"]; } else if ([type isEqualToString:@"env"]) { value = [self stringForKey: @"SOGoSecretValue"]; [self errorWithFormat: @"SOGo env fetching %@", value]; if(!value || [value length] < 1) { [self errorWithFormat: @"SOGoSecretValue is not set!"]; return nil; } env = [[NSProcessInfo processInfo] environment]; value = [env objectForKey:value]; } else if ([type isEqualToString:@"none"]) { return nil; } else { [self errorWithFormat: @"SOGo can't understand the type of secret SOGoSecretType"]; return nil; } if(!value || [value length] != 32){ [self errorWithFormat: @"SOGo doesn't have a correct secret value of 32 chars SOGoSecretValue"]; return nil; } return value; } - (BOOL) useRelativeURLs { return [self boolForKey: @"WOUseRelativeURLs"]; } - (NSString *) sieveFolderEncoding { return [self stringForKey: @"SOGoSieveFolderEncoding"]; } - (BOOL) isWebAccessEnabled { return [self boolForKey: @"SOGoWebAccessEnabled"]; } - (BOOL) isCalendarDAVAccessEnabled { return [self boolForKey: @"SOGoCalendarDAVAccessEnabled"]; } - (BOOL) isCalendarJitsiLinkEnabled { return [self boolForKey: @"SOGoCalendarEnableJitsiLink"]; } - (BOOL) isAddressBookDAVAccessEnabled { return [self boolForKey: @"SOGoAddressBookDAVAccessEnabled"]; } - (BOOL) enableEMailAlarms { return [self boolForKey: @"SOGoEnableEMailAlarms"]; } - (BOOL) disableOrganizerEventCheck { return [self boolForKey: @"SOGoDisableOrganizerEventCheck"]; } - (NSString *) faviconRelativeURL { return [self stringForKey: @"SOGoFaviconRelativeURL"]; } - (NSString *) zipPath { return [self stringForKey: @"SOGoZipPath"]; } - (int) port { return [self integerForKey: @"WOPort"]; } - (int) workers { return [self integerForKey: @"WOWorkersCount"]; } - (NSString *) logFile { return [self stringForKey: @"WOLogFile"]; } - (NSString *) pidFile { return [self stringForKey: @"WOPidFile"]; } - (NSTimeInterval) cacheCleanupInterval { return [self floatForKey: @"SOGoCacheCleanupInterval"]; } - (NSString *) memcachedHost { return [self stringForKey: @"SOGoMemcachedHost"]; } - (BOOL) uixDebugEnabled { return [self boolForKey: @"SOGoUIxDebugEnabled"]; } - (BOOL) easDebugEnabled { return [self boolForKey: @"SOGoEASDebugEnabled"]; } - (BOOL) openIdDebugEnabled { return [self boolForKey: @"SOGoOpenIDDebugEnabled"]; } - (BOOL) apiDebugEnabled { return [self boolForKey: @"SOGoAPIDebugEnabled"]; } - (BOOL) tnefDecoderDebugEnabled { return [self boolForKey: @"SOGoTnefDecoderDebugEnabled"]; } - (BOOL) xsrfValidationEnabled { id o; if (!(o = [self objectForKey: @"SOGoXSRFValidationEnabled"])) { return YES; } return [o boolValue]; } - (NSString *) pageTitle { return [self stringForKey: @"SOGoPageTitle"]; } - (NSString *) helpURL { return [self stringForKey: @"SOGoHelpURL"]; } NSComparisonResult languageSort(id el1, id el2, void *context) { NSString *t1, *t2; t1 = [context labelForKey: el1]; t2 = [context labelForKey: el2]; return [t1 compare: t2 options: NSCaseInsensitiveSearch]; } - (NSArray *) supportedLanguages { static NSArray *supportedLanguages = nil; if (!supportedLanguages) { supportedLanguages = [self stringArrayForKey: @"SOGoSupportedLanguages"]; [supportedLanguages retain]; } return supportedLanguages; } - (BOOL) userCanChangePassword { return [self boolForKey: SOGoPasswordChangeEnabled]; } - (BOOL) uixAdditionalPreferences { return [self boolForKey: @"SOGoUIxAdditionalPreferences"]; } - (NSString *) loginSuffix { return [self stringForKey: @"SOGoLoginSuffix"]; } - (NSString *) authenticationType { return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString]; } - (BOOL) isSsoUsed: (NSString *) domain { NSString* authType; authType = [self getLoginTypeForDomain: domain]; if(!authType) authType = [self authenticationType]; return ([authType isEqualToString: @"cas"] || [authType isEqualToString: @"saml2"] || [authType isEqualToString: @"openid"]); } - (NSString *) davAuthenticationType { return [[self stringForKey: @"SOGoDAVAuthenticationType"] lowercaseString]; } - (NSString *) CASServiceURL { return [self stringForKey: @"SOGoCASServiceURL"]; } - (BOOL) CASLogoutEnabled { return [self boolForKey: @"SOGoCASLogoutEnabled"]; } /* OpenId Support */ - (NSString *) openIdConfigUrl { return [self stringForKey: @"SOGoOpenIdConfigUrl"]; } - (NSString *) openIdScope { return [self stringForKey: @"SOGoOpenIdScope"]; } - (NSString *) openIdClient { return [self stringForKey: @"SOGoOpenIdClient"]; } - (NSString *) openIdClientSecret { return [self stringForKey: @"SOGoOpenIdClientSecret"]; } - (NSString *) openIdEmailParam { NSString *emailParam; emailParam = [self stringForKey: @"SOGoOpenIdEmailParam"]; if(!emailParam) emailParam = @"email"; return emailParam; } - (NSString *) openIdHttpVersion { NSString *httpVersion; httpVersion = [self stringForKey: @"SOGoOpenIdHttpVersion"]; if(!httpVersion) httpVersion = @"HTTP/1.1"; return httpVersion; } - (BOOL) openIdLogoutEnabled: (NSString *) _domain { if(_domain && [self doesLoginTypeByDomain]) { NSDictionary *config; NSString *type; id value; if((config = [self getLoginConfigForDomain: _domain])) { if((type = [config objectForKey: @"type"]) && [type isEqualToString:@"openid"]) return [self boolForKey: @"SOGoOpenIdLogoutEnabled" andDict: config]; } return NO; } return [self boolForKey: @"SOGoOpenIdLogoutEnabled"]; } - (BOOL) openIdSendDomainInfo { return [self boolForKey: @"SOGoOpenIdSendDomainInfo"]; } - (int) openIdTokenCheckInterval { int v; v = [self integerForKey: @"SOGoOpenIdTokenCheckInterval"]; if (!v) v = 0; if(v<0) v = 0; return v; } - (BOOL) openIdEnableRefreshToken { return [self boolForKey: @"SOGoOpenIdEnableRefreshToken"]; } /* SAML2 support */ - (NSString *) SAML2PrivateKeyLocation { return [self stringForKey: @"SOGoSAML2PrivateKeyLocation"]; } - (NSString *) SAML2CertificateLocation; { return [self stringForKey: @"SOGoSAML2CertificateLocation"]; } - (NSString *) SAML2IdpMetadataLocation { return [self stringForKey: @"SOGoSAML2IdpMetadataLocation"]; } - (NSString *) SAML2IdpPublicKeyLocation { return [self stringForKey: @"SOGoSAML2IdpPublicKeyLocation"]; } - (NSString *) SAML2IdpCertificateLocation { return [self stringForKey: @"SOGoSAML2IdpCertificateLocation"]; } - (BOOL) SAML2LogoutEnabled { return [self boolForKey: @"SOGoSAML2LogoutEnabled"]; } - (NSString *) SAML2LogoutURL { return [self stringForKey: @"SOGoSAML2LogoutURL"]; } - (NSString *) SAML2LoginAttribute { return [self stringForKey: @"SOGoSAML2LoginAttribute"]; } - (BOOL) enablePublicAccess { return [self boolForKey: @"SOGoEnablePublicAccess"]; } // // // - (int) maximumFailedLoginCount { return [self integerForKey: @"SOGoMaximumFailedLoginCount"]; } - (int) maximumFailedLoginInterval { int v; v = [self integerForKey: @"SOGoMaximumFailedLoginInterval"]; if (!v) v = 10; return v; } - (int) failedLoginBlockInterval { int v; v = [self integerForKey: @"SOGoFailedLoginBlockInterval"]; if (!v) v = 300; return v; } // // // - (int) maximumMessageSizeLimit { return [self integerForKey: @"SOGoMaximumMessageSizeLimit"]; } // // // - (NSUInteger) maximumMessageSubmissionCount { NSUInteger v; v = [self integerForKey: @"SOGoMaximumMessageSubmissionCount"]; if (!v) return NSUIntegerMax; return v; } - (NSUInteger) maximumRecipientCount { NSUInteger v; v = [self integerForKey: @"SOGoMaximumRecipientCount"]; if (!v) return NSUIntegerMax; return v; } - (int) maximumSubmissionInterval { int v; v = [self integerForKey: @"SOGoMaximumSubmissionInterval"]; if (!v) v = 30; return v; } - (int) messageSubmissionBlockInterval { int v; v = [self integerForKey: @"SOGoMessageSubmissionBlockInterval"]; if (!v) v = 300; return v; } // // SOGo rate-limiting // - (int) maximumRequestCount { return [self integerForKey: @"SOGoMaximumRequestCount"]; } - (int) maximumRequestInterval { int v; v = [self integerForKey: @"SOGoMaximumRequestInterval"]; if (!v) v = 30; return v; } - (int) requestBlockInterval { int v; v = [self integerForKey: @"SOGoRequestBlockInterval"]; if (!v) v = 300; return v; } // // SOGo EAS settings // - (int) maximumPingInterval { int v; v = [self integerForKey: @"SOGoMaximumPingInterval"]; if (!v) v = 10; return v; } - (int) maximumSyncInterval { int v; v = [self integerForKey: @"SOGoMaximumSyncInterval"]; if (!v) v = 30; return v; } - (int) internalSyncInterval { int v; v = [self integerForKey: @"SOGoInternalSyncInterval"]; if (!v) v = 10; return v; } - (int) maximumSyncWindowSize { return [self integerForKey: @"SOGoMaximumSyncWindowSize"]; } - (int) maximumSyncResponseSize { int v; v = [self integerForKey: @"SOGoMaximumSyncResponseSize"]; if (v > 0) v = v * 1024; return v; } - (BOOL) easSearchInBody { return [self boolForKey: @"SOGoEASSearchInBody"]; } - (BOOL) isEasUIDisabled { return [self boolForKey: @"SOGoEASDisableUI"]; } // // See https://msdn.microsoft.com/en-us/library/gg672032(v=exchg.80).aspx // - (int) maximumPictureSize { int v; v = [self integerForKey: @"SOGoMaximumPictureSize"]; if (!v) v = 102400; return v; } - (BOOL) isPasswordRecoveryEnabled { return [self boolForKey: @"SOGoPasswordRecoveryEnabled"]; } - (NSArray *) passwordRecoveryDomains { static NSArray *passwordRecoveryDomains = nil; if (!passwordRecoveryDomains) { passwordRecoveryDomains = [self stringArrayForKey: @"SOGoPasswordRecoveryDomains"]; [passwordRecoveryDomains retain]; } return passwordRecoveryDomains; } - (NSString *) JWTSecret { NSString *secret; secret = [self stringForKey: @"SOGoJWTSecret"]; if (!secret) secret = @"SOGo"; // Default secret return secret; } - (NSArray *) disableSharing { static NSArray *disableSharing = nil; if (!disableSharing) { disableSharing = [self stringArrayForKey: @"SOGoDisableSharing"]; [disableSharing retain]; } return disableSharing; } - (BOOL)isURLEncryptionEnabled { return [self boolForKey: @"SOGoURLEncryptionEnabled"]; } - (NSString *) urlEncryptionPassphrase { NSString *passphrase; passphrase = [self stringForKey: @"SOGoURLEncryptionPassphrase"]; if (!passphrase) passphrase = @"SOGoSuperSecret0"; // Default passphrase return passphrase; } - (NSArray *) disableSharingAnyAuthUser { static NSArray *disableSharingAnyAuthUser = nil; if (!disableSharingAnyAuthUser) { disableSharingAnyAuthUser = [self stringArrayForKey: @"SOGoDisableSharingAnyAuthUser"]; [disableSharingAnyAuthUser retain]; } return disableSharingAnyAuthUser; } - (NSArray *) disableExport { static NSArray *disableExport = nil; if (!disableExport) { disableExport = [self stringArrayForKey: @"SOGoDisableExport"]; [disableExport retain]; } return disableExport; } - (BOOL) enableMailCleaning { return [self boolForKey: @"SOGoEnableMailCleaning"]; } @end