From 0cb65ec8fe740b4a9518937f1ff2792f6208ed2b Mon Sep 17 00:00:00 2001
From: root <root@example.com>
Date: Sun, 13 Apr 2014 20:37:14 +0200
Subject: [PATCH] folder sync

---
 ActiveSync/NSString+ActiveSync.m      |    6 +
 ActiveSync/SOGoActiveSyncDispatcher.m |  245 +++++++++++++++++++++++++++++----
 SoObjects/Mailer/SOGoMailAccount.m    |   30 +++-
 SoObjects/Mailer/SOGoMailFolder.h     |    3 +
 SoObjects/Mailer/SOGoMailFolder.m     |   35 +++++
 5 files changed, 291 insertions(+), 28 deletions(-)

diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m
index b1fba0f..0d4140f 100644
--- a/ActiveSync/NSString+ActiveSync.m
+++ b/ActiveSync/NSString+ActiveSync.m
@@ -107,6 +107,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     {
       realCollectionId = [[v stringByUnescapingURL] substringFromIndex: 5];
       *folderType = ActiveSyncMailFolder;
+      
+      // tfu strip uidvalidity from mail-serverId (path-uidvalidity)
+      NSRange r1;
+      r1 = [realCollectionId rangeOfString: @"-"];
+      realCollectionId = [realCollectionId  substringToIndex: r1.location];
+
     }
   else
     {
diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m
index 7d99ceb..e2b3941 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher.m
@@ -133,6 +133,49 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   [[[context activeUser] userSettings] synchronize];
 }
 
+- (void) _setFolder: (NSString *) theUIDValidity
+                     foldername: (NSString *) theFolderName
+{
+  NSMutableDictionary *metadata;
+  
+  metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]];
+  
+  [metadata setObject: [NSDictionary dictionaryWithObject: theFolderName  forKey: @"FolderName"]  forKey: theUIDValidity];
+
+  [[[context activeUser] userSettings] setMicrosoftActiveSyncMetadata: metadata
+                                                               forDevice: [context objectForKey: @"DeviceId"]];
+
+  [[[context activeUser] userSettings] synchronize];
+}
+
+- (void) _setDeleteFolder: (NSString *) theUIDValidity
+{
+  NSMutableDictionary *metadata;
+  
+  metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]];
+  
+  [metadata removeObjectForKey: theUIDValidity];
+
+  [[[context activeUser] userSettings] setMicrosoftActiveSyncMetadata: metadata
+                                                               forDevice: [context objectForKey: @"DeviceId"]];
+
+  [[[context activeUser] userSettings] synchronize];
+}
+
+- (void) _clear
+{
+  NSMutableDictionary *metadata;
+  
+  metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]];
+  
+  [metadata removeAllObjects];
+
+  [[[context activeUser] userSettings] setMicrosoftActiveSyncMetadata: metadata
+                                                               forDevice: [context objectForKey: @"DeviceId"]];
+
+  [[[context activeUser] userSettings] synchronize];
+}
+
 //
 //
 //
@@ -190,6 +233,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   int type;
 
   parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue];
+ 
+  // strip uidvalidity from parentId (path-uidvalidity)
+  if (![parentId isEqualToString: @"0"]) {
+         NSRange r1;
+         r1 = [parentId rangeOfString: @"-"];
+         parentId = [parentId  substringToIndex: r1.location];
+  }
+
   displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue];
   type = [[[(id)[theDocumentElement getElementsByTagName: @"Type"] lastObject] textValue] intValue];
   userFolder = [[context activeUser] homeFolderInContext: context];
@@ -213,10 +264,21 @@ 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];
         
+
+        // tfu if the parrent is 0 -> ok ; otherwise need to build the foldername based on parentId + displayName
+        if ([parentId isEqualToString: @"0"])
         newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", [displayName stringByEncodingImap4FolderName]]
                                     inContext: context
                                       acquire: NO];
-        
+
+        else
+		// uidvalidity - for parrent in subfolder  !!!
+        newFolder = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@/%@", [[parentId stringByUnescapingURL] substringFromIndex:5] , [displayName stringByEncodingImap4FolderName]]
+                                    inContext: context
+                                      acquire: NO];
+
+         // FIXME
+
         // FIXME
         // handle exists (status == 2)
         // handle right synckey
@@ -226,12 +288,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
             
             // We strip the "folder" prefix
             nameInContainer = [nameInContainer substringFromIndex: 6];
-            nameInContainer = [[NSString stringWithFormat: @"mail/%@", nameInContainer] stringByEscapingURL];
+
+            nameInContainer = [[NSString stringWithFormat: @"mail/%@-%@",  nameInContainer ,[newFolder getUIDValidity]] stringByEscapingURL];
+            // _setFolder to avoid <Add> during foldersync
+            [self _setFolder:    [nameInContainer stringByUnescapingURL]  foldername: [NSString stringWithFormat: @"/%@", [[newFolder nameInContainer]  substringFromIndex: 6]]];
+
           }
         else
           {
-            [theResponse setStatus: 500];
-            [theResponse appendContentString: @"Unable to create folder."];
+  //          [theResponse setStatus: 500];
+  //          [theResponse appendContentString: @"Unable to create folder."];
+
+            // trigger a folderresync if create fails - bad thing is that user doesn't get warned about the failure
+            s = [NSMutableString string];
+            [s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
+            [s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
+            [s appendString: @"<FolderCreate xmlns=\"FolderHierarchy:\">"];
+            [s appendFormat: @"<Status>%d</Status>", 5]; 
+            [s appendString: @"</FolderCreate>"];
+
+            d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
+  
+            [theResponse setContent: d];
+
             return;
           }
       }
@@ -347,8 +426,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     }
   else
     {
-      [theResponse setStatus: 500];
-      [theResponse appendContentString: @"Unable to delete folder."];
+     // [theResponse setStatus: 500];
+     // [theResponse appendContentString: @"Unable to delete folder."];
+
+      // trigger a folderresync if delete fails - bad thing is that user doesn't get warned about the failure
+      NSMutableString *s;
+      NSData *d;
+      s = [NSMutableString string];
+      [s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
+      [s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
+      [s appendString: @"<FolderDelete xmlns=\"FolderHierarchy:\">"];
+      [s appendFormat: @"<Status>%d</Status>", 4];
+      [s appendString: @"</FolderDelete>"];
+      d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
+      [theResponse setContent: d];
+      
+
     }
 }
 
@@ -369,7 +462,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   int status;
   
   serverId = [[[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue] realCollectionIdWithFolderType: &folderType];
+
   parentId = [[(id)[theDocumentElement getElementsByTagName: @"ParentId"] lastObject] textValue];
+
+  if (![parentId isEqualToString: @"0"]) {
+       NSRange r1;
+  r1 = [parentId rangeOfString: @"-"];
+  parentId = [parentId  substringToIndex: r1.location];
+  }
+
   displayName = [[(id)[theDocumentElement getElementsByTagName: @"DisplayName"] lastObject] textValue];
 
   userFolder = [[context activeUser] homeFolderInContext: context];
@@ -380,7 +481,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                                    inContext: context
                                      acquire: NO];
 
-  error = [folderToUpdate renameTo: displayName];
+  // tfu if parrent is 0 or displayname is not changed it is either a rename of a folder in 0 or a move to 0
+  if ([parentId isEqualToString: @"0"] ||([serverId hasSuffix: [NSString stringWithFormat: @"/%@", displayName]] && [parentId isEqualToString: @"0"] ))
+  {
+      error = [folderToUpdate renameTo: [NSString stringWithFormat: @"/%@", [displayName stringByEncodingImap4FolderName] ]];
+  }
+  else
+  {
+      error = [folderToUpdate renameTo: [NSString stringWithFormat: @"%@/%@", [[parentId stringByUnescapingURL] substringFromIndex:5] , [displayName stringByEncodingImap4FolderName] ]];
+  }
 
   // Handle new name exist
   if (!error)
@@ -396,7 +505,8 @@ 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 = 9;
+      status = 1;
 
       [self _setFolderSyncKey: syncKey];
 
@@ -414,8 +524,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     }
   else
     {
-      [theResponse setStatus: 500];
-      [theResponse appendContentString: @"Unable to update folder."];
+      //[theResponse setStatus: 500];
+      //[theResponse appendContentString: @"Unable to update folder."];
+
+      NSMutableString *s;
+      NSData *d;
+      s = [NSMutableString string];
+      [s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
+      [s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
+      [s appendString: @"<FolderUpdate xmlns=\"FolderHierarchy:\">"];
+      [s appendFormat: @"<Status>%d</Status>", 4]; // issue a folderSync
+      [s appendString: @"</FolderUpdate>"];
+      
+      d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
+      
+      [theResponse setContent: d];
     }
 }
 
@@ -431,16 +554,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                 inResponse: (WOResponse *) theResponse
 {
   NSMutableDictionary *metadata;
-  NSMutableString *s;
+  NSMutableString *s ,*s1;
   NSString *syncKey;
   NSData *d;
   
-  BOOL first_sync;
+  BOOL first_sync , found;
   int status;
 
   metadata = [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]];
   syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
   s = [NSMutableString string];
+  s1 = [NSMutableString string];
 
   first_sync = NO;
   status = 1;
@@ -449,6 +573,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     {
       first_sync = YES;
       syncKey = @"1";
+      // tfu clear saved folder structure
+      [self _clear];
     }
   else if (![syncKey isEqualToString: [[metadata objectForKey: @"FolderSync"] objectForKey: @"SyncKey"]])
     {
@@ -458,13 +584,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
   [self _setFolderSyncKey: syncKey];
 
+
   [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;
@@ -474,21 +601,51 @@ 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;
+
       userFolder = [[context activeUser] homeFolderInContext: context];
       accountsFolder = [userFolder lookupName: @"Mail"  inContext: context  acquire: NO];
       accountFolder = [accountsFolder lookupName: @"0"  inContext: context  acquire: NO];
 
       allFoldersMetadata = [accountFolder allFoldersMetadata];
 
-      // 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];
+      // tfu deal with deleted folders
+      NSEnumerator *keyEnum = [ [[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]] keyEnumerator];
+      NSString *key;
+
+      command_count=0;
+      commands = [NSMutableString string];
+
+      while ((key = [keyEnum nextObject]))
+      {
+         found = NO;
+
+         for (i = 0; i < [allFoldersMetadata count]; i++)
+         {
+            folderMetadata = [allFoldersMetadata objectAtIndex: i];
+
+            if ( [key isEqualToString:   [NSString stringWithFormat: @"mail%@-%@", [folderMetadata objectForKey: @"path"], [folderMetadata objectForKey: @"uidvalidity"]]]) {
+               found=YES;
+               break;
+            }
+         }
+
+         // foldersync as a key which should be deleleted here - need to have a better struct
+         if (!found && ![key isEqualToString: @"FolderSync"] ) {
+             [ self _setDeleteFolder: key];
+             [commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [key stringByEscapingURL]],
 
-      for (i = 0; i < [allFoldersMetadata count]; i++)
+             command_count++;
+         }
+
+     }
+
+     // tfu deail with addition and changes
+     for (i = 0; i < [allFoldersMetadata count]; i++)
         {
           folderMetadata = [allFoldersMetadata objectAtIndex: i];
-          serverId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"path"]];
+          serverId = [NSString stringWithFormat: @"mail%@-%@", [folderMetadata objectForKey: @"path"], [folderMetadata objectForKey: @"uidvalidity"]];
           name = [folderMetadata objectForKey: @"displayName"];
           
           if ([name hasPrefix: @"/"])
@@ -503,16 +660,45 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
           if ([folderMetadata objectForKey: @"parent"])
             {
-              parentId = [NSString stringWithFormat: @"mail%@", [folderMetadata objectForKey: @"parent"]];
+              parentId = [NSString stringWithFormat: @"mail%@-%@", [folderMetadata objectForKey: @"parent"], [folderMetadata objectForKey: @"parentuidvalidity"]];
               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]];
-        }
+         if ([[[[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]] objectForKey:  serverId] objectForKey: @"FolderName"]) {
+            if (![ [folderMetadata objectForKey: @"path"] isEqualToString: [[[[[context activeUser] userSettings] microsoftActiveSyncMetadataForDevice: [context objectForKey: @"DeviceId"]] objectForKey: serverId] objectForKey: @"FolderName"]]) {
+                [commands appendFormat: @"<Update><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Update>",
+                         [serverId stringByEscapingURL],
+                         [parentId stringByEscapingURL],
+                         type,
+                         [name activeSyncRepresentationInContext: context]];
+
+                command_count++;
+            }
+         }
+         else {
+                [commands appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>",
+                         [serverId stringByEscapingURL],
+                         [parentId stringByEscapingURL],
+                         type,
+                         [name activeSyncRepresentationInContext: context]];
+                 command_count++;
+         }
+
+        [self _setFolder:   serverId  foldername: [folderMetadata objectForKey: @"path"]];
+
+       }
+
+       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];
+
+  // Initial sync, let's return the complete folder list
+  if (first_sync)
+    {
 
       // We add the personal calendar - events
       // FIXME: add all calendars
@@ -533,6 +719,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       [s appendFormat: @"<Add><ServerId>%@</ServerId><ParentId>%@</ParentId><Type>%d</Type><DisplayName>%@</DisplayName></Add>", name, @"0", 9, [[currentFolder displayName] activeSyncRepresentationInContext: context]];
     }
 
+// end status = 1
+    }
+
+
+
   [s appendString: @"</Changes></FolderSync>"];
 
   d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m
index 5f9a67c..7107e17 100644
--- a/SoObjects/Mailer/SOGoMailAccount.m
+++ b/SoObjects/Mailer/SOGoMailAccount.m
@@ -60,6 +60,7 @@
 #import "SOGoUser+Mailer.h"
 
 #import "SOGoMailAccount.h"
+#import <Foundation/NSProcessInfo.h>
 
 #define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
 
@@ -435,7 +436,7 @@ static NSString *inboxFolderName = @"INBOX";
 //
 - (NSArray *) allFoldersMetadata
 {
-  NSString *currentFolder, *currentDecodedFolder, *currentDisplayName, *currentFolderType, *login, *fullName, *parent;
+  NSString *currentFolder, *currentDecodedFolder, *currentDisplayName, *currentFolderType, *login, *fullName, *parent, *uidvalidity, *parentuidvalidity;
   NSMutableArray *pathComponents, *folders;
   SOGoUserManager *userManager;
   NSEnumerator *rawFolders;
@@ -497,12 +498,19 @@ static NSString *inboxFolderName = @"INBOX";
 
       parent = [self _parentForFolder: currentFolder  foldersList: allFolderPaths];
       
+      // tfu uidvalidity is used for foldersync 
+      uidvalidity = [self getUIDValidityForFolder: currentFolder];
+      parentuidvalidity = [self getUIDValidityForFolder: parent];
+      
       folderData = [NSDictionary dictionaryWithObjectsAndKeys:
                                    currentFolder, @"path",
                                  currentFolderType, @"type",
                                  currentDisplayName, @"displayName",
+                                 uidvalidity, @"uidvalidity",
+                                 parentuidvalidity, @"parentuidvalidity",
                                  parent, @"parent",
                                  nil];
+
       [folders addObject: folderData];
       [pool release];
     }
@@ -961,4 +969,24 @@ static NSString *inboxFolderName = @"INBOX";
   return [[self _mailAccount] objectForKey: @"name"];
 }
 
+
+// tfu get uidvalidity - to be used in allFoldersMetadata
+- (NSString *) getUIDValidityForFolder: (NSString *) folderName
+{
+  NGImap4Client *client;
+  NSString *uidValidity;
+  NSDictionary *result;
+
+  client = [[self imap4Connection] client];
+  result = [client select: folderName];
+
+  if ([[result objectForKey: @"result"] boolValue])
+    uidValidity = [result objectForKey:@"uidvalidity"];
+  else
+    uidValidity =  @"0"; 
+
+  return uidValidity;
+}
+
+
 @end /* SOGoMailAccount */
diff --git a/SoObjects/Mailer/SOGoMailFolder.h b/SoObjects/Mailer/SOGoMailFolder.h
index eac1aec..baabc95 100644
--- a/SoObjects/Mailer/SOGoMailFolder.h
+++ b/SoObjects/Mailer/SOGoMailFolder.h
@@ -120,6 +120,9 @@
 - (id) appendMessage: (NSData *) message
              usingId: (int *) imap4id;
 
+//tfu
+- (NSString *) getUIDValidity;
+
 @end
 
 @interface SOGoSpecialMailFolder : SOGoMailFolder
diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m
index 7f45ec8..b16d62c 100644
--- a/SoObjects/Mailer/SOGoMailFolder.m
+++ b/SoObjects/Mailer/SOGoMailFolder.m
@@ -291,10 +291,18 @@ static NSString *defaultUserID =  @"anyone";
           path = [[imap4URL path] stringByDeletingLastPathComponent];
           if (![path hasSuffix: @"/"])
             path = [path stringByAppendingString: @"/"];
+
+          if ([newName rangeOfString: @"/"].location == NSNotFound)
           destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme]
                                              host: [imap4URL host]
                                              path: [NSString stringWithFormat: @"%@%@",
                                                              path, [newName stringByEncodingImap4FolderName]]];
+          else
+          destURL = [[NSURL alloc] initWithScheme: [imap4URL scheme]
+                                             host: [imap4URL host]
+                                             path: [NSString stringWithFormat: @"%@",
+                                                              [newName stringByEncodingImap4FolderName]]];
+
           [destURL autorelease];
           error = [imap4 moveMailboxAtURL: imap4URL
                                     toURL: destURL];
@@ -1973,6 +1981,33 @@ static NSString *defaultUserID =  @"anyone";
   return tag;
 }
 
+
+// tfu
+- (NSString *) getUIDValidity
+{
+  NSString *uidValidity;
+
+  uidValidity = @"0";
+
+  if ([self imap4Connection])
+    {
+      NSString *folderName;
+      NSDictionary *result;
+
+      folderName = [imap4 imap4FolderNameForURL: [self imap4URL]];
+
+      [[imap4 client] unselect];
+
+      result = [[imap4 client] select: folderName];
+
+      uidValidity =  [result objectForKey: @"uidvalidity"];
+    }
+
+  return uidValidity;
+
+}
+
+
 //
 // FIXME - see below for code refactoring with MAPIStoreMailFolder.
 //
-- 
1.7.9.5

