Happy New Year
[squirrelmail.git] / functions / mime.php
index 38a058c43e92cb58f02bb1f625e34a1f69b66d2c..27c0e00846b72973e20f7a3835846073596f45eb 100644 (file)
@@ -6,7 +6,7 @@
  * This contains the functions necessary to detect and decode MIME
  * messages.
  *
- * @copyright 1999-2021 The SquirrelMail Project Team
+ * @copyright 1999-2024 The SquirrelMail Project Team
  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
@@ -71,7 +71,7 @@ function mime_structure ($bodystructure, $flags=array()) {
     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});
+            $char = strtoupper($flag[1]);
             switch ($char) {
                 case 'S':
                     if (strtolower($flag) == '\\seen') {
@@ -151,7 +151,7 @@ function mime_fetch_body($imap_stream, $id, $ent_id=1, $fetch_size=0) {
         /* 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} == '<') {
+//        if ($ret[0] == '<') {
 //            $data = sqimap_run_command ($imap_stream, "FETCH $id BODY[$ent_id.MIME]", true, $response, $message, TRUE);
 //        }
     } else if (preg_match('/"([^"]*)"/', $topline, $regs)) {
@@ -190,7 +190,8 @@ function mime_fetch_body($imap_stream, $id, $ent_id=1, $fetch_size=0) {
     return $ret;
 }
 
-function mime_print_body_lines ($imap_stream, $id, $ent_id=1, $encoding, $rStream='php://stdout', $force_crlf='') {
+// TODO: Needs documentation.  $ent_id default is usually 1
+function mime_print_body_lines ($imap_stream, $id, $ent_id, $encoding, $rStream='php://stdout', $force_crlf='') {
 
     /* Don't kill the connection if the browser is over a dialup
      * and it would take over 30 seconds to download it.
@@ -395,18 +396,38 @@ function formatBody($imap_stream, $message, $color, $wrap_at, $ent_num, $id, $ma
          */
 
         if ($body_message->header->type1 == 'html') {
-            if ($show_html_default <> 1) {
+            // Do we need to make an HTML part viewable as non-HTML plain text?
+            if ($show_html_default != 1) {
                 $entity_conv = array('&nbsp;' => ' ',
-                                     '<p>'    => "\n",
-                                     '<P>'    => "\n",
-                                     '<br>'   => "\n",
-                                     '<BR>'   => "\n",
-                                     '<br />' => "\n",
-                                     '<BR />' => "\n",
+                                     // These are better done by regex (below)
+                                     // '<p>'    => "\n",
+                                     // '<P>'    => "\n",
+                                     // '<br>'   => "\n",
+                                     // '<BR>'   => "\n",
+                                     // '<br />' => "\n",
+                                     // '<BR />' => "\n",
+                                     // '<tr>'   => "\n",
+                                     // '<div>'  => "\n",
                                      '&gt;'   => '>',
-                                     '&lt;'   => '<');
+                                     '&lt;'   => '<',
+                                     '&amp;'   => '&',
+                                     '&copy;'   => '©');
+                // first, completely remove <style> tags as they aren't useful in this context
+                $body = preg_replace('/<style.*>.*<\/style.*>/isU', '', $body);
+                // emulate how newlines are treated as spaces in HTML
+                $body = preg_replace('/(\r|\n)+/', ' ', $body);
+                // now replace the tags listed just above
                 $body = strtr($body, $entity_conv);
+                // <p>, <br>, <tr> and <div> are best replaced by a newline
+                $body = preg_replace('/<(p|br|tr|div).*>/isU', "\n", $body);
+                // remove the rest of the HTML tags
                 $body = strip_tags($body);
+                // condense multiple spaces into one
+                $body = preg_replace('/[ \t]+/', ' ', $body);
+                // trim each line
+                $body = preg_replace('/ *\n */', "\n", $body);
+                // allow maximum two newlines
+                $body = preg_replace('/\n\n\n+/', "\n\n", $body);
                 $body = trim($body);
                 translateText($body, $wrap_at,
                         $body_message->header->getParameter('charset'));
@@ -902,8 +923,11 @@ function decodeHeader ($string, $utfencode=true,$htmlsafe=true,$decide=false) {
                 case 'Q':
                     $replace = str_replace('_', ' ', $res[4]);
                     $replace = preg_replace_callback('/=([0-9a-f]{2})/i',
-                            create_function ('$matches', 'return chr(hexdec($matches[1]));'),
-                            $replace);
+                                             (check_php_version(5, 3, 0)
+                                              ? function($matches) { return chr(hexdec($matches[1])); }
+                                              : create_function ('$matches', 'return chr(hexdec($matches[1]));')
+                                             ),
+                                             $replace);
                     if ($utfencode) {
                         if ($can_be_encoded) {
                             /* convert string to different charset,
@@ -1001,7 +1025,7 @@ function encodeHeader ($string) {
     $iEncStart = $enc_init = false;
     $cur_l = $iOffset = 0;
     for($i = 0; $i < $j; ++$i) {
-        switch($string{$i})
+        switch($string[$i])
         {
             case '"':
             case '=':
@@ -1023,7 +1047,7 @@ function encodeHeader ($string) {
                     $ret = '';
                     $iEncStart = false;
                 } else {
-                    $ret .= sprintf("=%02X",ord($string{$i}));
+                    $ret .= sprintf("=%02X",ord($string[$i]));
                 }
                 break;
             case '(':
@@ -1053,7 +1077,7 @@ function encodeHeader ($string) {
                 }
                 break;
             default:
-                $k = ord($string{$i});
+                $k = ord($string[$i]);
                 if ($k > 126) {
                     if ($iEncStart === false) {
                         // do not start encoding in the middle of a string, also take the rest of the word.
@@ -1087,7 +1111,7 @@ function encodeHeader ($string) {
                             $cur_l = 0;
                             $ret = '';
                         } else {
-                            $ret .= $string{$i};
+                            $ret .= $string[$i];
                         }
                     }
                 }
@@ -1463,12 +1487,12 @@ function sq_findnxreg($body, $offset, $reg){
     $matches = Array();
     $retarr = Array();
     preg_match("%^(.*?)($reg)%si", substr($body, $offset), $matches);
-    if (!isset($matches{0}) || !$matches{0}){
+    if (!isset($matches[0]) || !$matches[0]){
         $retarr = false;
     } else {
-        $retarr{0} = $offset + strlen($matches{1});
-        $retarr{1} = $matches{1};
-        $retarr{2} = $matches{2};
+        $retarr[0] = $offset + strlen($matches[1]);
+        $retarr[1] = $matches[1];
+        $retarr[2] = $matches[2];
     }
     return $retarr;
 }
@@ -1626,8 +1650,8 @@ function sq_getnxtag($body, $offset){
             /**
              * Yep. So we did.
              */
-            $pos += strlen($matches{1});
-            if ($matches{2} == "/>"){
+            $pos += strlen($matches[1]);
+            if ($matches[2] == "/>"){
                 $tagtype = 3;
                 $pos++;
             }
@@ -1684,7 +1708,7 @@ function sq_getnxtag($body, $offset){
                     return $retary;
                 }
             case '>':
-                $attary{$attname} = '"yes"';
+                $attary[$attname] = '"yes"';
                 return Array($tagname, $attary, $tagtype, $lt, $pos);
                 break;
             default:
@@ -1718,7 +1742,7 @@ function sq_getnxtag($body, $offset){
                         }
                         list($pos, $attval, $match) = $regary;
                         $pos++;
-                        $attary{$attname} = "'" . $attval . "'";
+                        $attary[$attname] = "'" . $attval . "'";
                     } else if ($quot == '"'){
                         $regary = sq_findnxreg($body, $pos+1, '\"');
                         if ($regary == false){
@@ -1726,7 +1750,7 @@ function sq_getnxtag($body, $offset){
                         }
                         list($pos, $attval, $match) = $regary;
                         $pos++;
-                        $attary{$attname} = '"' . $attval . '"';
+                        $attary[$attname] = '"' . $attval . '"';
                     } else {
                         /**
                          * These are hateful. Look for \s, or >.
@@ -1740,13 +1764,13 @@ function sq_getnxtag($body, $offset){
                          * If it's ">" it will be caught at the top.
                          */
                         $attval = preg_replace("/\"/s", "&quot;", $attval);
-                        $attary{$attname} = '"' . $attval . '"';
+                        $attary[$attname] = '"' . $attval . '"';
                     }
                 } else if (preg_match("|[\w/>]|", $char)) {
                     /**
                      * That was attribute type 4.
                      */
-                    $attary{$attname} = '"yes"';
+                    $attary[$attname] = '"yes"';
                 } else {
                     /**
                      * An illegal character. Find next '>' and return.
@@ -1785,7 +1809,7 @@ function sq_deent(&$attvalue, $regex, $hex=false){
             if ($hex){
                 $numval = hexdec($numval);
             }
-            $repl{$matches[0][$i]} = chr($numval);
+            $repl[$matches[0][$i]] = chr($numval);
         }
         $attvalue = strtr($attvalue, $repl);
         return true;
@@ -1824,7 +1848,7 @@ function sq_fixatts($tagname,
             if (preg_match($matchtag, $tagname)){
                 foreach ($matchattrs as $matchattr){
                     if (preg_match($matchattr, $attname)){
-                        unset($attary{$attname});
+                        unset($attary[$attname]);
                         continue;
                     }
                 }
@@ -1845,7 +1869,7 @@ function sq_fixatts($tagname,
             // entities are used in the attribute value. In 99% of the cases it's there as XSS
             // i.e.<div style="{ left:exp&#x0280;essio&#x0274;( alert('XSS') ) }">
             $attvalue = "idiocy";
-            $attary{$attname} = $attvalue;
+            $attary[$attname] = $attvalue;
         }
         sq_unspace($attvalue);
 
@@ -1868,7 +1892,7 @@ function sq_fixatts($tagname,
                         $newvalue =
                             preg_replace($valmatch, $valrepl, $attvalue);
                         if ($newvalue != $attvalue){
-                            $attary{$attname} = $newvalue;
+                            $attary[$attname] = $newvalue;
                             $attvalue = $newvalue;
                         }
                     }
@@ -1878,7 +1902,7 @@ function sq_fixatts($tagname,
         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"';
+                $attary[$attname] = '"disallowed character"';
             }
             preg_match_all("/url\s*\((.+)\)/si",$attvalue,$aMatch);
             if (count($aMatch)) {
@@ -1886,7 +1910,7 @@ function sq_fixatts($tagname,
                     // url value
                     $urlvalue = $sMatch;
                     sq_fix_url($attname, $urlvalue, $message, $id, $mailbox,"'");
-                    $attary{$attname} = str_replace($sMatch,$urlvalue,$attvalue);
+                    $attary[$attname] = str_replace($sMatch,$urlvalue,$attvalue);
                 }
             }
         }
@@ -1897,7 +1921,7 @@ function sq_fixatts($tagname,
               || $attname == 'poster' || $attname == 'formaction'
               || $attname == 'background' || $attname == 'action') {
             sq_fix_url($attname, $attvalue, $message, $id, $mailbox);
-            $attary{$attname} = $attvalue;
+            $attary[$attname] = $attvalue;
         }
     }
     /**
@@ -2083,7 +2107,7 @@ function sq_fixstyle($body, $pos, $message, $id, $mailbox){
     $bSucces = false;
     $bEndTag = false;
     for ($i=$pos,$iCount=strlen($body);$i<$iCount;++$i) {
-        $char = $body{$i};
+        $char = $body[$i];
         switch ($char) {
             case '<':
                 $sToken = $char;
@@ -2114,7 +2138,7 @@ function sq_fixstyle($body, $pos, $message, $id, $mailbox){
             case '!':
                 if ($sToken == '<') {
                     // possible comment
-                    if (isset($body{$i+2}) && substr($body,$i,3) == '!--') {
+                    if (isset($body[$i+2]) && substr($body,$i,3) == '!--') {
                         $i = strpos($body,'-->',$i+3);
                         if ($i === false) { // no end comment
                             $i = strlen($body);
@@ -2337,7 +2361,7 @@ function sq_body2div($attary, $mailbox, $message, $id){
             $styledef .= "color: $text; ";
         }
         if (strlen($styledef) > 0){
-            $divattary{"style"} = "\"$styledef\"";
+            $divattary["style"] = "\"$styledef\"";
         }
     }
     return $divattary;
@@ -2488,9 +2512,9 @@ function sq_sanitize($body,
                         if ($tagname == "body"){
                             $tagname = "div";
                         }
-                        if (isset($open_tags{$tagname}) &&
-                                $open_tags{$tagname} > 0){
-                            $open_tags{$tagname}--;
+                        if (isset($open_tags[$tagname]) &&
+                                $open_tags[$tagname] > 0){
+                            $open_tags[$tagname]--;
                         } else {
                             $tagname = false;
                         }
@@ -2532,10 +2556,10 @@ function sq_sanitize($body,
                                         $message, $id);
                             }
                             if ($tagtype == 1){
-                                if (isset($open_tags{$tagname})){
-                                    $open_tags{$tagname}++;
+                                if (isset($open_tags[$tagname])){
+                                    $open_tags[$tagname]++;
                                 } else {
-                                    $open_tags{$tagname}=1;
+                                    $open_tags[$tagname]=1;
                                 }
                             }
                             /**
@@ -2757,13 +2781,13 @@ function magicHTML($body, $id, $message, $mailbox = 'INBOX', $take_mailto_links
          * Remove any references to http/https if view_unsafe_images set
          * to false.
          */
-        array_push($bad_attvals{'/.*/'}{'/^src|background/i'}[0],
+        array_push($bad_attvals['/.*/']['/^src|background/i'][0],
                 '/^([\'\"])\s*https*:.*([\'\"])/si');
-        array_push($bad_attvals{'/.*/'}{'/^src|background/i'}[1],
+        array_push($bad_attvals['/.*/']['/^src|background/i'][1],
                 "\\1$secremoveimg\\1");
-        array_push($bad_attvals{'/.*/'}{'/^style/i'}[0],
+        array_push($bad_attvals['/.*/']['/^style/i'][0],
                 '/url\([\'\"]?https?:[^\)]*[\'\"]?\)/si');
-        array_push($bad_attvals{'/.*/'}{'/^style/i'}[1],
+        array_push($bad_attvals['/.*/']['/^style/i'][1],
                 "url(\\1$secremoveimg\\1)");
     }