Rewrote MIME support and made it much, MUCH quicker. All parsing of the
authorlkehresman <lkehresman@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Wed, 21 Jun 2000 16:09:37 +0000 (16:09 +0000)
committerlkehresman <lkehresman@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Wed, 21 Jun 2000 16:09:37 +0000 (16:09 +0000)
messages are now handled on the IMAP server, rather than inside PHP, which
makes it about 20x faster (literally).

git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@551 7612ce4b-ef26-0410-bec9-ea0150e637f0

BUG
ChangeLog
TODO
config/conf.pl
functions/imap_messages.php
functions/mime.php
functions/strings.php
src/download.php
src/read_body.php

diff --git a/BUG b/BUG
index bb7caf6..98d421c 100644 (file)
--- a/BUG
+++ b/BUG
@@ -1,5 +1,11 @@
 Known BUGS to be fixed before 0.5pre1:
 ---------------------TO BE FIXED-----------------------
+   -   Attachments cannot be downloaded with IE.  This report was sent to
+       the mailing list:
+         What i noticed is that if your downlaoding an attachemnt with 
+         squirrel that netscape is ok with it but IE explorer have an 
+         error that he can't download file not found and a weird error 
+         that i can't resolve..
 
 
 
index 8a19bc9..2d1f278 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 Version 0.5pre1 -- DEVELOPMENT
 ------------------------------
+- Rewrote MIME support from scratch, optomizing it an unbelievable amount
 - Added support for message highlighting
 - Moved Address and Send buttons on Compose form for easier access 
 - Added Polish translation from Lukasz Klimek <casa@LO.Pila.PL>
diff --git a/TODO b/TODO
index 3d7ec33..1b7b083 100644 (file)
--- a/TODO
+++ b/TODO
@@ -8,16 +8,12 @@ initials = taken by that person
   -     Configurable headers shown on the message listing, like:  cc, to, etc
   -     Filters
   -     Better inline HTML support including graphics (content-disposition)
-  -     Rewrite of mime.php (see Gustav before any work is done)
-  -     Make Location redirects absolute rather than relative (index.php)
   -     Foreground themes
 (glp)   When deleting or moving messages (empty trash too), go back to the 
             list for the mail box you were looking at without having to click
             a link
 (mcp)   Spell checking
 (ssg)   Search mailbox(es) for given criteria
-(lme)   Background highlighting messages in list based on header criteria (like pine 4.2x)
-(lme)   Fix conf.pl script and add more error checking so it doesn't throw parse errors
 
 
 Finished:
@@ -35,3 +31,6 @@ Finished:
 (nre) (16.4.00)  Cache the sorted messages in mailbox and use that for navigation inside
                  of mailbox (next 25, prev 25, etc.)
 (lme) (21.4.00)  Configure script
+(lme) (18.6.00)  Background highlighting messages in list based on header criteria (like pine 4.2x)
+(lme) (18.6.00)  Fix conf.pl script and add more error checking so it doesn't throw parse errors
+(lme) (21.6.00)  Rewrite of mime.php (see Gustav before any work is done)
index f9dce28..4436710 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 # conf.pl
 # Written March 26, 2000
-# Luke Ehresman (lehresma@css.tayloru.edu)
+# Luke Ehresman (luke@squirrelmail.org)
 #
 # A simple configure script to configure squirrelmail
 ############################################################              
index d7f0166..8e4be59 100755 (executable)
@@ -5,6 +5,8 @@
     **  This implements functions that manipulate messages 
     **/
 
+   if (!$mime_php) include "../functions/mime.php";
+
    /******************************************************************************
     **  Copies specified messages to specified folder
     ******************************************************************************/
     **  the documentation folder for more information about this array.
     ******************************************************************************/
    function sqimap_get_message ($imap_stream, $id, $mailbox) {
-      $message["INFO"]["ID"] = $id;
-      $message["INFO"]["MAILBOX"] = $mailbox;
-      $message["HEADER"] = sqimap_get_message_header($imap_stream, $id);
-      $message["ENTITIES"] = sqimap_get_message_body($imap_stream, $message["HEADER"]["BOUNDARY"], $id, $message["HEADER"]["TYPE0"], $message["HEADER"]["TYPE1"], $message["HEADER"]["ENCODING"], $message["HEADER"]["CHARSET"]);
-      return $message;
+      
+      $header = sqimap_get_message_header($imap_stream, $id, $mailbox);
+      $msg = sqimap_get_message_body($imap_stream, &$header);
+      return $msg;
    }
 
    /******************************************************************************
     **  Wrapper function that reformats the header information.
     ******************************************************************************/
-   function sqimap_get_message_header ($imap_stream, $id) {
+   function sqimap_get_message_header ($imap_stream, $id, $mailbox) {
       fputs ($imap_stream, "a001 FETCH $id:$id BODY[HEADER]\r\n");
       $read = sqimap_read_data ($imap_stream, "a001", true, $response, $message);
      
-      return sqimap_get_header($imap_stream, $read); 
+      $header = sqimap_get_header($imap_stream, $read); 
+      $header->id = $id;
+      $header->mailbox = $mailbox;
+
+      return $header;
    }
 
    /******************************************************************************
     **  Wrapper function that returns entity headers for use by decodeMime
     ******************************************************************************/
+    /*
    function sqimap_get_entity_header ($imap_stream, &$read, &$type0, &$type1, &$bound, &$encoding, &$charset, &$filename) {
       $header = sqimap_get_header($imap_stream, $read);
       $type0 = $header["TYPE0"]; 
       $charset = $header["CHARSET"];
       $filename = $header["FILENAME"];
    }
+    */
 
    /******************************************************************************
     **  Queries the IMAP server and gets all header information.
     ******************************************************************************/
    function sqimap_get_header ($imap_stream, $read) {
+      $hdr = new msg_header();
       $i = 0;
       // Set up some defaults
-      $header["TYPE0"] = "text";
-      $header["TYPE1"] = "plain";
-      $header["CHARSET"] = "us-ascii";
+      $hdr->type0 = "text";
+      $hdr->type1 = "plain";
+      $hdr->charset = "us-ascii";
 
       while ($i < count($read)) {
          if (substr($read[$i], 0, 17) == "MIME-Version: 1.0") {
-            $header["MIME"] = true;
+            $hdr->mime = true;
             $i++;
          }
 
          /** ENCODING TYPE **/
          else if (substr(strtolower($read[$i]), 0, 26) == "content-transfer-encoding:") {
-            $header["ENCODING"] = strtolower(trim(substr($read[$i], 26)));
+            $hdr->encoding = strtolower(trim(substr($read[$i], 26)));
             $i++;
          }
 
 
 
             if (strpos($cont, "/")) {
-               $header["TYPE0"] = substr($cont, 0, strpos($cont, "/"));
-               $header["TYPE1"] = substr($cont, strpos($cont, "/")+1);
+               $hdr->type0 = substr($cont, 0, strpos($cont, "/"));
+               $hdr->type1 = substr($cont, strpos($cont, "/")+1);
             } else {
-               $header["TYPE0"] = $cont;
+               $hdr->type0 = $cont;
             }
 
 
 
             /** Detect the boundary of a multipart message **/
             if (eregi("boundary=\"([^\"]+)\"", $line, $regs)) {                             
-               $header["BOUNDARY"] = $regs[1];                                             
+               $hdr->boundary = $regs[1];                                             
             }
 
             /** Detect the charset **/
                   $charset = substr($charset, $pos);
                }
                $charset = str_replace("\"", "", $charset);
-               $header["CHARSET"] = $charset;
+               $hdr->charset = $charset;
             } else {
-               $header["CHARSET"] = "us-ascii";
+               $hdr->charset = "us-ascii";
             }
 
          }
                   $name = substr($name, $pos);
                }
                $name = str_replace("\"", "", $name);
-               $header["FILENAME"] = $name;
+               $hdr->filename = $name;
             }
          }
 
          /** REPLY-TO **/
          else if (strtolower(substr($read[$i], 0, 9)) == "reply-to:") {
-            $header["REPLYTO"] = trim(substr($read[$i], 9, strlen($read[$i])));
+            $hdr->replyto = trim(substr($read[$i], 9, strlen($read[$i])));
             $i++;
          }
 
          /** FROM **/
          else if (strtolower(substr($read[$i], 0, 5)) == "from:") {
-            $header["FROM"] = trim(substr($read[$i], 5, strlen($read[$i]) - 6));
-            if ($header["REPLYTO"] == "")
-               $header["REPLYTO"] = $header["FROM"];
+            $hdr->from = trim(substr($read[$i], 5, strlen($read[$i]) - 6));
+            if ($hdr->replyto == "")
+               $hdr->replyto = $hdr->from;
             $i++;
          }
          /** DATE **/
             $d = trim($d);
             $d = ereg_replace("  ", " ", $d);
             $d = explode(" ", $d);
-            $header["DATE"] = getTimeStamp($d);
+            $hdr->date = getTimeStamp($d);
             $i++;
          }
          /** SUBJECT **/
          else if (strtolower(substr($read[$i], 0, 8)) == "subject:") {
-            $header["SUBJECT"] = trim(substr($read[$i], 8, strlen($read[$i]) - 9));
-            if (strlen(Chop($header["SUBJECT"])) == 0)
-               $header["SUBJECT"] = _("(no subject)");
+            $hdr->subject = trim(substr($read[$i], 8, strlen($read[$i]) - 9));
+            if (strlen(Chop($hdr->subject)) == 0)
+               $hdr->subject = _("(no subject)");
             $i++;
          }
          /** CC **/
          else if (strtolower(substr($read[$i], 0, 3)) == "cc:") {
             $pos = 0;
-            $header["CC"][$pos] = trim(substr($read[$i], 4));
+            $hdr->cc[$pos] = trim(substr($read[$i], 4));
             $i++;
             while (((substr($read[$i], 0, 1) == " ") || (substr($read[$i], 0, 1) == "\t"))  && (trim($read[$i]) != "")){
                $pos++;
-               $header["CC"][$pos] = trim($read[$i]);
+               $hdr->cc[$pos] = trim($read[$i]);
                $i++;
             }
          }
          /** TO **/
          else if (strtolower(substr($read[$i], 0, 3)) == "to:") {
             $pos = 0;
-            $header["TO"][$pos] = trim(substr($read[$i], 4));
+            $hdr->to[$pos] = trim(substr($read[$i], 4));
             $i++;
             while (((substr($read[$i], 0, 1) == " ") || (substr($read[$i], 0, 1) == "\t"))  && (trim($read[$i]) != "")){
                $pos++;
-               $header["TO"][$pos] = trim($read[$i]);
+               $hdr->to[$pos] = trim($read[$i]);
                $i++;
             }
          }
          /** MESSAGE ID **/
          else if (strtolower(substr($read[$i], 0, 11)) == "message-id:") {
-            $header["MESSAGE-ID"] = trim(substr($read[$i], 11));
+            $hdr->message_id = trim(substr($read[$i], 11));
             $i++;
          }
 
 
          /** ERROR CORRECTION **/
          else if (substr($read[$i], 0, 1) == ")") {
-            if (strlen(trim($header["SUBJECT"])) == 0)
-                $header["SUBJECT"] = _("(no subject)");
+            if (strlen(trim($hdr->subject)) == 0)
+                $hdr->subject = _("(no subject)");
 
-            if (strlen(trim($header["FROM"])) == 0)
-                $header["FROM"] = _("(unknown sender)");
+            if (strlen(trim($hdr->from)) == 0)
+                $hdr->from = _("(unknown sender)");
 
-            if (strlen(trim($header["DATE"])) == 0)
-                $header["DATE"] = time();
+            if (strlen(trim($hdr->date)) == 0)
+                $hdr->date = time();
             $i++;
          }
          else {
             $i++;
          }
       }
-      return $header;
+      return $hdr;
    }
 
 
    /******************************************************************************
     **  Returns the body of a message.
     ******************************************************************************/
-   function sqimap_get_message_body ($imap_stream, $bound, $id, $type0, $type1, $encoding, $charset) {
-      fputs ($imap_stream, "a001 FETCH $id:$id BODY[TEXT]\r\n");
-      $read = sqimap_read_data ($imap_stream, "a001", true, $response, $message);
+   function sqimap_get_message_body ($imap_stream, &$header) {
+      $id = $header->id;
+      //fputs ($imap_stream, "a001 FETCH $id:$id BODY[TEXT]\r\n");
+      //$read = sqimap_read_data ($imap_stream, "a001", true, $response, $message);
        
       $i = 0;
       $j = 0;
       while ($i < count($read)-1) {
          if ( ($i != 0) ) {
-            $bodytmp[$j] = $read[$i];
+            $body[$j] = $read[$i];
             $j++;
          }
          $i++;
       }
-      $body = $bodytmp;
-
-      return decodeMime($body, $bound, $type0, $type1, $encoding, $charset);
+      return decodeMime($body, &$header);
    }
+
+
+   /******************************************************************************
+    **  Returns an array with the body structure 
+    ******************************************************************************/
 ?>
index ef00957..379e77f 100644 (file)
 
    if (!isset($i18n_php))
       include "../functions/i18n.php";
+   if (!isset($imap_php))
+      include "../functions/imap.php";
+   if (!isset($config_php))
+      include "../config/config.php";
+
+
+   /** Setting up the object that has the structure for the message **/
+
+   class msg_header {
+      /** msg_header contains generic variables for values that **/
+      /** could be in a header.                                 **/
+      
+      var $type0, $type1, $boundary, $charset, $encoding;
+      var $to, $from, $date, $cc, $bcc, $reply_to, $subject;
+      var $id, $mailbox;
+      var $entity_id;
+   }
+   
+   class message {
+      /** message is the object that contains messages.  It is a recursive
+          object in that through the $entities variable, it can contain 
+          more objects of type message.  See documentation in mime.txt for
+          a better description of how this works.
+       **/   
+      var $header;
+      var $entities;
+      
+      function addEntity ($msg) {
+         $this->entities[count($this->entities)] = $msg;
+      }
+   }
 
-   /** This is the first function called.  It decides if this is a multipart
-       message or if it should be handled as a single entity
-    **/
-   function decodeMime($body, $bound, $type0, $type1, $encoding, $charset, &$entities) {
-      if ($type0 == "multipart") {
-         $bound = trim($bound);
-         $i = 0;
-         while (($i < count($body)) && (substr($body[$i], 0, strlen("--$bound--")) != "--$bound--")) {
-            if (trim($body[$i]) == "--$bound") {
-               $j = $i+1;
-               $p = 0;
-
-               /** Lets find the header for this entity **/
-               /** If the first line after the boundary is blank, we
-                   use default values **/
-               if (trim($body[$j]) == "") {
-                  $ent_type0 = "text";
-                  $ent_type1 = "plain";
-                  $charset = "us-ascii";
-                  $j++;
-               /** If the first line ISNT blank, read in the header
-                   for this entity **/
-               } else {
-                  while ((substr(trim($body[$j]), 0, strlen("--$bound")) != "--$bound") && (trim($body[$j]) != "")) {
-                     $entity_header[$p] = $body[$j];
-                     $j++;
-                     $p++;
-                  }
-                  /** All of these values are getting passed back to us **/
-                  sqimap_get_entity_header($imapConnection, $entity_header, $ent_type0, $ent_type1, $ent_bound, $encoding, $charset, $filename);
-               }
 
 
-               /** OK, we have the header information, now lets decide
-                   what to do with it **/
-               if ($ent_type0 == "multipart") {
-                  $y = 0;
-                  while (substr($body[$j], 0, strlen("--$bound--")) != "--$bound--") {
-                     $ent_body[$y] = $body[$j];
-                     $y++;
-                     $j++;
-                  }
-                  $ent = decodeMime($ent_body, $ent_bound, $ent_type0, $ent_type1, $charset, $entities);
-                  $entities = $ent;
-               } else {
-                  $j++;
-                  $entity_body = "";
-                  while (substr(trim($body[$j]), 0, strlen("--$bound")) != "--$bound") {
-                     $entity_body .= $body[$j];
-                     $j++;
-                  }
-                  $count = count($entities);
-                  $entities[$count] = getEntity($entity_body, $ent_bound, $ent_type0, $ent_type1, $encoding, $charset, $filename);
-               }
+   /* --------------------------------------------------------------------------------- */
+   /* MIME DECODING                                                                     */
+   /* --------------------------------------------------------------------------------- */
+   
+   /** 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 ($imap_stream, $header) {
+      sqimap_messages_flag ($imap_stream, $header->id, $header->id, "Seen");
+      
+      $id = $header->id;
+      fputs ($imap_stream, "a001 FETCH $id BODYSTRUCTURE\r\n");
+      $read = sqimap_read_data ($imap_stream, "a001", true, $a, $b);
+      $read = strtolower($read[0]);
+
+      //echo $read."<br><br>";
+      // isolate the body structure and remove beginning and end parenthesis
+      $read = trim(substr ($read, strpos($read, "bodystructure") + 13));
+      $read = trim(substr ($read, 0, -2));
+      $read = trim(substr ($read, 1));
+
+      $msg = mime_parse_structure ($read);
+      $msg->header = $header;
+      return $msg;
+   }
+
+   function mime_parse_structure ($structure, $ent_id) {
+      //echo "<font color=008800><tt>START: mime_parse_structure()</tt></font><br>";
+      $msg = new message();
+      if (substr($structure, 0, 1) == "(") {
+         $ent_id = mime_new_element_level($ent_id);
+         $start = $end = -1;
+         do {
+            //echo "<font color=008800><tt>Found entity...</tt></font><br>";
+            $start = $end+1;
+            $end = mime_match_parenthesis ($start, $structure);
+            
+            $element = substr($structure, $start+1, ($end - $start)-1);
+            $ent_id = mime_increment_id($ent_id);
+            $newmsg = mime_parse_structure ($element, $ent_id);
+            $msg->addEntity ($newmsg);
+         } while (substr($structure, $end+1, 1) == "(");
+      } else {
+         // parse the elements
+         //echo "<br><font color=0000aa><tt>$structure</tt></font><br>";
+         $msg->header = new msg_header();
+         $msg->header = mime_get_element (&$structure, $header);
+         $msg->header->entity_id = $ent_id;
+         //echo "<br>";
+      }
+      return $msg;
+      //echo "<font color=008800><tt>&nbsp;&nbsp;END: mime_parse_structure()</tt></font><br>";
+   }
+
+   // Increments the element ID.  An element id can look like any of
+   // the following:  1, 1.2, 4.3.2.4.1, etc.  This function increments
+   // the last number of the element id, changing 1.2 to 1.3.
+   function mime_increment_id ($id) {
+      if (strpos($id, ".")) {
+         $first = substr($id, 0, strrpos($id, "."));
+         $last = substr($id, strlen($id) - strlen($first));
+         $last++;
+         $new = $first . $last;
+      } else {
+         $new = $id + 1;
+      }
+      return $new;
+   }
+
+   // See comment for mime_increment_id().
+   // This adds another level on to the entity_id changing 1.3 to 1.3.0
+   // NOTE:  1.3.0 is not a valid element ID.  It MUST be incremented 
+   //        before it can be used.  I left it this way so as not to have
+   //        to make a special case if it is the first entity_id.  It
+   //        always increments it, and that works fine.
+   function mime_new_element_level ($id) {
+      if (!$id)
+         $id = 0;
+      else 
+         $id . ".0";
+      return $id;   
+   }
+
+   function mime_get_element (&$structure, $header) {
+      $elem_num = 1;
+      
+      while (strlen($structure) > 0) {
+         $structure = trim($structure);
+         $char = substr($structure, 0, 1);
+
+         if (substr($structure, 0, 3) == "nil") {
+            $text = "";
+            $structure = substr($structure, 3);
+         } else if ($char == "\"") {
+            // loop through until we find the matching quote, and return that as a string
+            $pos = 1;
+            $char = substr($structure, $pos, 1);
+            while ($char != "\"" && $pos < strlen($structure)) {
+               $text .= $char;
+               $pos++;
+               $char = substr($structure, $pos, 1);
+            }   
+            $structure = substr($structure, strlen($text) + 2);
+         } else if ($char == "(") {
+            // comment me
+            $end = mime_match_parenthesis (0, $structure);
+            $sub = substr($structure, 1, $end-1);
+            $properties = mime_get_props($properties, $sub);
+            $structure = substr($structure, strlen($sub) + 2);
+         } else {
+            // loop through until we find a space or an end parenthesis
+            $pos = 0;
+            $char = substr($structure, $pos, 1);
+            while ($char != " " && $char != ")" && $pos < strlen($structure)) {
+               $text .= $char;
+               $pos++;
+               $char = substr($structure, $pos, 1);
             }
-            $i++;
+            $structure = substr($structure, strlen($text));
          }
-      } else {
-         /** If this isn't a multipart message **/
-         $j = 0;
-         $entity_body = "";
-         while ($j < count($body)) {
-            $entity_body .= $body[$j];
-            $j++;
+         //echo "$elem_num : $text<br>";
+
+         // This is where all the text parts get put into the header
+         switch ($elem_num) {
+            case 1: 
+               $header->type0 = $text;
+               //echo "<tt>type0 = $text</tt><br>";
+               break;
+            case 2: 
+               $header->type1 = $text;
+               //echo "<tt>type1 = $text</tt><br>";
+               break;
+            case 6:
+               $header->encoding = $text;
+               //echo "<tt>encoding = $text</tt><br>";
+               break;
+            case 7:
+               $header->size = $text;
+               //echo "<tt>size = $text</tt><br>";
+               break;
+            default:
+               if ($header->type0 == "text" && $elem_num == 8) {
+                  $header->num_lines = $text;
+                  //echo "<tt>num_lines = $text</tt><br>";
+               }
+               break;
+         }
+         $elem_num++;
+         $text = "";
+      }
+      // loop through the additional properties and put those in the various headers
+      for ($i=0; $i < count($properties); $i++) {
+         $header->{$properties[$i]["name"]} = $properties[$i]["value"];
+         //echo "<tt>".$properties[$i]["name"]." = " . $properties[$i]["value"] . "</tt><br>";
+      }
+      return $header;
+   }
+
+   // I did most of the MIME stuff yesterday (June 20, 2000), but I couldn't
+   // figure out how to do this part, so I decided to go to bed.  I woke up
+   // in the morning and had a flash of insight.  I went to the white-board
+   // and scribbled it out, then spent a bit programming it, and this is the
+   // result.  Nothing complicated, but I think my brain was fried yesterday.
+   //
+   // This gets properties in a nested parenthesisized list.  For example,
+   // this would get passed something like:  ("attachment" ("filename" "luke.tar.gz"))
+   // This returns an array called $props with all paired up properties.
+   // It ignores the "attachment" for now, maybe that should change later 
+   // down the road.  In this case, what is returned is:
+   //    $props[0]["name"] = "filename";
+   //    $props[0]["value"] = "luke.tar.gz";
+   function mime_get_props ($props, $structure) {
+      while (strlen($structure) > 0) {
+         $structure = trim($structure);
+         $char = substr($structure, 0, 1);
+
+         if ($char == "\"") {
+            $pos = 1;
+            $char = substr($structure, $pos, 1);
+            while ($char != "\"" && $pos < strlen($structure)) {
+               $tmp .= $char;
+               $pos++;
+               $char = substr($structure, $pos, 1);
+            }   
+            $structure = trim(substr($structure, strlen($tmp) + 2));
+            $char = substr($structure, 0, 1);
+
+            if ($char == "\"") {
+               $pos = 1;
+               $char = substr($structure, $pos, 1);
+               while ($char != "\"" && $pos < strlen($structure)) {
+                  $value .= $char;
+                  $pos++;
+                  $char = substr($structure, $pos, 1);
+               }   
+               $structure = trim(substr($structure, strlen($tmp) + 2));
+               
+               $k = count($props);
+               $props[$k]["name"] = $tmp;
+               $props[$k]["value"] = $value;
+            } else if ($char == "(") {
+               $end = mime_match_parenthesis (0, $structure);
+               $sub = substr($structure, 1, $end-1);
+               $props = mime_get_props($props, $sub);
+               $structure = substr($structure, strlen($sub) + 2);
+            }
+            return $props;
+         } else if ($char == "(") {
+            $end = mime_match_parenthesis (0, $structure);
+            $sub = substr($structure, 1, $end-1);
+            $props = mime_get_props($props, $sub);
+            $structure = substr($structure, strlen($sub) + 2);
+         } else {
+            return $props;
          }
+      }
+   }
 
-         $count = count($entities);
-         $entities[$count] = getEntity($entity_body, $bound, $type0, $type1, $encoding, $charset, $filename);
+   //  Matches parenthesis.  It will return the position of the matching
+   //  parenthesis in $structure.  For instance, if $structure was:
+   //     ("text" "plain" ("val1name", "1") nil ... )
+   //     x                                         x
+   //  then this would return 42 to match up those two.
+   function mime_match_parenthesis ($pos, $structure) {
+      $char = substr($structure, $pos, 1); 
+
+      // ignore all extra characters
+      while ($pos < strlen($structure)) {
+         $pos++;
+         $char = substr($structure, $pos, 1); 
+         if ($char == ")") {
+            return $pos;
+         } else if ($char == "(") {
+            $pos = mime_match_parenthesis ($pos, $structure);
+         }
       }
+   }
 
-      return $entities;
+   function mime_fetch_body ($imap_stream, $id, $ent_id) {
+      // 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) $ent_id = 1;
+
+      fputs ($imap_stream, "a001 FETCH $id BODY[$ent_id]\r\n");
+      $read = sqimap_read_data ($imap_stream, "a001", true, $a, $b);
+      for ($i=1; $i < count($read)-1; $i++) {
+         // This fixes a bug in UW.  UW doesn't return what would normall be
+         // expected from the BODY fetch command.  It has an extra line at the
+         // end.  So if the second from the last line is a ), then remove it.
+         if (trim($read[$i]) == ")" && $i == count($read)-2) {
+            continue;
+         }
+         $text .= $read[$i];
+      }
+      return $text;
    }
 
-   /** This gets one entity's properties **/
-   function getEntity($body, $bound, $type0, $type1, $encoding, $charset, $filename) {
-      $msg["TYPE0"] = $type0;
-      $msg["TYPE1"] = $type1;
-      $msg["ENCODING"] = $encoding;
-      $msg["CHARSET"] = $charset;
-      $msg["FILENAME"] = $filename;
+   /* -[ END MIME DECODING ]----------------------------------------------------------- */
 
-      $msg["BODY"] = $body;
 
-      return $msg;
-   }
 
-   /** This will check whether or not the message contains a certain type.  It
-       searches through all the entities for a match.
+   /** This is the first function called.  It decides if this is a multipart
+       message or if it should be handled as a single entity
     **/
-   function containsType($message, $type0, $type1, &$ent_num) {
-      $type0 = strtolower($type0);
-      $type1 = strtolower($type1);
-      for ($i = 0; $i < count($message["ENTITIES"]); $i++) {
-         /** Check only on type0 **/
-         if ( $type1 == "any_type" ) {
-            if ( ($message["ENTITIES"][$i]["TYPE0"] == $type0) ) {
-               $ent_num = $i;
-               return true;
-            }
+   function decodeMime ($body, $header) {
+      global $username, $key, $imapServerAddress, $imapPort;
+      $imap_stream = sqimap_login($username, $key, $imapServerAddress, $imapPort, 0);
+      sqimap_mailbox_select($imap_stream, $header->mailbox);
+
+      return mime_structure ($imap_stream, $header);
+   }
 
-         /** Check on type0 and type1 **/
+   function getEntity ($message, $ent_id) {
+      if ($message) {
+         if ($message->header->entity_id == $ent_id) {
+            return $message;
          } else {
-            if ( ($message["ENTITIES"][$i]["TYPE0"] == $type0) && ($message["ENTITIES"][$i]["TYPE1"] == $type1) ) {
-               $ent_num = $i;
-               return true;
+            for ($i = 0; $message->entities[$i]; $i++) {
+               $msg = getEntity ($message->entities[$i], $ent_id);
+               if ($msg)
+                  return $msg;
             }
-         }
+         }   
+      }
+   }
+
+   function findDisplayEntity ($message) {
+      if ($message) {
+         if ($message->header->type0 == "text") {
+            if ($message->header->type1 == "plain" ||
+                $message->header->type1 == "html") {
+               return $message->header->entity_id; 
+            }
+         } else {
+            for ($i=0; $message->entities[$i]; $i++) {
+               return findDisplayEntity($message->entities[$i]);
+            }   
+         }   
       }
-      return false;
    }
 
    /** This returns a parsed string called $body. That string can then
           primary message. To add more of them, just put them in the
           order that is their priority.
        **/
-      $id = $message["INFO"]["ID"];
-      $urlmailbox = urlencode($message["INFO"]["MAILBOX"]);
-
-      if (containsType($message, "text", "html", $ent_num)) {
-         $body = decodeBody($message["ENTITIES"][$ent_num]["BODY"], $message["ENTITIES"][$ent_num]["ENCODING"]);
-         $charset = $message["ENTITIES"][$ent_num]["CHARSET"];
-      } else if (containsType($message, "text", "plain", $ent_num)) {
-         $body = decodeBody($message["ENTITIES"][$ent_num]["BODY"], $message["ENTITIES"][$ent_num]["ENCODING"]);
-         $charset = $message["ENTITIES"][$ent_num]["CHARSET"];
-      }
-      // add other primary displaying message types here
-      else {
-         // find any type that's displayable
-         if (containsType($message, "text", "any_type", $ent_num)) {
-            $body = decodeBody($message["ENTITIES"][$ent_num]["BODY"], $message["ENTITIES"][$ent_num]["ENCODING"]);
-            $charset = $message["ENTITIES"][$ent_num]["CHARSET"];
-         } else if (containsType($message, "message", "any_type", $ent_num)) {
-            $body = decodeBody($message["ENTITIES"][$ent_num]["BODY"], $message["ENTITIES"][$ent_num]["ENCODING"]);
-            $charset = $message["ENTITIES"][$ent_num]["CHARSET"];
-         }
-      }
+      global $username, $key, $imapServerAddress, $imapPort;
+
+
+      $id = $message->header->id;
+      $urlmailbox = urlencode($message->header->mailbox);
+
+      $imap_stream = sqimap_login($username, $key, $imapServerAddress, $imapPort, 0);
+      sqimap_mailbox_select($imap_stream, $message->header->mailbox);
+
+      $ent_num = findDisplayEntity ($message);
+      $body = mime_fetch_body ($imap_stream, $id, $ent_num); 
 
       /** If there are other types that shouldn't be formatted, add
           them here **/
-      if ($message["ENTITIES"][$ent_num]["TYPE1"] != "html")
+      //if ($->type1 != "html") {   
          $body = translateText($body, $wrap_at, $charset);
-
+      //}   
 
       $body .= "<BR><SMALL><CENTER><A HREF=\"../src/download.php?absolute_dl=true&passed_id=$id&passed_ent_id=$ent_num&mailbox=$urlmailbox\">". _("Download this as a file") ."</A></CENTER><BR></SMALL>";
 
       /** Display the ATTACHMENTS: message if there's more than one part **/
-      if (count($message["ENTITIES"]) > 1) {
+      if ($message->entities) {
          $body .= "<TABLE WIDTH=100% CELLSPACING=0 CELLPADDING=4 BORDER=0><TR><TD BGCOLOR=\"$color[0]\">";
          $body .= "<TT><B>ATTACHMENTS:</B></TT>";
          $body .= "</TD></TR><TR><TD BGCOLOR=\"$color[0]\">";
          $num = 0;
 
-         for ($i = 0; $i < count($message["ENTITIES"]); $i++) {
-            /** If we've displayed this entity, go to the next one **/
-            if ($ent_num == $i)
-               continue;
-
-            $type0 = strtolower($message["ENTITIES"][$i]["TYPE0"]);
-            $type1 = strtolower($message["ENTITIES"][$i]["TYPE1"]);
-
-            $num++;
-            $filename = $message["ENTITIES"][$i]["FILENAME"];
-            if (trim($filename) == "") {
-               $display_filename = "untitled$i";
-            } else {
-               $display_filename = $filename;
-            }
-
-            $urlMailbox = urlencode($message["INFO"]["MAILBOX"]);
-            $id = $message["INFO"]["ID"];
-            $body .= "<TT>&nbsp;&nbsp;&nbsp;<A HREF=\"../src/download.php?passed_id=$id&mailbox=$urlMailbox&passed_ent_id=$i\">" . $display_filename . "</A>&nbsp;&nbsp;<SMALL>(TYPE: $type0/$type1)</SMALL></TT><BR>";
-         }
+         /** make this recurisve at some point **/
+         $body .= formatAttachments ($message, $ent_num, $message->header->mailbox, $id);
          $body .= "</TD></TR></TABLE>";
       }
       return $body;
    }
 
+   // A recursive function that returns a list of attachments with links
+   // to where to download these attachments
+   function formatAttachments ($message, $ent_id, $mailbox, $id) {
+      if ($message) {
+         if (!$message->entities) {
+            $type0 = strtolower($message->header->type0);
+            $type1 = strtolower($message->header->type1);
+            
+            if ($message->header->entity_id != $ent_id) {
+               $filename = $message->header->filename;
+               if (trim($filename) == "") {
+                  $display_filename = "untitled-$ent_id";
+               } else {
+                  $display_filename = $filename;
+               }
+   
+               $urlMailbox = urlencode($mailbox);
+               $ent = urlencode($message->header->entity_id);
+               $body .= "<TT>&nbsp;&nbsp;&nbsp;<A HREF=\"../src/download.php?passed_id=$id&mailbox=$urlMailbox&passed_ent_id=$ent\">" . $display_filename . "</A>&nbsp;&nbsp;<SMALL>(TYPE: $type0/$type1)</SMALL></TT><BR>";
+               $num++;
+            }
+            return $body;
+         } else {
+            for ($i = 0; $i < count($message->entities); $i++) {
+               $body .= formatAttachments ($message->entities[$i], $ent_id, $mailbox, $id);
+            }
+            return $body;
+         }
+      }
+   }
 
 
    /** this function decodes the body depending on the encoding type. **/
index 902cdf8..84663b5 100644 (file)
       return $data;
    }
 
+   // Searches for the next position in a string minus white space
+   function next_pos_minus_white ($haystack, $pos) {
+      while (substr($haystack, $pos, 1) == " " ||
+             substr($haystack, $pos, 1) == "\t" ||
+             substr($haystack, $pos, 1) == "\n" ||
+             substr($haystack, $pos, 1) == "\r") {
+         if ($pos >= strlen($haystack))
+            return -1;
+         $pos++;
+      }        
+      return $pos;        
+   }
+
    // Wraps text at $wrap characters
    function wordWrap($passed, $wrap) {
       $passed = str_replace("&gt;", ">", $passed);
index f57369c..884929f 100644 (file)
    // including header and body
    $message = sqimap_get_message($imapConnection, $passed_id, $mailbox);
 
-   $type0 = $message["ENTITIES"][$passed_ent_id]["TYPE0"];
-   $type1 = $message["ENTITIES"][$passed_ent_id]["TYPE1"];
-   $filename = $message["ENTITIES"][$passed_ent_id]["FILENAME"];
+   // lets redefine message as this particular entity that we wish to display.
+   // it should hold only the header for this entity.  We need to fetch the body
+   // yet before we can display anything.
+   $message = getEntity($message, $passed_ent_id);
+
+   $header = $message->header;
+   $body = mime_fetch_body($imapConnection, $passed_id, $passed_ent_id);
+
+   $type0 = $header->type0;
+   $type1 = $header->type1;
+   $filename = $header->filename;
 
    if (strlen($filename) < 1) {
       $filename = "untitled$passed_ent_id";
    if ($absolute_dl == "true") {
       switch($type0) {
          case "text":
-            $body = decodeBody($message["ENTITIES"][$passed_ent_id]["BODY"], $message["ENTITIES"][$passed_ent_id]["ENCODING"]);
+            $body = decodeBody($body, $header->encoding);
             header("Content-type: $type0/$type1; name=\"$filename\"");
             header("Content-Disposition: attachment; filename=\"$filename\"");
             echo trim($body);
             break;
          default:
-            $body = decodeBody($message["ENTITIES"][$passed_ent_id]["BODY"], $message["ENTITIES"][$passed_ent_id]["ENCODING"]);
+            $body = decodeBody($body, $header->encoding);
             header("Content-type: $type0/$type1; name=\"$filename\"");
             header("Content-Disposition: attachment; filename=\"$filename\"");
             echo $body;
    } else {
       switch ($type0) {
          case "text":
-            $body = decodeBody($message["ENTITIES"][$passed_ent_id]["BODY"], $message["ENTITIES"][$passed_ent_id]["ENCODING"]);
+            $body = decodeBody($body, $header->encoding);
             viewText($color, $body, $passed_id, $passed_ent_id, $mailbox, $type1, $wrap_at);
             break;
          case "message":
-            $body = decodeBody($message["ENTITIES"][$passed_ent_id]["BODY"], $message["ENTITIES"][$passed_ent_id]["ENCODING"]);
+            $body = decodeBody($body, $header->encoding);
             viewText($color, $body, $passed_id, $passed_ent_id, $mailbox, $type1, $wrap_at);
             break;
          default:
-            $body = decodeBody($message["ENTITIES"][$passed_ent_id]["BODY"], $message["ENTITIES"][$passed_ent_id]["ENCODING"]);
+            $body = decodeBody($body, $header->encoding);
             header("Content-type: $type0/$type1; name=\"$filename\"");
             header("Content-Disposition: attachment; filename=\"$filename\"");
             echo $body;
index 08a7d1f..e6e4e10 100644 (file)
    displayPageHeader($color, $mailbox);
 
    /** translate the subject and mailbox into url-able text **/
-   $url_subj = urlencode(trim(stripslashes($message["HEADER"]["SUBJECT"])));
+   $url_subj = urlencode(trim(stripslashes($message->header->subject)));
    $urlMailbox = urlencode($mailbox);
-   $url_replyto = urlencode($message["HEADER"]["REPLYTO"]);
+   $url_replyto = urlencode($message->header->replyto);
 
-   $url_replytoall   = urlencode($message["HEADER"]["REPLYTO"]);
-   $url_replytoallcc = urlencode(getLineOfAddrs($message["HEADER"]["TO"]) . ", " . getLineOfAddrs($message["HEADER"]["CC"]));
+   $url_replytoall   = urlencode($message->header->replyto);
+   $url_replytoallcc = urlencode(getLineOfAddrs($message->header->to) . ", " . getLineOfAddrs($message->header->cc));
 
-   $dateString = getLongDateString($message["HEADER"]["DATE"]);
+   $dateString = getLongDateString($message->header->date);
 
    /** TEXT STRINGS DEFINITIONS **/
    $echo_more = _("more");
@@ -91,7 +91,7 @@
    /** FORMAT THE TO STRING **/
    $i = 0;
    $to_string = "";
-   $to_ary = $message["HEADER"]["TO"];
+   $to_ary = $message->header->to;
    while ($i < count($to_ary)) {
       $to_ary[$i] = htmlspecialchars($to_ary[$i]);
 
    /** FORMAT THE CC STRING **/
    $i = 0;
    $cc_string = "";
-   $cc_ary = $message["HEADER"]["CC"];
+   $cc_ary = $message->header->cc;
    while ($i < count($cc_ary)) {
       $cc_ary[$i] = htmlspecialchars($cc_ary[$i]);
       if ($cc_string)
    }
 
    /** make sure everything will display in HTML format **/
-   $from_name = decodeHeader(htmlspecialchars($message["HEADER"]["FROM"]));
-   $subject = decodeHeader(htmlspecialchars(stripslashes($message["HEADER"]["SUBJECT"])));
+   $from_name = decodeHeader(htmlspecialchars($message->header->from));
+   $subject = decodeHeader(htmlspecialchars(stripslashes($message->header->subject)));
 
    echo "<BR>";
    echo "<TABLE COLS=1 CELLSPACING=0 WIDTH=98% BORDER=0 ALIGN=CENTER CELLPADDING=0>\n";
    echo "         </TD>\n";
    echo "      </TR>\n";
    /** cc **/
-   if ($message["HEADER"]["CC"][0]) {
+   if ($message->header->cc) {
       echo "      <TR>\n";
       echo "         <TD BGCOLOR=\"$color[4]\" WIDTH=15% ALIGN=RIGHT VALIGN=TOP>\n";
       echo "            Cc:\n";