From 91e3e19e40b843063cda21b84e6fcc072da2ad49 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 10 Jun 2014 11:04:27 -0400 Subject: [PATCH] Added the Ping ActiveSync command --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 20 ++- ActiveSync/SOGoActiveSyncDispatcher.m | 170 +++++++++++++++++++-- NEWS | 1 + 3 files changed, 177 insertions(+), 14 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 3102e92e8..8c206baaf 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -123,6 +123,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; + [[o properties] removeObjectForKey: @"SyncKey"]; [[o properties] removeObjectForKey: @"SyncCache"]; [[o properties] removeObjectForKey: @"DateCache"]; @@ -476,6 +477,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. lastServerKey: (NSString **) theLastServerKey { + NSMutableDictionary *folderMetadata; NSMutableString *s; BOOL more_available; @@ -602,12 +604,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendString: @""]; } } // for ... + + folderMetadata = [NSDictionary dictionaryWithObject: [theCollection davCollectionTag] + forKey: @"SyncKey"]; + [self _setFolderMetadata: folderMetadata + forKey: [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]]]; } break; case ActiveSyncMailFolder: default: { - NSMutableDictionary *syncCache, *dateCache, *folderMetadata; + NSMutableDictionary *syncCache, *dateCache; SOGoSyncCacheObject *lastCacheObject, *aCacheObject; NSMutableArray *allCacheObjects, *sortedBySequence; @@ -782,11 +789,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } if (more_available) - [folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"]; + { + [folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"]; + [folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"]; + } else - [folderMetadata removeObjectForKey: @"MoreAvailable"]; + { + [folderMetadata removeObjectForKey: @"MoreAvailable"]; + [folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"]; + } - [self _setFolderMetadata: folderMetadata forKey: [theCollection nameInContainer]]; } // default: diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 7f58e420a..13c0617de 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -122,6 +122,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +@interface SOGoActiveSyncDispatcher (Sync) + +- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey; + +@end + @implementation SOGoActiveSyncDispatcher - (id) init @@ -1261,30 +1267,73 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } // -// Ping requests make a little sense because the request -// doesn't contain the SyncKey on the client. So we can't -// really know if something has changed on the server. What we -// do for now is simply return Status=5 with the HeartbeatInterval -// set at 60 seconds or we wait 60 seconds before responding with -// Status=1 +// +// +// +// 3540 +// +// +// mail%2Fsogo_680f_193506d5_0 +// Email +// +// +// vevent/personal +// Calendar +// +// +// vcard/personal +// Contacts +// +// +// mail%2Fsogo_680f_193506d5_1 +// Email +// +// +// mail%2Fsogo_680f_193506d5_2 +// Email +// +// +// vtodo/personal +// Tasks +// +// +// mail%2Fsogo_753e_193511a1_0 +// Email +// +// +// mail%2Fsogo_753e_193511a1_1 +// Email +// +// +// // - (void) processPing: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { + NSString *collectionId, *realCollectionId, *syncKey; + NSMutableArray *foldersWithChanges, *allFoldersID; + SOGoMicrosoftActiveSyncFolderType folderType; + NSMutableDictionary *folderMetadata; SOGoSystemDefaults *defaults; + id aCollection; + NSArray *allCollections; + NSMutableString *s; + id collection; NSData *d; - int heartbeatInterval, defaultInterval, status; + + int i, j, heartbeatInterval, defaultInterval, internalInterval, status; defaults = [SOGoSystemDefaults sharedSystemDefaults]; defaultInterval = [defaults maximumPingInterval]; + internalInterval = [defaults internalSyncInterval]; if (theDocumentElement) heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue]; else heartbeatInterval = defaultInterval; - + if (heartbeatInterval > defaultInterval || heartbeatInterval == 0) { heartbeatInterval = defaultInterval; @@ -1295,15 +1344,116 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. status = 1; } - NSLog(@"Got Ping request with valid interval - sleeping for %d seconds.", heartbeatInterval); - sleep(heartbeatInterval); + // We build the list of folders to "ping". When the payload is empty, we use the list + // of "cached" folders. + allCollections = (id)[theDocumentElement getElementsByTagName: @"Folders"]; + allFoldersID = [NSMutableArray array]; + if (![allCollections count]) + { + SOGoMailAccounts *accountsFolder; + SOGoMailAccount *accountFolder; + SOGoUserFolder *userFolder; + NSArray *allValues; + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + allValues = [[accountFolder imapFolderGUIDs] allValues]; + + for (i = 0; i < [allValues count]; i++) + [allFoldersID addObject: [NSString stringWithFormat: @"mail/%@", [allValues objectAtIndex: i]]]; + + + // FIXME: handle multiple GCS collecitons + [allFoldersID addObject: @"vcard/personal"]; + [allFoldersID addObject: @"vevent/personal"]; + [allFoldersID addObject: @"vtodo/personal"]; + } + else + { + for (i = 0; i < [allCollections count]; i++) + { + aCollection = [allCollections objectAtIndex: i]; + collectionId = [[(id) [aCollection getElementsByTagName: @"Id"] lastObject] textValue]; + [allFoldersID addObject: collectionId]; + } + } + + foldersWithChanges = [NSMutableArray array]; + + // We enter our loop detection change + for (i = 0; i < (heartbeatInterval/internalInterval); i++) + { + for (j = 0; j < [allFoldersID count]; j++) + { + collectionId = [allFoldersID objectAtIndex: j]; + realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; + realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; + collection = [self collectionFromId: realCollectionId type: folderType]; + + switch (folderType) + { + case ActiveSyncContactFolder: + folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"vcard/%@", [collection nameInContainer]]]; + break; + case ActiveSyncEventFolder: + folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"vevent/%@", [collection nameInContainer]]]; + break; + case ActiveSyncTaskFolder: + folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"vtodo/%@", [collection nameInContainer]]]; + break; + default: + folderMetadata = [self _folderMetadataForKey: [collection nameInContainer]]; + } + + syncKey = [folderMetadata objectForKey: @"SyncKey"]; + + if (![syncKey isEqualToString: [collection davCollectionTag]]) + { + [foldersWithChanges addObject: collectionId]; + } + } + + if ([foldersWithChanges count]) + { + NSLog(@"Change detected, we push the content."); + status = 2; + break; + } + else + { + NSLog(@"Sleeping %d seconds while detecting changes...", internalInterval); + sleep(internalInterval); + } + } + // We generate our response s = [NSMutableString string]; [s appendString: @""]; [s appendString: @""]; [s appendString: @""]; [s appendFormat: @"%d", status]; + + if ([foldersWithChanges count]) + { + [s appendString: @""]; + + for (i = 0; i < [foldersWithChanges count]; i++) + { + // A bit tricky here because we must call stringByEscapingURL on mail folders, but not on GCS ones. + // We do the same thing in -processFolderSync + collectionId = [foldersWithChanges objectAtIndex: i]; + + if ([collectionId hasPrefix: @"mail/"]) + collectionId = [collectionId stringByEscapingURL]; + + [s appendFormat: @"%@", collectionId]; + } + + [s appendString: @""]; + } if (status == 5) { diff --git a/NEWS b/NEWS index b3350bcc5..7af43b7c2 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Enhancements - contacts photos are now synchronized using ActiveSync (#2807) - implemented the GetAttachment ActiveSync command (#2808) + - implemented the Ping ActiveSync command Bug fixes - better handling of empty "Flag" messages over ActiveSync (#2806)