From e0d49d24073ec57af26627f860df8c2dc3500468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Thu, 29 Oct 2015 23:04:42 +0100 Subject: [PATCH 01/50] oc-mail: Check the number of uids fetched on synchroniseCache Equals to the number of uids requested. There are corner-cases when this is not happening, we don't know yet why this is happening but IMAP server log should do the trick. This avoids to launch a NSException trying to access an element outside the array bounds. --- OpenChange/MAPIStoreMailFolder.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 7c8e4f8ac..7492f27bd 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -599,7 +599,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) uint64_t lastModseqNbr; EOQualifier *searchQualifier; NSArray *uids, *changeNumbers; - NSUInteger count, max; + NSUInteger count, max, nFetched; NSArray *fetchResults; NSDictionary *result; NSData *changeKey; @@ -678,6 +678,13 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) = [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ context: NULL]; + nFetched = [fetchResults count]; + if (nFetched != max) { + [self errorWithFormat: @"Error fetching UIDs. Asked: %d Received: %d." + @"Check the IMAP conversation for details", max, nFetched]; + return NO; + } + for (count = 0; count < max; count++) { result = [fetchResults objectAtIndex: count]; From 48832a4878d5f1763c22ab6d9f0f4d6b128b5b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 14:51:46 +0200 Subject: [PATCH 02/50] Add domain to lookupGroupEntryByUID/ByEmail --- SoObjects/SOGo/LDAPSource.h | 6 ++++-- SoObjects/SOGo/LDAPSource.m | 2 ++ SoObjects/SOGo/SOGoGroup.m | 10 +++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/SoObjects/SOGo/LDAPSource.h b/SoObjects/SOGo/LDAPSource.h index 3877ec4ee..9081196be 100644 --- a/SoObjects/SOGo/LDAPSource.h +++ b/SoObjects/SOGo/LDAPSource.h @@ -120,8 +120,10 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField; - (void) setContactMapping: (NSDictionary *) newMapping andObjectClasses: (NSArray *) newObjectClasses; -- (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID; -- (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail; +- (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID + inDomain: (NSString *) domain; +- (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail + inDomain: (NSString *) domain; @end diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index d537c6aec..f13541024 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -1339,12 +1339,14 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses } - (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID + inDomain: (NSString *) domain { return [self _lookupGroupEntryByAttributes: [NSArray arrayWithObject: UIDField] andValue: theUID]; } - (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail + inDomain: (NSString *) domain { return [self _lookupGroupEntryByAttributes: mailFields andValue: theEmail]; diff --git a/SoObjects/SOGo/SOGoGroup.m b/SoObjects/SOGo/SOGoGroup.m index cacdd7770..9b74f2dee 100644 --- a/SoObjects/SOGo/SOGoGroup.m +++ b/SoObjects/SOGo/SOGoGroup.m @@ -113,7 +113,7 @@ uid = [theID hasPrefix: @"@"] ? [theID substringFromIndex: 1] : theID; return [SOGoGroup groupWithValue: uid - andSourceSelector: @selector (lookupGroupEntryByUID:) + andSourceSelector: @selector (lookupGroupEntryByUID:inDomain:) inDomain: domain]; } @@ -121,7 +121,7 @@ inDomain: (NSString *) domain { return [SOGoGroup groupWithValue: theEmail - andSourceSelector: @selector (lookupGroupEntryByEmail:) + andSourceSelector: @selector (lookupGroupEntryByEmail:inDomain:) inDomain: domain]; } @@ -158,9 +158,9 @@ // Our different sources might not all implements groups support if ([source respondsToSelector: theSelector]) - entry = [source performSelector: theSelector - withObject: theValue]; - + entry = [source performSelector: theSelector + withObject: theValue + withObject: domain]; if (entry) break; From 7d49674d9d431dc6fbb838416f8862af74a95d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 15:09:45 +0200 Subject: [PATCH 03/50] Add domain to lookupContactEntry --- SoObjects/Contacts/SOGoContactSourceFolder.m | 21 ++++++++++++-------- SoObjects/SOGo/LDAPSource.m | 1 + SoObjects/SOGo/SOGoSource.h | 3 ++- SoObjects/SOGo/SOGoUserManager.m | 2 +- SoObjects/SOGo/SQLSource.m | 3 ++- Tools/SOGoToolBackup.m | 14 +++++++------ 6 files changed, 27 insertions(+), 17 deletions(-) diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index e385d9c09..26c965d9c 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -164,9 +164,10 @@ ldifEntry = [childRecords objectForKey: objectName]; if (!ldifEntry) { - ldifEntry = [source lookupContactEntry: objectName]; - if (ldifEntry) - [childRecords setObject: ldifEntry forKey: objectName]; + ldifEntry = [source lookupContactEntry: objectName + inDomain: [[context activeUser] domain]]; + if (ldifEntry) + [childRecords setObject: ldifEntry forKey: objectName]; else if ([self isValidContentName: objectName]) { url = [[[lookupContext request] uri] urlWithoutParameters]; @@ -324,10 +325,14 @@ NSDictionary *record; if (aName && [aName length] > 0) - record = [self _flattenedRecord: [source lookupContactEntry: aName]]; + { + record = [source lookupContactEntry: aName + inDomain: [[context activeUser] domain]]; + record = [self _flattenedRecord: record]; + } else record = nil; - + return record; } @@ -562,7 +567,7 @@ toResponse: (WOResponse *) response { NSObject *element; - NSString *url, *baseURL, *cname; + NSString *url, *baseURL, *cname, *domain; NSString **propertiesArray; NSMutableString *buffer; NSDictionary *object; @@ -579,13 +584,13 @@ max = [refs length]; buffer = [NSMutableString stringWithCapacity: max*512]; - + domain = [[context activeUser] domain]; for (count = 0; count < max; count++) { element = [refs objectAtIndex: count]; url = [[[element firstChild] nodeValue] stringByUnescapingURL]; cname = [self _deduceObjectNameFromURL: url fromBaseURL: baseURL]; - object = [source lookupContactEntry: cname]; + object = [source lookupContactEntry: cname inDomain: domain]; if (object) [self appendObject: object properties: propertiesArray diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index f13541024..2973c65a4 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -1242,6 +1242,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses } - (NSDictionary *) lookupContactEntry: (NSString *) theID + inDomain: (NSString *) domain { NGLdapEntry *ldapEntry; EOQualifier *qualifier; diff --git a/SoObjects/SOGo/SOGoSource.h b/SoObjects/SOGo/SOGoSource.h index b55a5a14a..d59a81086 100644 --- a/SoObjects/SOGo/SOGoSource.h +++ b/SoObjects/SOGo/SOGoSource.h @@ -56,7 +56,8 @@ newPassword: (NSString *) newPassword perr: (SOGoPasswordPolicyError *) perr; -- (NSDictionary *) lookupContactEntry: (NSString *) theID; +- (NSDictionary *) lookupContactEntry: (NSString *) theID + inDomain: (NSString *) domain; - (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) entryID inDomain: (NSString *) domain; diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index d5a6a94d0..3846da9d8 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -1047,7 +1047,7 @@ static Class NSNullK; while ((sourceID = [sources nextObject])) { currentSource = [_sources objectForKey: sourceID]; - contact = [currentSource lookupContactEntry: uid]; + contact = [currentSource lookupContactEntry: uid inDomain: domain]; if (contact) [contacts addObject: contact]; } diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index 7d0de6c07..06d40557a 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -618,8 +618,9 @@ - (NSDictionary *) lookupContactEntry: (NSString *) theID + inDomain: (NSString *) domain { - return [self _lookupContactEntry: theID considerEmail: NO inDomain: nil]; + return [self _lookupContactEntry: theID considerEmail: NO inDomain: domain]; } - (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) entryID diff --git a/Tools/SOGoToolBackup.m b/Tools/SOGoToolBackup.m index fb2b4d016..3f2cb27bc 100644 --- a/Tools/SOGoToolBackup.m +++ b/Tools/SOGoToolBackup.m @@ -363,6 +363,7 @@ } - (BOOL) extractUserLDIFRecord: (NSString *) uid + inDomain: (NSString *) domain intoRecord: (NSMutableDictionary *) userRecord { NSEnumerator *ldapSources; @@ -375,11 +376,11 @@ lm = [SOGoUserManager sharedUserManager]; done = NO; - ldapSources = [[lm authenticationSourceIDsInDomain: nil] objectEnumerator]; + ldapSources = [[lm authenticationSourceIDsInDomain: domain] objectEnumerator]; while (!done && (sourceID = [ldapSources nextObject])) { currentSource = [lm sourceWithID: sourceID]; - userEntry = [currentSource lookupContactEntry: uid]; + userEntry = [currentSource lookupContactEntry: uid inDomain: domain]; if (userEntry) { [userRecord setObject: [userEntry ldifRecordAsString] @@ -411,25 +412,26 @@ - (BOOL) exportUser: (NSDictionary *) theUser { - NSString *exportPath, *gcsUID, *ldapUID; + NSString *exportPath, *gcsUID, *ldapUID, *domain; NSMutableDictionary *userRecord; SOGoSystemDefaults *sd; - + sd = [SOGoSystemDefaults sharedSystemDefaults]; userRecord = [NSMutableDictionary dictionary]; ldapUID = [theUser objectForKey: @"c_uid"]; exportPath = [directory stringByAppendingPathComponent: ldapUID]; - + domain = [theUser objectForKey: @"c_domain"]; gcsUID = [theUser objectForKey: @"c_uid"]; if ([sd enableDomainBasedUID] && [gcsUID rangeOfString: @"@"].location == NSNotFound) gcsUID = [NSString stringWithFormat: @"%@@%@", gcsUID, [theUser objectForKey: @"c_domain"]]; - + return ([self extractUserFolders: gcsUID intoRecord: userRecord] && [self extractUserLDIFRecord: ldapUID + inDomain: domain intoRecord: userRecord] && [self extractUserPreferences: gcsUID intoRecord: userRecord] From 9d3f3c619d5172c9a303bd3681f8a4b01f514df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 15:11:08 +0200 Subject: [PATCH 04/50] LDAPSource: release displayName on dealloc --- SoObjects/SOGo/LDAPSource.m | 1 + 1 file changed, 1 insertion(+) diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index 2973c65a4..a4a2b9976 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -162,6 +162,7 @@ static Class NSStringK; [multipleBookingsField release]; [MSExchangeHostname release]; [modifiers release]; + [displayName release]; [super dealloc]; } From bf4a581d0f6ddd18493d4813292f7bd9c7ad4bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 15:36:39 +0200 Subject: [PATCH 05/50] Added isDomainDefined To check whether a domain is supported by our defined sources --- SoObjects/SOGo/SOGoSession.m | 3 ++- SoObjects/SOGo/SOGoUser.m | 2 +- SoObjects/SOGo/SOGoUserManager.h | 1 + SoObjects/SOGo/SOGoUserManager.m | 31 ++++++++++++++++++++++++++++++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/SoObjects/SOGo/SOGoSession.m b/SoObjects/SOGo/SOGoSession.m index 9af654b90..73bbbfa5d 100644 --- a/SoObjects/SOGo/SOGoSession.m +++ b/SoObjects/SOGo/SOGoSession.m @@ -41,6 +41,7 @@ #include #import "SOGoSystemDefaults.h" +#import "SOGoUserManager.h" @implementation SOGoSession @@ -262,7 +263,7 @@ // The domain is probably appended to the username; // make sure it is defined as a domain in the configuration. *theDomain = [*theLogin substringFromIndex: (r.location + r.length)]; - if (![[sd domainIds] containsObject: *theDomain]) + if (![[SOGoUserManager sharedUserManager] isDomainDefined: *theDomain]) *theDomain = nil; } } diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index d81dcf52c..d84eb6188 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -165,7 +165,7 @@ // The domain is probably appended to the username; // make sure it is defined as a domain in the configuration. domain = [newLogin substringFromIndex: (r.location + r.length)]; - if ([[sd domainIds] containsObject: domain] && + if ([[SOGoUserManager sharedUserManager] isDomainDefined: domain] && ![sd enableDomainBasedUID]) newLogin = [newLogin substringToIndex: r.location]; diff --git a/SoObjects/SOGo/SOGoUserManager.h b/SoObjects/SOGo/SOGoUserManager.h index d30821108..5cf246d3a 100644 --- a/SoObjects/SOGo/SOGoUserManager.h +++ b/SoObjects/SOGo/SOGoUserManager.h @@ -58,6 +58,7 @@ - (NSArray *) sourceIDsInDomain: (NSString *) domain; - (NSArray *) authenticationSourceIDsInDomain: (NSString *) domain; - (NSArray *) addressBookSourceIDsInDomain: (NSString *) domain; +- (BOOL) isDomainDefined: (NSString *) domain; - (NSObject *) sourceWithID: (NSString *) sourceID; - (NSDictionary *) metadataForSourceID: (NSString *) sourceID; diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 3846da9d8..9afd3a74c 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -305,6 +305,35 @@ static Class NSNullK; return sourceIDs; } +- (BOOL) isDomainDefined: (NSString *) domain +{ + NSEnumerator *allIDs; + NSArray *ids; + NSString *currentID, *sourceDomain; + SOGoSystemDefaults *sd; + + if (!domain) return NO; + + ids = [_sources allKeys]; + if ([ids containsObject: domain]) + // FIXME check SOGoMailDomain? + // Now source id is being considered as the domain + return YES; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; + if ([sd enableDomainBasedUID]) + { + allIDs = [ids objectEnumerator]; + while ((currentID = [allIDs nextObject])) + { + sourceDomain = [[_sources objectForKey: currentID] domain]; + if (!sourceDomain) // source that can identify any domain + return YES; + } + } + + return NO; +} - (NSString *) displayNameForSourceWithID: (NSString *) sourceID { NSDictionary *metadata; @@ -932,7 +961,7 @@ static Class NSNullK; // The domain is probably appended to the username; // make sure it is a defined domain in the configuration. domain = [uid substringFromIndex: (r.location + r.length)]; - if ([[sd domainIds] containsObject: domain]) + if ([self isDomainDefined: domain]) username = [uid substringToIndex: r.location]; else domain = nil; From 0d05e3579cb56b98cd80a3216ea8e6c51655e04e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 15:38:11 +0200 Subject: [PATCH 06/50] defaultsForDomain return sharedSystemDefaults for unknown domains --- SoObjects/SOGo/SOGoDomainDefaults.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index b55cc0851..4c7541b19 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -50,6 +50,9 @@ andParentSource: systemDefaults]; } + if (!domainDefaults) + domainDefaults = [SOGoSystemDefaults sharedSystemDefaults]; + return domainDefaults; } From ec533809aa46141545628be5d918a966ca8514e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 15:41:48 +0200 Subject: [PATCH 07/50] return enableDomainBasedUID based only on the key --- SoObjects/SOGo/SOGoSystemDefaults.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SoObjects/SOGo/SOGoSystemDefaults.m b/SoObjects/SOGo/SOGoSystemDefaults.m index 8c9181665..9447b1da3 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.m +++ b/SoObjects/SOGo/SOGoSystemDefaults.m @@ -261,7 +261,7 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict, - (BOOL) enableDomainBasedUID { - return ([[self domainIds] count] > 0 && [self boolForKey: @"SOGoEnableDomainBasedUID"]); + return [self boolForKey: @"SOGoEnableDomainBasedUID"]; } - (NSArray *) loginDomains From 30a94161af562ed83b9a06afbce2661f4529fa84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 15:48:21 +0200 Subject: [PATCH 08/50] SOGoUserManager.m: format and small refactor Removed all tabs used for indentation --- SoObjects/SOGo/SOGoUserManager.m | 239 ++++++++++++++----------------- 1 file changed, 110 insertions(+), 129 deletions(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 9afd3a74c..fe81be52d 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -104,65 +104,58 @@ static Class NSNullK; NSString *sourceID, *value, *type; NSMutableDictionary *metadata; NSObject *sogoSource; - BOOL isAddressBook, result; + BOOL isAddressBook; Class c; - result = NO; sourceID = [udSource objectForKey: @"id"]; - if ([sourceID length] > 0) + if (!sourceID || [sourceID length] == 0) { - if ([_sourcesMetadata objectForKey: sourceID]) - [self errorWithFormat: @"attempted to register a contact/user source" - @" with duplicated id (%@)", sourceID]; - else - { - type = [[udSource objectForKey: @"type"] lowercaseString]; - c = NSClassFromString([_registry sourceClassForType: type]); - sogoSource = [c sourceFromUDSource: udSource inDomain: domain]; - if (sourceID) - [_sources setObject: sogoSource forKey: sourceID]; - else - [self errorWithFormat: @"id field missing in an user source," - @" check the SOGoUserSources defaults"]; - metadata = [NSMutableDictionary dictionary]; - if (domain) - [metadata setObject: domain forKey: @"domain"]; - value = [udSource objectForKey: @"canAuthenticate"]; - if (value) - [metadata setObject: value forKey: @"canAuthenticate"]; - value = [udSource objectForKey: @"isAddressBook"]; - if (value) - { - [metadata setObject: value forKey: @"isAddressBook"]; - isAddressBook = [value boolValue]; - } - else - isAddressBook = NO; - value = [udSource objectForKey: @"displayName"]; - if (value) - [metadata setObject: value forKey: @"displayName"]; - else - { - if (isAddressBook) - [self errorWithFormat: @"addressbook source '%@' has" - @" no displayname", sourceID]; - } - value = [udSource objectForKey: @"MailFieldNames"]; - if (value) - [metadata setObject: value forKey: @"MailFieldNames"]; - value = [udSource objectForKey: @"SearchFieldNames"]; - if (value) - [metadata setObject: value forKey: @"SearchFieldNames"]; - - [_sourcesMetadata setObject: metadata forKey: sourceID]; - result = YES; - } + [self errorWithFormat: @"attempted to register a contact/user source " + @"without id (skipped)"]; + return NO; + } + if ([_sourcesMetadata objectForKey: sourceID]) + { + [self errorWithFormat: @"attempted to register a contact/user source " + @"with duplicated id (%@)", sourceID]; + return NO; + } + + type = [[udSource objectForKey: @"type"] lowercaseString]; + c = NSClassFromString([_registry sourceClassForType: type]); + sogoSource = [c sourceFromUDSource: udSource inDomain: domain]; + [_sources setObject: sogoSource forKey: sourceID]; + + metadata = [NSMutableDictionary dictionary]; + if (domain) + [metadata setObject: domain forKey: @"domain"]; + value = [udSource objectForKey: @"canAuthenticate"]; + if (value) + [metadata setObject: value forKey: @"canAuthenticate"]; + value = [udSource objectForKey: @"isAddressBook"]; + if (value) + { + [metadata setObject: value forKey: @"isAddressBook"]; + isAddressBook = [value boolValue]; } else - [self errorWithFormat: @"attempted to register a contact/user source" - @" without id (skipped)"]; + isAddressBook = NO; + value = [udSource objectForKey: @"displayName"]; + if (value) + [metadata setObject: value forKey: @"displayName"]; + else if (isAddressBook) + [self errorWithFormat: @"addressbook source '%@' has no displayname", sourceID]; - return result; + value = [udSource objectForKey: @"MailFieldNames"]; + if (value) + [metadata setObject: value forKey: @"MailFieldNames"]; + value = [udSource objectForKey: @"SearchFieldNames"]; + if (value) + [metadata setObject: value forKey: @"SearchFieldNames"]; + + [_sourcesMetadata setObject: metadata forKey: sourceID]; + + return YES; } - (int) _registerSourcesInDomain: (NSString *) domain @@ -171,11 +164,7 @@ static Class NSNullK; unsigned int count, max, total; SOGoDomainDefaults *dd; - if (domain) - dd = [SOGoDomainDefaults defaultsForDomain: domain]; - else - dd = [SOGoSystemDefaults sharedSystemDefaults]; - + dd = [SOGoDomainDefaults defaultsForDomain: domain]; userSources = [dd userSources]; max = [userSources count]; total = 0; @@ -357,7 +346,6 @@ static Class NSNullK; { NSDictionary *contactInfos; -// NSLog (@"getEmailForUID: %@", uid); contactInfos = [self contactInfosForUserWithUIDorEmail: uid]; return [contactInfos objectForKey: @"c_email"]; @@ -374,8 +362,7 @@ static Class NSNullK; if ([cn length] > 0) { if ([email length] > 0) - fullEmail = [NSString stringWithFormat: @"%@ <%@>", - cn, email]; + fullEmail = [NSString stringWithFormat: @"%@ <%@>", cn, email]; else fullEmail = cn; } @@ -398,11 +385,7 @@ static Class NSNullK; login = [contactInfos objectForKey: @"c_imaplogin"]; if (login == nil) { - if ([domain length]) - dd = [SOGoDomainDefaults defaultsForDomain: domain]; - else - dd = [SOGoSystemDefaults sharedSystemDefaults]; - + dd = [SOGoDomainDefaults defaultsForDomain: domain]; if ([dd forceExternalLoginWithEmail]) { sd = [SOGoSystemDefaults sharedSystemDefaults]; @@ -444,16 +427,16 @@ static Class NSNullK; - (BOOL) _sourceChangePasswordForLogin: (NSString *) login inDomain: (NSString *) domain oldPassword: (NSString *) oldPassword - newPassword: (NSString *) newPassword - perr: (SOGoPasswordPolicyError *) perr + newPassword: (NSString *) newPassword + perr: (SOGoPasswordPolicyError *) perr { NSObject *sogoSource; NSEnumerator *authIDs; NSString *currentID; BOOL didChange; - + didChange = NO; - + authIDs = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; while (!didChange && (currentID = [authIDs nextObject])) { @@ -478,8 +461,6 @@ static Class NSNullK; NSEnumerator *authIDs; NSString *currentID; BOOL checkOK; - SOGoSystemDefaults *sd; - NSRange r; checkOK = NO; @@ -577,7 +558,7 @@ static Class NSNullK; delta = current_time - start_time; block_time = [sd failedLoginBlockInterval]; - + if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [sd maximumFailedLoginCount] && delta >= [sd maximumFailedLoginInterval] && delta <= block_time ) @@ -585,7 +566,7 @@ static Class NSNullK; *_perr = PolicyAccountLocked; return NO; } - + if (delta > block_time) { [[SOGoCache sharedCache] setFailedCount: 0 @@ -669,7 +650,7 @@ static Class NSNullK; [[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1) forLogin: username]; } - + checkOK = NO; } @@ -680,7 +661,7 @@ static Class NSNullK; { NSObject *currentSource; NSEnumerator *sources; - + sources = [[_sources allValues] objectEnumerator]; while ((currentSource = [sources nextObject])) if ([currentSource conformsToProtocol: @protocol(SOGoDNSource)] && @@ -700,9 +681,9 @@ static Class NSNullK; // - (BOOL) changePasswordForLogin: (NSString *) login inDomain: (NSString *) domain - oldPassword: (NSString *) oldPassword - newPassword: (NSString *) newPassword - perr: (SOGoPasswordPolicyError *) perr + oldPassword: (NSString *) oldPassword + newPassword: (NSString *) newPassword + perr: (SOGoPasswordPolicyError *) perr { NSString *jsonUser, *userLogin; NSMutableDictionary *currentUser; @@ -817,7 +798,7 @@ static Class NSNullK; while (!userEntry && (sourceID = [sogoSources nextObject])) { currentSource = [_sources objectForKey: sourceID]; - + userEntry = [currentSource lookupContactEntryWithUIDorEmail: theUID inDomain: theDomain]; if (userEntry) @@ -932,7 +913,7 @@ static Class NSNullK; [user setObject: [NSNumber numberWithBool: YES] forKey: @"CalendarAccess"]; [user setObject: [NSNumber numberWithBool: NO] - forKey: @"MailAccess"]; + forKey: @"MailAccess"]; } return user; @@ -976,7 +957,7 @@ static Class NSNullK; // search using the original uid. infos = [self contactInfosForUserWithUIDorEmail: uid inDomain: nil]; - + return infos; } @@ -1005,29 +986,29 @@ static Class NSNullK; if ([currentUser isKindOfClass: NSNullK]) currentUser = nil; else if (!([currentUser objectForKey: @"emails"] - && [currentUser objectForKey: @"cn"])) - { - // We make sure that we either have no occurence of a cache entry or - // that we have an occurence with only a cached password. In the - // latter case, we update the entry with the remaining information - // and recache the value. - if (!currentUser || + && [currentUser objectForKey: @"cn"])) + { + // We make sure that we either have no occurence of a cache entry or + // that we have an occurence with only a cached password. In the + // latter case, we update the entry with the remaining information + // and recache the value. + if (!currentUser || ([currentUser count] == 1 && [currentUser objectForKey: @"password"]) || ([currentUser count] == 2 && [currentUser objectForKey: @"password"] && [currentUser objectForKey: @"DomainLessLogin"])) { - newUser = YES; + newUser = YES; - if (!currentUser) - currentUser = [NSMutableDictionary dictionary]; - } - else - newUser = NO; - [self _fillContactInfosForUser: currentUser + if (!currentUser) + currentUser = [NSMutableDictionary dictionary]; + } + else + newUser = NO; + [self _fillContactInfosForUser: currentUser withUIDorEmail: aUID inDomain: domain]; - if (newUser) - { - if ([[currentUser objectForKey: @"c_uid"] length] == 0) + if (newUser) + { + if ([[currentUser objectForKey: @"c_uid"] length] == 0) { [self _retainUser: (NSDictionary *) [NSNull null] withLogin: cacheUid]; @@ -1050,7 +1031,7 @@ static Class NSNullK; [self _retainUser: currentUser withLogin: cacheUid]; } } - } + } } else currentUser = nil; @@ -1105,30 +1086,30 @@ static Class NSNullK; { uid = [userEntry objectForKey: @"c_uid"]; if ([uid length]) - { - returnContact = [compactContacts objectForKey: uid]; - if (!returnContact) - { - returnContact = [NSMutableDictionary dictionary]; - [returnContact setObject: uid forKey: @"c_uid"]; + { + returnContact = [compactContacts objectForKey: uid]; + if (!returnContact) + { + returnContact = [NSMutableDictionary dictionary]; + [returnContact setObject: uid forKey: @"c_uid"]; source = [userEntry objectForKey: @"source"]; if (source) [returnContact setObject: source forKey: @"source"]; [compactContacts setObject: returnContact forKey: uid]; } - if (![[returnContact objectForKey: @"c_name"] length]) - [returnContact setObject: [userEntry objectForKey: @"c_name"] - forKey: @"c_name"]; - if (![[returnContact objectForKey: @"cn"] length]) - [returnContact setObject: [userEntry objectForKey: @"c_cn"] - forKey: @"cn"]; - emails = [returnContact objectForKey: @"emails"]; - if (!emails) - { - emails = [NSMutableArray array]; - [returnContact setObject: emails forKey: @"emails"]; - } - email = [userEntry objectForKey: @"mail"]; + if (![[returnContact objectForKey: @"c_name"] length]) + [returnContact setObject: [userEntry objectForKey: @"c_name"] + forKey: @"c_name"]; + if (![[returnContact objectForKey: @"cn"] length]) + [returnContact setObject: [userEntry objectForKey: @"c_cn"] + forKey: @"cn"]; + emails = [returnContact objectForKey: @"emails"]; + if (!emails) + { + emails = [NSMutableArray array]; + [returnContact setObject: emails forKey: @"emails"]; + } + email = [userEntry objectForKey: @"mail"]; if ([email isKindOfClass: [NSArray class]]) { allEmails = (NSArray *) email; @@ -1140,22 +1121,22 @@ static Class NSNullK; } } else if (email && ![emails containsObject: email]) - [emails addObject: email]; - email = [userEntry objectForKey: @"mozillasecondemail"]; - if (email && ![emails containsObject: email]) - [emails addObject: email]; - email = [userEntry objectForKey: @"xmozillasecondemail"]; - if (email && ![emails containsObject: email]) - [emails addObject: email]; + [emails addObject: email]; + email = [userEntry objectForKey: @"mozillasecondemail"]; + if (email && ![emails containsObject: email]) + [emails addObject: email]; + email = [userEntry objectForKey: @"xmozillasecondemail"]; + if (email && ![emails containsObject: email]) + [emails addObject: email]; info = [userEntry objectForKey: @"c_info"]; if ([info length] > 0 && ![[returnContact objectForKey: @"c_info"] length]) [returnContact setObject: info forKey: @"c_info"]; - [self _fillContactMailRecords: returnContact]; + [self _fillContactMailRecords: returnContact]; isGroup = [userEntry objectForKey: @"isGroup"]; if (isGroup) [returnContact setObject: isGroup forKey: @"isGroup"]; - } + } } newContacts = [compactContacts allValues]; @@ -1164,7 +1145,7 @@ static Class NSNullK; } - (NSArray *) _fetchEntriesInSources: (NSArray *) sourcesList - matching: (NSString *) filter + matching: (NSString *) filter inDomain: (NSString *) domain { NSMutableArray *contacts; @@ -1178,7 +1159,7 @@ static Class NSNullK; { currentSource = [_sources objectForKey: sourceID]; [contacts addObjectsFromArray: - [currentSource fetchContactsMatching: filter + [currentSource fetchContactsMatching: filter inDomain: domain]]; } From 08f57f6ad527f0f0b55fbf43c69c3609bc389bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 15:49:53 +0200 Subject: [PATCH 09/50] SOGoUserManager: get domain after authentication For multidomain source which has no domain defined, we get the domain from the username which must have @domain as suffix --- SoObjects/SOGo/SOGoUserManager.m | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index fe81be52d..60f69f9a3 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -477,7 +477,24 @@ static Class NSNullK; } if (checkOK && *domain == nil) - *domain = [sogoSource domain]; + { + SOGoSystemDefaults *sd = [SOGoSystemDefaults sharedSystemDefaults]; + BOOL multidomainSource = [sd enableDomainBasedUID] && + [sogoSource domain] == nil; + if (multidomainSource) + { + NSArray *parts = [login componentsSeparatedByString: @"@"]; + if ([parts count] != 2) + { + [self errorWithFormat: @"Authenticated with multidomain source " + @"but login is not an email (%@).", login]; + return NO; + } + *domain = [parts objectAtIndex: 1]; + } + else + *domain = [sogoSource domain]; + } return checkOK; } From 8cfb0f0d44830fe8972990dd781b77cc2372070b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 23 Sep 2015 15:52:41 +0200 Subject: [PATCH 10/50] Avoid inserting second email for Contacts When we have a contact with an email, avoid generating another one with SOGoMailDomain value (normally we ended up with a contact with two identical emails on 'emails' key and for multidomain source we would had ended up with an email @localhost) --- SoObjects/SOGo/SOGoUserManager.m | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 60f69f9a3..4425a8a8e 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -749,21 +749,19 @@ static Class NSNullK; SOGoDomainDefaults *dd; domain = [contact objectForKey: @"c_domain"]; - if ([domain length]) - dd = [SOGoDomainDefaults defaultsForDomain: domain]; - else - dd = [SOGoSystemDefaults sharedSystemDefaults]; + dd = [SOGoDomainDefaults defaultsForDomain: domain]; emails = [contact objectForKey: @"emails"]; - uid = [contact objectForKey: @"c_uid"]; - if ([uid rangeOfString: @"@"].location == NSNotFound) - systemEmail - = [NSString stringWithFormat: @"%@@%@", uid, [dd mailDomain]]; - else - systemEmail = uid; - - // We always add the system email, which will always be returned - // by SOGoUser -systemEmail. - [emails addObject: systemEmail]; + if ([emails count] == 0) + { + uid = [contact objectForKey: @"c_uid"]; + if ([uid rangeOfString: @"@"].location == NSNotFound) + systemEmail = [NSString stringWithFormat: @"%@@%@", uid, [dd mailDomain]]; + else + systemEmail = uid; + // We always add the system email, which will always be returned + // by SOGoUser -systemEmail. + [emails addObject: systemEmail]; + } [contact setObject: [emails objectAtIndex: 0] forKey: @"c_email"]; } From a6bbe13f9bb2766651a45b947bdc50135edc9f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20P=C3=A9rez-Aradros=20Herce?= Date: Thu, 5 Nov 2015 17:47:43 +0100 Subject: [PATCH 11/50] Add missing header files --- SoObjects/SOGo/GNUmakefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index 40e19725b..65713871a 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -32,6 +32,12 @@ SOGo_HEADER_FILES = \ SOGoGCSFolder.h \ SOGoParentFolder.h \ SOGoUserFolder.h \ + SOGoSource.h \ + SOGoSystemDefaults.h \ + SOGoDomainDefaults.h \ + SOGoLDAPDefaults.h \ + SOGoDefaultsSource.h \ + SOGoUserDefaults.h \ \ SOGoSieveManager.h \ \ From 946665ff4934fbf9ddb862e7ba5375ea125a8ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Vall=C3=A9s?= Date: Fri, 6 Nov 2015 11:05:58 +0100 Subject: [PATCH 12/50] oc-calendar: Make invitation/update mails have the event name as subject SOGo event notification mails add information to the event name in their subject (e.g. _Event invitation: "foo"_). The client uses the mail subject to create a copy of the event on the attendee's calendar, so we need to strip out that extra information if we want the event to have the proper name. --- OpenChange/MAPIStoreMailMessage.m | 47 +++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 55ad92bd0..5de68fcdc 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -522,6 +522,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) NSUInteger colIdx; NSString *stringValue; + /* As specified in [MS-OXCMAIL] 2.2.3.2.6.1, if there are three + or less characters followed by a colon at the beginning of + the subject, we can assume that's the subject prefix */ subject = [self subject]; colIdx = [subject rangeOfString: @":"].location; if (colIdx != NSNotFound && colIdx < 4) @@ -537,17 +540,45 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) - (int) getPidTagNormalizedSubject: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - NSString *subject; - NSUInteger colIdx; - NSString *stringValue; + NSString *stringValue, *subject; + NSUInteger quoteStartIdx, quoteEndIdx, colIdx; + NSRange quoteRange; + + if (!headerSetup) + [self _fetchHeaderData]; subject = [self subject]; - colIdx = [subject rangeOfString: @":"].location; - if (colIdx != NSNotFound && colIdx < 4) - stringValue = [[subject substringFromIndex: colIdx + 1] - stringByTrimmingLeadSpaces]; + if (mailIsMeetingRequest) + { + + /* SOGo "spices up" the invitation/update mail's subject, but + the client uses it to name the attendee's event, so we keep + only what's inside the quotes */ + quoteStartIdx = [subject rangeOfString: @"\""].location; + quoteEndIdx = [subject rangeOfString: @"\"" + options: NSBackwardsSearch].location; + if (quoteStartIdx != NSNotFound + && quoteEndIdx != NSNotFound + && quoteStartIdx != quoteEndIdx) + { + quoteRange = NSMakeRange(quoteStartIdx + 1, quoteEndIdx - quoteStartIdx - 1); + stringValue = [subject substringWithRange: quoteRange]; + } + else stringValue = subject; + } else - stringValue = subject; + { + + /* As specified in [MS-OXCMAIL] 2.2.3.2.6.1, if there are three + or less characters followed by a colon at the beginning of + the subject, we can assume that's the subject prefix */ + colIdx = [subject rangeOfString: @":"].location; + if (colIdx != NSNotFound && colIdx < 4) + stringValue = [[subject substringFromIndex: colIdx + 1] + stringByTrimmingLeadSpaces]; + else + stringValue = subject; + } if (!stringValue) stringValue = @""; *data = [stringValue asUnicodeInMemCtx: memCtx]; From 77bba8e5ea6253b7473f15e3fac684e0d1901aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Thu, 29 Oct 2015 23:10:22 +0100 Subject: [PATCH 13/50] Use application context when initialising quick tables ACLs As the stored context from initialisation may have changed by `setContext` by other operations when acting as a OpenChange library. This would make having a login set to nil and forcing a NSException when it attempts to set a nil key at [SOGoAppointmentFolder:roleForComponentsWithAccessClass:forUser] inside [SOGoAppointmentFolder:initializeQuickTablesAclsInContext]. --- SoObjects/Appointments/SOGoAppointmentFolder.m | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index cfda94c82..cff8d8c03 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -604,8 +604,16 @@ static Class iCalEventK = nil; NSNumber *classNumber; unsigned int grantedCount; iCalAccessClass currentClass; + WOContext *localContext; - [self initializeQuickTablesAclsInContext: context]; + /* FIXME: The stored context from initialisation may have changed + by setContext by other operations in OpenChange library, + so we keep tighly to use the current session one. Without + this, the login is set to nil and a NSException is raised + at [SOGoAppointmentFolder:roleForComponentsWithAccessClass:forUser] + inside [SOGoAppointmentFolder:initializeQuickTablesAclsInContext]. */ + localContext = [[WOApplication application] context]; + [self initializeQuickTablesAclsInContext: localContext]; grantedClasses = [NSMutableArray arrayWithCapacity: 3]; deniedClasses = [NSMutableArray arrayWithCapacity: 3]; for (currentClass = 0; From 08f05ac2ef532edc60f99fc1c92add7dbb75a5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Mon, 9 Nov 2015 19:06:36 +0100 Subject: [PATCH 14/50] sogo-openchange: Avoid compile warnings after changes in exchange.idl The changes in the exchange.idl file from OpenChange has changed some string pointers from 'const char *' to 'uint8_t *'. This changeset cast them to avoid compilation warnings. --- OpenChange/MAPIStoreDBFolder.m | 2 +- OpenChange/MAPIStoreMailFolder.m | 2 +- OpenChange/MAPIStoreTypes.m | 2 +- OpenChange/NSArray+MAPIStore.m | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenChange/MAPIStoreDBFolder.m b/OpenChange/MAPIStoreDBFolder.m index d1734dc7c..9e7a6d713 100644 --- a/OpenChange/MAPIStoreDBFolder.m +++ b/OpenChange/MAPIStoreDBFolder.m @@ -102,7 +102,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; { value = get_SPropValue_SRow (aRow, PidTagDisplayName_string8); if (value) - folderName = [NSString stringWithUTF8String: value->value.lpszA]; + folderName = [NSString stringWithUTF8String: (const char *) value->value.lpszA]; else folderName = nil; } diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 7492f27bd..6934e7a60 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -181,7 +181,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; if (aRow->lpProps[i].ulPropTag == PR_DISPLAY_NAME_UNICODE) folderName = [NSString stringWithUTF8String: aRow->lpProps[i].value.lpszW]; else if (aRow->lpProps[i].ulPropTag == PR_DISPLAY_NAME) - folderName = [NSString stringWithUTF8String: aRow->lpProps[i].value.lpszA]; + folderName = [NSString stringWithUTF8String: (const char *) aRow->lpProps[i].value.lpszA]; } if (folderName) diff --git a/OpenChange/MAPIStoreTypes.m b/OpenChange/MAPIStoreTypes.m index 76e72c5d6..eb3b499b3 100644 --- a/OpenChange/MAPIStoreTypes.m +++ b/OpenChange/MAPIStoreTypes.m @@ -198,7 +198,7 @@ NSObjectFromSPropValue (const struct SPropValue *value) break; case PT_STRING8: result = (value->value.lpszA - ? [NSString stringWithUTF8String: value->value.lpszA] + ? [NSString stringWithUTF8String: (const char *) value->value.lpszA] : (id) @""); break; case PT_SYSTIME: diff --git a/OpenChange/NSArray+MAPIStore.m b/OpenChange/NSArray+MAPIStore.m index fcb7d3896..be54e9259 100644 --- a/OpenChange/NSArray+MAPIStore.m +++ b/OpenChange/NSArray+MAPIStore.m @@ -228,7 +228,7 @@ mvResult = [NSMutableArray arrayWithCapacity: mvString->cValues]; for (count = 0; count < mvString->cValues; count++) { - subObject = [NSString stringWithUTF8String: mvString->lppszA[count]]; + subObject = [NSString stringWithUTF8String: (const char *) mvString->lppszA[count]]; [mvResult addObject: subObject]; } From 194c415631f97e8e85687df97c01bb4025c01661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Mon, 9 Nov 2015 19:55:55 +0100 Subject: [PATCH 15/50] Log an error for SOGoObject without wocontext --- SoObjects/SOGo/SOGoObject.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index 3f3afcb9b..df5bc6afc 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -180,6 +180,9 @@ [NSException raise: NSInvalidArgumentException format: @"'_name' must not be an empty string"]; context = [[WOApplication application] context]; + if (!context) + [self errorWithFormat: @"Error: initializing a SOGoObject (named %@) " + @"without wocontext", _name]; nameInContainer = [_name copy]; container = _container; if ([self doesRetainContainer]) From 5f5b5d9273ef2448007957b182efe7df3a6bff0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Mon, 9 Nov 2015 19:39:54 +0100 Subject: [PATCH 16/50] oc: activate user context on initialization (for real) Even with cached ones (completes cfab18e1b845bf24ac896014bce4d61a46f44fde) --- OpenChange/MAPIStoreUserContext.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m index bc91f56b0..de3dfadf1 100644 --- a/OpenChange/MAPIStoreUserContext.m +++ b/OpenChange/MAPIStoreUserContext.m @@ -73,6 +73,8 @@ static NSMapTable *contextsTable = nil; [userContext autorelease]; [contextsTable setObject: userContext forKey: username]; } + else + [userContext activate]; return userContext; } From 88a74bcb5cf5b89ce1778b0637786a1e0a7dbce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Wed, 4 Nov 2015 13:08:53 +0100 Subject: [PATCH 17/50] Do not save IPM.DistList messages --- OpenChange/MAPIStoreContactsMessage.m | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index cccb495e1..0236f1817 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -1024,10 +1024,12 @@ fromProperties: (NSDictionary *) attachmentProps || (!isNew && [roles containsObject: SOGoRole_ObjectEditor])); } -// -// -// -- (void) save:(TALLOC_CTX *) memCtx +- (void) saveDistList:(TALLOC_CTX *) memCtx +{ + [self warnWithFormat: @"IPM.DistList messages are ignored"]; +} + +- (void) saveContact:(TALLOC_CTX *) memCtx { NSArray *elements, *units; CardElement *element; @@ -1373,4 +1375,14 @@ fromProperties: (NSDictionary *) attachmentProps [self updateVersions]; } +- (void) save:(TALLOC_CTX *) memCtx +{ + NSString *messageClass = [properties objectForKey: MAPIPropertyKey(PR_MESSAGE_CLASS_UNICODE)]; + if ([messageClass isEqualToString: @"IPM.DistList"]) + [self saveDistList: memCtx]; + else + [self saveContact: memCtx]; +} + + @end From ec598321c4d1c2dedf509f6785a8f6673162c1f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20P=C3=A9rez-Aradros=20Herce?= Date: Tue, 10 Nov 2015 17:38:35 +0100 Subject: [PATCH 18/50] Add user defined user source --- SoObjects/SOGo/SOGoUserManager.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 4425a8a8e..67eabaf10 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -67,10 +67,12 @@ static Class NSNullK; if (type) { - if ([type isEqualToString: @"ldap"]) + if ([type caseInsensitiveCompare: @"ldap"] == NSOrderedSame) sourceClass = @"LDAPSource"; - else if ([type isEqualToString: @"sql"]) + else if ([type caseInsensitiveCompare: @"sql"] == NSOrderedSame) sourceClass = @"SQLSource"; + else if (NSClassFromString(type)) + sourceClass = type; else { [NSException raise: @"SOGoUserManagerRegistryException" @@ -121,7 +123,7 @@ static Class NSNullK; return NO; } - type = [[udSource objectForKey: @"type"] lowercaseString]; + type = [udSource objectForKey: @"type"]; c = NSClassFromString([_registry sourceClassForType: type]); sogoSource = [c sourceFromUDSource: udSource inDomain: domain]; [_sources setObject: sogoSource forKey: sourceID]; From a73df96b78b5824a3fa81231f94b1c7125d6c1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Mon, 16 Nov 2015 13:56:04 +0100 Subject: [PATCH 19/50] [MAPIStoreGCSMessage getPidTagChangeKey:inMemCtx:] return on error This method aborted when no PidTagChangeKey was found and the new flag was not set. Now it returns MAPISTORE_ERR_NOT_FOUND. --- OpenChange/MAPIStoreGCSMessage.m | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/OpenChange/MAPIStoreGCSMessage.m b/OpenChange/MAPIStoreGCSMessage.m index 747bb783a..f9d3a16d8 100644 --- a/OpenChange/MAPIStoreGCSMessage.m +++ b/OpenChange/MAPIStoreGCSMessage.m @@ -140,9 +140,16 @@ [parentFolder synchroniseCache]; changeKey = [parentFolder changeKeyForMessageWithKey: nameInContainer]; } - if (!changeKey) - abort (); - *data = [changeKey asBinaryInMemCtx: memCtx]; + if (changeKey) + *data = [changeKey asBinaryInMemCtx: memCtx]; + else + { + [self warnWithFormat: @"No change key for %@ in folder %@", + nameInContainer, + [parentFolder url] + ]; + rc = MAPISTORE_ERR_NOT_FOUND; + } } return rc; From 9554e92c8bbd9b1d18a092611a5456340df988ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20P=C3=A9rez-Aradros=20Herce?= Date: Fri, 20 Nov 2015 23:47:50 +0100 Subject: [PATCH 20/50] Adapt to new safe_ldb_search function --- OpenChange/MAPIStoreAppointmentWrapper.m | 6 +++--- OpenChange/MAPIStoreFolder.m | 5 ++--- OpenChange/MAPIStoreMailMessage.m | 8 ++------ OpenChange/MAPIStoreMailVolatileMessage.m | 9 +++------ OpenChange/MAPIStorePermissionsTable.m | 2 +- OpenChange/MAPIStoreSamDBUtils.h | 6 +++--- OpenChange/MAPIStoreSamDBUtils.m | 19 ++++++++++++------- 7 files changed, 26 insertions(+), 29 deletions(-) diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index 4b96a3515..c8c6560fb 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -266,7 +266,7 @@ static NSCharacterSet *hexCharacterSet = nil; { username = [contactInfos objectForKey: @"sAMAccountName"]; recipient->username = [username asUnicodeInMemCtx: msgData]; - entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username); + entryId = MAPIStoreInternalEntryId (connInfo, username); } else { @@ -367,7 +367,7 @@ static NSCharacterSet *hexCharacterSet = nil; { username = [contactInfos objectForKey: @"sAMAccountName"]; recipient->username = [username asUnicodeInMemCtx: msgData]; - entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username); + entryId = MAPIStoreInternalEntryId (connInfo, username); } else { @@ -932,7 +932,7 @@ static NSCharacterSet *hexCharacterSet = nil; if (contactInfos) { username = [contactInfos objectForKey: @"sAMAccountName"]; - entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username); + entryId = MAPIStoreInternalEntryId (connInfo, username); } else entryId = MAPIStoreExternalEntryId (cn, email); diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 735c04cd6..c111f2305 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -1549,7 +1549,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe struct Binary_r bin32; struct AddressBookEntryId *entryId; NSString *username; - struct ldb_context *samCtx; if (bin && bin->cb) { @@ -1559,8 +1558,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe entryId = get_AddressBookEntryId (NULL, &bin32); if (entryId) { - samCtx = [[self context] connectionInfo]->sam_ctx; - username = MAPIStoreSamDBUserAttribute (samCtx, @"legacyExchangeDN", + username = MAPIStoreSamDBUserAttribute ([[self context] connectionInfo], + @"legacyExchangeDN", [NSString stringWithUTF8String: entryId->X500DN], @"sAMAccountName"); } diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 5de68fcdc..235ca5698 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -811,7 +811,6 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) NSDictionary *contactInfos; NGMailAddress *ngAddress; NSData *entryId; - struct ldb_context *samCtx; int rc; if (fullMail) @@ -834,8 +833,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (contactInfos) { username = [contactInfos objectForKey: @"sAMAccountName"]; - samCtx = [[self context] connectionInfo]->sam_ctx; - entryId = MAPIStoreInternalEntryId (samCtx, username); + entryId = MAPIStoreInternalEntryId([[self context] connectionInfo], username); } else entryId = MAPIStoreExternalEntryId (cn, email); @@ -1478,11 +1476,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) NSData *entryId; NSDictionary *contactInfos; SOGoUserManager *mgr; - struct ldb_context *samCtx; struct mapistore_message *msgData; struct mapistore_message_recipient *recipient; - samCtx = [[self context] connectionInfo]->sam_ctx; [super getMessageData: &msgData inMemCtx: memCtx]; if (!headerSetup) @@ -1536,7 +1532,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) { username = [contactInfos objectForKey: @"sAMAccountName"]; recipient->username = [username asUnicodeInMemCtx: msgData]; - entryId = MAPIStoreInternalEntryId (samCtx, username); + entryId = MAPIStoreInternalEntryId ([[self context] connectionInfo], username); } else { diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index d6154c8e8..537a88854 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -338,13 +338,10 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; NSData *entryId; NSDictionary *allRecipients, *dict, *contactInfos; SOGoUserManager *mgr; - struct ldb_context *samCtx; struct mapistore_message *msgData; struct mapistore_message_recipient *recipient; enum ulRecipClass type; - samCtx = [[self context] connectionInfo]->sam_ctx; - // [super getMessageData: &msgData inMemCtx: memCtx]; msgData = talloc_zero (memCtx, struct mapistore_message); @@ -389,7 +386,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; { username = [contactInfos objectForKey: @"sAMAccountName"]; recipient->username = [username asUnicodeInMemCtx: msgData]; - entryId = MAPIStoreInternalEntryId (samCtx, username); + entryId = MAPIStoreInternalEntryId ([[self context] connectionInfo], username); } else { @@ -700,7 +697,7 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, hasSuffix: @"08002b2fe182"]) { /* TODO: better way to distinguish local and other ones */ - username = MAPIStoreSamDBUserAttribute (connInfo->sam_ctx, @"legacyExchangeDN", + username = MAPIStoreSamDBUserAttribute (connInfo, @"legacyExchangeDN", [NSString stringWithUTF8String: addrBookEntryId->X500DN], @"sAMAccountName"); if (username) { @@ -1008,7 +1005,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS - (NGMimeMessage *) _generateMessageWithBcc: (BOOL) withBcc { NSString *contentType; - NGMimeMessage *message; + NGMimeMessage *message; NGMutableHashMap *headers; id messageBody; diff --git a/OpenChange/MAPIStorePermissionsTable.m b/OpenChange/MAPIStorePermissionsTable.m index 3dba53d79..76896836f 100644 --- a/OpenChange/MAPIStorePermissionsTable.m +++ b/OpenChange/MAPIStorePermissionsTable.m @@ -103,7 +103,7 @@ else { connInfo = [(MAPIStoreContext *) [container context] connectionInfo]; - entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, userId); + entryId = MAPIStoreInternalEntryId (connInfo, userId); } *data = [entryId asBinaryInMemCtx: memCtx]; diff --git a/OpenChange/MAPIStoreSamDBUtils.h b/OpenChange/MAPIStoreSamDBUtils.h index 25ee5e4eb..84825332b 100644 --- a/OpenChange/MAPIStoreSamDBUtils.h +++ b/OpenChange/MAPIStoreSamDBUtils.h @@ -25,13 +25,13 @@ @class NSString; -struct ldb_context; +#include -NSString *MAPIStoreSamDBUserAttribute (struct ldb_context *samCtx, +NSString *MAPIStoreSamDBUserAttribute (struct mapistore_connection_info *connInfo, NSString *userKey, NSString *value, NSString *attributeName); -NSData *MAPIStoreInternalEntryId (struct ldb_context *, NSString *username); +NSData *MAPIStoreInternalEntryId (struct mapistore_connection_info *connInfo, NSString *username); NSData *MAPIStoreExternalEntryId (NSString *cn, NSString *email); #endif /* MAPISTORESAMDBUTILS_H */ diff --git a/OpenChange/MAPIStoreSamDBUtils.m b/OpenChange/MAPIStoreSamDBUtils.m index c210deb64..4b0db76ae 100644 --- a/OpenChange/MAPIStoreSamDBUtils.m +++ b/OpenChange/MAPIStoreSamDBUtils.m @@ -24,13 +24,17 @@ #import #include #include +#include #import "NSData+MAPIStore.h" #import "MAPIStoreSamDBUtils.h" + + + NSString * -MAPIStoreSamDBUserAttribute (struct ldb_context *samCtx, +MAPIStoreSamDBUserAttribute (struct mapistore_connection_info *connInfo, NSString *userKey, NSString *value, NSString *attributeName) @@ -48,10 +52,11 @@ MAPIStoreSamDBUserAttribute (struct ldb_context *samCtx, attrs[0] = [attributeName UTF8String]; searchFormat = [NSString stringWithFormat: @"(&(objectClass=user)(%@=%%s))", userKey]; - ret = ldb_search (samCtx, memCtx, &res, ldb_get_default_basedn(samCtx), - LDB_SCOPE_SUBTREE, attrs, - [searchFormat UTF8String], - [value UTF8String]); + ret = safe_ldb_search(&connInfo->sam_ctx, memCtx, &res, + ldb_get_default_basedn(connInfo->sam_ctx), + LDB_SCOPE_SUBTREE, attrs, + [searchFormat UTF8String], + [value UTF8String]); if (ret == LDB_SUCCESS && res->count == 1) { result = ldb_msg_find_attr_as_string (res->msgs[0], attrs[0], NULL); @@ -65,7 +70,7 @@ MAPIStoreSamDBUserAttribute (struct ldb_context *samCtx, } NSData * -MAPIStoreInternalEntryId (struct ldb_context *samCtx, NSString *username) +MAPIStoreInternalEntryId (struct mapistore_connection_info *connInfo, NSString *username) { static const uint8_t const providerUid[] = { 0xdc, 0xa7, 0x40, 0xc8, 0xc0, 0x42, 0x10, 0x1a, @@ -82,7 +87,7 @@ MAPIStoreInternalEntryId (struct ldb_context *samCtx, NSString *username) type: 32 X500DN: variable */ - legacyDN = MAPIStoreSamDBUserAttribute (samCtx, @"sAMAccountName", username, + legacyDN = MAPIStoreSamDBUserAttribute (connInfo, @"sAMAccountName", username, @"legacyExchangeDN"); if (legacyDN) { From fa4d3a0e20a64a005b1114e20c1e81083374c1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Mon, 9 Nov 2015 07:59:13 +0100 Subject: [PATCH 21/50] Reimplementation of [RTFHandler parseFontTable] --- OpenChange/RTFHandler.m | 280 ++++++++++++++++++++++++++++++---------- 1 file changed, 214 insertions(+), 66 deletions(-) diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index 0df3f1fd3..f02411884 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -29,6 +29,8 @@ #define ADVANCE_N(N) _bytes += (N); _current_pos += (N); #define REWIND _bytes--; _current_pos--; +#define DEFAULT_CHARSET 1 +#define FONTNAME_LEN_MAX 100 // // Charset definitions. See http://msdn.microsoft.com/en-us/goglobal/bb964654 for all details. @@ -282,6 +284,7 @@ const unsigned short ansicpg874[256] = { { } + return self; } @@ -332,6 +335,23 @@ const unsigned short ansicpg874[256] = { return NSMapGet(fontInfos, key); } +- (NSString *) description +{ + NSMutableString *description; + NSEnumerator *enumerator; + RTFFontInfo *fontInfo; + + description = [NSMutableString stringWithFormat: @"Number of fonts: %u\n", [fontInfos count]]; + enumerator = [fontInfos objectEnumerator]; + while ((fontInfo = [enumerator nextObject])) + { + [description appendString: [fontInfo description]]; + [description appendString: @"\n"]; + } + + return description; +} + @end // @@ -455,6 +475,87 @@ const unsigned short ansicpg874[256] = { return start+1; } + +- (const char *) parseControlWordAndSetLenIn: (unsigned int *) len + setHasIntArgumentIn: (BOOL *) hasArg + setIntArgumentIn: (int *) arg +{ + const char *start; + const char *end = NULL; + const char *startArg = NULL; + const char *endArg = NULL; + + ADVANCE; + start = _bytes; + + /* + A control word is defined by: + + \ + */ + while (isalpha(*_bytes)) + { + end = _bytes; + ADVANCE; + } + + if (end == NULL) + { + return NULL; + } + + /* + The can be one of the following: + + - A space. This serves only to delimit a control word and is + ignored in subsequent processing. + + - A numeric digit or an ASCII minus sign (-), which indicates + that a numeric parameter is associated with the control word. + Only this case requires to include it in the control word. + + - Any character other than a letter or a digit + */ + + if (*_bytes == '-' || isdigit(*_bytes)) + { + startArg = _bytes; + endArg = _bytes; + ADVANCE; + while (isdigit(*_bytes)) + { + endArg = _bytes; + ADVANCE; + } + } + + *hasArg = NO; + *arg = 0; + if (startArg) + { + NSString *s; + unsigned int argLength = endArg - startArg + 1; + // the next guard is to protect against a single '-' + if (argLength > 1 || (*startArg != '-')) + { + s = [[NSString alloc] initWithBytesNoCopy: (void *) startArg + length: argLength + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + [s autorelease]; + *hasArg = YES; + *arg = [s intValue]; // Warning: it does not detect conversion errors + } + } + + + /* In other cases, the delimiting character terminates the control + word and is not part of the control word. */ + + + *len = end - start + 1; + return start; +} // // {\colortbl\red0\green0\blue0;\red128\green0\blue0;\red255\green0\blue0;} @@ -531,107 +632,154 @@ const unsigned short ansicpg874[256] = { // - (RTFFontTable *) parseFontTable { - NSMutableString *fontName; RTFFontTable *fontTable; RTFFontInfo *fontInfo; - unsigned int count; + unsigned int level; fontTable = [[[RTFFontTable alloc] init] autorelease]; - fontName = nil; fontInfo = nil; - count = 0; + level = 0; do { if (*_bytes == '{') { - if (fontTable) + if (fontTable && level == 1) { fontInfo = [[[RTFFontInfo alloc] init] autorelease]; - fontName = [[[NSMutableString alloc] init] autorelease]; } ADVANCE; - count++; + level++; } else if (*_bytes == '}') { - if (fontTable) //&& ![NSAllMapTableValues(fontTable->fontInfos) containsObject: fontInfo]) + if (fontTable && level == 2) //&& ![NSAllMapTableValues(fontTable->fontInfos) containsObject: fontInfo]) { - ASSIGN(fontInfo->name, fontName); [fontTable addFontInfo: fontInfo atIndex: fontInfo->index]; } ADVANCE; - count--; + level--; } else if (*_bytes == '\\') { const char *cw; unsigned int len; - NSString *s; + BOOL hasArg; + int arg; - cw = [self parseControlWord: &len]; - - // Skip our control word - if (strncmp((const char*)cw, "fonttbl", len) == 0) + cw = [self parseControlWordAndSetLenIn: &len + setHasIntArgumentIn: &hasArg + setIntArgumentIn: &arg]; + if (level != 2) + continue; + else if (cw == NULL) continue; - // We must at least parse - s = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 - length: len-1 - encoding: NSASCIIStringEncoding - freeWhenDone: NO]; - [s autorelease]; - - // If we got a fontnum, let's parse all three fields at once) - if (isdigit(*(cw+1))) + if (len == 1) { - fontInfo->index = [s intValue]; - - // We now parse - cw = [self parseControlWord: &len]; - if (len == 0) // Possibly parsing a space - cw = [self parseControlWord: &len]; - - fontInfo->family = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 - length: len-1 - encoding: NSASCIIStringEncoding - freeWhenDone: NO]; - - cw = [self parseControlWord: &len]; - if (len == 0) // Possibly parsing a space - cw = [self parseControlWord: &len]; - - fontInfo->charset = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 - length: len-1 - encoding: NSASCIIStringEncoding - freeWhenDone: NO]; - - // We now skip everything until we find our final group closer ('}') - int cc = 1; - - do + if (strncmp((const char*) cw, "f", len) == 0) { - if (*_bytes == '{') - cc++; - if (*_bytes == '}') - cc--; - - ADVANCE; + if (hasArg) + fontInfo->index = arg; } - while (cc != 0); - - // move back our buffer; - REWIND; + } - } - else + else if (len == 4) + { + if (strncmp((const char*) cw, "fnil", len) == 0) + { + fontInfo->family = @"nil"; + } + else if (strncmp((const char*) cw, "fprq", len) == 0) + { + if (hasArg) + fontInfo->pitch = arg; + } + } + else if (len == 5) + { + if (strncmp((const char*) cw, "fbidi", len) == 0) + { + fontInfo->family = @"bidi"; + } + else if (strncmp((const char*) cw, "ftech", len) == 0) + { + fontInfo->family = @"tech"; + } + } + else if (len == 6) + { + if (strncmp((const char*) cw, "froman", len) == 0) + { + fontInfo->family = @"roman"; + } + else if (strncmp((const char*) cw, "fswiss", len) == 0) + { + fontInfo->family = @"swiss"; + } + else if (strncmp((const char*) cw, "fdecor", len) == 0) + { + fontInfo->family = @"decor"; + } + } + else if (len == 7) + { + if (strncmp((const char*) cw, "fmodern", len) == 0) + { + fontInfo->family = @"modern"; + } + } + else if (len == 8) + { + if (strncmp((const char* ) cw, "fcharset", len) == 0) + { + if (hasArg) + fontInfo->charset = [[NSString alloc] initWithFormat: @"%i", arg]; + } + else if (strncmp((const char*) cw, "fscript", len) == 0) + { + fontInfo->family = @"fscript"; + } + } + } + else // no char { - if (isalnum(*_bytes)) - [fontName appendFormat: @"%c", *_bytes]; + if (level == 2 && isalnum(*_bytes)) + { + // we assume this is the fontname + unsigned int fontnameLen; + const char *delim = strpbrk(_bytes, ";{}\\"); + if (delim == NULL) + { + // no delimiter found, we skip to next characters + ADVANCE; + continue; + } + fontnameLen = delim - _bytes; + // only valid if the delimiter is a correct ';' + if (*delim == ';') + { + // there is no explicit limit length but we took 100 + // as protection + if (delim && fontnameLen <= FONTNAME_LEN_MAX) + { + fontInfo->name = [[NSString alloc] initWithBytesNoCopy: (char *) _bytes + length: fontnameLen + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + ADVANCE_N(fontnameLen); + } + } + else { + // advance just before the special character + ADVANCE_N(fontnameLen - 1); + } + } ADVANCE; - } - } while (count != 0); + } + + } while (level > 0); return fontTable; } @@ -791,7 +939,7 @@ const unsigned short ansicpg874[256] = { REWIND; } else if (strncmp(cw, "stylesheet", 10) == 0) - { + { _bytes = cw-2; _current_pos -= 12; // Length: {\stylesheet [self parseStyleSheet]; From 01dcf3d6c8835f1860b429bab8bea4c5ee77ad22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Tue, 10 Nov 2015 12:50:22 +0100 Subject: [PATCH 22/50] Fixed activation of characters sets in [RTFHandler parse] --- OpenChange/RTFHandler.h | 3 +- OpenChange/RTFHandler.m | 124 +++++++++++++++++++++++++++++++++------- 2 files changed, 104 insertions(+), 23 deletions(-) diff --git a/OpenChange/RTFHandler.h b/OpenChange/RTFHandler.h index 52567cc29..b0ea0b684 100644 --- a/OpenChange/RTFHandler.h +++ b/OpenChange/RTFHandler.h @@ -67,6 +67,7 @@ int font_index; int color_index; int start_pos; + const unsigned short *charset; } @end @@ -77,7 +78,7 @@ { @public NSString *family; - NSString *charset; + unsigned char charset; NSString *name; unsigned int pitch; unsigned int index; diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index f02411884..bd2051298 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -231,7 +231,7 @@ const unsigned short ansicpg874[256] = { - (void) dealloc { - RELEASE(a); + [a release]; [super dealloc]; } @@ -246,7 +246,7 @@ const unsigned short ansicpg874[256] = { if ([a count]) { - o = AUTORELEASE([[a lastObject] retain]); + o = [[[a lastObject] retain] autorelease]; [a removeLastObject]; } @@ -259,7 +259,7 @@ const unsigned short ansicpg874[256] = { if ([a count]) { - o = AUTORELEASE([[a lastObject] retain]); + o = [[[a lastObject] retain] autorelease]; } return o; @@ -285,17 +285,27 @@ const unsigned short ansicpg874[256] = { } + charset = DEFAULT_CHARSET; return self; } - (void) dealloc { - RELEASE(family); - RELEASE(charset); - RELEASE(name); + [family release]; + [name release]; [super dealloc]; } +- (NSString *) description +{ + NSString *description; + description = [NSString stringWithFormat: + @"%u name=%@ family=%@ charset=%u pitch=%u", + index, name, family, charset, pitch + ]; + return description; +} + @end // @@ -377,7 +387,7 @@ const unsigned short ansicpg874[256] = { - (void) dealloc { - RELEASE(colorDefs); + [colorDefs release]; [super dealloc]; } @@ -408,16 +418,55 @@ const unsigned short ansicpg874[256] = { _current_pos = 0; _charsets = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 10); + // 238 — Eastern European - cpg1250 NSMapInsert(_charsets, @"ansicpg1250", ansicpg1250); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 238], ansicpg1250); + // 204 — Russian - cpg1251 NSMapInsert(_charsets, @"ansicpg1251", ansicpg1251); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 204], ansicpg1251); + // 0 - Latin 1 - cpg1252 - also know as ANSI + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 0], ansicpg1252); NSMapInsert(_charsets, @"ansicpg1252", ansicpg1252); + // 161 - Greek cpg1253 NSMapInsert(_charsets, @"ansicpg1253", ansicpg1253); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 161], ansicpg1253); + // 162 — Turkish - cpg1254 NSMapInsert(_charsets, @"ansicpg1254", ansicpg1254); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 162], ansicpg1254); + // 177 — Hebrew Traditional - cpg1255 + // also 181 - Hebrew user NSMapInsert(_charsets, @"ansicpg1255", ansicpg1255); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 177], ansicpg1255); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 181], ansicpg1255); + // 178 — Arabic - cpg1256 + // also 179 - Arabic traditional + // also 180 - Arabic User NSMapInsert(_charsets, @"ansicpg1256", ansicpg1256); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 178], ansicpg1256); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 179], ansicpg1256); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 180], ansicpg1256); + // 186 — Baltic - pg 1257 NSMapInsert(_charsets, @"ansicpg1257", ansicpg1257); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 186], ansicpg1257); + // 163 — Vietnamese - pg1259 NSMapInsert(_charsets, @"ansicpg1258", ansicpg1258); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 163], ansicpg1258); + // 222 — Thai - cpg874 NSMapInsert(_charsets, @"ansicpg874", ansicpg874); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 222], ansicpg874); + + // TODO: check differences between traditional/user/no-qualified for Arabic and Hebrew + // TODO: missing codepage for the following codes: + // 2 — Symbol + // 3 — Invalid + // 77 — Mac + // 128 — Shift Jis + // 129 — Hangul + // 130 — Johab + // 134 — GB2312 + // 136 — Big5 + // 254 — PC 437 + // 255 — OEM } return self; @@ -426,7 +475,7 @@ const unsigned short ansicpg874[256] = { - (void) dealloc { NSFreeMapTable(_charsets); - RELEASE(_data); + [_data release]; [super dealloc]; } @@ -734,8 +783,11 @@ const unsigned short ansicpg874[256] = { { if (strncmp((const char* ) cw, "fcharset", len) == 0) { - if (hasArg) - fontInfo->charset = [[NSString alloc] initWithFormat: @"%i", arg]; + if (hasArg) + { + fontInfo->charset = arg; + } + } else if (strncmp((const char*) cw, "fscript", len) == 0) { @@ -858,13 +910,13 @@ const unsigned short ansicpg874[256] = { RTFFontTable *fontTable; RTFStack *stack; - unsigned short *charset; + const unsigned short *default_charset; char c; stack = [[RTFStack alloc] init]; fontTable = nil; colorTable = nil; - charset = NULL; + default_charset = ansicpg1252; formattingOptions = nil; _html = [[NSMutableData alloc] init]; @@ -873,8 +925,9 @@ const unsigned short ansicpg874[256] = { // Check if we got RTF data + // this does not allow \s\n before '}' neither newline before control command if (_len > 4 && strncmp((const char*)_bytes, "{\\rtf", 4) != 0) - return NO; + return nil; while (_current_pos < _len) { @@ -887,7 +940,7 @@ const unsigned short ansicpg874[256] = { const char *cw; NSString *s; - if (*(_bytes+1) == '\'' && charset) + if (*(_bytes+1) == '\'') { // A hexadecimal value, based on the specified character set (may be used to identify 8-bit values). NSString *s; @@ -895,6 +948,14 @@ const unsigned short ansicpg874[256] = { const char *b1, *b2; unsigned short index; + + const unsigned short * active_charset; + + if (formattingOptions && formattingOptions->charset) + active_charset = formattingOptions->charset; + else + active_charset = default_charset; + ADVANCE; ADVANCE; @@ -905,7 +966,7 @@ const unsigned short ansicpg874[256] = { index = (isdigit(*b1) ? *b1 - 48 : toupper(*b1) - 55) * 16; index += (isdigit(*b2) ? *b2 - 48 : toupper(*b2) - 55); - s = [NSString stringWithCharacters: &(charset[index]) length: 1]; + s = [NSString stringWithCharacters: &(active_charset[index]) length: 1]; d = [s dataUsingEncoding: NSUTF8StringEncoding]; [_html appendData: d]; continue; @@ -924,9 +985,10 @@ const unsigned short ansicpg874[256] = { freeWhenDone: NO]; [s autorelease]; + // todo: This keyword should be emitted in the RTF header section right after the \ansi, \mac, \pc or \pca keyword. if (strncmp(cw, "ansicpg", 7) == 0) { - charset = NSMapGet(_charsets, s); + default_charset = NSMapGet(_charsets, s); } else if (strncmp(cw, "fonttbl", 7) == 0) { @@ -1055,6 +1117,18 @@ const unsigned short ansicpg874[256] = { v = calloc(7, sizeof(char)); sprintf(v, ""); } + + if (fontInfo && fontInfo->charset) + { + if (fontInfo->charset == 1) + /* charset 1 is default charset */ + formattingOptions->charset = NULL; + else { + NSNumber *key = [NSNumber numberWithUnsignedChar: fontInfo->charset]; + formattingOptions->charset = NSMapGet(_charsets, key); + } + } + [_html appendBytes: v length: strlen(v)]; free(v); } @@ -1088,6 +1162,7 @@ const unsigned short ansicpg874[256] = { } else if ([s hasPrefix: @"u"] && [s length] > 1 && isdigit([s characterAtIndex: 1])) { + // XXX TPFOX u argumrnt can be negative NSData *d; unichar ch; @@ -1109,7 +1184,7 @@ const unsigned short ansicpg874[256] = { } // If a space delimits the control word, the space does not appear in the document. - // Any characters following the delimiter, including spaces, will appear in the document. + // Any characters following the delimiter, including spaces, will appear in the document. (except newline!) if (*_bytes == ' ') { ADVANCE; @@ -1126,6 +1201,7 @@ const unsigned short ansicpg874[256] = { formattingOptions->font_index = -1; formattingOptions->color_index = -1; formattingOptions->start_pos = [_html length]; + formattingOptions->charset = default_charset; [stack push: formattingOptions]; ADVANCE; } @@ -1172,17 +1248,21 @@ const unsigned short ansicpg874[256] = { } else { - // We avoid appending NULL bytes - if (*_bytes) - [_html appendBytes: _bytes length: 1]; + /* XXXX TODO add special stick together chars? */ + // We avoid appending NULL bytes or endlines + if (*_bytes && (*_bytes != '\n')) + { + /* end lines are not part of rtf */ + [_html appendBytes: _bytes length: 1]; + } ADVANCE; } } [_html appendBytes: "" length: 14]; - RELEASE(stack); - return AUTORELEASE(_html); + [stack release]; + return [_html autorelease]; } @end From 8e3e4a5445ed8f9e3a61914a7536d32d5f3256b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Thu, 12 Nov 2015 15:39:04 +0100 Subject: [PATCH 23/50] Added RTF escapes parsing --- OpenChange/RTFHandler.m | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index bd2051298..1add58bcc 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -939,11 +939,11 @@ const unsigned short ansicpg874[256] = { unsigned int len; const char *cw; NSString *s; + char nextByte = *(_bytes+1); - if (*(_bytes+1) == '\'') + if (nextByte == '\'') { // A hexadecimal value, based on the specified character set (may be used to identify 8-bit values). - NSString *s; NSData *d; const char *b1, *b2; @@ -971,11 +971,31 @@ const unsigned short ansicpg874[256] = { [_html appendData: d]; continue; } - else if (*(_bytes+1) == '*') + else if (nextByte == '*') { [self parseIgnoringEverything]; continue; } + else if (!isalpha(nextByte)) + { + // escape + character + ADVANCE_N(2); + // check for special escapes for the no-implemented features + // for control of word breaking + if (nextByte == '~') + // no breaking space + nextByte = ' '; + else if (nextByte == '-') + // optional hyphen; we skip it + continue; + else if (nextByte == '_') + // no breaking hyphen, treat it as a normal hyphen + nextByte = '-'; + + [_html appendBytes: &nextByte length: 1]; + continue; + } + cw = [self parseControlWord: &len]; @@ -1248,7 +1268,6 @@ const unsigned short ansicpg874[256] = { } else { - /* XXXX TODO add special stick together chars? */ // We avoid appending NULL bytes or endlines if (*_bytes && (*_bytes != '\n')) { From 2518b37e322ba93879703d9f8aaa6b2fe960211a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Thu, 12 Nov 2015 16:17:31 +0100 Subject: [PATCH 24/50] Fixed RTF support for unicode characters greater than 32767 --- OpenChange/RTFHandler.m | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index 1add58bcc..7cd7ab404 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -1180,13 +1180,19 @@ const unsigned short ansicpg874[256] = { [_html appendBytes: "" length: 9]; formattingOptions->strikethrough = NO; } - else if ([s hasPrefix: @"u"] && [s length] > 1 && isdigit([s characterAtIndex: 1])) + else if ([s hasPrefix: @"u"] && [s length] > 1 && + (isdigit([s characterAtIndex: 1]) || '-' == [s characterAtIndex: 1])) { - // XXX TPFOX u argumrnt can be negative NSData *d; unichar ch; + int arg; + + arg = [[s substringFromIndex: 1] intValue]; + if (arg < 0) + // a negative value means a value greater than 32767 + arg = 32767 - arg; - ch = (unichar)[[s substringFromIndex: 1] intValue]; + ch = (unichar) arg; s = [NSString stringWithCharacters: &ch length: 1]; d = [s dataUsingEncoding: NSUTF8StringEncoding]; [_html appendData: d]; From 3155bd01726c81110ab82b7de72b55ac8c59c38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Thu, 12 Nov 2015 19:04:31 +0100 Subject: [PATCH 25/50] Fixed bug which created unnecesary font tags --- OpenChange/RTFHandler.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index 7cd7ab404..5ba1c0721 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -1093,7 +1093,7 @@ const unsigned short ansicpg874[256] = { { // ignore } - else if ([s hasPrefix: @"f"] && [s length] > 1) + else if ([s hasPrefix: @"f"] && [s length] > 1 && isdigit([s characterAtIndex: 1])) { RTFFontInfo *fontInfo; int font_index; From 2f63542e07b1b71efbaba1ad5d924b3a52a3fc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Fri, 13 Nov 2015 12:53:02 +0100 Subject: [PATCH 26/50] Fixed problem with ascii chars that had different unicode value Also cleaned a bit the parse loop --- OpenChange/RTFHandler.m | 79 +++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index 5ba1c0721..e170cccf6 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -31,7 +31,7 @@ #define DEFAULT_CHARSET 1 #define FONTNAME_LEN_MAX 100 - +#define UTF8_FIRST_BYTE_LAST_CODEPOINT 0x7F // // Charset definitions. See http://msdn.microsoft.com/en-us/goglobal/bb964654 for all details. // @@ -911,8 +911,13 @@ const unsigned short ansicpg874[256] = { RTFStack *stack; const unsigned short *default_charset; - char c; - + + // convenience variables for parsing + unsigned char c; + NSData *d; + NSString *s; + unichar uch; + stack = [[RTFStack alloc] init]; fontTable = nil; colorTable = nil; @@ -921,7 +926,6 @@ const unsigned short ansicpg874[256] = { _html = [[NSMutableData alloc] init]; [_html appendBytes: "" length: 34]; - // Check if we got RTF data @@ -938,14 +942,11 @@ const unsigned short ansicpg874[256] = { { unsigned int len; const char *cw; - NSString *s; char nextByte = *(_bytes+1); if (nextByte == '\'') { // A hexadecimal value, based on the specified character set (may be used to identify 8-bit values). - NSData *d; - const char *b1, *b2; unsigned short index; @@ -1073,26 +1074,26 @@ const unsigned short ansicpg874[256] = { [_html appendBytes: v length: strlen(v)]; free(v); } - else if ([s hasPrefix: @"fcs"]) - { - // ignore - } - else if ([s hasPrefix: @"fs"]) - { - // ignore - } - else if ([s hasPrefix: @"fbidis"]) - { - // ignore - } - else if ([s hasPrefix: @"fromhtml"]) - { - // ignore - } - else if ([s hasPrefix: @"fromtext"]) - { - // ignore - } + // else if ([s hasPrefix: @"fcs"]) + // { + // // ignore + // } + // else if ([s hasPrefix: @"fs"]) + // { + // // ignore + // } + // else if ([s hasPrefix: @"fbidis"]) + // { + // // ignore + // } + // else if ([s hasPrefix: @"fromhtml"]) + // { + // // ignore + // } + // else if ([s hasPrefix: @"fromtext"]) + // { + // // ignore + // } else if ([s hasPrefix: @"f"] && [s length] > 1 && isdigit([s characterAtIndex: 1])) { RTFFontInfo *fontInfo; @@ -1183,17 +1184,14 @@ const unsigned short ansicpg874[256] = { else if ([s hasPrefix: @"u"] && [s length] > 1 && (isdigit([s characterAtIndex: 1]) || '-' == [s characterAtIndex: 1])) { - NSData *d; - unichar ch; int arg; - arg = [[s substringFromIndex: 1] intValue]; if (arg < 0) // a negative value means a value greater than 32767 arg = 32767 - arg; - ch = (unichar) arg; - s = [NSString stringWithCharacters: &ch length: 1]; + uch = (unichar) arg; + s = [NSString stringWithCharacters: &uch length: 1]; d = [s dataUsingEncoding: NSUTF8StringEncoding]; [_html appendData: d]; } @@ -1274,11 +1272,22 @@ const unsigned short ansicpg874[256] = { } else { + c = *_bytes; // We avoid appending NULL bytes or endlines - if (*_bytes && (*_bytes != '\n')) + if (c && (c != '\n')) { - /* end lines are not part of rtf */ - [_html appendBytes: _bytes length: 1]; + if (c <= UTF8_FIRST_BYTE_LAST_CODEPOINT) + { + // in this case utf8 and ascii encoding are the same + [_html appendBytes: &c length: 1]; + } + else + { + uch = c; + s = [NSString stringWithCharacters: &uch length: 1]; + d = [s dataUsingEncoding: NSUTF8StringEncoding]; + [_html appendData: d]; + } } ADVANCE; } From 94d4da6e1ef1846da93d36fd7cc4c2c09284f0fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Fri, 13 Nov 2015 15:53:36 +0100 Subject: [PATCH 27/50] Reimplemented [RTFHandler parse] to have an unambiguous decoding of control words [RTFHandler parseFontTable] reimplemented on the same line. --- OpenChange/RTFHandler.m | 746 ++++++++++++++++++++++++---------------- 1 file changed, 452 insertions(+), 294 deletions(-) diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index e170cccf6..b18754e51 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -25,9 +25,9 @@ // // Useful macros // -#define ADVANCE _bytes++; _current_pos++; -#define ADVANCE_N(N) _bytes += (N); _current_pos += (N); -#define REWIND _bytes--; _current_pos--; +#define ADVANCE self->_bytes++; self->_current_pos++; +#define ADVANCE_N(N) self->_bytes += (N); self->_current_pos += (N); +#define REWIND self->_bytes--; self->_current_pos--; #define DEFAULT_CHARSET 1 #define FONTNAME_LEN_MAX 100 @@ -408,16 +408,47 @@ const unsigned short ansicpg874[256] = { // @implementation RTFHandler -- (id) initWithData: (NSData *) theData +static NSMapTable *_charsets = nil; +static NSMapTable *_cws = nil; +typedef enum { + CW_UNKNOWN = 0, + CW_ANSICPG, + CW_B, + CW_CF, + CW_COLORTBL, + CW_F, + CW_FONTTBL, + CW_I, + CW_PAR, + CW_PICT, + CW_SOFTLINE, + CW_STRIKE, + CW_STYLESHEET, + CW_TAB, + CW_U, + CW_UL, + CW_ULNONE +} commandWordId; + +static NSMapTable *_fontCws = nil; +typedef enum { + FONTCW_UNKNOWN = 0, + FONTCW_F, + FONTCW_FBIDI, + FONTCW_FCHARSET, + FONTCW_FDECOR, + FONTCW_FMODERN, + FONTCW_FNIL, + FONTCW_FPRQ, + FONTCW_FROMAN, + FONTCW_FSCRIPT, + FONTCW_FSWISS, + FONTCW_FTECH +} fontCommandWordId; + +static void _init_charsets_table() { - if ((self = [super init])) - { - ASSIGN(_data, theData); - _bytes = (char *)[_data bytes]; - _len = [_data length]; - _current_pos = 0; - - _charsets = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 10); + _charsets = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 23); // 238 — Eastern European - cpg1250 NSMapInsert(_charsets, @"ansicpg1250", ansicpg1250); NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 238], ansicpg1250); @@ -467,6 +498,59 @@ const unsigned short ansicpg874[256] = { // 136 — Big5 // 254 — PC 437 // 255 — OEM +} + +static void _init_cws_table() +{ + _cws = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 16); + NSMapInsert(_cws, @"ansicpg", (void *) CW_ANSICPG); + NSMapInsert(_cws, @"b", (void *) CW_B); + NSMapInsert(_cws, @"cf", (void *) CW_CF); + NSMapInsert(_cws, @"colortbl", (void *) CW_COLORTBL); + NSMapInsert(_cws, @"f", (void *) CW_F); + NSMapInsert(_cws, @"fonttbl", (void *) CW_FONTTBL); + NSMapInsert(_cws, @"i", (void *) CW_I); + NSMapInsert(_cws, @"par", (void *) CW_PAR); + NSMapInsert(_cws, @"pict", (void *) CW_PICT); + NSMapInsert(_cws, @"softline", (void *) CW_SOFTLINE); + NSMapInsert(_cws, @"strike", (void *) CW_STRIKE); + NSMapInsert(_cws, @"stylesheet", (void *) CW_STYLESHEET); + NSMapInsert(_cws, @"tab", (void *) CW_TAB); + NSMapInsert(_cws, @"u", (void *) CW_U); + NSMapInsert(_cws, @"ul", (void *) CW_UL); + NSMapInsert(_cws, @"ulnone", (void *) CW_ULNONE); +} + +static void _init_fontCws_table() +{ + _fontCws = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 23); + NSMapInsert(_fontCws, @"f", (void *) FONTCW_F); + NSMapInsert(_fontCws, @"fbidi", (void *) FONTCW_FBIDI); + NSMapInsert(_fontCws, @"fcharset", (void *) FONTCW_FCHARSET); + NSMapInsert(_fontCws, @"fdecor", (void *) FONTCW_FDECOR); + NSMapInsert(_fontCws, @"fmodern", (void *) FONTCW_FMODERN); + NSMapInsert(_fontCws, @"fnil", (void *) FONTCW_FNIL); + NSMapInsert(_fontCws, @"fprq", (void *) FONTCW_FPRQ); + NSMapInsert(_fontCws, @"froman", (void *) FONTCW_FROMAN); + NSMapInsert(_fontCws, @"fscript", (void *) FONTCW_FSCRIPT); + NSMapInsert(_fontCws, @"fswiss", (void *) FONTCW_FSWISS); + NSMapInsert(_fontCws, @"ftech", (void *) FONTCW_FTECH); +} + +- (id) initWithData: (NSData *) theData +{ + if ((self = [super init])) + { + ASSIGN(_data, theData); + _bytes = (char *)[_data bytes]; + _len = [_data length]; + _current_pos = 0; + if (_charsets == nil) + _init_charsets_table(); + if (_cws == nil) + _init_cws_table(); + if (_fontCws == nil) + _init_fontCws_table(); } return self; @@ -600,8 +684,6 @@ const unsigned short ansicpg874[256] = { /* In other cases, the delimiting character terminates the control word and is not part of the control word. */ - - *len = end - start + 1; return start; } @@ -716,6 +798,8 @@ const unsigned short ansicpg874[256] = { unsigned int len; BOOL hasArg; int arg; + NSString *cwKey; + fontCommandWordId cwId; cw = [self parseControlWordAndSetLenIn: &len setHasIntArgumentIn: &hasArg @@ -725,76 +809,57 @@ const unsigned short ansicpg874[256] = { else if (cw == NULL) continue; - if (len == 1) - { - if (strncmp((const char*) cw, "f", len) == 0) - { - if (hasArg) - fontInfo->index = arg; - } - - } - else if (len == 4) - { - if (strncmp((const char*) cw, "fnil", len) == 0) - { - fontInfo->family = @"nil"; - } - else if (strncmp((const char*) cw, "fprq", len) == 0) - { - if (hasArg) - fontInfo->pitch = arg; - } - } - else if (len == 5) - { - if (strncmp((const char*) cw, "fbidi", len) == 0) - { - fontInfo->family = @"bidi"; - } - else if (strncmp((const char*) cw, "ftech", len) == 0) - { - fontInfo->family = @"tech"; - } - } - else if (len == 6) - { - if (strncmp((const char*) cw, "froman", len) == 0) - { - fontInfo->family = @"roman"; - } - else if (strncmp((const char*) cw, "fswiss", len) == 0) - { - fontInfo->family = @"swiss"; - } - else if (strncmp((const char*) cw, "fdecor", len) == 0) - { - fontInfo->family = @"decor"; - } - } - else if (len == 7) - { - if (strncmp((const char*) cw, "fmodern", len) == 0) - { - fontInfo->family = @"modern"; - } - } - else if (len == 8) - { - if (strncmp((const char* ) cw, "fcharset", len) == 0) - { - if (hasArg) - { - fontInfo->charset = arg; - } + cwKey= [[NSString alloc] initWithBytesNoCopy: (void *)cw + length: len + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + [cwKey autorelease]; - } - else if (strncmp((const char*) cw, "fscript", len) == 0) - { - fontInfo->family = @"fscript"; - } + cwId = (fontCommandWordId) NSMapGet(_fontCws, cwKey); + switch (cwId) + { + case FONTCW_F: + if (hasArg) + fontInfo->index = arg; + break; + case FONTCW_FBIDI: + fontInfo->family = @"bidi"; + break; + case FONTCW_FCHARSET: + if (hasArg) + fontInfo->charset = arg; + break; + case FONTCW_FDECOR: + fontInfo->family = @"decor"; + break; + case FONTCW_FMODERN: + fontInfo->family = @"modern"; + break; + case FONTCW_FNIL: + fontInfo->family = @"nil"; + break; + case FONTCW_FPRQ: + if (hasArg) + fontInfo->pitch = arg; + break; + case FONTCW_FROMAN: + fontInfo->family = @"roman"; + break; + case FONTCW_FSCRIPT: + fontInfo->family = @"script"; + break; + case FONTCW_FSWISS: + fontInfo->family = @"swiss"; + break; + case FONTCW_FTECH: + fontInfo->family = @"tech"; + break; + case FONTCW_UNKNOWN: + default: + // do nothing + break; } - } + } else // no char { if (level == 2 && isalnum(*_bytes)) @@ -900,9 +965,238 @@ const unsigned short ansicpg874[256] = { [self parseIgnoringEverything]; } -// -// -// + +// todo: This keyword is only valid in the RTF header section right after the \ansi, \mac, \pc or \pca keyword. +inline static void parseAnsicpg (BOOL hasArg, int arg, const unsigned short **out_default_char) +{ + NSString *key; + const unsigned short *res; + + if (!hasArg) + return; + key = [NSString stringWithFormat: @"anscicpg%i", arg]; + res = NSMapGet(_charsets, key); + if (res) + *out_default_char = res; +} + +inline static void parseB(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions) +{ + if (!formattingOptions) + return; + if (hasArg && arg == 0) + { + [self->_html appendBytes: "" length: 4]; + formattingOptions->bold = NO; + } + else + { + [self->_html appendBytes: "" length: 3]; + formattingOptions->bold = YES; + } +} + +inline static void parseCf(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions, RTFColorTable *colorTable) +{ + RTFColorDef *colorDef; + char *v; + + if (!hasArg) + return; + if (!formattingOptions) + return; + + colorDef = [colorTable colorDefAtIndex: arg]; + if (!colorDef) + return; + + if (formattingOptions->color_index >= 0) + { + [self->_html appendBytes: "" length: 7]; + } + + formattingOptions->color_index = arg; + + v = calloc(23, sizeof(char)); + sprintf(v, "", colorDef->red, colorDef->green, colorDef->blue); + [self->_html appendBytes: v length: strlen(v)]; + free(v); +} + + +inline static void parseColorTableWrapper(RTFHandler *self, RTFColorTable **colorTable) +{ + *colorTable = [self parseColorTable]; +} + +inline static void parseF(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions, RTFFontTable *fontTable) +{ + RTFFontInfo *fontInfo; + + if (!hasArg) + return; + if (!formattingOptions) + return; + + if (formattingOptions->font_index >= 0 && arg != formattingOptions->font_index) + { + [self->_html appendBytes: "" length: 7]; + } + + formattingOptions->font_index = arg; + + fontInfo = [fontTable fontInfoAtIndex: arg]; + char *v = NULL; + if (fontInfo && fontInfo->name) + { + if ([fontInfo->name length] < 128) + { + int tag_size = 15 + [fontInfo->name length]; + v = calloc(tag_size, sizeof(char)); + snprintf(v, tag_size, "", [fontInfo->name UTF8String]); + } + else + { + NSLog(@"RTFHandler: Font %u has %d chars length, parse error? " + "Ignored", arg, [fontInfo->name length]); + v = calloc(7, sizeof(char)); + sprintf(v, ""); + } + } + else + { + // RTF badformed? We don't know about that font (arg index not found). + // Anyhow, we still open the html tag because in the future + // we will close it (e.g. when new font is used). + v = calloc(7, sizeof(char)); + sprintf(v, ""); + } + + if (fontInfo && fontInfo->charset) + { + if (fontInfo->charset == DEFAULT_CHARSET) + /* charset 1 is default charset */ + formattingOptions->charset = NULL; + else { + NSNumber *key = [NSNumber numberWithUnsignedChar: fontInfo->charset]; + formattingOptions->charset = NSMapGet(_charsets, key); + } + } + + [self->_html appendBytes: v length: strlen(v)]; + free(v); +} + +inline static void parseFontTableWrapper(RTFHandler *self, const char * cw, RTFFontTable **fontTable) +{ + // We rewind our buffer so we start at the beginning of {\fonttbl... + self->_bytes = cw-2; + self->_current_pos -= 9; // Length: {\fonttbl + *fontTable = [self parseFontTable]; + + // We go back 1 byte in order to end our section properly ('}' character) + REWIND; +} + +inline static void parseI(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions) +{ + if (!formattingOptions) + return; + if (hasArg && arg == 0) + { + [self->_html appendBytes: "" length: 4]; + formattingOptions->italic = NO; + } + else + { + [self->_html appendBytes: "" length: 3]; + formattingOptions->italic = YES; + } +} + +inline static void parsePar(RTFHandler *self) +{ + [self->_html appendBytes: "
" length: 4]; +} + +inline static void parsePictureWrapper(RTFHandler *self, const char * cw) +{ + self->_bytes = cw-2; + self->_current_pos -= 6; // Length: {\pict + [self parsePicture]; + REWIND; +} + +// same implementation that /par +inline static void parseSoftline(RTFHandler *self) +{ + [self->_html appendBytes: "
" length: 4]; +} + +inline static void parseStrike(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions) +{ + if (!formattingOptions) + return; + if (hasArg && arg == 0) + { + [self->_html appendBytes: "" length: 9]; + formattingOptions->strikethrough = NO; + } + else + { + [self->_html appendBytes: "" length: 8]; + formattingOptions->strikethrough = YES; + } +} + +inline static void parseStyleSheetWrapper(RTFHandler *self, const char * cw) +{ + self->_bytes = cw-2; + self->_current_pos -= 12; // Length: {\stylesheet + [self parseStyleSheet]; + REWIND; +} + +inline static void parseTab(RTFHandler *self) +{ + [self->_html appendBytes: "  " length: 12]; +} + +inline static void parseU(RTFHandler *self, BOOL hasArg, int arg) +{ + unichar uch; + NSString *s; + NSData *d; + + if (!hasArg) + return; + if (arg < 0) + // a negative value means a value greater than 32767 + arg = 32767 - arg; + + uch = (unichar) arg; + s = [NSString stringWithCharacters: &uch length: 1]; + d = [s dataUsingEncoding: NSUTF8StringEncoding]; + [self->_html appendData: d]; +} + +inline static void parseUl(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions) +{ + if (!formattingOptions) + return; + if (hasArg && arg ==0) + { + [self->_html appendBytes: "" length: 4]; + formattingOptions->underline = NO; + } + else + { + [self->_html appendBytes: "" length: 3]; + formattingOptions->underline = YES; + } +} + + - (NSMutableData *) parse { RTFFormattingOptions *formattingOptions; @@ -910,7 +1204,7 @@ const unsigned short ansicpg874[256] = { RTFFontTable *fontTable; RTFStack *stack; - const unsigned short *default_charset; + const unsigned short *defaultCharset; // convenience variables for parsing unsigned char c; @@ -921,7 +1215,7 @@ const unsigned short ansicpg874[256] = { stack = [[RTFStack alloc] init]; fontTable = nil; colorTable = nil; - default_charset = ansicpg1252; + defaultCharset = ansicpg1252; formattingOptions = nil; _html = [[NSMutableData alloc] init]; @@ -942,6 +1236,10 @@ const unsigned short ansicpg874[256] = { { unsigned int len; const char *cw; + BOOL hasArg; + int arg; + NSString *cwKey; + commandWordId cwId; char nextByte = *(_bytes+1); if (nextByte == '\'') @@ -955,7 +1253,7 @@ const unsigned short ansicpg874[256] = { if (formattingOptions && formattingOptions->charset) active_charset = formattingOptions->charset; else - active_charset = default_charset; + active_charset = defaultCharset; ADVANCE; @@ -998,213 +1296,73 @@ const unsigned short ansicpg874[256] = { } - cw = [self parseControlWord: &len]; + cw = [self parseControlWordAndSetLenIn: &len + setHasIntArgumentIn: &hasArg + setIntArgumentIn: &arg]; + if (cw == NULL) + continue; + + cwKey= [[NSString alloc] initWithBytesNoCopy: (void *)cw + length: len + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + [cwKey autorelease]; - s = [[NSString alloc] initWithBytesNoCopy: (void *)cw - length: len - encoding: NSASCIIStringEncoding - freeWhenDone: NO]; - [s autorelease]; - - // todo: This keyword should be emitted in the RTF header section right after the \ansi, \mac, \pc or \pca keyword. - if (strncmp(cw, "ansicpg", 7) == 0) + cwId = (commandWordId) NSMapGet(_cws, cwKey); + switch (cwId) { - default_charset = NSMapGet(_charsets, s); - } - else if (strncmp(cw, "fonttbl", 7) == 0) - { - // We rewind our buffer so we start at the beginning of {\fonttbl... - _bytes = cw-2; - _current_pos -= 9; // Length: {\fonttbl - fontTable = [self parseFontTable]; - - // We go back 1 byte in order to end our section properly ('}' character) - REWIND; - } - else if (strncmp(cw, "stylesheet", 10) == 0) - { - _bytes = cw-2; - _current_pos -= 12; // Length: {\stylesheet - [self parseStyleSheet]; - REWIND; - } - else if (strncmp(cw, "colortbl", 8) == 0) - { - colorTable = [self parseColorTable]; - } - else if (strncmp(cw, "pict", 4) == 0) - { - _bytes = cw-2; - _current_pos -= 6; // Length: {\pict - [self parsePicture]; - REWIND; - } - else if ([s isEqualToString: @"b"] && formattingOptions) - { - [_html appendBytes: "" length: 3]; - formattingOptions->bold = YES; - } - else if ([s isEqualToString: @"b0"] && formattingOptions) - { - [_html appendBytes: "" length: 4]; - formattingOptions->bold = NO; - } - else if ([s hasPrefix: @"cf"] && [s length] > 2) - { - RTFColorDef *colorDef; - int color_index; - char *v; - - if (!formattingOptions) continue; - - color_index = [[s substringFromIndex: 2] intValue]; - colorDef = [colorTable colorDefAtIndex: color_index]; - if (!colorDef) continue; - - if (formattingOptions->color_index >= 0) - { - [_html appendBytes: "
" length: 7]; - } - - formattingOptions->color_index = color_index; - - v = malloc(23*sizeof(char)); - memset(v, 0, 23); - sprintf(v, "", colorDef->red, colorDef->green, colorDef->blue); - [_html appendBytes: v length: strlen(v)]; - free(v); - } - // else if ([s hasPrefix: @"fcs"]) - // { - // // ignore - // } - // else if ([s hasPrefix: @"fs"]) - // { - // // ignore - // } - // else if ([s hasPrefix: @"fbidis"]) - // { - // // ignore - // } - // else if ([s hasPrefix: @"fromhtml"]) - // { - // // ignore - // } - // else if ([s hasPrefix: @"fromtext"]) - // { - // // ignore - // } - else if ([s hasPrefix: @"f"] && [s length] > 1 && isdigit([s characterAtIndex: 1])) - { - RTFFontInfo *fontInfo; - int font_index; - - font_index = [[s substringFromIndex: 1] intValue]; - - if (!formattingOptions) - continue; - - if (formattingOptions->font_index >= 0 && - font_index != formattingOptions->font_index) - { - [_html appendBytes: "" length: 7]; - } - - formattingOptions->font_index = font_index; - - fontInfo = [fontTable fontInfoAtIndex: font_index]; - char *v = NULL; - if (fontInfo && fontInfo->name) - { - if ([fontInfo->name length] < 128) - { - int tag_size = 15 + [fontInfo->name length]; - v = calloc(tag_size, sizeof(char)); - snprintf(v, tag_size, "", [fontInfo->name UTF8String]); - } - else - { - NSLog(@"RTFHandler: Font %u has %d chars length, parse error? " - "Ignored", font_index, [fontInfo->name length]); - v = calloc(7, sizeof(char)); - sprintf(v, ""); - } - } - else - { - // RTF badformed? We don't know about that font (font_index). - // Anyhow, we still open the html tag because in the future - // we will close it (e.g. when new font is used). - v = calloc(7, sizeof(char)); - sprintf(v, ""); - } - - if (fontInfo && fontInfo->charset) - { - if (fontInfo->charset == 1) - /* charset 1 is default charset */ - formattingOptions->charset = NULL; - else { - NSNumber *key = [NSNumber numberWithUnsignedChar: fontInfo->charset]; - formattingOptions->charset = NSMapGet(_charsets, key); - } - } - - [_html appendBytes: v length: strlen(v)]; - free(v); - } - else if ([s isEqualToString: @"i"] && formattingOptions) - { - [_html appendBytes: "" length: 3]; - formattingOptions->italic = YES; - } - else if ([s isEqualToString: @"i0"] && formattingOptions) - { - [_html appendBytes: "" length: 4]; - formattingOptions->italic = NO; - } - else if ([s isEqualToString: @"tab"]) - { - [_html appendBytes: "  " length: 12]; - } - else if ([s isEqualToString: @"softline"] || [s isEqualToString: @"par"]) - { - [_html appendBytes: "
" length: 4]; - } - else if ([s isEqualToString: @"strike"] && formattingOptions) - { - [_html appendBytes: "" length: 8]; - formattingOptions->strikethrough = YES; - } - else if ([s isEqualToString: @"strike0"] && formattingOptions) - { - [_html appendBytes: "" length: 9]; - formattingOptions->strikethrough = NO; - } - else if ([s hasPrefix: @"u"] && [s length] > 1 && - (isdigit([s characterAtIndex: 1]) || '-' == [s characterAtIndex: 1])) - { - int arg; - arg = [[s substringFromIndex: 1] intValue]; - if (arg < 0) - // a negative value means a value greater than 32767 - arg = 32767 - arg; - - uch = (unichar) arg; - s = [NSString stringWithCharacters: &uch length: 1]; - d = [s dataUsingEncoding: NSUTF8StringEncoding]; - [_html appendData: d]; - } - else if ([s isEqualToString: @"ul"] && formattingOptions) - { - [_html appendBytes: "" length: 3]; - formattingOptions->underline = YES; - } - else if (([s isEqualToString: @"ul0"] || [s isEqualToString: @"ulnone"]) - && formattingOptions) - { - [_html appendBytes: "" length: 4]; - formattingOptions->underline = NO; + case CW_ANSICPG: + parseAnsicpg(hasArg, arg, &defaultCharset); + break; + case CW_B: + parseB(self, hasArg, arg, formattingOptions); + break; + case CW_CF: + parseCf(self, hasArg, arg, formattingOptions, colorTable); + break; + case CW_COLORTBL: + parseColorTableWrapper(self, &colorTable); + break; + case CW_F: + parseF(self, hasArg, arg, formattingOptions, fontTable); + break; + case CW_FONTTBL: + parseFontTableWrapper(self, cw, &fontTable); + break; + case CW_I: + parseI(self, hasArg, arg, formattingOptions); + break; + case CW_PAR: + parsePar(self); + break; + case CW_PICT: + parsePictureWrapper(self, cw); + break; + case CW_SOFTLINE: + parseSoftline(self); + break; + case CW_STRIKE: + parseStrike(self, hasArg, arg, formattingOptions); + break; + case CW_STYLESHEET: + parseStyleSheetWrapper(self, cw); + break; + case CW_TAB: + parseTab(self); + break; + case CW_U: + parseU(self, hasArg, arg); + break; + case CW_UL: + parseUl(self, hasArg, arg, formattingOptions); + break; + case CW_ULNONE: + parseUl(self, YES, 0, formattingOptions); + break; + case CW_UNKNOWN: + default: + // do nothing + break; } // If a space delimits the control word, the space does not appear in the document. @@ -1225,7 +1383,7 @@ const unsigned short ansicpg874[256] = { formattingOptions->font_index = -1; formattingOptions->color_index = -1; formattingOptions->start_pos = [_html length]; - formattingOptions->charset = default_charset; + formattingOptions->charset = defaultCharset; [stack push: formattingOptions]; ADVANCE; } From 2deda01e5c70dbaccb4b2761d7197ee11f736697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Wed, 2 Dec 2015 16:06:31 +0100 Subject: [PATCH 28/50] oc-mail: Do not crash after reconnect sending mails After the providing the workaround on rebuilding the LDAP connection, the sam_ctx variable can be freed and it was used as memory context to store the AddressBookEntryId or OneOffEntryId when resolving recipients. After this changeset, a local memory context which I think is more sane. --- OpenChange/MAPIStoreMailVolatileMessage.m | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index 537a88854..90e459244 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -679,6 +679,14 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, if (!fromResolved) { + TALLOC_CTX *local_mem_ctx; + local_mem_ctx = talloc_new(NULL); + if (!local_mem_ctx) + { + NSLog (@"%s: Out of memory", __PRETTY_FUNCTION__); + return; + } + NSLog (@"Message without an orig from, try to guess it from PidTagSenderEntryId"); senderEntryId = [mailProperties objectForKey: MAPIPropertyKey (PR_SENDER_ENTRYID)]; if (senderEntryId) @@ -692,7 +700,7 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, bin32.cb = [senderEntryId length]; bin32.lpb = (uint8_t *) [senderEntryId bytes]; - addrBookEntryId = get_AddressBookEntryId (connInfo->sam_ctx, &bin32); + addrBookEntryId = get_AddressBookEntryId (local_mem_ctx, &bin32); if (addrBookEntryId && [[NSString stringWithGUID: &addrBookEntryId->ProviderUID] hasSuffix: @"08002b2fe182"]) { @@ -718,7 +726,7 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, /* Try with One-Off EntryId */ struct OneOffEntryId *oneOffEntryId; - oneOffEntryId = get_OneOffEntryId (connInfo->sam_ctx, &bin32); + oneOffEntryId = get_OneOffEntryId (local_mem_ctx, &bin32); if (oneOffEntryId && [[NSString stringWithGUID: &oneOffEntryId->ProviderUID] hasSuffix: @"00dd010f5402"]) { @@ -727,9 +735,7 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, [fromRecipient setObject: [NSString stringWithUTF8String: oneOffEntryId->EmailAddress.lpszW] forKey: @"email"]; } - talloc_free (oneOffEntryId); } - talloc_free (addrBookEntryId); if ([[fromRecipient allKeys] count] > 0) { @@ -739,6 +745,8 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, } } + /* Free entryId */ + talloc_free(local_mem_ctx); } if (!recipients) From 2a3367a13b5e13bae3a69cd9032b5eb42e1a6110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Thu, 3 Dec 2015 15:18:31 +0100 Subject: [PATCH 29/50] oc-contacts: returning properly personal contacts We needed to return AddressBookProviderEmailList, AddressBookProviderArrayType and Email1OriginalEntryId to make it compatible with the standard. Besides a refactorization to return better EmailNFoobar properties --- OpenChange/MAPIStoreContactsMessage.m | 389 ++++++++++++++++++++------ 1 file changed, 298 insertions(+), 91 deletions(-) diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index 0236f1817..62a03e9a4 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -34,11 +34,14 @@ #import #import #import +#import #import "MAPIStoreAttachment.h" #import "MAPIStoreContactsAttachment.h" #import "MAPIStoreContactsFolder.h" +#import "MAPIStoreContext.h" #import "MAPIStorePropertySelectors.h" +#import "MAPIStoreSamDBUtils.h" #import "MAPIStoreTypes.h" #import "NSArray+MAPIStore.h" #import "NSDate+MAPIStore.h" @@ -64,6 +67,15 @@ @end +enum { // [MS-OXOCNTC] 2.2.1.2.11 + AddressBookProviderEmailValueEmail1 = 0, + AddressBookProviderEmailValueEmail2, + AddressBookProviderEmailValueEmail3, + AddressBookProviderEmailValueBusinessFax, + AddressBookProviderEmailValueHomeFax, + AddressBookProviderEmailValuePrimaryFax +}; + @implementation MAPIStoreContactsMessage - (id) init @@ -258,39 +270,6 @@ return MAPISTORE_SUCCESS; } -- (int) getPidLidEmail1DisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NGVCard *vCard; - NSString *fn, *email; - - vCard = [sogoObject vCard]; - fn = [vCard fn]; - email = [vCard preferredEMail]; - *data = [[NSString stringWithFormat: @"%@ (%@)", fn, email] - asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidEmail1OriginalDisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getPidLidEmail1EmailAddress: data - inMemCtx: memCtx]; -} - -- (int) getPidLidEmail1EmailAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[sogoObject vCard] preferredEMail]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - - (int) getPidTagAccount: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -341,46 +320,6 @@ return [self getYes: data inMemCtx: memCtx]; } -- (int) getPidLidEmail2EmailAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSMutableArray *emails; - NSString *email, *stringValue; - NGVCard *card; - NSUInteger count, max; - - emails = [NSMutableArray array]; - stringValue = nil; - - card = [sogoObject vCard]; - [emails addObjectsFromArray: [card childrenWithTag: @"email"]]; - [emails removeObjectsInArray: [card childrenWithTag: @"email" - andAttribute: @"type" - havingValue: @"pref"]]; - - max = [emails count]; - for (count = 0; !stringValue && count < max; count++) - { - email = [[emails objectAtIndex: count] flattenedValuesForKey: @""]; - - if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame) - stringValue = email; - } - - if (!stringValue) - stringValue = @""; - - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidEmail2OriginalDisplayName: (void **) data // Other email - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getPidLidEmail2EmailAddress: data inMemCtx: memCtx]; -} - - (int) getPidTagBody: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -396,6 +335,292 @@ return rc; } +// ------------------------------------------------------- +// Electronic Address Properties [MS-OXOCNTC 2.2.1.2] +// ------------------------------------------------------- + +/* + Fetch Email1, Email2 and Email3 values from the vcard. + Returns nil if not found +*/ +- (NSString *) _fetchEmailAddress: (NSUInteger) position +{ + NSMutableArray *emails; + NSString *email, *stringValue = nil; + NGVCard *card; + NSUInteger count, max; + + card = [sogoObject vCard]; + + if (position == 1) + // Predefined email + return [card preferredEMail]; + + emails = [NSMutableArray array]; + [emails addObjectsFromArray: [card childrenWithTag: @"email"]]; + [emails removeObjectsInArray: [card childrenWithTag: @"email" + andAttribute: @"type" + havingValue: @"pref"]]; + max = [emails count]; + for (count = 0; !stringValue && count < max; count++) + { + email = [[emails objectAtIndex: count] flattenedValuesForKey: @""]; + + if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame) + { + position--; + if (position == 1) + stringValue = email; + } + } + + return stringValue; +} + +- (NSArray *) _buildAddressBookProviderEmailList +{ + // [MS-OXOCNTC] 2.2.1.2.11 + // https://msdn.microsoft.com/en-us/library/ee158427%28v=exchg.80%29.aspx + NSMutableArray *list = [[NSMutableArray alloc] init]; + NSArray *elements; + + // Is there a fax number? + elements = [[[sogoObject vCard] childrenWithTag: @"tel"] + cardElementsWithAttribute: @"type" + havingValue: @"fax"]; + if ([elements count] > 0) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueBusinessFax]]; + + // How many different email addresses? + if ([self _fetchEmailAddress: 1]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail1]]; + else + return list; + if ([self _fetchEmailAddress: 2]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail2]]; + else + return list; + if ([self _fetchEmailAddress: 3]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail3]]; + + return list; +} + +- (int) getPidLidAddressBookProviderArrayType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + // [MS-OXOCNTC] 2.2.1.2.12 + // https://msdn.microsoft.com/en-us/library/ee218011%28v=exchg.80%29.aspx + uint32_t value = 0; + NSArray *emailList = [self _buildAddressBookProviderEmailList]; + + for (NSNumber *maskValue in emailList) + value |= 1 << [maskValue intValue]; + + *data = MAPILongValue (memCtx, value); + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidAddressBookProviderEmailList: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSArray *emailList = [self _buildAddressBookProviderEmailList]; + + *data = [emailList asMVLongInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +/* + Store on *data a string with the display name for the given email (position) + It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist +*/ +- (int) _getPidLidEmailDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx + atPosition: (NSUInteger) position +{ + NGVCard *vCard; + NSString *fn, *email; + + email = [self _fetchEmailAddress: position]; + if (!email) return MAPISTORE_ERR_NOT_FOUND; + + vCard = [sogoObject vCard]; + fn = [vCard fn]; + + *data = [[NSString stringWithFormat: @"%@ (%@)", fn, email] + asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidEmail1DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 1]; +} + +- (int) getPidLidEmail2DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 2]; +} + +- (int) getPidLidEmail3DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 3]; +} + +/* + Return an entry id (either one-off or addressbook entry) for the given email + It can return nil if the email doesn't exist +*/ +- (NSData *) _buildEntryIdForEmail: (NSUInteger) position +{ + NSString *email, *username; + NSData *data; + SOGoUserManager *mgr; + NSDictionary *contactInfos; + + email = [self _fetchEmailAddress: position]; + if (!email) return nil; + + // Try to figure out if this email is from local domain + mgr = [SOGoUserManager sharedUserManager]; + contactInfos = [mgr contactInfosForUserWithUIDorEmail: email]; + if (contactInfos) + { + username = [contactInfos objectForKey: @"sAMAccountName"]; + data = MAPIStoreInternalEntryId ([[self context] connectionInfo], username); + } + else + data = MAPIStoreExternalEntryId ([[sogoObject vCard] fn], email); + + return data; +} + +/* + Store on *data an entryId for the given email (position) + It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist +*/ +- (int) _getPidLidEmailOriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx + atPosition: (NSUInteger) position +{ + NSData *value; + + value = [self _buildEntryIdForEmail: position]; + if (!value) return MAPISTORE_ERR_NOT_FOUND; + + *data = [value asBinaryInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidEmail1OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 1]; +} + +- (int) getPidLidEmail2OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 2]; +} + +- (int) getPidLidEmail3OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 3]; +} + +/* + Store on *data the given email (position) + It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist +*/ +- (int) _getPidLidEmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx + atPosition: (NSUInteger) position +{ + NSString *email; + + email = [self _fetchEmailAddress: position]; + if (!email) return MAPISTORE_ERR_NOT_FOUND; + + *data = [email asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidEmail1EmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailAddress: data inMemCtx: memCtx atPosition: 1]; +} + +- (int) getPidLidEmail2EmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailAddress: data inMemCtx: memCtx atPosition: 2]; +} + +- (int) getPidLidEmail3EmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailAddress: data inMemCtx: memCtx atPosition: 3]; +} + +- (int) getPidLidEmail1OriginalDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidEmail1EmailAddress: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail2OriginalDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidEmail2EmailAddress: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail3OriginalDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidEmail3EmailAddress: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail1AddressType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + if (![self _fetchEmailAddress: 1]) return MAPISTORE_ERR_NOT_FOUND; + + return [self getSMTPAddrType: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail2AddressType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + if (![self _fetchEmailAddress: 2]) return MAPISTORE_ERR_NOT_FOUND; + + return [self getSMTPAddrType: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail3AddressType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + if (![self _fetchEmailAddress: 3]) return MAPISTORE_ERR_NOT_FOUND; + + return [self getSMTPAddrType: data inMemCtx: memCtx]; +} + - (int) _getElement: (NSString *) elementTag ofType: (NSString *) aType excluding: (NSString *) aTypeToExclude @@ -488,24 +713,6 @@ atPos: 0 inData: data inMemCtx: memCtx]; } -- (int) getPidLidEmail1AddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getSMTPAddrType: data inMemCtx: memCtx]; -} - -- (int) getPidLidEmail2AddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getSMTPAddrType: data inMemCtx: memCtx]; -} - -- (int) getPidLidEmail3AddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getSMTPAddrType: data inMemCtx: memCtx]; -} - - (int) getPidLidInstantMessagingAddress: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { From 0ddbd79fadc2742c52cd583e9630fc9f575c38b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Thu, 3 Dec 2015 15:22:15 +0100 Subject: [PATCH 30/50] Remove commented code These methods don't make any sense here --- OpenChange/MAPIStoreContactsMessage.m | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index 62a03e9a4..c2900a50f 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -152,25 +152,6 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return MAPISTORE_SUCCESS; } -// - (int) getPidTagOfflineAddressBookName: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// *data = talloc_strdup (memCtx, "PR_OAB_NAME_UNICODE"); - -// return MAPISTORE_SUCCESS; -// } - -// - (int) getPidTagOfflineAddressBookLanguageId: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// /* see http://msdn.microsoft.com/en-us/goglobal/bb895996.asxp */ -// /* English US */ -// *data = MAPILongValue (memCtx, 0x0409); - -// return MAPISTORE_SUCCESS; -// } - - - (int) getPidTagTitle: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { From bc5aa3cacb00b901808e4643e2d291126ee7e634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Thu, 3 Dec 2015 15:25:29 +0100 Subject: [PATCH 31/50] Whitespaces and indentation No real changes --- OpenChange/MAPIStoreContactsMessage.m | 174 +++++++++++++------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index c2900a50f..3a2492ad2 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -182,7 +182,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 inMemCtx: (TALLOC_CTX *) memCtx { CardElement *org; - + org = [[sogoObject vCard] org]; *data = [[org flattenedValueAtIndex: 0 forKey: @""] asUnicodeInMemCtx: memCtx]; @@ -194,7 +194,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 inMemCtx: (TALLOC_CTX *) memCtx { CardElement *org; - + org = [[sogoObject vCard] org]; *data = [[org flattenedValueAtIndex: 1 forKey: @""] asUnicodeInMemCtx: memCtx]; @@ -619,14 +619,14 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 vCard = [sogoObject vCard]; elements = [[vCard childrenWithTag: elementTag] - cardElementsWithAttribute: @"type" - havingValue: aType]; + cardElementsWithAttribute: @"type" + havingValue: aType]; max = [elements count]; for (count = 0; !stringValue && count < max; count++) { ce = [elements objectAtIndex: count]; if (!aTypeToExclude - || ![ce hasAttribute: @"type" havingValue: aTypeToExclude]) + || ![ce hasAttribute: @"type" havingValue: aTypeToExclude]) stringValue = [ce flattenedValueAtIndex: pos forKey: @""]; } @@ -718,8 +718,8 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 vCard = [sogoObject vCard]; elements = [[vCard childrenWithTag: @"adr"] - cardElementsWithAttribute: @"type" - havingValue: @"pref"]; + cardElementsWithAttribute: @"type" + havingValue: @"pref"]; if ([elements count] > 0) { element = [elements objectAtIndex: 0]; @@ -731,7 +731,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 longValue = 2; // The Work Address is the mailing address. } *data = MAPILongValue (memCtx, longValue); - + return MAPISTORE_SUCCESS; } @@ -792,7 +792,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 // home address getters // - (int) getPidLidHomeAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getElement: @"label" ofType: @"home" excluding: nil atPos: 0 inData: data inMemCtx: memCtx]; @@ -892,7 +892,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 } // -// +// personal and social getters // - (int) getPidTagNickname: (void **) data inMemCtx: (TALLOC_CTX *) memCtx @@ -922,7 +922,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 } else rc = MAPISTORE_ERR_NOT_FOUND; - + return rc; } @@ -954,7 +954,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 int rc = MAPISTORE_SUCCESS; stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-spouse"] - flattenedValuesForKey: @""]; + flattenedValuesForKey: @""]; if (stringValue) *data = [stringValue asUnicodeInMemCtx: memCtx]; else @@ -970,7 +970,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 int rc = MAPISTORE_SUCCESS; stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-manager"] - flattenedValuesForKey: @""]; + flattenedValuesForKey: @""]; if (stringValue) *data = [stringValue asUnicodeInMemCtx: memCtx]; else @@ -986,7 +986,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 int rc = MAPISTORE_SUCCESS; stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-assistant"] - flattenedValuesForKey: @""]; + flattenedValuesForKey: @""]; if (stringValue) *data = [stringValue asUnicodeInMemCtx: memCtx]; else @@ -1002,7 +1002,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 int rc = MAPISTORE_SUCCESS; stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-office"] - flattenedValuesForKey: @""]; + flattenedValuesForKey: @""]; if (stringValue) *data = [stringValue asUnicodeInMemCtx: memCtx]; else @@ -1018,7 +1018,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 int rc = MAPISTORE_SUCCESS; stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"fburl"] - flattenedValuesForKey: @""]; + flattenedValuesForKey: @""]; if (stringValue) *data = [stringValue asUnicodeInMemCtx: memCtx]; else @@ -1032,9 +1032,9 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 // - (int) getPidTagSurname: (void **) data inMemCtx: (TALLOC_CTX *) memCtx -{ +{ NSString *stringValue; - + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] flattenedValueAtIndex: 0 forKey: @""]; @@ -1047,7 +1047,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 inMemCtx: (TALLOC_CTX *) memCtx { NSString *stringValue; - + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] flattenedValueAtIndex: 1 forKey: @""]; @@ -1060,12 +1060,12 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 inMemCtx: (TALLOC_CTX *) memCtx { NSString *stringValue; - + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] flattenedValueAtIndex: 2 forKey: @""]; *data = [stringValue asUnicodeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -1073,12 +1073,12 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 inMemCtx: (TALLOC_CTX *) memCtx { NSString *stringValue; - + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] flattenedValueAtIndex: 3 forKey: @""]; *data = [stringValue asUnicodeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -1086,12 +1086,12 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 inMemCtx: (TALLOC_CTX *) memCtx { NSString *stringValue; - + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] flattenedValueAtIndex: 4 forKey: @""]; *data = [stringValue asUnicodeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -1156,7 +1156,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 } - (void) _updatePhotoInVCard: (NGVCard *) card -fromProperties: (NSDictionary *) attachmentProps + fromProperties: (NSDictionary *) attachmentProps { NSString *photoExt, *photoType, *content; CardElement *photo; @@ -1233,7 +1233,7 @@ fromProperties: (NSDictionary *) attachmentProps [newCard setVersion: @"3.0"]; [newCard setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; [newCard setProfile: @"vCard"]; - + // Decomposed fullname [newCard setNWithFamily: [properties objectForKey: MAPIPropertyKey(PR_SURNAME_UNICODE)] given: [properties objectForKey: MAPIPropertyKey(PR_GIVEN_NAME_UNICODE)] @@ -1256,26 +1256,26 @@ fromProperties: (NSDictionary *) attachmentProps if (value) { if ([elements count] > 0) - [[elements objectAtIndex: 0] setSingleValue: value forKey: @""]; + [[elements objectAtIndex: 0] setSingleValue: value forKey: @""]; else - [newCard addEmail: value - types: [NSArray arrayWithObject: @"pref"]]; + [newCard addEmail: value + types: [NSArray arrayWithObject: @"pref"]]; } value = [properties objectForKey: MAPIPropertyKey (PidLidEmail2EmailAddress)]; if (value) { if ([elements count] > 1) - [[elements objectAtIndex: 1] setSingleValue: value forKey: @""]; + [[elements objectAtIndex: 1] setSingleValue: value forKey: @""]; else - [newCard addEmail: value types: nil]; + [newCard addEmail: value types: nil]; } value = [properties objectForKey: MAPIPropertyKey (PidLidEmail3EmailAddress)]; if (value) { if ([elements count] > 2) - [[elements objectAtIndex: 2] setSingleValue: value forKey: @""]; + [[elements objectAtIndex: 2] setSingleValue: value forKey: @""]; else - [newCard addEmail: value types: nil]; + [newCard addEmail: value types: nil]; } // @@ -1286,39 +1286,39 @@ fromProperties: (NSDictionary *) attachmentProps // 0x00000000 - No address is selected as the Mailing Address. // 0x00000001 - The Home Address is the Mailing Address. // 0x00000002 - The Work Address is the Mailing Address - // 0x00000003 - The Other Address is the Mailing Address. + // 0x00000003 - The Other Address is the Mailing Address. // // postalAddressId = [[properties objectForKey: MAPIPropertyKey (PidLidPostalAddressId)] - intValue]; - + intValue]; + value = [properties objectForKey: MAPIPropertyKey(PidLidWorkAddress)]; if ([value length]) { elements = [newCard childrenWithTag: @"label" - andAttribute: @"type" - havingValue: @"work"]; + andAttribute: @"type" + havingValue: @"work"]; if ([elements count] > 0) - element = [elements objectAtIndex: 0]; + element = [elements objectAtIndex: 0]; else - { - element = [CardElement elementWithTag: @"label"]; - [element addAttribute: @"type" value: @"work"]; - [newCard addChild: element]; - } + { + element = [CardElement elementWithTag: @"label"]; + [element addAttribute: @"type" value: @"work"]; + [newCard addChild: element]; + } if (postalAddressId == 2) - { - [element removeValue: @"pref" - fromAttribute: @"type"]; - [element addAttribute: @"type" - value: @"pref"]; - } + { + [element removeValue: @"pref" + fromAttribute: @"type"]; + [element addAttribute: @"type" + value: @"pref"]; + } [element setSingleValue: value forKey: @""]; } elements = [newCard childrenWithTag: @"adr" - andAttribute: @"type" - havingValue: @"work"]; + andAttribute: @"type" + havingValue: @"work"]; if ([elements count] > 0) element = [elements objectAtIndex: 0]; else @@ -1347,7 +1347,7 @@ fromProperties: (NSDictionary *) attachmentProps value = [properties objectForKey: MAPIPropertyKey(PidLidWorkAddressCountry)]; if (value) [element setSingleValue: value atIndex: 6 forKey: @""]; - + // // home postal addresses handling // @@ -1355,29 +1355,29 @@ fromProperties: (NSDictionary *) attachmentProps if ([value length]) { elements = [newCard childrenWithTag: @"label" - andAttribute: @"type" - havingValue: @"home"]; + andAttribute: @"type" + havingValue: @"home"]; if ([elements count] > 0) - element = [elements objectAtIndex: 0]; + element = [elements objectAtIndex: 0]; else - { - element = [CardElement elementWithTag: @"label"]; - [element addAttribute: @"type" value: @"home"]; - [newCard addChild: element]; - } + { + element = [CardElement elementWithTag: @"label"]; + [element addAttribute: @"type" value: @"home"]; + [newCard addChild: element]; + } if (postalAddressId == 1) - { - [element removeValue: @"pref" - fromAttribute: @"type"]; - [element addAttribute: @"type" - value: @"pref"]; - } + { + [element removeValue: @"pref" + fromAttribute: @"type"]; + [element addAttribute: @"type" + value: @"pref"]; + } [element setSingleValue: value forKey: @""]; } - + elements = [newCard childrenWithTag: @"adr" - andAttribute: @"type" - havingValue: @"home"]; + andAttribute: @"type" + havingValue: @"home"]; if ([elements count] > 0) element = [elements objectAtIndex: 0]; else @@ -1412,27 +1412,27 @@ fromProperties: (NSDictionary *) attachmentProps // // telephone numbers: work, home, fax, pager and mobile // - element = [self _elementWithTag: @"tel" ofType: @"work" forCard: newCard]; + element = [self _elementWithTag: @"tel" ofType: @"work" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_OFFICE_TELEPHONE_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; - element = [self _elementWithTag: @"tel" ofType: @"home" forCard: newCard]; + element = [self _elementWithTag: @"tel" ofType: @"home" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_HOME_TELEPHONE_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; - element = [self _elementWithTag: @"tel" ofType: @"fax" forCard: newCard]; + element = [self _elementWithTag: @"tel" ofType: @"fax" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_BUSINESS_FAX_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; - - element = [self _elementWithTag: @"tel" ofType: @"pager" forCard: newCard]; + + element = [self _elementWithTag: @"tel" ofType: @"pager" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_PAGER_TELEPHONE_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; - element = [self _elementWithTag: @"tel" ofType: @"cell" forCard: newCard]; + element = [self _elementWithTag: @"tel" ofType: @"cell" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_MOBILE_TELEPHONE_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; @@ -1454,7 +1454,7 @@ fromProperties: (NSDictionary *) attachmentProps value = [properties objectForKey: MAPIPropertyKey(PR_NICKNAME_UNICODE)]; if (value) [newCard setNickname: value]; - + value = [properties objectForKey: MAPIPropertyKey(PR_DEPARTMENT_NAME_UNICODE)]; if (value) units = [NSArray arrayWithObject: value]; @@ -1464,19 +1464,19 @@ fromProperties: (NSDictionary *) attachmentProps value = [properties objectForKey: MAPIPropertyKey(PR_COMPANY_NAME_UNICODE)]; if (value) [newCard setOrg: value units: units]; - + value = [properties objectForKey: MAPIPropertyKey(PR_BUSINESS_HOME_PAGE_UNICODE)]; if (value) { [[self _elementWithTag: @"url" ofType: @"work" forCard: newCard] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PidLidInstantMessagingAddress)]; if (value) { [[newCard uniqueChildWithTag: @"x-aim"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PR_BIRTHDAY)]; @@ -1500,35 +1500,35 @@ fromProperties: (NSDictionary *) attachmentProps if (value) { [[newCard uniqueChildWithTag: @"x-ms-spouse"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PR_MANAGER_NAME_UNICODE)]; if (value) { [[newCard uniqueChildWithTag: @"x-ms-manager"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PR_ASSISTANT_UNICODE)]; if (value) { [[newCard uniqueChildWithTag: @"x-ms-assistant"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PR_OFFICE_LOCATION_UNICODE)]; if (value) { [[newCard uniqueChildWithTag: @"x-ms-office"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PidLidFreeBusyLocation)]; if (value) { [[newCard uniqueChildWithTag: @"fburl"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } /* photo */ @@ -1569,7 +1569,7 @@ fromProperties: (NSDictionary *) attachmentProps if ([messageClass isEqualToString: @"IPM.DistList"]) [self saveDistList: memCtx]; else - [self saveContact: memCtx]; + [self saveContact: memCtx]; } From a9fe80c36775636ddfed29705afca15774a8fad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Thu, 3 Dec 2015 15:29:07 +0100 Subject: [PATCH 32/50] oc-contacts: don't return empty values If the property has an empty string, return not found --- OpenChange/MAPIStoreContactsMessage.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index 3a2492ad2..9fcbe6933 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -911,9 +911,9 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 NSCalendarDate *dateValue; NSString *stringValue; int rc = MAPISTORE_SUCCESS; - + stringValue = [[sogoObject vCard] bday]; - if (stringValue) + if ([stringValue length] != 0) { dateValue = [NSCalendarDate dateWithString: stringValue calendarFormat: @"%Y-%m-%d"]; @@ -935,7 +935,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-anniversary"] flattenedValuesForKey: @""]; - if (stringValue && ! [stringValue isEqualToString: @""]) + if ([stringValue length] != 0) { dateValue = [NSCalendarDate dateWithString: stringValue calendarFormat: @"%Y-%m-%d"]; @@ -955,7 +955,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-spouse"] flattenedValuesForKey: @""]; - if (stringValue) + if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; @@ -971,7 +971,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-manager"] flattenedValuesForKey: @""]; - if (stringValue) + if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; @@ -987,7 +987,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-assistant"] flattenedValuesForKey: @""]; - if (stringValue) + if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; @@ -1003,7 +1003,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-office"] flattenedValuesForKey: @""]; - if (stringValue) + if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; @@ -1019,7 +1019,7 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"fburl"] flattenedValuesForKey: @""]; - if (stringValue) + if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; From 25dbce42cdee7c472fc740491bbba8ee4aceeff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Thu, 3 Dec 2015 16:31:44 +0100 Subject: [PATCH 33/50] Reorganize methods according to MS-OXOCNTC Nothing has been change but this way is much more readable --- OpenChange/MAPIStoreContactsMessage.m | 987 +++++++++++++------------- 1 file changed, 503 insertions(+), 484 deletions(-) diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index 9fcbe6933..b183d445e 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -67,15 +67,6 @@ @end -enum { // [MS-OXOCNTC] 2.2.1.2.11 - AddressBookProviderEmailValueEmail1 = 0, - AddressBookProviderEmailValueEmail2, - AddressBookProviderEmailValueEmail3, - AddressBookProviderEmailValueBusinessFax, - AddressBookProviderEmailValueHomeFax, - AddressBookProviderEmailValuePrimaryFax -}; - @implementation MAPIStoreContactsMessage - (id) init @@ -152,56 +143,6 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return MAPISTORE_SUCCESS; } -- (int) getPidTagTitle: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[sogoObject vCard] title]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagProfession: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - int rc = MAPISTORE_SUCCESS; - - stringValue = [[sogoObject vCard] role]; - if (stringValue) - *data = [stringValue asUnicodeInMemCtx: memCtx]; - else - rc = MAPISTORE_ERR_NOT_FOUND; - - return rc; -} - -- (int) getPidTagCompanyName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - CardElement *org; - - org = [[sogoObject vCard] org]; - *data = [[org flattenedValueAtIndex: 0 forKey: @""] - asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagDepartmentName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - CardElement *org; - - org = [[sogoObject vCard] org]; - *data = [[org flattenedValueAtIndex: 1 forKey: @""] - asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - - (int) getPidTagSendInternetEncoding: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -316,10 +257,105 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return rc; } +- (int) getPidTagSensitivity: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getLongZero: data inMemCtx: memCtx]; +} + +// --------------------------------------------------------- +// Contact Name Properties [MS-OXOCNTC 2.2.1.1] +// --------------------------------------------------------- + +- (int) getPidTagNickname: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[sogoObject vCard] nickname]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagGeneration: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 4 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagSurname: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 0 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagMiddleName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 2 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagGivenName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 1 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagDisplayNamePrefix: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 3 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + // ------------------------------------------------------- // Electronic Address Properties [MS-OXOCNTC 2.2.1.2] // ------------------------------------------------------- +enum { // [MS-OXOCNTC] 2.2.1.2.11 + AddressBookProviderEmailValueEmail1 = 0, + AddressBookProviderEmailValueEmail2, + AddressBookProviderEmailValueEmail3, + AddressBookProviderEmailValueBusinessFax, + AddressBookProviderEmailValueHomeFax, + AddressBookProviderEmailValuePrimaryFax +}; + /* Fetch Email1, Email2 and Email3 values from the vcard. Returns nil if not found @@ -358,172 +394,6 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return stringValue; } -- (NSArray *) _buildAddressBookProviderEmailList -{ - // [MS-OXOCNTC] 2.2.1.2.11 - // https://msdn.microsoft.com/en-us/library/ee158427%28v=exchg.80%29.aspx - NSMutableArray *list = [[NSMutableArray alloc] init]; - NSArray *elements; - - // Is there a fax number? - elements = [[[sogoObject vCard] childrenWithTag: @"tel"] - cardElementsWithAttribute: @"type" - havingValue: @"fax"]; - if ([elements count] > 0) - [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueBusinessFax]]; - - // How many different email addresses? - if ([self _fetchEmailAddress: 1]) - [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail1]]; - else - return list; - if ([self _fetchEmailAddress: 2]) - [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail2]]; - else - return list; - if ([self _fetchEmailAddress: 3]) - [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail3]]; - - return list; -} - -- (int) getPidLidAddressBookProviderArrayType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - // [MS-OXOCNTC] 2.2.1.2.12 - // https://msdn.microsoft.com/en-us/library/ee218011%28v=exchg.80%29.aspx - uint32_t value = 0; - NSArray *emailList = [self _buildAddressBookProviderEmailList]; - - for (NSNumber *maskValue in emailList) - value |= 1 << [maskValue intValue]; - - *data = MAPILongValue (memCtx, value); - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidAddressBookProviderEmailList: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSArray *emailList = [self _buildAddressBookProviderEmailList]; - - *data = [emailList asMVLongInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -/* - Store on *data a string with the display name for the given email (position) - It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist -*/ -- (int) _getPidLidEmailDisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx - atPosition: (NSUInteger) position -{ - NGVCard *vCard; - NSString *fn, *email; - - email = [self _fetchEmailAddress: position]; - if (!email) return MAPISTORE_ERR_NOT_FOUND; - - vCard = [sogoObject vCard]; - fn = [vCard fn]; - - *data = [[NSString stringWithFormat: @"%@ (%@)", fn, email] - asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidEmail1DisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 1]; -} - -- (int) getPidLidEmail2DisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 2]; -} - -- (int) getPidLidEmail3DisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 3]; -} - -/* - Return an entry id (either one-off or addressbook entry) for the given email - It can return nil if the email doesn't exist -*/ -- (NSData *) _buildEntryIdForEmail: (NSUInteger) position -{ - NSString *email, *username; - NSData *data; - SOGoUserManager *mgr; - NSDictionary *contactInfos; - - email = [self _fetchEmailAddress: position]; - if (!email) return nil; - - // Try to figure out if this email is from local domain - mgr = [SOGoUserManager sharedUserManager]; - contactInfos = [mgr contactInfosForUserWithUIDorEmail: email]; - if (contactInfos) - { - username = [contactInfos objectForKey: @"sAMAccountName"]; - data = MAPIStoreInternalEntryId ([[self context] connectionInfo], username); - } - else - data = MAPIStoreExternalEntryId ([[sogoObject vCard] fn], email); - - return data; -} - -/* - Store on *data an entryId for the given email (position) - It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist -*/ -- (int) _getPidLidEmailOriginalEntryId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx - atPosition: (NSUInteger) position -{ - NSData *value; - - value = [self _buildEntryIdForEmail: position]; - if (!value) return MAPISTORE_ERR_NOT_FOUND; - - *data = [value asBinaryInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidEmail1OriginalEntryId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getPidLidEmailOriginalEntryId: data - inMemCtx: memCtx - atPosition: 1]; -} - -- (int) getPidLidEmail2OriginalEntryId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getPidLidEmailOriginalEntryId: data - inMemCtx: memCtx - atPosition: 2]; -} - -- (int) getPidLidEmail3OriginalEntryId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getPidLidEmailOriginalEntryId: data - inMemCtx: memCtx - atPosition: 3]; -} - /* Store on *data the given email (position) It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist @@ -602,6 +472,116 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return [self getSMTPAddrType: data inMemCtx: memCtx]; } +/* + Store on *data a string with the display name for the given email (position) + It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist +*/ +- (int) _getPidLidEmailDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx + atPosition: (NSUInteger) position +{ + NGVCard *vCard; + NSString *fn, *email; + + email = [self _fetchEmailAddress: position]; + if (!email) return MAPISTORE_ERR_NOT_FOUND; + + vCard = [sogoObject vCard]; + fn = [vCard fn]; + + *data = [[NSString stringWithFormat: @"%@ (%@)", fn, email] + asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidEmail1DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 1]; +} + +- (int) getPidLidEmail2DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 2]; +} + +- (int) getPidLidEmail3DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 3]; +} + +/* + Return an entry id (either one-off or addressbook entry) for the given email + It can return nil if the email doesn't exist +*/ +- (NSData *) _buildEntryIdForEmail: (NSUInteger) position +{ + NSString *email, *username; + NSData *data; + SOGoUserManager *mgr; + NSDictionary *contactInfos; + + email = [self _fetchEmailAddress: position]; + if (!email) return nil; + + // Try to figure out if this email is from local domain + mgr = [SOGoUserManager sharedUserManager]; + contactInfos = [mgr contactInfosForUserWithUIDorEmail: email]; + if (contactInfos) + { + username = [contactInfos objectForKey: @"sAMAccountName"]; + data = MAPIStoreInternalEntryId ([[self context] connectionInfo], username); + } + else + data = MAPIStoreExternalEntryId ([[sogoObject vCard] fn], email); + + return data; +} +/* + Store on *data an entryId for the given email (position) + It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist +*/ +- (int) _getPidLidEmailOriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx + atPosition: (NSUInteger) position +{ + NSData *value; + + value = [self _buildEntryIdForEmail: position]; + if (!value) return MAPISTORE_ERR_NOT_FOUND; + + *data = [value asBinaryInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidEmail1OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 1]; +} + +- (int) getPidLidEmail2OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 2]; +} + +- (int) getPidLidEmail3OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 3]; +} + - (int) _getElement: (NSString *) elementTag ofType: (NSString *) aType excluding: (NSString *) aTypeToExclude @@ -638,41 +618,6 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return MAPISTORE_SUCCESS; } -- (int) getPidTagBusinessTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"work" excluding: @"fax" - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagHomeTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"home" excluding: @"fax" - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagMobileTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"cell" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagPagerTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"pager" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagPrimaryTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"pref" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - - (int) getPidTagBusinessFaxNumber: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -680,130 +625,68 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 atPos: 0 inData: data inMemCtx: memCtx]; } -- (int) getPidTagBusinessHomePage: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"url" ofType: @"work" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagPersonalHomePage: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"url" ofType: @"home" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidLidInstantMessagingAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-aim"] - flattenedValuesForKey: @""]; - if (!stringValue) - stringValue = @""; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidPostalAddressId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +/* + [MS-OXOCNTC] 2.2.1.2.11 + https://msdn.microsoft.com/en-us/library/ee158427%28v=exchg.80%29.aspx +*/ +- (NSArray *) _buildAddressBookProviderEmailList { + NSMutableArray *list = [[NSMutableArray alloc] init]; NSArray *elements; - CardElement *element; - uint32_t longValue = 0; - NGVCard *vCard; - vCard = [sogoObject vCard]; - elements = [[vCard childrenWithTag: @"adr"] + // Is there a fax number? + elements = [[[sogoObject vCard] childrenWithTag: @"tel"] cardElementsWithAttribute: @"type" - havingValue: @"pref"]; + havingValue: @"fax"]; if ([elements count] > 0) - { - element = [elements objectAtIndex: 0]; - if ([element hasAttribute: @"type" - havingValue: @"home"]) - longValue = 1; // The Home Address is the mailing address. - else if ([element hasAttribute: @"type" - havingValue: @"work"]) - longValue = 2; // The Work Address is the mailing address. - } - *data = MAPILongValue (memCtx, longValue); + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueBusinessFax]]; + + // How many different email addresses? + if ([self _fetchEmailAddress: 1]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail1]]; + else + return list; + if ([self _fetchEmailAddress: 2]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail2]]; + else + return list; + if ([self _fetchEmailAddress: 3]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail3]]; + + return list; +} + +- (int) getPidLidAddressBookProviderArrayType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + // [MS-OXOCNTC] 2.2.1.2.12 + // https://msdn.microsoft.com/en-us/library/ee218011%28v=exchg.80%29.aspx + uint32_t value = 0; + NSArray *emailList = [self _buildAddressBookProviderEmailList]; + + for (NSNumber *maskValue in emailList) + value |= 1 << [maskValue intValue]; + + *data = MAPILongValue (memCtx, value); return MAPISTORE_SUCCESS; } - -// -// getters when no address is selected as the Mailing Address -// -- (int) getPidTagPostalAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidAddressBookProviderEmailList: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { - return [self _getElement: @"label" ofType: @"pref" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; + NSArray *emailList = [self _buildAddressBookProviderEmailList]; + + *data = [emailList asMVLongInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; } -- (int) getPidTagPostOfficeBox: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} +// --------------------------------------------------------- +// Physical Address Properties [MS-OXOCNTC 2.2.1.3] +// --------------------------------------------------------- -- (int) getPidTagStreetAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 2 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagLocality: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 3 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagStateOrProvince: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 4 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagPostalCode: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 5 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagCountry: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 6 inData: data inMemCtx: memCtx]; -} - -// -// home address getters -// -- (int) getPidLidHomeAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"label" ofType: @"home" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagHomeAddressPostOfficeBox: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"home" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} +// Home Address - (int) getPidTagHomeAddressStreet: (void **) data inMemCtx: (TALLOC_CTX *) memCtx @@ -839,23 +722,22 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 atPos: 6 inData: data inMemCtx: memCtx]; } -// -// Work addresss -// -- (int) getPidLidWorkAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidTagHomeAddressPostOfficeBox: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { - return [self _getElement: @"label" ofType: @"work" excluding: nil + return [self _getElement: @"adr" ofType: @"home" excluding: nil atPos: 0 inData: data inMemCtx: memCtx]; } -- (int) getPidLidWorkAddressPostOfficeBox: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidHomeAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { - return [self _getElement: @"adr" ofType: @"work" excluding: nil + return [self _getElement: @"label" ofType: @"home" excluding: nil atPos: 0 inData: data inMemCtx: memCtx]; } +// Work Address + - (int) getPidLidWorkAddressStreet: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -891,20 +773,141 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 atPos: 6 inData: data inMemCtx: memCtx]; } -// -// personal and social getters -// -- (int) getPidTagNickname: (void **) data +- (int) getPidLidWorkAddressPostOfficeBox: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"work" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidLidWorkAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"label" ofType: @"work" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +// Mailing Address + +- (int) getPidTagStreetAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 2 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagLocality: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - NSString *stringValue; + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 3 inData: data inMemCtx: memCtx]; +} - stringValue = [[sogoObject vCard] nickname]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; +- (int) getPidTagStateOrProvince: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 4 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagPostalCode: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 5 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagCountry: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 6 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagPostOfficeBox: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagPostalAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"label" ofType: @"pref" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidLidPostalAddressId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSArray *elements; + CardElement *element; + uint32_t longValue = 0; + NGVCard *vCard; + + vCard = [sogoObject vCard]; + elements = [[vCard childrenWithTag: @"adr"] + cardElementsWithAttribute: @"type" + havingValue: @"pref"]; + if ([elements count] > 0) + { + element = [elements objectAtIndex: 0]; + if ([element hasAttribute: @"type" + havingValue: @"home"]) + longValue = 1; // The Home Address is the mailing address. + else if ([element hasAttribute: @"type" + havingValue: @"work"]) + longValue = 2; // The Work Address is the mailing address. + } + *data = MAPILongValue (memCtx, longValue); return MAPISTORE_SUCCESS; } +// ------------------------------------------------------- +// Telephone Properties [MS-OXOCNTC 2.2.1.4] +// ------------------------------------------------------- + +- (int) getPidTagPagerTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"pager" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagBusinessTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"work" excluding: @"fax" + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagHomeTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"home" excluding: @"fax" + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagPrimaryTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"pref" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagMobileTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"cell" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +// --------------------------------------------------------- +// Event Properties [MS-OXOCNTC 2.2.1.5] +// --------------------------------------------------------- + - (int) getPidTagBirthday: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -947,13 +950,52 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return rc; } -- (int) getPidTagSpouseName: (void **) data +// --------------------------------------------------------- +// Professional Properties [MS-OXOCNTC 2.2.1.6] +// --------------------------------------------------------- + +- (int) getPidTagTitle: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[sogoObject vCard] title]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagCompanyName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + CardElement *org; + + org = [[sogoObject vCard] org]; + *data = [[org flattenedValueAtIndex: 0 forKey: @""] + asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagDepartmentName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + CardElement *org; + + org = [[sogoObject vCard] org]; + *data = [[org flattenedValueAtIndex: 1 forKey: @""] + asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagOfficeLocation: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { NSString *stringValue; int rc = MAPISTORE_SUCCESS; - stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-spouse"] + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-office"] flattenedValuesForKey: @""]; if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; @@ -995,15 +1037,14 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return rc; } -- (int) getPidTagOfficeLocation: (void **) data +- (int) getPidTagProfession: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { NSString *stringValue; int rc = MAPISTORE_SUCCESS; - stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-office"] - flattenedValuesForKey: @""]; - if ([stringValue length] != 0) + stringValue = [[sogoObject vCard] role]; + if (stringValue) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; @@ -1011,97 +1052,10 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 return rc; } -- (int) getPidLidFreeBusyLocation: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - int rc = MAPISTORE_SUCCESS; +// --------------------------------------------------------- +// Contact Photo Properties [MS-OXOCNTC 2.2.1.8] +// --------------------------------------------------------- - stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"fburl"] - flattenedValuesForKey: @""]; - if ([stringValue length] != 0) - *data = [stringValue asUnicodeInMemCtx: memCtx]; - else - rc = MAPISTORE_ERR_NOT_FOUND; - - return rc; -} - -// -// Decomposed fullname getters -// -- (int) getPidTagSurname: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 0 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagGivenName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 1 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagMiddleName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 2 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagDisplayNamePrefix: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 3 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagGeneration: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 4 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagSensitivity: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getLongZero: data inMemCtx: memCtx]; -} - -/* attachments (photos) */ - (void) _fetchAttachmentParts { NGVCardPhoto *photo; @@ -1196,6 +1150,72 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 } } +// --------------------------------------------------------- +// Other Properties [MS-OXOCNTC 2.2.1.10] +// --------------------------------------------------------- + +- (int) getPidTagSpouseName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-spouse"] + flattenedValuesForKey: @""]; + if ([stringValue length] != 0) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidLidInstantMessagingAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-aim"] + flattenedValuesForKey: @""]; + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidFreeBusyLocation: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"fburl"] + flattenedValuesForKey: @""]; + if ([stringValue length] != 0) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidTagPersonalHomePage: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"url" ofType: @"home" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagBusinessHomePage: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"url" ofType: @"work" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +// --------------------------------------------------------- + - (BOOL) subscriberCanReadMessage { return [[self activeUserRoles] containsObject: SOGoRole_ObjectViewer]; @@ -1572,5 +1592,4 @@ enum { // [MS-OXOCNTC] 2.2.1.2.11 [self saveContact: memCtx]; } - @end From 1c8b693656bfb79445fce3c99a97534c228de163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Wed, 9 Dec 2015 13:07:30 +0100 Subject: [PATCH 34/50] Request all contacts when there is no filter --- UI/WebServerResources/ContactsUI.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UI/WebServerResources/ContactsUI.js b/UI/WebServerResources/ContactsUI.js index 86782e2ae..1fa869782 100644 --- a/UI/WebServerResources/ContactsUI.js +++ b/UI/WebServerResources/ContactsUI.js @@ -24,6 +24,8 @@ function openContactsFolder(contactsFolder, reload, idx) { if (searchValue && searchValue.length > 0) url += ("&search=" + search["contacts"]["criteria"] + "&value=" + escape(searchValue.utf8encode())); + else + url += "&search=name_or_address&value=."; var sortAttribute = sorting["attribute"]; if (sortAttribute && sortAttribute.length > 0) url += ("&sort=" + sorting["attribute"] From 10cabca57ef437b906e67c7245521cce1147725a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Thu, 10 Dec 2015 16:36:22 +0100 Subject: [PATCH 35/50] oc: Use ReplicaID from connection info Instead of hardcoding it to 0x0001. --- OpenChange/MAPIStoreSOGoObject.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenChange/MAPIStoreSOGoObject.m b/OpenChange/MAPIStoreSOGoObject.m index 2d7b6ad99..7f1cc4b6a 100644 --- a/OpenChange/MAPIStoreSOGoObject.m +++ b/OpenChange/MAPIStoreSOGoObject.m @@ -197,6 +197,7 @@ static Class MAPIStoreFolderK; inMemCtx: (TALLOC_CTX *) memCtx { int rc; + struct mapistore_connection_info *connInfo; uint64_t obVersion; obVersion = [self objectVersion]; @@ -204,8 +205,9 @@ static Class MAPIStoreFolderK; rc = MAPISTORE_ERR_NOT_FOUND; else { + connInfo = [[self context] connectionInfo]; *data = MAPILongLongValue (memCtx, ((obVersion << 16) - | 0x0001)); + | connInfo->repl_id)); rc = MAPISTORE_SUCCESS; } From 98ed9c3b17f22c38a81e1140029cddf5fe2d88fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Fri, 11 Dec 2015 11:22:26 +0100 Subject: [PATCH 36/50] oc-mail: Manage multiple CN restriction By ignoring <, =< and = operators and simplifying the following request: MODSEQ >= x || MODSEQ >= y || MODSEQ >= z --> MODSEQ >= min(x, y, z) This hack will reduce the number of current retrieved UID keys from the IMAP server. Current status is to retrieve everything when the multiple CN restriction is sent as the required restriction is too complex and it is not defined by the IMAP spec. The proper implementation for: CN > x_1 & CN < x_2 | CN > y_1 & CN < y_2 | CN > z_1 It will be something like this: set(MODSEQ >= x_1 + 1) - set(MODSEQ >= x_2) U set(MODSEQ >= y_1 + 1) - set(MODSEQ >= y_2) U set(MODSEQ >= z_1) Assuming x_1 <= x_2 <= y_1 <= y_2 <= z_1. --- OpenChange/MAPIStoreMailFolder.m | 67 +++++++++++++++++++++++++- OpenChange/MAPIStoreMailMessageTable.m | 4 +- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 6934e7a60..02e25acbd 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -74,6 +74,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; #include #include #include +#include #include #include @@ -257,6 +258,67 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; return MAPISTORE_SUCCESS; } +- (EOQualifier *) simplifyQualifier: (EOQualifier *) qualifier +{ + /* Hack: Reduce the number of MODSEQ constraints to a single one as + we assume the difference among MODSEQs will be small enough to + return a small number of UIDs. + + This is the only case we do simplify: + MODSEQ >= x | MODSEQ >= y | MODSEQ >= z => MODSEQ >= min(x,y,z) + */ + if (qualifier && [qualifier isKindOfClass: [EOOrQualifier class]]) + { + EOQualifier *simplifiedQualifier; + NSArray *quals; + NSNumber *minModseq; + NSUInteger i, count; + + quals = [(EOOrQualifier *)qualifier qualifiers]; + count = [quals count]; + if (count < 2) + return qualifier; + + minModseq = [NSNumber numberWithUnsignedLongLong: ULLONG_MAX]; + + for (i = 0; i < count; i++) + { + EOQualifier *subQualifier; + + subQualifier = [quals objectAtIndex: i]; + if ([subQualifier isKindOfClass: [EOAndQualifier class]] + && [[(EOAndQualifier *)subQualifier qualifiers] count] == 1) + subQualifier = [[(EOAndQualifier *)subQualifier qualifiers] objectAtIndex: 0]; + + if ([subQualifier isKindOfClass: [EOKeyValueQualifier class]] + && [[(EOKeyValueQualifier *)subQualifier key] isEqualToString: @"MODSEQ"]) + { + NSNumber *value; + + value = (NSNumber *)[(EOKeyValueQualifier *)subQualifier value]; + if ([minModseq compare: value] == NSOrderedDescending + && [value unsignedLongLongValue] > 0) + minModseq = (NSNumber *)[(EOKeyValueQualifier *)subQualifier value]; + + } + else + return qualifier; + } + + if ([minModseq unsignedLongLongValue] > 0 && [minModseq unsignedLongLongValue] < ULLONG_MAX) + { + simplifiedQualifier = [[EOKeyValueQualifier alloc] + initWithKey: @"MODSEQ" + operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo + value: minModseq]; + [simplifiedQualifier autorelease]; + return simplifiedQualifier; + } + } + + return qualifier; +} + - (EOQualifier *) nonDeletedQualifier { static EOQualifier *nonDeletedQualifier = nil; @@ -281,7 +343,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; andSortOrderings: (NSArray *) sortOrderings { NSArray *uidKeys; - EOQualifier *fetchQualifier; + EOQualifier *fetchQualifier, *simplifiedQualifier; if ([self ensureFolderExists]) { @@ -290,9 +352,10 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; if (qualifier) { + simplifiedQualifier = [self simplifyQualifier: qualifier]; fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: - [self nonDeletedQualifier], qualifier, + [self nonDeletedQualifier], simplifiedQualifier, nil]; [fetchQualifier autorelease]; } diff --git a/OpenChange/MAPIStoreMailMessageTable.m b/OpenChange/MAPIStoreMailMessageTable.m index c71097d63..d315f6527 100644 --- a/OpenChange/MAPIStoreMailMessageTable.m +++ b/OpenChange/MAPIStoreMailMessageTable.m @@ -181,8 +181,8 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK; else { /* Ignore other operations as IMAP only support MODSEQ >= X */ - [self warnWithFormat: @"Ignoring %@ as only supported operators are > and >=", - [self operatorFromRestrictionOperator: res->relop]]; + [self warnWithFormat: @"Ignoring '%@' as only supported operators are > and >=", + NSStringFromSelector ([self operatorFromRestrictionOperator: res->relop])]; rc = MAPIRestrictionStateAlwaysTrue; } } From 2fc21e48d81e0a90cccfd058d3a10adfa86e1cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Fri, 11 Dec 2015 11:31:15 +0100 Subject: [PATCH 37/50] oc: Use proper variable name for last modified value --- OpenChange/MAPIStoreGCSFolder.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index 2dacdb4cf..336474675 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -681,12 +681,12 @@ static Class NSNumberK; - (NSNumber *) lastModifiedFromMessageChangeNumber: (NSString *) changeNumber { NSDictionary *mapping; - NSNumber *modseq; + NSNumber *lastModified; mapping = [[versionsMessage properties] objectForKey: @"VersionMapping"]; - modseq = [mapping objectForKey: changeNumber]; + lastModified = [mapping objectForKey: changeNumber]; - return modseq; + return lastModified; } - (NSString *) changeNumberForMessageWithKey: (NSString *) messageKey From 3dff73636c0c401e1c13fae41d9490e8c657b889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Fri, 11 Dec 2015 11:33:41 +0100 Subject: [PATCH 38/50] oc: Use NSString index for version lookup It was using NSNumber in versions Dictionary for GCSMessages but it is stored as the NSString representation (0x390300000000001), so the lookup has always failed. --- OpenChange/MAPIStoreGCSMessageTable.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/OpenChange/MAPIStoreGCSMessageTable.m b/OpenChange/MAPIStoreGCSMessageTable.m index e6da41039..7a544c173 100644 --- a/OpenChange/MAPIStoreGCSMessageTable.m +++ b/OpenChange/MAPIStoreGCSMessageTable.m @@ -27,6 +27,7 @@ #import #import +#import #import #import @@ -38,7 +39,6 @@ #import "MAPIStoreTypes.h" #import "MAPIStoreGCSFolder.h" - #import "MAPIStoreGCSMessageTable.h" #undef DEBUG @@ -89,9 +89,12 @@ if (res->ulPropTag == PidTagChangeNumber) { + NSString *changeNumber; + value = NSObjectFromMAPISPropValue (&res->lpProp); + changeNumber = [NSString stringWithUnsignedLongLong: [(NSNumber *)value unsignedLongLongValue]]; lastModified = [(MAPIStoreGCSFolder *) - container lastModifiedFromMessageChangeNumber: value]; + container lastModifiedFromMessageChangeNumber: changeNumber]; //[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]]; //[self logWithFormat: @" c_lastmodified: %@", lastModified]; if (lastModified) From 7cdf48335b7ae8b25463e7acd04159c42f9e3db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Fri, 11 Dec 2015 11:35:55 +0100 Subject: [PATCH 39/50] oc: Support every operator for CN restriction No limitation is found in SQL queries. So use them all. --- OpenChange/MAPIStoreGCSMessageTable.m | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/OpenChange/MAPIStoreGCSMessageTable.m b/OpenChange/MAPIStoreGCSMessageTable.m index 7a544c173..0dac1fff9 100644 --- a/OpenChange/MAPIStoreGCSMessageTable.m +++ b/OpenChange/MAPIStoreGCSMessageTable.m @@ -99,14 +99,21 @@ //[self logWithFormat: @" c_lastmodified: %@", lastModified]; if (lastModified) { + SEL operator; + + operator = [self operatorFromRestrictionOperator: res->relop]; *qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_lastmodified" - operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo + operatorSelector: operator value: lastModified]; [*qualifier autorelease]; rc = MAPIRestrictionStateNeedsEval; } else - rc = MAPIRestrictionStateAlwaysTrue; + { + [self logWithFormat: @"No last modified found for: 0x%.16"PRIx64". Then no restriction applied", + [value unsignedLongLongValue]]; + rc = MAPIRestrictionStateAlwaysTrue; + } } else { From 5993fe97c396d868cf9ed46049cee41ba3c0e849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Fri, 11 Dec 2015 11:37:02 +0100 Subject: [PATCH 40/50] oc-db: Implement description message to ease debugging Which dumps the properties NSMutableDictionary --- OpenChange/MAPIStoreDBMessage.m | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/OpenChange/MAPIStoreDBMessage.m b/OpenChange/MAPIStoreDBMessage.m index bb5acbb57..836cfb4e1 100644 --- a/OpenChange/MAPIStoreDBMessage.m +++ b/OpenChange/MAPIStoreDBMessage.m @@ -109,6 +109,49 @@ return self; } +- (NSString *) description +{ + id key, value; + NSEnumerator *propEnumerator; + NSMutableString *description; + + description = [NSMutableString stringWithFormat: @"%@ %@. Properties: {", NSStringFromClass ([self class]), + [self url]]; + + propEnumerator = [properties keyEnumerator]; + while ((key = [propEnumerator nextObject])) + { + uint32_t proptag = 0; + if ([key isKindOfClass: [NSString class]] && [(NSString *)key intValue] > 0) + proptag = [(NSString *)key intValue]; + else if ([key isKindOfClass: [NSNumber class]]) + proptag = [key unsignedLongValue]; + + if (proptag > 0) + { + const char *propTagName = get_proptag_name ([key unsignedLongValue]); + NSString *propName; + + if (propTagName) + propName = [NSString stringWithCString: propTagName + encoding: NSUTF8StringEncoding]; + else + propName = [NSString stringWithFormat: @"0x%.4x", [key unsignedLongValue]]; + + [description appendFormat: @"'%@': ", propName]; + } + else + [description appendFormat: @"'%@': ", key]; + + value = [properties objectForKey: key]; + [description appendFormat: @"%@ (%@), ", value, NSStringFromClass ([value class])]; + } + + [description appendString: @"}\n"]; + + return description; +} + - (uint64_t) objectVersion { NSNumber *versionNbr; From a618386d5ecec5e13a5b37fa7583dd7f9a08fdbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Fri, 11 Dec 2015 11:38:21 +0100 Subject: [PATCH 41/50] oc-db: Store version number instead of CN To be able to search in this for every kind of operator. As we cannot do a migration, we have to add a new key to the property dictionary (@"version_number") which stores the version: version = exchange_globcnt(CN >> 16) Instead of the CN structure which is stored in @"version" key. This way we can do searches for CN to download only missing data from the given state of the client for this kind of messages. --- OpenChange/MAPIStoreDBMessage.m | 29 +++++++++++++++++++++------- OpenChange/MAPIStoreDBMessageTable.m | 9 +++++---- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/OpenChange/MAPIStoreDBMessage.m b/OpenChange/MAPIStoreDBMessage.m index 836cfb4e1..98d357bf7 100644 --- a/OpenChange/MAPIStoreDBMessage.m +++ b/OpenChange/MAPIStoreDBMessage.m @@ -154,16 +154,25 @@ - (uint64_t) objectVersion { - NSNumber *versionNbr; + /* Return the global counter from CN structure. + See [MS-OXCFXICS] Section 2.2.2.1 */ + NSNumber *versionNbr, *cn; uint64_t objectVersion; [(SOGoMAPIDBMessage *) sogoObject reloadIfNeeded]; - versionNbr = [properties objectForKey: @"version"]; + versionNbr = [properties objectForKey: @"version_number"]; if (versionNbr) - objectVersion = (([versionNbr unsignedLongLongValue] >> 16) - & 0x0000ffffffffffffLL); + objectVersion = exchange_globcnt ([versionNbr unsignedLongLongValue]); else - objectVersion = ULLONG_MAX; + { + /* Old version which stored the CN structure not useful for searching */ + cn = [properties objectForKey: @"version"]; + if (cn) + objectVersion = (([cn unsignedLongLongValue] >> 16) + & 0x0000ffffffffffffLL); + else + objectVersion = ULLONG_MAX; + } return objectVersion; } @@ -326,13 +335,19 @@ [properties setObject: attachmentParts forKey: @"attachments"]; newVersion = [[self context] getNewChangeNumber]; + newVersion = exchange_globcnt ((newVersion >> 16) & 0x0000ffffffffffffLL); + [properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion] - forKey: @"version"]; + forKey: @"version_number"]; + + /* Remove old version */ + [properties removeObjectForKey: @"version"]; /* Update PredecessorChangeList accordingly */ [self _updatePredecessorChangeList]; - [self logWithFormat: @"%d props in dict", [properties count]]; + // [self logWithFormat: @"Saving %@", [self description]]; + // [self logWithFormat: @"%d props in dict", [properties count]]; [sogoObject save]; } diff --git a/OpenChange/MAPIStoreDBMessageTable.m b/OpenChange/MAPIStoreDBMessageTable.m index de22e238e..a8c68abd1 100644 --- a/OpenChange/MAPIStoreDBMessageTable.m +++ b/OpenChange/MAPIStoreDBMessageTable.m @@ -61,14 +61,15 @@ static Class MAPIStoreDBMessageK = Nil; if ((uint32_t) res->ulPropTag == PidTagChangeNumber) { + SEL operator; + value = NSObjectFromMAPISPropValue (&res->lpProp); cVersion = exchange_globcnt (([value unsignedLongLongValue] >> 16) & 0x0000ffffffffffffLL); version = [NSNumber numberWithUnsignedLongLong: cVersion]; - //[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]]; - [self logWithFormat: @" version: %.16lx", cVersion]; - *qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"version" - operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo + operator = [self operatorFromRestrictionOperator: res->relop]; + *qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"version_number" + operatorSelector: operator value: version]; [*qualifier autorelease]; rc = MAPIRestrictionStateNeedsEval; From db17872dd64cd1ac7771181e6066f1f9a94aeac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Mon, 14 Dec 2015 23:39:32 +0100 Subject: [PATCH 42/50] oc: Dump property canonical name in dbmsgreader --- OpenChange/GNUmakefile | 3 ++- OpenChange/dbmsgreader.m | 47 +++++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 8006b21b9..4fc207904 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -145,7 +145,8 @@ $(DBMSGREADER_TOOL)_LIB_DIRS += \ -L../SoObjects/SOGo/SOGo.framework/sogo -lSOGo \ -L../SOPE/GDLContentStore/obj/ -lGDLContentStore \ -L../SOPE/NGCards/obj/ -lNGCards \ - -lNGObjWeb + -lNGObjWeb \ + $(LIBMAPI_LIBS) TEST_TOOL_NAME += $(PLREADER_TOOL) $(DBMSGREADER_TOOL) diff --git a/OpenChange/dbmsgreader.m b/OpenChange/dbmsgreader.m index 3d892ca34..5ad74b82b 100644 --- a/OpenChange/dbmsgreader.m +++ b/OpenChange/dbmsgreader.m @@ -1,8 +1,8 @@ /* dbmsgreader.m - this file is part of SOGo * * Copyright (C) 2011-2012 Inverse inc + * 2015 Enrique J. Hernandez * - * Author: Wolfgang Sourdeau * * 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 @@ -33,21 +33,58 @@ #import #import +#import + #import "MAPIStoreUserContext.h" #import #import #import "NSObject+PropertyList.h" -Class MAPIStoreUserContextK, SOGoCacheGCSObjectK; + +Class MAPIStoreUserContextK, SOGoCacheGCSObjectK, NSStringK; static void DumpBSONData(NSData *data) { + id key, value; + NSEnumerator *dictEnum; NSDictionary *dvalue; + NSMutableString *outStr; + NSUInteger max; + dvalue = [data BSONValue]; - [dvalue displayWithIndentation:0]; - printf("\n"); + max = [dvalue count]; + dictEnum = [dvalue keyEnumerator]; + NSStringK = [NSString class]; + outStr = [NSMutableString stringWithFormat: @"{ %d items\n", max]; + while ((key = [dictEnum nextObject])) + { + uint32_t proptag = 0; + if ([key isKindOfClass: NSStringK] && [(NSString *)key intValue] > 0) + proptag = [(NSString *)key intValue]; + + if (proptag > 0) + { + const char *propTagName = get_proptag_name (proptag); + NSString *propName; + + if (propTagName) + propName = [NSString stringWithCString: propTagName + encoding: NSUTF8StringEncoding]; + else + propName = [NSString stringWithFormat: @"0x%.4x", [key unsignedLongValue]]; + + [outStr appendFormat: @" %@ = ", propName]; + } + else + [outStr appendFormat: @" %@ = ", key]; + + value = [dvalue objectForKey: key]; + [outStr appendFormat: @"(%@) %@,\n", NSStringFromClass ([value class]), value]; + } + [outStr appendFormat: @"}\n"]; + printf ("%s\n", [outStr UTF8String]); } static void @@ -67,7 +104,7 @@ DbDumpObject (NSString *username, NSString *path) { printf("record found: %p\n", record); content = [[record objectForKey: @"c_content"] dataByDecodingBase64]; - DumpBSONData(content); + DumpBSONData (content); } else NSLog (@"record not found"); From 41e388c5f6aad3f65c3b2e98782f21e7abfa2bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Amor=20Garc=C3=ADa?= Date: Thu, 3 Dec 2015 13:17:18 +0100 Subject: [PATCH 43/50] sogo-mail: Priorize filename attribute to get attachment name Some clients (such apple mail) use only the filename attribute in the content/disposition header and the name attribute from the content/type header is filled with uninternationalized characters. Example: Content-Type: application/msword; x-apple-part-url=C4977556-0D01-4C6C-8A51-451E0AADE431; name=_______.doc Content-Disposition: attachment; filename*=utf-8''%D0%A0%D0%95%D0%9A%D0%92%D0%98%D0%97%D0%98.doc This changeset gives priority to the filename attribute. --- SoObjects/Mailer/NSDictionary+Mail.m | 59 +++++++++++++++------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/SoObjects/Mailer/NSDictionary+Mail.m b/SoObjects/Mailer/NSDictionary+Mail.m index 177ad38ac..3ed67ff39 100644 --- a/SoObjects/Mailer/NSDictionary+Mail.m +++ b/SoObjects/Mailer/NSDictionary+Mail.m @@ -30,14 +30,13 @@ { NSDictionary *parameters; NSString *filename; - - filename = [[self objectForKey: @"parameterList"] - objectForKey: @"name"]; - - if (!filename) + + filename = nil; + parameters = [[self objectForKey: @"disposition"] + objectForKey: @"parameterList"]; + + if (parameters) { - parameters = [[self objectForKey: @"disposition"] - objectForKey: @"parameterList"]; filename = [parameters objectForKey: @"filename"]; @@ -45,29 +44,33 @@ // See RFC2231 for details. If it was folded before, it will // be unfolded when we get here. if (!filename) - { - filename = [parameters objectForKey: @"filename*"]; - - if (filename) - { - NSRange r; - - filename = [filename stringByUnescapingURL]; - - // We skip up to the language - r = [filename rangeOfString: @"'"]; - - if (r.length) - { - r = [filename rangeOfString: @"'" options: 0 range: NSMakeRange(r.location+1, [filename length]-r.location-1)]; - - if (r.length) - filename = [filename substringFromIndex: r.location+1]; - } - } - } + { + filename = [parameters objectForKey: @"filename*"]; + + if (filename) + { + NSRange r; + + filename = [filename stringByUnescapingURL]; + + // We skip up to the language + r = [filename rangeOfString: @"'"]; + + if (r.length) + { + r = [filename rangeOfString: @"'" options: 0 range: NSMakeRange(r.location+1, [filename length]-r.location-1)]; + + if (r.length) + filename = [filename substringFromIndex: r.location+1]; + } + } + } } + if (!filename) + filename = [[self objectForKey: @"parameterList"] + objectForKey: @"name"]; + return filename; } From 916c04387b52d551a9496c19eb428e9271492653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Fri, 18 Dec 2015 12:23:49 +0100 Subject: [PATCH 44/50] oc-mail: Return error when delivery was not successful For example, if the SMTP is down, then the message is not sent and an error is returned. We returned back this error code to be managed by upper layer. --- OpenChange/MAPIStoreMailVolatileMessage.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index 90e459244..1db930ef8 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -1053,6 +1053,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS - (int) submitWithFlags: (enum SubmitFlags) flags { + enum mapistore_error rc = MAPISTORE_SUCCESS; NSDictionary *recipients; NSData *messageData; NSMutableArray *recipientEmails; @@ -1099,7 +1100,10 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS withAuthenticator: authenticator inContext: woContext]; if (error) - [self logWithFormat: @"an error occurred: '%@'", error]; + { + [self errorWithFormat: @"an error occurred: '%@'", error]; + rc = MAPISTORE_ERR_MSG_SEND; + } // mapping = [self mapping]; // [mapping unregisterURLWithID: [self objectId]]; @@ -1111,7 +1115,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS [self logWithFormat: @"skipping submit of message with class '%@'", msgClass]; - return MAPISTORE_SUCCESS; + return rc; } - (void) save: (TALLOC_CTX *) memCtx From d2ea6fef2eaca8b82b3389b5d993171b094ac0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Vall=C3=A9s?= Date: Fri, 11 Dec 2015 10:36:59 +0100 Subject: [PATCH 45/50] oc-calendar: Initialise NSCalendarDate with a SYSTEMTIME struct The method computes the date of a SYSTEMTIME structure, in which the day within the month is given by the Nth occurrence of a weekday (see [MS-OXOCAL] 2.2.1.39). --- OpenChange/NSDate+MAPIStore.h | 2 ++ OpenChange/NSDate+MAPIStore.m | 61 ++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/OpenChange/NSDate+MAPIStore.h b/OpenChange/NSDate+MAPIStore.h index 0e417d21f..d61df7b11 100644 --- a/OpenChange/NSDate+MAPIStore.h +++ b/OpenChange/NSDate+MAPIStore.h @@ -37,6 +37,8 @@ - (BOOL) isNever; /* occurs on 4500-12-31 */ ++ (NSCalendarDate *) dateFromSystemTime: (struct SYSTEMTIME) date + andRuleYear: (uint16_t) rYear; @end NSComparisonResult NSDateCompare (id date1, id date2, void *); diff --git a/OpenChange/NSDate+MAPIStore.m b/OpenChange/NSDate+MAPIStore.m index b96812adf..b8f09dcc6 100644 --- a/OpenChange/NSDate+MAPIStore.m +++ b/OpenChange/NSDate+MAPIStore.m @@ -24,6 +24,7 @@ #import #import +#import "MAPIStoreTypes.h" #import "NSDate+MAPIStore.h" #undef DEBUG @@ -48,7 +49,7 @@ _setupRefDate () refDate = [[NSCalendarDate alloc] initWithYear: 1601 month: 1 day: 1 hour: 0 minute: 0 second: 0 - timeZone: [NSTimeZone timeZoneWithName: @"UTC"]]; + timeZone: utcTZ]; } + (NSCalendarDate *) dateFromMinutesSince1601: (uint32_t) minutes @@ -128,6 +129,64 @@ _setupRefDate () return [calDate yearOfCommonEra] == 4500; } ++ (NSCalendarDate *) dateFromSystemTime: (struct SYSTEMTIME) date + andRuleYear: (uint16_t) rYear +{ + NSCalendarDate *result; + NSInteger daysToDate; + NSUInteger firstDayOfWeek, year; + + /* ([MS-OXOCAL] 2.2.1.41.1) When we're provided an absolute date (i.e., it + happens once), the SYSTEMTIME structure is enough to fill the date. + When we're parsing a SYSTEMTIME field from a time zone rule, however, a + relative date can be provided for the peroidicity of its periods. In this + scenario, the wYear field is empty and we have to use the wYear field in + the parent rule */ + if (date.wYear != 0) + year = date.wYear; + else + year = rYear; + + /* The wDay field indicates the occurrence of the wDayOfWeek within the month. + The 5th occurrence means the last one, even if it is the 4th. */ + if (date.wDay < 5) + { + result = [[NSCalendarDate alloc] initWithYear: year month: date.wMonth day: 1 + hour: date.wHour minute: date.wMinute second: date.wSecond + timeZone: utcTZ]; + [result autorelease]; + + firstDayOfWeek = [result dayOfWeek]; + + daysToDate = 7 * (date.wDay - 1) + date.wDayOfWeek - firstDayOfWeek; + if (date.wDayOfWeek < firstDayOfWeek) + daysToDate += 7; + + result = [result dateByAddingYears: 0 months: 0 days: daysToDate + hours: 0 minutes: 0 + seconds: 0]; + } + else + { + result = [[NSCalendarDate alloc] initWithYear: year month: date.wMonth + 1 day: 1 + hour: date.wHour minute: date.wMinute second: date.wSecond + timeZone: utcTZ]; + [result autorelease]; + + firstDayOfWeek = [result dayOfWeek]; + + daysToDate = date.wDayOfWeek - firstDayOfWeek; + if (date.wDayOfWeek >= firstDayOfWeek) + daysToDate -= 7; + + result = [result dateByAddingYears: 0 months: 0 days: daysToDate + hours: 0 minutes: 0 + seconds: 0]; + } + + return result; +} + @end NSComparisonResult From 332508e2dbb1e238d1b9d0a11ea9188dabb4f865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Vall=C3=A9s?= Date: Tue, 15 Dec 2015 10:40:19 +0100 Subject: [PATCH 46/50] oc-calendar: Use signed integer for time zone biases This change adapts the bias fields in the TimeZoneStruct and TZRule structures to the changes in openchange that allow this offsets to be negative (zentyal/openchange bba372faea29d942b9471e6bed90bf425dc4b231) --- OpenChange/iCalTimeZone+MAPIStore.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenChange/iCalTimeZone+MAPIStore.m b/OpenChange/iCalTimeZone+MAPIStore.m index f692272db..9a9e8fbb2 100644 --- a/OpenChange/iCalTimeZone+MAPIStore.m +++ b/OpenChange/iCalTimeZone+MAPIStore.m @@ -103,18 +103,18 @@ { iCalTimeZonePeriod *period; struct TimeZoneStruct tz; - int lBias, dlBias; + int32_t lBias, dlBias; memset (&tz, 0, sizeof (struct TimeZoneStruct)); period = [self _mostRecentPeriodWithName: @"STANDARD"]; lBias = -[period secondsOffsetFromGMT] / 60; - tz.lBias = (uint32_t) lBias; + tz.lBias = lBias; [period _fillTZDate: &tz.stStandardDate]; period = [self _mostRecentPeriodWithName: @"DAYLIGHT"]; if (!period) tz.stStandardDate.wMonth = 0; dlBias = -([period secondsOffsetFromGMT] / 60) - lBias; - tz.lDaylightBias = (uint32_t) (dlBias); + tz.lDaylightBias = dlBias; [period _fillTZDate: &tz.stDaylightDate]; tz.wStandardYear = tz.stStandardDate.wYear; tz.wDaylightYear = tz.stDaylightDate.wYear; @@ -153,13 +153,13 @@ period = [self _mostRecentPeriodWithName: @"STANDARD"]; rule.wYear = [[period startDate] yearOfCommonEra]; lBias = -[period secondsOffsetFromGMT] / 60; - rule.lBias = (uint32_t) lBias; + rule.lBias = lBias; [period _fillTZDate: &rule.stStandardDate]; period = [self _mostRecentPeriodWithName: @"DAYLIGHT"]; if (!period) rule.stStandardDate.wMonth = 0; dlBias = -([period secondsOffsetFromGMT] / 60) - lBias; - rule.lDaylightBias = (uint32_t) (dlBias); + rule.lDaylightBias = dlBias; [period _fillTZDate: &rule.stDaylightDate]; From 4ae5feb1318212a0d9a65e01f591f97890aac6a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Vall=C3=A9s?= Date: Fri, 11 Dec 2015 12:15:11 +0100 Subject: [PATCH 47/50] oc-calendar: Extract time zone from TimeZoneDefinition All-day and recurrent events have a binary property that describes the time zone they take place in. We were using the user's time zone in the webmail, but it may not be equal to the one in the client. This difference eventually leads to time shifts in events. --- OpenChange/MAPIStoreRecurrenceUtils.h | 3 +- OpenChange/MAPIStoreRecurrenceUtils.m | 8 +- OpenChange/iCalEvent+MAPIStore.m | 65 ++++----- OpenChange/iCalTimeZone+MAPIStore.h | 3 + OpenChange/iCalTimeZone+MAPIStore.m | 185 ++++++++++++++++++++++++++ 5 files changed, 228 insertions(+), 36 deletions(-) diff --git a/OpenChange/MAPIStoreRecurrenceUtils.h b/OpenChange/MAPIStoreRecurrenceUtils.h index df3700575..6912cb294 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.h +++ b/OpenChange/MAPIStoreRecurrenceUtils.h @@ -29,6 +29,7 @@ #import #import +#import @class NSTimeZone; @@ -46,7 +47,7 @@ fromRecurrencePattern: (struct RecurrencePattern *) rp withExceptions: (struct ExceptionInfo *) exInfos andExceptionCount: (uint16_t) exInfoCount - inTimeZone: (NSTimeZone *) tz; + inTimeZone: (iCalTimeZone *) tz; @end diff --git a/OpenChange/MAPIStoreRecurrenceUtils.m b/OpenChange/MAPIStoreRecurrenceUtils.m index 9e0bf29c3..e88fb5e4a 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.m +++ b/OpenChange/MAPIStoreRecurrenceUtils.m @@ -35,6 +35,7 @@ #import #import #import +#import #import "NSDate+MAPIStore.h" #import "MAPIStoreRecurrenceUtils.h" @@ -51,7 +52,7 @@ fromRecurrencePattern: (struct RecurrencePattern *) rp withExceptions: (struct ExceptionInfo *) exInfos andExceptionCount: (uint16_t) exInfoCount - inTimeZone: (NSTimeZone *) tz + inTimeZone: (iCalTimeZone *) tz { NSCalendarDate *startDate, *olEndDate, *untilDate, *exDate; @@ -63,7 +64,7 @@ iCalWeekOccurrence weekOccurrence; iCalWeekOccurrences dayMaskDays; NSUInteger count, max; - NSInteger bySetPos; + NSInteger bySetPos, tzOffset; unsigned char maskValue; [entity removeAllRecurrenceRules]; @@ -242,9 +243,10 @@ { /* The OriginalStartDate is in local time */ exDate = [NSDate dateFromMinutesSince1601: exInfos[count].OriginalStartDate]; + tzOffset = -[[tz periodForDate: exDate] secondsOffsetFromGMT]; exDate = [exDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 - seconds: - [tz secondsFromGMT]]; + seconds: tzOffset]; [exceptionDates removeObject: exDate]; } } diff --git a/OpenChange/iCalEvent+MAPIStore.m b/OpenChange/iCalEvent+MAPIStore.m index 90bf4e8bd..25877029b 100644 --- a/OpenChange/iCalEvent+MAPIStore.m +++ b/OpenChange/iCalEvent+MAPIStore.m @@ -36,6 +36,7 @@ #import #import #import +#import #import #import #import @@ -70,11 +71,12 @@ #include #import "iCalEvent+MAPIStore.h" +#import "iCalTimeZone+MAPIStore.h" @implementation iCalEvent (MAPIStoreProperties) - (void) _setupEventRecurrence: (NSData *) mapiRecurrenceData - inTimeZone: (NSTimeZone *) tz + inTimeZone: (iCalTimeZone *) tz inMemCtx: (TALLOC_CTX *) memCtx { struct Binary_r *blob; @@ -250,10 +252,8 @@ BOOL isAllDay; iCalDateTime *start, *end; iCalTimeZone *tz; - NSTimeZone *userTimeZone; - NSString *priority, *class = nil; + NSString *priority, *class = nil, *tzDescription = nil; NSUInteger responseStatus = 0; - NSInteger tzOffset; SOGoUser *ownerUser; id value; @@ -274,7 +274,31 @@ [self setAccessClass: @"PUBLIC"]; } - userTimeZone = [userContext timeZone]; + /* Time zone = PidLidAppointmentTimeZoneDefinitionRecur + or PidLidAppointmentTimeZoneDefinition[Start|End]Display */ + value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionStartDisplay)]; + if (!value) + { + value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionEndDisplay)]; + if (!value) + { + /* If PidLidtimeZoneStruct, TZID SHOULD come from PidLidTimeZoneDescription, + if PidLidAppointmentTimeZoneDefinition[Start|End]Display it MUST be derived from KeyName + (MS-OXCICAL] 2.1.3.1.1.19.1) */ + value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionRecur)]; + tzDescription = [properties objectForKey: MAPIPropertyKey (PidLidTimeZoneDescription)]; + } + } + if (value) + { + tz = [[iCalTimeZone alloc] iCalTimeZoneFromDefinition: value + withDescription: tzDescription + inMemCtx: memCtx]; + } + else + /* The client is more likely to have the webmail's time zone than any other */ + tz = [iCalTimeZone timeZoneForName: [[userContext timeZone] name]]; + [(iCalCalendar *) parent addTimeZone: tz]; /* CREATED */ value = [properties objectForKey: MAPIPropertyKey (PidTagCreationTime)]; @@ -306,20 +330,13 @@ objectForKey: MAPIPropertyKey (PidLidAppointmentSubType)]; if (value) isAllDay = [value boolValue]; - if (!isAllDay) - { - tz = [iCalTimeZone timeZoneForName: [userTimeZone name]]; - [(iCalCalendar *) parent addTimeZone: tz]; - } - else - tz = nil; // recurrence-id value = [properties objectForKey: MAPIPropertyKey (PidLidExceptionReplaceTime)]; if (value) [self setRecurrenceId: value]; - + // start value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)]; if (!value) @@ -330,15 +347,7 @@ [start setTimeZone: tz]; if (isAllDay) { - /* when user TZ is positive (East) all-day events were not - shown properly in SOGo UI. This day delay fixes it */ - tzOffset = [userTimeZone secondsFromGMTForDate: value]; - if (tzOffset > 0) - { - value = [value dateByAddingYears: 0 months: 0 days: 1 - hours: 0 minutes: 0 - seconds: 0]; - } + /* All-day events are set in floating time ([MS-OXCICAL] 2.1.3.1.1.20.8) */ [start setDate: value]; [start setTimeZone: nil]; } @@ -356,15 +365,7 @@ [end setTimeZone: tz]; if (isAllDay) { - /* when user TZ is positive (East) all-day events were not - shown properly in SOGo UI. This day delay fixes it */ - tzOffset = [userTimeZone secondsFromGMTForDate: value]; - if (tzOffset > 0) - { - value = [value dateByAddingYears: 0 months: 0 days: 1 - hours: 0 minutes: 0 - seconds: 0]; - } + /* All-day events are set in floating time ([MS-OXCICAL] 2.1.3.1.1.20.8) */ [end setDate: value]; [end setTimeZone: nil]; } @@ -467,7 +468,7 @@ value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentRecur)]; if (value) - [self _setupEventRecurrence: value inTimeZone: userTimeZone inMemCtx: memCtx]; + [self _setupEventRecurrence: value inTimeZone: tz inMemCtx: memCtx]; /* alarm */ [self _setupEventAlarmFromProperties: properties]; diff --git a/OpenChange/iCalTimeZone+MAPIStore.h b/OpenChange/iCalTimeZone+MAPIStore.h index 76b0a00a2..01bc51b82 100644 --- a/OpenChange/iCalTimeZone+MAPIStore.h +++ b/OpenChange/iCalTimeZone+MAPIStore.h @@ -30,6 +30,9 @@ - (struct Binary_r *) asTimeZoneStructInMemCtx: (TALLOC_CTX *) memCtx; - (struct Binary_r *) asZoneTimeDefinitionWithFlags: (enum TZRuleFlag) flags inMemCtx: (TALLOC_CTX *) memCtx; +- (iCalTimeZone *) iCalTimeZoneFromDefinition: (NSData *) value + withDescription: (NSString *) description + inMemCtx: (TALLOC_CTX *) memCtx; @end diff --git a/OpenChange/iCalTimeZone+MAPIStore.m b/OpenChange/iCalTimeZone+MAPIStore.m index 9a9e8fbb2..bfdf64ed4 100644 --- a/OpenChange/iCalTimeZone+MAPIStore.m +++ b/OpenChange/iCalTimeZone+MAPIStore.m @@ -23,11 +23,15 @@ #import #import #import +#import #import +#import #import #import #import "NSString+MAPIStore.h" +#import "NSData+MAPIStore.h" +#import "NSDate+MAPIStore.h" #include #include @@ -166,5 +170,186 @@ return set_TimeZoneDefinition (memCtx, &definition); } +- (NSString *) _offsetStringFromOffset: (NSInteger) offset +{ + NSInteger offsetHours, offsetMins; + NSString *offsetSign; + + /* The offset format is, eg, "+0200" for 2 hours 0 minutes ahead */ + if (offset < 0) + offsetSign = @"-"; + else + offsetSign = @"+"; + offsetHours = abs (offset) / 60; + offsetMins = abs (offset) % 60; + + return [NSString stringWithFormat: @"%@%d%d%d%d", + offsetSign, offsetHours / 10, offsetHours % 10, + offsetMins / 10, offsetMins % 10]; + +} + +- (NSString *) _rRuleStringFromSystemTime: (struct SYSTEMTIME) date +{ + NSString *result, *byDay; + + /* The conversion tables between the SYSTEMTIME fields and the RRULE ones + can be found at [MS-OXCICAL] 2.1.3.2.1 */ + if (date.wDay == 5) + byDay = @"-1"; + else + byDay = [NSString stringWithFormat: @"%d", date.wDay]; + + switch (date.wDayOfWeek) + { + case iCalWeekDaySunday: + byDay = [byDay stringByAppendingString: @"SU"]; + break; + case iCalWeekDayMonday: + byDay = [byDay stringByAppendingString: @"MO"]; + break; + case iCalWeekDayTuesday: + byDay = [byDay stringByAppendingString: @"TU"]; + break; + case iCalWeekDayWednesday: + byDay = [byDay stringByAppendingString: @"WE"]; + break; + case iCalWeekDayThursday: + byDay = [byDay stringByAppendingString: @"TH"]; + break; + case iCalWeekDayFriday: + byDay = [byDay stringByAppendingString: @"FR"]; + break; + case iCalWeekDaySaturday: + byDay = [byDay stringByAppendingString: @"SA"]; + break; + } + + result = [NSString stringWithFormat: @"FREQ=YEARLY;BYDAY=%@;BYMONTH=%d", byDay, date.wMonth]; + + return result; +} + +- (iCalTimeZone *) iCalTimeZoneFromDefinition: (NSData *) value + withDescription: (NSString *) description + inMemCtx: (TALLOC_CTX *) memCtx +{ + BOOL daylightDefined = NO, ruleFound = NO; + iCalDateTime *daylightStart, *standardStart; + iCalRecurrenceRule *daylightRRule, *standardRRule; + iCalTimeZone *tz = nil; + iCalTimeZonePeriod *daylight, *standard; + NSCalendarDate *dlStartValue, *stStartValue; + NSString *strOffsetFrom, *strOffsetTo, *tzID; + char *keyName; + struct Binary_r *binValue; + struct SYSTEMTIME initDate; + struct TimeZoneDefinition *definition; + struct TZRule rule; + uint16_t count; + + binValue = [value asBinaryInMemCtx: memCtx]; + definition = get_TimeZoneDefinition (memCtx, binValue); + + if (!definition) + return nil; + + if (!definition->cRules) + goto end; + + for (count = 0; count < definition->cRules; count++) + { + /* ([MS-OXCICAL] 2.1.3.1.1.19) The TZRule with the + TZRULE_FLAG_EFFECTIVE_TZREG bit set in the TZRule flags field + is the one that MUST be exported */ + if (definition->TZRules[count].flags & TZRULE_FLAG_EFFECTIVE_TZREG) + { + rule = definition->TZRules[count]; + ruleFound = YES; + break; + } + } + + if (!ruleFound) + goto end; + + if (!description) + { + /* The cbHeader field contains the size, in bytes of the Reserved (2b), + cchKeyName (2b) keyName (variable Unicode string) and cRules (2b) + ([MS-OXOCAL] 2.2.1.41). The keyName field is a non-NULL-terminated + char array. */ + keyName = talloc_strndup (memCtx, definition->keyName, (definition->cbHeader - 6) / 2); + tzID = [NSString stringWithCString: keyName + encoding: [NSString defaultCStringEncoding]]; + talloc_free (keyName); + } + else + tzID = [NSString stringWithString: description]; + + tz = [iCalTimeZone groupWithTag: @"vtimezone"]; + [tz addChild: [CardElement simpleElementWithTag: @"tzid" + value: tzID]]; + + if (rule.stStandardDate.wMonth != 0) + daylightDefined = YES; + + /* STANDARD TIME ([MS-OXCICAL] 2.1.3.1.1.19.2) */ + standard = [iCalTimeZonePeriod groupWithTag: @"standard"]; + + /* TZOFFSETFROM = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lDaylightBias) */ + strOffsetFrom = [self _offsetStringFromOffset: -1 * (rule.lBias + rule.lDaylightBias)]; + [standard addChild: [CardElement simpleElementWithTag: @"tzoffsetfrom" + value: strOffsetFrom]]; + + /* TZOFFSETTO = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lStandardBias) */ + strOffsetTo = [self _offsetStringFromOffset: -1 * (rule.lBias + rule.lStandardBias)]; + [standard addChild: [CardElement simpleElementWithTag: @"tzoffsetto" + value: strOffsetTo]]; + + /* DTSTART & RRULE are derived from the stStandardDate and wYear properties */ + standardStart = [iCalDateTime elementWithTag: @"dtstart"]; + + initDate = rule.stStandardDate; + stStartValue = [NSCalendarDate dateFromSystemTime: initDate + andRuleYear: rule.wYear]; + + [standardStart setDateTime: stStartValue]; + [standard addChild: standardStart]; + + if (daylightDefined) + { + standardRRule = [[iCalRecurrenceRule alloc] initWithString: [self _rRuleStringFromSystemTime: initDate]]; + [standard addChild: standardRRule]; + + /* DAYLIGHT SAVING TIME ([MS-OXCICAL] 2.1.3.1.1.19.3) */ + daylight = [iCalTimeZonePeriod groupWithTag: @"daylight"]; + /* TZOFFSETFROM = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lStandardBias) */ + [daylight addChild: [CardElement simpleElementWithTag: @"tzoffsetfrom" + value: strOffsetTo]]; + /* TZOFFSETTO = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lDaylightBias) */ + [daylight addChild: [CardElement simpleElementWithTag: @"tzoffsetto" + value: strOffsetFrom]]; + + /* DTSTART & RRULE are derived from the stDaylightDate and wYear properties */ + daylightStart = [iCalDateTime elementWithTag: @"dtstart"]; + initDate = rule.stDaylightDate; + dlStartValue = [NSCalendarDate dateFromSystemTime: initDate + andRuleYear: rule.wYear]; + + [daylightStart setDateTime: dlStartValue]; + [daylight addChild: daylightStart]; + + daylightRRule = [[iCalRecurrenceRule alloc] initWithString: [self _rRuleStringFromSystemTime: initDate]]; + [daylight addChild: daylightRRule]; + [tz addChild: daylight]; + } + [tz addChild: standard]; + +end: + + talloc_free (definition); + return tz; +} @end From dbfd86db0497d16e2eb5b7a54cf0c7fd43657630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Wed, 23 Dec 2015 00:27:09 +0100 Subject: [PATCH 48/50] oc: Set Editor as role is now possible in Outlook According to [MS-OXCPERM] Section 2.2.7 in PidTagMemberRights possible values, once we set the DeleteAny flag, the DeleteOwned flag must be set. Likewise EditOwned must be set when EditAny is set. In this way, the rights sent by the MAPI client are equal to the returned by the server when Editor is set. In real world practice, makes more strict Outlook 2013 work with editor permissions the sharing of user's defined calendars, tasks or contacts folders as the recipients can be editors of that folder. --- OpenChange/MAPIStoreCalendarFolder.m | 4 ++-- OpenChange/MAPIStoreContactsFolder.m | 4 ++-- OpenChange/MAPIStoreDBFolder.m | 4 ++-- OpenChange/MAPIStoreMailFolder.m | 4 ++-- OpenChange/MAPIStoreTasksFolder.m | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 2e131387b..5c9d70b39 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -117,11 +117,11 @@ if ([roles containsObject: SOGoRole_ObjectCreator]) rights |= RightsCreateItems; if ([roles containsObject: SOGoRole_ObjectEraser]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: SOGoCalendarRole_PublicModifier] && [roles containsObject: SOGoCalendarRole_PrivateModifier] && [roles containsObject: SOGoCalendarRole_ConfidentialModifier]) - rights |= RightsReadItems | RightsEditAll; + rights |= RightsReadItems | RightsEditAll | RightsEditOwn; else if ([roles containsObject: SOGoCalendarRole_PublicViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) diff --git a/OpenChange/MAPIStoreContactsFolder.m b/OpenChange/MAPIStoreContactsFolder.m index 823f467cd..a04110d0f 100644 --- a/OpenChange/MAPIStoreContactsFolder.m +++ b/OpenChange/MAPIStoreContactsFolder.m @@ -96,9 +96,9 @@ if ([roles containsObject: SOGoRole_ObjectCreator]) rights |= RightsCreateItems; if ([roles containsObject: SOGoRole_ObjectEraser]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: SOGoRole_ObjectEditor]) - rights |= RightsEditAll; + rights |= RightsEditAll | RightsEditOwn; if ([roles containsObject: SOGoRole_ObjectViewer]) rights |= RightsReadItems; if (rights != 0) diff --git a/OpenChange/MAPIStoreDBFolder.m b/OpenChange/MAPIStoreDBFolder.m index 9e7a6d713..a7c49b72d 100644 --- a/OpenChange/MAPIStoreDBFolder.m +++ b/OpenChange/MAPIStoreDBFolder.m @@ -321,9 +321,9 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; if ([roles containsObject: MAPIStoreRightDeleteOwn]) rights |= RightsDeleteOwn; if ([roles containsObject: MAPIStoreRightEditAll]) - rights |= RightsEditAll; + rights |= RightsEditAll | RightsEditOwn; if ([roles containsObject: MAPIStoreRightDeleteAll]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: MAPIStoreRightCreateSubfolders]) rights |= RightsCreateSubfolders; if ([roles containsObject: MAPIStoreRightFolderOwner]) diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 02e25acbd..6e87d4ecd 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -1711,10 +1711,10 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) rights |= RightsCreateItems; if ([roles containsObject: SOGoRole_ObjectEraser] && [roles containsObject: SOGoRole_FolderEraser]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: SOGoRole_ObjectEditor]) - rights |= RightsEditAll; + rights |= RightsEditAll | RightsEditOwn; if ([roles containsObject: SOGoRole_ObjectViewer]) rights |= RightsReadItems; if ([roles containsObject: SOGoRole_FolderCreator]) diff --git a/OpenChange/MAPIStoreTasksFolder.m b/OpenChange/MAPIStoreTasksFolder.m index d777b8e8b..9bc76def1 100644 --- a/OpenChange/MAPIStoreTasksFolder.m +++ b/OpenChange/MAPIStoreTasksFolder.m @@ -107,11 +107,11 @@ if ([roles containsObject: SOGoRole_ObjectCreator]) rights |= RightsCreateItems; if ([roles containsObject: SOGoRole_ObjectEraser]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: SOGoCalendarRole_PublicModifier] && [roles containsObject: SOGoCalendarRole_PrivateModifier] && [roles containsObject: SOGoCalendarRole_ConfidentialModifier]) - rights |= RightsReadItems | RightsEditAll; + rights |= RightsReadItems | RightsEditAll | RightsEditOwn; else if ([roles containsObject: SOGoCalendarRole_PublicViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) From 7fe1b5f0468caa4543d12bc8ad634b811079b521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Fri, 18 Dec 2015 10:43:46 +0100 Subject: [PATCH 49/50] oc: Do compile with Samba 4.1 As requested by @extrafu to maintain sogo packages for a little while --- OpenChange/MAPIStoreSamDBUtils.m | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/OpenChange/MAPIStoreSamDBUtils.m b/OpenChange/MAPIStoreSamDBUtils.m index 4b0db76ae..f26d173c2 100644 --- a/OpenChange/MAPIStoreSamDBUtils.m +++ b/OpenChange/MAPIStoreSamDBUtils.m @@ -25,6 +25,7 @@ #include #include #include +#include #import "NSData+MAPIStore.h" @@ -52,11 +53,20 @@ MAPIStoreSamDBUserAttribute (struct mapistore_connection_info *connInfo, attrs[0] = [attributeName UTF8String]; searchFormat = [NSString stringWithFormat: @"(&(objectClass=user)(%@=%%s))", userKey]; - ret = safe_ldb_search(&connInfo->sam_ctx, memCtx, &res, - ldb_get_default_basedn(connInfo->sam_ctx), - LDB_SCOPE_SUBTREE, attrs, - [searchFormat UTF8String], - [value UTF8String]); +#if SAMBA_VERSION_MAJOR <= 4 && SAMBA_VERSION_MINOR < 3 + ret = ldb_search (connInfo->sam_ctx, memCtx, &res, + ldb_get_default_basedn(connInfo->sam_ctx), + LDB_SCOPE_SUBTREE, attrs, + [searchFormat UTF8String], + [value UTF8String]); +#else + ret = safe_ldb_search (&connInfo->sam_ctx, memCtx, &res, + ldb_get_default_basedn(connInfo->sam_ctx), + LDB_SCOPE_SUBTREE, attrs, + [searchFormat UTF8String], + [value UTF8String]); +#endif + if (ret == LDB_SUCCESS && res->count == 1) { result = ldb_msg_find_attr_as_string (res->msgs[0], attrs[0], NULL); From 376e717f459d1a74f90ba8da6471b9d80e30722d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Vall=C3=A9s?= Date: Wed, 16 Dec 2015 18:49:09 +0100 Subject: [PATCH 50/50] oc-calendar: Use the calendar's time zone if it is present If the event was created by the MAPI client, the client's time zone (if present) is assigned to the event's calendar in iCalEvent+MAPIStore. This way, we can use it to deliver the event's properties correctly. --- OpenChange/MAPIStoreAppointmentWrapper.h | 5 +- OpenChange/MAPIStoreAppointmentWrapper.m | 149 ++++++------------ OpenChange/MAPIStoreCalendarEmbeddedMessage.m | 1 - OpenChange/MAPIStoreCalendarMessage.m | 1 - OpenChange/MAPIStoreMailMessage.m | 1 - OpenChange/MAPIStoreRecurrenceUtils.h | 5 - OpenChange/MAPIStoreRecurrenceUtils.m | 6 +- OpenChange/iCalTimeZone+MAPIStore.h | 2 + OpenChange/iCalTimeZone+MAPIStore.m | 22 +++ 9 files changed, 73 insertions(+), 119 deletions(-) diff --git a/OpenChange/MAPIStoreAppointmentWrapper.h b/OpenChange/MAPIStoreAppointmentWrapper.h index aecf2200f..d114076fe 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.h +++ b/OpenChange/MAPIStoreAppointmentWrapper.h @@ -24,6 +24,7 @@ #define MAPISTORECALENDARWRAPPER_H #import +#import #import #import "MAPIStoreObjectProxy.h" @@ -42,7 +43,7 @@ iCalCalendar *calendar; iCalEvent *firstEvent; iCalEvent *event; - NSTimeZone *timeZone; + iCalTimeZone *timeZone; SOGoUser *user; NSString *senderEmail; NSData *globalObjectId; @@ -57,12 +58,10 @@ + (id) wrapperWithICalEvent: (iCalEvent *) newEvent andUser: (SOGoUser *) newUser andSenderEmail: (NSString *) newSenderEmail - inTimeZone: (NSTimeZone *) newTimeZone withConnectionInfo: (struct mapistore_connection_info *) newConnInfo; - (id) initWithICalEvent: (iCalEvent *) newEvent andUser: (SOGoUser *) newUser andSenderEmail: (NSString *) newSenderEmail - inTimeZone: (NSTimeZone *) newTimeZone withConnectionInfo: (struct mapistore_connection_info *) newConnInfo; /* getters */ diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index c8c6560fb..11a189500 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -37,7 +37,9 @@ #import #import #import +#import #import +#import #import #import @@ -80,7 +82,6 @@ static NSCharacterSet *hexCharacterSet = nil; + (id) wrapperWithICalEvent: (iCalEvent *) newEvent andUser: (SOGoUser *) newUser andSenderEmail: (NSString *) newSenderEmail - inTimeZone: (NSTimeZone *) newTimeZone withConnectionInfo: (struct mapistore_connection_info *) newConnInfo { MAPIStoreAppointmentWrapper *wrapper; @@ -88,7 +89,6 @@ static NSCharacterSet *hexCharacterSet = nil; wrapper = [[self alloc] initWithICalEvent: newEvent andUser: newUser andSenderEmail: newSenderEmail - inTimeZone: newTimeZone withConnectionInfo: newConnInfo]; [wrapper autorelease]; @@ -182,10 +182,10 @@ static NSCharacterSet *hexCharacterSet = nil; - (id) initWithICalEvent: (iCalEvent *) newEvent andUser: (SOGoUser *) newUser andSenderEmail: (NSString *) newSenderEmail - inTimeZone: (NSTimeZone *) newTimeZone withConnectionInfo: (struct mapistore_connection_info *) newConnInfo { NSArray *events; + iCalTimeZone *tz; if ((self = [self init])) { @@ -194,9 +194,20 @@ static NSCharacterSet *hexCharacterSet = nil; event = newEvent; events = [calendar events]; firstEvent = [events objectAtIndex: 0]; - ASSIGN (timeZone, newTimeZone); ASSIGN (user, newUser); ASSIGN (senderEmail, newSenderEmail); + /* If newEvent comes from the client, we set its time zone in + updateFromMAPIProperties. If it is not present, we use the + time zone of the user */ + tz = (iCalTimeZone *) [calendar firstChildWithTag: @"vtimezone"]; + if (!tz) + { + tz = [iCalTimeZone timeZoneForName: [[[user userDefaults] timeZone] name]]; + if (!tz) + [self logWithFormat: @"no time zone could be set"]; + } + ASSIGN (timeZone, tz); + [self _setupITIPContext]; } @@ -721,22 +732,15 @@ static NSCharacterSet *hexCharacterSet = nil; inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; - NSInteger offset; // if ([event isRecurrent]) // dateValue = [event firstRecurrenceStartDate]; // else dateValue = [event startDate]; if ([event isAllDay]) - { - offset = -[timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } - [dateValue setTimeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -749,22 +753,14 @@ static NSCharacterSet *hexCharacterSet = nil; exceptions, where it is the normal start date for the day of the exception. */ NSCalendarDate *dateValue; - NSInteger offset; dateValue = [event recurrenceId]; if (!dateValue) dateValue = [event startDate]; - [dateValue setTimeZone: timeZone]; if ([event isAllDay]) - { - offset = -[timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } - [dateValue setTimeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -772,19 +768,12 @@ static NSCharacterSet *hexCharacterSet = nil; inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; - NSInteger offset; dateValue = [firstEvent startDate]; if ([firstEvent isAllDay]) - { - offset = -[timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } - [dateValue setTimeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -804,8 +793,8 @@ static NSCharacterSet *hexCharacterSet = nil; month: [start monthOfYear] day: [start dayOfMonth] hour: 0 minute: 0 second: 0 - timeZone: timeZone]; - [dateValue setTimeZone: utcTZ]; + timeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; rc = MAPISTORE_SUCCESS; } @@ -829,7 +818,7 @@ static NSCharacterSet *hexCharacterSet = nil; dateValue = [event startDate]; offset = [event durationAsTimeInterval]; if ([event isAllDay]) - offset -= [timeZone secondsFromGMTForDate: dateValue]; + offset -= [[timeZone periodForDate: dateValue] secondsOffsetFromGMT]; dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: offset]; @@ -847,15 +836,14 @@ static NSCharacterSet *hexCharacterSet = nil; dateValue = [event recurrenceId]; if (!dateValue) dateValue = [event startDate]; - [dateValue setTimeZone: timeZone]; offset = [firstEvent durationAsTimeInterval]; if ([firstEvent isAllDay]) - offset -= [timeZone secondsFromGMTForDate: dateValue]; + offset -= [[timeZone periodForDate: dateValue] secondsOffsetFromGMT]; dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: offset]; *data = [dateValue asFileTimeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -871,7 +859,7 @@ static NSCharacterSet *hexCharacterSet = nil; dateValue = [firstEvent startDate]; offset = [firstEvent durationAsTimeInterval]; if ([event isAllDay]) - offset -= [timeZone secondsFromGMTForDate: dateValue]; + offset -= [[timeZone periodForDate: dateValue] secondsOffsetFromGMT]; dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: offset]; @@ -885,23 +873,14 @@ static NSCharacterSet *hexCharacterSet = nil; { enum mapistore_error rc; NSCalendarDate *dateValue; - NSInteger offset; iCalRecurrenceRule *rrule; if ([event isRecurrent]) { rrule = [[event recurrenceRules] objectAtIndex: 0]; dateValue = [rrule untilDate]; - if (dateValue) - { - if ([event isAllDay]) - offset = -[timeZone secondsFromGMTForDate: dateValue]; - else - offset = 0; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } + if (dateValue && [event isAllDay]) + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; else dateValue = [NSCalendarDate dateWithYear: 4500 month: 8 day: 31 hour: 23 minute: 59 second: 00 @@ -1338,21 +1317,14 @@ static NSCharacterSet *hexCharacterSet = nil; { enum mapistore_error rc; NSCalendarDate *dateValue; - NSInteger offset; dateValue = [event recurrenceId]; if (dateValue) { rc = MAPISTORE_SUCCESS; - + if ([event isAllDay]) - { - offset = -[timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } - [dateValue setTimeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; } else @@ -1377,7 +1349,6 @@ static NSCharacterSet *hexCharacterSet = nil; iCalEventChanges *changes; NSArray *changedProperties; NSCalendarDate *dateValue; - NSInteger offset; changes = [iCalEventChanges changesFromEvent: event toEvent: exceptionEvent]; @@ -1385,28 +1356,17 @@ static NSCharacterSet *hexCharacterSet = nil; memset (extendedException, 0, sizeof (struct ExtendedException)); extendedException->ChangeHighlight.Size = sizeof (uint32_t); - dateValue = [exceptionEvent startDate]; - offset = [timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; + dateValue = [timeZone computedDateForDate: [exceptionEvent startDate]]; exceptionInfo->StartDateTime = [dateValue asMinutesSince1601]; extendedException->ChangeHighlight.Value = BIT_CH_START; extendedException->StartDateTime = exceptionInfo->StartDateTime; - dateValue = [exceptionEvent endDate]; - offset = [timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; + dateValue = [timeZone computedDateForDate: [exceptionEvent endDate]]; exceptionInfo->EndDateTime = [dateValue asMinutesSince1601]; extendedException->ChangeHighlight.Value |= BIT_CH_END; extendedException->EndDateTime = exceptionInfo->EndDateTime; - dateValue = [[exceptionEvent recurrenceId] - dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; + dateValue = [timeZone computedDateForDate: [exceptionEvent recurrenceId]]; exceptionInfo->OriginalStartDate = [dateValue asMinutesSince1601]; extendedException->OriginalStartDate = exceptionInfo->OriginalStartDate; @@ -1464,7 +1424,6 @@ static NSCharacterSet *hexCharacterSet = nil; arp = talloc_zero (NULL, struct AppointmentRecurrencePattern); [rule fillRecurrencePattern: &arp->RecurrencePattern withEvent: event - inTimeZone: timeZone inMemCtx: arp]; arp->ReaderVersion2 = 0x00003006; arp->WriterVersion2 = 0x00003008; /* 0x3008 for compatibility with @@ -1475,7 +1434,7 @@ static NSCharacterSet *hexCharacterSet = nil; fields are relative to midnight of those days ([MS-OXOCAL] 2.2.1.44.5), so no time zone adjustment is needed */ if (![event isAllDay]) - [firstStartDate setTimeZone: timeZone]; + firstStartDate = [timeZone computedDateForDate: firstStartDate]; startMinutes = ([firstStartDate hourOfDay] * 60 + [firstStartDate minuteOfHour]); arp->StartTimeOffset = startMinutes; @@ -1701,15 +1660,16 @@ ReservedBlockEE2Size: 00 00 00 00 fromDate: (NSCalendarDate *) instanceDate; { uint16_t year; + NSCalendarDate *dateValue; if (instanceDate) { - [instanceDate setTimeZone: timeZone]; - year = [instanceDate yearOfCommonEra]; + dateValue = [timeZone computedDateForDate: instanceDate]; + year = [dateValue yearOfCommonEra]; newGlobalId->YH = year >> 8; newGlobalId->YL = year & 0xff; - newGlobalId->Month = [instanceDate monthOfYear]; - newGlobalId->D = [instanceDate dayOfMonth]; + newGlobalId->Month = [dateValue monthOfYear]; + newGlobalId->D = [dateValue dayOfMonth]; } } @@ -1974,7 +1934,6 @@ ReservedBlockEE2Size: 00 00 00 00 if (alarm) { alarmDate = [alarm nextAlarmDate]; - [alarmDate setTimeZone: utcTZ]; *data = [alarmDate asFileTimeInMemCtx: memCtx]; } else @@ -2036,8 +1995,7 @@ ReservedBlockEE2Size: 00 00 00 00 enum mapistore_error rc; NSString *tzid; - tzid = [(iCalDateTime *) [event firstChildWithTag: @"dtstart"] - value: 0 ofAttribute: @"tzid"]; + tzid = [timeZone tzId]; if ([tzid length] > 0) { *data = [tzid asUnicodeInMemCtx: memCtx]; @@ -2053,16 +2011,9 @@ ReservedBlockEE2Size: 00 00 00 00 inMemCtx: (TALLOC_CTX *) memCtx { enum mapistore_error rc; - iCalTimeZone *icalTZ; - icalTZ = [(iCalDateTime *) [event firstChildWithTag: @"dtstart"] timeZone]; - if (icalTZ) - { - *data = [icalTZ asTimeZoneStructInMemCtx: memCtx]; - rc = MAPISTORE_SUCCESS; - } - else - rc = MAPISTORE_ERR_NOT_FOUND; + *data = [timeZone asTimeZoneStructInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; return rc; } @@ -2071,24 +2022,16 @@ ReservedBlockEE2Size: 00 00 00 00 inMemCtx: (TALLOC_CTX *) memCtx { enum mapistore_error rc; - iCalTimeZone *icalTZ; /* [MS-OXOCAL] 3.1.5.5.1: This property is used in floating (all-day) events, specified in floating time, to convert the start date from UTC to the user's time zone */ - if ([event isAllDay]) - icalTZ = [iCalTimeZone timeZoneForName: [timeZone timeZoneName]]; - else if ([event isRecurrent]) - icalTZ = [(iCalDateTime *) [event firstChildWithTag: @"dtstart"] timeZone]; - else - icalTZ = nil; - - if (icalTZ) + if ([event isAllDay] | [event isRecurrent]) { /* [MS-OXOCAL] 2.2.1.42: This property can only have the E flag set in the TimeZoneDefinition struct */ - *data = [icalTZ asZoneTimeDefinitionWithFlags: TZRULE_FLAG_EFFECTIVE_TZREG - inMemCtx: memCtx]; + *data = [timeZone asZoneTimeDefinitionWithFlags: TZRULE_FLAG_EFFECTIVE_TZREG + inMemCtx: memCtx]; rc = MAPISTORE_SUCCESS; } else diff --git a/OpenChange/MAPIStoreCalendarEmbeddedMessage.m b/OpenChange/MAPIStoreCalendarEmbeddedMessage.m index ca3ba12d6..986b106d3 100644 --- a/OpenChange/MAPIStoreCalendarEmbeddedMessage.m +++ b/OpenChange/MAPIStoreCalendarEmbeddedMessage.m @@ -58,7 +58,6 @@ wrapperWithICalEvent: [newContainer event] andUser: [userContext sogoUser] andSenderEmail: nil - inTimeZone: [userContext timeZone] withConnectionInfo: [context connectionInfo]]; [self addProxy: appointmentWrapper]; } diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index 7f025e400..108471eae 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -197,7 +197,6 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK; = [MAPIStoreAppointmentWrapper wrapperWithICalEvent: masterEvent andUser: [userContext sogoUser] andSenderEmail: nil - inTimeZone: [userContext timeZone] withConnectionInfo: [context connectionInfo]]; [self addProxy: appointmentWrapper]; } diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 235ca5698..f79511eb6 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -340,7 +340,6 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) wrapperWithICalEvent: event andUser: [context activeUser] andSenderEmail: senderEmail - inTimeZone: [[self userContext] timeZone] withConnectionInfo: [context connectionInfo]]; [appointmentWrapper retain]; } diff --git a/OpenChange/MAPIStoreRecurrenceUtils.h b/OpenChange/MAPIStoreRecurrenceUtils.h index 6912cb294..dbb2f0c19 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.h +++ b/OpenChange/MAPIStoreRecurrenceUtils.h @@ -25,14 +25,10 @@ #include -#import - #import #import #import -@class NSTimeZone; - @class iCalEvent; @class iCalRepeatableEntityObject; @class iCalRecurrenceRule; @@ -55,7 +51,6 @@ - (void) fillRecurrencePattern: (struct RecurrencePattern *) rp withEvent: (iCalEvent *) event - inTimeZone: (NSTimeZone *) timeZone inMemCtx: (TALLOC_CTX *) memCtx; @end diff --git a/OpenChange/MAPIStoreRecurrenceUtils.m b/OpenChange/MAPIStoreRecurrenceUtils.m index e88fb5e4a..90052474a 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.m +++ b/OpenChange/MAPIStoreRecurrenceUtils.m @@ -24,7 +24,6 @@ #import #import #import -#import #import #import @@ -265,7 +264,6 @@ - (void) fillRecurrencePattern: (struct RecurrencePattern *) rp withEvent: (iCalEvent *) event - inTimeZone: (NSTimeZone *) timeZone inMemCtx: (TALLOC_CTX *) memCtx { iCalRecurrenceFrequency freq; @@ -281,10 +279,8 @@ NSMutableArray *deletedDates, *modifiedDates; startDate = [event firstRecurrenceStartDate]; - [startDate setTimeZone: timeZone]; endDate = [event lastPossibleRecurrenceStartDate]; - [endDate setTimeZone: timeZone]; - + rp->ReaderVersion = 0x3004; rp->WriterVersion = 0x3004; diff --git a/OpenChange/iCalTimeZone+MAPIStore.h b/OpenChange/iCalTimeZone+MAPIStore.h index 01bc51b82..25817392a 100644 --- a/OpenChange/iCalTimeZone+MAPIStore.h +++ b/OpenChange/iCalTimeZone+MAPIStore.h @@ -33,6 +33,8 @@ - (iCalTimeZone *) iCalTimeZoneFromDefinition: (NSData *) value withDescription: (NSString *) description inMemCtx: (TALLOC_CTX *) memCtx; +- (NSCalendarDate *) shiftedCalendarDateForDate: (NSCalendarDate *) date; + @end diff --git a/OpenChange/iCalTimeZone+MAPIStore.m b/OpenChange/iCalTimeZone+MAPIStore.m index bfdf64ed4..7cf380fdf 100644 --- a/OpenChange/iCalTimeZone+MAPIStore.m +++ b/OpenChange/iCalTimeZone+MAPIStore.m @@ -40,6 +40,7 @@ #include #import "iCalTimeZone+MAPIStore.h" +#import "MAPIStoreTypes.h" @interface iCalTimeZonePeriod (MAPIStorePropertiesPrivate) @@ -352,4 +353,25 @@ end: return tz; } +/** + * Adjust a date in this vTimeZone to its representation in UTC + * Example: Timezone is +0001, the date is 2015-12-15 00:00:00 +0000 + * it returns 2015-12-14 23:00:00 +0000 + * @param date the date to adjust to the timezone. + * @return a new GMT date adjusted with the offset of the timezone. + */ +- (NSCalendarDate *) shiftedCalendarDateForDate: (NSCalendarDate *) date +{ + NSCalendarDate *tmpDate; + + tmpDate = [date copy]; + [tmpDate autorelease]; + + [tmpDate setTimeZone: utcTZ]; + + return [tmpDate addYear: 0 month: 0 day: 0 + hour: 0 minute: 0 + second: -[[self periodForDate: tmpDate] secondsOffsetFromGMT]]; +} + @end