* @param $add_attr_to_tag see description above
* @param $message message object
* @param $id message id
+ * @param $recursively_called boolean flag for recursive calls into this function (optional; default FALSE)
* @return sanitized html safe to show on your pages.
*/
function sq_sanitize($body,
$add_attr_to_tag,
$message,
$id,
- $mailbox
+ $mailbox,
+ $recursively_called=FALSE
){
$me = 'sq_sanitize';
+
+ /**
+ * See if tag_list is of tags to remove or tags to allow.
+ * false means remove these tags
+ * true means allow these tags
+ */
+ $orig_tag_list = $tag_list;
$rm_tags = array_shift($tag_list);
+
/**
* Normalize rm_tags and rm_tags_with_content.
*/
@array_walk($tag_list, 'sq_casenormalize');
@array_walk($rm_tags_with_content, 'sq_casenormalize');
@array_walk($self_closing_tags, 'sq_casenormalize');
- /**
- * See if tag_list is of tags to remove or tags to allow.
- * false means remove these tags
- * true means allow these tags
- */
+
$curpos = 0;
$open_tags = Array();
$trusted = "\n<!-- begin sanitized html -->\n";
while (($curtag = sq_getnxtag($body, $curpos)) != FALSE){
list($tagname, $attary, $tagtype, $lt, $gt) = $curtag;
+
+ /**
+ * RCDATA and RAWTEXT tags are handled differently:
+ * next instance of closing tag is used, whether or not
+ * the HTML is well formed before that
+ */
+ global $rcdata_rawtext_tags;
+ if (!$recursively_called
+ && in_array($tagname, $rcdata_rawtext_tags)
+ && $tagtype === 1){
+ $closing_tag = false;
+ $closing_tag_offset = $curpos;
+ // seek out the closing tag for the current RCDATA/RAWTEXT tag
+ while (1) {
+ // first we need to move forward to next available closing tag
+ // (intentionally leave off the closing > and let sq_getnxtag() validate a proper tag syntax)
+ $next_tag = sq_findnxreg($body, $closing_tag_offset, "</\s*$tagname");
+ if ($next_tag === false) {
+ $closing_tag = false;
+ break;
+ }
+ // but then we have to make sure it's a well-formed tag
+ $closing_tag = sq_getnxtag($body, $next_tag[0]);
+ if ($closing_tag === false)
+ break;
+ else if ($closing_tag[0] !== false
+ // these should be redundant
+ && $closing_tag[0] === $tagname && $closing_tag[2] === 2) {
+ $trusted .= sq_sanitize(substr($body, $curpos, $closing_tag[4] - $curpos + 1),
+ $orig_tag_list, $rm_tags_with_content, $self_closing_tags,
+ $force_tag_closing, $rm_attnames, $bad_attvals, $add_attr_to_tag,
+ $message, $id, $mailbox, true);
+ $curpos = $closing_tag[4] + 1;
+ continue 2;
+ }
+ $closing_tag_offset = $next_tag[0] + 1;
+ }
+ if ($closing_tag === false)
+ { /* no-op... there was no closing tag for this RCDATA/RAWTEXT tag - we could probably set $curpos to the end of $body, but this HTML is malformed anyway and should just fall apart on its own */ }
+ }
+
$free_content = substr($body, $curpos, $lt-$curpos);
/**
* Take care of <style>
// require_once(SM_PATH . 'functions/url_parser.php'); // for $MailTo_PReg_Match
global $attachment_common_show_images, $view_unsafe_images,
- $has_unsafe_images, $allow_svg_display;
+ $has_unsafe_images, $allow_svg_display, $rcdata_rawtext_tags,
+ $remove_rcdata_rawtext_tags_and_content;
+
+ $rcdata_rawtext_tags = array(
+ "noscript",
+ "noframes",
+ "noembed",
+ "textarea",
+ // also "title", "xmp", "script", "iframe", "plaintext" which we already remove below
+ );
+
/**
* Don't display attached images in HTML mode.
*
*/
$attachment_common_show_images = false;
$tag_list = Array(
- false,
+ false, // remove these tags
"meta",
"html",
"head",
);
if (!$allow_svg_display)
$rm_tags_with_content[] = 'svg';
+ /**
+ * SquirrelMail will parse RCDATA and RAWTEXT tags and handle them as the special
+ * case that they are, but if you prefer to remove them and their contents entirely
+ * (in most cases, should be a safe thing with minimal impact), you can add the
+ * following to config/config_local.php
+ * $remove_rcdata_rawtext_tags_and_content = TRUE;
+ */
+ if ($remove_rcdata_rawtext_tags_and_content)
+ $rm_tags_with_content = array_merge($rm_tags_with_content, $rcdata_rawtext_tags);
$self_closing_tags = Array(
"img",