/** * Return the rendered information about the Horde_Mime_Part object. * * URL parameters used by this function: * - zip_contents: (integer) If set, show contents of ZIP file. * * @return array See Horde_Mime_Viewer_Driver::render(). */ protected function _renderInfo() { global $injector; $vars = $injector->getInstance('Horde_Variables'); if (!$this->getConfigParam('show_contents') && !$vars->zip_contents) { $status = new IMP_Mime_Status($this->_mimepart, _("This is a compressed file.")); $status->addMimeAction('zipViewContents', _("Click to display the file contents.")); $status->icon('mime/compressed.png'); return array($this->_mimepart->getMimeId() => array('data' => '', 'status' => $status, 'type' => 'text/html; charset=UTF-8')); } $view = new Horde_View(array('templatePath' => IMP_TEMPLATES . '/mime')); $view->addHelper('Text'); $view->downloadclass = 'zipdownload'; $view->files = array(); $view->tableclass = 'zipcontents'; $zlib = Horde_Util::extensionExists('zlib'); foreach ($this->_getZipInfo() as $key => $val) { $file = new stdClass(); $file->name = $val['name']; $file->size = IMP::sizeFormat($val['size']); /* TODO: Add ability to render in-browser for filetypes we can * handle. */ if (!empty($val['size']) && strstr($val['attr'], 'D') === false && ($zlib && $val['method'] == 0x8 || $val['method'] == 0x0)) { $file->download = $this->getConfigParam('imp_contents')->linkView($this->_mimepart, 'download_render', '', array('class' => 'iconImg downloadAtc', 'jstext' => _("Download"), 'params' => array('zip_attachment' => $key))); } else { $file->download = ''; } $view->files[] = $file; } return array($this->_mimepart->getMimeId() => array('data' => $view->render('compressed'), 'type' => 'text/html; charset=UTF-8')); }
/** * Return the rendered information about the Horde_Mime_Part object. * * URL parameters used by this function: * - tgz_contents: (integer) If set, show contents of ZIP file. * * @return array See Horde_Mime_Viewer_Driver::render(). */ protected function _renderInfo() { global $injector; $vars = $injector->getInstance('Horde_Variables'); if (!$this->getConfigParam('show_contents') && !isset($vars->tgz_contents)) { $status = new IMP_Mime_Status($this->_mimepart, _("This is a compressed file.")); $status->addMimeAction('tgzViewContents', _("Click to display the file contents.")); $status->icon('mime/compressed.png'); return array($this->_mimepart->getMimeId() => array('data' => '', 'status' => $status, 'type' => 'text/html; charset=UTF-8')); } $view = new Horde_View(array('templatePath' => IMP_TEMPLATES . '/mime')); $view->addHelper('Text'); $view->downloadclass = 'tgzdownload'; $view->files = array(); $view->tableclass = 'tgzcontents'; $tgzInfo = $this->_getTgzInfo(); foreach ($tgzInfo as $key => $val) { if (!strlen($val['data'])) { continue; } $file = new stdClass(); $file->download = ''; $file->name = $val['name']; $file->size = IMP::sizeFormat($val['size']); if (!empty($val['size'])) { $file->download = $this->getConfigParam('imp_contents')->linkView($this->_mimepart, 'download_render', '', array('class' => 'iconImg downloadAtc', 'jstext' => _("Download"), 'params' => array('tgz_attachment' => $key))); } $view->files[] = $file; } return array($this->_mimepart->getMimeId() => array('data' => $view->render('compressed'), 'type' => 'text/html; charset=UTF-8')); }
/** * 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. */ $mime_id = $this->_mimepart->getMimeId(); $parts_list = array_keys($this->_mimepart->contentTypeMap()); reset($parts_list); $applefile_id = next($parts_list); $data_id = Horde_Mime::mimeIdArithmetic($applefile_id, 'next'); $applefile_part = $this->_mimepart->getPart($applefile_id); $data_part = $this->_mimepart->getPart($data_id); $data_name = $this->getConfigParam('imp_contents')->getPartName($data_part); $status = new IMP_Mime_Status(array(sprintf(_("This message contains a Macintosh file (named \"%s\")."), $data_name), sprintf(_("The Macintosh resource fork can be downloaded %s."), $this->getConfigParam('imp_contents')->linkViewJS($applefile_part, 'download_attach', _("HERE"), array('jstext' => _("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 ($parts_list as $val) { if (!isset($ret[$val]) && strcmp($val, $data_id) !== 0) { $ret[$val] = strcmp($val, $mime_id) === 0 ? array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap') : null; } } return $ret; }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $mdn_id = $this->_mimepart->getMimeId(); $parts = array_keys($this->_mimepart->contentTypeMap()); $status = new IMP_Mime_Status(_("A message you have sent has resulted in a return notification from the recipient.")); $status->icon('info_icon.png', _("Info")); /* 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) */ /* Get the human readable message. */ reset($parts); $part1_id = next($parts); /* Display a link to more detailed message. */ $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); $part = $this->getConfigParam('imp_contents')->getMIMEPart($part2_id); if ($part) { $status->addText(sprintf(_("Technical details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/plain', 'mode' => IMP_Contents::RENDER_FULL))))); } $ret[$part2_id] = null; /* Display a link to the sent message. */ $part3_id = Horde_Mime::mimeIdArithmetic($part2_id, 'next'); $part = $this->getConfigParam('imp_contents')->getMIMEPart($part3_id); if ($part) { $status->addText(sprintf(_("The text of the sent message can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("The text of the sent message"), 'params' => array('ctype' => 'message/rfc822', 'mode' => IMP_Contents::RENDER_FULL))))); foreach (array_keys($part->contentTypeMap()) as $key) { $ret[$key] = null; } } $ret[$mdn_id] = array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'); return $ret; }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { global $registry; if ($registry->getView() == $registry::VIEW_MINIMAL) { return array(); } $status = array(); $mime_id = $this->_mimepart->getMimeId(); $headers = Horde_Mime_Headers::parseHeaders($this->getConfigParam('imp_contents')->getBodyPart($mime_id, array('length' => 0, 'mimeheaders' => true, 'stream' => true))->data); if (($duration = $headers->getValue('content-duration')) !== null) { $text = array(); if ($minutes = floor($duration / 60)) { $text[] = sprintf(ngettext(_("%d minute"), _("%d minutes"), $minutes), $minutes); } if ($seconds = $duration % 60) { $text[] = sprintf(ngettext(_("%d second"), _("%d seconds"), $seconds), $seconds); } $status[] = sprintf(_("This video file is reported to be %s in length."), implode(' ', $text)); } if ($this->_thumbnailBinary()) { $status[] = _("This is a thumbnail of a video attachment."); $status[] = $this->getConfigParam('imp_contents')->linkViewJS($this->_mimepart, 'view_attach', '<img src="' . $this->getConfigParam('imp_contents')->urlView($this->_mimepart, 'view_attach', array('params' => array('imp_video_view' => 'view_thumbnail'))) . '" />', null, null, null); } if (empty($status)) { return array(); } $s = new IMP_Mime_Status($status); $s->icon('mime/video.png'); return array($this->_mimepart->getMimeId() => array('data' => '', 'status' => $s, 'type' => 'text/html; charset=UTF-8')); }
/** * 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; }
/** * Return the rendered inline version of the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInline() { $GLOBALS['page_output']->growler = true; $data = $this->_mimepart->getContents(); $mime_id = $this->_mimepart->getMimeId(); // Parse the iCal file. $vCal = new Horde_Icalendar(); if (!$vCal->parsevCalendar($data, 'VCALENDAR', $this->_mimepart->getCharset())) { $status = new IMP_Mime_Status(_("The calendar data is invalid")); $status->action(IMP_Mime_Status::ERROR); return array($mime_id => array('data' => '', 'status' => $status, 'type' => 'text/html; charset=UTF-8')); } // Check if we got vcard data with the wrong vcalendar mime type. $imp_contents = $this->getConfigParam('imp_contents'); $c = $vCal->getComponentClasses(); if (count($c) == 1 && !empty($c['horde_icalendar_vcard'])) { return $imp_contents->renderMIMEPart($mime_id, IMP_Contents::RENDER_INLINE, array('type' => 'text/x-vcard')); } $imple = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Imple')->create('IMP_Ajax_Imple_ItipRequest', array('mime_id' => $mime_id, 'muid' => strval($imp_contents->getIndicesOb()))); // Get the method type. try { $method = $vCal->getAttribute('METHOD'); } catch (Horde_Icalendar_Exception $e) { $method = ''; } $out = array(); $components = $vCal->getComponents(); foreach ($components as $key => $component) { switch ($component->getType()) { case 'vEvent': try { if ($component->getAttribute('RECURRENCE-ID')) { break; } } catch (Horde_ICalendar_Exception $e) { } $out[] = $this->_vEvent($component, $key, $method, $components); break; case 'vTodo': $out[] = $this->_vTodo($component, $key, $method); break; case 'vTimeZone': // Ignore them. break; case 'vFreebusy': $out[] = $this->_vFreebusy($component, $key, $method); break; // @todo: handle stray vcards here as well. // @todo: handle stray vcards here as well. default: $out[] = sprintf(_("Unhandled component of type: %s"), $component->getType()); break; } } $view = $this->_getViewOb(); $view->formid = $imple->getDomId(); $view->out = implode('', $out); return array($mime_id => array('data' => $view->render('base'), 'type' => 'text/html; charset=UTF-8')); }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { /* Check to see if convert utility is available. */ if (!$this->_getHordeImageOb(false)) { return array(); } $status = new IMP_Mime_Status($this->_mimepart, _("This is a thumbnail of a PDF file attachment.")); $status->icon('mime/image.png'); $status->addText($this->getConfigParam('imp_contents')->linkViewJS($this->_mimepart, 'view_attach', $this->_outputImgTag(), null, null, null)); return array($this->_mimepart->getMimeId() => array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'))); }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $mime_id = $this->_mimepart->getMimeId(); $headers = Horde_Mime_Headers::parseHeaders($this->getConfigParam('imp_contents')->getBodyPart($mime_id, array('length' => 0, 'mimeheaders' => true, 'stream' => true))->data); if (($duration = $headers->getValue('content-duration')) === null) { return array(); } $text = array(); if ($minutes = floor($duration / 60)) { $text[] = sprintf(ngettext(_("%d minute"), _("%d minutes"), $minutes), $minutes); } if ($seconds = $duration % 60) { $text[] = sprintf(ngettext(_("%d second"), _("%d seconds"), $seconds), $seconds); } $status = new IMP_Mime_Status(sprintf(_("This audio file is reported to be %s in length."), implode(' ', $text))); $status->icon('mime/audio.png'); return array($this->_mimepart->getMimeId() => array('data' => '', 'status' => $status, 'type' => 'text/html; charset=UTF-8')); }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { /* Check to see if convert utility is available. */ if (!$this->_getHordeImageOb(false)) { return array(); } $status = new IMP_Mime_Status(_("This is a thumbnail of a PDF file attachment.")); $status->icon('mime/image.png'); switch ($GLOBALS['registry']->getView()) { case Horde_Registry::VIEW_MINIMAL: $status->addText(Horde::link($this->getConfigParam('imp_contents')->urlView($this->_mimepart, 'view_attach')) . $this->_outputImgTag() . '</a>'); break; default: $status->addText($this->getConfigParam('imp_contents')->linkViewJS($this->_mimepart, 'view_attach', $this->_outputImgTag(), null, null, null)); break; } return array($this->_mimepart->getMimeId() => array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'))); }
/** * If this MIME part can contain embedded MIME part(s), and those part(s) * exist, return a representation of that data. * * @return mixed A Horde_Mime_Part object representing the embedded data. * Returns null if no embedded MIME part(s) exist. */ protected function _getEmbeddedMimeParts() { $id = $this->_mimepart->getContentTypeParameter('id'); $number = $this->_mimepart->getContentTypeParameter('number'); $total = $this->_mimepart->getContentTypeParameter('total'); if (is_null($id) || is_null($number) || is_null($total)) { return null; } /* Perform the search to find the other parts of the message. */ $query = new Horde_Imap_Client_Search_Query(); $query->headerText('Content-Type', $id); $indices = $this->getConfigParam('imp_contents')->getMailbox()->runSearchQuery($query); /* If not able to find the other parts of the message, prepare a * status message. */ $msg_count = count($indices); if ($msg_count != $total) { $status = new IMP_Mime_Status($this->_mimepart, sprintf(_("Cannot display message - found only %s of %s parts of this message in the current mailbox."), $msg_count, $total)); $status->action(IMP_Mime_Status::ERROR); $cache = $this->getConfigParam('imp_contents')->getViewCache(); $cache->partial[$this->_mimepart->getMimeId()] = $status; return null; } /* Get the contents of each of the parts. */ $parts = array(); foreach ($indices as $ob) { foreach ($ob->uids as $val) { /* No need to fetch the current part again. */ if ($val == $number) { $parts[$number] = $this->_mimepart->getContents(); } else { $ic = $GLOBALS['injector']->getInstance('IMP_Factory_Contents')->create($ob->mbox->getIndicesOb($val)); $parts[$ic->getMIMEMessage()->getContentTypeParameter('number')] = $ic->getBody(); } } } /* Sort the parts in numerical order. */ ksort($parts, SORT_NUMERIC); /* Combine the parts. */ $mime_part = Horde_Mime_Part::parseMessage(implode('', $parts), array('forcemime' => true)); return $mime_part === false ? null : $mime_part; }
/** * Output status block HTML. * * @return string The formatted status message HTML. */ public function __toString() { global $registry; $out = ''; switch ($registry->getView()) { case $registry::VIEW_SMARTMOBILE: break; default: $unique_id = strval(new Horde_Support_Randomid()); $this->addMimeAction('showRenderIssues', _("Click to show message part display errors."), array('domid' => $unique_id)); $this->icon('info_icon.png', _("Info")); $out = parent::__toString(); $out .= '<div id="' . $unique_id . '" style="display:none">'; foreach ($this->_issues as $val) { $out .= strval($val); } $out .= '</div>'; } return $out; }
/** * Render the object. * * @param boolean $inline Viewing inline? * * @return array See parent::render(). */ protected function _impRender($inline) { global $injector, $prefs, $registry; $cache = $this->getConfigParam('imp_contents')->getViewCache(); $mime_id = $this->_mimepart->getMimeId(); if (isset($cache->plain[$mime_id])) { return array($mime_id => null); } // Trim extra whitespace in the text. $charset = $this->_mimepart->getCharset(); $text = trim($this->_mimepart->getContents()); if ($text == '') { return array($mime_id => array('data' => '', 'type' => 'text/html; charset=' . $charset)); } // Convert to the local charset. if ($inline) { $text = Horde_String::convertCharset($text, $charset, 'UTF-8'); $charset = $this->getConfigParam('charset'); } $type = 'text/html; charset=' . $charset; // Check for 'flowed' text data. if ($this->_mimepart->getContentTypeParameter('format') == 'flowed') { $text = $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp')); } else { /* A "From" located at the beginning of a line in the body text * will be escaped with a '>' by the IMAP server. Remove this * escape character or else the line will display as being * quoted. Flowed conversion would have already taken care of this * for us. */ $text = preg_replace('/(\\n+)> ?From(\\s+)/', "\$1From\$2", $text); } $text = IMP::filterText($text); /* Done processing if in minimal mode. */ if ($registry->getView() == Horde_Registry::VIEW_MINIMAL) { $filters = array('text2html' => array('charset' => $charset, 'parselevel' => Horde_Text_Filter_Text2html::NOHTML_NOBREAK)); $text = $this->_textFilter($text, array_keys($filters), array_values($filters)); return array($mime_id => array('data' => $text, 'type' => $type)); } // Build filter stack. Starts with HTML markup and tab expansion. $filters = array('text2html' => array('charset' => $charset, 'parselevel' => $inline ? Horde_Text_Filter_Text2html::MICRO : Horde_Text_Filter_Text2html::MICRO_LINKURL), 'tabs2spaces' => array()); // Highlight quoted parts of an email. if ($prefs->getValue('highlight_text')) { $hideBlocks = $js_blocks = false; if ($registry->getView() !== $registry::VIEW_SMARTMOBILE) { $js_blocks = $inline; if ($inline) { $show = $prefs->getValue('show_quoteblocks'); $hideBlocks = $show == 'hidden' || $show == 'thread' && $injector->getInstance('Horde_Variables')->page == 'thread'; if (!$hideBlocks && in_array($show, array('list', 'listthread'))) { $header = $this->getConfigParam('imp_contents')->getHeader(); $list_info = $injector->getInstance('IMP_Message_Ui')->getListInformation($header); $hideBlocks = $list_info['exists']; } } } if ($js_blocks) { $filters['highlightquotes'] = array('hideBlocks' => $hideBlocks, 'noJS' => $registry->getView() == Horde_Registry::VIEW_DYNAMIC); } else { $filters['Horde_Text_Filter_Highlightquotes'] = array('hideBlocks' => $hideBlocks); } } // Highlight simple markup of an email. if ($prefs->getValue('highlight_simple_markup')) { $filters['simplemarkup'] = array(); } // Dim signatures. if ($prefs->getValue('dim_signature')) { $filters['dimsignature'] = array(); } if ($prefs->getValue('emoticons')) { $filters['emoticons'] = array('entities' => true); } // Run filters. $status = array(); $text = $this->_textFilter($text, array_keys($filters), array_values($filters)); if (strlen($text)) { // Wordwrap. $text = str_replace(array(' ', "\n "), array(' ', "\n "), $text); if (!strncmp($text, ' ', 1)) { $text = ' ' . substr($text, 1); } } else { $error = new IMP_Mime_Status(array(_("Cannot display message text."), _("The message part may contain incorrect character set information preventing correct display."))); $error->action(IMP_Mime_Status::ERROR); $status[] = $error; } return array($mime_id => array('data' => "<div class=\"fixed leftAlign\">\n" . $text . '</div>', 'status' => $status, 'type' => $type)); }
/** * 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, 65536)); } 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($mime_part, array(_("This message part cannot be viewed because it is too large."), $this->linkView($mime_part, 'download_attach', _("Click to download the data.")))); $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; }
/** * 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; }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $parts = array_keys($this->_mimepart->contentTypeMap()); /* 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) * * Information on the message status is found in the 'Action' field * located in part #2 (RFC 3464 [2.3.3]). It can be either 'failed', * 'delayed', 'delivered', 'relayed', or 'expanded'. */ if (count($parts) < 2) { return array(); } reset($parts); $part1_id = next($parts); $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); $part3_id = Horde_Mime::mimeIdArithmetic($part2_id, 'next'); /* Get the action first - it appears in the second part. */ $action = null; $part2 = $this->getConfigParam('imp_contents')->getMIMEPart($part2_id); foreach (explode("\n", $part2->getContents()) as $line) { if (stristr($line, 'Action:') !== false) { $action = strtolower(trim(substr($line, strpos($line, ':') + 1))); if (strpos($action, ' ') !== false) { $action = substr($action, 0, strpos($action, ' ')); } break; } } if (is_null($action)) { return array(); } /* Get the correct text strings for the action type. */ switch ($action) { case 'failed': case 'delayed': $status = new IMP_Mime_Status(array(_("ERROR: Your message could not be delivered."), sprintf(_("Technical error details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/plain', 'mode' => IMP_Contents::RENDER_FULL)))))); $status->action(IMP_Mime_Status::ERROR); $msg_link = _("The text of the returned message can be viewed %s."); $msg_link_status = _("The text of the returned message"); break; case 'delivered': case 'expanded': case 'relayed': $status = new IMP_Mime_Status(array(_("Your message was successfully delivered."), sprintf(_("Technical message details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/x-simple', 'mode' => IMP_Contents::RENDER_FULL)))))); $status->action(IMP_Mime_Status::SUCCESS); $msg_link = _("The text of the message can be viewed %s."); $msg_link_status = _("The text of the message"); break; default: return array(); } /* Display a link to the returned message, if it exists. */ $part3 = $this->getConfigParam('imp_contents')->getMIMEPart($part3_id); if ($part3) { $status->addText(sprintf($msg_link, $this->getConfigParam('imp_contents')->linkViewJS($part3, 'view_attach', _("HERE"), array('jstext' => $msg_link_status, 'params' => array('ctype' => 'message/rfc822'))))); } $ret = array_fill_keys(array_diff($parts, array($part1_id)), null); $ret[$this->_mimepart->getMimeId()] = array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'); return $ret; }
/** * Generates HTML output for 'multipart/signed' MIME parts. * * @return string The HTML output. */ protected function _outputPGPSigned() { global $conf, $injector, $prefs, $registry, $session; $partlist = array_keys($this->_mimepart->contentTypeMap()); $base_id = reset($partlist); $signed_id = next($partlist); $sig_id = Horde_Mime::mimeIdArithmetic($signed_id, 'next'); if (!$prefs->getValue('use_pgp') || empty($conf['gnupg']['path'])) { return array($sig_id => null); } $status = new IMP_Mime_Status(); $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(); 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_Crypt_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'); 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()); } $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 { switch ($registry->getView()) { case Horde_Registry::VIEW_BASIC: $status->addText(Horde::link(Horde::selfUrlParams()->add(array('pgp_verify_msg' => 1))) . _("Click HERE to verify the message.") . '</a>'); break; case Horde_Registry::VIEW_DYNAMIC: $status->addText(Horde::link('#', '', 'pgpVerifyMsg') . _("Click HERE to verify the message.") . '</a>'); break; } } return $ret; }
/** * 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 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_fill_keys(array_keys($this->_mimepart->contentTypeMap()), 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 ($data_id !== $related_id) { $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); } foreach (array_diff(array_keys($ret), $used) as $val) { if ($val !== $id && !Horde_Mime::isChild($id, $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(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(IMP_Mime_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; }
/** * Create the object used to display the message. * * @param array $args Configuration parameters: * - headers: (array) The headers desired in the returned headers array * (only used with non-preview view). * - preview: (boolean) Is this the preview view? * * @return array Array with the following keys: * - atc_download: The download all link * - atc_label: The label to use for Attachments * - atc_list: The list (HTML code) of attachments * - bcc (FULL): The Bcc addresses * - cc: The CC addresses * - fulldate (FULL): The full canonical date. * - from: The From addresses * - headers (FULL): An array of headers (not including basic headers) * - js: Javascript code to run on display * - list_info (FULL): List information. * - localdate (PREVIEW): The date formatted to the user's timezone * - md: Metadata * - msgtext: The text of the message * - onepart: True if message only contains one part. * - replyTo (FULL): The Reply-to addresses * - save_as: The save link * - subject: The subject * - subjectlink: The subject with linked URLs/email addresses (defaults * to 'subject') * - title (FULL): The title of the page * - to: The To addresses * * @throws IMP_Exception */ public function showMessage($args) { global $injector, $page_output, $prefs, $registry, $session; $preview = !empty($args['preview']); $result = array(); $mime_headers = $this->_peek ? $this->_contents->getHeader() : $this->_contents->getHeaderAndMarkAsSeen(); $headers = array(); $imp_ui = $injector->getInstance('IMP_Message_Ui'); /* Develop the list of Headers to display now. Deal with the 'basic' * header information first since there are various manipulations * done to them. */ $basic_headers = $imp_ui->basicHeaders(); if (empty($args['headers'])) { $args['headers'] = array('from', 'date', 'to', 'cc', 'bcc'); } $headers_list = array_intersect_key($basic_headers, array_flip($args['headers'])); /* Build From/To/Cc/Bcc/Reply-To links. */ foreach (array('from', 'to', 'cc', 'bcc', 'reply-to') as $val) { if (isset($headers_list[$val]) && (!$preview || $val != 'reply-to')) { if ($tmp = $this->getAddressHeader($val)) { $result[$val] = $tmp; } if ($preview) { unset($headers_list[$val]); } } } /* Build the rest of the headers. */ foreach ($headers_list as $head => $str) { if ($val = $mime_headers->getValue($head)) { if ($head == 'date') { /* Add local time to date header. */ $date_ob = new IMP_Message_Date($this->_envelope->date); $val = htmlspecialchars($date_ob->format($date_ob::DATE_LOCAL)); if ($preview) { $result['localdate'] = $val; } else { $result['fulldate'] = $date_ob->format($date_ob::DATE_FORCE); } } elseif (!$preview) { $val = htmlspecialchars($val); } if (!$preview) { $headers[$head] = array('id' => Horde_String::ucfirst($head), 'name' => $str, 'value' => $val); } } } if (empty($result['reply-to']) || $result['from']['addr'][0]->b == $result['reply-to']['addr'][0]->b) { unset($result['reply-to'], $headers['reply-to']); } /* JS requires camelized name for reply-to. */ if (!$preview && isset($headers['reply-to'])) { $result['replyTo'] = $result['reply-to']; $headers['reply-to']['id'] = 'ReplyTo'; unset($result['reply-to']); } /* Maillog information. */ $ajax_queue = $injector->getInstance('IMP_Ajax_Queue'); $ajax_queue->maillog($this->_indices); if (!$preview) { /* Display the user-specified headers for the current identity. */ $user_hdrs = $imp_ui->getUserHeaders(); foreach ($user_hdrs as $user_hdr) { $user_val = $mime_headers->getValue($user_hdr); if (!empty($user_val)) { if (!is_array($user_val)) { $user_val = array($user_val); } foreach ($user_val as $val) { $headers[] = array('name' => $user_hdr, 'value' => htmlspecialchars($val)); } } } $result['headers'] = array_values($headers); } /* Process the subject. */ $subject = $mime_headers->getValue('subject'); if ($subject) { $text_filter = $injector->getInstance('Horde_Core_Factory_TextFilter'); $filtered_subject = preg_replace("/\\b\\s+\\b/", ' ', IMP::filterText($subject)); $result['subject'] = $text_filter->filter($filtered_subject, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::NOHTML)); $subjectlink = $text_filter->filter($filtered_subject, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)); if ($subjectlink != $result['subject']) { $result['subjectlink'] = $subjectlink; } if (!$preview) { $result['title'] = $subject; } } else { $result['subject'] = _("[No Subject]"); if (!$preview) { $result['title'] = _("[No Subject]"); } } // Create message text and attachment list. $result['msgtext'] = ''; $show_parts = $prefs->getValue('parts_display'); switch ($registry->getView()) { case $registry::VIEW_SMARTMOBILE: $contents_mask = 0; break; default: $contents_mask = IMP_Contents::SUMMARY_BYTES | IMP_Contents::SUMMARY_SIZE | IMP_Contents::SUMMARY_ICON | IMP_Contents::SUMMARY_DESCRIP_LINK | IMP_Contents::SUMMARY_DOWNLOAD | IMP_Contents::SUMMARY_DOWNLOAD_ZIP | IMP_Contents::SUMMARY_PRINT_STUB; break; } $part_info = $part_info_display = array('icon', 'description', 'size', 'download', 'download_zip'); $part_info_display[] = 'print'; list($mbox, $uid) = $this->_indices->getSingle(); /* Do MDN processing now. */ switch ($registry->getView()) { case $registry::VIEW_DYNAMIC: if ($imp_ui->MDNCheck(new IMP_Indices($mbox, $uid), $mime_headers)) { $status = new IMP_Mime_Status(array(_("The sender of this message is requesting notification from you when you have read this message."), sprintf(_("Click %s to send the notification message."), Horde::link('#', '', '', '', '', '', '', array('id' => 'send_mdn_link')) . _("HERE") . '</a>'))); $status->domid('sendMdnMessage'); $result['msgtext'] .= strval($status); } } /* Build body text. This needs to be done before we build the * attachment list. */ $session->close(); $inlineout = $this->_contents->getInlineOutput(array('mask' => $contents_mask, 'part_info_display' => $part_info_display, 'show_parts' => $show_parts)); $session->start(); $result['md'] = $inlineout['metadata']; $result['msgtext'] .= $inlineout['msgtext']; if ($inlineout['one_part']) { $result['onepart'] = true; } if (count($inlineout['atc_parts']) || $show_parts == 'all' && count($inlineout['display_ids']) > 2) { $result['atc_label'] = $show_parts == 'all' ? _("Parts") : sprintf(ngettext("%d Attachment", "%d Attachments", count($inlineout['atc_parts'])), count($inlineout['atc_parts'])); if (count($inlineout['atc_parts']) > 2) { $result['atc_download'] = Horde::link($this->_contents->urlView($this->_contents->getMIMEMessage(), 'download_all')) . '[' . _("Save All") . ']</a>'; } } /* Show attachment information in headers? */ if (!empty($inlineout['atc_parts'])) { $partlist = array(); if ($show_parts == 'all') { array_unshift($part_info, 'id'); } foreach ($inlineout['atc_parts'] as $id) { $contents_mask |= IMP_Contents::SUMMARY_DESCRIP | IMP_Contents::SUMMARY_DOWNLOAD | IMP_Contents::SUMMARY_ICON | IMP_Contents::SUMMARY_SIZE; $part_info[] = 'description_raw'; $part_info[] = 'download_url'; $summary = $this->_contents->getSummary($id, $contents_mask); $tmp = array(); foreach ($part_info as $val) { if (isset($summary[$val])) { $tmp[$val] = $summary[$val] instanceof Horde_Url ? strval($summary[$val]->setRaw(true)) : $summary[$val]; } } $partlist[] = array_filter($tmp); } $result['atc_list'] = $partlist; } $result['save_as'] = IMP_Contents_View::downloadUrl(htmlspecialchars_decode($result['subject']), array_merge(array('actionID' => 'save_message'), $mbox->urlParams($uid))); if ($preview) { /* Need to grab cached inline scripts. */ Horde::startBuffer(); $page_output->outputInlineScript(true); if ($js_inline = Horde::endBuffer()) { $result['js'] = array($js_inline); } $result['save_as'] = strval($result['save_as']->setRaw(true)); } else { $list_info = $imp_ui->getListInformation($mime_headers); if (!empty($list_info['exists'])) { $result['list_info'] = $list_info; } } /* Add changed flag information. */ if (!$this->_peek && $mbox->is_imap) { $status = $mbox->imp_imap->status($mbox, Horde_Imap_Client::STATUS_PERMFLAGS); if (in_array(Horde_Imap_Client::FLAG_SEEN, $status['permflags'])) { $ajax_queue->flag(array(Horde_Imap_Client::FLAG_SEEN), true, $this->_indices); } } return array_filter($result); }
/** * 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; }
/** * Parse signed data. * * @param boolean $sig_only Only do signature checking? * * @return mixed See self::_getEmbeddedMimeParts(). */ protected function _parseSignedData($sig_only = false) { $partlist = array_keys($this->_mimepart->contentTypeMap()); $base_id = reset($partlist); $data_id = next($partlist); $sig_id = Horde_Mime::mimeIdArithmetic($data_id, 'next'); /* Initialize inline data. */ $status = new IMP_Mime_Status(_("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 (!$GLOBALS['prefs']->getValue('use_smime')) { $status->addText(_("S/MIME support is not enabled so the digital signature is unable to be verified.")); return null; } /* Sanity checking to make sure MIME structure is correct. */ if (!in_array($sig_id, $partlist)) { $status->action(IMP_Mime_Status::ERROR); $cache->smime[$base_id]['wrap'] = 'mimePartWrapInvalid'; $status->addText(_("Invalid S/MIME data.")); /* This will suppress displaying the invalid part. */ $cache->smime[$base_id]['sig'] = $data_id; 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); } $cache->smime[$base_id]['wrap'] = 'mimePartWrapValid'; $email = is_array($sig_result->email) ? implode(', ', $sig_result->email) : $sig_result->email; $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(strlen($email) ? $email : $sig_result->email), array('jstext' => _("View certificate details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE, 'view_smime_key' => 1))))); try { $this->_impsmime->getPublicKey($sig_result->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>'); } } 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 { switch ($GLOBALS['registry']->getView()) { case Horde_Registry::VIEW_BASIC: $status->addText(Horde::link(Horde::selfUrlParams()->add('smime_verify_msg', 1)) . _("Click HERE to verify the data.") . '</a>'); break; case Horde_Registry::VIEW_DYNAMIC: $status->addText(Horde::link('#', '', 'smimeVerifyMsg') . _("Click HERE to verify the data.") . '</a>'); break; } } if ($sig_only) { return; } $subpart = $imp_contents->getMIMEPart($sig_id); if (empty($subpart)) { 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; }
/** * 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; }
/** * Render out the currently set contents. * * @param boolean $inline Are we viewing inline? * * @return array Two elements: html and status. */ protected function _IMPrender($inline) { global $injector, $registry; $data = $this->_mimepart->getContents(); $view = $registry->getView(); $contents = $this->getConfigParam('imp_contents'); $convert_text = $view == $registry::VIEW_MINIMAL || $injector->getInstance('Horde_Variables')->convert_text; /* Don't do IMP DOM processing if in mimp mode or converting to * text. */ $this->_imptmp = array(); if ($inline && !$convert_text) { $this->_imptmp += array('cid' => null, 'cid_used' => array(), 'cssblock' => false, 'cssbroken' => false, 'imgblock' => false, 'imgbroken' => false, 'inline' => $inline, 'style' => array()); } /* Search for inlined data that we can display (multipart/related * parts) - see RFC 2392. */ if ($related_part = $contents->findMimeType($this->_mimepart->getMimeId(), 'multipart/related')) { $this->_imptmp['cid'] = $related_part->getMetadata('related_ob'); } /* Sanitize the HTML. */ $data = $this->_cleanHTML($data, array('noprefetch' => $inline && $view != Horde_Registry::VIEW_MINIMAL, 'phishing' => $inline)); if (!empty($this->_imptmp['style'])) { $this->_processDomDocument($data->dom); } if ($inline) { $charset = 'UTF-8'; $data = $data->returnHtml(array('charset' => $charset, 'metacharset' => true)); } else { $charset = $this->_mimepart->getCharset(); $data = $data->returnHtml(); } $status = array(); if ($this->_phishWarn) { $status[] = new IMP_Mime_Status(array(sprintf(_("%s: This message may not be from whom it claims to be."), _("WARNING")), _("Beware of following any links in it or of providing the sender with any personal information."), _("The links that caused this warning have this background color:") . ' <span style="' . $this->_phishCss . '">' . _("EXAMPLE LINK") . '</span>')); } /* We are done processing if in mimp mode, or we are converting to * text. */ if ($convert_text) { $data = $this->_textFilter($data, 'Html2text', array('wrap' => false)); // Filter bad language. return array('data' => IMP::filterText($data), 'type' => 'text/plain; charset=' . $charset); } if ($inline) { switch ($view) { case $registry::VIEW_SMARTMOBILE: if ($this->_imptmp['imgblock']) { $tmp_txt = _("Show images..."); } elseif ($this->_imptmp['cssblock']) { $tmp_txt = _("Load message styling..."); } else { $tmp_txt = null; } if (!is_null($tmp_txt)) { $tmp = new IMP_Mime_Status(array('<a href="#unblock-image" data-role="button" data-theme="e">' . $tmp_txt . '</a>')); $tmp->views = array($view); $status[] = $tmp; } break; default: $class = 'unblockImageLink'; if (!$injector->getInstance('IMP_Prefs_Special_ImageReplacement')->canAddToSafeAddrList() || $injector->getInstance('IMP_Identity')->hasAddress($contents->getHeader()->getOb('from'))) { $class .= ' noUnblockImageAdd'; } if ($this->_imptmp['imgblock']) { $tmp = new IMP_Mime_Status(array(_("Images have been blocked in this message part."), Horde::link('#', '', $class, '', '', '', '', array('muid' => strval($contents->getIndicesOb()))) . _("Show Images?") . '</a>')); $tmp->icon('mime/image.png'); $status[] = $tmp; } elseif ($this->_imptmp['cssblock']) { /* This is a bit less intuitive for end users, so hide * within image blocking if possible. */ $tmp = new IMP_Mime_Status(array(_("Message styling has been suppressed in this message part since the style data lives on a remote server."), Horde::link('#', '', $class) . _("Load Styling?") . '</a>')); $tmp->icon('mime/image.png'); $status[] = $tmp; } if ($this->_imptmp['cssbroken']) { $tmp = new IMP_Mime_Status(array(_("This message contains corrupt styling data so the message contents may not appear correctly below."), $contents->linkViewJS($this->_mimepart, 'view_attach', _("Click to view HTML data in new window; it is possible this will allow you to view the message correctly.")))); $tmp->icon('mime/image.png'); $status[] = $tmp; } if ($this->_imptmp['imgbroken']) { $tmp = new IMP_Mime_Status(array(_("This message contains images that cannot be loaded."))); $tmp->icon('mime/image.png'); $status[] = $tmp; } break; } } /* Add used CID information. */ if ($inline && !empty($this->_imptmp['cid'])) { $related_part->setMetadata('related_cids_used', $this->_imptmp['cid_used']); } return array('data' => $data, 'status' => $status, 'type' => 'text/html; charset=' . $charset); }
/** * Create the object used to display the message. * * @return array Array with the following keys: * - atc: (object) Attachment information. * - download: (string) The URL for the download all action. * - label: (string) The attachment label. * - list: (array) Attachment information. * - md: (array) Metadata. * - msgtext: (string) The text of the message. * - onepart: (boolean) True if message only contains one part. * * @throws IMP_Exception */ public function showMessage() { global $prefs, $registry, $session; $result = array(); // Create message text and attachment list. $result['msgtext'] = ''; $show_parts = $prefs->getValue('parts_display'); /* Do MDN processing now. */ switch ($registry->getView()) { case $registry::VIEW_DYNAMIC: if ($this->_indices->mdnCheck($this->_loadHeaders())) { $status = new IMP_Mime_Status(null, array(_("The sender of this message is requesting notification from you when you have read this message."), Horde::link('#', '', '', '', '', '', '', array('id' => 'send_mdn_link')) . _("Click to send the notification message.") . '</a>')); $status->domid('sendMdnMessage'); $result['msgtext'] .= strval($status); } } /* Build body text. This needs to be done before we build the * attachment list. */ $session->close(); $inlineout = $this->getInlineOutput(); $session->start(); $result['md'] = $inlineout['metadata']; $result['msgtext'] .= $inlineout['msgtext']; if ($inlineout['one_part']) { $result['onepart'] = true; } if (count($inlineout['atc_parts']) || $show_parts == 'all' && count($inlineout['display_ids']) > 2) { $result['atc']['label'] = $show_parts == 'all' ? _("Parts") : sprintf(ngettext("%d Attachment", "%d Attachments", count($inlineout['atc_parts'])), count($inlineout['atc_parts'])); if (count($inlineout['atc_parts']) > 1) { $result['atc']['download'] = strval($this->contents->urlView($this->contents->getMIMEMessage(), 'download_all')->setRaw(true)); } } /* Show attachment information in headers? */ if (!empty($inlineout['atc_parts'])) { $partlist = array(); $contents_mask = IMP_Contents::SUMMARY_DESCRIP | IMP_Contents::SUMMARY_DESCRIP_LINK | IMP_Contents::SUMMARY_DOWNLOAD | IMP_Contents::SUMMARY_ICON | IMP_Contents::SUMMARY_SIZE; $part_info = array('icon', 'description', 'size', 'download', 'description_raw', 'download_url'); if ($show_parts == 'all') { array_unshift($part_info, 'id'); } foreach ($inlineout['atc_parts'] as $id) { $summary = $this->contents->getSummary($id, $contents_mask); $tmp = array(); foreach ($part_info as $val) { if (isset($summary[$val])) { $tmp[$val] = $summary[$val] instanceof Horde_Url ? strval($summary[$val]->setRaw(true)) : $summary[$val]; } } $partlist[] = array_filter($tmp); } $result['atc']['list'] = $partlist; } return $result; }