From bb227443ed632cb03cba58e24259510c9686e1d2 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Sat, 22 Nov 2014 08:14:31 -0500 Subject: [PATCH 01/24] Check lenght of string before trying to use parameters --- ActiveSync/NSString+ActiveSync.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m index 047a5d45a..8317f8fe9 100644 --- a/ActiveSync/NSString+ActiveSync.m +++ b/ActiveSync/NSString+ActiveSync.m @@ -155,6 +155,10 @@ static NSArray *easCommandParameters = nil; const char* qs_bytes; queryString = [[components objectAtIndex: 0] dataByDecodingBase64]; + + if (![queryString length]) + return nil; + qs_bytes = (const char*)[queryString bytes]; if (!easCommandCodes) From b0633ba1f454b9b09d646ff0b512f5d13a0f236e Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Tue, 18 Jun 2013 17:50:28 +0200 Subject: [PATCH 02/24] Add check for remote_user variable for trusted proxy auth If trusted proxy authentication is on, yet the proxy did not authenticate the user, then the default authentication method is used instead of returning 'Unauthorized'. --- Apache/SOGo.conf | 14 +++++++++++++- Main/SOGo.m | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Apache/SOGo.conf b/Apache/SOGo.conf index bfe7560ba..3dd0df476 100644 --- a/Apache/SOGo.conf +++ b/Apache/SOGo.conf @@ -26,12 +26,23 @@ Alias /SOGo/WebServerResources/ \ ## need to set the "SOGoTrustProxyAuthentication" SOGo user default to YES and ## adjust the "x-webobjects-remote-user" proxy header in the "Proxy" section ## below. +# +## For full proxy-side authentication: # # AuthType XXX # Require valid-user # SetEnv proxy-nokeepalive 1 # Allow from all # +# +## For proxy-side authentication only for CardDAV and GroupDAV from external +## clients: +# +# AuthType XXX +# Require valid-user +# SetEnv proxy-nokeepalive 1 +# Allow from all +# ProxyRequests Off SetEnv proxy-nokeepalive 1 @@ -64,7 +75,8 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0 ## When using proxy-side autentication, you need to uncomment and ## adjust the following line: -# RequestHeader set "x-webobjects-remote-user" "%{REMOTE_USER}e" + RequestHeader unset "x-webobjects-remote-user" +# RequestHeader set "x-webobjects-remote-user" "%{REMOTE_USER}e" env=REMOTE_USER RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0" diff --git a/Main/SOGo.m b/Main/SOGo.m index 2eb4a9bd8..c3b015771 100644 --- a/Main/SOGo.m +++ b/Main/SOGo.m @@ -283,7 +283,7 @@ static BOOL debugLeaks; { id authenticator; - if (trustProxyAuthentication) + if (trustProxyAuthentication && [[context request] headerForKey: @"x-webobjects-remote-user"]) authenticator = [SOGoProxyAuthenticator sharedSOGoProxyAuthenticator]; else { From 5f14bc11011394ff6f1333880320eec7ceb675af Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 25 Nov 2014 17:27:03 -0500 Subject: [PATCH 03/24] Report the correct preference keys --- SoObjects/SOGo/SOGoSAML2Session.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SoObjects/SOGo/SOGoSAML2Session.m b/SoObjects/SOGo/SOGoSAML2Session.m index 9fda46089..328fb92fb 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.m +++ b/SoObjects/SOGo/SOGoSAML2Session.m @@ -102,7 +102,7 @@ LassoServerInContext (WOContext *context) filename = [sd SAML2PrivateKeyLocation]; if (!filename) [NSException raise: NSInvalidArgumentException - format: @"'SAML2PrivateKeyLocation' not set"]; + format: @"'SOGoSAML2PrivateKeyLocation' not set"]; keyContent = [NSString stringWithContentsOfFile: filename]; if (!keyContent) [NSException raise: NSGenericException @@ -112,7 +112,7 @@ LassoServerInContext (WOContext *context) filename = [sd SAML2CertificateLocation]; if (!filename) [NSException raise: NSInvalidArgumentException - format: @"'SAML2CertificateLocation' not set"]; + format: @"'SOGoSAML2CertificateLocation' not set"]; certContent = [NSString stringWithContentsOfFile: filename]; if (!certContent) [NSException raise: NSGenericException From 20e728afac25c9930af97eb2d1fb7519d415c853 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 25 Nov 2014 17:28:12 -0500 Subject: [PATCH 04/24] Remove unnecessary comments --- SoObjects/SOGo/SOGoCache.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index dac0b2d85..e2a8a51a4 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -31,8 +31,8 @@ * +defaults value = NSDictionary instance > user's defaults * +settings value = NSDictionary instance > user's settings * +attributes value = NSMutableDictionary instance > user's LDAP attributes - * +failedlogins value = - * +messagesubmissions value = + * +failedlogins value = NSDictionary instance holding the failed count and the date of the first failed authentication + * +messagesubmissions value = NSDictionary instance holding the number of messages sent, and number of recipients * +dn value = NSString instance > cached user's DN * +acl value = NSDictionary instance > ACLs on an object at specified path * + value = NSString instance (array components separated by ",") or group member logins for a specific group in domain @@ -40,8 +40,6 @@ * cas-ticket:< > value = * cas-pgtiou:< > value = * session:< > value = - * +failedlogins value = NSDictionary instance holding the failed count and the date of the first failed authentication - * +messagesubmissions value = NSDictionary instance holding the number of messages sent, and number of recipients */ From 913a75f410d8e4409960e59c110f707ffad47371 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 26 Nov 2014 13:00:47 -0500 Subject: [PATCH 05/24] Fix for bug # --- SoObjects/SOGo/SOGoCache.m | 1 + SoObjects/SOGo/SOGoSAML2Session.h | 4 +-- SoObjects/SOGo/SOGoSAML2Session.m | 50 +++++++++++++++++++++-------- SoObjects/SOGo/SOGoSession.h | 5 +-- SoObjects/SOGo/SOGoSession.m | 6 +--- SoObjects/SOGo/SOGoSystemDefaults.h | 1 + SoObjects/SOGo/SOGoSystemDefaults.m | 5 +++ 7 files changed, 46 insertions(+), 26 deletions(-) diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index e2a8a51a4..557beb09a 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -40,6 +40,7 @@ * cas-ticket:< > value = * cas-pgtiou:< > value = * session:< > value = + * saml2-login:< > value = */ diff --git a/SoObjects/SOGo/SOGoSAML2Session.h b/SoObjects/SOGo/SOGoSAML2Session.h index 00a3c4945..de7e086ea 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.h +++ b/SoObjects/SOGo/SOGoSAML2Session.h @@ -1,8 +1,6 @@ /* SOGoSAML2Session.h - this file is part of SOGo * - * Copyright (C) 2012 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2012-2014 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 diff --git a/SoObjects/SOGo/SOGoSAML2Session.m b/SoObjects/SOGo/SOGoSAML2Session.m index 328fb92fb..0f947d565 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.m +++ b/SoObjects/SOGo/SOGoSAML2Session.m @@ -1,8 +1,6 @@ /* SOGoSAML2Session.m - this file is part of SOGo * - * Copyright (C) 2012 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2012-2014 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 @@ -215,7 +213,6 @@ LassoServerInContext (WOContext *context) - (void) _updateDataFromLogin { - // LassoSamlp2Response *response; LassoSaml2Assertion *saml2Assertion; GList *statementList, *attributeList; LassoSaml2AttributeStatement *statement; @@ -223,10 +220,15 @@ LassoServerInContext (WOContext *context) LassoSaml2AttributeValue *value; LassoMiscTextNode *textNode; LassoSaml2NameID *nameIdentifier; + SOGoSystemDefaults *sd; + NSString *loginAttribue; + gchar *dump; - saml2Assertion - = LASSO_SAML2_ASSERTION (lasso_login_get_assertion (lassoLogin)); + saml2Assertion = LASSO_SAML2_ASSERTION (lasso_login_get_assertion (lassoLogin)); + sd = [SOGoSystemDefaults sharedSystemDefaults]; + loginAttribue = [sd SAML2LoginAttribute]; + if (saml2Assertion) { /* deduce user login */ @@ -241,22 +243,42 @@ LassoServerInContext (WOContext *context) while (!login && attributeList) { attribute = LASSO_SAML2_ATTRIBUTE (attributeList->data); - if (strcmp (attribute->Name, "uid") == 0) + if (loginAttribue && (strcmp (attribute->Name, [loginAttribue UTF8String]) == 0)) { value = LASSO_SAML2_ATTRIBUTE_VALUE (attribute->AttributeValue->data); textNode = value->any->data; + + // If we got an @ sign in the value, it's most likely an email address + // so we'll ask SOGoUserManager about this login = [NSString stringWithUTF8String: textNode->content]; + + if ([login rangeOfString: @"@"].location != NSNotFound) + { + login = [[SOGoUserManager sharedUserManager] getUIDForEmail: login]; + } + [login retain]; } - else if (strcmp (attribute->Name, "mail") == 0) + else if (!loginAttribue) { - value = LASSO_SAML2_ATTRIBUTE_VALUE (attribute->AttributeValue->data); - textNode = value->any->data; - login = [[SOGoUserManager sharedUserManager] getUIDForEmail: [NSString stringWithUTF8String: textNode->content]]; - [login retain]; + // We fallback on "standard" attributes such as "uid" and "mail" + if (strcmp (attribute->Name, "uid") == 0) + { + value = LASSO_SAML2_ATTRIBUTE_VALUE (attribute->AttributeValue->data); + textNode = value->any->data; + login = [NSString stringWithUTF8String: textNode->content]; + [login retain]; + } + else if (strcmp (attribute->Name, "mail") == 0) + { + value = LASSO_SAML2_ATTRIBUTE_VALUE (attribute->AttributeValue->data); + textNode = value->any->data; + login = [[SOGoUserManager sharedUserManager] getUIDForEmail: [NSString stringWithUTF8String: textNode->content]]; + [login retain]; + } } - else - attributeList = attributeList->next; + + attributeList = attributeList->next; } statementList = statementList->next; } diff --git a/SoObjects/SOGo/SOGoSession.h b/SoObjects/SOGo/SOGoSession.h index 42e2ddd9a..a22b46e94 100644 --- a/SoObjects/SOGo/SOGoSession.h +++ b/SoObjects/SOGo/SOGoSession.h @@ -1,9 +1,6 @@ /* SOGoSession.h - this file is part of SOGo * - * Copyright (C) 2010-2011 Inverse inc. - * - * Author: Ludovic Marcotte - * Francis Lachapelle + * Copyright (C) 2010-2014 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 diff --git a/SoObjects/SOGo/SOGoSession.m b/SoObjects/SOGo/SOGoSession.m index e2137349f..235ef371e 100644 --- a/SoObjects/SOGo/SOGoSession.m +++ b/SoObjects/SOGo/SOGoSession.m @@ -1,10 +1,6 @@ /* SOGoSession.m - this file is part of SOGo * - * Copyright (C) 2010-2011 Inverse inc. - * - * Author: Ludovic Marcotte - * Francis Lachapelle - + * Copyright (C) 2010-2014 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 diff --git a/SoObjects/SOGo/SOGoSystemDefaults.h b/SoObjects/SOGo/SOGoSystemDefaults.h index bf6032c9b..4d68d7538 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.h +++ b/SoObjects/SOGo/SOGoSystemDefaults.h @@ -80,6 +80,7 @@ - (NSString *) SAML2IdpMetadataLocation; - (NSString *) SAML2IdpPublicKeyLocation; - (NSString *) SAML2IdpCertificateLocation; +- (NSString *) SAML2LoginAttribute; - (BOOL) SAML2LogoutEnabled; - (BOOL) enablePublicAccess; diff --git a/SoObjects/SOGo/SOGoSystemDefaults.m b/SoObjects/SOGo/SOGoSystemDefaults.m index b9f1f0225..d48ab7c03 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.m +++ b/SoObjects/SOGo/SOGoSystemDefaults.m @@ -511,6 +511,11 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict, return [self boolForKey: @"SOGoSAML2LogoutEnabled"]; } +- (NSString *) SAML2LoginAttribute +{ + return [self stringForKey: @"SOGoSAML2LoginAttribute"]; +} + - (BOOL) enablePublicAccess { return [self boolForKey: @"SOGoEnablePublicAccess"]; From 89917c941caf9f7c4ca18e498bba06298f7f26f6 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 26 Nov 2014 13:01:50 -0500 Subject: [PATCH 06/24] New entry for bug #2381 --- NEWS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS b/NEWS index 3a1e23e6c..dd4869758 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +2.2.11 (2014-xx-xx) +------------------- + +Bug fixes + - Now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) + 2.2.10 (2014-11-21) ------------------- From 5a5464dc610cddb87b9c07e97699c24a767f5ed7 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 26 Nov 2014 13:24:04 -0500 Subject: [PATCH 07/24] An other fix for #2930 --- UI/MainUI/SOGoUserHomePage.m | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index f3a517549..85e6c669c 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -216,21 +216,22 @@ interval = [endDate timeIntervalSinceDate: startDate] + 60; - // Slices of 15 minutes. The +4 is to take into account that we can - // have a timezone change during the freebusy lookup. - intervals = interval / intervalSeconds + 4; + // Slices of 15 minutes. The +8 is to take into account that we can + // have a timezone change during the freebusy lookup. We have +4 at the + // beginning and +4 at the end. + intervals = interval / intervalSeconds + 8; // Build a bit string representation of the freebusy data for the period freeBusyItems = NSZoneCalloc (NULL, intervals, sizeof (int)); - [self _fillFreeBusyItems: freeBusyItems + [self _fillFreeBusyItems: (freeBusyItems+4) count: intervals withRecords: [fb fetchFreeBusyInfosFrom: start to: end forContact: uid] fromStartDate: startDate toEndDate: endDate]; - // Convert bit string to a NSArray + // Convert bit string to a NSArray. We also skip by the default the non-requested information. freeBusy = [NSMutableArray arrayWithCapacity: intervals]; - for (count = 0; count < intervals; count++) + for (count = 4; count < (intervals-4); count++) { [freeBusy addObject: [NSString stringWithFormat: @"%d", *(freeBusyItems + count)]]; } @@ -299,14 +300,16 @@ sd = [SOGoSystemDefaults sharedSystemDefaults]; if ([[sd authenticationType] isEqualToString: @"cas"]) - redirectURL = [SOGoCASSession CASURLWithAction: @"logout" - andParameters: nil]; + { + redirectURL = [SOGoCASSession CASURLWithAction: @"logout" + andParameters: nil]; + } else { container = [[self clientObject] container]; redirectURL = [container baseURLInContext: context]; } - + return redirectURL; } From be608dc76c7217c62152e869ab17c9b237a4e99a Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 26 Nov 2014 15:09:30 -0500 Subject: [PATCH 08/24] Bug fixes for #2378 and #2377 and documentation improvements --- Documentation/SOGoInstallationGuide.asciidoc | 37 ++++++++++-- NEWS | 7 ++- SoObjects/SOGo/SOGoSAML2Metadata.xml | 27 +++++++++ SoObjects/SOGo/SOGoSAML2Session.h | 4 +- SoObjects/SOGo/SOGoSAML2Session.m | 43 ++++++++++++-- UI/MainUI/SOGoSAML2Actions.m | 62 +++++++++++++++++++- UI/MainUI/product.plist | 5 ++ 7 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 SoObjects/SOGo/SOGoSAML2Metadata.xml diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 757055903..728c3d9bf 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -425,25 +425,31 @@ Defaults to `YES` when unset. |The location of the SSL private key file on the filesystem that is used by SOGo to sign and encrypt communications with the SAML2 identity provider. This file must be generated for each running SOGo service -(rather than host). +(rather than host). Make sure this file is readable by the SOGo user. |S |SOGoSAML2CertiticateLocation |The location of the SSL certificate file. This file must be generated -for each running SOGo service. +for each running SOGo service. Make sure this file is readable by the SOGo user. |S |SOGoSAML2IdpMetadataLocation |The location of the metadata file that describes the services available -on the SAML2 identify provider. +on the SAML2 identify provider. The content of this file is usually generated +directly by your SAML 2.0 IdP solution. For example, using SimpleSAMLphp, you +can get the metadata directly from https://MYSERVER/simplesaml/saml2/idp/metadata.php +Make sure this file is readable by the SOGo user. |S |SOGoSAML2IdpPublicKeyLocation |The location of the SSL public key file on the filesystem that is used by SOGo to sign and encrypt communications with the SAML2 identity provider. This file should be part of the setup of your identity -provider. +provider. Make sure this file is readable by the SOGo user. |S |SOGoSAML2IdpCertificateLocation |The location of the SSL certificate file. This file should be part of -the setup of your identity provider. +the setup of your identity provider. Make sure this file is readable by the SOGo user. + +|S |SOGoSAML2LoginAttribute +|The attribute provided by the IdP to identify the user in SOGo. |S |SOGoSAML2LogoutEnabled |Boolean value indicated whether the "Logout" link is enabled when using @@ -1230,13 +1236,32 @@ documentation of your identity provider and the SAML2 configuration keys that are listed above for proper setup. Once a SOGo instance is configured properly, the metadata for that instance can be retrieved from `http:///SOGo/saml2-metadata` for registration with the -identity provider. +identity provider. SOGo will dynamically generate the metadata based on +the SOGoSAML2CertificateLocation's content and the SOGo server name. + +When using SimpleSAMLphp, make sure the convert OID to names by modifying your +`metadata/saml20-idp-hosted.php` to contain something like this: + +---- + 'attributes.NameFormat' => 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri', + 'authproc' => array( + 100 => array('class' => 'core:AttributeMap', 'oid2name'), + ), +---- + +If you want to test the IdP-initiated logout using SimpleSAMLphp, you can do so by opening +the following URL: + +---- +https://idp.example.org/simplesaml/saml2/idp/SingleLogoutService.php?ReturnTo=www.sogo.nu +---- In order to relay authentication information to your IMAP server and if you make use of the CrudeSAML SASL plugin, you need to make sure that _NGImap4AuthMechanism_ is configured to use the `SAML` mechanism. If you make use of the CrudeSAML PAM plugin, this value may be left empty. + Database Configuration ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/NEWS b/NEWS index dd4869758..2ee8838af 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,13 @@ 2.2.11 (2014-xx-xx) ------------------- +Enhancements + - Improved the SAML2 documentation + Bug fixes - - Now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) + - Now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) (#2381) + - Added support for IdP-initiated SAML2 logout (#2377) + - We now generate SAML2 metadata on the fly (#2378) 2.2.10 (2014-11-21) ------------------- diff --git a/SoObjects/SOGo/SOGoSAML2Metadata.xml b/SoObjects/SOGo/SOGoSAML2Metadata.xml new file mode 100644 index 000000000..9e4980b47 --- /dev/null +++ b/SoObjects/SOGo/SOGoSAML2Metadata.xml @@ -0,0 +1,27 @@ + + + + %{certificate}%{certificate} + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + diff --git a/SoObjects/SOGo/SOGoSAML2Session.h b/SoObjects/SOGo/SOGoSAML2Session.h index de7e086ea..e08217441 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.h +++ b/SoObjects/SOGo/SOGoSAML2Session.h @@ -39,7 +39,9 @@ NSString *assertion; } -+ (NSString *) metadataInContext: (WOContext *) context; ++ (NSString *) metadataInContext: (WOContext *) context + certificate: (NSString *) certificate; + + (NSString *) authenticationURLInContext: (WOContext *) context; + (SOGoSAML2Session *) SAML2SessionInContext: (WOContext *) context; diff --git a/SoObjects/SOGo/SOGoSAML2Session.m b/SoObjects/SOGo/SOGoSAML2Session.m index 0f947d565..261b3c057 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.m +++ b/SoObjects/SOGo/SOGoSAML2Session.m @@ -46,6 +46,28 @@ #import "SOGoSAML2Session.h" +@interface NSString (SOGoCertificateExtension) + +- (NSString *) cleanedUpCertificate; + +@end + +@implementation NSString (SOGoCertificateExtension) + +- (NSString *) cleanedUpCertificate +{ + NSMutableArray *a; + + a = [NSMutableArray arrayWithArray: [self componentsSeparatedByString: @"\n"]]; + [a removeObjectAtIndex: 0]; + [a removeLastObject]; + [a removeLastObject]; + + return [a componentsJoinedByString: @""]; +} + +@end + @interface WOContext (SOGoSAML2Extension) - (NSString *) SAML2ServerURLString; @@ -117,7 +139,9 @@ LassoServerInContext (WOContext *context) format: @"certificate file '%@' could not be read", filename]; - metadata = [SOGoSAML2Session metadataInContext: context]; + metadata = [SOGoSAML2Session metadataInContext: context + certificate: certContent]; + /* FIXME: enable key password in config ? */ server = lasso_server_new_from_buffers ([metadata UTF8String], [keyContent UTF8String], @@ -179,8 +203,10 @@ LassoServerInContext (WOContext *context) } + (NSString *) metadataInContext: (WOContext *) context + certificate: (NSString *) certificate { - NSString *metadata, *serverURLString, *filename; + NSString *serverURLString, *filename; + NSMutableString *metadata; NSBundle *bundle; bundle = [NSBundle bundleForClass: self]; @@ -188,9 +214,16 @@ LassoServerInContext (WOContext *context) if (filename) { serverURLString = [context SAML2ServerURLString]; - metadata = [[NSString stringWithContentsOfFile: filename] - stringByReplacingString: @"%{base_url}" - withString: serverURLString]; + + metadata = [NSMutableString stringWithContentsOfFile: filename]; + [metadata replaceOccurrencesOfString: @"%{base_url}" + withString: serverURLString + options: 0 + range: NSMakeRange(0, [metadata length])]; + [metadata replaceOccurrencesOfString: @"%{certificate}" + withString: [certificate cleanedUpCertificate] + options: 0 + range: NSMakeRange(0, [metadata length])]; } else metadata = nil; diff --git a/UI/MainUI/SOGoSAML2Actions.m b/UI/MainUI/SOGoSAML2Actions.m index 53ab5d1db..834213493 100644 --- a/UI/MainUI/SOGoSAML2Actions.m +++ b/UI/MainUI/SOGoSAML2Actions.m @@ -33,6 +33,8 @@ #import #import +#import +#import #import @interface SOGoSAML2Actions : WODirectAction @@ -42,20 +44,76 @@ - (WOResponse *) saml2MetadataAction { + NSString *metadata, *certContent; + SOGoSystemDefaults *sd; WOResponse *response; - NSString *metadata; response = [context response]; [response setHeader: @"application/xml; charset=utf-8" forKey: @"content-type"]; - metadata = [SOGoSAML2Session metadataInContext: context]; + sd = [SOGoSystemDefaults sharedSystemDefaults]; + + certContent = [NSString stringWithContentsOfFile: [sd SAML2CertificateLocation]]; + + metadata = [SOGoSAML2Session metadataInContext: context + certificate: certContent]; + [response setContentEncoding: NSUTF8StringEncoding]; [response appendContentString: metadata]; return response; } +- (WOResponse *) saml2SingleLogoutServiceAction +{ + NSString *userName, *value, *cookieName; + SOGoWebAuthenticator *auth; + WOResponse *response; + NSCalendarDate *date; + WOCookie *cookie; + NSArray *creds; + + userName = [[context activeUser] login]; + [self logWithFormat: @"SAML2 IdP-initiated SLO for user '%@'", userName]; + + response = [context response]; + + if ([userName isEqualToString: @"anonymous"]) + return response; + + cookie = nil; + + date = [NSCalendarDate calendarDate]; + [date setTimeZone: [NSTimeZone timeZoneWithAbbreviation: @"GMT"]]; + + // We cleanup the memecached/database session cache. We do this before + // invoking _logoutCookieWithDate: in order to obtain its value. + auth = [[SoApplication application] authenticatorInContext: context]; + + if ([auth respondsToSelector: @selector (cookieNameInContext:)]) + { + cookieName = [auth cookieNameInContext: context]; + value = [[context request] cookieValueForKey: cookieName]; + creds = [auth parseCredentials: value]; + + if ([creds count] > 1) + [SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]]; + + if ([cookieName length]) + { + cookie = [WOCookie cookieWithName: cookieName value: @"discard"]; + [cookie setPath: [NSString stringWithFormat: @"/%@/", [[context request] applicationName]]]; + [cookie setExpires: [date yesterday]]; + } + } + + if (cookie) + [response addCookie: cookie]; + + return response; +} + - (WOCookie *) _authLocationResetCookieWithName: (NSString *) cookieName { WOCookie *locationCookie; diff --git a/UI/MainUI/product.plist b/UI/MainUI/product.plist index e5c9d2ec1..c9e230177 100644 --- a/UI/MainUI/product.plist +++ b/UI/MainUI/product.plist @@ -133,6 +133,11 @@ actionClass = "SOGoSAML2Actions"; actionName = "saml2SignOnPOST"; }; + saml2-sls = { + protectedBy = ""; + actionClass = "SOGoSAML2Actions"; + actionName = "saml2SingleLogoutService"; + }; /* saml2-signon-redirect = { protectedBy = ""; actionClass = "SOGoSAML2Actions"; From c3715c94857efa77e1e38b814e0f0ee09cc9c678 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 26 Nov 2014 15:27:36 -0500 Subject: [PATCH 09/24] Added additional bugfix for #2982 --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 1c15f37e6..5690fea08 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -1050,11 +1050,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { // Collection not found - next folderSync will do the cleanup //NSLog(@"Sync Collection not found %@ %@", collectionId, realCollectionId); - [theBuffer appendString: @""]; - [theBuffer appendFormat: @"%@", syncKey]; - [theBuffer appendFormat: @"%@", collectionId]; - [theBuffer appendFormat: @"%d", 8]; - [theBuffer appendString: @""]; + //Outlook doesn't like following response + //[theBuffer appendString: @""]; + //[theBuffer appendFormat: @"%@", syncKey]; + //[theBuffer appendFormat: @"%@", collectionId]; + //[theBuffer appendFormat: @"%d", 8]; + //[theBuffer appendString: @""]; return; } From 1b715e0812dba3d9f2c4d3f2daa0cbd4313f8def Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 27 Nov 2014 11:37:08 -0500 Subject: [PATCH 10/24] We now handle correctly the SOGo logout when using SAML (#2376 and #2379) --- Documentation/SOGoInstallationGuide.asciidoc | 4 +- NEWS | 1 + SoObjects/SOGo/SOGoCASSession.h | 4 +- SoObjects/SOGo/SOGoCASSession.m | 4 +- SoObjects/SOGo/SOGoCache.h | 1 + SoObjects/SOGo/SOGoCache.m | 14 +++++- SoObjects/SOGo/SOGoSAML2Session.h | 6 +++ SoObjects/SOGo/SOGoSAML2Session.m | 51 ++++++++++++++------ SoObjects/SOGo/SOGoWebAuthenticator.m | 2 +- UI/MainUI/SOGoSAML2Actions.m | 18 ++++++- UI/MainUI/SOGoUserHomePage.m | 46 ++++++++++++++++++ 11 files changed, 125 insertions(+), 26 deletions(-) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 728c3d9bf..b28e512a4 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -453,7 +453,9 @@ the setup of your identity provider. Make sure this file is readable by the SOGo |S |SOGoSAML2LogoutEnabled |Boolean value indicated whether the "Logout" link is enabled when using -SAML2 as authentication mechanism. +SAML2 as authentication mechanism. When using this feature, SOGo will invoke +the IdP to proceed with the logout procedure. When the user clicks on the logout +button, a redirection will be made to the IdP to trigger the logout. |D |SOGoTimeZone |Parameter used to set a default time zone for users. The default diff --git a/NEWS b/NEWS index 2ee8838af..6c4e3e58f 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ Bug fixes - Now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) (#2381) - Added support for IdP-initiated SAML2 logout (#2377) - We now generate SAML2 metadata on the fly (#2378) + - We now handle correctly the SOGo logout when using SAML (#2376 and #2379) 2.2.10 (2014-11-21) ------------------- diff --git a/SoObjects/SOGo/SOGoCASSession.h b/SoObjects/SOGo/SOGoCASSession.h index ade1cf0a5..e7d6fabb9 100644 --- a/SoObjects/SOGo/SOGoCASSession.h +++ b/SoObjects/SOGo/SOGoCASSession.h @@ -1,8 +1,6 @@ /* SOGoCASSession.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2014 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 diff --git a/SoObjects/SOGo/SOGoCASSession.m b/SoObjects/SOGo/SOGoCASSession.m index 4d684c382..8c77df29b 100644 --- a/SoObjects/SOGo/SOGoCASSession.m +++ b/SoObjects/SOGo/SOGoCASSession.m @@ -1,8 +1,6 @@ /* SOGoCASSession.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2014 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 diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index 648fc4b60..69ef6f942 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -132,6 +132,7 @@ - (NSDictionary *) saml2LoginDumpsForIdentifier: (NSString *) identifier; - (void) setSaml2LoginDumps: (NSDictionary *) dump forIdentifier: (NSString *) identifier; +- (void) removeSAML2LoginDumpsForIdentifier: (NSString *) identifier; // // ACL caching support diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index 557beb09a..13e171e09 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -673,11 +673,13 @@ static memcached_st *handle = NULL; { key = [NSString stringWithFormat: @"cas-ticket:%@", ticket]; [self removeValueForKey: key]; - [self debugWithFormat: @"Removed session: %@", session]; + [self debugWithFormat: @"Removed CAS session: %@", session]; } } +// // SAML2 support +// - (NSDictionary *) saml2LoginDumpsForIdentifier: (NSString *) identifier { NSString *key, *jsonString; @@ -698,6 +700,16 @@ static memcached_st *handle = NULL; [self setValue: [dump jsonRepresentation] forKey: key]; } +- (void) removeSAML2LoginDumpsForIdentifier: (NSString *) identifier +{ + NSString *key; + + key = [NSString stringWithFormat: @"saml2-login:%@", identifier]; + + [self removeValueForKey: key]; + [self debugWithFormat: @"Removed SAML2 session for identifier: %@", identifier]; +} + // // ACL caching code // diff --git a/SoObjects/SOGo/SOGoSAML2Session.h b/SoObjects/SOGo/SOGoSAML2Session.h index e08217441..0592f38e1 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.h +++ b/SoObjects/SOGo/SOGoSAML2Session.h @@ -37,8 +37,12 @@ NSString *login; NSString *identifier; NSString *assertion; + NSString *identity; + NSString *session; } ++ (LassoServer *) lassoServerInContext: (WOContext *) context; + + (NSString *) metadataInContext: (WOContext *) context certificate: (NSString *) certificate; @@ -54,6 +58,8 @@ - (NSString *) login; - (NSString *) identifier; - (NSString *) assertion; +- (NSString *) identity; +- (NSString *) session; @end diff --git a/SoObjects/SOGo/SOGoSAML2Session.m b/SoObjects/SOGo/SOGoSAML2Session.m index 261b3c057..96ced1516 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.m +++ b/SoObjects/SOGo/SOGoSAML2Session.m @@ -105,8 +105,7 @@ static NSMapTable *serverTable = nil; lasso_init (); } -static LassoServer * -LassoServerInContext (WOContext *context) ++ (LassoServer *) lassoServerInContext: (WOContext *) context { NSString *urlString, *metadata, *filename, *keyContent, *certContent, *idpKeyFilename, *idpCertFilename; @@ -170,7 +169,7 @@ LassoServerInContext (WOContext *context) NSString *url; GList *providers; - server = LassoServerInContext (context); + server = [SOGoSAML2Session lassoServerInContext: context]; tempLogin = lasso_login_new (server); providers = g_hash_table_get_keys (server->providers); @@ -239,6 +238,8 @@ LassoServerInContext (WOContext *context) login = nil; identifier = nil; assertion = nil; + identity = nil; + session = nil; } return self; @@ -350,7 +351,7 @@ LassoServerInContext (WOContext *context) if ((self = [self init])) { - server = LassoServerInContext (context); + server = [SOGoSAML2Session lassoServerInContext: context]; lassoLogin = lasso_login_new (server); if (saml2Dump) { @@ -358,12 +359,17 @@ LassoServerInContext (WOContext *context) ASSIGN (login, [saml2Dump objectForKey: @"login"]); ASSIGN (identifier, [saml2Dump objectForKey: @"identifier"]); ASSIGN (assertion, [saml2Dump objectForKey: @"assertion"]); - dump = [[saml2Dump objectForKey: @"identity"] UTF8String]; + + ASSIGN(identity, [saml2Dump objectForKey: @"identity"]); + dump = [identity UTF8String]; if (dump) lasso_profile_set_identity_from_dump (profile, dump); - dump = [[saml2Dump objectForKey: @"session"] UTF8String]; + + ASSIGN (session, [saml2Dump objectForKey: @"session"]); + dump = [session UTF8String]; if (dump) lasso_profile_set_session_from_dump (profile, dump); + lasso_login_accept_sso (lassoLogin); // if (rc) // [NSException raiseSAML2Exception: rc]; @@ -381,6 +387,9 @@ LassoServerInContext (WOContext *context) [login release]; [identifier release]; [assertion release]; + [identity release]; + [session release]; + [super dealloc]; } @@ -433,13 +442,23 @@ LassoServerInContext (WOContext *context) return assertion; } +- (NSString *) identity +{ + return identity; +} + +- (NSString *) session +{ + return session; +} + - (void) processAuthnResponse: (NSString *) authnResponse { lasso_error_t rc; gchar *responseData, *dump; LassoProfile *profile; - LassoIdentity *identity; - LassoSession *session; + LassoIdentity *lasso_identity; + LassoSession *lasso_session; NSString *nsDump; NSMutableDictionary *saml2Dump; @@ -463,22 +482,22 @@ LassoServerInContext (WOContext *context) profile = LASSO_PROFILE (lassoLogin); - session = lasso_profile_get_session (profile); - if (session) + lasso_session = lasso_profile_get_session (profile); + if (lasso_session) { - dump = lasso_session_dump (session); + dump = lasso_session_dump (lasso_session); nsDump = [NSString stringWithUTF8String: dump]; [saml2Dump setObject: nsDump forKey: @"session"]; - lasso_session_destroy (session); + lasso_session_destroy (lasso_session); } - identity = lasso_profile_get_identity (profile); - if (identity) + lasso_identity = lasso_profile_get_identity (profile); + if (lasso_identity) { - dump = lasso_identity_dump (identity); + dump = lasso_identity_dump (lasso_identity); nsDump = [NSString stringWithUTF8String: dump]; [saml2Dump setObject: nsDump forKey: @"identity"]; - lasso_identity_destroy (identity); + lasso_identity_destroy (lasso_identity); } [[SOGoCache sharedCache] setSaml2LoginDumps: saml2Dump diff --git a/SoObjects/SOGo/SOGoWebAuthenticator.m b/SoObjects/SOGo/SOGoWebAuthenticator.m index 6d6aacad1..bd386d035 100644 --- a/SoObjects/SOGo/SOGoWebAuthenticator.m +++ b/SoObjects/SOGo/SOGoWebAuthenticator.m @@ -1,6 +1,6 @@ /* SOGoWebAuthenticator.m - this file is part of SOGo * - * Copyright (C) 2007-2013 Inverse inc. + * Copyright (C) 2007-2014 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 diff --git a/UI/MainUI/SOGoSAML2Actions.m b/UI/MainUI/SOGoSAML2Actions.m index 834213493..4a92221bf 100644 --- a/UI/MainUI/SOGoSAML2Actions.m +++ b/UI/MainUI/SOGoSAML2Actions.m @@ -32,6 +32,7 @@ #import #import +#import #import #import #import @@ -65,9 +66,12 @@ return response; } +// +// +// - (WOResponse *) saml2SingleLogoutServiceAction { - NSString *userName, *value, *cookieName; + NSString *userName, *value, *cookieName, *domain, *username, *password; SOGoWebAuthenticator *auth; WOResponse *response; NSCalendarDate *date; @@ -96,6 +100,18 @@ cookieName = [auth cookieNameInContext: context]; value = [[context request] cookieValueForKey: cookieName]; creds = [auth parseCredentials: value]; + + // We first delete our memcached entry + value = [SOGoSession valueForSessionKey: [creds lastObject]]; + domain = nil; + + [SOGoSession decodeValue: value + usingKey: [creds objectAtIndex: 0] + login: &username + domain: &domain + password: &password]; + + [[SOGoCache sharedCache] removeSAML2LoginDumpsForIdentifier: password]; if ([creds count] > 1) [SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]]; diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 85e6c669c..b3527a085 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -33,7 +33,12 @@ #import #import + +#import #import +#if defined(SAML2_CONFIG) +#import +#endif #import #import #import @@ -304,6 +309,47 @@ redirectURL = [SOGoCASSession CASURLWithAction: @"logout" andParameters: nil]; } +#if defined(SAML2_CONFIG) + else if ([[sd authenticationType] isEqualToString: @"saml2"]) + { + NSString *username, *password, *domain, *value; + SOGoSAML2Session *saml2Session; + SOGoWebAuthenticator *auth; + LassoServer *server; + LassoLogout *logout; + NSArray *creds; + + auth = [[self clientObject] authenticatorInContext: context]; + value = [[context request] cookieValueForKey: [auth cookieNameInContext: context]]; + creds = [auth parseCredentials: value]; + + value = [SOGoSession valueForSessionKey: [creds lastObject]]; + + domain = nil; + + [SOGoSession decodeValue: value + usingKey: [creds objectAtIndex: 0] + login: &username + domain: &domain + password: &password]; + + saml2Session = [SOGoSAML2Session SAML2SessionWithIdentifier: password + inContext: context]; + + server = [SOGoSAML2Session lassoServerInContext: context]; + + logout = lasso_logout_new(server); + + lasso_profile_set_session_from_dump(LASSO_PROFILE(logout), [[saml2Session session] UTF8String]); + lasso_profile_set_identity_from_dump(LASSO_PROFILE(logout), [[saml2Session session] UTF8String]); + lasso_logout_init_request(logout, NULL, LASSO_HTTP_METHOD_REDIRECT); + lasso_logout_build_request_msg(logout); + redirectURL = [NSString stringWithFormat: @"%s", LASSO_PROFILE(logout)->msg_url]; + + // We destroy our cache entry, the session will be taken care by the caller + [[SOGoCache sharedCache] removeSAML2LoginDumpsForIdentifier: password]; + } +#endif else { container = [[self clientObject] container]; From 9ffa32eebdcc5d3b102aa4d86a93590684800cdc Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Sun, 30 Nov 2014 17:35:39 -0500 Subject: [PATCH 11/24] Enable SAML support on all Debian-based distro --- packaging/debian-multiarch/control | 4 ++-- packaging/debian-multiarch/rules | 4 +++- packaging/debian/control | 4 ++-- packaging/debian/control-squeeze | 4 ++-- packaging/debian/rules | 3 +-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packaging/debian-multiarch/control b/packaging/debian-multiarch/control index 1c142936f..d3376f5c2 100644 --- a/packaging/debian-multiarch/control +++ b/packaging/debian-multiarch/control @@ -1,7 +1,7 @@ Source: sogo Priority: optional Maintainer: Inverse Support -Build-Depends: debhelper (>= 8.0.0), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libmapi-dev, libmapistore-dev, libmapiproxy-dev, libwbxml2-dev (>= 0.11.2) +Build-Depends: debhelper (>= 8.0.0), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libmapi-dev, libmapistore-dev, libmapiproxy-dev, libwbxml2-dev (>= 0.11.2), liblasso3-dev (>= 2.3.5) Section: web Standards-Version: 3.9.2 @@ -10,7 +10,7 @@ Pre-Depends: ${misc:Pre-Depends} Multi-Arch: same Section: web Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, tmpreaper, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3, zip +Depends: ${shlibs:Depends}, ${misc:Depends}, tmpreaper, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3, zip, liblasso3 (>= 2.3.5) Recommends: memcached Suggests: nginx Description: a modern and scalable groupware diff --git a/packaging/debian-multiarch/rules b/packaging/debian-multiarch/rules index 977edfa24..4b733f2a8 100755 --- a/packaging/debian-multiarch/rules +++ b/packaging/debian-multiarch/rules @@ -6,12 +6,14 @@ export DH_VERBOSE=1 DESTDIR=$(CURDIR)/debian/tmp DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) +SAML2_CONFIG=--enable-saml2 + include /etc/GNUstep/GNUstep.conf include /usr/share/GNUstep/Makefiles/common.make config.make: configure dh_testdir - ./configure --prefix=$(GNUSTEP_SYSTEM_ROOT) + ./configure --prefix=$(GNUSTEP_SYSTEM_ROOT) $(SAML2_CONFIG) #Architecture build: build-arch diff --git a/packaging/debian/control b/packaging/debian/control index e60b86803..ceee29543 100644 --- a/packaging/debian/control +++ b/packaging/debian/control @@ -1,14 +1,14 @@ Source: sogo Priority: optional Maintainer: Inverse Support -Build-Depends: debhelper (>= 7.0.15), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libwbxml2-dev (>= 0.11.2) +Build-Depends: debhelper (>= 7.0.15), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libwbxml2-dev (>= 0.11.2), liblasso3-dev (>= 2.3.5) Section: web Standards-Version: 3.9.1 Package: sogo Section: web Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, tmpreaper, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3, zip +Depends: ${shlibs:Depends}, ${misc:Depends}, tmpreaper, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3, zip, liblasso3 (>= 2.3.5) Recommends: memcached Suggests: nginx Description: a modern and scalable groupware diff --git a/packaging/debian/control-squeeze b/packaging/debian/control-squeeze index 44329f87e..9e8ab3a82 100644 --- a/packaging/debian/control-squeeze +++ b/packaging/debian/control-squeeze @@ -1,14 +1,14 @@ Source: sogo Priority: optional Maintainer: Inverse Support -Build-Depends: debhelper (>= 7.0.15), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libmapi-dev, libmapistore-dev, libmapiproxy-dev, libwbxml2-dev +Build-Depends: debhelper (>= 7.0.15), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libmapi-dev, libmapistore-dev, libmapiproxy-dev, libwbxml2-dev, liblasso3-dev (>= 2.3.5) Section: web Standards-Version: 3.9.1 Package: sogo Section: web Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, tmpreaper, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3 +Depends: ${shlibs:Depends}, ${misc:Depends}, tmpreaper, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3, liblasso3 (>= 2.3.5) Recommends: memcached Suggests: nginx Description: a modern and scalable groupware diff --git a/packaging/debian/rules b/packaging/debian/rules index bd557291b..d09f6cc6d 100755 --- a/packaging/debian/rules +++ b/packaging/debian/rules @@ -6,9 +6,8 @@ export DH_VERBOSE=1 DESTDIR=$(CURDIR)/debian/tmp DIST_CODENAME=$(shell lsb_release -cs) -# NOTYET #ifeq ($(DIST_CODENAME), squeeze) -# SAML2_CONFIG=--enable-saml2 +SAML2_CONFIG=--enable-saml2 #endif include /etc/GNUstep/GNUstep.conf From 47094b6d91c6375bb48160b355c643a628c139be Mon Sep 17 00:00:00 2001 From: extrafu Date: Tue, 2 Dec 2014 19:33:54 -0500 Subject: [PATCH 12/24] Update SOGoSAML2Metadata.xml Fixed XML template generation. --- SoObjects/SOGo/SOGoSAML2Metadata.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SoObjects/SOGo/SOGoSAML2Metadata.xml b/SoObjects/SOGo/SOGoSAML2Metadata.xml index 9e4980b47..478f786f5 100644 --- a/SoObjects/SOGo/SOGoSAML2Metadata.xml +++ b/SoObjects/SOGo/SOGoSAML2Metadata.xml @@ -6,7 +6,7 @@ AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> - %{certificate}%{certificate} + %{certificate}%{certificate} From fe9ad9c6e9b43fa276f6e71972ffd9823e4df9de Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 4 Dec 2014 11:27:10 -0500 Subject: [PATCH 13/24] Radically reduced EAS memory usage --- ActiveSync/NSData+ActiveSync.m | 13 +++---- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 44 ++++++++++++++++------ ActiveSync/SOGoActiveSyncDispatcher.m | 13 +++++-- ActiveSync/SOGoMailObject+ActiveSync.m | 10 +---- NEWS | 1 + SoObjects/SOGo/SOGoCacheGCSObject.m | 20 ++++++++++ 6 files changed, 69 insertions(+), 32 deletions(-) diff --git a/ActiveSync/NSData+ActiveSync.m b/ActiveSync/NSData+ActiveSync.m index abd91995a..34fa4da95 100644 --- a/ActiveSync/NSData+ActiveSync.m +++ b/ActiveSync/NSData+ActiveSync.m @@ -86,15 +86,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return nil; } - data = [[NSData alloc] initWithBytes: xml length: xml_len]; + data = [NSData dataWithBytesNoCopy: xml length: xml_len freeWhenDone: YES]; #if WBXMLDEBUG [data writeToFile: @"/tmp/protocol.decoded" atomically: YES]; #endif - free(xml); - - return AUTORELEASE(data); + return data; } @@ -138,15 +136,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return nil; } - data = [[NSData alloc] initWithBytes: wbxml length: wbxml_len]; + data = [NSData dataWithBytesNoCopy: wbxml length: wbxml_len freeWhenDone: YES]; #if WBXMLDEBUG [data writeToFile: @"/tmp/protocol.encoded" atomically: YES]; #endif - free(wbxml); wbxml_conv_xml2wbxml_destroy(conv); - - return AUTORELEASE(data); + + return data; } @end diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 5690fea08..bb2f67d4e 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import "SOGoActiveSyncDispatcher+Sync.h" #import +#import #import #import #import @@ -114,22 +115,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. forKey: (NSString *) theFolderKey { SOGoCacheGCSObject *o; + NSDictionary *values; NSString *key; key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey]; - + values = [theFolderMetadata copy]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; [o setObjectType: ActiveSyncFolderCacheObject]; [o setTableUrl: [self folderTableURL]]; - [o reloadIfNeeded]; + //[o reloadIfNeeded]; [[o properties] removeObjectForKey: @"SyncKey"]; [[o properties] removeObjectForKey: @"SyncCache"]; [[o properties] removeObjectForKey: @"DateCache"]; [[o properties] removeObjectForKey: @"MoreAvailable"]; - [[o properties] addEntriesFromDictionary: theFolderMetadata]; + [[o properties] addEntriesFromDictionary: values]; [o save]; + [values release]; } - (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey @@ -534,6 +538,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { NSMutableDictionary *folderMetadata, *dateCache, *syncCache; + NSAutoreleasePool *pool; NSMutableString *s; BOOL more_available; @@ -635,6 +640,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType]; allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending:YES], nil]]; + // Check for the WindowSize max = [allComponents count]; @@ -643,14 +649,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. for (i = 0; i < max; i++) { + pool = [[NSAutoreleasePool alloc] init]; + // Check for the WindowSize and slice accordingly if (return_count >= theWindowSize) { more_available = YES; // -1 to make sure that we miss no event in case there are more with the same c_lastmodified - *theLastServerKey = [NSString stringWithFormat: @"%d", [[component objectForKey: @"c_lastmodified"] intValue] - 1]; + *theLastServerKey = [[NSString alloc] initWithFormat: @"%d", [[component objectForKey: @"c_lastmodified"] intValue] - 1]; + DESTROY(pool); break; } @@ -747,6 +756,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendString: @""]; return_count++; + + DESTROY(pool); } } // for ... @@ -763,6 +774,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [self _setFolderMetadata: folderMetadata forKey: [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]]]; + + RELEASE(*theLastServerKey); } break; case ActiveSyncMailFolder: @@ -830,6 +843,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. for (; k < [allCacheObjects count]; k++) { + pool = [[NSAutoreleasePool alloc] init]; + // Check for the WindowSize and slice accordingly if (return_count >= theWindowSize) { @@ -837,8 +852,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. more_available = YES; lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]); - *theLastServerKey = [NSString stringWithFormat: @"%@-%@", [aCacheObject uid], lastSequence]; + *theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence]; //NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey); + DESTROY(pool); break; } @@ -919,7 +935,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } } - } + DESTROY(pool); + } // for (; k < ...) if (more_available) { @@ -933,6 +950,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; + RELEASE(*theLastServerKey); + } // default: break; } // switch (folderType) ... @@ -1119,11 +1138,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [self processSyncGetChanges: theDocumentElement inCollection: collection withWindowSize: windowSize - //withWindowSize: 5 withSyncKey: syncKey withFolderType: folderType withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]] - //withFilterType: [NSCalendarDate dateFromFilterType: @"7"] inBuffer: changeBuffer lastServerKey: &lastServerKey]; } @@ -1310,7 +1327,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. BOOL changeDetected; // We initialize our output buffer - output = [NSMutableString string]; + output = [[NSMutableString alloc] init]; [output appendString: @""]; [output appendString: @""]; @@ -1366,8 +1383,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [output appendString: s]; [output appendString: @""]; - - d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + // Avoid overloading the autorelease pool here, as Sync command can + // generate fairly large responses. + d = [output dataUsingEncoding: NSUTF8StringEncoding]; + RELEASE(output); + + d = [d xml2wbxml]; [theResponse setContent: d]; } diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index cc57836c8..5d1fd7da9 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SOGoActiveSyncDispatcher.h" #import +#import #import #import #import @@ -2329,21 +2330,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. context: (id) theContext { id documentElement; + NSAutoreleasePool *pool; id builder, dom; SEL aSelector; NSString *cmdName, *deviceId; NSData *d; + pool = [[NSAutoreleasePool alloc] init]; + ASSIGN(context, theContext); - + // Get the device ID, device type and "stash" them deviceId = [[theRequest uri] deviceId]; [context setObject: deviceId forKey: @"DeviceId"]; [context setObject: [[theRequest uri] deviceType] forKey: @"DeviceType"]; [context setObject: [[theRequest uri] attachmentName] forKey: @"AttachmentName"]; - cmdName = [[theRequest uri] command]; // We make sure our cache table exists @@ -2379,6 +2382,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { d = [[theRequest content] wbxml2xml]; } + documentElement = nil; if (!d) @@ -2414,8 +2418,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [theResponse setHeader: @"Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,ResolveRecipients,ValidateCert" forKey: @"MS-ASProtocolCommands"]; [theResponse setHeader: @"2.0,2.1,2.5,12.0,12.1,14.0,14.1" forKey: @"MS-ASProtocolVersions"]; - RELEASE(context); - + RELEASE(context); + RELEASE(pool); + return nil; } diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m index e111b9edb..85a1bb61e 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.m +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -30,7 +30,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SOGoMailObject+ActiveSync.h" #import -#import #import #import #import @@ -511,7 +510,6 @@ struct GlobalObjectId { // - (NSString *) activeSyncRepresentationInContext: (WOContext *) _context { - NSAutoreleasePool *pool; NSData *d, *globalObjId; NSArray *attachmentKeys; NSMutableString *s; @@ -704,10 +702,6 @@ struct GlobalObjectId { // Body - namespace 17 preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue]; - // Make use of a local pool here as _preferredBodyDataUsingType:nativeType: will consume - // a significant amout of RAM and file descriptors - pool = [[NSAutoreleasePool alloc] init]; - nativeBodyType = 1; d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType]; @@ -747,9 +741,7 @@ struct GlobalObjectId { } [s appendString: @""]; } - - DESTROY(pool); - + // Attachments -namespace 16 attachmentKeys = [self fetchFileAttachmentKeys]; if ([attachmentKeys count]) diff --git a/NEWS b/NEWS index 6c4e3e58f..d23120a7c 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ Enhancements - Improved the SAML2 documentation + - Radically reduced AES memory usage Bug fixes - Now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) (#2381) diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.m b/SoObjects/SOGo/SOGoCacheGCSObject.m index 1f83f8ed2..a2251173a 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.m +++ b/SoObjects/SOGo/SOGoCacheGCSObject.m @@ -36,6 +36,7 @@ #import #import #import +#import #import #import #import @@ -96,10 +97,29 @@ static EOAttribute *textColumn = nil; - (void) dealloc { + //NSLog(@"SOGoCacheGCSObject: -dealloc for name: %@", nameInContainer); [tableUrl release]; [super dealloc]; } ++ (id) objectWithName: (NSString *) key inContainer: (id) theContainer +{ + SOGoCache *cache; + id o; + + cache = [SOGoCache sharedCache]; + o = [cache objectNamed: key inContainer: theContainer]; + + if (!o) + { + o = [super objectWithName: key inContainer: theContainer]; + //NSLog(@"Caching object with key: %@", key); + [cache registerObject: o withName: key inContainer: theContainer]; + } + + return o; +} + - (void) setTableUrl: (NSURL *) newTableUrl { ASSIGN (tableUrl, newTableUrl); From 9ef4d1f551bcfe597395bc7d9ec7bef2ef3e9a0f Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 4 Dec 2014 12:21:23 -0500 Subject: [PATCH 14/24] Fix for bug #3010 --- NEWS | 3 ++- UI/MainUI/SOGoUserHomePage.m | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index d23120a7c..69ae92e56 100644 --- a/NEWS +++ b/NEWS @@ -10,7 +10,8 @@ Bug fixes - Added support for IdP-initiated SAML2 logout (#2377) - We now generate SAML2 metadata on the fly (#2378) - We now handle correctly the SOGo logout when using SAML (#2376 and #2379) - + - Fixed freebusy lookups going off bounds for resources (#3010) + 2.2.10 (2014-11-21) ------------------- diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index b3527a085..1345e214c 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -229,7 +229,7 @@ // Build a bit string representation of the freebusy data for the period freeBusyItems = NSZoneCalloc (NULL, intervals, sizeof (int)); [self _fillFreeBusyItems: (freeBusyItems+4) - count: intervals + count: (intervals-4) withRecords: [fb fetchFreeBusyInfosFrom: start to: end forContact: uid] fromStartDate: startDate toEndDate: endDate]; From 3f3673cf5ad2691c04f975dc58a58173e7c6c2ee Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 4 Dec 2014 17:59:17 -0500 Subject: [PATCH 15/24] Added SOGoSAML2LogoutURL --- Documentation/SOGoInstallationGuide.asciidoc | 5 +++++ SoObjects/SOGo/SOGoSystemDefaults.h | 1 + SoObjects/SOGo/SOGoSystemDefaults.m | 5 +++++ UI/MainUI/SOGoSAML2Actions.m | 9 +++++++++ 4 files changed, 20 insertions(+) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index b28e512a4..7b332cf26 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -457,6 +457,11 @@ SAML2 as authentication mechanism. When using this feature, SOGo will invoke the IdP to proceed with the logout procedure. When the user clicks on the logout button, a redirection will be made to the IdP to trigger the logout. +|S |SOGoSAML2LogoutURL +|The URL to which redirect the user after the "Logout" link is clicked. +SOGoSAML2LogoutEnabled must be set to YES. If unset, the user will be +redirected to a blank page. + |D |SOGoTimeZone |Parameter used to set a default time zone for users. The default timezone is set to UTC. The Olson database is a standard database that diff --git a/SoObjects/SOGo/SOGoSystemDefaults.h b/SoObjects/SOGo/SOGoSystemDefaults.h index 4d68d7538..de5a140fa 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.h +++ b/SoObjects/SOGo/SOGoSystemDefaults.h @@ -82,6 +82,7 @@ - (NSString *) SAML2IdpCertificateLocation; - (NSString *) SAML2LoginAttribute; - (BOOL) SAML2LogoutEnabled; +- (NSString *) SAML2LogoutURL; - (BOOL) enablePublicAccess; diff --git a/SoObjects/SOGo/SOGoSystemDefaults.m b/SoObjects/SOGo/SOGoSystemDefaults.m index d48ab7c03..f9aa5fcc2 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.m +++ b/SoObjects/SOGo/SOGoSystemDefaults.m @@ -511,6 +511,11 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict, return [self boolForKey: @"SOGoSAML2LogoutEnabled"]; } +- (NSString *) SAML2LogoutURL +{ + return [self stringForKey: @"SOGoSAML2LogoutURL"]; +} + - (NSString *) SAML2LoginAttribute { return [self stringForKey: @"SOGoSAML2LoginAttribute"]; diff --git a/UI/MainUI/SOGoSAML2Actions.m b/UI/MainUI/SOGoSAML2Actions.m index 4a92221bf..09bc3d060 100644 --- a/UI/MainUI/SOGoSAML2Actions.m +++ b/UI/MainUI/SOGoSAML2Actions.m @@ -73,6 +73,7 @@ { NSString *userName, *value, *cookieName, *domain, *username, *password; SOGoWebAuthenticator *auth; + SOGoSystemDefaults *sd; WOResponse *response; NSCalendarDate *date; WOCookie *cookie; @@ -81,8 +82,16 @@ userName = [[context activeUser] login]; [self logWithFormat: @"SAML2 IdP-initiated SLO for user '%@'", userName]; + sd = [SOGoSystemDefaults sharedSystemDefaults]; + response = [context response]; + if ([sd SAML2LogoutURL]) + { + [response setStatus: 302]; + [response setHeader: [sd SAML2LogoutURL] forKey: @"location"]; + } + if ([userName isEqualToString: @"anonymous"]) return response; From ae0cbfe6a61610acfe3b3325e6cc9ef58332f6b4 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 5 Dec 2014 13:52:10 -0500 Subject: [PATCH 16/24] Fix for bug #2982 --- ActiveSync/SOGoActiveSyncDispatcher.m | 16 ++++++++++++++-- NEWS | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 5d1fd7da9..2223ea379 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -1464,8 +1464,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (!dstMessageId) { - // FIXME: should we return 1 or 2 here? - [s appendFormat: @"%d", 2]; + // Our destination message ID doesn't exist OR even our source message ID doesn't. + // This can happen if you Move items from your EAS client and immediately closes it + // before the server had the time to receive or process the query. Then, if that message + // is moved away by an other client behing the EAS' client back, it obvisouly won't find it. + // The issue the "result" will still be a success, but in fact, it's a failure. Cyrus generates + // this kind of query/response for an 'unkknown' message UID (696969) when trying to copy it + // over to the folder "Trash". + // + // 3 uid copy 696969 "Trash" + // 3 OK Completed + // + // See http://msdn.microsoft.com/en-us/library/gg651088(v=exchg.80).aspx for Status response codes. + // + [s appendFormat: @"%d", 1]; } else { diff --git a/NEWS b/NEWS index 69ae92e56..d0282ba99 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ Bug fixes - We now generate SAML2 metadata on the fly (#2378) - We now handle correctly the SOGo logout when using SAML (#2376 and #2379) - Fixed freebusy lookups going off bounds for resources (#3010) + - Fixed EAS clients moving mails between folders but disconnecting before receiving server's response (#2982) 2.2.10 (2014-11-21) ------------------- From 9e14a37cb81cf1dce955e5e16440fe413e4f656a Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 8 Dec 2014 10:25:37 -0500 Subject: [PATCH 17/24] Improvements over fixes for #2982 --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 1 + ActiveSync/SOGoActiveSyncDispatcher.m | 57 ++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index bb2f67d4e..d59102eb5 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -130,6 +130,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [[o properties] removeObjectForKey: @"SyncCache"]; [[o properties] removeObjectForKey: @"DateCache"]; [[o properties] removeObjectForKey: @"MoreAvailable"]; + [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; [[o properties] addEntriesFromDictionary: values]; [o save]; diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 2223ea379..2c05a1fae 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -130,6 +130,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @interface SOGoActiveSyncDispatcher (Sync) - (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey; +- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata forKey: (NSString *) theFolderKey; @end @@ -1398,7 +1399,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (void) processMoveItems: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - NSString *srcMessageId, *srcFolderId, *dstFolderId, *dstMessageId; + NSString *srcMessageId, *srcFolderId, *dstFolderId, *dstMessageId, *nameInCache, *currentFolder; + NSMutableDictionary *folderMetadata, *prevSuccessfulMoveItemsOps, *newSuccessfulMoveItemsOps; SOGoMicrosoftActiveSyncFolderType srcFolderType, dstFolderType; id aMoveOperation; NSArray *moveOperations; @@ -1407,6 +1409,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. NSData *d; int i; + currentFolder = nil; + moveOperations = (id)[theDocumentElement getElementsByTagName: @"Move"]; s = [NSMutableString string]; @@ -1422,6 +1426,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. srcMessageId = [[(id)[aMoveOperation getElementsByTagName: @"SrcMsgId"] lastObject] textValue]; srcFolderId = [[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] realCollectionIdWithFolderType: &srcFolderType]; dstFolderId = [[[(id)[aMoveOperation getElementsByTagName: @"DstFldId"] lastObject] textValue] realCollectionIdWithFolderType: &dstFolderType]; + + if (srcFolderType == ActiveSyncMailFolder) + nameInCache = [NSString stringWithFormat: @"folder%@", [[[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] stringByUnescapingURL] substringFromIndex: 5]]; + else + nameInCache = [[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] stringByUnescapingURL]; + + if (![nameInCache isEqualToString: currentFolder]) + { + folderMetadata = [self _folderMetadataForKey: nameInCache]; + prevSuccessfulMoveItemsOps = [folderMetadata objectForKey: @"SuccessfulMoveItemsOps"]; + newSuccessfulMoveItemsOps = [NSMutableDictionary dictionary] ; + currentFolder = nameInCache; + } [s appendString: @""]; @@ -1477,7 +1494,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // See http://msdn.microsoft.com/en-us/library/gg651088(v=exchg.80).aspx for Status response codes. // - [s appendFormat: @"%d", 1]; + if ([prevSuccessfulMoveItemsOps objectForKey: srcMessageId]) + { + // Previous move failed operation but we can recover the dstMessageId from previous request + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%@", [prevSuccessfulMoveItemsOps objectForKey: srcMessageId]]; + [s appendFormat: @"%d", 3]; + [newSuccessfulMoveItemsOps setObject: [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] forKey: srcMessageId]; + } + else + { + [s appendFormat: @"%d", 1]; + } } else { @@ -1506,6 +1534,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendFormat: @"%@", srcMessageId]; [s appendFormat: @"%@", dstMessageId]; [s appendFormat: @"%d", 3]; + + // Save dstMessageId in cache - it will help to recover if the request fails before the response can be sent to client + [newSuccessfulMoveItemsOps setObject: dstMessageId forKey: srcMessageId]; } } @@ -1549,11 +1580,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendFormat: @"%@", srcMessageId]; [s appendFormat: @"%@", newUID]; [s appendFormat: @"%d", 3]; + + // Save dstMessageId in cache - it will help to recover if the request fails before the response can be sent to client + [newSuccessfulMoveItemsOps setObject: newUID forKey: srcMessageId]; } else { - [s appendFormat: @"%@", srcMessageId]; - [s appendFormat: @"%d", 1]; + if ([prevSuccessfulMoveItemsOps objectForKey: srcMessageId]) + { + // Move failed but we can recover the dstMessageId from previous request + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%@", [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] ]; + [s appendFormat: @"%d", 3]; + [newSuccessfulMoveItemsOps setObject: [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] forKey: srcMessageId]; + } + else + { + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%d", 1]; + } } } else @@ -1570,6 +1615,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } [s appendString: @""]; + + [folderMetadata removeObjectForKey: @"SuccessfulMoveItemsOps"]; + [folderMetadata setObject: newSuccessfulMoveItemsOps forKey: @"SuccessfulMoveItemsOps"]; + [self _setFolderMetadata: folderMetadata forKey: nameInCache]; } [s appendString: @""]; From 2b95dd2c0a04b28839a721cb3ba40c674f3981b5 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 8 Dec 2014 10:29:23 -0500 Subject: [PATCH 18/24] Avoid generating GUID for "Other user"/"Shared" folders --- SoObjects/Mailer/SOGoMailAccount.m | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index ab6021f07..a7c492f20 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -664,7 +664,7 @@ static NSString *inboxFolderName = @"INBOX"; - (NSDictionary *) imapFolderGUIDs { - NSDictionary *result, *nresult, *folderData; + NSDictionary *result, *nresult, *namespaceDict; NSMutableDictionary *folders; NGImap4Client *client; SOGoUserDefaults *ud; @@ -684,16 +684,26 @@ static NSString *inboxFolderName = @"INBOX"; folders = [NSMutableDictionary dictionary]; client = [[self imap4Connection] client]; + namespaceDict = [client namespace]; + result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"]; e = [folderList objectEnumerator]; - while (object = [e nextObject]) + while ((object = [e nextObject])) { guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"]; if (!guid) { + // Don't generate a GUID for "Other users" and "Shared" namespace folders - user foldername instead + if ([[object substringFromIndex: 1] isEqualToString: [[[[namespaceDict objectForKey: @"other users"] lastObject] objectForKey: @"prefix"] substringFromIndex: 1]] || + [[object substringFromIndex: 1] isEqualToString: [[[[namespaceDict objectForKey: @"shared"] lastObject] objectForKey: @"prefix"] substringFromIndex: 1]]) + { + [folders setObject: [NSString stringWithFormat: @"folder%@", [object substringFromIndex: 1]] forKey: [NSString stringWithFormat: @"folder%@", [object substringFromIndex: 1]]]; + continue; + } + guid = [[NSProcessInfo processInfo] globallyUniqueString]; nresult = [client annotation: [object substringFromIndex: 1] entryName: @"/comment" attributeName: @"value.priv" attributeValue: guid]; From 12788c847d5ba3d609848bf52589b4c7f3907595 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 8 Dec 2014 10:45:34 -0500 Subject: [PATCH 19/24] New sogo-tool feature to manage EAS data --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 2 +- ActiveSync/SOGoActiveSyncDispatcher.m | 37 ++- NEWS | 3 + Tools/GNUmakefile | 3 +- Tools/SOGoToolManageEAS.m | 282 +++++++++++++++++++++ 5 files changed, 321 insertions(+), 6 deletions(-) create mode 100644 Tools/SOGoToolManageEAS.m diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index d59102eb5..db70bde32 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -1118,7 +1118,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *changeDetected = YES; if (!([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"displayName"])) - status = 13; // need folderSync + status = 12; // need folderSync else status = 3; // do a complete resync } diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 2c05a1fae..8becc4adb 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -908,6 +908,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o reloadIfNeeded]; [[o properties ] setObject: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] forKey: @"displayName"]; + + // clean cache content to avoid stale data + [[o properties] removeObjectForKey: @"SyncKey"]; + [[o properties] removeObjectForKey: @"SyncCache"]; + [[o properties] removeObjectForKey: @"DateCache"]; + [[o properties] removeObjectForKey: @"MoreAvailable"]; + [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; [o save]; command_count++; @@ -951,11 +958,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Decide between add and change if (![[o properties ] objectForKey: @"displayName"] || first_sync) - operation = @"Add"; + operation = @"Add"; else if (![[[o properties ] objectForKey: @"displayName"] isEqualToString: [[folders objectAtIndex:fi] displayName]]) - operation = @"Update"; - else - operation = nil; + operation = @"Update"; + else + operation = nil; if (operation) { @@ -983,6 +990,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; [[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"]; + + if ([operation isEqualToString: @"Add"]) + { + // clean cache content to avoid stale data + [[o properties] removeObjectForKey: @"SyncKey"]; + [[o properties] removeObjectForKey: @"SyncCache"]; + [[o properties] removeObjectForKey: @"DateCache"]; + [[o properties] removeObjectForKey: @"MoreAvailable"]; + [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; + } + [o save]; } else @@ -994,6 +1012,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. command_count++; [[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"]; + + if ([operation isEqualToString: @"Add"]) + { + // clean cache content to avoid stale data + [[o properties] removeObjectForKey: @"SyncKey"]; + [[o properties] removeObjectForKey: @"SyncCache"]; + [[o properties] removeObjectForKey: @"DateCache"]; + [[o properties] removeObjectForKey: @"MoreAvailable"]; + [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; + } + [o save]; } } diff --git a/NEWS b/NEWS index d0282ba99..a62f77dba 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ 2.2.11 (2014-xx-xx) ------------------- +New features + - sogo-tool can now be used to manage EAS metadata for all devices + Enhancements - Improved the SAML2 documentation - Radically reduced AES memory usage diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index bb580edc9..5b1eed2e9 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -22,7 +22,8 @@ $(SOGO_TOOL)_OBJC_FILES += \ SOGoToolRemoveDoubles.m \ SOGoToolRenameUser.m \ SOGoToolRestore.m \ - SOGoToolUserPreferences.m + SOGoToolUserPreferences.m \ + SOGoToolManageEAS.m TOOL_NAME += $(SOGO_TOOL) ### diff --git a/Tools/SOGoToolManageEAS.m b/Tools/SOGoToolManageEAS.m new file mode 100644 index 000000000..e001c67f8 --- /dev/null +++ b/Tools/SOGoToolManageEAS.m @@ -0,0 +1,282 @@ +/* SOGoToolManageEAS.m - this file is part of SOGo + * + * Copyright (C) 2014 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import + +#import +#import + +#import +#import +#import "SOGo/SOGoCredentialsFile.h" +#import +#import +#import +#import +#import +#import +#import + +#import + + +#import "SOGoTool.h" + +typedef enum +{ + ManageEASUnknown = -1, + ManageEASListDevices = 0, + ManageEASListFolders = 2, + ManageEASResetDevice = 3, + ManageEASRestFolder = 4, +} SOGoManageEASCommand; + +@interface SOGoToolManageEAS : SOGoTool +@end + +@implementation SOGoToolManageEAS + ++ (void) initialize +{ +} + ++ (NSString *) command +{ + return @"manage-eas"; +} + ++ (NSString *) description +{ + return @"manage EAS folders"; +} + +- (void) usage +{ + fprintf (stderr, "manage-eas listdevices|resetdevice|resetfolder user \n\n" + " user the user of whom to reset the whole device or a single folder\n" + " Examples:\n" + " sogo-tool manage-eas listdevices janedoe\n" + " sogo-tool manage-eas listfolders janedoe androidc316986417\n" + " sogo-tool manage-eas resetdevice janedoe androidc316986417\n" + " sogo-tool manage-eas resetfolder janedow androidc316986417+folderlala-dada-sasa_7a13_1a2386e0_e\n") ; +} + + +- (SOGoManageEASCommand) _cmdFromString: (NSString *) theString +{ + if ([theString length] > 2) + { + if ([theString caseInsensitiveCompare: @"listdevices"] == NSOrderedSame) + return ManageEASListDevices; + else if ([theString caseInsensitiveCompare: @"listfolders"] == NSOrderedSame) + return ManageEASListFolders; + else if ([theString caseInsensitiveCompare: @"resetdevice"] == NSOrderedSame) + return ManageEASResetDevice; + else if ([theString caseInsensitiveCompare: @"resetfolder"] == NSOrderedSame) + return ManageEASRestFolder; + } + + return ManageEASUnknown; +} + +- (BOOL) run +{ + NSString *userId; + SOGoManageEASCommand cmd; + SOGoCacheGCSObject *oc, *foc; + NSString *urlString, *ocFSTableName, *deviceId; + NSURL *folderTableURL; + NSMutableArray *parts; + + BOOL rc; + int max; + + max = [sanitizedArguments count]; + rc = NO; + + if (max > 1) + { + SOGoUser *user; + + cmd = [self _cmdFromString: [sanitizedArguments objectAtIndex: 0]]; + + userId = [sanitizedArguments objectAtIndex: 1]; + + user = [SOGoUser userWithLogin: userId]; + + if (![user loginInDomain]) + return NO; + + urlString = [[user domainDefaults] folderInfoURL]; + parts = [[urlString componentsSeparatedByString: @"/"] + mutableCopy]; + [parts autorelease]; + if ([parts count] == 5) + { + /* If "OCSFolderInfoURL" is properly configured, we must have 5 + parts in this url. We strip the '-' character in case we have + this in the domain part - like foo@bar-zot.com */ + ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@", + [[[user loginInDomain] asCSSIdentifier] + stringByReplacingOccurrencesOfString: @"-" + withString: @"_"]]; + [parts replaceObjectAtIndex: 4 withObject: ocFSTableName]; + folderTableURL + = [NSURL URLWithString: [parts componentsJoinedByString: @"/"]]; + [folderTableURL retain]; + } + + switch (cmd) + { + case ManageEASListDevices: + oc = [SOGoCacheGCSObject objectWithName: @"0" inContainer: nil]; + [oc setObjectType: ActiveSyncGlobalCacheObject]; + + [oc setTableUrl: folderTableURL]; + + for (id cacheEntry in [oc cacheEntriesForDeviceId: nil newerThanVersion: -1]) + fprintf(stdout,"%s\n", [[cacheEntry substringFromIndex: 1] UTF8String]); + + rc = YES; + break; + + case ManageEASListFolders: + if (max > 2) + { + /* value specified on command line */ + deviceId = [sanitizedArguments objectAtIndex: 2]; + + oc = [SOGoCacheGCSObject objectWithName: @"0" inContainer: nil]; + [oc setObjectType: ActiveSyncFolderCacheObject]; + + [oc setTableUrl: folderTableURL]; + + for (id cacheEntry in [oc cacheEntriesForDeviceId: deviceId newerThanVersion: -1]) { + fprintf(stdout,"Folder Key: %s\n", [[cacheEntry substringFromIndex: 1] UTF8String]); + + foc = [SOGoCacheGCSObject objectWithName: [cacheEntry substringFromIndex: 1] inContainer: nil]; + [foc setObjectType: ActiveSyncFolderCacheObject]; + [foc setTableUrl: folderTableURL]; + + [foc reloadIfNeeded]; + + fprintf(stdout, " Folder Name: %s\n\n", [[[foc properties] objectForKey: @"displayName"] UTF8String]); + + if (verbose) + fprintf(stdout, " metadata Name: %s\n\n", [[[foc properties] description] UTF8String]); + + } + + rc = YES; + } + else + { + fprintf(stderr, "\nERROR: deviceId not specified\n\n"); + } + + break; + + + case ManageEASResetDevice: + if (max > 2) + { + /* value specified on command line */ + deviceId = [sanitizedArguments objectAtIndex: 2]; + oc = [SOGoCacheGCSObject objectWithName: deviceId inContainer: nil]; + [oc setObjectType: ActiveSyncGlobalCacheObject]; + [oc setTableUrl: folderTableURL]; + + [oc reloadIfNeeded]; + if ([oc isNew]) { + fprintf(stderr, "ERROR: Device with ID '%s' not found\n", [deviceId UTF8String]); + return rc; + } + + NSMutableString *sql; + + sql = [NSMutableString stringWithFormat: @"DELETE FROM %@" @" WHERE c_path like '/%@'", [oc tableName], deviceId]; + + [oc performBatchSQLQueries: [NSArray arrayWithObject: sql]]; + rc = YES; + } + else + { + fprintf(stderr, "\nERROR: deviceId not specified\n\n"); + } + + break; + + case ManageEASRestFolder: + if (max > 2) + { + /* value specified on command line */ + deviceId = [sanitizedArguments objectAtIndex: 2]; + + //if ([deviceId rangeOfString: @"+"].location == NSNotFound) { + // fprintf(stderr, "ERROR: Deviceid invalid folder \"%@\" not found\n", deviceId); + // return rc; + //} + + oc = [SOGoCacheGCSObject objectWithName: deviceId inContainer: nil]; + [oc setObjectType: ActiveSyncFolderCacheObject]; + [oc setTableUrl: folderTableURL]; + + [oc reloadIfNeeded]; + + if ([oc isNew]) { + fprintf(stderr, "ERROR: Folder with ID \"%s\" not found\n", [deviceId UTF8String]); + return rc; + } + [[oc properties] removeObjectForKey: @"SyncKey"]; + [[oc properties] removeObjectForKey: @"SyncCache"]; + [[oc properties] removeObjectForKey: @"DateCache"]; + [[oc properties] removeObjectForKey: @"MoreAvailable"]; + + [oc save]; + rc = YES; + + } + else + { + fprintf(stderr, "\nERROR: folderId not specified\n\n"); + } + + break; + + case ManageEASUnknown: + break; + } + } + + if (!rc) + { + [self usage]; + } + + return rc; +} + +@end From 4e6214d6fcaa278f4039765de2b74fd862ced228 Mon Sep 17 00:00:00 2001 From: Jens Erat Date: Tue, 9 Dec 2014 13:11:03 +0100 Subject: [PATCH 20/24] Fixed ActiveSync URL in documentation --- Documentation/SOGoInstallationGuide.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 7b332cf26..e33177cda 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -2556,7 +2556,7 @@ any mobile devices that support Microsoft ActiveSync. Microsoft Outlook 2013 is also supported. The Microsoft ActiveSync server URL is generally something -like: `http://localhost/Microsoft-Active-Sync`. +like: `http://localhost/Microsoft-Server-ActiveSync`. Upgrading --------- From 64637d842b9b4979324e98d057909d57c86eb9ec Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 9 Dec 2014 07:21:34 -0500 Subject: [PATCH 21/24] Prevent compilation failures with old gcc versions --- Tools/SOGoToolManageEAS.m | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/Tools/SOGoToolManageEAS.m b/Tools/SOGoToolManageEAS.m index e001c67f8..6003adef3 100644 --- a/Tools/SOGoToolManageEAS.m +++ b/Tools/SOGoToolManageEAS.m @@ -110,9 +110,11 @@ typedef enum NSString *urlString, *ocFSTableName, *deviceId; NSURL *folderTableURL; NSMutableArray *parts; - + NSArray *entries; + id cacheEntry; + BOOL rc; - int max; + int i, max; max = [sanitizedArguments count]; rc = NO; @@ -156,9 +158,13 @@ typedef enum [oc setObjectType: ActiveSyncGlobalCacheObject]; [oc setTableUrl: folderTableURL]; + entries = [oc cacheEntriesForDeviceId: nil newerThanVersion: -1]; - for (id cacheEntry in [oc cacheEntriesForDeviceId: nil newerThanVersion: -1]) - fprintf(stdout,"%s\n", [[cacheEntry substringFromIndex: 1] UTF8String]); + for (i = 0; i < [entries count]; i++) + { + cacheEntry = [entries objectAtIndex: i]; + fprintf(stdout,"%s\n", [[cacheEntry substringFromIndex: 1] UTF8String]); + } rc = YES; break; @@ -173,21 +179,23 @@ typedef enum [oc setObjectType: ActiveSyncFolderCacheObject]; [oc setTableUrl: folderTableURL]; + entries = [oc cacheEntriesForDeviceId: deviceId newerThanVersion: -1]; - for (id cacheEntry in [oc cacheEntriesForDeviceId: deviceId newerThanVersion: -1]) { - fprintf(stdout,"Folder Key: %s\n", [[cacheEntry substringFromIndex: 1] UTF8String]); + for (i = 0; i < [entries count]; i++) + { + cacheEntry = [entries objectAtIndex: i]; + fprintf(stdout,"Folder Key: %s\n", [[cacheEntry substringFromIndex: 1] UTF8String]); - foc = [SOGoCacheGCSObject objectWithName: [cacheEntry substringFromIndex: 1] inContainer: nil]; - [foc setObjectType: ActiveSyncFolderCacheObject]; - [foc setTableUrl: folderTableURL]; + foc = [SOGoCacheGCSObject objectWithName: [cacheEntry substringFromIndex: 1] inContainer: nil]; + [foc setObjectType: ActiveSyncFolderCacheObject]; + [foc setTableUrl: folderTableURL]; - [foc reloadIfNeeded]; + [foc reloadIfNeeded]; - fprintf(stdout, " Folder Name: %s\n\n", [[[foc properties] objectForKey: @"displayName"] UTF8String]); - - if (verbose) - fprintf(stdout, " metadata Name: %s\n\n", [[[foc properties] description] UTF8String]); + fprintf(stdout, " Folder Name: %s\n\n", [[[foc properties] objectForKey: @"displayName"] UTF8String]); + if (verbose) + fprintf(stdout, " metadata Name: %s\n\n", [[[foc properties] description] UTF8String]); } rc = YES; From 39e8f055d702e0d33da6af130403e1fd0287c102 Mon Sep 17 00:00:00 2001 From: Jens Erat Date: Tue, 9 Dec 2014 13:23:21 +0100 Subject: [PATCH 22/24] Added information on common configuration issues A very common issue (watching the mailing list) is not wrapping the whole configuration in a dictionary. SOGo is not very helpful at debugging broken configuration files, thus hinting `plparse` already installed with the GNUstep runtime. --- Documentation/SOGoInstallationGuide.asciidoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index e33177cda..45c4fc943 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -273,6 +273,11 @@ is not required, only recommended. Block comments are delimited by `/*` and `*/` and can span multiple lines while line comments must start with `//`. +The configuration must be contained in a root dictionary, thus be completely +wrapped within curly brackets `{ [configuration] }`. If SOGo refuses to +start due to syntax errors in its configuration file, `plparse` is helpful +for finding these, as it indicates the line containing the problem. + Preferences Hierarchy ~~~~~~~~~~~~~~~~~~~~~ From aac6b22ab56f594e2c796d015e949549bf22e517 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 9 Dec 2014 09:08:54 -0500 Subject: [PATCH 23/24] Preparation for the release --- Documentation/docinfo.xml | 6 +++--- Documentation/includes/global-attributes.asciidoc | 2 +- NEWS | 2 +- Version | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/docinfo.xml b/Documentation/docinfo.xml index 76b82caf2..f3d35a35c 100644 --- a/Documentation/docinfo.xml +++ b/Documentation/docinfo.xml @@ -1,7 +1,7 @@ -Version 2.2.10 - November 2014 -for version 2.2.10 -2014-11-21 +Version 2.2.11 - December 2014 +for version 2.2.11 +2014-12-09 Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". diff --git a/Documentation/includes/global-attributes.asciidoc b/Documentation/includes/global-attributes.asciidoc index 62d2f66df..96aebb5da 100644 --- a/Documentation/includes/global-attributes.asciidoc +++ b/Documentation/includes/global-attributes.asciidoc @@ -13,6 +13,6 @@ // TODO have the build system take care of this -:release_version: 2.2.10 +:release_version: 2.2.11 // vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab: diff --git a/NEWS b/NEWS index a62f77dba..21a42d621 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -2.2.11 (2014-xx-xx) +2.2.11 (2014-12-09) ------------------- New features diff --git a/Version b/Version index f5f76e90f..7df880e8e 100644 --- a/Version +++ b/Version @@ -4,4 +4,4 @@ MAJOR_VERSION=2 MINOR_VERSION=2 -SUBMINOR_VERSION=10 +SUBMINOR_VERSION=11 From 1c0e64f169439f0284a1427043c88d5fcc525455 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 9 Dec 2014 09:09:18 -0500 Subject: [PATCH 24/24] Update ChangeLog --- ChangeLog | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) diff --git a/ChangeLog b/ChangeLog index 18af70a99..8d06dd75f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,250 @@ +commit aac6b22ab56f594e2c796d015e949549bf22e517 +Author: Ludovic Marcotte +Date: Tue Dec 9 09:08:54 2014 -0500 + + Preparation for the release + +M Documentation/docinfo.xml +M Documentation/includes/global-attributes.asciidoc +M NEWS +M Version + +commit 39e8f055d702e0d33da6af130403e1fd0287c102 +Author: Jens Erat +Date: Tue Dec 9 13:23:21 2014 +0100 + + Added information on common configuration issues + + A very common issue (watching the mailing list) is not wrapping the + whole configuration in a dictionary. SOGo is not very helpful at + debugging broken configuration files, thus hinting `plparse` already + installed with the GNUstep runtime. + +M Documentation/SOGoInstallationGuide.asciidoc + +commit 64637d842b9b4979324e98d057909d57c86eb9ec +Author: Ludovic Marcotte +Date: Tue Dec 9 07:21:34 2014 -0500 + + Prevent compilation failures with old gcc versions + +M Tools/SOGoToolManageEAS.m + +commit 4e6214d6fcaa278f4039765de2b74fd862ced228 +Author: Jens Erat +Date: Tue Dec 9 13:11:03 2014 +0100 + + Fixed ActiveSync URL in documentation + +M Documentation/SOGoInstallationGuide.asciidoc + +commit 12788c847d5ba3d609848bf52589b4c7f3907595 +Author: Ludovic Marcotte +Date: Mon Dec 8 10:45:34 2014 -0500 + + New sogo-tool feature to manage EAS data + +M ActiveSync/SOGoActiveSyncDispatcher+Sync.m +M ActiveSync/SOGoActiveSyncDispatcher.m +M NEWS +M Tools/GNUmakefile +A Tools/SOGoToolManageEAS.m + +commit 2b95dd2c0a04b28839a721cb3ba40c674f3981b5 +Author: Ludovic Marcotte +Date: Mon Dec 8 10:29:23 2014 -0500 + + Avoid generating GUID for "Other user"/"Shared" folders + +M SoObjects/Mailer/SOGoMailAccount.m + +commit 9e14a37cb81cf1dce955e5e16440fe413e4f656a +Author: Ludovic Marcotte +Date: Mon Dec 8 10:25:37 2014 -0500 + + Improvements over fixes for #2982 + +M ActiveSync/SOGoActiveSyncDispatcher+Sync.m +M ActiveSync/SOGoActiveSyncDispatcher.m + +commit ae0cbfe6a61610acfe3b3325e6cc9ef58332f6b4 +Author: Ludovic Marcotte +Date: Fri Dec 5 13:52:10 2014 -0500 + + Fix for bug #2982 + +M ActiveSync/SOGoActiveSyncDispatcher.m +M NEWS + +commit 3f3673cf5ad2691c04f975dc58a58173e7c6c2ee +Author: Ludovic Marcotte +Date: Thu Dec 4 17:59:17 2014 -0500 + + Added SOGoSAML2LogoutURL + +M Documentation/SOGoInstallationGuide.asciidoc +M SoObjects/SOGo/SOGoSystemDefaults.h +M SoObjects/SOGo/SOGoSystemDefaults.m +M UI/MainUI/SOGoSAML2Actions.m + +commit 9ef4d1f551bcfe597395bc7d9ec7bef2ef3e9a0f +Author: Ludovic Marcotte +Date: Thu Dec 4 12:21:23 2014 -0500 + + Fix for bug #3010 + +M NEWS +M UI/MainUI/SOGoUserHomePage.m + +commit fe9ad9c6e9b43fa276f6e71972ffd9823e4df9de +Author: Ludovic Marcotte +Date: Thu Dec 4 11:27:10 2014 -0500 + + Radically reduced EAS memory usage + +M ActiveSync/NSData+ActiveSync.m +M ActiveSync/SOGoActiveSyncDispatcher+Sync.m +M ActiveSync/SOGoActiveSyncDispatcher.m +M ActiveSync/SOGoMailObject+ActiveSync.m +M NEWS +M SoObjects/SOGo/SOGoCacheGCSObject.m + +commit 47094b6d91c6375bb48160b355c643a628c139be +Author: extrafu +Date: Tue Dec 2 19:33:54 2014 -0500 + + Update SOGoSAML2Metadata.xml + + Fixed XML template generation. + +M SoObjects/SOGo/SOGoSAML2Metadata.xml + +commit 9ffa32eebdcc5d3b102aa4d86a93590684800cdc +Author: Ludovic Marcotte +Date: Sun Nov 30 17:35:39 2014 -0500 + + Enable SAML support on all Debian-based distro + +M packaging/debian-multiarch/control +M packaging/debian-multiarch/rules +M packaging/debian/control +M packaging/debian/control-squeeze +M packaging/debian/rules + +commit 1b715e0812dba3d9f2c4d3f2daa0cbd4313f8def +Author: Ludovic Marcotte +Date: Thu Nov 27 11:37:08 2014 -0500 + + We now handle correctly the SOGo logout when using SAML (#2376 and #2379) + +M Documentation/SOGoInstallationGuide.asciidoc +M NEWS +M SoObjects/SOGo/SOGoCASSession.h +M SoObjects/SOGo/SOGoCASSession.m +M SoObjects/SOGo/SOGoCache.h +M SoObjects/SOGo/SOGoCache.m +M SoObjects/SOGo/SOGoSAML2Session.h +M SoObjects/SOGo/SOGoSAML2Session.m +M SoObjects/SOGo/SOGoWebAuthenticator.m +M UI/MainUI/SOGoSAML2Actions.m +M UI/MainUI/SOGoUserHomePage.m + +commit c3715c94857efa77e1e38b814e0f0ee09cc9c678 +Author: Ludovic Marcotte +Date: Wed Nov 26 15:27:36 2014 -0500 + + Added additional bugfix for #2982 + +M ActiveSync/SOGoActiveSyncDispatcher+Sync.m + +commit be608dc76c7217c62152e869ab17c9b237a4e99a +Author: Ludovic Marcotte +Date: Wed Nov 26 15:09:30 2014 -0500 + + Bug fixes for #2378 and #2377 and documentation improvements + +M Documentation/SOGoInstallationGuide.asciidoc +M NEWS +A SoObjects/SOGo/SOGoSAML2Metadata.xml +M SoObjects/SOGo/SOGoSAML2Session.h +M SoObjects/SOGo/SOGoSAML2Session.m +M UI/MainUI/SOGoSAML2Actions.m +M UI/MainUI/product.plist + +commit 5a5464dc610cddb87b9c07e97699c24a767f5ed7 +Author: Ludovic Marcotte +Date: Wed Nov 26 13:24:04 2014 -0500 + + An other fix for #2930 + +M UI/MainUI/SOGoUserHomePage.m + +commit 89917c941caf9f7c4ca18e498bba06298f7f26f6 +Author: Ludovic Marcotte +Date: Wed Nov 26 13:01:50 2014 -0500 + + New entry for bug #2381 + +M NEWS + +commit 913a75f410d8e4409960e59c110f707ffad47371 +Author: Ludovic Marcotte +Date: Wed Nov 26 13:00:47 2014 -0500 + + Fix for bug # + +M SoObjects/SOGo/SOGoCache.m +M SoObjects/SOGo/SOGoSAML2Session.h +M SoObjects/SOGo/SOGoSAML2Session.m +M SoObjects/SOGo/SOGoSession.h +M SoObjects/SOGo/SOGoSession.m +M SoObjects/SOGo/SOGoSystemDefaults.h +M SoObjects/SOGo/SOGoSystemDefaults.m + +commit 20e728afac25c9930af97eb2d1fb7519d415c853 +Author: Ludovic Marcotte +Date: Tue Nov 25 17:28:12 2014 -0500 + + Remove unnecessary comments + +M SoObjects/SOGo/SOGoCache.m + +commit 5f14bc11011394ff6f1333880320eec7ceb675af +Author: Ludovic Marcotte +Date: Tue Nov 25 17:27:03 2014 -0500 + + Report the correct preference keys + +M SoObjects/SOGo/SOGoSAML2Session.m + +commit b0633ba1f454b9b09d646ff0b512f5d13a0f236e +Author: Robin McCorkell +Date: Tue Jun 18 17:50:28 2013 +0200 + + Add check for remote_user variable for trusted proxy auth + + If trusted proxy authentication is on, yet the proxy did not authenticate the + user, then the default authentication method is used instead of returning 'Unauthorized'. + +M Apache/SOGo.conf +M Main/SOGo.m + +commit bb227443ed632cb03cba58e24259510c9686e1d2 +Author: Ludovic Marcotte +Date: Sat Nov 22 08:14:31 2014 -0500 + + Check lenght of string before trying to use parameters + +M ActiveSync/NSString+ActiveSync.m + +commit efff32e44d1b6df1dfe03f81bbd0e1f449a8fbb2 +Author: Ludovic Marcotte +Date: Fri Nov 21 09:13:11 2014 -0500 + + Update ChangeLog + +M ChangeLog + commit f7f78eaba6ac5304df937f5b26f5d21979657787 Author: Francis Lachapelle Date: Fri Nov 21 09:07:39 2014 -0500