X-Git-Url: https://vcs.fsf.org/?p=squirrelmail.git;a=blobdiff_plain;f=functions%2Fmime.php;h=f642133ec7bde39cc671be78ff689cfef6746b68;hp=fc5891aab650bf725712b6f00e54570c6b8588ff;hb=8b096f0a2427cf0019f4dc4433a3e02b9f6f5951;hpb=e842b215cc74203501fe50e52497f9f0329e721b diff --git a/functions/mime.php b/functions/mime.php index fc5891aa..f642133e 100644 --- a/functions/mime.php +++ b/functions/mime.php @@ -10,20 +10,24 @@ * messages. * * $Id$ + * @package squirrelmail */ +/** The typical includes... */ require_once(SM_PATH . 'functions/imap.php'); require_once(SM_PATH . 'functions/attachment_common.php'); -/* --------------------------------------------------------------------------------- */ -/* MIME DECODING */ -/* --------------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* MIME DECODING */ +/* -------------------------------------------------------------------------- */ -/* This function gets the structure of a message and stores it in the "message" class. +/** + * Get the MIME structure + * + * This function gets the structure of a message and stores it in the "message" class. * It will return this object for use with all relevant header information and * fully parsed into the standard "message" object format. */ - function mime_structure ($bodystructure, $flags=array()) { /* Isolate the body structure and remove beginning and end parenthesis. */ @@ -36,11 +40,9 @@ function mime_structure ($bodystructure, $flags=array()) { global $color, $mailbox; /* removed urldecode because $_GET is auto urldecoded ??? */ displayPageHeader( $color, $mailbox ); - echo "\n\n" . - '
'; $errormessage = _("SquirrelMail could not decode the bodystructure of the message"); $errormessage .= '
'._("the provided bodystructure by your imap-server").':

'; - $errormessage .= '
' . htmlspecialchars($read) . '
'; + $errormessage .= '
' . htmlspecialchars($read) . '
'; plain_error_message( $errormessage, $color ); echo ''; exit; @@ -94,17 +96,20 @@ function mime_structure ($bodystructure, $flags=array()) { * to mime_get_elements() */ -function mime_fetch_body($imap_stream, $id, $ent_id=1) { - global $uid_support; +function mime_fetch_body($imap_stream, $id, $ent_id=1, $fetch_size=0) { + global $uid_support; /* Do a bit of error correction. If we couldn't find the entity id, just guess * that it is the first one. That is usually the case anyway. */ + if (!$ent_id) { - $cmd = "FETCH $id BODY[]"; + $cmd = "FETCH $id BODY[]"; } else { - $cmd = "FETCH $id BODY[$ent_id]"; + $cmd = "FETCH $id BODY[$ent_id]"; } + if ($fetch_size!=0) $cmd .= "<0.$fetch_size>"; + $data = sqimap_run_command ($imap_stream, $cmd, true, $response, $message, $uid_support); do { $topline = trim(array_shift($data)); @@ -116,9 +121,9 @@ function mime_fetch_body($imap_stream, $id, $ent_id=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, $uid_support); - } +// if ($ret{0} == '<') { +// $data = sqimap_run_command ($imap_stream, "FETCH $id BODY[$ent_id.MIME]", true, $response, $message, $uid_support); +// } } else if (ereg('"([^"]*)"', $topline, $regs)) { $ret = $regs[1]; } else { @@ -156,7 +161,6 @@ function mime_fetch_body($imap_stream, $id, $ent_id=1) { function mime_print_body_lines ($imap_stream, $id, $ent_id=1, $encoding) { global $uid_support; - $sid = sqimap_session_id($uid_support); /* Don't kill the connection if the browser is over a dialup * and it would take over 30 seconds to download it. * DonĀ“t call set_time_limit in safe mode. @@ -165,14 +169,35 @@ function mime_print_body_lines ($imap_stream, $id, $ent_id=1, $encoding) { if (!ini_get('safe_mode')) { set_time_limit(0); } - if ($uid_support) { - $sid_s = substr($sid,0,strpos($sid, ' ')); + /* in case of base64 encoded attachments, do not buffer them. + Instead, echo the decoded attachment directly to screen */ + if (strtolower($encoding) == 'base64') { + if (!$ent_id) { + $query = "FETCH $id BODY[]"; + } else { + $query = "FETCH $id BODY[$ent_id]"; + } + sqimap_run_command($imap_stream,$query,true,$response,$message,$uid_support,'sqimap_base64_decode','php://stdout',true); } else { - $sid_s = $sid; + $body = mime_fetch_body ($imap_stream, $id, $ent_id); + echo decodeBody($body, $encoding); } - $body = mime_fetch_body ($imap_stream, $id, $ent_id); - echo decodeBody($body, $encoding); + /* + TODO, use the same method for quoted printable. + However, I assume that quoted printable attachments aren't that large + so the performancegain / memory usage drop will be minimal. + If we decide to add that then we need to adapt sqimap_fread because + we need to split te result on \n and fread doesn't stop at \n. That + means we also should provide $results from sqimap_fread (by ref) to + te function and set $no_return to false. The $filter function for + quoted printable should handle unsetting of $results. + */ + /* + TODO 2: find out how we write to the output stream php://stdout. fwrite + doesn't work because 'php://stdout isn't a stream. + */ + return; /* fputs ($imap_stream, "$sid FETCH $id BODY[$ent_id]\r\n"); @@ -320,7 +345,7 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma */ global $startMessage, $username, $key, $imapServerAddress, $imapPort, $show_html_default, $sort, $has_unsafe_images, $passed_ent_id; - global $languages, $squirrelmail_language; + global $languages, $squirrelmail_language; if( !sqgetGlobalVar('view_unsafe_images', $view_unsafe_images, SQ_GET) ) { $view_unsafe_images = false; @@ -368,24 +393,24 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma translateText($body, $wrap_at, $body_message->header->getParameter('charset')); } - $link = 'read_body.php?passed_id=' . $id . '&ent_id='.$ent_num. - '&mailbox=' . $urlmailbox .'&sort=' . $sort . - '&startMessage=' . $startMessage . '&show_more=0'; - if (isset($passed_ent_id)) { - $link .= '&passed_ent_id='.$passed_ent_id; - } + $link = 'read_body.php?passed_id=' . $id . '&ent_id='.$ent_num. + '&mailbox=' . $urlmailbox .'&sort=' . $sort . + '&startMessage=' . $startMessage . '&show_more=0'; + if (isset($passed_ent_id)) { + $link .= '&passed_ent_id='.$passed_ent_id; + } if ($view_unsafe_images) { $text = _("Hide Unsafe Images"); } else { - if (isset($has_unsafe_images) && $has_unsafe_images) { - $link .= '&view_unsafe_images=1'; - $text = _("View Unsafe Images"); - } else { - $text = ''; - } + if (isset($has_unsafe_images) && $has_unsafe_images) { + $link .= '&view_unsafe_images=1'; + $text = _("View Unsafe Images"); + } else { + $text = ''; + } } $body .= '
'.$text. - '

' . "\n"; + '

' . "\n"; } return $body; } @@ -410,26 +435,25 @@ function formatAttachments($message, $exclude_id, $mailbox, $id) { $type1 = strtolower($header->type1); $name = ''; $links['download link']['text'] = _("download"); - $links['download link']['href'] = - "../src/download.php?absolute_dl=true&passed_id=$id&mailbox=$urlMailbox&ent_id=$ent"; + $links['download link']['href'] = SM_PATH . + "src/download.php?absolute_dl=true&passed_id=$id&mailbox=$urlMailbox&ent_id=$ent"; $ImageURL = ''; if ($type0 =='message' && $type1 == 'rfc822') { - $default_page = '../src/read_body.php'; + $default_page = SM_PATH . 'src/read_body.php'; $rfc822_header = $att->rfc822_header; $filename = $rfc822_header->subject; if (trim( $filename ) == '') { $filename = 'untitled-[' . $ent . ']' ; - } + } $from_o = $rfc822_header->from; if (is_object($from_o)) { - $from_name = $from_o->getAddress(false); + $from_name = decodeHeader($from_o->getAddress(false)); } else { $from_name = _("Unknown sender"); } - $from_name = decodeHeader(($from_name)); $description = $from_name; } else { - $default_page = '../src/download.php'; + $default_page = SM_PATH . 'src/download.php'; if (is_object($header->disposition)) { $filename = $header->disposition->getProperty('filename'); if (trim($filename) == '') { @@ -441,24 +465,24 @@ function formatAttachments($message, $exclude_id, $mailbox, $id) { $filename = 'untitled-[' . $ent . ']' ; } else { $filename = 'cid: ' . $header->id; - } + } } else { - $filename = $name; + $filename = $name; } } else { $filename = $name; } } } else { - $filename = $header->getParameter('name'); - if (!trim($filename)) { - if (trim( $header->id ) == '') { - $filename = 'untitled-[' . $ent . ']' ; - } else { - $filename = 'cid: ' . $header->id; - } - } - } + $filename = $header->getParameter('name'); + if (!trim($filename)) { + if (trim( $header->id ) == '') { + $filename = 'untitled-[' . $ent . ']' ; + } else { + $filename = 'cid: ' . $header->id; + } + } + } if ($header->description) { $description = decodeHeader($header->description); } else { @@ -474,7 +498,7 @@ function formatAttachments($message, $exclude_id, $mailbox, $id) { } $defaultlink = $default_page . "?startMessage=$startMessage" . "&passed_id=$id&mailbox=$urlMailbox" - . '&ent_id='.$ent.$passed_ent_id_link.'&absolute_dl=true'; + . '&ent_id='.$ent.$passed_ent_id_link; if ($where && $what) { $defaultlink .= '&where='. urlencode($where).'&what='.urlencode($what); } @@ -515,9 +539,17 @@ function formatAttachments($message, $exclude_id, $mailbox, $id) { unset($links); $attachments .= "\n"; } + $attachmentadd = do_hook_function('attachments_bottom',$attachments); + if ($attachmentadd != '') + $attachments = $attachmentadd; return $attachments; } +function sqimap_base64_decode(&$string) { + $string = str_replace("\r\n", "\n", $string); + $string = base64_decode($string); +} + /* This function decodes the body depending on the encoding type. */ function decodeBody($body, $encoding) { global $show_html_default; @@ -525,7 +557,15 @@ function decodeBody($body, $encoding) { $body = str_replace("\r\n", "\n", $body); $encoding = strtolower($encoding); - if ($encoding == 'quoted-printable' || + $encoding_handler = do_hook_function('decode_body', $encoding); + + + // plugins get first shot at decoding the body + // + if (!empty($encoding_handler) && function_exists($encoding_handler)) { + $body = $encoding_handler('decode', $body); + + } else if ($encoding == 'quoted-printable' || $encoding == 'quoted_printable') { $body = quoted_printable_decode($body); @@ -551,31 +591,52 @@ function decodeHeader ($string, $utfencode=true,$htmlsave=true) { if (is_array($string)) { $string = implode("\n", $string); } - + if (isset($languages[$squirrelmail_language]['XTRA_CODE']) && function_exists($languages[$squirrelmail_language]['XTRA_CODE'])) { $string = $languages[$squirrelmail_language]['XTRA_CODE']('decodeheader', $string); - // Do we need to return at this point? - // return $string; + // Do we need to return at this point? + // return $string; } $i = 0; + $iLastMatch = -2; + $encoded = true; + $aString = explode(' ',$string); + $ret = ''; foreach ($aString as $chunk) { + if ($encoded && $chunk === '') { + continue; + } elseif ($chunk === '') { + $ret .= ' '; + continue; + } $encoded = false; - $aString[$i] = ''; - while (preg_match('/^(.*)=\?([^?]*)\?(Q|B)\?([^?]*)\?=(.*)$/Ui',$chunk,$res)) { - //$aString[$i] .= $res[1]; - //echo "$chunk match ". $res[5] . "
"; + /* if encoded words are not separated by a linear-space-white we still catch them */ + $j = $i-1; +// if ($chunk{0} === '=') { /* performance, saves an unnessecarry preg call */ + while ($match = preg_match('/^(.*)=\?([^?]*)\?(Q|B)\?([^?]*)\?=(.*)$/Ui',$chunk,$res)) { + /* if the last chunk isn't an encoded string then put back the space, otherwise don't */ + if ($iLastMatch !== $j) { + if ($htmlsave) { + $ret .= ' '; + } else { + $ret .= ' '; + } + } + $iLastMatch = $i; + $j = $i; + $ret .= $res[1]; $encoding = ucfirst($res[3]); switch ($encoding) { case 'B': $replace = base64_decode($res[4]); - $aString[$i] .= charset_decode($res[2],$replace); + $ret .= charset_decode($res[2],$replace); break; case 'Q': $replace = str_replace('_', ' ', $res[4]); - $replace = preg_replace('/=([0-9a-f]{2})/ie', 'chr(hexdec("\1"))', + $replace = preg_replace('/=([0-9a-f]{2})/ie', 'chr(hexdec("\1"))', $replace); /* Only encode into entities by default. Some places * don't need the encoding, like the compose form. @@ -587,23 +648,40 @@ function decodeHeader ($string, $utfencode=true,$htmlsave=true) { $replace = htmlspecialchars($replace); } } - $aString[$i] .= $replace; + $ret .= $replace; break; default: break; } $chunk = $res[5]; $encoded = true; - } + } +// } + if (!$encoded) { + if ($htmlsave) { + $ret .= ' '; + } else { + $ret .= ' '; + } + } + if (!$encoded && $htmlsave) { - $aString[$i] = htmlspecialchars($chunk); + $ret .= htmlspecialchars($chunk); } else { - $aString[$i] .= $chunk; + $ret .= $chunk; } ++$i; } - return implode (' ',$aString); + /* remove the first added space */ + if ($ret) { + if ($htmlsave) { + $ret = substr($ret,6); + } else { + $ret = substr($ret,1); + } + } + return $ret; } /* @@ -643,6 +721,7 @@ function encodeHeader ($string) { } $cur_l+=3; if ($cur_l > ($max_l-2)) { + /* if there is an stringpart that doesn't need encoding, add it */ $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); $aRet[] = "=?$default_charset?Q?$ret?="; $iOffset = $i; @@ -656,7 +735,7 @@ function encodeHeader ($string) { case '(': case ')': if ($iEncStart !== false) { - $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); + $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); $aRet[] = "=?$default_charset?Q?$ret?="; $iOffset = $i; $cur_l = 0; @@ -668,13 +747,13 @@ function encodeHeader ($string) { if ($iEncStart !== false) { $cur_l++; if ($cur_l > $max_l) { - $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); + $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); $aRet[] = "=?$default_charset?Q?$ret?="; $iOffset = $i; $cur_l = 0; $ret = ''; $iEncStart = false; - } else { + } else { $ret .= '_'; } } @@ -683,33 +762,37 @@ function encodeHeader ($string) { $k = ord($string{$i}); if ($k > 126) { if ($iEncStart === false) { - $iEncStart = $i; + // do not start encoding in the middle of a string, also take the rest of the word. + $sLeadString = substr($string,0,$i); + $aLeadString = explode(' ',$sLeadString); + $sToBeEncoded = array_pop($aLeadString); + $iEncStart = $i - strlen($sToBeEncoded); + $ret .= $sToBeEncoded; + $cur_l += strlen($sToBeEncoded); } $cur_l += 3; + /* first we add the encoded string that reached it's max size */ if ($cur_l > ($max_l-2)) { - if ($iEncStart !== false) { - $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); - $aRet[] = "=?$default_charset?Q?$ret?="; - } else { - $aRet[] = substr($string,$iOffset,$i-$iOffset); - } + $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); + $aRet[] = "=?$default_charset?Q?$ret?= "; /* the next part is also encoded => separate by space */ $cur_l = 3; $ret = ''; $iOffset = $i; + $iEncStart = $i; } - $enc_init = true; + $enc_init = true; $ret .= sprintf("=%02X", $k); } else { if ($iEncStart !== false) { $cur_l++; if ($cur_l > $max_l) { - $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); + $aRet[] = substr($string,$iOffset,$iEncStart-$iOffset); $aRet[] = "=?$default_charset?Q?$ret?="; $iEncStart = false; $iOffset = $i; $cur_l = 0; $ret = ''; - } else { + } else { $ret .= $string{$i}; } } @@ -739,7 +822,7 @@ function find_ent_id($id, $message) { if (strcasecmp($message->entities[$i]->header->id, $id) == 0) { // if (sq_check_save_extension($message->entities[$i])) { return $message->entities[$i]->entity_id; -// } +// } } } } @@ -758,9 +841,44 @@ function sq_check_save_extension($message) { ** HTMLFILTER ROUTINES */ +/** + * This function is more or less a wrapper around stripslashes. Apparently + * Explorer is stupid enough to just remove the backslashes and then + * execute the content of the attribute as if nothing happened. + * Who does that? + * + * @param attvalue The value of the attribute + * @return attvalue The value of the attribute stripslashed. + */ +function sq_unbackslash($attvalue){ + /** + * Remove any backslashes. See if there are any first. + */ + if (strstr($attvalue, '\\') !== false){ + $attvalue = stripslashes($attvalue); + } + return $attvalue; +} + +/** + * Kill any tabs, newlines, or carriage returns. Our friends the + * makers of the browser with 95% market value decided that it'd + * be funny to make "java[tab]script" be just as good as "javascript". + * + * @param attvalue The attribute value before extraneous spaces removed. + * @return attvalue The attribute value after extraneous spaces removed. + */ +function sq_unspace($attvalue){ + if (strcspn($attvalue, "\t\r\n") != strlen($attvalue)){ + $attvalue = str_replace(Array("\t", "\r", "\n"), Array('', '', ''), + $attvalue); + } + return $attvalue; +} + /** * This function returns the final tag out of the tag name, an array - * of attributes, and the type of the tag. This function is called by + * of attributes, and the type of the tag. This function is called by * sq_sanitize internally. * * @param $tagname the name of the tag. @@ -804,7 +922,7 @@ function sq_casenormalize(&$val){ /** * This function skips any whitespace from the current position within * a string and to the next non-whitespace value. - * + * * @param $body the string * @param $offset the offset within the string where we should start * looking for the next non-whitespace character. @@ -858,8 +976,8 @@ function sq_findnxreg($body, $offset, $reg){ $me = 'sq_findnxreg'; $matches = Array(); $retarr = Array(); - preg_match("%^(.*?)($reg)%s", substr($body, $offset), $matches); - if (!$matches{0}){ + preg_match("%^(.*?)($reg)%si", substr($body, $offset), $matches); + if (!isset($matches{0}) || !$matches{0}){ $retarr = false; } else { $retarr{0} = $offset + strlen($matches{1}); @@ -959,7 +1077,7 @@ function sq_getnxtag($body, $offset){ * '>' indicating the end of the tag entirely. * '\s' indicating the end of the tag name. * '/' indicating that this is type-3 xhtml tag. - * + * * Whatever else we find there indicates an invalid tag. */ switch ($match){ @@ -988,7 +1106,7 @@ function sq_getnxtag($body, $offset){ /** * This is an invalid tag! Look for the next closing ">". */ - $gt = sq_findnxstr($body, $offset, ">"); + $gt = sq_findnxstr($body, $lt, ">"); return Array(false, false, false, $lt, $gt); } break; @@ -1047,7 +1165,7 @@ function sq_getnxtag($body, $offset){ * double quotes. Type 4 we convert into: * attrname="yes". */ - $regary = sq_findnxreg($body, $pos, "[^\w\-_]"); + $regary = sq_findnxreg($body, $pos, "[^:\w\-_]"); if ($regary == false){ /** * Looks like body ended before the end of tag. @@ -1197,7 +1315,7 @@ function sq_deent($attvalue){ for ($asc=1; $asc<256; $asc++){ if (!in_array($asc, $omit)){ $chr = chr($asc); - $attvalue = preg_replace("/\�*$asc;*(\D)/si", "$chr\\1", + $attvalue = preg_replace("/\�*$asc;*(\D)/si", "$chr\\1", $attvalue); $attvalue = preg_replace("/\�*".dechex($asc).";*(\W)/si", "$chr\\1", $attvalue); @@ -1219,8 +1337,8 @@ function sq_deent($attvalue){ * @param $id message id * @return Array with modified attributes. */ -function sq_fixatts($tagname, - $attary, +function sq_fixatts($tagname, + $attary, $rm_attnames, $bad_attvals, $add_attr_to_tag, @@ -1244,9 +1362,11 @@ function sq_fixatts($tagname, } } /** - * Remove any entities. + * Remove any backslashes, entities, and extraneous whitespace. */ + $attvalue = sq_unbackslash($attvalue); $attvalue = sq_deent($attvalue); + $attvalue = sq_unspace($attvalue); /** * Now let's run checks on the attvalues. @@ -1264,7 +1384,7 @@ function sq_fixatts($tagname, * Second one is replacements */ list($valmatch, $valrepl) = $valary; - $newvalue = + $newvalue = preg_replace($valmatch, $valrepl, $attvalue); if ($newvalue != $attvalue){ $attary{$attname} = $newvalue; @@ -1294,15 +1414,21 @@ function sq_fixatts($tagname, /** * This function edits the style definition to make them friendly and * usable in squirrelmail. - * + * * @param $message the message object * @param $id the message id * @param $content a string with whatever is between * @return a string with edited content. */ -function sq_fixstyle($message, $id, $content){ +function sq_fixstyle($body, $pos, $message, $id){ global $view_unsafe_images; $me = 'sq_fixstyle'; + $ret = sq_findnxreg($body, $pos, ''); + if ($ret == FALSE){ + return array(FALSE, strlen($body)); + } + $newpos = $ret[0] + strlen($ret[2]); + $content = $ret[1]; /** * First look for general BODY style declaration, which would be * like so: @@ -1314,25 +1440,25 @@ function sq_fixstyle($message, $id, $content){ /** * Fix url('blah') declarations. */ - $content = preg_replace("|url\(([\'\"])\s*\S+script\s*:.*?([\'\"])\)|si", + $content = preg_replace("|url\s*\(\s*([\'\"])\s*\S+script\s*:.*?([\'\"])\s*\)|si", "url(\\1$secremoveimg\\2)", $content); /** * Fix url('https*://.*) declarations but only if $view_unsafe_images * is false. */ if (!$view_unsafe_images){ - $content = preg_replace("|url\(([\'\"])\s*https*:.*?([\'\"])\)|si", + $content = preg_replace("|url\s*\(\s*([\'\"])\s*https*:.*?([\'\"])\s*\)|si", "url(\\1$secremoveimg\\2)", $content); } - + /** * Fix urls that refer to cid: */ - while (preg_match("|url\(([\'\"]\s*cid:.*?[\'\"])\)|si", $content, - $matches)){ + while (preg_match("|url\s*\(\s*([\'\"]\s*cid:.*?[\'\"])\s*\)|si", + $content, $matches)){ $cidurl = $matches{1}; $httpurl = sq_cid2http($message, $id, $cidurl); - $content = preg_replace("|url\($cidurl\)|si", + $content = preg_replace("|url\s*\(\s*$cidurl\s*\)|si", "url($httpurl)", $content); } @@ -1340,12 +1466,13 @@ function sq_fixstyle($message, $id, $content){ * Fix stupid css declarations which lead to vulnerabilities * in IE. */ - $match = Array('/expression/si', - '/behaviou*r/si', - '/binding/si'); - $replace = Array('idiocy', 'idiocy', 'idiocy'); + $match = Array('/expression/i', + '/behaviou*r/i', + '/binding/i', + '/include-source/i'); + $replace = Array('idiocy', 'idiocy', 'idiocy', 'idiocy'); $content = preg_replace($match, $replace, $content); - return $content; + return array($content, $newpos); } /** @@ -1362,14 +1489,18 @@ function sq_cid2http($message, $id, $cidurl, $mailbox){ * Get rid of quotes. */ $quotchar = substr($cidurl, 0, 1); - $cidurl = str_replace($quotchar, "", $cidurl); + if ($quotchar == '"' || $quotchar == "'"){ + $cidurl = str_replace($quotchar, "", $cidurl); + } else { + $quotchar = ''; + } $cidurl = substr(trim($cidurl), 4); $linkurl = find_ent_id($cidurl, $message); /* in case of non-save cid links $httpurl should be replaced by a sort of unsave link image */ $httpurl = ''; if ($linkurl) { - $httpurl = $quotchar . '../src/download.php?absolute_dl=true&' . + $httpurl = $quotchar . SM_PATH . 'src/download.php?absolute_dl=true&' . "passed_id=$id&mailbox=" . urlencode($mailbox) . '&ent_id=' . $linkurl . $quotchar; } @@ -1380,14 +1511,18 @@ function sq_cid2http($message, $id, $cidurl, $mailbox){ * This function changes the tag into a
tag since we * can't really have a body-within-body. * - * @param $attary an array of attributes and values of - * @return a modified array of attributes to be set for
+ * @param $attary an array of attributes and values of + * @param $mailbox mailbox we're currently reading (for cid2http) + * @param $message current message (for cid2http) + * @param $id current message id (for cid2http) + * @return a modified array of attributes to be set for
*/ -function sq_body2div($attary){ +function sq_body2div($attary, $mailbox, $message, $id){ $me = 'sq_body2div'; $divattary = Array('class' => "'bodyclass'"); $bgcolor = '#ffffff'; $text = '#000000'; + $has_bgc_stl = $has_txt_stl = false; $styledef = ''; if (is_array($attary) && sizeof($attary) > 0){ foreach ($attary as $attname=>$attvalue){ @@ -1395,16 +1530,25 @@ function sq_body2div($attary){ $attvalue = str_replace($quotchar, "", $attvalue); switch ($attname){ case 'background': + $attvalue = sq_cid2http($message, $id, + $attvalue, $mailbox); $styledef .= "background-image: url('$attvalue'); "; break; case 'bgcolor': + $has_bgc_stl = true; $styledef .= "background-color: $attvalue; "; break; case 'text': + $has_txt_stl = true; $styledef .= "color: $attvalue; "; break; } } + // Outlook defines a white bgcolor and no text color. This can lead to + // white text on a white bg with certain themes. + if ($has_bgc_stl && !$has_txt_stl) { + $styledef .= "color: $text; "; + } if (strlen($styledef) > 0){ $divattary{"style"} = "\"$styledef\""; } @@ -1432,8 +1576,8 @@ function sq_body2div($attary){ * @param $id message id * @return sanitized html safe to show on your pages. */ -function sq_sanitize($body, - $tag_list, +function sq_sanitize($body, + $tag_list, $rm_tags_with_content, $self_closing_tags, $force_tag_closing, @@ -1445,10 +1589,11 @@ function sq_sanitize($body, $mailbox ){ $me = 'sq_sanitize'; + $rm_tags = array_shift($tag_list); /** * Normalize rm_tags and rm_tags_with_content. */ - @array_walk($rm_tags, 'sq_casenormalize'); + @array_walk($tag_list, 'sq_casenormalize'); @array_walk($rm_tags_with_content, 'sq_casenormalize'); @array_walk($self_closing_tags, 'sq_casenormalize'); /** @@ -1456,10 +1601,9 @@ function sq_sanitize($body, * false means remove these tags * true means allow these tags */ - $rm_tags = array_shift($tag_list); $curpos = 0; $open_tags = Array(); - $trusted = "\n"; + $trusted = "\n\n"; $skip_content = false; /** * Take care of netscape's stupid javascript entities like @@ -1467,18 +1611,21 @@ function sq_sanitize($body, */ $body = preg_replace("/&(\{.*?\};)/si", "&\\1", $body); - while (($curtag=sq_getnxtag($body, $curpos)) != FALSE){ + while (($curtag = sq_getnxtag($body, $curpos)) != FALSE){ list($tagname, $attary, $tagtype, $lt, $gt) = $curtag; $free_content = substr($body, $curpos, $lt-$curpos); /** * Take care of . Edit the - * content before we apply it. - */ - $free_content = sq_fixstyle($message, $id, $free_content); + if ($tagname == "style" && $tagtype == 1){ + list($free_content, $curpos) = + sq_fixstyle($body, $gt+1, $message, $id); + if ($free_content != FALSE){ + $trusted .= sq_tagprint($tagname, $attary, $tagtype); + $trusted .= $free_content; + $trusted .= sq_tagprint($tagname, false, 2); + } + continue; } if ($skip_content == false){ $trusted .= $free_content; @@ -1495,13 +1642,12 @@ function sq_sanitize($body, if ($skip_content == false){ if ($tagname == "body"){ $tagname = "div"; + } + if (isset($open_tags{$tagname}) && + $open_tags{$tagname} > 0){ + $open_tags{$tagname}--; } else { - if (isset($open_tags{$tagname}) && - $open_tags{$tagname} > 0){ - $open_tags{$tagname}--; - } else { - $tagname = false; - } + $tagname = false; } } } @@ -1516,7 +1662,7 @@ function sq_sanitize($body, */ if ($tagtype == 1 && in_array($tagname, $self_closing_tags)){ - $tagtype=3; + $tagtype = 3; } /** * See if we should skip this tag and any content @@ -1526,12 +1672,20 @@ function sq_sanitize($body, in_array($tagname, $rm_tags_with_content)){ $skip_content = $tagname; } else { - if (($rm_tags == false + if (($rm_tags == false && in_array($tagname, $tag_list)) || ($rm_tags == true && !in_array($tagname, $tag_list))){ $tagname = false; } else { + /** + * Convert body into div. + */ + if ($tagname == "body"){ + $tagname = "div"; + $attary = sq_body2div($attary, $mailbox, + $message, $id); + } if ($tagtype == 1){ if (isset($open_tags{$tagname})){ $open_tags{$tagname}++; @@ -1553,13 +1707,6 @@ function sq_sanitize($body, $mailbox ); } - /** - * Convert body into div. - */ - if ($tagname == "body"){ - $tagname = "div"; - $attary = sq_body2div($attary, $message, $id); - } } } } @@ -1607,14 +1754,18 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX') { "base", "link", "frame", - "iframe" + "iframe", + "plaintext", + "marquee" ); $rm_tags_with_content = Array( "script", "applet", "embed", - "title" + "title", + "frameset", + "xml" ); $self_closing_tags = Array( @@ -1624,15 +1775,16 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX') { "input" ); - $force_tag_closing = false; + $force_tag_closing = true; $rm_attnames = Array( "/.*/" => Array( - "/target/si", - "/^on.*/si", - "/^dynsrc/si", - "/^data.*/si" + "/target/i", + "/^on.*/i", + "/^dynsrc/i", + "/^data.*/i", + "/^lowsrc.*/i" ) ); @@ -1643,7 +1795,6 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX') { "/^src|background/i" => Array( Array( - "|^([\'\"])\s*\.\./.*([\'\"])|si", "/^([\'\"])\s*\S+script\s*:.*([\'\"])/si", "/^([\'\"])\s*mocha\s*:*.*([\'\"])/si", "/^([\'\"])\s*about\s*:.*([\'\"])/si" @@ -1658,43 +1809,43 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX') { "/^href|action/i" => Array( Array( - "|^([\'\"])\s*\.\./.*([\'\"])|si", "/^([\'\"])\s*\S+script\s*:.*([\'\"])/si", "/^([\'\"])\s*mocha\s*:*.*([\'\"])/si", "/^([\'\"])\s*about\s*:.*([\'\"])/si" ), Array( - "\\1#\\2", - "\\1#\\2", - "\\1#\\2", - "\\1#\\2" + "\\1#\\1", + "\\1#\\1", + "\\1#\\1", + "\\1#\\1" ) ), - "/^style/si" => + "/^style/i" => Array( Array( - "/expression/si", - "/binding/si", - "/behaviou*r/si", - "|url\(([\'\"])\s*\.\./.*([\'\"])\)|si", - "/url\(([\'\"])\s*\S+script\s*:.*([\'\"])\)/si", - "/url\(([\'\"])\s*mocha\s*:.*([\'\"])\)/si", - "/url\(([\'\"])\s*about\s*:.*([\'\"])\)/si" + "/expression/i", + "/binding/i", + "/behaviou*r/i", + "/include-source/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" ), Array( "idiocy", "idiocy", "idiocy", - "url(\\1#\\2)", - "url(\\1#\\2)", - "url(\\1#\\2)", - "url(\\1#\\2)" + "idiocy", + "url(\\1#\\1)", + "url(\\1#\\1)", + "url(\\1#\\1)", + "url(\\1#\\1)" ) ) ) ); if( !sqgetGlobalVar('view_unsafe_images', $view_unsafe_images, SQ_GET) ) { - $view_unsafe_images = false; + $view_unsafe_images = false; } if (!$view_unsafe_images){ /** @@ -1704,18 +1855,21 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX') { array_push($bad_attvals{'/.*/'}{'/^src|background/i'}[0], '/^([\'\"])\s*https*:.*([\'\"])/si'); array_push($bad_attvals{'/.*/'}{'/^src|background/i'}[1], - "\\1$secremoveimg\\2"); - array_push($bad_attvals{'/.*/'}{'/^style/si'}[0], + "\\1$secremoveimg\\1"); + array_push($bad_attvals{'/.*/'}{'/^style/i'}[0], '/url\(([\'\"])\s*https*:.*([\'\"])\)/si'); - array_push($bad_attvals{'/.*/'}{'/^style/si'}[1], - "url(\\1$secremoveimg\\2)"); + array_push($bad_attvals{'/.*/'}{'/^style/i'}[1], + "url(\\1$secremoveimg\\1)"); } $add_attr_to_tag = Array( - "/^a$/si" => Array('target'=>'"_new"') - ); - $trusted = sq_sanitize($body, - $tag_list, + "/^a$/i" => + Array('target'=>'"_new"', + 'title'=>'"'._("This external link will open in a new window").'"' + ) + ); + $trusted = sq_sanitize($body, + $tag_list, $rm_tags_with_content, $self_closing_tags, $force_tag_closing, @@ -1726,10 +1880,86 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX') { $id, $mailbox ); - if (preg_match("|$secremoveimg|si", $trusted)){ + if (preg_match("|$secremoveimg|i", $trusted)){ $has_unsafe_images = true; - } + } return $trusted; } -?> \ No newline at end of file +/** + * function SendDownloadHeaders - send file to the browser + * + * Original Source: SM core src/download.php + * moved here to make it available to other code, and separate + * front end from back end functionality. + * + * @param string $type0 first half of mime type + * @param string $type1 second half of mime type + * @param string $filename filename to tell the browser for downloaded file + * @param boolean $force whether to force the download dialog to pop + * @return void + */ + function SendDownloadHeaders($type0, $type1, $filename, $force) { + global $languages, $squirrelmail_language; + $isIE = $isIE6 = 0; + + sqgetGlobalVar('HTTP_USER_AGENT', $HTTP_USER_AGENT, SQ_SERVER); + + if (strstr($HTTP_USER_AGENT, 'compatible; MSIE ') !== false && + strstr($HTTP_USER_AGENT, 'Opera') === false) { + $isIE = 1; + } + + if (strstr($HTTP_USER_AGENT, 'compatible; MSIE 6') !== false && + strstr($HTTP_USER_AGENT, 'Opera') === false) { + $isIE6 = 1; + } + + if (isset($languages[$squirrelmail_language]['XTRA_CODE']) && + function_exists($languages[$squirrelmail_language]['XTRA_CODE'])) { + $filename = + $languages[$squirrelmail_language]['XTRA_CODE']('downloadfilename', $filename, $HTTP_USER_AGENT); + } else { + $filename = ereg_replace('[\\/:\*\?"<>\|;]', '_', str_replace(' ', ' ', $filename)); + } + + // A Pox on Microsoft and it's Office! + if (!$force) { + // Try to show in browser window + header("Content-Disposition: inline; filename=\"$filename\""); + header("Content-Type: $type0/$type1; name=\"$filename\""); + } else { + // Try to pop up the "save as" box + // IE makes this hard. It pops up 2 save boxes, or none. + // http://support.microsoft.com/support/kb/articles/Q238/5/88.ASP + // But, accordint to Microsoft, it is "RFC compliant but doesn't + // take into account some deviations that allowed within the + // specification." Doesn't that mean RFC non-compliant? + // http://support.microsoft.com/support/kb/articles/Q258/4/52.ASP + // + // The best thing you can do for IE is to upgrade to the latest + // version + if ($isIE && !$isIE6) { + // http://support.microsoft.com/support/kb/articles/Q182/3/15.asp + // Do not have quotes around filename, but that applied to + // "attachment"... does it apply to inline too? + // + // This combination seems to work mostly. IE 5.5 SP 1 has + // known issues (see the Microsoft Knowledge Base) + header("Content-Disposition: inline; filename=$filename"); + // This works for most types, but doesn't work with Word files + header("Content-Type: application/download; name=\"$filename\""); + + // These are spares, just in case. :-) + //header("Content-Type: $type0/$type1; name=\"$filename\""); + //header("Content-Type: application/x-msdownload; name=\"$filename\""); + //header("Content-Type: application/octet-stream; name=\"$filename\""); + } else { + header("Content-Disposition: attachment; filename=\"$filename\""); + // application/octet-stream forces download for Netscape + header("Content-Type: application/octet-stream; name=\"$filename\""); + } + } + } + +?>