/** * Render the part based on the view mode. * * @param boolean $inline True if viewing inline. * * @return array See parent::render(). */ protected function _IMPrender($inline) { /* RFC 1740 [4]: There are two parts to an appledouble message: * (1) application/applefile * (2) Data embedded in the Mac file * Since the resource fork is not very useful to us, only provide a * means to download. */ /* Display the resource fork download link. */ $iterator = $this->_mimepart->partIterator(); $iterator->rewind(); $mime_id = $iterator->current()->getMimeId(); $iterator->next(); $applefile_id = $iterator->current()->getMimeId(); $id_ob = new Horde_Mime_Id($applefile_id); $data_id = $id_ob->idArithmetic($id_ob::ID_NEXT); $applefile_part = $this->_mimepart[$applefile_id]; $data_part = $this->_mimepart[$data_id]; $data_name = $this->getConfigParam('imp_contents')->getPartName($data_part); $status = new IMP_Mime_Status($this->_mimepart, array(sprintf(_("This message contains a Macintosh file (named \"%s\")."), $data_name), $this->getConfigParam('imp_contents')->linkViewJS($applefile_part, 'download_attach', "Download the Macintosh resource fork."))); $status->icon('mime/apple.png', _("Macintosh File")); /* For inline viewing, attempt to display the data inline. */ $ret = array(); if ($inline && ($disp = $this->getConfigParam('imp_contents')->canDisplay($data_part, IMP_Contents::RENDER_INLINE | IMP_Contents::RENDER_INFO))) { $ret = $this->getConfigParam('imp_contents')->renderMIMEPart($data_id, $disp); } foreach ($iterator as $ob) { $id = $ob->getMimeId(); if (!isset($ret[$id]) && strcmp($id, $data_id) !== 0) { $ret[$id] = strcmp($id, $mime_id) === 0 ? array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap') : null; } } return $ret; }
/** * @dataProvider isChildProvider */ public function testIsChild($base, $id, $expected) { $id_ob = new Horde_Mime_Id($base); if ($expected) { $this->assertTrue($id_ob->isChild($id)); } else { $this->assertFalse($id_ob->isChild($id)); } }
/** * 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_ob = new Horde_Mime_Id($id); while (($id_ob->id = $id_ob->idArithmetic($id_ob::ID_UP)) !== null) { if (($part = $this->getMimePart($id_ob->id, array('nocontents' => true))) && $part->getType() == $type) { return $part; } } return null; }
/** * Render out the currently set contents. * * @param boolean $inline Are we viewing inline? * * @return array See self::render(). */ protected function _IMPrender($inline) { $related_id = $this->_mimepart->getMimeId(); $used = array($related_id); if (!($id = $this->_init($inline))) { return array(); } $render = $this->getConfigParam('imp_contents')->renderMIMEPart($id, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL); if (!$inline) { foreach (array_keys($render) as $key) { if (!is_null($render[$key])) { return array($related_id => $render[$key]); } } return null; } $data_id = null; $ret = array(); foreach ($this->_mimepart->partIterator(false) as $val) { $ret[$val->getMimeId()] = null; } foreach (array_keys($render) as $val) { $ret[$val] = $render[$val]; if ($ret[$val]) { $data_id = $val; } } if (!is_null($data_id)) { $this->_mimepart->setMetadata('viewable_part', $data_id); /* We want the inline display to show multipart/related vs. the * viewable MIME part. This is because a multipart/related part * is not downloadable and clicking on the MIME part may not * produce the desired result in the full display (i.e. HTML parts * with related images). */ if (strcmp($data_id, $related_id) !== 0) { $ret[$related_id] = $ret[$data_id]; $ret[$data_id] = null; } } /* Fix for broken messages that don't refer to a related CID part * within the base part. */ if ($cids_used = $this->_mimepart->getMetadata('related_cids_used')) { $used = array_merge($used, $cids_used); } $id_ob = new Horde_Mime_Id($id); foreach (array_diff(array_keys($ret), $used) as $val) { if (strcmp($val, $id) !== 0 && !$id_ob->isChild($val)) { $summary = $this->getConfigParam('imp_contents')->getSummary($val, IMP_Contents::SUMMARY_SIZE | IMP_Contents::SUMMARY_ICON | IMP_Contents::SUMMARY_DESCRIP_LINK | IMP_Contents::SUMMARY_DOWNLOAD); $status = new IMP_Mime_Status_RenderIssue($this->_mimepart, array(_("This part contains an attachment that can not be displayed within this part:"), implode(' ', array($summary['icon'], $summary['description'], $summary['size'], $summary['download'])))); $status->action($status::WARNING); if (isset($ret[$related_id]['status'])) { if (!is_array($ret[$related_id]['status'])) { $ret[$related_id]['status'] = array($ret[$related_id]['status']); } } else { $ret[$related_id]['status'] = array(); } $ret[$related_id]['status'][] = $status; } } return $ret; }
/** * Generates HTML output for 'multipart/signed' MIME parts. * * @return string The HTML output. */ protected function _outputPGPSigned() { global $injector, $prefs, $session; $iterator = $this->_mimepart->partIterator(); $iterator->rewind(); $base_id = $iterator->current()->getMimeId(); $iterator->next(); $signed_id = $iterator->current()->getMimeId(); $id_ob = new Horde_Mime_Id($signed_id); $sig_id = $id_ob->idArithmetic($id_ob::ID_NEXT); if (!IMP_Pgp::enabled()) { return array($sig_id => null); } $status = new IMP_Mime_Status($this->_mimepart); $status->addText(_("The data in this part has been digitally signed via PGP.")); $status->icon('mime/encryption.png', 'PGP'); $ret = array($base_id => array('data' => '', 'nosummary' => true, 'status' => array($status), 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'), $sig_id => null); if ($prefs->getValue('pgp_verify') || $injector->getInstance('Horde_Variables')->pgp_verify_msg) { $imp_contents = $this->getConfigParam('imp_contents'); $sig_part = $imp_contents->getMimePart($sig_id); $status2 = new IMP_Mime_Status($this->_mimepart); if (!$sig_part) { $status2->action(IMP_Mime_Status::ERROR); $sig_text = _("This digitally signed message is broken."); $ret[$base_id]['wrap'] = 'mimePartWrapInvalid'; } else { /* Close session, since this may be a long-running * operation. */ $session->close(); try { $imp_pgp = $injector->getInstance('IMP_Pgp'); if ($sig_raw = $sig_part->getMetadata(Horde_Crypt_Pgp_Parse::SIG_RAW)) { $sig_result = $imp_pgp->verifySignature($sig_raw, $this->_getSender()->bare_address, null, $sig_part->getMetadata(Horde_Crypt_Pgp_Parse::SIG_CHARSET)); } else { $stream = $imp_contents->isEmbedded($signed_id) ? $this->_mimepart->getMetadata(self::PGP_SIGN_ENC) : $imp_contents->getBodyPart($signed_id, array('mimeheaders' => true, 'stream' => true))->data; rewind($stream); stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol'); $filter = stream_filter_append($stream, 'horde_eol', STREAM_FILTER_READ, array('eol' => Horde_Mime_Part::RFC_EOL)); $sig_result = $imp_pgp->verifySignature(stream_get_contents($stream), $this->_getSender()->bare_address, $sig_part->getContents()); stream_filter_remove($filter); } $status2->action(IMP_Mime_Status::SUCCESS); $sig_text = $sig_result->message; $ret[$base_id]['wrap'] = 'mimePartWrapValid'; } catch (Horde_Exception $e) { $status2->action(IMP_Mime_Status::ERROR); $sig_text = $e->getMessage(); $ret[$base_id]['wrap'] = 'mimePartWrapInvalid'; } } $status2->addText($this->_textFilter($sig_text, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::NOHTML))); $ret[$base_id]['status'][] = $status2; } else { $status->addMimeAction('pgpVerifyMsg', _("Click to verify the message.")); } return $ret; }
/** * Generate inline message display. * * @param array $options Options: * - mask: (integer) The mask needed for a getSummary() call. * - mimeid: (string) Restrict output to this MIME ID (and children). * * @return array See getInlineOutput(). */ protected function _getInlineOutput(array $options) { global $prefs, $registry; $atc_parts = $display_ids = $i = $metadata = $msgtext = $wrap_ids = array(); $text_out = ''; $view = $registry->getView(); $contents_mask = isset($options['mask']) ? $options['mask'] : 0; $mimeid_filter = isset($options['mimeid']) ? new Horde_Mime_Id($options['mimeid']) : null; $show_parts = $prefs->getValue('parts_display'); foreach ($this->contents->getMIMEMessage()->partIterator() as $part) { $mime_id = $part->getMimeId(); $i[] = $mime_id; if (isset($display_ids[$mime_id]) || isset($atc_parts[$mime_id])) { continue; } if ($mimeid_filter && (strval($mimeid_filter) != $mime_id && !$mimeid_filter->isChild($mime_id))) { continue; } if (!($render_mode = $this->contents->canDisplay($mime_id, IMP_Contents::RENDER_INLINE_AUTO))) { if (IMP_Mime_Attachment::isAttachment($part)) { if ($show_parts == 'atc') { $atc_parts[$mime_id] = 1; } if ($contents_mask) { $msgtext[$mime_id] = array('text' => $this->_formatSummary($this->contents->getSummary($mime_id, $contents_mask), true)); } } continue; } $render_part = $this->contents->renderMIMEPart($mime_id, $render_mode); if ($show_parts == 'atc' && IMP_Mime_Attachment::isAttachment($part) && (empty($render_part) || !($render_mode & IMP_Contents::RENDER_INLINE))) { $atc_parts[$mime_id] = 1; } if (empty($render_part)) { if ($contents_mask && IMP_Mime_Attachment::isAttachment($part)) { $msgtext[$mime_id] = array('text' => $this->_formatSummary($this->contents->getSummary($mime_id, $contents_mask), true)); } continue; } reset($render_part); while (list($id, $info) = each($render_part)) { $display_ids[$id] = 1; if (empty($info)) { continue; } $part_text = $contents_mask && empty($info['nosummary']) ? $this->_formatSummary($this->contents->getSummary($id, $contents_mask), !empty($info['attach'])) : ''; if (empty($info['attach'])) { if (isset($info['status'])) { if (!is_array($info['status'])) { $info['status'] = array($info['status']); } $render_issues = array(); foreach ($info['status'] as $val) { if (in_array($view, $val->views)) { if ($val instanceof IMP_Mime_Status_RenderIssue) { $render_issues[] = $val; } else { $part_text .= strval($val); } } } if (!empty($render_issues)) { $render_issues_ob = new IMP_Mime_Status_RenderIssue_Display(); $render_issues_ob->addIssues($render_issues); $part_text .= strval($render_issues_ob); } } $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['metadata'])) { /* Format: array(identifier, ...[data]...) */ $metadata = array_merge($metadata, $info['metadata']); } } } if (!empty($msgtext)) { uksort($msgtext, 'strnatcmp'); } reset($msgtext); while (list($id, $part) = each($msgtext)) { while (!empty($wrap_ids)) { $id_ob = new Horde_Mime_Id(end($wrap_ids)); if ($id_ob->isChild($id)) { break; } array_pop($wrap_ids); $text_out .= '</div>'; } if (!empty($part['wrap'])) { $text_out .= '<div class="' . $part['wrap'] . '" impcontentsmimeid="' . $id . '">'; $wrap_ids[] = $id; } $text_out .= '<div class="mimePartBase"' . (empty($part['wrap']) ? ' impcontentsmimeid="' . $id . '"' : '') . '>' . $part['text'] . '</div>'; } $text_out .= str_repeat('</div>', count($wrap_ids)); if (!strlen($text_out)) { $text_out = strval(new IMP_Mime_Status(null, _("There are no parts that can be shown inline."))); } $atc_parts = $show_parts == 'all' ? $i : array_keys($atc_parts); return array('atc_parts' => $atc_parts, 'display_ids' => array_keys($display_ids), 'metadata' => $metadata, 'msgtext' => $text_out, 'one_part' => count($i) === 1); }
/** * @deprecated Use Horde_Mime_Id instead. */ public static function isChild($base, $id) { $id_ob = new Horde_Mime_Id($base); return $id_ob->isChild($id); }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $imp_contents = $this->getConfigParam('imp_contents'); $machine = $original = null; $ret = array(); switch ($this->_mimepart->getType()) { case 'message/disposition-notification': /* Outlook can send a disposition-notification without the * RFC-required multipart/report wrapper. */ $machine = $imp_contents->getMimePart($this->_mimepart->getMimeId()); break; case 'multipart/report': /* RFC 3798 [3]: There are three parts to a delivery status * multipart/report message: * (1) Human readable message * (2) Machine parsable body part * [message/disposition-notification] * (3) Original message (optional) */ $iterator = $this->_mimepart->partIterator(false); $iterator->rewind(); if (!($curr = $iterator->current())) { break; } $part1_id = $curr->getMimeId(); $id_ob = new Horde_Mime_Id($part1_id); /* Technical details. */ $id_ob->id = $id_ob->idArithmetic($id_ob::ID_NEXT); $ret[$id_ob->id] = null; $machine = $imp_contents->getMimePart($id_ob->id); /* Original sent message. */ $original = $imp_contents->getMimePart($id_ob->idArithmetic($id_ob::ID_NEXT)); if ($original) { foreach ($this->_mimepart->partIterator() as $val) { $ret[$val->getMimeId()] = null; } /* Allow the human readable part to be displayed * separately. */ unset($ret[$part1_id]); } break; default: return array($this->_mimepart->getMimeId() => null); } $mdn_status = array(_("A message you have sent has resulted in a return notification from the recipient.")); if ($machine) { $parse = Horde_Mime_Headers::parseHeaders(preg_replace('/\\n{2,}/', "\n", strtr($machine->getContents(), "\r", "\n"))); if (isset($parse['Final-Recipient'])) { list(, $recip) = explode(';', $parse['Final-Recipient']->value_single); if ($recip) { $mdn_status[] = sprintf(_("Recipient: %s"), trim($recip)); } } if (isset($parse['Disposition'])) { list($modes, $type) = explode(';', $parse['Disposition']->value_single); list($action, $sent) = explode('/', $modes); switch (trim(Horde_String::lower($type))) { case 'displayed': $mdn_status[] = _("The message has been displayed to the recipient."); break; case 'deleted': $mdn_status[] = _("The message has been deleted by the recipient; it is unknown whether they viewed the message."); break; } switch (trim(Horde_String::lower($action))) { case 'manual-action': // NOOP break; case 'automatic-action': // NOOP break; } switch (trim(Horde_String::lower($sent))) { case 'mdn-sent-manually': $mdn_status[] = _("This notification was explicitly sent by the recipient."); break; case 'mdn-sent-automatically': // NOOP break; } } } $status = new IMP_Mime_Status($this->_mimepart, $mdn_status); $status->icon('info_icon.png', _("Info")); if ($original) { $status->addText($imp_contents->linkViewJS($original, 'view_attach', _("View the text of the original sent message."), array('params' => array('ctype' => 'message/rfc822', 'mode' => IMP_Contents::RENDER_FULL)))); } $ret[$this->_mimepart->getMimeId()] = array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'); return $ret; }
/** * Parse signed data. * * @param boolean $sig_only Only do signature checking? * * @return mixed See self::_getEmbeddedMimeParts(). */ protected function _parseSignedData($sig_only = false) { $iterator = $this->_mimepart->partIterator(); $iterator->rewind(); if (!($curr = $iterator->current())) { return null; } $base_id = $curr->getMimeId(); $iterator->next(); if (!($curr = $iterator->current())) { // application/pkcs-7-mime might be the base part. // See RFC 5751 3.4.2 $data_id = $base_id; } else { $data_id = $curr->getMimeId(); } $id_ob = new Horde_Mime_Id($data_id); $sig_id = $id_ob->idArithmetic($id_ob::ID_NEXT); /* Initialize inline data. */ $status = new IMP_Mime_Status($this->_mimepart, _("The data in this part has been digitally signed via S/MIME.")); $status->icon('mime/encryption.png', 'S/MIME'); $cache = $this->getConfigParam('imp_contents')->getViewCache(); $cache->smime[$base_id] = array('sig' => $sig_id, 'status' => $status, 'wrap' => 'mimePartWrap'); if (!IMP_Smime::enabled()) { $status->addText(_("S/MIME support is not enabled so the digital signature is unable to be verified.")); return null; } $imp_contents = $this->getConfigParam('imp_contents'); $stream = $imp_contents->isEmbedded($base_id) ? $this->_mimepart->getMetadata('imp-smime-decrypt')->stream : $this->_getPartStream($base_id); $raw_text = $this->_mimepart->replaceEOL($stream, Horde_Mime_Part::RFC_EOL); $this->_initSmime(); $sig_result = null; if ($GLOBALS['prefs']->getValue('smime_verify') || $GLOBALS['injector']->getInstance('Horde_Variables')->smime_verify_msg) { try { $sig_result = $this->_impsmime->verifySignature($raw_text); if ($sig_result->verify) { $status->action(IMP_Mime_Status::SUCCESS); } else { $status->action(IMP_Mime_Status::WARNING); } if (!is_array($sig_result->email)) { $sig_result->email = array($sig_result->email); } $email = implode(', ', $sig_result->email); $cache->smime[$base_id]['wrap'] = 'mimePartWrapValid'; $status->addText($sig_result->msg); if (!empty($sig_result->cert)) { $cert = $this->_impsmime->parseCert($sig_result->cert); if (isset($cert['certificate']['subject']['CommonName']) && strcasecmp($email, $cert['certificate']['subject']['CommonName']) !== 0) { $email = $cert['certificate']['subject']['CommonName'] . ' (' . trim($email) . ')'; } } if (!empty($sig_result->cert) && isset($sig_result->email) && $GLOBALS['registry']->hasMethod('contacts/addField') && $GLOBALS['prefs']->getValue('add_source')) { $status->addText(sprintf(_("Sender: %s"), $imp_contents->linkViewJS($this->_mimepart, 'view_attach', htmlspecialchars($email), array('jstext' => _("View certificate details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE, 'view_smime_key' => 1))))); foreach ($sig_result->email as $single_email) { try { $this->_impsmime->getPublicKey($single_email); } catch (Horde_Exception $e) { $imple = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Imple')->create('IMP_Ajax_Imple_ImportEncryptKey', array('mime_id' => $base_id, 'muid' => strval($imp_contents->getIndicesOb()), 'type' => 'smime')); $status->addText(Horde::link('#', '', '', '', '', '', '', array('id' => $imple->getDomId())) . _("Save the certificate to your Address Book.") . '</a>'); break; } } } elseif (strlen($email)) { $status->addText(sprintf(_("Sender: %s"), htmlspecialchars($email))); } } catch (Horde_Exception $e) { $status->action(IMP_Mime_Status::ERROR); $cache->smime[$base_id]['wrap'] = 'mimePartWrapInvalid'; $status->addText($e->getMessage()); } } else { $status->addMimeAction('smimeVerifyMsg', _("Click to verify the data.")); } if ($sig_only) { return; } if (!($subpart = $imp_contents->getMimePart($sig_id))) { try { $msg_data = $this->_impsmime->extractSignedContents($raw_text); $subpart = Horde_Mime_Part::parseMessage($msg_data, array('forcemime' => true)); } catch (Horde_Exception $e) { $status->addText($e->getMessage()); return null; } } return $subpart; }
/** * Regenerates body text for use in the compose screen from IMAP data. * * @param IMP_Contents $contents An IMP_Contents object. * @param array $options Additional options: * <ul> * <li>html: (boolean) Return text/html part, if available.</li> * <li>imp_msg: (integer) If non-empty, the message data was created by * IMP. Either: * <ul> * <li>self::COMPOSE</li> * <li>self::FORWARD</li> * <li>self::REPLY</li> * </ul> * </li> * <li>replylimit: (boolean) Enforce length limits?</li> * <li>toflowed: (boolean) Do flowed conversion?</li> * </ul> * * @return mixed Null if bodypart not found, or array with the following * keys: * - charset: (string) The guessed charset to use. * - flowed: (Horde_Text_Flowed) A flowed object, if the text is flowed. * Otherwise, null. * - id: (string) The MIME ID of the bodypart. * - mode: (string) Either 'text' or 'html'. * - text: (string) The body text. */ protected function _getMessageText($contents, array $options = array()) { global $conf, $injector, $notification, $prefs; $body_id = null; $mode = 'text'; $options = array_merge(array('imp_msg' => self::COMPOSE), $options); if (!empty($options['html']) && self::canHtmlCompose() && ($body_id = $contents->findBody('html')) !== null) { $mime_message = $contents->getMIMEMessage(); switch ($mime_message->getPrimaryType()) { case 'multipart': if ($body_id != '1' && $mime_message->getSubType() == 'mixed' && ($id_ob = new Horde_Mime_Id('1')) && !$id_ob->isChild($body_id)) { $body_id = null; } else { $mode = 'html'; } break; default: if (strval($body_id) != '1') { $body_id = null; } else { $mode = 'html'; } break; } } if (is_null($body_id)) { $body_id = $contents->findBody(); if (is_null($body_id)) { return null; } } if (!($part = $contents->getMimePart($body_id))) { return null; } $type = $part->getType(); $part_charset = $part->getCharset(); $msg = Horde_String::convertCharset($part->getContents(), $part_charset, 'UTF-8'); /* Enforce reply limits. */ if (!empty($options['replylimit']) && !empty($conf['compose']['reply_limit'])) { $limit = $conf['compose']['reply_limit']; if (Horde_String::length($msg) > $limit) { $msg = Horde_String::substr($msg, 0, $limit) . "\n" . _("[Truncated Text]"); } } if ($mode == 'html') { $dom = $injector->getInstance('Horde_Core_Factory_TextFilter')->filter($msg, 'Xss', array('charset' => $this->charset, 'return_dom' => true, 'strip_style_attributes' => false)); /* If we are replying to a related part, and this part refers * to local message parts, we need to move those parts into this * message (since the original message may disappear during the * compose process). */ if ($related_part = $contents->findMimeType($body_id, 'multipart/related')) { $this->_setMetadata('related_contents', $contents); $related_ob = new Horde_Mime_Related($related_part); $related_ob->cidReplace($dom, array($this, '_getMessageTextCallback'), $part_charset); $this->_setMetadata('related_contents', null); } /* Convert any Data URLs to attachments. */ $xpath = new DOMXPath($dom->dom); foreach ($xpath->query('//*[@src]') as $val) { $data_url = new Horde_Url_Data($val->getAttribute('src')); if (strlen($data_url->data)) { $data_part = new Horde_Mime_Part(); $data_part->setContents($data_url->data); $data_part->setType($data_url->type); try { $atc = $this->addAttachmentFromPart($data_part); $val->setAttribute('src', $atc->viewUrl()); $this->addRelatedAttachment($atc, $val, 'src'); } catch (IMP_Compose_Exception $e) { $notification->push($e, 'horde.warning'); } } } $msg = $dom->returnBody(); } elseif ($type == 'text/html') { $msg = $injector->getInstance('Horde_Core_Factory_TextFilter')->filter($msg, 'Html2text'); $type = 'text/plain'; } /* Always remove leading/trailing whitespace. The data in the * message body is not intended to be the exact representation of the * original message (use forward as message/rfc822 part for that). */ $msg = trim($msg); if ($type == 'text/plain') { if ($prefs->getValue('reply_strip_sig') && ($pos = strrpos($msg, "\n-- ")) !== false) { $msg = rtrim(substr($msg, 0, $pos)); } /* Remove PGP armored text. */ $pgp = $injector->getInstance('Horde_Crypt_Pgp_Parse')->parseToPart($msg); if (!is_null($pgp)) { $msg = ''; $pgp->buildMimeIds(); foreach ($pgp->partIterator() as $val) { if ($val->getPrimaryType() === 'text') { $msg .= $val->getContents(); } } } if ($part->getContentTypeParameter('format') == 'flowed') { $flowed = new Horde_Text_Flowed($msg, 'UTF-8'); if (Horde_String::lower($part->getContentTypeParameter('delsp')) == 'yes') { $flowed->setDelSp(true); } $flowed->setMaxLength(0); $msg = $flowed->toFixed(false); } else { /* If the input is *not* in flowed format, make sure there is * no padding at the end of lines. */ $msg = preg_replace("/\\s*\n/U", "\n", $msg); } if (isset($options['toflowed'])) { $flowed = new Horde_Text_Flowed($msg, 'UTF-8'); $msg = $options['toflowed'] ? $flowed->toFlowed(true) : $flowed->toFlowed(false, array('nowrap' => true)); } } if (strcasecmp($part->getCharset(), 'windows-1252') === 0) { $part_charset = 'ISO-8859-1'; } return array('charset' => $part_charset, 'flowed' => isset($flowed) ? $flowed : null, 'id' => $body_id, 'mode' => $mode, 'text' => $msg); }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $imp_contents = $this->getConfigParam('imp_contents'); $machine = $original = $status = null; $mime_id = $this->_mimepart->getMimeId(); $ret = array(); switch ($this->_mimepart->getType()) { case 'message/delivery-status': $machine = $imp_contents->getMimePart($mime_id); break; case 'multipart/report': /* RFC 3464 [2]: There are three parts to a delivery status * multipart/report message: * (1) Human readable message * (2) Machine parsable body part (message/delivery-status) * (3) Returned message (optional) */ $iterator = $this->_mimepart->partIterator(false); $iterator->rewind(); if (!($curr = $iterator->current())) { break; } $part1_id = $curr->getMimeId(); $id_ob = new Horde_Mime_Id($part1_id); /* Technical details. */ $id_ob->id = $id_ob->idArithmetic($id_ob::ID_NEXT); $ret[$id_ob->id] = null; $machine = $imp_contents->getMimePart($id_ob->id); /* Returned message. */ $original = $imp_contents->getMimePart($id_ob->idArithmetic($id_ob::ID_NEXT)); if ($original) { foreach ($this->_mimepart->partIterator() as $val) { $ret[$val->getMimeId()] = null; } /* Allow the human readable part to be displayed * separately. */ unset($ret[$part1_id]); } break; } if (!$machine) { return array($mime_id => null); } $parse = Horde_Mime_Headers::parseHeaders(preg_replace('/\\n{2,}/', "\n", strtr($machine->getContents(), "\r", "\n"))); /* Information on the message status is found in the 'Action' * field located in part #2 (RFC 3464 [2.3.3]). */ if (isset($parse['Action'])) { switch (trim($parse['Action']->value_single)) { case 'failed': case 'delayed': $msg_link = _("View details of the returned message."); $status_action = IMP_Mime_Status::ERROR; $status_msg = _("ERROR: Your message could not be delivered."); break; case 'delivered': case 'expanded': case 'relayed': $msg_link = _("View details of the delivered message."); $status_action = IMP_Mime_Status::SUCCESS; $status_msg = _("Your message was successfully delivered."); break; } if (isset($msg_link)) { $status = new IMP_Mime_Status($this->_mimepart, $status_msg); $status->action($status_action); if (isset($parse['Final-Recipient'])) { list(, $recip) = explode(';', $parse['Final-Recipient']->value_single); $recip_ob = new Horde_Mail_Rfc822_List($recip); if (count($recip_ob)) { $status->addText(sprintf(_("Recipient: %s"), $recip_ob[0])); } } /* Display a link to the returned message, if it exists. */ if ($original) { $status->addText($imp_contents->linkViewJS($original, 'view_attach', $msg_link, array('params' => array('ctype' => 'message/rfc822')))); } } } $ret[$mime_id] = array_filter(array('data' => '', 'status' => $status ?: null, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap')); return $ret; }
/** * Render out the currently set contents. * * @param boolean $inline Are we viewing inline? * * @return array See parent::render(). */ protected function _IMPrender($inline) { $base_id = $this->_mimepart->getMimeId(); $display_ids = $ret = array(); $prefer_plain = $GLOBALS['prefs']->getValue('alternative_display') == 'text'; /* Look for a displayable part. RFC: show the LAST choice that can be * displayed inline. If an alternative is itself a multipart, the user * agent is allowed to show that alternative, an earlier alternative, * or both. If we find a multipart alternative that contains at least * one viewable part, we will display all viewable subparts of that * alternative. */ $imp_contents = $this->getConfigParam('imp_contents'); foreach ($this->_mimepart->partIterator() as $val) { $id = $val->getMimeId(); $ret[$id] = null; if (strcmp($base_id, $id) !== 0 && $imp_contents->canDisplay($id, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL) && (!$prefer_plain || $val->getType() != 'text/html' && $val->getPrimaryType() == 'text')) { $display_ids[strval($id)] = true; } } /* If we found no IDs, return now. */ if (empty($display_ids)) { $ret[$base_id] = array('data' => '', 'status' => new IMP_Mime_Status($this->_mimepart, _("There are no alternative parts that can be displayed inline.")), 'type' => 'text/html; charset=' . $this->getConfigParam('charset')); return $ret; } /* If the last viewable message exists in a subpart, back up to the * base multipart and display all viewable parts in that multipart. * Else, display the single part. */ end($display_ids); $curr_id = key($display_ids); while (!is_null($curr_id) && strcmp($base_id, $curr_id) !== 0) { if (array_key_exists($curr_id, $ret)) { $disp_id = $curr_id; } $id_ob = new Horde_Mime_Id($curr_id); $curr_id = $id_ob->idArithmetic($id_ob::ID_UP); } /* At this point, $ret contains stubs for all parts living in the base * alternative part. * Go through all subparts of displayable part and make sure all parts * are rendered. Parts not rendered will be marked as not being * handled by this viewer (Bug #9365). */ $render_part = $this->_mimepart[$disp_id]; foreach ($render_part->partIterator() as $val) { $id = $val->getMimeId(); $need_render[$id] = $subparts[$id] = true; } /* Track whether there is at least one viewable (non-empty) part. */ $viewable = false; $viewable_ret = $ret; foreach (array_keys($subparts) as $val) { if (isset($display_ids[$val]) && isset($need_render[$val])) { $render = $this->getConfigParam('imp_contents')->renderMIMEPart($val, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL); foreach (array_keys($render) as $id) { unset($need_render[$id]); if (!$inline) { if (!is_null($render[$id])) { return array($base_id => $render[$id]); } } else { $ret[$id] = $render[$id]; if (!is_null($ret[$id])) { $viewable = true; } } } } } if (!$inline) { return null; } unset($need_render[$disp_id]); foreach (array_keys($need_render) as $val) { unset($ret[$val]); } /* If we reach this point, and have at least one subpart with no * viewable parts, check to see there is not a richer, non-inline * viewable part that exists in the message. */ if (!$viewable) { $id_ob = new Horde_Mime_Id($disp_id); if (array_key_exists($id_ob->idArithmetic($id_ob::ID_NEXT), $viewable_ret)) { $ret[$disp_id] = array('data' => '', 'status' => new IMP_Mime_Status($this->_mimepart, array(_("This part contains no message contents."), _("There are no alternative parts that can be displayed inline."))), 'type' => 'text/html; charset=' . $this->getConfigParam('charset')); } } return $ret; }