From f0e459ca448eadefd315a7eb952db50fb5de8a0e Mon Sep 17 00:00:00 2001
From: root <root@example.com>
Date: Sun, 25 May 2014 23:57:54 +0200
Subject: [PATCH] foldersync

---
 ActiveSync/SOGoActiveSyncDispatcher+Sync.m |   33 ++--
 ActiveSync/SOGoActiveSyncDispatcher.m      |  254 +++++++++++++++++++++++-----
 SoObjects/Mailer/SOGoMailAccount.h         |    2 +
 SoObjects/Mailer/SOGoMailAccount.m         |   57 +++++++
 SoObjects/SOGo/SOGoCacheGCSObject.h        |    3 +
 SoObjects/SOGo/SOGoCacheGCSObject.m        |   41 +++++
 6 files changed, 340 insertions(+), 50 deletions(-)

diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
index 49cc21c..91f5d5d 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
@@ -123,7 +123,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   [o setTableUrl: [self folderTableURL]];
   [o reloadIfNeeded];
   
-  [[o properties] removeAllObjects];
+  //[[o properties] removeAllObjects];
+  [[o properties] removeObjectForKey: @"SyncCache"];
+  [[o properties] removeObjectForKey: @"DateCache"];
+
   [[o properties] addEntriesFromDictionary: theFolderMetadata];
   [o save];
 }
@@ -146,7 +149,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 //
 // <?xml version="1.0"?>
-// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
+// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microm/">
 // <Sync xmlns="AirSync:">
 //  <Collections>
 //   <Collection>
@@ -627,20 +630,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                                                                             sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
           }
         
-        // If it's a new Sync operation, ignore anything we might have
-        // in our preferences.
+        // If it's a new Sync operation, DateCache and SyncCache need to be deleted
+        // but GUID stored by folderSync shouldn't be touched
+        folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
         if ([theSyncKey isEqualToString: @"-1"])
           {
-            folderMetadata = [NSMutableDictionary dictionary];
-            
             [folderMetadata setObject: [NSMutableDictionary dictionary]  forKey: @"SyncCache"];
             [folderMetadata setObject: [NSMutableDictionary dictionary]  forKey: @"DateCache"];
-            
-            // TODO - Generate GUID
-            //[folderMetadata setObject: @"FOO-BAR-BAZ"  forKey: @"GUID"];
           }
-        else
-          folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
+        // check whether GUID in cache is equal to the GUID from imap - this is to avoid cache corruptions if a folder has been renamed and a new folder
+        // with the same name has been created but folderSync has not yet updated the cache
+        if (!([[theCollection nameInContainer] isEqualToString: 
+                      [NSString stringWithFormat: @"folder%@", [self _translateGuidToImapFolderName: [folderMetadata objectForKey: @"GUID"] type: theFolderType]]])) {
+           NSLog(@"guid mismatch don't sync now!"); // not sure whether it is ok just to return
+           return;
+	}
 
         syncCache = [folderMetadata objectForKey: @"SyncCache"];
         dateCache = [folderMetadata objectForKey: @"DateCache"];
@@ -711,6 +715,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     [s appendString: @"</Delete>"];
                     
                     [syncCache removeObjectForKey: [aCacheObject uid]];
+                    [dateCache removeObjectForKey: [aCacheObject uid]];
                   }
                 else
                   {
@@ -773,13 +778,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     //NSLog(@"skipping old deleted UID: %@",  [aCacheObject uid]);
                   }
               }
+
           }
-        
+
         if (more_available)
           [folderMetadata setObject: [NSNumber numberWithBool: YES]  forKey: @"MoreAvailable"];
         else
           [folderMetadata removeObjectForKey: @"MoreAvailable"];
         
+
         [self _setFolderMetadata: folderMetadata
                           forKey: [theCollection nameInContainer]];
       } // default:
@@ -890,6 +897,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
   collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
   realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
+  realCollectionId = [self _translateGuidToImapFolderName: realCollectionId type: folderType];
   collection = [self collectionFromId: realCollectionId  type: folderType];
   
   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];
     }
 
+
   // Generate the response buffer
   [theBuffer appendString: @"<Collection>"];
   
diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m
index 40cefbf..48d5c53 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher.m
@@ -129,7 +129,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   [super init];
 
   folderTableURL = nil;
-  
   return self;
 }
 
@@ -165,6 +164,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   return [o properties];
 }
 
+- (id) _translateGuidToImapFolderName: (NSString *) theIdToTranslate  type: (SOGoMicrosoftActiveSyncFolderType) theFolderType
+{
+
+  SOGoMailAccounts *accountsFolder;
+  SOGoMailAccount *accountFolder;
+  SOGoUserFolder *userFolder;
+
+  switch (theFolderType)
+    {
+    case ActiveSyncMailFolder:
+    {
+
+  NSArray *allFoldersMetadata;
+
+  userFolder = [[context activeUser] homeFolderInContext: context];
+  accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
+  accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
+
+  // get guids of folder (imap)
+  NSDictionary *imapGuids = [accountFolder getImapFolderGuids];
+  return [[imapGuids allKeysForObject: theIdToTranslate] objectAtIndex: 0];
+}
+break;
+  default: 
+  {
+  return theIdToTranslate;
+}
+  }
+ 
+
+}
+
+
 //
 //
 //
@@ -199,6 +231,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
         accountsFolder = [userFolder lookupName: @"Mail"  inContext: context  acquire: NO];
         currentFolder = [accountsFolder lookupName: @"0"  inContext: context  acquire: NO];
         
+        SOGoMailAccount *accountFolder;
+        accountFolder = [accountsFolder lookupName: @"0"  inContext: context  acquire: NO];
+        
         collection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", theCollectionId]
                                      inContext: context
                                        acquire: NO];
@@ -250,12 +285,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
           newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", [displayName stringByEncodingImap4FolderName]]
                                       inContext: context
                                         acquire: NO];
-        else
-          newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@/%@", [[parentId stringByUnescapingURL] substringFromIndex: 5],
+        else {
+          parentId = [self _translateGuidToImapFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type:ActiveSyncMailFolder];
+          newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@/%@", [parentId stringByEncodingImap4FolderName],
                                                            [displayName stringByEncodingImap4FolderName]]
                                       inContext: context
                                         acquire: NO];
-
+        }
         // FIXME
         // handle exists (status == 2)
         // handle right synckey
@@ -265,6 +301,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
             
             // We strip the "folder" prefix
             nameInContainer = [nameInContainer substringFromIndex: 6];
+
+            // save new guid into cache
+            SOGoMailAccount *accountFolder;
+            accountFolder = [accountsFolder lookupName: @"0"  inContext: context  acquire: NO];
+
+            // update GUID in cache
+            NSDictionary *imapGuids = [accountFolder getImapFolderGuids];
+
+            NSString *key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], nameInContainer ];
+            SOGoCacheGCSObject *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];
           }
         else
@@ -348,6 +402,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
   
   serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
+  serverId = [self _translateGuidToImapFolderName: serverId type:folderType];
 
   userFolder = [[context activeUser] homeFolderInContext: context];
   accountsFolder = [userFolder lookupName: @"Mail"  inContext: context  acquire: NO];
@@ -408,6 +463,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   int status;
   
   serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
+  serverId = [self _translateGuidToImapFolderName: serverId type:folderType];
   parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue];
   displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue];
 
@@ -427,7 +483,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     }
   else
     {
-      error = [folderToUpdate renameTo: [NSString stringWithFormat: @"%@/%@", [[parentId stringByUnescapingURL] substringFromIndex: 5],
+      parentId = [self _translateGuidToImapFolderName: [[parentId stringByUnescapingURL] substringFromIndex: 5] type:folderType];
+      error = [folderToUpdate renameTo: [NSString stringWithFormat: @"%@/%@", [parentId stringByEncodingImap4FolderName],
                                                   [displayName stringByEncodingImap4FolderName]]];
     }
 
@@ -445,7 +502,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
       // See http://msdn.microsoft.com/en-us/library/gg675615(v=exchg.80).aspx
       // we return '9' - we force a FolderSync
-      status = 9;
+      status = 1;
 
       [self _setFolderSyncKey: syncKey];
 
@@ -481,12 +538,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 - (void) processFolderSync: (id <DOMElement>) theDocumentElement
                 inResponse: (WOResponse *) theResponse
 {
+
   NSMutableDictionary *metadata;
   NSMutableString *s;
   NSString *syncKey;
   NSData *d;
-  
-  BOOL first_sync;
+ 
+  BOOL first_sync, found;
   int status;
 
   metadata = [self _globalMetadataForDevice];
@@ -512,10 +570,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   [s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
   [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];
-  
-  // Initial sync, let's return the complete folder list
-  if (first_sync)
+ 
+  if (status = 1)
     {
+
       SOGoMailAccounts *accountsFolder;
       SOGoMailAccount *accountFolder;
       SOGoUserFolder *userFolder;
@@ -525,21 +583,86 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       NSArray *allFoldersMetadata;
       NSString *name, *serverId, *parentId;
 
-      int i, type;
-      
+      int i, type, command_count;
+      NSMutableString *commands;
+
+      SOGoCacheGCSObject *o;
+      NSString *key, *nkey;
+
       userFolder = [[context activeUser] homeFolderInContext: context];
-      accountsFolder = [userFolder lookupName: @"Mail"  inContext: context  acquire: NO];
-      accountFolder = [accountsFolder lookupName: @"0"  inContext: context  acquire: NO];
+      accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
+      accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
 
       allFoldersMetadata = [accountFolder allFoldersMetadata];
+  
+      // get guids of folder (imap)
+      // e.g. {INBOX = "sogo_73c_192bd57b_d8"
+      NSDictionary *imapGuids = [accountFolder getImapFolderGuids];
+
+      NSMutableDictionary *cacheGuids = [[NSMutableDictionary alloc] init];
+      NSArray *foldersInCache = [[NSArray alloc] init];
+
+      // no need to read cached folder infos during first sync
+      // e.g. {"sogo_73c_192bd57b_d8" = INBOX} - guid = foldername for easy reverse lookup with imapGuids
+      if (!first_sync) {
+         // 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 (NSString *folderName in foldersInCache) {
+             key = [folderName substringFromIndex: 1];
+   
+             o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
+             [o setObjectType: ActiveSyncFolderCacheObject];
+             [o setTableUrl: [self folderTableURL]];
+             [o reloadIfNeeded];
+
+             if ([[o properties ]  objectForKey: @"GUID"])
+                [cacheGuids setObject:  [key substringFromIndex:[key rangeOfString: @"+"].location+7] forKey: [[o properties ]  objectForKey: @"GUID"]];
+         }
+      }
+
+      // handle folders deleted in imap
+      command_count=0;
+      commands = [NSMutableString string];
 
-      // See 2.2.3.170.3 Type (FolderSync) - http://msdn.microsoft.com/en-us/library/gg650877(v=exchg.80).aspx
-      [s appendFormat: @"<Count>%d</Count>", [allFoldersMetadata count]+3];
+      for (NSString *cKey in cacheGuids)
+      {
 
+         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"], [cacheGuids objectForKey: cKey]];
+            o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
+            [o setObjectType: ActiveSyncFolderCacheObject];
+            [o setTableUrl: [self folderTableURL]];
+            [o reloadIfNeeded];
+
+            // only send a delete 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++)
         {
           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"];
           
           if ([name hasPrefix: @"/"])
@@ -547,24 +670,75 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
           
           if ([name hasSuffix: @"/"])
             name = [name substringToIndex: [name length]-1];
-
+  
           type = [[folderMetadata objectForKey: @"type"] activeSyncFolderType];
 
           parentId = @"0";
 
           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];
             }
 
-          [s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>",
-             [serverId stringByEscapingURL],
-             [parentId stringByEscapingURL],
-             type,
-             [name activeSyncRepresentationInContext: context]];
-        }
-
+          // decide between add and change
+          if ([cacheGuids objectForKey: [imapGuids objectForKey: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]])  {
+             // search guid to check name change in cache (diff between imap and cache)
+             if ((![ [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] isEqualToString: [imapGuids objectForKey: [cacheGuids objectForKey: 
+                                         [[folderMetadata objectForKey: @"path"] substringFromIndex: 1]]]] )) {
+                 key =  [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cacheGuids 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++;
+               }
+       }
+
+       if (first_sync)
+             [s appendFormat: @"<Count>%d</Count>", command_count+3];
+       else
+             [s appendFormat: @"<Count>%d</Count>", command_count];
+
+       if (command_count > 0)
+         [s appendFormat: @"%@", commands];
+
+     if (first_sync)
+     {
       // We add the personal calendar - events
       // FIXME: add all calendars
       currentFolder = [[context activeUser] personalCalendarFolderInContext: context];
@@ -582,14 +756,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       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>"];
 
   d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
 
   [theResponse setContent: d];
-}
+
+} 
 
 //
 // From: http://msdn.microsoft.com/en-us/library/ee157980(v=exchg.80).aspx :
@@ -636,6 +812,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
   collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
   realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
+  realCollectionId = [self _translateGuidToImapFolderName: realCollectionId type:folderType];
   currentCollection = [self collectionFromId: realCollectionId  type: folderType];
   
   //
@@ -710,6 +887,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   fileReference = [[[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
 
   realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
+  realCollectionId = [self _translateGuidToImapFolderName: realCollectionId type:folderType];
   
   if (folderType == ActiveSyncMailFolder)
     {
@@ -803,6 +981,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   status = 1;
 
   realCollectionId = [[[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
+  realCollectionId = [self _translateGuidToImapFolderName: realCollectionId type:folderType];
   userResponse = [[[(id)[theDocumentElement getElementsByTagName: @"UserResponse"] lastObject] textValue] intValue];
   requestId = [[(id)[theDocumentElement getElementsByTagName: @"RequestId"] lastObject] textValue];  
   appointmentObject = nil;
@@ -953,6 +1132,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
           NSDictionary *response;
           NSString *v;
           
+  srcFolderId = [self _translateGuidToImapFolderName: srcFolderId type:srcFolderType];
+  dstFolderId = [self _translateGuidToImapFolderName: dstFolderId type:dstFolderType];
+
           currentCollection = [self collectionFromId: srcFolderId  type: srcFolderType];
           
           client = [[currentCollection imap4Connection] client];
@@ -1476,6 +1658,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue];
   itemId = [[(id)[theDocumentElement getElementsByTagName: @"ItemId"] lastObject] textValue];
   realCollectionId = [folderId realCollectionIdWithFolderType: &folderType];
+  realCollectionId = [self _translateGuidToImapFolderName: realCollectionId type:folderType];
 
   value = [theDocumentElement getElementsByTagName: @"ReplaceMime"];
 
@@ -1705,10 +1888,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   if (!folderTableURL)
     {
       user = [context activeUser];
-
-      if (![user loginInDomain])
-        return nil;
-
       urlString = [[user domainDefaults] folderInfoURL];
       parts = [[urlString componentsSeparatedByString: @"/"]
                 mutableCopy];
@@ -1718,7 +1897,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
           /* If "OCSFolderInfoURL" is properly configured, we must have 5
              parts in this url. */
           ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
-                                    [[user loginInDomain] asCSSIdentifier]];
+                                    [[[context activeUser] loginInDomain] asCSSIdentifier]];
           [parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
           folderTableURL
             = [NSURL URLWithString: [parts componentsJoinedByString: @"/"]];
@@ -1746,10 +1925,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
   /* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */
   tableName = [[folderTableURL path] lastPathComponent];
-  if (tableName &&
-      [channel evaluateExpressionX:
-                 [NSString stringWithFormat: @"SELECT count(*) FROM %@",
-                           tableName]])
+  if ([channel evaluateExpressionX:
+        [NSString stringWithFormat: @"SELECT count(*) FROM %@",
+                  tableName]])
     {
       queries = [channel specialQueries];
       query = [queries createSOGoCacheGCSFolderTableWithName: tableName];
diff --git a/SoObjects/Mailer/SOGoMailAccount.h b/SoObjects/Mailer/SOGoMailAccount.h
index 2efc05f..37f002c 100644
--- a/SoObjects/Mailer/SOGoMailAccount.h
+++ b/SoObjects/Mailer/SOGoMailAccount.h
@@ -84,6 +84,8 @@ typedef enum {
 - (NSArray *) allFolderPaths;
 - (NSArray *) allFoldersMetadata;
 
+- (NSDictionary *) getImapFolderGuids;
+
 - (BOOL) isInDraftsFolder;
 
 /* special folders */
diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m
index 5f9a67c..d56ffef 100644
--- a/SoObjects/Mailer/SOGoMailAccount.m
+++ b/SoObjects/Mailer/SOGoMailAccount.m
@@ -60,6 +60,8 @@
 #import "SOGoUser+Mailer.h"
 
 #import "SOGoMailAccount.h"
+#import <Foundation/NSProcessInfo.h>
+
 
 #define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
 
@@ -659,6 +661,61 @@ static NSString *inboxFolderName = @"INBOX";
   return password;
 }
 
+
+- (NSDictionary *) getImapFolderGuids
+{
+  NSDictionary *result, *nresult, *folderData;
+  NGImap4Client *client;
+  NSMutableDictionary *folders;
+  NSArray *folderList;
+
+  SOGoUserDefaults *ud;
+  BOOL subscribedOnly;
+
+  ud = [[context activeUser] userDefaults];
+  subscribedOnly = [ud mailShowSubscribedFoldersOnly];
+
+  folderList = [[self imap4Connection] allFoldersForURL: [self imap4URL]
+                               onlySubscribedFolders: subscribedOnly];
+
+  folders = [[NSMutableDictionary alloc] init];
+
+  client = [[self imap4Connection] client];
+  result = [client annotation: @"*"  entryName: @"/comment" attributeName: @"value.priv"];
+
+  if (![[result objectForKey: @"result"] boolValue]) {
+     NSLog(@"tfu getannotation failed: x %@", [result objectForKey: @"ResponseResult"]);
+  }
+
+  NSEnumerator *e = [folderList objectEnumerator];
+  id object;
+  NSString *guid;
+  while (object = [e nextObject]) {
+
+   if (!(guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"])) {
+            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 firinig a command per folder to imap
+               //guid = [NSString stringWithFormat: @"%@-%@", [object substringFromIndex: 1],  @"xxx" ];
+               nresult = [client status: [object substringFromIndex: 1] flags: [NSArray arrayWithObject: @"x-guid"]];
+               if (!(guid = [nresult objectForKey: @"x-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 */
 
 - (id) lookupName: (NSString *) _key
diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.h b/SoObjects/SOGo/SOGoCacheGCSObject.h
index 8d26a49..3b2a8d4 100644
--- a/SoObjects/SOGo/SOGoCacheGCSObject.h
+++ b/SoObjects/SOGo/SOGoCacheGCSObject.h
@@ -67,6 +67,9 @@ typedef enum {
 - (NSDictionary *) lookupRecord: (NSString *) path
                newerThanVersion: (NSInteger) startVersion;
 
+- (NSArray *) folderList: (NSString *) deviceId
+               newerThanVersion: (NSInteger) startVersion;
+
 - (void) setObjectType: (SOGoCacheObjectType) newObjectType;
 - (SOGoCacheObjectType) objectType; /* message, fai, folder */
 
diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.m b/SoObjects/SOGo/SOGoCacheGCSObject.m
index bb2e690..232ba82 100644
--- a/SoObjects/SOGo/SOGoCacheGCSObject.m
+++ b/SoObjects/SOGo/SOGoCacheGCSObject.m
@@ -375,6 +375,47 @@ static EOAttribute *textColumn = nil;
   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: @"MAPIStoreIOException"
+                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
 {
   /* if object is uninitialized: reload without condition, otherwise, load if
-- 
1.7.9.5

