diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 5a4153eac..7cd0e5c53 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -61,6 +61,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import #import +#import #import #import @@ -95,6 +96,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import #import +#import #import #import @@ -110,6 +112,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import #import +#import +#import + #import #import @@ -2389,26 +2394,19 @@ static BOOL debugOn = NO; } -// -// -// -// -// C9FF94FE-EA40-473A-B3E2-AAEE94F753A4 -// -// -// -// mail/INBOX -// 82 -// -// ... the data ... -// -// -- (void) processSmartForward: (id ) theDocumentElement - inResponse: (WOResponse *) theResponse +- (void) _processSmartCommand: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse + isSmartForward: (BOOL ) isSmartForward { NSString *folderId, *itemId, *realCollectionId; SOGoMicrosoftActiveSyncFolderType folderType; + SOGoUserDefaults *ud; + + BOOL htmlComposition, isHTML; id value; + + isHTML = NO; + ud = [[context activeUser] userDefaults]; folderId = [[(id)[theDocumentElement getElementsByTagName: @"FolderId"] lastObject] textValue]; @@ -2457,11 +2455,15 @@ static BOOL debugOn = NO; NGMutableHashMap *map; NGMimeFileData *fdata; NSException *error; + NSArray *attachmentKeys; + NSMutableArray *attachments; - id body, bodyFromSmartForward; - NSString *fullName, *email; + id body, bodyFromSmartForward, htmlPart, textPart; + NSString *fullName, *email, *charset, *s; NSDictionary *identity; + int a; + userFolder = [[context activeUser] homeFolderInContext: context]; accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; @@ -2472,7 +2474,6 @@ static BOOL debugOn = NO; mailObject = [currentCollection lookupName: itemId inContext: context acquire: NO]; - parser = [[NGMimeMessageParser alloc] init]; data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]; messageFromSmartForward = [parser parsePartFromData: data]; @@ -2493,8 +2494,8 @@ static BOOL debugOn = NO; else [map setObject: email forKey: @"from"]; - if ([[mailObject envelope] messageId]) - [map setObject: [[mailObject envelope] messageId] forKey: @"in-reply-to"]; + if ([mailObject messageId]) + [map setObject: [mailObject messageId] forKey: @"in-reply-to"]; messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease]; body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease]; @@ -2503,12 +2504,16 @@ static BOOL debugOn = NO; // we take the first part text/* part we see. map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease]; bodyFromSmartForward = nil; + textPart = nil; + htmlPart = nil; + + attachments = [NSMutableArray array]; if ([[messageFromSmartForward body] isKindOfClass: [NGMimeMultipartBody class]]) { - NGMimeBodyPart *part; - NSArray *parts; - int i; + NGMimeBodyPart *part, *apart; + NSArray *parts, *aparts; + int i, j; parts = [[messageFromSmartForward body] parts]; @@ -2516,37 +2521,159 @@ static BOOL debugOn = NO; { part = [parts objectAtIndex: i]; - if ([[[part contentType] type] isEqualToString: @"text"]) + if ([[[part contentType] type] isEqualToString: @"multipart"] && [[[part contentType] subType] isEqualToString: @"alternative"]) { - [map setObject: [[part contentType] stringValue] forKey: @"content-type"]; - bodyFromSmartForward = [part body]; - break; + aparts = [[part body] parts]; + for (j = 0; j < [aparts count]; j++) + { + apart = [aparts objectAtIndex: j]; + if ([[[apart contentType] type] isEqualToString: @"text"] && [[[apart contentType] subType] isEqualToString: @"html"]) + htmlPart = apart; + if ([[[apart contentType] type] isEqualToString: @"text"] && [[[apart contentType] subType] isEqualToString: @"plain"]) + textPart = apart; + } } + else + { + if ([[[part contentType] type] isEqualToString: @"text"] && [[[part contentType] subType] isEqualToString: @"html"]) + htmlPart = part; + else if ([[[part contentType] type] isEqualToString: @"text"] && [[[part contentType] subType] isEqualToString: @"plain"]) + textPart = part; + else + [attachments addObject: part]; + } } } else { - [map setObject: [[messageFromSmartForward contentType] stringValue] forKey: @"content-type"]; - bodyFromSmartForward = [messageFromSmartForward body]; + if ([[[messageFromSmartForward contentType] type] isEqualToString: @"text"] && [[[messageFromSmartForward contentType] subType] isEqualToString: @"html"]) + htmlPart = messageFromSmartForward; + else + textPart = messageFromSmartForward; } + htmlComposition = [[ud mailComposeMessageType] isEqualToString: @"html"]; + + if (htmlComposition && htmlPart) + { + bodyFromSmartForward = [htmlPart body]; + charset = [[htmlPart contentType] valueOfParameter: @"charset"]; + isHTML = YES; + } + else if (!htmlComposition && !textPart) + { + bodyFromSmartForward = [htmlPart body]; + charset = [[htmlPart contentType] valueOfParameter: @"charset"]; + isHTML = YES; + } + else + { + bodyFromSmartForward = [textPart body]; + charset = [[textPart contentType] valueOfParameter: @"charset"]; + } + + // We make sure everything is encoded in UTF-8. + if ([bodyFromSmartForward isKindOfClass: [NSData class]]) + { + if (![charset length]) + charset = @"utf-8"; + + s = [NSString stringWithData: bodyFromSmartForward usingEncodingNamed: charset]; + + // We fallback to ISO-8859-1 string encoding. We avoid #3103. + if (!s) + s = [[[NSString alloc] initWithData: bodyFromSmartForward encoding: NSISOLatin1StringEncoding] autorelease]; + + bodyFromSmartForward = s; + } + + if (htmlComposition && !isHTML) + { + [map setObject: @"text/html; charset=utf-8" forKey: @"content-type"]; + bodyFromSmartForward = [[bodyFromSmartForward stringByEscapingHTMLString] stringByConvertingCRLNToHTML]; + } + else if (!htmlComposition && isHTML) + { + [map setObject: @"text/plain; charset=utf-8" forKey: @"content-type"]; + bodyFromSmartForward = [bodyFromSmartForward htmlToText]; + } + else if (htmlComposition && isHTML) + { + [map setObject: @"text/html; charset=utf-8" forKey: @"content-type"]; + } + else + { + [map setObject: @"text/plain; charset=utf-8" forKey: @"content-type"]; + } + bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease]; - [bodyPart setBody: bodyFromSmartForward]; + + if (isSmartForward && [[ud mailMessageForwarding] isEqualToString: @"attached"]) + [bodyPart setBody: [bodyFromSmartForward dataUsingEncoding: NSUTF8StringEncoding]]; + else + [bodyPart setBody: [[NSString stringWithFormat: @"%@%@", bodyFromSmartForward, [mailObject contentForEditing]] dataUsingEncoding: NSUTF8StringEncoding]]; + [body addBodyPart: bodyPart]; - // Second part - map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease]; - [map setObject: @"message/rfc822" forKey: @"content-type"]; - [map setObject: @"8bit" forKey: @"content-transfer-encoding"]; - bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease]; - - data = [mailObject content]; - fdata = [[NGMimeFileData alloc] initWithBytes:[data bytes] - length:[data length]]; + // Add attachments + for (a = 0; a < [attachments count]; a++) + { + [body addBodyPart: [attachments objectAtIndex: a]]; + } + + // For a forward decide whether do it inline or as an attachment. + if (isSmartForward) + { + if ([[ud mailMessageForwarding] isEqualToString: @"attached"]) + { + map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease]; + [map setObject: @"message/rfc822" forKey: @"content-type"]; + [map setObject: @"8bit" forKey: @"content-transfer-encoding"]; + [map addObject: [NSString stringWithFormat: @"attachment; filename=\"%@\"", [mailObject filenameForForward]] forKey: @"content-disposition"]; + bodyPart = [[[NGMimeBodyPart alloc] initWithHeader: map] autorelease]; + + data = [mailObject content]; + fdata = [[NGMimeFileData alloc] initWithBytes: [data bytes] length: [data length]]; + + [bodyPart setBody: fdata]; + RELEASE(fdata); + [body addBodyPart: bodyPart]; + } + else + { + attachmentKeys = [mailObject fetchFileAttachmentKeys]; + if ([attachmentKeys count]) + { + id currentAttachment; + NGHashMap *response; + NSData *bodydata; + NSArray *paths; + + paths = [attachmentKeys keysWithFormat: @"BODY[%{path}]"]; + response = [[mailObject fetchParts: paths] objectForKey: @"RawResponse"]; + + for (a = 0; a < [attachmentKeys count]; a++) + { + currentAttachment = [attachmentKeys objectAtIndex: a]; + bodydata = [[[response objectForKey: @"fetch"] objectForKey: [NSString stringWithFormat: @"body[%@]", [currentAttachment objectForKey: @"path"]]] valueForKey: @"data"]; + + map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease]; + [map setObject: [currentAttachment objectForKey: @"mimetype"] forKey: @"content-type"]; + [map setObject: [currentAttachment objectForKey: @"encoding"] forKey: @"content-transfer-encoding"]; + [map addObject: [NSString stringWithFormat: @"attachment; filename=\"%@\"", [currentAttachment objectForKey: @"filename"]] forKey: @"content-disposition"]; + bodyPart = [[[NGMimeBodyPart alloc] initWithHeader: map] autorelease]; + + fdata = [[NGMimeFileData alloc] initWithBytes:[bodydata bytes] length:[bodydata length]]; + + [bodyPart setBody: fdata]; + RELEASE(fdata); + [body addBodyPart: bodyPart]; + } + + } + } + } - [bodyPart setBody: fdata]; - RELEASE(fdata); - [body addBodyPart: bodyPart]; [messageToSend setBody: body]; generator = [[[NGMimeMessageGenerator alloc] init] autorelease]; @@ -2570,6 +2697,29 @@ static BOOL debugOn = NO; } } +// +// +// +// +// C9FF94FE-EA40-473A-B3E2-AAEE94F753A4 +// +// +// +// mail/INBOX +// 82 +// +// ... the data ... +// +// +- (void) processSmartForward: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + [self _processSmartCommand: theDocumentElement + inResponse: theResponse + isSmartForward: YES]; +} + + // // // @@ -2587,11 +2737,11 @@ static BOOL debugOn = NO; - (void) processSmartReply: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - [self processSmartForward: theDocumentElement inResponse: theResponse]; + [self _processSmartCommand: theDocumentElement + inResponse: theResponse + isSmartForward: NO]; } - - // // // diff --git a/NEWS b/NEWS index 96f96e766..4fdabf5d7 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ Enhancements - make sure sure email invitations can always be read by EAS clients - now able to print event/task's description (new components only) in the list view (#2881) - now possible to log EAS commands using the SOGoEASDebugEnabled system defaults + - many improvements to EAS SmartReply/SmartForward commands Bug fixes - now keep the BodyPreference for future EAS use and default to MIME if none set (#3146)