1/* SOGoSystemDefaults.m - this file is part of SOGo 2 * 3 * Copyright (C) 2009-2021 Inverse inc. 4 * Copyright (C) 2012 Jeroen Dekkers <jeroen@dekkers.ch> 5 * 6 * This file is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2, or (at your option) 9 * any later version. 10 * 11 * This file is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; see the file COPYING. If not, write to 18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 * Boston, MA 02111-1307, USA. 20 */ 21 22#import <dlfcn.h> 23 24#import <Foundation/NSBundle.h> 25#import <Foundation/NSFileManager.h> 26#import <Foundation/NSFileManager.h> 27#import <Foundation/NSUserDefaults.h> 28 29#import <NGExtensions/NSObject+Logs.h> 30 31#import "NSArray+Utilities.h" 32#import "NSDictionary+Utilities.h" 33#import "SOGoStartupLogger.h" 34 35#import "SOGoSystemDefaults.h" 36#import "SOGoConstants.h" 37 38@implementation SOGoSystemDefaults 39 40#if defined(LDAP_CONFIG) 41#endif 42 43typedef void (*NSUserDefaultsInitFunction) (); 44 45#define DIR_SEP "/" 46 47#ifndef NSUIntegerMax 48#define NSUIntegerMax UINTPTR_MAX 49#endif 50 51static void 52BootstrapNSUserDefaults () 53{ 54 char *filename; 55 NSUserDefaultsInitFunction SOGoNSUserDefaultsBootstrap; 56 void *handle; 57 58 filename = SOGO_LIBDIR DIR_SEP "libSOGoNSUserDefaults.so.1"; 59 handle = dlopen (filename, RTLD_NOW | RTLD_GLOBAL); 60 if (handle) 61 { 62 SOGoNSUserDefaultsBootstrap = dlsym (handle, 63 "SOGoNSUserDefaultsBootstrap"); 64 if (SOGoNSUserDefaultsBootstrap) 65 SOGoNSUserDefaultsBootstrap (); 66 } 67} 68 69static void 70_injectConfigurationFromFile (NSMutableDictionary *defaultsDict, 71 NSString *filename, NSObject *logger) 72{ 73 NSDictionary *newConfig, *fileAttrs; 74 NSFileManager *fm; 75 76 fm = [NSFileManager defaultManager]; 77 if ([fm fileExistsAtPath: filename]) 78 { 79 fileAttrs = [fm fileAttributesAtPath: filename 80 traverseLink: YES]; 81 if (![fileAttrs objectForKey: @"NSFileSize"]) 82 { 83 [logger errorWithFormat: 84 @"Can't get file attributes from '%@'", 85 filename]; 86 exit(1); 87 } 88 if ([[fileAttrs objectForKey: @"NSFileSize"] intValue] == 0 ) 89 { 90 [logger warnWithFormat: 91 @"Empty file: '%@'. Skipping", 92 filename]; 93 } 94 else 95 { 96 newConfig = [NSDictionary dictionaryWithContentsOfFile: filename]; 97 if (newConfig) 98 [defaultsDict addEntriesFromDictionary: newConfig]; 99 else 100 { 101 [logger errorWithFormat: 102 @"Cannot read configuration from '%@'. Aborting", 103 filename]; 104 exit(1); 105 } 106 } 107 } 108} 109 110+ (void) prepareUserDefaults 111{ 112 /* Load settings from configuration files and 113 * enforce the following order of precedence. 114 * First match wins 115 * 1. Command line arguments 116 * 2. .GNUstepDefaults 117 * 3. /usr/local/etc/sogo/{debconf,sogo}.conf 118 * 4. SOGoDefaults.plist 119 * 120 * The default standardUserDefaults search list is as follows: 121 * GSPrimaryDomain 122 * NSArgumentDomain (command line arguments) 123 * applicationDomain (sogod) 124 * NSGlobalDomain 125 * GSConfigDomain 126 * (languages) 127 * NSRegistrationDomain 128 * 129 * We'll end up with this search list: 130 * NSArgumentDomain (command line arguments) 131 * sogodRuntimeDomain (config from all config files) 132 * GSPrimaryDomain 133 * NSGlobalDomain 134 * GSConfigDomain 135 * (languages) 136 * NSRegistrationDomain (SOPE loads its defaults in this one) 137 */ 138 139 NSDictionary *sogodDomain; 140 NSMutableDictionary *configFromFiles; 141 NSUserDefaults *ud; 142 SOGoStartupLogger *logger; 143 NSBundle *bundle; 144 NSString *confFiles[] = {@"/usr/local/etc/sogo/debconf.conf", 145 @"/usr/local/etc/sogo/sogo.conf"}; 146 NSString *filename, *redirectURL; 147 NSUInteger count; 148 149 logger = [SOGoStartupLogger sharedLogger]; 150 151 /* Load the configuration from the standard user default files */ 152 ud = [NSUserDefaults standardUserDefaults]; 153 154 /* Populate configFromFiles with default values from SOGoDefaults.plist */ 155 configFromFiles = [NSMutableDictionary dictionaryWithCapacity:0]; 156 bundle = [NSBundle bundleForClass: self]; 157 filename = [bundle pathForResource: @"SOGoDefaults" ofType: @"plist"]; 158 if (filename) 159 _injectConfigurationFromFile (configFromFiles, filename, logger); 160 161 /* Fill/Override configFromFiles values with configuration stored 162 * in "/etc" */ 163 for (count = 0; count < sizeof(confFiles)/sizeof(confFiles[0]); count++) 164 _injectConfigurationFromFile (configFromFiles, confFiles[count], logger); 165 166 /* This dance is required to let other appplications (sogo-tool) use 167 * options from the sogod domain while preserving the order of precedence 168 * - remove the 'sogod' domain from the user defaults search list 169 * - Load the content of the sogod domain into configFromFiles 170 * Thereby overriding values from the config files loaded above 171 */ 172 [ud removeSuiteNamed: @"sogod"]; 173 sogodDomain = [ud persistentDomainForName: @"sogod"]; 174 if ([sogodDomain count]) 175 [configFromFiles addEntriesFromDictionary: sogodDomain]; 176 177 /* Add a volatile domain containing the config to the search list. 178 * The domain is added at the very front of the search list 179 */ 180 [ud setVolatileDomain: configFromFiles 181 forName: @"sogodRuntimeDomain"]; 182 [ud addSuiteNamed: @"sogodRuntimeDomain"]; 183 184 /* NSArgumentsDomain goes back in front of the search list */ 185 [ud addSuiteNamed: @"NSArgumentDomain"]; 186 187 /* issue a warning if WOApplicationRedirectURL is used */ 188 redirectURL = [ud stringForKey: @"WOApplicationRedirectURL"]; 189 if (redirectURL) 190 { 191 [logger warnWithFormat: 192 @"Using obsolete 'WOApplicationRedirectURL' user default."]; 193 [logger warnWithFormat: 194 @" Please configure the use of the x-webobjects-XXX headers" 195 @" with your webserver (see sample files)."]; 196 if ([redirectURL hasSuffix: @"/"]) 197 [ud setObject: [redirectURL substringToIndex: [redirectURL length] - 1] 198 forKey: @"WOApplicationRedirectURL"]; 199 } 200} 201 202+ (void) initialize 203{ 204 BootstrapNSUserDefaults (); 205 [self prepareUserDefaults]; 206} 207 208+ (SOGoSystemDefaults *) sharedSystemDefaults 209{ 210 static SOGoSystemDefaults *sharedSystemDefaults = nil; 211 NSUserDefaults *ud; 212 213 if (!sharedSystemDefaults) 214 { 215 ud = [NSUserDefaults standardUserDefaults]; 216 sharedSystemDefaults = [self defaultsSourceWithSource: ud 217 andParentSource: nil]; 218 [sharedSystemDefaults retain]; 219 } 220 221 return sharedSystemDefaults; 222} 223 224- (id) init 225{ 226 if ((self = [super init])) 227 { 228 loginDomains = nil; 229 } 230 231 return self; 232} 233 234- (void) dealloc 235{ 236 [loginDomains release]; 237 [super dealloc]; 238} 239 240- (BOOL) migrate 241{ 242 static NSDictionary *migratedKeys = nil; 243 244 if (!migratedKeys) 245 { 246 migratedKeys = [NSDictionary dictionaryWithObjectsAndKeys: 247 @"SOGoProfileURL", @"AgenorProfileURL", 248 @"SOGoTimeZone", @"SOGoServerTimeZone", 249 nil]; 250 [migratedKeys retain]; 251 } 252 253 return ([self migrateOldDefaultsWithDictionary: migratedKeys] 254 | [super migrate]); 255} 256 257- (NSArray *) domainIds 258{ 259 return [[self dictionaryForKey: @"domains"] allKeys]; 260} 261 262- (BOOL) enableDomainBasedUID 263{ 264 return [self boolForKey: @"SOGoEnableDomainBasedUID"]; 265} 266 267- (NSArray *) loginDomains 268{ 269 NSMutableArray *filteredLoginDomains; 270 NSArray *domains; 271 id currentObject; 272 int count; 273 274 if (self->loginDomains == nil) 275 { 276 filteredLoginDomains = [NSMutableArray arrayWithArray: [self stringArrayForKey: @"SOGoLoginDomains"]]; 277 domains = [self domainIds]; 278 count = [filteredLoginDomains count]; 279 while (count > 0) 280 { 281 count--; 282 currentObject = [filteredLoginDomains objectAtIndex: count]; 283 if (![domains containsObject: currentObject]) 284 { 285 [filteredLoginDomains removeObject: currentObject]; 286 [self warnWithFormat: @"SOGoLoginDomains contains an invalid domain : %@", currentObject]; 287 } 288 } 289 290 ASSIGN (self->loginDomains, filteredLoginDomains); 291 } 292 293 return self->loginDomains; 294} 295 296- (NSArray *) visibleDomainsForDomain: (NSString *) domain 297{ 298 NSMutableArray *domains; 299 NSArray *definedDomains, *visibleDomains, *currentGroup; 300 NSEnumerator *groups; 301 NSString *currentDomain; 302 303 definedDomains = [self domainIds]; 304 visibleDomains = [self arrayForKey: @"SOGoDomainsVisibility"]; 305 domains = [NSMutableArray array]; 306 groups = [visibleDomains objectEnumerator]; 307 while ((currentGroup = (NSArray *)[groups nextObject])) 308 { 309 if ([currentGroup containsObject: domain]) 310 [domains addObjectsFromArray: currentGroup]; 311 } 312 313 // Remove lookup domain and invalid domains 314 groups = [domains objectEnumerator]; 315 while ((currentDomain = [groups nextObject])) 316 { 317 if ([currentDomain isEqualToString: domain] || ![definedDomains containsObject: currentDomain]) 318 [domains removeObject: currentDomain]; 319 } 320 321 return [domains uniqueObjects]; 322} 323 324/* System-level only */ 325 326- (BOOL) crashOnSessionCreate 327{ 328 return [self boolForKey: @"SOGoCrashOnSessionCreate"]; 329} 330 331- (BOOL) debugRequests 332{ 333 return [self boolForKey: @"SOGoDebugRequests"]; 334} 335 336- (BOOL) debugLeaks; 337{ 338 return [self boolForKey: @"SOGoDebugLeaks"]; 339} 340 341- (int) vmemLimit 342{ 343 return [self integerForKey: @"SxVMemLimit"]; 344} 345 346- (BOOL) trustProxyAuthentication; 347{ 348 return [self boolForKey: @"SOGoTrustProxyAuthentication"]; 349} 350 351- (NSString *) encryptionKey; 352{ 353 return [self stringForKey: @"SOGoEncryptionKey"]; 354} 355 356- (BOOL) useRelativeURLs 357{ 358 return [self boolForKey: @"WOUseRelativeURLs"]; 359} 360 361- (NSString *) sieveFolderEncoding 362{ 363 return [self stringForKey: @"SOGoSieveFolderEncoding"]; 364} 365 366 367- (BOOL) isWebAccessEnabled 368{ 369 return [self boolForKey: @"SOGoWebAccessEnabled"]; 370} 371 372- (BOOL) isCalendarDAVAccessEnabled 373{ 374 return [self boolForKey: @"SOGoCalendarDAVAccessEnabled"]; 375} 376 377- (BOOL) isAddressBookDAVAccessEnabled 378{ 379 return [self boolForKey: @"SOGoAddressBookDAVAccessEnabled"]; 380} 381 382- (BOOL) enableEMailAlarms 383{ 384 return [self boolForKey: @"SOGoEnableEMailAlarms"]; 385} 386 387- (NSString *) faviconRelativeURL 388{ 389 return [self stringForKey: @"SOGoFaviconRelativeURL"]; 390} 391 392- (NSString *) zipPath 393{ 394 return [self stringForKey: @"SOGoZipPath"]; 395} 396 397- (int) port 398{ 399 return [self integerForKey: @"WOPort"]; 400} 401 402- (int) workers 403{ 404 return [self integerForKey: @"WOWorkersCount"]; 405} 406 407- (NSString *) logFile 408{ 409 return [self stringForKey: @"WOLogFile"]; 410} 411 412- (NSString *) pidFile 413{ 414 return [self stringForKey: @"WOPidFile"]; 415} 416 417- (NSTimeInterval) cacheCleanupInterval 418{ 419 return [self floatForKey: @"SOGoCacheCleanupInterval"]; 420} 421 422- (NSString *) memcachedHost 423{ 424 return [self stringForKey: @"SOGoMemcachedHost"]; 425} 426 427- (BOOL) uixDebugEnabled 428{ 429 return [self boolForKey: @"SOGoUIxDebugEnabled"]; 430} 431 432- (BOOL) easDebugEnabled 433{ 434 return [self boolForKey: @"SOGoEASDebugEnabled"]; 435} 436 437- (BOOL) tnefDecoderDebugEnabled 438{ 439 return [self boolForKey: @"SOGoTnefDecoderDebugEnabled"]; 440} 441 442- (BOOL) xsrfValidationEnabled 443{ 444 id o; 445 446 if (!(o = [self objectForKey: @"SOGoXSRFValidationEnabled"])) 447 { 448 return YES; 449 } 450 451 return [o boolValue]; 452} 453 454- (NSString *) pageTitle 455{ 456 return [self stringForKey: @"SOGoPageTitle"]; 457} 458 459- (NSString *) helpURL 460{ 461 return [self stringForKey: @"SOGoHelpURL"]; 462} 463 464- (NSArray *) supportedLanguages 465{ 466 static NSArray *supportedLanguages = nil; 467 468 if (!supportedLanguages) 469 { 470 supportedLanguages = [self stringArrayForKey: @"SOGoSupportedLanguages"]; 471 [supportedLanguages retain]; 472 } 473 474 return supportedLanguages; 475} 476 477- (BOOL) userCanChangePassword 478{ 479 return [self boolForKey: SOGoPasswordChangeEnabled]; 480} 481 482- (BOOL) uixAdditionalPreferences 483{ 484 return [self boolForKey: @"SOGoUIxAdditionalPreferences"]; 485} 486 487- (NSString *) loginSuffix 488{ 489 return [self stringForKey: @"SOGoLoginSuffix"]; 490} 491 492- (NSString *) authenticationType 493{ 494 return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString]; 495} 496 497- (NSString *) davAuthenticationType 498{ 499 return [[self stringForKey: @"SOGoDAVAuthenticationType"] lowercaseString]; 500} 501 502- (NSString *) CASServiceURL 503{ 504 return [self stringForKey: @"SOGoCASServiceURL"]; 505} 506 507- (BOOL) CASLogoutEnabled 508{ 509 return [self boolForKey: @"SOGoCASLogoutEnabled"]; 510} 511 512/* SAML2 support */ 513- (NSString *) SAML2PrivateKeyLocation 514{ 515 return [self stringForKey: @"SOGoSAML2PrivateKeyLocation"]; 516} 517 518- (NSString *) SAML2CertificateLocation; 519{ 520 return [self stringForKey: @"SOGoSAML2CertificateLocation"]; 521} 522 523- (NSString *) SAML2IdpMetadataLocation 524{ 525 return [self stringForKey: @"SOGoSAML2IdpMetadataLocation"]; 526} 527 528- (NSString *) SAML2IdpPublicKeyLocation 529{ 530 return [self stringForKey: @"SOGoSAML2IdpPublicKeyLocation"]; 531} 532 533- (NSString *) SAML2IdpCertificateLocation 534{ 535 return [self stringForKey: @"SOGoSAML2IdpCertificateLocation"]; 536} 537 538- (BOOL) SAML2LogoutEnabled 539{ 540 return [self boolForKey: @"SOGoSAML2LogoutEnabled"]; 541} 542 543- (NSString *) SAML2LogoutURL 544{ 545 return [self stringForKey: @"SOGoSAML2LogoutURL"]; 546} 547 548- (NSString *) SAML2LoginAttribute 549{ 550 return [self stringForKey: @"SOGoSAML2LoginAttribute"]; 551} 552 553- (BOOL) enablePublicAccess 554{ 555 return [self boolForKey: @"SOGoEnablePublicAccess"]; 556} 557 558// 559// 560// 561- (int) maximumFailedLoginCount 562{ 563 return [self integerForKey: @"SOGoMaximumFailedLoginCount"]; 564} 565 566- (int) maximumFailedLoginInterval 567{ 568 int v; 569 570 v = [self integerForKey: @"SOGoMaximumFailedLoginInterval"]; 571 572 if (!v) 573 v = 10; 574 575 return v; 576} 577 578- (int) failedLoginBlockInterval 579{ 580 int v; 581 582 v = [self integerForKey: @"SOGoFailedLoginBlockInterval"]; 583 584 if (!v) 585 v = 300; 586 587 return v; 588} 589 590// 591// 592// 593- (int) maximumMessageSizeLimit 594{ 595 return [self integerForKey: @"SOGoMaximumMessageSizeLimit"]; 596} 597 598// 599// 600// 601- (NSUInteger) maximumMessageSubmissionCount 602{ 603 NSUInteger v; 604 605 v = [self integerForKey: @"SOGoMaximumMessageSubmissionCount"]; 606 607 if (!v) 608 return NSUIntegerMax; 609 610 return v; 611} 612 613- (NSUInteger) maximumRecipientCount 614{ 615 NSUInteger v; 616 617 v = [self integerForKey: @"SOGoMaximumRecipientCount"]; 618 619 if (!v) 620 return NSUIntegerMax; 621 622 return v; 623} 624 625- (int) maximumSubmissionInterval 626{ 627 int v; 628 629 v = [self integerForKey: @"SOGoMaximumSubmissionInterval"]; 630 631 if (!v) 632 v = 30; 633 634 return v; 635} 636 637- (int) messageSubmissionBlockInterval 638{ 639 int v; 640 641 v = [self integerForKey: @"SOGoMessageSubmissionBlockInterval"]; 642 643 if (!v) 644 v = 300; 645 646 return v; 647} 648 649// 650// SOGo rate-limiting 651// 652- (int) maximumRequestCount 653{ 654 return [self integerForKey: @"SOGoMaximumRequestCount"]; 655} 656 657- (int) maximumRequestInterval 658{ 659 int v; 660 661 v = [self integerForKey: @"SOGoMaximumRequestInterval"]; 662 663 if (!v) 664 v = 30; 665 666 return v; 667} 668 669- (int) requestBlockInterval 670{ 671 int v; 672 673 v = [self integerForKey: @"SOGoRequestBlockInterval"]; 674 675 if (!v) 676 v = 300; 677 678 return v; 679} 680 681 682// 683// SOGo EAS settings 684// 685- (int) maximumPingInterval 686{ 687 int v; 688 689 v = [self integerForKey: @"SOGoMaximumPingInterval"]; 690 691 if (!v) 692 v = 10; 693 694 return v; 695} 696 697- (int) maximumSyncInterval 698{ 699 int v; 700 701 v = [self integerForKey: @"SOGoMaximumSyncInterval"]; 702 703 if (!v) 704 v = 30; 705 706 return v; 707} 708 709- (int) internalSyncInterval 710{ 711 int v; 712 713 v = [self integerForKey: @"SOGoInternalSyncInterval"]; 714 715 if (!v) 716 v = 10; 717 718 return v; 719} 720 721- (int) maximumSyncWindowSize 722{ 723 return [self integerForKey: @"SOGoMaximumSyncWindowSize"]; 724} 725 726- (int) maximumSyncResponseSize 727{ 728 int v; 729 730 v = [self integerForKey: @"SOGoMaximumSyncResponseSize"]; 731 732 if (v > 0) 733 v = v * 1024; 734 735 return v; 736} 737 738- (BOOL) easSearchInBody 739{ 740 return [self boolForKey: @"SOGoEASSearchInBody"]; 741} 742 743// 744// See https://msdn.microsoft.com/en-us/library/gg672032(v=exchg.80).aspx 745// 746- (int) maximumPictureSize 747{ 748 int v; 749 750 v = [self integerForKey: @"SOGoMaximumPictureSize"]; 751 752 if (!v) 753 v = 102400; 754 755 return v; 756} 757 758@end 759