Added EAS folder merge support and fixed long GUID issue (fixes #3460)
Conflicts: ActiveSync/SOGoActiveSyncDispatcher+Sync.m
This commit is contained in:
parent
4c4d3e4c25
commit
16356aaf36
|
@ -96,6 +96,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Mailer/SOGoMailNamespace.h>
|
||||
|
||||
#include "iCalEvent+ActiveSync.h"
|
||||
#include "iCalToDo+ActiveSync.h"
|
||||
|
@ -161,7 +162,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
NSNumber *processIdentifier, *processIdentifierInCache;
|
||||
SOGoCacheGCSObject *o;
|
||||
NSDictionary *values;
|
||||
NSString *key;
|
||||
NSString *key, *pkey;
|
||||
NSArray *a;
|
||||
|
||||
if ([theFolderKey hasPrefix: @"folder"])
|
||||
key = [NSString stringWithFormat: @"SyncRequest+mail/%@", [theFolderKey substringFromIndex: 6]];
|
||||
|
@ -172,7 +174,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
processIdentifierInCache = [[self globalMetadataForDevice] objectForKey: key];
|
||||
|
||||
// Don't update the cache if another request is processing the same collection.
|
||||
if (!([processIdentifierInCache isEqual: processIdentifier]))
|
||||
// I case of a merged folder we have to check personal folder's lock.
|
||||
a = [key componentsSeparatedByString: @"/"];
|
||||
pkey = [NSString stringWithFormat: @"%@/personal", [a objectAtIndex:0]];
|
||||
|
||||
if (!([processIdentifierInCache isEqual: processIdentifier] || [[[self globalMetadataForDevice] objectForKey: pkey] isEqual: processIdentifier]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - We lost our lock - discard folder cache update %@ %@ <> %@", key, processIdentifierInCache, processIdentifier];
|
||||
|
@ -191,13 +197,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"UidCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"FolderPermissions"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
||||
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
||||
[[o properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||
[[o properties] removeObjectForKey: @"MergedFolder"];
|
||||
[[o properties] removeObjectForKey: @"CleanoutDate"];
|
||||
|
||||
[[o properties] addEntriesFromDictionary: values];
|
||||
[o save];
|
||||
|
@ -285,8 +296,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||
inBuffer: (NSMutableString *) theBuffer
|
||||
{
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *allValues;
|
||||
NSString *clientId, *serverId;
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *uidCache, *allValues;
|
||||
NSString *clientId, *serverId, *easId;
|
||||
NSArray *additions;
|
||||
|
||||
id anAddition, sogoObject, o;
|
||||
|
@ -364,23 +375,54 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
[sogoObject setIsNew: is_new];
|
||||
[sogoObject saveComponent: o];
|
||||
|
||||
// Everything is fine, lets generate our response
|
||||
[theBuffer appendString: @"<Add>"];
|
||||
[theBuffer appendFormat: @"<ClientId>%@</ClientId>", clientId];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
[theBuffer appendString: @"</Add>"];
|
||||
|
||||
// Update syncCache
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
uidCache = [folderMetadata objectForKey: @"UidCache"];
|
||||
|
||||
if (uidCache)
|
||||
{
|
||||
if (is_new && [serverId length] > 64)
|
||||
{
|
||||
easId = [theCollection globallyUniqueObjectId];
|
||||
[uidCache setObject: easId forKey: serverId];
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Generated new easId: %@ for serverId: %@", easId, serverId];
|
||||
}
|
||||
else
|
||||
{
|
||||
easId = [uidCache objectForKey: serverId];
|
||||
if (easId)
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Reuse easId: %@ for serverId: %@", easId, serverId];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Use original serverId: %@ %@", serverId, easId];
|
||||
|
||||
easId = serverId;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
easId = serverId;
|
||||
|
||||
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||
[dateCache setObject: [NSCalendarDate date] forKey: serverId];
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
// Everything is fine, lets generate our response
|
||||
[theBuffer appendString: @"<Add>"];
|
||||
[theBuffer appendFormat: @"<ClientId>%@</ClientId>", clientId];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", easId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
[theBuffer appendString: @"</Add>"];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -427,10 +469,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
inBuffer: (NSMutableString *) theBuffer
|
||||
{
|
||||
NSDictionary *allChanges;
|
||||
NSString *serverId;
|
||||
NSArray *changes;
|
||||
NSString *serverId, *easId, *origServerId, *mergedFolder;
|
||||
NSArray *changes, *a;
|
||||
id aChange, o, sogoObject;
|
||||
NSMutableDictionary *folderMetadata, *syncCache;
|
||||
NSMutableDictionary *folderMetadata, *syncCache, *uidCache;
|
||||
|
||||
int i;
|
||||
|
||||
|
@ -440,14 +482,50 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
uidCache = [folderMetadata objectForKey: @"UidCache"];
|
||||
|
||||
for (i = 0; i < [changes count]; i++)
|
||||
{
|
||||
aChange = [changes objectAtIndex: i];
|
||||
|
||||
serverId = [[(id)[aChange getElementsByTagName: @"ServerId"] lastObject] textValue];
|
||||
origServerId = [[(id)[aChange getElementsByTagName: @"ServerId"] lastObject] textValue];
|
||||
easId = origServerId;
|
||||
|
||||
a = [origServerId componentsSeparatedByString: @"{+}"];
|
||||
if ([a count] > 1)
|
||||
{
|
||||
easId = [a objectAtIndex: 1];
|
||||
mergedFolder = [a objectAtIndex: 0];
|
||||
|
||||
// Make sure that the change goes to the target folder and not to personal.
|
||||
if ([[theCollection nameInContainer] isEqualToString: @"personal"])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Change - Target folder %@, easId %@", mergedFolder, easId];
|
||||
|
||||
[self processSyncChangeCommand: theDocumentElement
|
||||
inCollection: [self collectionFromId: mergedFolder type: theFolderType]
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Change - Process change for folder %@ easId %@", [theCollection nameInContainer],easId];
|
||||
|
||||
allChanges = [[(id)[aChange getElementsByTagName: @"ApplicationData"] lastObject] applicationData];
|
||||
|
||||
if (uidCache && (serverId = [[uidCache allKeysForObject: easId] objectAtIndex: 0]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Found serverId: %@ for easId: %@", serverId, easId];
|
||||
|
||||
}
|
||||
else
|
||||
serverId = easId;
|
||||
|
||||
// Fetch the object and apply the changes
|
||||
sogoObject = [theCollection lookupName: [serverId sanitizedServerIdWithType: theFolderType]
|
||||
inContext: context
|
||||
|
@ -456,7 +534,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
// Object was removed inbetween sync/commands?
|
||||
if ([sogoObject isKindOfClass: [NSException class]])
|
||||
{
|
||||
// FIXME - return status == 8
|
||||
[theBuffer appendString: @"<Change>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", origServerId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 8];
|
||||
[theBuffer appendString: @"</Change>"];
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -512,7 +593,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
|
||||
[theBuffer appendString: @"<Change>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", origServerId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
[theBuffer appendString: @"</Change>"];
|
||||
}
|
||||
|
@ -551,8 +632,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
|
||||
id aDelete, sogoObject, value;
|
||||
NSArray *deletions;
|
||||
NSString *serverId;
|
||||
NSArray *deletions, *a;
|
||||
NSString *serverId, *easId, *origServerId, *mergedFolder;
|
||||
|
||||
BOOL deletesAsMoves, useTrash;
|
||||
int i;
|
||||
|
@ -561,6 +642,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
if ([deletions count])
|
||||
{
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *uidCache;
|
||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
uidCache = [folderMetadata objectForKey: @"UidCache"];
|
||||
|
||||
// From the documention, if DeletesAsMoves is missing, we must assume it's a YES.
|
||||
// See https://msdn.microsoft.com/en-us/library/gg675480(v=exchg.80).aspx for all details.
|
||||
value = [theDocumentElement getElementsByTagName: @"DeletesAsMoves"];
|
||||
|
@ -574,8 +662,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
aDelete = [deletions objectAtIndex: i];
|
||||
|
||||
serverId = [[(id)[aDelete getElementsByTagName: @"ServerId"] lastObject] textValue];
|
||||
origServerId = [[(id)[aDelete getElementsByTagName: @"ServerId"] lastObject] textValue];
|
||||
easId = origServerId;
|
||||
|
||||
a = [origServerId componentsSeparatedByString: @"{+}"];
|
||||
if ([a count] > 1)
|
||||
{
|
||||
easId = [a objectAtIndex: 1];
|
||||
mergedFolder = [a objectAtIndex: 0];
|
||||
|
||||
// Make sure that the delete goes to the target folder and not to personal.
|
||||
if ([[theCollection nameInContainer] isEqualToString: @"personal"])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Delete - Target folder %@, easId %@", mergedFolder, easId];
|
||||
|
||||
[self processSyncDeleteCommand: theDocumentElement
|
||||
inCollection: [self collectionFromId: mergedFolder type: theFolderType]
|
||||
withType: theFolderType
|
||||
inBuffer: theBuffer];
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Delete - Process delete for folder %@ easId %@", [theCollection nameInContainer],easId];
|
||||
|
||||
if (uidCache && (serverId = [[uidCache allKeysForObject: easId] objectAtIndex: 0]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Found serverId: %@ for easId: %@", serverId, easId];
|
||||
}
|
||||
else
|
||||
serverId = easId;
|
||||
|
||||
sogoObject = [theCollection lookupName: [serverId sanitizedServerIdWithType: theFolderType]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
@ -592,24 +713,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
}
|
||||
else
|
||||
[sogoObject delete];
|
||||
}
|
||||
|
||||
[theBuffer appendString: @"<Delete>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
[theBuffer appendString: @"</Delete>"];
|
||||
[syncCache removeObjectForKey: serverId];
|
||||
[dateCache removeObjectForKey: serverId];
|
||||
//[uidCache removeObjectForKey: serverId];
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
|
||||
// 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]];
|
||||
[theBuffer appendString: @"<Delete>"];
|
||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", origServerId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||
[theBuffer appendString: @"</Delete>"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -662,8 +776,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
inBuffer: (NSMutableString *) theBuffer
|
||||
lastServerKey: (NSString **) theLastServerKey
|
||||
defaultInterval: (unsigned int) theDefaultInterval
|
||||
mergeFolders: (BOOL) theMergeFolder
|
||||
{
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *uidCache;
|
||||
NSString *davCollectionTagToStore;
|
||||
NSAutoreleasePool *pool;
|
||||
NSMutableString *s;
|
||||
|
@ -681,6 +796,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
{
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
|
||||
if (theFolderType != ActiveSyncMailFolder)
|
||||
{
|
||||
[folderMetadata setObject: [NSCalendarDate date] forKey: @"CleanoutDate"];
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"UidCache"];
|
||||
}
|
||||
}
|
||||
else if ([folderMetadata objectForKey: @"SyncKey"] && !([theSyncKey isEqualToString: [folderMetadata objectForKey: @"SyncKey"]]))
|
||||
{
|
||||
|
@ -692,6 +812,32 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||
uidCache = [folderMetadata objectForKey: @"UidCache"];
|
||||
|
||||
if (!cleanup_needed && -[[folderMetadata objectForKey: @"CleanoutDate"] timeIntervalSinceNow] > 3600)
|
||||
{
|
||||
NSArray *allKeys;
|
||||
NSString *key;
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cleanout UidCache of folder %@", [theCollection nameInContainer]];
|
||||
|
||||
allKeys = [uidCache allKeys];
|
||||
for (i = 0; i < [allKeys count]; i++)
|
||||
{
|
||||
key = [allKeys objectAtIndex: i];
|
||||
if (![syncCache objectForKey:key] && ![syncCache objectForKey:key])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Delete UidCache entry %@", key];
|
||||
|
||||
[uidCache removeObjectForKey: key];
|
||||
}
|
||||
}
|
||||
|
||||
[folderMetadata setObject: [NSCalendarDate date] forKey: @"CleanoutDate"];
|
||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||
}
|
||||
|
||||
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
|
||||
(cleanup_needed ||
|
||||
|
@ -743,6 +889,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// Now we are save to remove the dateCache entry.
|
||||
[dateCache removeObjectForKey: key];
|
||||
//[uidCache removeObjectForKey: key];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -781,7 +928,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
case ActiveSyncTaskFolder:
|
||||
{
|
||||
id sogoObject, componentObject;
|
||||
NSString *uid, *component_name;
|
||||
NSString *uid, *easId, *component_name;
|
||||
NSDictionary *component;
|
||||
NSArray *allComponents;
|
||||
|
||||
|
@ -860,7 +1007,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
[syncCache removeObjectForKey: uid];
|
||||
[dateCache removeObjectForKey: uid];
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", uid];
|
||||
|
@ -901,13 +1048,49 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
}
|
||||
|
||||
uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
|
||||
|
||||
if (uidCache)
|
||||
{
|
||||
easId = [uidCache objectForKey: uid];
|
||||
|
||||
if (!easId)
|
||||
{
|
||||
if ([uid length] > 64)
|
||||
{
|
||||
easId = [theCollection globallyUniqueObjectId];
|
||||
[uidCache setObject: easId forKey: uid];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Generated new easId: %@ for serverId: %@", easId, uid];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Use original serverId: %@ %@", uid, easId];
|
||||
|
||||
easId = uid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Reuse easId: %@ for serverId: %@", easId, uid];
|
||||
}
|
||||
}
|
||||
else
|
||||
easId = uid;
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
if ([syncCache objectForKey: uid])
|
||||
{
|
||||
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
||||
|
||||
if (![[theCollection nameInContainer] isEqualToString: @"personal"] && theMergeFolder)
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@{+}%@</ServerId>", [theCollection nameInContainer], easId];
|
||||
else
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", easId];
|
||||
|
||||
[s appendString: @"</Delete>"];
|
||||
|
||||
[syncCache removeObjectForKey: uid];
|
||||
|
@ -960,7 +1143,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
else
|
||||
[s appendString: @"<Add xmlns=\"AirSync:\">"];
|
||||
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
|
||||
if (![[theCollection nameInContainer] isEqualToString: @"personal"] && theMergeFolder)
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@{+}%@</ServerId>", [theCollection nameInContainer], easId];
|
||||
else
|
||||
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", easId];
|
||||
|
||||
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||
|
||||
[s appendString: [componentObject activeSyncRepresentationInContext: context]];
|
||||
|
@ -1497,6 +1684,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
folderKey = [self _getNameInCache: collection withType: folderType];
|
||||
folderMetadata = [self _folderMetadataForKey: folderKey];
|
||||
|
||||
if (![folderMetadata count])
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - processSyncCollection: no folderMetadata found: %@", [collection nameInContainer]];
|
||||
|
||||
// We request foldersync to initialize the missing metadata.
|
||||
// We skip root-folders for shared mailboxes (shared and shared/jdoe@example.com).
|
||||
if (![collection isKindOfClass: [SOGoMailNamespace class]])
|
||||
{
|
||||
[theBuffer appendString: @"<Collection>"];
|
||||
[theBuffer appendFormat: @"<SyncKey>%@</SyncKey>", syncKey];
|
||||
[theBuffer appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
|
||||
[theBuffer appendFormat: @"<Status>%d</Status>", 12];
|
||||
[theBuffer appendString: @"</Collection>"];
|
||||
|
||||
*changeDetected = YES;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
|
@ -1677,11 +1885,215 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
||||
inBuffer: changeBuffer
|
||||
lastServerKey: &lastServerKey
|
||||
defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]];
|
||||
defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]
|
||||
mergeFolders: NO];
|
||||
}
|
||||
|
||||
folderMetadata = [self _folderMetadataForKey: folderKey];
|
||||
|
||||
// We only check for changes in other folders if we got nothing back from personal folder and
|
||||
// MergedFolder is set (sogo-tool manage-eas mergeVCard).
|
||||
if (getChanges && !first_sync &&
|
||||
([[collection nameInContainer] isEqualToString: @"personal"]) &&
|
||||
([folderMetadata objectForKey: @"MergedFolder"]))
|
||||
{
|
||||
NSArray *unsortedArray;
|
||||
NSMutableDictionary *mergedFolderMetadata, *mergedFoldersSyncKeys;
|
||||
NSString *mergedFolderSyncKey, *component_name, *mfLastServerKey;
|
||||
id mfCollection;
|
||||
BOOL cacheUpdateNeeded;
|
||||
|
||||
mfLastServerKey = nil;
|
||||
cacheUpdateNeeded=NO;
|
||||
|
||||
if (folderType == ActiveSyncContactFolder)
|
||||
component_name = @"vcard";
|
||||
else if (folderType == ActiveSyncEventFolder)
|
||||
component_name = @"vevent";
|
||||
else
|
||||
component_name = @"vtodo";
|
||||
|
||||
mergedFoldersSyncKeys = [folderMetadata objectForKey: @"MergedFoldersSyncKeys"];
|
||||
|
||||
// Initialize if MergedFoldersSyncKeys doesn't exists.
|
||||
// We user MergedFoldersSyncKeys to store the SyncKey for merged folders:
|
||||
// MergedFoldersSyncKeys = { 1447166580 = {"vcard/34B2-543AB980-D-DB9ECF0" = 1447157519; "vcard/5DE4-5640BC80-3-37E3EE00" = 1447166074; }
|
||||
// 1447166638 = {"vcard/34B2-543AB980-D-DB9ECF0" = 1447157519; "vcard/5DE4-5640BC80-3-37E3EE00" = 1447166074; } }
|
||||
if (!mergedFoldersSyncKeys || [syncKey isEqualToString: @"-1"])
|
||||
{
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionaryWithObject: [NSMutableDictionary dictionary] forKey: syncKey]
|
||||
forKey: @"MergedFoldersSyncKeys"];
|
||||
mergedFoldersSyncKeys = [folderMetadata objectForKey: @"MergedFoldersSyncKeys"];
|
||||
|
||||
cacheUpdateNeeded=YES;
|
||||
}
|
||||
|
||||
// Copy the MergedFoldersSyncKeys entry. Later we update this entry with new SyncKeys if there are changes.
|
||||
if (![syncKey isEqualToString: [folderMetadata objectForKey: @"SyncKey"]])
|
||||
{
|
||||
[mergedFoldersSyncKeys setObject: [mergedFoldersSyncKeys objectForKey: syncKey] forKey: [folderMetadata objectForKey: @"SyncKey"]];
|
||||
cacheUpdateNeeded=YES;
|
||||
}
|
||||
|
||||
// We only keep 5 entries in MergedFoldersSyncKeys.
|
||||
unsortedArray = [mergedFoldersSyncKeys allKeys];
|
||||
if ([unsortedArray count] > 5)
|
||||
[mergedFoldersSyncKeys removeObjectForKey: [[unsortedArray sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:0]];
|
||||
|
||||
// Don't check for changes in other folders if personal folder has changes.
|
||||
if (![changeBuffer length])
|
||||
{
|
||||
NSArray *foldersInCache;
|
||||
SOGoCacheGCSObject *o;
|
||||
NSString *folderName, *realCollectionId;
|
||||
NSArray *a, *folderNames;
|
||||
SOGoMicrosoftActiveSyncFolderType mergedFolderType;
|
||||
|
||||
o = [SOGoCacheGCSObject objectWithName: @"0" inContainer: nil];
|
||||
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||
[o setTableUrl: folderTableURL];
|
||||
|
||||
foldersInCache = [o cacheEntriesForDeviceId: [context objectForKey: @"DeviceId"] newerThanVersion: -1];
|
||||
|
||||
// Add folders to MergedFoldersSyncKeys.
|
||||
for (i = 0; i < [foldersInCache count]; i++)
|
||||
{
|
||||
a = [[foldersInCache objectAtIndex: i] componentsSeparatedByString: @"+"];
|
||||
folderName = [a objectAtIndex: 1];
|
||||
|
||||
if (![folderName hasPrefix: component_name] || [folderName hasSuffix: @"/personal"])
|
||||
continue;
|
||||
|
||||
mergedFolderSyncKey = [[mergedFoldersSyncKeys objectForKey: [folderMetadata objectForKey: @"SyncKey"]] objectForKey: folderName ];
|
||||
|
||||
if (!mergedFolderSyncKey)
|
||||
{
|
||||
realCollectionId = [folderName realCollectionIdWithFolderType: &mergedFolderType];
|
||||
mfCollection = [self collectionFromId: realCollectionId type: mergedFolderType];
|
||||
|
||||
// Cache-entry still exists but folder doesn't exists or synchronize flag is not set.
|
||||
// We ignore the folder and wait for foldersync to do the cleanup.
|
||||
if (!(mfCollection && [mfCollection synchronize]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Folder %@ not found. Ignoring ...", folderName];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
mergedFolderMetadata = [self _folderMetadataForKey: folderName];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Add folder %@ for merging into personal folder", folderName];
|
||||
|
||||
// Initialize MergedFoldersSyncKeys entry. e.g. "vcard/34B2-543AB980-D-DB9ECF0" = -1
|
||||
[[mergedFoldersSyncKeys objectForKey: [folderMetadata objectForKey: @"SyncKey"]] setObject: @"-1" forKey: folderName];
|
||||
cacheUpdateNeeded = YES;
|
||||
|
||||
// Flag folder as a merged folder.
|
||||
if (![[mergedFolderMetadata objectForKey: @"MergedFolder"] isEqualToString: @"2"])
|
||||
{
|
||||
[mergedFolderMetadata setObject: @"1" forKey: @"MergedFolder"];
|
||||
[self _setFolderMetadata: mergedFolderMetadata forKey: folderName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check merged folders for changes.
|
||||
folderNames = [[mergedFoldersSyncKeys objectForKey: [folderMetadata objectForKey: @"SyncKey"]] allKeys];
|
||||
for (i = 0; i < [folderNames count]; i++)
|
||||
{
|
||||
folderName = [folderNames objectAtIndex: i];
|
||||
realCollectionId = [folderName realCollectionIdWithFolderType: &mergedFolderType];
|
||||
mfCollection = [self collectionFromId: realCollectionId type: mergedFolderType];
|
||||
|
||||
if (!(mfCollection && [mfCollection synchronize]))
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Folder %@ not found. Reset peronal folder to cleanup", folderName];
|
||||
|
||||
davCollectionTag = @"0";
|
||||
*changeDetected = YES;
|
||||
status = 3;
|
||||
|
||||
// Reset personal folder - Cleanup metadata of personal folder.
|
||||
[folderMetadata removeObjectForKey: @"SyncKey"];
|
||||
[folderMetadata removeObjectForKey: @"SyncCache"];
|
||||
[folderMetadata removeObjectForKey: @"DateCache"];
|
||||
[folderMetadata removeObjectForKey: @"UidCache"];
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
[folderMetadata removeObjectForKey: @"CleanoutDate"];
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: folderKey];
|
||||
break;
|
||||
}
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Merging folder %@ into personal folder", folderName];
|
||||
|
||||
mergedFolderSyncKey = [[mergedFoldersSyncKeys objectForKey: [folderMetadata objectForKey: @"SyncKey"]] objectForKey: folderName ];
|
||||
|
||||
[self processSyncGetChanges: theDocumentElement
|
||||
inCollection: mfCollection
|
||||
withWindowSize: windowSize
|
||||
withMaxSyncResponseSize: theMaxSyncResponseSize
|
||||
withSyncKey: mergedFolderSyncKey
|
||||
withFolderType: folderType
|
||||
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
||||
inBuffer: changeBuffer
|
||||
lastServerKey: &mfLastServerKey
|
||||
defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]
|
||||
mergeFolders: YES];
|
||||
|
||||
mergedFolderMetadata = [self _folderMetadataForKey: folderName];
|
||||
|
||||
if ([changeBuffer length] || [commandsBuffer length])
|
||||
{
|
||||
if (mfLastServerKey)
|
||||
mergedFolderSyncKey = mfLastServerKey;
|
||||
else
|
||||
{
|
||||
mergedFolderSyncKey = [mergedFolderMetadata objectForKey: @"SyncKey"];
|
||||
|
||||
if (!mergedFolderSyncKey)
|
||||
mergedFolderSyncKey = [mfCollection davCollectionTag];
|
||||
}
|
||||
|
||||
*changeDetected = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure that client is updated with the right syncKey. - This keeps vtodo's and vevent's syncKey in sync.
|
||||
syncKeyInCache = [mergedFolderMetadata objectForKey: @"SyncKey"];
|
||||
if (syncKeyInCache && !([mergedFolderSyncKey isEqualToString:syncKeyInCache]) && !first_sync)
|
||||
{
|
||||
mergedFolderSyncKey = syncKeyInCache;
|
||||
*changeDetected = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Update MergedFoldersSyncKeys with new SyncKey.
|
||||
[[mergedFoldersSyncKeys objectForKey: [folderMetadata objectForKey: @"SyncKey"]] setObject: mergedFolderSyncKey forKey: folderName];
|
||||
|
||||
if (*changeDetected)
|
||||
{
|
||||
// Set MoreAvailable to make sure we come back and check remaining folders.
|
||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||
|
||||
break;
|
||||
}
|
||||
else if ([folderMetadata objectForKey: @"MoreAvailable"])
|
||||
{
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
}
|
||||
} // end if
|
||||
|
||||
if (*changeDetected || cacheUpdateNeeded)
|
||||
[self _setFolderMetadata: folderMetadata forKey: folderKey];
|
||||
}
|
||||
|
||||
// If we got any changes or if we have applied any commands
|
||||
// let's regenerate our SyncKey based on the collection tag.
|
||||
if ([changeBuffer length] || [commandsBuffer length])
|
||||
|
|
|
@ -102,6 +102,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/WORequest+SOGo.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
|
||||
#import <Appointments/SOGoAppointmentFolder.h>
|
||||
#import <Appointments/SOGoAppointmentFolders.h>
|
||||
|
@ -119,6 +120,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#import <Mailer/SOGoMailObject.h>
|
||||
#import <Mailer/SOGoMailObject+Draft.h>
|
||||
#import <Mailer/NSString+Mail.h>
|
||||
#import <Mailer/SOGoSentFolder.h>
|
||||
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
@ -157,6 +159,9 @@ void handle_eas_terminate(int signum)
|
|||
|
||||
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
|
||||
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata forKey: (NSString *) theFolderKey;
|
||||
- (void) _setOrUnsetSyncRequest: (BOOL) set
|
||||
collections: (NSArray *) collections;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
@ -853,7 +858,7 @@ void handle_eas_terminate(int signum)
|
|||
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
|
||||
else
|
||||
[cachedGUIDs setObject: [key substringFromIndex: [key rangeOfString: @"+"].location+1] // e.g. CDB648DDBC5040F8AC90792383DBBBAA+vcard/personal
|
||||
forKey: [key substringFromIndex: [key rangeOfString: @"+"].location+1]];
|
||||
}
|
||||
|
@ -896,12 +901,26 @@ void handle_eas_terminate(int signum)
|
|||
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];
|
||||
else
|
||||
currentFolder = nil;
|
||||
|
||||
// We skip personal GCS folders - we always want to synchronize these
|
||||
if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
|
||||
[[currentFolder nameInContainer] isEqualToString: @"personal"])
|
||||
continue;
|
||||
|
||||
// We remove the folder from device but keep the cache entry,
|
||||
// otherwise the user would see duplication objects, one in personal folder and one in the merged folder.
|
||||
// MergedFolder=1 - Folder need to be removed from device.
|
||||
// MergedFolder=2 - Folder has been removed from device; i.e. <Delete> has been sent already.
|
||||
if ([[[o properties] objectForKey: @"MergedFolder"] isEqualToString: @"1"])
|
||||
{
|
||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
|
||||
command_count++;
|
||||
[[o properties] setObject: @"2" forKey: @"MergedFolder"];
|
||||
[o save];
|
||||
}
|
||||
|
||||
// Remove the folder from device if it doesn't exist, we don't want to sync it, or it doesn't have the proper permissions
|
||||
if (!currentFolder ||
|
||||
![currentFolder synchronize] ||
|
||||
|
@ -912,10 +931,16 @@ void handle_eas_terminate(int signum)
|
|||
onObject: currentFolder
|
||||
inContext: context])
|
||||
{
|
||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
|
||||
command_count++;
|
||||
// Don't send a delete when MergedFoler is set, we have done it above.
|
||||
// Windows Phones don't like when a <Delete>-folder is sent twice.
|
||||
if (![[[o properties] objectForKey: @"MergedFolder"] isEqualToString: @"2"])
|
||||
{
|
||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
|
||||
command_count++;
|
||||
}
|
||||
[o destroy];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1004,6 +1029,7 @@ void handle_eas_terminate(int signum)
|
|||
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"UidCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||
|
@ -1011,6 +1037,9 @@ void handle_eas_terminate(int signum)
|
|||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
||||
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
||||
[[o properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||
[[o properties] removeObjectForKey: @"MergedFolder"];
|
||||
[[o properties] removeObjectForKey: @"CleanoutDate"];
|
||||
|
||||
[o save];
|
||||
|
||||
|
@ -1069,7 +1098,7 @@ void handle_eas_terminate(int signum)
|
|||
operation = @"Add";
|
||||
else if (![[[o properties ] objectForKey: @"displayName"] isEqualToString: [[folders objectAtIndex:fi] displayName]])
|
||||
operation = @"Update";
|
||||
else
|
||||
else
|
||||
operation = nil;
|
||||
|
||||
if (operation)
|
||||
|
@ -1105,6 +1134,7 @@ void handle_eas_terminate(int signum)
|
|||
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"UidCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||
|
@ -1112,6 +1142,9 @@ void handle_eas_terminate(int signum)
|
|||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
||||
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
||||
[[o properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||
[[o properties] removeObjectForKey: @"MergedFolder"];
|
||||
[[o properties] removeObjectForKey: @"CleanoutDate"];
|
||||
}
|
||||
|
||||
[o save];
|
||||
|
@ -1132,6 +1165,7 @@ void handle_eas_terminate(int signum)
|
|||
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"UidCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||
|
@ -1139,6 +1173,9 @@ void handle_eas_terminate(int signum)
|
|||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
||||
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
||||
[[o properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||
[[o properties] removeObjectForKey: @"MergedFolder"];
|
||||
[[o properties] removeObjectForKey: @"CleanoutDate"];
|
||||
}
|
||||
|
||||
[o save];
|
||||
|
@ -1606,10 +1643,11 @@ void handle_eas_terminate(int signum)
|
|||
- (void) processMeetingResponse: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSString *realCollectionId, *requestId, *participationStatus, *calendarId;
|
||||
NSString *realCollectionId, *requestId, *easRequestId, *participationStatus, *calendarId;
|
||||
SOGoAppointmentObject *appointmentObject;
|
||||
SOGoMailObject *mailObject;
|
||||
NSMutableString *s;
|
||||
NSMutableDictionary *uidCache, *folderMetadata;
|
||||
NSMutableString *s, *nameInCache;
|
||||
NSData *d;
|
||||
|
||||
id collection;
|
||||
|
@ -1624,7 +1662,7 @@ void handle_eas_terminate(int signum)
|
|||
realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
userResponse = [[[(id)[theDocumentElement getElementsByTagName: @"UserResponse"] lastObject] textValue] intValue];
|
||||
requestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue];
|
||||
easRequestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue];
|
||||
appointmentObject = nil;
|
||||
calendarId = nil;
|
||||
|
||||
|
@ -1643,10 +1681,35 @@ void handle_eas_terminate(int signum)
|
|||
if (folderType == ActiveSyncEventFolder)
|
||||
{
|
||||
collection = [[context activeUser] personalCalendarFolderInContext: context];
|
||||
|
||||
nameInCache = [NSString stringWithFormat: @"vevent/%@", [collection nameInContainer]];
|
||||
folderMetadata = [self _folderMetadataForKey: nameInCache];
|
||||
|
||||
uidCache = [folderMetadata objectForKey: @"UidCache"];
|
||||
if (uidCache)
|
||||
{
|
||||
requestId = [[uidCache allKeysForObject: easRequestId] objectAtIndex: 0];
|
||||
|
||||
if (requestId)
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Found requestId: %@ for easRequestId: %@", requestId, easRequestId];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Use original requestId: %@", easRequestId];
|
||||
|
||||
requestId = easRequestId;
|
||||
}
|
||||
}
|
||||
else
|
||||
requestId = easRequestId;
|
||||
|
||||
appointmentObject = [collection lookupName: [requestId sanitizedServerIdWithType: ActiveSyncEventFolder]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
calendarId = requestId;
|
||||
calendarId = easRequestId;
|
||||
|
||||
// Object not found, let's fallback on the INBOX folder
|
||||
if ([appointmentObject isKindOfClass: [NSException class]])
|
||||
|
@ -1666,7 +1729,7 @@ void handle_eas_terminate(int signum)
|
|||
// We fetch the calendar information based on the email (requestId) in the user's INBOX (or elsewhere)
|
||||
//
|
||||
// FIXME: that won't work too well for external invitations...
|
||||
mailObject = [collection lookupName: requestId
|
||||
mailObject = [collection lookupName: easRequestId
|
||||
inContext: context
|
||||
acquire: 0];
|
||||
|
||||
|
@ -1681,6 +1744,12 @@ void handle_eas_terminate(int signum)
|
|||
|
||||
// Fetch the SOGoAppointmentObject
|
||||
collection = [[context activeUser] personalCalendarFolderInContext: context];
|
||||
nameInCache = [NSString stringWithFormat: @"vevent/%@", [collection nameInContainer]];
|
||||
|
||||
[self _setOrUnsetSyncRequest: YES collections: [NSArray arrayWithObject: nameInCache]];
|
||||
folderMetadata = [self _folderMetadataForKey: nameInCache];
|
||||
uidCache = [folderMetadata objectForKey: @"UidCache"];
|
||||
|
||||
appointmentObject = [collection lookupName: [NSString stringWithFormat: @"%@.ics", [event uid]]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
@ -1692,6 +1761,27 @@ void handle_eas_terminate(int signum)
|
|||
inContainer: collection];
|
||||
[appointmentObject saveComponent: event];
|
||||
}
|
||||
|
||||
if (uidCache && [calendarId length] > 64)
|
||||
{
|
||||
calendarId = [uidCache objectForKey: [event uid]];
|
||||
|
||||
if (![calendarId length])
|
||||
{
|
||||
calendarId = [collection globallyUniqueObjectId];
|
||||
[uidCache setObject: calendarId forKey: [event uid]];
|
||||
|
||||
[self _setFolderMetadata: folderMetadata forKey: nameInCache];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Generated new calendarId: %@ for serverId: %@", calendarId, [event uid]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Reuse calendarId: %@ for serverId: %@", calendarId, [event uid]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1715,7 +1805,7 @@ void handle_eas_terminate(int signum)
|
|||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<MeetingResponse xmlns=\"MeetingResponse:\">"];
|
||||
[s appendString: @"<Result>"];
|
||||
[s appendFormat: @"<RequestId>%@</RequestId>", requestId];
|
||||
[s appendFormat: @"<RequestId>%@</RequestId>", easRequestId];
|
||||
[s appendFormat: @"<CalendarId>%@</CalendarId>", calendarId];
|
||||
[s appendFormat: @"<Status>%d</Status>", status];
|
||||
[s appendString: @"</Result>"];
|
||||
|
@ -1731,7 +1821,7 @@ void handle_eas_terminate(int signum)
|
|||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<MeetingResponse xmlns=\"MeetingResponse:\">"];
|
||||
[s appendString: @"<Result>"];
|
||||
[s appendFormat: @"<RequestId>%@</RequestId>", requestId];
|
||||
[s appendFormat: @"<RequestId>%@</RequestId>", easRequestId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 2];
|
||||
[s appendString: @"</Result>"];
|
||||
[s appendString: @"</MeetingResponse>"];
|
||||
|
@ -1756,8 +1846,8 @@ void handle_eas_terminate(int signum)
|
|||
- (void) processMoveItems: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSString *srcMessageId, *srcFolderId, *dstFolderId, *dstMessageId, *nameInCache, *currentFolder;
|
||||
NSMutableDictionary *folderMetadata, *prevSuccessfulMoveItemsOps, *newSuccessfulMoveItemsOps;
|
||||
NSString *srcMessageId, *srcFolderId, *dstFolderId, *dstMessageId, *srcNameInCache, *dstNameInCache, *currentSrcFolder, *currentDstFolder;
|
||||
NSMutableDictionary *srcFolderMetadata, *dstFolderMetadata, *prevSuccessfulMoveItemsOps, *newSuccessfulMoveItemsOps;
|
||||
SOGoMicrosoftActiveSyncFolderType srcFolderType, dstFolderType;
|
||||
id <DOMElement> aMoveOperation;
|
||||
NSArray *moveOperations;
|
||||
|
@ -1766,12 +1856,17 @@ void handle_eas_terminate(int signum)
|
|||
NSData *d;
|
||||
int i;
|
||||
|
||||
currentSrcFolder = nil;
|
||||
currentDstFolder = nil;
|
||||
|
||||
moveOperations = (id)[theDocumentElement getElementsByTagName: @"Move"];
|
||||
|
||||
newSuccessfulMoveItemsOps = [NSMutableDictionary dictionary];
|
||||
prevSuccessfulMoveItemsOps = nil;
|
||||
folderMetadata = nil;
|
||||
currentFolder = nil;
|
||||
srcFolderMetadata = nil;
|
||||
dstFolderMetadata = nil;
|
||||
currentSrcFolder = nil;
|
||||
currentDstFolder = nil;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
|
@ -1787,18 +1882,33 @@ void handle_eas_terminate(int signum)
|
|||
srcFolderId = [[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] realCollectionIdWithFolderType: &srcFolderType];
|
||||
dstFolderId = [[[(id)[aMoveOperation getElementsByTagName: @"DstFldId"] lastObject] textValue] realCollectionIdWithFolderType: &dstFolderType];
|
||||
|
||||
[self _setOrUnsetSyncRequest: YES collections: [NSArray arrayWithObjects:
|
||||
[[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] stringByUnescapingURL],
|
||||
[[[(id)[aMoveOperation getElementsByTagName: @"DstFldId"] lastObject] textValue] stringByUnescapingURL], nil ]];
|
||||
|
||||
if (srcFolderType == ActiveSyncMailFolder)
|
||||
nameInCache = [NSString stringWithFormat: @"folder%@", [[[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] stringByUnescapingURL] substringFromIndex: 5]];
|
||||
srcNameInCache = [NSString stringWithFormat: @"folder%@", [[[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] stringByUnescapingURL] substringFromIndex: 5]];
|
||||
else
|
||||
nameInCache = [[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] stringByUnescapingURL];
|
||||
srcNameInCache = [[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] stringByUnescapingURL];
|
||||
|
||||
if (![nameInCache isEqualToString: currentFolder])
|
||||
if (![srcNameInCache isEqualToString: currentSrcFolder])
|
||||
{
|
||||
folderMetadata = [self _folderMetadataForKey: nameInCache];
|
||||
prevSuccessfulMoveItemsOps = [folderMetadata objectForKey: @"SuccessfulMoveItemsOps"];
|
||||
currentFolder = nameInCache;
|
||||
srcFolderMetadata = [self _folderMetadataForKey: srcNameInCache];
|
||||
prevSuccessfulMoveItemsOps = [srcFolderMetadata objectForKey: @"SuccessfulMoveItemsOps"];
|
||||
currentSrcFolder = srcNameInCache;
|
||||
}
|
||||
|
||||
|
||||
if (dstFolderType == ActiveSyncMailFolder)
|
||||
dstNameInCache = [NSString stringWithFormat: @"folder%@", [[[[(id)[aMoveOperation getElementsByTagName: @"DstFldId"] lastObject] textValue] stringByUnescapingURL] substringFromIndex: 5]];
|
||||
else
|
||||
dstNameInCache = [[[(id)[aMoveOperation getElementsByTagName: @"DstFldId"] lastObject] textValue] stringByUnescapingURL];
|
||||
|
||||
if (![dstNameInCache isEqualToString: currentDstFolder])
|
||||
{
|
||||
dstFolderMetadata = [self _folderMetadataForKey: dstNameInCache];
|
||||
currentDstFolder = dstNameInCache;
|
||||
}
|
||||
|
||||
[s appendString: @"<Response>"];
|
||||
|
||||
if (srcFolderType == ActiveSyncMailFolder && dstFolderType == ActiveSyncMailFolder)
|
||||
|
@ -1903,14 +2013,44 @@ void handle_eas_terminate(int signum)
|
|||
{
|
||||
id srcCollection, dstCollection, srcSogoObject, dstSogoObject;
|
||||
NSArray *elements;
|
||||
NSString *newUID;
|
||||
NSString *newUID, *origSrcMessageId;
|
||||
NSMutableDictionary *srcUidCache, *dstUidCache, *dstSyncCache;
|
||||
NSException *ex;
|
||||
|
||||
unsigned int count, max;
|
||||
|
||||
srcCollection = [self collectionFromId: srcFolderId type: srcFolderType];
|
||||
|
||||
if ([srcCollection isKindOfClass: [NSException class]])
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||
continue;
|
||||
}
|
||||
|
||||
dstCollection = [self collectionFromId: dstFolderId type: srcFolderType];
|
||||
|
||||
|
||||
if ([dstCollection isKindOfClass: [NSException class]])
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 2];
|
||||
continue;
|
||||
}
|
||||
|
||||
origSrcMessageId = srcMessageId;
|
||||
srcUidCache = [srcFolderMetadata objectForKey: @"UidCache"];
|
||||
dstUidCache = [dstFolderMetadata objectForKey: @"UidCache"];
|
||||
dstSyncCache = [dstFolderMetadata objectForKey: @"SyncCache"];
|
||||
|
||||
if (srcUidCache)
|
||||
{
|
||||
srcMessageId = [[srcUidCache allKeysForObject: origSrcMessageId] objectAtIndex: 0];
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Found serverId: %@ for easId: %@", srcMessageId, origSrcMessageId];
|
||||
}
|
||||
else
|
||||
srcMessageId = origSrcMessageId;
|
||||
|
||||
srcSogoObject = [srcCollection lookupName: [srcMessageId sanitizedServerIdWithType: srcFolderType]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
@ -1918,7 +2058,8 @@ void handle_eas_terminate(int signum)
|
|||
sm = [SoSecurityManager sharedSecurityManager];
|
||||
if (![sm validatePermission: SoPerm_DeleteObjects
|
||||
onObject: srcCollection
|
||||
inContext: context])
|
||||
inContext: context] &&
|
||||
![srcSogoObject isKindOfClass: [NSException class]])
|
||||
{
|
||||
if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
||||
onObject: dstCollection
|
||||
|
@ -1936,7 +2077,18 @@ void handle_eas_terminate(int signum)
|
|||
if (!ex)
|
||||
{
|
||||
ex = [srcSogoObject delete];
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
||||
|
||||
if (dstUidCache)
|
||||
{
|
||||
[dstUidCache setObject: newUID forKey: newUID];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Saved new easId: %@ for serverId: %@", newUID, newUID];
|
||||
}
|
||||
|
||||
[dstSyncCache setObject: [dstFolderMetadata objectForKey: @"SyncKey"] forKey: newUID];
|
||||
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<DstMsgId>%@</DstMsgId>", newUID];
|
||||
[s appendFormat: @"<Status>%d</Status>", 3];
|
||||
|
||||
|
@ -1948,36 +2100,45 @@ void handle_eas_terminate(int signum)
|
|||
if ([prevSuccessfulMoveItemsOps objectForKey: srcMessageId])
|
||||
{
|
||||
// Move failed but we can recover the dstMessageId from previous request
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<DstMsgId>%@</DstMsgId>", [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] ];
|
||||
[s appendFormat: @"<Status>%d</Status>", 3];
|
||||
[newSuccessfulMoveItemsOps setObject: [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] forKey: srcMessageId];
|
||||
|
||||
if (dstUidCache)
|
||||
{
|
||||
[dstUidCache setObject: newUID forKey: newUID];
|
||||
|
||||
if (debugOn)
|
||||
[self logWithFormat: @"EAS - Saved new easId: %@ for serverId: %@", newUID, newUID];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 2];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||
}
|
||||
}
|
||||
|
||||
[s appendString: @"</Response>"];
|
||||
|
||||
[folderMetadata removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[folderMetadata setObject: newSuccessfulMoveItemsOps forKey: @"SuccessfulMoveItemsOps"];
|
||||
[self _setFolderMetadata: folderMetadata forKey: nameInCache];
|
||||
[srcFolderMetadata removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[srcFolderMetadata setObject: newSuccessfulMoveItemsOps forKey: @"SuccessfulMoveItemsOps"];
|
||||
[self _setFolderMetadata: srcFolderMetadata forKey: srcNameInCache];
|
||||
[self _setFolderMetadata: dstFolderMetadata forKey: dstNameInCache];
|
||||
}
|
||||
|
||||
[s appendString: @"</MoveItems>"];
|
||||
|
@ -2155,7 +2316,7 @@ void handle_eas_terminate(int signum)
|
|||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"folder%@", [[collectionId stringByUnescapingURL] substringFromIndex:5]]];
|
||||
else
|
||||
else
|
||||
folderMetadata = [self _folderMetadataForKey: [collectionId stringByUnescapingURL]];
|
||||
|
||||
collection = [self collectionFromId: realCollectionId type: folderType];
|
||||
|
@ -3209,7 +3370,7 @@ void handle_eas_terminate(int signum)
|
|||
textPart = apart;
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
if ([[[part contentType] type] isEqualToString: @"text"] && [[[part contentType] subType] isEqualToString: @"html"])
|
||||
htmlPart = part;
|
||||
|
@ -3242,7 +3403,7 @@ void handle_eas_terminate(int signum)
|
|||
charset = [[htmlPart contentType] valueOfParameter: @"charset"];
|
||||
isHTML = YES;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
bodyFromSmartForward = [textPart body];
|
||||
charset = [[textPart contentType] valueOfParameter: @"charset"];
|
||||
|
@ -3559,7 +3720,7 @@ void handle_eas_terminate(int signum)
|
|||
if (([cmdName rangeOfString: @"ItemOperations" options: NSCaseInsensitiveSearch].location != NSNotFound) &&
|
||||
([[theRequest headerForKey: @"MS-ASAcceptMultiPart"] isEqualToString:@"T"] || [[theRequest uri] acceptsMultiPart]))
|
||||
[theResponse setHeader: @"application/vnd.ms-sync.multipart" forKey: @"Content-Type"];
|
||||
else
|
||||
else
|
||||
[theResponse setHeader: @"application/vnd.ms-sync.wbxml" forKey: @"Content-Type"];
|
||||
|
||||
[self performSelector: aSelector withObject: documentElement withObject: theResponse];
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <Foundation/NSProcessInfo.h>
|
||||
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/WOApplication.h>
|
||||
|
@ -52,6 +54,8 @@ typedef enum
|
|||
ManageEASListFolders = 2,
|
||||
ManageEASResetDevice = 3,
|
||||
ManageEASRestFolder = 4,
|
||||
ManageEASVCard = 5,
|
||||
ManageEASVEvent = 6,
|
||||
} SOGoManageEASCommand;
|
||||
|
||||
@interface SOGoToolManageEAS : SOGoTool
|
||||
|
@ -59,6 +63,8 @@ typedef enum
|
|||
|
||||
@implementation SOGoToolManageEAS
|
||||
|
||||
NSURL *folderTableURL;
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
}
|
||||
|
@ -73,15 +79,58 @@ typedef enum
|
|||
return @"manage EAS folders";
|
||||
}
|
||||
|
||||
- (void) _setOrUnsetSyncRequest: (BOOL) set
|
||||
collections: (NSArray *) collections
|
||||
{
|
||||
SOGoCacheGCSObject *o;
|
||||
NSNumber *processIdentifier;
|
||||
NSString *key;
|
||||
NSArray *a;
|
||||
int i;
|
||||
|
||||
processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
|
||||
|
||||
o = [SOGoCacheGCSObject objectWithName: [[[collections objectAtIndex: 0] componentsSeparatedByString: @"+"] objectAtIndex: 0] inContainer: nil useCache: NO];
|
||||
[o setObjectType: ActiveSyncGlobalCacheObject];
|
||||
[o setTableUrl: folderTableURL];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
if (set)
|
||||
{
|
||||
[[o properties] setObject: [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]] forKey: @"SyncRequest"];
|
||||
|
||||
for (i = 0; i < [collections count]; i++)
|
||||
{
|
||||
a = [[collections objectAtIndex: i] componentsSeparatedByString: @"+"];
|
||||
key = [NSString stringWithFormat: @"SyncRequest+%@", [a objectAtIndex: 1]];
|
||||
[[o properties] setObject: processIdentifier forKey: key];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[[o properties] removeObjectForKey: @"SyncRequest"];
|
||||
for (i = 0; i < [collections count]; i++)
|
||||
{
|
||||
a = [[collections objectAtIndex: i] componentsSeparatedByString: @"+"];
|
||||
key = [NSString stringWithFormat: @"SyncRequest+%@", [a objectAtIndex: 1]];
|
||||
[[o properties] removeObjectForKey: key];
|
||||
}
|
||||
}
|
||||
|
||||
[o save];
|
||||
}
|
||||
|
||||
- (void) usage
|
||||
{
|
||||
fprintf (stderr, "manage-eas listdevices|resetdevice|resetfolder user <devinceId | folderId> \n\n"
|
||||
fprintf (stderr, "manage-eas listdevices|resetdevice|resetfolder|mergevcard|mergevevent user <devinceId | folderId> <YES | NO>\n\n"
|
||||
" user the user of whom to reset the whole device or a single folder\n"
|
||||
" Examples:\n"
|
||||
" sogo-tool manage-eas listdevices janedoe\n"
|
||||
" sogo-tool manage-eas listfolders janedoe androidc316986417\n"
|
||||
" sogo-tool manage-eas resetdevice janedoe androidc316986417\n"
|
||||
" sogo-tool manage-eas resetfolder janedow androidc316986417+folderlala-dada-sasa_7a13_1a2386e0_e\n") ;
|
||||
" sogo-tool manage-eas resetfolder janedow androidc316986417+folderlala-dada-sasa_7a13_1a2386e0_e\n"
|
||||
" sogo-tool manage-eas mergevcard janedow androidc316986417 YES\n"
|
||||
" sogo-tool manage-eas mergevevent janedow androidc316986417 YES\n") ;
|
||||
}
|
||||
|
||||
|
||||
|
@ -97,6 +146,10 @@ typedef enum
|
|||
return ManageEASResetDevice;
|
||||
else if ([theString caseInsensitiveCompare: @"resetfolder"] == NSOrderedSame)
|
||||
return ManageEASRestFolder;
|
||||
else if ([theString caseInsensitiveCompare: @"mergevcard"] == NSOrderedSame)
|
||||
return ManageEASVCard;
|
||||
else if ([theString caseInsensitiveCompare: @"mergevevent"] == NSOrderedSame)
|
||||
return ManageEASVEvent;
|
||||
}
|
||||
|
||||
return ManageEASUnknown;
|
||||
|
@ -107,7 +160,6 @@ typedef enum
|
|||
NSString *urlString, *deviceId, *userId;
|
||||
NSMutableString *ocFSTableName;
|
||||
SOGoCacheGCSObject *oc, *foc;
|
||||
NSURL *folderTableURL;
|
||||
NSMutableArray *parts;
|
||||
NSArray *entries;
|
||||
id cacheEntry;
|
||||
|
@ -169,6 +221,7 @@ typedef enum
|
|||
}
|
||||
|
||||
rc = YES;
|
||||
|
||||
break;
|
||||
|
||||
case ManageEASListFolders:
|
||||
|
@ -195,6 +248,8 @@ typedef enum
|
|||
[foc reloadIfNeeded];
|
||||
|
||||
fprintf(stdout, " Folder Name: %s\n\n", [[[foc properties] objectForKey: @"displayName"] UTF8String]);
|
||||
if ([[foc properties] objectForKey: @"MergedFolder"])
|
||||
fprintf(stdout, " MergedFolder = YES\n\n");
|
||||
|
||||
if (verbose)
|
||||
fprintf(stdout, " metadata Name: %s\n\n", [[[foc properties] description] UTF8String]);
|
||||
|
@ -227,7 +282,7 @@ typedef enum
|
|||
|
||||
NSMutableString *sql;
|
||||
|
||||
sql = [NSMutableString stringWithFormat: @"DELETE FROM %@ WHERE c_path like '/%@'", [oc tableName], deviceId];
|
||||
sql = [NSMutableString stringWithFormat: @"DELETE FROM %@ WHERE c_path like '/%@%'", [oc tableName], deviceId];
|
||||
|
||||
[oc performBatchSQLQueries: [NSArray arrayWithObject: sql]];
|
||||
rc = YES;
|
||||
|
@ -260,16 +315,138 @@ typedef enum
|
|||
fprintf(stderr, "ERROR: Folder with ID \"%s\" not found\n", [deviceId UTF8String]);
|
||||
return rc;
|
||||
}
|
||||
[[oc properties] removeObjectForKey: @"SyncKey"];
|
||||
[[oc properties] removeObjectForKey: @"SyncCache"];
|
||||
[[oc properties] removeObjectForKey: @"DateCache"];
|
||||
[[oc properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[oc properties] removeObjectForKey: @"FirstIdInCache"];
|
||||
[[oc properties] removeObjectForKey: @"LastIdInCache"];
|
||||
|
||||
[oc save];
|
||||
if ((![deviceId hasSuffix: @"/personal"]) && [[oc properties] objectForKey: @"MergedFolder"])
|
||||
{
|
||||
fprintf(stderr, "ERROR: MergedFolder = true; only personal folder can be reset");
|
||||
return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _setOrUnsetSyncRequest: YES collections: [NSArray arrayWithObject: deviceId]];
|
||||
|
||||
[[oc properties] removeObjectForKey: @"SyncKey"];
|
||||
[[oc properties] removeObjectForKey: @"SyncCache"];
|
||||
[[oc properties] removeObjectForKey: @"DateCache"];
|
||||
[[oc properties] removeObjectForKey: @"UidCache"];
|
||||
[[oc properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[oc properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[oc properties] removeObjectForKey: @"SupportedElements"];
|
||||
[[oc properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[[oc properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
[[oc properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||
[[oc properties] removeObjectForKey: @"CleanoutDate"];
|
||||
|
||||
[oc save];
|
||||
rc = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "\nERROR: folderId not specified\n\n");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ManageEASVCard:
|
||||
case ManageEASVEvent:
|
||||
if (max > 3)
|
||||
{
|
||||
NSString *folderType;
|
||||
|
||||
if (cmd == ManageEASVCard)
|
||||
folderType = @"vcard";
|
||||
else
|
||||
folderType = @"vevent";
|
||||
|
||||
/* value specified on command line */
|
||||
deviceId = [sanitizedArguments objectAtIndex: 2];
|
||||
|
||||
oc = [SOGoCacheGCSObject objectWithName: @"0" inContainer: nil];
|
||||
[oc setObjectType: ActiveSyncFolderCacheObject];
|
||||
|
||||
[oc setTableUrl: folderTableURL];
|
||||
entries = [oc cacheEntriesForDeviceId: deviceId newerThanVersion: -1];
|
||||
|
||||
vtodo:
|
||||
|
||||
for (i = 0; i < [entries count]; i++)
|
||||
{
|
||||
cacheEntry = [entries objectAtIndex: i];
|
||||
|
||||
if ([[cacheEntry substringFromIndex: 1] hasPrefix: [NSString stringWithFormat: @"%@+%@/", deviceId, folderType]])
|
||||
{
|
||||
fprintf(stdout,"Folder Key: %s\n", [[cacheEntry substringFromIndex: 1] UTF8String]);
|
||||
|
||||
foc = [SOGoCacheGCSObject objectWithName: [cacheEntry substringFromIndex: 1] inContainer: nil];
|
||||
[foc setObjectType: ActiveSyncFolderCacheObject];
|
||||
[foc setTableUrl: folderTableURL];
|
||||
|
||||
[foc reloadIfNeeded];
|
||||
|
||||
if ([foc isNew])
|
||||
continue;
|
||||
|
||||
[self _setOrUnsetSyncRequest: YES collections: [NSArray arrayWithObject: [cacheEntry substringFromIndex: 1]]];
|
||||
|
||||
if (![[cacheEntry substringFromIndex: 1] hasPrefix: [NSString stringWithFormat: @"%@+%@/personal", deviceId, folderType]] &&
|
||||
[[sanitizedArguments objectAtIndex: 3] isEqualToString: @"NO"] &&
|
||||
[[[foc properties] objectForKey: @"MergedFolder"] isEqualToString: @"2"])
|
||||
{
|
||||
[foc destroy];
|
||||
continue;
|
||||
}
|
||||
else if ([[sanitizedArguments objectAtIndex: 3] isEqualToString: @"NO"])
|
||||
{
|
||||
[[foc properties] removeObjectForKey: @"SyncKey"];
|
||||
[[foc properties] removeObjectForKey: @"SyncCache"];
|
||||
[[foc properties] removeObjectForKey: @"DateCache"];
|
||||
[[foc properties] removeObjectForKey: @"UidCache"];
|
||||
[[foc properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[foc properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[foc properties] removeObjectForKey: @"SupportedElements"];
|
||||
[[foc properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[[foc properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
[[foc properties] removeObjectForKey: @"FirstIdInCache"];
|
||||
[[foc properties] removeObjectForKey: @"LastIdInCache"];
|
||||
[[foc properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||
[[foc properties] removeObjectForKey: @"CleanoutDate"];
|
||||
|
||||
[[foc properties] removeObjectForKey: @"MergedFolder"];
|
||||
}
|
||||
else if ([[sanitizedArguments objectAtIndex: 3] isEqualToString: @"YES"] && ![[foc properties] objectForKey: @"MergedFolder"])
|
||||
{
|
||||
if (![[cacheEntry substringFromIndex: 1] hasPrefix: [NSString stringWithFormat: @"%@+%@/personal", deviceId, folderType]])
|
||||
{
|
||||
[[foc properties] removeObjectForKey: @"SyncKey"];
|
||||
[[foc properties] removeObjectForKey: @"SyncCache"];
|
||||
[[foc properties] removeObjectForKey: @"DateCache"];
|
||||
[[foc properties] removeObjectForKey: @"UidCache"];
|
||||
[[foc properties] removeObjectForKey: @"MoreAvailable"];
|
||||
[[foc properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||
[[foc properties] removeObjectForKey: @"SupportedElements"];
|
||||
[[foc properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||
[[foc properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||
[[foc properties] removeObjectForKey: @"FirstIdInCache"];
|
||||
[[foc properties] removeObjectForKey: @"LastIdInCache"];
|
||||
[[foc properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||
[[foc properties] removeObjectForKey: @"CleanoutDate"];
|
||||
}
|
||||
|
||||
[[foc properties] setObject: @"1" forKey: @"MergedFolder"];
|
||||
}
|
||||
|
||||
[foc save];
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd == ManageEASVEvent && [folderType isEqualToString: @"vevent"])
|
||||
{
|
||||
folderType = @"vtodo";
|
||||
goto vtodo;
|
||||
}
|
||||
|
||||
rc = YES;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue