diff --git a/ChangeLog b/ChangeLog index 7669b9008..4e8bd9180 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2009-06-10 Wolfgang Sourdeau + + * UI/MailPartViewers/UIxMailPartHTMLViewer.m: fixed rendering of + CSS content containing comment characters. Ignore certain HTML + tags that can alter the appearance of the SOGo display altogether + (SCRIPT, LINK, BASE, META, TITLE). + 2009-06-09 Wolfgang Sourdeau * SoObjects/Appointments/SOGoFreeBusyObject.m diff --git a/UI/MailPartViewers/UIxMailPartHTMLViewer.m b/UI/MailPartViewers/UIxMailPartHTMLViewer.m index 284fb134c..dc0a571dd 100644 --- a/UI/MailPartViewers/UIxMailPartHTMLViewer.m +++ b/UI/MailPartViewers/UIxMailPartHTMLViewer.m @@ -49,6 +49,9 @@ #define showWhoWeAre() #endif +/* Tags that are forbidden within the body of the html content */ +static NSArray *BannedTags = nil; + static xmlCharEncoding _xmlCharsetForCharset (NSString *charset) { @@ -102,14 +105,14 @@ _xmlCharsetForCharset (NSString *charset) NSMutableString *result; NSMutableString *css; NSDictionary *attachmentIds; + BOOL ignoreContent; + NSString *ignoreTag; BOOL inBody; BOOL inStyle; - BOOL inScript; BOOL inCSSDeclaration; BOOL hasEmbeddedCSS; BOOL hasExternalImages; BOOL unsafe; - NSMutableArray *crumb; xmlCharEncoding contentEncoding; } @@ -119,13 +122,23 @@ _xmlCharsetForCharset (NSString *charset) @implementation _UIxHTMLMailContentHandler ++ (void) initialize +{ + if (!BannedTags) + { + BannedTags = [NSArray arrayWithObjects: @"SCRIPT", @"LINK", @"BASE", + @"META", @"TITLE", nil]; + [BannedTags retain]; + } +} + - (id) init { if ((self = [super init])) { - crumb = nil; css = nil; result = nil; + ignoreTag = nil; attachmentIds = nil; contentEncoding = XML_CHAR_ENCODING_UTF8; } @@ -135,7 +148,6 @@ _xmlCharsetForCharset (NSString *charset) - (void) dealloc { - [crumb release]; [result release]; [css release]; [super dealloc]; @@ -181,34 +193,25 @@ _xmlCharsetForCharset (NSString *charset) { showWhoWeAre(); - [crumb release]; [css release]; [result release]; result = [NSMutableString new]; css = [NSMutableString new]; - crumb = [NSMutableArray new]; + + ignoreContent = NO; + [ignoreTag release]; + ignoreTag = nil; inBody = NO; inStyle = NO; - inScript = NO; inCSSDeclaration = NO; hasEmbeddedCSS = NO; } - (void) endDocument { - unsigned int count, max; - - showWhoWeAre(); - max = [crumb count]; - if (max > 0) - for (count = max - 1; count > -1; count--) - { - [result appendFormat: @"", [crumb objectAtIndex: count]]; - [crumb removeObjectAtIndex: count]; - } } - (void) startPrefixMapping: (NSString *)_prefix @@ -225,10 +228,13 @@ _xmlCharsetForCharset (NSString *charset) - (void) _appendStyle: (unichar *) _chars length: (int) _len { - unsigned int count; + unsigned int count, length; unichar *start, *currentChar; start = _chars; + while (*start < 33) + start++; + currentChar = start; for (count = 0; count < _len; count++) { @@ -243,28 +249,39 @@ _xmlCharsetForCharset (NSString *charset) } else { - if (*currentChar == '{') - inCSSDeclaration = YES; - if (*currentChar == ',') - hasEmbeddedCSS = NO; - else if (!hasEmbeddedCSS) + if (*currentChar < 32) { - if (*currentChar == '@') - hasEmbeddedCSS = YES; - else - if (*currentChar > 32) - { - [css appendString: [NSString stringWithCharacters: start - length: (currentChar - start)]]; - [css appendString: @".SOGoHTMLMail-CSS-Delimiter "]; + if (currentChar > start) + [css appendString: [NSString stringWithCharacters: start + length: (currentChar - start)]]; + start = currentChar + 1; + } + else + { + if (*currentChar == '{') + inCSSDeclaration = YES; + else if (*currentChar == ',') + hasEmbeddedCSS = NO; + else if (!hasEmbeddedCSS) + { + if (*currentChar == '@') hasEmbeddedCSS = YES; - start = currentChar; - } + else + if (*currentChar > 32) + { + length = (currentChar - start); + [css appendFormat: @"%@\n.SOGoHTMLMail-CSS-Delimiter ", + [NSString stringWithCharacters: start length: length]]; + hasEmbeddedCSS = YES; + start = currentChar; + } + } } } } - [css appendString: [NSString stringWithCharacters: start - length: (currentChar - start)]]; + if (currentChar > start) + [css appendString: [NSString stringWithCharacters: start + length: (currentChar - start)]]; } - (void) startElement: (NSString *) _localName @@ -273,111 +290,146 @@ _xmlCharsetForCharset (NSString *charset) attributes: (id ) _attributes { unsigned int count, max; - NSString *name, *value, *cid; + NSString *name, *value, *cid, *upperName; NSMutableString *resultPart; BOOL skipAttribute; showWhoWeAre(); - if (inStyle || inScript) + + upperName = [_localName uppercaseString]; + if (inStyle || ignoreContent) ; - else if ([_localName caseInsensitiveCompare: @"base"] == NSOrderedSame) + else if ([upperName isEqualToString: @"BASE"]) ; - else if ([_localName caseInsensitiveCompare: @"meta"] == NSOrderedSame) + else if ([upperName isEqualToString: @"META"]) ; - else if ([_localName caseInsensitiveCompare: @"body"] == NSOrderedSame) + else if ([upperName isEqualToString: @"BODY"]) inBody = YES; - else if ([_localName caseInsensitiveCompare: @"script"] == NSOrderedSame) - inScript = YES; - else if ([_localName caseInsensitiveCompare: @"style"] == NSOrderedSame) + else if ([upperName isEqualToString: @"STYLE"]) inStyle = YES; else if (inBody) { - resultPart = [NSMutableString new]; - [resultPart appendFormat: @"<%@", _rawName]; - - max = [_attributes count]; - for (count = 0; count < max; count++) + if ([BannedTags containsObject: upperName]) { - skipAttribute = NO; - name = [_attributes nameAtIndex: count]; - if ([[name lowercaseString] hasPrefix: @"on"]) - skipAttribute = YES; - else if ([name caseInsensitiveCompare: @"src"] == NSOrderedSame) - { - value = [_attributes valueAtIndex: count]; - if ([value hasPrefix: @"cid:"]) - { - cid = [NSString stringWithFormat: @"<%@>", - [value substringFromIndex: 4]]; - value = [attachmentIds objectForKey: cid]; - skipAttribute = (value == nil); - } - else if ([_rawName caseInsensitiveCompare: @"img"] == NSOrderedSame) - { - hasExternalImages = YES; - - if (!unsafe) skipAttribute = YES; - } - else - skipAttribute = YES; - } - else if ([name caseInsensitiveCompare: @"href"] == NSOrderedSame - || [name caseInsensitiveCompare: @"action"] == NSOrderedSame) - { - value = [_attributes valueAtIndex: count]; - skipAttribute = ([value rangeOfString: @"://"].location - == NSNotFound - && ![value hasPrefix: @"#"]); - } - else - value = [_attributes valueAtIndex: count]; - if (!skipAttribute) - [resultPart appendFormat: @" %@=\"%@\"", - name, [value stringByReplacingString: @"\"" - withString: @"\\\""]]; + ignoreTag = [upperName copy]; + ignoreContent = YES; } + else + { + resultPart = [NSMutableString new]; + [resultPart appendFormat: @"<%@", _rawName]; - [resultPart appendString: @">"]; - [result appendString: resultPart]; - [resultPart release]; + max = [_attributes count]; + for (count = 0; count < max; count++) + { + skipAttribute = NO; + name = [[_attributes nameAtIndex: count] uppercaseString]; + if ([name hasPrefix: @"ON"]) + skipAttribute = YES; + else if ([name isEqualToString: @"SRC"]) + { + value = [_attributes valueAtIndex: count]; + if ([value hasPrefix: @"cid:"]) + { + cid = [NSString stringWithFormat: @"<%@>", + [value substringFromIndex: 4]]; + value = [attachmentIds objectForKey: cid]; + skipAttribute = (value == nil); + } + else if ([upperName isEqualToString: @"IMG"]) + { + hasExternalImages = YES; + + if (!unsafe) skipAttribute = YES; + } + else + skipAttribute = YES; + } + else if ([name isEqualToString: @"HREF"] + || [name isEqualToString: @"ACTION"]) + { + value = [_attributes valueAtIndex: count]; + skipAttribute = ([value rangeOfString: @"://"].location + == NSNotFound + && ![value hasPrefix: @"#"]); + } + else + value = [_attributes valueAtIndex: count]; + if (!skipAttribute) + [resultPart appendFormat: @" %@=\"%@\"", + name, [value stringByReplacingString: @"\"" + withString: @"\\\""]]; + } + + [resultPart appendString: @">"]; + [result appendString: resultPart]; + [resultPart release]; + } } } - (void) _finishCSS { + NSRange excessiveDelimiter; + + [css replaceString: @"" withString: @""]; [css replaceString: @".SOGoHTMLMail-CSS-Delimiter body" withString: @".SOGoHTMLMail-CSS-Delimiter"]; [css replaceString: @";" withString: @" !important;"]; - [css replaceString: @"" withString: @""]; + + excessiveDelimiter = [css rangeOfString: @".SOGoHTMLMail-CSS-Delimiter " + options: NSBackwardsSearch]; + if (excessiveDelimiter.location != NSNotFound) + { + if (NSMaxRange (excessiveDelimiter) == [css length]) + [css deleteCharactersInRange: excessiveDelimiter]; + } } - (void) endElement: (NSString *) _localName namespace: (NSString *) _ns rawName: (NSString *) _rawName { + NSString *upperName; + showWhoWeAre(); - if (inStyle) + upperName = [_localName uppercaseString]; + + if (ignoreContent) { - if ([_localName caseInsensitiveCompare: @"style"] == NSOrderedSame) - { - inStyle = NO; - inCSSDeclaration = NO; - } + if ([upperName isEqualToString: ignoreTag]) + { + ignoreContent = NO; + [ignoreTag release]; + ignoreTag = nil; + } } - else if (inScript) - inScript = ([_localName caseInsensitiveCompare: @"script"] != NSOrderedSame); - else if (inBody) + else { - if ([_localName caseInsensitiveCompare: @"body"] == NSOrderedSame) - { - inBody = NO; - if (css) - [self _finishCSS]; - } - else - [result appendFormat: @"", _localName]; + if (inStyle) + { + if ([upperName isEqualToString: @"STYLE"]) + { + inStyle = NO; + inCSSDeclaration = NO; + } + } + else if (inBody) + { + if ([upperName isEqualToString: @"BODY"]) + { + inBody = NO; + if (css) + [self _finishCSS]; + } + else + { + NSLog (@"%@", _localName); + [result appendFormat: @"", _localName]; + } + } } } @@ -385,7 +437,7 @@ _xmlCharsetForCharset (NSString *charset) length: (int) _len { showWhoWeAre(); - if (!inScript) + if (!ignoreContent) { if (inStyle) [self _appendStyle: _chars length: _len]; @@ -394,7 +446,7 @@ _xmlCharsetForCharset (NSString *charset) NSString *tmpString; tmpString = [NSString stringWithCharacters: _chars length: _len]; - + // HACK: This is to avoid appending the useless junk in the tag // that Outlook adds. It seems to confuse the XML parser for // forwarded messages as we get this in the _body_ of the email