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/NSObject.h>
|
||||||
#import <Foundation/NSString.h>
|
#import <Foundation/NSString.h>
|
||||||
|
#import <Mailer/SOGoMailNamespace.h>
|
||||||
|
|
||||||
#include "iCalEvent+ActiveSync.h"
|
#include "iCalEvent+ActiveSync.h"
|
||||||
#include "iCalToDo+ActiveSync.h"
|
#include "iCalToDo+ActiveSync.h"
|
||||||
|
@ -161,7 +162,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
NSNumber *processIdentifier, *processIdentifierInCache;
|
NSNumber *processIdentifier, *processIdentifierInCache;
|
||||||
SOGoCacheGCSObject *o;
|
SOGoCacheGCSObject *o;
|
||||||
NSDictionary *values;
|
NSDictionary *values;
|
||||||
NSString *key;
|
NSString *key, *pkey;
|
||||||
|
NSArray *a;
|
||||||
|
|
||||||
if ([theFolderKey hasPrefix: @"folder"])
|
if ([theFolderKey hasPrefix: @"folder"])
|
||||||
key = [NSString stringWithFormat: @"SyncRequest+mail/%@", [theFolderKey substringFromIndex: 6]];
|
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];
|
processIdentifierInCache = [[self globalMetadataForDevice] objectForKey: key];
|
||||||
|
|
||||||
// Don't update the cache if another request is processing the same collection.
|
// 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)
|
if (debugOn)
|
||||||
[self logWithFormat: @"EAS - We lost our lock - discard folder cache update %@ %@ <> %@", key, processIdentifierInCache, processIdentifier];
|
[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: @"SyncKey"];
|
||||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||||
[[o properties] removeObjectForKey: @"DateCache"];
|
[[o properties] removeObjectForKey: @"DateCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"UidCache"];
|
||||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||||
|
[[o properties] removeObjectForKey: @"FolderPermissions"];
|
||||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||||
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||||
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
||||||
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||||
|
[[o properties] removeObjectForKey: @"MergedFolder"];
|
||||||
|
[[o properties] removeObjectForKey: @"CleanoutDate"];
|
||||||
|
|
||||||
[[o properties] addEntriesFromDictionary: values];
|
[[o properties] addEntriesFromDictionary: values];
|
||||||
[o save];
|
[o save];
|
||||||
|
@ -285,8 +296,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||||
inBuffer: (NSMutableString *) theBuffer
|
inBuffer: (NSMutableString *) theBuffer
|
||||||
{
|
{
|
||||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *allValues;
|
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *uidCache, *allValues;
|
||||||
NSString *clientId, *serverId;
|
NSString *clientId, *serverId, *easId;
|
||||||
NSArray *additions;
|
NSArray *additions;
|
||||||
|
|
||||||
id anAddition, sogoObject, o;
|
id anAddition, sogoObject, o;
|
||||||
|
@ -364,23 +375,54 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[sogoObject setIsNew: is_new];
|
[sogoObject setIsNew: is_new];
|
||||||
[sogoObject saveComponent: o];
|
[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
|
// Update syncCache
|
||||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||||
|
|
||||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
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];
|
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
|
||||||
[dateCache setObject: [NSCalendarDate date] forKey: serverId];
|
[dateCache setObject: [NSCalendarDate date] forKey: serverId];
|
||||||
|
|
||||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
[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
|
inBuffer: (NSMutableString *) theBuffer
|
||||||
{
|
{
|
||||||
NSDictionary *allChanges;
|
NSDictionary *allChanges;
|
||||||
NSString *serverId;
|
NSString *serverId, *easId, *origServerId, *mergedFolder;
|
||||||
NSArray *changes;
|
NSArray *changes, *a;
|
||||||
id aChange, o, sogoObject;
|
id aChange, o, sogoObject;
|
||||||
NSMutableDictionary *folderMetadata, *syncCache;
|
NSMutableDictionary *folderMetadata, *syncCache, *uidCache;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -440,14 +482,50 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
{
|
{
|
||||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||||
|
uidCache = [folderMetadata objectForKey: @"UidCache"];
|
||||||
|
|
||||||
for (i = 0; i < [changes count]; i++)
|
for (i = 0; i < [changes count]; i++)
|
||||||
{
|
{
|
||||||
aChange = [changes objectAtIndex: 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];
|
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
|
// Fetch the object and apply the changes
|
||||||
sogoObject = [theCollection lookupName: [serverId sanitizedServerIdWithType: theFolderType]
|
sogoObject = [theCollection lookupName: [serverId sanitizedServerIdWithType: theFolderType]
|
||||||
inContext: context
|
inContext: context
|
||||||
|
@ -456,7 +534,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
// Object was removed inbetween sync/commands?
|
// Object was removed inbetween sync/commands?
|
||||||
if ([sogoObject isKindOfClass: [NSException class]])
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +593,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
[theBuffer appendString: @"<Change>"];
|
[theBuffer appendString: @"<Change>"];
|
||||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", origServerId];
|
||||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||||
[theBuffer appendString: @"</Change>"];
|
[theBuffer appendString: @"</Change>"];
|
||||||
}
|
}
|
||||||
|
@ -551,8 +632,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
{
|
{
|
||||||
|
|
||||||
id aDelete, sogoObject, value;
|
id aDelete, sogoObject, value;
|
||||||
NSArray *deletions;
|
NSArray *deletions, *a;
|
||||||
NSString *serverId;
|
NSString *serverId, *easId, *origServerId, *mergedFolder;
|
||||||
|
|
||||||
BOOL deletesAsMoves, useTrash;
|
BOOL deletesAsMoves, useTrash;
|
||||||
int i;
|
int i;
|
||||||
|
@ -561,6 +642,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
if ([deletions count])
|
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.
|
// 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.
|
// See https://msdn.microsoft.com/en-us/library/gg675480(v=exchg.80).aspx for all details.
|
||||||
value = [theDocumentElement getElementsByTagName: @"DeletesAsMoves"];
|
value = [theDocumentElement getElementsByTagName: @"DeletesAsMoves"];
|
||||||
|
@ -574,8 +662,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
{
|
{
|
||||||
aDelete = [deletions objectAtIndex: i];
|
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]
|
sogoObject = [theCollection lookupName: [serverId sanitizedServerIdWithType: theFolderType]
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
|
@ -592,24 +713,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[sogoObject delete];
|
[sogoObject delete];
|
||||||
}
|
|
||||||
|
|
||||||
[theBuffer appendString: @"<Delete>"];
|
[syncCache removeObjectForKey: serverId];
|
||||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
[dateCache removeObjectForKey: serverId];
|
||||||
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
//[uidCache removeObjectForKey: serverId];
|
||||||
[theBuffer appendString: @"</Delete>"];
|
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
||||||
|
|
||||||
// update syncCache
|
[theBuffer appendString: @"<Delete>"];
|
||||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", origServerId];
|
||||||
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
|
||||||
|
[theBuffer appendString: @"</Delete>"];
|
||||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
}
|
||||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
|
||||||
|
|
||||||
[syncCache removeObjectForKey: serverId];
|
|
||||||
[dateCache removeObjectForKey: serverId];
|
|
||||||
|
|
||||||
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,8 +776,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
inBuffer: (NSMutableString *) theBuffer
|
inBuffer: (NSMutableString *) theBuffer
|
||||||
lastServerKey: (NSString **) theLastServerKey
|
lastServerKey: (NSString **) theLastServerKey
|
||||||
defaultInterval: (unsigned int) theDefaultInterval
|
defaultInterval: (unsigned int) theDefaultInterval
|
||||||
|
mergeFolders: (BOOL) theMergeFolder
|
||||||
{
|
{
|
||||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
NSMutableDictionary *folderMetadata, *dateCache, *syncCache, *uidCache;
|
||||||
NSString *davCollectionTagToStore;
|
NSString *davCollectionTagToStore;
|
||||||
NSAutoreleasePool *pool;
|
NSAutoreleasePool *pool;
|
||||||
NSMutableString *s;
|
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: @"SyncCache"];
|
||||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
|
[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"]]))
|
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"];
|
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
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) &&
|
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
|
||||||
(cleanup_needed ||
|
(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.
|
// Now we are save to remove the dateCache entry.
|
||||||
[dateCache removeObjectForKey: key];
|
[dateCache removeObjectForKey: key];
|
||||||
|
//[uidCache removeObjectForKey: key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +928,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
case ActiveSyncTaskFolder:
|
case ActiveSyncTaskFolder:
|
||||||
{
|
{
|
||||||
id sogoObject, componentObject;
|
id sogoObject, componentObject;
|
||||||
NSString *uid, *component_name;
|
NSString *uid, *easId, *component_name;
|
||||||
NSDictionary *component;
|
NSDictionary *component;
|
||||||
NSArray *allComponents;
|
NSArray *allComponents;
|
||||||
|
|
||||||
|
@ -860,7 +1007,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[syncCache removeObjectForKey: uid];
|
[syncCache removeObjectForKey: uid];
|
||||||
[dateCache removeObjectForKey: uid];
|
[dateCache removeObjectForKey: uid];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (debugOn)
|
if (debugOn)
|
||||||
[self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", uid];
|
[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];
|
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 (deleted)
|
||||||
{
|
{
|
||||||
if ([syncCache objectForKey: uid])
|
if ([syncCache objectForKey: uid])
|
||||||
{
|
{
|
||||||
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
|
[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>"];
|
[s appendString: @"</Delete>"];
|
||||||
|
|
||||||
[syncCache removeObjectForKey: uid];
|
[syncCache removeObjectForKey: uid];
|
||||||
|
@ -960,7 +1143,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
else
|
else
|
||||||
[s appendString: @"<Add xmlns=\"AirSync:\">"];
|
[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: @"<ApplicationData xmlns=\"AirSync:\">"];
|
||||||
|
|
||||||
[s appendString: [componentObject activeSyncRepresentationInContext: context]];
|
[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];
|
folderKey = [self _getNameInCache: collection withType: folderType];
|
||||||
folderMetadata = [self _folderMetadataForKey: folderKey];
|
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
|
// We check for a window size, default to 100 if not specfied or out of bounds
|
||||||
windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue];
|
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]]
|
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
|
||||||
inBuffer: changeBuffer
|
inBuffer: changeBuffer
|
||||||
lastServerKey: &lastServerKey
|
lastServerKey: &lastServerKey
|
||||||
defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]];
|
defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]
|
||||||
|
mergeFolders: NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
folderMetadata = [self _folderMetadataForKey: folderKey];
|
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
|
// If we got any changes or if we have applied any commands
|
||||||
// let's regenerate our SyncKey based on the collection tag.
|
// let's regenerate our SyncKey based on the collection tag.
|
||||||
if ([changeBuffer length] || [commandsBuffer length])
|
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/NSString+Utilities.h>
|
||||||
#import <SOGo/WORequest+SOGo.h>
|
#import <SOGo/WORequest+SOGo.h>
|
||||||
#import <SOGo/NSArray+Utilities.h>
|
#import <SOGo/NSArray+Utilities.h>
|
||||||
|
#import <SOGo/NSString+Utilities.h>
|
||||||
|
|
||||||
#import <Appointments/SOGoAppointmentFolder.h>
|
#import <Appointments/SOGoAppointmentFolder.h>
|
||||||
#import <Appointments/SOGoAppointmentFolders.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.h>
|
||||||
#import <Mailer/SOGoMailObject+Draft.h>
|
#import <Mailer/SOGoMailObject+Draft.h>
|
||||||
#import <Mailer/NSString+Mail.h>
|
#import <Mailer/NSString+Mail.h>
|
||||||
|
#import <Mailer/SOGoSentFolder.h>
|
||||||
|
|
||||||
|
|
||||||
#import <Foundation/NSObject.h>
|
#import <Foundation/NSObject.h>
|
||||||
|
@ -157,6 +159,9 @@ void handle_eas_terminate(int signum)
|
||||||
|
|
||||||
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
|
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
|
||||||
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata forKey: (NSString *) theFolderKey;
|
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata forKey: (NSString *) theFolderKey;
|
||||||
|
- (void) _setOrUnsetSyncRequest: (BOOL) set
|
||||||
|
collections: (NSArray *) collections;
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -853,7 +858,7 @@ void handle_eas_terminate(int signum)
|
||||||
if ([key rangeOfString: @"+folder" options: NSCaseInsensitiveSearch].location != NSNotFound)
|
if ([key rangeOfString: @"+folder" options: NSCaseInsensitiveSearch].location != NSNotFound)
|
||||||
[cachedGUIDs setObject: [NSString stringWithFormat: @"folder%@", [[o properties] objectForKey: @"displayName"]] // e.g. CDB648DDBC5040F8AC90792383DBBBAA+folderINBOX
|
[cachedGUIDs setObject: [NSString stringWithFormat: @"folder%@", [[o properties] objectForKey: @"displayName"]] // e.g. CDB648DDBC5040F8AC90792383DBBBAA+folderINBOX
|
||||||
forKey: [key substringFromIndex: [key rangeOfString: @"+"].location+1]];
|
forKey: [key substringFromIndex: [key rangeOfString: @"+"].location+1]];
|
||||||
else
|
else
|
||||||
[cachedGUIDs setObject: [key substringFromIndex: [key rangeOfString: @"+"].location+1] // e.g. CDB648DDBC5040F8AC90792383DBBBAA+vcard/personal
|
[cachedGUIDs setObject: [key substringFromIndex: [key rangeOfString: @"+"].location+1] // e.g. CDB648DDBC5040F8AC90792383DBBBAA+vcard/personal
|
||||||
forKey: [key substringFromIndex: [key rangeOfString: @"+"].location+1]];
|
forKey: [key substringFromIndex: [key rangeOfString: @"+"].location+1]];
|
||||||
}
|
}
|
||||||
|
@ -896,12 +901,26 @@ void handle_eas_terminate(int signum)
|
||||||
if ([cKey rangeOfString: @"/"].location != NSNotFound)
|
if ([cKey rangeOfString: @"/"].location != NSNotFound)
|
||||||
currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO]
|
currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO]
|
||||||
lookupName: [cKey substringFromIndex: [cKey rangeOfString: @"/"].location+1] 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
|
// We skip personal GCS folders - we always want to synchronize these
|
||||||
if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
|
if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
|
||||||
[[currentFolder nameInContainer] isEqualToString: @"personal"])
|
[[currentFolder nameInContainer] isEqualToString: @"personal"])
|
||||||
continue;
|
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
|
// 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 ||
|
if (!currentFolder ||
|
||||||
![currentFolder synchronize] ||
|
![currentFolder synchronize] ||
|
||||||
|
@ -912,10 +931,16 @@ void handle_eas_terminate(int signum)
|
||||||
onObject: currentFolder
|
onObject: currentFolder
|
||||||
inContext: context])
|
inContext: context])
|
||||||
{
|
{
|
||||||
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
|
// Don't send a delete when MergedFoler is set, we have done it above.
|
||||||
command_count++;
|
// 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];
|
[o destroy];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1004,6 +1029,7 @@ void handle_eas_terminate(int signum)
|
||||||
[[o properties] removeObjectForKey: @"SyncKey"];
|
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||||
[[o properties] removeObjectForKey: @"DateCache"];
|
[[o properties] removeObjectForKey: @"DateCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"UidCache"];
|
||||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||||
|
@ -1011,6 +1037,9 @@ void handle_eas_terminate(int signum)
|
||||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||||
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
||||||
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||||
|
[[o properties] removeObjectForKey: @"MergedFolder"];
|
||||||
|
[[o properties] removeObjectForKey: @"CleanoutDate"];
|
||||||
|
|
||||||
[o save];
|
[o save];
|
||||||
|
|
||||||
|
@ -1069,7 +1098,7 @@ void handle_eas_terminate(int signum)
|
||||||
operation = @"Add";
|
operation = @"Add";
|
||||||
else if (![[[o properties ] objectForKey: @"displayName"] isEqualToString: [[folders objectAtIndex:fi] displayName]])
|
else if (![[[o properties ] objectForKey: @"displayName"] isEqualToString: [[folders objectAtIndex:fi] displayName]])
|
||||||
operation = @"Update";
|
operation = @"Update";
|
||||||
else
|
else
|
||||||
operation = nil;
|
operation = nil;
|
||||||
|
|
||||||
if (operation)
|
if (operation)
|
||||||
|
@ -1105,6 +1134,7 @@ void handle_eas_terminate(int signum)
|
||||||
[[o properties] removeObjectForKey: @"SyncKey"];
|
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||||
[[o properties] removeObjectForKey: @"DateCache"];
|
[[o properties] removeObjectForKey: @"DateCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"UidCache"];
|
||||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||||
|
@ -1112,6 +1142,9 @@ void handle_eas_terminate(int signum)
|
||||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||||
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
||||||
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||||
|
[[o properties] removeObjectForKey: @"MergedFolder"];
|
||||||
|
[[o properties] removeObjectForKey: @"CleanoutDate"];
|
||||||
}
|
}
|
||||||
|
|
||||||
[o save];
|
[o save];
|
||||||
|
@ -1132,6 +1165,7 @@ void handle_eas_terminate(int signum)
|
||||||
[[o properties] removeObjectForKey: @"SyncKey"];
|
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||||
[[o properties] removeObjectForKey: @"DateCache"];
|
[[o properties] removeObjectForKey: @"DateCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"UidCache"];
|
||||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||||
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
|
||||||
[[o properties] removeObjectForKey: @"SupportedElements"];
|
[[o properties] removeObjectForKey: @"SupportedElements"];
|
||||||
|
@ -1139,6 +1173,9 @@ void handle_eas_terminate(int signum)
|
||||||
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
|
||||||
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
[[o properties] removeObjectForKey: @"FirstIdInCache"];
|
||||||
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
[[o properties] removeObjectForKey: @"LastIdInCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"MergedFoldersSyncKeys"];
|
||||||
|
[[o properties] removeObjectForKey: @"MergedFolder"];
|
||||||
|
[[o properties] removeObjectForKey: @"CleanoutDate"];
|
||||||
}
|
}
|
||||||
|
|
||||||
[o save];
|
[o save];
|
||||||
|
@ -1606,10 +1643,11 @@ void handle_eas_terminate(int signum)
|
||||||
- (void) processMeetingResponse: (id <DOMElement>) theDocumentElement
|
- (void) processMeetingResponse: (id <DOMElement>) theDocumentElement
|
||||||
inResponse: (WOResponse *) theResponse
|
inResponse: (WOResponse *) theResponse
|
||||||
{
|
{
|
||||||
NSString *realCollectionId, *requestId, *participationStatus, *calendarId;
|
NSString *realCollectionId, *requestId, *easRequestId, *participationStatus, *calendarId;
|
||||||
SOGoAppointmentObject *appointmentObject;
|
SOGoAppointmentObject *appointmentObject;
|
||||||
SOGoMailObject *mailObject;
|
SOGoMailObject *mailObject;
|
||||||
NSMutableString *s;
|
NSMutableDictionary *uidCache, *folderMetadata;
|
||||||
|
NSMutableString *s, *nameInCache;
|
||||||
NSData *d;
|
NSData *d;
|
||||||
|
|
||||||
id collection;
|
id collection;
|
||||||
|
@ -1624,7 +1662,7 @@ void handle_eas_terminate(int signum)
|
||||||
realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
||||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||||
userResponse = [[[(id)[theDocumentElement getElementsByTagName: @"UserResponse"] lastObject] textValue] intValue];
|
userResponse = [[[(id)[theDocumentElement getElementsByTagName: @"UserResponse"] lastObject] textValue] intValue];
|
||||||
requestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue];
|
easRequestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue];
|
||||||
appointmentObject = nil;
|
appointmentObject = nil;
|
||||||
calendarId = nil;
|
calendarId = nil;
|
||||||
|
|
||||||
|
@ -1643,10 +1681,35 @@ void handle_eas_terminate(int signum)
|
||||||
if (folderType == ActiveSyncEventFolder)
|
if (folderType == ActiveSyncEventFolder)
|
||||||
{
|
{
|
||||||
collection = [[context activeUser] personalCalendarFolderInContext: context];
|
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]
|
appointmentObject = [collection lookupName: [requestId sanitizedServerIdWithType: ActiveSyncEventFolder]
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
calendarId = requestId;
|
calendarId = easRequestId;
|
||||||
|
|
||||||
// Object not found, let's fallback on the INBOX folder
|
// Object not found, let's fallback on the INBOX folder
|
||||||
if ([appointmentObject isKindOfClass: [NSException class]])
|
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)
|
// 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...
|
// FIXME: that won't work too well for external invitations...
|
||||||
mailObject = [collection lookupName: requestId
|
mailObject = [collection lookupName: easRequestId
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: 0];
|
acquire: 0];
|
||||||
|
|
||||||
|
@ -1681,6 +1744,12 @@ void handle_eas_terminate(int signum)
|
||||||
|
|
||||||
// Fetch the SOGoAppointmentObject
|
// Fetch the SOGoAppointmentObject
|
||||||
collection = [[context activeUser] personalCalendarFolderInContext: context];
|
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]]
|
appointmentObject = [collection lookupName: [NSString stringWithFormat: @"%@.ics", [event uid]]
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
|
@ -1692,6 +1761,27 @@ void handle_eas_terminate(int signum)
|
||||||
inContainer: collection];
|
inContainer: collection];
|
||||||
[appointmentObject saveComponent: event];
|
[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: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||||
[s appendString: @"<MeetingResponse xmlns=\"MeetingResponse:\">"];
|
[s appendString: @"<MeetingResponse xmlns=\"MeetingResponse:\">"];
|
||||||
[s appendString: @"<Result>"];
|
[s appendString: @"<Result>"];
|
||||||
[s appendFormat: @"<RequestId>%@</RequestId>", requestId];
|
[s appendFormat: @"<RequestId>%@</RequestId>", easRequestId];
|
||||||
[s appendFormat: @"<CalendarId>%@</CalendarId>", calendarId];
|
[s appendFormat: @"<CalendarId>%@</CalendarId>", calendarId];
|
||||||
[s appendFormat: @"<Status>%d</Status>", status];
|
[s appendFormat: @"<Status>%d</Status>", status];
|
||||||
[s appendString: @"</Result>"];
|
[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: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||||
[s appendString: @"<MeetingResponse xmlns=\"MeetingResponse:\">"];
|
[s appendString: @"<MeetingResponse xmlns=\"MeetingResponse:\">"];
|
||||||
[s appendString: @"<Result>"];
|
[s appendString: @"<Result>"];
|
||||||
[s appendFormat: @"<RequestId>%@</RequestId>", requestId];
|
[s appendFormat: @"<RequestId>%@</RequestId>", easRequestId];
|
||||||
[s appendFormat: @"<Status>%d</Status>", 2];
|
[s appendFormat: @"<Status>%d</Status>", 2];
|
||||||
[s appendString: @"</Result>"];
|
[s appendString: @"</Result>"];
|
||||||
[s appendString: @"</MeetingResponse>"];
|
[s appendString: @"</MeetingResponse>"];
|
||||||
|
@ -1756,8 +1846,8 @@ void handle_eas_terminate(int signum)
|
||||||
- (void) processMoveItems: (id <DOMElement>) theDocumentElement
|
- (void) processMoveItems: (id <DOMElement>) theDocumentElement
|
||||||
inResponse: (WOResponse *) theResponse
|
inResponse: (WOResponse *) theResponse
|
||||||
{
|
{
|
||||||
NSString *srcMessageId, *srcFolderId, *dstFolderId, *dstMessageId, *nameInCache, *currentFolder;
|
NSString *srcMessageId, *srcFolderId, *dstFolderId, *dstMessageId, *srcNameInCache, *dstNameInCache, *currentSrcFolder, *currentDstFolder;
|
||||||
NSMutableDictionary *folderMetadata, *prevSuccessfulMoveItemsOps, *newSuccessfulMoveItemsOps;
|
NSMutableDictionary *srcFolderMetadata, *dstFolderMetadata, *prevSuccessfulMoveItemsOps, *newSuccessfulMoveItemsOps;
|
||||||
SOGoMicrosoftActiveSyncFolderType srcFolderType, dstFolderType;
|
SOGoMicrosoftActiveSyncFolderType srcFolderType, dstFolderType;
|
||||||
id <DOMElement> aMoveOperation;
|
id <DOMElement> aMoveOperation;
|
||||||
NSArray *moveOperations;
|
NSArray *moveOperations;
|
||||||
|
@ -1766,12 +1856,17 @@ void handle_eas_terminate(int signum)
|
||||||
NSData *d;
|
NSData *d;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
currentSrcFolder = nil;
|
||||||
|
currentDstFolder = nil;
|
||||||
|
|
||||||
moveOperations = (id)[theDocumentElement getElementsByTagName: @"Move"];
|
moveOperations = (id)[theDocumentElement getElementsByTagName: @"Move"];
|
||||||
|
|
||||||
newSuccessfulMoveItemsOps = [NSMutableDictionary dictionary];
|
newSuccessfulMoveItemsOps = [NSMutableDictionary dictionary];
|
||||||
prevSuccessfulMoveItemsOps = nil;
|
prevSuccessfulMoveItemsOps = nil;
|
||||||
folderMetadata = nil;
|
srcFolderMetadata = nil;
|
||||||
currentFolder = nil;
|
dstFolderMetadata = nil;
|
||||||
|
currentSrcFolder = nil;
|
||||||
|
currentDstFolder = nil;
|
||||||
|
|
||||||
s = [NSMutableString string];
|
s = [NSMutableString string];
|
||||||
|
|
||||||
|
@ -1787,18 +1882,33 @@ void handle_eas_terminate(int signum)
|
||||||
srcFolderId = [[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] realCollectionIdWithFolderType: &srcFolderType];
|
srcFolderId = [[[(id)[aMoveOperation getElementsByTagName: @"SrcFldId"] lastObject] textValue] realCollectionIdWithFolderType: &srcFolderType];
|
||||||
dstFolderId = [[[(id)[aMoveOperation getElementsByTagName: @"DstFldId"] lastObject] textValue] realCollectionIdWithFolderType: &dstFolderType];
|
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)
|
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
|
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];
|
srcFolderMetadata = [self _folderMetadataForKey: srcNameInCache];
|
||||||
prevSuccessfulMoveItemsOps = [folderMetadata objectForKey: @"SuccessfulMoveItemsOps"];
|
prevSuccessfulMoveItemsOps = [srcFolderMetadata objectForKey: @"SuccessfulMoveItemsOps"];
|
||||||
currentFolder = nameInCache;
|
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>"];
|
[s appendString: @"<Response>"];
|
||||||
|
|
||||||
if (srcFolderType == ActiveSyncMailFolder && dstFolderType == ActiveSyncMailFolder)
|
if (srcFolderType == ActiveSyncMailFolder && dstFolderType == ActiveSyncMailFolder)
|
||||||
|
@ -1903,14 +2013,44 @@ void handle_eas_terminate(int signum)
|
||||||
{
|
{
|
||||||
id srcCollection, dstCollection, srcSogoObject, dstSogoObject;
|
id srcCollection, dstCollection, srcSogoObject, dstSogoObject;
|
||||||
NSArray *elements;
|
NSArray *elements;
|
||||||
NSString *newUID;
|
NSString *newUID, *origSrcMessageId;
|
||||||
|
NSMutableDictionary *srcUidCache, *dstUidCache, *dstSyncCache;
|
||||||
NSException *ex;
|
NSException *ex;
|
||||||
|
|
||||||
unsigned int count, max;
|
unsigned int count, max;
|
||||||
|
|
||||||
srcCollection = [self collectionFromId: srcFolderId type: srcFolderType];
|
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];
|
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]
|
srcSogoObject = [srcCollection lookupName: [srcMessageId sanitizedServerIdWithType: srcFolderType]
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
|
@ -1918,7 +2058,8 @@ void handle_eas_terminate(int signum)
|
||||||
sm = [SoSecurityManager sharedSecurityManager];
|
sm = [SoSecurityManager sharedSecurityManager];
|
||||||
if (![sm validatePermission: SoPerm_DeleteObjects
|
if (![sm validatePermission: SoPerm_DeleteObjects
|
||||||
onObject: srcCollection
|
onObject: srcCollection
|
||||||
inContext: context])
|
inContext: context] &&
|
||||||
|
![srcSogoObject isKindOfClass: [NSException class]])
|
||||||
{
|
{
|
||||||
if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
|
||||||
onObject: dstCollection
|
onObject: dstCollection
|
||||||
|
@ -1936,7 +2077,18 @@ void handle_eas_terminate(int signum)
|
||||||
if (!ex)
|
if (!ex)
|
||||||
{
|
{
|
||||||
ex = [srcSogoObject delete];
|
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: @"<DstMsgId>%@</DstMsgId>", newUID];
|
||||||
[s appendFormat: @"<Status>%d</Status>", 3];
|
[s appendFormat: @"<Status>%d</Status>", 3];
|
||||||
|
|
||||||
|
@ -1948,36 +2100,45 @@ void handle_eas_terminate(int signum)
|
||||||
if ([prevSuccessfulMoveItemsOps objectForKey: srcMessageId])
|
if ([prevSuccessfulMoveItemsOps objectForKey: srcMessageId])
|
||||||
{
|
{
|
||||||
// Move failed but we can recover the dstMessageId from previous request
|
// 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: @"<DstMsgId>%@</DstMsgId>", [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] ];
|
||||||
[s appendFormat: @"<Status>%d</Status>", 3];
|
[s appendFormat: @"<Status>%d</Status>", 3];
|
||||||
[newSuccessfulMoveItemsOps setObject: [prevSuccessfulMoveItemsOps objectForKey: srcMessageId] forKey: srcMessageId];
|
[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
|
else
|
||||||
{
|
{
|
||||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||||
[s appendFormat: @"<Status>%d</Status>", 2];
|
[s appendFormat: @"<Status>%d</Status>", 2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", srcMessageId];
|
[s appendFormat: @"<SrcMsgId>%@</SrcMsgId>", origSrcMessageId];
|
||||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[s appendString: @"</Response>"];
|
[s appendString: @"</Response>"];
|
||||||
|
|
||||||
[folderMetadata removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
[srcFolderMetadata removeObjectForKey: @"SuccessfulMoveItemsOps"];
|
||||||
[folderMetadata setObject: newSuccessfulMoveItemsOps forKey: @"SuccessfulMoveItemsOps"];
|
[srcFolderMetadata setObject: newSuccessfulMoveItemsOps forKey: @"SuccessfulMoveItemsOps"];
|
||||||
[self _setFolderMetadata: folderMetadata forKey: nameInCache];
|
[self _setFolderMetadata: srcFolderMetadata forKey: srcNameInCache];
|
||||||
|
[self _setFolderMetadata: dstFolderMetadata forKey: dstNameInCache];
|
||||||
}
|
}
|
||||||
|
|
||||||
[s appendString: @"</MoveItems>"];
|
[s appendString: @"</MoveItems>"];
|
||||||
|
@ -2155,7 +2316,7 @@ void handle_eas_terminate(int signum)
|
||||||
|
|
||||||
if (folderType == ActiveSyncMailFolder)
|
if (folderType == ActiveSyncMailFolder)
|
||||||
folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"folder%@", [[collectionId stringByUnescapingURL] substringFromIndex:5]]];
|
folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"folder%@", [[collectionId stringByUnescapingURL] substringFromIndex:5]]];
|
||||||
else
|
else
|
||||||
folderMetadata = [self _folderMetadataForKey: [collectionId stringByUnescapingURL]];
|
folderMetadata = [self _folderMetadataForKey: [collectionId stringByUnescapingURL]];
|
||||||
|
|
||||||
collection = [self collectionFromId: realCollectionId type: folderType];
|
collection = [self collectionFromId: realCollectionId type: folderType];
|
||||||
|
@ -3209,7 +3370,7 @@ void handle_eas_terminate(int signum)
|
||||||
textPart = apart;
|
textPart = apart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ([[[part contentType] type] isEqualToString: @"text"] && [[[part contentType] subType] isEqualToString: @"html"])
|
if ([[[part contentType] type] isEqualToString: @"text"] && [[[part contentType] subType] isEqualToString: @"html"])
|
||||||
htmlPart = part;
|
htmlPart = part;
|
||||||
|
@ -3242,7 +3403,7 @@ void handle_eas_terminate(int signum)
|
||||||
charset = [[htmlPart contentType] valueOfParameter: @"charset"];
|
charset = [[htmlPart contentType] valueOfParameter: @"charset"];
|
||||||
isHTML = YES;
|
isHTML = YES;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bodyFromSmartForward = [textPart body];
|
bodyFromSmartForward = [textPart body];
|
||||||
charset = [[textPart contentType] valueOfParameter: @"charset"];
|
charset = [[textPart contentType] valueOfParameter: @"charset"];
|
||||||
|
@ -3559,7 +3720,7 @@ void handle_eas_terminate(int signum)
|
||||||
if (([cmdName rangeOfString: @"ItemOperations" options: NSCaseInsensitiveSearch].location != NSNotFound) &&
|
if (([cmdName rangeOfString: @"ItemOperations" options: NSCaseInsensitiveSearch].location != NSNotFound) &&
|
||||||
([[theRequest headerForKey: @"MS-ASAcceptMultiPart"] isEqualToString:@"T"] || [[theRequest uri] acceptsMultiPart]))
|
([[theRequest headerForKey: @"MS-ASAcceptMultiPart"] isEqualToString:@"T"] || [[theRequest uri] acceptsMultiPart]))
|
||||||
[theResponse setHeader: @"application/vnd.ms-sync.multipart" forKey: @"Content-Type"];
|
[theResponse setHeader: @"application/vnd.ms-sync.multipart" forKey: @"Content-Type"];
|
||||||
else
|
else
|
||||||
[theResponse setHeader: @"application/vnd.ms-sync.wbxml" forKey: @"Content-Type"];
|
[theResponse setHeader: @"application/vnd.ms-sync.wbxml" forKey: @"Content-Type"];
|
||||||
|
|
||||||
[self performSelector: aSelector withObject: documentElement withObject: theResponse];
|
[self performSelector: aSelector withObject: documentElement withObject: theResponse];
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#import <Foundation/NSString.h>
|
#import <Foundation/NSString.h>
|
||||||
#import <Foundation/NSUserDefaults.h>
|
#import <Foundation/NSUserDefaults.h>
|
||||||
#import <Foundation/NSURL.h>
|
#import <Foundation/NSURL.h>
|
||||||
|
#import <Foundation/NSValue.h>
|
||||||
|
#import <Foundation/NSProcessInfo.h>
|
||||||
|
|
||||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||||
#import <NGObjWeb/WOApplication.h>
|
#import <NGObjWeb/WOApplication.h>
|
||||||
|
@ -52,6 +54,8 @@ typedef enum
|
||||||
ManageEASListFolders = 2,
|
ManageEASListFolders = 2,
|
||||||
ManageEASResetDevice = 3,
|
ManageEASResetDevice = 3,
|
||||||
ManageEASRestFolder = 4,
|
ManageEASRestFolder = 4,
|
||||||
|
ManageEASVCard = 5,
|
||||||
|
ManageEASVEvent = 6,
|
||||||
} SOGoManageEASCommand;
|
} SOGoManageEASCommand;
|
||||||
|
|
||||||
@interface SOGoToolManageEAS : SOGoTool
|
@interface SOGoToolManageEAS : SOGoTool
|
||||||
|
@ -59,6 +63,8 @@ typedef enum
|
||||||
|
|
||||||
@implementation SOGoToolManageEAS
|
@implementation SOGoToolManageEAS
|
||||||
|
|
||||||
|
NSURL *folderTableURL;
|
||||||
|
|
||||||
+ (void) initialize
|
+ (void) initialize
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -73,15 +79,58 @@ typedef enum
|
||||||
return @"manage EAS folders";
|
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
|
- (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"
|
" user the user of whom to reset the whole device or a single folder\n"
|
||||||
" Examples:\n"
|
" Examples:\n"
|
||||||
" sogo-tool manage-eas listdevices janedoe\n"
|
" sogo-tool manage-eas listdevices janedoe\n"
|
||||||
" sogo-tool manage-eas listfolders janedoe androidc316986417\n"
|
" sogo-tool manage-eas listfolders janedoe androidc316986417\n"
|
||||||
" sogo-tool manage-eas resetdevice 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;
|
return ManageEASResetDevice;
|
||||||
else if ([theString caseInsensitiveCompare: @"resetfolder"] == NSOrderedSame)
|
else if ([theString caseInsensitiveCompare: @"resetfolder"] == NSOrderedSame)
|
||||||
return ManageEASRestFolder;
|
return ManageEASRestFolder;
|
||||||
|
else if ([theString caseInsensitiveCompare: @"mergevcard"] == NSOrderedSame)
|
||||||
|
return ManageEASVCard;
|
||||||
|
else if ([theString caseInsensitiveCompare: @"mergevevent"] == NSOrderedSame)
|
||||||
|
return ManageEASVEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ManageEASUnknown;
|
return ManageEASUnknown;
|
||||||
|
@ -107,7 +160,6 @@ typedef enum
|
||||||
NSString *urlString, *deviceId, *userId;
|
NSString *urlString, *deviceId, *userId;
|
||||||
NSMutableString *ocFSTableName;
|
NSMutableString *ocFSTableName;
|
||||||
SOGoCacheGCSObject *oc, *foc;
|
SOGoCacheGCSObject *oc, *foc;
|
||||||
NSURL *folderTableURL;
|
|
||||||
NSMutableArray *parts;
|
NSMutableArray *parts;
|
||||||
NSArray *entries;
|
NSArray *entries;
|
||||||
id cacheEntry;
|
id cacheEntry;
|
||||||
|
@ -169,6 +221,7 @@ typedef enum
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = YES;
|
rc = YES;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ManageEASListFolders:
|
case ManageEASListFolders:
|
||||||
|
@ -195,6 +248,8 @@ typedef enum
|
||||||
[foc reloadIfNeeded];
|
[foc reloadIfNeeded];
|
||||||
|
|
||||||
fprintf(stdout, " Folder Name: %s\n\n", [[[foc properties] objectForKey: @"displayName"] UTF8String]);
|
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)
|
if (verbose)
|
||||||
fprintf(stdout, " metadata Name: %s\n\n", [[[foc properties] description] UTF8String]);
|
fprintf(stdout, " metadata Name: %s\n\n", [[[foc properties] description] UTF8String]);
|
||||||
|
@ -227,7 +282,7 @@ typedef enum
|
||||||
|
|
||||||
NSMutableString *sql;
|
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]];
|
[oc performBatchSQLQueries: [NSArray arrayWithObject: sql]];
|
||||||
rc = YES;
|
rc = YES;
|
||||||
|
@ -260,16 +315,138 @@ typedef enum
|
||||||
fprintf(stderr, "ERROR: Folder with ID \"%s\" not found\n", [deviceId UTF8String]);
|
fprintf(stderr, "ERROR: Folder with ID \"%s\" not found\n", [deviceId UTF8String]);
|
||||||
return rc;
|
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;
|
rc = YES;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue