diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 730936132..86a2e5c89 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -505,7 +505,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. softdelete_count = 0; - folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]]; + folderMetadata = [self _folderMetadataForKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; + dateCache = [folderMetadata objectForKey: @"DateCache"]; syncCache = [folderMetadata objectForKey: @"SyncCache"]; @@ -529,7 +530,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 nameInContainer]]; + [self _setFolderMetadata: folderMetadata forKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; more_available = YES; *theLastServerKey = theSyncKey; @@ -541,7 +542,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } [folderMetadata removeObjectForKey: @"MoreAvailable"]; - [self _setFolderMetadata: folderMetadata forKey: [theCollection nameInContainer]]; + [self _setFolderMetadata: folderMetadata forKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; } // @@ -695,23 +696,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } // If it's a new Sync operation, DateCache and SyncCache need to be deleted - // but GUID stored by folderSync shouldn't be touched - folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]]; + 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"]; } - // Check whether GUID in cache is equal to the GUID from imap - this is to avoid cache corruptions if a folder has been renamed and a new folder - // with the same name has been created but folderSync has not yet updated the cache - if (!([[theCollection nameInContainer] isEqualToString: - [NSString stringWithFormat: @"folder%@", [self globallyUniqueIDToIMAPFolderName: [folderMetadata objectForKey: @"GUID"] type: theFolderType]]])) - { - NSLog(@"GUID mismatch don't sync now!"); - return; - } - syncCache = [folderMetadata objectForKey: @"SyncCache"]; dateCache = [folderMetadata objectForKey: @"DateCache"]; @@ -859,7 +851,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } [self _setFolderMetadata: folderMetadata - forKey: [theCollection nameInContainer]]; + forKey: [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]]]; } // default: break; } // switch (folderType) ... @@ -958,7 +950,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. inBuffer: (NSMutableString *) theBuffer changeDetected: (BOOL *) changeDetected { - NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey; + NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *lastServerKey, *nameInCache; SOGoMicrosoftActiveSyncFolderType folderType; id collection, value; @@ -975,6 +967,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. collection = [self collectionFromId: realCollectionId type: folderType]; syncKey = davCollectionTag = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue]; + + if (collection == nil) + { + // 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: @""]; + + return; + } // We check for a window size, default to 100 if not specfied or out of bounds windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue]; @@ -1059,9 +1064,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { if (lastServerKey) davCollectionTag = lastServerKey; - else if (![[self _folderMetadataForKey: [collection nameInContainer]] objectForKey: @"MoreAvailable"]) - davCollectionTag = [collection davCollectionTag]; - + else + { + if (folderType == ActiveSyncMailFolder) + nameInCache = [[[collection mailAccountFolder] imapFolderGUIDs] objectForKey: [collection nameInContainer]]; + else + nameInCache = [collection nameInContainer]; + + if (![[self _folderMetadataForKey: nameInCache] objectForKey: @"MoreAvailable"]) + davCollectionTag = [collection davCollectionTag]; + } + *changeDetected = YES; } else diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 0d5287a8a..083829b22 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -36,6 +36,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import +#import +#import #import #import #import @@ -89,6 +91,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import #import +#import #import #import @@ -186,7 +189,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (theFilter) { - o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil]; + o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil]; [o setObjectType: ActiveSyncGlobalCacheObject]; [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; @@ -223,7 +226,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Get the GUID of the IMAP folder imapGUIDs = [accountFolder imapFolderGUIDs]; - return [[imapGUIDs allKeysForObject: theIdToTranslate] objectAtIndex: 0]; + //return [[imapGUIDs allKeysForObject: theIdToTranslate] objectAtIndex: 0]; + return [[[imapGUIDs allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ; } return theIdToTranslate; @@ -244,13 +248,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { case ActiveSyncContactFolder: { - collection = [[context activeUser] personalContactsFolderInContext: context]; + collection = [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] lookupName: theCollectionId inContext: context acquire: NO]; + if (!collection || ([collection isKindOfClass: [NSException class]])) + collection = nil; + } break; case ActiveSyncEventFolder: case ActiveSyncTaskFolder: { - collection = [[context activeUser] personalCalendarFolderInContext: context]; + collection = [[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] lookupName: theCollectionId inContext: context acquire: NO]; + if (!collection || ([collection isKindOfClass: [NSException class]])) + collection = nil; } break; case ActiveSyncMailFolder: @@ -267,6 +276,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId] inContext: context acquire: NO]; + if (![(SOGoMailFolder *)collection exists]) + collection = nil; } } @@ -335,27 +346,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. NSString *key; nameInContainer = [newFolder nameInContainer]; - - // We strip the "folder" prefix - nameInContainer = [nameInContainer substringFromIndex: 6]; - // save new guid into cache accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - - // update GUID in cache imapGUIDs = [accountFolder imapFolderGUIDs]; + nameInContainer =[imapGUIDs objectForKey: nameInContainer]; - key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], nameInContainer ]; + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], nameInContainer ]; o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; [o setObjectType: ActiveSyncFolderCacheObject]; [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; - nameInContainer =[imapGUIDs objectForKey: nameInContainer]; - - [[o properties ] setObject: nameInContainer forKey: @"GUID"]; + [[o properties ] setObject: [[newFolder nameInContainer] substringFromIndex: 6] forKey: @"displayName"]; [o save]; - nameInContainer = [[NSString stringWithFormat: @"mail/%@", nameInContainer] stringByEscapingURL]; + nameInContainer = [[NSString stringWithFormat: @"mail/%@", [nameInContainer substringFromIndex: 6]] stringByEscapingURL]; } else { @@ -369,7 +373,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. case 15: { SOGoAppointmentFolders *appointmentFolders; - + SOGoCacheGCSObject *o; + NSString *key; + + nameInContainer = nil; + appointmentFolders = [userFolder privateCalendars: @"Calendar" inContext: context]; [appointmentFolders newFolderWithName: displayName nameInContainer: &nameInContainer]; @@ -377,16 +385,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nameInContainer = [NSString stringWithFormat: @"vevent/%@", nameInContainer]; else nameInContainer = [NSString stringWithFormat: @"vtodo/%@", nameInContainer]; + + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], nameInContainer ]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + [[o properties ] setObject: displayName forKey: @"displayName"]; + [o save]; } break; case 14: { SOGoContactFolders *contactFolders; + SOGoCacheGCSObject *o; + NSString *key; + + nameInContainer = nil; contactFolders = [userFolder privateContacts: @"Contacts" inContext: context]; [contactFolders newFolderWithName: displayName nameInContainer: &nameInContainer]; nameInContainer = [NSString stringWithFormat: @"vcard/%@", nameInContainer]; + + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], nameInContainer ]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + [[o properties ] setObject: displayName forKey: @"displayName"]; + [o save]; } break; default: @@ -428,66 +456,87 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. inResponse: (WOResponse *) theResponse { SOGoMailAccounts *accountsFolder; - SOGoMailFolder *folderToDelete; SOGoUserFolder *userFolder; - id currentFolder; - NSException *error; - NSString *serverId; - + id currentFolder, folderToDelete; + NSString *serverId, *nameInCache, *key, *syncKey; + SOGoCacheGCSObject *o; + NSMutableString *s; + NSData *d; + SOGoMicrosoftActiveSyncFolderType folderType; - + serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType]; + nameInCache = serverId; serverId = [self globallyUniqueIDToIMAPFolderName: serverId type: folderType]; - userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - folderToDelete = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", serverId] - inContext: context - acquire: NO]; - - error = [folderToDelete delete]; - - if (!error) + switch (folderType) { - NSString *syncKey, *key; - SOGoCacheGCSObject *o; - NSMutableString *s; - NSData *d; + case ActiveSyncMailFolder: + { + nameInCache = [NSString stringWithFormat: @"folder%@", nameInCache]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + folderToDelete = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", serverId] + inContext: context + acquire: NO]; + } + break; + case ActiveSyncEventFolder: + case ActiveSyncTaskFolder: + { + SOGoAppointmentFolders *appointmentFolders; - // - // We destroy the cache object - // - key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], [folderToDelete nameInContainer]]; - o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; - [o setTableUrl: [self folderTableURL]]; - [o destroy]; - - // - // We update the FolderSync's synckey - // - syncKey = [[NSProcessInfo processInfo] globallyUniqueString]; - - [self _setFolderSyncKey: syncKey]; - - s = [NSMutableString string]; - [s appendString: @""]; - [s appendString: @""]; - [s appendString: @""]; - [s appendFormat: @"%d", 1]; - [s appendFormat: @"%@", syncKey]; - [s appendString: @""]; - - d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; - - [theResponse setContent: d]; - } - else - { - [theResponse setStatus: 500]; - [theResponse appendContentString: @"Unable to delete folder."]; + if (folderType == ActiveSyncEventFolder) + nameInCache = [NSString stringWithFormat: @"vevent/%@", serverId]; + else + nameInCache = [NSString stringWithFormat: @"vtodo/%@", serverId]; + + appointmentFolders = [userFolder privateCalendars: @"Calendar" inContext: context]; + + folderToDelete = [appointmentFolders lookupName: [NSString stringWithFormat: @"%@", serverId] + inContext: context + acquire: NO]; + } + break; + default: + { + [theResponse setStatus: 500]; + [theResponse appendContentString: @"Unsupported folder type during creation."]; + return; + } } + + // FIXME: we should handle exception here + [folderToDelete delete]; + + // + // We destroy the cache object + // + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], nameInCache]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setTableUrl: [self folderTableURL]]; + [o destroy]; + + + // + // We update the FolderSync's synckey + // + syncKey = [[NSProcessInfo processInfo] globallyUniqueString]; + + [self _setFolderSyncKey: syncKey]; + + s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%d", 1]; + [s appendFormat: @"%@", syncKey]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + [theResponse setContent: d]; } // @@ -496,89 +545,127 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (void) processFolderUpdate: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - NSString *serverId, *parentId, *displayName; - SOGoMailAccounts *accountsFolder; + NSString *serverId, *parentId, *displayName, *newName, *nameInCache, *syncKey, *key; SOGoUserFolder *userFolder; - SOGoMailFolder *folderToUpdate; + SOGoCacheGCSObject *o; + NSMutableString *s; id currentFolder; - NSException *error; + NSData *d; SOGoMicrosoftActiveSyncFolderType folderType; - int status; serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType]; + + nameInCache = [NSString stringWithFormat: @"folder%@", serverId]; + serverId = [self globallyUniqueIDToIMAPFolderName: serverId type: folderType]; parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue]; displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue]; userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + + + switch (folderType) + { + case ActiveSyncMailFolder: + { + SOGoMailAccounts *accountsFolder; + SOGoMailFolder *folderToUpdate; + + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - folderToUpdate = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", serverId] - inContext: context - acquire: NO]; + folderToUpdate = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", serverId] + inContext: context + acquire: NO]; - // If parent is 0 or displayname is not changed it is either a rename of a folder in 0 or a move to 0 - if ([parentId isEqualToString: @"0"] || - ([serverId hasSuffix: [NSString stringWithFormat: @"/%@", displayName]] && [parentId isEqualToString: @"0"])) - { - error = [folderToUpdate renameTo: [NSString stringWithFormat: @"/%@", [displayName stringByEncodingImap4FolderName]]]; - } - else - { - parentId = [self globallyUniqueIDToIMAPFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type: folderType]; - error = [folderToUpdate renameTo: [NSString stringWithFormat: @"%@/%@", [parentId stringByEncodingImap4FolderName], - [displayName stringByEncodingImap4FolderName]]]; + // If parent is 0 or displayname is not changed it is either a rename of a folder in 0 or a move to 0 + if ([parentId isEqualToString: @"0"] || + ([serverId hasSuffix: [NSString stringWithFormat: @"/%@", displayName]] && [parentId isEqualToString: @"0"])) + { + newName = [NSString stringWithFormat: @"%@", [displayName stringByEncodingImap4FolderName]]; + + // FIXME: handle exception here + [folderToUpdate renameTo: [NSString stringWithFormat: @"/%@", [displayName stringByEncodingImap4FolderName]]]; + } + else + { + parentId = [self globallyUniqueIDToIMAPFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type: folderType]; + newName = [NSString stringWithFormat: @"%@/%@", [parentId stringByEncodingImap4FolderName], [displayName stringByEncodingImap4FolderName]]; + + // FIXME: handle exception here + [folderToUpdate renameTo: newName]; + } + + + // + // We update our cache + // + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], nameInCache]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + [[o properties ] setObject: newName forKey: @"displayName"]; + [o save]; + } + break; + case ActiveSyncEventFolder: + case ActiveSyncTaskFolder: + { + SOGoAppointmentFolders *appointmentFolders; + SOGoAppointmentFolder *folderToUpdate; + NSString *nameInCache; + + appointmentFolders = [userFolder privateCalendars: @"Calendar" inContext: context]; + + folderToUpdate = [appointmentFolders lookupName: [NSString stringWithFormat: @"%@", serverId] + inContext: context + acquire: NO]; + + // update the cache anyway regardless of any error; if the rename fails next folderSync will to the cleanup + [folderToUpdate renameTo: [NSString stringWithFormat: @"%@", [displayName stringByEncodingImap4FolderName]]]; + + if (folderType == ActiveSyncEventFolder) + nameInCache = [NSString stringWithFormat: @"vevent/%@", serverId]; + else + nameInCache = [NSString stringWithFormat: @"vtodo/%@",serverId]; + + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], nameInCache ]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + [[o properties ] setObject: displayName forKey: @"displayName"]; + [o save]; + } + break; + default: + { + [theResponse setStatus: 500]; + [theResponse appendContentString: @"Unsupported folder type during creation."]; + return; + } } - // Handle new name exist - if (!error) - { - NSString *syncKey, *key; - SOGoCacheGCSObject *o; - NSMutableString *s; - NSData *d; - - // - // We update our cache - // - key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], serverId]; - o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; - [o setTableUrl: [self folderTableURL]]; - [o reloadIfNeeded]; - - key = [NSString stringWithFormat: @"/%@+%@", [context objectForKey: @"DeviceId"], [folderToUpdate nameInContainer]]; - [o changePathTo: key]; + // + // We update the FolderSync's synckey + // + syncKey = [[NSProcessInfo processInfo] globallyUniqueString]; - // - // We update the FolderSync's synckey - // - syncKey = [[NSProcessInfo processInfo] globallyUniqueString]; + [self _setFolderSyncKey: syncKey]; - // See http://msdn.microsoft.com/en-us/library/gg675615(v=exchg.80).aspx - // we return '9' - we force a FolderSync - status = 1; - - [self _setFolderSyncKey: syncKey]; - - s = [NSMutableString string]; - [s appendString: @""]; - [s appendString: @""]; - [s appendString: @""]; - [s appendFormat: @"%d", status]; - [s appendFormat: @"%@", syncKey]; - [s appendString: @""]; + s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendFormat: @"%d", 1]; + [s appendFormat: @"%@", syncKey]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; - d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; - - [theResponse setContent: d]; - } - else - { - [theResponse setStatus: 500]; - [theResponse appendContentString: @"Unable to update folder."]; - } + [theResponse setContent: d]; } @@ -592,21 +679,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (void) processFolderSync: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - - NSMutableDictionary *metadata; - NSMutableString *s; - NSString *syncKey; + NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType; + NSDictionary *folderMetadata, *imapGUIDs; + NSArray *allFoldersMetadata, *allKeys; + NSMutableDictionary *cachedGUIDs, *metadata; + SOGoMailAccounts *accountsFolder; + SOGoMailAccount *accountFolder; + NSMutableString *s, *commands; + SOGoUserFolder *userFolder; + NSMutableArray *folders; + SoSecurityManager *sm; + SOGoCacheGCSObject *o; + id currentFolder; NSData *d; - - BOOL first_sync; - int status; + int status, command_count, i, type, fi, count; + + BOOL first_sync; + + sm = [SoSecurityManager sharedSecurityManager]; metadata = [self _globalMetadataForDevice]; syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue]; s = [NSMutableString string]; first_sync = NO; status = 1; + command_count = 0; + commands = [NSMutableString string]; + + [s appendString: @""]; + [s appendString: @""]; if ([syncKey isEqualToString: @"0"]) { @@ -616,215 +718,296 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. else if (![syncKey isEqualToString: [metadata objectForKey: @"FolderSyncKey"]]) { // Synchronization key mismatch or invalid synchronization key - status = 9; + //NSLog(@"FolderSync syncKey mismatch %@ <> %@", syncKey, metadata); + [s appendFormat: @"9"]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + [theResponse setContent: d]; + return; } - [self _setFolderSyncKey: syncKey]; + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - [s appendString: @""]; - [s appendString: @""]; - [s appendFormat: @"%d%@", status, syncKey]; - - if (status == 1) - { - SOGoMailAccounts *accountsFolder; - SOGoMailAccount *accountFolder; - SOGoUserFolder *userFolder; - id currentFolder; - - NSString *key, *cKey, *nkey, *name, *serverId, *parentId; - NSDictionary *folderMetadata, *imapGUIDs; - NSArray *allFoldersMetadata, *allKeys; - NSMutableDictionary *cachedGUIDs; - NSMutableString *commands; - SOGoCacheGCSObject *o; - - int i, type, command_count; - - userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - - allFoldersMetadata = [accountFolder allFoldersMetadata]; + allFoldersMetadata = [accountFolder allFoldersMetadata]; - // Get GUIDs of folder (IMAP) - // e.g. {INBOX = "sogo_73c_192bd57b_d8" - imapGUIDs = [accountFolder imapFolderGUIDs]; + // Get GUIDs of folder (IMAP) + // e.g. {folderINBOX = folder6b93c528176f1151c7260000aef6df92} + imapGUIDs = [accountFolder imapFolderGUIDs]; - cachedGUIDs = [NSMutableDictionary dictionary]; + cachedGUIDs = [NSMutableDictionary dictionary]; - // No need to read cached folder infos during first sync. Otherwise, pull it from the database. - // e.g. {"sogo_73c_192bd57b_d8" = INBOX} - guid = foldername for easy reverse lookup with imapGUIDs - if (!first_sync) - { - NSArray *foldersInCache; - NSString *folderName; + // No need to read cached folder infos during first sync. Otherwise, pull it from the database. + // e.g. {folder6b93c528176f1151c7260000aef6df92 = folderINBOX} - guid = foldername for easy reverse lookup with imapGUIDs + if (!first_sync) + { + NSArray *foldersInCache; - // get the list of folder stored in cache - key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], @"0"]; - o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; - [o setObjectType: ActiveSyncFolderCacheObject]; - [o setTableUrl: [self folderTableURL]]; - [o reloadIfNeeded]; - foldersInCache = [o folderList: [context objectForKey: @"DeviceId"] newerThanVersion: -1]; - - // get guids of folders stored in cache - for (i = 0; i < [foldersInCache count]; i++) - { - folderName = [foldersInCache objectAtIndex: i]; - key = [folderName substringFromIndex: 1]; - - o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; - [o setObjectType: ActiveSyncFolderCacheObject]; - [o setTableUrl: [self folderTableURL]]; - [o reloadIfNeeded]; - - if ([[o properties ] objectForKey: @"GUID"]) - [cachedGUIDs setObject: [key substringFromIndex: [key rangeOfString: @"+"].location+7] - forKey: [[o properties] objectForKey: @"GUID"]]; - } - } + o = [SOGoCacheGCSObject objectWithName: @"0" inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: folderTableURL]; + + foldersInCache = [o cacheEntriesForDeviceId: [context objectForKey: @"DeviceId"] newerThanVersion: -1]; + + // get guids of folders stored in cache + for (i = 0; i < [foldersInCache count]; i++) + { + key = [[foldersInCache objectAtIndex: i] substringFromIndex: 1]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + + // When the GUID entry exists the name of the entry has to be changed to new name + if ([[o properties] objectForKey: @"GUID"]) + { + //NSLog(@"Old cacheEntry: %@ displayName: %@ GUID: %@", key, [[o properties] objectForKey: @"displayName"], [[o properties] objectForKey: @"GUID"]); + key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[o properties] objectForKey: @"GUID"]]; + //NSLog(@"New cacheEntry: %@", key); + [[o properties] removeObjectForKey: @"GUID"]; + [[o properties ] setObject: @"updateMe" forKey: @"displayName"]; + [o save]; + [o changePathTo: [NSString stringWithFormat: @"%@", key]]; + } + + // no dispalay Name + if (![[o properties] objectForKey: @"displayName"]) + continue; + + if ([key rangeOfString: @"+folder" options: NSCaseInsensitiveSearch].location != NSNotFound) + [cachedGUIDs setObject: [NSString stringWithFormat: @"folder%@", [[o properties] objectForKey: @"displayName"]] // e.g. CDB648DDBC5040F8AC90792383DBBBAA+folderINBOX + forKey: [key substringFromIndex: [key rangeOfString: @"+"].location+1]]; + else + [cachedGUIDs setObject: [key substringFromIndex: [key rangeOfString: @"+"].location+1] // e.g. CDB648DDBC5040F8AC90792383DBBBAA+vcard/personal + forKey: [key substringFromIndex: [key rangeOfString: @"+"].location+1]]; + } + } - // Handle folders that have been deleted over IMAP - command_count = 0; - commands = [NSMutableString string]; - allKeys = [cachedGUIDs allKeys]; + // Handle folders that have been deleted on server + allKeys = [cachedGUIDs allKeys]; - for (i = 0; i < [allKeys count]; i++) - { - cKey = [allKeys objectAtIndex: i]; + for (i = 0; i < [allKeys count]; i++) + { + cKey = [allKeys objectAtIndex: i]; - if (![imapGUIDs allKeysForObject: cKey]) - { - // Destroy folders cache content to avoid stale data if a new folder gets created with the same name - key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey: cKey]]; - o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; - [o setObjectType: ActiveSyncFolderCacheObject]; - [o setTableUrl: [self folderTableURL]]; - [o reloadIfNeeded]; - - // Only send a delete command if GUID is found - if ([[o properties] objectForKey: @"GUID"]) + // ignore invalid folder in cache caused by fs code bugs + if ([cKey isEqualToString:@"(null)"]) + continue; + + // if a cache entry is not found in imapGUIDs its either an imap which has been deleted or its an other folder type which can be checked via lookupName. + if (![imapGUIDs allKeysForObject: cKey]) + { + // Destroy folders cache content to avoid stale data if a new folder gets created with the same name + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], cKey]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + + if ([cKey hasPrefix: @"folder"]) + { + [commands appendFormat: @"%@", [[NSString stringWithFormat: @"mail/%@", [cKey substringFromIndex: 6]] stringByEscapingURL]] ; + command_count++; + + [o destroy]; + } + else + { + if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound || + [cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound) + folderType = @"Calendar"; + else + folderType = @"Contacts"; + + if ([ cKey rangeOfString: @"/"].location != NSNotFound) + currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO] + lookupName: [cKey substringFromIndex: [cKey rangeOfString: @"/"].location+1] inContext: context acquire: NO]; + + // remove the folder from device if it doesn't exists or it has not the proper permissions + if (!currentFolder || + [sm validatePermission: SoPerm_DeleteObjects + onObject: currentFolder + inContext: context] || + [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: currentFolder + inContext: context]) { - [commands appendFormat: @"%@", [[NSString stringWithFormat: @"mail/%@", [[o properties ] objectForKey: @"GUID"]] stringByEscapingURL] ]; + [commands appendFormat: @"%@", [cKey stringByEscapingURL] ]; command_count++; - } - - [o destroy]; - } - } - - // Handle addition and changes - for (i = 0; i < [allFoldersMetadata count]; i++) - { - folderMetadata = [allFoldersMetadata objectAtIndex: i]; - - // No GUID -> no sync - if (!([imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]])) - continue; - - serverId = [NSString stringWithFormat: @"mail/%@", [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]; - name = [folderMetadata objectForKey: @"displayName"]; - - if ([name hasPrefix: @"/"]) - name = [name substringFromIndex: 1]; - - if ([name hasSuffix: @"/"]) - name = [name substringToIndex: [name length]-1]; - - type = [[folderMetadata objectForKey: @"type"] activeSyncFolderType]; - parentId = @"0"; - - if ([folderMetadata objectForKey: @"parent"]) - { - parentId = [NSString stringWithFormat: @"mail/%@", [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]]; - name = [[name pathComponents] lastObject]; - } - - // Decide between add and change - if ([cachedGUIDs objectForKey: [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]) - { - // Search GUID to check name change in cache (diff between IMAP and cache) - if ((![[[folderMetadata objectForKey: @"path"] substringFromIndex: 1] isEqualToString: [imapGUIDs objectForKey: [cachedGUIDs objectForKey: - [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]])) - { - key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey: - [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]]; - nkey = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] ]; - - if (![key isEqualToString: nkey]) - { - [commands appendFormat: @"%@%@%d%@", - [serverId stringByEscapingURL], - [parentId stringByEscapingURL], - type, - [name activeSyncRepresentationInContext: context]]; - - - // Change path in cache - o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; - [o setObjectType: ActiveSyncFolderCacheObject]; - [o setTableUrl: [self folderTableURL]]; - [o reloadIfNeeded]; - [o changePathTo: [NSString stringWithFormat: @"/%@", nkey]]; // ?? why is '/' prefix needed - problem in changePathTo? - - command_count++; - } + [o destroy]; } } - else - { - [commands appendFormat: @"%@%@%d%@", + } + } + + // Handle addition and changes + for (i = 0; i < [allFoldersMetadata count]; i++) + { + folderMetadata = [allFoldersMetadata objectAtIndex: i]; + + nameInCache = [NSString stringWithFormat: @"folder%@", [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]; + serverId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: nameInCache] substringFromIndex: 6]]; + name = [folderMetadata objectForKey: @"displayName"]; + + if ([name hasPrefix: @"/"]) + name = [name substringFromIndex: 1]; + + if ([name hasSuffix: @"/"]) + name = [name substringToIndex: [name length]-1]; + + type = [[folderMetadata objectForKey: @"type"] activeSyncFolderType]; + parentId = @"0"; + + if ([folderMetadata objectForKey: @"parent"]) + { + parentId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: [NSString stringWithFormat: @"folder%@", [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]] substringFromIndex: 6]]; + name = [[name pathComponents] lastObject]; + } + + // Decide between add and change + if ([cachedGUIDs objectForKey: [imapGUIDs objectForKey: nameInCache]]) + { + // Search GUID to check name change in cache (diff between IMAP and cache) + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey: [imapGUIDs objectForKey: nameInCache ]]]; + nkey = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] ]; + + if (![key isEqualToString: nkey]) + { + [commands appendFormat: @"%@%@%@%d", + [serverId stringByEscapingURL], + [parentId stringByEscapingURL], + [name activeSyncRepresentationInContext: context], type]; + + // Change path in cache + o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], [imapGUIDs objectForKey: nameInCache ]] inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + + [[o properties ] setObject: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] forKey: @"displayName"]; + [o save]; + + command_count++; + } + } + else + { + [commands appendFormat: @"%@%@%@%d", [serverId stringByEscapingURL], [parentId stringByEscapingURL], - type, - [name activeSyncRepresentationInContext: context]]; + [name activeSyncRepresentationInContext: context], type]; - // Store folder's GUID in cache - key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]; - o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; - [o setObjectType: ActiveSyncFolderCacheObject]; - [o setTableUrl: [self folderTableURL]]; - [o reloadIfNeeded]; + // Store folder's displayName in cache + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], [imapGUIDs objectForKey: nameInCache ]]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; - [[o properties ] setObject: [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]] forKey: @"GUID"]; - [o save]; + [[o properties ] setObject: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] forKey: @"displayName"]; + [o save]; - command_count++; - } - } - - if (first_sync) - [s appendFormat: @"%d", command_count+3]; - else - [s appendFormat: @"%d", command_count]; - - if (command_count > 0) - [s appendFormat: @"%@", commands]; - - if (first_sync) - { - // We add the personal calendar - events - // FIXME: add all calendars - currentFolder = [[context activeUser] personalCalendarFolderInContext: context]; - name = [NSString stringWithFormat: @"vevent/%@", [currentFolder nameInContainer]]; - [s appendFormat: @"%@%@%d%@", name, @"0", 8, [[currentFolder displayName] activeSyncRepresentationInContext: context]]; - - // We add the personal calendar - tasks - // FIXME: add all calendars - currentFolder = [[context activeUser] personalCalendarFolderInContext: context]; - name = [NSString stringWithFormat: @"vtodo/%@", [currentFolder nameInContainer]]; - [s appendFormat: @"%@%@%d%@", name, @"0", 7, [[currentFolder displayName] activeSyncRepresentationInContext: context]]; - - // We add the personal address book - // FIXME: add all address books - currentFolder = [[context activeUser] personalContactsFolderInContext: context]; - name = [NSString stringWithFormat: @"vcard/%@", [currentFolder nameInContainer]]; - [s appendFormat: @"%@%@%d%@", name, @"0", 9, [[currentFolder displayName] activeSyncRepresentationInContext: context]]; - } + command_count++; + } } + + personalFolderName = [[[context activeUser] personalCalendarFolderInContext: context] nameInContainer]; + folders = [[[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] subFolders] mutableCopy]; + [folders addObjectsFromArray: [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] subFolders]]; + + // Inside this loop we remove all the folder without write/delete permissions + count = [folders count]-1; + for (; count >= 0; count--) + { + if ([sm validatePermission: SoPerm_DeleteObjects + onObject: [folders objectAtIndex: count] + inContext: context] || + [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: [folders objectAtIndex: count] + inContext: context]) + { + [folders removeObjectAtIndex: count]; + } + } + + count = [folders count]-1; + NSString *operation; + + for (fi = 0; fi <= count ; fi++) + { + if ([[folders objectAtIndex:fi] isKindOfClass: [SOGoAppointmentFolder class]]) + name = [NSString stringWithFormat: @"vevent/%@", [[folders objectAtIndex:fi] nameInContainer]]; + else + name = [NSString stringWithFormat: @"vcard/%@", [[folders objectAtIndex:fi] nameInContainer]]; + + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], name]; + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + + // Decide between add and change + if (![[o properties ] objectForKey: @"displayName"] || first_sync) + operation = @"Add"; + else if (![[[o properties ] objectForKey: @"displayName"] isEqualToString: [[folders objectAtIndex:fi] displayName]]) + operation = @"Update"; + else + operation = nil; + + if (operation) + { + if ([[folders objectAtIndex:fi] isKindOfClass: [SOGoAppointmentFolder class]]) + { + type = ([[[folders objectAtIndex:fi] nameInContainer] isEqualToString: personalFolderName] ? 8 : 13); + [commands appendFormat: @"<%@>%@%@%@%d", operation, + [name stringByEscapingURL], @"0", [[[folders objectAtIndex:fi] displayName] activeSyncRepresentationInContext: context], type, operation]; + + command_count++; + + [[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"]; + [o save]; + + name = [NSString stringWithFormat: @"vtodo/%@", [[folders objectAtIndex:fi] nameInContainer]]; + type = ([[[folders objectAtIndex:fi] nameInContainer] isEqualToString: personalFolderName] ? 7 : 15); + [commands appendFormat: @"<%@>%@%@%@%d", operation, + [name stringByEscapingURL], @"0", [[[folders objectAtIndex:fi] displayName] activeSyncRepresentationInContext: context], type, operation]; + + command_count++; + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], name]; + + o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; + [o setObjectType: ActiveSyncFolderCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + [[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"]; + [o save]; + } + else + { + type = ([[[folders objectAtIndex:fi] nameInContainer] isEqualToString: personalFolderName] ? 9 : 14); + [commands appendFormat: @"<%@>%@%@%@%d", operation, + [name stringByEscapingURL], @"0", [[[folders objectAtIndex:fi] displayName] activeSyncRepresentationInContext: context], type, operation]; + + command_count++; + + [[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"]; + [o save]; + } + } + } + - [s appendString: @""]; + // set a new syncKey if there are folder changes + if (command_count > 0) + { + syncKey = [[NSProcessInfo processInfo] globallyUniqueString]; + [self _setFolderSyncKey: syncKey]; + } + + + [s appendFormat: @"%d", status]; + [s appendFormat: @"%@%d%@", syncKey, command_count, commands]; d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; @@ -906,7 +1089,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (void) processGetItemEstimate: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - NSString *collectionId, *realCollectionId; + NSString *collectionId, *realCollectionId, *nameInCache; id currentCollection; NSMutableString *s; NSData *d; @@ -920,7 +1103,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue]; realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; + + if (folderType == ActiveSyncMailFolder) + nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId]; + else + nameInCache = collectionId; + realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; + currentCollection = [self collectionFromId: realCollectionId type: folderType]; // @@ -955,7 +1145,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. count = [uids count]; // Add the number of UIDs expected to "soft delete" - count += [self _softDeleteCountWithFilter: filter collectionId: realCollectionId]; + count += [self _softDeleteCountWithFilter: filter collectionId: nameInCache]; } else @@ -1211,6 +1401,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOGoMicrosoftActiveSyncFolderType srcFolderType, dstFolderType; id aMoveOperation; NSArray *moveOperations; + SoSecurityManager *sm; NSMutableString *s; NSData *d; int i; @@ -1233,8 +1424,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendString: @""]; - // FIXME - we should support moving events between calendars, for example, or - // or contacts between address books. if (srcFolderType == ActiveSyncMailFolder && dstFolderType == ActiveSyncMailFolder) { NGImap4Client *client; @@ -1309,13 +1498,66 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } else { - // Non-mail move operations - unsupported for now. - [s appendFormat: @"%d", 1]; + id srcCollection, dstCollection, srcSogoObject, dstSogoObject; + NSArray *elements; + NSString *newUID; + NSException *ex; + + unsigned int count, max; + + srcCollection = [self collectionFromId: srcFolderId type: srcFolderType]; + dstCollection = [self collectionFromId: dstFolderId type: srcFolderType]; + + srcSogoObject = [srcCollection lookupName: [srcMessageId sanitizedServerIdWithType: srcFolderType] + inContext: context + acquire: NO]; + + sm = [SoSecurityManager sharedSecurityManager]; + if (![sm validatePermission: SoPerm_DeleteObjects + onObject: srcCollection + inContext: context]) + { + if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: dstCollection + inContext: context]) + { + newUID = [srcSogoObject globallyUniqueObjectId]; + dstSogoObject = [[SOGoAppointmentObject alloc] initWithName: [newUID sanitizedServerIdWithType: srcFolderType] + inContainer: dstCollection]; + elements = [[srcSogoObject calendar: NO secure: NO] allObjects]; + max = [elements count]; + for (count = 0; count < max; count++) + [[elements objectAtIndex: count] setUid: newUID]; + + ex = [dstSogoObject saveCalendar: [srcSogoObject calendar: NO secure: NO]]; + if (!ex) + { + ex = [srcSogoObject delete]; + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%@", newUID]; + [s appendFormat: @"%d", 3]; + } + else + { + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%d", 1]; + } + } + else + { + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%d", 2]; + } + } + else + { + [s appendFormat: @"%@", srcMessageId]; + [s appendFormat: @"%d", 1]; + } } [s appendString: @""]; } - [s appendString: @""]; @@ -1421,7 +1663,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. allValues = [[accountFolder imapFolderGUIDs] allValues]; for (i = 0; i < [allValues count]; i++) - [allFoldersID addObject: [NSString stringWithFormat: @"mail/%@", [allValues objectAtIndex: i]]]; + [allFoldersID addObject: [NSString stringWithFormat: @"mail/%@", [[allValues objectAtIndex: i] substringFromIndex: 6]]]; // FIXME: handle multiple GCS collecitons @@ -1449,22 +1691,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. collectionId = [allFoldersID objectAtIndex: j]; realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; + + if (folderType == ActiveSyncMailFolder) + folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"folder%@", [[collectionId stringByUnescapingURL] substringFromIndex:5]]]; + else + folderMetadata = [self _folderMetadataForKey: [collectionId stringByUnescapingURL]]; + 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]]; - } + + // if collection doesn't exists skip it - next foldersync will do the cleanup + if (!collection) + continue; syncKey = [folderMetadata objectForKey: @"SyncKey"]; @@ -2142,7 +2379,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { d = [[theRequest content] wbxml2xml]; } - documentElement = nil; if (!d) diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index 5bc3ff2ae..ab6021f07 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -710,7 +710,8 @@ static NSString *inboxFolderName = @"INBOX"; } } - [folders setObject: guid forKey: [object substringFromIndex: 1]]; + [folders setObject: [NSString stringWithFormat: @"folder%@", guid] forKey: [NSString stringWithFormat: @"folder%@", [object substringFromIndex: 1]]]; + } return folders; diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.h b/SoObjects/SOGo/SOGoCacheGCSObject.h index 90f7d1ba1..b456c1775 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.h +++ b/SoObjects/SOGo/SOGoCacheGCSObject.h @@ -67,8 +67,8 @@ typedef enum { - (NSDictionary *) lookupRecord: (NSString *) path newerThanVersion: (NSInteger) startVersion; -- (NSArray *) folderList: (NSString *) deviceId - newerThanVersion: (NSInteger) startVersion; +- (NSArray *) cacheEntriesForDeviceId: (NSString *) deviceId + newerThanVersion: (NSInteger) startVersion; - (void) setObjectType: (SOGoCacheObjectType) newObjectType; - (SOGoCacheObjectType) objectType; /* message, fai, folder */ diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.m b/SoObjects/SOGo/SOGoCacheGCSObject.m index 0b3ba25e8..1f83f8ed2 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.m +++ b/SoObjects/SOGo/SOGoCacheGCSObject.m @@ -250,7 +250,7 @@ static EOAttribute *textColumn = nil; newParentPath = NULL; sql = [NSMutableString stringWithFormat: @"UPDATE %@" - @" SET c_path = '%@'", + @" SET c_path = '/%@'", [self tableName], newPath]; if (newParentPath) @@ -375,9 +375,8 @@ static EOAttribute *textColumn = nil; return record; } -// get a list of all folders -- (NSArray *) folderList: (NSString *) deviceId - newerThanVersion: (NSInteger) startVersion +- (NSArray *) cacheEntriesForDeviceId: (NSString *) deviceId + newerThanVersion: (NSInteger) startVersion { NSMutableArray *recordsOut; NSArray *records; @@ -392,16 +391,22 @@ static EOAttribute *textColumn = nil; tableName = [self tableName]; adaptor = [self tableChannelAdaptor]; - pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@+folder%", deviceId] + pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@", deviceId] forAttribute: textColumn]; /* query */ sql = [NSMutableString stringWithFormat: - @"SELECT * FROM %@ WHERE c_path LIKE %@ AND c_deleted <> 1", - tableName, pathValue]; + @"SELECT * FROM %@ WHERE c_type = %d AND c_deleted <> 1", tableName, objectType]; + if (startVersion > -1) [sql appendFormat: @" AND c_version > %d", startVersion]; + if (deviceId) { + pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@%", deviceId] + forAttribute: textColumn]; + [sql appendFormat: @" AND c_path like %@", pathValue]; + } + /* execution */ records = [self performSQLQuery: sql];