From 15bbfeda3abe564a140c1f02de6bbc8d4a3b3d9c Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 2 Jun 2015 08:25:54 -0400 Subject: [PATCH 01/47] (fix) renamed scripts 2.1.17 -> 2.2.17 --- ...1.17_to_2.3.0-mysql.sh => sql-update-2.2.17_to_2.3.0-mysql.sh} | 0 ...ql-update-2.1.17_to_2.3.0.sh => sql-update-2.2.17_to_2.3.0.sh} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Scripts/{sql-update-2.1.17_to_2.3.0-mysql.sh => sql-update-2.2.17_to_2.3.0-mysql.sh} (100%) rename Scripts/{sql-update-2.1.17_to_2.3.0.sh => sql-update-2.2.17_to_2.3.0.sh} (100%) diff --git a/Scripts/sql-update-2.1.17_to_2.3.0-mysql.sh b/Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh similarity index 100% rename from Scripts/sql-update-2.1.17_to_2.3.0-mysql.sh rename to Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh diff --git a/Scripts/sql-update-2.1.17_to_2.3.0.sh b/Scripts/sql-update-2.2.17_to_2.3.0.sh similarity index 100% rename from Scripts/sql-update-2.1.17_to_2.3.0.sh rename to Scripts/sql-update-2.2.17_to_2.3.0.sh From 9435a22905eca35b4976940f2cb366073f88649e Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 2 Jun 2015 08:41:53 -0400 Subject: [PATCH 02/47] (fix) added tmpfs systemd script --- Scripts/sogo-systemd.conf | 2 ++ packaging/rhel/sogo.spec | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 Scripts/sogo-systemd.conf diff --git a/Scripts/sogo-systemd.conf b/Scripts/sogo-systemd.conf new file mode 100644 index 000000000..6755a2969 --- /dev/null +++ b/Scripts/sogo-systemd.conf @@ -0,0 +1,2 @@ +# SOGo needs directory in /var/run +d /var/run/sogo 0755 sogo sogo diff --git a/packaging/rhel/sogo.spec b/packaging/rhel/sogo.spec index 0b73d702b..364e86fe7 100644 --- a/packaging/rhel/sogo.spec +++ b/packaging/rhel/sogo.spec @@ -253,6 +253,8 @@ cp Scripts/logrotate ${RPM_BUILD_ROOT}/etc/logrotate.d/sogo %if 0%{?_with_systemd} cp Scripts/sogo-systemd-redhat ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service chmod 644 ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service + cp Scripts/sogo-systemd.conf ${RPM_BUILD_ROOT}/etc/tmpfiles.d/ + chmod 644 ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo-systemd.conf %else cp Scripts/sogo-init.d-redhat ${RPM_BUILD_ROOT}/etc/init.d/sogod chmod 755 ${RPM_BUILD_ROOT}/etc/init.d/sogod @@ -289,6 +291,7 @@ rm -fr ${RPM_BUILD_ROOT} %if 0%{?_with_systemd} /usr/lib/systemd/system/sogod.service +/etc/tmpfiles.d/sogo-systemd.conf %else /etc/init.d/sogod %endif From 4df323eddb23ddc5703501711baf18e2311c3e6b Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 2 Jun 2015 08:46:17 -0400 Subject: [PATCH 03/47] (fix) fixed the target path and name --- packaging/rhel/sogo.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/rhel/sogo.spec b/packaging/rhel/sogo.spec index 364e86fe7..74fdef58c 100644 --- a/packaging/rhel/sogo.spec +++ b/packaging/rhel/sogo.spec @@ -253,7 +253,7 @@ cp Scripts/logrotate ${RPM_BUILD_ROOT}/etc/logrotate.d/sogo %if 0%{?_with_systemd} cp Scripts/sogo-systemd-redhat ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service chmod 644 ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service - cp Scripts/sogo-systemd.conf ${RPM_BUILD_ROOT}/etc/tmpfiles.d/ + cp Scripts/sogo-systemd.conf ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo.conf chmod 644 ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo-systemd.conf %else cp Scripts/sogo-init.d-redhat ${RPM_BUILD_ROOT}/etc/init.d/sogod @@ -291,7 +291,7 @@ rm -fr ${RPM_BUILD_ROOT} %if 0%{?_with_systemd} /usr/lib/systemd/system/sogod.service -/etc/tmpfiles.d/sogo-systemd.conf +/etc/tmpfiles.d/sogo.conf %else /etc/init.d/sogod %endif From 2f533f6dae241d28d0145abc9934dbde31317d91 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 2 Jun 2015 09:00:21 -0400 Subject: [PATCH 04/47] (fix) create ${RPM_BUILD_ROOT}/etc/tmpfiles.d/ if not present (test) --- packaging/rhel/sogo.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/rhel/sogo.spec b/packaging/rhel/sogo.spec index 74fdef58c..3635373ba 100644 --- a/packaging/rhel/sogo.spec +++ b/packaging/rhel/sogo.spec @@ -253,6 +253,7 @@ cp Scripts/logrotate ${RPM_BUILD_ROOT}/etc/logrotate.d/sogo %if 0%{?_with_systemd} cp Scripts/sogo-systemd-redhat ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service chmod 644 ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service + mkdir ${RPM_BUILD_ROOT}/etc/tmpfiles.d cp Scripts/sogo-systemd.conf ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo.conf chmod 644 ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo-systemd.conf %else From 18a0422367b442790d205c3bd5ed8b9ef8f4d9a0 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 2 Jun 2015 09:03:34 -0400 Subject: [PATCH 05/47] (fix) wrong filename for chmod --- packaging/rhel/sogo.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/rhel/sogo.spec b/packaging/rhel/sogo.spec index 3635373ba..d853d23b0 100644 --- a/packaging/rhel/sogo.spec +++ b/packaging/rhel/sogo.spec @@ -255,7 +255,7 @@ cp Scripts/logrotate ${RPM_BUILD_ROOT}/etc/logrotate.d/sogo chmod 644 ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service mkdir ${RPM_BUILD_ROOT}/etc/tmpfiles.d cp Scripts/sogo-systemd.conf ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo.conf - chmod 644 ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo-systemd.conf + chmod 644 ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo.conf %else cp Scripts/sogo-init.d-redhat ${RPM_BUILD_ROOT}/etc/init.d/sogod chmod 755 ${RPM_BUILD_ROOT}/etc/init.d/sogod From 75206f1f2cefa256f8fb324109f11fd132565706 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 2 Jun 2015 12:57:37 -0400 Subject: [PATCH 06/47] (fix) improved EAS speed, especially when fetching big attachments --- ActiveSync/NSData+ActiveSync.m | 28 +++++++++++++++++++++++++++- NEWS | 6 ++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/ActiveSync/NSData+ActiveSync.m b/ActiveSync/NSData+ActiveSync.m index dd925c715..cf059e66f 100644 --- a/ActiveSync/NSData+ActiveSync.m +++ b/ActiveSync/NSData+ActiveSync.m @@ -66,7 +66,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { - return [[self stringByEncodingBase64] stringByReplacingString: @"\n" withString: @""]; + NSString *tmp, *s; + unichar *buf, *start, c; + int len, i, j; + + tmp = [self stringByEncodingBase64] ; + + len = [tmp length]; + + start = buf = (unichar *)malloc(len*sizeof(unichar)); + [tmp getCharacters: buf range: NSMakeRange(0, len)]; + + for (i = 0, j = 0; i < len; i++) + { + c = *buf; + + if (!(c == 0xA)) + { + *(start+j) = c; + j++; + } + + buf++; + } + + s = [[NSString alloc] initWithCharactersNoCopy: start length: j freeWhenDone: YES]; + + return AUTORELEASE(s); } - (NSData *) wbxml2xml diff --git a/NEWS b/NEWS index ec8cf7a00..62ded5385 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +2.3.1 (2015-06-XX) +------------------ + +Enhancements + - improved EAS speed, especially when fetching big attachments + 2.3.0 (2015-06-01) ------------------- From ccbaea69995aaf17b15e55312dd398636de9ead2 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 2 Jun 2015 13:05:37 -0400 Subject: [PATCH 07/47] (fix) EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders --- ActiveSync/SOGoActiveSyncDispatcher.m | 193 ++++++++++++++++++++------ NEWS | 3 + 2 files changed, 154 insertions(+), 42 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 7cd0e5c53..2ed420f13 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -1084,6 +1084,8 @@ static BOOL debugOn = NO; SOGoMailAccounts *accountsFolder; SOGoUserFolder *userFolder; SOGoMailObject *mailObject; + NSArray *partKeys; + int p; NSRange r1, r2; @@ -1103,7 +1105,14 @@ static BOOL debugOn = NO; acquire: NO]; mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; - currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context]; + + partKeys = [pathToPart componentsSeparatedByString: @"."]; + + currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context]; + for (p = 1; p < [partKeys count]; p++) + { + currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context]; + } [theResponse setHeader: [NSString stringWithFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]] forKey: @"Content-Type"]; @@ -1158,11 +1167,11 @@ static BOOL debugOn = NO; { collectionId = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"CollectionId"] lastObject] textValue]; realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; - + if (folderType == ActiveSyncMailFolder) - nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId]; + nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId]; else - nameInCache = collectionId; + nameInCache = collectionId; realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; @@ -1234,10 +1243,11 @@ static BOOL debugOn = NO; - (void) processItemOperations: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - NSString *fileReference, *realCollectionId; + NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId; NSMutableString *s; NSArray *fetchRequests; id aFetch; + NSData *d; int i; SOGoMicrosoftActiveSyncFolderType folderType; @@ -1247,8 +1257,6 @@ static BOOL debugOn = NO; [s appendString: @""]; [s appendString: @""]; [s appendString: @""]; - [s appendString: @"1"]; - [s appendString: @""]; fetchRequests = (id)[theDocumentElement getElementsByTagName: @"Fetch"]; @@ -1256,17 +1264,25 @@ static BOOL debugOn = NO; { NSMutableData *bytes, *parts; NSMutableArray *partLength; - NSData *d; bytes = [NSMutableData data]; parts = [NSMutableData data]; partLength = [NSMutableArray array]; + [s appendString: @"1"]; + [s appendString: @""]; + for (i = 0; i < [fetchRequests count]; i++) { aFetch = [fetchRequests objectAtIndex: i]; fileReference = [[[(id)[aFetch getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL]; - realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType]; + collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue]; + + // its either a itemOperation to fetch an attachment or an email + if ([fileReference length]) + realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType]; + else + realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; if (folderType == ActiveSyncMailFolder) { @@ -1276,43 +1292,80 @@ static BOOL debugOn = NO; SOGoUserFolder *userFolder; SOGoMailObject *mailObject; - NSRange r1, r2; - - r1 = [realCollectionId rangeOfString: @"/"]; - r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)]; - - folderName = [realCollectionId substringToIndex: r1.location]; - messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)]; - pathToPart = [realCollectionId substringFromIndex: r2.location+1]; - - userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - - currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName] - inContext: context - acquire: NO]; - - mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; - currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context]; - - [s appendString: @""]; - [s appendString: @"1"]; - [s appendFormat: @"%@", [fileReference stringByEscapingURL]]; - [s appendString: @""]; - - [s appendFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]; - - if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"]) + if ([fileReference length]) { - [s appendFormat: @"%d", i+1]; - [partLength addObject: [NSNumber numberWithInteger: [[currentBodyPart fetchBLOB] length]]]; - [parts appendData:[currentBodyPart fetchBLOB]]; + // fetch attachment + NSRange r1, r2; + NSArray *partKeys; + int p; + + r1 = [realCollectionId rangeOfString: @"/"]; + r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)]; + + folderName = [realCollectionId substringToIndex: r1.location]; + messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)]; + pathToPart = [realCollectionId substringFromIndex: r2.location+1]; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName] + inContext: context + acquire: NO]; + + mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; + + partKeys = [pathToPart componentsSeparatedByString: @"."]; + + currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context]; + for (p = 1; p < [partKeys count]; p++) + { + currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context]; + } + + [s appendString: @""]; + [s appendString: @"1"]; + [s appendFormat: @"%@", [fileReference stringByEscapingURL]]; + [s appendString: @""]; + + [s appendFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]; + + if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"]) + { + NSData *d; + d = [currentBodyPart fetchBLOB]; + + [s appendFormat: @"%d", i+1]; + [partLength addObject: [NSNumber numberWithInteger: [d length]]]; + [parts appendData: d]; + } + else + { + NSString *a; + a = [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]; + + [s appendFormat: @"0-%d", [a length]-1]; + [s appendFormat: @"%@", a]; + } } else { - [s appendFormat: @"0-%d", [[[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context] length]-1]; - [s appendFormat: @"%@", [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]]; + // fetch mail + realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; + serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue]; + bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue]; + [context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"]; + + currentCollection = [self collectionFromId: realCollectionId type: folderType]; + + mailObject = [currentCollection lookupName: serverId inContext: context acquire: NO]; + [s appendString: @""]; + [s appendString: @"1"]; + [s appendFormat: @"%@", collectionId]; + [s appendFormat: @"%@", serverId]; + [s appendString: @""]; + [s appendString: [mailObject activeSyncRepresentationInContext: context]]; } [s appendString: @""]; @@ -1366,6 +1419,62 @@ static BOOL debugOn = NO; { [theResponse setContent: d]; } + } + else if ([theDocumentElement getElementsByTagName: @"EmptyFolderContents"]) + { + NGImap4Connection *connection; + NSEnumerator *subfolders; + NSException *error; + NSURL *currentURL; + id co; + + collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue]; + realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; + realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; + + if (folderType == ActiveSyncMailFolder) + { + co = [self collectionFromId: realCollectionId type: folderType]; + error = [co addFlagsToAllMessages: @"deleted"]; + + if (!error) + error = [(SOGoMailFolder *)co expunge]; + + if (!error) + { + [co flushMailCaches]; + + if ([theDocumentElement getElementsByTagName: @"DeleteSubFolders"]) + { + // Delete sub-folders + connection = [co imap4Connection]; + subfolders = [[co allFolderURLs] objectEnumerator]; + + while ((currentURL = [subfolders nextObject])) + { + [[connection client] unsubscribe: [currentURL path]]; + [connection deleteMailboxAtURL: currentURL]; + } + } + + [s appendString: @"1"]; + [s appendString: @""]; + } + + if (error) + { + [s appendString: @"3"]; + [s appendString: @""]; + } + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + [theResponse setContent: d]; + } + else + { + [theResponse setStatus: 500]; + return; + } } } diff --git a/NEWS b/NEWS index 62ded5385..d220b304e 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,9 @@ Enhancements - improved EAS speed, especially when fetching big attachments +Bug fixes + - EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders + 2.3.0 (2015-06-01) ------------------- From 8c4cb64246d1f5cfa7511882c963f30318eed4d1 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 4 Jun 2015 11:39:54 -0400 Subject: [PATCH 08/47] (fix) now always enforce the organizer's default identity in appointments --- NEWS | 1 + SoObjects/Appointments/SOGoAppointmentObject.m | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/NEWS b/NEWS index d220b304e..e94d10e20 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ Enhancements - improved EAS speed, especially when fetching big attachments + - now always enforce the organizer's default identity in appointments Bug fixes - EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 4e1371dc8..42bc57d55 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -1802,6 +1802,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { NSArray *allEvents; iCalEvent *event; + NSString *uid; NSUInteger i; allEvents = [rqCalendar events]; @@ -1824,6 +1825,20 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { [[event organizer] setCn: [[event organizer] rfc822Email]]; } + + // We now make sure that the organizer, if managed by SOGo, is using + // its default email when creating events and inviting attendees. + uid = [[SOGoUserManager sharedUserManager] getUIDForEmail: [[event organizer] rfc822Email]]; + if (uid) + { + NSDictionary *defaultIdentity; + SOGoUser *organizer; + + organizer = [SOGoUser userWithLogin: uid]; + defaultIdentity = [organizer defaultIdentity]; + [[event organizer] setCn: [defaultIdentity objectForKey: @"fullName"]]; + [[event organizer] setEmail: [defaultIdentity objectForKey: @"email"]]; + } } } From 05232526a7f84107f0379af0922898899ca33b0c Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 4 Jun 2015 11:46:59 -0400 Subject: [PATCH 09/47] (fix) small improvement over previous commit --- .../Appointments/SOGoAppointmentObject.m | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 42bc57d55..ef56c917c 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -1802,7 +1802,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { NSArray *allEvents; iCalEvent *event; - NSString *uid; NSUInteger i; allEvents = [rqCalendar events]; @@ -1828,16 +1827,21 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // We now make sure that the organizer, if managed by SOGo, is using // its default email when creating events and inviting attendees. - uid = [[SOGoUserManager sharedUserManager] getUIDForEmail: [[event organizer] rfc822Email]]; - if (uid) + if ([event organizer]) { - NSDictionary *defaultIdentity; - SOGoUser *organizer; + NSString *uid; - organizer = [SOGoUser userWithLogin: uid]; - defaultIdentity = [organizer defaultIdentity]; - [[event organizer] setCn: [defaultIdentity objectForKey: @"fullName"]]; - [[event organizer] setEmail: [defaultIdentity objectForKey: @"email"]]; + uid = [[SOGoUserManager sharedUserManager] getUIDForEmail: [[event organizer] rfc822Email]]; + if (uid) + { + NSDictionary *defaultIdentity; + SOGoUser *organizer; + + organizer = [SOGoUser userWithLogin: uid]; + defaultIdentity = [organizer defaultIdentity]; + [[event organizer] setCn: [defaultIdentity objectForKey: @"fullName"]]; + [[event organizer] setEmail: [defaultIdentity objectForKey: @"email"]]; + } } } } From 24edb2bce82cc44f27c4bb96726218805f038674 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 4 Jun 2015 13:46:50 -0400 Subject: [PATCH 10/47] (fix) code improvemnent to previous commits --- SoObjects/Appointments/SOGoAppointmentObject.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index ef56c917c..fcf79292d 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -1820,17 +1820,17 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent [self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]]; } - if ([event organizer] && ![[[event organizer] cn] length]) - { - [[event organizer] setCn: [[event organizer] rfc822Email]]; - } - - // We now make sure that the organizer, if managed by SOGo, is using - // its default email when creating events and inviting attendees. if ([event organizer]) { NSString *uid; + if (![[[event organizer] cn] length]) + { + [[event organizer] setCn: [[event organizer] rfc822Email]]; + } + + // We now make sure that the organizer, if managed by SOGo, is using + // its default email when creating events and inviting attendees. uid = [[SOGoUserManager sharedUserManager] getUIDForEmail: [[event organizer] rfc822Email]]; if (uid) { From f3c2d3ca090ad5bb17151fcecd7a2e889713e122 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 4 Jun 2015 14:02:50 -0400 Subject: [PATCH 11/47] Slightly improvement to the text displayed to users --- Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh | 1 + Scripts/sql-update-2.2.17_to_2.3.0.sh | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh b/Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh index bd8f3dd77..1ed8a4818 100755 --- a/Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh +++ b/Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh @@ -49,6 +49,7 @@ function adjustSchema() { echo "This script will ask for the sql password twice" >&2 echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2 +echo "Adding c_description column as mediumtext in calendar quick tables" >&2 tables=`mysql -p -s -u $username -h $hostname $database -e "select SUBSTRING_INDEX(c_quick_location, '/', -1) from $indextable where c_path3 = 'Calendar';"` for table in $tables; diff --git a/Scripts/sql-update-2.2.17_to_2.3.0.sh b/Scripts/sql-update-2.2.17_to_2.3.0.sh index fb6010d49..a9ff62559 100755 --- a/Scripts/sql-update-2.2.17_to_2.3.0.sh +++ b/Scripts/sql-update-2.2.17_to_2.3.0.sh @@ -45,7 +45,8 @@ function adjustSchema() { IFS="$oldIFS" } -echo "Converting c_cycleinfo from VARCHAR(255) to TEXT in calendar quick tables" >&2 +echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2 +echo "Adding c_description column as mediumtext in calendar quick tables" >&2 tables=`psql -t -U $username -h $hostname $database -c "select split_part(c_quick_location, '/', 5) from $indextable where c_path3 = 'Calendar';"` for table in $tables; From b93cbeee26f332a07b295cbcf3699b023ebd7b1f Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 5 Jun 2015 12:18:27 -0400 Subject: [PATCH 12/47] (fix) fixed some rare cornercases in multidomain configurations --- NEWS | 1 + SoObjects/SOGo/SOGoUser.m | 13 ++++++++++- SoObjects/SOGo/SOGoUserManager.m | 37 +++++++++++++++++++++++--------- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index e94d10e20..de9c157b5 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Enhancements Bug fixes - EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders + - Fixed some rare cornercases in multidomain configurations 2.3.0 (2015-06-01) ------------------- diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index fa6459f59..d81dcf52c 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -202,7 +202,18 @@ // the real login most likely is the email address. if (r.location != NSNotFound && ![sd enableDomainBasedUID]) uid = [realUID substringToIndex: r.location-1]; - else + // If we don't have the domain in the UID but SOGoEnableDomainBasedUID is + // enabled, let's add it internally so so it becomes unique across + // all potential domains. + else if (r.location == NSNotFound && [sd enableDomainBasedUID]) + { + uid = [NSString stringWithString: realUID]; + realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain]; + } + // We found the domain and SOGoEnableDomainBasedUID is enabled, + // we keep realUID.. This would happen for example if the user + // authenticates with foo@bar.com and the UIDFieldName is also foo@bar.com + else if ([sd enableDomainBasedUID]) uid = [NSString stringWithString: realUID]; } } diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 21cebce2d..0993b8d5e 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -491,10 +491,10 @@ static Class NSNullK; NSMutableDictionary *currentUser; NSDictionary *failedCount; NSString *dictPassword, *username, *jsonUser; - SOGoSystemDefaults *dd; + SOGoSystemDefaults *sd; BOOL checkOK; - dd = [SOGoSystemDefaults sharedSystemDefaults]; + sd = [SOGoSystemDefaults sharedSystemDefaults]; username = _login; @@ -533,10 +533,10 @@ static Class NSNullK; start_time = [[failedCount objectForKey: @"InitialDate"] unsignedIntValue]; delta = current_time - start_time; - block_time = [dd failedLoginBlockInterval]; + block_time = [sd failedLoginBlockInterval]; - if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [dd maximumFailedLoginCount] && - delta >= [dd maximumFailedLoginInterval] && + if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [sd maximumFailedLoginCount] && + delta >= [sd maximumFailedLoginInterval] && delta <= block_time ) { *_perr = PolicyAccountLocked; @@ -574,6 +574,13 @@ static Class NSNullK; currentUser = [NSMutableDictionary dictionary]; } + // Before caching user attributes, we must check if SOGoEnableDomainBasedUID is enabled + // but we don't have a domain. That would happen for example if a user authenticates + // without the domain part. + if ([sd enableDomainBasedUID] && + [username rangeOfString: @"@"].location == NSNotFound) + username = [NSString stringWithFormat: @"%@@%@", username, *_domain]; + // It's important to cache the password here as we might have cached the // user's entry in -contactInfosForUserWithUIDorEmail: and if we don't // set the password and recache the entry, the password would never be @@ -587,7 +594,7 @@ static Class NSNullK; else { // If failed login "rate-limiting" is enabled, we adjust the stats - if ([dd maximumFailedLoginCount]) + if ([sd maximumFailedLoginCount]) { [[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1) forLogin: username]; @@ -890,8 +897,9 @@ static Class NSNullK; - (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid inDomain: (NSString *) domain { - NSMutableDictionary *currentUser; NSString *aUID, *cacheUid, *jsonUser; + NSMutableDictionary *currentUser; + BOOL newUser; if ([uid isEqualToString: @"anonymous"]) @@ -936,9 +944,18 @@ static Class NSNullK; currentUser = nil; } else - [self _retainUser: currentUser - withLogin: cacheUid]; - } + { + SOGoSystemDefaults *sd; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; + + // to true but we don't have a domain part. + if ([sd enableDomainBasedUID] && !domain) + cacheUid = [NSString stringWithFormat: @"%@@%@", cacheUid, [currentUser objectForKey: @"c_domain"]]; + + [self _retainUser: currentUser withLogin: cacheUid]; + } + } } } else From aa623c5171f0171742dc13431605bed0f3eb8ad7 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 9 Jun 2015 09:46:38 -0400 Subject: [PATCH 13/47] (fix) don't add the domain if already in the uid --- UI/MainUI/SOGoUserHomePage.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 42f373f8d..762de1f85 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -463,7 +463,7 @@ if (!activeUserIsInDomain || ![uid isEqualToString: login]) { jsonLine = [NSMutableArray arrayWithCapacity: 4]; - if ([domain length]) + if ([domain length] && [uid rangeOfString: @"@"].location == NSNotFound) uid = [NSString stringWithFormat: @"%@@%@", uid, domain]; [jsonLine addObject: uid]; [jsonLine addObject: [contact objectForKey: @"cn"]]; From 76360958a2a82a67bee00590fd8c76b1fe5733e3 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 9 Jun 2015 13:38:10 -0400 Subject: [PATCH 14/47] (fix) make sure PRODID is set to Inverse/SOGo when importing events --- SoObjects/Appointments/SOGoAppointmentFolder.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 915fd4aa5..f0d310bd7 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -3157,6 +3157,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir inContainer: self]; [object setIsNew: YES]; content = [NSMutableString stringWithString: @"BEGIN:VCALENDAR\n"]; + [content appendFormat: @"PRODID:-//Inverse inc./SOGo %@//EN\n", SOGoVersion]; + if (timezone) [content appendFormat: @"%@\n", [timezone versitString]]; [content appendFormat: @"%@\nEND:VCALENDAR", [event versitString]]; From a56dbeb6a9e06777e4117e97a0e71b23a1558cc6 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 9 Jun 2015 20:30:22 -0400 Subject: [PATCH 15/47] (fix) improved the handling of default calendar categories/colors (#3200) --- Documentation/SOGoInstallationGuide.asciidoc | 12 +++++++++--- NEWS | 1 + SoObjects/SOGo/SOGoDefaults.plist | 7 +++++-- SoObjects/SOGo/SOGoDomainDefaults.h | 2 -- SoObjects/SOGo/SOGoDomainDefaults.m | 5 ----- SoObjects/SOGo/SOGoUserDefaults.m | 2 +- UI/PreferencesUI/UIxPreferences.h | 1 - UI/PreferencesUI/UIxPreferences.m | 11 ----------- 8 files changed, 16 insertions(+), 25 deletions(-) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 46980463b..e337f6eb0 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -1920,10 +1920,11 @@ events. This parameter is an array of arbitrary strings. Defaults to a list that depends on the language. -|U |SOGoCalendarDefaultCategoryColor -|Parameter used to define the default colour of categories. +|U |SOGoCalendarCategoriesColors +|Parameter used to define the colour of categories. This parameter +is a dictionary of category name/color. -Defaults to `#F0F0F0` when unset. +Defaults to `#F0F0F0` for all categories when unset. |U |SOGoCalendarEventsDefaultClassification |Parameter used to defined the default classification for new events. @@ -2712,6 +2713,11 @@ current version of SOGo from the previous release. [cols="100a"] |======================================================================= +h|2.3.1 +|The SOGoCalendarDefaultCategoryColor default has been removed. If you +want to customize the color of calendar categories, use the +SOGoCalendarCategories and SOGoCalendarCategoriesColors defaults. + h|2.3.0 |Run the shell script `sql-update-2.2.17_to_2.3.0.sh` or `sql-update-2.2.17_to_2.3.0-mysql.sh` (if you use MySQL). diff --git a/NEWS b/NEWS index de9c157b5..00e308a60 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Enhancements - improved EAS speed, especially when fetching big attachments - now always enforce the organizer's default identity in appointments + - improved the handling of default calendar categories/colors (#3200) Bug fixes - EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders diff --git a/SoObjects/SOGo/SOGoDefaults.plist b/SoObjects/SOGo/SOGoDefaults.plist index e804095f0..d0557c666 100644 --- a/SoObjects/SOGo/SOGoDefaults.plist +++ b/SoObjects/SOGo/SOGoDefaults.plist @@ -70,7 +70,6 @@ SOGoMailAutoSave = "5"; - SOGoCalendarDefaultCategoryColor = "#aaa"; SOGoCalendarShouldDisplayWeekend = YES; SOGoCalendarEventsDefaultClassification = "PUBLIC"; SOGoCalendarTasksDefaultClassification = "PUBLIC"; @@ -87,6 +86,10 @@ $label4 = ("To Do", "#3333FF"); $label5 = ("Later", "#993399"); }; - + + SOGoCalendarCategories = ("Customer", "Calls", "Favorites", "Meeting", "Ideas", "Miscellaneous", "Birthday", "Anniversary", "Vacation", "Travel", "Projects", "Suppliers", "Gifts", "Clients", "Issues", "Business", "Holidays", "Personal", "Status", "Competition", "Follow up", "Public Holiday"); + + SOGoCalendarCategoriesColors = { "Customer" = "#F0F0F0"; "Calls" = "#F0F0F0"; "Favorites" = "#F0F0F0"; "Meeting" = "#F0F0F0"; "Ideas" = "#F0F0F0"; "Miscellaneous" = "#F0F0F0"; "Birthday" = "#F0F0F0"; "Anniversary" = "#F0F0F0"; "Vacation" = "#F0F0F0"; "Travel" = "#F0F0F0"; "Projects" = "#F0F0F0"; "Suppliers" = "#F0F0F0"; "Gifts" = "#F0F0F0"; "Clients" = "#F0F0F0"; "Issues" = "#F0F0F0"; "Business" = "#F0F0F0"; "Holidays" = "#F0F0F0"; "Personal" = "#F0F0F0"; "Status" = "#F0F0F0"; "Competition" = "#F0F0F0"; "Follow up" = "#F0F0F0"; "Public Holiday" = "#F0F0F0"; }; + SOGoSubscriptionFolderFormat = "%{FolderName} (%{UserName} <%{Email}>)"; } diff --git a/SoObjects/SOGo/SOGoDomainDefaults.h b/SoObjects/SOGo/SOGoDomainDefaults.h index fbc665852..7dea88121 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.h +++ b/SoObjects/SOGo/SOGoDomainDefaults.h @@ -68,8 +68,6 @@ - (NSArray *) refreshViewIntervals; - (NSString *) subscriptionFolderFormat; -- (NSString *) calendarDefaultCategoryColor; - - (NSArray *) freeBusyDefaultInterval; - (int) davCalendarStartTimeLimit; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index ff02b6a96..b55cc0851 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -294,11 +294,6 @@ return [self stringForKey: @"SOGoLDAPContactInfoAttribute"]; } -- (NSString *) calendarDefaultCategoryColor -{ - return [self stringForKey: @"SOGoCalendarDefaultCategoryColor"]; -} - - (NSArray *) freeBusyDefaultInterval { return [self arrayForKey: @"SOGoFreeBusyDefaultInterval"]; diff --git a/SoObjects/SOGo/SOGoUserDefaults.m b/SoObjects/SOGo/SOGoUserDefaults.m index 5d975f146..9cc4b38e2 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.m +++ b/SoObjects/SOGo/SOGoUserDefaults.m @@ -702,7 +702,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek"; - (NSDictionary *) calendarCategoriesColors { - return [self dictionaryForKey: @"SOGoCalendarCategoriesColors"]; + return [self objectForKey: @"SOGoCalendarCategoriesColors"]; } - (void) setCalendarShouldDisplayWeekend: (BOOL) newValue diff --git a/UI/PreferencesUI/UIxPreferences.h b/UI/PreferencesUI/UIxPreferences.h index bbbb0a2b1..51607c9a1 100644 --- a/UI/PreferencesUI/UIxPreferences.h +++ b/UI/PreferencesUI/UIxPreferences.h @@ -44,7 +44,6 @@ NSDictionary *calendarCategoriesColors; NSArray *contactsCategories; - NSString *defaultCategoryColor; NSCalendarDate *today; // Mail labels/tags diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index 990fda1ed..c73206d0b 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -128,7 +128,6 @@ static NSArray *reminderValues = nil; calendarCategories = nil; calendarCategoriesColors = nil; - defaultCategoryColor = nil; category = nil; label = nil; @@ -175,7 +174,6 @@ static NSArray *reminderValues = nil; [vacationOptions release]; [calendarCategories release]; [calendarCategoriesColors release]; - [defaultCategoryColor release]; [category release]; [label release]; [mailLabels release]; @@ -1521,15 +1519,6 @@ static NSArray *reminderValues = nil; ASSIGN (calendarCategoriesColors, [userDefaults calendarCategoriesColors]); categoryColor = [calendarCategoriesColors objectForKey: category]; - if (!categoryColor) - { - if (!defaultCategoryColor) - { - dd = [[context activeUser] domainDefaults]; - ASSIGN (defaultCategoryColor, [dd calendarDefaultCategoryColor]); - } - categoryColor = defaultCategoryColor; - } return categoryColor; } From e1ecf797f5274b3a95ae6f55f68b5f72e137bb93 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 10 Jun 2015 09:08:10 -0400 Subject: [PATCH 16/47] Use double-quotes for setTimeout of UIxJSClose.wox --- UI/Contacts/UIxContactEditor.m | 7 +++---- UI/Contacts/UIxListEditor.m | 9 +++++---- UI/SOGoUI/UIxComponent.m | 4 ++-- UI/Templates/UIxJSClose.wox | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index 82ad1941e..418945337 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -1,6 +1,6 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2005-2010 Inverse inc. + Copyright (C) 2005-2015 Inverse inc. This file is part of SOGo @@ -378,9 +378,8 @@ static Class SOGoContactGCSEntryK = Nil; result = [self redirectToLocation: [self modulePath]]; else { - jsRefreshMethod - = [NSString stringWithFormat: @"refreshContacts(\"%@\")", - [contact nameInContainer]]; + jsRefreshMethod = [NSString stringWithFormat: @"refreshContacts('%@')", + [contact nameInContainer]]; result = [self jsCloseWithRefreshMethod: jsRefreshMethod]; } diff --git a/UI/Contacts/UIxListEditor.m b/UI/Contacts/UIxListEditor.m index 69d63bd21..ea4920b15 100644 --- a/UI/Contacts/UIxListEditor.m +++ b/UI/Contacts/UIxListEditor.m @@ -1,6 +1,6 @@ /* UIxListEditor.m - this file is part of SOGo * - * Copyright (C) 2008-2014 Inverse inc. + * Copyright (C) 2008-2015 Inverse inc. * * 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 @@ -29,6 +29,8 @@ #import #import +#import + #import #import #import @@ -289,9 +291,8 @@ result = [self redirectToLocation: [self modulePath]]; else { - jsRefreshMethod - = [NSString stringWithFormat: @"refreshContacts(\"%@\")", - [co nameInContainer]]; + jsRefreshMethod = [NSString stringWithFormat: @"refreshContacts('%@')", + [co nameInContainer]]; result = [self jsCloseWithRefreshMethod: jsRefreshMethod]; } } diff --git a/UI/SOGoUI/UIxComponent.m b/UI/SOGoUI/UIxComponent.m index 336bd734d..b2a1315af 100644 --- a/UI/SOGoUI/UIxComponent.m +++ b/UI/SOGoUI/UIxComponent.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2012 Inverse inc. + Copyright (C) 2007-2015 Inverse inc. Copyright (C) 2004 SKYRIX Software AG This file is part of SOGo @@ -452,7 +452,7 @@ static SoProduct *commonProduct = nil; jsClose = [UIxJSClose new]; [jsClose autorelease]; - [jsClose setRefreshMethod: methodName]; + [jsClose setRefreshMethod: [methodName doubleQuotedString]]; return jsClose; } diff --git a/UI/Templates/UIxJSClose.wox b/UI/Templates/UIxJSClose.wox index 3a4487f5b..f1bdef0ef 100644 --- a/UI/Templates/UIxJSClose.wox +++ b/UI/Templates/UIxJSClose.wox @@ -17,7 +17,7 @@ > From 88cab67f6d68c037be10bd02a31bd731af39907b Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 10:12:15 -0400 Subject: [PATCH 17/47] (fix) properly escape folder after creation using EAS (#3237) --- ActiveSync/SOGoActiveSyncDispatcher.m | 2 +- NEWS | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 2ed420f13..83314be2f 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -453,7 +453,7 @@ static BOOL debugOn = NO; [s appendString: @""]; [s appendFormat: @"%d", 1]; [s appendFormat: @"%@", syncKey]; - [s appendFormat: @"%@", nameInContainer]; + [s appendFormat: @"%@", [nameInContainer stringByEscapingURL]]; [s appendString: @""]; d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; diff --git a/NEWS b/NEWS index 00e308a60..e325847da 100644 --- a/NEWS +++ b/NEWS @@ -8,7 +8,8 @@ Enhancements Bug fixes - EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders - - Fixed some rare cornercases in multidomain configurations + - fixed some rare cornercases in multidomain configurations + - properly escape folder after creation using EAS (#3237) 2.3.0 (2015-06-01) ------------------- From b1453e1d7e4dc180d33b782eee21f4a374b46938 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 10:47:30 -0400 Subject: [PATCH 18/47] (fix) fixed potential organizer highjacking when using EAS (#3131) --- ActiveSync/iCalEvent+ActiveSync.m | 26 ++++++++++++++++++++------ NEWS | 1 + 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ActiveSync/iCalEvent+ActiveSync.m b/ActiveSync/iCalEvent+ActiveSync.m index 586bf5798..2156b1eac 100644 --- a/ActiveSync/iCalEvent+ActiveSync.m +++ b/ActiveSync/iCalEvent+ActiveSync.m @@ -506,14 +506,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [self setOrganizer: person]; } - // - // iOS is plain stupid here. It seends event invitations with no Organizer. - // We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details) - // and there's no organizer, we fake one. - // if ((o = [theValues objectForKey: @"MeetingStatus"])) { - if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"]) + // + // iOS is plain stupid here. It seends event invitations with no Organizer. + // We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details) + // and there's no organizer, we fake one. + // + if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length]) { iCalPerson *person; @@ -523,6 +523,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [person setPartStat: @"ACCEPTED"]; [self setOrganizer: person]; } + + // + // When MeetingResponse fails Outlook still sends a new calendar entry with MeetingStatus=3. + // Use the organizer from the request if the event has no organizer. + // + if ([o intValue] == 3 && [theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length]) + { + iCalPerson *person; + person = [iCalPerson elementWithTag: @"organizer"]; + [person setEmail: [theValues objectForKey: @"Organizer_Email"]]; + [person setCn: [theValues objectForKey: @"Organizer_Name"]]; + [person setPartStat: @"ACCEPTED"]; + [self setOrganizer: person]; + } } diff --git a/NEWS b/NEWS index e325847da..b06a4175c 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ Bug fixes - EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders - fixed some rare cornercases in multidomain configurations - properly escape folder after creation using EAS (#3237) + - fixed potential organizer highjacking when using EAS (#3131) 2.3.0 (2015-06-01) ------------------- From 6bc471ad9a4dc35ec0789dd8efb73a6de5a6ea3f Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 10:58:59 -0400 Subject: [PATCH 19/47] (fix) properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082) --- ActiveSync/NSString+ActiveSync.m | 94 ++++++++++++++++++++++++-- ActiveSync/SOGoMailObject+ActiveSync.m | 46 ++++++++++--- NEWS | 1 + 3 files changed, 126 insertions(+), 15 deletions(-) diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m index a66f38252..7060d569c 100644 --- a/ActiveSync/NSString+ActiveSync.m +++ b/ActiveSync/NSString+ActiveSync.m @@ -46,6 +46,94 @@ static NSArray *easCommandParameters = nil; @implementation NSString (ActiveSync) +// +// This is a copy from NSString+XMLEscaping.m from SOPE. +// The difference here is that we use wchar_t instead of unichar. +// This is needed to get the rigth numeric character reference. +// e.g. SMILING FACE WITH OPEN MOUTH +// ok: wchar_t -> 😃 wrong: unichar -> � � +// +- (NSString *)stringByEscapingXMLStringUsingCharacters { + register unsigned i, len, j; + register wchar_t *buf; + const wchar_t *chars; + unsigned escapeCount; + + if ([self length] == 0) return @""; + + NSData *data = [self dataUsingEncoding:NSUTF32StringEncoding]; + chars = [data bytes]; + len = [data length]/4; + + /* check for characters to escape ... */ + for (i = 0, escapeCount = 0; i < len; i++) { + switch (chars[i]) { + case '&': case '"': case '<': case '>': case '\r': + escapeCount++; + break; + default: + if (chars[i] > 127) + escapeCount++; + break; + } + } + if (escapeCount == 0 ) { + /* nothing to escape ... */ + return [[self copy] autorelease]; + } + + buf = calloc((len + 5) + (escapeCount * 16), sizeof(wchar_t)); + for (i = 0, j = 0; i < len; i++) { + switch (chars[i]) { + /* escape special chars */ + case '\r': + buf[j] = '&'; j++; buf[j] = '#'; j++; buf[j] = '1'; j++; + buf[j] = '3'; j++; buf[j] = ';'; j++; + break; + case '&': + buf[j] = '&'; j++; buf[j] = 'a'; j++; buf[j] = 'm'; j++; + buf[j] = 'p'; j++; buf[j] = ';'; j++; + break; + case '"': + buf[j] = '&'; j++; buf[j] = 'q'; j++; buf[j] = 'u'; j++; + buf[j] = 'o'; j++; buf[j] = 't'; j++; buf[j] = ';'; j++; + break; + case '<': + buf[j] = '&'; j++; buf[j] = 'l'; j++; buf[j] = 't'; j++; + buf[j] = ';'; j++; + break; + case '>': + buf[j] = '&'; j++; buf[j] = 'g'; j++; buf[j] = 't'; j++; + buf[j] = ';'; j++; + break; + + default: + /* escape big chars */ + if (chars[i] > 127) { + unsigned char nbuf[32]; + unsigned int k; + + sprintf((char *)nbuf, "&#%i;", (int)chars[i]); + for (k = 0; nbuf[k] != '\0'; k++) { + buf[j] = nbuf[k]; + j++; + } + } + else if (chars[i] == 0x9 || chars[i] == 0xA || chars[i] == 0xD || chars[i] >= 0x20) { // ignore any unsupported control character + /* nothing to escape */ + buf[j] = chars[i]; + j++; + } + break; + } + } + + self = [[[NSString alloc] initWithBytes:buf length:(j) * sizeof(wchar_t) encoding:NSUTF32StringEncoding] autorelease]; + + if (buf) free(buf); + return self; +} + - (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType { if (folderType == ActiveSyncEventFolder) @@ -65,11 +153,7 @@ static NSArray *easCommandParameters = nil; - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { - NSString *s; - - s = [self safeString]; - - return [s stringByEscapingHTMLString]; + return [self stringByEscapingXMLStringUsingCharacters]; } - (int) activeSyncFolderType diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m index d3095a966..164abb6db 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.m +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -381,11 +381,6 @@ struct GlobalObjectId { if (s) { - // We sanitize the content immediately, in case we have non-UNICODE safe - // characters that would be re-encoded later in HTML entities and thus, - // ignore afterwards. - s = [s safeString]; - body = [s dataUsingEncoding: NSUTF8StringEncoding]; } @@ -866,10 +861,41 @@ struct GlobalObjectId { if (d) { + NSMutableData *sanitizedData; NSString *content; int len, truncated; - - content = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding]; + + // Outlook fails to decode quoted-printable (see #3082) if lines are not termined by CRLF. + const char *bytes; + char *mbytes; + int mlen; + + len = [d length]; + mlen = 0; + + sanitizedData = [NSMutableData dataWithLength: len*2]; + + bytes = [d bytes]; + mbytes = [sanitizedData mutableBytes]; + + while (len > 0) + { + if (*bytes == '\n' && *(bytes-1) != '\r' && mlen > 0) + { + *mbytes = '\r'; + mbytes++; + mlen++; + } + + *mbytes = *bytes; + mbytes++; bytes++; + len--; + mlen++; + } + + [sanitizedData setLength: mlen]; + + content = [[NSString alloc] initWithData: sanitizedData encoding: NSUTF8StringEncoding]; // FIXME: This is a hack. We should normally avoid doing this as we might get // broken encodings. We should rather tell that the data was truncated and expect @@ -879,15 +905,15 @@ struct GlobalObjectId { // for an "interesting" discussion around this. // if (!content) - content = [[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding]; + content = [[NSString alloc] initWithData: sanitizedData encoding: NSISOLatin1StringEncoding]; AUTORELEASE(content); content = [content activeSyncRepresentationInContext: context]; truncated = 0; - + len = [content length]; - + if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"]) { [s appendFormat: @"%@", content]; diff --git a/NEWS b/NEWS index b06a4175c..950aa010a 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ Bug fixes - fixed some rare cornercases in multidomain configurations - properly escape folder after creation using EAS (#3237) - fixed potential organizer highjacking when using EAS (#3131) + - properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082) 2.3.0 (2015-06-01) ------------------- From e40a19cf8990136e55ba728b4626ca1877e064be Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 10 Jun 2015 14:00:29 -0400 Subject: [PATCH 20/47] Improve creation of CSS identifier Now escaping (), [], {}, ", and ; --- SoObjects/SOGo/NSString+Utilities.m | 12 +++++++++--- UI/WebServerResources/JavascriptAPIExtensions.js | 10 ++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 46f42af8e..6938e9478 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -333,12 +333,18 @@ static int cssEscapingCount; int count; strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_", - @"_C_", @"_CO_", @"_SP_", @"_SQ_", @"_AM_", @"_P_", @"_DS_", nil]; + @"_C_", @"_SC_", + @"_CO_", @"_SP_", @"_SQ_", @"_DQ_", + @"_LP_", @"_RP_", @"_LS_", @"_RS_", @"_LC_", @"_RC_", + @"_AM_", @"_P_", @"_DS_", nil]; [strings retain]; cssEscapingStrings = [strings asPointersOfObjects]; - characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", @":", - @",", @" ", @"'", @"&", @"+", @"$", nil]; + characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", + @":", @";", + @",", @" ", @"'", @"\"", + @"(", @")", @"[", @"]", @"{", @"}", + @"&", @"+", @"$", nil]; cssEscapingCount = [strings count]; cssEscapingCharacters = NSZoneMalloc (NULL, (cssEscapingCount + 1) diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index 245078268..c8cd358ec 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -95,10 +95,12 @@ String.prototype.asDate = function () { }; String.prototype.asCSSIdentifier = function() { - var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ' - , "'", '&', '\\+' ]; - var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_', - '_SP_', '_SQ_', '_AM_', '_P_' ]; + var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ';' , ',' , ' ', + '\\(', '\\)', '\\[', '\\]', '\\{', '\\}', + , "'", '"', '&', '\\+' ]; + var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', + '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', + '_SQ_', '_DQ_', '_AM_', '_P_' ]; var newString = this; for (var i = 0; i < characters.length; i++) { From 1f83ba9d507d9d1fca78f2d221aefe68a9e35cdf Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 14:31:32 -0400 Subject: [PATCH 21/47] (fix) fixed multidomain issue where UID isn't part of the email address --- SoObjects/SOGo/SOGoUserManager.m | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 0993b8d5e..b671d5ec1 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -435,21 +435,39 @@ static Class NSNullK; grace: (int *) grace { NSObject *sogoSource; + SOGoSystemDefaults *sd; NSEnumerator *authIDs; NSString *currentID; + BOOL checkOK; - + NSRange r; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; checkOK = NO; authIDs = [[self authenticationSourceIDsInDomain: *domain] objectEnumerator]; while (!checkOK && (currentID = [authIDs nextObject])) { sogoSource = [_sources objectForKey: currentID]; + r = [login rangeOfString: [NSString stringWithFormat: @"@%@", *domain]]; + checkOK = [sogoSource checkLogin: login password: password perr: perr expire: expire grace: grace]; + + // If we are using multidomain and the UIDFieldName is not part of the email address + // we must also try to bind without the domain part since internally, SOGo will use + // UID @ domain as its unique identifier. + if (!checkOK && *domain && [sd enableDomainBasedUID] && r.location != NSNotFound) + { + checkOK = [sogoSource checkLogin: [login substringToIndex: r.location] + password: password + perr: perr + expire: expire + grace: grace]; + } } if (checkOK && *domain == nil) From 9897cee77981274f86e68397eb231dee24b2b7ac Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 10 Jun 2015 15:43:00 -0400 Subject: [PATCH 22/47] (fix) JavaScript syntax error in 'asCSSIdentifier' --- UI/WebServerResources/JavascriptAPIExtensions.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index c8cd358ec..f51c31445 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -73,6 +73,10 @@ String.prototype.decodeEntities = function() { }); }; +String.prototype.unescapeHTMLEntities = function() { + return this.unescapeHTML().replace(/"/g,'"'); +}; + String.prototype.asDate = function () { var newDate; var date = this.split("/"); @@ -97,7 +101,7 @@ String.prototype.asDate = function () { String.prototype.asCSSIdentifier = function() { var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ';' , ',' , ' ', '\\(', '\\)', '\\[', '\\]', '\\{', '\\}', - , "'", '"', '&', '\\+' ]; + "'", '"', '&', '\\+' ]; var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', '_SQ_', '_DQ_', '_AM_', '_P_' ]; From 662d06d24a5c8c5e364246359be500b5cc33db2c Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 16:30:56 -0400 Subject: [PATCH 23/47] Revert "(fix) JavaScript syntax error in 'asCSSIdentifier'" This reverts commit 9897cee77981274f86e68397eb231dee24b2b7ac. --- UI/WebServerResources/JavascriptAPIExtensions.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index f51c31445..c8cd358ec 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -73,10 +73,6 @@ String.prototype.decodeEntities = function() { }); }; -String.prototype.unescapeHTMLEntities = function() { - return this.unescapeHTML().replace(/"/g,'"'); -}; - String.prototype.asDate = function () { var newDate; var date = this.split("/"); @@ -101,7 +97,7 @@ String.prototype.asDate = function () { String.prototype.asCSSIdentifier = function() { var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ';' , ',' , ' ', '\\(', '\\)', '\\[', '\\]', '\\{', '\\}', - "'", '"', '&', '\\+' ]; + , "'", '"', '&', '\\+' ]; var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', '_SQ_', '_DQ_', '_AM_', '_P_' ]; From 7d018221ef753942711826b534f23f8e3bf7447e Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 16:31:15 -0400 Subject: [PATCH 24/47] Revert "Improve creation of CSS identifier" This reverts commit e40a19cf8990136e55ba728b4626ca1877e064be. --- SoObjects/SOGo/NSString+Utilities.m | 12 +++--------- UI/WebServerResources/JavascriptAPIExtensions.js | 10 ++++------ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 6938e9478..46f42af8e 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -333,18 +333,12 @@ static int cssEscapingCount; int count; strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_", - @"_C_", @"_SC_", - @"_CO_", @"_SP_", @"_SQ_", @"_DQ_", - @"_LP_", @"_RP_", @"_LS_", @"_RS_", @"_LC_", @"_RC_", - @"_AM_", @"_P_", @"_DS_", nil]; + @"_C_", @"_CO_", @"_SP_", @"_SQ_", @"_AM_", @"_P_", @"_DS_", nil]; [strings retain]; cssEscapingStrings = [strings asPointersOfObjects]; - characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", - @":", @";", - @",", @" ", @"'", @"\"", - @"(", @")", @"[", @"]", @"{", @"}", - @"&", @"+", @"$", nil]; + characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", @":", + @",", @" ", @"'", @"&", @"+", @"$", nil]; cssEscapingCount = [strings count]; cssEscapingCharacters = NSZoneMalloc (NULL, (cssEscapingCount + 1) diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index c8cd358ec..245078268 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -95,12 +95,10 @@ String.prototype.asDate = function () { }; String.prototype.asCSSIdentifier = function() { - var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ';' , ',' , ' ', - '\\(', '\\)', '\\[', '\\]', '\\{', '\\}', - , "'", '"', '&', '\\+' ]; - var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', - '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', - '_SQ_', '_DQ_', '_AM_', '_P_' ]; + var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ' + , "'", '&', '\\+' ]; + var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_', + '_SP_', '_SQ_', '_AM_', '_P_' ]; var newString = this; for (var i = 0; i < characters.length; i++) { From 977406de765f78d34f390da9319d2371f3a22632 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 16:31:30 -0400 Subject: [PATCH 25/47] Improved rationale --- SoObjects/SOGo/SOGoUserManager.m | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index b671d5ec1..d50ac05f2 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -459,7 +459,16 @@ static Class NSNullK; // If we are using multidomain and the UIDFieldName is not part of the email address // we must also try to bind without the domain part since internally, SOGo will use - // UID @ domain as its unique identifier. + // UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to + // authenticate. This can happen for example of one has in LDAP: + // + // dn: uid=foo,dc=example,dc=com + // uid: foo + // mail: broccoli@example.com + // + // and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES; + // The -checkLogin:... above would have failed because SOGo would first try to bind using: foo@example.com + // if (!checkOK && *domain && [sd enableDomainBasedUID] && r.location != NSNotFound) { checkOK = [sogoSource checkLogin: [login substringToIndex: r.location] From 6994c029b8d1fe53840aedd3916c775f66465ebe Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 19:11:04 -0400 Subject: [PATCH 26/47] Revert "Improved rationale" This reverts commit 977406de765f78d34f390da9319d2371f3a22632. --- SoObjects/SOGo/SOGoUserManager.m | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index d50ac05f2..b671d5ec1 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -459,16 +459,7 @@ static Class NSNullK; // If we are using multidomain and the UIDFieldName is not part of the email address // we must also try to bind without the domain part since internally, SOGo will use - // UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to - // authenticate. This can happen for example of one has in LDAP: - // - // dn: uid=foo,dc=example,dc=com - // uid: foo - // mail: broccoli@example.com - // - // and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES; - // The -checkLogin:... above would have failed because SOGo would first try to bind using: foo@example.com - // + // UID @ domain as its unique identifier. if (!checkOK && *domain && [sd enableDomainBasedUID] && r.location != NSNotFound) { checkOK = [sogoSource checkLogin: [login substringToIndex: r.location] From ac30c821658316e8daf35386b1ea0fed9a116234 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 19:11:26 -0400 Subject: [PATCH 27/47] Revert "(fix) fixed multidomain issue where UID isn't part of the email address" This reverts commit 1f83ba9d507d9d1fca78f2d221aefe68a9e35cdf. --- SoObjects/SOGo/SOGoUserManager.m | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index b671d5ec1..0993b8d5e 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -435,39 +435,21 @@ static Class NSNullK; grace: (int *) grace { NSObject *sogoSource; - SOGoSystemDefaults *sd; NSEnumerator *authIDs; NSString *currentID; - BOOL checkOK; - NSRange r; - - sd = [SOGoSystemDefaults sharedSystemDefaults]; + checkOK = NO; authIDs = [[self authenticationSourceIDsInDomain: *domain] objectEnumerator]; while (!checkOK && (currentID = [authIDs nextObject])) { sogoSource = [_sources objectForKey: currentID]; - r = [login rangeOfString: [NSString stringWithFormat: @"@%@", *domain]]; - checkOK = [sogoSource checkLogin: login password: password perr: perr expire: expire grace: grace]; - - // If we are using multidomain and the UIDFieldName is not part of the email address - // we must also try to bind without the domain part since internally, SOGo will use - // UID @ domain as its unique identifier. - if (!checkOK && *domain && [sd enableDomainBasedUID] && r.location != NSNotFound) - { - checkOK = [sogoSource checkLogin: [login substringToIndex: r.location] - password: password - perr: perr - expire: expire - grace: grace]; - } } if (checkOK && *domain == nil) From 2c5f598a23dbab988535812eb37f90a87101eb65 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 20:04:51 -0400 Subject: [PATCH 28/47] Revert "Revert "(fix) fixed multidomain issue where UID isn't part of the email address"" This reverts commit ac30c821658316e8daf35386b1ea0fed9a116234. --- SoObjects/SOGo/SOGoUserManager.m | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 0993b8d5e..b671d5ec1 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -435,21 +435,39 @@ static Class NSNullK; grace: (int *) grace { NSObject *sogoSource; + SOGoSystemDefaults *sd; NSEnumerator *authIDs; NSString *currentID; + BOOL checkOK; - + NSRange r; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; checkOK = NO; authIDs = [[self authenticationSourceIDsInDomain: *domain] objectEnumerator]; while (!checkOK && (currentID = [authIDs nextObject])) { sogoSource = [_sources objectForKey: currentID]; + r = [login rangeOfString: [NSString stringWithFormat: @"@%@", *domain]]; + checkOK = [sogoSource checkLogin: login password: password perr: perr expire: expire grace: grace]; + + // If we are using multidomain and the UIDFieldName is not part of the email address + // we must also try to bind without the domain part since internally, SOGo will use + // UID @ domain as its unique identifier. + if (!checkOK && *domain && [sd enableDomainBasedUID] && r.location != NSNotFound) + { + checkOK = [sogoSource checkLogin: [login substringToIndex: r.location] + password: password + perr: perr + expire: expire + grace: grace]; + } } if (checkOK && *domain == nil) From 85ad849189d1372459da5c5015eb0fae9f53cb77 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 20:05:13 -0400 Subject: [PATCH 29/47] Revert "Revert "Improved rationale"" This reverts commit 6994c029b8d1fe53840aedd3916c775f66465ebe. --- SoObjects/SOGo/SOGoUserManager.m | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index b671d5ec1..d50ac05f2 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -459,7 +459,16 @@ static Class NSNullK; // If we are using multidomain and the UIDFieldName is not part of the email address // we must also try to bind without the domain part since internally, SOGo will use - // UID @ domain as its unique identifier. + // UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to + // authenticate. This can happen for example of one has in LDAP: + // + // dn: uid=foo,dc=example,dc=com + // uid: foo + // mail: broccoli@example.com + // + // and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES; + // The -checkLogin:... above would have failed because SOGo would first try to bind using: foo@example.com + // if (!checkOK && *domain && [sd enableDomainBasedUID] && r.location != NSNotFound) { checkOK = [sogoSource checkLogin: [login substringToIndex: r.location] From d9896192f30d4f350c4678036f69da5698109b76 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 20:05:40 -0400 Subject: [PATCH 30/47] Revert "Revert "Improve creation of CSS identifier"" This reverts commit 7d018221ef753942711826b534f23f8e3bf7447e. --- SoObjects/SOGo/NSString+Utilities.m | 12 +++++++++--- UI/WebServerResources/JavascriptAPIExtensions.js | 10 ++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 46f42af8e..6938e9478 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -333,12 +333,18 @@ static int cssEscapingCount; int count; strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_", - @"_C_", @"_CO_", @"_SP_", @"_SQ_", @"_AM_", @"_P_", @"_DS_", nil]; + @"_C_", @"_SC_", + @"_CO_", @"_SP_", @"_SQ_", @"_DQ_", + @"_LP_", @"_RP_", @"_LS_", @"_RS_", @"_LC_", @"_RC_", + @"_AM_", @"_P_", @"_DS_", nil]; [strings retain]; cssEscapingStrings = [strings asPointersOfObjects]; - characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", @":", - @",", @" ", @"'", @"&", @"+", @"$", nil]; + characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", + @":", @";", + @",", @" ", @"'", @"\"", + @"(", @")", @"[", @"]", @"{", @"}", + @"&", @"+", @"$", nil]; cssEscapingCount = [strings count]; cssEscapingCharacters = NSZoneMalloc (NULL, (cssEscapingCount + 1) diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index 245078268..c8cd358ec 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -95,10 +95,12 @@ String.prototype.asDate = function () { }; String.prototype.asCSSIdentifier = function() { - var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ' - , "'", '&', '\\+' ]; - var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_', - '_SP_', '_SQ_', '_AM_', '_P_' ]; + var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ';' , ',' , ' ', + '\\(', '\\)', '\\[', '\\]', '\\{', '\\}', + , "'", '"', '&', '\\+' ]; + var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', + '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', + '_SQ_', '_DQ_', '_AM_', '_P_' ]; var newString = this; for (var i = 0; i < characters.length; i++) { From 54224637b685a913feb451be61dadddcb9242355 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 20:06:00 -0400 Subject: [PATCH 31/47] Revert "Revert "(fix) JavaScript syntax error in 'asCSSIdentifier'"" This reverts commit 662d06d24a5c8c5e364246359be500b5cc33db2c. --- UI/WebServerResources/JavascriptAPIExtensions.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index c8cd358ec..f51c31445 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -73,6 +73,10 @@ String.prototype.decodeEntities = function() { }); }; +String.prototype.unescapeHTMLEntities = function() { + return this.unescapeHTML().replace(/"/g,'"'); +}; + String.prototype.asDate = function () { var newDate; var date = this.split("/"); @@ -97,7 +101,7 @@ String.prototype.asDate = function () { String.prototype.asCSSIdentifier = function() { var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ';' , ',' , ' ', '\\(', '\\)', '\\[', '\\]', '\\{', '\\}', - , "'", '"', '&', '\\+' ]; + "'", '"', '&', '\\+' ]; var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', '_SQ_', '_DQ_', '_AM_', '_P_' ]; From 44af47f69e6dbaecd23adf250de8d149be9abb6a Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 10 Jun 2015 20:09:40 -0400 Subject: [PATCH 32/47] (fix) avoid method clash from categories that would lead to unexpected behaviors --- ActiveSync/NSString+ActiveSync.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m index 7060d569c..884083db8 100644 --- a/ActiveSync/NSString+ActiveSync.m +++ b/ActiveSync/NSString+ActiveSync.m @@ -53,7 +53,10 @@ static NSArray *easCommandParameters = nil; // e.g. SMILING FACE WITH OPEN MOUTH // ok: wchar_t -> 😃 wrong: unichar -> � � // -- (NSString *)stringByEscapingXMLStringUsingCharacters { +// We avoir naming it like the one in SOPE since if the ActiveSync +// bundle is loaded, it'll overwrite the one provided by SOPE. +// +- (NSString *) _stringByEscapingXMLStringUsingCharacters { register unsigned i, len, j; register wchar_t *buf; const wchar_t *chars; @@ -153,7 +156,7 @@ static NSArray *easCommandParameters = nil; - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { - return [self stringByEscapingXMLStringUsingCharacters]; + return [self _stringByEscapingXMLStringUsingCharacters]; } - (int) activeSyncFolderType From 5e66e8e299a08275be0ce517031f815169057aa9 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 11 Jun 2015 11:36:03 -0400 Subject: [PATCH 33/47] (fix) improvement over previous commit to avoid double-bind --- SoObjects/SOGo/SOGoUserManager.m | 145 +++++++++++++++++-------------- 1 file changed, 82 insertions(+), 63 deletions(-) diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index d50ac05f2..a29ebb356 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -435,48 +435,22 @@ static Class NSNullK; grace: (int *) grace { NSObject *sogoSource; - SOGoSystemDefaults *sd; NSEnumerator *authIDs; NSString *currentID; - BOOL checkOK; - NSRange r; - sd = [SOGoSystemDefaults sharedSystemDefaults]; checkOK = NO; authIDs = [[self authenticationSourceIDsInDomain: *domain] objectEnumerator]; while (!checkOK && (currentID = [authIDs nextObject])) { sogoSource = [_sources objectForKey: currentID]; - r = [login rangeOfString: [NSString stringWithFormat: @"@%@", *domain]]; checkOK = [sogoSource checkLogin: login password: password perr: perr expire: expire grace: grace]; - - // If we are using multidomain and the UIDFieldName is not part of the email address - // we must also try to bind without the domain part since internally, SOGo will use - // UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to - // authenticate. This can happen for example of one has in LDAP: - // - // dn: uid=foo,dc=example,dc=com - // uid: foo - // mail: broccoli@example.com - // - // and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES; - // The -checkLogin:... above would have failed because SOGo would first try to bind using: foo@example.com - // - if (!checkOK && *domain && [sd enableDomainBasedUID] && r.location != NSNotFound) - { - checkOK = [sogoSource checkLogin: [login substringToIndex: r.location] - password: password - perr: perr - expire: expire - grace: grace]; - } } if (checkOK && *domain == nil) @@ -582,6 +556,28 @@ static Class NSNullK; // authentication source and try to validate there, then cache it. jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username]; currentUser = [jsonUser objectFromJSONString]; + + // + // If we are using multidomain and the UIDFieldName is not part of the email address + // we must bind without the domain part since internally, SOGo will use + // UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to + // authenticate. This can happen for example of one has in LDAP: + // + // dn: uid=foo,dc=example,dc=com + // uid: foo + // mail: broccoli@example.com + // + // and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES; + // Otherwise, -_sourceCheckLogin:... would have failed because SOGo would to bind using: foo@example.com + // + if ([[currentUser objectForKey: @"DomainLessLogin"] boolValue]) + { + NSRange r; + + r = [_login rangeOfString: [NSString stringWithFormat: @"@%@", *_domain]]; + _login = [_login substringToIndex: r.location]; + } + dictPassword = [currentUser objectForKey: @"password"]; if (useCache && currentUser && dictPassword) { @@ -602,11 +598,16 @@ static Class NSNullK; } // Before caching user attributes, we must check if SOGoEnableDomainBasedUID is enabled - // but we don't have a domain. That would happen for example if a user authenticates - // without the domain part. + // but we don't have a domain. That would happen for example if the user authenticates + // without the domain part. We must also cache that information, since SOGo will try + // afterward to bind with UIDFieldName@domain, and it could potentially not exist + // in the authentication source. See the rationale in _sourceCheckLogin: ... if ([sd enableDomainBasedUID] && [username rangeOfString: @"@"].location == NSNotFound) - username = [NSString stringWithFormat: @"%@@%@", username, *_domain]; + { + username = [NSString stringWithFormat: @"%@@%@", username, *_domain]; + [currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"DomainLessLogin"]; + } // It's important to cache the password here as we might have cached the // user's entry in -contactInfosForUserWithUIDorEmail: and if we don't @@ -729,9 +730,9 @@ static Class NSNullK; // // // -- (void) _fillContactInfosForUser: (NSMutableDictionary *) currentUser - withUIDorEmail: (NSString *) uid - inDomain: (NSString *) domain +- (void) _fillContactInfosForUser: (NSMutableDictionary *) theCurrentUser + withUIDorEmail: (NSString *) theUID + inDomain: (NSString *) theDomain { NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin, *c_sievehostname; NSObject *currentSource; @@ -750,21 +751,31 @@ static Class NSNullK; c_imaplogin = nil; c_sievehostname = nil; - [currentUser setObject: [NSNumber numberWithBool: YES] - forKey: @"CalendarAccess"]; - [currentUser setObject: [NSNumber numberWithBool: YES] - forKey: @"MailAccess"]; + [theCurrentUser setObject: [NSNumber numberWithBool: YES] + forKey: @"CalendarAccess"]; + [theCurrentUser setObject: [NSNumber numberWithBool: YES] + forKey: @"MailAccess"]; - sogoSources = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; + if ([[theCurrentUser objectForKey: @"DomainLessLogin"] boolValue]) + { + NSRange r; + + r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]]; + theUID = [theUID substringToIndex: r.location]; + } + + + sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator]; userEntry = nil; while (!userEntry && (sourceID = [sogoSources nextObject])) { currentSource = [_sources objectForKey: sourceID]; - userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid - inDomain: domain]; + + userEntry = [currentSource lookupContactEntryWithUIDorEmail: theUID + inDomain: theDomain]; if (userEntry) { - [currentUser setObject: sourceID forKey: @"SOGoSource"]; + [theCurrentUser setObject: sourceID forKey: @"SOGoSource"]; if (!cn) cn = [userEntry objectForKey: @"c_cn"]; if (!c_uid) @@ -782,30 +793,30 @@ static Class NSNullK; c_sievehostname = [userEntry objectForKey: @"c_sievehostname"]; access = [[userEntry objectForKey: @"CalendarAccess"] boolValue]; if (!access) - [currentUser setObject: [NSNumber numberWithBool: NO] - forKey: @"CalendarAccess"]; + [theCurrentUser setObject: [NSNumber numberWithBool: NO] + forKey: @"CalendarAccess"]; access = [[userEntry objectForKey: @"MailAccess"] boolValue]; if (!access) - [currentUser setObject: [NSNumber numberWithBool: NO] - forKey: @"MailAccess"]; + [theCurrentUser setObject: [NSNumber numberWithBool: NO] + forKey: @"MailAccess"]; // We check if it's a group isGroup = [userEntry objectForKey: @"isGroup"]; if (isGroup) - [currentUser setObject: isGroup forKey: @"isGroup"]; + [theCurrentUser setObject: isGroup forKey: @"isGroup"]; // We also fill the resource attributes, if any if ([userEntry objectForKey: @"isResource"]) - [currentUser setObject: [userEntry objectForKey: @"isResource"] - forKey: @"isResource"]; + [theCurrentUser setObject: [userEntry objectForKey: @"isResource"] + forKey: @"isResource"]; if ([userEntry objectForKey: @"numberOfSimultaneousBookings"]) - [currentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"] - forKey: @"numberOfSimultaneousBookings"]; + [theCurrentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"] + forKey: @"numberOfSimultaneousBookings"]; // This is Active Directory specific attribute (needed on OpenChange/* layer) if ([userEntry objectForKey: @"samaccountname"]) - [currentUser setObject: [userEntry objectForKey: @"samaccountname"] - forKey: @"sAMAccountName"]; + [theCurrentUser setObject: [userEntry objectForKey: @"samaccountname"] + forKey: @"sAMAccountName"]; } } @@ -817,20 +828,20 @@ static Class NSNullK; c_domain = @""; if (c_imaphostname) - [currentUser setObject: c_imaphostname forKey: @"c_imaphostname"]; + [theCurrentUser setObject: c_imaphostname forKey: @"c_imaphostname"]; if (c_imaplogin) - [currentUser setObject: c_imaplogin forKey: @"c_imaplogin"]; + [theCurrentUser setObject: c_imaplogin forKey: @"c_imaplogin"]; if (c_sievehostname) - [currentUser setObject: c_sievehostname forKey: @"c_sievehostname"]; + [theCurrentUser setObject: c_sievehostname forKey: @"c_sievehostname"]; - [currentUser setObject: emails forKey: @"emails"]; - [currentUser setObject: cn forKey: @"cn"]; - [currentUser setObject: c_uid forKey: @"c_uid"]; - [currentUser setObject: c_domain forKey: @"c_domain"]; + [theCurrentUser setObject: emails forKey: @"emails"]; + [theCurrentUser setObject: cn forKey: @"cn"]; + [theCurrentUser setObject: c_uid forKey: @"c_uid"]; + [theCurrentUser setObject: c_domain forKey: @"c_domain"]; // If our LDAP queries gave us nothing, we add at least one default // email address based on the default domain. - [self _fillContactMailRecords: currentUser]; + [self _fillContactMailRecords: theCurrentUser]; } // @@ -939,8 +950,10 @@ static Class NSNullK; cacheUid = [NSString stringWithFormat: @"%@@%@", aUID, domain]; else cacheUid = aUID; + jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: cacheUid]; currentUser = [jsonUser objectFromJSONString]; + if ([currentUser isKindOfClass: NSNullK]) currentUser = nil; else if (!([currentUser objectForKey: @"emails"] @@ -950,8 +963,10 @@ static Class NSNullK; // 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"])) - { + if (!currentUser || + ([currentUser count] == 1 && [currentUser objectForKey: @"password"]) || + ([currentUser count] == 2 && [currentUser objectForKey: @"password"] && [currentUser objectForKey: @"DomainLessLogin"])) + { newUser = YES; if (!currentUser) @@ -976,9 +991,13 @@ static Class NSNullK; sd = [SOGoSystemDefaults sharedSystemDefaults]; - // to true but we don't have a domain part. + // SOGoEnableDomainBasedUID is set to YES but we don't have a domain part. This happens in + // multi-domain environments authenticating only with the UIDFieldName if ([sd enableDomainBasedUID] && !domain) - cacheUid = [NSString stringWithFormat: @"%@@%@", cacheUid, [currentUser objectForKey: @"c_domain"]]; + { + cacheUid = [NSString stringWithFormat: @"%@@%@", cacheUid, [currentUser objectForKey: @"c_domain"]]; + [currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"DomainLessLogin"]; + } [self _retainUser: currentUser withLogin: cacheUid]; } From a70ea385722f364c01aa227351480d31684e040a Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 11 Jun 2015 11:44:17 -0400 Subject: [PATCH 34/47] New method [NSString+Utilities asSafeJSString] --- SoObjects/SOGo/NSString+Utilities.h | 5 ++++- SoObjects/SOGo/NSString+Utilities.m | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/SoObjects/SOGo/NSString+Utilities.h b/SoObjects/SOGo/NSString+Utilities.h index 8474dc4e3..d172ff167 100644 --- a/SoObjects/SOGo/NSString+Utilities.h +++ b/SoObjects/SOGo/NSString+Utilities.h @@ -1,6 +1,6 @@ /* NSString+Utilities.h - this file is part of SOGo * - * Copyright (C) 2006-2014 Inverse inc. + * Copyright (C) 2006-2015 Inverse inc. * * 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 @@ -46,6 +46,9 @@ - (NSString *) asCSSIdentifier; - (NSString *) fromCSSIdentifier; +/* JavaScript safety */ +- (NSString *) asSafeJSString; + /* SQL safety */ - (NSString *) asSafeSQLString; diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 6938e9478..d7d85e5f5 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -1,6 +1,6 @@ /* NSString+Utilities.m - this file is part of SOGo * - * Copyright (C) 2006-2014 Inverse inc. + * Copyright (C) 2006-2015 Inverse inc. * * 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 @@ -257,7 +257,7 @@ static int cssEscapingCount; return selfCopy; } -- (NSString *) doubleQuotedString +- (NSString *) asSafeJSString { NSMutableString *representation; @@ -270,7 +270,12 @@ static int cssEscapingCount; [representation replaceString: @"\r" withString: @"\\r"]; [representation replaceString: @"\t" withString: @"\\t"]; - return [NSString stringWithFormat: @"\"%@\"", representation]; + return representation; +} + +- (NSString *) doubleQuotedString +{ + return [NSString stringWithFormat: @"\"%@\"", [self asSafeJSString]]; } // From 5435c2dd04cdc718952b08d4f8c1fdea21f5707b Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 11 Jun 2015 11:45:24 -0400 Subject: [PATCH 35/47] (js) New method String.prototype.fromCSSIdentifier --- .../JavascriptAPIExtensions.js | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index f51c31445..12db867bd 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -98,22 +98,39 @@ String.prototype.asDate = function () { return newDate; }; -String.prototype.asCSSIdentifier = function() { - var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ';' , ',' , ' ', - '\\(', '\\)', '\\[', '\\]', '\\{', '\\}', - "'", '"', '&', '\\+' ]; - var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', - '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', - '_SQ_', '_DQ_', '_AM_', '_P_' ]; +RegExp.escape = function(text) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); +} +var css_invalid_characters = [ '_' , '.', '#' , '@' , '*', ':' , ';' , ',' , ' ', + '(', ')', '[', ']', '{', '}', + "'", '"', '&', '+' ]; +var css_escape_characters = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', + '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', + '_SQ_', '_DQ_', '_AM_', '_P_' ]; + +String.prototype.asCSSIdentifier = function() { var newString = this; - for (var i = 0; i < characters.length; i++) { - var re = new RegExp(characters[i], 'g'); - newString = newString.replace(re, escapeds[i]); + for (var i = 0; i < css_invalid_characters.length; i++) { + var re = new RegExp(RegExp.escape(css_invalid_characters[i]), 'g'); + newString = newString.replace(re, css_escape_characters[i]); } - if (/^\d+/.test(newString)) { + if (/^\d/.test(newString)) newString = '_' + newString; + + return newString; +}; + +String.prototype.fromCSSIdentifier = function() { + var newString = this; + + if (/^_\d/.test(newString)) + newString = newString.substring(1); + + for (var i = 0; i < css_escape_characters.length; i++) { + var re = new RegExp(css_escape_characters[i], 'g'); + newString = newString.replace(re, css_invalid_characters[i]); } return newString; From 76196bf6f4f460fb93b7a791e2f58a6fb20c33f4 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 11 Jun 2015 12:03:11 -0400 Subject: [PATCH 36/47] (fix) Handling of cards with unexpected UID Fixes #3239 Fixes #3245 --- NEWS | 1 + .../ContactsUI/UIxContactFoldersView.wox | 10 +-- UI/WebServerResources/ContactsUI.js | 82 ++++++++++--------- 3 files changed, 49 insertions(+), 44 deletions(-) diff --git a/NEWS b/NEWS index 950aa010a..48db9ee3d 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ Bug fixes - properly escape folder after creation using EAS (#3237) - fixed potential organizer highjacking when using EAS (#3131) - properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082) + - properly encode id of DOM elements in Address Book module (#3239, #3245) 2.3.0 (2015-06-01) ------------------- diff --git a/UI/Templates/ContactsUI/UIxContactFoldersView.wox b/UI/Templates/ContactsUI/UIxContactFoldersView.wox index 2f8a4c415..323badb7d 100644 --- a/UI/Templates/ContactsUI/UIxContactFoldersView.wox +++ b/UI/Templates/ContactsUI/UIxContactFoldersView.wox @@ -149,11 +149,11 @@ - - + var:categories="currentContact.c_categories.asSafeJSString" + var:id="currentContact.c_name.asCSSIdentifier" + var:contactname="currentContact.c_cn.asSafeJSString"> + + diff --git a/UI/WebServerResources/ContactsUI.js b/UI/WebServerResources/ContactsUI.js index fd460859c..3386bff6e 100644 --- a/UI/WebServerResources/ContactsUI.js +++ b/UI/WebServerResources/ContactsUI.js @@ -31,7 +31,7 @@ function openContactsFolder(contactsFolder, reload, idx) { var selection; if (idx) { - selection = [idx]; + selection = [idx.asCSSIdentifier()]; } else if (contactsFolder == Contact.currentAddressBook) { var contactsList = $("contactsList"); @@ -74,7 +74,7 @@ function contactsListCallback(http) { var contact = data[i]; var row = rows[i]; row.className = contact["c_component"]; - row.setAttribute("id", contact["c_name"]); + row.setAttribute("id", contact["c_name"].asCSSIdentifier()); row.setAttribute("categories", contact["c_categories"]); row.setAttribute("contactname", contact["c_cn"]); var cells = row.getElementsByTagName("TD"); @@ -111,7 +111,7 @@ function contactsListCallback(http) { for (var j = i; j < data.length; j++) { var contact = data[j]; var row = createElement("tr", - contact["c_name"], + contact["c_name"].asCSSIdentifier(), contact["c_component"], null, { categories: contact["c_categories"], @@ -272,7 +272,7 @@ function _onContactMenuAction(folderItem, action, refresh) { if (Object.isArray(document.menuTarget) && selectedFolders.length > 0) { var selectedFolderId = $(selectedFolders[0]).readAttribute("id"); var contactIds = $(document.menuTarget).collect(function(row) { - return row.getAttribute("id"); + return row.getAttribute("id").fromCSSIdentifier(); }); for (var i = 0; i < contactIds.length; i++) { @@ -283,9 +283,7 @@ function _onContactMenuAction(folderItem, action, refresh) { } var url = ApplicationBaseURL + "/" + selectedFolderId + "/" + action; - var uids = contactIds.collect(function (s) { - return encodeURIComponent(s.unescapeHTML()); - }).join('&uid='); + var uids = contactIds.collect(encodeURIComponent).join('&uid='); if (refresh) triggerAjaxRequest(url, actionContactCallback, selectedFolderId, ('folder='+ folderId + '&uid=' + uids), @@ -312,22 +310,22 @@ function onMenuExportContact (event) { if (canExport) { var selectedFolderId = $(selectedFolders[0]).readAttribute("id"); var contactIds = document.menuTarget.collect(function(row) { - return row.readAttribute("id"); + return row.readAttribute("id").fromCSSIdentifier(); }); var url = ApplicationBaseURL + "/" + selectedFolderId + "/export" - + "?uid=" + contactIds.join("&uid="); + + "?uid=" + contactIds.collect(encodeURIComponent).join("&uid="); window.location.href = url; } } function onMenuRawContact (event) { var cname = document.menuTarget.collect(function(row) { - return row.readAttribute("id"); + return row.readAttribute("id").fromCSSIdentifier(); }); $(function() { openGenericWindow(URLForFolderID(Contact.currentAddressBook) - + "/" + cname + "/raw"); + + "/" + encodeURIComponent(cname) + "/raw"); }).delay(0.1); } @@ -350,22 +348,22 @@ function actionContactCallback(http) { } } -function loadContact(idx) { +function loadContact(cname) { if (document.contactAjaxRequest) { document.contactAjaxRequest.aborted = true; document.contactAjaxRequest.abort(); } - if (cachedContacts[Contact.currentAddressBook + "/" + idx]) { + if (cachedContacts[Contact.currentAddressBook + "/" + cname]) { var div = $('contactView'); - Contact.currentContactId = idx; - div.innerHTML = cachedContacts[Contact.currentAddressBook + "/" + idx]; + Contact.currentContactId = cname; + div.innerHTML = cachedContacts[Contact.currentAddressBook + "/" + cname]; } else { var url = (URLForFolderID(Contact.currentAddressBook) - + "/" + encodeURIComponent(idx.unescapeHTML()) + "/view?noframe=1"); + + "/" + encodeURIComponent(cname) + "/view?noframe=1"); document.contactAjaxRequest - = triggerAjaxRequest(url, contactLoadCallback, idx); + = triggerAjaxRequest(url, contactLoadCallback, cname); } } @@ -418,8 +416,9 @@ function moveTo(uri) { /* contact menu entries */ function onContactRowDblClick(event) { var t = getTarget(event); - var cname = t.parentNode.getAttribute('id'); + var cname = t.parentNode.getAttribute('id').fromCSSIdentifier(); + cname = encodeURIComponent(cname); openContactWindow(URLForFolderID(Contact.currentAddressBook) + "/" + cname + "/edit", cname); @@ -438,7 +437,7 @@ function onContactSelectionChange(event) { if (rows.length == 1) { var node = $(rows[0]); - loadContact(node.getAttribute('id')); + loadContact(node.getAttribute('id').fromCSSIdentifier()); } else if (rows.length > 1) { $('contactView').update(); @@ -479,8 +478,9 @@ function onToolbarEditSelectedContacts(event) { } for (var i = 0; i < rows.length; i++) { + var id = encodeURIComponent(rows[i].fromCSSIdentifier()); openContactWindow(URLForFolderID(Contact.currentAddressBook) - + "/" + rows[i] + "/edit", rows[i]); + + "/" + id + "/edit", rows[i]); } return false; @@ -488,16 +488,17 @@ function onToolbarEditSelectedContacts(event) { function onToolbarWriteToSelectedContacts(event) { var contactsList = $('contactsList'); - var rows = contactsList.getSelectedRowsId(); - var rowsWithEmail = 0; + var rowIds = contactsList.getSelectedRowsId(); - if (rows.length == 0) { + if (rowIds.length == 0) { showAlertDialog(_("Please select a contact.")); } else { openMailComposeWindow(ApplicationBaseURL + "/../Mail/compose" + "?folder=" + Contact.currentAddressBook.substring(1) - + "&uid=" + rows.join("&uid=")); + + "&uid=" + rowIds.collect(function(id) { + return encodeURIComponent(id.fromCSSIdentifier()); + }).join("&uid=")); if (document.body.hasClassName("popup")) window.close(); } @@ -524,26 +525,28 @@ function onToolbarDeleteSelectedContactsConfirm(dialogId) { var contactsList = $('contactsList'); var rowIds = contactsList.getSelectedRowsId(); var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/batchDelete"); + for (var i = 0; i < rowIds.length; i++) $(rowIds[i]).hide(); triggerAjaxRequest(urlstr, onContactDeleteEventCallback, rowIds, - ('ids=' + rowIds.collect(function (s) { - return encodeURIComponent(s.unescapeHTML()); + ('ids=' + rowIds.collect(function(id) { + return encodeURIComponent(id.fromCSSIdentifier()); }).join(",")), { "Content-type": "application/x-www-form-urlencoded" }); } function onContactDeleteEventCallback(http) { - var rowIds = http.callbackData; if (http.readyState == 4) { if (isHttpStatus204(http.status)) { + var rowIds = http.callbackData; var row; var nextRow = null; for (var i = 0; i < rowIds.length; i++) { - delete cachedContacts[Contact.currentAddressBook + "/" + rowIds[i]]; + var id = rowIds[i].fromCSSIdentifier(); + delete cachedContacts[Contact.currentAddressBook + "/" + id]; row = $(rowIds[i]); var displayName = row.readAttribute("contactname"); - if (Contact.currentContactId == row) { + if (Contact.currentContactId == id) { Contact.currentContactId = null; } var nextRow = row.next("tr"); @@ -555,7 +558,7 @@ function onContactDeleteEventCallback(http) { } } if (nextRow) { - Contact.currentContactId = nextRow.getAttribute("id"); + Contact.currentContactId = nextRow.getAttribute("id").fromCSSIdentifier(); nextRow.selectElement(); loadContact(Contact.currentContactId); } @@ -670,7 +673,7 @@ function onConfirmContactSelection(event) { var contactsList = $("contactsList"); var rows = contactsList.getSelectedRows(); for (i = 0; i < rows.length; i++) { - var cid = rows[i].getAttribute("id"); + var cid = rows[i].getAttribute("id").fromCSSIdentifier(); if (cid.endsWith(".vlf")) { addListToOpener(tag, Contact.currentAddressBook, currentAddressBookName, cid); } @@ -1295,7 +1298,7 @@ function onDocumentKeydown(event) { else if (keyCode == Event.KEY_DOWN || keyCode == Event.KEY_UP) { if (Contact.currentContactId) { - var row = $(Contact.currentContactId); + var row = $(Contact.currentContactId.asCSSIdentifier()); var nextRow; if (keyCode == Event.KEY_DOWN) nextRow = row.next("tr"); @@ -1319,7 +1322,7 @@ function onDocumentKeydown(event) { // Select and load the next message nextRow.selectElement(); - loadContact(nextRow.readAttribute("id")); + loadContact(nextRow.readAttribute("id").fromCSSIdentifier()); } Event.stop(event); } @@ -1465,11 +1468,12 @@ function onCategoriesMenuItemClick() { var rowIds = contactsList.getSelectedRowsId(); if (rowIds.length > 0) { for (var i = 0; i < rowIds.length; i++) { + var id = rowIds[i].fromCSSIdentifier(); var url = (URLForFolderID(Contact.currentAddressBook) - + "/" + rowIds[i] + "/" + method); + + "/" + encodeURIComponent(id) + "/" + method); url += "?category=" + encodeURIComponent(this.category); triggerAjaxRequest(url, onCategoriesMenuItemCallback, - { 'addressBook' : Contact.currentAddressBook, 'id' : rowIds[i] }); + { 'addressBook' : Contact.currentAddressBook, 'id' : id }); if (set) { setCategoryOnNode($(rowIds[i]), this.category); } @@ -1497,7 +1501,7 @@ function onCategoriesMenuItemCallback(http) { function setCategoryOnNode(contactNode, category) { var catList = contactNode.getAttribute("categories"); - var catsArray = catList.split(","); + var catsArray = catList? catList.split(",") : []; if (catsArray.indexOf(category) == -1) { catsArray.push(category); contactNode.setAttribute("categories", catsArray.join(",")); @@ -1607,9 +1611,9 @@ function dropSelectedContacts(action, toId) { if ((!currentFolderIsRemote() || action != "move") && fromId.substring(1) != toId) { - var url = ApplicationBaseURL + "/" + fromId + "/" + action; - var uids = contactIds.collect(function (s) { - return encodeURIComponent(s.unescapeHTML()); + var url = ApplicationBaseURL + fromId + "/" + action; + var uids = contactIds.collect(function(id) { + return encodeURIComponent(id.fromCSSIdentifier()); }).join('&uid='); triggerAjaxRequest(url, actionContactCallback, fromId, ('folder='+ toId + '&uid=' + uids), From 667e38ff0c42e6ed30f706ac3f25b2eea162e70a Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 11 Jun 2015 13:14:19 -0400 Subject: [PATCH 37/47] (fix) Escaping of unexpected characters in emails Properly encode characters in DOM attributes. Fixes #3241 --- UI/Contacts/UIxContactView.m | 39 +++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/UI/Contacts/UIxContactView.m b/UI/Contacts/UIxContactView.m index 259a518e9..4a4541794 100644 --- a/UI/Contacts/UIxContactView.m +++ b/UI/Contacts/UIxContactView.m @@ -1,6 +1,6 @@ /* Copyright (C) 2004 SKYRIX Software AG - Copyright (C) 2005-2014 Inverse inc. + Copyright (C) 2005-2015 Inverse inc. This file is part of SOGo. @@ -138,9 +138,13 @@ if ([email length] > 0) { fn = [card fn]; - fn = [fn stringByReplacingString: @"\"" withString: @""]; - fn = [fn stringByReplacingString: @"'" withString: @"\\\'"]; - attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@ <%@>');\"", fn, email]; + if ([fn length] > 0) + attrs = [NSString stringWithFormat: @"%@ <%@>", fn, email]; + else + attrs = email; + attrs = [attrs stringByReplacingString: @"'" withString: @"\\'"]; + attrs = [attrs stringByReplacingString: @"\"" withString: @"\\\""]; + attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@');\"", attrs]; } else { @@ -181,16 +185,23 @@ for (i = 0; i < [emails count]; i++) { email = [[emails objectAtIndex: i] flattenedValuesForKey: @""]; - fn = [card fn]; - fn = [fn stringByReplacingString: @"\"" withString: @""]; - fn = [fn stringByReplacingString: @"'" withString: @"\\\'"]; - attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@ <%@>');\"", fn, email]; - - [secondaryEmails addObject: [self _cardStringWithLabel: nil - value: email - byEscapingHTMLString: YES - asLinkScheme: @"mailto:" - withLinkAttributes: attrs]]; + if ([email length]) + { + fn = [card fn]; + if ([fn length]) + attrs = [NSString stringWithFormat: @"%@ <%@>", fn, email]; + else + attrs = email; + attrs = [attrs stringByReplacingString: @"'" withString: @"\\'"]; + attrs = [attrs stringByReplacingString: @"\"" withString: @"\\\""]; + attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@');\"", attrs]; + + [secondaryEmails addObject: [self _cardStringWithLabel: nil + value: email + byEscapingHTMLString: YES + asLinkScheme: @"mailto:" + withLinkAttributes: attrs]]; + } } } else From 5ad36626406e55e82867351c9775a357748e2b28 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 11 Jun 2015 13:32:57 -0400 Subject: [PATCH 38/47] (fix) fixed multi-domain support for sogo-tool backup/restore (#2600) --- NEWS | 1 + SoObjects/SOGo/SOGoUserManager.m | 2 +- Tools/SOGoToolBackup.m | 47 +++++++++++++++++++------------- Tools/SOGoToolRestore.m | 36 ++++++++++++++++++++---- Tools/sogo-tool.m | 4 +-- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/NEWS b/NEWS index 48db9ee3d..cb6c05995 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ Bug fixes - fixed potential organizer highjacking when using EAS (#3131) - properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082) - properly encode id of DOM elements in Address Book module (#3239, #3245) + - fixed multi-domain support for sogo-tool backup/restore (#2600) 2.3.0 (2015-06-01) ------------------- diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index a29ebb356..5dcd007df 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -568,7 +568,7 @@ static Class NSNullK; // mail: broccoli@example.com // // and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES; - // Otherwise, -_sourceCheckLogin:... would have failed because SOGo would to bind using: foo@example.com + // Otherwise, -_sourceCheckLogin:... would have failed because SOGo would try to bind using: foo@example.com // if ([[currentUser objectForKey: @"DomainLessLogin"] boolValue]) { diff --git a/Tools/SOGoToolBackup.m b/Tools/SOGoToolBackup.m index 8dd7ea04a..fb2b4d016 100644 --- a/Tools/SOGoToolBackup.m +++ b/Tools/SOGoToolBackup.m @@ -1,9 +1,6 @@ /* SOGoToolBackup.m - this file is part of SOGo * - * Copyright (C) 2009-2011 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Francis Lachapelle + * Copyright (C) 2009-2015 Inverse inc. * * 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 @@ -42,6 +39,7 @@ #import #import #import +#import #import #import "SOGoTool.h" @@ -55,7 +53,7 @@ @interface SOGoToolBackup : SOGoTool { NSString *directory; - NSArray *userIDs; + NSArray *usersToBackup; } @end @@ -83,7 +81,7 @@ if ((self = [super init])) { directory = nil; - userIDs = nil; + usersToBackup = nil; } return self; @@ -92,7 +90,7 @@ - (void) dealloc { [directory release]; - [userIDs release]; + [usersToBackup release]; [super dealloc]; } @@ -143,6 +141,7 @@ lm = [SOGoUserManager sharedUserManager]; pool = [[NSAutoreleasePool alloc] init]; + max = [users count]; user = [users objectAtIndex: 0]; @@ -198,11 +197,11 @@ NSLog (@"user '%@' unknown", user); } [allUsers autorelease]; - - ASSIGN (userIDs, [allUsers objectsForKey: @"c_uid" notFoundMarker: nil]); + + ASSIGN (usersToBackup, allUsers); DESTROY(pool); - return ([userIDs count] > 0); + return ([usersToBackup count] > 0); } - (BOOL) parseArguments @@ -410,19 +409,29 @@ return YES; } -- (BOOL) exportUser: (NSString *) uid +- (BOOL) exportUser: (NSDictionary *) theUser { + NSString *exportPath, *gcsUID, *ldapUID; NSMutableDictionary *userRecord; - NSString *exportPath; - + SOGoSystemDefaults *sd; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; userRecord = [NSMutableDictionary dictionary]; - exportPath = [directory stringByAppendingPathComponent: uid]; - return ([self extractUserFolders: uid + ldapUID = [theUser objectForKey: @"c_uid"]; + exportPath = [directory stringByAppendingPathComponent: ldapUID]; + + 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: uid + && [self extractUserLDIFRecord: ldapUID intoRecord: userRecord] - && [self extractUserPreferences: uid + && [self extractUserPreferences: gcsUID intoRecord: userRecord] && [userRecord writeToFile: exportPath atomically: NO]); @@ -438,10 +447,10 @@ pool = [NSAutoreleasePool new]; - max = [userIDs count]; + max = [usersToBackup count]; for (count = 0; rc && count < max; count++) { - rc = [self exportUser: [userIDs objectAtIndex: count]]; + rc = [self exportUser: [usersToBackup objectAtIndex: count]]; if ((count % 10) == 0) [pool emptyPool]; } diff --git a/Tools/SOGoToolRestore.m b/Tools/SOGoToolRestore.m index d3fb601f9..5e43b769e 100644 --- a/Tools/SOGoToolRestore.m +++ b/Tools/SOGoToolRestore.m @@ -1,6 +1,6 @@ /* SOGoToolRestore.m - this file is part of SOGo * - * Copyright (C) 2009-2014 Inverse inc. + * Copyright (C) 2009-2015 Inverse inc. * * 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 @@ -40,6 +40,7 @@ #import #import #import +#import #import #import @@ -65,6 +66,7 @@ typedef enum SOGoToolRestoreMode { { NSString *directory; NSString *userID; + NSString *filename; NSString *restoreFolder; BOOL destructive; /* destructive mode not handled */ SOGoToolRestoreMode restoreMode; @@ -90,6 +92,7 @@ typedef enum SOGoToolRestoreMode { { directory = nil; userID = nil; + filename = nil; restoreFolder = nil; destructive = NO; } @@ -101,6 +104,7 @@ typedef enum SOGoToolRestoreMode { { [directory release]; [userID release]; + [filename release]; [restoreFolder release]; [super dealloc]; } @@ -169,13 +173,35 @@ typedef enum SOGoToolRestoreMode { - (BOOL) fetchUserID: (NSString *) identifier { - BOOL rc; + SOGoSystemDefaults *sd; SOGoUserManager *lm; NSDictionary *infos; + NSString *uid; + + BOOL rc; lm = [SOGoUserManager sharedUserManager]; infos = [lm contactInfosForUserWithUIDorEmail: identifier]; - ASSIGN (userID, [infos objectForKey: @"c_uid"]); + uid = nil; + + if (infos) + { + sd = [SOGoSystemDefaults sharedSystemDefaults]; + uid = [infos objectForKey: @"c_uid"]; + + if ([sd enableDomainBasedUID] && [uid rangeOfString: @"@"].location == NSNotFound) + uid = [NSString stringWithFormat: @"%@@%@", + [infos objectForKey: @"c_uid"], + [infos objectForKey: @"c_domain"]]; + + if ([[infos objectForKey: @"DomainLessLogin"] boolValue]) + ASSIGN(filename, [infos objectForKey: @"c_uid"]); + else + ASSIGN(filename, uid); + } + + ASSIGN (userID, uid); + if (userID) rc = YES; else @@ -613,7 +639,7 @@ typedef enum SOGoToolRestoreMode { NSString *importPath; BOOL rc; - importPath = [directory stringByAppendingPathComponent: userID]; + importPath = [directory stringByAppendingPathComponent: filename]; userRecord = [NSDictionary dictionaryWithContentsOfFile: importPath]; if (userRecord) { @@ -631,7 +657,7 @@ typedef enum SOGoToolRestoreMode { else { rc = NO; - NSLog (@"user backup file could not be loaded"); + NSLog(@"user backup (%@) file could not be loaded", importPath); } return rc; diff --git a/Tools/sogo-tool.m b/Tools/sogo-tool.m index d2d8b5c87..bc4755416 100644 --- a/Tools/sogo-tool.m +++ b/Tools/sogo-tool.m @@ -1,8 +1,6 @@ /* sogo-tool.m - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2009-2015 Inverse inc. * * 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 From d9b4abc2f1f6f959432f4501d0937a7019c123f2 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 17 Jun 2015 08:46:09 -0400 Subject: [PATCH 39/47] (fix) improved memory usage --- ActiveSync/NSString+ActiveSync.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m index 884083db8..1564b7482 100644 --- a/ActiveSync/NSString+ActiveSync.m +++ b/ActiveSync/NSString+ActiveSync.m @@ -131,10 +131,12 @@ static NSArray *easCommandParameters = nil; } } - self = [[[NSString alloc] initWithBytes:buf length:(j) * sizeof(wchar_t) encoding:NSUTF32StringEncoding] autorelease]; + self = [[NSString alloc] initWithBytesNoCopy: buf + length: (j*sizeof(wchar_t)) + encoding: NSUTF32StringEncoding + freeWhenDone: YES]; - if (buf) free(buf); - return self; + return [self autorelease]; } - (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType From 3809c002ede0c7aebe1cc0b846362418b87b2a48 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 17 Jun 2015 15:57:16 -0400 Subject: [PATCH 40/47] Revert "(fix) ensure calendar objects have a (fake) filename" This reverts commit a145fdbb5366703c8eb564430a3d1ec8953e3275. --- SoObjects/Mailer/SOGoMailObject.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index 8dd632d65..2aabcaa75 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -777,8 +777,6 @@ static BOOL debugSoParts = NO; filename = [NSString stringWithFormat: @"unknown_%@", path]; else if ([mimeType isEqualToString: @"message/rfc822"]) filename = [NSString stringWithFormat: @"email_%@.eml", path]; - else if ([mimeType isEqualToString: @"text/calendar"]) - filename = [NSString stringWithFormat: @"calendar_%@.ics", path]; if (filename) From 82798ccb99b66c1a9d7da80e83c4fd40db63cb5c Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 19 Jun 2015 22:08:08 -0400 Subject: [PATCH 41/47] Change signature of [iCalAlarm alarmForEvent:..] The first argument doesn't have to be a iCalRepeatableEntity; a simple iCalEntity is enough. --- SoObjects/Appointments/iCalAlarm+SOGo.h | 6 +++--- SoObjects/Appointments/iCalAlarm+SOGo.m | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/SoObjects/Appointments/iCalAlarm+SOGo.h b/SoObjects/Appointments/iCalAlarm+SOGo.h index 2980f7bc6..741be01fa 100644 --- a/SoObjects/Appointments/iCalAlarm+SOGo.h +++ b/SoObjects/Appointments/iCalAlarm+SOGo.h @@ -1,6 +1,6 @@ /* iCalAlarm+SOGo.h - this file is part of SOGo * - * Copyright (C) 2014 Inverse inc. + * Copyright (C) 2015 Inverse inc. * * 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 @@ -20,11 +20,11 @@ #import -@class iCalRepeatableEntityObject; +@class iCalEntityObject; @interface iCalAlarm (SOGoExtensions) -+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity ++ (id) alarmForEvent: (iCalEntityObject *) theEntity owner: (NSString *) theOwner action: (NSString *) reminderAction unit: (NSString *) reminderUnit diff --git a/SoObjects/Appointments/iCalAlarm+SOGo.m b/SoObjects/Appointments/iCalAlarm+SOGo.m index c98c316db..cb1cc57c0 100644 --- a/SoObjects/Appointments/iCalAlarm+SOGo.m +++ b/SoObjects/Appointments/iCalAlarm+SOGo.m @@ -1,6 +1,6 @@ /* iCalAlarm+SOGo.m - this file is part of SOGo * - * Copyright (C) 2014 Inverse inc. + * Copyright (C) 2015 Inverse inc. * * 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 @@ -28,6 +28,7 @@ #import #import +#import @implementation iCalAlarm (SOGoExtensions) @@ -65,7 +66,7 @@ [alarm addChild: aAttendee]; } -+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity ++ (id) alarmForEvent: (iCalEntityObject *) theEntity owner: (NSString *) theOwner action: (NSString *) reminderAction unit: (NSString *) reminderUnit From dfecce738187a57da17e4792d0daa5cc3fc147d2 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 22 Jun 2015 09:19:54 -0400 Subject: [PATCH 42/47] (feat) added support for DeletesAsMoves over EAS --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 21 +++++++++++++++++++-- NEWS | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index e0561ff31..47ec81990 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -453,16 +453,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType inBuffer: (NSMutableString *) theBuffer { + + id aDelete, sogoObject, value; NSArray *deletions; NSString *serverId; - id aDelete, sogoObject; + BOOL deletesAsMoves, useTrash; int i; deletions = (id)[theDocumentElement getElementsByTagName: @"Delete"]; if ([deletions count]) { + // From the documention, if DeletesAsMoves is missing, we must assume it's a YES. + // See https://msdn.microsoft.com/en-us/library/gg675480(v=exchg.80).aspx for all details. + value = [theDocumentElement getElementsByTagName: @"DeletesAsMoves"]; + deletesAsMoves = YES; + useTrash = YES; + + if ([value count] && [[[value lastObject] textValue] length]) + deletesAsMoves = [[[value lastObject] textValue] boolValue]; + for (i = 0; i < [deletions count]; i++) { aDelete = [deletions objectAtIndex: i]; @@ -474,7 +485,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. acquire: NO]; if (![sogoObject isKindOfClass: [NSException class]]) - [sogoObject delete]; + { + // FIXME: handle errors here + if (deletesAsMoves) + [(SOGoMailFolder *)[sogoObject container] deleteUIDs: [NSArray arrayWithObjects: serverId, nil] useTrashFolder: &useTrash inContext: context]; + else + [sogoObject delete]; + } [theBuffer appendString: @""]; [theBuffer appendFormat: @"%@", serverId]; diff --git a/NEWS b/NEWS index cb6c05995..e195967d3 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ Enhancements - improved EAS speed, especially when fetching big attachments - now always enforce the organizer's default identity in appointments - improved the handling of default calendar categories/colors (#3200) + - added support for DeletesAsMoves over EAS Bug fixes - EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders From b5270999070251be966fd226bf38b8f595982944 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Tue, 23 Jun 2015 09:23:29 -0400 Subject: [PATCH 43/47] (fix) Events list display in Calendar module Fixes #3261 --- NEWS | 1 + UI/WebServerResources/SchedulerUI.js | 30 ++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index e195967d3..9d18569c5 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,7 @@ Bug fixes - properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082) - properly encode id of DOM elements in Address Book module (#3239, #3245) - fixed multi-domain support for sogo-tool backup/restore (#2600) + - fixed data ordering in events list of Calendar module (#3261) 2.3.0 (2015-06-01) ------------------- diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 5ee6cb528..7d0a1a353 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -1015,6 +1015,32 @@ function eventsListCallback(http) { if (http.responseText.length > 0) { var data = http.responseText.evalJSON(true); + + // [0] Event ID + // [1] Calendar ID + // [2] Calendar name + // [3] Status + // [4] Title + // [5] Start date + // [6] End date + // [7] Location + // [8] Is all day? + // [9] Classification (0 = public, 1, = private, 2 = confidential) + // [10] Category + // [11] Participants email addresses + // [12] Participants states + // [13] Owner + // [14] Is cyclic? + // [15] Next alarm + // [16] recurrence-id + // [17] isException + // [18] Editable? + // [19] Erasable? + // [20] Owner is organizer? + // [21] Description + // [22] Formatted start date + // [23] Formatted end date + for (var i = 0; i < data.length; i++) { var row = createElement("tr"); table.tBodies[0].appendChild(row); @@ -1056,12 +1082,12 @@ function eventsListCallback(http) { td = createElement("td"); row.appendChild(td); td.observe("mousedown", listRowMouseDownHandler, true); - td.update(data[i][21]); // start date + td.update(data[i][22]); // start date td = createElement("td"); row.appendChild(td); td.observe("mousedown", listRowMouseDownHandler, true); - td.update(data[i][22]); // end date + td.update(data[i][23]); // end date td = createElement("td"); row.appendChild(td); From d16bf7c1a793396534fa6824dec5b0cf51395350 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 24 Jun 2015 07:48:01 -0400 Subject: [PATCH 44/47] (fix) Tasks list display in Calendar module Fixes #3267 --- NEWS | 1 + UI/WebServerResources/SchedulerUI.js | 15 ++++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 9d18569c5..080088c49 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,7 @@ Bug fixes - properly encode id of DOM elements in Address Book module (#3239, #3245) - fixed multi-domain support for sogo-tool backup/restore (#2600) - fixed data ordering in events list of Calendar module (#3261) + - fixed data ordering in tasks list of Calendar module (#3267) 2.3.0 (2015-06-01) ------------------- diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 7d0a1a353..2151ce95e 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -1191,8 +1191,9 @@ function tasksListCallback(http) { // [12] Owner // [13] recurrence-id // [14] isException - // [15] Status CSS class (duelater, completed, etc) - // [16] Due date (formatted) + // [15] Description + // [16] Status CSS class (duelater, completed, etc) + // [17] Due date (formatted) for (var i = 0; i < data.length; i++) { var row = createElement("tr"); @@ -1208,16 +1209,12 @@ function tasksListCallback(http) { if (rTime) id += "-" + escape(rTime); row.setAttribute("id", id); - //row.cname = escape(data[i][0]); - //row.calendar = calendar; if (rTime) row.recurrenceTime = escape(rTime); row.isException = data[i][14]; - - //row.setAttribute("id", calendar + "-" + cname); //listItem.addClassName(data[i][5]); // Classification - //row.addClassName(data[i][14]); // status + row.addClassName(data[i][16]); // status row.addClassName("taskRow"); row.calendar = calendar; row.cname = cname; @@ -1262,8 +1259,8 @@ function tasksListCallback(http) { cell = createElement("td"); row.appendChild(cell); - if (data[i][16]) - cell.update(data[i][16]); // end date + if (data[i][17]) + cell.update(data[i][17]); // end date cell = createElement("td"); row.appendChild(cell); From 67d0d9586986337d5e73769df5e5484d47d6e182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Garc=C3=ADa?= Date: Fri, 26 Jun 2015 09:35:26 +0200 Subject: [PATCH 45/47] Revert "Revert "Fixed mess regarding uid/realUID"" This reverts commit 96d88de564f36e45bf9d82258c1378b881532b7f. --- SoObjects/SOGo/SOGoUser.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 91d3b1b5a..c1b0f7603 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -197,8 +197,12 @@ // When the user is associated to a domain, the [SOGoUser login] // method returns the combination login@domain while // [SOGoUser loginInDomain] only returns the login. - uid = [NSString stringWithString: realUID]; - realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain]; + r = [realUID rangeOfString: domain options: NSBackwardsSearch|NSCaseInsensitiveSearch]; + + if (r.location != NSNotFound) + uid = [realUID substringToIndex: r.location-1]; + else + uid = [NSString stringWithString: realUID]; } } From 3f1a43cc13b223d94a987bbe2a7d00f2f3a7a2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Garc=C3=ADa?= Date: Fri, 26 Jun 2015 09:35:37 +0200 Subject: [PATCH 46/47] Revert "Revert "(fix) more multi-domain fixes and cleanups"" This reverts commit 90e11d76f61dd8855f7c6ddb267feb39bf26bc52. --- SoObjects/SOGo/SOGoUser.m | 8 ++++---- SoObjects/SOGo/SOGoUserManager.m | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index c1b0f7603..12d028fd3 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -165,9 +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]) - newLogin = [newLogin substringToIndex: r.location]; - else + if (![[sd domainIds] containsObject: domain]) domain = nil; if (domain != nil && ![sd enableDomainBasedUID]) @@ -199,7 +197,9 @@ // [SOGoUser loginInDomain] only returns the login. r = [realUID rangeOfString: domain options: NSBackwardsSearch|NSCaseInsensitiveSearch]; - if (r.location != NSNotFound) + // Do NOT strip @domain.com if SOGoEnableDomainBasedUID is enabled since + // the real login most likely is the email address. + if (r.location != NSNotFound && ![sd enableDomainBasedUID]) uid = [realUID substringToIndex: r.location-1]; else uid = [NSString stringWithString: realUID]; diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 86d198d2b..c43892978 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -922,7 +922,7 @@ static Class NSNullK; { // Remove the "@" prefix used to identified groups in the ACL tables. aUID = [uid hasPrefix: @"@"] ? [uid substringFromIndex: 1] : uid; - if (domain) + if (domain && [aUID rangeOfString: @"@"].location == NSNotFound) cacheUid = [NSString stringWithFormat: @"%@@%@", aUID, domain]; else cacheUid = aUID; From dc2cc14d25857200aae9ac583532255e9ee031eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Garc=C3=ADa?= Date: Fri, 26 Jun 2015 09:35:47 +0200 Subject: [PATCH 47/47] Revert "Revert "(fix) more fixes and clarifications on the mutli-domain settings"" This reverts commit 3dd8bddc0e0e5f8cab3507e36e37f643ea70bea6. --- Documentation/SOGoInstallationGuide.asciidoc | 12 ++++++++---- SoObjects/SOGo/SOGoUser.m | 5 +++-- SoObjects/SOGo/SOGoUserManager.m | 14 +------------- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 5ecd53450..46980463b 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -2136,7 +2136,8 @@ Multi-domains Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want your installation to isolate two groups of users, you must -define a distinct authentication source for each _domain_. Following is +define a distinct authentication source for each _domain_. Your domain keys +must have the same value as your email domain you want to add. Following is the same configuration that now includes two domains (acme.com and coyote.com): @@ -2144,7 +2145,7 @@ coyote.com): { ... domains = { - acme = { + acme.com = { SOGoMailDomain = acme.com; SOGoDraftsFolderName = Drafts; SOGoUserSources = ( @@ -2165,7 +2166,7 @@ coyote.com): } ); }; - coyote = { + coyote.com = { SOGoMailDomain = coyote.com; SOGoIMAPServer = imap.coyote.com; SOGoUserSources = ( @@ -2196,7 +2197,7 @@ domains. [cols="3,47,50a"] |======================================================================= |S |SOGoEnableDomainBasedUID -|Parameter used to activate user identification by domain. Users will be +|Parameter used to enable user identification by domain. Users will be able (without being required) to login using the form `username@domain`, meaning that values of _UIDFieldName_ no longer have to be unique among all domains but only within the same domain. Internally, users will @@ -2718,6 +2719,9 @@ h|2.3.0 This will grow the "participant states" field of calendar quick tables to a larger size and add the the "c_description" column to calendar quick tables. +Moreover, if you are using a multi-domain configuration, make sure the keys for +your domains match the email domains you have defined. + h|2.2.8 |The configuration configuration parameters were renamed: diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 12d028fd3..fa6459f59 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -165,8 +165,9 @@ // 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]) - domain = nil; + if ([[sd domainIds] containsObject: domain] && + ![sd enableDomainBasedUID]) + newLogin = [newLogin substringToIndex: r.location]; if (domain != nil && ![sd enableDomainBasedUID]) // Login domains are enabled (SOGoLoginDomains) but not diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index c43892978..e53e9cb99 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -524,21 +524,9 @@ static Class NSNullK; if (r.location != NSNotFound) { - NSArray *allDomains; - int i; - *_domain = [username substringFromIndex: r.location+1]; - allDomains = [[dd dictionaryForKey: @"domains"] allValues]; - - for (i = 0; i < [allDomains count]; i++) - { - if ([*_domain isEqualToString: [[allDomains objectAtIndex: i] objectForKey: @"SOGoMailDomain"]]) - break; - } - - // We haven't found one - if (i == [allDomains count]) + if (![[[SOGoSystemDefaults sharedSystemDefaults] domainIds] containsObject: *_domain]) *_domain = nil; } }