2006-06-15 21:34:10 +02:00
|
|
|
/*
|
2017-12-23 15:21:28 +01:00
|
|
|
Copyright (C) 2007-2017 Inverse inc.
|
2006-06-15 21:34:10 +02:00
|
|
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
|
|
|
|
2009-04-01 15:49:24 +02:00
|
|
|
This file is part of SOGo.
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2009-04-01 15:49:24 +02:00
|
|
|
SOGo is free software; you can redistribute it and/or modify it under
|
2006-06-15 21:34:10 +02:00
|
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
later version.
|
|
|
|
|
2009-04-01 15:49:24 +02:00
|
|
|
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
2006-06-15 21:34:10 +02:00
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2015-05-13 04:37:58 +02:00
|
|
|
License along with SOGo; see the file COPYING. If not, write to the
|
2006-06-15 21:34:10 +02:00
|
|
|
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2007-02-20 17:15:37 +01:00
|
|
|
#import <Foundation/NSDictionary.h>
|
|
|
|
|
2007-07-30 16:10:18 +02:00
|
|
|
#import <NGExtensions/NSNull+misc.h>
|
|
|
|
#import <NGExtensions/NSObject+Logs.h>
|
|
|
|
#import <NGExtensions/NSString+Encoding.h>
|
|
|
|
#import <NGExtensions/NSString+misc.h>
|
2007-08-01 20:53:25 +02:00
|
|
|
|
2017-12-23 15:21:28 +01:00
|
|
|
#import <NGMime/NGMimeBodyPart.h>
|
|
|
|
#import <NGMime/NGMimeMultipartBody.h>
|
|
|
|
|
2015-05-13 04:37:58 +02:00
|
|
|
#import <NGObjWeb/WOResponse.h>
|
|
|
|
|
2011-02-13 23:57:04 +01:00
|
|
|
#import <SOGo/NSString+Utilities.h>
|
|
|
|
#import <Mailer/NSData+Mail.h>
|
2013-07-16 21:18:04 +02:00
|
|
|
#import <Mailer/NSDictionary+Mail.h>
|
2011-02-13 23:57:04 +01:00
|
|
|
#import <Mailer/SOGoMailBodyPart.h>
|
2007-07-30 16:10:18 +02:00
|
|
|
|
2011-02-13 23:57:04 +01:00
|
|
|
#import "MailerUI/WOContext+UIxMailer.h"
|
2007-02-20 17:15:37 +01:00
|
|
|
#import "UIxMailRenderingContext.h"
|
|
|
|
#import "UIxMailSizeFormatter.h"
|
2007-07-30 16:10:18 +02:00
|
|
|
|
|
|
|
#import "UIxMailPartViewer.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
@implementation UIxMailPartViewer
|
|
|
|
|
2017-01-11 22:08:05 +01:00
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
attachmentIds = nil;
|
2017-12-23 15:21:28 +01:00
|
|
|
flatContent = nil;
|
|
|
|
decodedContent = nil;
|
2017-01-11 22:08:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
2007-07-30 16:10:18 +02:00
|
|
|
[flatContent release];
|
2017-12-23 15:21:28 +01:00
|
|
|
[decodedContent release];
|
2007-08-01 20:53:25 +02:00
|
|
|
[bodyInfo release];
|
|
|
|
[partPath release];
|
2006-06-15 21:34:10 +02:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* caches */
|
|
|
|
|
2017-12-23 15:21:28 +01:00
|
|
|
//
|
|
|
|
// This is called when -setPartPath: is called
|
|
|
|
//
|
2007-08-01 20:53:25 +02:00
|
|
|
- (void) resetPathCaches
|
|
|
|
{
|
2017-12-23 15:21:28 +01:00
|
|
|
DESTROY(flatContent);
|
|
|
|
DESTROY(decodedContent);
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-08-01 20:53:25 +02:00
|
|
|
|
|
|
|
- (void) resetBodyInfoCaches
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* notifications */
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (void) sleep
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
[self resetPathCaches];
|
|
|
|
[self resetBodyInfoCaches];
|
2007-08-01 20:53:25 +02:00
|
|
|
[partPath release];
|
|
|
|
[bodyInfo release];
|
|
|
|
partPath = nil;
|
|
|
|
bodyInfo = nil;
|
2006-06-15 21:34:10 +02:00
|
|
|
[super sleep];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* accessors */
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (void) setPartPath: (NSArray *) _path
|
|
|
|
{
|
|
|
|
if ([_path isEqual: partPath])
|
2006-06-15 21:34:10 +02:00
|
|
|
return;
|
2007-08-01 20:53:25 +02:00
|
|
|
|
2007-07-30 16:10:18 +02:00
|
|
|
ASSIGN(partPath, _path);
|
2007-08-01 20:53:25 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
[self resetPathCaches];
|
|
|
|
}
|
2007-08-01 20:53:25 +02:00
|
|
|
|
|
|
|
- (NSArray *) partPath
|
|
|
|
{
|
2007-07-30 16:10:18 +02:00
|
|
|
return partPath;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (void) setBodyInfo: (id) _info
|
|
|
|
{
|
2007-07-30 16:10:18 +02:00
|
|
|
ASSIGN(bodyInfo, _info);
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-08-01 20:53:25 +02:00
|
|
|
|
|
|
|
- (id) bodyInfo
|
|
|
|
{
|
2007-07-30 16:10:18 +02:00
|
|
|
return bodyInfo;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (NSData *) flatContent
|
|
|
|
{
|
2007-07-30 16:10:18 +02:00
|
|
|
if (flatContent != nil)
|
2009-04-01 15:49:24 +02:00
|
|
|
return [flatContent isNotNull] ? (id)flatContent : nil;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2017-12-23 15:21:28 +01:00
|
|
|
flatContent = [[[context mailRenderingContext] flatContentForPartPath: partPath] retain];
|
|
|
|
|
2007-07-30 16:10:18 +02:00
|
|
|
return flatContent;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2017-12-23 15:21:28 +01:00
|
|
|
- (void) setFlatContent: (NSData *) theData
|
|
|
|
{
|
|
|
|
ASSIGN(flatContent, theData);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// This methods decodes quoted-printable or
|
|
|
|
// based64 encoded data coming straight
|
|
|
|
// from the IMAP server.
|
|
|
|
//
|
|
|
|
- (id) decodedFlatContent
|
2007-08-01 20:53:25 +02:00
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
NSString *enc;
|
2017-12-23 15:21:28 +01:00
|
|
|
|
|
|
|
if (decodedContent != nil)
|
|
|
|
return [decodedContent isNotNull] ? (id)decodedContent : nil;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
enc = [[bodyInfo objectForKey:@"encoding"] lowercaseString];
|
|
|
|
|
2017-12-23 15:21:28 +01:00
|
|
|
decodedContent = [[[self flatContent] bodyDataFromEncoding: enc] retain];
|
|
|
|
|
|
|
|
return decodedContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setDecodedContent: (id) theData
|
|
|
|
{
|
|
|
|
ASSIGN(decodedContent, theData);
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
- (SOGoMailBodyPart *) clientPart
|
2007-08-01 20:53:25 +02:00
|
|
|
{
|
|
|
|
id currentObject;
|
|
|
|
NSString *currentPart;
|
2007-12-17 21:24:07 +01:00
|
|
|
NSEnumerator *parts;
|
2007-08-01 20:53:25 +02:00
|
|
|
|
|
|
|
currentObject = [self clientObject];
|
|
|
|
parts = [partPath objectEnumerator];
|
2007-12-17 21:24:07 +01:00
|
|
|
while ((currentPart = [parts nextObject]))
|
|
|
|
currentObject = [currentObject lookupName: currentPart
|
|
|
|
inContext: context
|
|
|
|
acquire: NO];
|
2007-08-01 20:53:25 +02:00
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
return currentObject;
|
|
|
|
}
|
2007-08-01 20:53:25 +02:00
|
|
|
|
2015-05-13 04:37:58 +02:00
|
|
|
- (id) renderedPart
|
|
|
|
{
|
|
|
|
NSString *type;
|
|
|
|
|
|
|
|
type = [NSString stringWithFormat: @"%@/%@",
|
|
|
|
[bodyInfo objectForKey: @"type"],
|
|
|
|
[bodyInfo objectForKey: @"subtype"]];
|
|
|
|
|
|
|
|
return [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[self className], @"type",
|
|
|
|
type, @"contentType",
|
|
|
|
[[self generateResponse] contentAsString], @"content",
|
|
|
|
nil];
|
|
|
|
}
|
|
|
|
|
2017-12-23 15:21:28 +01:00
|
|
|
//
|
|
|
|
// Attachment IDs are used to replace CID from HTML content
|
|
|
|
// with their MIME parts when viewing an HTML mail with
|
|
|
|
// embedded images, defined as CID.
|
|
|
|
//
|
2017-01-11 22:08:05 +01:00
|
|
|
- (void) setAttachmentIds: (NSDictionary *) newAttachmentIds
|
|
|
|
{
|
|
|
|
attachmentIds = newAttachmentIds;
|
|
|
|
}
|
|
|
|
|
2007-08-24 20:48:32 +02:00
|
|
|
- (NSString *) flatContentAsString
|
|
|
|
{
|
2009-12-19 23:50:40 +01:00
|
|
|
NSString *charset, *s;
|
2009-12-15 01:05:06 +01:00
|
|
|
NSData *content;
|
2007-02-15 22:06:46 +01:00
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
content = [self decodedFlatContent];
|
|
|
|
if (content)
|
|
|
|
{
|
2017-12-23 15:21:28 +01:00
|
|
|
// We handle special cases for S/MIME encrypted message
|
|
|
|
// as we won't deal with IMAP body structure objects here
|
|
|
|
// but rather directly with NGMime objects.
|
|
|
|
if ([content isKindOfClass: [NSData class]])
|
|
|
|
{
|
2018-01-23 16:35:46 +01:00
|
|
|
charset = [[bodyInfo objectForKey: @"parameterList"] objectForKey: @"charset"];
|
2017-12-23 15:21:28 +01:00
|
|
|
s = [content bodyStringFromCharset: charset];
|
|
|
|
}
|
|
|
|
else if ([content isKindOfClass: [NGMimeBodyPart class]] ||
|
|
|
|
[content isKindOfClass: [NGMimeMultipartBody class]])
|
|
|
|
s = [content body];
|
|
|
|
else
|
|
|
|
s = (id)content;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2007-08-01 20:53:25 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
[self errorWithFormat:@"got no text content: %@",
|
|
|
|
[partPath componentsJoinedByString:@"."]];
|
|
|
|
s = nil;
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* path extension */
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (NSString *) pathExtensionForType: (NSString *) _mt
|
|
|
|
subtype: (NSString *) _st
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
// TODO: support /etc/mime.types
|
|
|
|
|
|
|
|
if (![_mt isNotNull] || ![_st isNotNull])
|
|
|
|
return nil;
|
|
|
|
if ([_mt length] == 0) return nil;
|
|
|
|
if ([_st length] == 0) return nil;
|
|
|
|
_mt = [_mt lowercaseString];
|
|
|
|
_st = [_st lowercaseString];
|
|
|
|
|
|
|
|
if ([_mt isEqualToString:@"image"]) {
|
|
|
|
if ([_st isEqualToString:@"gif"]) return @"gif";
|
|
|
|
if ([_st isEqualToString:@"jpeg"]) return @"jpg";
|
|
|
|
if ([_st isEqualToString:@"png"]) return @"png";
|
|
|
|
}
|
|
|
|
else if ([_mt isEqualToString:@"text"]) {
|
|
|
|
if ([_st isEqualToString:@"plain"]) return @"txt";
|
|
|
|
if ([_st isEqualToString:@"xml"]) return @"xml";
|
|
|
|
if ([_st isEqualToString:@"calendar"]) return @"ics";
|
|
|
|
if ([_st isEqualToString:@"x-vcard"]) return @"vcf";
|
|
|
|
}
|
|
|
|
else if ([_mt isEqualToString:@"message"]) {
|
2017-12-23 15:21:28 +01:00
|
|
|
if ([_st isEqualToString:@"rfc822"]) return @"eml";
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
else if ([_mt isEqualToString:@"application"]) {
|
|
|
|
if ([_st isEqualToString:@"pdf"]) return @"pdf";
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (NSString *) preferredPathExtension
|
|
|
|
{
|
|
|
|
return [self pathExtensionForType: [bodyInfo valueForKey:@"type"]
|
|
|
|
subtype: [bodyInfo valueForKey:@"subtype"]];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (NSString *) filename
|
|
|
|
{
|
2013-07-16 21:18:04 +02:00
|
|
|
return [bodyInfo filename];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (NSString *) filenameForDisplay
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
if ((s = [self filename]) != nil)
|
|
|
|
return s;
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
s = [partPath componentsJoinedByString:@"-"];
|
2006-06-15 21:34:10 +02:00
|
|
|
return ([s length] > 0)
|
2009-04-01 15:49:24 +02:00
|
|
|
? (id)[@"untitled-" stringByAppendingString:s]
|
|
|
|
: (id)@"untitled";
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-08-01 20:53:25 +02:00
|
|
|
- (NSFormatter *) sizeFormatter
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return [UIxMailSizeFormatter sharedMailSizeFormatter];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* URL generation */
|
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
- (NSString *) _filenameForAttachment: (SOGoMailBodyPart *) bodyPart
|
2007-08-01 20:53:25 +02:00
|
|
|
{
|
2007-12-17 21:24:07 +01:00
|
|
|
NSMutableString *filename;
|
|
|
|
NSString *extension;
|
|
|
|
|
2013-08-07 14:49:59 +02:00
|
|
|
filename = [NSMutableString stringWithString: [self filename]];
|
2014-02-07 20:00:36 +01:00
|
|
|
if ([filename length])
|
|
|
|
// We replace any slash by a dash since Apache won't allow encoded slashes by default.
|
|
|
|
// See http://httpd.apache.org/docs/2.2/mod/core.html#allowencodedslashes
|
2016-06-22 22:35:05 +02:00
|
|
|
filename = [NSMutableString stringWithString: [filename stringByReplacingString: @"/" withString: @"-"]];
|
2014-02-07 20:00:36 +01:00
|
|
|
else
|
2007-12-17 21:24:07 +01:00
|
|
|
[filename appendFormat: @"%@-%@",
|
|
|
|
[self labelForKey: @"Untitled"],
|
|
|
|
[bodyPart nameInContainer]];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
if (![[filename pathExtension] length])
|
2007-08-20 22:44:11 +02:00
|
|
|
{
|
2007-12-17 21:24:07 +01:00
|
|
|
extension = [self preferredPathExtension];
|
|
|
|
if (extension)
|
|
|
|
[filename appendFormat: @".%@", extension];
|
2007-08-01 20:53:25 +02:00
|
|
|
}
|
2007-12-17 21:24:07 +01:00
|
|
|
|
|
|
|
return [filename stringByEscapingURL];
|
|
|
|
}
|
|
|
|
|
2015-05-29 16:49:40 +02:00
|
|
|
- (NSString *) _pathForAttachmentOrDownload: (BOOL) forDownload
|
2007-12-17 21:24:07 +01:00
|
|
|
{
|
|
|
|
SOGoMailBodyPart *bodyPart;
|
2017-12-23 15:21:28 +01:00
|
|
|
NSString *s, *attachment;
|
|
|
|
NSMutableString *url;
|
2007-12-17 21:24:07 +01:00
|
|
|
|
|
|
|
bodyPart = [self clientPart];
|
2017-12-23 15:21:28 +01:00
|
|
|
s = [[self clientObject] baseURLInContext: [self context]];
|
|
|
|
|
2007-12-17 21:24:07 +01:00
|
|
|
url = [NSMutableString stringWithString: s];
|
|
|
|
if (![url hasSuffix: @"/"])
|
|
|
|
[url appendString: @"/"];
|
|
|
|
|
2017-12-23 15:21:28 +01:00
|
|
|
[url appendString: [[self partPath] componentsJoinedByString: @"/"]];
|
|
|
|
[url appendString: @"/"];
|
|
|
|
|
2008-01-24 16:03:12 +01:00
|
|
|
if ([bodyPart isKindOfClass: [SOGoMailBodyPart class]])
|
|
|
|
attachment = [self _filenameForAttachment: bodyPart];
|
2017-12-23 15:21:28 +01:00
|
|
|
else if ([[self decodedFlatContent] isKindOfClass: [NGMimeBodyPart class]])
|
|
|
|
attachment = [[[self decodedFlatContent] headerForKey: @"content-disposition"] filename];
|
2008-01-24 16:03:12 +01:00
|
|
|
else
|
|
|
|
attachment = @"0";
|
2015-05-29 16:49:40 +02:00
|
|
|
|
|
|
|
if (forDownload)
|
|
|
|
[url appendString: @"asAttachment/"];
|
2017-12-23 15:21:28 +01:00
|
|
|
|
2008-01-24 16:03:12 +01:00
|
|
|
[url appendString: attachment];
|
2007-08-01 20:53:25 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
2017-12-23 15:21:28 +01:00
|
|
|
//
|
|
|
|
// Used by UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox
|
|
|
|
//
|
2015-06-04 01:42:30 +02:00
|
|
|
- (NSString *) pathToAttachmentFromMessage
|
|
|
|
{
|
2017-04-06 20:31:41 +02:00
|
|
|
NSMutableArray *parts;
|
2015-06-04 01:42:30 +02:00
|
|
|
SOGoMailBodyPart *bodyPart;
|
|
|
|
|
|
|
|
bodyPart = [self clientPart];
|
2016-02-03 21:53:12 +01:00
|
|
|
if ([bodyPart isKindOfClass: [SOGoMailBodyPart class]])
|
2017-04-06 20:31:41 +02:00
|
|
|
{
|
|
|
|
parts = [NSMutableArray arrayWithObject: [self _filenameForAttachment: bodyPart]];
|
|
|
|
do
|
|
|
|
{
|
|
|
|
[parts insertObject: [bodyPart nameInContainer] atIndex: 0];
|
|
|
|
bodyPart = [bodyPart container];
|
|
|
|
}
|
|
|
|
while ([bodyPart isKindOfClass: [SOGoMailBodyPart class]]);
|
|
|
|
return [parts componentsJoinedByString: @"/"];
|
|
|
|
}
|
2016-02-03 21:53:12 +01:00
|
|
|
|
|
|
|
return @"0";
|
2015-06-04 01:42:30 +02:00
|
|
|
}
|
|
|
|
|
2015-05-29 16:49:40 +02:00
|
|
|
- (NSString *) pathToAttachment
|
|
|
|
{
|
|
|
|
return [self _pathForAttachmentOrDownload: NO];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) pathForDownload
|
|
|
|
{
|
|
|
|
return [self _pathForAttachmentOrDownload: YES];
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
@end /* UIxMailPartViewer */
|