diff --git a/UI/MailPartViewers/UIxMailPartHTMLViewer.m b/UI/MailPartViewers/UIxMailPartHTMLViewer.m index 060d25db0..4c10338af 100644 --- a/UI/MailPartViewers/UIxMailPartHTMLViewer.m +++ b/UI/MailPartViewers/UIxMailPartHTMLViewer.m @@ -255,29 +255,96 @@ _xmlCharsetForCharset (NSString *charset) - (void) _appendStyle: (unichar *) _chars length: (NSUInteger) _len { - NSMutableString *declaration; - NSUInteger count, length; - unichar *start, *currentChar; + 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; start = _chars; - while (*start < 33) - start++; - - currentChar = start; for (count = 0; count < _len; count++) { currentChar = _chars + count; + if (*currentChar < 32) + { + // Ignore control characters + if (!inComment && currentChar > start) + [sanitizedStyle appendString: [NSString stringWithCharacters: start + length: (currentChar - start)]]; + start = currentChar + 1; + } + else + { + if ((currentChar < _chars + _len - 3) && + *currentChar == '<' && + *(currentChar+1) == '!' && + *(currentChar+2) == '-' && + *(currentChar+3) == '-') + { + // 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; + } + 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 < 32) - { - // Append substring since last valid character and reset start counter - if (currentChar > start) - [declaration appendString: [NSString stringWithCharacters: start - length: (currentChar - start)]]; - start = currentChar + 1; - } - else if (*currentChar == '}') + if (*currentChar == '}') { inCSSDeclaration = NO; if (hasEmbeddedCSS) @@ -291,14 +358,14 @@ _xmlCharsetForCharset (NSString *charset) // Prefix CSS rule including ending curly bracket length = (currentChar - start) + 1; [declaration appendString: [NSString stringWithCharacters: start length: length]]; - [css appendFormat: @".SOGoHTMLMail-CSS-Delimiter %@\n", declaration]; - start = currentChar; + [css appendString: declaration]; + start = currentChar + 1; } } else if (*currentChar == ';') { // Add !important - if ((currentChar < _chars - 10) || + if ((currentChar < sanitizedChars + 10) || !((*(currentChar-1) == 't' || *(currentChar-1) == 'T') && (*(currentChar-2) == 'n' || *(currentChar-2) == 'N') && (*(currentChar-3) == 'a' || *(currentChar-3) == 'A') && @@ -319,48 +386,49 @@ _xmlCharsetForCharset (NSString *charset) } else { - if (*currentChar < 32) + if (*currentChar == '{') { - // Append substring since last valid character and reset start counter - if (currentChar > start) - [css appendString: [NSString stringWithCharacters: start - length: (currentChar - start)]]; + // 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]; start = currentChar + 1; } - else + if (*currentChar == '}') { - if (*currentChar == '{') + // 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 == ';') { - // Start of rule declaration - inCSSDeclaration = YES; - declaration = [NSMutableString string]; - } - if (*currentChar == '}') - // CSS syntax error: ending declaration character while not in a CSS declaration. - // Ignore eveything from last CSS declaration. - start = currentChar + 1; - else if (hasEmbeddedCSS) - { - if (*currentChar == ';') - { - // End of at-rule definition; remove it from the stylesheet - hasEmbeddedCSS = NO; - start = currentChar + 1; - } - } - else if (*currentChar == ',') - { - // Prefix CSS selector - length = (currentChar - start); - [css appendFormat: @" .SOGoHTMLMail-CSS-Delimiter %@,", - [NSString stringWithCharacters: start length: length]]; + // End of at-rule definition; remove it from the stylesheet + hasEmbeddedCSS = NO; start = currentChar + 1; } - else if (*currentChar == '@') - { - // Start of at-rule definition - hasEmbeddedCSS = YES; - } + } + 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; } } }