1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16: class IMP_Contents
17: {
18:
19: const SUMMARY_BYTES = 1;
20: const SUMMARY_SIZE = 2;
21: const SUMMARY_ICON = 4;
22: const SUMMARY_ICON_RAW = 16384;
23: const SUMMARY_DESCRIP_LINK = 8;
24: const SUMMARY_DESCRIP_NOLINK = 16;
25: const SUMMARY_DESCRIP_NOLINK_NOHTMLSPECCHARS = 32;
26: const SUMMARY_DOWNLOAD = 64;
27: const SUMMARY_DOWNLOAD_NOJS = 128;
28: const SUMMARY_DOWNLOAD_ZIP = 256;
29: const SUMMARY_IMAGE_SAVE = 512;
30: const SUMMARY_PRINT = 1024;
31: const SUMMARY_PRINT_STUB = 2048;
32: const SUMMARY_STRIP_LINK = 4096;
33: const SUMMARY_STRIP_STUB = 8192;
34:
35:
36: const RENDER_FULL = 1;
37: const RENDER_INLINE = 2;
38: const RENDER_INLINE_DISP_NO = 4;
39: const RENDER_INFO = 8;
40: const RENDER_INLINE_AUTO = 16;
41: const RENDER_RAW = 32;
42: const RENDER_RAW_FALLBACK = 64;
43:
44:
45: const = 1;
46: const = 2;
47: const = 3;
48:
49: 50: 51: 52: 53: 54:
55: public $lastBodyPartDecode = null;
56:
57: 58: 59: 60: 61:
62: protected $_build = false;
63:
64: 65: 66: 67: 68:
69: protected $_embedded = array();
70:
71: 72: 73: 74: 75:
76: protected ;
77:
78: 79: 80: 81: 82:
83: protected $_mailbox;
84:
85: 86: 87: 88: 89:
90: protected $_message;
91:
92: 93: 94: 95: 96:
97: protected $_uid = null;
98:
99: 100: 101: 102: 103: 104: 105:
106: public function __construct($in)
107: {
108: if ($in instanceof Horde_Mime_Part) {
109: $this->_message = $in;
110: } else {
111: list($this->_mailbox, $this->_uid) = $in->getSingle();
112:
113:
114: $query = new Horde_Imap_Client_Fetch_Query();
115: $query->structure();
116:
117: $ret = $this->_fetchData($query);
118: if (!isset($ret[$this->_uid])) {
119: throw new IMP_Exception(_("Error displaying message: message does not exist on server."), Horde_Log::NOTICE);
120: }
121:
122: $this->_message = $ret[$this->_uid]->getStructure();
123: }
124: }
125:
126: 127: 128: 129: 130:
131: public function getUid()
132: {
133: return $this->_uid;
134: }
135:
136: 137: 138: 139: 140:
141: public function getMailbox()
142: {
143: return $this->_mailbox;
144: }
145:
146: 147: 148: 149: 150: 151: 152: 153: 154: 155:
156: public function getBody($options = array())
157: {
158: if (!$this->_mailbox) {
159: return $this->_message->toString(array(
160: 'headers' => true,
161: 'stream' => !empty($options['stream'])
162: ));
163: }
164:
165: $query = new Horde_Imap_Client_Fetch_Query();
166: $query->bodytext(array(
167: 'peek' => true
168: ));
169:
170: $res = $this->_fetchData($query);
171:
172: return isset($res[$this->_uid])
173: ? $res[$this->_uid]->getBodyText(0, !empty($options['stream']))
174: : '';
175: }
176:
177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196:
197: public function getBodyPart($id, $options = array())
198: {
199: $this->lastBodyPartDecode = null;
200:
201: if (empty($id)) {
202: return '';
203: }
204:
205: if (!$this->_mailbox || $this->isEmbedded($id)) {
206: if (empty($options['mimeheaders']) ||
207: in_array($id, $this->_embedded)) {
208: $ob = $this->getMIMEPart($id, array('nocontents' => true));
209:
210: if (empty($options['stream'])) {
211: return is_null($ob)
212: ? ''
213: : $ob->getContents();
214: }
215:
216: return is_null($ob)
217: ? fopen('php://temp', 'r+')
218: : $ob->getContents(array('stream' => true));
219: }
220:
221: $base_id = $id;
222: while (!in_array($base_id, $this->_embedded, true)) {
223: $base_id = Horde_Mime::mimeIdArithmetic($base_id, 'up');
224: if (is_null($base_id)) {
225: return '';
226: }
227: }
228:
229: $part = $this->getMIMEPart($base_id, array('nocontents' => true));
230: $txt = $part->addMimeHeaders()->toString() .
231: "\n" .
232: $part->getContents();
233:
234: try {
235: $body = Horde_Mime_Part::getRawPartText($txt, 'header', '1') .
236: "\n\n" .
237: Horde_Mime_Part::getRawPartText($txt, 'body', '1');
238: } catch (Horde_Mime_Exception $e) {
239: $body = '';
240: }
241:
242: if (empty($options['stream'])) {
243: return $body;
244: }
245:
246: $fp = fopen('php://temp', 'r+');
247: if (strlen($body)) {
248: fwrite($fp, $body);
249: fseek($fp, 0);
250: }
251: return $fp;
252: }
253:
254: $query = new Horde_Imap_Client_Fetch_Query();
255: if (!isset($options['length']) || !empty($options['length'])) {
256: $bodypart_params = array(
257: 'decode' => !empty($options['decode']),
258: 'peek' => true
259: );
260:
261: if (isset($options['length'])) {
262: $bodypart_params['start'] = 0;
263: $bodypart_params['length'] = $options['length'];
264: }
265:
266: $query->bodyPart($id, $bodypart_params);
267: }
268:
269: if (!empty($options['mimeheaders'])) {
270: $query->mimeHeader($id, array(
271: 'peek' => true
272: ));
273: }
274:
275: $res = $this->_fetchData($query);
276: if (isset($res[$this->_uid])) {
277: try {
278: if (empty($options['mimeheaders'])) {
279: $this->lastBodyPartDecode = $res[$this->_uid]->getBodyPartDecode($id);
280: return $res[$this->_uid]->getBodyPart($id);
281: } elseif (empty($options['stream'])) {
282: return $res[$this->_uid]->getMimeHeader($id) . $res[$this->_uid]->getBodyPart($id);
283: }
284:
285: $swrapper = new Horde_Support_CombineStream(array($res[$this->_uid]->getMimeHeader($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM), $res[$this->_uid]->getBodyPart($id, true)));
286: return $swrapper->fopen();
287: } catch (Horde_Exception $e) {}
288: }
289:
290: return empty($options['stream'])
291: ? ''
292: : fopen('php://temp', 'r+');
293: }
294:
295: 296: 297: 298: 299: 300: 301: 302: 303: 304:
305: public function fullMessageText($options = array())
306: {
307: if (!$this->_mailbox) {
308: return $this->_message->toString();
309: }
310:
311: $query = new Horde_Imap_Client_Fetch_Query();
312: $query->bodyText(array(
313: 'peek' => true
314: ));
315:
316: $res = $this->_fetchData($query);
317: if (isset($res[$this->_uid])) {
318: try {
319: if (empty($options['stream'])) {
320: return $this->getHeader(self::HEADER_TEXT) . $res[$this->_uid]->getBodyText(0);
321: }
322:
323: $swrapper = new Horde_Support_CombineStream(array($this->getHeader(self::HEADER_STREAM), $res[$this->_uid]->getBodyText(0, true)));
324: return $swrapper->fopen();
325: } catch (Horde_Exception $e) {}
326: }
327:
328: return empty($options['stream'])
329: ? ''
330: : fopen('php://temp', 'r+');
331: }
332:
333: 334: 335: 336: 337: 338: 339: 340:
341: public function ($type = self::HEADER_OB)
342: {
343: return $this->_getHeader($type, false);
344: }
345:
346: 347: 348: 349: 350: 351: 352:
353: public function getHeaderAndMarkAsSeen($type = self::HEADER_OB)
354: {
355: if ($this->_mailbox->readonly) {
356: $seen = false;
357: } else {
358: $seen = true;
359:
360: if (isset($this->_header)) {
361: try {
362: $imp_imap = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create();
363: $imp_imap->store($this->_mailbox, array(
364: 'add' => array(
365: Horde_Imap_Client::FLAG_SEEN
366: ),
367: 'ids' => $imp_imap->getIdsOb($this->_uid)
368: ));
369: } catch (Exception $e) {}
370: }
371: }
372:
373: return $this->_getHeader($type, $seen);
374: }
375:
376: 377: 378: 379: 380: 381: 382: 383:
384: protected function ($type, $seen)
385: {
386: if (!isset($this->_header)) {
387: if (is_null($this->_uid)) {
388: $this->_header = $this->_message->addMimeHeaders();
389: } else {
390: $query = new Horde_Imap_Client_Fetch_Query();
391: $query->headerText(array(
392: 'peek' => !$seen
393: ));
394:
395: $res = $this->_fetchData($query);
396: $this->_header = isset($res[$this->_uid])
397: ? $res[$this->_uid]
398: : new Horde_Imap_Client_Data_Fetch();
399: }
400: }
401:
402: switch ($type) {
403: case self::HEADER_OB:
404: return is_null($this->_uid)
405: ? $this->_header
406: : $this->_header->getHeaderText(0, Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
407:
408: case self::HEADER_TEXT:
409: return is_null($this->_uid)
410: ? $this->_header->toString()
411: : $this->_header->getHeaderText();
412:
413: case self::HEADER_STREAM:
414: if (is_null($this->_uid)) {
415: $stream = new Horde_Support_StringStream($this->_header->toString());
416: $stream->fopen();
417: return $stream;
418: }
419:
420: return $this->_header->getHeaderText(0, Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
421: }
422: }
423:
424: 425: 426: 427: 428:
429: public function getMIMEMessage()
430: {
431: return $this->_message;
432: }
433:
434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446:
447: public function getMIMEPart($id, $options = array())
448: {
449: $this->_buildMessage();
450:
451: $part = $this->_message->getPart($id);
452:
453: 454: 455: 456:
457: if ($part &&
458: (strcasecmp($part->getCharset(), 'ISO-8859-1') === 0)) {
459: $part->setCharset('windows-1252');
460: }
461:
462: 463: 464:
465: if (!empty($id) &&
466: !is_null($part) &&
467: (substr($id, -2) != '.0') &&
468: empty($options['nocontents']) &&
469: $this->_mailbox &&
470: !$part->getContents(array('stream' => true))) {
471: $body = $this->getBodyPart($id, array('decode' => true, 'length' => empty($options['length']) ? null : $options['length'], 'stream' => true));
472: $part->setContents($body, array('encoding' => $this->lastBodyPartDecode, 'usestream' => true));
473: }
474:
475: return $part;
476: }
477:
478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498:
499: public function renderMIMEPart($mime_id, $mode, $options = array())
500: {
501: $this->_buildMessage();
502:
503: $mime_part = empty($options['mime_part'])
504: ? $this->getMIMEPart($mime_id)
505: : $options['mime_part'];
506:
507: if (!empty($options['autodetect']) &&
508: ($tempfile = Horde::getTempFile()) &&
509: ($fp = fopen($tempfile, 'w')) &&
510: !is_null($contents = $mime_part->getContents(array('stream' => true)))) {
511: rewind($contents);
512: while (!feof($contents)) {
513: fwrite($fp, fread($contents, 8192));
514: }
515: fclose($fp);
516:
517: $options['type'] = Horde_Mime_Magic::analyzeFile($tempfile, empty($GLOBALS['conf']['mime']['magic_db']) ? null : $GLOBALS['conf']['mime']['magic_db']);
518: }
519:
520: $type = empty($options['type'])
521: ? null
522: : $options['type'];
523:
524: $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($mime_part, $this, $type);
525:
526: switch ($mode) {
527: case self::RENDER_FULL:
528: $textmode = 'full';
529: break;
530:
531: case self::RENDER_INLINE:
532: case self::RENDER_INLINE_AUTO:
533: case self::RENDER_INLINE_DISP_NO:
534: $textmode = 'inline';
535: $limit = $viewer->getConfigParam('limit_inline_size');
536:
537: if ($limit && ($mime_part->getBytes() > $limit)) {
538: $data = '';
539: $status = new IMP_Mime_Status(array(
540: _("This message part cannot be viewed because it is too large."),
541: sprintf(_("Click %s to download the data."), $this->linkView($mime_part, 'download_attach', _("HERE")))
542: ));
543: $status->icon('alerts/warning.png', _("Warning"));
544:
545: if (method_exists($viewer, 'overLimitText')) {
546: $data = $viewer->overLimitText();
547: $status->addText(_("The initial portion of this text part is displayed below."));
548: }
549:
550: return array(
551: $mime_id => array(
552: 'data' => $data,
553: 'name' => '',
554: 'status' => $status,
555: 'type' => 'text/html; charset=' . 'UTF-8'
556: )
557: );
558: }
559: break;
560:
561: case self::RENDER_INFO:
562: $textmode = 'info';
563: break;
564:
565: case self::RENDER_RAW:
566: $textmode = 'raw';
567: break;
568:
569: case self::RENDER_RAW_FALLBACK:
570: $textmode = $viewer->canRender('raw')
571: ? 'raw'
572: : 'full';
573: break;
574: }
575:
576: $ret = $viewer->render($textmode);
577:
578: if (empty($ret)) {
579: return ($mode == self::RENDER_INLINE_AUTO)
580: ? $this->renderMIMEPart($mime_id, self::RENDER_INFO, $options)
581: : array();
582: }
583:
584: if (!empty($ret[$mime_id]) && !isset($ret[$mime_id]['name'])) {
585: $ret[$mime_id]['name'] = $mime_part->getName(true);
586: }
587:
588: if (!is_null($ret[$mime_id]['data']) &&
589: ($textmode == 'inline') &&
590: !strlen($ret[$mime_id]['data']) &&
591: $this->isAttachment($type) &&
592: !isset($ret[$mime_id]['status'])) {
593: $ret[$mime_id]['status'] = new IMP_Mime_Status(_("This part is empty."));
594: }
595:
596: return $ret;
597: }
598:
599: 600: 601: 602: 603: 604: 605: 606:
607: public function findBody($subtype = null)
608: {
609: $this->_buildMessage();
610: return $this->_message->findBody($subtype);
611: }
612:
613: 614: 615: 616: 617: 618: 619:
620: public function generatePreview()
621: {
622:
623:
624:
625: $oldbuild = $this->_build;
626: $this->_build = true;
627: $mimeid = $this->findBody();
628:
629: if (is_null($mimeid)) {
630: $this->_build = $oldbuild;
631: return array('cut' => false, 'text' => '');
632: }
633:
634: $maxlen = empty($GLOBALS['conf']['msgcache']['preview_size'])
635: ? $GLOBALS['prefs']->getValue('preview_maxlen')
636: : $GLOBALS['conf']['msgcache']['preview_size'];
637:
638:
639:
640: $pmime = $this->getMIMEPart($mimeid, array('length' => $maxlen * 3));
641:
642: $ptext = Horde_String::convertCharset($pmime->getContents(), $pmime->getCharset(), 'UTF-8');
643:
644: if ($pmime->getType() == 'text/html') {
645: $ptext = $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($ptext, 'Html2text');
646: }
647:
648: $this->_build = $oldbuild;
649:
650: if (Horde_String::length($ptext) > $maxlen) {
651: return array(
652: 'cut' => true,
653: 'text' => Horde_String::truncate($ptext, $maxlen)
654: );
655: }
656:
657: return array(
658: 'cut' => false,
659: 'text' => $ptext
660: );
661: }
662:
663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707:
708: public function getSummary($id, $mask = 0)
709: {
710: $autodetect_link = false;
711: $download_zip = (($mask & self::SUMMARY_DOWNLOAD_ZIP) && Horde_Util::extensionExists('zlib'));
712: $param_array = array();
713:
714: $this->_buildMessage();
715:
716: $part = array(
717: 'bytes' => null,
718: 'download' => null,
719: 'download_zip' => null,
720: 'id' => $id,
721: 'img_save' => null,
722: 'size' => null,
723: 'strip' => null
724: );
725:
726: $mime_part = $this->getMIMEPart($id, array('nocontents' => true));
727: $mime_type = $mime_part->getType();
728:
729: 730:
731: if (in_array($mime_type, array('application/octet-stream', 'application/base64'))) {
732: $mime_type = Horde_Mime_Magic::filenameToMIME($mime_part->getName());
733: if ($mime_type == $mime_part->getType()) {
734: $autodetect_link = true;
735: } else {
736: $mime_part = clone $mime_part;
737: $mime_part->setType($mime_type);
738: $param_array['ctype'] = $mime_type;
739: }
740: }
741: $part['type'] = $mime_type;
742:
743:
744: $is_atc = $this->isAttachment($mime_type);
745:
746:
747: if (($mask & self::SUMMARY_BYTES) ||
748: $download_zip ||
749: ($mask & self::SUMMARY_SIZE)) {
750: $part['bytes'] = $size = $mime_part->getBytes(true);
751: $part['size'] = ($size > 1048576)
752: ? sprintf(_("%s MB"), IMP::numberFormat($size / 1048576, 1))
753: : sprintf(_("%s KB"), max(round($size / 1024), 1));
754: }
755:
756:
757: if (($mask & self::SUMMARY_ICON) ||
758: ($mask & self::SUMMARY_ICON_RAW)) {
759: $part['icon'] = $GLOBALS['injector']->getInstance('Horde_Core_Factory_MimeViewer')->getIcon($mime_type);
760: if ($mask & self::SUMMARY_ICON) {
761: $part['icon'] = Horde::img($part['icon'], '', array('title' => $mime_type), '');
762: }
763: } else {
764: $part['icon'] = null;
765: }
766:
767:
768: $description = $this->getPartName($mime_part, true);
769:
770: if ($mask & self::SUMMARY_DESCRIP_LINK) {
771: if (($can_d = $this->canDisplay($mime_part, self::RENDER_FULL)) ||
772: $autodetect_link) {
773: $part['description'] = $this->linkViewJS($mime_part, 'view_attach', htmlspecialchars($description), array('jstext' => sprintf(_("View %s"), $description), 'params' => array_filter(array_merge($param_array, array(
774: 'autodetect' => !$can_d
775: )))));
776: } else {
777: $part['description'] = htmlspecialchars($description);
778: }
779: } elseif ($mask & self::SUMMARY_DESCRIP_NOLINK) {
780: $part['description'] = htmlspecialchars($description);
781: } elseif ($mask & self::SUMMARY_DESCRIP_NOLINK_NOHTMLSPECCHARS) {
782: $part['description'] = $description;
783: }
784:
785:
786: if ($is_atc &&
787: (is_null($part['bytes']) || $part['bytes'])) {
788: if ($mask & self::SUMMARY_DOWNLOAD) {
789: $part['download'] = $this->linkView($mime_part, 'download_attach', '', array('class' => 'iconImg downloadAtc', 'dload' => true, 'jstext' => _("Download")));
790: } elseif ($mask & self::SUMMARY_DOWNLOAD_NOJS) {
791: $part['download'] = $this->urlView($mime_part, 'download_attach', array('dload' => true));
792: }
793: }
794:
795: 796:
797: if ($is_atc &&
798: $download_zip &&
799: ($part['bytes'] > 204800)) {
800: $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($mime_part, $this, $mime_type);
801: if (!$viewer->getMetadata('compressed')) {
802: $part['download_zip'] = $this->linkView($mime_part, 'download_attach', null, array('class' => 'iconImg downloadZipAtc', 'dload' => true, 'jstext' => sprintf(_("Download %s in .zip Format"), $description), 'params' => array('zip' => 1)));
803: }
804: }
805:
806: 807:
808: if (($mask & self::SUMMARY_IMAGE_SAVE) &&
809: $GLOBALS['registry']->hasMethod('images/selectGalleries') &&
810: ($mime_part->getPrimaryType() == 'image')) {
811: $part['img_save'] = Horde::link('#', _("Save Image in Gallery"), 'iconImg saveImgAtc', null, Horde::popupJs(Horde::url('saveimage.php'), array('params' => array('mbox' => $this->_mailbox, 'uid' => $this->_uid, 'id' => $id), 'height' => 200, 'width' => 450, 'urlencode' => true)) . 'return false;') . '</a>';
812: }
813:
814:
815: if ((($mask & self::SUMMARY_PRINT) ||
816: ($mask & self::SUMMARY_PRINT_STUB)) &&
817: $this->canDisplay($id, self::RENDER_FULL)) {
818: $part['print'] = ($mask & self::SUMMARY_PRINT)
819: ? $this->linkViewJS($mime_part, 'print_attach', '', array('css' => 'iconImg printAtc', 'jstext' => _("Print"), 'onload' => 'IMP_JS.printWindow', 'params' => $param_array))
820: : Horde::link('#', _("Print"), 'iconImg printAtc', null, null, null, null, array('mimeid' => $id)) . '</a>';
821: }
822:
823: 824:
825: if ((($mask & self::SUMMARY_STRIP_LINK) ||
826: ($mask & self::SUMMARY_STRIP_STUB)) &&
827: ($id != 0) &&
828: (intval($id) != 1) &&
829: (strpos($id, '.') === false)) {
830: if ($mask & self::SUMMARY_STRIP_LINK) {
831: $url = Horde::selfUrl(true)->remove(array('actionID', 'imapid', 'uid'))->add(array('actionID' => 'strip_attachment', 'imapid' => $id, 'uid' => $this->_uid, 'message_token' => $GLOBALS['injector']->getInstance('Horde_Token')->get('imp.impcontents')));
832: $part['strip'] = Horde::link($url, _("Strip Attachment"), 'iconImg deleteImg', null, 'return window.confirm(' . Horde_Serialize::serialize(_("Are you sure you wish to PERMANENTLY delete this attachment?"), Horde_Serialize::JSON, 'UTF-8') . ')') . '</a>';
833: } else {
834: $part['strip'] = Horde::link('#', _("Strip Attachment"), 'iconImg deleteImg stripAtc', null, null, null, null, array('mimeid' => $id)) . '</a>';
835: }
836: }
837:
838: return $part;
839: }
840:
841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852:
853: public function urlView($mime_part = null, $actionID = 'view_attach',
854: $options = array())
855: {
856: $params = $this->_urlViewParams($mime_part, $actionID, isset($options['params']) ? $options['params'] : array());
857:
858: return empty($options['dload'])
859: ? Horde::url('view.php', true)->add($params)
860: : Horde::downloadUrl($mime_part->getName(true), $params);
861: }
862:
863: 864: 865: 866: 867: 868: 869: 870: 871:
872: protected function _urlViewParams($mime_part, $actionID, $params)
873: {
874:
875: $params = array_merge($params, array(
876: 'actionID' => $actionID,
877: 'id' => isset($params['id']) ? $params['id'] : $mime_part->getMIMEId()
878: ));
879:
880: if ($this->_mailbox) {
881: $params['uid'] = $this->_uid;
882: $params['mailbox'] = $this->_mailbox->form_to;
883: }
884:
885: return $params;
886: }
887:
888: 889: 890: 891: 892: 893: 894: 895: 896: 897: 898: 899: 900: 901: 902:
903: public function linkView($mime_part, $actionID, $text, $options = array())
904: {
905: $options = array_merge(array(
906: 'class' => null,
907: 'jstext' => $text,
908: 'params' => array()
909: ), $options);
910:
911: return Horde::link($this->urlView($mime_part, $actionID, $options), $options['jstext'], $options['class'], empty($options['dload']) ? null : 'view_' . hash('md5', $mime_part->getMIMEId() . $this->_mailbox . $this->_uid)) . $text . '</a>';
912: }
913:
914: 915: 916: 917: 918: 919: 920: 921: 922: 923: 924: 925: 926: 927: 928: 929: 930: 931:
932: public function linkViewJS($mime_part, $actionID, $text,
933: $options = array())
934: {
935: if (empty($options['params'])) {
936: $options['params'] = array();
937: }
938:
939: if (empty($options['jstext'])) {
940: $options['jstext'] = sprintf(_("View %s"), $mime_part->getDescription(true));
941: }
942:
943: $url = Horde::popupJs(Horde::url('view.php'), array('menu' => true, 'onload' => empty($options['onload']) ? '' : $options['onload'], 'params' => $this->_urlViewParams($mime_part, $actionID, isset($options['params']) ? $options['params'] : array()), 'urlencode' => true));
944:
945: return empty($options['widget'])
946: ? Horde::link('#', $options['jstext'], empty($options['css']) ? null : $options['css'], null, $url) . $text . '</a>'
947: : Horde::widget('#', $options['jstext'], empty($options['css']) ? null : $options['css'], null, $url, $text);
948: }
949:
950: 951: 952: 953: 954: 955: 956: 957: 958: 959:
960: public function isAttachment($mime_type)
961: {
962: switch ($mime_type) {
963: case 'application/ms-tnef':
964: return false;
965: }
966:
967: list($ptype,) = explode('/', $mime_type, 2);
968:
969: switch ($ptype) {
970: case 'message':
971: return in_array($mime_type, array('message/rfc822', 'message/disposition-notification'));
972:
973: case 'multipart':
974: return false;
975:
976: default:
977: return true;
978: }
979: }
980:
981: 982: 983: 984: 985: 986:
987: protected function _buildMessage($parts = null)
988: {
989: if (is_null($parts)) {
990: if ($this->_build) {
991: return;
992: }
993: $this->_build = true;
994: $parts = array_keys($this->_message->contentTypeMap());
995: $first_id = reset($parts);
996: } else {
997: $first_id = null;
998: }
999:
1000: $last_id = null;
1001: $to_process = array();
1002:
1003: foreach ($parts as $id) {
1004: if (!is_null($last_id) &&
1005: (strpos($id, $last_id) === 0)) {
1006: continue;
1007: }
1008:
1009: $last_id = null;
1010:
1011: $mime_part = $this->getMIMEPart($id, array('nocontents' => true));
1012: $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($mime_part, $this);
1013: if ($viewer->embeddedMimeParts()) {
1014: $mime_part = $this->getMIMEPart($id);
1015: $viewer->setMIMEPart($mime_part);
1016: $new_part = $viewer->getEmbeddedMimeParts();
1017: if (!is_null($new_part)) {
1018: $mime_part->addPart($new_part);
1019: $mime_part->buildMimeIds($id);
1020: $this->_embedded[] = $new_part->getMimeId();
1021: $to_process = array_merge($to_process, array_keys($new_part->contentTypeMap()));
1022: $last_id = $id;
1023: }
1024: }
1025: }
1026:
1027: if (!empty($to_process)) {
1028: $this->_buildMessage($to_process);
1029: }
1030: }
1031:
1032: 1033: 1034: 1035: 1036: 1037: 1038: 1039: 1040: 1041:
1042: public function canDisplay($part, $mask, $type = null)
1043: {
1044: if (!is_object($part)) {
1045: $part = $this->getMIMEPart($part, array('nocontents' => true));
1046: }
1047: $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($part, $this, $type);
1048:
1049: if ($mask & self::RENDER_INLINE_AUTO) {
1050: $mask |= self::RENDER_INLINE | self::RENDER_INFO;
1051: }
1052:
1053: if (($mask & self::RENDER_RAW) && $viewer->canRender('raw')) {
1054: return self::RENDER_RAW;
1055: }
1056:
1057: if (($mask & self::RENDER_FULL) && $viewer->canRender('full')) {
1058: return self::RENDER_FULL;
1059: }
1060:
1061: if ($mask & self::RENDER_INLINE) {
1062: if ($viewer->canRender('inline')) {
1063: return self::RENDER_INLINE;
1064: }
1065: } elseif (($mask & self::RENDER_INLINE_DISP_NO) &&
1066: $viewer->canRender('inline')) {
1067: return self::RENDER_INLINE_DISP_NO;
1068: }
1069:
1070: if (($mask & self::RENDER_INFO) && $viewer->canRender('info')) {
1071: return self::RENDER_INFO;
1072: }
1073:
1074: return 0;
1075: }
1076:
1077: 1078: 1079: 1080: 1081: 1082:
1083: public function getContentTypeMap()
1084: {
1085: $this->_buildMessage();
1086: return $this->_message->contentTypeMap();
1087: }
1088:
1089: 1090: 1091: 1092: 1093: 1094: 1095: 1096: 1097:
1098: public function getTree($renderer = 'Horde_Core_Tree_Html')
1099: {
1100: $tree = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Tree')->create('mime-' . $this->_uid, $renderer, array(
1101: 'nosession' => true
1102: ));
1103: $this->_addTreeNodes($tree, $this->_message);
1104: return $tree;
1105: }
1106:
1107: 1108: 1109: 1110: 1111: 1112: 1113: 1114:
1115: protected function _addTreeNodes($tree, $part, $parent = null)
1116: {
1117: $mimeid = $part->getMimeId();
1118:
1119: $summary = $this->getSummary(
1120: $mimeid,
1121: self::SUMMARY_ICON_RAW | self::SUMMARY_DESCRIP_LINK | self::SUMMARY_SIZE | self::SUMMARY_DOWNLOAD
1122: );
1123:
1124: $tree->addNode(
1125: $mimeid,
1126: $parent,
1127: $summary['description'] . ' (' . $summary['size'] . ') ' . $summary['download']
1128: );
1129: $tree->addNodeParams(
1130: $mimeid,
1131: array(
1132: 'class' => 'partsTreeDiv',
1133: 'icon' => $summary['icon']
1134: )
1135: );
1136:
1137: foreach ($part->getParts() as $part) {
1138: $this->_addTreeNodes($tree, $part, $mimeid);
1139: }
1140: }
1141:
1142: 1143: 1144: 1145: 1146:
1147: public function downloadAllList()
1148: {
1149: $ret = array();
1150:
1151: foreach ($this->getContentTypeMap() as $key => $val) {
1152: if ($this->isAttachment($val)) {
1153: $ret[] = $key;
1154: }
1155: }
1156:
1157: return $ret;
1158: }
1159:
1160: 1161: 1162: 1163: 1164: 1165: 1166:
1167: public function buildMessageContents($ignore = array())
1168: {
1169: $message = $this->_message;
1170: $curr_ignore = null;
1171:
1172: foreach ($message->contentTypeMap() as $key => $val) {
1173: if (is_null($curr_ignore) && in_array($key, $ignore)) {
1174: $curr_ignore = $key . '.';
1175: } elseif (is_null($curr_ignore) ||
1176: (strpos($key, $curr_ignore) === false)) {
1177: $curr_ignore = null;
1178: if (($key != 0) &&
1179: ($val != 'message/rfc822') &&
1180: (strpos($val, 'multipart/') === false)) {
1181: $part = $this->getMIMEPart($key);
1182: $message->alterPart($key, $part);
1183: }
1184: }
1185: }
1186:
1187: return $message;
1188: }
1189:
1190: 1191: 1192: 1193: 1194: 1195: 1196:
1197: public function isEmbedded($mime_id)
1198: {
1199: foreach ($this->_embedded as $val) {
1200: if (($mime_id == $val) || Horde_Mime::isChild($val, $mime_id)) {
1201: return true;
1202: }
1203: }
1204:
1205: return false;
1206: }
1207:
1208: 1209: 1210: 1211: 1212: 1213: 1214: 1215:
1216: public function findMimeType($id, $type)
1217: {
1218: $id = Horde_Mime::mimeIdArithmetic($id, 'up');
1219:
1220: while (!is_null($id)) {
1221: if (($part = $this->getMIMEPart($id, array('nocontents' => true))) &&
1222: ($part->getType() == $type)) {
1223: return $part;
1224: }
1225: $id = Horde_Mime::mimeIdArithmetic($id, 'up');
1226: }
1227:
1228: return null;
1229: }
1230:
1231: 1232: 1233: 1234: 1235: 1236: 1237: 1238:
1239: public function getPartName(Horde_Mime_Part $part, $use_descrip = false)
1240: {
1241: $name = $use_descrip
1242: ? $part->getDescription(true)
1243: : $part->getName(true);
1244:
1245: if ($name) {
1246: return $name;
1247: }
1248:
1249: switch ($ptype = $part->getPrimaryType()) {
1250: case 'multipart':
1251: if (($part->getSubType() == 'related') &&
1252: ($view_id = $part->getMetaData('viewable_part')) &&
1253: ($viewable = $this->getMIMEPart($view_id, array('nocontents' => true)))) {
1254: return $this->getPartName($viewable, $use_descrip);
1255: }
1256:
1257:
1258: case 'application':
1259: case 'model':
1260: $ptype = $part->getSubType();
1261: break;
1262: }
1263:
1264: switch ($ptype) {
1265: case 'audio':
1266: return _("Audio part");
1267:
1268: case 'image':
1269: return _("Image part");
1270:
1271: case 'message':
1272: case Horde_Mime_Part::UNKNOWN:
1273: return _("Message part");
1274:
1275: case 'multipart':
1276: return _("Multipart part");
1277:
1278: case 'text':
1279: return _("Text part");
1280:
1281: case 'video':
1282: return _("Video part");
1283:
1284: default:
1285:
1286:
1287: return sprintf(_("%s part"), _(Horde_String::ucfirst($ptype)));
1288: }
1289: }
1290:
1291: 1292: 1293: 1294: 1295: 1296: 1297: 1298: 1299: 1300: 1301: 1302: 1303: 1304: 1305: 1306: 1307: 1308: 1309:
1310: public function getInlineOutput(array $options = array())
1311: {
1312: $atc_parts = $display_ids = $js_onload = $wrap_ids = array();
1313: $msgtext = array();
1314: $parts_list = $this->getContentTypeMap();
1315: $text_out = '';
1316:
1317: $contents_mask = isset($options['mask'])
1318: ? $options['mask']
1319: : 0;
1320: $display_mask = isset($options['display_mask'])
1321: ? $options['display_mask']
1322: : IMP_Contents::RENDER_INLINE_AUTO;
1323: $no_inline_all = !empty($options['no_inline_all']);
1324: $part_info_display = isset($options['part_info_display'])
1325: ? $options['part_info_display']
1326: : array();
1327: $show_parts = isset($options['show_parts'])
1328: ? $options['show_parts']
1329: : $GLOBALS['prefs']->getValue('parts_display');
1330:
1331: foreach ($parts_list as $mime_id => $mime_type) {
1332: if (isset($display_ids[$mime_id]) ||
1333: isset($atc_parts[$mime_id])) {
1334: continue;
1335: }
1336:
1337: if (!($render_mode = $this->canDisplay($mime_id, $display_mask))) {
1338: if ($this->isAttachment($mime_type)) {
1339: if ($show_parts == 'atc') {
1340: $atc_parts[$mime_id] = 1;
1341: }
1342:
1343: if ($contents_mask && empty($info['nosummary'])) {
1344: $msgtext[$mime_id] = array(
1345: 'text' => $this->_formatSummary($mime_id, $contents_mask, $part_info_display, true)
1346: );
1347: }
1348: }
1349: continue;
1350: }
1351:
1352: $render_part = $this->renderMIMEPart($mime_id, $render_mode);
1353: if (($show_parts == 'atc') &&
1354: $this->isAttachment($mime_type) &&
1355: (empty($render_part) ||
1356: !($render_mode & IMP_Contents::RENDER_INLINE))) {
1357: $atc_parts[$mime_id] = 1;
1358: }
1359:
1360: if (empty($render_part)) {
1361: if ($contents_mask &&
1362: empty($info['nosummary']) &&
1363: $this->isAttachment($mime_type)) {
1364: $msgtext[$mime_id] = array(
1365: 'text' => $this->_formatSummary($mime_id, $contents_mask, $part_info_display, true)
1366: );
1367: }
1368: continue;
1369: }
1370:
1371: reset($render_part);
1372: while (list($id, $info) = each($render_part)) {
1373: $display_ids[$id] = 1;
1374:
1375: if (empty($info)) {
1376: continue;
1377: }
1378:
1379: if ($no_inline_all === 1) {
1380: $atc_parts[$id] = 1;
1381: continue;
1382: }
1383:
1384: $part_text = '';
1385:
1386: if (empty($info['attach'])) {
1387: if ($contents_mask) {
1388: if (empty($info['nosummary'])) {
1389: $part_text .= $this->_formatSummary($id, $contents_mask, $part_info_display);
1390: }
1391:
1392: if (isset($info['status'])) {
1393: if (!is_array($info['status'])) {
1394: $info['status'] = array($info['status']);
1395: }
1396: $part_text .= implode('', array_map('strval', $info['status']));
1397: }
1398:
1399: $part_text .= '<div class="mimePartData">' . $info['data'] . '</div>';
1400: } else {
1401: if ($part_text && !empty($options['sep'])) {
1402: $part_text .= $options['sep'];
1403: }
1404: $part_text .= $info['data'];
1405: }
1406: } else {
1407: if ($show_parts == 'atc') {
1408: $atc_parts[$id] = 1;
1409: }
1410:
1411: if ($contents_mask && empty($info['nosummary'])) {
1412: $part_text .= $this->_formatSummary($id, $contents_mask, $part_info_display, true);
1413: }
1414: }
1415:
1416: $msgtext[$id] = array(
1417: 'text' => $part_text,
1418: 'wrap' => empty($info['wrap']) ? null : $info['wrap']
1419: );
1420:
1421: if (isset($info['js'])) {
1422: $js_onload = array_merge($js_onload, $info['js']);
1423: }
1424:
1425: if ($no_inline_all) {
1426: $no_inline_all = 1;
1427: }
1428: }
1429: }
1430:
1431: if (!empty($msgtext)) {
1432: uksort($msgtext, 'strnatcmp');
1433: }
1434:
1435: reset($msgtext);
1436: while (list($id, $part) = each($msgtext)) {
1437: while (count($wrap_ids) &&
1438: !Horde_Mime::isChild(end($wrap_ids), $id)) {
1439: array_pop($wrap_ids);
1440: $text_out .= '</div>';
1441: }
1442:
1443: if (!empty($part['wrap'])) {
1444: $text_out .= '<div class="' . $part['wrap'] . '">';
1445: $wrap_ids[] = $id;
1446: }
1447:
1448: $text_out .= $part['text'];
1449: }
1450:
1451: $text_out .= str_repeat('</div>', count($wrap_ids));
1452:
1453: if (!strlen($text_out)) {
1454: $text_out = strval(new IMP_Mime_Status(_("There are no parts that can be shown inline.")));
1455: }
1456:
1457: $atc_parts = ($show_parts == 'all')
1458: ? array_keys($parts_list)
1459: : array_keys($atc_parts);
1460:
1461: return array(
1462: 'atc_parts' => $atc_parts,
1463: 'display_ids' => array_keys($display_ids),
1464: 'js_onload' => $js_onload,
1465: 'msgtext' => $text_out
1466: );
1467: }
1468:
1469: 1470: 1471: 1472: 1473: 1474: 1475: 1476: 1477: 1478: 1479:
1480: protected function _formatSummary($id, $mask, $display, $atc = false)
1481: {
1482: $summary = $this->getSummary($id, $mask);
1483: $tmp_summary = array();
1484:
1485: foreach ($display as $val) {
1486: if (isset($summary[$val])) {
1487: switch ($val) {
1488: case 'description':
1489: $summary[$val] = '<span class="mimePartInfoDescrip">' . $summary[$val] . '</span>';
1490: break;
1491:
1492: case 'size':
1493: $summary[$val] = '<span class="mimePartInfoSize">(' . $summary[$val] . ')</span>';
1494: break;
1495: }
1496: $tmp_summary[] = $summary[$val];
1497: }
1498: }
1499:
1500: return '<div class="mimePartInfo' .
1501: ($atc ? ' mimePartInfoAtc' : '') .
1502: '"><div>' .
1503: implode(' ', $tmp_summary) .
1504: '</div></div>';
1505: }
1506:
1507: 1508: 1509: 1510: 1511: 1512: 1513:
1514: protected function _fetchData(Horde_Imap_Client_Fetch_Query $query)
1515: {
1516: try {
1517: $imp_imap = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create();
1518: return $imp_imap->fetch($this->_mailbox, $query, array(
1519: 'ids' => $imp_imap->getIdsOb($this->_uid)
1520: ));
1521: } catch (Horde_Imap_Client_Exception $e) {
1522: return array();
1523: }
1524: }
1525:
1526: }
1527: