From 3e51b66d8ef1656567ec7ceb6e2aa4c2a96cefab Mon Sep 17 00:00:00 2001
From: root <root@example.com>
Date: Thu, 24 Apr 2014 21:21:41 +0200
Subject: [PATCH] mail-softdelete

---
 ActiveSync/SOGoActiveSyncDispatcher+Sync.m |   69 ++++++++++++++++++++++++----
 ActiveSync/SOGoActiveSyncDispatcher.m      |   19 ++++++++
 SoObjects/Mailer/SOGoMailFolder.h          |    3 +-
 SoObjects/Mailer/SOGoMailFolder.m          |   33 +++++++++++++
 4 files changed, 115 insertions(+), 9 deletions(-)

diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
index 396c266..0668cb0 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
@@ -442,12 +442,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   BOOL more_available;
   int i, max;
 
+  // tfu softdelete - extract the 3rd pard of the SyncKey if available (this is the date of the oldest mail synced)
+  NSArray *a;
+  NSString *oldestDate;
+
+  NSLog(@"tfu - old theSyncKey %@", theSyncKey);
+  if (theFolderType == ActiveSyncMailFolder) {
+      a = [theSyncKey componentsSeparatedByString: @"-"];
+      if ([a count] > 2) {
+         oldestDate = [a objectAtIndex: 2];
+         theSyncKey = [NSString stringWithFormat: @"%@-%@",[a objectAtIndex: 0], [a objectAtIndex: 1]];
+      } else {
+        oldestDate = nil;
+      }
+  } else {
+    oldestDate = nil;
+  }
+
+  NSLog(@"tfu - new theSyncKey %@ oldestDate %@", theSyncKey, oldestDate);
+
   //
   // No changes in the collection - 2.2.2.19.1.1 Empty Sync Request.
   // We check this and we don't generate any commands if we don't have to.
   //
-  if ([theSyncKey isEqualToString: [theCollection davCollectionTag]])
-    return;
+  if ([theSyncKey isEqualToString: [theCollection davCollectionTag]]) {
+    NSLog(@"tfu no change -- checking cutof date");
+    // tfu if the oldestDate is == to the current filter (cutoff date) then there is nothing to do about softdelets
+    if (oldestDate && theFilterType) {
+         NSLog(@"tfu dayOfCommonEra Oldest %d", [[[NSCalendarDate alloc] initWithTimeIntervalSince1970:[oldestDate intValue]] dayOfCommonEra]);
+         NSLog(@"tfu dayOfCommonEra Filder %d", [theFilterType dayOfCommonEra]);
+         if ( [[[NSCalendarDate alloc] initWithTimeIntervalSince1970:[oldestDate intValue]] dayOfCommonEra] == [theFilterType dayOfCommonEra])
+            return;
+    }
+    else 
+        return;
+  }
   
   s = [NSMutableString string];
   more_available = NO;
@@ -574,10 +603,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
         NSArray *allMessages;
         int deleted_count;
 
-        allMessages = [theCollection syncTokenFieldsWithProperties: nil   matchingSyncToken: theSyncKey  fromDate: theFilterType];
+        allMessages = [theCollection syncTokenFieldsWithProperties: nil   matchingSyncToken: theSyncKey  fromDate: theFilterType  oldestDate: oldestDate];
         addedOrChangedMessages = [NSMutableArray array];
         deleted_count = 0;
-
+ 
         // Check for the WindowSize.
         // FIXME: we should eventually check for modseq and slice the maximum
         //        amount of messages returned to ensure we don't have the same
@@ -597,12 +626,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
             
             if ([command isEqualToString: @"deleted"])
               {
+		NSLog(@"tfu delete uid=%@", uid);
                 [s appendString: @"<Delete xmlns=\"AirSync:\">"];
                 [s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
                 [s appendString: @"</Delete>"];
                 deleted_count++;
               }
-            else
+            else if ([command isEqualToString: @"softdelete"]) {
+		NSLog(@"tfu softdelete uid=%@", uid);
+                [s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
+                [s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", uid];
+                [s appendString: @"</SoftDelete>"];
+                deleted_count++;
+            }
+             else
               {
                 [addedOrChangedMessages addObject: aMessage];
               }
@@ -633,6 +670,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                 command = @"changed";
               }
             
+            NSLog(@"tfu %@ uid %@", command, uid);
+
             if ([command isEqualToString: @"added"])
               [s appendString: @"<Add xmlns=\"AirSync:\">"];
             else
@@ -768,7 +807,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   NSMutableString *changeBuffer, *commandsBuffer;
   BOOL getChanges, first_sync;
   unsigned int windowSize, v;
-  
+
   changeBuffer = [NSMutableString string];
   commandsBuffer = [NSMutableString string];
   
@@ -783,7 +822,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
   if (windowSize == 0 || windowSize > 512)
     windowSize = 100;
-  
+
   // We check if we must overwrite the windowSize with a system preference. This can be useful
   // if the user population has large mailboxes and slow connectivity
   if ((v = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncWindowSize]))
@@ -879,7 +918,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   else if (folderType == ActiveSyncTaskFolder)
     [theBuffer appendString: @"<Class>Tasks</Class>"];
   
-  [theBuffer appendFormat: @"<SyncKey>%@</SyncKey>", davCollectionTag];
+
+  //tfu soft-delete - if a date filter is set and changes have been found then there should be no older mails on device -> the "oldestDate" can be updated
+  if ([NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]] && [changeBuffer length]) {
+    [theBuffer appendFormat: @"<SyncKey>%@-%@</SyncKey>", davCollectionTag, 
+                                  [NSString stringWithFormat: @"%d",(int)[[NSCalendarDate dateFromFilterType: 
+                                                      [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]] timeIntervalSince1970]]];
+    NSLog(@"tfu serverkey with date x %@ %@",davCollectionTag, 
+                                  [NSString stringWithFormat: @"%d",(int)[[NSCalendarDate dateFromFilterType: 
+                                                      [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]] timeIntervalSince1970]]);
+  }
+  else {
+    [theBuffer appendFormat: @"<SyncKey>%@</SyncKey>", davCollectionTag];
+  }
+
+
   [theBuffer appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
   [theBuffer appendFormat: @"<Status>%d</Status>", 1];
 
diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m
index b1b8e4e..68925aa 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher.m
@@ -634,6 +634,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                                               sortOrdering: @"REVERSE ARRIVAL"
                                                   threaded: NO];
       count = [uids count];
+
+      // tfu if nothing found lets see whether there are softdeletes which needs to be handled.
+      if (count == 0) {
+           NSString *syncKey;
+           NSArray *a;
+           NSString *oldestDate;
+
+           syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
+           a = [syncKey componentsSeparatedByString: @"-"];
+           if ([a count] > 2) {
+              oldestDate = [a objectAtIndex: 2];
+              NSLog(@"tfu GetItmeEstimate dayOfCommonEra Oldest %d", [[[NSCalendarDate alloc] initWithTimeIntervalSince1970:[oldestDate intValue]] dayOfCommonEra]);
+              NSLog(@"tfu GetItmeEstimate dayOfCommonEra Filder %d", [filter dayOfCommonEra]);
+              if ( [[[NSCalendarDate alloc] initWithTimeIntervalSince1970:[oldestDate intValue]] dayOfCommonEra] != [filter dayOfCommonEra] && [filter dayOfCommonEra] > 0)
+                 count=1;
+           }
+      }
     }
   else
     {
@@ -647,6 +664,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   [s appendFormat: @"<CollectionId>%@</CollectionId>", collectionId];
   [s appendFormat: @"<Estimate>%d</Estimate>", count];
   
+  NSLog(@"tfu GetItmeEstimate count %d", count);
+
   [s appendString: @"</Collection></Response></GetItemEstimate>"];
 
   d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
diff --git a/SoObjects/Mailer/SOGoMailFolder.h b/SoObjects/Mailer/SOGoMailFolder.h
index eac1aec..441031b 100644
--- a/SoObjects/Mailer/SOGoMailFolder.h
+++ b/SoObjects/Mailer/SOGoMailFolder.h
@@ -99,7 +99,8 @@
 
 - (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
                           matchingSyncToken: (NSString *) syncToken
-                                   fromDate: (NSCalendarDate *) theStartDate;
+                                   fromDate: (NSCalendarDate *) theStartDate
+                                 oldestDate: (NSString *) theOldestDate;
 
 /* flags */
 
diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m
index 6408828..c42ad8f 100644
--- a/SoObjects/Mailer/SOGoMailFolder.m
+++ b/SoObjects/Mailer/SOGoMailFolder.m
@@ -2030,6 +2030,7 @@ static NSString *defaultUserID =  @"anyone";
 - (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
                           matchingSyncToken: (NSString *) theSyncToken
                                    fromDate: (NSCalendarDate *) theStartDate
+                                 oldestDate: (NSString *) theOldestDate
 {
   EOQualifier *searchQualifier;
   NSMutableArray *allTokens;
@@ -2122,6 +2123,38 @@ static NSString *defaultUserID =  @"anyone";
           d = [NSDictionary dictionaryWithObject: @"deleted"  forKey: uid];
           [allTokens addObject: d];
         }
+        if (theStartDate)
+        {
+            EOQualifier *sinceDateQualifier, *beforeDateQualifier;
+            EOAndQualifier *qualifier;
+            NSArray *uids;
+
+            beforeDateQualifier = [EOQualifier qualifierWithQualifierFormat:
+                                                       @"(DATE >= %@)", [[NSCalendarDate alloc] initWithTimeIntervalSince1970:[theOldestDate intValue]] ];
+
+
+            sinceDateQualifier = [EOQualifier qualifierWithQualifierFormat:
+                                                       @"(DATE <= %@)", theStartDate ];
+
+
+            qualifier = [[EOAndQualifier alloc] initWithQualifiers: [self _nonDeletedQualifier], sinceDateQualifier, beforeDateQualifier,
+                                          nil];
+            AUTORELEASE(qualifier);
+
+            uids = [self fetchUIDsMatchingQualifier: qualifier
+                                               sortOrdering: nil
+                                                  ];
+
+            for (i = 0; i < [uids count]; i++)
+              {
+                uid = [uids objectAtIndex: i];
+                NSLog(@"tfu uid %@ OldestDate %@", uid , theOldestDate);
+                d = [NSDictionary dictionaryWithObject: @"softdelete"  forKey: uid];
+                [allTokens addObject: d];
+              }
+
+            NSLog(@"tfu softdelete %d theOldestDate %@ theStartDate %@", [uids count], theOldestDate, theStartDate);
+       }
     }
 
   return allTokens;
-- 
1.7.9.5

