From b3cd609ae76414237db120fd76545afe11f6346b Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 14 Nov 2014 09:13:14 -0500 Subject: [PATCH] Added WindowSize support for GCS collections --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 200 +++++++++++++++------ NEWS | 1 + 2 files changed, 149 insertions(+), 52 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 86a2e5c89..1c15f37e6 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -29,11 +29,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "SOGoActiveSyncDispatcher+Sync.h" - #import #import #import #import +#import #import #import #import @@ -122,7 +122,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o setObjectType: ActiveSyncFolderCacheObject]; [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; - + [[o properties] removeObjectForKey: @"SyncKey"]; [[o properties] removeObjectForKey: @"SyncCache"]; [[o properties] removeObjectForKey: @"DateCache"]; @@ -147,6 +147,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return [o properties]; } +- (NSString *) _getNameInCache: (id) theCollection withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType +{ + NSString *nameInCache; + + if (theFolderType == ActiveSyncMailFolder) + nameInCache= [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]; + else + { + NSString *component_name; + if (theFolderType == ActiveSyncContactFolder) + component_name = @"vcard"; + else if (theFolderType == ActiveSyncEventFolder) + component_name = @"vevent"; + else + component_name = @"vtodo"; + + nameInCache= [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]]; + } + + return nameInCache; +} + + // // @@ -190,7 +213,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType inBuffer: (NSMutableString *) theBuffer { - NSMutableDictionary *allValues; + NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *allValues; NSString *clientId, *serverId; NSArray *additions; @@ -276,6 +299,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [theBuffer appendFormat: @"%@", serverId]; [theBuffer appendFormat: @"%d", 1]; [theBuffer appendString: @""]; + + // Update syncCache + folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]]; + + syncCache = [folderMetadata objectForKey: @"SyncCache"]; + dateCache = [folderMetadata objectForKey: @"DateCache"]; + + [syncCache setObject: [folderMetadata objectForKey: @"SyncKey"] forKey: serverId]; + [dateCache setObject: [NSCalendarDate date] forKey: serverId]; + + [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; } } } @@ -440,6 +474,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [theBuffer appendFormat: @"%@", serverId]; [theBuffer appendFormat: @"%d", 1]; [theBuffer appendString: @""]; + + // update syncCache + NSMutableDictionary *folderMetadata, *dateCache, *syncCache; + folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]]; + + syncCache = [folderMetadata objectForKey: @"SyncCache"]; + dateCache = [folderMetadata objectForKey: @"DateCache"]; + + [syncCache removeObjectForKey: serverId]; + [dateCache removeObjectForKey: serverId]; + + [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; } } } @@ -497,19 +543,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. more_available = NO; - if (theFolderType == ActiveSyncMailFolder && !([theSyncKey isEqualToString: @"-1"]) && theFilterType) + folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]]; + + // If this is a new sync operation, DateCache and SyncCache needs to be deleted + if ([theSyncKey isEqualToString: @"-1"]) + { + [folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"]; + [folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"]; + } + + syncCache = [folderMetadata objectForKey: @"SyncCache"]; + dateCache = [folderMetadata objectForKey: @"DateCache"]; + + if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) && + !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize + !([theSyncKey isEqualToString: @"-1"]) && // new sync operation + theFilterType) { NSArray *allKeys; NSString *key; + int softdelete_count; softdelete_count = 0; - folderMetadata = [self _folderMetadataForKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; - - dateCache = [folderMetadata objectForKey: @"DateCache"]; - syncCache = [folderMetadata objectForKey: @"SyncCache"]; - allKeys = [dateCache allKeys]; for (i = 0; i < [allKeys count]; i++) { @@ -520,7 +577,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendString: @""]; [s appendFormat: @"%@", key]; [s appendString: @""]; - + [syncCache removeObjectForKey: key]; [dateCache removeObjectForKey: key]; @@ -530,7 +587,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (softdelete_count >= theWindowSize) { [folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"]; - [self _setFolderMetadata: folderMetadata forKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; + [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; more_available = YES; *theLastServerKey = theSyncKey; @@ -542,7 +599,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } [folderMetadata removeObjectForKey: @"MoreAvailable"]; - [self _setFolderMetadata: folderMetadata forKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; + [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; } // @@ -567,7 +624,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. NSArray *allComponents; BOOL updated; - int deleted; + int deleted, return_count; if (theFolderType == ActiveSyncContactFolder) component_name = @"vcard"; @@ -577,19 +634,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. component_name = @"vtodo"; 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]; - // Disabled for now for GCS folders. - // if (max > theWindowSize) - // { - // max = theWindowSize; - // more_available = YES; - // } - + return_count = 0; + for (i = 0; i < max; i++) { + // 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]; + + break; + } + component = [allComponents objectAtIndex: i]; deleted = [[component objectForKey: @"c_deleted"] intValue]; @@ -600,17 +664,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (deleted) { - [s appendString: @""]; - [s appendFormat: @"%@", uid]; - [s appendString: @""]; + if ([syncCache objectForKey: uid]) + { + [s appendString: @""]; + [s appendFormat: @"%@", uid]; + [s appendString: @""]; + + [syncCache removeObjectForKey: uid]; + [dateCache removeObjectForKey: uid]; + return_count++; + } } else { updated = YES; - if ([[component objectForKey: @"c_creationdate"] intValue] > [theSyncKey intValue]) + if (![syncCache objectForKey: uid]) updated = NO; + else if ([[component objectForKey: @"c_lastmodified"] intValue] == [[syncCache objectForKey: uid] intValue]) + continue; + return_count++; + sogoObject = [theCollection lookupName: [uid sanitizedServerIdWithType: theFolderType] inContext: context acquire: 0]; @@ -645,11 +720,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. continue; } } + + [syncCache setObject: [component objectForKey: @"c_lastmodified"] forKey: uid]; if (updated) [s appendString: @""]; else + { + // no need to set dateCache for Contacts + if ((theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder)) + [dateCache setObject: [componentObject startDate] ? [componentObject startDate] : [NSCalendarDate date] forKey: uid]; // FIXME: need to set proper date for recurring events - softDelete + [s appendString: @""]; + } [s appendFormat: @"%@", uid]; [s appendString: @""]; @@ -662,11 +745,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendString: @""]; else [s appendString: @""]; + + return_count++; } } // for ... - folderMetadata = [NSDictionary dictionaryWithObject: [theCollection davCollectionTag] - forKey: @"SyncKey"]; + if (more_available) + { + [folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"]; + [folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"]; + } + else + { + [folderMetadata removeObjectForKey: @"MoreAvailable"]; + [folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"]; + } + [self _setFolderMetadata: folderMetadata forKey: [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]]]; } @@ -695,18 +789,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]]; } - // If it's a new Sync operation, DateCache and SyncCache need to be deleted - folderMetadata = [self _folderMetadataForKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; - - if ([theSyncKey isEqualToString: @"-1"]) - { - [folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"]; - [folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"]; - } - - syncCache = [folderMetadata objectForKey: @"SyncCache"]; - dateCache = [folderMetadata objectForKey: @"DateCache"]; - sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache]; [sortedBySequence sortUsingSelector: @selector(compareSequence:)]; [sortedBySequence autorelease]; @@ -850,8 +932,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"]; } - [self _setFolderMetadata: folderMetadata - forKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; + [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; } // default: break; } // switch (folderType) ... @@ -863,9 +944,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [theBuffer appendString: @""]; [theBuffer appendString: s]; [theBuffer appendString: @""]; - - if (more_available) - [theBuffer appendString: @""]; } } @@ -950,13 +1028,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. inBuffer: (NSMutableString *) theBuffer changeDetected: (BOOL *) changeDetected { - NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey, *nameInCache; + NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey; SOGoMicrosoftActiveSyncFolderType folderType; id collection, value; NSMutableString *changeBuffer, *commandsBuffer; BOOL getChanges, first_sync; - unsigned int windowSize, v; + unsigned int windowSize, v, status; changeBuffer = [NSMutableString string]; commandsBuffer = [NSMutableString string]; @@ -993,6 +1071,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. windowSize = v; lastServerKey = nil; + status = 1; // From the documention, if GetChanges is missing, we must assume it's a YES. // See http://msdn.microsoft.com/en-us/library/gg675447(v=exchg.80).aspx for all details. @@ -1010,6 +1089,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. first_sync = YES; *changeDetected = YES; } + else if ((![syncKey isEqualToString: @"-1"]) && !([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"SyncCache"])) + { + //NSLog(@"Reset folder: %@", [collection nameInContainer]); + davCollectionTag = @"0"; + first_sync = YES; + *changeDetected = YES; + + if (!([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"displayName"])) + status = 13; // need folderSync + else + status = 3; // do a complete resync + } // We check our sync preferences and we stash them bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue]; @@ -1019,7 +1110,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"]; - // We generate the commands, if any, for the response. We might also have // generated some in processSyncCommand:inResponse: as we could have // received a Fetch command @@ -1028,9 +1118,11 @@ 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]; } @@ -1066,12 +1158,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. davCollectionTag = lastServerKey; else { - if (folderType == ActiveSyncMailFolder) - nameInCache = [[[collection mailAccountFolder] imapFolderGUIDs] objectForKey: [collection nameInContainer]]; - else - nameInCache = [collection nameInContainer]; + // Use the SyncKey saved by processSyncGetChanges - if processSyncGetChanges is not called (because of getChanges=false) + // SyncKey has the value of the previous sync operation. + davCollectionTag = [[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"SyncKey"]; - if (![[self _folderMetadataForKey: nameInCache] objectForKey: @"MoreAvailable"]) + if (!davCollectionTag) davCollectionTag = [collection davCollectionTag]; } @@ -1097,7 +1188,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [theBuffer appendFormat: @"%@", davCollectionTag]; [theBuffer appendFormat: @"%@", collectionId]; - [theBuffer appendFormat: @"%d", 1]; + [theBuffer appendFormat: @"%d", status]; + + // MoreAvailable breaks Windows Mobile devices if not between and + // https://social.msdn.microsoft.com/Forums/en-US/040b254e-f47e-4cc1-a397-6d8393cdb819/airsyncmoreavailable-breaks-windows-mobile-devices-what-am-i-doing-wrong?forum=os_exchangeprotocols + if ([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"MoreAvailable"]) + [theBuffer appendString: @""]; [theBuffer appendString: commandsBuffer]; [theBuffer appendString: changeBuffer]; diff --git a/NEWS b/NEWS index 027a5fd67..6c5d0b038 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,7 @@ Bug fixes - fixed handling of event invitations on iOS/EAS with no organizer (#2978) - fixed corrupted png files (#2975) - improved dramatically the BSON decoding speed + - added WindowSize support for GCS collections when using EAS 2.2.9a (2014-09-29) -------------------