2007-02-15 22:04:31 +01:00
|
|
|
/* UIxMailPartHTMLViewer.m - this file is part of SOGo
|
|
|
|
*
|
2019-12-04 21:45:26 +01:00
|
|
|
* Copyright (C) 2007-2019 Inverse inc.
|
2007-02-15 22:04:31 +01:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2011-05-16 17:08:23 +02:00
|
|
|
#import <Foundation/NSData.h>
|
2007-04-04 16:12:18 +02:00
|
|
|
#import <Foundation/NSDictionary.h>
|
2007-11-20 23:18:19 +01:00
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
#import <SaxObjC/SaxLexicalHandler.h>
|
|
|
|
#import <SaxObjC/SaxXMLReaderFactory.h>
|
|
|
|
#import <NGExtensions/NSString+misc.h>
|
2009-12-15 01:05:06 +01:00
|
|
|
#import <NGExtensions/NSString+Encoding.h>
|
2017-12-23 15:02:32 +01:00
|
|
|
#import <NGMime/NGMimeBodyPart.h>
|
|
|
|
#import <NGMime/NGMimeType.h>
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-11-20 23:18:19 +01:00
|
|
|
#include <libxml/encoding.h>
|
|
|
|
|
2016-12-06 20:05:27 +01:00
|
|
|
#import <SoObjects/SOGo/NSString+Utilities.h>
|
2017-12-23 15:02:32 +01:00
|
|
|
#import <SoObjects/Mailer/NSData+Mail.h>
|
|
|
|
#import <SoObjects/Mailer/SOGoMailAccounts.h>
|
2007-12-13 00:59:36 +01:00
|
|
|
#import <SoObjects/Mailer/SOGoMailObject.h>
|
2008-08-22 20:43:08 +02:00
|
|
|
#import <SoObjects/Mailer/SOGoMailBodyPart.h>
|
2007-12-13 00:59:36 +01:00
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
#import "UIxMailPartHTMLViewer.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-08-20 19:46:15 +02:00
|
|
|
#if 0
|
2007-02-15 22:04:31 +01:00
|
|
|
#define showWhoWeAre() NSLog(@"invoked '%@'", NSStringFromSelector(_cmd))
|
2007-08-20 19:46:15 +02:00
|
|
|
#else
|
|
|
|
#define showWhoWeAre()
|
|
|
|
#endif
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2009-06-10 22:00:45 +02:00
|
|
|
/* Tags that are forbidden within the body of the html content */
|
|
|
|
static NSArray *BannedTags = nil;
|
|
|
|
|
2012-02-08 00:48:57 +01:00
|
|
|
/* Tags that can't have any contents (no end tag) */
|
2012-08-24 21:58:04 +02:00
|
|
|
static NSArray *VoidTags= nil;
|
2012-02-08 00:08:46 +01:00
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
static xmlCharEncoding
|
|
|
|
_xmlCharsetForCharset (NSString *charset)
|
|
|
|
{
|
|
|
|
struct { NSString *name; xmlCharEncoding encoding; } xmlEncodings[] = {
|
|
|
|
{ @"us-ascii", XML_CHAR_ENCODING_ASCII},
|
|
|
|
{ @"utf-8", XML_CHAR_ENCODING_UTF8},
|
2012-12-03 20:56:11 +01:00
|
|
|
{ @"utf8", XML_CHAR_ENCODING_UTF8}, // broken mailers
|
2008-08-22 20:43:08 +02:00
|
|
|
{ @"utf-16le", XML_CHAR_ENCODING_UTF16LE},
|
|
|
|
{ @"utf-16be", XML_CHAR_ENCODING_UTF16BE},
|
|
|
|
{ @"ucs-4le", XML_CHAR_ENCODING_UCS4LE},
|
|
|
|
{ @"ucs-4be", XML_CHAR_ENCODING_UCS4BE},
|
|
|
|
{ @"ebcdic", XML_CHAR_ENCODING_EBCDIC},
|
|
|
|
// { @"iso-10646" , XML_CHAR_ENCODING_UCS4_2143},
|
|
|
|
// { , XML_CHAR_ENCODING_UCS4_3412},
|
|
|
|
// { @"ucs-2", XML_CHAR_ENCODING_UCS2},
|
|
|
|
{ @"iso8859_1", XML_CHAR_ENCODING_8859_1},
|
|
|
|
{ @"iso-8859-1", XML_CHAR_ENCODING_8859_1},
|
|
|
|
{ @"iso-8859-2", XML_CHAR_ENCODING_8859_2},
|
|
|
|
{ @"iso-8859-3", XML_CHAR_ENCODING_8859_3},
|
|
|
|
{ @"iso-8859-4", XML_CHAR_ENCODING_8859_4},
|
|
|
|
{ @"iso-8859-5", XML_CHAR_ENCODING_8859_5},
|
|
|
|
{ @"iso-8859-6", XML_CHAR_ENCODING_8859_6},
|
|
|
|
{ @"iso-8859-7", XML_CHAR_ENCODING_8859_7},
|
|
|
|
{ @"iso-8859-8", XML_CHAR_ENCODING_8859_8},
|
|
|
|
{ @"iso-8859-9", XML_CHAR_ENCODING_8859_9},
|
2009-12-15 01:05:06 +01:00
|
|
|
{ @"iso-8859-13", XML_CHAR_ENCODING_ERROR},
|
2008-08-22 20:43:08 +02:00
|
|
|
{ @"iso-2022-jp", XML_CHAR_ENCODING_2022_JP},
|
|
|
|
// { @"iso-2022-jp", XML_CHAR_ENCODING_SHIFT_JIS},
|
2009-05-21 17:12:10 +02:00
|
|
|
{ @"koi8-r", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger koi8-r -> utf8 conversion
|
2009-10-13 14:08:40 +02:00
|
|
|
{ @"windows-1250", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1250 -> utf8 conversion
|
2009-05-21 17:12:10 +02:00
|
|
|
{ @"windows-1251", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1251 -> utf8 conversion
|
2017-04-20 20:35:44 +02:00
|
|
|
{ @"windows-1255", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1255 -> utf8 conversion
|
2019-08-01 17:15:17 +02:00
|
|
|
{ @"windows-1256", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1255 -> utf8 conversion
|
2009-12-15 01:05:06 +01:00
|
|
|
{ @"windows-1257", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1257 -> utf8 conversion
|
2011-07-29 15:26:14 +02:00
|
|
|
{ @"gb2312", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
2014-06-10 14:58:44 +02:00
|
|
|
{ @"gbk", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
|
|
|
{ @"gb18030", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
|
|
|
{ @"big5", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
2008-08-22 20:43:08 +02:00
|
|
|
{ @"euc-jp", XML_CHAR_ENCODING_EUC_JP}};
|
|
|
|
unsigned count;
|
|
|
|
xmlCharEncoding encoding;
|
|
|
|
|
|
|
|
encoding = XML_CHAR_ENCODING_NONE;
|
|
|
|
count = 0;
|
|
|
|
|
|
|
|
while (encoding == XML_CHAR_ENCODING_NONE
|
|
|
|
&& count < (sizeof (xmlEncodings) / sizeof (xmlEncodings[0])))
|
|
|
|
if ([charset isEqualToString: xmlEncodings[count].name])
|
|
|
|
encoding = xmlEncodings[count].encoding;
|
|
|
|
else
|
|
|
|
count++;
|
|
|
|
|
|
|
|
if (encoding == XML_CHAR_ENCODING_NONE)
|
|
|
|
encoding = XML_CHAR_ENCODING_8859_1;
|
|
|
|
|
|
|
|
return encoding;
|
|
|
|
}
|
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
@interface _UIxHTMLMailContentHandler : NSObject <SaxContentHandler, SaxLexicalHandler>
|
|
|
|
{
|
|
|
|
NSMutableString *result;
|
|
|
|
NSMutableString *css;
|
|
|
|
NSDictionary *attachmentIds;
|
2010-04-20 19:45:17 +02:00
|
|
|
int ignoredContent;
|
2009-06-10 22:00:45 +02:00
|
|
|
NSString *ignoreTag;
|
2007-02-15 22:04:31 +01:00
|
|
|
BOOL inBody;
|
|
|
|
BOOL inStyle;
|
|
|
|
BOOL inCSSDeclaration;
|
2007-02-26 17:37:59 +01:00
|
|
|
BOOL hasEmbeddedCSS;
|
2007-11-20 23:18:19 +01:00
|
|
|
xmlCharEncoding contentEncoding;
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
- (NSString *) result;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
@implementation _UIxHTMLMailContentHandler
|
|
|
|
|
2009-06-10 22:00:45 +02:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (!BannedTags)
|
2010-04-20 19:45:17 +02:00
|
|
|
BannedTags = [[NSArray alloc] initWithObjects: @"script", @"frameset",
|
|
|
|
@"frame", @"iframe", @"applet", @"link",
|
|
|
|
@"base", @"meta", @"title", nil];
|
2012-02-08 00:08:46 +01:00
|
|
|
if (!VoidTags)
|
2012-02-10 11:42:33 +01:00
|
|
|
{
|
|
|
|
/* see http://www.w3.org/TR/html4/index/elements.html */
|
|
|
|
VoidTags = [[NSArray alloc] initWithObjects: @"area", @"base",
|
|
|
|
@"basefont", @"br", @"col", @"frame", @"hr",
|
|
|
|
@"img", @"input", @"isindex", @"link",
|
|
|
|
@"meta", @"param", @"", nil];
|
|
|
|
}
|
2009-06-10 22:00:45 +02:00
|
|
|
}
|
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
css = nil;
|
|
|
|
result = nil;
|
2009-06-10 22:00:45 +02:00
|
|
|
ignoreTag = nil;
|
2007-02-15 22:04:31 +01:00
|
|
|
attachmentIds = nil;
|
2007-11-20 23:18:19 +01:00
|
|
|
contentEncoding = XML_CHAR_ENCODING_UTF8;
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
2007-09-07 17:45:46 +02:00
|
|
|
[result release];
|
|
|
|
[css release];
|
2009-10-10 20:37:10 +02:00
|
|
|
[ignoreTag release];
|
2007-02-15 22:04:31 +01:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2007-11-20 23:18:19 +01:00
|
|
|
- (void) setContentEncoding: (xmlCharEncoding) newContentEncoding
|
|
|
|
{
|
|
|
|
contentEncoding = newContentEncoding;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (xmlCharEncoding) contentEncoding
|
|
|
|
{
|
|
|
|
return contentEncoding;
|
|
|
|
}
|
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
- (void) setAttachmentIds: (NSDictionary *) newAttachmentIds
|
|
|
|
{
|
|
|
|
attachmentIds = newAttachmentIds;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) css
|
|
|
|
{
|
2007-09-07 17:45:46 +02:00
|
|
|
return css;
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) result
|
|
|
|
{
|
2007-09-07 17:45:46 +02:00
|
|
|
return result;
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SaxContentHandler */
|
|
|
|
- (void) startDocument
|
|
|
|
{
|
2007-08-20 19:46:15 +02:00
|
|
|
showWhoWeAre();
|
|
|
|
|
2007-09-07 17:45:46 +02:00
|
|
|
[css release];
|
|
|
|
[result release];
|
2007-02-15 22:04:31 +01:00
|
|
|
|
|
|
|
result = [NSMutableString new];
|
|
|
|
css = [NSMutableString new];
|
2009-06-10 22:00:45 +02:00
|
|
|
|
2010-04-20 19:45:17 +02:00
|
|
|
ignoredContent = 0;
|
2009-06-10 22:00:45 +02:00
|
|
|
[ignoreTag release];
|
|
|
|
ignoreTag = nil;
|
2007-09-07 17:45:46 +02:00
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
inBody = NO;
|
|
|
|
inStyle = NO;
|
|
|
|
inCSSDeclaration = NO;
|
2007-02-26 17:37:59 +01:00
|
|
|
hasEmbeddedCSS = NO;
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) endDocument
|
|
|
|
{
|
2007-08-20 19:46:15 +02:00
|
|
|
showWhoWeAre();
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) startPrefixMapping: (NSString *)_prefix
|
|
|
|
uri: (NSString *)_uri
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) endPrefixMapping: (NSString *)_prefix
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
2016-05-27 19:42:44 +02:00
|
|
|
/**
|
|
|
|
* About CSS At-Rules (https://css-tricks.com/the-at-rules-of-css/)
|
|
|
|
*
|
|
|
|
* At-Rules follow two possible synthaxes:
|
|
|
|
*
|
|
|
|
* @[KEYWORD] (RULE);
|
|
|
|
* Examples:
|
|
|
|
* @charset "UTF-8";
|
|
|
|
* @import 'global.css';
|
|
|
|
* @namespace svg url(http://www.w3.org/2000/svg);
|
|
|
|
*
|
|
|
|
* @[KEYWORD] { (Nested Statements) }
|
|
|
|
* Examples:
|
|
|
|
* @font-face {
|
|
|
|
* font-family: 'MyWebFont';
|
|
|
|
* src: url('myfont.woff2') format('woff2'),
|
|
|
|
* url('myfont.woff') format('woff');
|
|
|
|
* }
|
|
|
|
* @media only screen
|
|
|
|
* and (min-device-width: 320px)
|
|
|
|
* and (max-device-width: 480px)
|
|
|
|
* and (-webkit-min-device-pixel-ratio: 2) {
|
|
|
|
* .module { width: 100%; }
|
|
|
|
* }
|
|
|
|
*/
|
2007-02-15 22:04:31 +01:00
|
|
|
- (void) _appendStyle: (unichar *) _chars
|
2014-06-16 21:30:26 +02:00
|
|
|
length: (NSUInteger) _len
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2018-06-07 18:33:38 +02:00
|
|
|
NSMutableString *sanitizedStyle, *declaration, *rule;
|
|
|
|
NSUInteger count, length, max;
|
|
|
|
unichar *sanitizedChars, *start, *currentChar;
|
|
|
|
BOOL inComment;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sanitize style
|
|
|
|
* - remove control characters
|
|
|
|
* - remove HTML comment delimiters
|
|
|
|
* - remove CSS comments
|
|
|
|
*/
|
|
|
|
sanitizedStyle = [NSMutableString string];
|
|
|
|
inComment = NO;
|
2007-02-15 22:04:31 +01:00
|
|
|
start = _chars;
|
|
|
|
for (count = 0; count < _len; count++)
|
|
|
|
{
|
|
|
|
currentChar = _chars + count;
|
2018-06-07 18:33:38 +02:00
|
|
|
if (*currentChar < 32)
|
|
|
|
{
|
|
|
|
// Ignore control characters
|
|
|
|
if (!inComment && currentChar > start)
|
|
|
|
[sanitizedStyle appendString: [NSString stringWithCharacters: start
|
|
|
|
length: (currentChar - start)]];
|
|
|
|
start = currentChar + 1;
|
|
|
|
}
|
|
|
|
else
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2018-06-07 18:33:38 +02:00
|
|
|
if ((currentChar < _chars + _len - 3) &&
|
|
|
|
*currentChar == '<' &&
|
|
|
|
*(currentChar+1) == '!' &&
|
|
|
|
*(currentChar+2) == '-' &&
|
|
|
|
*(currentChar+3) == '-')
|
2016-05-27 19:42:44 +02:00
|
|
|
{
|
2018-06-07 18:33:38 +02:00
|
|
|
// Ignore starting HTML comment
|
|
|
|
if (!inComment && currentChar > start)
|
|
|
|
[sanitizedStyle appendString: [NSString stringWithCharacters: start
|
|
|
|
length: (currentChar - start)]];
|
|
|
|
start = currentChar + 4;
|
|
|
|
}
|
|
|
|
else if ((currentChar < _chars + _len - 2) &&
|
|
|
|
*currentChar == '-' &&
|
|
|
|
*(currentChar+1) == '-' &&
|
|
|
|
*(currentChar+2) == '>')
|
|
|
|
{
|
|
|
|
// Ignore ending HTML comment
|
|
|
|
if (!inComment && currentChar > start)
|
|
|
|
[sanitizedStyle appendString: [NSString stringWithCharacters: start
|
|
|
|
length: (currentChar - start)]];
|
|
|
|
start = currentChar + 3;
|
2016-05-27 19:42:44 +02:00
|
|
|
}
|
2018-06-07 18:33:38 +02:00
|
|
|
if (currentChar < _chars + _len - 1)
|
|
|
|
{
|
|
|
|
// Ignore CSS comments
|
|
|
|
if (*currentChar == '/' && *(currentChar+1) == '*')
|
|
|
|
{
|
|
|
|
inComment = YES;
|
|
|
|
if (currentChar > start)
|
|
|
|
[sanitizedStyle appendString: [NSString stringWithCharacters: start
|
|
|
|
length: (currentChar - start)]];
|
|
|
|
}
|
|
|
|
else if (*currentChar == '*' && *(currentChar+1) == '/')
|
|
|
|
{
|
|
|
|
inComment = NO;
|
|
|
|
start = currentChar + 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!inComment && currentChar > start)
|
|
|
|
[sanitizedStyle appendString: [NSString stringWithCharacters: start
|
|
|
|
length: (currentChar - start)]];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse sanitized style
|
|
|
|
* - remove at-rule definitions
|
|
|
|
* - add custom class to selectors
|
|
|
|
* - add !important suffix to all rules
|
|
|
|
*/
|
|
|
|
rule = [NSMutableString string];
|
|
|
|
max = [sanitizedStyle length];
|
|
|
|
sanitizedChars = NSZoneMalloc (NULL, max * sizeof (unichar));
|
|
|
|
[sanitizedStyle getCharacters: sanitizedChars];
|
|
|
|
start = sanitizedChars;
|
|
|
|
currentChar = start;
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
currentChar = sanitizedChars + count;
|
|
|
|
if (inCSSDeclaration)
|
|
|
|
{
|
|
|
|
if (*currentChar == '}')
|
2007-02-26 17:37:59 +01:00
|
|
|
{
|
|
|
|
inCSSDeclaration = NO;
|
2016-05-27 19:42:44 +02:00
|
|
|
if (hasEmbeddedCSS)
|
|
|
|
{
|
|
|
|
// End of at-rule definition; remove it from the stylesheet
|
|
|
|
hasEmbeddedCSS = NO;
|
|
|
|
start = currentChar + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-01-17 19:58:42 +01:00
|
|
|
// Prefix CSS rule including ending curly bracket
|
|
|
|
length = (currentChar - start) + 1;
|
2016-05-27 19:42:44 +02:00
|
|
|
[declaration appendString: [NSString stringWithCharacters: start length: length]];
|
2018-06-07 18:33:38 +02:00
|
|
|
[css appendString: declaration];
|
|
|
|
start = currentChar + 1;
|
2016-05-27 19:42:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (*currentChar == ';')
|
|
|
|
{
|
|
|
|
// Add !important
|
2018-06-07 18:33:38 +02:00
|
|
|
if ((currentChar < sanitizedChars + 10) ||
|
2016-05-27 19:42:44 +02:00
|
|
|
!((*(currentChar-1) == 't' || *(currentChar-1) == 'T') &&
|
|
|
|
(*(currentChar-2) == 'n' || *(currentChar-2) == 'N') &&
|
|
|
|
(*(currentChar-3) == 'a' || *(currentChar-3) == 'A') &&
|
|
|
|
(*(currentChar-4) == 't' || *(currentChar-4) == 'T') &&
|
|
|
|
(*(currentChar-5) == 'r' || *(currentChar-5) == 'R') &&
|
|
|
|
(*(currentChar-6) == 'o' || *(currentChar-6) == 'O') &&
|
|
|
|
(*(currentChar-7) == 'p' || *(currentChar-7) == 'P') &&
|
|
|
|
(*(currentChar-8) == 'm' || *(currentChar-8) == 'M') &&
|
|
|
|
(*(currentChar-9) == 'i' || *(currentChar-9) == 'I') &&
|
|
|
|
*(currentChar-10) == '!'))
|
|
|
|
{
|
|
|
|
length = (currentChar - start);
|
|
|
|
[declaration appendFormat: @"%@ !important;",
|
|
|
|
[NSString stringWithCharacters: start length: length]];
|
|
|
|
start = currentChar + 1;
|
|
|
|
}
|
2007-02-26 17:37:59 +01:00
|
|
|
}
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-06-07 18:33:38 +02:00
|
|
|
if (*currentChar == '{')
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2018-06-07 18:33:38 +02:00
|
|
|
// Start of rule declaration
|
|
|
|
inCSSDeclaration = YES;
|
|
|
|
if (!hasEmbeddedCSS)
|
|
|
|
{
|
|
|
|
length = (currentChar - start);
|
|
|
|
[rule appendFormat: @".SOGoHTMLMail-CSS-Delimiter %@ {",
|
|
|
|
[NSString stringWithCharacters: start length: length]];
|
|
|
|
[css appendString: rule];
|
|
|
|
}
|
|
|
|
rule = [NSMutableString string];
|
|
|
|
declaration = [NSMutableString string];
|
2009-06-10 22:00:45 +02:00
|
|
|
start = currentChar + 1;
|
|
|
|
}
|
2018-06-07 18:33:38 +02:00
|
|
|
if (*currentChar == '}')
|
2009-06-10 22:00:45 +02:00
|
|
|
{
|
2018-06-07 18:33:38 +02:00
|
|
|
// CSS syntax error: ending declaration character while not in a CSS declaration.
|
|
|
|
// Ignore eveything from last CSS declaration.
|
|
|
|
start = currentChar + 1;
|
|
|
|
rule = [NSMutableString string];
|
|
|
|
}
|
|
|
|
else if (hasEmbeddedCSS)
|
|
|
|
{
|
|
|
|
if (*currentChar == ';')
|
2009-06-10 22:00:45 +02:00
|
|
|
{
|
2018-06-07 18:33:38 +02:00
|
|
|
// End of at-rule definition; remove it from the stylesheet
|
|
|
|
hasEmbeddedCSS = NO;
|
2016-05-27 19:42:44 +02:00
|
|
|
start = currentChar + 1;
|
|
|
|
}
|
2018-06-07 18:33:38 +02:00
|
|
|
}
|
|
|
|
else if (*currentChar == ',')
|
|
|
|
{
|
|
|
|
// Prefix CSS selector
|
|
|
|
length = (currentChar - start);
|
|
|
|
[rule appendFormat: @" .SOGoHTMLMail-CSS-Delimiter %@,",
|
|
|
|
[NSString stringWithCharacters: start length: length]];
|
|
|
|
start = currentChar + 1;
|
|
|
|
}
|
|
|
|
else if (*currentChar == '@')
|
|
|
|
{
|
|
|
|
// Start of at-rule definition
|
|
|
|
hasEmbeddedCSS = YES;
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-06-10 22:00:45 +02:00
|
|
|
if (currentChar > start)
|
|
|
|
[css appendString: [NSString stringWithCharacters: start
|
|
|
|
length: (currentChar - start)]];
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) startElement: (NSString *) _localName
|
|
|
|
namespace: (NSString *) _ns
|
|
|
|
rawName: (NSString *) _rawName
|
|
|
|
attributes: (id <SaxAttributes>) _attributes
|
|
|
|
{
|
|
|
|
unsigned int count, max;
|
2009-09-15 01:05:45 +02:00
|
|
|
NSString *name, *value, *cid, *lowerName;
|
2007-02-15 22:04:31 +01:00
|
|
|
NSMutableString *resultPart;
|
|
|
|
BOOL skipAttribute;
|
|
|
|
|
2007-08-20 19:46:15 +02:00
|
|
|
showWhoWeAre();
|
2009-06-10 22:00:45 +02:00
|
|
|
|
2009-09-15 01:05:45 +02:00
|
|
|
lowerName = [_localName lowercaseString];
|
2010-04-20 19:45:17 +02:00
|
|
|
if (inStyle || ignoredContent)
|
2007-02-15 22:04:31 +01:00
|
|
|
;
|
2009-09-15 01:05:45 +02:00
|
|
|
else if ([lowerName isEqualToString: @"base"])
|
2009-02-06 02:15:24 +01:00
|
|
|
;
|
2009-09-15 01:05:45 +02:00
|
|
|
else if ([lowerName isEqualToString: @"meta"])
|
2009-05-21 17:12:10 +02:00
|
|
|
;
|
2009-09-15 01:05:45 +02:00
|
|
|
else if ([lowerName isEqualToString: @"body"])
|
2007-02-15 22:04:31 +01:00
|
|
|
inBody = YES;
|
2009-09-15 01:05:45 +02:00
|
|
|
else if ([lowerName isEqualToString: @"style"])
|
2007-02-15 22:04:31 +01:00
|
|
|
inStyle = YES;
|
|
|
|
else if (inBody)
|
|
|
|
{
|
2009-09-15 01:05:45 +02:00
|
|
|
if ([BannedTags containsObject: lowerName])
|
2009-06-10 22:00:45 +02:00
|
|
|
{
|
2010-04-20 19:45:17 +02:00
|
|
|
if (!ignoredContent)
|
|
|
|
ignoreTag = [lowerName copy];
|
|
|
|
ignoredContent++;
|
2009-06-10 22:00:45 +02:00
|
|
|
}
|
|
|
|
else
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2009-10-13 16:10:17 +02:00
|
|
|
resultPart = [NSMutableString string];
|
2009-06-10 22:00:45 +02:00
|
|
|
[resultPart appendFormat: @"<%@", _rawName];
|
|
|
|
|
2012-02-08 00:08:46 +01:00
|
|
|
if ([VoidTags containsObject: lowerName])
|
|
|
|
{
|
|
|
|
if (!ignoredContent)
|
|
|
|
ignoreTag = [lowerName copy];
|
|
|
|
ignoredContent++;
|
|
|
|
}
|
2012-02-08 00:47:25 +01:00
|
|
|
max = [_attributes count];
|
|
|
|
for (count = 0; count < max; count++)
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2012-02-08 00:47:25 +01:00
|
|
|
skipAttribute = NO;
|
|
|
|
name = [[_attributes nameAtIndex: count] lowercaseString];
|
|
|
|
if ([name hasPrefix: @"ON"])
|
|
|
|
skipAttribute = YES;
|
|
|
|
else if ([name isEqualToString: @"src"])
|
2009-06-10 22:00:45 +02:00
|
|
|
{
|
2012-02-08 00:47:25 +01:00
|
|
|
value = [_attributes valueAtIndex: count];
|
|
|
|
if ([value hasPrefix: @"cid:"])
|
2012-02-08 00:08:46 +01:00
|
|
|
{
|
2017-01-13 18:23:38 +01:00
|
|
|
cid = [NSString stringWithFormat: @"<%@>",
|
|
|
|
[value substringFromIndex: 4]];
|
2012-02-08 00:47:25 +01:00
|
|
|
value = [attachmentIds objectForKey: cid];
|
|
|
|
skipAttribute = (value == nil);
|
2012-02-08 00:08:46 +01:00
|
|
|
}
|
2012-02-08 00:47:25 +01:00
|
|
|
else if ([lowerName isEqualToString: @"img"])
|
2009-06-10 22:00:45 +02:00
|
|
|
{
|
2012-02-08 00:47:25 +01:00
|
|
|
/* [resultPart appendString:
|
|
|
|
@"src=\"/SOGo.woa/WebServerResources/empty.gif\""]; */
|
|
|
|
name = @"unsafe-src";
|
2009-06-10 22:00:45 +02:00
|
|
|
}
|
|
|
|
else
|
2012-02-08 00:47:25 +01:00
|
|
|
skipAttribute = YES;
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
2015-10-31 07:10:03 +01:00
|
|
|
else if (([name isEqualToString: @"background"] ||
|
2014-01-09 15:13:11 +01:00
|
|
|
([name isEqualToString: @"data"]
|
2015-10-31 07:10:03 +01:00
|
|
|
|| [name isEqualToString: @"classid"]))
|
2012-02-08 00:47:25 +01:00
|
|
|
&& [lowerName isEqualToString: @"object"])
|
|
|
|
{
|
|
|
|
value = [_attributes valueAtIndex: count];
|
|
|
|
name = [NSString stringWithFormat: @"unsafe-%@", name];
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"href"]
|
|
|
|
|| [name isEqualToString: @"action"])
|
|
|
|
{
|
|
|
|
value = [_attributes valueAtIndex: count];
|
|
|
|
skipAttribute = ([value rangeOfString: @"://"].location
|
|
|
|
== NSNotFound
|
2012-07-20 20:34:59 +02:00
|
|
|
&& ![value hasPrefix: @"mailto:"]
|
2012-02-08 00:47:25 +01:00
|
|
|
&& ![value hasPrefix: @"#"]);
|
2019-06-10 19:19:46 +02:00
|
|
|
if (!skipAttribute)
|
|
|
|
[resultPart appendString: @" rel=\"noopener\""];
|
2012-02-08 00:47:25 +01:00
|
|
|
}
|
2014-04-23 15:22:20 +02:00
|
|
|
// Avoid: <div style="background:url('http://www.sogo.nu/fileadmin/sogo/logos/sogo.bts.png' ); width: 200px; height: 200px;" title="ssss">
|
|
|
|
else if ([name isEqualToString: @"style"])
|
|
|
|
{
|
|
|
|
value = [_attributes valueAtIndex: count];
|
|
|
|
if ([value rangeOfString: @"url" options: NSCaseInsensitiveSearch].location != NSNotFound)
|
|
|
|
name = [NSString stringWithFormat: @"unsafe-%@", name];
|
|
|
|
}
|
2013-10-23 22:37:03 +02:00
|
|
|
else if (
|
|
|
|
// Mouse Events
|
|
|
|
[name isEqualToString: @"onclick"] ||
|
|
|
|
[name isEqualToString: @"ondblclick"] ||
|
|
|
|
[name isEqualToString: @"onmousedown"] ||
|
|
|
|
[name isEqualToString: @"onmousemove"] ||
|
|
|
|
[name isEqualToString: @"onmouseout"] ||
|
|
|
|
[name isEqualToString: @"onmouseup"] ||
|
|
|
|
[name isEqualToString: @"onmouseover"] ||
|
2020-03-06 18:14:46 +01:00
|
|
|
[name isEqualToString: @"onpointerrawupdate"] ||
|
2013-10-23 22:37:03 +02:00
|
|
|
|
|
|
|
// Keyboard Events
|
|
|
|
[name isEqualToString: @"onkeydown"] ||
|
|
|
|
[name isEqualToString: @"onkeypress"] ||
|
|
|
|
[name isEqualToString: @"onkeyup"] ||
|
|
|
|
|
|
|
|
// Frame/Object Events
|
|
|
|
[name isEqualToString: @"onabort"] ||
|
|
|
|
[name isEqualToString: @"onerror"] ||
|
|
|
|
[name isEqualToString: @"onload"] ||
|
|
|
|
[name isEqualToString: @"onresize"] ||
|
|
|
|
[name isEqualToString: @"onscroll"] ||
|
|
|
|
[name isEqualToString: @"onunload"] ||
|
|
|
|
|
|
|
|
// Form Events
|
|
|
|
[name isEqualToString: @"onblur"] ||
|
|
|
|
[name isEqualToString: @"onchange"] ||
|
|
|
|
[name isEqualToString: @"onfocus"] ||
|
|
|
|
[name isEqualToString: @"onreset"] ||
|
|
|
|
[name isEqualToString: @"onselect"] ||
|
|
|
|
[name isEqualToString: @"onsubmit"])
|
2013-10-23 22:04:37 +02:00
|
|
|
{
|
|
|
|
skipAttribute = YES;
|
|
|
|
}
|
2012-02-08 00:47:25 +01:00
|
|
|
else
|
|
|
|
value = [_attributes valueAtIndex: count];
|
2014-04-23 15:22:20 +02:00
|
|
|
|
2012-02-08 00:47:25 +01:00
|
|
|
if (!skipAttribute)
|
|
|
|
[resultPart appendFormat: @" %@=\"%@\"",
|
2017-08-22 16:02:28 +02:00
|
|
|
name, [value stringByReplacingString: @"\""
|
|
|
|
withString: @""]];
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
2014-04-23 15:22:20 +02:00
|
|
|
|
2012-02-08 00:47:25 +01:00
|
|
|
if ([VoidTags containsObject: lowerName])
|
|
|
|
[resultPart appendString: @"/"];
|
2009-06-10 22:00:45 +02:00
|
|
|
[resultPart appendString: @">"];
|
|
|
|
[result appendString: resultPart];
|
|
|
|
}
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-07 17:45:46 +02:00
|
|
|
- (void) _finishCSS
|
|
|
|
{
|
2009-06-10 22:00:45 +02:00
|
|
|
[css replaceString: @"<!--" withString: @""];
|
|
|
|
[css replaceString: @"-->" withString: @""];
|
2007-09-07 17:45:46 +02:00
|
|
|
[css replaceString: @".SOGoHTMLMail-CSS-Delimiter body"
|
|
|
|
withString: @".SOGoHTMLMail-CSS-Delimiter"];
|
|
|
|
}
|
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
- (void) endElement: (NSString *) _localName
|
|
|
|
namespace: (NSString *) _ns
|
|
|
|
rawName: (NSString *) _rawName
|
|
|
|
{
|
2009-09-15 01:05:45 +02:00
|
|
|
NSString *lowerName;
|
2009-06-10 22:00:45 +02:00
|
|
|
|
2007-08-20 19:46:15 +02:00
|
|
|
showWhoWeAre();
|
|
|
|
|
2009-09-15 01:05:45 +02:00
|
|
|
lowerName = [_localName lowercaseString];
|
2009-06-10 22:00:45 +02:00
|
|
|
|
2010-04-20 19:45:17 +02:00
|
|
|
if (ignoredContent)
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2009-09-15 01:05:45 +02:00
|
|
|
if ([lowerName isEqualToString: ignoreTag])
|
2009-06-10 22:00:45 +02:00
|
|
|
{
|
2010-04-20 19:45:17 +02:00
|
|
|
ignoredContent--;
|
|
|
|
if (!ignoredContent)
|
|
|
|
{
|
|
|
|
[ignoreTag release];
|
|
|
|
ignoreTag = nil;
|
|
|
|
}
|
2009-06-10 22:00:45 +02:00
|
|
|
}
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
2009-06-10 22:00:45 +02:00
|
|
|
else
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2009-06-10 22:00:45 +02:00
|
|
|
if (inStyle)
|
|
|
|
{
|
2009-09-15 01:05:45 +02:00
|
|
|
if ([lowerName isEqualToString: @"style"])
|
2009-06-10 22:00:45 +02:00
|
|
|
{
|
|
|
|
inStyle = NO;
|
|
|
|
inCSSDeclaration = NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (inBody)
|
|
|
|
{
|
2009-09-15 01:05:45 +02:00
|
|
|
if ([lowerName isEqualToString: @"body"])
|
2009-06-10 22:00:45 +02:00
|
|
|
{
|
2018-06-27 21:32:54 +02:00
|
|
|
// Assume <body> never ends to properly display incorrectly constructed messages.
|
|
|
|
// See bug #4492
|
|
|
|
// inBody = NO;
|
2009-06-10 22:00:45 +02:00
|
|
|
if (css)
|
|
|
|
[self _finishCSS];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-06-30 20:01:14 +02:00
|
|
|
//NSLog (@"%@", _localName);
|
2009-06-10 22:00:45 +02:00
|
|
|
[result appendFormat: @"</%@>", _localName];
|
|
|
|
}
|
|
|
|
}
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) characters: (unichar *) _chars
|
2014-06-16 21:30:26 +02:00
|
|
|
length: (NSUInteger) _len
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2007-08-20 19:46:15 +02:00
|
|
|
showWhoWeAre();
|
2010-04-20 19:45:17 +02:00
|
|
|
if (!ignoredContent)
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
|
|
|
if (inStyle)
|
|
|
|
[self _appendStyle: _chars length: _len];
|
2007-09-07 17:45:46 +02:00
|
|
|
else if (inBody)
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2014-04-23 15:22:20 +02:00
|
|
|
NSString *s;
|
2007-12-12 17:47:01 +01:00
|
|
|
|
2014-04-23 15:22:20 +02:00
|
|
|
s = [NSString stringWithCharacters: _chars length: _len];
|
2009-06-10 22:00:45 +02:00
|
|
|
|
2007-12-12 17:47:01 +01:00
|
|
|
// HACK: This is to avoid appending the useless junk in the <html> tag
|
|
|
|
// that Outlook adds. It seems to confuse the XML parser for
|
|
|
|
// forwarded messages as we get this in the _body_ of the email
|
|
|
|
// while we really aren't in it!
|
2014-04-23 15:22:20 +02:00
|
|
|
if (![s hasPrefix: @" xmlns:v=\"urn:schemas-microsoft-com:vml\""])
|
|
|
|
[result appendString: [s stringByEscapingHTMLString]];
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) ignorableWhitespace: (unichar *) _chars
|
2014-06-16 21:30:26 +02:00
|
|
|
length: (NSUInteger) _len
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) processingInstruction: (NSString *) _pi
|
|
|
|
data: (NSString *) _data
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setDocumentLocator: (id <NSObject, SaxLocator>) _locator
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) skippedEntity: (NSString *) _entityName
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* SaxLexicalHandler */
|
|
|
|
- (void) comment: (unichar *) _chars
|
2015-10-31 07:10:03 +01:00
|
|
|
length: (int) _len
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2007-08-20 19:46:15 +02:00
|
|
|
showWhoWeAre();
|
2007-02-15 22:04:31 +01:00
|
|
|
if (inStyle)
|
|
|
|
[self _appendStyle: _chars length: _len];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) startDTD: (NSString *) _name
|
|
|
|
publicId: (NSString *) _pub
|
|
|
|
systemId: (NSString *) _sys
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) endDTD
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) startEntity: (NSString *) _name
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) endEntity: (NSString *) _name
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) startCDATA
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) endCDATA
|
|
|
|
{
|
|
|
|
showWhoWeAre();
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSDictionary (SOGoDebug)
|
|
|
|
|
|
|
|
- (void) dump;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSDictionary (SOGoDebug)
|
|
|
|
|
|
|
|
- (void) dump
|
|
|
|
{
|
|
|
|
NSEnumerator *keys;
|
|
|
|
NSString *key;
|
|
|
|
NSMutableString *dump;
|
|
|
|
|
|
|
|
dump = [NSMutableString new];
|
|
|
|
[dump appendFormat: @"\nNSDictionary dump (%@):\n", self];
|
|
|
|
keys = [[self allKeys] objectEnumerator];
|
|
|
|
key = [keys nextObject];
|
|
|
|
while (key)
|
|
|
|
{
|
|
|
|
[dump appendFormat: @"%@: %@\n", key, [self objectForKey: key]];
|
|
|
|
key = [keys nextObject];
|
|
|
|
}
|
|
|
|
[dump appendFormat: @"--- end ---\n"];
|
|
|
|
|
2015-10-31 07:10:03 +01:00
|
|
|
NSLog(@"%@", dump);
|
2007-02-15 22:04:31 +01:00
|
|
|
[dump release];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
@implementation UIxMailPartHTMLViewer
|
2007-02-15 22:04:31 +01:00
|
|
|
|
2007-09-07 17:45:46 +02:00
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
handler = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[handler release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
- (xmlCharEncoding) _xmlCharEncoding
|
2007-11-20 23:18:19 +01:00
|
|
|
{
|
2008-08-22 20:43:08 +02:00
|
|
|
NSString *charset;
|
2007-11-20 23:18:19 +01:00
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
charset = [[bodyInfo objectForKey:@"parameterList"]
|
|
|
|
objectForKey: @"charset"];
|
|
|
|
if (![charset length])
|
|
|
|
charset = @"us-ascii";
|
2007-11-20 23:18:19 +01:00
|
|
|
|
2009-05-21 17:12:10 +02:00
|
|
|
return _xmlCharsetForCharset([charset lowercaseString]);
|
2008-08-22 20:43:08 +02:00
|
|
|
}
|
2007-11-20 23:18:19 +01:00
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
- (void) _parseContent
|
|
|
|
{
|
|
|
|
NSObject <SaxXMLReader> *parser;
|
|
|
|
NSData *preparsedContent;
|
2019-12-04 21:45:26 +01:00
|
|
|
NSMutableData *htmlContent;
|
2016-12-06 20:05:27 +01:00
|
|
|
NSString *s;
|
|
|
|
|
2009-05-21 17:12:10 +02:00
|
|
|
xmlCharEncoding enc;
|
2007-11-20 23:18:19 +01:00
|
|
|
|
2017-12-23 15:02:32 +01:00
|
|
|
if ([[self decodedFlatContent] isKindOfClass: [NGMimeBodyPart class]])
|
|
|
|
preparsedContent = [[[self decodedFlatContent] body] sanitizedContentUsingVoidTags: VoidTags];
|
|
|
|
else
|
|
|
|
preparsedContent = [[self decodedFlatContent] sanitizedContentUsingVoidTags: VoidTags];
|
2008-08-22 20:43:08 +02:00
|
|
|
|
|
|
|
parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
|
|
|
|
createXMLReaderForMimeType: @"text/html"];
|
|
|
|
|
|
|
|
handler = [_UIxHTMLMailContentHandler new];
|
2017-01-11 22:08:05 +01:00
|
|
|
[handler setAttachmentIds: attachmentIds];
|
2009-05-21 17:12:10 +02:00
|
|
|
|
2020-01-30 21:36:16 +01:00
|
|
|
// Some broken email messages have some additionnal content outside the main HTML tags which are
|
|
|
|
// ignored by libxml.
|
|
|
|
// We surround the whole part with additional HTML tags to render all content.
|
|
|
|
htmlContent = [NSMutableData dataWithBytes: "<html>" length: 6];
|
|
|
|
[htmlContent appendData: preparsedContent];
|
|
|
|
[htmlContent appendBytes: "</html>" length: 7];
|
|
|
|
preparsedContent = (NSData *)htmlContent;
|
|
|
|
|
2009-05-21 17:12:10 +02:00
|
|
|
// We check if we got an unsupported charset. If so
|
|
|
|
// we convert everything to UTF-16{LE,BE} so it passes
|
|
|
|
// in libxml2 and also in characters: length: defined
|
|
|
|
// in this file (that expects unichar:s)
|
|
|
|
enc = [self _xmlCharEncoding];
|
|
|
|
if (enc == XML_CHAR_ENCODING_ERROR)
|
|
|
|
{
|
2009-12-15 01:05:06 +01:00
|
|
|
s = [NSString stringWithData: preparsedContent
|
|
|
|
usingEncodingNamed: [[bodyInfo objectForKey:@"parameterList"]
|
|
|
|
objectForKey: @"charset"]];
|
|
|
|
|
2015-07-23 16:22:36 +02:00
|
|
|
// In some rare cases (like #3276), we can get utterly broken email messages where
|
|
|
|
// HTML parts are wrongly encoded. We try to fall back to UTF-8 if that happens and
|
|
|
|
// if it still happens, we fall back to ISO-Latin-1.
|
|
|
|
if (!s)
|
|
|
|
{
|
|
|
|
s = [[NSString alloc] initWithData: preparsedContent encoding: NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
if (!s)
|
|
|
|
s = [[NSString alloc] initWithData: preparsedContent encoding: NSISOLatin1StringEncoding];
|
|
|
|
|
|
|
|
AUTORELEASE(s);
|
|
|
|
}
|
|
|
|
|
2009-05-21 17:12:10 +02:00
|
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
|
|
preparsedContent = [s dataUsingEncoding: NSUTF16BigEndianStringEncoding];
|
|
|
|
enc = XML_CHAR_ENCODING_UTF16BE;
|
|
|
|
#else
|
|
|
|
preparsedContent = [s dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
|
|
|
|
enc = XML_CHAR_ENCODING_UTF16LE;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-12-06 20:05:27 +01:00
|
|
|
// Let's sanitize the string to make sure libxml doesn't go havoc
|
|
|
|
if (enc == XML_CHAR_ENCODING_UTF8)
|
|
|
|
{
|
|
|
|
s = [[NSString alloc] initWithData: preparsedContent encoding: NSUTF8StringEncoding];
|
2019-06-13 19:54:16 +02:00
|
|
|
|
|
|
|
// Again, In some rare cases (like #4513), we can get utterly broken email messages where
|
|
|
|
// HTML parts are wrongly encoded. We try to fall back to UTF-8 if that happens and
|
|
|
|
// if it still happens, we fall back to ISO-Latin-1.
|
|
|
|
if (!s)
|
|
|
|
s = [[NSString alloc] initWithData: preparsedContent encoding: NSISOLatin1StringEncoding];
|
|
|
|
|
2016-12-06 20:05:27 +01:00
|
|
|
preparsedContent = [[s safeString] dataUsingEncoding: NSUTF8StringEncoding];
|
|
|
|
RELEASE(s);
|
|
|
|
}
|
|
|
|
|
2009-05-21 17:12:10 +02:00
|
|
|
[handler setContentEncoding: enc];
|
2008-09-30 21:34:31 +02:00
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
[parser setContentHandler: handler];
|
2020-01-30 21:36:16 +01:00
|
|
|
[parser parseFromSource: preparsedContent];
|
2007-11-20 23:18:19 +01:00
|
|
|
}
|
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
- (NSString *) cssContent
|
|
|
|
{
|
|
|
|
NSString *cssContent, *css;
|
|
|
|
|
|
|
|
if (!handler)
|
|
|
|
[self _parseContent];
|
|
|
|
|
|
|
|
css = [handler css];
|
|
|
|
if ([css length])
|
|
|
|
cssContent
|
|
|
|
= [NSString stringWithFormat: @"<style type=\"text/css\">%@</style>",
|
|
|
|
[handler css]];
|
|
|
|
else
|
|
|
|
cssContent = @"";
|
|
|
|
|
|
|
|
return cssContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) flatContentAsString
|
|
|
|
{
|
|
|
|
if (!handler)
|
|
|
|
[self _parseContent];
|
2009-05-21 17:12:10 +02:00
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
return [handler result];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation UIxMailPartExternalHTMLViewer
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
handler = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
2007-11-20 23:18:19 +01:00
|
|
|
{
|
2008-08-22 20:43:08 +02:00
|
|
|
[handler release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
2007-11-20 23:18:19 +01:00
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
- (xmlCharEncoding) _xmlCharEncoding
|
|
|
|
{
|
2007-11-20 23:18:19 +01:00
|
|
|
NSString *charset;
|
|
|
|
|
|
|
|
charset = [[bodyInfo objectForKey:@"parameterList"]
|
|
|
|
objectForKey: @"charset"];
|
|
|
|
if (![charset length])
|
|
|
|
charset = @"us-ascii";
|
2008-08-22 20:43:08 +02:00
|
|
|
|
2009-05-21 17:12:10 +02:00
|
|
|
return _xmlCharsetForCharset([charset lowercaseString]);
|
2007-11-20 23:18:19 +01:00
|
|
|
}
|
|
|
|
|
2007-09-07 17:45:46 +02:00
|
|
|
- (void) _parseContent
|
2007-02-15 22:04:31 +01:00
|
|
|
{
|
2007-11-20 23:18:19 +01:00
|
|
|
NSObject <SaxXMLReader> *parser;
|
2007-08-20 19:46:15 +02:00
|
|
|
NSData *preparsedContent;
|
2008-08-22 20:43:08 +02:00
|
|
|
SOGoMailBodyPart *part;
|
|
|
|
NSString *encoding;
|
2009-05-21 17:12:10 +02:00
|
|
|
xmlCharEncoding enc;
|
2007-12-13 00:59:36 +01:00
|
|
|
|
2007-02-15 22:04:31 +01:00
|
|
|
parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
|
|
|
|
createXMLReaderForMimeType: @"text/html"];
|
2017-12-23 15:02:32 +01:00
|
|
|
|
|
|
|
if ([[self decodedFlatContent] isKindOfClass: [NGMimeBodyPart class]])
|
|
|
|
{
|
|
|
|
preparsedContent = [[[self decodedFlatContent] body] sanitizedContentUsingVoidTags: VoidTags];
|
|
|
|
encoding = [[[self decodedFlatContent] contentType] valueOfParameter: @"charset"];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
part = [self clientObject];
|
|
|
|
preparsedContent = [[part fetchBLOB] sanitizedContentUsingVoidTags: VoidTags];
|
|
|
|
encoding = [[part partInfo] valueForKey: @"encoding"];
|
|
|
|
}
|
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
if (![encoding length])
|
|
|
|
encoding = @"us-ascii";
|
2007-02-15 22:04:31 +01:00
|
|
|
|
|
|
|
handler = [_UIxHTMLMailContentHandler new];
|
2017-01-11 22:08:05 +01:00
|
|
|
[handler setAttachmentIds: attachmentIds];
|
2009-05-21 17:12:10 +02:00
|
|
|
|
|
|
|
// We check if we got an unsupported charset. If so
|
|
|
|
// we convert everything to UTF-16{LE,BE} so it passes
|
|
|
|
// in libxml2 and also in characters: length: defined
|
|
|
|
// in this file (that expects unichar:s)
|
|
|
|
enc = _xmlCharsetForCharset(encoding);
|
|
|
|
if (enc == XML_CHAR_ENCODING_ERROR)
|
|
|
|
{
|
|
|
|
NSString *s;
|
|
|
|
|
2009-12-15 01:05:06 +01:00
|
|
|
s = [NSString stringWithData: preparsedContent
|
|
|
|
usingEncodingNamed: [[bodyInfo objectForKey:@"parameterList"]
|
|
|
|
objectForKey: @"charset"]];
|
2009-05-21 17:12:10 +02:00
|
|
|
|
|
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
|
|
preparsedContent = [s dataUsingEncoding: NSUTF16BigEndianStringEncoding];
|
|
|
|
enc = XML_CHAR_ENCODING_UTF16BE;
|
|
|
|
#else
|
|
|
|
preparsedContent = [s dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
|
|
|
|
enc = XML_CHAR_ENCODING_UTF16LE;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
[handler setContentEncoding: enc];
|
2007-02-15 22:04:31 +01:00
|
|
|
[parser setContentHandler: handler];
|
|
|
|
[parser parseFromSource: preparsedContent];
|
2007-09-07 17:45:46 +02:00
|
|
|
}
|
|
|
|
|
2008-08-22 20:43:08 +02:00
|
|
|
- (NSString *) filename
|
|
|
|
{
|
|
|
|
return [[self clientObject] filename];
|
|
|
|
}
|
|
|
|
|
2007-09-07 17:45:46 +02:00
|
|
|
- (NSString *) cssContent
|
|
|
|
{
|
|
|
|
NSString *cssContent, *css;
|
|
|
|
|
|
|
|
if (!handler)
|
|
|
|
[self _parseContent];
|
2007-02-15 22:04:31 +01:00
|
|
|
|
|
|
|
css = [handler css];
|
|
|
|
if ([css length])
|
2007-11-19 21:01:27 +01:00
|
|
|
cssContent
|
|
|
|
= [NSString stringWithFormat: @"<style type=\"text/css\">%@</style>",
|
|
|
|
[handler css]];
|
2007-09-07 17:45:46 +02:00
|
|
|
else
|
|
|
|
cssContent = @"";
|
|
|
|
|
|
|
|
return cssContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) flatContentAsString
|
|
|
|
{
|
|
|
|
if (!handler)
|
|
|
|
[self _parseContent];
|
2007-02-15 22:04:31 +01:00
|
|
|
|
2007-09-07 17:45:46 +02:00
|
|
|
return [handler result];
|
2007-02-15 22:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|