minor correction
[squirrelmail.git] / functions / mime.php
index 1427ab37b50efbc28f72942e8cb478f3459322c8..1d7f0321136923d0039f953174bf4180e9dceb7a 100644 (file)
@@ -6,17 +6,38 @@
  * This contains the functions necessary to detect and decode MIME
  * messages.
  *
- * @copyright © 1999-2005 The SquirrelMail Project Team
+ * @copyright © 1999-2006 The SquirrelMail Project Team
  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
  */
 
-/** The typical includes... */
-require_once(SM_PATH . 'functions/imap.php');
-require_once(SM_PATH . 'functions/attachment_common.php');
-/** add sqm_baseuri()*/
-include_once(SM_PATH . 'functions/display_messages.php');
+/**
+ * dependency information
+   functions       dependency
+   mime_structure
+        class/mime/Message.class.php
+            Message::parseStructure
+        functions/page_header.php
+            displayPageHeader
+        functions/display_messages.php
+            plain_error_message
+   mime_fetch_body
+        functions/imap_general.php
+            sqimap_run_command
+   mime_print_body_lines
+
+
+
+functions/imap.php
+functions/attachment_common.php
+functions/display_messages.php
+
+magicHtml => url_parser
+translateText => url_parser
+
+*/
+
 
 /* -------------------------------------------------------------------------- */
 /* MIME DECODING                                                              */
@@ -37,7 +58,6 @@ function mime_structure ($bodystructure, $flags=array()) {
     $i = 0;
     $msg = Message::parseStructure($read,$i);
     if (!is_object($msg)) {
-        include_once(SM_PATH . 'functions/display_messages.php');
         global $color, $mailbox;
         /* removed urldecode because $_GET is auto urldecoded ??? */
         displayPageHeader( $color, $mailbox );
@@ -264,7 +284,7 @@ function translateText(&$body, $wrap_at, $charset) {
     global $where, $what;   /* from searching */
     global $color;          /* color theme */
 
-    require_once(SM_PATH . 'functions/url_parser.php');
+    // require_once(SM_PATH . 'functions/url_parser.php');
 
     $body_ary = explode("\n", $body);
     for ($i=0; $i < count($body_ary); $i++) {
@@ -334,7 +354,8 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma
      * order that is their priority.
      */
     global $startMessage, $languages, $squirrelmail_language,
-           $show_html_default, $sort, $has_unsafe_images, $passed_ent_id, $use_iframe,$iframe_height;
+           $show_html_default, $sort, $has_unsafe_images, $passed_ent_id,
+           $use_iframe, $iframe_height, $download_and_unsafe_link;
 
     // workaround for not updated config.php
     if (! isset($use_iframe)) $use_iframe = false;
@@ -387,7 +408,7 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma
                  * If we don't add html message between iframe tags,
                  * we must detect unsafe images and modify $has_unsafe_images.
                  */
-                $html_body = magicHTML($body, $id, $message, $mailbox); 
+                $html_body = magicHTML($body, $id, $message, $mailbox);
                 // Convert character set in order to display html mails in different character set
                 $html_body = charset_decode($body_message->header->getParameter('charset'),$html_body,false,true);
 
@@ -429,10 +450,10 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma
                 // old way of html rendering
                 $body = magicHTML($body, $id, $message, $mailbox);
                 /**
-                 * convert character set. charset_decode does not remove html special chars 
+                 * 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. 
-                 */ 
+                 * fourth argument is true.
+                 */
                 $body = charset_decode($body_message->header->getParameter('charset'),$body,false,true);
             }
         } else {
@@ -445,13 +466,15 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma
             return $body;
         }
 
+        $download_and_unsafe_link = '';
+
         $link = 'passed_id=' . $id . '&amp;ent_id='.$ent_num.
             '&amp;mailbox=' . $urlmailbox .'&amp;sort=' . $sort .
             '&amp;startMessage=' . $startMessage . '&amp;show_more=0';
         if (isset($passed_ent_id)) {
             $link .= '&amp;passed_ent_id='.$passed_ent_id;
         }
-        $body .= '<center><small><a href="download.php?absolute_dl=true&amp;' .
+        $download_and_unsafe_link .= '&nbsp;|&nbsp;<a href="download.php?absolute_dl=true&amp;' .
             $link . '">' . _("Download this as a file") .  '</a>';
         if ($view_unsafe_images) {
             $text = _("Hide Unsafe Images");
@@ -464,9 +487,8 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma
             }
         }
         if($text != '') {
-            $body .= '&nbsp;|&nbsp;<a href="read_body.php?' . $link . '">' . $text . '</a>';
+            $download_and_unsafe_link .= '&nbsp;|&nbsp;<a href="read_body.php?' . $link . '">' . $text . '</a>';
         }
-        $body .= '</small></center><br />' . "\n";
     }
     return $body;
 }
@@ -487,7 +509,7 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma
  * @return string html formated attachment information.
  */
 function formatAttachments($message, $exclude_id, $mailbox, $id) {
-    global $where, $what, $startMessage, $color, $passed_ent_id;
+    global $where, $what, $startMessage, $color, $passed_ent_id, $base_uri;
 
     $att_ar = $message->getAttachments($exclude_id);
 
@@ -504,10 +526,10 @@ function formatAttachments($message, $exclude_id, $mailbox, $id) {
         $type1 = strtolower($header->type1);
         $name = '';
         $links['download link']['text'] = _("Download");
-        $links['download link']['href'] = sqm_baseuri() .
+        $links['download link']['href'] = $base_uri .
             "src/download.php?absolute_dl=true&amp;passed_id=$id&amp;mailbox=$urlMailbox&amp;ent_id=$ent";
         if ($type0 =='message' && $type1 == 'rfc822') {
-            $default_page = sqm_baseuri() . 'src/read_body.php';
+            $default_page = $base_uri  . 'src/read_body.php';
             $rfc822_header = $att->rfc822_header;
             $filename = $rfc822_header->subject;
             if (trim( $filename ) == '') {
@@ -516,12 +538,19 @@ function formatAttachments($message, $exclude_id, $mailbox, $id) {
             $from_o = $rfc822_header->from;
             if (is_object($from_o)) {
                 $from_name = decodeHeader($from_o->getAddress(false));
+            } elseif (is_array($from_o) && count($from_o) && is_object($from_o[0])) {
+                // something weird happens when a digest message is opened and you return to the digest
+                // now the from object is part of an array. Probably the parseHeader call overwrites the info
+                // retrieved from the bodystructure in a different way. We need to fix this later.
+                // possible starting point, do not fetch header we already have and inspect how
+                // the rfc822_header object behaves.
+                $from_name = decodeHeader($from_o[0]->getAddress(false));
             } else {
                 $from_name = _("Unknown sender");
             }
             $description = $from_name;
         } else {
-            $default_page = sqm_baseuri() . 'src/download.php';
+            $default_page = $base_uri  . 'src/download.php';
             $filename = $att->getFilename();
             if ($header->description) {
                 $description = decodeHeader($header->description);
@@ -642,7 +671,7 @@ function decodeBody($body, $encoding) {
         /**
          * quoted_printable_decode() function is broken in older
          * php versions. Text with \r\n decoding was fixed only
-         * in php 4.3.0. Minimal code requirement 4.0.4 + 
+         * in php 4.3.0. Minimal code requirement 4.0.4 +
          * str_replace("\r\n", "\n", $body); call.
          */
         $body = quoted_printable_decode($body);
@@ -730,7 +759,7 @@ function decodeHeader ($string, $utfencode=true,$htmlsave=true,$decide=false) {
                             /* convert string to different charset,
                              * if functions asks for it (usually in compose)
                              */
-                            $ret .= charset_convert($res[2],$replace,$default_charset);
+                            $ret .= charset_convert($res[2],$replace,$default_charset,$htmlsave);
                         } else {
                             // convert string to html codes in order to display it
                             $ret .= charset_decode($res[2],$replace);
@@ -751,7 +780,7 @@ function decodeHeader ($string, $utfencode=true,$htmlsave=true,$decide=false) {
                             /* convert string to different charset,
                              * if functions asks for it (usually in compose)
                              */
-                            $replace = charset_convert($res[2], $replace,$default_charset);
+                            $replace = charset_convert($res[2], $replace,$default_charset,$htmlsave);
                         } else {
                             // convert string to html codes in order to display it
                             $replace = charset_decode($res[2], $replace);
@@ -801,13 +830,13 @@ function decodeHeader ($string, $utfencode=true,$htmlsave=true,$decide=false) {
  *
  * Function uses XTRA_CODE _encodeheader function, if such function exists.
  *
- * Function uses Q encoding by default and encodes a string according to RFC 
- * 1522 for use in headers if it contains 8-bit characters or anything that 
+ * Function uses Q encoding by default and encodes a string according to RFC
+ * 1522 for use in headers if it contains 8-bit characters or anything that
  * looks like it should be encoded.
  *
- * Function switches to B encoding and encodeHeaderBase64() function, if 
- * string is 8bit and multibyte character set supported by mbstring extension 
- * is used. It can cause E_USER_NOTICE errors, if interface is used with 
+ * Function switches to B encoding and encodeHeaderBase64() function, if
+ * string is 8bit and multibyte character set supported by mbstring extension
+ * is used. It can cause E_USER_NOTICE errors, if interface is used with
  * multibyte character set unsupported by mbstring extension.
  *
  * @param string $string header string, that has to be encoded
@@ -951,27 +980,27 @@ function encodeHeader ($string) {
 /**
  * Encodes string according to rfc2047 B encoding header formating rules
  *
- * It is recommended way to encode headers with character sets that store 
+ * It is recommended way to encode headers with character sets that store
  * symbols in more than one byte.
  *
  * Function requires mbstring support. If required mbstring functions are missing,
  * function returns false and sets E_USER_WARNING level error message.
  *
- * Minimal requirements - php 4.0.6 with mbstring extension. Please note, 
- * that mbstring functions will generate E_WARNING errors, if unsupported 
+ * Minimal requirements - php 4.0.6 with mbstring extension. Please note,
+ * that mbstring functions will generate E_WARNING errors, if unsupported
  * character set is used. mb_encode_mimeheader function provided by php
  * mbstring extension is not used in order to get better control of header
  * encoding.
  *
- * Used php code functions - function_exists(), trigger_error(), strlen() 
- * (is used with charset names and base64 strings). Used php mbstring 
+ * Used php code functions - function_exists(), trigger_error(), strlen()
+ * (is used with charset names and base64 strings). Used php mbstring
  * functions - mb_strlen and mb_substr.
  *
- * Related documents: rfc 2045 (BASE64 encoding), rfc 2047 (mime header 
+ * Related documents: rfc 2045 (BASE64 encoding), rfc 2047 (mime header
  * encoding), rfc 2822 (header folding)
  *
  * @param string $string header string that must be encoded
- * @param string $charset character set. Must be supported by mbstring extension. 
+ * @param string $charset character set. Must be supported by mbstring extension.
  * Use sq_mb_list_encodings() to detect supported charsets.
  * @return string string encoded according to rfc2047 B encoding formating rules
  * @since 1.5.1
@@ -1692,44 +1721,50 @@ function sq_fixstyle($body, $pos, $message, $id, $mailbox){
     //                           "url(\\1$secremoveimg\\2)", $content);
     // remove NUL
     $content = str_replace("\0", "", $content);
+    // translate ur\l and variations (IE parses that)
+    $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);
-            }
-            break;
-          /**
-           * Fix urls that refer to cid:
-           */
-          case 'cid':
-            $cidurl = 'cid:'. $matches[2];
-            $httpurl = sq_cid2http($message, $id, $cidurl, $mailbox);
-            $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.
+             * Fix url('https*://.*) declarations but only if $view_unsafe_images
+             * is false.
              */
-            $content = preg_replace("/url\s*\(\s*[\'\"]?([^:]+):(.*)?[\'\"]?\s*\)/si",
-                                "", $content);
-            break;
+            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;
         }
-        break;
     }
     // remove NUL
     $content = str_replace("\0", "", $content);
-
    /**
     * Remove any backslashes, entities, and extraneous whitespace.
     */
@@ -1741,11 +1776,12 @@ function sq_fixstyle($body, $pos, $message, $id, $mailbox){
      * Fix stupid css declarations which lead to vulnerabilities
      * in IE.
      */
-    $match   = Array('/expression/i',
+    $match   = Array('/\/\*.*\*\//',
+                    '/expression/i',
                     '/behaviou*r/i',
                     '/binding/i',
                     '/include-source/i');
-    $replace = Array('idiocy', 'idiocy', 'idiocy', 'idiocy');
+    $replace = Array('','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
@@ -2057,7 +2093,7 @@ function sq_sanitize($body,
  */
 function magicHTML($body, $id, $message, $mailbox = 'INBOX', $take_mailto_links =true) {
 
-    require_once(SM_PATH . 'functions/url_parser.php');  // for $MailTo_PReg_Match
+    // require_once(SM_PATH . 'functions/url_parser.php');  // for $MailTo_PReg_Match
 
     global $attachment_common_show_images, $view_unsafe_images,
            $has_unsafe_images;
@@ -2143,22 +2179,26 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX', $take_mailto_links
         "/^style/i" =>
             Array(
                 Array(
+                    "/\/\*.*\*\//",
                     "/expression/i",
                     "/binding/i",
                     "/behaviou*r/i",
                     "/include-source/i",
                     "/position\s*:\s*absolute/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"
                     ),
                 Array(
+                    "",
                     "idiocy",
                     "idiocy",
                     "idiocy",
                     "idiocy",
                     "",
+                    "url",
                     "url(\\1#\\1)",
                     "url(\\1#\\1)",
                     "url(\\1#\\1)",
@@ -2215,7 +2255,7 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX', $take_mailto_links
     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);
+        $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];
@@ -2278,18 +2318,18 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX', $take_mailto_links
  */
 function SendDownloadHeaders($type0, $type1, $filename, $force, $filesize=0) {
     global $languages, $squirrelmail_language;
-    $isIE = $isIE6 = 0;
+    $isIE = $isIE6plus = false;
 
     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;
+        $isIE = true;
     }
 
-    if (strstr($HTTP_USER_AGENT, 'compatible; MSIE 6') !== false &&
-            strstr($HTTP_USER_AGENT, 'Opera') === false) {
-        $isIE6 = 1;
+    if (preg_match('/compatible; MSIE ([0-9]+)/', $HTTP_USER_AGENT, $match) &&
+        ((int)$match[1]) >= 6 && strstr($HTTP_USER_AGENT, 'Opera') === false) {
+        $isIE6plus = true;
     }
 
     if (isset($languages[$squirrelmail_language]['XTRA_CODE']) &&
@@ -2346,7 +2386,7 @@ function SendDownloadHeaders($type0, $type1, $filename, $force, $filesize=0) {
         // "attachment"... does it apply to inline too?
         header ("Content-Disposition: attachment; filename=\"$filename\"");
 
-        if ($isIE && !$isIE6) {
+        if ($isIE && !$isIE6plus) {
             // This combination seems to work mostly.  IE 5.5 SP 1 has
             // known issues (see the Microsoft Knowledge Base)
 
@@ -2369,5 +2409,3 @@ function SendDownloadHeaders($type0, $type1, $filename, $force, $filesize=0) {
     }
 
 }  // end fn SendDownloadHeaders
-
-?>
\ No newline at end of file