diff --git a/ActiveSync/NGVCard+ActiveSync.m b/ActiveSync/NGVCard+ActiveSync.m index 3f246c8d1f..eb62b9722c 100644 --- a/ActiveSync/NGVCard+ActiveSync.m +++ b/ActiveSync/NGVCard+ActiveSync.m @@ -33,6 +33,7 @@ #import #import #import +#import #import @@ -230,7 +231,7 @@ - (void) takeActiveSyncValues: (NSDictionary *) theValues if ((o = [theValues objectForKey: @"Birthday"])) { o = [o calendarDate]; - [self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d"]]; + [self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]]; } // diff --git a/ActiveSync/README b/ActiveSync/README index 0bdb380852..a261d48f3b 100644 --- a/ActiveSync/README +++ b/ActiveSync/README @@ -4,7 +4,7 @@ to negotiate the fees associated to your user base. To contact Microsoft, please visit: -http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx +http://www.microsoft.com/en-us/legal/intellectualproperty/ and send an email to iplicreq@microsoft.com diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 100c858070..6a6f44d898 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -733,7 +733,10 @@ - (void) processSyncGetChanges: (id ) theDocumentElement partstat = [attendee participationStatus]; if (partstat == iCalPersonPartStatNeedsAction) - continue; + { + DESTROY(pool); + continue; + } } } @@ -772,10 +775,10 @@ - (void) processSyncGetChanges: (id ) theDocumentElement [s appendString: @""]; return_count++; - - DESTROY(pool); } - } // for ... + + DESTROY(pool); + } // for (i = 0; i < max; i++) ... if (more_available) { @@ -806,7 +809,6 @@ - (void) processSyncGetChanges: (id ) theDocumentElement int j, k, return_count; BOOL found_in_cache; - allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType]; max = [allMessages count]; @@ -1017,7 +1019,7 @@ - (void) processSyncCommands: (id ) theDocumentElement if ([[element tagName] isEqualToString: @"Add"]) { // Add - [self processSyncAddCommand: aCommand + [self processSyncAddCommand: element inCollection: theCollection withType: theFolderType inBuffer: theBuffer]; @@ -1026,7 +1028,7 @@ - (void) processSyncCommands: (id ) theDocumentElement else if ([[element tagName] isEqualToString: @"Change"]) { // Change - [self processSyncChangeCommand: aCommand + [self processSyncChangeCommand: element inCollection: theCollection withType: theFolderType inBuffer: theBuffer]; @@ -1035,7 +1037,7 @@ - (void) processSyncCommands: (id ) theDocumentElement else if ([[element tagName] isEqualToString: @"Delete"]) { // Delete - [self processSyncDeleteCommand: aCommand + [self processSyncDeleteCommand: element inCollection: theCollection withType: theFolderType inBuffer: theBuffer]; @@ -1044,7 +1046,7 @@ - (void) processSyncCommands: (id ) theDocumentElement else if ([[element tagName] isEqualToString: @"Fetch"]) { // Fetch - [self processSyncFetchCommand: aCommand + [self processSyncFetchCommand: element inCollection: theCollection withType: theFolderType inBuffer: theBuffer]; diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 4ddd1427b5..393e192b81 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -35,6 +35,7 @@ #import #import #import +#import #import #import @@ -1215,71 +1216,136 @@ - (void) processItemOperations: (id ) theDocumentElement { NSString *fileReference, *realCollectionId; NSMutableString *s; + NSArray *fetchRequests; + id aFetch; + int i; SOGoMicrosoftActiveSyncFolderType folderType; - fileReference = [[[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL]; + s = [NSMutableString string]; - realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @"1"]; + [s appendString: @""]; + + fetchRequests = (id)[theDocumentElement getElementsByTagName: @"Fetch"]; - if (folderType == ActiveSyncMailFolder) + if ([fetchRequests count]) { - id currentFolder, currentCollection, currentBodyPart; - NSString *folderName, *messageName, *pathToPart; - SOGoMailAccounts *accountsFolder; - SOGoUserFolder *userFolder; - SOGoMailObject *mailObject; + NSMutableData *bytes, *parts; + NSMutableArray *partLength; NSData *d; - NSRange r1, r2; + bytes = [NSMutableData data]; + parts = [NSMutableData data]; + partLength = [NSMutableArray array]; - r1 = [realCollectionId rangeOfString: @"/"]; - r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)]; - - folderName = [realCollectionId substringToIndex: r1.location]; - messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)]; - pathToPart = [realCollectionId substringFromIndex: r2.location+1]; - - userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + for (i = 0; i < [fetchRequests count]; i++) + { + aFetch = [fetchRequests objectAtIndex: i]; + fileReference = [[[(id)[aFetch getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL]; + realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType]; - currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName] - inContext: context - acquire: NO]; + if (folderType == ActiveSyncMailFolder) + { + id currentFolder, currentCollection, currentBodyPart; + NSString *folderName, *messageName, *pathToPart; + SOGoMailAccounts *accountsFolder; + SOGoUserFolder *userFolder; + SOGoMailObject *mailObject; + + NSRange r1, r2; + + r1 = [realCollectionId rangeOfString: @"/"]; + r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)]; - mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; - currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context]; + folderName = [realCollectionId substringToIndex: r1.location]; + messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)]; + pathToPart = [realCollectionId substringFromIndex: r2.location+1]; + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - s = [NSMutableString string]; - [s appendString: @""]; - [s appendString: @""]; - [s appendString: @""]; - [s appendString: @"1"]; - [s appendString: @""]; + currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName] + inContext: context + acquire: NO]; - [s appendString: @""]; - [s appendString: @"1"]; - [s appendFormat: @"%@", [fileReference stringByEscapingURL]]; - [s appendString: @""]; + mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; + currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context]; - [s appendFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]; - [s appendFormat: @"%@", [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]]; + [s appendString: @""]; + [s appendString: @"1"]; + [s appendFormat: @"%@", [fileReference stringByEscapingURL]]; + [s appendString: @""]; - [s appendString: @""]; - [s appendString: @""]; + [s appendFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]; + + if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"]) + { + [s appendFormat: @"%d", i+1]; + [partLength addObject: [NSNumber numberWithInteger: [[currentBodyPart fetchBLOB] length]]]; + [parts appendData:[currentBodyPart fetchBLOB]]; + } + else + { + [s appendFormat: @"0-%d", [[[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context] length]-1]; + [s appendFormat: @"%@", [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]]; + } + [s appendString: @""]; + [s appendString: @""]; + } + else + { + [theResponse setStatus: 500]; + return; + } + } [s appendString: @""]; [s appendString: @""]; - + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; - [theResponse setContent: d]; - } - else - { - [theResponse setStatus: 500]; + + if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"]) + { + uint32_t PartCount; + uint32_t Offset; + uint32_t Len; + + // 2.2.2.9.1.1 - MultiPartResponse -- http://msdn.microsoft.com/en-us/library/jj663270%28v=exchg.80%29.aspx + PartCount = [partLength count] + 1; + Offset = ((PartCount) * 2) * 4 + 4; + Len = [d length]; + + [bytes appendBytes: &PartCount length: 4]; + [bytes appendBytes: &Offset length: 4]; + [bytes appendBytes: &Len length: 4]; + + // 2.2.2.9.1.1.1 - PartMetaData -- http://msdn.microsoft.com/en-us/library/jj663267%28v=exchg.80%29.aspx + for (i = 0; i < [fetchRequests count]; i++) + { + Offset = Offset + Len; + Len = [[partLength objectAtIndex:i] intValue]; + [bytes appendBytes: &Offset length: 4]; + [bytes appendBytes: &Len length: 4]; + } + + // First part - webxml + [bytes appendData: d]; + + // Subsequent parts - requested data + [bytes appendData: parts]; + + [theResponse setContent: bytes]; + } + else + { + [theResponse setContent: d]; + } } } @@ -2159,6 +2225,11 @@ - (void) processSendMail: (id ) theDocumentElement NGMimeMessage *message; NSException *error; NSData *data; + NGMutableHashMap *map; + NGMimeMessage *messageToSend; + NGMimeMessageGenerator *generator; + NSDictionary *identity; + NSString *fullName, *email; // We get the mail's data data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]; @@ -2167,6 +2238,24 @@ - (void) processSendMail: (id ) theDocumentElement parser = [[NGMimeMessageParser alloc] init]; message = [parser parsePartFromData: data]; RELEASE(parser); + + map = [NGHashMap hashMapWithDictionary: [message headers]]; + + identity = [[context activeUser] primaryIdentity]; + + fullName = [identity objectForKey: @"fullName"]; + email = [identity objectForKey: @"email"]; + if ([fullName length]) + [map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"]; + else + [map setObject: email forKey: @"from"]; + + messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease]; + + [messageToSend setBody: [message body]]; + + generator = [[[NGMimeMessageGenerator alloc] init] autorelease]; + data = [generator generateMimeFromPart: messageToSend]; error = [self _sendMail: data recipients: [message allRecipients] @@ -2295,6 +2384,8 @@ - (void) processSmartForward: (id ) theDocumentElement NSException *error; id body, bodyFromSmartForward; + NSString *fullName, *email; + NSDictionary *identity; userFolder = [[context activeUser] homeFolderInContext: context]; accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; @@ -2318,6 +2409,15 @@ - (void) processSmartForward: (id ) theDocumentElement map = [NGHashMap hashMapWithDictionary: [messageFromSmartForward headers]]; [map setObject: @"multipart/mixed" forKey: @"content-type"]; + identity = [[context activeUser] primaryIdentity]; + + fullName = [identity objectForKey: @"fullName"]; + email = [identity objectForKey: @"email"]; + if ([fullName length]) + [map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"]; + else + [map setObject: email forKey: @"from"]; + messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease]; body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease]; @@ -2504,19 +2604,24 @@ - (NSException *) dispatchRequest: (id) theRequest // Ping or Sync command with empty body cmdName = [NSString stringWithFormat: @"process%@:inResponse:", cmdName]; } - + aSelector = NSSelectorFromString(cmdName); + // The -processItemOperations: method will generate a multipart response when Content-Type is application/vnd.ms-sync.multipart + if ([[theRequest headerForKey: @"MS-ASAcceptMultiPart"] isEqualToString:@"T"]) + [theResponse setHeader: @"application/vnd.ms-sync.multipart" forKey: @"Content-Type"]; + else + [theResponse setHeader: @"application/vnd.ms-sync.wbxml" forKey: @"Content-Type"]; + [self performSelector: aSelector withObject: documentElement withObject: theResponse]; - [theResponse setHeader: @"application/vnd.ms-sync.wbxml" forKey: @"Content-Type"]; [theResponse setHeader: @"14.1" forKey: @"MS-Server-ActiveSync"]; [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(pool); - + return nil; } diff --git a/ChangeLog b/ChangeLog index f8728b5e49..d18933d3a0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,187 @@ +commit daa7ab87d7de8553c4df8790a10f18dead51e61f +Author: Ludovic Marcotte +Date: Mon Jan 19 13:54:04 2015 -0500 + + Fix cosmetic change when fetching fb info + +M Documentation/SOGoInstallationGuide.asciidoc +M UI/MainUI/SOGoUserHomePage.m + +commit 3e59b1ad6eb4dcca87b75fc478931bb9a404900e +Author: Ludovic Marcotte +Date: Fri Jan 16 12:55:38 2015 -0500 + + Improved documentation regarding password policies over LDAP + +M Documentation/SOGoInstallationGuide.asciidoc + +commit e45451a9f9a38f869268f4db47e4e24045b63530 +Author: Ludovic Marcotte +Date: Fri Jan 16 09:18:37 2015 -0500 + + Improved comments over previous commit + +M SOPE/NGCards/iCalTimeZonePeriod.m + +commit e9e3dd5646cdf004896f350e35fddd661463f029 +Author: Ludovic Marcotte +Date: Thu Jan 15 15:29:02 2015 -0500 + + fixed timezone calculation on recurring event + +M NEWS +M SOPE/NGCards/iCalTimeZonePeriod.m +M SoObjects/Appointments/SOGoAppointmentFolder.m + +commit 16c863d89dc7555d3c1a490c3467c08c00bd421f +Author: Ludovic Marcotte +Date: Thu Jan 15 11:55:04 2015 -0500 + + Fixed potential issue when handling multiple Add/Change/Delete/Fetch EAS commands (#3057) + +M ActiveSync/SOGoActiveSyncDispatcher+Sync.m +M NEWS + +commit d87056ebfadff870518d032a0fbbd52d5c6d0a88 +Author: Ludovic Marcotte +Date: Mon Jan 12 14:38:55 2015 -0500 + + handle multipart for EAS/ItemOperations + +M ActiveSync/SOGoActiveSyncDispatcher.m +M NEWS + +commit 135b463e8fa770f47aadb58ede53898ce6a8bfdc +Author: Ludovic Marcotte +Date: Mon Jan 12 13:39:06 2015 -0500 + + Updated the EAS doc + +M ActiveSync/README +M Documentation/SOGoInstallationGuide.asciidoc + +commit f4c4f5af6271956de6a9a339d8834b4f2f66ea45 +Author: Ludovic Marcotte +Date: Mon Jan 12 09:09:06 2015 -0500 + + fixed From's full name over EAS + +M ActiveSync/SOGoActiveSyncDispatcher.m +M NEWS + +commit 70e45f78430e07f75e1fe094f8087902f572fab6 +Author: Ludovic Marcotte +Date: Mon Jan 12 08:40:06 2015 -0500 + + fixed birthday offset in EAS + +M ActiveSync/NGVCard+ActiveSync.m +M NEWS + +commit 28e449b05077b2f0686fae645a84447af1b6d4e8 +Author: Ludovic Marcotte +Date: Sat Jan 10 07:55:55 2015 -0500 + + Updated NEWS file for bugs fixed + +M NEWS + +commit 6b52e9c945af28188b221f4cbf26e0ee28f58195 +Author: Ludovic Marcotte +Date: Fri Jan 9 09:04:15 2015 -0500 + + Fix for bug #2960 + +M NEWS +M SoObjects/SOGo/WOContext+SOGo.h +M SoObjects/SOGo/WOContext+SOGo.m +M SoObjects/SOGo/WORequest+SOGo.h +M SoObjects/SOGo/WORequest+SOGo.m + +commit 1cfbea69c14468424f5eb713bcb5918a70b3d676 +Author: Ludovic Marcotte +Date: Thu Jan 8 15:56:16 2015 -0500 + + Make sure we always release local pool + +M ActiveSync/SOGoActiveSyncDispatcher+Sync.m + +commit cb9118d869d1d2535c50d46df5457038160de59e +Author: Ludovic Marcotte +Date: Wed Jan 7 09:29:31 2015 -0500 + + Moved the pool destruction at the correct location + +M ActiveSync/SOGoActiveSyncDispatcher+Sync.m + +commit dd6dd7251ca15dc5e758e733e2529003e08e7c7c +Author: Francis Lachapelle +Date: Tue Jan 6 13:59:20 2015 -0500 + + Fix selection of calendar in event/task editors + + Fixes #3049 + Fixes #3050 + +M UI/Scheduler/UIxComponentEditor.m +M UI/Templates/SchedulerUI/UIxAppointmentEditor.wox +M UI/Templates/SchedulerUI/UIxTaskEditor.wox + +commit 24c6c8c91d421594bd51f07904d4cd3911cd187c +Author: Ludovic Marcotte +Date: Tue Jan 6 10:59:56 2015 -0500 + + Fix for bug #3054 + +M NEWS +M SoObjects/Appointments/SOGoAppointmentFolders.m +M SoObjects/SOGo/SOGoFolder.h +M SoObjects/SOGo/SOGoFolder.m +M SoObjects/SOGo/SOGoGCSFolder.h +M SoObjects/SOGo/SOGoObject.h +M SoObjects/SOGo/SOGoObject.m +M SoObjects/SOGo/SOGoParentFolder.h +M SoObjects/SOGo/SOGoParentFolder.m + +commit b1ac7a0cca0ace6aecd3ee650e3029ca16709831 +Author: Ludovic Marcotte +Date: Mon Jan 5 13:49:28 2015 -0500 + + MultipleBookingsFieldName can be set to -1 + +M Documentation/SOGoInstallationGuide.asciidoc +M NEWS +M SoObjects/Appointments/SOGoAppointmentObject.m +M SoObjects/SOGo/SOGoUser.m +M SoObjects/SOGo/SOGoUserManager.h +M SoObjects/SOGo/SOGoUserManager.m +M UI/MainUI/SOGoUserHomePage.m + +commit 59e6d6df4aa131ca1ea658a212ec1c30e516b5f1 +Author: Francis Lachapelle +Date: Mon Jan 5 10:12:57 2015 -0500 + + Fix selection of calendar in event/task editors + + Fixes #3049 + Fixes #3050 + +M NEWS +M UI/Templates/SchedulerUI/UIxAppointmentEditor.wox +M UI/Templates/SchedulerUI/UIxTaskEditor.wox + +commit 71ad5de910de96a293f75c7c0caba7a36b96c020 +Author: Ludovic Marcotte +Date: Tue Dec 30 08:00:41 2014 -0500 + + Bumped everything for the release + +M ChangeLog +M Documentation/docinfo.xml +M Documentation/includes/global-attributes.asciidoc +M NEWS +M Version + commit ead665de85e2202dbde926c316cbba927d38dfa7 Author: Ludovic Marcotte Date: Mon Dec 29 16:19:10 2014 -0500 diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 08c8191540..0b2f94f217 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -911,7 +911,8 @@ resource if the entry has the calendarresource objectClass set. to which a resource can be part of at any point in time. If this is set to `0`, or if the attribute is missing, it means no -limit. +limit. If set to `-1`, no limit is imposed but the resource will +be marked as busy the first time it is booked. |filter (optional) |The filter to use for LDAP queries, it should be defined as an @@ -998,7 +999,9 @@ context as Dovecot stores in its database. |passwordPolicy |If set to `YES`, SOGo will use the extended LDAP Password Policies attributes. If you LDAP server does not support those and you activate -this feature, every LDAP requests will fail. +this feature, every LDAP requests will fail. Note that some LDAP servers +require LDAP/SSL for password policies to work. This is the case for +example with 389 Directory Server. |isAddressBook |If set to `YES`, this LDAP source is used as a shared address book @@ -1476,7 +1479,10 @@ case, SOGo will consider the returned entry to be a resource. which a resource can be part of at any point in time. If this is set to `0`, or if the attribute is missing, it means no -limit. +limit and the resource will always be marked as free. If set to `-1`, +no limit is imposed but the resource will be marked as busy the first +time it is booked. If greater than 0, the resource will get marked as +busy once it reaches the value. |DomainFieldName (optional) |If set, SOGo will use the value of that field as the domain associated @@ -2411,24 +2417,21 @@ have unexpected behaviour with various ActiveSync clients. Please be aware of the following limitations: -* Currently, only the personal calendar and address book are -synchronized. Adding support for all folders is planned. * When creating an Outlook 2013 profile, you must actually kill Outlook before the end of the creation process. See http://www.vionblog.com/connect-zimbra-community-with-outlook-2013 for a procedure example. * Outlook 2013 does not search the GAL. One possible alternative solution is to configure Outlook to use a LDAP server (over SSL) with -authentication. Alternatively, when supporting more than just the -personal address book, we'll also be able to expose the LDAP/SQL based -address books in SOGo over ActiveSync. +authentication. Outlook 2013 also does not seem to support multiple +address books over ActiveSync. * Make sure you do not use a self-signed certificate. While this will work, Outlook will work intermittently as it will raise popups for certificate validation, sometimes in background, preventing the user to see the warning and thus, preventing any synchronization to happen. * ActiveSync clients keep connections open for a while. Each connection will grab a hold on a sogod process so you will need a lot of processes -to handle many clients. This limitation will eventually be overcome in -SOGo. +to handle many clients. Make sure you tune your SOGo server when having +lots of ActiveSync clients. * Repetitive events with occurrences exceptions are currently not supported. * Outlook 2013 Autodiscovery is currently not supported. @@ -2450,7 +2453,7 @@ user base. To contact Microsoft, please visit: -http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exchangeactivesyncprotocol.aspx +http://www.microsoft.com/en-us/legal/intellectualproperty/   and send an email to iplicreq@microsoft.com diff --git a/Documentation/docinfo.xml b/Documentation/docinfo.xml index aa1164747d..e3f9dc1b8e 100644 --- a/Documentation/docinfo.xml +++ b/Documentation/docinfo.xml @@ -1,7 +1,7 @@ -Version 2.2.13 - December 2014 -for version 2.2.13 -2014-12-30 +Version 2.2.14 - January 2015 +for version 2.2.14 +2015-01-20 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 0bce4709c2..db6c0f09c7 100644 --- a/Documentation/includes/global-attributes.asciidoc +++ b/Documentation/includes/global-attributes.asciidoc @@ -6,13 +6,13 @@ Authors: - Inverse inc. - Copyright (C) 2008-2014 Inverse inc. + Copyright (C) 2008-2015 Inverse inc. License: GFDL 1.2 or later. http://www.gnu.org/licenses/fdl.html //// // TODO have the build system take care of this -:release_version: 2.2.13 +:release_version: 2.2.14 // vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab: diff --git a/NEWS b/NEWS index e6aeb7c61e..047e7ea084 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,22 @@ -2.2.13 (2014-12-30) +2.2.14 (2015-01-20) ------------------- +Enhancements + - MultipleBookingsFieldName can be set to -1 to show busy status when booked at least once + - handle multipart objects in EAS/ItemOperations + Bug fixes - - fix contact description truncation on WP8 phones (#3028) - - fix freebusy information not always returned - - fix tz issue when the user one was different from the system one with EAS + - fixed calendar selection in event and task editors (#3049, #3050) + - check for resources existence when listing subscribed ones (#3054) + - correctly recognize Apple Calendar on Yosemite (#2960) + - fixed two potential autorelease pool leaks (#3026 and #3051) + - fixed birthday offset in EAS + - fixed From's full name over EAS + - fixed potential issue when handling multiple Add/Change/Delete/Fetch EAS commands (#3057) + - fixed wrong timezone calculation on recurring events + +2.2.13 (2014-12-30) +------------------- Enhancements - initial support for empty sync request/response for EAS @@ -12,11 +24,16 @@ Enhancements support memory-limited sync response sizes - we now not only use the creation date for event's cutoff date (EAS) +Bug fixes + - fixed contact description truncation on WP8 phones (#3028) + - fixed freebusy information not always returned + - fixed tz issue when the user one was different from the system one with EAS + 2.2.12a (2014-12-19) -------------------- Bug fixes - - fix empty HTML mails being sent (#3034) + - fixed empty HTML mails being sent (#3034) 2.2.12 (2014-12-18) ------------------- @@ -32,12 +49,12 @@ Enhancements - updated Czech, Dutch, Finnish, French, German, Polish and Spanish (Spain) translations Bug fixes - - fix for privacy and categories for EAS (#3022) + - fixed for privacy and categories for EAS (#3022) - correctly set MeetingStatus for EAS on iOS devices - Ubuntu Lucid fixes for EAS - - fix calendar reminders for future events (#3008) + - fixed calendar reminders for future events (#3008) - make sure all text parts are UTF-8 re-encoded for Outlook 2013 over EAS (#3003) - - fix task description truncation affecting WP8 phones over EAS (#3028) + - fixed task description truncation affecting WP8 phones over EAS (#3028) 2.2.11a (2014-12-10) -------------------- diff --git a/SOPE/NGCards/iCalTimeZonePeriod.m b/SOPE/NGCards/iCalTimeZonePeriod.m index 5da8db47ad..1326b329fc 100644 --- a/SOPE/NGCards/iCalTimeZonePeriod.m +++ b/SOPE/NGCards/iCalTimeZonePeriod.m @@ -156,13 +156,15 @@ - (NSCalendarDate *) _occurrenceForDate: (NSCalendarDate *) refDate [tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]]; tmpDate = [NSCalendarDate dateWithYear: [refDate yearOfCommonEra] month: [[[rrule byMonth] objectAtIndex: 0] intValue] - day: 1 hour: [tzStart hourOfDay] + day: 1 + hour: [tzStart hourOfDay] minute: [tzStart minuteOfHour] second: 0 timeZone: [NSTimeZone timeZoneWithName: @"GMT"]]; + tmpDate = [tmpDate addYear: 0 month: ((pos > 0) ? 0 : 1) day: 0 hour: 0 minute: 0 second: 0]; - + /* If the day of the time change is "-XSU", we need to determine whether the first day of next month is in the same week. In practice, as most time changes occurs on sundays, it will be false only when that first day is a @@ -171,10 +173,42 @@ - (NSCalendarDate *) _occurrenceForDate: (NSCalendarDate *) refDate if (dateDayOfWeek > dayOfWeek && pos < 0) pos++; + /* We check if the days of the week are identical. This is important because if they + are, "pos" actually includes the first day of tmpDate which means we must decrement + pos by 1. This happens for example in the eastern timezone (America/Montreal) + in 2015. We have: + + BEGIN:VTIMEZONE + TZID:America/Montreal + X-LIC-LOCATION:America/Montreal + BEGIN:DAYLIGHT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + DTSTART:19700308T020000 + RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU + END:DAYLIGHT + BEGIN:STANDARD + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:EST + DTSTART:19701101T020000 + RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU + END:STANDARD + END:VTIMEZONE + + The time changes occure on a Sunday, but in March, the 1st is a Sunday itself and in November + the 1st is also a Sunday. If we don't decrement "pos" by one, tmpDate (which is set to March or November 1st + because of "day: 1" will have 14 more days added for March and 7 more days added for November - which will + effectively shift the time change by a whole week. + */ + if (dayOfWeek == dateDayOfWeek) + pos--; + offset = (dayOfWeek - dateDayOfWeek) + (pos * 7); tmpDate = [tmpDate addYear: 0 month: 0 day: offset hour: 0 minute: 0 second: 0]; - + return tmpDate; } diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index c3e29c9caf..fc15522358 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -926,11 +926,6 @@ - (NSMutableDictionary *) fixupCycleRecord: (NSDictionary *) theRecord dateSecs = [NSNumber numberWithInt: [date timeIntervalSince1970]]; [record setObject: dateSecs forKey: @"c_enddate"]; - // The first instance date is added to the dictionary so it can - // be used by UIxCalListingActions to compute the DST offset. - date = [theFirstCycle startDate]; - [record setObject: date forKey: @"cycleStartDate"]; - return record; } @@ -1103,9 +1098,7 @@ - (void) _appendCycleException: (iCalRepeatableEntityObject *) component [newRecord setObject: dateSecs forKey: @"c_recurrence_id"]; [newRecord setObject: [NSNumber numberWithInt: 1] forKey: @"c_iscycle"]; - // The first instance date is added to the dictionary so it can - // be used by UIxCalListingActions to compute the DST offset. - [newRecord setObject: [fir startDate] forKey: @"cycleStartDate"]; + // We identified the record as an exception. [newRecord setObject: [NSNumber numberWithInt: 1] forKey: @"isException"]; diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.m b/SoObjects/Appointments/SOGoAppointmentFolders.m index 10361c0062..2b8ee88c77 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.m +++ b/SoObjects/Appointments/SOGoAppointmentFolders.m @@ -1,7 +1,7 @@ /* SOGoAppointmentFolders.m - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 1d7538da14..64baf52d77 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -637,10 +637,10 @@ - (NSException *) _handleResourcesConflicts: (NSArray *) theAttendees if ([fbInfo count]) { - // If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit + // If we always force the auto-accept if numberOfSimultaneousBookings <= 0 (ie., no limit // is imposed) or if numberOfSimultaneousBookings is greater than the number of // overlapping events - if ([user numberOfSimultaneousBookings] == 0 || + if ([user numberOfSimultaneousBookings] <= 0 || [user numberOfSimultaneousBookings] > [fbInfo count]) { if (currentAttendee) diff --git a/SoObjects/SOGo/SOGoFolder.h b/SoObjects/SOGo/SOGoFolder.h index 018cfe7e07..eb435a501e 100644 --- a/SoObjects/SOGo/SOGoFolder.h +++ b/SoObjects/SOGo/SOGoFolder.h @@ -1,6 +1,6 @@ /* SOGoFolder.h - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoFolder.m b/SoObjects/SOGo/SOGoFolder.m index a390b9267c..b089c258a5 100644 --- a/SoObjects/SOGo/SOGoFolder.m +++ b/SoObjects/SOGo/SOGoFolder.m @@ -1,6 +1,6 @@ /* SOGoFolder.m - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoGCSFolder.h b/SoObjects/SOGo/SOGoGCSFolder.h index 806be91058..9b8de6b35b 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.h +++ b/SoObjects/SOGo/SOGoGCSFolder.h @@ -1,6 +1,6 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2006-2014 Inverse inc. + Copyright (C) 2006-2015 Inverse inc. This file is part of SOGo. diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index 114b0c55dc..e6de44944f 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -1,14 +1,15 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG + Copyright (C) 2006-2015 Inverse inc. - This file is part of OpenGroupware.org. + This file is part of SOGo. - OGo is free software; you can redistribute it and/or modify it under + SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOGo 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 Lesser General Public License for more details. diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index 716e8f194c..3f3afcb9bd 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -1,7 +1,7 @@ /* SOGoObject.m - this file is part of SOGo * * Copyright (C) 2004-2005 SKYRIX Software AG - * Copyright (C) 2006-2013 Inverse inc. + * Copyright (C) 2006-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoParentFolder.h b/SoObjects/SOGo/SOGoParentFolder.h index 73ee5deeb6..97cf89f8ec 100644 --- a/SoObjects/SOGo/SOGoParentFolder.h +++ b/SoObjects/SOGo/SOGoParentFolder.h @@ -1,6 +1,6 @@ /* SOGoParentFolder.h - this file is part of SOGo * - * Copyright (C) 2006-2014 Inverse inc. + * Copyright (C) 2006-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index 164296b836..c0b44c5a44 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -1,8 +1,6 @@ /* SOGoParentFolder.m - this file is part of SOGo * - * Copyright (C) 2006-2009 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2006-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -294,7 +292,13 @@ - (BOOL) _appendSubscribedSource: (NSString *) sourceKey subscribedFolder = [subFolderClass folderWithSubscriptionReference: sourceKey inContainer: self]; + + // We check with -ocsFolderForPath if the folder also exists in the database. + // This is important because user A could delete folder X, and user B has subscribed to it. + // If the "default roles" are enabled for calendars/address books, -validatePersmission:.. will + // work (grabbing the default role) and the deleted resource will be incorrectly returned. if (subscribedFolder + && [subscribedFolder ocsFolderForPath: [subscribedFolder ocsPath]] && ![sm validatePermission: SOGoPerm_AccessObject onObject: subscribedFolder inContext: context]) diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 8f2768571e..da0ed8c6ca 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2013 Inverse inc. + Copyright (C) 2006-2015 Inverse inc. Copyright (C) 2005 SKYRIX Software AG This file is part of SOGo. diff --git a/SoObjects/SOGo/SOGoUserManager.h b/SoObjects/SOGo/SOGoUserManager.h index 6564e10b29..2373470532 100644 --- a/SoObjects/SOGo/SOGoUserManager.h +++ b/SoObjects/SOGo/SOGoUserManager.h @@ -1,9 +1,6 @@ /* SOGoUserManager.h - this file is part of SOGo * - * Copyright (C) 2007-2013 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Francis Lachapelle + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 03cf78e62b..d8e09ba6c7 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -1,9 +1,6 @@ /* SOGoUserManager.m - this file is part of SOGo * - * Copyright (C) 2007-2013 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Francis Lachapelle + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/WOContext+SOGo.h b/SoObjects/SOGo/WOContext+SOGo.h index f13bbcd0c1..c6d50cd681 100644 --- a/SoObjects/SOGo/WOContext+SOGo.h +++ b/SoObjects/SOGo/WOContext+SOGo.h @@ -1,8 +1,6 @@ /* WOContext+SOGo.h - this file is part of SOGo * - * Copyright (C) 2008 Inverse inc. - * - * Author: Francis Lachapelle + * Copyright (C) 2008-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/WOContext+SOGo.m b/SoObjects/SOGo/WOContext+SOGo.m index e5f992ccff..c558b1be63 100644 --- a/SoObjects/SOGo/WOContext+SOGo.m +++ b/SoObjects/SOGo/WOContext+SOGo.m @@ -1,8 +1,6 @@ /* WOContext+SOGo.m - this file is part of SOGo * - * Copyright (C) 2008 Inverse inc. - * - * Author: Francis Lachapelle + * Copyright (C) 2008-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/WORequest+SOGo.h b/SoObjects/SOGo/WORequest+SOGo.h index f749d183c1..9ae6c91040 100644 --- a/SoObjects/SOGo/WORequest+SOGo.h +++ b/SoObjects/SOGo/WORequest+SOGo.h @@ -1,8 +1,6 @@ /* WORequest+SOGo.h - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/WORequest+SOGo.m b/SoObjects/SOGo/WORequest+SOGo.m index ff3554fd6a..f528343dd3 100644 --- a/SoObjects/SOGo/WORequest+SOGo.m +++ b/SoObjects/SOGo/WORequest+SOGo.m @@ -1,6 +1,6 @@ /* WORequest+SOGo.m - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,10 +127,14 @@ - (BOOL) isIPhone || [self isAppleDAVWithSubstring: @"iOS/"]; } +// +// Mac+OS+X/10.10.1 (14B25) CalendarAgent/315 +// - (BOOL) isICal { return ([self isAppleDAVWithSubstring: @"Mac OS X/10."] || [self isAppleDAVWithSubstring: @"Mac_OS_X/"] + || [self isAppleDAVWithSubstring: @"Mac+OS+X/"] || [self isAppleDAVWithSubstring: @"CoreDAV/"]); } diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 1345e214c4..2a8415d708 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -1,6 +1,6 @@ /* SOGoUserHomePage.m - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -103,8 +103,8 @@ - (void) _fillFreeBusyItems: (unsigned int *) items maxBookings = [user numberOfSimultaneousBookings]; isResource = [user isResource]; - // Don't fetch freebusy information if the user is of type 'resource' and has unlimited bookings - if (!isResource || maxBookings > 0) + // Fetch freebusy information if the user is NOT a resource or if multiplebookings isn't unlimited + if (!isResource || maxBookings != 0) { for (recordCount = 0; recordCount < recordMax; recordCount++) { @@ -167,10 +167,10 @@ - (void) _fillFreeBusyItems: (unsigned int *) items endInterval += (delta/60/15); // Update bit string representation - // If the user is a resource, keep the sum of overlapping events + // If the user is a resource with restristed amount of bookings, keep the sum of overlapping events for (count = startInterval; count < endInterval; count++) { - *(items + count) = isResource ? *(items + count) + 1 : 1; + *(items + count) = (isResource && maxBookings > 0) ? *(items + count) + 1 : 1; } } } @@ -189,6 +189,9 @@ - (void) _fillFreeBusyItems: (unsigned int *) items } } +// +// +// - (NSString *) _freeBusyFromStartDate: (NSCalendarDate *) startDate toEndDate: (NSCalendarDate *) endDate forFreeBusy: (SOGoFreeBusyObject *) fb diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 60ee34c124..35a29219a7 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -1428,7 +1428,8 @@ - (NSString *) componentCalendarName - (void) setComponentCalendar: (SOGoAppointmentFolder *) _componentCalendar { - ASSIGN(componentCalendar, _componentCalendar); + if (_componentCalendar) + ASSIGN(componentCalendar, _componentCalendar); } /* priorities */ diff --git a/Version b/Version index 7e063e9964..b651985e2a 100644 --- a/Version +++ b/Version @@ -4,4 +4,4 @@ MAJOR_VERSION=2 MINOR_VERSION=2 -SUBMINOR_VERSION=13 +SUBMINOR_VERSION=14