if (!is_object($msg)) {
global $color, $mailbox;
- /* removed urldecode because $_GET is auto urldecoded ??? */
displayPageHeader( $color, $mailbox );
$errormessage = _("SquirrelMail could not decode the bodystructure of the message");
$errormessage .= '<br />'._("The bodystructure provided by your IMAP server:").'<br /><br />';
$errormessage .= '<pre>' . htmlspecialchars($read) . '</pre>';
- plain_error_message( $errormessage, $color );
+ plain_error_message( $errormessage );
echo '</body></html>';
exit;
}
if (count($flags)) {
foreach ($flags as $flag) {
+//FIXME: please document why it is we have to check the first char of the flag but we then go ahead and do a full string comparison anyway. Is this a speed enhancement? If not, let's keep it simple and just compare the full string and forget the switch block.
$char = strtoupper($flag{1});
switch ($char) {
case 'S':
// }
} else if (ereg('"([^"]*)"', $topline, $regs)) {
$ret = $regs[1];
+ } else if ((stristr($topline, 'nil') !== false) && (empty($wholemessage))) {
+ $ret = $wholemessage;
} else {
global $where, $what, $mailbox, $passed_id, $startMessage;
$par = 'mailbox=' . urlencode($mailbox) . '&passed_id=' . $passed_id;
global $startMessage, $languages, $squirrelmail_language,
$show_html_default, $sort, $has_unsafe_images, $passed_ent_id,
$use_iframe, $iframe_height, $download_and_unsafe_link,
- $download_href, $unsafe_image_toggle_href, $unsafe_image_toggle_text;
+ $download_href, $unsafe_image_toggle_href, $unsafe_image_toggle_text,
+ $oTemplate, $nbsp;
// workaround for not updated config.php
if (! isset($use_iframe)) $use_iframe = false;
global $oTemplate;
$oTemplate->assign('iframe_url', $iframeurl);
+ $oTemplate->assign('iframe_height', $iframe_height);
$oTemplate->assign('html_body', $html_body);
$body = $oTemplate->fetch('read_html_iframe.tpl');
} else {
// old way of html rendering
- $body = magicHTML($body, $id, $message, $mailbox);
/**
* convert character set. charset_decode does not remove html special chars
* applied by magicHTML functions and does not sanitize them second time if
* fourth argument is true.
*/
- $body = charset_decode($body_message->header->getParameter('charset'),$body,false,true);
+ $charset = $body_message->header->getParameter('charset');
+ if (!empty($charset)) {
+ $body = charset_decode($charset,$body,false,true);
+ }
+ $body = magicHTML($body, $id, $message, $mailbox);
}
} else {
translateText($body, $wrap_at,
$link .= '&passed_ent_id='.$passed_ent_id;
}
$download_href = SM_PATH . 'src/download.php?absolute_dl=true&' . $link;
- $download_and_unsafe_link .= ' | <a href="'. $download_href .'">' . _("Download this as a file") . '</a>';
+ $download_and_unsafe_link .= "$nbsp|$nbsp"
+ . create_hyperlink($download_href, _("Download this as a file"));
if ($view_unsafe_images) {
$text = _("Hide Unsafe Images");
} else {
if($text != '') {
$unsafe_image_toggle_href = SM_PATH . 'src/read_body.php?'.$link;
$unsafe_image_toggle_text = $text;
- $download_and_unsafe_link .= ' | <a href="'. $unsafe_image_toggle_href .'">' . $text . '</a>';
+ $download_and_unsafe_link .= "$nbsp|$nbsp"
+ . create_hyperlink($unsafe_image_toggle_href, $text);
}
}
return $body;
* for a more generic type. Finally, a hook for ALL attachment
* types is run as well.
*/
+ // First remember the default link.
+ $defaultlink_orig = $defaultlink;
+
/* The API for this hook has changed as of 1.5.2 so that all plugin
arguments are passed in an array instead of each their own plugin
argument, and arguments are passed by reference, so instead of
returning any changes, changes should simply be made to the original
arguments themselves. */
- do_hook("attachment $type0/$type1", $temp=array(&$links,
- &$startMessage, &$id, &$urlMailbox, &$ent, &$defaultlink,
- &$display_filename, &$where, &$what));
- if(count($links) <= 1) {
+ $temp = array(&$links, &$startMessage, &$id, &$urlMailbox, &$ent,
+ &$defaultlink, &$display_filename, &$where, &$what);
+ do_hook("attachment $type0/$type1", $temp);
+ if(count($links) <= 1 && $defaultlink == $defaultlink_orig) {
/* The API for this hook has changed as of 1.5.2 so that all plugin
arguments are passed in an array instead of each their own plugin
argument, and arguments are passed by reference, so instead of
returning any changes, changes should simply be made to the original
arguments themselves. */
- do_hook("attachment $type0/*", $temp=array(&$links,
- &$startMessage, &$id, &$urlMailbox, &$ent, &$defaultlink,
- &$display_filename, &$where, &$what));
+ $temp = array(&$links, &$startMessage, &$id, &$urlMailbox, &$ent,
+ &$defaultlink, &$display_filename, &$where, &$what);
+ do_hook("attachment $type0/*", $temp);
}
/* The API for this hook has changed as of 1.5.2 so that all plugin
arguments are passed in an array instead of each their own plugin
argument, and arguments are passed by reference, so instead of
returning any changes, changes should simply be made to the original
arguments themselves. */
- do_hook("attachment */*", $temp=array(&$links,
- &$startMessage, &$id, &$urlMailbox, &$ent, &$defaultlink,
- &$display_filename, &$where, &$what));
+ $temp = array(&$links, &$startMessage, &$id, &$urlMailbox, &$ent,
+ &$defaultlink, &$display_filename, &$where, &$what);
+ // Do not let a generic plugin change the default link if a more
+ // specialized one already did it...
+ if ($defaultlink != $defaultlink_orig) {
+ $dummy = '';
+ $temp[5] = &$dummy;
+ }
+ do_hook("attachment */*", $temp);
$this_attachment = array();
$this_attachment['Name'] = decodeHeader($display_filename);
/**
* Decodes headers
*
- * This functions decode strings that is encoded according to
+ * This function decodes strings that are encoded according to
* RFC1522 (MIME Part Two: Message Header Extensions for Non-ASCII Text).
* Patched by Christian Schmidt <christian@ostenfeld.dk> 23/03/2002
*
array('ʟ', 'ʟ' ,/* L UNICODE IPA Extension */
'ʀ', 'ʀ' ,/* R UNICODE IPA Extension */
'ɴ', 'ɴ' ,/* N UNICODE IPA Extension */
- 'E', 'E' ,/* Unicode FULLWIDTH LATIN CAPITAL LETTER E */
- 'e', 'e' ,/* Unicode FULLWIDTH LATIN SMALL LETTER E */
+ 'E', 'E' ,/* Unicode FULLWIDTH LATIN CAPITAL LETTER E */
+ 'e', 'e' ,/* Unicode FULLWIDTH LATIN SMALL LETTER E */
'X', 'X',/* Unicode FULLWIDTH LATIN CAPITAL LETTER X */
'x', 'x',/* Unicode FULLWIDTH LATIN SMALL LETTER X */
'P', 'P',/* Unicode FULLWIDTH LATIN CAPITAL LETTER P */
'U', 'U',/* Unicode FULLWIDTH LATIN CAPITAL LETTER U */
'u', 'u',/* Unicode FULLWIDTH LATIN SMALL LETTER U */
'ⁿ', 'ⁿ' ,/* Unicode SUPERSCRIPT LATIN SMALL LETTER N */
- '艤', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER E */ // in unicode this is some Chinese char range
- '芅', /* Shift JIS FULLWIDTH LATIN SMALL LETTER E */
- '艷', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER X */
- '芘', /* Shift JIS FULLWIDTH LATIN SMALL LETTER X */
- '良', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER P */
- '芐', /* Shift JIS FULLWIDTH LATIN SMALL LETTER P */
- '艱', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER R */
- '芒', /* Shift JIS FULLWIDTH LATIN SMALL LETTER R */
- '色', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER S */
- '芓', /* Shift JIS FULLWIDTH LATIN SMALL LETTER S */
- '艨', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER I */
- '芉', /* Shift JIS FULLWIDTH LATIN SMALL LETTER I */
- '艮', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER O */
- '芏', /* Shift JIS FULLWIDTH LATIN SMALL LETTER O */
- '艭', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER N */
- '芎'), /* Shift JIS FULLWIDTH LATIN SMALL LETTER N */
+ "\xEF\xBC\xA5", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER E */ // in unicode this is some Chinese char range
+ "\xEF\xBD\x85", /* Shift JIS FULLWIDTH LATIN SMALL LETTER E */
+ "\xEF\xBC\xB8", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER X */
+ "\xEF\xBD\x98", /* Shift JIS FULLWIDTH LATIN SMALL LETTER X */
+ "\xEF\xBC\xB0", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER P */
+ "\xEF\xBD\x90", /* Shift JIS FULLWIDTH LATIN SMALL LETTER P */
+ "\xEF\xBC\xB2", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER R */
+ "\xEF\xBD\x92", /* Shift JIS FULLWIDTH LATIN SMALL LETTER R */
+ "\xEF\xBC\xB3", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER S */
+ "\xEF\xBD\x93", /* Shift JIS FULLWIDTH LATIN SMALL LETTER S */
+ "\xEF\xBC\xA9", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER I */
+ "\xEF\xBD\x89", /* Shift JIS FULLWIDTH LATIN SMALL LETTER I */
+ "\xEF\xBC\xAF", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER O */
+ "\xEF\xBD\x8F", /* Shift JIS FULLWIDTH LATIN SMALL LETTER O */
+ "\xEF\xBC\xAE", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER N */
+ "\xEF\xBD\x8E", /* Shift JIS FULLWIDTH LATIN SMALL LETTER N */
+ "\xEF\xBC\xAC", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER L */
+ "\xEF\xBD\x8C", /* Shift JIS FULLWIDTH LATIN SMALL LETTER L */
+ "\xEF\xBC\xB5", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER U */
+ "\xEF\xBD\x95", /* Shift JIS FULLWIDTH LATIN SMALL LETTER U */
+ "\xE2\x81\xBF", /* Shift JIS FULLWIDTH SUPERSCRIPT N */
+ "\xCA\x9F", /* L UNICODE IPA Extension */
+ "\xCA\x80", /* R UNICODE IPA Extension */
+ "\xC9\xB4"), /* N UNICODE IPA Extension */
array('l', 'l', 'r','r','n','n',
- 'E','E','e','e','X','X','x','x','P','P','p','p','S','S','s','s','I','I',
- 'i','i','O','O','o','o','N','N','n','n','L','L','l','l','U','U','u','u','n',
- 'E','e','X','x','P','p','S','s','I','i','O','o','N','n'));
+ 'E','E','e','e','X','X','x','x','P','P','p','p','R','R','r','r','S','S','s','s','I','I',
+ 'i','i','O','O','o','o','N','N','n','n','L','L','l','l','U','U','u','u','n','n',
+ 'E','e','X','x','P','p','R','r','S','s','I','i','O','o','N','n','L','l','U','u','n','l','r','n'));
$attvalue = str_replace($aDangerousCharsReplacementTable[0],$aDangerousCharsReplacementTable[1],$attvalue);
// Escapes are useful for special characters like "{}[]()'&. In other cases they are
preg_replace($valmatch, $valrepl, $attvalue);
if ($newvalue != $attvalue){
$attary{$attname} = $newvalue;
+ $attvalue = $newvalue;
}
}
}
}
}
-
- /**
- * Replace empty src tags with the blank image. src is only used
- * for frames, images, and image inputs. Doing a replace should
- * not affect them working as should be, however it will stop
- * IE from being kicked off when src for img tags are not set
- */
- if (($attname == 'src') && ($attvalue == '""')) {
- $attary{$attname} = '"' . SM_PATH . 'images/blank.png"';
- }
-
- /**
- * Turn cid: urls into http-friendly ones.
- */
- if (preg_match("/^[\'\"]\s*cid:/si", $attvalue)){
- $attary{$attname} = sq_cid2http($message, $id, $attvalue, $mailbox);
+ if ($attname == 'style') {
+ if (preg_match('/[\0-\37\200-\377]+/',$attvalue)) {
+ // 8bit and control characters in style attribute values can be used for XSS, remove them
+ $attary{$attname} = '"disallowed character"';
+ }
+ preg_match_all("/url\s*\((.+)\)/si",$attvalue,$aMatch);
+ if (count($aMatch)) {
+ foreach($aMatch[1] as $sMatch) {
+ // url value
+ $urlvalue = $sMatch;
+ sq_fix_url($attname, $urlvalue, $message, $id, $mailbox,"'");
+ $attary{$attname} = str_replace($sMatch,$urlvalue,$attvalue);
+ }
+ }
}
-
/**
- * "Hack" fix for Outlook using propriatary outbind:// protocol in img tags.
- * One day MS might actually make it match something useful, for now, falling
- * back to using cid2http, so we can grab the blank.png.
+ * Use white list based filtering on attributes which can contain url's
*/
- if (preg_match("/^[\'\"]\s*outbind:\/\//si", $attvalue)) {
- $attary{$attname} = sq_cid2http($message, $id, $attvalue, $mailbox);
+ else if ($attname == 'href' || $attname == 'src' || $attname == 'background') {
+ sq_fix_url($attname, $attvalue, $message, $id, $mailbox);
+ $attary{$attname} = $attvalue;
}
-
}
/**
* See if we need to append any attributes to this tag.
return $attary;
}
+/**
+ * This function filters url's
+ *
+ * @param $attvalue String with attribute value to filter
+ * @param $message message object
+ * @param $id message id
+ * @param $mailbox mailbox
+ * @param $sQuote quoting characters around url's
+ */
+function sq_fix_url($attname, &$attvalue, $message, $id, $mailbox,$sQuote = '"') {
+ $attvalue = trim($attvalue);
+ if ($attvalue && ($attvalue[0] =='"'|| $attvalue[0] == "'")) {
+ // remove the double quotes
+ $sQuote = $attvalue[0];
+ $attvalue = trim(substr($attvalue,1,-1));
+ }
+
+ if( !sqgetGlobalVar('view_unsafe_images', $view_unsafe_images, SQ_GET) ) {
+ $view_unsafe_images = false;
+ }
+ $secremoveimg = '../images/' . _("sec_remove_eng.png");
+
+ /**
+ * Replace empty src tags with the blank image. src is only used
+ * for frames, images, and image inputs. Doing a replace should
+ * not affect them working as should be, however it will stop
+ * IE from being kicked off when src for img tags are not set
+ */
+ if ($attvalue == '') {
+ $attvalue = '"' . SM_PATH . 'images/blank.png"';
+ } else {
+ // first, disallow 8 bit characters and control characters
+ if (preg_match('/[\0-\37\200-\377]+/',$attvalue)) {
+ switch ($attname) {
+ case 'href':
+ $attvalue = $sQuote . 'http://invalid-stuff-detected.example.com' . $sQuote;
+ break;
+ default:
+ $attvalue = $sQuote . SM_PATH . 'images/blank.png'. $sQuote;
+ break;
+ }
+ } else {
+ $aUrl = parse_url($attvalue);
+ if (isset($aUrl['scheme'])) {
+ switch(strtolower($aUrl['scheme'])) {
+ case 'mailto':
+ case 'http':
+ case 'https':
+ case 'ftp':
+ if ($attname != 'href') {
+ if ($view_unsafe_images == false) {
+ $attvalue = $sQuote . $secremoveimg . $sQuote;
+ } else {
+ if (isset($aUrl['path'])) {
+ // validate image extension.
+ $ext = strtolower(substr($aUrl['path'],strrpos($aUrl['path'],'.')));
+ if (!in_array($ext,array('.jpeg','.jpg','xjpeg','.gif','.bmp','.jpe','.png','.xbm'))) {
+ $attvalue = $sQuote . SM_PATH . 'images/blank.png'. $sQuote;
+ }
+ } else {
+ $attvalue = $sQuote . SM_PATH . 'images/blank.png'. $sQuote;
+ }
+ }
+ }
+ break;
+ case 'outbind':
+ /**
+ * "Hack" fix for Outlook using propriatary outbind:// protocol in img tags.
+ * One day MS might actually make it match something useful, for now, falling
+ * back to using cid2http, so we can grab the blank.png.
+ */
+ $attvalue = sq_cid2http($message, $id, $attvalue, $mailbox);
+ break;
+ case 'cid':
+ /**
+ * Turn cid: urls into http-friendly ones.
+ */
+ $attvalue = sq_cid2http($message, $id, $attvalue, $mailbox);
+ break;
+ default:
+ $attvalue = $sQuote . SM_PATH . 'images/blank.png' . $sQuote;
+ break;
+ }
+ } else {
+ if (!(isset($aUrl['path']) && $aUrl['path'] == $secremoveimg)) {
+ // parse_url did not lead to satisfying result
+ $attvalue = $sQuote . SM_PATH . 'images/blank.png' . $sQuote;
+ }
+ }
+ }
+ }
+}
+
/**
* This function edits the style definition to make them friendly and
* usable in SquirrelMail.
* @return a string with edited content.
*/
function sq_fixstyle($body, $pos, $message, $id, $mailbox){
- global $view_unsafe_images;
$me = 'sq_fixstyle';
// workaround for </style> in between comments
$iCurrentPos = $pos;
// $content = preg_replace("|url\s*\(\s*([\'\"])\s*\S+script\s*:.*?([\'\"])\s*\)|si",
// "url(\\1$secremoveimg\\2)", $content);
+ // first check for 8bit sequences and disallowed control characters
+ if (preg_match('/[\16-\37\200-\377]+/',$content)) {
+ $content = '<!-- style block removed by html filter due to presence of 8bit characters -->';
+ return array($content, $newpos);
+ }
+
// IE Sucks hard. We have a special function for it.
sq_fixIE_idiocy($content);
// translate ur\l and variations (IE parses that)
// TODO check if the sq_fixIE_idiocy function already handles this.
$content = preg_replace("/(\\\\)?u(\\\\)?r(\\\\)?l(\\\\)?/i", 'url', $content);
- // NB I insert NUL characters to keep to avoid an infinite loop. They are removed after the loop.
- while (preg_match("/url\s*\(\s*[\'\"]?([^:]+):(.*)?[\'\"]?\s*\)/si", $content, $matches)) {
- $sProto = strtolower($matches[1]);
- switch ($sProto) {
- /**
- * Fix url('https*://.*) declarations but only if $view_unsafe_images
- * is false.
- */
- case 'https':
- case 'http':
- if (!$view_unsafe_images){
-
- $sExpr = "/url\s*\(\s*[\'\"]?\s*$sProto*:.*[\'\"]?\s*\)/si";
- $content = preg_replace($sExpr, "u\0r\0l(\\1$secremoveimg\\2)", $content);
-
- } else {
- $content = preg_replace('/url/i',"u\0r\0l",$content);
- }
- break;
- /**
- * Fix urls that refer to cid:
- */
- case 'cid':
- $cidurl = 'cid:'. $matches[2];
- $httpurl = sq_cid2http($message, $id, $cidurl, $mailbox);
- // escape parentheses that can modify the regular expression
- $cidurl = str_replace(array('(',')'),array('\\(','\\)'),$cidurl);
- $content = preg_replace("|url\s*\(\s*$cidurl\s*\)|si",
- "u\0r\0l($httpurl)", $content);
- break;
- default:
- /**
- * replace url with protocol other then the white list
- * http,https and cid by an empty string.
- */
- $content = preg_replace("/url\s*\(\s*[\'\"]?([^:]+):(.*)?[\'\"]?\s*\)/si",
- "", $content);
- break;
+ preg_match_all("/url\s*\((.+)\)/si",$content,$aMatch);
+ if (count($aMatch)) {
+ $aValue = $aReplace = array();
+ foreach($aMatch[1] as $sMatch) {
+ // url value
+ $urlvalue = $sMatch;
+ sq_fix_url('style',$urlvalue, $message, $id, $mailbox,"'");
+ $aValue[] = $sMatch;
+ $aReplace[] = $urlvalue;
}
+ $content = str_replace($aValue,$aReplace,$content);
}
- // remove NUL
- $content = str_replace("\0", "", $content);
+
/**
* Remove any backslashes, entities, and extraneous whitespace.
*/
"idiocy",
"idiocy",
"idiocy",
- "",
+ "idiocy",
"url",
"url(\\1#\\1)",
"url(\\1#\\1)",
$id,
$mailbox
);
- if (preg_match("|$secremoveimg|i", $trusted)){
+ if (strpos($trusted,$secremoveimg)){
$has_unsafe_images = true;
}