This will probably fix the incorrect parsing of literals inside
[squirrelmail.git] / class / mime / Message.class.php
index fdf01bad4efda14b97ea842e5507557cee4614c2..c41f67097479540619b4466324b88db59111e3bc 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * Message.class.php
  *
- * Copyright (c) 2002 The SquirrelMail Project Team
+ * Copyright (c) 2003 The SquirrelMail Project Team
  * Licensed under the GNU GPL. For full terms see the file COPYING.
  *
  * This contains functions needed to handle mime messages.
@@ -39,7 +39,6 @@ class Message {
     }
 
     function addEntity ($msg) {
-        $msg->parent = &$this;
         $this->entities[] = $msg;
     }
 
@@ -125,35 +124,6 @@ class Message {
         return $msg->mailbox;
     }
 
-    function calcEntity($msg) {
-        if (($this->type0 == 'message') && ($this->type1 == 'rfc822')) {
-            $msg->entity_id = $this->entity_id .'.0'; /* header of message/rfc822 */
-        } else if (isset($this->entity_id) && ($this->entity_id != '')) {
-            $entCount = count($this->entities) + 1;
-            $par_ent = substr($this->entity_id, -2);
-            if ($par_ent{0} == '.') {
-                $par_ent = $par_ent{1};
-            }
-            if ($par_ent == '0') {
-                if ($entCount > 0) {
-                    $ent = substr($this->entity_id, 0, strrpos($this->entity_id, '.'));
-                    $ent = ($ent ? $ent . '.' : '') . $entCount;
-                    $msg->entity_id = $ent;
-                } else {
-                    $msg->entity_id = $entCount;
-                }
-            } else {
-                $ent = $this->entity_id . '.' . $entCount;
-                $msg->entity_id = $ent;
-            }
-        } else {
-            $msg->entity_id = '0';
-        }
-
-        return $msg->entity_id;
-    }
-
-
     /*
      * Bodystructure parser, a recursive function for generating the
      * entity-tree with all the mime-parts.
@@ -166,9 +136,51 @@ class Message {
      * Ask for me (Marc Groot Koerkamp, stekkel@users.sourceforge.net)
      *
      */
-    function parseStructure($read, $i = 0) {
+    function parseStructure($read, &$i, $sub_msg = '') {
+        $msg = Message::parseBodyStructure($read, $i, $sub_msg);
+        if($msg) $msg->setEntIds($msg,false,0);
+        return $msg;
+    }
+    
+    function setEntIds(&$msg,$init=false,$i=0) {
+        $iCnt = count($msg->entities);
+       if ($init !==false) {
+           $iEntSub = $i+1;
+           if ($msg->parent->type0 == 'message' && 
+               $msg->parent->type1 == 'rfc822' &&
+               $msg->type0 == 'multipart') {
+               $iEntSub = '0';
+           }
+           if ($init) {
+               $msg->entity_id = "$init.$iEntSub";
+           } else {
+               $msg->entity_id = $iEntSub;
+           }
+       } else if ($iCnt) {
+           $msg->entity_id='0';
+       } else {
+           $msg->entity_id='1';
+       }
+        for ($i=0;$i<$iCnt;++$i) {
+           $msg->entities[$i]->parent =& $msg; 
+            if (strrchr($msg->entity_id, '.') != '.0') {       
+               $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->entity_id,$i);
+           } else {
+               $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->parent->entity_id,$i);
+           }
+           
+       }
+    }
+
+    function parseBodyStructure($read, &$i, $sub_msg = '') {
         $arg_no = 0;
         $arg_a  = array();
+       if ($sub_msg) {
+           $message = $sub_msg;
+       } else {
+           $message = new Message();
+       }
+       $this = $message;    
         for ($cnt = strlen($read); $i < $cnt; ++$i) {
             $char = strtoupper($read{$i});
             switch ($char) {
@@ -181,14 +193,11 @@ class Message {
                                 $hdr->type0 = 'text';
                                 $hdr->type1 = 'plain';
                                 $hdr->encoding = 'us-ascii';
-                                $msg->entity_id = $this->calcEntity($msg);
                             } else {
                                 $msg->header->type0 = 'multipart';
                                 $msg->type0 = 'multipart';
                                 while ($read{$i} == '(') {
-                                    $res = $msg->parseStructure($read, $i);
-                                    $i = $res[1];
-                                    $msg->addEntity($res[0]);
+                                    $msg->addEntity($this->parseBodyStructure($read, $i, $msg));
                                 }
                             }
                             break;
@@ -201,16 +210,16 @@ class Message {
                         case 2:
                             if (isset($msg->type0) && ($msg->type0 == 'multipart')) {
                                 ++$i;
-                                $arg_a[] = $msg->parseDisposition($read, $i);
+                                $arg_a[] = $this->parseDisposition($read, $i);
                             } else { /* properties */
-                                $arg_a[] = $msg->parseProperties($read, $i);
+                                $arg_a[] = $this->parseProperties($read, $i);
                             }
                             ++$arg_no;
                             break;
                         case 3:
                             if (isset($msg->type0) && ($msg->type0 == 'multipart')) {
                                 ++$i;
-                                $arg_a[]= $msg->parseLanguage($read, $i);
+                                $arg_a[]= $this->parseLanguage($read, $i);
                             }
                         case 7:
                             if (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822')) {
@@ -219,42 +228,40 @@ class Message {
                                 $msg->type0 = $arg_a[0];
                                 $msg->type1 = $arg_a[1];
                                 $rfc822_hdr = new Rfc822Header();
-                                $msg->rfc822_header = $msg->parseEnvelope($read, $i, $rfc822_hdr);
+                                $msg->rfc822_header = $this->parseEnvelope($read, $i, $rfc822_hdr);
                                 while (($i < $cnt) && ($read{$i} != '(')) {
                                     ++$i;
                                 }
-                                $res = $msg->parseStructure($read, $i);
-                                $i = $res[1];
-                                $msg->addEntity($res[0]);
+                                $msg->addEntity($this->parseBodyStructure($read, $i,$msg));
                             }
                             break;
                         case 8:
                             ++$i;
-                            $arg_a[] = $msg->parseDisposition($read, $i);
+                            $arg_a[] = $this->parseDisposition($read, $i);
                             ++$arg_no;
                             break;
                         case 9:
                             ++$i;
                             if (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) {
-                                $arg_a[] = $msg->parseDisposition($read, $i);
+                                $arg_a[] = $this->parseDisposition($read, $i);
                             } else {
-                                $arg_a[] = $msg->parseLanguage($read, $i);
+                                $arg_a[] = $this->parseLanguage($read, $i);
                             }
                             ++$arg_no;
                             break;
                        case 10:
                            if (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) {
                                ++$i;
-                               $arg_a[] = $msg->parseLanguage($read, $i);
+                               $arg_a[] = $this->parseLanguage($read, $i);
                            } else {
-                               $i = $msg->parseParenthesis($read, $i);
+                               $i = $this->parseParenthesis($read, $i);
                                $arg_a[] = ''; /* not yet described in rfc2060 */
                            }
                            ++$arg_no;
                            break;
                        default:
                            /* unknown argument, skip this part */
-                           $i = $msg->parseParenthesis($read, $i);
+                           $i = $this->parseParenthesis($read, $i);
                            $arg_a[] = '';
                            ++$arg_no;
                            break;
@@ -262,8 +269,7 @@ class Message {
                    break;
                 case '"':
                     /* inside an entity -> start processing */
-                    $debug = substr($read, $i, 20);
-                    $arg_s = $msg->parseQuote($read, $i);
+                    $arg_s = $this->parseQuote($read, $i);
                     ++$arg_no;
                     if ($arg_no < 3) {
                         $arg_s = strtolower($arg_s); /* type0 and type1 */
@@ -273,7 +279,8 @@ class Message {
                 case 'n':
                 case 'N':
                     /* probably NIL argument */
-                    if (strtoupper(substr($read, $i, 4)) == 'NIL ') {
+                    $tmpnil = strtoupper(substr($read, $i, 4));
+                    if ($tmpnil == 'NIL ' || $tmpnil == 'NIL)') {
                         $arg_a[] = '';
                         ++$arg_no;
                         $i += 2;
@@ -281,18 +288,20 @@ class Message {
                     break;
                 case '{':
                     /* process the literal value */
-                    $arg_s = $msg->parseLiteral($read, $i);
+                    $arg_s = $this->parseLiteral($read, $i);
                     ++$arg_no;
                     break;
+               case '0':    
                 case is_numeric($read{$i}):
                     /* process integers */
                     if ($read{$i} == ' ') { break; }
-                    $arg_s = $read{$i};;
-                    for (++$i; preg_match('/^[0-9]{1}$/', $read{$i}); ++$i) {
-                        $arg_s .= $read{$i};
-                    }
-                    ++$arg_no;
-                    $arg_a[] = $arg_s;
+                   ++$arg_no;
+                   if (preg_match('/^([0-9]+).*/',substr($read,$i), $regs)) {
+                       $i += strlen($regs[1])-1;
+                       $arg_a[] = $regs[1];
+                   } else {
+                       $arg_a[] = 0;
+                   }
                     break;
                 case ')':
                     $multipart = (isset($msg->type0) && ($msg->type0 == 'multipart'));
@@ -322,9 +331,6 @@ class Message {
                         $hdr->disposition = (isset($arg_a[8+$s]) ? $arg_a[8+$s] : $hdr->disposition);
                         $hdr->language = (isset($arg_a[9+$s]) ? $arg_a[9+$s] : $hdr->language);
                         $msg->header = $hdr;
-                        if ((strrchr($msg->entity_id, '.') == '.0') && ($msg->type0 !='multipart')) {
-                           $msg->entity_id = $this->entity_id . '.1';
-                        }
                     } else {
                         $hdr->type0 = 'multipart';
                         $hdr->type1 = $arg_a[0];
@@ -335,11 +341,9 @@ class Message {
                         $hdr->language = (isset($arg_a[3]) ? $arg_a[3] : $hdr->language);
                         $msg->header = $hdr;
                     }
-                    ++$i;
-                    return (array($msg, $i));
+                    return $msg;
                 default: break;
             } /* switch */
-
         } /* for */
     } /* parsestructure */
 
@@ -371,9 +375,8 @@ class Message {
     function parseEnvelope($read, &$i, $hdr) {
         $arg_no = 0;
         $arg_a = array();
-
+        ++$i;
         for ($cnt = strlen($read); ($i < $cnt) && ($read{$i} != ')'); ++$i) {
-            ++$i;
             $char = strtoupper($read{$i});
             switch ($char) {
                 case '"':
@@ -382,6 +385,9 @@ class Message {
                     break;
                 case '{':
                     $arg_a[] = $this->parseLiteral($read, $i);
+                   /* temp bugfix (SM 1.5 will have a working clean version)
+                      too much work to implement that version right now */
+//                 --$i;
                     ++$arg_no;
                     break;
                 case 'N':
@@ -431,15 +437,12 @@ class Message {
         }
 
         if (count($arg_a) > 9) {
-            /* argument 1: date */
             $d = strtr($arg_a[0], array('  ' => ' '));
             $d = explode(' ', $d);
-            $hdr->date = getTimeStamp($d);
-
-            /* argument 2: subject */
-            $arg_a[1] = (!trim($arg_a[1]) ? _("(no subject)") : $arg_a[1]);
-            $hdr->subject = $arg_a[1];
+           if (!$arg_a[1]) $arg_1[1] = _("(no subject)");          
 
+            $hdr->date = getTimeStamp($d); /* argument 1: date */
+            $hdr->subject = $arg_a[1];     /* argument 2: subject */
             $hdr->from = $arg_a[2][0];     /* argument 3: from        */
             $hdr->sender = $arg_a[3][0];   /* argument 4: sender      */
             $hdr->replyto = $arg_a[4][0];  /* argument 5: reply-to    */
@@ -454,39 +457,46 @@ class Message {
 
     function parseLiteral($read, &$i) {
         $lit_cnt = '';
-        for (++$i; $read{$i} != '}'; ++$i) {
-            $lit_cnt .= $read{$i};
-        }
-
-        $lit_cnt +=2; /* add the { and } characters */
-        $s = '';
-        for ($j = 0; $j < $lit_cnt; ++$j) {
-            $s .= $read{++$i};
-        }
-        return (array($s, $i));
+       ++$i;
+       $iPos = strpos($read,'}',$i);
+       if ($iPos) {
+          $lit_cnt = substr($read, $i, $iPos - $i);
+          $i += strlen($lit_cnt) + 3; /* skip } + \r + \n */
+          /* Now read the literal */
+          $s = ($lit_cnt ? substr($read,$i,$lit_cnt): '');
+          $i += $lit_cnt;
+          /* temp bugfix (SM 1.5 will have a working clean version)
+             too much work to implement that version right now */
+          --$i;
+       } else { /* should never happen */
+          $i += 3; /* } + \r + \n */
+          $s = '';
+       }
+        return $s;
     }
 
     function parseQuote($read, &$i) {
         $s = '';
-        for (++$i; $read{$i} != '"'; ++$i) {
-            if ($read{$i} == '\\') {
-                ++$i;
-             }
-             $s .= $read{$i};
-        }
+       $iPos = ++$i;
+       while (true) {
+          $iPos = strpos($read,'"',$iPos);
+          if ($iPos === false) break;
+          if ($iPos && $read{$iPos -1} != '\\') {
+             $s = substr($read,$i,($iPos-$i));
+             $i = $iPos;
+             break;
+          }
+       }
         return $s;
     }
 
     function parseAddress($read, &$i) {
         $arg_a = array();
-
         for (; $read{$i} != ')'; ++$i) {
             $char = strtoupper($read{$i});
             switch ($char) {
-                case '"':
-                case '{':
-                    $arg_a[] = ($char == '"' ? $this->parseQuote($read, $i) : $this->parseLiteral($read, $i));
-                    break;
+                case '"': $arg_a[] = $this->parseQuote($read, $i); break;
+                case '{': $arg_a[] = $this->parseLiteral($read, $i); break;
                 case 'n':
                 case 'N':
                     if (strtoupper(substr($read, $i, 3)) == 'NIL') {
@@ -669,6 +679,7 @@ class Message {
             }
         } else { /* If not multipart, then just compare with each entry from $alt_order */
             $type = $this->type0.'/'.$this->type1;
+//         $alt_order[] = "message/rfc822";
             foreach ($alt_order as $alt) {
                 if( ($alt == $type) && isset($this->entity_id) ) {
                     if ((count($this->entities) == 0) && 
@@ -687,6 +698,7 @@ class Message {
                     $found = true;
                 }
             }
+           
         }
         if(!$strict && !$found) {
             if (($this->type0 == 'text') &&
@@ -699,7 +711,6 @@ class Message {
                 }
             }
         }
-
         return $entity;
     }
 
@@ -740,14 +751,16 @@ class Message {
     }
 
     function getAttachments($exclude_id=array(), $result = array()) {
-        if (($this->type0 == 'message') && ($this->type1 == 'rfc822')) {
+/*
+        if (($this->type0 == 'message') && 
+           ($this->type1 == 'rfc822') &&
+           ($this->entity_id) ) {
             $this = $this->entities[0];
         }
-
+*/
         if (count($this->entities)) {
             foreach ($this->entities as $entity) {
                 $exclude = false;
-
                 foreach ($exclude_id as $excl) {
                     if ($entity->entity_id === $excl) {
                         $exclude = true;
@@ -773,7 +786,6 @@ class Message {
                 $result[] = $this;
             }
         }
-
         return $result;
     }