]> err.no Git - scalable-opengroupware.org/blobdiff - SoObjects/Mailer/SOGoMailObject.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1026 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / Mailer / SOGoMailObject.m
index 3e60fc73302d327e6052fe48671856ef7c1632b2..749882e168842f4b78d0bd0d4f840d95e9e075e9 100644 (file)
@@ -173,6 +173,10 @@ static BOOL debugSoParts       = NO;
 /* message */
 
 - (id)fetchParts:(NSArray *)_parts {
+  // TODO: explain what it does
+  /*
+    Called by -fetchPlainTextParts:
+  */
   return [[self imap4Connection] fetchURL:[self imap4URL] parts:_parts];
 }
 
@@ -406,34 +410,39 @@ static BOOL debugSoParts       = NO;
 /* bulk fetching of plain/text content */
 
 - (BOOL)shouldFetchPartOfType:(NSString *)_type subtype:(NSString *)_subtype {
+  /*
+    This method decides which parts are 'prefetched' for display. Those are
+    usually text parts (the set is currently hardcoded in this method ...).
+  */
   _type    = [_type    lowercaseString];
   _subtype = [_subtype lowercaseString];
   
-  if ([_type isEqualToString:@"text"]) {
-    if ([_subtype isEqualToString:@"plain"])
-      return YES;
-
-    if ([_subtype isEqualToString:@"calendar"]) /* we also fetch calendars */
-      return YES;
-  }
-
-  if ([_type isEqualToString:@"application"]) {
-    if ([_subtype isEqualToString:@"pgp-signature"])
-      return YES;
-  }
-  
-  return NO;
+  return (([_type isEqualToString:@"text"]
+           && ([_subtype isEqualToString:@"plain"]
+               || [_subtype isEqualToString:@"html"]
+               || [_subtype isEqualToString:@"calendar"]))
+          || ([_type isEqualToString:@"application"]
+              && ([_subtype isEqualToString:@"pgp-signature"]
+                  || [_subtype hasPrefix:@"x-vnd.kolab."])));
 }
 
 - (void)addRequiredKeysOfStructure:(id)_info path:(NSString *)_p
   toArray:(NSMutableArray *)_keys
   recurse:(BOOL)_recurse
 {
+  /* 
+     This is used to collect the set of IMAP4 fetch-keys required to fetch
+     the basic parts of the body structure. That is, to fetch all parts which
+     are displayed 'inline' in a single IMAP4 fetch.
+     
+     The method calls itself recursively to walk the body structure.
+  */
   NSArray  *parts;
   unsigned i, count;
   BOOL fetchPart;
   id body;
   
+  /* Note: if the part itself doesn't qualify, we still check subparts */
   fetchPart = [self shouldFetchPartOfType:[_info valueForKey:@"type"]
                    subtype:[_info valueForKey:@"subtype"]];
   if (fetchPart) {
@@ -463,7 +472,7 @@ static BOOL debugSoParts       = NO;
     NSString *sp;
     id childInfo;
     
-    sp = [_p length] > 0
+    sp = ([_p length] > 0)
       ? [_p stringByAppendingFormat:@".%d", i + 1]
       : [NSString stringWithFormat:@"%d", i + 1];
     
@@ -489,6 +498,10 @@ static BOOL debugSoParts       = NO;
 }
 
 - (NSArray *)plainTextContentFetchKeys {
+  /*
+    The name is not 100% correct. The method returns all body structure fetch
+    keys which are marked by the -shouldFetchPartOfType:subtype: method.
+  */
   NSMutableArray *ma;
   
   ma = [NSMutableArray arrayWithCapacity:4];
@@ -498,6 +511,7 @@ static BOOL debugSoParts       = NO;
 }
 
 - (NSDictionary *)fetchPlainTextParts:(NSArray *)_fetchKeys {
+  // TODO: is the name correct or does it also fetch other parts?
   NSMutableDictionary *flatContents;
   unsigned i, count;
   id result;
@@ -506,7 +520,7 @@ static BOOL debugSoParts       = NO;
   
   result = [self fetchParts:_fetchKeys];
   result = [result valueForKey:@"RawResponse"]; // hackish
-
+  
   // Note: -valueForKey: doesn't work!
   result = [(NSDictionary *)result objectForKey:@"fetch"]; 
   
@@ -521,12 +535,12 @@ static BOOL debugSoParts       = NO;
                            objectForKey:@"data"];
     
     if (![data isNotNull]) {
-      [self debugWithFormat:@"got no data fork key: %@", key];
+      [self errorWithFormat:@"got no data for key: %@", key];
       continue;
     }
-
+    
     if ([key isEqualToString:@"body[text]"])
-      key = @""; // see key collector
+      key = @""; // see key collector for explanation (TODO: where?)
     else if ([key hasPrefix:@"body["]) {
       NSRange r;
       
@@ -546,24 +560,36 @@ static BOOL debugSoParts       = NO;
 
 /* convert parts to strings */
 
-- (NSString *)stringForData:(NSData *)_data partInfo:(NSDictionary *)_info {
-  NSString *charset;
-  NSString *s;
+- (NSString *)stringForData:(NSData *)_data partInfo:(NSDictionary *)_info
+{
+  NSString *charset, *encoding, *s;
+  NSData *mailData;
   
   if (![_data isNotNull])
     return nil;
 
   s = nil;
-  
-  charset = [[_info valueForKey:@"parameterList"] valueForKey:@"charset"];
-  if ([charset isNotNull] && [charset length] > 0)
-    s = [NSString stringWithData:_data usingEncodingNamed:charset];
-  
-  if (s == nil) { /* no charset provided, fall back to UTF-8 */
-    s = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];
-    s = [s autorelease];
-  }
-  
+
+  encoding = [[_info objectForKey:@"encoding"] lowercaseString];
+
+  if ([encoding isEqualToString: @"7bit"]
+      || [encoding isEqualToString: @"8bit"])
+    mailData = _data;
+  else if ([encoding isEqualToString: @"base64"])
+    mailData = [_data dataByDecodingBase64];
+  else if ([encoding isEqualToString: @"quoted-printable"])
+    mailData = [_data dataByDecodingQuotedPrintable];
+  
+  charset = [[_info valueForKey:@"parameterList"] valueForKey: @"charset"];
+  if (![charset length])
+    {
+      s = [[NSString alloc] initWithData:mailData encoding:NSUTF8StringEncoding];
+      [s autorelease];
+    }
+  else
+    s = [NSString stringWithData: mailData
+                  usingEncodingNamed: charset];
+
   return s;
 }
 
@@ -777,6 +803,71 @@ static BOOL debugSoParts       = NO;
   return nil;
 }
 
+- (NSException *) moveToFolderNamed: (NSString *) folderName
+                          inContext: (id)_ctx
+{
+  /*
+    Trashing is three actions:
+    a) copy to trash folder
+    b) mark mail as deleted
+    c) expunge folder
+    
+    In case b) or c) fails, we can't do anything because IMAP4 doesn't tell us
+    the ID used in the trash folder.
+  */
+  SOGoMailAccounts *destFolder;
+  NSEnumerator *folders;
+  NSString *currentFolderName, *reason;
+  NSException    *error;
+
+  // TODO: check for safe HTTP method
+
+  destFolder = [self mailAccountsFolder];
+  folders = [[folderName componentsSeparatedByString: @"/"] objectEnumerator];
+  currentFolderName = [folders nextObject];
+  currentFolderName = [folders nextObject];
+
+  while (currentFolderName)
+    {
+      destFolder = [destFolder lookupName: currentFolderName
+                               inContext: _ctx
+                               acquire: NO];
+      if ([destFolder isKindOfClass: [NSException class]])
+        return (NSException *) destFolder;
+      currentFolderName = [folders nextObject];
+    }
+
+  if (!([destFolder isKindOfClass: [SOGoMailFolder class]]
+        && [destFolder isNotNull]))
+    {
+      reason = [NSString stringWithFormat: @"Did not find folder name '%@'!",
+                         folderName];
+      return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+                          reason: reason];
+    }
+  [destFolder flushMailCaches];
+
+  /* a) copy */
+  
+  error = [self davCopyToTargetObject: destFolder
+               newName:@"fakeNewUnusedByIMAP4" /* autoassigned */
+               inContext:_ctx];
+  if (error != nil) return error;
+
+  /* b) mark deleted */
+  
+  error = [[self imap4Connection] markURLDeleted: [self imap4URL]];
+  if (error != nil) return error;
+  
+  /* c) expunge */
+
+  error = [[self imap4Connection] expungeAtURL:[[self container] imap4URL]];
+  if (error != nil) return error; // TODO: unflag as deleted?
+  [self flushMailCaches];
+  
+  return nil;
+}
+
 - (NSException *)delete {
   /* 
      Note: delete is different to DELETEAction: for mails! The 'delete' runs
@@ -808,6 +899,17 @@ static BOOL debugSoParts       = NO;
 
 /* some mail classification */
 
+- (BOOL)isKolabObject {
+  NSDictionary *h;
+  
+  if ((h = [self mailHeaders]) != nil)
+    return [[h objectForKey:@"x-kolab-type"] isNotEmpty];
+  
+  // TODO: we could check the body structure?
+  
+  return NO;
+}
+
 - (BOOL)isMailingListMail {
   NSDictionary *h;
   
@@ -902,6 +1004,30 @@ static BOOL debugSoParts       = NO;
   */
   return mailETag;
 }
+- (int)zlGenerationCount {
+  return 0; /* mails never change */
+}
+
+/* Outlook mail tagging */
+
+- (NSString *)outlookMessageClass {
+  NSString *type;
+  
+  if ((type = [[self mailHeaders] objectForKey:@"x-kolab-type"]) != nil) {
+    if ([type isEqualToString:@"application/x-vnd.kolab.contact"])
+      return @"IPM.Contact";
+    if ([type isEqualToString:@"application/x-vnd.kolab.task"])
+      return @"IPM.Task";
+    if ([type isEqualToString:@"application/x-vnd.kolab.event"])
+      return @"IPM.Appointment";
+    if ([type isEqualToString:@"application/x-vnd.kolab.note"])
+      return @"IPM.Note";
+    if ([type isEqualToString:@"application/x-vnd.kolab.journal"])
+      return @"IPM.Journal";
+  }
+  
+  return @"IPM.Message"; /* email, default class */
+}
 
 /* debugging */