diff --git a/OpenChange/MAPIStoreMailMessage.h b/OpenChange/MAPIStoreMailMessage.h index e12a4e9d7..580be0a6e 100644 --- a/OpenChange/MAPIStoreMailMessage.h +++ b/OpenChange/MAPIStoreMailMessage.h @@ -44,11 +44,11 @@ NSMutableDictionary *bodyPartsEncodings; NSMutableDictionary *bodyPartsCharsets; NSMutableDictionary *bodyPartsMimeTypes; + NSMutableDictionary *bodyPartsMixed; NSString *headerCharset; NSString *headerMimeType; BOOL bodySetup; - BOOL multipartMixed; NSArray *bodyContent; BOOL fetchedAttachments; diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 23a520e70..6c20a6b04 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -25,6 +25,7 @@ #import #import #import +#import #import #import #import @@ -42,6 +43,7 @@ #import #import #import +#import #import "Codepages.h" #import "NSData+MAPIStore.h" @@ -131,11 +133,11 @@ static NSArray *acceptedMimeTypes; bodyPartsEncodings = nil; bodyPartsCharsets = nil; bodyPartsMimeTypes = nil; + bodyPartsMixed = nil; headerSetup = NO; bodySetup = NO; bodyContent = nil; - multipartMixed = NO; mailIsEvent = NO; mailIsMeetingRequest = NO; @@ -155,6 +157,7 @@ static NSArray *acceptedMimeTypes; [bodyPartsEncodings release]; [bodyPartsCharsets release]; [bodyPartsMimeTypes release]; + [bodyPartsMixed release]; [bodyContent release]; @@ -260,7 +263,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) keys = [NSMutableArray array]; [sogoObject addRequiredKeysOfStructure: [sogoObject bodyStructure] - path: @"" toArray: keys + path: @"" + toArray: keys acceptedTypes: acceptedMimeTypes withPeek: YES]; [keys sortUsingFunction: _compareBodyKeysByPriority context: acceptedMimeTypes]; @@ -268,39 +272,29 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (keysCount > 0) { NSUInteger i; - id bodyStructure; BOOL hasHtml = NO; BOOL hasText = NO; - - bodyStructure = [sogoObject bodyStructure]; - /* multipart/mixed is the default type. - multipart/alternative and multipart/related are the only other type of multipart supported for the - message body - */ - if ([[bodyStructure objectForKey: @"type"] isEqualToString: @"multipart"]) - { - NSString *subtype = [bodyStructure objectForKey: @"subtype"]; - multipartMixed = !([subtype isEqualToString: @"alternative"] || - [subtype isEqualToString: @"related"]); - } - else - multipartMixed = NO; bodyContentKeys = [[NSMutableArray alloc] initWithCapacity: keysCount]; bodyPartsEncodings = [[NSMutableDictionary alloc] initWithCapacity: keysCount]; bodyPartsCharsets = [[NSMutableDictionary alloc] initWithCapacity: keysCount]; bodyPartsMimeTypes = [[NSMutableDictionary alloc] initWithCapacity: keysCount]; + bodyPartsMixed = [[NSMutableDictionary alloc] initWithCapacity: keysCount]; for (i = 0; i < keysCount; i++) { + NSDictionary *bodyStructureKey; NSString *key; NSString *mimeType; + BOOL mixedPart; NSString *strippedKey; NSString *encoding; NSString *charset; NSDictionary *partParameters; + NSString *multipart; - key = [[keys objectAtIndex: i] objectForKey: @"key"]; + bodyStructureKey = [keys objectAtIndex: i]; + key = [bodyStructureKey objectForKey: @"key"]; if (key == nil) continue; @@ -312,7 +306,21 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) partParameters = [partHeaderData objectForKey: @"parameterList"]; encoding = [partHeaderData objectForKey: @"encoding"]; charset = [partParameters objectForKey: @"charset"]; - mimeType = [[keys objectAtIndex: i] objectForKey: @"mimeType"]; + mimeType = [bodyStructureKey objectForKey: @"mimeType"]; + + /* multipart/mixed is the default type. + multipart/alternative is the only other type of multipart supported now. + */ + multipart = [bodyStructureKey objectForKey: @"multipart"]; + if ([multipart isEqualToString: @""]) + { + mixedPart = NO; + } + else + { + mixedPart = !([multipart isEqualToString: @"multipart/alternative"] || + [multipart isEqualToString: @"multipart/related"]); + } if (encoding) [bodyPartsEncodings setObject: encoding forKey: key]; @@ -326,6 +334,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) else if ([mimeType isEqualToString: @"text/html"]) hasHtml = YES; } + [bodyPartsMixed setObject: [NSNumber numberWithBool: mixedPart] forKey: key]; if (i == 0) { @@ -336,17 +345,28 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (charset) { if (headerCharset == nil) - ASSIGN (headerCharset, charset); + { + ASSIGN (headerCharset, charset); + } else if (![headerCharset isEqualToString: charset]) { /* Because we have different charsets we will encode all in UTF-8 */ ASSIGN (headerCharset, @"utf-8"); } } + } if (!hasHtml || !hasText) - multipartMixed = NO; + { + NSArray *bodyPartsMixedKeys = [bodyPartsMixed allKeys]; + for (i = 0; i < [keys count]; i++) + { + NSString *key = [bodyPartsMixedKeys objectAtIndex: i]; + [bodyPartsMixed setObject: [NSNumber numberWithBool: NO] forKey: key]; + } + } + if ([headerMimeType isEqualToString: @"text/calendar"] || [headerMimeType isEqualToString: @"application/ics"]) @@ -410,9 +430,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) NSString *mimeType = [bodyPartsMimeTypes objectForKey: key]; if (mimeType == nil) continue; - NSString *encoding = [bodyPartsEncodings objectForKey: key]; - if (encoding == nil) - encoding = @"7-bit"; + NSString *contentEncoding = [bodyPartsEncodings objectForKey: key]; + if (contentEncoding == nil) + contentEncoding = @"7-bit"; /* We should provide a case for each of the types in acceptedMimeTypes */ if (!mailIsEvent) @@ -421,6 +441,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) NSStringEncoding charsetEncoding; NSString *stringValue; BOOL html; + BOOL mixed = [[bodyPartsMixed objectForKey: key] boolValue]; if ([mimeType isEqualToString: @"text/html"]) { html = YES; @@ -436,7 +457,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) continue; } - content = [content bodyDataFromEncoding: encoding]; + content = [content bodyDataFromEncoding: contentEncoding]; charset = [bodyPartsCharsets objectForKey: key]; stringValue = nil; @@ -459,7 +480,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) [textContent appendData: [stringValue dataUsingEncoding: headerEncoding]]; } - if (multipartMixed) + if (mixed) { // We must add it also to the other mail representation if (html) @@ -491,9 +512,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) else { /* Without charset we cannot mangle the text, so we add as it stands */ - if (html || multipartMixed) + if (html || mixed) [htmlContent appendData: content]; - if (!html || multipartMixed) + if (!html || mixed) [textContent appendData: content]; } @@ -501,7 +522,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) else if ([mimeType isEqualToString: @"text/calendar"] || [mimeType isEqualToString: @"application/ics"]) { - content = [content bodyDataFromEncoding: encoding]; + content = [content bodyDataFromEncoding: contentEncoding]; [textContent appendData: content]; } else diff --git a/SoObjects/Mailer/SOGoMailObject.h b/SoObjects/Mailer/SOGoMailObject.h index af52334ee..8089bcf0b 100644 --- a/SoObjects/Mailer/SOGoMailObject.h +++ b/SoObjects/Mailer/SOGoMailObject.h @@ -129,9 +129,9 @@ NSArray *SOGoMailCoreInfoKeys; inContext: (id)_ctx; - (void) addRequiredKeysOfStructure: (NSDictionary *) info - path: (NSString *) p - toArray: (NSMutableArray *) keys - acceptedTypes: (NSArray *) types + path: (NSString *) p + toArray: (NSMutableArray *) keys + acceptedTypes: (NSArray *) types withPeek: (BOOL) withPeek; @end diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index dd4ee0358..2c4738061 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -516,12 +516,15 @@ static BOOL debugSoParts = NO; return s; } +/* This is defined before the public version without parentMimeType + argument to be able to call it recursively */ /* bulk fetching of plain/text content */ - (void) addRequiredKeysOfStructure: (NSDictionary *) info - path: (NSString *) p - toArray: (NSMutableArray *) keys - acceptedTypes: (NSArray *) types + path: (NSString *) p + toArray: (NSMutableArray *) keys + acceptedTypes: (NSArray *) types withPeek: (BOOL) withPeek + parentMultipart: (NSString *) parentMPart { /* This is used to collect the set of IMAP4 fetch-keys required to fetch @@ -536,6 +539,7 @@ static BOOL debugSoParts = NO; id body; NSString *bodyToken, *sp, *mimeType; id childInfo; + NSString *multipart; bodyToken = (withPeek ? @"body.peek" : @"body"); @@ -543,6 +547,12 @@ static BOOL debugSoParts = NO; [info valueForKey: @"type"], [info valueForKey: @"subtype"]] lowercaseString]; + + if ([[info valueForKey: @"type"] isEqualToString: @"multipart"]) + multipart = mimeType; + else + multipart = parentMPart; + if ([types containsObject: mimeType]) { if ([p length] > 0) @@ -557,7 +567,8 @@ static BOOL debugSoParts = NO; k = [NSString stringWithFormat: @"%@[text]", bodyToken]; } [keys addObject: [NSDictionary dictionaryWithObjectsAndKeys: k, @"key", - mimeType, @"mimeType", nil]]; + mimeType, @"mimeType", + multipart, @"multipart", nil]]; } parts = [info objectForKey: @"parts"]; @@ -571,9 +582,11 @@ static BOOL debugSoParts = NO; childInfo = [parts objectAtIndex: i]; [self addRequiredKeysOfStructure: childInfo - path: sp toArray: keys - acceptedTypes: types - withPeek: withPeek]; + path: sp + toArray: keys + acceptedTypes: types + withPeek: withPeek + parentMultipart: multipart]; } /* check body */ @@ -597,12 +610,28 @@ static BOOL debugSoParts = NO; else sp = [p length] > 0 ? (id)[p stringByAppendingString: @".1"] : (id)@"1"; [self addRequiredKeysOfStructure: body - path: sp toArray: keys - acceptedTypes: types - withPeek: withPeek]; + path: sp + toArray: keys + acceptedTypes: types + withPeek: withPeek + parentMultipart: multipart]; } } +- (void) addRequiredKeysOfStructure: (NSDictionary *) info + path: (NSString *) p + toArray: (NSMutableArray *) keys + acceptedTypes: (NSArray *) types + withPeek: (BOOL) withPeek +{ + [self addRequiredKeysOfStructure: (NSDictionary *) info + path: (NSString *) p + toArray: (NSMutableArray *) keys + acceptedTypes: (NSArray *) types + withPeek: (BOOL) withPeek + parentMultipart: @""]; +} + - (NSArray *) plainTextContentFetchKeys { /*