⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.97
Server IP:
41.128.143.86
Server:
Linux host.raqmix.cloud 6.8.0-1025-azure #30~22.04.1-Ubuntu SMP Wed Mar 12 15:28:20 UTC 2025 x86_64
Server Software:
Apache
PHP Version:
8.3.23
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
usr
/
share
/
psa-horde
/
imp
/
lib
/
Edit File: Contents.php
<?php /** * Copyright 2002-2017 Horde LLC (http://www.horde.org/) * * See the enclosed file COPYING for license information (GPL). If you * did not receive this file, see http://www.horde.org/licenses/gpl. * * @category Horde * @copyright 2002-2017 Horde LLC * @license http://www.horde.org/licenses/gpl GPL * @package IMP */ /** * The IMP_Contents:: class contains all functions related to handling the * content and output of mail messages in IMP. * * @author Michael Slusarz <slusarz@horde.org> * @category Horde * @copyright 2002-2017 Horde LLC * @license http://www.horde.org/licenses/gpl GPL * @package IMP */ class IMP_Contents { /* Mask entries for getSummary(). */ const SUMMARY_BYTES = 1; const SUMMARY_SIZE = 2; const SUMMARY_ICON = 4; const SUMMARY_ICON_RAW = 16384; const SUMMARY_DESCRIP = 8; const SUMMARY_DESCRIP_LINK = 16; const SUMMARY_DOWNLOAD = 32; const SUMMARY_DOWNLOAD_ZIP = 64; const SUMMARY_IMAGE_SAVE = 128; const SUMMARY_PRINT = 256; const SUMMARY_PRINT_STUB = 512; const SUMMARY_STRIP = 1024; /* Rendering mask entries. */ const RENDER_FULL = 1; const RENDER_INLINE = 2; const RENDER_INLINE_DISP_NO = 4; const RENDER_INFO = 8; const RENDER_INLINE_AUTO = 16; const RENDER_RAW = 32; const RENDER_RAW_FALLBACK = 64; /* Header return type for getHeader(). */ const HEADER_OB = 1; const HEADER_TEXT = 2; const HEADER_STREAM = 3; /** * Have we scanned for embedded parts? * * @var boolean */ protected $_build = false; /** * The list of MIME IDs that consist of embedded data. * * @var array */ protected $_embedded = array(); /** * Message header. * * @var mixed */ protected $_header; /** * The index of the current message. * * @var IMP_Indices_Mailbox */ protected $_indices; /** * The Horde_Mime_Part object for the message. * * @var Horde_Mime_Part */ protected $_message; /** * Cached data for the MIME Viewer objects. * * @var object */ protected $_viewcache; /** * Constructor. * * @param mixed $in An IMP_Indices_Mailbox or Horde_Mime_Part object. * * @throws IMP_Exception */ public function __construct($in) { if ($in instanceof Horde_Mime_Part) { $this->_message = $in; } else { $this->_indices = $in; /* Get the Horde_Mime_Part object for the given UID. */ $query = new Horde_Imap_Client_Fetch_Query(); $query->structure(); if (!($ret = $this->_fetchData($query))) { $e = new IMP_Exception(_("Error displaying message: message does not exist on server.")); $e->setLogLevel('NOTICE'); throw $e; } $this->_message = $ret->getStructure(); } } /** * String representation of object. * * @return string The indices string. */ public function __toString() { return strval($this->getIndicesOb()); } /** * Returns the IMAP UID for the current message. * * @return integer The message UID. */ public function getUid() { list(,$uid) = $this->_indices->getSingle(); return $uid; } /** * Returns the IMAP mailbox for the current message. * * @return IMP_Mailbox The message mailbox. */ public function getMailbox() { list($mbox,) = $this->_indices->getSingle(); return $mbox; } /** * Return an IMP_Indices object for the current message. * * @return IMP_Indices An indices object. */ public function getIndicesOb() { return $this->_indices; } /** * Returns the entire body of the message. * * @param array $options Additional options: * - stream: (boolean) If true, return a stream. * DEFAULT: No * * @return mixed The text of the part, or a stream resource if 'stream' * is true. */ public function getBody($options = array()) { if (!$this->_indices) { return $this->_message->toString(array( 'headers' => true, 'stream' => !empty($options['stream']) )); } $query = new Horde_Imap_Client_Fetch_Query(); $query->bodytext(array( 'peek' => true )); return ($res = $this->_fetchData($query)) ? $res->getBodyText(0, !empty($options['stream'])) : ''; } /** * Gets the raw text for one section of the message. * * @param integer $id The ID of the MIME part. * @param array $options Additional options: * - decode: (boolean) Attempt to decode the bodypart on the remote * server. * DEFAULT: No * - length: (integer) If set, only download this many bytes of the * bodypart from the server. * DEFAULT: All data is retrieved. * - mimeheaders: (boolean) Include the MIME headers also? * DEFAULT: No * - stream: (boolean) If true, return a stream. * DEFAULT: No * * @return object Object with the following properties: * <pre> * - data: (mixed) The text of the part or a stream resource if 'stream' * option is true. * - decode: (string) If 'decode' option is true, and bodypart decoded * on server, the content-type of the decoded data. * </pre> */ public function getBodyPart($id, $options = array()) { $ret = new stdClass; $ret->data = ''; $ret->decode = null; if (empty($id)) { return $ret; } if (!$this->_indices || $this->isEmbedded($id)) { if (empty($options['mimeheaders']) || in_array($id, $this->_embedded)) { $ob = $this->getMIMEPart($id, array('nocontents' => true)); if (empty($options['stream'])) { if (!is_null($ob)) { $ret->data = $ob->getContents(); } } else { $ret->data = is_null($ob) ? fopen('php://temp', 'r+') : $ob->getContents(array('stream' => true)); } return $ret; } $base_id = $id; while (!in_array($base_id, $this->_embedded, true)) { $base_id = Horde_Mime::mimeIdArithmetic($base_id, 'up'); if (is_null($base_id)) { return $ret; } } $part = $this->getMIMEPart($base_id, array('nocontents' => true)); $txt = $part->addMimeHeaders()->toString() . "\n" . $part->getContents(); try { $body = Horde_Mime_Part::getRawPartText($txt, 'header', '1') . "\n\n" . Horde_Mime_Part::getRawPartText($txt, 'body', '1'); } catch (Horde_Mime_Exception $e) { $body = ''; } if (empty($options['stream'])) { $ret->data = $body; return $ret; } $ret->data = fopen('php://temp', 'r+'); if (strlen($body)) { fwrite($ret->data, $body); fseek($ret->data, 0); } return $ret; } $query = new Horde_Imap_Client_Fetch_Query(); if (substr($id, -2) === '.0') { $rfc822 = true; $id = substr($id, 0, -2); } else { $rfc822 = false; } if (!isset($options['length']) || !empty($options['length'])) { $bodypart_params = array( 'decode' => !empty($options['decode']), 'peek' => true ); if (isset($options['length'])) { $bodypart_params['start'] = 0; $bodypart_params['length'] = $options['length']; } if ($rfc822) { $bodypart_params['id'] = $id; $query->bodyText($bodypart_params); } else { $query->bodyPart($id, $bodypart_params); } } if (!empty($options['mimeheaders'])) { if ($rfc822) { $query->headerText(array( 'id' => $id, 'peek' => true )); } else { $query->mimeHeader($id, array( 'peek' => true )); } } if ($res = $this->_fetchData($query)) { try { if (empty($options['mimeheaders'])) { $ret->decode = $res->getBodyPartDecode($id); $ret->data = $rfc822 ? $res->getBodyText($id, !empty($options['stream'])) : $res->getBodyPart($id, !empty($options['stream'])); return $ret; } elseif (empty($options['stream'])) { $ret->data = $rfc822 ? ($res->getHeaderText($id) . $res->getBodyText($id)) : ($res->getMimeHeader($id) . $res->getBodyPart($id)); return $ret; } if ($rfc822) { $data = array( $res->getHeaderText($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM), $res->getBodyText($id, true) ); } else { $data = array( $res->getMimeHeader($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM), $res->getBodyPart($id, true) ); } $ret->data = Horde_Stream_Wrapper_Combine::getStream($data); return $ret; } catch (Horde_Exception $e) {} } if (!empty($options['stream'])) { $ret->data = fopen('php://temp', 'r+'); } return $ret; } /** * Returns the full message text. * * @param array $options Additional options: * - stream: (boolean) If true, return a stream for bodytext. * DEFAULT: No * * @return mixed The full message text or a stream resource if 'stream' * is true. */ public function fullMessageText($options = array()) { if (!$this->_indices) { return $this->_message->toString(); } $query = new Horde_Imap_Client_Fetch_Query(); $query->bodyText(array( 'peek' => true )); if ($res = $this->_fetchData($query)) { try { if (empty($options['stream'])) { return $this->getHeader(self::HEADER_TEXT) . $res->getBodyText(0); } return Horde_Stream_Wrapper_Combine::getStream(array( $this->getHeader(self::HEADER_STREAM), $res->getBodyText(0, true) )); } catch (Horde_Exception $e) {} } return empty($options['stream']) ? '' : fopen('php://temp', 'r+'); } /** * Returns base header information. * * @param integer $type Return type (HEADER_* constant). * * @return mixed Either a Horde_Mime_Headers object (HEADER_OB), header * text (HEADER_TEXT), or a stream resource (HEADER_STREAM). */ public function getHeader($type = self::HEADER_OB) { return $this->_getHeader($type, false); } /** * Returns base header information and marks the message as seen. * * @param integer $type See getHeader(). * * @return mixed See getHeader(). */ public function getHeaderAndMarkAsSeen($type = self::HEADER_OB) { $mbox = $this->getMailbox(); if ($mbox->readonly) { $seen = false; } else { $seen = true; if (isset($this->_header)) { try { $imp_imap = $mbox->imp_imap; $imp_imap->store($mbox, array( 'add' => array( Horde_Imap_Client::FLAG_SEEN ), 'ids' => $imp_imap->getIdsOb($this->getUid()) )); } catch (Exception $e) {} } } return $this->_getHeader($type, $seen); } /** * Returns base header information. * * @param integer $type See getHeader(). * @param boolean $seen Mark message as seen? * * @return mixed See getHeader(). */ protected function _getHeader($type, $seen) { if (!isset($this->_header)) { if (!$this->_indices) { $this->_header = $this->_message->addMimeHeaders(); } else { $query = new Horde_Imap_Client_Fetch_Query(); $query->headerText(array( 'peek' => !$seen )); $this->_header = ($res = $this->_fetchData($query)) ? $res : new Horde_Imap_Client_Data_Fetch(); } } switch ($type) { case self::HEADER_OB: return $this->_indices ? $this->_header->getHeaderText(0, Horde_Imap_Client_Data_Fetch::HEADER_PARSE) : $this->_header; case self::HEADER_TEXT: return $this->_indices ? $this->_header->getHeaderText() : $this->_header->toString(); case self::HEADER_STREAM: if ($this->_indices) { return $this->_header->getHeaderText(0, Horde_Imap_Client_Data_Fetch::HEADER_STREAM); } $stream = new Horde_Support_StringStream($this->_header->toString()); $stream->fopen(); return $stream; } } /** * Returns the Horde_Mime_Part object. * * @return Horde_Mime_Part A Horde_Mime_Part object. */ public function getMIMEMessage() { return $this->_message; } /** * Fetch a part of a MIME message. * * @param integer $id The MIME index of the part requested. * @param array $options Additional options: * - length: (integer) If set, only download this many bytes of the * bodypart from the server. * DEFAULT: All data is retrieved. * - nocontents: (boolean) If true, don't add the contents to the part * DEFAULT: Contents are added to the part * * @return Horde_Mime_Part The raw MIME part asked for (reference). */ public function getMIMEPart($id, $options = array()) { $this->_buildMessage(); $part = $this->_message->getPart($id); /* Ticket #9201: Treat 'ISO-8859-1' as 'windows-1252'. 1252 has some * characters (e.g. euro sign, back quote) not in 8859-1. There * shouldn't be any issue doing this since the additional code points * in 1252 don't map to anything in 8859-1. */ if ($part && (strcasecmp($part->getCharset(), 'ISO-8859-1') === 0)) { $part->setCharset('windows-1252'); } /* Don't download contents of entire body if ID == 0 (indicating the * body of the main multipart message). I'm pretty sure we never * want to download the body of that part here. */ if (!empty($id) && !is_null($part) && (substr($id, -2) != '.0') && empty($options['nocontents']) && $this->_indices && !$part->getContents(array('stream' => true))) { $body = $this->getBodyPart($id, array( 'decode' => true, 'length' => empty($options['length']) ? null : $options['length'], 'stream' => true )); $part->setContents($body->data, array( 'encoding' => $body->decode, 'usestream' => true )); } return $part; } /** * Render a MIME Part. * * @param string $mime_id The MIME ID to render. * @param integer $mode One of the RENDER_ constants. * @param array $options Additional options: * - autodetect: (boolean) Attempt to auto-detect MIME type? * - mime_part: (Horde_Mime_Part) The MIME part to render. * - type: (string) Use this MIME type instead of the MIME type * identified in the MIME part. * * @return array See Horde_Mime_Viewer_Base::render(). The following * fields may also be present in addition to the fields * defined in Horde_Mime_Viewer_Base: * - attach: (boolean) Force display of this part as an attachment. * - js: (array) A list of javascript commands to run after the content * is displayed on screen. * - name: (string) Contains the MIME name information. * - wrap: (string) If present, indicates that this part, and all child * parts, will be wrapped in a DIV with the given class name. */ public function renderMIMEPart($mime_id, $mode, array $options = array()) { $this->_buildMessage(); $mime_part = empty($options['mime_part']) ? $this->getMIMEPart($mime_id) : $options['mime_part']; if (!$mime_part) { return array($mime_id => null); } if (!empty($options['autodetect']) && ($tempfile = Horde::getTempFile()) && ($fp = fopen($tempfile, 'w')) && !is_null($contents = $mime_part->getContents(array('stream' => true)))) { rewind($contents); while (!feof($contents)) { fwrite($fp, fread($contents, 8192)); } fclose($fp); $options['type'] = Horde_Mime_Magic::analyzeFile($tempfile, empty($GLOBALS['conf']['mime']['magic_db']) ? null : $GLOBALS['conf']['mime']['magic_db']); } $type = empty($options['type']) ? null : $options['type']; $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($mime_part, array('contents' => $this, 'type' => $type)); switch ($mode) { case self::RENDER_INLINE: case self::RENDER_INLINE_AUTO: case self::RENDER_INLINE_DISP_NO: $textmode = 'inline'; $limit = $viewer->getConfigParam('limit_inline_size'); if ($limit && ($mime_part->getBytes() > $limit)) { $data = ''; $status = new IMP_Mime_Status(array( _("This message part cannot be viewed because it is too large."), sprintf(_("Click %s to download the data."), $this->linkView($mime_part, 'download_attach', _("HERE"))) )); $status->icon('alerts/warning.png', _("Warning")); if (method_exists($viewer, 'overLimitText')) { $data = $viewer->overLimitText(); $status->addText(_("The initial portion of this text part is displayed below.")); } return array( $mime_id => array( 'data' => $data, 'name' => '', 'status' => $status, 'type' => 'text/html; charset=' . 'UTF-8' ) ); } break; case self::RENDER_INFO: $textmode = 'info'; break; case self::RENDER_RAW: $textmode = 'raw'; break; case self::RENDER_RAW_FALLBACK: $textmode = $viewer->canRender('raw') ? 'raw' : 'full'; break; case self::RENDER_FULL: default: $textmode = 'full'; break; } $ret = $viewer->render($textmode); if (empty($ret)) { return ($mode == self::RENDER_INLINE_AUTO) ? $this->renderMIMEPart($mime_id, self::RENDER_INFO, $options) : array(); } if (!empty($ret[$mime_id]) && !isset($ret[$mime_id]['name'])) { $ret[$mime_id]['name'] = $mime_part->getName(true); } /* Don't show empty parts. */ if (($textmode == 'inline') && !is_null($ret[$mime_id]['data']) && !strlen($ret[$mime_id]['data']) && !isset($ret[$mime_id]['status'])) { $ret[$mime_id] = null; } return $ret; } /** * Finds the main "body" text part (if any) in a message. * "Body" data is the first text part in the base MIME part. * * @param string $subtype Specifically search for this subtype. * * @return string The MIME ID of the main body part. */ public function findBody($subtype = null) { $this->_buildMessage(); return $this->_message->findBody($subtype); } /** * Generate the preview text. * * @return array Array with the following keys: * - cut: (boolean) Was the preview text cut? * - text: (string) The preview text. */ public function generatePreview() { // For preview generation, don't go through overhead of scanning for // embedded parts. Necessary evil, or else very large parts (e.g // 5 MB+ text parts) will take ages to scan. $oldbuild = $this->_build; $this->_build = true; $mimeid = $this->findBody(); if (is_null($mimeid)) { $this->_build = $oldbuild; return array('cut' => false, 'text' => ''); } $maxlen = empty($GLOBALS['conf']['msgcache']['preview_size']) ? $GLOBALS['prefs']->getValue('preview_maxlen') : $GLOBALS['conf']['msgcache']['preview_size']; // Retrieve 3x the size of $maxlen of bodytext data. This should // account for any content-encoding & HTML tags. $pmime = $this->getMIMEPart($mimeid, array('length' => $maxlen * 3)); $ptext = Horde_String::convertCharset($pmime->getContents(), $pmime->getCharset(), 'UTF-8'); if ($pmime->getType() == 'text/html') { $ptext = $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($ptext, 'Html2text'); } $this->_build = $oldbuild; if (Horde_String::length($ptext) > $maxlen) { return array( 'cut' => true, 'text' => Horde_String::truncate($ptext, $maxlen) ); } return array( 'cut' => false, 'text' => $ptext ); } /** * Get summary info for a MIME ID. * * @param string $id The MIME ID. * @param integer $mask A bitmask indicating what information to return: * <pre> * Always output: * 'type' = MIME type * * IMP_Contents::SUMMARY_BYTES * Output: parts = 'bytes' * * IMP_Contents::SUMMARY_SIZE * Output: parts = 'size' * * IMP_Contents::SUMMARY_ICON * IMP_Contents::SUMMARY_ICON_RAW * Output: parts = 'icon' * * IMP_Contents::SUMMARY_DESCRIP * Output: parts = 'description_raw' * * IMP_Contents::SUMMARY_DESCRIP_LINK * Output: parts = 'description' * * IMP_Contents::SUMMARY_DOWNLOAD * Output: parts = 'download', 'download_url' * * IMP_Contents::SUMMARY_DOWNLOAD_ZIP * Output: parts = 'download_zip' * * IMP_Contents::SUMMARY_IMAGE_SAVE * Output: parts = 'img_save' * * IMP_Contents::SUMMARY_PRINT * IMP_Contents::SUMMARY_PRINT_STUB * Output: parts = 'print' * * IMP_Contents::SUMMARY_STRIP * Output: parts = 'strip' * </pre> * * @return array An array with the requested information. */ public function getSummary($id, $mask = 0) { $autodetect_link = false; $download_zip = (($mask & self::SUMMARY_DOWNLOAD_ZIP) && Horde_Util::extensionExists('zlib')); $param_array = array(); $this->_buildMessage(); $part = array( 'bytes' => null, 'download' => null, 'download_url' => null, 'download_zip' => null, 'id' => $id, 'img_save' => null, 'size' => null, 'strip' => null ); $mime_part = $this->getMIMEPart($id, array('nocontents' => true)); if (empty($mime_part)) { throw new IMP_Exception('MIME Part not found.'); } $mime_type = $mime_part->getType(); /* If this is an attachment that has no specific MIME type info, see * if we can guess a rendering type. */ if (in_array($mime_type, array('application/octet-stream', 'application/base64'))) { $mime_type = Horde_Mime_Magic::filenameToMIME($mime_part->getName()); if ($mime_type == $mime_part->getType()) { $autodetect_link = true; } else { $mime_part = clone $mime_part; $mime_part->setType($mime_type); $param_array['ctype'] = $mime_type; } } $part['type'] = $mime_type; /* Is this part an attachment? */ $is_atc = $this->isAttachment($mime_type); /* Get bytes/size information. */ if (($mask & self::SUMMARY_BYTES) || $download_zip || ($mask & self::SUMMARY_SIZE)) { $part['bytes'] = $size = $mime_part->getBytes(); $part['size'] = ($size > 1048576) ? sprintf(_("%s MB"), IMP::numberFormat($size / 1048576, 1)) : sprintf(_("%s KB"), max(round($size / 1024), 1)); } /* Get part's icon. */ if (($mask & self::SUMMARY_ICON) || ($mask & self::SUMMARY_ICON_RAW)) { $part['icon'] = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->getIcon($mime_type); if ($mask & self::SUMMARY_ICON) { $part['icon'] = Horde_Themes_Image::tag($part['icon'], array( 'attr' => array( 'title' => $mime_type ) )); } } else { $part['icon'] = null; } /* Get part's description. */ $description = $this->getPartName($mime_part, true); if ($mask & self::SUMMARY_DESCRIP_LINK) { if (($can_d = $this->canDisplay($mime_part, self::RENDER_FULL)) || $autodetect_link) { $part['description'] = $this->linkViewJS($mime_part, 'view_attach', htmlspecialchars($description), array('jstext' => sprintf(_("View %s"), $description), 'params' => array_filter(array_merge($param_array, array( 'autodetect' => !$can_d ))))); } else { $part['description'] = htmlspecialchars($description); } } if ($mask & self::SUMMARY_DESCRIP) { $part['description_raw'] = $description; } /* Download column. */ if (($mask & self::SUMMARY_DOWNLOAD) && $is_atc && (is_null($part['bytes']) || $part['bytes'])) { $part['download'] = $this->linkView($mime_part, 'download_attach', '', array('class' => 'iconImg downloadAtc', 'jstext' => _("Download"))); $part['download_url'] = $this->urlView($mime_part, 'download_attach'); } /* Display the compressed download link only if size is greater * than 200 KB. */ if ($is_atc && $download_zip && ($part['bytes'] > 204800)) { $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($mime_part, array('contents' => $this, 'type' => $mime_type)); if (!$viewer->getMetadata('compressed')) { $part['download_zip'] = $this->linkView($mime_part, 'download_attach', null, array('class' => 'iconImg downloadZipAtc', 'jstext' => sprintf(_("Download %s in .zip Format"), $description), 'params' => array('zip' => 1))); } } /* Display the image save link if the required registry calls are * present. */ if (($mask & self::SUMMARY_IMAGE_SAVE) && $GLOBALS['registry']->hasMethod('images/selectGalleries') && ($mime_part->getPrimaryType() == 'image')) { $part['img_save'] = Horde::link('#', _("Save Image in Gallery"), 'iconImg saveImgAtc', null, Horde::popupJs(IMP_Basic_Saveimage::url(), array('params' => array('muid' => strval($this->getIndicesOb()), 'id' => $id), 'height' => 200, 'width' => 450, 'urlencode' => true)) . 'return false;') . '</a>'; } /* Add print link? */ if ((($mask & self::SUMMARY_PRINT) || ($mask & self::SUMMARY_PRINT_STUB)) && $this->canDisplay($id, self::RENDER_FULL)) { $part['print'] = ($mask & self::SUMMARY_PRINT) ? $this->linkViewJS($mime_part, 'print_attach', '', array('css' => 'iconImg printAtc', 'jstext' => _("Print"), 'onload' => 'IMP_JS.printWindow', 'params' => $param_array)) : Horde::link('#', _("Print"), 'iconImg printAtc', null, null, null, null, array('mimeid' => $id)) . '</a>'; } /* Strip Attachment? Allow stripping of base parts other than the * base multipart and the base text (body) part. */ if (($mask & self::SUMMARY_STRIP) && ($id != 0) && (intval($id) != 1) && (strpos($id, '.') === false)) { $part['strip'] = Horde::link( Horde::selfUrlParams()->add(array( 'actionID' => 'strip_attachment', 'imapid' => $id, 'muid' => strval($this->getIndicesOb()), 'token' => $GLOBALS['session']->getToken() )), _("Strip Attachment"), 'iconImg deleteImg stripAtc', null, null, null, null, array('mimeid' => $id) ) . '</a>'; } return $part; } /** * Return the URL to the download/view page. * * @param Horde_Mime_Part $mime_part The MIME part to view. * @param integer $actionID The actionID to perform. * @param array $options Additional options: * - params: (array) A list of any additional parameters that need to be * passed to the download/view page (key => name). * * @return Horde_Url The URL to the download/view page. */ public function urlView($mime_part = null, $actionID = 'view_attach', array $options = array()) { $params = $this->_urlViewParams($mime_part, $actionID, isset($options['params']) ? $options['params'] : array()); return (strpos($actionID, 'download_') === 0) ? IMP_Contents_View::downloadUrl($mime_part->getName(true), $params) : Horde::url('view.php', true)->add($params); } /** * Generates the necessary URL parameters for the download/view page. * * @param Horde_Mime_Part $mime_part The MIME part to view. * @param integer $actionID The actionID to perform. * @param array $params Additional parameters to pass. * * @return array The array of parameters. */ protected function _urlViewParams($mime_part, $actionID, $params) { /* Add the necessary local parameters. */ $params = array_merge($params, array( 'actionID' => $actionID, 'id' => isset($params['id']) ? $params['id'] : $mime_part->getMIMEId() )); if ($this->_indices) { $params['muid'] = strval($this->getIndicesOb()); } return IMP_Contents_View::addToken($params); } /** * Generate a link to the download/view page. * * @param Horde_Mime_Part $mime_part The MIME part to view. * @param integer $actionID The actionID value. * @param string $text The ESCAPED (!) link text. * @param array $options Additional parameters: * - class: (string) The CSS class to use. * - jstext: (string) The JS text to use. * - params: (array) A list of any additional parameters that need to be * passed to the download/view page. * * @return string A HTML href link to the download/view page. */ public function linkView($mime_part, $actionID, $text, $options = array()) { $options = array_merge(array( 'class' => null, 'jstext' => $text, 'params' => array() ), $options); return Horde::link( $this->urlView($mime_part, $actionID, $options), $options['jstext'], $options['class'], ($actionID == 'download_attach') ? null : strval(new Horde_Support_Randomid()) ) . $text . '</a>'; } /** * Generate a javascript link to the download/view page. * * @param Horde_Mime_Part $mime_part The MIME part to view. * @param string $actionID The actionID to perform. * @param string $text The ESCAPED (!) link text. * @param array $options Additional options: * - css: (string) The CSS class to use. * - jstext: (string) The javascript link text. * - onload: (string) A JS function to run when popup window is * fully loaded. * - params: (array) A list of any additional parameters that need to be * passed to download/view page. (key = name) * - widget: (boolean) If true use Horde::widget() to generate, * Horde::link() otherwise. * * @return string A HTML href link to the download/view page. */ public function linkViewJS($mime_part, $actionID, $text, $options = array()) { if (empty($options['params'])) { $options['params'] = array(); } if (empty($options['jstext'])) { $options['jstext'] = sprintf(_("View %s"), $mime_part->getDescription(true)); } $url = Horde::popupJs(Horde::url('view.php'), array( 'menu' => true, 'onload' => empty($options['onload']) ? 'IMP_JS.resizePopup' : $options['onload'], 'params' => $this->_urlViewParams($mime_part, $actionID, isset($options['params']) ? $options['params'] : array()), 'urlencode' => true )); return empty($options['widget']) ? Horde::link('#', $options['jstext'], empty($options['css']) ? null : $options['css'], null, $url) . $text . '</a>' : Horde::widget(array('url' => '#', 'class' => empty($options['css']) ? null : $options['css'], 'onclick' => $url, 'title' => $text)); } /** * Determines if a MIME type is an attachment. * For IMP's purposes, an attachment is any MIME part that can be * downloaded by itself (i.e. all the data needed to view the part is * contained within the download data). * * @param string $mime_type The MIME type. * * @return boolean True if an attachment. */ public function isAttachment($mime_type) { switch ($mime_type) { case 'application/ms-tnef': return false; } list($ptype,) = explode('/', $mime_type, 2); switch ($ptype) { case 'message': return in_array($mime_type, array('message/rfc822', 'message/disposition-notification')); case 'multipart': return false; default: return true; } } /** * Builds the "virtual" Horde_Mime_Part object by checking for embedded * parts. * * @param array $parts The parts list to process. */ protected function _buildMessage($parts = null) { global $injector; if (is_null($parts)) { if ($this->_build) { return; } $this->_build = true; $parts = array_keys($this->_message->contentTypeMap()); $first_id = reset($parts); } else { $first_id = null; } $last_id = null; $to_process = array(); $mv_factory = $injector->getInstance('IMP_Factory_MimeViewer'); foreach ($parts as $id) { if (!is_null($last_id) && (strpos($id, $last_id) === 0)) { continue; } $last_id = null; $mime_part = $this->getMIMEPart($id, array('nocontents' => true)); $viewer = $mv_factory->create($mime_part, array('contents' => $this)); if ($viewer->embeddedMimeParts()) { $mime_part = $this->getMIMEPart($id); $viewer->setMIMEPart($mime_part); $new_part = $viewer->getEmbeddedMimeParts(); if (!is_null($new_part)) { $mime_part->addPart($new_part); $mime_part->buildMimeIds($id); $this->_embedded[] = $new_part->getMimeId(); $to_process = array_merge($to_process, array_keys($new_part->contentTypeMap())); $last_id = $id; } } } if (!empty($to_process)) { $this->_buildMessage($to_process); } } /** * Can this MIME part be displayed in the given mode? * * @param mixed $part The MIME part or a MIME ID string. * @param integer $mask One of the RENDER_ constants. * @param string $type The type to use (overrides the MIME ID if $id is * a MIME part). * * @return integer The RENDER_ constant of the allowable display. */ public function canDisplay($part, $mask, $type = null) { if (!is_object($part)) { $part = $this->getMIMEPart($part, array('nocontents' => true)); } if (!$part) { return 0; } $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($part, array('contents' => $this, 'type' => $type)); if ($mask & self::RENDER_INLINE_AUTO) { $mask |= self::RENDER_INLINE | self::RENDER_INFO; } if (($mask & self::RENDER_RAW) && $viewer->canRender('raw')) { return self::RENDER_RAW; } if (($mask & self::RENDER_FULL) && $viewer->canRender('full')) { return self::RENDER_FULL; } if ($mask & self::RENDER_INLINE) { if ($viewer->canRender('inline')) { return self::RENDER_INLINE; } } elseif (($mask & self::RENDER_INLINE_DISP_NO) && $viewer->canRender('inline')) { return self::RENDER_INLINE_DISP_NO; } if (($mask & self::RENDER_INFO) && $viewer->canRender('info')) { return self::RENDER_INFO; } return 0; } /** * Returns the Content-Type map for the entire message, regenerating * embedded parts if needed. * * @return array See Horde_Mime_Part::contentTypeMap(). */ public function getContentTypeMap() { $this->_buildMessage(); return $this->_message->contentTypeMap(); } /** * Returns the MIME part tree of the message. * * @param string $renderer Either the tree renderer driver or a full * class name to use. * * @return Horde_Tree_Renderer_Base A tree instance representing the MIME parts. * @throws Horde_Tree_Exception */ public function getTree($renderer = 'Horde_Core_Tree_Renderer_Html') { $tree = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Tree')->create('mime-' . $this->getUid(), $renderer, array( 'nosession' => true )); $this->_addTreeNodes($tree, $this->_message); return $tree; } /** * Adds MIME parts to the tree instance. * * @param Horde_Tree_Renderer_Base tree A tree instance. * @param Horde_Mime_Part $part The MIME part to add to the * tree, including its sub-parts. * @param string $parent The parent part's MIME id. */ protected function _addTreeNodes($tree, $part, $parent = null) { $mimeid = $part->getMimeId(); $summary_mask = self::SUMMARY_ICON_RAW | self::SUMMARY_DESCRIP_LINK | self::SUMMARY_SIZE | self::SUMMARY_DOWNLOAD; if ($GLOBALS['prefs']->getValue('strip_attachments')) { $summary_mask += self::SUMMARY_STRIP; } $summary = $this->getSummary($mimeid, $summary_mask); $tree->addNode(array( 'id' => $mimeid, 'parent' => $parent, 'label' => sprintf( '%s (%s) %s %s', $summary['description'], $summary['size'], $summary['download'], $summary['strip'] ), 'params' => array( 'class' => 'partsTreeDiv', 'icon' => $summary['icon'] ) )); foreach ($part->getParts() as $part) { $this->_addTreeNodes($tree, $part, $mimeid); } } /** * Get download all list. * * @return array An array of downloadable parts. */ public function downloadAllList() { $ret = array(); foreach ($this->getContentTypeMap() as $key => $val) { if ($this->isAttachment($val)) { $ret[] = $key; } } return $ret; } /** * Injects body contents into the base Horde_Mime_part object. * * @param array $ignore A list of MIME IDs to ignore. * * @return Horde_Mime_Part The part with body contents set. */ public function buildMessageContents($ignore = array()) { $message = $this->_message; $curr_ignore = null; foreach ($message->contentTypeMap() as $key => $val) { if (is_null($curr_ignore) && in_array($key, $ignore)) { $curr_ignore = $key . '.'; } elseif (is_null($curr_ignore) || (strpos($key, $curr_ignore) === false)) { $curr_ignore = null; if (($key != 0) && ($val != 'message/rfc822') && (strpos($val, 'multipart/') === false)) { $part = $this->getMIMEPart($key); $message->alterPart($key, $part); } } } return $message; } /** * Determines if a given MIME part ID is a part of embedded data. * * @param string $mime_id The MIME ID. * * @return boolean True if the MIME ID is part of embedded data. */ public function isEmbedded($mime_id) { foreach ($this->_embedded as $val) { if (($mime_id == $val) || Horde_Mime::isChild($val, $mime_id)) { return true; } } return false; } /** * Find a MIME type in parent parts. * * @param string $id The MIME ID to begin the search at. * @param string $type The MIME type to search for. * * @return mixed Either the requested MIME part, or null if not found. */ public function findMimeType($id, $type) { $id = Horde_Mime::mimeIdArithmetic($id, 'up'); while (!is_null($id)) { if (($part = $this->getMIMEPart($id, array('nocontents' => true))) && ($part->getType() == $type)) { return $part; } $id = Horde_Mime::mimeIdArithmetic($id, 'up'); } return null; } /** * Return the descriptive part label, making sure it is not empty. * * @param Horde_Mime_Part $part The MIME Part object. * @param boolean $use_descrip Use description? If false, uses name. * * @return string The part label (non-empty). */ public function getPartName(Horde_Mime_Part $part, $use_descrip = false) { $name = $use_descrip ? $part->getDescription(true) : $part->getName(true); if ($name) { return $name; } switch ($ptype = $part->getPrimaryType()) { case 'multipart': if (($part->getSubType() == 'related') && ($view_id = $part->getMetaData('viewable_part')) && ($viewable = $this->getMIMEPart($view_id, array('nocontents' => true)))) { return $this->getPartName($viewable, $use_descrip); } /* Fall-through. */ case 'application': case 'model': $ptype = $part->getSubType(); break; } switch ($ptype) { case 'audio': return _("Audio"); case 'image': return _("Image"); case 'message': case '': case Horde_Mime_Part::UNKNOWN: return _("Message"); case 'multipart': return _("Multipart"); case 'text': return _("Text"); case 'video': return _("Video"); default: // Attempt to translate this type, if possible. Odds are that // it won't appear in the dictionary though. return _(Horde_String::ucfirst($ptype)); } } /** * Generate inline message display. * * @param array $options Options: * - display_mask: (integer) The mask of display view type to render * inline (DEFAULT: RENDER_INLINE_AUTO). * - mask: (integer) The mask needed for a getSummary() call. * - no_inline_all: (boolean) If true, only display first inline part. * Subsequent inline parts will be treated as * attachments. * - part_info_display: (array) The list of summary fields to display. * - show_parts: (string) The value of the 'parts_display' pref. * * @return array An array with the following keys: * - atc_parts: (array) The list of attachment MIME IDs. * - display_ids: (array) The list of display MIME IDs. * - js_onload: (array) A list of javascript code to run onload. * - msgtext: (string) The rendered HTML code. * - one_part: (boolean) If true, the message only consists of one part. */ public function getInlineOutput(array $options = array()) { global $prefs, $registry; $atc_parts = $display_ids = $msgtext = $js_onload = $wrap_ids = array(); $parts_list = $this->getContentTypeMap(); $text_out = ''; $view = $registry->getView(); $contents_mask = isset($options['mask']) ? $options['mask'] : 0; $display_mask = isset($options['display_mask']) ? $options['display_mask'] : self::RENDER_INLINE_AUTO; $no_inline_all = !empty($options['no_inline_all']); $part_info_display = isset($options['part_info_display']) ? $options['part_info_display'] : array(); $show_parts = isset($options['show_parts']) ? $options['show_parts'] : $prefs->getValue('parts_display'); foreach ($parts_list as $mime_id => $mime_type) { if (isset($display_ids[$mime_id]) || isset($atc_parts[$mime_id])) { continue; } if (!($render_mode = $this->canDisplay($mime_id, $display_mask))) { if ($this->isAttachment($mime_type)) { if ($show_parts == 'atc') { $atc_parts[$mime_id] = 1; } if ($contents_mask) { $msgtext[$mime_id] = array( 'text' => $this->_formatSummary($mime_id, $contents_mask, $part_info_display, true) ); } } continue; } $render_part = $this->renderMIMEPart($mime_id, $render_mode); if (($show_parts == 'atc') && $this->isAttachment($mime_type) && (empty($render_part) || !($render_mode & self::RENDER_INLINE))) { $atc_parts[$mime_id] = 1; } if (empty($render_part)) { if ($contents_mask && $this->isAttachment($mime_type)) { $msgtext[$mime_id] = array( 'text' => $this->_formatSummary($mime_id, $contents_mask, $part_info_display, true) ); } continue; } reset($render_part); while (list($id, $info) = each($render_part)) { $display_ids[$id] = 1; if (empty($info)) { continue; } if ($no_inline_all === 1) { $atc_parts[$id] = 1; continue; } $part_text = ($contents_mask && empty($info['nosummary'])) ? $this->_formatSummary($id, $contents_mask, $part_info_display, !empty($info['attach'])) : ''; if (empty($info['attach'])) { if (isset($info['status'])) { if (!is_array($info['status'])) { $info['status'] = array($info['status']); } foreach ($info['status'] as $val) { if (in_array($view, $val->views)) { $part_text .= strval($val); } } } $part_text .= '<div class="mimePartData">' . $info['data'] . '</div>'; } elseif ($show_parts == 'atc') { $atc_parts[$id] = 1; } $msgtext[$id] = array( 'text' => $part_text, 'wrap' => empty($info['wrap']) ? null : $info['wrap'] ); if (isset($info['js'])) { $js_onload = array_merge($js_onload, $info['js']); } if ($no_inline_all) { $no_inline_all = 1; } } } if (!empty($msgtext)) { uksort($msgtext, 'strnatcmp'); } reset($msgtext); while (list($id, $part) = each($msgtext)) { while (!empty($wrap_ids) && !Horde_Mime::isChild(end($wrap_ids), $id)) { array_pop($wrap_ids); $text_out .= '</div>'; } if (!empty($part['wrap'])) { $text_out .= '<div class="' . $part['wrap'] . '">'; $wrap_ids[] = $id; } $text_out .= '<div class="mimePartBase">' . $part['text'] . '</div>'; } $text_out .= str_repeat('</div>', count($wrap_ids)); if (!strlen($text_out)) { $text_out = strval(new IMP_Mime_Status(_("There are no parts that can be shown inline."))); } $atc_parts = ($show_parts == 'all') ? array_keys($parts_list) : array_keys($atc_parts); return array( 'atc_parts' => $atc_parts, 'display_ids' => array_keys($display_ids), 'js_onload' => $js_onload, 'msgtext' => $text_out, 'one_part' => (count($parts_list) == 1) ); } /** * Prints out a MIME summary (in HTML). * * @param string $id The MIME ID. * @param integer $mask A bitmask indicating what summary information to * return. * @param array $display The fields to display (in this order). * @param boolean $atc Is this an attachment? * * @return string The formatted summary string. */ protected function _formatSummary($id, $mask, $display, $atc = false) { $summary = $this->getSummary($id, $mask); $tmp_summary = array(); foreach ($display as $val) { if (isset($summary[$val])) { switch ($val) { case 'description': $summary[$val] = '<span class="mimePartInfoDescrip">' . $summary[$val] . '</span>'; break; case 'size': $summary[$val] = '<span class="mimePartInfoSize">(' . $summary[$val] . ')</span>'; break; } $tmp_summary[] = $summary[$val]; } } return '<div class="mimePartInfo' . ($atc ? ' mimePartInfoAtc' : '') . '"><div>' . implode(' ', $tmp_summary) . '</div></div>'; } /** * Get FETCH data from IMAP server for this message. * * @param Horde_Imap_Client_Fetch_Query $query Search query. * * @return Horde_Imap_Client_Data_Fetch Fetch data for the message. */ protected function _fetchData(Horde_Imap_Client_Fetch_Query $query) { try { $mbox = $this->getMailbox(); $imp_imap = $mbox->imp_imap; return $imp_imap->fetch($mbox, $query, array( 'ids' => $imp_imap->getIdsOb($this->getUid()) ))->first(); } catch (Horde_Imap_Client_Exception $e) { return new Horde_Imap_Client_Data_Fetch(); } } /** * Return the view cache object for this message. * * @return object View object. */ public function getViewCache() { if (!isset($this->_viewcache)) { $this->_viewcache = new stdClass; } return $this->_viewcache; } }
Simpan