* This contains the functions necessary to detect and decode MIME
* messages.
*
- * @copyright © 1999-2007 The SquirrelMail Project Team
+ * @copyright 1999-2018 The SquirrelMail Project Team
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version $Id$
* @package squirrelmail
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>';
+ $errormessage .= '<pre>' . sm_encode_html_special_chars($read) . '</pre>';
plain_error_message( $errormessage );
echo '</body></html>';
exit;
if (strtolower($flag) == '\\flagged') {
$msg->is_flagged = true;
}
+ else if (strtolower($flag) == '$forwarded') {
+ $msg->is_forwarded = true;
+ }
break;
case 'M':
if (strtolower($flag) == '$mdnsent') {
$data = sqimap_run_command ($imap_stream, $cmd, true, $response, $message, TRUE);
do {
$topline = trim(array_shift($data));
- } while($topline && ($topline[0] == '*') && !preg_match('/\* [0-9]+ FETCH.*/i', $topline)) ;
+ } while($topline && ($topline[0] == '*') && !preg_match('/\* [0-9]+ FETCH .*BODY.*/i', $topline)) ;
+ // Matching with "BODY" above is difficult: in most cases "FETCH \(BODY" would work
+ // but some servers may put other things in the same result, perhaps something such
+ // as "* 23 FETCH (FLAGS (\Seen) BODY[1] {174}". There is some small chance that
+ // if the character sequence "BODY" appears in a response where it isn't actually
+ // a FETCH response data item name, the current regex will break things. The better
+ // way to do this would be to parse the response correctly and not use a regex.
$wholemessage = implode('', $data);
- if (ereg('\\{([^\\}]*)\\}', $topline, $regs)) {
+ if (preg_match('/\{([^\}]*)\}/', $topline, $regs)) {
$ret = substr($wholemessage, 0, $regs[1]);
/* There is some information in the content info header that could be important
* in order to parse html messages. Let's get them here.
// if ($ret{0} == '<') {
// $data = sqimap_run_command ($imap_stream, "FETCH $id BODY[$ent_id.MIME]", true, $response, $message, TRUE);
// }
- } else if (ereg('"([^"]*)"', $topline, $regs)) {
+ } else if (preg_match('/"([^"]*)"/', $topline, $regs)) {
$ret = $regs[1];
} else if ((stristr($topline, 'nil') !== false) && (empty($wholemessage))) {
$ret = $wholemessage;
}
/* This executes the attachment hook with a specific MIME-type.
- * If that doesn't have results, it tries if there's a rule
- * for a more generic type. Finally, a hook for ALL attachment
- * types is run as well.
+ * It also allows plugins to run if there's a rule for a more
+ * generic type. Finally, a hook for ALL attachment types is
+ * run as well.
*/
// First remember the default link.
$defaultlink_orig = $defaultlink;
argument, and arguments are passed by reference, so instead of
returning any changes, changes should simply be made to the original
arguments themselves. */
- $temp = array(&$links, &$startMessage, &$id, &$urlMailbox, &$ent,
+ $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. */
- $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. */
+ $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 $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
$this_attachment['DownloadHREF'] = $links['download link']['href'];
$this_attachment['ViewHREF'] = isset($links['attachment_common']) ? $links['attachment_common']['href'] : '';
$this_attachment['Size'] = $header->size;
- $this_attachment['ContentType'] = htmlspecialchars($type0 .'/'. $type1);
+ $this_attachment['ContentType'] = sm_encode_html_special_chars($type0 .'/'. $type1);
$this_attachment['OtherLinks'] = array();
foreach ($links as $val) {
if ($val['text']==_("Download") || $val['text'] == _("View"))
* all be converted to LF; if "CRLF",
* line endings will all be converted
* to CRLF. If given as an empty value,
- * the global $default_force_crlf will
+ * the global $force_crlf_default will
* be consulted (it can be specified in
* config/config_local.php). Otherwise,
* any other value will cause the string
$iLastMatch = -2;
$encoded = true;
+// FIXME: spaces are allowed inside quoted-printable encoding, but the following line will bust up any such encoded strings
$aString = explode(' ',$string);
$ret = '';
foreach ($aString as $chunk) {
$iLastMatch = $i;
$j = $i;
if ($htmlsafe) {
- $ret .= htmlspecialchars($res[1]);
+ $ret .= sm_encode_html_special_chars($res[1]);
} else {
$ret .= $res[1];
}
}
} else {
if ($htmlsafe) {
- $replace = htmlspecialchars($replace);
+ $replace = sm_encode_html_special_chars($replace);
}
$ret.= $replace;
}
break;
case 'Q':
$replace = str_replace('_', ' ', $res[4]);
- $replace = preg_replace('/=([0-9a-f]{2})/ie', 'chr(hexdec("\1"))',
+ $replace = preg_replace_callback('/=([0-9a-f]{2})/i',
+ create_function ('$matches', 'return chr(hexdec($matches[1]));'),
$replace);
if ($utfencode) {
if ($can_be_encoded) {
}
} else {
if ($htmlsafe) {
- $replace = htmlspecialchars($replace);
+ $replace = sm_encode_html_special_chars($replace);
}
}
$ret .= $replace;
}
if (!$encoded && $htmlsafe) {
- $ret .= htmlspecialchars($chunk);
+ $ret .= sm_encode_html_special_chars($chunk);
} else {
$ret .= $chunk;
}
for($i = 0; $i < $j; ++$i) {
switch($string{$i})
{
+ case '"':
case '=':
case '<':
case '>':
// images off by default.
sqgetGlobalVar('view_unsafe_images', $view_unsafe_images, SQ_GET, FALSE);
- $secremoveimg = '../images/' . _("sec_remove_eng.png");
+ global $use_transparent_security_image;
+ if ($use_transparent_security_image) $secremoveimg = '../images/spacer.png';
+ else $secremoveimg = '../images/' . _("sec_remove_eng.png");
/**
* Replace empty src tags with the blank image. src is only used
break;
}
} else {
- if (!(isset($aUrl['path']) && $aUrl['path'] == $secremoveimg)) {
+ if (!isset($aUrl['path']) || $aUrl['path'] != $secremoveimg) {
// parse_url did not lead to satisfying result
$attvalue = $sQuote . SM_PATH . 'images/blank.png' . $sQuote;
}
* and change it to .bodyclass so we can just assign it to a <div>
*/
$content = preg_replace("|body(\s*\{.*?\})|si", ".bodyclass\\1", $content);
- $secremoveimg = '../images/' . _("sec_remove_eng.png");
+
+ global $use_transparent_security_image;
+ if ($use_transparent_security_image) $secremoveimg = '../images/spacer.png';
+ else $secremoveimg = '../images/' . _("sec_remove_eng.png");
+
/**
* Fix url('blah') declarations.
*/
/**
* Fix stupid css declarations which lead to vulnerabilities
* in IE.
+ *
+ * Also remove "position" attribute, as it can easily be set
+ * to "fixed" or "absolute" with "left" and "top" attributes
+ * of zero, taking over the whole content frame. It can also
+ * be set to relative and move itself anywhere it wants to,
+ * displaying content in areas it shouldn't be allowed to touch.
*/
$match = Array('/\/\*.*\*\//',
'/expression/i',
'/binding/i',
'/include-source/i',
'/javascript/i',
- '/script/i');
- $replace = Array('','idiocy', 'idiocy', 'idiocy', 'idiocy', 'idiocy', 'idiocy');
+ '/script/i',
+ '/position/i');
+ $replace = Array('','idiocy', 'idiocy', 'idiocy', 'idiocy', 'idiocy', 'idiocy', '');
$contentNew = preg_replace($match, $replace, $contentTemp);
if ($contentNew !== $contentTemp) {
// insecure css declarations are used. From now on we don't care
list($free_content, $curpos) =
sq_fixstyle($body, $gt+1, $message, $id, $mailbox);
if ($free_content != FALSE){
+ if ( !empty($attary) ) {
+ $attary = sq_fixatts($tagname,
+ $attary,
+ $rm_attnames,
+ $bad_attvals,
+ $add_attr_to_tag,
+ $message,
+ $id,
+ $mailbox
+ );
+ }
$trusted .= sq_tagprint($tagname, $attary, $tagtype);
$trusted .= $free_content;
$trusted .= sq_tagprint($tagname, false, 2);
)
);
- $secremoveimg = "../images/" . _("sec_remove_eng.png");
+ global $use_transparent_security_image;
+ if ($use_transparent_security_image) $secremoveimg = '../images/spacer.png';
+ else $secremoveimg = '../images/' . _("sec_remove_eng.png");
+
$bad_attvals = Array(
"/.*/" =>
Array(
"/binding/i",
"/behaviou*r/i",
"/include-source/i",
- "/position\s*:\s*absolute/i",
+
+ // position:relative can also be exploited
+ // to put content outside of email body area
+ // and position:fixed is similarly exploitable
+ // as position:absolute, so we'll remove it
+ // altogether....
+ //
+ // Does this screw up legitimate HTML messages?
+ // If so, the only fix I see is to allow position
+ // attributes (any values? I think we still have
+ // to block static and fixed) only if $use_iframe
+ // is enabled (1.5.0+)
+ //
+ // was: "/position\s*:\s*absolute/i",
+ //
+ "/position\s*:/i",
+
"/(\\\\)?u(\\\\)?r(\\\\)?l(\\\\)?/i",
"/url\s*\(\s*([\'\"])\s*\S+script\s*:.*([\'\"])\s*\)/si",
"/url\s*\(\s*([\'\"])\s*mocha\s*:.*([\'\"])\s*\)/si",
"/url\s*\(\s*([\'\"])\s*about\s*:.*([\'\"])\s*\)/si",
- "/(.*)\s*:\s*url\s*\(\s*([\'\"]*)\s*\S+script\s*:.*([\'\"]*)\s*\)/si"
+ "/(.*)\s*:\s*url\s*\(\s*([\'\"]*)\s*\S+script\s*:.*([\'\"]*)\s*\)/si",
),
Array(
"",
if ($take_mailto_links) {
// parseUrl($trusted); // this even parses URLs inside of tags... too aggressive
global $MailTo_PReg_Match;
- $MailTo_PReg_Match = '/mailto:' . substr($MailTo_PReg_Match, 1) ;
+ // some mailers (Microsoft, surprise surprise) produce mailto strings without being
+ // inside an anchor (link) tag, so we have to make sure the regex looks for the
+ // quote before mailto, and we'll also try to convert the non-links back into links
+ $MailTo_PReg_Match = '/([\'"])?mailto:' . substr($MailTo_PReg_Match, 1) ;
if ((preg_match_all($MailTo_PReg_Match, $trusted, $regs)) && ($regs[0][0] != '')) {
foreach ($regs[0] as $i => $mailto_before) {
- $mailto_params = $regs[10][$i];
+ $mailto_params = $regs[11][$i];
+
+ // get rid of any leading quote we may have captured but don't care about
+ //
+ $mailto_before = ltrim($mailto_before, '"\'');
+
// get rid of any tailing quote since we have to add send_to to the end
//
- if (substr($mailto_before, strlen($mailto_before) - 1) == '"')
- $mailto_before = substr($mailto_before, 0, strlen($mailto_before) - 1);
- if (substr($mailto_params, strlen($mailto_params) - 1) == '"')
- $mailto_params = substr($mailto_params, 0, strlen($mailto_params) - 1);
+ $mailto_before = rtrim($mailto_before, '"\'');
+ $mailto_params = rtrim($mailto_params, '"\'');
- if ($regs[1][$i]) { //if there is an email addr before '?', we need to merge it with the params
- $to = 'to=' . $regs[1][$i];
+ if ($regs[2][$i]) { //if there is an email addr before '?', we need to merge it with the params
+ $to = 'to=' . $regs[2][$i];
if (strpos($mailto_params, 'to=') > -1) //already a 'to='
$mailto_params = str_replace('to=', $to . '%2C%20', $mailto_params);
else {
// remove <a href=" and anything after the next quote (we only
// need the uri, not the link HTML) in compose uri
//
- $comp_uri = substr($comp_uri, 9);
- $comp_uri = substr($comp_uri, 0, strpos($comp_uri, '"', 1));
+ // but only do this if the original mailto was in a real anchor tag
+ //
+ if (!empty($regs[1][$i])) {
+ $comp_uri = substr($comp_uri, 9);
+ $comp_uri = substr($comp_uri, 0, strpos($comp_uri, '"', 1));
+ }
$trusted = str_replace($mailto_before, $comp_uri, $trusted);
}
}
$filename =
call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_downloadfilename', $filename, $HTTP_USER_AGENT);
} else {
- $filename = ereg_replace('[\\/:\*\?"<>\|;]', '_', str_replace(' ', ' ', $filename));
+ $filename = preg_replace('/[\\\\\/:*?"<>|;]/', '_', str_replace(' ', ' ', $filename));
}
// A Pox on Microsoft and it's Internet Explorer!
$filename=rawurlencode($filename);
header ("Pragma: public");
header ("Cache-Control: no-store, max-age=0, no-cache, must-revalidate"); // HTTP/1.1
- header ("Cache-Control: post-check=0, pre-check=0", false);
+ // does nothing - see: https://blogs.msdn.microsoft.com/ieinternals/2009/07/20/internet-explorers-cache-control-extensions/
+ // header ("Cache-Control: post-check=0, pre-check=0", false);
header ("Cache-Control: private");
//set the inline header for IE, we'll add the attachment header later if we need it