fix(mail): Fix images not displayed when forward / reply to a mail. Fixes #3981

This commit is contained in:
smizrahi
2023-03-07 10:17:02 +01:00
parent 0d2f6d8c2a
commit 4dc8ef946d
5 changed files with 193 additions and 17 deletions

View File

@@ -859,7 +859,7 @@ static NSString *userAgent = nil;
//
//
//
- (void) _fetchAttachmentsFromMail: (SOGoMailObject *) sourceMail
- (void) _fetchAttachmentsFromMail: (SOGoMailObject *) sourceMail onlyImages: (BOOL) onlyImages
{
NSMutableDictionary *currentInfo;
NSArray *attachments;
@@ -871,15 +871,19 @@ static NSString *userAgent = nil;
for (count = 0; count < max; count++)
{
currentInfo = [attachments objectAtIndex: count];
[self saveAttachment: [currentInfo objectForKey: @"body"]
if (!onlyImages
|| (onlyImages && [[NGMimeBodyPart imageMimeTypes] containsObject: [currentInfo objectForKey: @"mimetype"]])) {
[self saveAttachment: [currentInfo objectForKey: @"body"]
withMetadata: currentInfo];
}
}
}
//
//
//
- (void) _fileAttachmentsFromPart: (id) thePart
- (void) _fileAttachmentsFromPart: (id) thePart onlyImages: (BOOL) onlyImages
{
// Small hack to avoid SOPE's stupid behavior to wrap a multipart
// object in a NGMimeBodyPart.
@@ -889,10 +893,15 @@ static NSString *userAgent = nil;
if ([thePart isKindOfClass: [NGMimeBodyPart class]])
{
NSString *filename, *mimeType;
NSString *filename, *mimeType, *bodyId;
id body;
if (onlyImages && ![thePart isImage]) {
return;
}
mimeType = [[thePart contentType] stringValue];
bodyId = [[thePart contentId] stringValue];
body = [thePart body];
filename = [(NGMimeContentDispositionHeaderField *)[thePart headerForKey: @"content-disposition"] filename];
@@ -906,6 +915,7 @@ static NSString *userAgent = nil;
currentInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
filename, @"filename",
mimeType, @"mimetype",
bodyId, @"bodyid",
nil];
[self saveAttachment: body
withMetadata: currentInfo];
@@ -919,7 +929,7 @@ static NSString *userAgent = nil;
parts = [thePart parts];
for (i = 0; i < [parts count]; i++)
{
[self _fileAttachmentsFromPart: [parts objectAtIndex: i]];
[self _fileAttachmentsFromPart: [parts objectAtIndex: i] onlyImages: onlyImages];
}
}
}
@@ -928,7 +938,7 @@ static NSString *userAgent = nil;
//
//
//
- (void) _fetchAttachmentsFromEncryptedMail: (SOGoMailObject *) sourceMail
- (void) _fetchAttachmentsFromEncryptedMail: (SOGoMailObject *) sourceMail onlyImages: (BOOL) onlyImages
{
NSData *certificate;
@@ -941,7 +951,7 @@ static NSString *userAgent = nil;
NGMimeMessage *m;
m = [[sourceMail content] messageFromEncryptedDataAndCertificate: certificate];
[self _fileAttachmentsFromPart: [m body]];
[self _fileAttachmentsFromPart: [m body] onlyImages: onlyImages];
}
}
@@ -949,12 +959,12 @@ static NSString *userAgent = nil;
//
//
//
- (void) _fetchAttachmentsFromOpaqueSignedMail: (SOGoMailObject *) sourceMail
- (void) _fetchAttachmentsFromOpaqueSignedMail: (SOGoMailObject *) sourceMail onlyImages: (BOOL) onlyImages
{
NGMimeMessage *m;
m = [[sourceMail content] messageFromOpaqueSignedData];
[self _fileAttachmentsFromPart: [m body]];
[self _fileAttachmentsFromPart: [m body] onlyImages: onlyImages];
}
@@ -973,7 +983,7 @@ static NSString *userAgent = nil;
[sourceMail fetchCoreInfos];
[self _fetchAttachmentsFromMail: sourceMail];
[self _fetchAttachmentsFromMail: sourceMail onlyImages: NO];
info = [NSMutableDictionary dictionaryWithCapacity: 16];
subject = [sourceMail subject];
if ([subject length] > 0)
@@ -1057,6 +1067,22 @@ static NSString *userAgent = nil;
[self setSourceIMAP4ID: [[sourceMail nameInContainer] intValue]];
[self setSourceFolderWithMailObject: sourceMail];
ud = [[context activeUser] userDefaults];
// TODO: Change mailMessageForwarding for reply
if ([[ud mailMessageForwarding] isEqualToString: @"inline"])
{
[self setText: [sourceMail contentForInlineForward]];
if ([sourceMail isEncrypted])
[self _fetchAttachmentsFromEncryptedMail: sourceMail onlyImages: YES];
else if ([sourceMail isOpaqueSigned])
[self _fetchAttachmentsFromOpaqueSignedMail: sourceMail onlyImages: YES];
else
[self _fetchAttachmentsFromMail: sourceMail onlyImages: YES];
}
[self save];
[self storeInfo];
}
@@ -1090,15 +1116,16 @@ static NSString *userAgent = nil;
/* attach message */
ud = [[context activeUser] userDefaults];
if ([[ud mailMessageForwarding] isEqualToString: @"inline"])
{
[self setText: [sourceMail contentForInlineForward]];
if ([sourceMail isEncrypted])
[self _fetchAttachmentsFromEncryptedMail: sourceMail];
[self _fetchAttachmentsFromEncryptedMail: sourceMail onlyImages: NO];
else if ([sourceMail isOpaqueSigned])
[self _fetchAttachmentsFromOpaqueSignedMail: sourceMail];
[self _fetchAttachmentsFromOpaqueSignedMail: sourceMail onlyImages: NO];
else
[self _fetchAttachmentsFromMail: sourceMail];
[self _fetchAttachmentsFromMail: sourceMail onlyImages: NO];
}
else
{
@@ -1195,7 +1222,7 @@ static NSString *userAgent = nil;
withMetadata: (NSMutableDictionary *) metadata
{
NSFileManager *fm;
NSString *p, *pmime, *name, *baseName, *extension, *mimeType;
NSString *p, *pmime, *pbodyId, *name, *baseName, *extension, *mimeType, *bodyId;
int i;
if (![_attach isNotNull])
@@ -1216,6 +1243,7 @@ static NSString *userAgent = nil;
fm = [NSFileManager defaultManager];
p = [self pathToAttachmentWithName: name];
i = 1;
bodyId = nil;
while ([fm isReadableFileAtPath: p])
{
@@ -1244,6 +1272,18 @@ static NSString *userAgent = nil;
reason: @"Could not write attachment to draft!"];
}
}
bodyId = [metadata objectForKey: @"bodyId"];
if ([bodyId length] > 0)
{
pbodyId = [self pathToAttachmentWithName: [NSString stringWithFormat: @".%@.bodyid", name]];
if (![[bodyId dataUsingEncoding: NSUTF8StringEncoding] writeToFile: pbodyId atomically: YES])
{
[[NSFileManager defaultManager] removeFileAtPath: p handler: nil];
return [NSException exceptionWithHTTPStatus: 500 /* Server Error */
reason: @"Could not write body idattachment to draft!"];
}
}
return nil; /* everything OK */
}
@@ -1381,6 +1421,27 @@ static NSString *userAgent = nil;
return s;
}
- (NSString *) bodyIdForAttachmentWithName: (NSString *) _name
{
NSString *s, *p;
NSData *bodyIdData;
p = [self pathToAttachmentWithName: [NSString stringWithFormat: @".%@.bodyid", _name]];
bodyIdData = [NSData dataWithContentsOfFile: p];
if (bodyIdData)
{
s = [[NSString alloc] initWithData: bodyIdData
encoding: NSUTF8StringEncoding];
[s autorelease];
}
else
{
s = nil;
}
return s;
}
- (NSString *) contentDispositionForAttachmentWithName: (NSString *) _name
andContentType: (NSString *) _type
{
@@ -1441,6 +1502,9 @@ static NSString *userAgent = nil;
else if ([s hasPrefix: @"message/rfc822"])
attachAsRFC822 = YES;
}
if ((s = [self bodyIdForAttachmentWithName:_name]) != nil) {
[map setObject: s forKey: @"content-id"];
}
if ((s = [self contentDispositionForAttachmentWithName: _name andContentType: s]))
{
NGMimeContentDispositionHeaderField *o;

View File

@@ -91,7 +91,9 @@ SOGo_HEADER_FILES = \
SOGoTextTemplateFile.h \
SOGoZipArchiver.h \
\
JWT.h
JWT.h \
\
NGMimeBodyPart+SOGo.h
all::
@touch SOGoBuild.m
@@ -176,7 +178,9 @@ SOGo_OBJC_FILES = \
SOGoTextTemplateFile.m \
SOGoZipArchiver.m \
\
JWT.m
JWT.m \
\
NGMimeBodyPart+SOGo.m
SOGo_C_FILES += lmhash.c aes.c crypt_blowfish.c pkcs5_pbkdf2.c

View File

@@ -0,0 +1,35 @@
/* NGMimeBodyPart+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2023 Alinto
*
* Author: Sébastien Mizrahi <smizrahi@alinto.eu>
*
* 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)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef NGMIMEBODYPART_SOGO_H
#define NGMIMEBODYPART_SOGO_H
#import <NGMime/NGMimeBodyPart.h>
@interface NGMimeBodyPart (SOGoExtensions)
+ (NSArray *) imageMimeTypes;
- (BOOL) isImage;
@end
#endif /* NGMIMEBODYPART_SOGO_H */

View File

@@ -0,0 +1,41 @@
/* NGMimeBodyPart+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2023 Alinto
*
* Author: Sébastien Mizrahi <smizrahi@alinto.eu>
*
* 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)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import "NGMimeBodyPart+SOGo.h"
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
@implementation NGMimeBodyPart (SOGoExtensions)
+ (NSArray *) imageMimeTypes
{
return [NSArray arrayWithObjects: @"image/png", @"image/gif", @"image/tiff"
, @"image/jpeg", @"image/bmp", @"image/svg+xml", @"image/webp", nil];
}
- (BOOL) isImage
{
return [[NGMimeBodyPart imageMimeTypes] containsObject: [[self contentType] stringValue]];
}
@end

View File

@@ -42,6 +42,7 @@
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/WOResourceManager+SOGo.h>
#import <SOGo/NGMimeBodyPart+SOGo.h>
#import <SOGoUI/UIxComponent.h>
#import <Mailer/SOGoDraftObject.h>
#import <Mailer/SOGoMailObject+Draft.h>
@@ -702,6 +703,34 @@ static NSArray *infoKeys = nil;
return [[self attachmentAttrs] count] > 0 ? YES : NO;
}
/*
* This method replace cid images in body with base64 inline image data
*/
- (void) setBase64ImagesInText:(SOGoDraftObject *) draft
{
NSString *contentId, *lText;
NGMimeBodyPart *mime;
if ([self isHTML] && [[draft fetchAttachmentAttrs] count] > 0) {
for (NSDictionary *draftFileAttachement in [draft fetchAttachmentAttrs]) {
mime = [draftFileAttachement objectForKey: @"part"];
if ([mime isImage]) {
contentId = [mime contentId];
contentId = [contentId stringByReplacingOccurrencesOfString: @"<" withString: @"cid:"];
contentId = [contentId stringByReplacingOccurrencesOfString: @">" withString: @""];
if ([[mime encoding] isEqualToString: @"base64"]) {
lText = [text stringByReplacingOccurrencesOfString: contentId
withString: [NSString stringWithFormat: @"data:%@;base64,%@",
[[mime contentType] stringValue],
[NSString stringWithUTF8String: [[mime body] bytes]]]];
[self setText: lText];
}
}
}
}
}
- (id <WOActionResults>) editAction
{
id <WOActionResults> response;
@@ -720,7 +749,6 @@ static NSArray *infoKeys = nil;
data = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[self localeCode], @"locale",
[NSNumber numberWithBool: [self isHTML]], @"isHTML",
text, @"text",
nil];
if ((value = [self from]))
[data setObject: value forKey: @"from"];
@@ -737,6 +765,10 @@ static NSArray *infoKeys = nil;
if ((value = [self attachmentAttrs]))
[data setObject: value forKey: @"attachmentAttrs"];
[self setBase64ImagesInText: co];
[data setObject: text forKey: @"text"];
response = [self responseWithStatus: 200
andString: [data jsonRepresentation]];