X-Git-Url: https://vcs.fsf.org/?p=squirrelmail.git;a=blobdiff_plain;f=class%2Fmime%2FMessage.class.php;h=8677c92b971d02bb6e257d295f3a428567c8eb95;hp=cc12be4dbe9550111a63932a25433909ecfed814;hb=609f416c1a66ef8cbe1377a0860614e4f0ddec53;hpb=19d470aa121cacdd916319ec6b55af180562805e diff --git a/class/mime/Message.class.php b/class/mime/Message.class.php index cc12be4d..8677c92b 100644 --- a/class/mime/Message.class.php +++ b/class/mime/Message.class.php @@ -3,64 +3,201 @@ /** * Message.class.php * - * Copyright (c) 2002 The SquirrelMail Project Team - * Licensed under the GNU GPL. For full terms see the file COPYING. + * This file contains functions needed to handle mime messages. * - * This contains functions needed to handle mime messages. - * - * $Id$ + * @copyright © 2003-2007 The SquirrelMail Project Team + * @license http://opensource.org/licenses/gpl-license.php GNU Public License + * @version $Id$ + * @package squirrelmail + * @subpackage mime + * @since 1.3.2 */ +/** + * The object that contains a 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. + * @package squirrelmail + * @subpackage mime + * @since 1.3.0 + */ 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 $rfc822_header = '', - $mime_header = '', - $flags = '', - $type0='', - $type1='', - $entities = array(), - $parent_ent, $entity, - $parent = '', $decoded_body='', - $is_seen = 0, $is_answered = 0, $is_deleted = 0, $is_flagged = 0, - $is_mdnsent = 0, - $body_part = '', - $offset = 0, /* for fetching body parts out of raw messages */ - $length = 0, /* for fetching body parts out of raw messages */ - $att_local_name = ''; /* location where the tempory attachment - is stored. For future usage in smtp.php */ + /** + * rfc822header object + * @var object + */ + var $rfc822_header = ''; + /** + * MessageHeader object + * @var object + */ + var $mime_header = ''; + /** + * @var mixed + */ + var $flags = ''; + /** + * Media type + * @var string + */ + var $type0=''; + /** + * Media subtype + * @var string + */ + var $type1=''; + /** + * Nested mime parts + * @var array + */ + var $entities = array(); + /** + * Message part id + * @var string + */ + var $entity_id = ''; + /** + * Parent message part id + * @var string + */ + var $parent_ent; + /** + * @var mixed + */ + var $entity; + /** + * @var mixed + */ + var $parent = ''; + /** + * @var string + */ + var $decoded_body=''; + /** + * Message \seen status + * @var boolean + */ + var $is_seen = 0; + /** + * Message \answered status + * @var boolean + */ + var $is_answered = 0; + /** + * Message \deleted status + * @var boolean + */ + var $is_deleted = 0; + /** + * Message \flagged status + * @var boolean + */ + var $is_flagged = 0; + /** + * Message mdn status + * @var boolean + */ + var $is_mdnsent = 0; + /** + * Message text body + * @var string + */ + var $body_part = ''; + /** + * Message part offset + * for fetching body parts out of raw messages + * @var integer + */ + var $offset = 0; + /** + * Message part length + * for fetching body parts out of raw messages + * @var integer + */ + var $length = 0; + /** + * Local attachment filename location where the tempory attachment is + * stored. For use in delivery class. + * @var string + */ + var $att_local_name = ''; + /** + * @param string $ent entity id + */ function setEnt($ent) { $this->entity_id= $ent; } + /** + * Add nested message part + * @param object $msg + */ function addEntity ($msg) { - $msg->parent = &$this; $this->entities[] = $msg; } + /** + * Get file name used for mime part + * @return string file name + * @since 1.3.2 + */ function getFilename() { - $filename = ''; - $filename = $this->header->getParameter('filename'); - if (!$filename) { - $filename = $this->header->getParameter('name'); - } - - if (!$filename) { - $filename = 'untitled-'.$this->entity_id; - } - return $filename; + $filename = ''; + $header = $this->header; + if (is_object($header->disposition)) { + $filename = $header->disposition->getProperty('filename'); + if (trim($filename) == '') { + $name = decodeHeader($header->disposition->getProperty('name')); + if (!trim($name)) { + $name = $header->getParameter('name'); + if(!trim($name)) { + if (!trim( $header->id )) { + $filename = 'untitled-[' . $this->entity_id . ']' ; + } else { + $filename = 'cid: ' . $header->id; + } + } else { + $filename = $name; + } + } else { + $filename = $name; + } + } + } else { + $filename = $header->getParameter('filename'); + if (!trim($filename)) { + $filename = $header->getParameter('name'); + if (!trim($filename)) { + if (!trim( $header->id )) { + $filename = 'untitled-[' . $this->entity_id . ']' ; + } else { + $filename = 'cid: ' . $header->id; + } + } + } + } + return $filename; } - + /** + * Add header object to message object. + * WARNING: Unfinished code. Don't expect it to work in older sm versions. + * @param mixed $read array or string with message headers + * @todo FIXME: rfc822header->parseHeader() does not return rfc822header object + */ function addRFC822Header($read) { $header = new Rfc822Header(); $this->rfc822_header = $header->parseHeader($read); } + /** + * @param string $ent + * @return mixed (object or string?) + */ function getEntity($ent) { $cur_ent = $this->entity_id; $msg = $this; @@ -71,8 +208,7 @@ class Message { } $ent_a = explode('.', $ent); - $entCount = count($ent_a) - 1; - for ($i = 0; $i < $entCount; ++$i) { + for ($i = 0,$entCount = count($ent_a) - 1; $i < $entCount; ++$i) { if (isset($cur_ent_a[$i]) && ($cur_ent_a[$i] != $ent_a[$i])) { $msg = $msg->parent; $cur_ent_a = explode('.', $msg->entity_id); @@ -95,19 +231,26 @@ class Message { $msg = $msg->entities[0]; } - if (isset($msg->entities[($ent_a[$cnt])-1])) { - if (is_object($msg->entities[($ent_a[$cnt])-1])) { - $msg = $msg->entities[($ent_a[$cnt]-1)]; + if (isset($msg->entities[($ent_a[$entCount])-1])) { + if (is_object($msg->entities[($ent_a[$entCount])-1])) { + $msg = $msg->entities[($ent_a[$entCount]-1)]; } } return $msg; } + /** + * Set message body + * @param string $s message body + */ function setBody($s) { $this->body_part = $s; } + /** + * Clean message object + */ function clean_up() { $msg = $this; $msg->body_part = ''; @@ -117,6 +260,9 @@ class Message { } } + /** + * @return string + */ function getMailbox() { $msg = $this; while (is_object($msg->parent)) { @@ -125,35 +271,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. @@ -164,11 +281,71 @@ class Message { * Question/Bugs: * * Ask for me (Marc Groot Koerkamp, stekkel@users.sourceforge.net) - * + * @param string $read + * @param integer $i + * @param mixed $sub_msg + * @return object Message object + * @todo define argument and return types */ - 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; + } + + /** + * @param object $msg + * @param mixed $init + * @param integer $i + * @todo document me + * @since 1.4.0 + */ + 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); + } + } + } + + /** + * @param string $read + * @param integer $i + * @param mixed $sub_msg + * @return object Message object + * @todo document me + * @since 1.4.0 (code was part of parseStructure() in 1.3.x) + */ + function parseBodyStructure($read, &$i, $sub_msg = '') { $arg_no = 0; $arg_a = array(); + if ($sub_msg) { + $message = $sub_msg; + } else { + $message = new Message(); + } + for ($cnt = strlen($read); $i < $cnt; ++$i) { $char = strtoupper($read{$i}); switch ($char) { @@ -181,42 +358,33 @@ 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($msg->parseBodyStructure($read, $i, $msg)); } } break; case 1: /* multipart properties */ ++$i; - $res = $this->parseProperties($read, $i); - $arg_a[] = $res[0]; - $i = $res[1]; + $arg_a[] = $msg->parseProperties($read, $i); ++$arg_no; break; case 2: if (isset($msg->type0) && ($msg->type0 == 'multipart')) { ++$i; - $res = $msg->parseDisposition($read, $i); + $arg_a[] = $msg->parseDisposition($read, $i); } else { /* properties */ - $res = $msg->parseProperties($read, $i); + $arg_a[] = $msg->parseProperties($read, $i); } - $arg_a[] = $res[0]; - $i = $res[1]; ++$arg_no; break; case 3: if (isset($msg->type0) && ($msg->type0 == 'multipart')) { ++$i; - $res= $msg->parseLanguage($read, $i); - $arg_a[] = $res[0]; - $i = $res[1]; + $arg_a[]= $msg->parseLanguage($read, $i); } case 7: if (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822')) { @@ -225,41 +393,31 @@ class Message { $msg->type0 = $arg_a[0]; $msg->type1 = $arg_a[1]; $rfc822_hdr = new Rfc822Header(); - $res = $msg->parseEnvelope($read, $i, $rfc822_hdr); - $msg->rfc822_header = $res[0]; - $i = $res[1] + 1; + $msg->rfc822_header = $msg->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($msg->parseBodyStructure($read, $i,$msg)); } break; case 8: ++$i; - $res = $msg->parseDisposition($read, $i); - $arg_a[] = $res[0]; - $i = $res[1]; + $arg_a[] = $msg->parseDisposition($read, $i); ++$arg_no; break; case 9: ++$i; if (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) { - $res = $msg->parseDisposition($read, $i); + $arg_a[] = $msg->parseDisposition($read, $i); } else { - $res = $msg->parseLanguage($read, $i); + $arg_a[] = $msg->parseLanguage($read, $i); } - $arg_a[] = $res[0]; - $i = $res[1]; ++$arg_no; break; case 10: if (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) { ++$i; - $res = $msg->parseLanguage($read, $i); - $arg_a[] = $res[0]; - $i = $res[1]; + $arg_a[] = $msg->parseLanguage($read, $i); } else { $i = $msg->parseParenthesis($read, $i); $arg_a[] = ''; /* not yet described in rfc2060 */ @@ -276,10 +434,7 @@ class Message { break; case '"': /* inside an entity -> start processing */ - $debug = substr($read, $i, 20); - $res = $msg->parseQuote($read, $i); - $arg_s = $res[0]; - $i = $res[1]; + $arg_s = $msg->parseQuote($read, $i); ++$arg_no; if ($arg_no < 3) { $arg_s = strtolower($arg_s); /* type0 and type1 */ @@ -289,7 +444,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; @@ -297,20 +453,20 @@ class Message { break; case '{': /* process the literal value */ - $res = $msg->parseLiteral($read, $i); - $arg_s = $res[0]; - $i = $res[1]; + $arg_a[] = $msg->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')); @@ -340,9 +496,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]; @@ -353,28 +506,27 @@ 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 */ - function parseProperties($read, $i) { + /** + * @param string $read + * @param integer $i + * @return array + */ + function parseProperties($read, &$i) { $properties = array(); $prop_name = ''; for (; $read{$i} != ')'; ++$i) { $arg_s = ''; if ($read{$i} == '"') { - $res = $this->parseQuote($read, $i); - $arg_s = $res[0]; - $i = $res[1]; + $arg_s = $this->parseQuote($read, $i); } else if ($read{$i} == '{') { - $res = $this->parseLiteral($read, $i); - $arg_s = $res[0]; - $i = $res[1]; + $arg_s = $this->parseLiteral($read, $i); } if ($arg_s != '') { @@ -387,27 +539,31 @@ class Message { } } } - return array($properties, $i); + return $properties; } - function parseEnvelope($read, $i, $hdr) { + /** + * @param string $read + * @param integer $i + * @param object $hdr MessageHeader object + * @return object MessageHeader object + */ + 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 '"': - $res = $this->parseQuote($read, $i); - $arg_a[] = $res[0]; - $i = $res[1]; + $arg_a[] = $this->parseQuote($read, $i); ++$arg_no; break; case '{': - $res = $this->parseLiteral($read, $i); - $arg_a[] = $res[0]; - $i = $res[1]; + $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': @@ -428,9 +584,7 @@ class Message { $a=0; for (; $i < $cnt && $read{$i} != ')'; ++$i) { if ($read{$i} == '(') { - $res = $this->parseAddress($read, $i); - $addr = $res[0]; - $i = $res[1]; + $addr = $this->parseAddress($read, $i); if (($addr->host == '') && ($addr->mailbox != '')) { /* start of group */ $group = $addr->mailbox; @@ -459,64 +613,131 @@ 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]; - - $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 */ + $d_parts = explode(' ', $d); + if (!$arg_a[1]) $arg_a[1] = _("(no subject)"); + + $hdr->date = getTimeStamp($d_parts); /* argument 1: date */ + $hdr->date_unparsed = strtr($d,'<>',' '); /* original date */ + $hdr->subject = $arg_a[1]; /* argument 2: subject */ + $hdr->from = is_array($arg_a[2]) ? $arg_a[2][0] : ''; /* argument 3: from */ + $hdr->sender = is_array($arg_a[3]) ? $arg_a[3][0] : ''; /* argument 4: sender */ + $hdr->reply_to = is_array($arg_a[4]) ? $arg_a[4][0] : ''; /* argument 5: reply-to */ $hdr->to = $arg_a[5]; /* argument 6: to */ $hdr->cc = $arg_a[6]; /* argument 7: cc */ $hdr->bcc = $arg_a[7]; /* argument 8: bcc */ - $hdr->inreplyto = $arg_a[8]; /* argument 9: in-reply-to */ + $hdr->in_reply_to = $arg_a[8]; /* argument 9: in-reply-to */ $hdr->message_id = $arg_a[9]; /* argument 10: message-id */ } - return (array($hdr, $i)); + return $hdr; } - function parseLiteral($read, $i) { + /** + * @param string $read + * @param integer $i + * @return string + * @todo document me + */ + function parseLiteral($read, &$i) { $lit_cnt = ''; - for (++$i; $read{$i} != '}'; ++$i) { - $lit_cnt .= $read{$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 = ''; } - - $lit_cnt +=2; /* add the { and } characters */ - $s = ''; - for ($j = 0; $j < $lit_cnt; ++$j) { - $s .= $read{++$i}; - } - return (array($s, $i)); + return $s; } - function parseQuote($read, $i) { + /** + * function parseQuote + * + * This extract the string value from a quoted string. After the end-quote + * character is found it returns the string. The offset $i when calling + * this function points to the first double quote. At the end it points to + * The ending quote. This function takes care of escaped double quotes. + * "some \"string\"" + * ^ ^ + * initial $i end position $i + * + * @param string $read + * @param integer $i offset in $read + * @return string string inbetween the double quotes + * @author Marc Groot Koerkamp + */ + function parseQuote($read, &$i) { $s = ''; - for (++$i; $read{$i} != '"'; ++$i) { - if ($read{$i} == '\\') { - ++$i; - } - $s .= $read{$i}; + $iPos = ++$i; + $iPosStart = $iPos; + while (true) { + $iPos = strpos($read,'"',$iPos); + if (!$iPos) break; + if ($iPos && $read{$iPos -1} != '\\') { + $s = substr($read,$i,($iPos-$i)); + $i = $iPos; + break; + } else if ($iPos > 1 && $read{$iPos -1} == '\\' && $read{$iPos-2} == '\\') { + // This is an unique situation where the fast detection of the string + // fails. If the quote string ends with \\ then we need to iterate + // through the entire string to make sure we detect the unexcaped + // double quotes correctly. + $s = ''; + $bEscaped = false; + $k = 0; + for ($j=$iPosStart,$iCnt=strlen($read);$j<$iCnt;++$j) { + $cChar = $read{$j}; + switch ($cChar) { + case '\\': + $bEscaped = !$bEscaped; + $s .= $cChar; + break; + case '"': + if ($bEscaped) { + $s .= $cChar; + $bEscaped = false; + } else { + $i = $j; + break 3; + } + break; + default: + if ($bEscaped) { + $bEscaped = false; + } + $s .= $cChar; + break; + } + } + } + ++$iPos; + if ($iPos > strlen($read)) { + break; + } } - return (array($s, $i)); + return $s; } - function parseAddress($read, $i) { + /** + * @param string $read + * @param integer $i + * @return object AddressStructure object + */ + function parseAddress($read, &$i) { $arg_a = array(); - for (; $read{$i} != ')'; ++$i) { $char = strtoupper($read{$i}); switch ($char) { - case '"': - case '{': - $res = ($char == '"' ? $this->parseQuote($read, $i) : $this->parseLiteral($read, $i)); - $arg_a[] = $res[0]; - $i = $res[1]; - 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') { @@ -537,25 +758,21 @@ class Message { } else { $adr = ''; } - return (array($adr, $i)); + return $adr; } - function parseDisposition($read, $i) { + /** + * @param string $read + * @param integer $i + * @param object Disposition object or empty string + */ + function parseDisposition($read, &$i) { $arg_a = array(); - for (; $read{$i} != ')'; ++$i) { switch ($read{$i}) { - case '"': - case '{': - case '(': - switch ($read{$i}) { - case '"': $res = $this->parseQuote($read, $i); break; - case '{': $res = $this->parseLiteral($read, $i); break; - case '(': $res = $this->parseProperties($read, $i); break; - } - $arg_a[] = $res[0]; - $i = $res[1]; - break; + case '"': $arg_a[] = $this->parseQuote($read, $i); break; + case '{': $arg_a[] = $this->parseLiteral($read, $i); break; + case '(': $arg_a[] = $this->parseProperties($read, $i); break; default: break; } } @@ -566,27 +783,23 @@ class Message { $disp->properties = $arg_a[1]; } } - - return (is_object($disp) ? array($disp, $i) : array('', $i)); + return (is_object($disp) ? $disp : ''); } - function parseLanguage($read, $i) { + /** + * @param string $read + * @param integer $i + * @return object Language object or empty string + */ + function parseLanguage($read, &$i) { /* no idea how to process this one without examples */ $arg_a = array(); for (; $read{$i} != ')'; ++$i) { switch ($read{$i}) { - case '"': - case '{': - case '(': - switch ($read{$i}) { - case '"': $res = $this->parseQuote($read, $i); break; - case '{': $res = $this->parseLiteral($read, $i); break; - case '(': $res = $this->parseProperties($read, $i); break; - } - $arg_a[] = $res[0]; - $i = $res[1]; - break; + case '"': $arg_a[] = $this->parseQuote($read, $i); break; + case '{': $arg_a[] = $this->parseLiteral($read, $i); break; + case '(': $arg_a[] = $this->parseProperties($read, $i); break; default: break; } } @@ -597,31 +810,36 @@ class Message { $lang->properties = $arg_a[1]; } } - - return (is_object($lang) ? array($lang, $i) : array('', $i)); + return (is_object($lang) ? $lang : ''); } + /** + * Parse message text enclosed in parenthesis + * @param string $read + * @param integer $i + * @return integer + */ function parseParenthesis($read, $i) { for (; $read{$i} != ')'; ++$i) { switch ($read{$i}) { - case '"': - case '{': - case '(': - switch ($read{$i}) { - case '"': $res = $this->parseQuote($read, $i); break; - case '{': $res = $this->parseLiteral($read, $i); break; - case '(': $res = $this->parseProperties($read, $i); break; - } - $i = $res[1]; - break; + case '"': $this->parseQuote($read, $i); break; + case '{': $this->parseLiteral($read, $i); break; + case '(': $this->parseProperties($read, $i); break; default: break; } } return $i; } - /* Function to fill the message structure in case the */ - /* bodystructure is not available NOT FINISHED YET */ + /** + * Function to fill the message structure in case the + * bodystructure is not available + * NOT FINISHED YET + * @param string $read + * @param string $type0 message part type + * @param string $type1 message part subtype + * @return string (only when type0 is not message or multipart) + */ function parseMessage($read, $type0, $type1) { switch ($type0) { case 'message': @@ -689,6 +907,12 @@ class Message { } } + /** + * @param array $entity + * @param array $alt_order + * @param boolean $strict + * @return array + */ function findDisplayEntity($entity = array(), $alt_order = array('text/plain', 'text/html'), $strict=false) { $found = false; if ($this->type0 == 'multipart') { @@ -714,8 +938,10 @@ class Message { } } else { /* Treat as multipart/mixed */ foreach ($this->entities as $ent) { - if((strtolower($ent->header->disposition->name) != 'attachment') && - (($ent->type0 != 'message') && ($ent->type1 != 'rfc822'))) { + if(!(is_object($ent->header->disposition) && strtolower($ent->header->disposition->name) == 'attachment') && + (!isset($ent->header->parameters['filename'])) && + (!isset($ent->header->parameters['name'])) && + (($ent->type0 != 'message') && ($ent->type1 != 'rfc822'))) { $entity = $ent->findDisplayEntity($entity, $alt_order, $strict); $found = true; } @@ -723,10 +949,14 @@ 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) && - (strtolower($this->header->disposition->name) != 'attachment')) { + if ((count($this->entities) == 0) && + (!isset($this->header->parameters['filename'])) && + (!isset($this->header->parameters['name'])) && + isset($this->header->disposition) && is_object($this->header->disposition) && + !(is_object($this->header->disposition) && strtolower($this->header->disposition->name) == 'attachment')) { $entity[] = $this->entity_id; $found = true; } @@ -735,7 +965,7 @@ class Message { } if(!$found) { foreach ($this->entities as $ent) { - if((strtolower($ent->header->disposition->name) != 'attachment') && + if(!(is_object($ent->header->disposition) && strtolower($ent->header->disposition->name) == 'attachment') && (($ent->type0 != 'message') && ($ent->type1 != 'rfc822'))) { $entity = $ent->findDisplayEntity($entity, $alt_order, $strict); $found = true; @@ -747,16 +977,19 @@ class Message { in_array($this->type1, array('plain', 'html', 'message')) && isset($this->entity_id)) { if (count($this->entities) == 0) { - if (strtolower($this->header->disposition->name) != 'attachment') { + if (!is_object($this->header->disposition) || strtolower($this->header->disposition->name) != 'attachment') { $entity[] = $this->entity_id; } } } } - return $entity; } + /** + * @param array $alt_order + * @return array + */ function findAlternativeEntity($alt_order) { /* If we are dealing with alternative parts then we */ /* choose the best viewable message supported by SM. */ @@ -766,6 +999,8 @@ class Message { $type = $ent->header->type0 . '/' . $ent->header->type1; if ($type == 'multipart/related') { $type = $ent->header->getParameter('type'); + // Mozilla bug. Mozilla does not provide the parameter type. + if (!$type) $type = 'text/html'; } $altCount = count($alt_order); for ($j = $best_view; $j < $altCount; ++$j) { @@ -775,33 +1010,43 @@ class Message { } } } - return $entity; } + /** + * @return array + */ function findRelatedEntity() { $msgs = array(); - + $related_type = $this->header->getParameter('type'); + // Mozilla bug. Mozilla does not provide the parameter type. + if (!$related_type) $related_type = 'text/html'; $entCount = count($this->entities); for ($i = 0; $i < $entCount; ++$i) { $type = $this->entities[$i]->header->type0.'/'.$this->entities[$i]->header->type1; - if ($this->header->getParameter('type') == $type) { + if ($related_type == $type) { $msgs[] = $this->entities[$i]; } } - return $msgs; } + /** + * @param array $exclude_id + * @param array $result + * @return array + */ 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; @@ -827,9 +1072,46 @@ class Message { $result[] = $this; } } - return $result; } -} -?> + /** + * Add attachment to message object + * @param string $type attachment type + * @param string $name attachment name + * @param string $location path to attachment + */ + function initAttachment($type, $name, $location) { + $attachment = new Message(); + $mime_header = new MessageHeader(); + $mime_header->setParameter('name', $name); + // FIXME: duplicate code. see ContentType class + $pos = strpos($type, '/'); + if ($pos > 0) { + $mime_header->type0 = substr($type, 0, $pos); + $mime_header->type1 = substr($type, $pos+1); + } else { + $mime_header->type0 = $type; + } + $attachment->att_local_name = $location; + $disposition = new Disposition('attachment'); + $disposition->properties['filename'] = $name; + $mime_header->disposition = $disposition; + $attachment->mime_header = $mime_header; + $this->entities[]=$attachment; + } + + /** + * Delete all attachments from this object from disk. + * @since 1.5.1 + */ + function purgeAttachments() { + if ($this->att_local_name && file_exists($this->att_local_name)) { + unlink($this->att_local_name); + } + // recursively delete attachments from entities contained in this object + for ($i=0, $entCount=count($this->entities);$i< $entCount; ++$i) { + $this->entities[$i]->purgeAttachments(); + } + } +}