From 5d3a958c64c46f4c2c628fedd4fe3152a56ea4ce Mon Sep 17 00:00:00 2001
From: root <root@example.com>
Date: Mon, 28 Apr 2014 21:25:01 +0200
Subject: [PATCH] annotations

---
 sope-mime/NGImap4/NGImap4Client.h             |    2 +
 sope-mime/NGImap4/NGImap4Client.m             |   76 ++++++++++++++++++++++++-
 sope-mime/NGImap4/NGImap4ResponseNormalizer.m |    6 ++
 sope-mime/NGImap4/NGImap4ResponseParser.m     |   70 ++++++++++++++++++++++-
 4 files changed, 150 insertions(+), 4 deletions(-)

diff --git a/sope-mime/NGImap4/NGImap4Client.h b/sope-mime/NGImap4/NGImap4Client.h
index 8442062..a5c3d17 100644
--- a/sope-mime/NGImap4/NGImap4Client.h
+++ b/sope-mime/NGImap4/NGImap4Client.h
@@ -136,6 +136,8 @@ typedef enum {
 - (NSDictionary *)select:(NSString *)_folder;
 - (NSDictionary *)unselect;
 - (NSDictionary *)status:(NSString *)_folder flags:(NSArray *)_flags;
+- (NSDictionary *)annotation:(NSString *)_folder entryName:(NSString *)_entry attributeName:(NSString *)_attribute;
+- (NSDictionary *)annotation:(NSString *)_folder entryName:(NSString *)_entry attributeName:(NSString *)_attribute  attributeValue:(NSString *)_value;
 - (NSDictionary *)rename:(NSString *)_folder to:(NSString *)_newName;
 - (NSDictionary *)delete:(NSString *)_folder;
 - (NSDictionary *)create:(NSString *)_name;
diff --git a/sope-mime/NGImap4/NGImap4Client.m b/sope-mime/NGImap4/NGImap4Client.m
index 02af32b..1d84204 100644
--- a/sope-mime/NGImap4/NGImap4Client.m
+++ b/sope-mime/NGImap4/NGImap4Client.m
@@ -118,7 +118,7 @@ static int          ProfileImapEnabled = -1;
 static int          LogImapEnabled     = -1;
 static int          PreventExceptions  = -1;
 static BOOL         fetchDebug         = NO;
-static BOOL         ImapDebugEnabled   = NO;
+static BOOL         ImapDebugEnabled   = YES;
 static NSArray      *Imap4SystemFlags  = nil;
 
 static NSMutableDictionary *capabilities;
@@ -908,6 +908,77 @@ static NSMutableDictionary *namespaces;
   return [self->normer normalizeResponse:[self processCommand:@"unselect"]];
 }
 
+- (NSDictionary *)annotation:(NSString *)_folder entryName:(NSString *)_entry attributeName:(NSString *)_attribute {
+/*
+ result dict looks like the following  
+ {"/comment" = {"value.priv" = "My comment"; }; "/vendor/cmu/cyrus-imapd/freespace" = {"value.shared" = 93498228; }; }
+
+ getannotation:
+
+ result = [client annotation: folderName entryName: @"/comment" attributeName: @"value.priv"];
+ result = [client annotation: folderName entryName: @"/comment" attributeName: @"value"];
+ result = [client annotation: folderName entryName: @"/*" attributeName: @"value"];
+ result = [client annotation: @"" entryName: @"/*" attributeName: @"value"];
+
+*/
+  NSString *cmd;
+  NGHashMap *_map;
+  NSDictionary        *obj;
+  NSMutableDictionary *result;
+  
+  if (_folder == nil)
+    return nil;
+  if ((_entry == nil))
+    return nil;
+  if ((_attribute == nil))
+    return nil;
+  if ((_folder = [self _folder2ImapFolder:_folder]) == nil)
+    return nil;
+  
+  cmd     = [NSString stringWithFormat:@"getannotation \"%@\" \"%@\" \"%@\"",
+                      SaneFolderName(_folder), _entry, _attribute];
+  
+  result  = [NSMutableDictionary dictionaryWithCapacity:2];
+  _map = [self processCommand:cmd];
+
+  NSEnumerator        *enumerator;
+  enumerator = [_map objectEnumeratorForKey:@"entry"];
+  while ((obj = [enumerator nextObject]) != nil) {
+
+    NSLog(@"tfu getannotation entry %@", obj);
+    [result addEntriesFromDictionary:[NSDictionary dictionaryWithDictionary:obj]];
+  }
+
+  NSLog(@"tfu getannotation result %@", result);
+  return result;
+
+}
+
+- (NSDictionary *)annotation:(NSString *)_folder entryName:(NSString *)_entry attributeName:(NSString *)_attribute attributeValue:(NSString *)_value {
+  NSString *cmd;
+  NSMutableDictionary *result;
+  
+  if (_folder == nil)
+    return nil;
+  if ((_entry == nil))
+    return nil;
+  if ((_attribute == nil))
+    return nil;
+  if ((_value == nil))
+    return nil;
+  if ((_folder = [self _folder2ImapFolder:_folder]) == nil)
+    return nil;
+  
+  cmd     = [NSString stringWithFormat:@"setannotation \"%@\" \"%@\" (\"%@\" \"%@\")",
+                      SaneFolderName(_folder), _entry, _attribute, _value];
+  
+  result  = [NSMutableDictionary dictionaryWithCapacity:2];
+  result = [self->normer normalizeResponse:[self processCommand:cmd]];
+  NSLog(@"tfu setannotation result %@", result);
+  return result;
+
+}
+
 - (NSDictionary *)status:(NSString *)_folder flags:(NSArray *)_flags {
   NSString *cmd;
   
@@ -918,7 +989,7 @@ static NSMutableDictionary *namespaces;
   if ((_folder = [self _folder2ImapFolder:_folder]) == nil)
     return nil;
   
-  cmd     = [NSString stringWithFormat:@"status \"%@\" (%@)",
+  cmd = [NSString stringWithFormat:@"status \"%@\" (%@)",
                       SaneFolderName(_folder), [_flags componentsJoinedByString:@" "]];
   return [self->normer normalizeStatusResponse:[self processCommand:cmd]];
 }
@@ -988,6 +1059,7 @@ static NSMutableDictionary *namespaces;
   return [_parts componentsJoinedByString:@" "];
 }
 
+
 - (NSDictionary *)fetchUids:(NSArray *)_uids parts:(NSArray *)_parts {
   /*
     eg: 'UID FETCH 1189,1325,1326 ([TODO])'
diff --git a/sope-mime/NGImap4/NGImap4ResponseNormalizer.m b/sope-mime/NGImap4/NGImap4ResponseNormalizer.m
index 687c3dc..95c9a85 100644
--- a/sope-mime/NGImap4/NGImap4ResponseNormalizer.m
+++ b/sope-mime/NGImap4/NGImap4ResponseNormalizer.m
@@ -268,6 +268,8 @@ static int      LogImapEnabled = -1;
         [result setObject:o forKey:@"highestmodseq"];
       else if ((o = [obj objectForKey:@"UIDNEXT"]))
         [result setObject:o forKey:@"uidnext"];
+      else if ((o = [obj objectForKey:@"UIDVALIDITY"]))
+        [result setObject:o forKey:@"uidvalidity"];
     }
     else
       [self warnWithFormat:@"unexpected OK object: %@", obj];
@@ -318,6 +320,10 @@ static int      LogImapEnabled = -1;
   }
   if ((o = [obj  objectForKey:@"unseen"]) != nil)
     [result setObject:o forKey:@"unseen"];
+
+//tfu support x-guid (dovecot)
+  if ((o = [obj  objectForKey:@"x-guid"]) != nil)
+    [result setObject:o forKey:@"x-guid"];
   
   return result;
 }
diff --git a/sope-mime/NGImap4/NGImap4ResponseParser.m b/sope-mime/NGImap4/NGImap4ResponseParser.m
index f14c889..02ffe69 100644
--- a/sope-mime/NGImap4/NGImap4ResponseParser.m
+++ b/sope-mime/NGImap4/NGImap4ResponseParser.m
@@ -11,7 +11,7 @@
   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
+/bin/bash: 58: command not found
 
   You should have received a copy of the GNU Lesser General Public
   License along with SOPE; see the file COPYING.  If not, write to the
@@ -604,7 +604,10 @@ static void _parseUntaggedResponse(NGImap4ResponseParser *self,
   l0 = _la(self, 0);
   switch (l0) {
   case 'A':
-    if ([self _parseACLResponseIntoHashMap:result_])
+    l1 = _la(self, 1);
+    if (l1 == 'C' && [self _parseACLResponseIntoHashMap:result_])
+      return;
+    if (l1 == 'N' && [self _parseAnnotationResponseIntoHashMap:result_]) 
       return;
     break;
     
@@ -1451,6 +1454,69 @@ _purifyQuotedString(NSMutableString *quotedString) {
   return YES;
 }
 
+- (BOOL)_parseAnnotationResponseIntoHashMap:(NGMutableHashMap *)result_ {
+  NSString            *name  = nil;
+  NSString            *entry  = nil;
+  NSMutableDictionary *attributes = nil;
+  NSDictionary *d;
+
+  if (!_matchesString(self, "ANNOTATION "))
+    return NO;
+
+  _consume(self, 11);
+
+  if (_la(self, 0) == '"') {
+    name = [self _parseQuotedString];
+    _consumeIfMatch(self, ' ');
+  }
+  else if (_la(self, 0) == '{') {
+    name = [self _parseQuotedStringOrNIL];
+    _consumeIfMatch(self, ' ');
+  }
+  else {
+    name = _parseUntil(self, ' ');
+  }
+
+  if (_la(self, 0) == '"') {
+    entry = [self _parseQuotedString];
+    _consumeIfMatch(self, ' ');
+  }
+  else if (_la(self, 0) == '{') {
+    entry = [self _parseQuotedStringOrNIL];
+    _consumeIfMatch(self, ' ');
+  }
+  else {
+    entry = _parseUntil(self, ' ');
+  }
+
+  _consumeIfMatch(self, '(');
+
+  attributes = [NSMutableDictionary dictionaryWithCapacity:2];
+  d = [NSMutableDictionary dictionaryWithCapacity:2];
+
+  while (_la(self, 0) != ')') {
+    NSString *key   = [self _parseQuotedString];
+    _consumeIfMatch(self, ' ');
+    NSString *value = [self _parseQuotedString];
+
+    if (_la(self, 0) == ' ')
+      _consume(self, 1);
+
+    [attributes setObject:value
+           forKey:[key lowercaseString]];
+
+    [d setObject:[NSDictionary dictionaryWithDictionary:attributes]
+       forKey:entry];
+  }
+  _consumeIfMatch(self, ')');
+  _parseUntil(self, '\n');
+
+  [result_ addObject:d forKey:@"entry"];
+  [d release];
+  return YES;
+}
+
+
 - (BOOL)_parseByeUntaggedResponseIntoHashMap:(NGMutableHashMap *)result_ {
   NSString *reason;
   
-- 
1.7.9.5

