(fix) remaining S/MIME fixes to handle image/CIDs in HTML mails

pull/240/head
Ludovic Marcotte 2018-01-23 10:35:46 -05:00
parent 341e5cbab0
commit 511aa63a34
13 changed files with 221 additions and 92 deletions

View File

@ -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;

View File

@ -37,6 +37,7 @@
#import <NGMime/NGMimeMultipartBody.h>
#import <SoObjects/SOGo/NSDictionary+Utilities.h>
#import <SoObjects/SOGo/NSObject+Utilities.h>
#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];
}

View File

@ -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 <DOM/DOMProtocols.h>
@class NSArray;
@class NSString;
@class WOContext;
@ -39,6 +40,8 @@
+ (void) memoryStatistics;
- (NSArray *) parts;
@end
#endif /* NSOBJECT+UTILITIES_H */

View File

@ -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 <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeMultipartBody.h>
#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

View File

@ -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 <Foundation/NSDictionary.h>
#import <Foundation/NSNull.h>
#import <SOGo/NSObject+Utilities.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGMail/NGMimeMessageParser.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeMultipartBody.h>
#import <NGMime/NGMimeType.h>
@ -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]];
}

View File

@ -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 <Foundation/NSDictionary.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGMail/NGMimeMessageParser.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeHeaderFields.h>
#import <NGMime/NGMimeMultipartBody.h>
#import <NGMime/NGMimeType.h>
#import <NGMail/NGMimeMessageParser.h>
#import <SoObjects/Mailer/NSData+SMIME.h>
#import <SoObjects/Mailer/NSString+Mail.h>
#import <SoObjects/Mailer/SOGoMailAccount.h>
#import <SoObjects/Mailer/SOGoMailObject.h>
#import <UI/MailerUI/WOContext+UIxMailer.h>
@ -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 */

View File

@ -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

View File

@ -22,6 +22,9 @@
#import <Foundation/NSDictionary.h>
#import <SOGo/NSObject+Utilities.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeMultipartBody.h>
#import <UI/MailerUI/WOContext+UIxMailer.h>
@ -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]];
}

View File

@ -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]];
}

View File

@ -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]] ||

View File

@ -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];

View File

@ -2,8 +2,6 @@
*
* Copyright (C) 2006 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* 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)

View File

@ -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: