1/* 2 Copyright (C) 2006-2015 Inverse inc. 3 Copyright (C) 2005 SKYRIX Software AG 4 5 This file is part of SOGo. 6 7 SOGo is free software; you can redistribute it and/or modify it under 8 the terms of the GNU Lesser General Public License as published by the 9 Free Software Foundation; either version 2, or (at your option) any 10 later version. 11 12 SOGo is distributed in the hope that it will be useful, but WITHOUT ANY 13 WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with OGo; see the file COPYING. If not, write to the 19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 20 02111-1307, USA. 21*/ 22 23#import <Foundation/NSArray.h> 24#import <Foundation/NSCalendarDate.h> 25#import <Foundation/NSDictionary.h> 26#import <Foundation/NSEnumerator.h> 27#import <Foundation/NSNull.h> 28#import <Foundation/NSTimeZone.h> 29#import <Foundation/NSValue.h> 30#import <Foundation/NSURL.h> 31#import <NGObjWeb/WOApplication.h> 32#import <NGObjWeb/WOContext.h> 33#import <NGObjWeb/WORequest.h> 34#import <NGObjWeb/SoObject.h> 35#import <NGExtensions/NSCalendarDate+misc.h> 36#import <NGExtensions/NSNull+misc.h> 37#import <NGExtensions/NSObject+Logs.h> 38 39#import <Appointments/SOGoAppointmentFolders.h> 40#import <Contacts/SOGoContactFolders.h> 41 42#import "NSArray+Utilities.h" 43#import "SOGoCache.h" 44#import "SOGoDateFormatter.h" 45#import "SOGoDomainDefaults.h" 46#import "SOGoObject.h" 47#import "SOGoPermissions.h" 48#import "SOGoSystemDefaults.h" 49#import "SOGoUserDefaults.h" 50#import "SOGoUserFolder.h" 51#import "SOGoUserManager.h" 52#import "SOGoUserProfile.h" 53#import "SOGoUserSettings.h" 54#import "WOResourceManager+SOGo.h" 55 56#import "SOGoUser.h" 57 58@implementation SoUser (SOGoExtension) 59 60- (SOGoDomainDefaults *) userDefaults 61{ 62 return [SOGoSystemDefaults sharedSystemDefaults]; 63} 64 65- (SOGoDomainDefaults *) domainDefaults 66{ 67 return [SOGoSystemDefaults sharedSystemDefaults]; 68} 69 70@end 71 72@implementation SOGoUser 73 74// + (NSString *) language 75// { 76// NSArray *bLanguages; 77// WOContext *context; 78// NSString *lng; 79 80// context = [[WOApplication application] context]; 81// bLanguages = [[context request] browserLanguages]; 82// if ([bLanguages count] > 0) 83// lng = [bLanguages objectAtIndex: 0]; 84 85// if (![lng length]) 86// lng = defaultLanguage; 87 88// return lng; 89// } 90 91+ (SOGoUser *) userWithLogin: (NSString *) newLogin 92{ 93 return [self userWithLogin: newLogin roles: nil]; 94} 95 96+ (SOGoUser *) userWithLogin: (NSString *) newLogin 97 roles: (NSArray *) newRoles 98{ 99 return [self userWithLogin: newLogin roles: newRoles trust: NO]; 100} 101 102+ (SOGoUser *) userWithLogin: (NSString *) newLogin 103 roles: (NSArray *) newRoles 104 trust: (BOOL) b 105{ 106 SOGoCache *cache; 107 SOGoUser *user; 108 109 cache = [SOGoCache sharedCache]; 110 user = [cache userNamed: newLogin]; 111 if (!user) 112 { 113 user = [[self alloc] initWithLogin: newLogin roles: newRoles trust: b]; 114 if (user) 115 { 116 [cache registerUser: user withName: newLogin]; 117 [user release]; 118 } 119 } 120 if (newRoles) 121 [user setPrimaryRoles: newRoles]; 122 123 return user; 124} 125 126/** 127 * Return a new instance for the login name, which can be appended by a 128 * domain name. The domain is extracted only if the system defaults 129 * SOGoEnableDomainBasedUID is enabled. 130 * 131 * @param newLogin a login name optionally follow by @domain 132 * @param newRoles 133 * @param b is set to YES if newLogin can be trust 134 * @see loginInDomain 135 * @see [SOGoSession decodeValue:usingKey:login:domain:password:] 136 */ 137- (id) initWithLogin: (NSString *) newLogin 138 roles: (NSArray *) newRoles 139 trust: (BOOL) b 140{ 141 SOGoUserManager *um; 142 SOGoSystemDefaults *sd; 143 NSDictionary *contactInfos; 144 NSString *realUID, *uid, *domain; 145 NSRange r; 146 147 _defaults = nil; 148 _settings = nil; 149 150 uid = nil; 151 realUID = nil; 152 domain = nil; 153 154 if ([newLogin isEqualToString: @"anonymous"] 155 || [newLogin isEqualToString: @"freebusy"]) 156 realUID = newLogin; 157 else 158 { 159 sd = [SOGoSystemDefaults sharedSystemDefaults]; 160 if ([sd enableDomainBasedUID] || [[sd loginDomains] count] > 0) 161 { 162 r = [newLogin rangeOfString: @"@" options: NSBackwardsSearch]; 163 if (r.location != NSNotFound) 164 { 165 // The domain is probably appended to the username; 166 // make sure it is defined as a domain in the configuration. 167 domain = [newLogin substringFromIndex: (r.location + r.length)]; 168 if ([[SOGoUserManager sharedUserManager] isDomainDefined: domain] && 169 ![sd enableDomainBasedUID]) 170 newLogin = [newLogin substringToIndex: r.location]; 171 172 if (domain != nil && ![sd enableDomainBasedUID]) 173 // Login domains are enabled (SOGoLoginDomains) but not 174 // domain-based UID (SOGoEnableDomainBasedUID). 175 // Drop the domain from the login name. 176 domain = nil; 177 } 178 } 179 180 newLogin = [newLogin stringByReplacingString: @"%40" 181 withString: @"@"]; 182 if (b) 183 realUID = newLogin; 184 else 185 { 186 um = [SOGoUserManager sharedUserManager]; 187 contactInfos = [um contactInfosForUserWithUIDorEmail: newLogin 188 inDomain: domain]; 189 realUID = [contactInfos objectForKey: @"c_uid"]; 190 if (domain == nil && [sd enableDomainBasedUID]) 191 domain = [contactInfos objectForKey: @"c_domain"]; 192 } 193 194 if ([realUID length] && [domain length]) 195 { 196 // When the user is associated to a domain, the [SOGoUser login] 197 // method returns the combination login@domain while 198 // [SOGoUser loginInDomain] only returns the login. 199 r = [realUID rangeOfString: domain options: NSBackwardsSearch|NSCaseInsensitiveSearch]; 200 201 // Do NOT strip @domain.com if SOGoEnableDomainBasedUID is enabled since 202 // the real login most likely is the email address. 203 if (r.location != NSNotFound && ![sd enableDomainBasedUID]) 204 uid = [realUID substringToIndex: r.location-1]; 205 // If we don't have the domain in the UID but SOGoEnableDomainBasedUID is 206 // enabled, let's add it internally so so it becomes unique across 207 // all potential domains. 208 else if (r.location == NSNotFound && [sd enableDomainBasedUID]) 209 { 210 uid = [NSString stringWithString: realUID]; 211 realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain]; 212 } 213 // We found the domain and SOGoEnableDomainBasedUID is enabled, 214 // we keep realUID.. This would happen for example if the user 215 // authenticates with foo@bar.com and the UIDFieldName is also foo@bar.com 216 else if ([sd enableDomainBasedUID]) 217 uid = [NSString stringWithString: realUID]; 218 } 219 } 220 221 if ([realUID length]) 222 { 223 if ((self = [super initWithLogin: realUID roles: newRoles])) 224 { 225 allEmails = nil; 226 currentPassword = nil; 227 cn = nil; 228 ASSIGN (loginInDomain, (uid ? uid : realUID)); 229 _defaults = nil; 230 _domainDefaults = nil; 231 _settings = nil; 232 mailAccounts = nil; 233 } 234 } 235 else 236 { 237 [self release]; 238 self = nil; 239 } 240 241 return self; 242} 243 244- (void) dealloc 245{ 246 [_defaults release]; 247 [_domainDefaults release]; 248 [_settings release]; 249 [allEmails release]; 250 [mailAccounts release]; 251 [currentPassword release]; 252 [cn release]; 253 [loginInDomain release]; 254 [super dealloc]; 255} 256 257- (void) setPrimaryRoles: (NSArray *) newRoles 258{ 259 ASSIGN (roles, newRoles); 260} 261 262- (void) setCurrentPassword: (NSString *) newPassword 263{ 264 ASSIGN (currentPassword, newPassword); 265} 266 267- (NSString *) currentPassword 268{ 269 return currentPassword; 270} 271 272- (NSString *) loginInDomain 273{ 274 return loginInDomain; 275} 276 277- (id) _fetchFieldForUser: (NSString *) field 278{ 279 NSDictionary *contactInfos; 280 SOGoUserManager *um; 281 282 um = [SOGoUserManager sharedUserManager]; 283 contactInfos = [um contactInfosForUserWithUIDorEmail: login]; 284 285 return [contactInfos objectForKey: field]; 286} 287 288- (void) _fetchAllEmails 289{ 290 allEmails = [self _fetchFieldForUser: @"emails"]; 291 [allEmails retain]; 292} 293 294- (void) _fetchCN 295{ 296 cn = [[self _fetchFieldForUser: @"cn"] stringByTrimmingSpaces]; 297 [cn retain]; 298} 299 300/* properties */ 301- (NSString *) domain 302{ 303 return [self _fetchFieldForUser: @"c_domain"]; 304} 305 306- (id <SOGoSource>) authenticationSource 307{ 308 NSString *sourceID; 309 SOGoUserManager *um; 310 311 sourceID = [self _fetchFieldForUser: @"SOGoSource"]; 312 um = [SOGoUserManager sharedUserManager]; 313 314 return [um sourceWithID: sourceID]; 315} 316 317- (NSArray *) allEmails 318{ 319 if (!allEmails) 320 [self _fetchAllEmails]; 321 322 return allEmails; 323} 324 325// 326// We always return the last object among our list of email addresses. This value 327// is always added in SOGoUserManager: -_fillContactMailRecords: 328// 329- (NSString *) systemEmail 330{ 331 if (!allEmails) 332 [self _fetchAllEmails]; 333 334 return [allEmails lastObject]; 335} 336 337- (BOOL) hasEmail: (NSString *) email 338{ 339 if (!allEmails) 340 [self _fetchAllEmails]; 341 342 return [allEmails containsCaseInsensitiveString: email]; 343} 344 345- (NSString *) cn 346{ 347 if (!cn) 348 [self _fetchCN]; 349 350 return cn; 351} 352 353- (NSMutableDictionary *) defaultIdentity 354{ 355 NSMutableDictionary *currentIdentity, *defaultIdentity; 356 NSEnumerator *identities; 357 358 defaultIdentity = nil; 359 360 identities = [[self allIdentities] objectEnumerator]; 361 while (!defaultIdentity 362 && (currentIdentity = [identities nextObject])) 363 if ([[currentIdentity objectForKey: @"isDefault"] boolValue]) 364 defaultIdentity = currentIdentity; 365 366 return defaultIdentity; 367} 368 369- (SOGoDateFormatter *) dateFormatterInContext: (WOContext *) context 370{ 371 SOGoDateFormatter *dateFormatter; 372 NSString *format; 373 SOGoUserDefaults *ud; 374 NSDictionary *locale; 375 WOResourceManager *resMgr; 376 377 dateFormatter = [SOGoDateFormatter new]; 378 [dateFormatter autorelease]; 379 380 ud = [self userDefaults]; 381 resMgr = [[WOApplication application] resourceManager]; 382 locale = [resMgr localeForLanguageNamed: [ud language]]; 383 [dateFormatter setLocale: locale]; 384 format = [ud shortDateFormat]; 385 if (format) 386 [dateFormatter setShortDateFormat: format]; 387 format = [ud longDateFormat]; 388 if (format) 389 [dateFormatter setLongDateFormat: format]; 390 format = [ud timeFormat]; 391 if (format) 392 [dateFormatter setTimeFormat: format]; 393 394 return dateFormatter; 395} 396 397- (SOGoUserDefaults *) userDefaults 398{ 399 if (!_defaults) 400 { 401 _defaults = [SOGoUserDefaults defaultsForUser: login 402 inDomain: [self domain]]; 403 [_defaults retain]; 404 } 405 //else 406 // NSLog(@"User defaults cache hit for %@", login); 407 408 return _defaults; 409} 410 411- (SOGoDomainDefaults *) domainDefaults 412{ 413 NSString *domain; 414 415 if (!_domainDefaults) 416 { 417 domain = [self domain]; 418 if ([domain length]) 419 { 420 _domainDefaults = [SOGoDomainDefaults defaultsForDomain: domain]; 421 if (!_domainDefaults) 422 { 423 //[self errorWithFormat: @"domain '%@' does not exist!", domain]; 424 _domainDefaults = [SOGoSystemDefaults sharedSystemDefaults]; 425 } 426 } 427 else 428 _domainDefaults = [SOGoSystemDefaults sharedSystemDefaults]; 429 [_domainDefaults retain]; 430 } 431 //else 432 // NSLog(@"User defaults cache hit for %@", login); 433 434 return _domainDefaults; 435} 436 437- (SOGoUserSettings *) userSettings 438{ 439 if (!_settings) 440 { 441 _settings = [SOGoUserSettings settingsForUser: login]; 442 [_settings retain]; 443 } 444 445 return _settings; 446} 447 448- (NSCalendarDate *) firstDayOfWeekForDate: (NSCalendarDate *) date 449{ 450 int offset; 451 NSCalendarDate *firstDay; 452 453 offset = [[self userDefaults] firstDayOfWeek] - [date dayOfWeek]; 454 if (offset > 0) 455 offset -= 7; 456 457 firstDay = [date addTimeInterval: offset * 86400]; 458 459 return firstDay; 460} 461 462- (unsigned int) dayOfWeekForDate: (NSCalendarDate *) date 463{ 464 unsigned int offset, baseDayOfWeek, dayOfWeek; 465 466 offset = [[self userDefaults] firstDayOfWeek]; 467 baseDayOfWeek = [date dayOfWeek]; 468 if (offset > baseDayOfWeek) 469 baseDayOfWeek += 7; 470 471 dayOfWeek = baseDayOfWeek - offset; 472 473 return dayOfWeek; 474} 475 476- (NSCalendarDate *) firstWeekOfYearForDate: (NSCalendarDate *) date 477{ 478 NSString *firstWeekRule; 479 NSCalendarDate *januaryFirst, *firstWeek; 480 unsigned int dayOfWeek; 481 482 firstWeekRule = [[self userDefaults] firstWeekOfYear]; 483 484 januaryFirst = [NSCalendarDate dateWithYear: [date yearOfCommonEra] 485 month: 1 day: 1 hour: 0 minute: 0 second: 0 486 timeZone: [date timeZone]]; 487 if ([firstWeekRule isEqualToString: SOGoWeekStartFirst4DayWeek]) 488 { 489 dayOfWeek = [self dayOfWeekForDate: januaryFirst]; 490 if (dayOfWeek < 4) 491 firstWeek = [self firstDayOfWeekForDate: januaryFirst]; 492 else 493 firstWeek = [self firstDayOfWeekForDate: [januaryFirst 494 dateByAddingYears: 0 495 months: 0 496 days: 7]]; 497 } 498 else if ([firstWeekRule isEqualToString: SOGoWeekStartFirstFullWeek]) 499 { 500 dayOfWeek = [self dayOfWeekForDate: januaryFirst]; 501 if (dayOfWeek == 0) 502 firstWeek = [self firstDayOfWeekForDate: januaryFirst]; 503 else 504 firstWeek = [self firstDayOfWeekForDate: [januaryFirst 505 dateByAddingYears: 0 506 months: 0 507 days: 7]]; 508 } 509 else 510 firstWeek = [self firstDayOfWeekForDate: januaryFirst]; 511 512 return firstWeek; 513} 514 515- (unsigned int) weekNumberForDate: (NSCalendarDate *) date 516{ 517 NSCalendarDate *firstWeek, *previousWeek; 518 unsigned int weekNumber; 519 520 firstWeek = [self firstWeekOfYearForDate: date]; 521 if ([firstWeek earlierDate: date] == firstWeek) 522 { 523 weekNumber = ([date timeIntervalSinceDate: firstWeek] / (86400 * 7) + 1); 524 } 525 else 526 { 527 // Date is within the last week of the previous year; 528 // Compute the previous week number to find the week number of the requested date. 529 // The number will either be 52 or 53. 530 previousWeek = [date dateByAddingYears: 0 531 months: 0 532 days: -7]; 533 firstWeek = [self firstWeekOfYearForDate: previousWeek]; 534 weekNumber = ([previousWeek timeIntervalSinceDate: firstWeek] / (86400 * 7) + 1); 535 weekNumber += 1; 536 } 537 538 return weekNumber; 539} 540 541/* mail */ 542- (BOOL) _migrateFolderWithPurpose: (NSString *) purpose 543 withName: (NSString *) folderName 544{ 545 NSString *methodName; 546 SEL methodSel; 547 BOOL rc; 548 549 [self userDefaults]; 550 methodName = [NSString stringWithFormat: @"set%@FolderName:", purpose]; 551 methodSel = NSSelectorFromString (methodName); 552 if ([_defaults respondsToSelector: methodSel]) 553 { 554 [_defaults performSelector: methodSel withObject: folderName]; 555 rc = YES; 556 } 557 else 558 { 559 [self errorWithFormat: @"method '%@' not available with user defaults" 560 @" object, folder migration fails", methodName]; 561 rc = NO; 562 } 563 564 return rc; 565} 566 567- (void) _migrateFolderSettings 568{ 569 NSMutableDictionary *mailSettings; 570 NSString *folderName, *key; 571 BOOL migrated; 572 NSString **purpose; 573 NSString *purposes[] = { @"Drafts", @"Sent", @"Trash", nil }; 574 575 [self userSettings]; 576 mailSettings = [_settings objectForKey: @"Mail"]; 577 if (mailSettings) 578 { 579 migrated = NO; 580 purpose = purposes; 581 while (*purpose) 582 { 583 key = [NSString stringWithFormat: @"%@Folder", *purpose]; 584 folderName = [mailSettings objectForKey: key]; 585 if ([folderName length] 586 && [self _migrateFolderWithPurpose: *purpose 587 withName: folderName]) 588 { 589 migrated = YES; 590 [mailSettings removeObjectForKey: key]; 591 folderName = nil; 592 } 593 purpose++; 594 } 595 if (migrated) 596 { 597 [_settings synchronize]; 598 [self userDefaults]; 599 [_defaults synchronize]; 600 } 601 } 602} 603 604- (void) _appendSystemMailAccount 605{ 606 NSString *fullName, *replyTo, *imapLogin, *imapServer, *cImapServer, *signature, 607 *encryption, *scheme, *action, *query, *customEmail, *defaultEmail, *sieveServer; 608 NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts; 609 NSNumber *port; 610 NSMutableArray *identities, *mails; 611 NSURL *url, *cUrl; 612 unsigned int count, max, default_identity; 613 NSInteger defaultPort; 614 NSUInteger index; 615 616 [self userDefaults]; 617 618 mailAccount = [NSMutableDictionary new]; 619 620 // 1. login 621 imapLogin = [[SOGoUserManager sharedUserManager] 622 getExternalLoginForUID: [self loginInDomain] 623 inDomain: [self domain]]; 624 [mailAccount setObject: imapLogin forKey: @"userName"]; 625 626 // 2. server 627 // imapServer might have the following format 628 // localhost 629 // localhost:143 630 // imap://localhost 631 // imap://localhost:143 632 // imaps://localhost:993 633 // imaps://localhost:143/?tls=YES 634 // imaps://localhost/?tls=YES 635 636 cImapServer = [self _fetchFieldForUser: @"c_imaphostname"]; 637 imapServer = [[self domainDefaults] imapServer]; 638 cUrl = [NSURL URLWithString: (cImapServer ? cImapServer : @"")]; 639 url = [NSURL URLWithString: imapServer]; 640 if([cUrl host]) 641 imapServer = [cUrl host]; 642 else 643 if(cImapServer) 644 imapServer = cImapServer; 645 else 646 if([url host]) 647 imapServer = [url host]; 648 [mailAccount setObject: imapServer forKey: @"serverName"]; 649 650 // 3. port & encryption 651 scheme = [cUrl scheme] ? [cUrl scheme] : [url scheme]; 652 query = [cUrl query] ? [cUrl query] : [url query]; 653 654 if (scheme 655 && [scheme caseInsensitiveCompare: @"imaps"] == NSOrderedSame) 656 { 657 if (query && [query caseInsensitiveCompare: @"tls=YES"] == NSOrderedSame) 658 { 659 defaultPort = 143; 660 encryption = @"tls"; 661 } 662 else 663 { 664 encryption = @"ssl"; 665 defaultPort = 993; 666 } 667 } 668 else 669 { 670 if (query && [query caseInsensitiveCompare: @"tls=YES"] == NSOrderedSame) 671 encryption = @"tls"; 672 else 673 encryption = @"none"; 674 675 defaultPort = 143; 676 } 677 port = [cUrl port] ? [cUrl port] : [url port]; 678 if ([port intValue] == 0) /* port is nil or intValue == 0 */ 679 port = [NSNumber numberWithInt: defaultPort]; 680 [mailAccount setObject: port forKey: @"port"]; 681 [mailAccount setObject: encryption forKey: @"encryption"]; 682 683 // Sieve server 684 sieveServer = [self _fetchFieldForUser: @"c_sievehostname"]; 685 686 if (sieveServer) 687 { 688 [mailAccount setObject: sieveServer forKey: @"sieveServerName"]; 689 } 690 691 // Identities 692 defaultEmail = [NSString stringWithFormat: @"%@@%@", [self loginInDomain], [self domain]]; 693 default_identity = 0; 694 identities = [NSMutableArray new]; 695 mails = [NSMutableArray arrayWithArray: [self allEmails]]; 696 [mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"]; 697 698 replyTo = [_defaults mailReplyTo]; 699 700 max = [mails count]; 701 702 /* custom from */ 703 if ([[self domainDefaults] mailCustomFromEnabled]) 704 { 705 [self userDefaults]; 706 customEmail = [_defaults mailCustomEmail]; 707 fullName = [_defaults mailCustomFullName]; 708 if ([customEmail length] > 0 || [fullName length] > 0) 709 { 710 if ([customEmail length] == 0) 711 customEmail = [mails objectAtIndex: 0]; 712 else if ([fullName length] == 0) 713 { 714 // Custom email but default fullname; if the custom email is 715 // one of the user's emails, remove the duplicated entry 716 index = [mails indexOfObject: customEmail]; 717 if (index != NSNotFound) 718 { 719 [mails removeObjectAtIndex: index]; 720 max--; 721 } 722 } 723 724 if ([fullName length] == 0) 725 { 726 fullName = [self cn]; 727 if ([fullName length] == 0) 728 fullName = login; 729 } 730 731 identity = [NSMutableDictionary new]; 732 [identity setObject: customEmail forKey: @"email"]; 733 [identity setObject: fullName forKey: @"fullName"]; 734 735 if ([replyTo length] > 0) 736 [identity setObject: replyTo forKey: @"replyTo"]; 737 738 signature = [_defaults mailSignature]; 739 if (signature) 740 [identity setObject: signature forKey: @"signature"]; 741 [identities addObject: identity]; 742 743 if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame) 744 default_identity = [identities count]-1; 745 746 [identity release]; 747 } 748 } 749 750 for (count = 0; count < max; count++) 751 { 752 identity = [NSMutableDictionary new]; 753 fullName = [self cn]; 754 if (![fullName length]) 755 fullName = login; 756 [identity setObject: fullName forKey: @"fullName"]; 757 [identity setObject: [[mails objectAtIndex: count] stringByTrimmingSpaces] 758 forKey: @"email"]; 759 760 if ([replyTo length] > 0) 761 [identity setObject: replyTo forKey: @"replyTo"]; 762 763 signature = [_defaults mailSignature]; 764 if (signature) 765 [identity setObject: signature forKey: @"signature"]; 766 [identities addObject: identity]; 767 768 if ([[identity objectForKey: @"email"] caseInsensitiveCompare: defaultEmail] == NSOrderedSame) 769 default_identity = [identities count]-1; 770 771 [identity release]; 772 } 773 [[identities objectAtIndex: default_identity] setObject: [NSNumber numberWithBool: YES] 774 forKey: @"isDefault"]; 775 776 [mailAccount setObject: identities forKey: @"identities"]; 777 [identities release]; 778 779 /* receipts */ 780 if ([_defaults allowUserReceipt]) 781 { 782 receipts = [NSMutableDictionary new]; 783 784 [receipts setObject: @"allow" forKey: @"receiptAction"]; 785 action = [_defaults userReceiptNonRecipientAction]; 786 if (action) 787 [receipts setObject: action forKey: @"receiptNonRecipientAction"]; 788 action = [_defaults userReceiptOutsideDomainAction]; 789 if (action) 790 [receipts setObject: action forKey: @"receiptOutsideDomainAction"]; 791 action = [_defaults userReceiptAnyAction]; 792 if (action) 793 [receipts setObject: action forKey: @"receiptAnyAction"]; 794 795 [mailAccount setObject: receipts forKey: @"receipts"]; 796 [receipts release]; 797 } 798 799 /* mailboxes */ 800 mailboxes = [NSMutableDictionary new]; 801 802 [self _migrateFolderSettings]; 803 [mailboxes setObject: [_defaults draftsFolderName] 804 forKey: @"Drafts"]; 805 [mailboxes setObject: [_defaults sentFolderName] 806 forKey: @"Sent"]; 807 [mailboxes setObject: [_defaults trashFolderName] 808 forKey: @"Trash"]; 809 [mailboxes setObject: [_defaults junkFolderName] 810 forKey: @"Junk"]; 811 [mailAccount setObject: mailboxes forKey: @"mailboxes"]; 812 [mailboxes release]; 813 814 [mailAccounts addObject: mailAccount]; 815 [mailAccount release]; 816} 817 818- (NSArray *) mailAccounts 819{ 820 NSArray *auxAccounts; 821 822 if (!mailAccounts) 823 { 824 mailAccounts = [NSMutableArray new]; 825 [self _appendSystemMailAccount]; 826 if ([[self domainDefaults] mailAuxiliaryUserAccountsEnabled]) 827 { 828 auxAccounts = [[self userDefaults] auxiliaryMailAccounts]; 829 if (auxAccounts) 830 [mailAccounts addObjectsFromArray: auxAccounts]; 831 } 832 } 833 834 return mailAccounts; 835} 836 837- (NSDictionary *) accountWithName: (NSString *) accountName; 838{ 839 NSEnumerator *accounts; 840 NSDictionary *mailAccount, *currentAccount; 841 842 mailAccount = nil; 843 844 accounts = [[self mailAccounts] objectEnumerator]; 845 while (!mailAccount 846 && ((currentAccount = [accounts nextObject]))) 847 if ([[currentAccount objectForKey: @"name"] 848 isEqualToString: accountName]) 849 mailAccount = currentAccount; 850 851 return mailAccount; 852} 853 854- (NSArray *) allIdentities 855{ 856 NSArray *identities; 857 858 identities = [[self mailAccounts] objectsForKey: @"identities" 859 notFoundMarker: nil]; 860 861 return [identities flattenedArray]; 862} 863 864- (NSDictionary *) primaryIdentity 865{ 866 NSDictionary *defaultAccount; 867 868 defaultAccount = [[self mailAccounts] objectAtIndex: 0]; 869 870 return [[defaultAccount objectForKey: @"identities"] objectAtIndex: 0]; 871} 872 873/* folders */ 874 875// TODO: those methods should check whether the traversal stack in the context 876// already contains proper folders to improve caching behaviour 877 878- (SOGoUserFolder *) homeFolderInContext: (id) context 879{ 880 return [SOGoUserFolder objectWithName: login 881 inContainer: [WOApplication application]]; 882} 883 884- (SOGoAppointmentFolders *) calendarsFolderInContext: (WOContext *) context 885{ 886 return [[self homeFolderInContext: context] lookupName: @"Calendar" 887 inContext: context 888 acquire: NO]; 889} 890 891- (SOGoAppointmentFolder *) personalCalendarFolderInContext: (WOContext *) context 892{ 893 return [[self calendarsFolderInContext: context] lookupPersonalFolder: @"personal" 894 ignoringRights: YES]; 895} 896 897- (SOGoContactFolder *) personalContactsFolderInContext: (WOContext *) context 898{ 899 SOGoContactFolders *folders; 900 901 folders = [[self homeFolderInContext: context] lookupName: @"Contacts" 902 inContext: context 903 acquire: NO]; 904 905 return [folders lookupPersonalFolder: @"personal" 906 ignoringRights: YES]; 907} 908 909 910- (NSArray *) rolesForObject: (NSObject *) object 911 inContext: (WOContext *) context 912{ 913 NSMutableArray *rolesForObject; 914 NSArray *sogoRoles; 915 NSString *rqMethod; 916 917 rolesForObject = [NSMutableArray array]; 918 919 sogoRoles = [super rolesForObject: object inContext: context]; 920 if (sogoRoles) 921 [rolesForObject addObjectsFromArray: sogoRoles]; 922 923 if ([self isSuperUser] 924 || [[object ownerInContext: context] isEqualToString: login]) 925 [rolesForObject addObject: SoRole_Owner]; 926 else if ([object isKindOfClass: [SOGoObject class]]) 927 { 928 sogoRoles = [(SOGoObject *) object aclsForUser: login]; 929 if ([sogoRoles count]) 930 [rolesForObject addObjectsFromArray: sogoRoles]; 931 sogoRoles = [(SOGoObject *) object subscriptionRoles]; 932 if ([sogoRoles firstObjectCommonWithArray: rolesForObject]) 933 [rolesForObject addObject: SOGoRole_AuthorizedSubscriber]; 934 if ([login isEqualToString: @"anonymous"] 935 && [(SOGoObject *) object isInPublicZone]) 936 [rolesForObject addObject: SOGoRole_PublicUser]; 937 } 938 939#warning this is a hack to work-around the poor implementation of PROPPATCH in SOPE 940 rqMethod = [[context request] method]; 941 if ([rqMethod isEqualToString: @"PROPPATCH"]) 942 [rolesForObject addObject: @"PROPPATCHer"]; 943 944 return rolesForObject; 945} 946 947- (BOOL) isEqual: (id) otherUser 948{ 949 return ([otherUser isKindOfClass: [SoUser class]] 950 && [login isEqualToString: [otherUser login]]); 951} 952 953- (BOOL) isSuperUser 954{ 955 [self domainDefaults]; 956 957 return [[_domainDefaults superUsernames] containsObject: login]; 958} 959 960- (BOOL) canAuthenticate 961{ 962 id authValue; 963 964 authValue = [self _fetchFieldForUser: @"canAuthenticate"]; 965 966 return [authValue boolValue]; 967} 968 969/* resource */ 970- (BOOL) isResource 971{ 972 NSNumber *v; 973 974 v = [self _fetchFieldForUser: @"isResource"]; 975 976 return (v && [v intValue]); 977} 978 979- (int) numberOfSimultaneousBookings 980{ 981 NSNumber *v; 982 983 v = [self _fetchFieldForUser: @"numberOfSimultaneousBookings"]; 984 985 if (v) 986 return [v intValue]; 987 988 return 0; 989} 990 991/* module access */ 992- (BOOL) canAccessModule: (NSString *) module 993{ 994 id accessValue; 995 996 accessValue = [self _fetchFieldForUser: 997 [NSString stringWithFormat: @"%@Access", module]]; 998 999 return [accessValue boolValue]; 1000} 1001 1002@end /* SOGoUser */ 1003