fix in unfolding headerlines
[squirrelmail.git] / class / mime.class.php
1 <?php
2
3 /**
4 * mime.class
5 *
6 * Copyright (c) 2002 The SquirrelMail Project Team
7 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 *
9 *
10 * This contains functions needed to handle mime messages.
11 *
12 * $Id$
13 */
14
15
16
17 /*
18 * rdc822_header class
19 * input: header_string or array
20 */
21 class rfc822_header
22 {
23 var $date = '',
24 $subject = '',
25 $from = array(),
26 $sender = '',
27 $reply_to = array(),
28 $to = array(),
29 $cc = array(),
30 $bcc = array(),
31 $in_reply_to = '',
32 $message_id = '',
33 $mime = false,
34 $content_type = '',
35 $disposition = '',
36 $xmailer = '',
37 $priority = 3,
38 $dnt = '',
39 $mlist = array(),
40 $optional_headers = array(); /* only needed for
41 constructing headers in smtp.php */
42
43 function parseHeader($hdr) {
44 if (is_array($hdr)) {
45 $hdr = implode('',$hdr);
46 }
47 /* first we unfold the header */
48 $hdr = str_replace(array("\r\n\t","\r\n "),array('',''),$hdr);
49 /*
50 * now we can make a new header array with each element representing
51 * a headerline
52 */
53 $hdr = explode("\r\n" , $hdr);
54 foreach ($hdr as $line) {
55 $pos = strpos($line,':');
56 if ($pos > 0) {
57 $field = substr($line,0,$pos);
58 $value = trim(substr($line,$pos+1));
59 $value = $this->stripComments($value);
60 $this->parseField($field,$value);
61 }
62 }
63 if ($this->content_type == '') {
64 $this->parseContentType('text/plain; charset=us-ascii');
65 }
66 }
67
68 function stripComments($value) {
69 $cnt = strlen($value);
70 $s = '';
71 $i = 0;
72 while ($i < $cnt) {
73 switch ($value{$i})
74 {
75 case ('"'):
76 $s .= '"';
77 $i++;
78 while ($value{$i} != '"') {
79 if ($value{$i} == '\\') {
80 $s .= '\\';
81 $i++;
82 }
83 $s .= $value{$i};
84 $i++;
85 }
86 $s .= $value{$i};
87 break;
88 case ('('):
89 while ($value{$i} != ')') {
90 if ($value{$i} == '\\') {
91 $i++;
92 }
93 $i++;
94 }
95 break;
96 default:
97 $s .= $value{$i};
98 break;
99 }
100 $i++;
101 }
102 return $s;
103 }
104
105 function parseField($field,$value)
106 {
107 $field = strtolower($field);
108 switch($field)
109 {
110 case ('date'):
111 $d = strtr($value, array(' ' => ' '));
112 $d = explode(' ', $d);
113 $this->date = getTimeStamp($d);
114 break;
115 case ('subject'):
116 $this->subject = $value;
117 break;
118 case ('from'):
119 $this->from = $this->parseAddress($value,true);
120 break;
121 case ('sender'):
122 $this->sender = $this->parseAddress($value);
123 break;
124 case ('reply-to'):
125 $this->reply_to = $this->parseAddress($value, true);
126 break;
127 case ('to'):
128 $this->to = $this->parseAddress($value, true);
129 break;
130 case ('cc'):
131 $this->cc = $this->parseAddress($value, true);
132 break;
133 case ('bcc'):
134 $this->bcc = $this->parseAddress($value, true);
135 break;
136 case ('in-reply-to'):
137 $this->in_reply_to = $value;
138 break;
139 case ('message_id'):
140 $this->message_id = $value;
141 break;
142 case ('disposition-notification-to'):
143 $this->dnt = $this->parseAddress($value);
144 break;
145 case ('mime-Version'):
146 $value = str_replace(' ','',$value);
147 if ($value == '1.0') {
148 $this->mime = true;
149 }
150 break;
151 case ('content-type'):
152 $this->parseContentType($value);
153 break;
154 case ('content-disposition'):
155 $this->parseDisposition($value);
156 break;
157 case ('x-mailer'):
158 $this->xmailer = $value;
159 break;
160 case ('x-priority'):
161 $this->priority = $value;
162 break;
163 case ('list-post'):
164 $this->mlist('post',$value);
165 break;
166 case ('list-reply'):
167 $this->mlist('reply',$value);
168 break;
169 case ('list-subscribe'):
170 $this->mlist('subscribe',$value);
171 break;
172 case ('list-unsubscribe'):
173 $this->mlist('unsubscribe',$value);
174 break;
175 case ('list-archive'):
176 $this->mlist('archive',$value);
177 break;
178 case ('list-owner'):
179 $this->mlist('owner',$value);
180 break;
181 case ('list-help'):
182 $this->mlist('help',$value);
183 break;
184 case ('list-id'):
185 $this->mlist('id',$value);
186 break;
187 default:
188 break;
189 }
190 }
191
192 function parseAddress($address, $ar=false, $addr_ar = array(), $group = '')
193 {
194 $pos = 0;
195 $j = strlen( $address );
196 $name = '';
197 $addr = '';
198 while ( $pos < $j ) {
199 switch ($address{$pos})
200 {
201 case ('"'): /* get the personal name */
202 $pos++;
203 if ($address{$pos} == '"') {
204 $pos++;
205 } else {
206 while ( $pos < $j && $address{$pos} != '"') {
207 if (substr($address, $pos, 2) == '\\"') {
208 $name .= $address{$pos};
209 $pos++;
210 } elseif (substr($address, $pos, 2) == '\\\\') {
211 $name .= $address{$pos};
212 $pos++;
213 }
214 $name .= $address{$pos};
215 $pos++;
216 }
217 }
218 $pos++;
219 break;
220 case ('<'): /* get email address */
221 $addr_start=$pos;
222 $pos++;
223 while ( $pos < $j && $address{$pos} != '>' ) {
224 $addr .= $address{$pos};
225 $pos++;
226 }
227 $pos++;
228 break;
229 case ('('): /* rip off comments */
230 $addr_start=$pos;
231 $pos++;
232 while ( $pos < $j && $address{$pos} != ')' ) {
233 $addr .= $address{$pos};
234 $pos++;
235 }
236 $address_start = substr($address,0,$addr_start);
237 $address_end = substr($address,$pos+1);
238 $address = $address_start . $address_end;
239 $j = strlen( $address );
240 $pos = $addr_start;
241 $pos++;
242 break;
243 case (','): /* we reached a delimiter */
244 if ($addr == '') {
245 $addr = substr($address,0,$pos);
246 } elseif ($name == '') {
247 $name = substr($address,0,$addr_start);
248 }
249
250 $at = strpos($addr, '@');
251 $addr_structure = new address_structure();
252 $addr_structure->personal = $name;
253 $addr_structure->group = $group;
254 if ($at) {
255 $addr_structure->mailbox = substr($addr,0,$at);
256 $addr_structure->host = substr($addr,$at+1);
257 } else {
258 $addr_structure->mailbox = $addr;
259 }
260 $address = trim(substr($address,$pos+1));
261 $j = strlen( $address );
262 $pos = 0;
263 $name = '';
264 $addr = '';
265 $addr_ar[] = $addr_structure;
266 break;
267 case (':'): /* process the group addresses */
268 /* group marker */
269 $group = substr($address,0,$pos);
270 $address = substr($address,$pos+1);
271 $result = $this->parseAddress($address, $ar, $addr_ar, $group);
272 $addr_ar = $result[0];
273 $pos = $result[1];
274 $address = substr($address,$pos);
275 $j = strlen( $address );
276 $group = '';
277 $pos++;
278 break;
279 case (';'):
280 if ($group) {
281 $address = substr($address, 0, $pos-1);
282 }
283 $pos++;
284 break;
285 default:
286 $pos++;
287 break;
288 }
289
290 }
291 if ($addr == '') {
292 $addr = substr($address,0,$pos);
293 } elseif ($name == '') {
294 $name = substr($address,0,$addr_start);
295 }
296 $at = strpos($addr, '@');
297 $addr_structure = new address_structure();
298 $addr_structure->group = $group;
299 if ($at) {
300 $addr_structure->mailbox = trim(substr($addr,0,$at));
301 $addr_structure->host = trim(substr($addr,$at+1));
302 } else {
303 $addr_structure->mailbox = trim($addr);
304 }
305 if ($group && $addr == '') { /* no addresses found in group */
306 $name = "$group: Undisclosed recipients;";
307 $addr_structure->personal = $name;
308 $addr_ar[] = $addr_structure;
309 return (array($addr_ar,$pos+1));
310 } else {
311 $addr_structure->personal = $name;
312 if ($name || $addr) {
313 $addr_ar[] = $addr_structure;
314 }
315 }
316 if ($ar) {
317 return ($addr_ar);
318 } else {
319 return ($addr_ar[0]);
320 }
321 }
322
323 function parseContentType($value) {
324 $pos = strpos($value,';');
325 $props = '';
326 if ($pos > 0) {
327 $type = trim(substr($value,0,$pos));
328 $props = trim(substr($type,$pos+1));
329 } else {
330 $type = $value;
331 }
332 $content_type = new content_type($type);
333 if ($props) {
334 $properties = $this->parseProperties($props);
335 if (!isset($properties['charset'])) {
336 $properties['charset'] = 'us-ascii';
337 }
338 $content_type->properties = $this->parseProperties($props);
339 }
340 $this->content_type = $content_type;
341 }
342
343 function parseProperties($value) {
344 $propArray = explode(';',$value);
345 $propResultArray = array();
346 foreach ($propArray as $prop) {
347 $prop = trim($prop);
348 $pos = strpos($prop,'=');
349 if ($pos>0) {
350 $key = trim(substr($prop,0,$pos));
351 $val = trim(substr($prop,$pos+1));
352 if ($val{0} == '"') {
353 $val = substr($val,1,-1);
354 }
355 $propResultArray[$key] = $val;
356 }
357 }
358 return $propResultArray;
359 }
360
361 function parseDisposition($value) {
362 $pos = strpos($value,';');
363 $props = '';
364 if ($pos > 0) {
365 $name = trim(substr($value,0,$pos));
366 $props = trim(substr($type,$pos+1));
367 } else {
368 $name = $value;
369 }
370 $props_a = $this->parseProperties($props);
371 $disp = new disposition($name);
372 $disp->properties = $props_a;
373 $this->disposition = $disp;
374 }
375
376 function mlist($field, $value) {
377 $res_a = array();
378 $value_a = explode(',',$value);
379 foreach ($value_a as $val) {
380 $val = trim($val);
381 if ($val{0} == '<') {
382 $val = substr($val,1,-1);
383 }
384 if (substr($val,0,7) == 'mailto:') {
385 $res_a['mailto'] = substr($val,7);
386 } else {
387 $res_a['href'] = $val;
388 }
389 }
390 $this->mlist[$field] = $res_a;
391 }
392
393 /*
394 * function to get the addres strings out of the header.
395 * Arguments: string or array of strings !
396 * example1: header->getAddr_s('to').
397 * example2: header->getAddr_s(array('to','cc','bcc'))
398 */
399 function getAddr_s($arr, $separator=', ') {
400 if (is_array($arr)) {
401 $s = '';
402 foreach($arr as $arg ) {
403 $result = $this->getAddr_s($arg);
404 if ($result) {
405 $s .= $separator . $result;
406 }
407 }
408 if ($s) $s = substr($s,2);
409 return $s;
410 } else {
411 $s = '';
412 eval('$addr = $this->'.$arr.';') ;
413 if (is_array($addr)) {
414 foreach ($addr as $addr_o) {
415 if (is_object($addr_o)) {
416 $s .= $addr_o->getAddress() . $separator;
417 }
418 }
419 $s = substr($s,0,-strlen($separator));
420 } else {
421 if (is_object($addr)) {
422 $s .= $addr->getAddress();
423 }
424 }
425 return $s;
426 }
427 }
428
429 function getAddr_a($arg, $excl_arr=array(), $arr = array()) {
430 if (is_array($arg)) {
431 foreach($arg as $argument ) {
432 $arr = $this->getAddr_a($argument, $excl_arr, $arr);
433 }
434 return $arr;
435 } else {
436 eval('$addr = $this->'.$arg.';') ;
437 if (is_array($addr)) {
438 foreach ($addr as $addr_o) {
439 if (is_object($addr_o)) {
440 if (isset($addr_o->host) && $addr_o->host !='') {
441 $email = $addr_o->mailbox.'@'.$addr_o->host;
442 } else {
443 $email = $addr_o->mailbox;
444 }
445 $email = strtolower($email);
446 if ($email && !isset($arr[$email]) && !isset($excl_arr[$email])) {
447 $arr[$email] = $addr_o->personal;
448 }
449 }
450 }
451 } else {
452 if (is_object($addr)) {
453 if (isset($addr->host)) {
454 $email = $addr->mailbox.'@'.$addr->host;
455 } else {
456 $email = $addr->mailbox;
457 }
458 $email = strtolower($email);
459 if ($email && !isset($arr[$email]) && !isset($excl_arr[$email])) {
460 $arr[$email] = $addr->personal;
461 }
462 }
463 }
464 return $arr;
465 }
466 }
467
468 function getContentType($type0, $type1) {
469 $type0 = $this->content_type->type0;
470 $type1 = $this->content_type->type1;
471 return $this->content_type->properties;
472 }
473 }
474
475 class msg_header {
476 /** msg_header contains all variables available in a bodystructure **/
477 /** entity like described in rfc2060 **/
478
479 var $type0 = '',
480 $type1 = '',
481 $parameters = array(),
482 $id = 0,
483 $description = '',
484 $encoding='',
485 $size = 0,
486 $md5='',
487 $disposition = '',
488 $language='';
489
490 /*
491 * returns addres_list of supplied argument
492 * arguments: array('to', 'from', ...) or just a string like 'to'.
493 * result: string: address1, addres2, ....
494 */
495
496 function setVar($var, $value) {
497 $this->{$var} = $value;
498 }
499
500 function getParameter($par) {
501 $value = strtolower($par);
502 if (isset($this->parameters[$par])) {
503 return $this->parameters[$par];
504 }
505 return '';
506 }
507
508 function setParameter($parameter, $value) {
509 $this->parameters[strtolower($parameter)] = $value;
510 }
511 }
512
513
514
515 class address_structure {
516 var $personal = '', $adl = '', $mailbox = '', $host = '', $group = '';
517
518 function getAddress($full=true) {
519 if (is_object($this)) {
520 if (isset($this->host) && $this->host !='') {
521 $email = $this->mailbox.'@'.$this->host;
522 } else {
523 $email = $this->mailbox;
524 }
525 if (trim($this->personal) !='') {
526 if ($email) {
527 $addr = '"' . $this->personal . '" <' .$email.'>';
528 } else {
529 $addr = $this->personal;
530 }
531 $best_dpl = $this->personal;
532 } else {
533 $addr = $email;
534 $best_dpl = $email;
535 }
536 if ($full) {
537 return $addr;
538 } else {
539 return $best_dpl;
540 }
541 } else return '';
542 }
543 }
544
545 class message {
546 /** message is the object that contains messages. It is a recursive
547 object in that through the $entities variable, it can contain
548 more objects of type message. See documentation in mime.txt for
549 a better description of how this works.
550 **/
551 var $rfc822_header = '',
552 $mime_header = '',
553 $flags = '',
554 $type0='',
555 $type1='',
556 $entities = array(),
557 $parent_ent, $entity,
558 $parent = '', $decoded_body='',
559 $is_seen = 0, $is_answered = 0, $is_deleted = 0, $is_flagged = 0,
560 $is_mdnsent = 0,
561 $body_part = '';
562
563 function setEnt($ent) {
564 $this->entity_id= $ent;
565 }
566
567 function addEntity ($msg) {
568 $msg->parent = &$this;
569 $this->entities[] = $msg;
570 }
571
572 function addRFC822Header($read) {
573 $header = new msg_header();
574 $this->header = sqimap_parse_RFC822Header($read,$header);
575 }
576
577 function getEntity($ent) {
578
579 $cur_ent = $this->entity_id;
580 $msg = $this;
581 if ($cur_ent == '' || $cur_ent == '0') {
582 $cur_ent_a = array();
583 } else {
584 $cur_ent_a = explode('.',$this->entity_id);
585 }
586 $ent_a = explode('.',$ent);
587
588 $cnt = count($ent_a);
589
590 for ($i=0;$i<$cnt -1;$i++) {
591 if (isset($cur_ent_a[$i]) && $cur_ent_a[$i] != $ent_a[$i]) {
592 $msg = $msg->parent;
593 $cur_ent_a = explode('.',$msg->entity_id);
594 $i--;
595 } else if (!isset($cur_ent_a[$i])) {
596 if (isset($msg->entities[($ent_a[$i]-1)])) {
597 $msg = $msg->entities[($ent_a[$i]-1)];
598 } else {
599 $msg = $msg->entities[0];
600 }
601 }
602 if ($msg->type0 == 'message' && $msg->type1 == 'rfc822') {
603 /*this is a header for a message/rfc822 entity */
604 $msg = $msg->entities[0];
605 }
606 }
607
608 if ($msg->type0 == 'message' && $msg->type1 == 'rfc822') {
609 /*this is a header for a message/rfc822 entity */
610 $msg = $msg->entities[0];
611 }
612
613 if (isset($msg->entities[($ent_a[$cnt-1])-1])) {
614 $msg = $msg->entities[($ent_a[$cnt-1]-1)];
615 }
616
617 return $msg;
618 }
619
620 function setBody($s) {
621 $this->body_part = $s;
622 }
623
624 function clean_up() {
625 $msg = $this;
626 $msg->body_part = '';
627 $i=0;
628 while ( isset($msg->entities[$i])) {
629 $msg->entities[$i]->clean_up();
630 $i++;
631 }
632 }
633
634 function getMailbox() {
635 $msg = $this;
636 while (is_object($msg->parent)) {
637 $msg = $msg->parent;
638 }
639 return $msg->mailbox;
640 }
641
642 /*
643 * Bodystructure parser, a recursive function for generating the
644 * entity-tree with all the mime-parts.
645 *
646 * It follows RFC2060 and stores all the described fields in the
647 * message object.
648 *
649 * Question/Bugs:
650 *
651 * Ask for me (Marc Groot Koerkamp, stekkel@users.sourceforge.net.
652 *
653 */
654 function &parseStructure($read, $i=0, $message = false) {
655 $arg_no = 0;
656 $arg_a = array();
657 $cnt = strlen($read);
658 while ($i < $cnt) {
659 $char = strtoupper($read{$i});
660 switch ($char) {
661 case '(':
662 if ($arg_no == 0 ) {
663 if (!isset($msg)) {
664 $msg = new message();
665 $hdr = new msg_header();
666 $hdr->type0 = 'text';
667 $hdr->type1 = 'plain';
668 $hdr->encoding = 'us-ascii';
669
670 if ($this->type0 == 'message' && $this->type1 == 'rfc822') {
671 $msg->entity_id = $this->entity_id .'.0'; /* header of message/rfc822 */
672 } else if (isset($this->entity_id) && $this->entity_id !='') {
673 $ent_no = count($this->entities)+1;
674 $par_ent = substr($this->entity_id,-2);
675 if ($par_ent{0} == '.') {
676 $par_ent = $par_ent{1};
677 }
678 if ($par_ent == '0') {
679 $ent_no = count($this->entities)+1;
680 if ($ent_no > 0) {
681 $ent = substr($this->entity_id,0,strrpos($this->entity_id,'.'));
682 if ($ent) {
683 $ent = $ent . ".$ent_no";
684 } else {
685 $ent = $ent_no;
686 }
687 $msg->entity_id = $ent;
688 } else {
689 $msg->entity_id = $ent_no;
690 }
691 } else {
692 $ent = $this->entity_id . ".$ent_no";
693 $msg->entity_id = $ent;
694 }
695 } else {
696 $msg->entity_id = '0';
697 }
698 } else {
699 $msg->header->type0 = 'multipart';
700 $msg->type0 = 'multipart';
701 while ($read{$i} == '(') {
702 $msg->addEntity($msg->parseStructure($read,&$i));
703 }
704 }
705 } else {
706 switch ($arg_no)
707 {
708 case 1:
709 /* multipart properties */
710 $i++;
711 $arg_a[] = $this->parseProperties($read,&$i);
712 $arg_no++;
713 break;
714 case 2:
715 if (isset($msg->type0) && $msg->type0 == 'multipart') {
716 $i++;
717 $arg_a[]= $msg->parseDisposition($read,&$i);
718 } else { /* properties */
719 /* properties */
720 $arg_a[] = $msg->parseProperties($read,&$i);
721 }
722 $arg_no++;
723 break;
724 case 3:
725 if (isset($msg->type0) && $msg->type0 == 'multipart') {
726 $i++;
727 $arg_a[]= $msg->parseLanguage($read,&$i);
728 }
729 case 7:
730 if ($arg_a[0] == 'message' && $arg_a[1] == 'rfc822') {
731
732 $msg->header->type0 = $arg_a[0];
733 $msg->type0 = $arg_a[0];
734
735 $msg->header->type1 = $arg_a[1];
736 $msg->type1 = $arg_a[1];
737 $rfc822_hdr = new rfc822_header();
738 $msg->parseEnvelope($read,&$i,&$rfc822_hdr);
739 $msg->rfc822_header = $rfc822_hdr;
740 $i++;
741 while ($i < $cnt && $read{$i} != '(') {
742 $i++;
743 }
744 $msg->addEntity($msg->parseStructure($read,&$i));
745 }
746 break;
747 case 8:
748 $i++;
749 $arg_a[] = $msg->parseDisposition($read,&$i);
750 $arg_no++;
751 break;
752 case 9:
753 if ($arg_a[0] == 'text' ||
754 ($arg_a[0] == 'message' && $arg_a[1] == 'rfc822')) {
755 $i++;
756 $arg_a[] = $msg->parseDisposition($read,&$i);
757 } else {
758 $i++;
759 $arg_a[] = $msg->parseLanguage($read,&$i);
760 }
761 $arg_no++;
762 break;
763 case 10:
764 if ($arg_a[0] == 'text' ||
765 ($arg_a[0] == 'message' && $arg_a[1] == 'rfc822')) {
766 $i++;
767 $arg_a[] = $msg->parseLanguage($read,&$i);
768 } else {
769 $msg->parseParenthesis($read,&$i);
770 $arg_a[] = ''; /* not yet desribed in rfc2060 */
771 }
772 $arg_no++;
773 break;
774 default:
775 /* unknown argument, skip this part */
776 $msg->parseParenthesis($read,&$i);
777 $arg_a[] = '';
778 $arg_no++;
779 break;
780 } /* switch */
781 }
782 break;
783 case '"':
784 /* inside an entity -> start processing */
785 $debug = substr($read,$i,20);
786 $arg_s = $msg->parseQuote($read,&$i);
787 $arg_no++;
788 if ($arg_no < 3) $arg_s = strtolower($arg_s); /* type0 and type1 */
789 $arg_a[] = $arg_s;
790 break;
791 case 'n':
792 case 'N':
793 /* probably NIL argument */
794 if (strtoupper(substr($read,$i,4)) == 'NIL ' ||
795 strtoupper(substr($read,$i,4)) == 'NIL)') {
796 $arg_a[] = '';
797 $arg_no++;
798 $i = $i+2;
799 }
800 break;
801 case '{':
802 /* process the literal value */
803 $arg_a[] = $msg->parseLiteral($read,&$i);
804 $arg_no++;
805 break;
806 case (is_numeric($read{$i}) ):
807 /* process integers */
808 if ($read{$i} == ' ') break;
809 $arg_s = $read{$i};;
810 $i++;
811 while (preg_match('/^[0-9]{1}$/',$read{$i})) {
812 $arg_s .= $read{$i};
813 $i++;
814 }
815 $arg_no++;
816 $arg_a[] = $arg_s;
817 break;
818 case ')':
819 if (isset($msg->type0) && $msg->type0 == 'multipart') {
820 $multipart = true;
821 } else {
822 $multipart = false;
823 }
824 if (!$multipart) {
825 if ($arg_a[0] == 'text' ||
826 ($arg_a[0] == 'message' && $arg_a[1] == 'rfc822')) {
827 $shifted_args = true;
828 } else {
829 $shifted_args = false;
830 }
831 $hdr->type0 = $arg_a[0];
832 $hdr->type1 = $arg_a[1];
833
834 $msg->type0 = $arg_a[0];
835 $msg->type1 = $arg_a[1];
836
837 $arr = $arg_a[2];
838 if (is_array($arr)) {
839 $hdr->parameters = $arg_a[2];
840 }
841 $hdr->id = str_replace( '<', '', str_replace( '>', '', $arg_a[3] ) );
842 $hdr->description = $arg_a[4];
843 $hdr->encoding = strtolower($arg_a[5]);
844 $hdr->entity_id = $msg->entity_id;
845 $hdr->size = $arg_a[6];
846 if ($shifted_args) {
847 $hdr->lines = $arg_a[7];
848 if (isset($arg_a[8])) {
849 $hdr->md5 = $arg_a[8];
850 }
851 if (isset($arg_a[9])) {
852 $hdr->disposition = $arg_a[9];
853 }
854 if (isset($arg_a[10])) {
855 $hdr->language = $arg_a[10];
856 }
857 } else {
858 if (isset($arg_a[7])) {
859 $hdr->md5 = $arg_a[7];
860 }
861 if (isset($arg_a[8])) {
862 $hdr->disposition = $arg_a[8];
863 }
864 if (isset($arg_a[9])) {
865 $hdr->language = $arg_a[9];
866 }
867 }
868 $msg->header = $hdr;
869 $arg_no = 0;
870 $i++;
871 if (substr($msg->entity_id,-2) == '.0' && $msg->type0 !='multipart') {
872 $msg->entity_id++;
873 }
874 return $msg;
875 } else {
876 $hdr->type0 = 'multipart';
877 $hdr->type1 = $arg_a[0];
878
879 $msg->type0 = 'multipart';
880 $msg->type1 = $arg_a[0];
881 if (is_array($arg_a[1])) {
882 $hdr->parameters = $arg_a[1];
883 }
884 if (isset($arg_a[2])) {
885 $hdr->disposition = $arg_a[2];
886 }
887 if (isset($arg_a[3])) {
888 $hdr->language = $arg_a[3];
889 }
890 $msg->header = $hdr;
891 return $msg;
892 }
893 default:
894 break;
895 } /* switch */
896 $i++;
897 } /* while */
898 } /* parsestructure */
899
900 function parseProperties($read, $i) {
901 $properties = array();
902 $arg_s = '';
903 $prop_name = '';
904 while ($read{$i} != ')') {
905 if ($read{$i} == '"') {
906 $arg_s = $this->parseQuote($read,&$i);
907 } else if ($read{$i} == '{') {
908 $arg_s = $this->parseLiteral($read,&$i);
909 }
910 if ($prop_name == '' && $arg_s) {
911 $prop_name = strtolower($arg_s);
912 $properties[$prop_name] = '';
913 $arg_s = '';
914 } elseif ($prop_name != '' && $arg_s != '') {
915 $properties[$prop_name] = $arg_s;
916 $prop_name = '';
917 $arg_s = '';
918 }
919 $i++;
920 }
921 return $properties;
922 }
923
924 function parseEnvelope($read, $i, $hdr) {
925 $arg_no = 0;
926 $arg_a = array();
927 $cnt = strlen($read);
928 while ($i< $cnt && $read{$i} != ')') {
929 $i++;
930 $char = strtoupper($read{$i});
931 switch ($char)
932 {
933 case '"':
934 $arg_a[] = $this->parseQuote($read,&$i);
935 $arg_no++;
936 break;
937 case '{':
938 $arg_a[] = $this->parseLiteral($read,&$i);
939 $arg_no++;
940 break;
941 case 'N':
942 /* probably NIL argument */
943 if (strtoupper(substr($read,$i,3)) == 'NIL') {
944 $arg_a[] = '';
945 $arg_no++;
946 $i = $i+2;
947 }
948 break;
949 case '(':
950 /* Address structure
951 * With group support.
952 * Note: Group support is useless on SMTP connections
953 * because the protocol doesn't support it
954 */
955 $addr_a = array();
956 $group = '';
957 $a=0;
958 while ($i < $cnt && $read{$i} != ')') {
959 if ($read{$i} == '(') {
960 $addr = $this->parseAddress($read,&$i);
961 if ($addr->host == '' && $addr->mailbox != '') {
962 /* start of group */
963 $group = $addr->mailbox;
964 $group_addr = $addr;
965 $j = $a;
966 } elseif ($group && $addr->host == '' && $addr->mailbox == '') {
967 /* end group */
968 if ($a == $j+1) { /* no group members */
969 $group_addr->group = $group;
970 $group_addr->mailbox = '';
971 $group_addr->personal = "$group: Undisclosed recipients;";
972 $addr_a[] = $group_addr;
973 $group ='';
974 }
975 } else {
976 $addr->group = $group;
977 $addr_a[] = $addr;
978 }
979 $a++;
980 }
981 $i++;
982 }
983 $arg_a[] = $addr_a;
984 break;
985 default:
986 break;
987 }
988 $i++;
989 }
990 if (count($arg_a) > 9) {
991 /* argument 1: date */
992 $d = strtr($arg_a[0], array(' ' => ' '));
993 $d = explode(' ', $d);
994 $hdr->date = getTimeStamp($d);
995 /* argument 2: subject */
996 if (!trim($arg_a[1])) {
997 $arg_a[1]= _("(no subject)");
998 }
999 $hdr->subject = $arg_a[1];
1000 /* argument 3: from */
1001 $hdr->from = $arg_a[2][0];
1002 /* argument 4: sender */
1003 $hdr->sender = $arg_a[3][0];
1004 /* argument 5: reply-to */
1005 $hdr->replyto = $arg_a[4][0];
1006 /* argument 6: to */
1007 $hdr->to = $arg_a[5];
1008 /* argument 7: cc */
1009 $hdr->cc = $arg_a[6];
1010 /* argument 8: bcc */
1011 $hdr->bcc = $arg_a[7];
1012 /* argument 9: in-reply-to */
1013 $hdr->inreplyto = $arg_a[8];
1014 /* argument 10: message-id */
1015 $hdr->message_id = $arg_a[9];
1016 }
1017 }
1018
1019 function parseLiteral($read, $i) {
1020 $lit_cnt = '';
1021 $i++;
1022 while ($read{$i} != '}') {
1023 $lit_cnt .= $read{$i};
1024 $i++;
1025 }
1026 $lit_cnt +=2; /* add the { and } characters */
1027 $s = '';
1028 for ($j = 0; $j < $lit_cnt; $j++) {
1029 $i++;
1030 $s .= $read{$i};
1031 }
1032 return $s;
1033 }
1034
1035 function parseQuote($read, $i) {
1036 $i++;
1037 $s = '';
1038 while ($read{$i} != '"') {
1039 if ($read{$i} == '\\') {
1040 $i++;
1041 }
1042 $s .= $read{$i};
1043 $i++;
1044 }
1045 return $s;
1046 }
1047
1048 function parseAddress($read, $i) {
1049 $arg_a = array();
1050 while ($read{$i} != ')' ) { //&& $i < count($read)) {
1051 $char = strtoupper($read{$i});
1052 switch ($char)
1053 {
1054 case '"':
1055 $arg_a[] = $this->parseQuote($read,&$i);
1056 break;
1057 case '{':
1058 $arg_a[] = $this->parseLiteral($read,&$i);
1059 break;
1060 case 'n':
1061 case 'N':
1062 if (strtoupper(substr($read,$i,3)) == 'NIL') {
1063 $arg_a[] = '';
1064 $i = $i+2;
1065 }
1066 break;
1067 default:
1068 break;
1069 }
1070 $i++;
1071 }
1072 if (count($arg_a) == 4) {
1073 $adr = new address_structure();
1074 $adr->personal = $arg_a[0];
1075 $adr->adl = $arg_a[1];
1076 $adr->mailbox = $arg_a[2];
1077 $adr->host = $arg_a[3];
1078 } else {
1079 $adr = '';
1080 }
1081 return $adr;
1082 }
1083
1084 function parseDisposition($read,&$i) {
1085 $arg_a = array();
1086 while ($read{$i} != ')') {
1087 switch ($read{$i})
1088 {
1089 case '"':
1090 $arg_a[] = $this->parseQuote($read,&$i);
1091 break;
1092 case '{':
1093 $arg_a[] = $this->parseLiteral($read,&$i);
1094 break;
1095 case '(':
1096 $arg_a[] = $this->parseProperties($read,&$i);
1097 break;
1098 default:
1099 break;
1100 }
1101 $i++;
1102 }
1103 if (isset($arg_a[0])) {
1104 $disp = new disposition($arg_a[0]);
1105 if (isset($arg_a[1])) {
1106 $disp->properties = $arg_a[1];
1107 }
1108 }
1109 if (is_object($disp)) {
1110 return $disp;
1111 }
1112 }
1113
1114 function parseLanguage($read,&$i) {
1115 /* no idea how to process this one without examples */
1116 $arg_a = array();
1117 while ($read{$i} != ')') {
1118 switch ($read{$i})
1119 {
1120 case '"':
1121 $arg_a[] = $this->parseQuote($read,&$i);
1122 break;
1123 case '{':
1124 $arg_a[] = $this->parseLiteral($read,&$i);
1125 break;
1126 case '(':
1127 $arg_a[] = $this->parseProperties($read,&$i);
1128 break;
1129 default:
1130 break;
1131 }
1132 $i++;
1133 }
1134 if (isset($arg_a[0])) {
1135 $lang = new language($arg_a[0]);
1136 if (isset($arg_a[1])) {
1137 $lang->properties = $arg_a[1];
1138 }
1139 }
1140 if (is_object($lang)) {
1141 return $lang;
1142 } else {
1143 return '';
1144 }
1145 }
1146
1147 function parseParenthesis($read,&$i) {
1148 while ($read{$i} != ')') {
1149 switch ($read{$i})
1150 {
1151 case '"':
1152 $this->parseQuote($read,&$i);
1153 break;
1154 case '{':
1155 $this->parseLiteral($read,&$i);
1156 break;
1157 case '(':
1158 $this->parseParenthesis($read,&$i);
1159 break;
1160 default:
1161 break;
1162 }
1163 $i++;
1164 }
1165 }
1166
1167 function findDisplayEntity ($entity = array(), $alt_order = array('text/plain','text/html')) {
1168 $found = false;
1169 $type = $this->type0.'/'.$this->type1;
1170 if ( $type == 'multipart/alternative') {
1171 $msg = $this->findAlternativeEntity($alt_order);
1172 if (count($msg->entities) == 0) {
1173 $entity[] = $msg->entity_id;
1174 } else {
1175 $msg->findDisplayEntity(&$entity, $alt_order);
1176 }
1177 $found = true;
1178 } else if ( $type == 'multipart/related') {
1179 $msgs = $this->findRelatedEntity();
1180 for ($i = 0; $i < count($msgs); $i++) {
1181 $msg = $msgs[$i];
1182 if (count($msg->entities) == 0) {
1183 $entity[] = $msg->entity_id;
1184 } else {
1185 $msg->findDisplayEntity(&$entity,$alt_order);
1186 }
1187 $found = true;
1188 }
1189 } else if ( $this->type0 == 'text' &&
1190 ( $this->type1 == 'plain' ||
1191 $this->type1 == 'html' ||
1192 $this->type1 == 'message') &&
1193 isset($this->entity_id) ) {
1194 if (count($this->entities) == 0) {
1195 if (strtolower($this->header->disposition->name) != 'attachment') {
1196 $entity[] = $this->entity_id;
1197 }
1198 }
1199 }
1200 $i = 0;
1201 while ( isset($this->entities[$i]) && !$found &&
1202 (strtolower($this->entities[$i]->header->disposition->name)
1203 != 'attachment') &&
1204 ($this->entities[$i]->type0 != 'message' &&
1205 $this->entities[$i]->type1 != 'rfc822' )
1206 )
1207 {
1208 $this->entities[$i]->findDisplayEntity(&$entity, $alt_order);
1209 $i++;
1210 }
1211
1212 if ( !isset($entity[0]) ) {
1213 $entity[]="";
1214 }
1215 return( $entity );
1216 }
1217
1218 function findAlternativeEntity ($alt_order) {
1219 /* if we are dealing with alternative parts then we choose the best
1220 * viewable message supported by SM.
1221 */
1222 $best_view = 0;
1223 $ent_id = 0;
1224 $k = 0;
1225 for ($i = 0; $i < count($this->entities); $i ++) {
1226 $type = $this->entities[$i]->header->type0.'/'.$this->entities[$i]->header->type1;
1227 if ($type == 'multipart/related') {
1228 $type = $this->entities[$i]->header->getParameter('type');
1229 }
1230 for ($j = $k; $j < count($alt_order); $j++) {
1231 if ($alt_order[$j] == $type && $j > $best_view) {
1232 $best_view = $j;
1233 $ent_id = $i;
1234 $k = $j;
1235 }
1236 }
1237 }
1238 return $this->entities[$ent_id];
1239 }
1240
1241 function findRelatedEntity () {
1242 $msgs = array();
1243 for ($i = 0; $i < count($this->entities); $i ++) {
1244 $type = $this->entities[$i]->header->type0.'/'.$this->entities[$i]->header->type1;
1245 if ($this->header->getParameter('type') == $type) {
1246 $msgs[] = $this->entities[$i];
1247 }
1248 }
1249 return $msgs;
1250 }
1251
1252 function getAttachments($exclude_id=array(), $result = array()) {
1253 if ($this->type0 == 'message' && $this->type1 == 'rfc822') {
1254 $this = $this->entities[0];
1255 }
1256 if (count($this->entities)) {
1257 foreach ($this->entities as $entity) {
1258 $exclude = false;
1259 foreach ($exclude_id as $excl) {
1260 if ($entity->entity_id == $excl) {
1261 $exclude = true;
1262 }
1263 }
1264 if (!$exclude) {
1265 if ($entity->type0 == 'multipart' &&
1266 $entity->type1 != 'related') {
1267 $result = $entity->getAttachments($exclude_id, $result);
1268 } else if ($entity->type0 != 'multipart') {
1269 $result[] = $entity;
1270 }
1271 }
1272 }
1273 } else {
1274 $exclude = false;
1275 foreach ($exclude_id as $excl) {
1276 if ($this->entity_id == $excl) {
1277 $exclude = true;
1278 }
1279 }
1280 if (!$exclude) {
1281 $result[] = $this;
1282 }
1283 }
1284 return $result;
1285 }
1286
1287 }
1288
1289 class disposition {
1290 function disposition($name) {
1291 $this->name = $name;
1292 $this->properties = array();
1293 }
1294 }
1295
1296 class language {
1297 function language($name) {
1298 $this->name = $name;
1299 $this->properties = array();
1300 }
1301 }
1302
1303 class content_type {
1304 var $type0='text',
1305 $type1='plain',
1306 $properties='';
1307 function content_type($type) {
1308 $pos = strpos($type,'/');
1309 if ($pos > 0) {
1310 $this->type0 = substr($type,0,$pos);
1311 $this->type1 = substr($type,$pos+1);
1312 } else {
1313 $this->type0 = $type;
1314 }
1315 $this->properties = array();
1316 }
1317 }
1318
1319 ?>