From 8beafbbc32f58365b2d541d42d6c81ebf8f8f74e Mon Sep 17 00:00:00 2001 From: lkehresman Date: Wed, 21 Jun 2000 16:09:37 +0000 Subject: [PATCH] Rewrote MIME support and made it much, MUCH quicker. All parsing of the 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 | 6 + ChangeLog | 1 + TODO | 7 +- config/conf.pl | 2 +- functions/imap_messages.php | 104 ++++---- functions/mime.php | 496 ++++++++++++++++++++++++++---------- functions/strings.php | 13 + src/download.php | 24 +- src/read_body.php | 20 +- 9 files changed, 469 insertions(+), 204 deletions(-) diff --git a/BUG b/BUG index bb7caf6a..98d421cb 100644 --- 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.. diff --git a/ChangeLog b/ChangeLog index 8a19bc9c..2d1f2781 100644 --- 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 diff --git a/TODO b/TODO index 3d7ec33a..1b7b0831 100644 --- 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) diff --git a/config/conf.pl b/config/conf.pl index f9dce285..4436710d 100755 --- a/config/conf.pl +++ b/config/conf.pl @@ -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 ############################################################ diff --git a/functions/imap_messages.php b/functions/imap_messages.php index d7f01665..8e4be59f 100755 --- a/functions/imap_messages.php +++ b/functions/imap_messages.php @@ -5,6 +5,8 @@ ** This implements functions that manipulate messages **/ + if (!$mime_php) include "../functions/mime.php"; + /****************************************************************************** ** Copies specified messages to specified folder ******************************************************************************/ @@ -101,26 +103,30 @@ ** 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"]; @@ -130,26 +136,28 @@ $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++; } @@ -161,10 +169,10 @@ 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; } @@ -179,7 +187,7 @@ /** Detect the boundary of a multipart message **/ if (eregi("boundary=\"([^\"]+)\"", $line, $regs)) { - $header["BOUNDARY"] = $regs[1]; + $hdr->boundary = $regs[1]; } /** Detect the charset **/ @@ -192,9 +200,9 @@ $charset = substr($charset, $pos); } $charset = str_replace("\"", "", $charset); - $header["CHARSET"] = $charset; + $hdr->charset = $charset; } else { - $header["CHARSET"] = "us-ascii"; + $hdr->charset = "us-ascii"; } } @@ -221,21 +229,21 @@ $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 **/ @@ -244,83 +252,87 @@ $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 + ******************************************************************************/ ?> diff --git a/functions/mime.php b/functions/mime.php index ef00957c..379e77f5 100644 --- a/functions/mime.php +++ b/functions/mime.php @@ -10,116 +10,340 @@ 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."

"; + // 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 "START: mime_parse_structure()
"; + $msg = new message(); + if (substr($structure, 0, 1) == "(") { + $ent_id = mime_new_element_level($ent_id); + $start = $end = -1; + do { + //echo "Found entity...
"; + $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 "
$structure
"; + $msg->header = new msg_header(); + $msg->header = mime_get_element (&$structure, $header); + $msg->header->entity_id = $ent_id; + //echo "
"; + } + return $msg; + //echo "  END: mime_parse_structure()
"; + } + + // 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
"; + + // This is where all the text parts get put into the header + switch ($elem_num) { + case 1: + $header->type0 = $text; + //echo "type0 = $text
"; + break; + case 2: + $header->type1 = $text; + //echo "type1 = $text
"; + break; + case 6: + $header->encoding = $text; + //echo "encoding = $text
"; + break; + case 7: + $header->size = $text; + //echo "size = $text
"; + break; + default: + if ($header->type0 == "text" && $elem_num == 8) { + $header->num_lines = $text; + //echo "num_lines = $text
"; + } + 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 "".$properties[$i]["name"]." = " . $properties[$i]["value"] . "
"; + } + 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 @@ -132,68 +356,70 @@ 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 .= "
". _("Download this as a file") ."

"; /** Display the ATTACHMENTS: message if there's more than one part **/ - if (count($message["ENTITIES"]) > 1) { + if ($message->entities) { $body .= "
"; $body .= "ATTACHMENTS:"; $body .= "
"; $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 .= "   " . $display_filename . "  (TYPE: $type0/$type1)
"; - } + /** make this recurisve at some point **/ + $body .= formatAttachments ($message, $ent_num, $message->header->mailbox, $id); $body .= "
"; } 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 .= "   " . $display_filename . "  (TYPE: $type0/$type1)
"; + $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. **/ diff --git a/functions/strings.php b/functions/strings.php index 902cdf86..84663b53 100644 --- a/functions/strings.php +++ b/functions/strings.php @@ -31,6 +31,19 @@ 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(">", ">", $passed); diff --git a/src/download.php b/src/download.php index f57369cf..884929fe 100644 --- a/src/download.php +++ b/src/download.php @@ -43,9 +43,17 @@ // 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"; @@ -54,13 +62,13 @@ 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; @@ -69,15 +77,15 @@ } 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; diff --git a/src/read_body.php b/src/read_body.php index 08a7d1fa..e6e4e108 100644 --- a/src/read_body.php +++ b/src/read_body.php @@ -75,14 +75,14 @@ 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]); @@ -116,7 +116,7 @@ /** 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) @@ -138,8 +138,8 @@ } /** 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 "
"; echo "\n"; @@ -223,7 +223,7 @@ echo " \n"; echo " \n"; /** cc **/ - if ($message["HEADER"]["CC"][0]) { + if ($message->header->cc) { echo " \n"; echo "
\n"; echo " Cc:\n"; -- 2.25.1