From c875302a86b4bc50254199a85c99c08e0705a0cb Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 5 Jan 2018 13:55:05 -0500 Subject: [PATCH] (fix) proper handling of file attachments in S/MIME encrypted mails --- SoObjects/Mailer/NSString+Mail.h | 3 +- SoObjects/Mailer/NSString+Mail.m | 31 +++++++++- SoObjects/Mailer/SOGoDraftObject.m | 95 ++++++++++++++++++++++++++---- SoObjects/Mailer/SOGoMailObject.m | 21 +------ 4 files changed, 118 insertions(+), 32 deletions(-) diff --git a/SoObjects/Mailer/NSString+Mail.h b/SoObjects/Mailer/NSString+Mail.h index 7415f55e5..1d7fca682 100644 --- a/SoObjects/Mailer/NSString+Mail.h +++ b/SoObjects/Mailer/NSString+Mail.h @@ -1,6 +1,6 @@ /* NSString+Mail.h - this file is part of SOGo * - * Copyright (C) 2007-2014 Inverse inc. + * Copyright (C) 2007-2018 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,6 +34,7 @@ - (int) indexOf: (unichar) _c; - (NSString *) decodedHeader; - (NSString *) asSafeFilename; +- (NSString *) asPreferredFilenameUsingPath: (NSString *) thePath; @end diff --git a/SoObjects/Mailer/NSString+Mail.m b/SoObjects/Mailer/NSString+Mail.m index fd9f25263..038274dcd 100644 --- a/SoObjects/Mailer/NSString+Mail.m +++ b/SoObjects/Mailer/NSString+Mail.m @@ -1,6 +1,6 @@ /* NSString+Mail.m - this file is part of SOGo * - * Copyright (C) 2008-2014 Inverse inc. + * Copyright (C) 2008-2018 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -732,4 +732,33 @@ convertChars (const char *oldString, unsigned int oldLength, return safeName; } +// +// We might end up here because of MUA that actually strips the +// Content-Disposition (and thus, the filename) when mails containing +// attachments have been forwarded. Thunderbird (2.x) does just that +// when forwarding mails with images attached to them (using cid:...). +// +- (NSString *) asPreferredFilenameUsingPath: (NSString *) thePath +{ + NSString *filename; + + filename = nil; + if (!thePath) + thePath = @"1"; + + if ([self hasPrefix: @"application/"] || + [self hasPrefix: @"audio/"] || + [self hasPrefix: @"image/"] || + [self hasPrefix: @"video/"]) + { + filename = [NSString stringWithFormat: @"unknown_%@", thePath]; + } + else + { + if ([self isEqualToString: @"message/rfc822"]) + filename = [NSString stringWithFormat: @"email_%@.eml", thePath]; + } + + return filename; +} @end diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 038e40733..a497b65bf 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -779,9 +779,10 @@ static NSString *userAgent = nil; // - (void) _fetchAttachmentsFromMail: (SOGoMailObject *) sourceMail { - unsigned int max, count; - NSArray *attachments; NSDictionary *currentInfo; + NSArray *attachments; + + unsigned int max, count; attachments = [sourceMail fetchFileAttachments]; max = [attachments count]; @@ -793,6 +794,70 @@ static NSString *userAgent = nil; } } +// +// +// +- (void) _fileAttachmentsFromPart: (id) thePart +{ + if ([thePart isKindOfClass: [NGMimeBodyPart class]]) + { + NSString *filename, *mimeType; + id body; + + mimeType = [[thePart contentType] stringValue]; + body = [thePart body]; + filename = [(NGMimeContentDispositionHeaderField *)[thePart headerForKey: @"content-disposition"] filename]; + + if (!filename) + filename = [mimeType asPreferredFilenameUsingPath: nil]; + + if (filename) + { + NSDictionary *currentInfo; + + currentInfo = [NSDictionary dictionaryWithObjectsAndKeys: + filename, @"filename", + mimeType, @"mimetype", + nil]; + [self saveAttachment: body + withMetadata: currentInfo]; + } + } + else if ([thePart isKindOfClass: [NGMimeMultipartBody class]]) + { + NSArray *parts; + int i; + + parts = [thePart parts]; + for (i = 0; i < [parts count]; i++) + { + [self _fileAttachmentsFromPart: [parts objectAtIndex: i]]; + } + } +} + + +// +// +// +- (void) _fetchAttachmentsFromEncryptedMail: (SOGoMailObject *) sourceMail +{ + NSData *certificate; + + certificate = [[self mailAccountFolder] certificate]; + + // If we got a user certificate, let's use it. Otherwise we fallback we + // don't try to get any attachments from the encrypted content + if (certificate) + { + NGMimeMessage *m; + + m = [[sourceMail content] messageFromEncryptedDataAndCertificate: certificate]; + [self _fileAttachmentsFromPart: [m body]]; + } +} + + // // // @@ -912,7 +977,10 @@ static NSString *userAgent = nil; if ([[ud mailMessageForwarding] isEqualToString: @"inline"]) { [self setText: [sourceMail contentForInlineForward]]; - [self _fetchAttachmentsFromMail: sourceMail]; + if ([sourceMail isEncrypted]) + [self _fetchAttachmentsFromEncryptedMail: sourceMail]; + else + [self _fetchAttachmentsFromMail: sourceMail]; } else { @@ -1009,21 +1077,24 @@ static NSString *userAgent = nil; { NSString *p, *pmime, *name, *mimeType; - if (![_attach isNotNull]) { - return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ - reason: @"Missing attachment content!"]; - } + if (![_attach isNotNull]) + { + return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ + reason: @"Missing attachment content!"]; + } - if (![self _ensureDraftFolderPath]) { - return [NSException exceptionWithHTTPStatus:500 /* Server Error */ - reason: @"Could not create folder for draft!"]; - } + if (![self _ensureDraftFolderPath]) + { + return [NSException exceptionWithHTTPStatus: 500 /* Server Error */ + reason: @"Could not create folder for draft!"]; + } name = [[metadata objectForKey: @"filename"] asSafeFilename]; p = [self pathToAttachmentWithName: name]; + if (![_attach writeToFile: p atomically: YES]) { - return [NSException exceptionWithHTTPStatus:500 /* Server Error */ + return [NSException exceptionWithHTTPStatus: 500 /* Server Error */ reason: @"Could not write attachment to draft!"]; } diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index ba39480d0..fdf51291c 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -786,8 +786,8 @@ static BOOL debugSoParts = NO; if ([prefix hasSuffix: @"/"]) prefix = [prefix substringToIndex: [prefix length] - 1]; [self _feedFileAttachmentIds: attachmentIds - withInfos: [coreInfos objectForKey: @"bodystructure"] - andPrefix: prefix]; + withInfos: [coreInfos objectForKey: @"bodystructure"] + andPrefix: prefix]; return attachmentIds; } @@ -811,22 +811,7 @@ static BOOL debugSoParts = NO; if (!filename) { - // We might end up here because of MUA that actually strips the - // Content-Disposition (and thus, the filename) when mails containing - // attachments have been forwarded. Thunderbird (2.x) does just that - // when forwarding mails with images attached to them (using cid:...). - if ([mimeType hasPrefix: @"application/"] || - [mimeType hasPrefix: @"audio/"] || - [mimeType hasPrefix: @"image/"] || - [mimeType hasPrefix: @"video/"]) - { - filename = [NSString stringWithFormat: @"unknown_%@", path]; - } - else - { - if ([mimeType isEqualToString: @"message/rfc822"]) - filename = [NSString stringWithFormat: @"email_%@.eml", path]; - } + filename = [mimeType asPreferredFilenameUsingPath: path]; } if (filename)