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