diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index a497b65bf..1a8e68924 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2016 Inverse inc. + Copyright (C) 2007-2018 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. @@ -799,6 +799,12 @@ static NSString *userAgent = nil; // - (void) _fileAttachmentsFromPart: (id) thePart { + // Small hack to avoid SOPE's stupid behavior to wrap a multipart + // object in a NGMimeBodyPart. + if ([thePart isKindOfClass: [NGMimeBodyPart class]] && + [[[thePart contentType] type] isEqualToString: @"multipart"]) + thePart = [thePart body]; + if ([thePart isKindOfClass: [NGMimeBodyPart class]]) { NSString *filename, *mimeType; diff --git a/SoObjects/Mailer/SOGoMailBodyPart.m b/SoObjects/Mailer/SOGoMailBodyPart.m index 1cc1d485d..266b18bb9 100644 --- a/SoObjects/Mailer/SOGoMailBodyPart.m +++ b/SoObjects/Mailer/SOGoMailBodyPart.m @@ -37,6 +37,7 @@ #import #import +#import #import "NSData+SMIME.h" #import "NSDictionary+Mail.h" @@ -169,35 +170,65 @@ static BOOL debugOn = NO; inContext: (WOContext *) localContext { // TODO: we might want to check for existence prior controller creation - Class clazz; - NSArray *subParts; - unsigned int nbr; - id obj; NSDictionary *subPart, *infos; + NSString *mimeType; + NSArray *subParts; + Class clazz; + id o, obj; + + unsigned int nbr; nbr = [key intValue]; - infos = [self partInfo]; - subParts = [infos objectForKey: @"parts"]; - if (!subParts) - subParts = [[infos objectForKey: @"body"] objectForKey: @"parts"]; + o = [self container]; - if (nbr > 0 && nbr < ([subParts count] + 1)) + while (![o isKindOfClass: [SOGoMailObject class]]) + o = [o container]; + + if ([o isEncrypted]) { - subPart = [subParts objectAtIndex: nbr - 1]; - clazz - = [[self class] bodyPartClassForMimeType: - [subPart keysWithFormat: @"%{type}/%{subtype}"] - inContext: localContext]; - obj = [clazz objectWithName: key inContainer: self]; + NSData *certificate; + NGMimeMessage *m; + id part; + + int i; + + certificate = [[self mailAccountFolder] certificate]; + m = [[o content] messageFromEncryptedDataAndCertificate: certificate]; + part = [m body]; + + for (i = 0; i < [[self bodyPartPath] count]; i++) + { + nbr = [[[self bodyPartPath] objectAtIndex: i] intValue]-1; + part = [[part parts] objectAtIndex: nbr];; + } + + //part = [[[m body] parts] objectAtIndex: ([key intValue]-1)]; + part = [[part parts] objectAtIndex: ([key intValue]-1)]; + mimeType = [[part contentType] stringValue]; + clazz = [SOGoMailBodyPart bodyPartClassForMimeType: mimeType + inContext: localContext]; + obj = [clazz objectWithName:key inContainer: self]; } else - obj = self; + { + infos = [self partInfo]; + subParts = [infos objectForKey: @"parts"]; + if (!subParts) + subParts = [[infos objectForKey: @"body"] objectForKey: @"parts"]; - return obj; -// clazz = [SOGoMailBodyPart bodyPartClassForKey: _key -// inContext: _ctx]; + if (nbr > 0 && nbr < ([subParts count] + 1)) + { + subPart = [subParts objectAtIndex: nbr - 1]; + mimeType = [subPart keysWithFormat: @"%{type}/%{subtype}"]; + clazz = [[self class] bodyPartClassForMimeType: mimeType + inContext: localContext]; + obj = [clazz objectWithName: key inContainer: self]; + } + else + obj = self; + } -// return [clazz objectWithName: _key inContainer: self]; + return obj; } - (NSString *) filename @@ -304,23 +335,29 @@ static BOOL debugOn = NO; if ([o isEncrypted]) { NSData *certificate; + NGMimeMessage *m; + id part; + unsigned int i, nbr; + + // No need to check if the cert is valid as we already do so + // in SOGoMailObject. certificate = [[self mailAccountFolder] certificate]; - // If we got a user certificate, let's use it. Otherwise we fallback - // to the current BLOB fetching code. - if (certificate) + m = [[o content] messageFromEncryptedDataAndCertificate: certificate]; + part = [m body]; + + for (i = 0; i < [[self bodyPartPath] count]; i++) { - NGMimeMessage *m; - id part; - - m = [[container content] messageFromEncryptedDataAndCertificate: certificate]; - part = [[[m body] parts] objectAtIndex: ([[self nameInContainer] intValue]-1)]; - - return [part body]; + nbr = [[[self bodyPartPath] objectAtIndex: i] intValue]-1; + part = [[part parts] objectAtIndex: nbr];; } + + return [part body]; } + // The mail is not encrypted, lets fetch the body party normally + // straight from the IMAP server return [self fetchBLOBWithPeek: NO]; } diff --git a/SoObjects/SOGo/NSObject+Utilities.h b/SoObjects/SOGo/NSObject+Utilities.h index 867725db8..8c97b2fd9 100644 --- a/SoObjects/SOGo/NSObject+Utilities.h +++ b/SoObjects/SOGo/NSObject+Utilities.h @@ -1,6 +1,6 @@ /* NSObject+Utilities.h - this file is part of SOGo * - * Copyright (C) 2007-2015 Inverse inc. + * Copyright (C) 2007-2017 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 @@ -24,6 +24,7 @@ #import +@class NSArray; @class NSString; @class WOContext; @@ -39,6 +40,8 @@ + (void) memoryStatistics; +- (NSArray *) parts; + @end #endif /* NSOBJECT+UTILITIES_H */ diff --git a/SoObjects/SOGo/NSObject+Utilities.m b/SoObjects/SOGo/NSObject+Utilities.m index 21d366083..dfd313c3d 100644 --- a/SoObjects/SOGo/NSObject+Utilities.m +++ b/SoObjects/SOGo/NSObject+Utilities.m @@ -1,6 +1,6 @@ /* NSObject+Utilities.m - this file is part of SOGo * - * Copyright (C) 2007-2015 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 @@ -24,6 +24,9 @@ #import #import +#import +#import + #import "NSDictionary+Utilities.h" #import "SOGoUser.h" #import "SOGoUserDefaults.h" @@ -147,4 +150,20 @@ printf("Done!\n"); } +// +// Small hack to avoid SOPE's stupid behavior to wrap a multipart +// object in a NGMimeBodyPart. +// +- (NSArray *) parts +{ + if ([self isKindOfClass: [NGMimeMultipartBody class]]) + return [self parts]; + + if ([self isKindOfClass: [NGMimeBodyPart class]] && + [[(id)self body] isKindOfClass: [NGMimeMultipartBody class]]) + return [[(id)self body] parts]; + + return [NSArray array]; +} + @end diff --git a/UI/MailPartViewers/UIxMailPartAlternativeViewer.m b/UI/MailPartViewers/UIxMailPartAlternativeViewer.m index ac2fed8f2..8b6a89f13 100644 --- a/UI/MailPartViewers/UIxMailPartAlternativeViewer.m +++ b/UI/MailPartViewers/UIxMailPartAlternativeViewer.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2017 Inverse inc. + Copyright (C) 2007-2018 Inverse inc. Copyright (C) 2004 SKYRIX Software AG This file is part of SOGo. @@ -23,8 +23,11 @@ #import #import +#import + #import #import +#import #import #import @@ -65,20 +68,20 @@ NSUInteger i, count; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) childParts = [[self decodedFlatContent] parts]; else - childParts = [[self bodyInfo] valueForKey:@"parts"]; + childParts = [[self bodyInfo] valueForKey: @"parts"]; count = [childParts count]; types = [NSMutableArray arrayWithCapacity: count]; for (i = 0; i < count; i++) { - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) { - mt = [[[[[self decodedFlatContent] parts] objectAtIndex: i] contentType] type]; - st = [[[[[self decodedFlatContent] parts] objectAtIndex: i] contentType] subType]; + mt = [[[childParts objectAtIndex: i] contentType] type]; + st = [[[childParts objectAtIndex: i] contentType] subType]; } else { @@ -141,20 +144,19 @@ { NSUInteger idx; - [childInfo release]; childInfo = nil; + DESTROY(childInfo); childIndex = 0; idx = [self _selectPartIndexFromTypes: [self childPartTypes]]; if (idx == NSNotFound) { - [self errorWithFormat:@"could not select a part of types: %@", - [self childPartTypes]]; + [self errorWithFormat:@"could not select a part of types: %@", [self childPartTypes]]; return; } childIndex = idx + 1; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) childInfo = [[[[self decodedFlatContent] parts] objectAtIndex: idx] bodyInfo]; else childInfo = [[[self bodyInfo] valueForKey:@"parts"] objectAtIndex: idx]; @@ -172,17 +174,18 @@ return [[[self context] mailRenderingContext] viewerForBodyInfo: info]; } -- (id) renderedPart { +- (id) renderedPart +{ id info, viewer; NSArray *parts; NSMutableArray *renderedParts; NSString *preferredType; NSUInteger i, max; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) parts = [[self decodedFlatContent] parts]; else - parts = [[self bodyInfo] valueForKey:@"parts"]; + parts = [[self bodyInfo] valueForKey: @"parts"]; max = [parts count]; renderedParts = [NSMutableArray arrayWithCapacity: max]; @@ -190,7 +193,7 @@ { [self setChildIndex: i]; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) [self setChildInfo: [[parts objectAtIndex: i] bodyInfo]]; else [self setChildInfo: [parts objectAtIndex: i]]; @@ -200,7 +203,7 @@ [viewer setBodyInfo: info]; [viewer setPartPath: [self childPartPath]]; [viewer setAttachmentIds: attachmentIds]; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) [viewer setDecodedContent: [parts objectAtIndex: i]]; [renderedParts addObject: [viewer renderedPart]]; } diff --git a/UI/MailPartViewers/UIxMailPartEncryptedViewer.m b/UI/MailPartViewers/UIxMailPartEncryptedViewer.m index 7ca8f5a49..43a314222 100644 --- a/UI/MailPartViewers/UIxMailPartEncryptedViewer.m +++ b/UI/MailPartViewers/UIxMailPartEncryptedViewer.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2017 Inverse inc. + Copyright (C) 2017-2018 Inverse inc. This file is part of SOGo. @@ -21,12 +21,17 @@ #import #import +#import #import -#import +#import +#import +#import #import +#import #import +#import #import #import #import @@ -36,7 +41,45 @@ @implementation UIxMailPartEncryptedViewer -/* nested viewers */ +- (void) _attachmentIdsFromBodyPart: (id) thePart + partPath: (NSString *) thePartPath +{ + // Small hack to avoid SOPE's stupid behavior to wrap a multipart + // object in a NGMimeBodyPart. + if ([thePart isKindOfClass: [NGMimeBodyPart class]] && + [[[thePart contentType] type] isEqualToString: @"multipart"]) + thePart = [thePart body]; + + if ([thePart isKindOfClass: [NGMimeBodyPart class]]) + { + NSString *filename, *mimeType; + + mimeType = [[thePart contentType] stringValue]; + filename = [(NGMimeContentDispositionHeaderField *)[thePart headerForKey: @"content-disposition"] filename]; + + if (!filename) + filename = [mimeType asPreferredFilenameUsingPath: nil]; + + if (filename) + { + [(id)attachmentIds setObject: [NSString stringWithFormat: @"%@%@%@", + [[self clientObject] baseURLInContext: [self context]], + thePartPath, + filename] + forKey: [NSString stringWithFormat: @"<%@>", filename]]; + } + } + else if ([thePart isKindOfClass: [NGMimeMultipartBody class]]) + { + int i; + + for (i = 0; i < [[thePart parts] count]; i++) + { + [self _attachmentIdsFromBodyPart: [[thePart parts] objectAtIndex: i] + partPath: [NSString stringWithFormat: @"%@%d/", thePartPath, i+1]]; + } + } +} - (id) contentViewerComponent { @@ -49,7 +92,6 @@ - (id) renderedPart { NSData *certificate, *decryptedData, *encryptedData; - id info, viewer; certificate = [[[self clientObject] mailAccountFolder] certificate]; @@ -71,16 +113,25 @@ [viewer setFlatContent: decryptedData]; [viewer setDecodedContent: [part body]]; + // attachmentIds is empty in an ecrypted email as the IMAP body structure + // is of course not available for file attachments + [self _attachmentIdsFromBodyPart: [part body] partPath: @""]; + [viewer setAttachmentIds: attachmentIds]; + return [NSDictionary dictionaryWithObjectsAndKeys: [self className], @"type", + [NSNumber numberWithBool: YES], @"valid", [NSArray arrayWithObject: [viewer renderedPart]], @"content", nil]; } // Decryption failed, let's return something else... - // FIXME - does not work for now. - return nil; + return [NSDictionary dictionaryWithObjectsAndKeys: + [self className], @"type", + [NSNumber numberWithBool: NO], @"valid", + [NSArray array], @"content", + nil]; } @end /* UIxMailPartAlternativeViewer */ diff --git a/UI/MailPartViewers/UIxMailPartImageViewer.m b/UI/MailPartViewers/UIxMailPartImageViewer.m index d528f133a..5cc80c928 100644 --- a/UI/MailPartViewers/UIxMailPartImageViewer.m +++ b/UI/MailPartViewers/UIxMailPartImageViewer.m @@ -1,6 +1,5 @@ /* - Copyright (C) 2007-2017 Inverse inc. - Copyright (C) 2004-2005 SKYRIX Software AG + Copyright (C) 2007-2018 Inverse inc. This file is part of SOGo diff --git a/UI/MailPartViewers/UIxMailPartMixedViewer.m b/UI/MailPartViewers/UIxMailPartMixedViewer.m index 971f25707..6ae891695 100644 --- a/UI/MailPartViewers/UIxMailPartMixedViewer.m +++ b/UI/MailPartViewers/UIxMailPartMixedViewer.m @@ -22,6 +22,9 @@ #import +#import + +#import #import #import @@ -31,24 +34,25 @@ @implementation UIxMailPartMixedViewer -- (void)dealloc { +- (void) dealloc +{ [self->childInfo release]; [super dealloc]; } -/* caches */ - -- (void)resetBodyInfoCaches { - [self->childInfo release]; self->childInfo = nil; +- (void)resetBodyInfoCaches +{ + DESTROY(self->childInfo); [super resetBodyInfoCaches]; } -/* accessors */ - -- (void)setChildInfo:(id)_info { +- (void) setChildInfo:(id)_info +{ ASSIGN(self->childInfo, _info); } -- (id)childInfo { + +- (id) childInfo +{ return self->childInfo; } @@ -56,6 +60,7 @@ { self->childIndex = _index; } + - (NSUInteger) childIndex { return self->childIndex; @@ -67,7 +72,7 @@ (unsigned int) ([self childIndex] + 1)]; } -- (id)childPartPath +- (id) childPartPath { NSArray *pp; @@ -96,7 +101,7 @@ NSUInteger i, max; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) parts = [[self decodedFlatContent] parts]; else parts = [[self bodyInfo] valueForKey: @"parts"]; @@ -108,7 +113,7 @@ { [self setChildIndex: i]; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) [self setChildInfo: [[parts objectAtIndex: i] bodyInfo]]; else [self setChildInfo: [parts objectAtIndex: i]]; @@ -117,9 +122,10 @@ viewer = [[[self context] mailRenderingContext] viewerForBodyInfo: info]; [viewer setBodyInfo: info]; [viewer setPartPath: [self childPartPath]]; - [viewer setAttachmentIds: attachmentIds]; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) [viewer setDecodedContent: [parts objectAtIndex: i]]; + [viewer setAttachmentIds: attachmentIds]; + [renderedParts addObject: [viewer renderedPart]]; } diff --git a/UI/MailPartViewers/UIxMailPartSignedViewer.m b/UI/MailPartViewers/UIxMailPartSignedViewer.m index 282abab45..ab0fa4053 100644 --- a/UI/MailPartViewers/UIxMailPartSignedViewer.m +++ b/UI/MailPartViewers/UIxMailPartSignedViewer.m @@ -247,7 +247,7 @@ NSUInteger i, max; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) parts = [[self decodedFlatContent] parts]; else parts = [[self bodyInfo] objectForKey: @"parts"]; @@ -259,7 +259,7 @@ { [self setChildIndex: i]; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) [self setChildInfo: [[parts objectAtIndex: i] bodyInfo]]; else [self setChildInfo: [parts objectAtIndex: i]]; @@ -268,10 +268,11 @@ viewer = [[[self context] mailRenderingContext] viewerForBodyInfo: info]; [viewer setBodyInfo: info]; [viewer setPartPath: [self childPartPath]]; - [viewer setAttachmentIds: attachmentIds]; - if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]]) + if ([self decodedFlatContent]) [viewer setDecodedContent: [[parts objectAtIndex: i] body]]; + + [viewer setAttachmentIds: attachmentIds]; [renderedParts addObject: [viewer renderedPart]]; } diff --git a/UI/MailPartViewers/UIxMailPartViewer.m b/UI/MailPartViewers/UIxMailPartViewer.m index de519b375..f0db8d56c 100644 --- a/UI/MailPartViewers/UIxMailPartViewer.m +++ b/UI/MailPartViewers/UIxMailPartViewer.m @@ -214,8 +214,7 @@ // but rather directly with NGMime objects. if ([content isKindOfClass: [NSData class]]) { - charset = [[bodyInfo objectForKey:@"parameterList"] - objectForKey: @"charset"]; + charset = [[bodyInfo objectForKey: @"parameterList"] objectForKey: @"charset"]; s = [content bodyStringFromCharset: charset]; } else if ([content isKindOfClass: [NGMimeBodyPart class]] || diff --git a/UI/MailPartViewers/UIxMailRenderingContext.m b/UI/MailPartViewers/UIxMailRenderingContext.m index b2f252654..e2cf01155 100644 --- a/UI/MailPartViewers/UIxMailRenderingContext.m +++ b/UI/MailPartViewers/UIxMailRenderingContext.m @@ -203,7 +203,7 @@ static BOOL showNamedTextAttachmentsInline = NO; else if ([mt isEqualToString: @"text"]) { if ([st isEqualToString: @"plain"] || [st isEqualToString: @"html"]) { - if (!showNamedTextAttachmentsInline && [self _shouldDisplayAsAttachment: _info textPart:YES]) + if (!showNamedTextAttachmentsInline && [self _shouldDisplayAsAttachment: _info textPart: YES]) return [self linkViewer]; return [st isEqualToString: @"html"] @@ -219,7 +219,7 @@ static BOOL showNamedTextAttachmentsInline = NO; if ([mt isEqualToString: @"image"] && !([st isEqualToString: @"tiff"] || [st isEqualToString: @"pdf"])) { - if ([self _shouldDisplayAsAttachment: _info textPart: NO]) + if ([self _shouldDisplayAsAttachment: _info textPart: NO]) return [self linkViewer]; return [self imageViewer]; diff --git a/UI/MailerUI/UIxMailPopupView.m b/UI/MailerUI/UIxMailPopupView.m index e8d6e737b..92b2559ff 100644 --- a/UI/MailerUI/UIxMailPopupView.m +++ b/UI/MailerUI/UIxMailPopupView.m @@ -2,8 +2,6 @@ * * Copyright (C) 2006 Inverse inc. * - * Author: Wolfgang Sourdeau - * * 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 * the Free Software Foundation; either version 2, or (at your option) diff --git a/UI/MailerUI/UIxMailView.m b/UI/MailerUI/UIxMailView.m index 1d937ed17..58097ce47 100644 --- a/UI/MailerUI/UIxMailView.m +++ b/UI/MailerUI/UIxMailView.m @@ -226,19 +226,26 @@ static NSString *mailETag = nil; viewer = [[context mailRenderingContext] viewerForBodyInfo: info]; [viewer setBodyInfo: info]; - max = [[self attachmentAttrs] count]; - attachmentIds = [NSMutableDictionary dictionaryWithCapacity: max]; - for (count = 0; count < max; count++) + if (![[self clientObject] isEncrypted]) { - attributes = [[self attachmentAttrs] objectAtIndex: count]; - filename = [NSString stringWithFormat: @"<%@>", [attributes objectForKey: @"filename"]]; - [attachmentIds setObject: [attributes objectForKey: @"url"] - forKey: filename]; - if ([[attributes objectForKey: @"bodyId"] length]) - [attachmentIds setObject: [attributes objectForKey: @"url"] - forKey: [attributes objectForKey: @"bodyId"]]; + max = [[self attachmentAttrs] count]; + attachmentIds = [NSMutableDictionary dictionaryWithCapacity: max]; + for (count = 0; count < max; count++) + { + attributes = [[self attachmentAttrs] objectAtIndex: count]; + filename = [NSString stringWithFormat: @"<%@>", [attributes objectForKey: @"filename"]]; + [attachmentIds setObject: [attributes objectForKey: @"url"] + forKey: filename]; + if ([[attributes objectForKey: @"bodyId"] length]) + [attachmentIds setObject: [attributes objectForKey: @"url"] + forKey: [attributes objectForKey: @"bodyId"]]; + } + // Attachment IDs will be decoded in UIxMailPartEncryptedViewer for + // S/MIME encrypted emails with file attachments. + [viewer setAttachmentIds: attachmentIds]; } - [viewer setAttachmentIds: attachmentIds]; + else + [viewer setAttachmentIds: [NSMutableDictionary dictionary]]; // If we are looking at a S/MIME signed mail which wasn't sent // by our actual active user, we update the certificate of that @@ -327,7 +334,7 @@ static NSString *mailETag = nil; data = [NSDictionary dictionaryWithObject: [self labelForKey: @"Did not find specified message"] forKey: @"message"]; return [self responseWithStatus: 404 /* Not Found */ - andJSONRepresentation: data]; + andJSONRepresentation: data]; } data = [NSMutableDictionary dictionaryWithObjectsAndKeys: