Fix for bug #2688
parent
6bab4c94f2
commit
d35c52bb38
|
@ -123,7 +123,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[o setTableUrl: [self folderTableURL]];
|
[o setTableUrl: [self folderTableURL]];
|
||||||
[o reloadIfNeeded];
|
[o reloadIfNeeded];
|
||||||
|
|
||||||
[[o properties] removeAllObjects];
|
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"DateCache"];
|
||||||
|
|
||||||
[[o properties] addEntriesFromDictionary: theFolderMetadata];
|
[[o properties] addEntriesFromDictionary: theFolderMetadata];
|
||||||
[o save];
|
[o save];
|
||||||
}
|
}
|
||||||
|
@ -627,21 +629,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
|
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's a new Sync operation, ignore anything we might have
|
// If it's a new Sync operation, DateCache and SyncCache need to be deleted
|
||||||
// in our preferences.
|
// but GUID stored by folderSync shouldn't be touched
|
||||||
|
folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
|
||||||
if ([theSyncKey isEqualToString: @"-1"])
|
if ([theSyncKey isEqualToString: @"-1"])
|
||||||
{
|
{
|
||||||
folderMetadata = [NSMutableDictionary dictionary];
|
|
||||||
|
|
||||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
|
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
|
||||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
|
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
|
||||||
|
|
||||||
// TODO - Generate GUID
|
|
||||||
//[folderMetadata setObject: @"FOO-BAR-BAZ" forKey: @"GUID"];
|
|
||||||
}
|
}
|
||||||
else
|
// Check whether GUID in cache is equal to the GUID from imap - this is to avoid cache corruptions if a folder has been renamed and a new folder
|
||||||
folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
|
// with the same name has been created but folderSync has not yet updated the cache
|
||||||
|
if (!([[theCollection nameInContainer] isEqualToString:
|
||||||
|
[NSString stringWithFormat: @"folder%@", [self globallyUniqueIDToIMAPFolderName: [folderMetadata objectForKey: @"GUID"] type: theFolderType]]]))
|
||||||
|
{
|
||||||
|
NSLog(@"GUID mismatch don't sync now!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||||
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||||
|
|
||||||
|
@ -711,6 +715,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[s appendString: @"</Delete>"];
|
[s appendString: @"</Delete>"];
|
||||||
|
|
||||||
[syncCache removeObjectForKey: [aCacheObject uid]];
|
[syncCache removeObjectForKey: [aCacheObject uid]];
|
||||||
|
[dateCache removeObjectForKey: [aCacheObject uid]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -773,13 +778,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
|
//NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (more_available)
|
if (more_available)
|
||||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||||
else
|
else
|
||||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||||
|
|
||||||
|
|
||||||
[self _setFolderMetadata: folderMetadata
|
[self _setFolderMetadata: folderMetadata
|
||||||
forKey: [theCollection nameInContainer]];
|
forKey: [theCollection nameInContainer]];
|
||||||
} // default:
|
} // default:
|
||||||
|
@ -890,6 +897,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
||||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||||
|
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||||
collection = [self collectionFromId: realCollectionId type: folderType];
|
collection = [self collectionFromId: realCollectionId type: folderType];
|
||||||
|
|
||||||
syncKey = davCollectionTag = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
|
syncKey = davCollectionTag = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
|
||||||
|
@ -988,6 +996,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
davCollectionTag = [collection davCollectionTag];
|
davCollectionTag = [collection davCollectionTag];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Generate the response buffer
|
// Generate the response buffer
|
||||||
[theBuffer appendString: @"<Collection>"];
|
[theBuffer appendString: @"<Collection>"];
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
- (id) collectionFromId: (NSString *) theCollectionId
|
- (id) collectionFromId: (NSString *) theCollectionId
|
||||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||||
|
|
||||||
|
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
|
||||||
|
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
|
||||||
|
|
||||||
- (NSException *) dispatchRequest: (id) theRequest
|
- (NSException *) dispatchRequest: (id) theRequest
|
||||||
inResponse: (id) theResponse
|
inResponse: (id) theResponse
|
||||||
context: (id) theContext;
|
context: (id) theContext;
|
||||||
|
|
|
@ -129,7 +129,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[super init];
|
[super init];
|
||||||
|
|
||||||
folderTableURL = nil;
|
folderTableURL = nil;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +164,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
return [o properties];
|
return [o properties];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
|
||||||
|
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||||
|
{
|
||||||
|
if (theFolderType == ActiveSyncMailFolder)
|
||||||
|
{
|
||||||
|
SOGoMailAccounts *accountsFolder;
|
||||||
|
SOGoMailAccount *accountFolder;
|
||||||
|
SOGoUserFolder *userFolder;
|
||||||
|
NSDictionary *imapGUIDs;
|
||||||
|
|
||||||
|
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||||
|
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||||
|
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||||
|
|
||||||
|
// Get the GUID of the IMAP folder
|
||||||
|
imapGUIDs = [accountFolder imapFolderGUIDs];
|
||||||
|
|
||||||
|
return [[imapGUIDs allKeysForObject: theIdToTranslate] objectAtIndex: 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return theIdToTranslate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -198,7 +221,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||||
|
|
||||||
collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId]
|
collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId]
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
|
@ -251,20 +274,45 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
else
|
else
|
||||||
newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@/%@", [[parentId stringByUnescapingURL] substringFromIndex: 5],
|
{
|
||||||
[displayName stringByEncodingImap4FolderName]]
|
parentId = [self globallyUniqueIDToIMAPFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type: ActiveSyncMailFolder];
|
||||||
inContext: context
|
newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@/%@", [parentId stringByEncodingImap4FolderName],
|
||||||
acquire: NO];
|
[displayName stringByEncodingImap4FolderName]]
|
||||||
|
inContext: context
|
||||||
|
acquire: NO];
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
// handle exists (status == 2)
|
// handle exists (status == 2)
|
||||||
// handle right synckey
|
// handle right synckey
|
||||||
if ([newFolder create])
|
if ([newFolder create])
|
||||||
{
|
{
|
||||||
|
SOGoMailAccount *accountFolder;
|
||||||
|
NSDictionary *imapGUIDs;
|
||||||
|
SOGoCacheGCSObject *o;
|
||||||
|
NSString *key;
|
||||||
|
|
||||||
nameInContainer = [newFolder nameInContainer];
|
nameInContainer = [newFolder nameInContainer];
|
||||||
|
|
||||||
// We strip the "folder" prefix
|
// We strip the "folder" prefix
|
||||||
nameInContainer = [nameInContainer substringFromIndex: 6];
|
nameInContainer = [nameInContainer substringFromIndex: 6];
|
||||||
|
|
||||||
|
// save new guid into cache
|
||||||
|
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||||
|
|
||||||
|
// update GUID in cache
|
||||||
|
imapGUIDs = [accountFolder imapFolderGUIDs];
|
||||||
|
|
||||||
|
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], nameInContainer ];
|
||||||
|
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||||
|
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||||
|
[o setTableUrl: [self folderTableURL]];
|
||||||
|
[o reloadIfNeeded];
|
||||||
|
nameInContainer =[imapGUIDs objectForKey: nameInContainer];
|
||||||
|
|
||||||
|
[[o properties ] setObject: nameInContainer forKey: @"GUID"];
|
||||||
|
[o save];
|
||||||
|
|
||||||
nameInContainer = [[NSString stringWithFormat: @"mail/%@", nameInContainer] stringByEscapingURL];
|
nameInContainer = [[NSString stringWithFormat: @"mail/%@", nameInContainer] stringByEscapingURL];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -345,9 +393,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
NSString *serverId;
|
NSString *serverId;
|
||||||
|
|
||||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||||
|
|
||||||
|
|
||||||
serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
||||||
|
serverId = [self globallyUniqueIDToIMAPFolderName: serverId type: folderType];
|
||||||
|
|
||||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||||
|
@ -408,6 +456,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
||||||
|
serverId = [self globallyUniqueIDToIMAPFolderName: serverId type: folderType];
|
||||||
parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue];
|
parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue];
|
||||||
displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue];
|
displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue];
|
||||||
|
|
||||||
|
@ -427,7 +476,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error = [folderToUpdate renameTo: [NSString stringWithFormat: @"%@/%@", [[parentId stringByUnescapingURL] substringFromIndex: 5],
|
parentId = [self globallyUniqueIDToIMAPFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type: folderType];
|
||||||
|
error = [folderToUpdate renameTo: [NSString stringWithFormat: @"%@/%@", [parentId stringByEncodingImap4FolderName],
|
||||||
[displayName stringByEncodingImap4FolderName]]];
|
[displayName stringByEncodingImap4FolderName]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,7 +495,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// See http://msdn.microsoft.com/en-us/library/gg675615(v=exchg.80).aspx
|
// See http://msdn.microsoft.com/en-us/library/gg675615(v=exchg.80).aspx
|
||||||
// we return '9' - we force a FolderSync
|
// we return '9' - we force a FolderSync
|
||||||
status = 9;
|
status = 1;
|
||||||
|
|
||||||
[self _setFolderSyncKey: syncKey];
|
[self _setFolderSyncKey: syncKey];
|
||||||
|
|
||||||
|
@ -481,11 +531,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
- (void) processFolderSync: (id <DOMElement>) theDocumentElement
|
- (void) processFolderSync: (id <DOMElement>) theDocumentElement
|
||||||
inResponse: (WOResponse *) theResponse
|
inResponse: (WOResponse *) theResponse
|
||||||
{
|
{
|
||||||
|
|
||||||
NSMutableDictionary *metadata;
|
NSMutableDictionary *metadata;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
NSString *syncKey;
|
NSString *syncKey;
|
||||||
NSData *d;
|
NSData *d;
|
||||||
|
|
||||||
BOOL first_sync;
|
BOOL first_sync;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
@ -512,34 +563,107 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||||
[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 appendFormat: @"<FolderSync xmlns=\"FolderHierarchy:\"><Status>%d</Status><SyncKey>%@</SyncKey><Changes>", status, syncKey];
|
[s appendFormat: @"<FolderSync xmlns=\"FolderHierarchy:\"><Status>%d</Status><SyncKey>%@</SyncKey><Changes>", status, syncKey];
|
||||||
|
|
||||||
// Initial sync, let's return the complete folder list
|
if (status == 1)
|
||||||
if (first_sync)
|
|
||||||
{
|
{
|
||||||
SOGoMailAccounts *accountsFolder;
|
SOGoMailAccounts *accountsFolder;
|
||||||
SOGoMailAccount *accountFolder;
|
SOGoMailAccount *accountFolder;
|
||||||
SOGoUserFolder *userFolder;
|
SOGoUserFolder *userFolder;
|
||||||
id currentFolder;
|
id currentFolder;
|
||||||
|
|
||||||
NSDictionary *folderMetadata;
|
NSString *key, *cKey, *nkey, *name, *serverId, *parentId;
|
||||||
NSArray *allFoldersMetadata;
|
NSDictionary *folderMetadata, *imapGUIDs;
|
||||||
NSString *name, *serverId, *parentId;
|
NSArray *allFoldersMetadata, *allKeys;
|
||||||
|
NSMutableDictionary *cachedGUIDs;
|
||||||
|
NSMutableString *commands;
|
||||||
|
SOGoCacheGCSObject *o;
|
||||||
|
|
||||||
|
int i, type, command_count;
|
||||||
|
|
||||||
int i, type;
|
|
||||||
|
|
||||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||||
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||||
|
|
||||||
allFoldersMetadata = [accountFolder allFoldersMetadata];
|
allFoldersMetadata = [accountFolder allFoldersMetadata];
|
||||||
|
|
||||||
|
// Get GUIDs of folder (IMAP)
|
||||||
|
// e.g. {INBOX = "sogo_73c_192bd57b_d8"
|
||||||
|
imapGUIDs = [accountFolder imapFolderGUIDs];
|
||||||
|
|
||||||
// See 2.2.3.170.3 Type (FolderSync) - http://msdn.microsoft.com/en-us/library/gg650877(v=exchg.80).aspx
|
cachedGUIDs = [NSMutableDictionary dictionary];
|
||||||
[s appendFormat: @"<Count>%d</Count>", [allFoldersMetadata count]+3];
|
|
||||||
|
// No need to read cached folder infos during first sync. Otherwise, pull it from the database.
|
||||||
|
// e.g. {"sogo_73c_192bd57b_d8" = INBOX} - guid = foldername for easy reverse lookup with imapGUIDs
|
||||||
|
if (!first_sync)
|
||||||
|
{
|
||||||
|
NSArray *foldersInCache;
|
||||||
|
NSString *folderName;
|
||||||
|
|
||||||
|
// get the list of folder stored in cache
|
||||||
|
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], @"0"];
|
||||||
|
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||||
|
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||||
|
[o setTableUrl: [self folderTableURL]];
|
||||||
|
[o reloadIfNeeded];
|
||||||
|
foldersInCache = [o folderList: [context objectForKey: @"DeviceId"] newerThanVersion: -1];
|
||||||
|
|
||||||
|
// get guids of folders stored in cache
|
||||||
|
for (i = 0; i < [foldersInCache count]; i++)
|
||||||
|
{
|
||||||
|
folderName = [foldersInCache objectAtIndex: i];
|
||||||
|
key = [folderName substringFromIndex: 1];
|
||||||
|
|
||||||
|
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||||
|
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||||
|
[o setTableUrl: [self folderTableURL]];
|
||||||
|
[o reloadIfNeeded];
|
||||||
|
|
||||||
|
if ([[o properties ] objectForKey: @"GUID"])
|
||||||
|
[cachedGUIDs setObject: [key substringFromIndex: [key rangeOfString: @"+"].location+7]
|
||||||
|
forKey: [[o properties] objectForKey: @"GUID"]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle folders that have been deleted over IMAP
|
||||||
|
command_count = 0;
|
||||||
|
commands = [NSMutableString string];
|
||||||
|
allKeys = [cachedGUIDs allKeys];
|
||||||
|
|
||||||
|
for (i = 0; i < [allKeys count]; i++)
|
||||||
|
{
|
||||||
|
cKey = [allKeys objectAtIndex: i];
|
||||||
|
|
||||||
|
if (![imapGUIDs allKeysForObject: cKey])
|
||||||
|
{
|
||||||
|
// Delete folders cache content to avoid stale data if a new folder gets created with the same name
|
||||||
|
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey: cKey]];
|
||||||
|
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||||
|
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||||
|
[o setTableUrl: [self folderTableURL]];
|
||||||
|
[o reloadIfNeeded];
|
||||||
|
|
||||||
|
// Only send a delete command if GUID is found
|
||||||
|
if ([[o properties] objectForKey: @"GUID"])
|
||||||
|
{
|
||||||
|
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [[NSString stringWithFormat: @"mail/%@", [[o properties ] objectForKey: @"GUID"]] stringByEscapingURL] ];
|
||||||
|
command_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[o properties] removeAllObjects];
|
||||||
|
[o save];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle addition and changes
|
||||||
for (i = 0; i < [allFoldersMetadata count]; i++)
|
for (i = 0; i < [allFoldersMetadata count]; i++)
|
||||||
{
|
{
|
||||||
folderMetadata = [allFoldersMetadata objectAtIndex: i];
|
folderMetadata = [allFoldersMetadata objectAtIndex: i];
|
||||||
serverId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"path"]];
|
|
||||||
|
// No GUID -> no sync
|
||||||
|
if (!([imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
serverId = [NSString stringWithFormat: @"mail/%@", [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]];
|
||||||
name = [folderMetadata objectForKey: @"displayName"];
|
name = [folderMetadata objectForKey: @"displayName"];
|
||||||
|
|
||||||
if ([name hasPrefix: @"/"])
|
if ([name hasPrefix: @"/"])
|
||||||
|
@ -547,49 +671,105 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
if ([name hasSuffix: @"/"])
|
if ([name hasSuffix: @"/"])
|
||||||
name = [name substringToIndex: [name length]-1];
|
name = [name substringToIndex: [name length]-1];
|
||||||
|
|
||||||
type = [[folderMetadata objectForKey: @"type"] activeSyncFolderType];
|
type = [[folderMetadata objectForKey: @"type"] activeSyncFolderType];
|
||||||
|
|
||||||
parentId = @"0";
|
parentId = @"0";
|
||||||
|
|
||||||
if ([folderMetadata objectForKey: @"parent"])
|
if ([folderMetadata objectForKey: @"parent"])
|
||||||
{
|
{
|
||||||
parentId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"parent"]];
|
parentId = [NSString stringWithFormat: @"mail/%@", [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]];
|
||||||
name = [[name pathComponents] lastObject];
|
name = [[name pathComponents] lastObject];
|
||||||
}
|
}
|
||||||
|
|
||||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>",
|
// Decide between add and change
|
||||||
[serverId stringByEscapingURL],
|
if ([cachedGUIDs objectForKey: [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]])
|
||||||
[parentId stringByEscapingURL],
|
{
|
||||||
type,
|
// Search GUID to check name change in cache (diff between IMAP and cache)
|
||||||
[name activeSyncRepresentationInContext: context]];
|
if ((![[[folderMetadata objectForKey: @"path"] substringFromIndex: 1] isEqualToString: [imapGUIDs objectForKey: [cachedGUIDs objectForKey:
|
||||||
|
[[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]]))
|
||||||
|
{
|
||||||
|
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey:
|
||||||
|
[imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]];
|
||||||
|
nkey = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] ];
|
||||||
|
|
||||||
|
if (![key isEqualToString: nkey])
|
||||||
|
{
|
||||||
|
[commands appendFormat: @"<Update><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Update>",
|
||||||
|
[serverId stringByEscapingURL],
|
||||||
|
[parentId stringByEscapingURL],
|
||||||
|
type,
|
||||||
|
[name activeSyncRepresentationInContext: context]];
|
||||||
|
|
||||||
|
|
||||||
|
// Change path in cache
|
||||||
|
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||||
|
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||||
|
[o setTableUrl: [self folderTableURL]];
|
||||||
|
[o reloadIfNeeded];
|
||||||
|
[o changePathTo: [NSString stringWithFormat: @"/%@", nkey]]; // ?? why is '/' prefix needed - problem in changePathTo?
|
||||||
|
|
||||||
|
command_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[commands appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>",
|
||||||
|
[serverId stringByEscapingURL],
|
||||||
|
[parentId stringByEscapingURL],
|
||||||
|
type,
|
||||||
|
[name activeSyncRepresentationInContext: context]];
|
||||||
|
|
||||||
|
// Store folder's GUID in cache
|
||||||
|
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]];
|
||||||
|
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||||
|
[o setObjectType: ActiveSyncFolderCacheObject];
|
||||||
|
[o setTableUrl: [self folderTableURL]];
|
||||||
|
[o reloadIfNeeded];
|
||||||
|
|
||||||
|
[[o properties ] setObject: [imapGUIDs objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]] forKey: @"GUID"];
|
||||||
|
[o save];
|
||||||
|
|
||||||
|
command_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We add the personal calendar - events
|
|
||||||
// FIXME: add all calendars
|
|
||||||
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
|
|
||||||
name = [NSString stringWithFormat: @"vevent/%@", [currentFolder nameInContainer]];
|
|
||||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 8, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
|
||||||
|
|
||||||
// We add the personal calendar - tasks
|
|
||||||
// FIXME: add all calendars
|
|
||||||
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
|
|
||||||
name = [NSString stringWithFormat: @"vtodo/%@", [currentFolder nameInContainer]];
|
|
||||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 7, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
|
||||||
|
|
||||||
// We add the personal address book
|
if (first_sync)
|
||||||
// FIXME: add all address books
|
[s appendFormat: @"<Count>%d</Count>", command_count+3];
|
||||||
currentFolder = [[context activeUser] personalContactsFolderInContext: context];
|
else
|
||||||
name = [NSString stringWithFormat: @"vcard/%@", [currentFolder nameInContainer]];
|
[s appendFormat: @"<Count>%d</Count>", command_count];
|
||||||
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 9, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
|
||||||
|
if (command_count > 0)
|
||||||
|
[s appendFormat: @"%@", commands];
|
||||||
|
|
||||||
|
if (first_sync)
|
||||||
|
{
|
||||||
|
// We add the personal calendar - events
|
||||||
|
// FIXME: add all calendars
|
||||||
|
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
|
||||||
|
name = [NSString stringWithFormat: @"vevent/%@", [currentFolder nameInContainer]];
|
||||||
|
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 8, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||||
|
|
||||||
|
// We add the personal calendar - tasks
|
||||||
|
// FIXME: add all calendars
|
||||||
|
currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
|
||||||
|
name = [NSString stringWithFormat: @"vtodo/%@", [currentFolder nameInContainer]];
|
||||||
|
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 7, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||||
|
|
||||||
|
// We add the personal address book
|
||||||
|
// FIXME: add all address books
|
||||||
|
currentFolder = [[context activeUser] personalContactsFolderInContext: context];
|
||||||
|
name = [NSString stringWithFormat: @"vcard/%@", [currentFolder nameInContainer]];
|
||||||
|
[s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 9, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[s appendString: @"</Changes></FolderSync>"];
|
[s appendString: @"</Changes></FolderSync>"];
|
||||||
|
|
||||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||||
|
|
||||||
[theResponse setContent: d];
|
[theResponse setContent: d];
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// From: http://msdn.microsoft.com/en-us/library/ee157980(v=exchg.80).aspx :
|
// From: http://msdn.microsoft.com/en-us/library/ee157980(v=exchg.80).aspx :
|
||||||
|
@ -636,6 +816,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
||||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||||
|
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||||
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -710,6 +891,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
fileReference = [[[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
fileReference = [[[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
||||||
|
|
||||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||||
|
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||||
|
|
||||||
if (folderType == ActiveSyncMailFolder)
|
if (folderType == ActiveSyncMailFolder)
|
||||||
{
|
{
|
||||||
|
@ -803,6 +985,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
status = 1;
|
status = 1;
|
||||||
|
|
||||||
realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
|
realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &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];
|
requestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue];
|
||||||
appointmentObject = nil;
|
appointmentObject = nil;
|
||||||
|
@ -953,6 +1136,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
NSDictionary *response;
|
NSDictionary *response;
|
||||||
NSString *v;
|
NSString *v;
|
||||||
|
|
||||||
|
srcFolderId = [self globallyUniqueIDToIMAPFolderName: srcFolderId type: srcFolderType];
|
||||||
|
dstFolderId = [self globallyUniqueIDToIMAPFolderName: dstFolderId type: dstFolderType];
|
||||||
|
|
||||||
currentCollection = [self collectionFromId: srcFolderId type: srcFolderType];
|
currentCollection = [self collectionFromId: srcFolderId type: srcFolderType];
|
||||||
|
|
||||||
client = [[currentCollection imap4Connection] client];
|
client = [[currentCollection imap4Connection] client];
|
||||||
|
@ -1496,6 +1682,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue];
|
folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue];
|
||||||
itemId = [[(id)[theDocumentElement getElementsByTagName: @"ItemId"] lastObject] textValue];
|
itemId = [[(id)[theDocumentElement getElementsByTagName: @"ItemId"] lastObject] textValue];
|
||||||
realCollectionId = [folderId realCollectionIdWithFolderType: &folderType];
|
realCollectionId = [folderId realCollectionIdWithFolderType: &folderType];
|
||||||
|
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||||
|
|
||||||
value = [theDocumentElement getElementsByTagName: @"ReplaceMime"];
|
value = [theDocumentElement getElementsByTagName: @"ReplaceMime"];
|
||||||
|
|
||||||
|
|
1
NEWS
1
NEWS
|
@ -17,6 +17,7 @@ Enhancements
|
||||||
- updated French translation
|
- updated French translation
|
||||||
- added more sycned contact properties when using ActiveSync (#2775)
|
- added more sycned contact properties when using ActiveSync (#2775)
|
||||||
- now possible to configure the default subscribed resource name using SOGoSubscriptionFolderFormat
|
- now possible to configure the default subscribed resource name using SOGoSubscriptionFolderFormat
|
||||||
|
- now handle server-side folder updates using ActiveSync (#2688)
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
- fixed saved HTML content of draft when attaching a file
|
- fixed saved HTML content of draft when attaching a file
|
||||||
|
|
|
@ -84,6 +84,8 @@ typedef enum {
|
||||||
- (NSArray *) allFolderPaths;
|
- (NSArray *) allFolderPaths;
|
||||||
- (NSArray *) allFoldersMetadata;
|
- (NSArray *) allFoldersMetadata;
|
||||||
|
|
||||||
|
- (NSDictionary *) imapFolderGUIDs;
|
||||||
|
|
||||||
- (BOOL) isInDraftsFolder;
|
- (BOOL) isInDraftsFolder;
|
||||||
|
|
||||||
/* special folders */
|
/* special folders */
|
||||||
|
|
|
@ -60,6 +60,8 @@
|
||||||
#import "SOGoUser+Mailer.h"
|
#import "SOGoUser+Mailer.h"
|
||||||
|
|
||||||
#import "SOGoMailAccount.h"
|
#import "SOGoMailAccount.h"
|
||||||
|
#import <Foundation/NSProcessInfo.h>
|
||||||
|
|
||||||
|
|
||||||
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
|
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
|
||||||
|
|
||||||
|
@ -659,6 +661,71 @@ static NSString *inboxFolderName = @"INBOX";
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (NSDictionary *) imapFolderGUIDs
|
||||||
|
{
|
||||||
|
NSDictionary *result, *nresult, *folderData;
|
||||||
|
NSMutableDictionary *folders;
|
||||||
|
NGImap4Client *client;
|
||||||
|
SOGoUserDefaults *ud;
|
||||||
|
NSArray *folderList;
|
||||||
|
NSEnumerator *e;
|
||||||
|
NSString *guid;
|
||||||
|
id object;
|
||||||
|
|
||||||
|
BOOL subscribedOnly;
|
||||||
|
|
||||||
|
ud = [[context activeUser] userDefaults];
|
||||||
|
subscribedOnly = [ud mailShowSubscribedFoldersOnly];
|
||||||
|
|
||||||
|
folderList = [[self imap4Connection] allFoldersForURL: [self imap4URL]
|
||||||
|
onlySubscribedFolders: subscribedOnly];
|
||||||
|
|
||||||
|
folders = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
client = [[self imap4Connection] client];
|
||||||
|
result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"];
|
||||||
|
|
||||||
|
if (![[result objectForKey: @"result"] boolValue])
|
||||||
|
{
|
||||||
|
NSLog(@"IMAP annotation call failed.");
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = [folderList objectEnumerator];
|
||||||
|
|
||||||
|
while (object = [e nextObject])
|
||||||
|
{
|
||||||
|
guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"];
|
||||||
|
|
||||||
|
if (!guid)
|
||||||
|
{
|
||||||
|
guid = [[NSProcessInfo processInfo] globallyUniqueString];
|
||||||
|
nresult = [client annotation: [object substringFromIndex: 1] entryName: @"/comment" attributeName: @"value.priv" attributeValue: guid];
|
||||||
|
|
||||||
|
if (![[nresult objectForKey: @"result"] boolValue])
|
||||||
|
{
|
||||||
|
// need to implement X-GUID query for Dovecot - this requires modification in SOPE to support following command:
|
||||||
|
// 1 list "" "*" return (status (x-guid)) -> this would avoid firing a command per folder to IMAP
|
||||||
|
//guid = [NSString stringWithFormat: @"%@-%@", [object substringFromIndex: 1], @"xxx" ];
|
||||||
|
nresult = [client status: [object substringFromIndex: 1] flags: [NSArray arrayWithObject: @"x-guid"]];
|
||||||
|
guid = [nresult objectForKey: @"x-guid"];
|
||||||
|
if (!guid)
|
||||||
|
{
|
||||||
|
guid = [NSString stringWithFormat: @"%@", [object substringFromIndex: 1]];
|
||||||
|
}
|
||||||
|
NSLog(@"tfu setannotation failed: %@", nresult );
|
||||||
|
NSLog(@"tfu uniqueid folderid: %@", guid );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[folders setObject: guid forKey: [object substringFromIndex: 1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* name lookup */
|
/* name lookup */
|
||||||
|
|
||||||
- (id) lookupName: (NSString *) _key
|
- (id) lookupName: (NSString *) _key
|
||||||
|
|
|
@ -67,6 +67,9 @@ typedef enum {
|
||||||
- (NSDictionary *) lookupRecord: (NSString *) path
|
- (NSDictionary *) lookupRecord: (NSString *) path
|
||||||
newerThanVersion: (NSInteger) startVersion;
|
newerThanVersion: (NSInteger) startVersion;
|
||||||
|
|
||||||
|
- (NSArray *) folderList: (NSString *) deviceId
|
||||||
|
newerThanVersion: (NSInteger) startVersion;
|
||||||
|
|
||||||
- (void) setObjectType: (SOGoCacheObjectType) newObjectType;
|
- (void) setObjectType: (SOGoCacheObjectType) newObjectType;
|
||||||
- (SOGoCacheObjectType) objectType; /* message, fai, folder */
|
- (SOGoCacheObjectType) objectType; /* message, fai, folder */
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ static EOAttribute *textColumn = nil;
|
||||||
tableUrl = [container tableUrl];
|
tableUrl = [container tableUrl];
|
||||||
[tableUrl retain];
|
[tableUrl retain];
|
||||||
if (!tableUrl)
|
if (!tableUrl)
|
||||||
[NSException raise: @"MAPIStoreIOException"
|
[NSException raise: @"SOGoCacheIOException"
|
||||||
format: @"table url is not set for object '%@'", self];
|
format: @"table url is not set for object '%@'", self];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ static EOAttribute *textColumn = nil;
|
||||||
path = [NSMutableString stringWithFormat: @"/%@", nameInContainer];
|
path = [NSMutableString stringWithFormat: @"/%@", nameInContainer];
|
||||||
|
|
||||||
if ([path rangeOfString: @"//"].location != NSNotFound)
|
if ([path rangeOfString: @"//"].location != NSNotFound)
|
||||||
[NSException raise: @"MAPIStoreIOException"
|
[NSException raise: @"SOGoCacheIOException"
|
||||||
format: @"object path has not been properly set for"
|
format: @"object path has not been properly set for"
|
||||||
" folder '%@' (%@)",
|
" folder '%@' (%@)",
|
||||||
self, path];
|
self, path];
|
||||||
|
@ -190,7 +190,7 @@ static EOAttribute *textColumn = nil;
|
||||||
- (NSCalendarDate *) creationDate
|
- (NSCalendarDate *) creationDate
|
||||||
{
|
{
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
[NSException raise: @"MAPIStoreIOException"
|
[NSException raise: @"SOGoCacheIOException"
|
||||||
format: @"record has not been initialized: %@", self];
|
format: @"record has not been initialized: %@", self];
|
||||||
|
|
||||||
return creationDate;
|
return creationDate;
|
||||||
|
@ -199,7 +199,7 @@ static EOAttribute *textColumn = nil;
|
||||||
- (NSCalendarDate *) lastModified
|
- (NSCalendarDate *) lastModified
|
||||||
{
|
{
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
[NSException raise: @"MAPIStoreIOException"
|
[NSException raise: @"SOGoCacheIOException"
|
||||||
format: @"record has not been initialized: %@", self];
|
format: @"record has not been initialized: %@", self];
|
||||||
|
|
||||||
return lastModified;
|
return lastModified;
|
||||||
|
@ -350,7 +350,7 @@ static EOAttribute *textColumn = nil;
|
||||||
EOAdaptor *adaptor;
|
EOAdaptor *adaptor;
|
||||||
|
|
||||||
if ([path hasSuffix: @"/"])
|
if ([path hasSuffix: @"/"])
|
||||||
[NSException raise: @"MAPIStoreIOException"
|
[NSException raise: @"SOGoCacheIOException"
|
||||||
format: @"path ends with a slash: %@", path];
|
format: @"path ends with a slash: %@", path];
|
||||||
|
|
||||||
tableName = [self tableName];
|
tableName = [self tableName];
|
||||||
|
@ -375,6 +375,47 @@ static EOAttribute *textColumn = nil;
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get a list of all folders
|
||||||
|
- (NSArray *) folderList: (NSString *) deviceId
|
||||||
|
newerThanVersion: (NSInteger) startVersion
|
||||||
|
{
|
||||||
|
NSMutableArray *recordsOut;
|
||||||
|
NSArray *records;
|
||||||
|
NSString *tableName, *pathValue;
|
||||||
|
NSMutableString *sql;
|
||||||
|
EOAdaptor *adaptor;
|
||||||
|
NSUInteger count, max;
|
||||||
|
|
||||||
|
if ([deviceId hasSuffix: @"/"])
|
||||||
|
[NSException raise: @"SOGoCacheIOException"
|
||||||
|
format: @"path ends with a slash: %@", deviceId];
|
||||||
|
|
||||||
|
tableName = [self tableName];
|
||||||
|
adaptor = [self tableChannelAdaptor];
|
||||||
|
pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@+folder%", deviceId]
|
||||||
|
forAttribute: textColumn];
|
||||||
|
|
||||||
|
/* query */
|
||||||
|
sql = [NSMutableString stringWithFormat:
|
||||||
|
@"SELECT * FROM %@ WHERE c_path LIKE %@ AND c_deleted <> 1",
|
||||||
|
tableName, pathValue];
|
||||||
|
if (startVersion > -1)
|
||||||
|
[sql appendFormat: @" AND c_version > %d", startVersion];
|
||||||
|
|
||||||
|
/* execution */
|
||||||
|
records = [self performSQLQuery: sql];
|
||||||
|
|
||||||
|
max = [records count];
|
||||||
|
recordsOut = [[NSMutableArray alloc] init];
|
||||||
|
for (count = 0; count < max; count++)
|
||||||
|
{
|
||||||
|
[recordsOut addObject: [[records objectAtIndex: count] objectForKey: @"c_path"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return recordsOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void) reloadIfNeeded
|
- (void) reloadIfNeeded
|
||||||
{
|
{
|
||||||
/* if object is uninitialized: reload without condition, otherwise, load if
|
/* if object is uninitialized: reload without condition, otherwise, load if
|
||||||
|
@ -428,7 +469,7 @@ static EOAttribute *textColumn = nil;
|
||||||
NSException *result;
|
NSException *result;
|
||||||
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
[NSException raise: @"MAPIStoreIOException"
|
[NSException raise: @"SOGoCacheIOException"
|
||||||
format: @"record has not been initialized: %@", self];
|
format: @"record has not been initialized: %@", self];
|
||||||
|
|
||||||
cm = [GCSChannelManager defaultChannelManager];
|
cm = [GCSChannelManager defaultChannelManager];
|
||||||
|
@ -447,7 +488,7 @@ static EOAttribute *textColumn = nil;
|
||||||
lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970];
|
lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970];
|
||||||
|
|
||||||
if (objectType == -1)
|
if (objectType == -1)
|
||||||
[NSException raise: @"MAPIStoreIOException"
|
[NSException raise: @"SOGoCacheIOException"
|
||||||
format: @"object type has not been set for object '%@'",
|
format: @"object type has not been set for object '%@'",
|
||||||
self];
|
self];
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue