6 * This file contains functions needed to handle mime messages.
8 * @copyright 2003-2024 The SquirrelMail Project Team
9 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
11 * @package squirrelmail
17 * The object that contains a message.
19 * message is the object that contains messages. It is a recursive object in
20 * that through the $entities variable, it can contain more objects of type
21 * message. See documentation in mime.txt for a better description of how this
23 * @package squirrelmail
33 var $rfc822_header = '';
35 * MessageHeader object
38 var $mime_header = '';
57 var $entities = array();
64 * Parent message part id
81 * Message \seen status
86 * Message \answered status
91 * Message forward status
94 var $is_forwarded = 0;
96 * Message \deleted status
101 * Message \flagged status
116 * Message part offset
117 * for fetching body parts out of raw messages
122 * Message part length
123 * for fetching body parts out of raw messages
128 * Local attachment filename location where the tempory attachment is
129 * stored. For use in delivery class.
132 var $att_local_name = '';
135 * @param string $ent entity id
137 function setEnt($ent) {
138 $this->entity_id
= $ent;
142 * Add nested message part
145 function addEntity ($msg) {
146 $this->entities
[] = $msg;
150 * Get file name used for mime part
151 * @return string file name
154 function getFilename() {
156 $header = $this->header
;
157 if (is_object($header->disposition
)) {
158 $filename = $header->disposition
->getProperty('filename');
159 if (trim($filename) == '') {
160 $name = decodeHeader($header->disposition
->getProperty('name'));
162 $name = $header->getParameter('name');
164 if (!trim( $header->id
)) {
165 $filename = 'untitled-[' . $this->entity_id
. ']' . '.' . strtolower($header->type1
);
167 $filename = 'cid: ' . $header->id
. '.' . strtolower($header->type1
);
177 $filename = $header->getParameter('filename');
178 if (!trim($filename)) {
179 $filename = $header->getParameter('name');
180 if (!trim($filename)) {
181 if (!trim( $header->id
)) {
182 $filename = 'untitled-[' . $this->entity_id
. ']' . '.' . strtolower($header->type1
);
184 $filename = 'cid: ' . $header->id
. '.' . strtolower($header->type1
);
193 * Add header object to message object.
194 * WARNING: Unfinished code. Don't expect it to work in older sm versions.
195 * @param mixed $read array or string with message headers
196 * @todo FIXME: rfc822header->parseHeader() does not return rfc822header object
198 function addRFC822Header($read) {
199 $header = new Rfc822Header();
200 $this->rfc822_header
= $header->parseHeader($read);
205 * @return mixed (object or string?)
207 function getEntity($ent) {
208 $cur_ent = $this->entity_id
;
210 if (($cur_ent == '') ||
($cur_ent == '0')) {
211 $cur_ent_a = array();
213 $cur_ent_a = explode('.', $this->entity_id
);
215 $ent_a = explode('.', $ent);
217 for ($i = 0,$entCount = count($ent_a) - 1; $i < $entCount; ++
$i) {
218 if (isset($cur_ent_a[$i]) && ($cur_ent_a[$i] != $ent_a[$i])) {
220 $cur_ent_a = explode('.', $msg->entity_id
);
222 } else if (!isset($cur_ent_a[$i])) {
223 if (isset($msg->entities
[($ent_a[$i]-1)])) {
224 $msg = $msg->entities
[($ent_a[$i]-1)];
226 $msg = $msg->entities
[0];
229 if (($msg->type0
== 'message') && ($msg->type1
== 'rfc822')) {
230 /*this is a header for a message/rfc822 entity */
231 $msg = $msg->entities
[0];
235 if (($msg->type0
== 'message') && ($msg->type1
== 'rfc822')) {
236 /*this is a header for a message/rfc822 entity */
237 $msg = $msg->entities
[0];
240 if (isset($msg->entities
[($ent_a[$entCount])-1])) {
241 if (is_object($msg->entities
[($ent_a[$entCount])-1])) {
242 $msg = $msg->entities
[($ent_a[$entCount]-1)];
251 * @param string $s message body
253 function setBody($s) {
254 $this->body_part
= $s;
258 * Clean message object
260 function clean_up() {
262 $msg->body_part
= '';
264 foreach ($msg->entities
as $m) {
272 function getMailbox() {
274 while (is_object($msg->parent
)) {
277 return $msg->mailbox
;
281 * Bodystructure parser, a recursive function for generating the
282 * entity-tree with all the mime-parts.
284 * It follows RFC2060 and stores all the described fields in the
289 * Ask for me (Marc Groot Koerkamp, stekkel@users.sourceforge.net)
290 * @param string $read
292 * @param mixed $sub_msg
293 * @return object Message object
294 * @todo define argument and return types
296 static function parseStructure($read, &$i, $sub_msg = '') {
297 $msg = Message
::parseBodyStructure($read, $i, $sub_msg);
298 if($msg) $msg->setEntIds($msg,false,0);
309 function setEntIds(&$msg,$init=false,$i=0) {
310 $iCnt = count($msg->entities
);
311 if ($init !==false) {
313 if ($msg->parent
->type0
== 'message' &&
314 $msg->parent
->type1
== 'rfc822' &&
315 $msg->type0
== 'multipart') {
319 $msg->entity_id
= "$init.$iEntSub";
321 $msg->entity_id
= $iEntSub;
328 for ($i=0;$i<$iCnt;++
$i) {
329 $msg->entities
[$i]->parent
=& $msg;
330 if (strrchr($msg->entity_id
, '.') != '.0') {
331 $msg->entities
[$i]->setEntIds($msg->entities
[$i],$msg->entity_id
,$i);
333 $msg->entities
[$i]->setEntIds($msg->entities
[$i],$msg->parent
->entity_id
,$i);
339 * @param string $read
341 * @param mixed $sub_msg
342 * @return object Message object
344 * @since 1.4.0 (code was part of parseStructure() in 1.3.x)
346 static function parseBodyStructure($read, &$i, $sub_msg = '') {
352 $message = new Message();
355 for ($cnt = strlen($read); $i < $cnt; ++
$i) {
356 $char = strtoupper($read[$i]);
362 $msg = new Message();
363 $hdr = new MessageHeader();
364 $hdr->type0
= 'text';
365 $hdr->type1
= 'plain';
366 $hdr->encoding
= '7bit';
369 $msg->header
->type0
= 'multipart';
370 $msg->type0
= 'multipart';
371 while ($read[$i] == '(') {
372 $msg->addEntity($msg->parseBodyStructure($read, $i, $msg));
377 /* multipart properties */
379 $arg_a[] = $msg->parseProperties($read, $i);
383 if (isset($msg->type0
) && ($msg->type0
== 'multipart')) {
385 $arg_a[] = $msg->parseDisposition($read, $i);
386 } else { /* properties */
387 $arg_a[] = $msg->parseProperties($read, $i);
392 if (isset($msg->type0
) && ($msg->type0
== 'multipart')) {
394 $arg_a[]= $msg->parseLanguage($read, $i);
397 if (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822')) {
398 $msg->header
->type0
= $arg_a[0];
399 $msg->header
->type1
= $arg_a[1];
400 $msg->type0
= $arg_a[0];
401 $msg->type1
= $arg_a[1];
402 $rfc822_hdr = new Rfc822Header();
403 $msg->rfc822_header
= $msg->parseEnvelope($read, $i, $rfc822_hdr);
404 while (($i < $cnt) && ($read[$i] != '(')) {
407 $msg->addEntity($msg->parseBodyStructure($read, $i,$msg));
412 $arg_a[] = $msg->parseDisposition($read, $i);
417 if (($arg_a[0] == 'text') ||
(($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) {
418 $arg_a[] = $msg->parseDisposition($read, $i);
420 $arg_a[] = $msg->parseLanguage($read, $i);
425 if (($arg_a[0] == 'text') ||
(($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) {
427 $arg_a[] = $msg->parseLanguage($read, $i);
429 $i = $msg->parseParenthesis($read, $i);
430 $arg_a[] = ''; /* not yet described in rfc2060 */
435 /* unknown argument, skip this part */
436 $i = $msg->parseParenthesis($read, $i);
443 /* inside an entity -> start processing */
444 $arg_s = $msg->parseQuote($read, $i);
447 $arg_s = strtolower($arg_s); /* type0 and type1 */
453 /* probably NIL argument */
454 $tmpnil = strtoupper(substr($read, $i, 4));
455 if ($tmpnil == 'NIL ' ||
$tmpnil == 'NIL)') {
462 /* process the literal value */
463 $arg_a[] = $msg->parseLiteral($read, $i);
467 case is_numeric($read[$i]):
468 /* process integers */
469 if ($read[$i] == ' ') { break; }
471 if (preg_match('/^([0-9]+).*/',substr($read,$i), $regs)) {
472 $i +
= strlen($regs[1])-1;
479 $multipart = (isset($msg->type0
) && ($msg->type0
== 'multipart'));
481 $shifted_args = (($arg_a[0] == 'text') ||
(($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822')));
482 $hdr->type0
= $arg_a[0];
483 $hdr->type1
= $arg_a[1];
485 $msg->type0
= $arg_a[0];
486 $msg->type1
= $arg_a[1];
488 if (is_array($arr)) {
489 $hdr->parameters
= $arg_a[2];
491 $hdr->id
= str_replace('<', '', str_replace('>', '', $arg_a[3]));
492 $hdr->description
= $arg_a[4];
493 $hdr->encoding
= strtolower($arg_a[5]);
494 $hdr->entity_id
= $msg->entity_id
;
495 $hdr->size
= $arg_a[6];
497 $hdr->lines
= $arg_a[7];
502 $hdr->md5
= (isset($arg_a[7+
$s]) ?
$arg_a[7+
$s] : $hdr->md5
);
503 $hdr->disposition
= (isset($arg_a[8+
$s]) ?
$arg_a[8+
$s] : $hdr->disposition
);
504 $hdr->language
= (isset($arg_a[9+
$s]) ?
$arg_a[9+
$s] : $hdr->language
);
507 $hdr->type0
= 'multipart';
508 $hdr->type1
= $arg_a[0];
509 $msg->type0
= 'multipart';
510 $msg->type1
= $arg_a[0];
511 $hdr->parameters
= (isset($arg_a[1]) ?
$arg_a[1] : $hdr->parameters
);
512 $hdr->disposition
= (isset($arg_a[2]) ?
$arg_a[2] : $hdr->disposition
);
513 $hdr->language
= (isset($arg_a[3]) ?
$arg_a[3] : $hdr->language
);
520 } /* parsestructure */
523 * @param string $read
527 function parseProperties($read, &$i) {
528 $properties = array();
531 for (; $read[$i] != ')'; ++
$i) {
533 if ($read[$i] == '"') {
534 $arg_s = $this->parseQuote($read, $i);
535 } else if ($read[$i] == '{') {
536 $arg_s = $this->parseLiteral($read, $i);
540 if ($prop_name == '') {
541 $prop_name = strtolower($arg_s);
542 $properties[$prop_name] = '';
543 } else if ($prop_name != '') {
544 $properties[$prop_name] = $arg_s;
549 return $this->handleRfc2231($properties);
553 * Joins RFC-2231 continuations, converts encoding to RFC-2047 style
554 * @param array $properties
557 function handleRfc2231($properties) {
559 /* STAGE 1: look for multi-line parameters, convert to the single line
560 form, and normalize values */
563 foreach($properties as $key=>$value) {
564 /* Look for parameters followed by "*", a number, and an optional "*"
566 if (preg_match('/^(.*\*)(\d+)(|\*)$/', $key, $matches)) {
567 unset($properties[$key]);
568 $prop_name = $matches[1];
569 if (!isset($cont[$prop_name])) $cont[$prop_name] = array();
571 /* An asterisk at the end of parameter name indicates that there
572 may be an encoding information present, and the parameter
573 value is percent-hex encoded. If parameter is not encoded, we
574 encode it to simplify further processing.
576 if ($matches[3] == '') $value = rawurlencode($value);
577 /* Use the number from parameter name as segment index */
578 $cont[$prop_name][$matches[2]] = $value;
581 foreach($cont as $key=>$values) {
582 /* Sort segments of multi-line parameters by index number. */
584 /* Join segments. We can do it safely, because:
585 - All segments are encoded.
586 - Per RFC-2231, chapter 4.1 notes.
588 $value = implode('', $values);
589 /* Add language and character set field delimiters if not present,
590 as required per RFC-2231, chapter 4.1, note #5. */
591 if (strpos($value, "'") === false) $value = "''".$value;
592 $properties[$key] = $value;
595 /* STAGE 2: Convert single line RFC-2231 encoded parameters, and
596 previously converted multi-line parameters, to RFC-2047 encoding */
598 foreach($properties as $key=>$value) {
599 if ($idx = strpos($key, '*')) {
600 unset($properties[$key]);
601 /* Extract the charset & language. */
602 $charset = substr($value,0,strpos($value,"'"));
603 $value = substr($value,strlen($charset)+
1);
604 $language = substr($value,0,strpos($value,"'"));
605 $value = substr($value,strlen($language)+
1);
606 /* No character set defaults to US-ASCII */
607 if (!$charset) $charset = 'US-ASCII';
608 if ($language) $language = '*'.$language;
609 /* Convert to RFC-2047 base64 encoded string. */
610 $properties[substr($key, 0, $idx)] = '=?'.$charset.$language.'?B?'.base64_encode(rawurldecode($value)).'?=';
617 * @param string $read
619 * @param object $hdr MessageHeader object
620 * @return object MessageHeader object
622 function parseEnvelope($read, &$i, $hdr) {
626 for ($cnt = strlen($read); ($i < $cnt) && ($read[$i] != ')'); ++
$i) {
627 $char = strtoupper($read[$i]);
630 $arg_a[] = $this->parseQuote($read, $i);
634 $arg_a[] = $this->parseLiteral($read, $i);
635 /* temp bugfix (SM 1.5 will have a working clean version)
636 too much work to implement that version right now */
641 /* probably NIL argument */
642 if (strtoupper(substr($read, $i, 3)) == 'NIL') {
649 /* Address structure (with group support)
650 * Note: Group support is useless on SMTP connections
651 * because the protocol doesn't support it
656 for (; $i < $cnt && $read[$i] != ')'; ++
$i) {
657 if ($read[$i] == '(') {
658 $addr = $this->parseAddress($read, $i);
659 if (($addr->host
== '') && ($addr->mailbox
!= '')) {
661 $group = $addr->mailbox
;
664 } else if ($group && ($addr->host
== '') && ($addr->mailbox
== '')) {
666 if ($a == ($j+
1)) { /* no group members */
667 $group_addr->group
= $group;
668 $group_addr->mailbox
= '';
669 $group_addr->personal
= "$group: Undisclosed recipients;";
670 $addr_a[] = $group_addr;
674 $addr->group
= $group;
686 if (count($arg_a) > 9) {
687 $d = strtr($arg_a[0], array(' ' => ' '));
688 $d_parts = explode(' ', $d);
689 if (!$arg_a[1]) $arg_a[1] = _("(no subject)");
691 $hdr->date
= getTimeStamp($d_parts); /* argument 1: date */
692 $hdr->date_unparsed
= strtr($d,'<>',' '); /* original date */
693 $hdr->subject
= $arg_a[1]; /* argument 2: subject */
694 $hdr->from
= is_array($arg_a[2]) ?
$arg_a[2][0] : ''; /* argument 3: from */
695 $hdr->sender
= is_array($arg_a[3]) ?
$arg_a[3][0] : ''; /* argument 4: sender */
696 $hdr->reply_to
= is_array($arg_a[4]) ?
$arg_a[4][0] : ''; /* argument 5: reply-to */
697 $hdr->to
= $arg_a[5]; /* argument 6: to */
698 $hdr->cc
= $arg_a[6]; /* argument 7: cc */
699 $hdr->bcc
= $arg_a[7]; /* argument 8: bcc */
700 $hdr->in_reply_to
= $arg_a[8]; /* argument 9: in-reply-to */
701 $hdr->message_id
= $arg_a[9]; /* argument 10: message-id */
707 * @param string $read
712 function parseLiteral($read, &$i) {
715 $iPos = strpos($read,'}',$i);
717 $lit_cnt = substr($read, $i, $iPos - $i);
718 $i +
= strlen($lit_cnt) +
3; /* skip } + \r + \n */
719 /* Now read the literal */
720 $s = ($lit_cnt ?
substr($read,$i,$lit_cnt): '');
722 /* temp bugfix (SM 1.5 will have a working clean version)
723 too much work to implement that version right now */
725 } else { /* should never happen */
726 $i +
= 3; /* } + \r + \n */
733 * function parseQuote
735 * This extract the string value from a quoted string. After the end-quote
736 * character is found it returns the string. The offset $i when calling
737 * this function points to the first double quote. At the end it points to
738 * The ending quote. This function takes care of escaped double quotes.
741 * initial $i end position $i
743 * @param string $read
744 * @param integer $i offset in $read
745 * @return string string inbetween the double quotes
746 * @author Marc Groot Koerkamp
748 function parseQuote($read, &$i) {
753 $iPos = strpos($read,'"',$iPos);
755 if ($iPos && $read[$iPos -1] != '\\') {
756 $s = substr($read,$i,($iPos-$i));
759 } else if ($iPos > 1 && $read[$iPos -1] == '\\' && $read[$iPos-2] == '\\') {
760 // This is an unique situation where the fast detection of the string
761 // fails. If the quote string ends with \\ then we need to iterate
762 // through the entire string to make sure we detect the unexcaped
763 // double quotes correctly.
767 for ($j=$iPosStart,$iCnt=strlen($read);$j<$iCnt;++
$j) {
771 $bEscaped = !$bEscaped;
793 if ($iPos > strlen($read)) {
801 * @param string $read
803 * @return object AddressStructure object
805 function parseAddress($read, &$i) {
807 for (; $read[$i] != ')'; ++
$i) {
808 $char = strtoupper($read[$i]);
810 case '"': $arg_a[] = $this->parseQuote($read, $i); break;
811 case '{': $arg_a[] = $this->parseLiteral($read, $i); break;
814 if (strtoupper(substr($read, $i, 3)) == 'NIL') {
823 if (count($arg_a) == 4) {
824 $adr = new AddressStructure();
825 $adr->personal
= $arg_a[0];
826 $adr->adl
= $arg_a[1];
827 $adr->mailbox
= $arg_a[2];
828 $adr->host
= $arg_a[3];
836 * @param string $read
838 * @param object Disposition object or empty string
840 function parseDisposition($read, &$i) {
842 for (; $read[$i] != ')'; ++
$i) {
844 case '"': $arg_a[] = $this->parseQuote($read, $i); break;
845 case '{': $arg_a[] = $this->parseLiteral($read, $i); break;
846 case '(': $arg_a[] = $this->parseProperties($read, $i); break;
851 if (isset($arg_a[0])) {
852 $disp = new Disposition($arg_a[0]);
853 if (isset($arg_a[1])) {
854 $disp->properties
= $arg_a[1];
857 return (is_object($disp) ?
$disp : '');
861 * @param string $read
863 * @return object Language object or empty string
865 function parseLanguage($read, &$i) {
866 /* no idea how to process this one without examples */
869 for (; $read[$i] != ')'; ++
$i) {
871 case '"': $arg_a[] = $this->parseQuote($read, $i); break;
872 case '{': $arg_a[] = $this->parseLiteral($read, $i); break;
873 case '(': $arg_a[] = $this->parseProperties($read, $i); break;
878 if (isset($arg_a[0])) {
879 $lang = new Language($arg_a[0]);
880 if (isset($arg_a[1])) {
881 $lang->properties
= $arg_a[1];
884 return (is_object($lang) ?
$lang : '');
888 * Parse message text enclosed in parenthesis
889 * @param string $read
893 function parseParenthesis($read, $i) {
894 for ($i++
; $read[$i] != ')'; ++
$i) {
896 case '"': $this->parseQuote($read, $i); break;
897 case '{': $this->parseLiteral($read, $i); break;
898 case '(': $this->parseProperties($read, $i); break;
906 * Function to fill the message structure in case the
907 * bodystructure is not available
909 * @param string $read
910 * @param string $type0 message part type
911 * @param string $type1 message part subtype
912 * @return string (only when type0 is not message or multipart)
914 function parseMessage($read, $type0, $type1) {
917 $rfc822_header = true;
918 $mime_header = false;
921 $rfc822_header = false;
924 default: return $read;
927 for ($i = 1; $i < $count; ++
$i) {
928 $line = trim($body[$i]);
929 if (($mime_header ||
$rfc822_header) &&
930 (preg_match("/^.*boundary=\"?(.+(?=\")|.+).*/i", $line, $reg))) {
933 $bndreg = str_replace("\\", "\\\\", $bndreg);
934 $bndreg = str_replace("?", "\\?", $bndreg);
935 $bndreg = str_replace("+", "\\+", $bndreg);
936 $bndreg = str_replace(".", "\\.", $bndreg);
937 $bndreg = str_replace("/", "\\/", $bndreg);
938 $bndreg = str_replace("-", "\\-", $bndreg);
939 $bndreg = str_replace("(", "\\(", $bndreg);
940 $bndreg = str_replace(")", "\\)", $bndreg);
941 } else if ($rfc822_header && $line == '') {
942 $rfc822_header = false;
943 if ($msg->type0
== 'multipart') {
948 if ((($line[0] == '-') ||
$rfc822_header) && isset($boundaries[0])) {
949 $cnt = count($boundaries)-1;
950 $bnd = $boundaries[$cnt]['bnd'];
951 $bndreg = $boundaries[$cnt]['bndreg'];
953 $regstr = '/^--'."($bndreg)".".*".'/';
954 if (preg_match($regstr, $line, $reg)) {
955 $bndlen = strlen($reg[1]);
957 if (strlen($line) > ($bndlen +
3)) {
958 if (($line[$bndlen+
2] == '-') && ($line[$bndlen+
3] == '-')) {
963 /* calc offset and return $msg */
964 //$entStr = CalcEntity("$entStr", -1);
965 array_pop($boundaries);
971 //$entStr = CalcEntity("$entStr", 0);
982 * @param array $entity
983 * @param array $alt_order
984 * @param boolean $strict
987 function findDisplayEntity($entity = array(), $alt_order = array('text/plain', 'text/html'), $strict=false) {
989 if ($this->type0
== 'multipart') {
990 if($this->type1
== 'alternative') {
991 $msg = $this->findAlternativeEntity($alt_order);
992 if ( ! is_null($msg) ) {
993 if (count($msg->entities
) == 0) {
994 $entity[] = $msg->entity_id
;
996 $entity = $msg->findDisplayEntity($entity, $alt_order, $strict);
1000 } else if ($this->type1
== 'related') { /* RFC 2387 */
1001 $msgs = $this->findRelatedEntity();
1002 foreach ($msgs as $msg) {
1003 if (count($msg->entities
) == 0) {
1004 $entity[] = $msg->entity_id
;
1006 $entity = $msg->findDisplayEntity($entity, $alt_order, $strict);
1009 if (count($msgs) > 0) {
1012 } else { /* Treat as multipart/mixed */
1013 foreach ($this->entities
as $ent) {
1014 if(!(is_object($ent->header
->disposition
) && strtolower($ent->header
->disposition
->name
) == 'attachment') &&
1015 (!isset($ent->header
->parameters
['filename'])) &&
1016 (!isset($ent->header
->parameters
['name'])) &&
1017 (($ent->type0
!= 'message') && ($ent->type1
!= 'rfc822'))) {
1018 $entity = $ent->findDisplayEntity($entity, $alt_order, $strict);
1023 } else { /* If not multipart, then just compare with each entry from $alt_order */
1024 $type = $this->type0
.'/'.$this->type1
;
1025 // $alt_order[] = "message/rfc822";
1026 foreach ($alt_order as $alt) {
1027 if( ($alt == $type) && isset($this->entity_id
) ) {
1028 if ((count($this->entities
) == 0) &&
1029 (!isset($this->header
->parameters
['filename'])) &&
1030 (!isset($this->header
->parameters
['name'])) &&
1031 isset($this->header
->disposition
) && is_object($this->header
->disposition
) &&
1032 !(is_object($this->header
->disposition
) && strtolower($this->header
->disposition
->name
) == 'attachment')) {
1033 $entity[] = $this->entity_id
;
1040 foreach ($this->entities
as $ent) {
1041 if(!(is_object($ent->header
->disposition
) && strtolower($ent->header
->disposition
->name
) == 'attachment') &&
1042 (($ent->type0
!= 'message') && ($ent->type1
!= 'rfc822'))) {
1043 $entity = $ent->findDisplayEntity($entity, $alt_order, $strict);
1048 if(!$strict && !$found) {
1049 if (($this->type0
== 'text') &&
1050 in_array($this->type1
, array('plain', 'html', 'message')) &&
1051 isset($this->entity_id
)) {
1052 if (count($this->entities
) == 0) {
1053 if (!is_object($this->header
->disposition
) ||
strtolower($this->header
->disposition
->name
) != 'attachment') {
1054 $entity[] = $this->entity_id
;
1063 * @param array $alt_order
1066 function findAlternativeEntity($alt_order) {
1067 /* If we are dealing with alternative parts then we */
1068 /* choose the best viewable message supported by SM. */
1071 foreach($this->entities
as $ent) {
1072 $type = $ent->header
->type0
. '/' . $ent->header
->type1
;
1073 if ($type == 'multipart/related') {
1074 $type = $ent->header
->getParameter('type');
1075 // Mozilla bug. Mozilla does not provide the parameter type.
1076 if (!$type) $type = 'text/html';
1078 $altCount = count($alt_order);
1079 for ($j = $best_view; $j < $altCount; ++
$j) {
1080 if (($alt_order[$j] == $type) && ($j >= $best_view)) {
1092 function findRelatedEntity() {
1094 $related_type = $this->header
->getParameter('type');
1095 // Mozilla bug. Mozilla does not provide the parameter type.
1096 if (!$related_type) $related_type = 'text/html';
1097 $entCount = count($this->entities
);
1098 for ($i = 0; $i < $entCount; ++
$i) {
1099 $type = $this->entities
[$i]->header
->type0
.'/'.$this->entities
[$i]->header
->type1
;
1100 if ($related_type == $type) {
1101 $msgs[] = $this->entities
[$i];
1108 * @param array $exclude_id
1109 * @param array $result
1112 function getAttachments($exclude_id=array(), $result = array()) {
1114 if (($this->type0 == 'message') &&
1115 ($this->type1 == 'rfc822') &&
1116 ($this->entity_id) ) {
1117 $this = $this->entities[0];
1120 if (count($this->entities
)) {
1121 foreach ($this->entities
as $entity) {
1123 foreach ($exclude_id as $excl) {
1124 if ($entity->entity_id
=== $excl) {
1130 if ($entity->type0
== 'multipart') {
1131 $result = $entity->getAttachments($exclude_id, $result);
1132 } else if ($entity->type0
!= 'multipart') {
1133 $result[] = $entity;
1139 foreach ($exclude_id as $excl) {
1140 $exclude = $exclude ||
($this->entity_id
== $excl);
1151 * Add attachment to message object
1152 * @param string $type attachment type
1153 * @param string $name attachment name
1154 * @param string $location path to attachment
1156 function initAttachment($type, $name, $location) {
1157 $attachment = new Message();
1158 $mime_header = new MessageHeader();
1159 $mime_header->setParameter('name', $name);
1160 // FIXME: duplicate code. see ContentType class
1161 $pos = strpos($type, '/');
1163 $mime_header->type0
= substr($type, 0, $pos);
1164 $mime_header->type1
= substr($type, $pos+
1);
1166 $mime_header->type0
= $type;
1168 $attachment->att_local_name
= $location;
1169 $disposition = new Disposition('attachment');
1170 $disposition->properties
['filename'] = $name;
1171 $mime_header->disposition
= $disposition;
1172 $attachment->mime_header
= $mime_header;
1173 $this->entities
[]=$attachment;
1177 * Delete all attachments from this object from disk.
1180 function purgeAttachments() {
1181 if ($this->att_local_name
) {
1182 global $username, $attachment_dir;
1183 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1184 if ( file_exists($hashed_attachment_dir . '/' . $this->att_local_name
) ) {
1185 unlink($hashed_attachment_dir . '/' . $this->att_local_name
);
1188 // recursively delete attachments from entities contained in this object
1189 for ($i=0, $entCount=count($this->entities
);$i< $entCount; ++
$i) {
1190 $this->entities
[$i]->purgeAttachments();