Updating documentation
[squirrelmail.git] / class / mime / Message.class.php
CommitLineData
19d470aa 1<?php
2
3/**
4 * Message.class.php
5 *
9ed80157 6 * This file contains functions needed to handle mime messages.
19d470aa 7 *
4b4abf93 8 * @copyright &copy; 2003-2005 The SquirrelMail Project Team
9 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
883d9cd3 10 * @version $Id$
2b646597 11 * @package squirrelmail
0f459286 12 * @subpackage mime
13 * @since 1.3.2
19d470aa 14 */
15
2b646597 16/**
17 * The object that contains a message
18 *
19 * message is the object that contains messages. It is a recursive
20 * object in that through the $entities variable, it can contain
21 * more objects of type message. See documentation in mime.txt for
22 * a better description of how this works.
23 * @package squirrelmail
0f459286 24 * @subpackage mime
25 * @since 1.3.0
2b646597 26 */
19d470aa 27class Message {
9ed80157 28 /**
29 * rfc822header object
30 * @var object
31 */
32 var $rfc822_header = '';
33 /**
34 * MessageHeader object
35 * @var object
36 */
37 var $mime_header = '';
38 /**
39 * @var mixed
40 */
41 var $flags = '';
42 /**
43 * Media type
44 * @var string
45 */
46 var $type0='';
47 /**
48 * Media subtype
49 * @var string
50 */
51 var $type1='';
52 /**
53 * Nested mime parts
54 * @var array
55 */
56 var $entities = array();
57 /**
58 * Message part id
59 * @var string
60 */
61 var $entity_id = '';
62 /**
63 * Parent message part id
64 * @var string
65 */
66 var $parent_ent;
67 /**
68 * @var mixed
69 */
70 var $entity;
71 /**
72 * @var mixed
73 */
74 var $parent = '';
75 /**
76 * @var string
77 */
78 var $decoded_body='';
79 /**
80 * Message \seen status
81 * @var boolean
82 */
83 var $is_seen = 0;
84 /**
85 * Message \answered status
86 * @var boolean
87 */
88 var $is_answered = 0;
89 /**
90 * Message \deleted status
91 * @var boolean
92 */
93 var $is_deleted = 0;
94 /**
95 * Message \flagged status
96 * @var boolean
97 */
98 var $is_flagged = 0;
99 /**
100 * Message mdn status
101 * @var boolean
102 */
103 var $is_mdnsent = 0;
104 /**
105 * Message text body
106 * @var string
107 */
108 var $body_part = '';
109 /**
110 * Message part offset
111 * for fetching body parts out of raw messages
112 * @var integer
113 */
114 var $offset = 0;
115 /**
116 * Message part length
117 * for fetching body parts out of raw messages
118 * @var integer
119 */
120 var $length = 0;
121 /**
122 * Local attachment filename
123 * location where the tempory attachment
124 * is stored. For use in delivery class.
125 * @var string
126 */
127 var $att_local_name = '';
128
129 /**
130 * @param string $ent entity id
0f459286 131 */
19d470aa 132 function setEnt($ent) {
133 $this->entity_id= $ent;
134 }
135
0f459286 136 /**
9ed80157 137 * Add nested message part
138 * @param object $msg
0f459286 139 */
19d470aa 140 function addEntity ($msg) {
19d470aa 141 $this->entities[] = $msg;
142 }
143
0f459286 144 /**
145 * Get file name used for mime part
146 * @return string file name
147 * @since 1.3.2
148 */
19d470aa 149 function getFilename() {
02474e43 150 $filename = '';
151 $header = $this->header;
152 if (is_object($header->disposition)) {
153 $filename = $header->disposition->getProperty('filename');
154 if (trim($filename) == '') {
155 $name = decodeHeader($header->disposition->getProperty('name'));
156 if (!trim($name)) {
157 $name = $header->getParameter('name');
158 if(!trim($name)) {
159 if (!trim( $header->id )) {
160 $filename = 'untitled-[' . $this->entity_id . ']' ;
161 } else {
162 $filename = 'cid: ' . $header->id;
163 }
164 } else {
165 $filename = $name;
166 }
167 } else {
168 $filename = $name;
169 }
170 }
171 } else {
172 $filename = $header->getParameter('filename');
173 if (!trim($filename)) {
174 $filename = $header->getParameter('name');
175 if (!trim($filename)) {
176 if (!trim( $header->id )) {
177 $filename = 'untitled-[' . $this->entity_id . ']' ;
178 } else {
179 $filename = 'cid: ' . $header->id;
180 }
181 }
182 }
183 }
184 return $filename;
19d470aa 185 }
186
0f459286 187 /**
188 * Add header object to message object.
189 * WARNING: Unfinished code. Don't expect it to work in older sm versions.
190 * @param mixed $read array or string with message headers
191 * @todo FIXME: rfc822header->parseHeader() does not return rfc822header object
192 */
19d470aa 193 function addRFC822Header($read) {
194 $header = new Rfc822Header();
195 $this->rfc822_header = $header->parseHeader($read);
196 }
197
0f459286 198 /**
199 * @param string $ent
9ed80157 200 * @return mixed (object or string?)
0f459286 201 */
19d470aa 202 function getEntity($ent) {
203 $cur_ent = $this->entity_id;
204 $msg = $this;
205 if (($cur_ent == '') || ($cur_ent == '0')) {
206 $cur_ent_a = array();
207 } else {
208 $cur_ent_a = explode('.', $this->entity_id);
209 }
210 $ent_a = explode('.', $ent);
91e0dccc 211
0a408606 212 for ($i = 0,$entCount = count($ent_a) - 1; $i < $entCount; ++$i) {
19d470aa 213 if (isset($cur_ent_a[$i]) && ($cur_ent_a[$i] != $ent_a[$i])) {
214 $msg = $msg->parent;
215 $cur_ent_a = explode('.', $msg->entity_id);
216 --$i;
217 } else if (!isset($cur_ent_a[$i])) {
218 if (isset($msg->entities[($ent_a[$i]-1)])) {
219 $msg = $msg->entities[($ent_a[$i]-1)];
220 } else {
221 $msg = $msg->entities[0];
222 }
223 }
224 if (($msg->type0 == 'message') && ($msg->type1 == 'rfc822')) {
225 /*this is a header for a message/rfc822 entity */
226 $msg = $msg->entities[0];
227 }
228 }
229
230 if (($msg->type0 == 'message') && ($msg->type1 == 'rfc822')) {
231 /*this is a header for a message/rfc822 entity */
232 $msg = $msg->entities[0];
233 }
234
0a408606 235 if (isset($msg->entities[($ent_a[$entCount])-1])) {
236 if (is_object($msg->entities[($ent_a[$entCount])-1])) {
237 $msg = $msg->entities[($ent_a[$entCount]-1)];
19d470aa 238 }
239 }
240
241 return $msg;
242 }
243
0f459286 244 /**
245 * Set message body
246 * @param string $s message body
247 */
19d470aa 248 function setBody($s) {
249 $this->body_part = $s;
250 }
251
0f459286 252 /**
253 * Clean message object
254 */
19d470aa 255 function clean_up() {
256 $msg = $this;
257 $msg->body_part = '';
258
259 foreach ($msg->entities as $m) {
260 $m->clean_up();
261 }
262 }
263
0f459286 264 /**
265 * @return string
266 */
19d470aa 267 function getMailbox() {
268 $msg = $this;
269 while (is_object($msg->parent)) {
270 $msg = $msg->parent;
271 }
272 return $msg->mailbox;
273 }
274
19d470aa 275 /*
276 * Bodystructure parser, a recursive function for generating the
277 * entity-tree with all the mime-parts.
278 *
279 * It follows RFC2060 and stores all the described fields in the
280 * message object.
281 *
282 * Question/Bugs:
283 *
284 * Ask for me (Marc Groot Koerkamp, stekkel@users.sourceforge.net)
0f459286 285 * @param string $read
286 * @param integer $i
287 * @param mixed $sub_msg
288 * @return object Message object
289 * @todo define argument and return types
19d470aa 290 */
df627053 291 function parseStructure($read, &$i, $sub_msg = '') {
e3d6469a 292 $msg = Message::parseBodyStructure($read, $i, $sub_msg);
27dd7a0c 293 if($msg) $msg->setEntIds($msg,false,0);
e3d6469a 294 return $msg;
295 }
91e0dccc 296
0f459286 297 /**
298 * @param object $msg
299 * @param mixed $init
300 * @param integer $i
301 * @todo document me
302 * @since 1.4.0
303 */
e3d6469a 304 function setEntIds(&$msg,$init=false,$i=0) {
305 $iCnt = count($msg->entities);
0b914738 306 if ($init !==false) {
307 $iEntSub = $i+1;
91e0dccc 308 if ($msg->parent->type0 == 'message' &&
0b914738 309 $msg->parent->type1 == 'rfc822' &&
310 $msg->type0 == 'multipart') {
311 $iEntSub = '0';
312 }
313 if ($init) {
314 $msg->entity_id = "$init.$iEntSub";
315 } else {
316 $msg->entity_id = $iEntSub;
317 }
318 } else if ($iCnt) {
319 $msg->entity_id='0';
320 } else {
321 $msg->entity_id='1';
322 }
e3d6469a 323 for ($i=0;$i<$iCnt;++$i) {
91e0dccc 324 $msg->entities[$i]->parent =& $msg;
325 if (strrchr($msg->entity_id, '.') != '.0') {
0b914738 326 $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->entity_id,$i);
327 } else {
328 $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->parent->entity_id,$i);
329 }
330 }
e3d6469a 331 }
332
0f459286 333 /**
334 * @param string $read
335 * @param integer $i
336 * @param mixed $sub_msg
337 * @return object Message object
338 * @todo document me
339 * @since 1.4.0 (code was part of parseStructure() in 1.3.x)
340 */
e3d6469a 341 function parseBodyStructure($read, &$i, $sub_msg = '') {
19d470aa 342 $arg_no = 0;
343 $arg_a = array();
0b914738 344 if ($sub_msg) {
345 $message = $sub_msg;
346 } else {
347 $message = new Message();
348 }
91e0dccc 349
19d470aa 350 for ($cnt = strlen($read); $i < $cnt; ++$i) {
351 $char = strtoupper($read{$i});
352 switch ($char) {
353 case '(':
354 switch($arg_no) {
355 case 0:
356 if (!isset($msg)) {
357 $msg = new Message();
358 $hdr = new MessageHeader();
359 $hdr->type0 = 'text';
360 $hdr->type1 = 'plain';
361 $hdr->encoding = 'us-ascii';
19d470aa 362 } else {
363 $msg->header->type0 = 'multipart';
364 $msg->type0 = 'multipart';
365 while ($read{$i} == '(') {
055659ab 366 $msg->addEntity($msg->parseBodyStructure($read, $i, $msg));
19d470aa 367 }
368 }
369 break;
370 case 1:
371 /* multipart properties */
372 ++$i;
055659ab 373 $arg_a[] = $msg->parseProperties($read, $i);
19d470aa 374 ++$arg_no;
375 break;
376 case 2:
377 if (isset($msg->type0) && ($msg->type0 == 'multipart')) {
378 ++$i;
055659ab 379 $arg_a[] = $msg->parseDisposition($read, $i);
19d470aa 380 } else { /* properties */
055659ab 381 $arg_a[] = $msg->parseProperties($read, $i);
19d470aa 382 }
19d470aa 383 ++$arg_no;
384 break;
385 case 3:
386 if (isset($msg->type0) && ($msg->type0 == 'multipart')) {
387 ++$i;
055659ab 388 $arg_a[]= $msg->parseLanguage($read, $i);
19d470aa 389 }
390 case 7:
391 if (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822')) {
392 $msg->header->type0 = $arg_a[0];
393 $msg->header->type1 = $arg_a[1];
394 $msg->type0 = $arg_a[0];
395 $msg->type1 = $arg_a[1];
396 $rfc822_hdr = new Rfc822Header();
055659ab 397 $msg->rfc822_header = $msg->parseEnvelope($read, $i, $rfc822_hdr);
19d470aa 398 while (($i < $cnt) && ($read{$i} != '(')) {
399 ++$i;
400 }
055659ab 401 $msg->addEntity($msg->parseBodyStructure($read, $i,$msg));
19d470aa 402 }
403 break;
404 case 8:
405 ++$i;
055659ab 406 $arg_a[] = $msg->parseDisposition($read, $i);
19d470aa 407 ++$arg_no;
408 break;
409 case 9:
410 ++$i;
411 if (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) {
055659ab 412 $arg_a[] = $msg->parseDisposition($read, $i);
19d470aa 413 } else {
055659ab 414 $arg_a[] = $msg->parseLanguage($read, $i);
19d470aa 415 }
19d470aa 416 ++$arg_no;
417 break;
418 case 10:
419 if (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) {
420 ++$i;
055659ab 421 $arg_a[] = $msg->parseLanguage($read, $i);
19d470aa 422 } else {
055659ab 423 $i = $msg->parseParenthesis($read, $i);
19d470aa 424 $arg_a[] = ''; /* not yet described in rfc2060 */
425 }
426 ++$arg_no;
427 break;
428 default:
429 /* unknown argument, skip this part */
055659ab 430 $i = $msg->parseParenthesis($read, $i);
19d470aa 431 $arg_a[] = '';
432 ++$arg_no;
433 break;
434 } /* switch */
435 break;
436 case '"':
437 /* inside an entity -> start processing */
055659ab 438 $arg_s = $msg->parseQuote($read, $i);
19d470aa 439 ++$arg_no;
440 if ($arg_no < 3) {
441 $arg_s = strtolower($arg_s); /* type0 and type1 */
442 }
443 $arg_a[] = $arg_s;
444 break;
445 case 'n':
446 case 'N':
447 /* probably NIL argument */
27dd7a0c 448 $tmpnil = strtoupper(substr($read, $i, 4));
449 if ($tmpnil == 'NIL ' || $tmpnil == 'NIL)') {
19d470aa 450 $arg_a[] = '';
451 ++$arg_no;
452 $i += 2;
453 }
454 break;
455 case '{':
456 /* process the literal value */
055659ab 457 $arg_a[] = $msg->parseLiteral($read, $i);
19d470aa 458 ++$arg_no;
459 break;
91e0dccc 460 case '0':
19d470aa 461 case is_numeric($read{$i}):
462 /* process integers */
463 if ($read{$i} == ' ') { break; }
0b914738 464 ++$arg_no;
465 if (preg_match('/^([0-9]+).*/',substr($read,$i), $regs)) {
466 $i += strlen($regs[1])-1;
467 $arg_a[] = $regs[1];
468 } else {
469 $arg_a[] = 0;
470 }
19d470aa 471 break;
472 case ')':
473 $multipart = (isset($msg->type0) && ($msg->type0 == 'multipart'));
474 if (!$multipart) {
475 $shifted_args = (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822')));
476 $hdr->type0 = $arg_a[0];
477 $hdr->type1 = $arg_a[1];
478
479 $msg->type0 = $arg_a[0];
480 $msg->type1 = $arg_a[1];
481 $arr = $arg_a[2];
482 if (is_array($arr)) {
483 $hdr->parameters = $arg_a[2];
484 }
485 $hdr->id = str_replace('<', '', str_replace('>', '', $arg_a[3]));
486 $hdr->description = $arg_a[4];
487 $hdr->encoding = strtolower($arg_a[5]);
488 $hdr->entity_id = $msg->entity_id;
489 $hdr->size = $arg_a[6];
490 if ($shifted_args) {
491 $hdr->lines = $arg_a[7];
492 $s = 1;
493 } else {
494 $s = 0;
495 }
496 $hdr->md5 = (isset($arg_a[7+$s]) ? $arg_a[7+$s] : $hdr->md5);
497 $hdr->disposition = (isset($arg_a[8+$s]) ? $arg_a[8+$s] : $hdr->disposition);
498 $hdr->language = (isset($arg_a[9+$s]) ? $arg_a[9+$s] : $hdr->language);
499 $msg->header = $hdr;
19d470aa 500 } else {
501 $hdr->type0 = 'multipart';
502 $hdr->type1 = $arg_a[0];
503 $msg->type0 = 'multipart';
504 $msg->type1 = $arg_a[0];
505 $hdr->parameters = (isset($arg_a[1]) ? $arg_a[1] : $hdr->parameters);
506 $hdr->disposition = (isset($arg_a[2]) ? $arg_a[2] : $hdr->disposition);
507 $hdr->language = (isset($arg_a[3]) ? $arg_a[3] : $hdr->language);
508 $msg->header = $hdr;
509 }
df627053 510 return $msg;
19d470aa 511 default: break;
512 } /* switch */
19d470aa 513 } /* for */
514 } /* parsestructure */
515
0f459286 516 /**
517 * @param string $read
518 * @param integer $i
519 * @return array
520 */
21a50099 521 function parseProperties($read, &$i) {
19d470aa 522 $properties = array();
523 $prop_name = '';
524
525 for (; $read{$i} != ')'; ++$i) {
526 $arg_s = '';
527 if ($read{$i} == '"') {
21a50099 528 $arg_s = $this->parseQuote($read, $i);
19d470aa 529 } else if ($read{$i} == '{') {
21a50099 530 $arg_s = $this->parseLiteral($read, $i);
19d470aa 531 }
532
533 if ($arg_s != '') {
534 if ($prop_name == '') {
535 $prop_name = strtolower($arg_s);
536 $properties[$prop_name] = '';
537 } else if ($prop_name != '') {
538 $properties[$prop_name] = $arg_s;
539 $prop_name = '';
540 }
541 }
542 }
21a50099 543 return $properties;
19d470aa 544 }
545
0f459286 546 /**
547 * @param string $read
548 * @param integer $i
549 * @param object $hdr MessageHeader object
550 * @return object MessageHeader object
551 */
2bf8f74a 552 function parseEnvelope($read, &$i, $hdr) {
19d470aa 553 $arg_no = 0;
554 $arg_a = array();
3cb8baa6 555 ++$i;
19d470aa 556 for ($cnt = strlen($read); ($i < $cnt) && ($read{$i} != ')'); ++$i) {
19d470aa 557 $char = strtoupper($read{$i});
558 switch ($char) {
559 case '"':
21a50099 560 $arg_a[] = $this->parseQuote($read, $i);
19d470aa 561 ++$arg_no;
562 break;
563 case '{':
21a50099 564 $arg_a[] = $this->parseLiteral($read, $i);
0b914738 565 /* temp bugfix (SM 1.5 will have a working clean version)
566 too much work to implement that version right now */
567// --$i;
19d470aa 568 ++$arg_no;
569 break;
570 case 'N':
571 /* probably NIL argument */
572 if (strtoupper(substr($read, $i, 3)) == 'NIL') {
573 $arg_a[] = '';
574 ++$arg_no;
575 $i += 2;
576 }
577 break;
578 case '(':
579 /* Address structure (with group support)
580 * Note: Group support is useless on SMTP connections
581 * because the protocol doesn't support it
582 */
583 $addr_a = array();
584 $group = '';
585 $a=0;
586 for (; $i < $cnt && $read{$i} != ')'; ++$i) {
587 if ($read{$i} == '(') {
2bf8f74a 588 $addr = $this->parseAddress($read, $i);
19d470aa 589 if (($addr->host == '') && ($addr->mailbox != '')) {
590 /* start of group */
591 $group = $addr->mailbox;
592 $group_addr = $addr;
593 $j = $a;
594 } else if ($group && ($addr->host == '') && ($addr->mailbox == '')) {
595 /* end group */
596 if ($a == ($j+1)) { /* no group members */
597 $group_addr->group = $group;
598 $group_addr->mailbox = '';
599 $group_addr->personal = "$group: Undisclosed recipients;";
600 $addr_a[] = $group_addr;
601 $group ='';
602 }
603 } else {
604 $addr->group = $group;
605 $addr_a[] = $addr;
606 }
607 ++$a;
608 }
609 }
610 $arg_a[] = $addr_a;
611 break;
612 default: break;
613 }
614 }
615
616 if (count($arg_a) > 9) {
19d470aa 617 $d = strtr($arg_a[0], array(' ' => ' '));
618 $d = explode(' ', $d);
91e0dccc 619 if (!$arg_a[1]) $arg_a[1] = _("(no subject)");
19d470aa 620
8750d90e 621 $hdr->date = getTimeStamp($d); /* argument 1: date */
622 $hdr->subject = $arg_a[1]; /* argument 2: subject */
df0db9ce 623 $hdr->from = is_array($arg_a[2]) ? $arg_a[2][0] : ''; /* argument 3: from */
624 $hdr->sender = is_array($arg_a[3]) ? $arg_a[3][0] : ''; /* argument 4: sender */
625 $hdr->replyto = is_array($arg_a[4]) ? $arg_a[4][0] : ''; /* argument 5: reply-to */
19d470aa 626 $hdr->to = $arg_a[5]; /* argument 6: to */
627 $hdr->cc = $arg_a[6]; /* argument 7: cc */
628 $hdr->bcc = $arg_a[7]; /* argument 8: bcc */
629 $hdr->inreplyto = $arg_a[8]; /* argument 9: in-reply-to */
630 $hdr->message_id = $arg_a[9]; /* argument 10: message-id */
631 }
2bf8f74a 632 return $hdr;
19d470aa 633 }
634
0f459286 635 /**
636 * @param string $read
637 * @param integer $i
638 * @return string
639 * @todo document me
640 */
21a50099 641 function parseLiteral($read, &$i) {
19d470aa 642 $lit_cnt = '';
0b914738 643 ++$i;
644 $iPos = strpos($read,'}',$i);
645 if ($iPos) {
646 $lit_cnt = substr($read, $i, $iPos - $i);
647 $i += strlen($lit_cnt) + 3; /* skip } + \r + \n */
648 /* Now read the literal */
649 $s = ($lit_cnt ? substr($read,$i,$lit_cnt): '');
650 $i += $lit_cnt;
651 /* temp bugfix (SM 1.5 will have a working clean version)
652 too much work to implement that version right now */
653 --$i;
654 } else { /* should never happen */
655 $i += 3; /* } + \r + \n */
656 $s = '';
657 }
5520fad8 658 return $s;
19d470aa 659 }
660
0f459286 661 /**
662 * @param string $read
663 * @param integer $i
664 * @return string
665 * @todo document me
666 */
21a50099 667 function parseQuote($read, &$i) {
19d470aa 668 $s = '';
0b914738 669 $iPos = ++$i;
670 while (true) {
671 $iPos = strpos($read,'"',$iPos);
672 if (!$iPos) break;
673 if ($iPos && $read{$iPos -1} != '\\') {
674 $s = substr($read,$i,($iPos-$i));
675 $i = $iPos;
676 break;
677 }
678 ++$iPos;
679 if ($iPos > strlen($read)) {
680 break;
681 }
682 }
21a50099 683 return $s;
19d470aa 684 }
685
0f459286 686 /**
687 * @param string $read
688 * @param integer $i
689 * @return object AddressStructure object
690 */
2bf8f74a 691 function parseAddress($read, &$i) {
19d470aa 692 $arg_a = array();
19d470aa 693 for (; $read{$i} != ')'; ++$i) {
694 $char = strtoupper($read{$i});
695 switch ($char) {
df627053 696 case '"': $arg_a[] = $this->parseQuote($read, $i); break;
697 case '{': $arg_a[] = $this->parseLiteral($read, $i); break;
19d470aa 698 case 'n':
699 case 'N':
700 if (strtoupper(substr($read, $i, 3)) == 'NIL') {
701 $arg_a[] = '';
702 $i += 2;
703 }
704 break;
705 default: break;
706 }
707 }
708
709 if (count($arg_a) == 4) {
710 $adr = new AddressStructure();
711 $adr->personal = $arg_a[0];
712 $adr->adl = $arg_a[1];
713 $adr->mailbox = $arg_a[2];
714 $adr->host = $arg_a[3];
715 } else {
716 $adr = '';
717 }
2bf8f74a 718 return $adr;
19d470aa 719 }
720
0f459286 721 /**
722 * @param string $read
723 * @param integer $i
724 * @param object Disposition object or empty string
725 */
21a50099 726 function parseDisposition($read, &$i) {
19d470aa 727 $arg_a = array();
19d470aa 728 for (; $read{$i} != ')'; ++$i) {
729 switch ($read{$i}) {
21a50099 730 case '"': $arg_a[] = $this->parseQuote($read, $i); break;
731 case '{': $arg_a[] = $this->parseLiteral($read, $i); break;
732 case '(': $arg_a[] = $this->parseProperties($read, $i); break;
19d470aa 733 default: break;
734 }
735 }
736
737 if (isset($arg_a[0])) {
738 $disp = new Disposition($arg_a[0]);
739 if (isset($arg_a[1])) {
740 $disp->properties = $arg_a[1];
741 }
742 }
21a50099 743 return (is_object($disp) ? $disp : '');
19d470aa 744 }
745
0f459286 746 /**
747 * @param string $read
748 * @param integer $i
749 * @return object Language object or empty string
750 */
21a50099 751 function parseLanguage($read, &$i) {
19d470aa 752 /* no idea how to process this one without examples */
753 $arg_a = array();
754
755 for (; $read{$i} != ')'; ++$i) {
756 switch ($read{$i}) {
21a50099 757 case '"': $arg_a[] = $this->parseQuote($read, $i); break;
758 case '{': $arg_a[] = $this->parseLiteral($read, $i); break;
759 case '(': $arg_a[] = $this->parseProperties($read, $i); break;
19d470aa 760 default: break;
761 }
762 }
763
764 if (isset($arg_a[0])) {
765 $lang = new Language($arg_a[0]);
766 if (isset($arg_a[1])) {
767 $lang->properties = $arg_a[1];
768 }
769 }
21a50099 770 return (is_object($lang) ? $lang : '');
19d470aa 771 }
772
0f459286 773 /**
774 * Parse message text enclosed in parenthesis
775 * @param string $read
776 * @param integer $i
777 * @return integer
778 */
19d470aa 779 function parseParenthesis($read, $i) {
780 for (; $read{$i} != ')'; ++$i) {
781 switch ($read{$i}) {
21a50099 782 case '"': $this->parseQuote($read, $i); break;
783 case '{': $this->parseLiteral($read, $i); break;
784 case '(': $this->parseProperties($read, $i); break;
19d470aa 785 default: break;
786 }
787 }
788 return $i;
789 }
790
0f459286 791 /**
792 * Function to fill the message structure in case the
793 * bodystructure is not available
794 * NOT FINISHED YET
795 * @param string $read
796 * @param string $type0 message part type
797 * @param string $type1 message part subtype
9ed80157 798 * @return string (only when type0 is not message or multipart)
0f459286 799 */
19d470aa 800 function parseMessage($read, $type0, $type1) {
801 switch ($type0) {
802 case 'message':
803 $rfc822_header = true;
804 $mime_header = false;
805 break;
806 case 'multipart':
807 $rfc822_header = false;
808 $mime_header = true;
809 break;
810 default: return $read;
811 }
812
813 for ($i = 1; $i < $count; ++$i) {
814 $line = trim($body[$i]);
815 if (($mime_header || $rfc822_header) &&
816 (preg_match("/^.*boundary=\"?(.+(?=\")|.+).*/i", $line, $reg))) {
817 $bnd = $reg[1];
818 $bndreg = $bnd;
819 $bndreg = str_replace("\\", "\\\\", $bndreg);
820 $bndreg = str_replace("?", "\\?", $bndreg);
821 $bndreg = str_replace("+", "\\+", $bndreg);
822 $bndreg = str_replace(".", "\\.", $bndreg);
823 $bndreg = str_replace("/", "\\/", $bndreg);
824 $bndreg = str_replace("-", "\\-", $bndreg);
825 $bndreg = str_replace("(", "\\(", $bndreg);
826 $bndreg = str_replace(")", "\\)", $bndreg);
827 } else if ($rfc822_header && $line == '') {
828 $rfc822_header = false;
829 if ($msg->type0 == 'multipart') {
830 $mime_header = true;
831 }
832 }
833
834 if ((($line{0} == '-') || $rfc822_header) && isset($boundaries[0])) {
835 $cnt = count($boundaries)-1;
836 $bnd = $boundaries[$cnt]['bnd'];
837 $bndreg = $boundaries[$cnt]['bndreg'];
838
839 $regstr = '/^--'."($bndreg)".".*".'/';
840 if (preg_match($regstr, $line, $reg)) {
841 $bndlen = strlen($reg[1]);
842 $bndend = false;
843 if (strlen($line) > ($bndlen + 3)) {
844 if (($line{$bndlen+2} == '-') && ($line{$bndlen+3} == '-')) {
845 $bndend = true;
846 }
847 }
848 if ($bndend) {
849 /* calc offset and return $msg */
850 //$entStr = CalcEntity("$entStr", -1);
851 array_pop($boundaries);
852 $mime_header = true;
853 $bnd_end = true;
854 } else {
855 $mime_header = true;
856 $bnd_end = false;
857 //$entStr = CalcEntity("$entStr", 0);
858 ++$content_indx;
859 }
860 } else {
861 if ($header) { }
862 }
863 }
864 }
865 }
866
0f459286 867 /**
868 * @param array $entity
869 * @param array $alt_order
870 * @param boolean $strict
871 * @return array
872 */
19d470aa 873 function findDisplayEntity($entity = array(), $alt_order = array('text/plain', 'text/html'), $strict=false) {
874 $found = false;
875 if ($this->type0 == 'multipart') {
876 if($this->type1 == 'alternative') {
877 $msg = $this->findAlternativeEntity($alt_order);
878 if (count($msg->entities) == 0) {
879 $entity[] = $msg->entity_id;
880 } else {
881 $entity = $msg->findDisplayEntity($entity, $alt_order, $strict);
882 }
883 $found = true;
884 } else if ($this->type1 == 'related') { /* RFC 2387 */
885 $msgs = $this->findRelatedEntity();
886 foreach ($msgs as $msg) {
887 if (count($msg->entities) == 0) {
888 $entity[] = $msg->entity_id;
889 } else {
890 $entity = $msg->findDisplayEntity($entity, $alt_order, $strict);
891 }
892 }
893 if (count($msgs) > 0) {
894 $found = true;
895 }
896 } else { /* Treat as multipart/mixed */
897 foreach ($this->entities as $ent) {
c102e0a7 898 if(!(is_object($ent->header->disposition) && strtolower($ent->header->disposition->name) == 'attachment') &&
0b914738 899 (!isset($ent->header->parameters['filename'])) &&
900 (!isset($ent->header->parameters['name'])) &&
19d470aa 901 (($ent->type0 != 'message') && ($ent->type1 != 'rfc822'))) {
902 $entity = $ent->findDisplayEntity($entity, $alt_order, $strict);
903 $found = true;
904 }
905 }
906 }
907 } else { /* If not multipart, then just compare with each entry from $alt_order */
908 $type = $this->type0.'/'.$this->type1;
0b914738 909// $alt_order[] = "message/rfc822";
19d470aa 910 foreach ($alt_order as $alt) {
911 if( ($alt == $type) && isset($this->entity_id) ) {
91e0dccc 912 if ((count($this->entities) == 0) &&
c102e0a7 913 (!isset($this->header->parameters['filename'])) &&
914 (!isset($this->header->parameters['name'])) &&
915 isset($this->header->disposition) && is_object($this->header->disposition) &&
916 !(is_object($this->header->disposition) && strtolower($this->header->disposition->name) == 'attachment')) {
19d470aa 917 $entity[] = $this->entity_id;
918 $found = true;
919 }
920 }
921 }
922 }
923 if(!$found) {
924 foreach ($this->entities as $ent) {
c102e0a7 925 if(!(is_object($ent->header->disposition) && strtolower($ent->header->disposition->name) == 'attachment') &&
19d470aa 926 (($ent->type0 != 'message') && ($ent->type1 != 'rfc822'))) {
927 $entity = $ent->findDisplayEntity($entity, $alt_order, $strict);
928 $found = true;
929 }
930 }
931 }
932 if(!$strict && !$found) {
933 if (($this->type0 == 'text') &&
934 in_array($this->type1, array('plain', 'html', 'message')) &&
935 isset($this->entity_id)) {
936 if (count($this->entities) == 0) {
c102e0a7 937 if (!is_object($this->header->disposition) || strtolower($this->header->disposition->name) != 'attachment') {
19d470aa 938 $entity[] = $this->entity_id;
939 }
940 }
941 }
942 }
19d470aa 943 return $entity;
944 }
945
0f459286 946 /**
947 * @param array $alt_order
948 * @return array
949 */
19d470aa 950 function findAlternativeEntity($alt_order) {
951 /* If we are dealing with alternative parts then we */
952 /* choose the best viewable message supported by SM. */
953 $best_view = 0;
954 $entity = array();
955 foreach($this->entities as $ent) {
956 $type = $ent->header->type0 . '/' . $ent->header->type1;
957 if ($type == 'multipart/related') {
958 $type = $ent->header->getParameter('type');
0b914738 959 // Mozilla bug. Mozilla does not provide the parameter type.
960 if (!$type) $type = 'text/html';
19d470aa 961 }
962 $altCount = count($alt_order);
963 for ($j = $best_view; $j < $altCount; ++$j) {
964 if (($alt_order[$j] == $type) && ($j >= $best_view)) {
965 $best_view = $j;
966 $entity = $ent;
967 }
968 }
969 }
19d470aa 970 return $entity;
971 }
972
0f459286 973 /**
974 * @return array
975 */
19d470aa 976 function findRelatedEntity() {
977 $msgs = array();
0b914738 978 $related_type = $this->header->getParameter('type');
979 // Mozilla bug. Mozilla does not provide the parameter type.
980 if (!$related_type) $related_type = 'text/html';
19d470aa 981 $entCount = count($this->entities);
982 for ($i = 0; $i < $entCount; ++$i) {
983 $type = $this->entities[$i]->header->type0.'/'.$this->entities[$i]->header->type1;
0b914738 984 if ($related_type == $type) {
19d470aa 985 $msgs[] = $this->entities[$i];
986 }
987 }
19d470aa 988 return $msgs;
989 }
990
0f459286 991 /**
992 * @param array $exclude_id
993 * @param array $result
994 * @return array
995 */
19d470aa 996 function getAttachments($exclude_id=array(), $result = array()) {
e3d6469a 997/*
91e0dccc 998 if (($this->type0 == 'message') &&
0b914738 999 ($this->type1 == 'rfc822') &&
1000 ($this->entity_id) ) {
19d470aa 1001 $this = $this->entities[0];
1002 }
e3d6469a 1003*/
19d470aa 1004 if (count($this->entities)) {
1005 foreach ($this->entities as $entity) {
1006 $exclude = false;
19d470aa 1007 foreach ($exclude_id as $excl) {
1008 if ($entity->entity_id === $excl) {
1009 $exclude = true;
1010 }
1011 }
1012
1013 if (!$exclude) {
1014 if (($entity->type0 == 'multipart') &&
1015 ($entity->type1 != 'related')) {
1016 $result = $entity->getAttachments($exclude_id, $result);
1017 } else if ($entity->type0 != 'multipart') {
1018 $result[] = $entity;
1019 }
1020 }
1021 }
1022 } else {
1023 $exclude = false;
1024 foreach ($exclude_id as $excl) {
1025 $exclude = $exclude || ($this->entity_id == $excl);
1026 }
1027
1028 if (!$exclude) {
1029 $result[] = $this;
1030 }
1031 }
19d470aa 1032 return $result;
1033 }
91e0dccc 1034
0f459286 1035 /**
1036 * Add attachment to message object
1037 * @param string $type attachment type
1038 * @param string $name attachment name
1039 * @param string $location path to attachment
1040 */
a56f52b9 1041 function initAttachment($type, $name, $location) {
1042 $attachment = new Message();
1043 $mime_header = new MessageHeader();
1044 $mime_header->setParameter('name', $name);
0f459286 1045 // FIXME: duplicate code. see ContentType class
0b914738 1046 $pos = strpos($type, '/');
1047 if ($pos > 0) {
1048 $mime_header->type0 = substr($type, 0, $pos);
1049 $mime_header->type1 = substr($type, $pos+1);
1050 } else {
1051 $mime_header->type0 = $type;
1052 }
1053 $attachment->att_local_name = $location;
1054 $disposition = new Disposition('attachment');
1055 $disposition->properties['filename'] = $name;
1056 $mime_header->disposition = $disposition;
1057 $attachment->mime_header = $mime_header;
1058 $this->entities[]=$attachment;
a56f52b9 1059 }
19d470aa 1060}
1061
91e0dccc 1062?>