From: Michael M Slusarz Date: Mon, 10 Nov 2008 21:46:37 +0000 (-0700) Subject: More MIME->Mime fixes. More work on render API. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=ea47cc909918579d6fa70b835bb87c0e4d49c7f4;p=horde.git More MIME->Mime fixes. More work on render API. Rendering API is starting to solidify. Give calling code full control over the information to parse. Determine inline/info rendering status when obtaining summary information. --- diff --git a/imp/lib/Contents.php b/imp/lib/Contents.php index 4522042ac..d4bff5381 100644 --- a/imp/lib/Contents.php +++ b/imp/lib/Contents.php @@ -13,6 +13,19 @@ */ class IMP_Contents { + /* Mask entries for getSummary(). */ + const SUMMARY_RENDER = 1; + const SUMMARY_BYTES = 2; + const SUMMARY_SIZE = 4; + const SUMMARY_ICON = 8; + const SUMMARY_DESCRIP_LINK = 16; + const SUMMARY_DESCRIP_NOLINK = 32; + const SUMMARY_DOWNLOAD = 64; + const SUMMARY_DOWNLOAD_ZIP = 128; + const SUMMARY_IMAGE_SAVE = 256; + const SUMMARY_STRIP_LINK = 512; + const SUMMARY_DOWNLOAD_ALL = 1024; + /** * The IMAP index of the message. * @@ -349,10 +362,9 @@ class IMP_Contents /** * Get message summary info. * - * @param array $options Additional options: + * @param integer $mask A mask of information to return: *
-     * 'show_links' - (boolean)
-     * 'strip' - (boolean)
+     * IMP_Contents::SUMMARY_ICON
      * 
* * @return array The following fields: @@ -362,160 +374,165 @@ class IMP_Contents * 'type' - (string) The MIME type. * */ - public function getSummary($options = array()) + public function getSummary($mask = 0) { - $msg = array( + $last_id = null; + $info = array( 'download_all' => array(), - 'has_download_link' => false, - 'has_img_save' => false, - 'has_strip' => false, - 'has_zip' => false, + 'has' => array(), + 'render' => array() ); - $ret = array(); - $slinks = !empty($options['show_links']); + $parts = array(); + + // TODO: build message (any embedded message added to structure) + $mime_message = $this->_message; + + // Cache some settings before we enter the loop. + $download_zip = (($mask & self::SUMMARY_DOWNLOAD_ZIP) && Util::extensionExists('zlib')); + if ($download_zip) { + $zip_img = Horde::img('compressed.png', _("Download in .zip Format"), null, $GLOBALS['registry']->getImageDir('horde') . '/mime'); + } - foreach ($this->_message->contentTypeMap() as $mime_id => $mime_type) { - if ($slinks && - in_array($mime_type, array('application/octet-stream', 'application/base64'))) { + if (($mask && self::SUMMARY_IMAGE_SAVE) && + $GLOBALS['registry']->hasMethod('images/selectGalleries') && + ($image_app = $GLOBALS['registry']->hasMethod('images/saveImage'))) { + $image_img = '' . _('; + } else { + $image_img = null; + } + + if ($mask && self::SUMMARY_STRIP_LINK) { + $message_token = IMP::getRequestToken('imp.impcontents'); + } + + foreach ($mime_message->contentTypeMap() as $mime_id => $mime_type) { + $parts[$mime_id] = array( + 'bytes' => null, + 'download' => null, + 'download_zip' => null, + 'id' => $mime_id, + 'img_save' => null, + 'render_info' => false, + 'render_inline' => false, + 'size' => null, + 'strip' => null + ); + $part = &$parts[$mime_id]; + + $mime_part = $this->getMIMEPart($mime_id, array('nocontents' => true, 'nodecode' => true)); + + /* If this is an attachment that has no specific MIME type info, + * see if we can guess a rendering type. */ + $param_array = array(); + if (in_array($mime_type, array('application/octet-stream', 'application/base64'))) { $mime_type = Horde_Mime_Magic::filenameToMIME($mime_part->getName()); $param_array['ctype'] = $mime_type; - } else { - $param_array = array(); + } + $part['type'] = $mime_type; + + /* Determine if part can be viewed inline or has viewable info. */ + if (($mask & self::SUMMARY_RENDER) && + (is_null($last_id) || + (($last_id !== 0) && + (strpos($mime_id, $last_id) !== 0)))) { + $last_id = null; + $viewer = Horde_Mime_Viewer::factory($mime_type); + + if ($viewer->canDisplayInline() && + ($mime_part->getDisposition() == 'inline')) { + $part['render_inline'] = true; + $info['render'][$mime_id] = 'inline'; + $last_id = $mime_id; + } elseif (is_null($last_id) && $viewer->canDisplayInfo()) { + // TODO - Need way to show info while allowing display of + // subparts. + $part['render_info'] = true; + $info['render'][$mime_id] = 'info'; + $last_id = $mime_id; + } } - $mime_part = $this->getMIMEPart($mime_id, array('nocontents' => true, 'nodecode' => true)); + /* Get bytes/size information. */ + if (($mask & self::SUMMARY_BYTES) || + $download_zip || + ($mask & self::SUMMARY_SIZE)) { + $part['bytes'] = $mime_part->getBytes(); + + if ($part['bytes'] && + ($mime_part->getCurrentEncoding() == 'base64')) { + /* From RFC 2045 [6.8]: "...the encoded data are + * consistently only about 33 percent larger than the + * unencoded data." Thus, adding 33% to the byte size is + * a good estimate for our purposes. */ + $size = number_format(max((($part['bytes'] * 0.75) / 1024), 1)); + } else { + $size = $mime_part->getSize(true); + } + $part['size'] = ($size > 1024) + ? sprintf(_("%s MB"), number_format(max(($size / 1024), 1))) + : sprintf(_("%s KB"), $size); + } - $bytes = $mime_part->getBytes(); - $icon = Horde::img(Horde_Mime_Viewer::getIcon($mime_type), '', array('title' => $mime_type)); + /* Get part's icon. */ + $part['icon'] = ($mask & self::SUMMARY_ICON) ? Horde::img(Horde_Mime_Viewer::getIcon($mime_type), '', array('title' => $mime_type)) : null; + /* Get part's description. */ $description = $mime_part->getDescription(true); if (empty($description)) { $description = _("unnamed"); } - if ($slinks) { - $descrip = $this->linkViewJS($mime_part, 'view_attach', htmlspecialchars($description), array('jstext' => sprintf(_("View %s [%s]"), $description, $mime_type), 'params' => $param_array)); - } else { - $descrip = htmlspecialchars($description); + if ($mask & self::SUMMARY_DESCRIP_LINK) { + $part['description'] = $this->linkViewJS($mime_part, 'view_attach', htmlspecialchars($description), array('jstext' => sprintf(_("View %s [%s]"), $description, $mime_type), 'params' => $param_array)); + } elseif ($mask & self::SUMMARY_DESCRIP_NOLINK) { + $part['description'] = htmlspecialchars($description); } - if (!empty($bytes) && - ($mime_part->getCurrentEncoding() == 'base64')) { - /* From RFC 2045 [6.8]: "...the encoded data are consistently - * only about 33 percent larger than the unencoded data." */ - $size = number_format(max((($bytes * 0.75) / 1024), 1)); - } else { - $size = $mime_part->getSize(true); - } - $size = ($size > 1024) - ? sprintf(_("%s MB"), number_format(max(($size / 1024), 1))) - : sprintf(_("%s KB"), $size); - /* Download column. */ - if ($slinks && $bytes) { - $download_link = $this->linkView($mime_part, 'download_attach', '', array('class' => 'download', 'dload' => true, 'jstext' => sprintf(_("Download %s"), $description))); - $msg['has_download_link'] = true; - } else { - $download_link = null; + if (($mask & self::SUMMARY_DOWNLOAD) && + (is_null($part['bytes']) || $part['bytes'])) { + $part['download'] = $this->linkView($mime_part, 'download_attach', '', array('class' => 'download', 'dload' => true, 'jstext' => sprintf(_("Download %s"), $description))); + $info['has']['download'] = true; } /* Display the compressed download link only if size is greater * than 200 KB. */ - if ($slinks && - ($mime_part->getBytes() > 204800) && - Util::extensionExists('zlib') && + if ($download_zip && + ($part['bytes'] > 204800) && !in_array($mime_type, array('application/zip', 'application/x-zip-compressed'))) { - $zip = $this->linkView($mime_part, 'download_attach', Horde::img('compressed.png', _("Download in .zip Format"), null, $GLOBALS['registry']->getImageDir('horde') . '/mime'), array('dload' => true, 'jstext' => sprintf(_("Download %s in .zip Format"), $mime_part->getDescription(true)), 'params' => array('zip' => 1))); - $msg['has_zip'] = true; - } else { - $zip = null; + $part['download_zip'] = $this->linkView($mime_part, 'download_attach', $zip_img, array('dload' => true, 'jstext' => sprintf(_("Download %s in .zip Format"), $mime_part->getDescription(true)), 'params' => array('zip' => 1))); + $info['has']['download_zip'] = true; } /* Display the image save link if the required registry calls are * present. */ - if ($slinks && - ($mime_part->getPrimaryType() == 'image') && - $GLOBALS['registry']->hasMethod('images/selectGalleries') && - ($image_app = $GLOBALS['registry']->hasMethod('images/saveImage'))) { - if (!$msg['has_img_save']) { + if (!is_null($image_img) && + ($mime_part->getPrimaryType() == 'image')) { + if (empty($info['has']['img_save'])) { Horde::addScriptFile('prototype.js', 'horde', true); Horde::addScriptFile('popup.js', 'imp', true); - $msg['has_img_save'] = true; + $info['has']['img_save'] = true; } - $img_save = Horde::link('#', _("Save Image in Gallery"), null, null, IMP::popupIMPString('saveimage.php', array('index' => ($this->_index . IMP::IDX_SEP . $this->_mailbox), 'id' => $mime_id), 450, 200) . "return false;") . '' . _('; - } else { - $img_save = null; + $part['img_save'] = Horde::link('#', _("Save Image in Gallery"), null, null, IMP::popupIMPString('saveimage.php', array('index' => ($this->_index . IMP::IDX_SEP . $this->_mailbox), 'id' => $mime_id), 450, 200) . "return false;") . $image_img . ''; } /* Strip the Attachment? */ - if ($slinks && !empty($options['strip'])) { - // TODO: No stripping of RFC822 part. + if ($mask && self::SUMMARY_STRIP_LINK) { + // TODO: No stripping of RFC 822 parts. $url = Util::removeParameter(Horde::selfUrl(true), array('actionID', 'imapid', 'index')); - $url = Util::addParameter($url, array('actionID' => 'strip_attachment', 'imapid' => $mime_id, 'index' => $this->_index, 'message_token' => $options['message_token'])); - $strip = Horde::link($url, _("Strip Attachment"), null, null, "return window.confirm('" . addslashes(_("Are you sure you wish to PERMANENTLY delete this attachment?")) . "');") . Horde::img('delete.png', _("Strip Attachment"), null, $GLOBALS['registry']->getImageDir('horde')) . ''; - $msg['has_strip'] = true; - } else { - $strip = null; - } - - if ($download = $this->isDownloadable($mime_part)) { - $msg['download_all'][] = $mime_id; - } - - $ret[$mime_id] = array( - 'description' => $descrip, - 'download' => $download, - 'download_link' => $download_link, - 'icon' => $icon, - 'id' => $mime_id, - 'img_save' => $img_save, - 'size' => $size, - 'strip' => $strip, - 'type' => $mime_type, - 'zip' => $zip - ); - } - - return array('message' => $msg, 'parts' => $ret); - } - - /** - * Get the viewable inline parts. - * - * @return array TODO - */ - public function getInlineParts() - { - $ret = array(); - $last_id = null; - - foreach ($this->_message->contentTypeMap() as $mime_id => $mime_type) { - if (!is_null($last_id) && - (($last_id === 0) || - (strpos($mime_id, $last_id) === 0))) { - continue; + $url = Util::addParameter($url, array('actionID' => 'strip_attachment', 'imapid' => $mime_id, 'index' => $this->_index, 'message_token' => $message_token)); + $part['strip'] = Horde::link($url, _("Strip Attachment"), null, null, "return window.confirm('" . addslashes(_("Are you sure you wish to PERMANENTLY delete this attachment?")) . "');") . Horde::img('delete.png', _("Strip Attachment"), null, $GLOBALS['registry']->getImageDir('horde')) . ''; + $info['has']['strip'] = true; } - $last_id = null; - $viewer = Horde_Mime_Viewer::factory($mime_type); - - if ($viewer->canDisplayInline()) { - $mime_part = $this->getMIMEPart($mime_id, array('nocontents' => true, 'nodecode' => true)); - if ($mime_part->getDisposition() == 'inline') { - $res = $this->renderMIMEPart($mime_id, array('format' => 'inline')); - $ret[$mime_id] = $res['data']; - $last_id = $mime_id; + if ($mask && self::SUMMARY_DOWNLOAD_ALL) { + if ($download = $this->isDownloadable($mime_part)) { + $info['download_all'][] = $mime_id; } } - - if (is_null($last_id) && $viewer->canDisplayInfo()) { - $res = $this->renderMIMEPart($mime_id, array('format' => 'info')); - $ret[$mime_id] = $res['data']; - $last_id = $mime_id; - } } - return $ret; + return array('info' => $info, 'parts' => $parts); } /** diff --git a/imp/lib/MIME/Viewer/alternative.php b/imp/lib/MIME/Viewer/alternative.php deleted file mode 100644 index 83844e809..000000000 --- a/imp/lib/MIME/Viewer/alternative.php +++ /dev/null @@ -1,128 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_alternative extends Horde_Mime_Viewer_Driver -{ - /** - * The content-type of the preferred part. - * Default: application/octet-stream - * - * @var string - */ - protected $_contentType = 'application/octet-stream'; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - $contents = &$params[0]; - - $display_id = null; - $summaryList = array(); - $text = ''; - - /* Look for a displayable part. - * RFC 2046: We show the LAST choice that can be displayed inline. */ - $partList = $this->mime_part->getParts(); - foreach ($partList as $part) { - if ($contents->canDisplayInline($part)) { - $text = $contents->renderMIMEPart($part); - $this->_contentType = $part->getType(); - $display_id = $part->getMIMEId(); - } - } - - /* Show links to alternative parts. */ - if (($text === null) || (count($partList) > 1)) { - if ($text === null) { - $text = '' . _("There are no alternative parts that can be displayed.") . ''; - } - - /* Generate the list of summaries to use. */ - foreach ($partList as $part) { - $id = $part->getMIMEId(); - if ($id && $id != $display_id) { - $summary = $contents->partSummary($part); - /* We don't want to show the MIME ID for alt parts. */ - if (!empty($summary)) { - array_splice($summary, 1, 1); - $summaryList[] = $summary; - } - } - } - - /* Make sure there is at least one summary before showing the - * alternative parts. */ - $alternative_display = $GLOBALS['prefs']->getValue('alternative_display'); - if (!empty($summaryList) && - !$this->viewAsAttachment() && - $alternative_display != 'none') { - $status_array = array(); - $status = _("Alternative parts for this section:"); - if ($contents->showSummaryLinks()) { - require_once 'Horde/Help.php'; - $status .= '  ' . Help::link('imp', 'alternative-msg'); - } - $status_array[] = $status; - $status = ''; - foreach ($summaryList as $summary) { - $status .= ''; - foreach ($summary as $val) { - if (!empty($val)) { - $status .= "\n"; - } - } - $status .= "\n"; - } - $status .= '
$val 
'; - $status_array[] = $status; - $status_msg = $this->formatStatusMsg($status_array, Horde::img('mime/binary.png', _("Multipart/alternative"), null, $GLOBALS['registry']->getImageDir('horde')), false); - switch ($alternative_display) { - case 'above': - $text = $status_msg . $text; - break; - - case 'below': - $text .= $status_msg; - break; - } - } - } - - /* Remove attachment information for the displayed part if we - * can. */ - if (is_callable(array($contents, 'removeAtcEntry'))) { - $contents->removeAtcEntry($this->mime_part->getMIMEId()); - } - - return $text; - } - - /** - * Return the content-type. - * - * @return string The content-type of the message. - * Returns 'application/octet-stream' until actual - * content type of the message can be determined. - */ - public function getType() - { - return $this->_contentType; - } -} diff --git a/imp/lib/MIME/Viewer/appledouble.php b/imp/lib/MIME/Viewer/appledouble.php deleted file mode 100644 index 7b89bb65a..000000000 --- a/imp/lib/MIME/Viewer/appledouble.php +++ /dev/null @@ -1,73 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_appledouble extends Horde_Mime_Viewer_Driver -{ - /** - * Force viewing of a part inline, regardless of the Content-Disposition - * of the MIME Part. - * - * @var boolean - */ - protected $_forceinline = true; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - $contents = &$params[0]; - $text = ''; - - /* RFC 1740 [4]: There are two parts to an appledouble message: - (1) application/applefile - (2) Data embedded in the Mac file */ - - /* Display the macintosh download link. */ - $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(1)); - if ($part) { - $status = array( - _("This message contains a Macintosh file."), - sprintf(_("The Macintosh resource fork can be downloaded %s."), $contents->linkViewJS($part, 'download_attach', _("HERE"), _("The Macintosh resource fork"))), - _("The contents of the Macintosh file are below.") - ); - $text .= $this->formatStatusMsg($status, Horde::img('apple.png', _("Macintosh File")), false); - } - - /* Display the content of the file. */ - $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(2)); - if ($part) { - $mime_message = Horde_Mime_Message::convertMIMEPart($part); - $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$contents)); - $mc->buildMessage(); - $text .= '' . $mc->getMessage(true) . '
'; - } - - return $text; - } - - /** - * Return the content-type. - * - * @return string The content-type of the message. - */ - public function getType() - { - return 'text/html; charset=' . NLS::getCharset(); - } -} diff --git a/imp/lib/MIME/Viewer/enriched.php b/imp/lib/MIME/Viewer/enriched.php deleted file mode 100644 index d3e4cc1b9..000000000 --- a/imp/lib/MIME/Viewer/enriched.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_enriched extends Horde_Mime_Viewer_enriched -{ - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - $contents = &$params[0]; - - global $prefs; - - if (($text = $this->mime_part->getContents()) === false) { - return $this->formatPartError(_("There was an error displaying this message part")); - } - - if (trim($text) == '') { - return $text; - } - - $text = parent::render(); - - // Highlight quoted parts of an email. - if ($prefs->getValue('highlight_text')) { - $text = implode("\n", preg_replace('|^(\s*>.+)$|', '\1', explode("\n", $text))); - $indent = 1; - while (preg_match('|>(\s?>){' . $indent . '}|', $text)) { - $text = implode("\n", preg_replace('|^(\s*>(\s?>){' . $indent . '}.+)$|', '\1', explode("\n", $text))); - $indent++; - } - } - - // Dim signatures. - if ($prefs->getValue('dim_signature')) { - $parts = preg_split('|(\n--\s*\n)|', $text, 2, PREG_SPLIT_DELIM_CAPTURE); - $text = array_shift($parts); - if (count($parts)) { - $text .= '' . $parts[0] . - preg_replace('|class="[^"]+"|', 'class="signature-fixed"', $parts[1]) . - ''; - } - } - - // Filter bad language. - $text = IMP::filterText($text); - - return $text; - } -} diff --git a/imp/lib/MIME/Viewer/html.php b/imp/lib/MIME/Viewer/html.php deleted file mode 100644 index b98565cd4..000000000 --- a/imp/lib/MIME/Viewer/html.php +++ /dev/null @@ -1,275 +0,0 @@ - - * @author Jon Parise - * @author Michael Slusarz - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_html extends Horde_Mime_Viewer_html -{ - /** - * Cached block image. - * - * @var string - */ - protected $_blockimg = null; - - /** - * The regular expression to catch any tags and attributes that load - * external images. - * - * @var string - */ - protected $_img_regex = '/ - # match 1 - ( - # tags - ]+src= - # tags - |]*src= - # "background" attributes - |]*background=|]*background=|]*background= - # "style" attributes; match 2; quotes: match 3 - |(style=\s*("|\')?[^>]*background(?:-image)?:(?(3)[^"\']|[^>])*?url\s*\() - ) - # whitespace - \s* - # opening quotes, parenthesis; match 4 - ("|\')? - # the image url; match 5 - ((?(2) - # matched a "style" attribute - (?(4)[^"\')>]*|[^\s)>]*) - # did not match a "style" attribute - |(?(4)[^"\'>]*|[^\s>]*) - )) - # closing quotes - (?(4)\\4) - # matched a "style" attribute? - (?(2) - # closing parenthesis - \s*\) - # remainder of the "style" attribute; match 5 - ((?(3)[^"\'>]*|[^\s>]*)) - ) - /isx'; - - /** - * TODO - */ - protected function _render() - { - $render = $this->_IMPrender(false); - - return array( - 'data' => $render['html'], - 'status' => $render['status'], - 'type' => $this->_mimepart->getType(true) - ); - } - - /** - * TODO - */ - protected function _renderInline() - { - $render = $this->_IMPrender(true); - - return array( - 'data' => $render['html'], - 'status' => $render['status'] - ); - } - - /** - * Render out the currently set contents. - * - * @param boolean $inline Are we viewing inline? - * - * @return array Two elements: html and status. - */ - protected function _IMPrender($inline) - { - $data = $this->_mimepart->getContents(); - $charset = NLS::getCharset(); - $msg_charset = $this->_mimepart->getCharset(); - - if ($inline) { - $data = String::convertCharset($data, $msg_charset); - $msg_charset = $charset; - } - - /* Run tidy on the HTML. */ - if ($this->getConfigParam('tidy') && - ($tidy_config = IMP::getTidyConfig(String::length($data)))) { - if ($msg_charset == 'us-ascii') { - $tidy = tidy_parse_string($data, $tidy_config, 'ascii'); - $tidy->cleanRepair(); - $data = tidy_get_output($tidy); - } else { - $tidy = tidy_parse_string(String::convertCharset($data, $msg_charset, 'UTF-8'), $tidy_config, 'utf8'); - $tidy->cleanRepair(); - $data = String::convertCharset(tidy_get_output($tidy), 'UTF-8', $msg_charset); - } - } - - /* Sanitize the HTML. */ - $cleanhtml = $this->_cleanHTML($data, $inline); - $data = $cleanhtml['html']; - - /* Reset absolutely positioned elements. */ - if ($inline) { - $data = preg_replace('/(style\s*=\s*)(["\'])?([^>"\']*)position\s*:\s*absolute([^>"\']*)\2/i', '$1"$3$4"', $data); - } - - /* Search for inlined images that we can display. */ - // TODO - $related = $this->_mimepart->getInformation('related_part'); - if ($related !== false) { - $relatedPart = $this->_params['contents']->getMIMEPart($related); - foreach ($relatedPart->getCIDList() as $ref => $id) { - $id = trim($id, '<>'); - $cid_part = $this->_params['contents']->getDecodedMIMEPart($ref); - $data = str_replace("cid:$id", $this->_params['contents']->urlView($cid_part, 'view_attach'), $data); - } - } - - /* Convert links to open in new windows. First we hide all - * mailto: links, links that have an "#xyz" anchor and ignore - * all links that already have a target. */ - $data = preg_replace( - array('/]*\s*href=["\']?(#|mailto:))/i', - '/]*)\s*target=["\']?[^>"\'\s]*["\']?/i', - '/]*\s*href=["\']?(#|mailto:))/i', - '/]*)\s*target=["\']?[^>"\'\s]*["\']?/i', - '/hasMethod('mail/compose')) { - $data = preg_replace_callback('/href\s*=\s*(["\'])?mailto:((?(1)[^\1]*?|[^\s>]+))(?(1)\1|)/i', array($this, '_mailtoCallback'), $data); - } - - /* Filter bad language. */ - $data = IMP::filterText($data); - - if ($inline) { - /* Put div around message. */ - $data = '
' . $data . '
'; - } - - /* Only display images if specifically allowed by user. */ - $msg = $script = ''; - if (!IMP::printMode() && - $GLOBALS['prefs']->getValue('html_image_replacement') && - preg_match($this->_img_regex, $data)) { - /* Make sure the URL parameters are correct for the current - * message. */ - $url = Util::removeParameter(Horde::selfUrl(true), array('index')); - if ($inline) { - $url = Util::removeParameter($url, array('actionID')); - } - $url = Util::addParameter($url, 'index', $this->_params['contents']->getMessageIndex()); - - $view_img = Util::getFormData('view_html_images'); - $addr_check = ($GLOBALS['prefs']->getValue('html_image_addrbook') && $this->_inAddressBook()); - - if (!$view_img && !$addr_check) { - $script = Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true) . - Util::bufferOutput(array('Horde', 'addScriptFile'), 'unblockImages.js', 'imp', true); - - $url = Util::addParameter($url, 'view_html_images', 1); - $attributes = $inline ? array() : array('style' => 'color:blue'); - $msg = Horde::img('mime/image.png') . ' ' . String::convertCharset(_("Images have been blocked to protect your privacy."), $charset, $msg_charset) . ' ' . Horde::link($url, '', '', '', 'return IMP.unblockImages(' . (!$inline ? 'document.body' : '$(\'html-message\')') . ', \'block-images\');', '', '', $attributes) . String::convertCharset(_("Show Images?"), $charset, $msg_charset) . ''; - $data = preg_replace_callback($this->_img_regex, array($this, '_blockImages'), $data); - if (!$inline) { - $msg = '' . nl2br($msg) . '
'; - } - $msg = '' . $msg . ''; - } - } - - /* If we are viewing inline, give option to view in separate window. */ - if ($inline && $this->getConfigParam('external')) { - $cleanhtml['status'][] = array( - 'data' => $this->_params['contents']->linkViewJS($this->mime_part, 'view_attach', _("Show this HTML in a new window?")), - 'type' => 'info' - ); - } - - return array( - 'html' => $data, - 'status' => $cleanhtml['status'] - ); - } - - /** - * TODO - */ - protected function _mailtoCallback($m) - { - return 'href="' . $GLOBALS['registry']->call('mail/compose', array(String::convertCharset(html_entity_decode($m[2]), 'ISO-8859-1', NLS::getCharset()))) . '"'; - } - - /** - * Called from the image-blocking regexp to construct the new image tags. - * - * @param array $matches - * - * @return string The new image tag. - */ - protected function _blockImages($matches) - { - if (is_null($this->_blockimg)) { - $this->_blockimg = Horde::url($GLOBALS['registry']->getImageDir('imp') . '/spacer_red.png', false, -1); - } - - return empty($matches[2]) - ? $matches[1] . '"' . $this->_blockimg . '" blocked="' . rawurlencode(str_replace('&', '&', trim($matches[5], '\'" '))) . '"' - : $matches[1] . "'" . $this->_blockimg . '\')' . $matches[6] . '" blocked="' . rawurlencode(str_replace('&', '&', trim($matches[5], '\'" '))); - } - - /** - * Determine whether the sender appears in an available addressbook. - * - * @return boolean Does the sender appear in an addressbook? - */ - protected function _inAddressBook() - { - /* If we don't have a contacts provider available, give up. */ - if (!$GLOBALS['registry']->hasMethod('contacts/getField')) { - return false; - } - - $params = IMP_Compose::getAddressSearchParams(); - - // TODO - // get mime_message. - - /* Try to get back a result from the search. */ - $result = $GLOBALS['registry']->call('contacts/getField', array($base_ob->getFromAddress(), '__key', $params['sources'], false, true)); - - return is_a($result, 'PEAR_Error') - ? false - : (count($result) > 0); - } -} diff --git a/imp/lib/MIME/Viewer/images.php b/imp/lib/MIME/Viewer/images.php deleted file mode 100644 index a8c787204..000000000 --- a/imp/lib/MIME/Viewer/images.php +++ /dev/null @@ -1,265 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_images extends Horde_Mime_Viewer_images -{ - /** - * The content-type of the generated data. - * - * @var string - */ - protected $_contentType; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered information. - */ - public function render($params) - { - $contents = $params[0]; - - global $browser; - - /* If calling page is asking us to output data, do that without any - * further delay and exit. */ - if (Util::getFormData('img_data')) { - return parent::render(); - } - - /* Convert the image to browser-viewable format and display. */ - if (Util::getFormData('images_view_convert')) { - return $this->_viewConvert(); - } - - /* Create the thumbnail and display. */ - if (Util::getFormData('images_view_thumbnail')) { - return $this->_viewConvert(true); - } - - if (Util::getFormData('images_load_convert')) { - return $this->_popupImageWindow(); - } - - if ($this->viewAsAttachment()) { - if (!$browser->hasFeature('javascript')) { - /* If the browser doesn't support javascript then simply - render the image data. */ - return parent::render(); - } elseif ($browser->isViewable(parent::getType())) { - /* The browser can display the image type directly - just - output the javascript code to render the auto resize popup - image window. */ - return $this->_popupImageWindow(); - } - } - - if ($browser->isViewable($this->mime_part->getType())) { - /* If we are viewing inline, and the browser can handle the image - type directly, output an tag to load the image. */ - $alt = $this->mime_part->getName(false, true); - return Horde::img($contents->urlView($this->mime_part, 'view_attach'), $alt, null, ''); - } else { - /* If we have made it this far, than the browser cannot view this - image inline. Inform the user of this and, possibly, ask user - if we should convert to another image type. */ - $msg = _("Your browser does not support inline display of this image type."); - - if ($this->viewAsAttachment()) { - $msg .= '
' . sprintf(_("Click %s to download the image."), $contents->linkView($this->mime_part, 'download_attach', _("HERE"), array('viewparams' => array('img_data' => 1)), true)); - } - - /* See if we can convert to an inline browser viewable form. */ - $img = $this->_getHordeImageOb(false); - if ($img && $browser->isViewable($img->getContentType())) { - if ($this->viewAsAttachment()) { - $convert_link = Horde::link($contents->urlView($this->mime_part, 'view_attach', array('images_load_convert' => 1))) . _("HERE") . ''; - } else { - $convert_link = $contents->linkViewJS($this->mime_part, 'view_attach', _("HERE"), null, null, array('images_load_convert' => 1)); - } - $msg .= '
' . sprintf(_("Click %s to convert the image file into a format your browser can view."), $convert_link); - } - - $this->_contentType = 'text/html; charset=' . NLS::getCharset(); - return $this->formatStatusMsg($msg); - } - } - - /** - * Return the content-type - * - * @return string The content-type of the output. - */ - public function getType() - { - return ($this->_contentType) ? $this->_contentType : parent::getType(); - } - - /** - * Render out attachment information. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function renderAttachmentInfo($params) - { - $contents = &$params[0]; - - /* Display the thumbnail link only if we show thumbs for all images or - if image is over 50 KB. */ - if (!$this->getConfigParam('allthumbs') && - ($this->mime_part->getBytes() < 51200)) { - return ''; - } - - if (is_a($contents, 'IMP_Contents')) { - $this->mime_part = &$contents->getDecodedMIMEPart($this->mime_part->getMIMEId(), true); - } - - /* Check to see if convert utility is available. */ - if (!$this->_getHordeImageOb(false)) { - return ''; - } - - $status = array( - sprintf(_("An image named %s is attached to this message. A thumbnail is below."), - $this->mime_part->getName(true)), - ); - - if (!$GLOBALS['browser']->hasFeature('javascript')) { - $status[] = Horde::link($contents->urlView($this->mime_part, - 'view_attach')) . - Horde::img($contents->urlView($this->mime_part, - 'view_attach', array('images_view_thumbnail' => 1), false), - _("View Attachment"), null, '') . ''; - } else { - $status[] = $contents->linkViewJS($this->mime_part, 'view_attach', - Horde::img($contents->urlView($this->mime_part, - 'view_attach', array('images_view_thumbnail' => 1), - false), _("View Attachment"), null, ''), null, null, - null); - } - - return $this->formatStatusMsg($status, Horde::img('mime/image.png', - _("Thumbnail of attached image"), null, $GLOBALS['registry']->getImageDir('horde')), false); - } - - /** - * Generate the HTML output for the JS auto-resize view window. - * - * @return string The HTML output. - */ - protected function _popupImageWindow() - { - $params = $remove_params = array(); - if (Util::getFormData('images_load_convert')) { - $params['images_view_convert'] = 1; - $remove_params[] = 'images_load_convert'; - } else { - $params['img_data'] = 1; - } - $self_url = Util::addParameter(Util::removeParameter(Horde::selfUrl(true), $remove_params), $params); - $title = MIME::decode($this->mime_part->getName(false, true)); - $this->_contentType = 'text/html; charset=' . NLS::getCharset(); - return parent::_popupImageWindow($self_url, $title); - } - - /** - * View thumbnail sized image. - * - * @param boolean $thumb View thumbnail size? - * - * @return string The image data. - */ - protected function _viewConvert($thumb = false) - { - $mime = $this->mime_part; - $img = $this->_getHordeImageOb(); - - if ($img) { - if ($thumb) { - $img->resize(96, 96, true); - } - $type = $img->getContentType(); - $data = $img->raw(true); - } - - if (!$img || !$data) { - $type = 'image/png'; - $data = file_get_contents(IMP_BASE . '/themes/graphics/mini-error.png'); - } - - $mime->setType($type); - $this->_contentType = $type; - $mime->setContents($data); - - return $mime->getContents(); - } - - /** - * Return a Horde_Image object. - * - * @param boolean $load Whether to load the image data. - * - * @return Horde_Image The requested object. - */ - protected function _getHordeImageOb($load = true) - { - include_once 'Horde/Image.php'; - $params = array('temp' => Horde::getTempdir()); - if (!empty($GLOBALS['conf']['image']['convert'])) { - $img = &Horde_Image::singleton('im', $params); - } elseif (Util::extensionExists('gd')) { - $img = &Horde_Image::singleton('gd', $params); - } else { - return false; - } - - if (is_a($img, 'PEAR_Error')) { - return false; - } - - if ($load) { - $ret = $img->loadString(1, $this->mime_part->getContents()); - if (is_a($ret, 'PEAR_Error')) { - return false; - } - } - - return $img; - } - - /** - * Can this driver render the the data inline? - * - * @return boolean True if the driver can display inline. - */ - public function canDisplayInline() - { - /* Only display the image inline if the configuration parameter is set, - the browser can actually display it, and the size of the image is - small. */ - global $browser; - if (($this->getConfigParam('inline')) && ($browser->isViewable($this->mime_part->getType())) && - ($this->mime_part->getBytes() < 51200)) { - return true; - } else { - return false; - } - } -} diff --git a/imp/lib/MIME/Viewer/itip.php b/imp/lib/MIME/Viewer/itip.php deleted file mode 100644 index 6d7ebdaaa..000000000 --- a/imp/lib/MIME/Viewer/itip.php +++ /dev/null @@ -1,978 +0,0 @@ - - * @author Mike Cochrane - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_itip extends Horde_Mime_Viewer_Driver -{ - /** - * Force viewing of a part inline, regardless of the Content-Disposition - * of the MIME Part. - * - * @var boolean - */ - protected $_forceinline = true; - - /** - * The messages to output to the user. - * - * @var array - */ - protected $_msgs = array(); - - /** - * The method as marked in either the iCal structure or message header. - * - * @var string - */ - protected $_method = 'PUBLISH'; - - /** - * The headers of the message. - * - * @var string - */ - protected $_headers; - - /** - * Render out the currently set iCalendar contents. - * - * @param array $params Any parameters the Viewer may need. - * - * @return string The rendered contents. - */ - public function render($params = array()) - { - global $registry; - - // Extract the data. - $data = $this->mime_part->getContents(); - if (empty($this->_headers) && is_a($params[0], 'IMP_Contents')) { - $this->_headers = $params[0]->getHeaderOb(); - } - - // Parse the iCal file. - $vCal = new Horde_iCalendar(); - if (!$vCal->parsevCalendar($data, 'VCALENDAR', $this->mime_part->getCharset())) { - return '

' . _("The calendar data is invalid") . '

' . - '
' . htmlspecialchars($data) . '
'; - } - - // Check if we got vcard data with the wrong vcalendar mime type. - $c = $vCal->getComponentClasses(); - if (count($c) == 1 && !empty($c['horde_icalendar_vcard'])) { - $vcard_renderer = &Horde_Mime_Viewer::factory($this->mime_part, 'text/x-vcard'); - return $vcard_renderer->render($params); - } - - // Get the method type. - $this->_method = $vCal->getAttribute('METHOD'); - if (is_a($this->_method, 'PEAR_Error')) { - $this->_method = ''; - } - - // Get the iCalendar file components. - $components = $vCal->getComponents(); - - // Handle the action requests. - $actions = Util::getFormData('action', array()); - foreach ($actions as $key => $action) { - $this->_msgs[$key] = array(); - switch ($action) { - case 'delete': - // vEvent cancellation. - if ($registry->hasMethod('calendar/delete')) { - $guid = $components[$key]->getAttribute('UID'); - $event = $registry->call('calendar/delete', array('guid' => $guid)); - if (is_a($event, 'PEAR_Error')) { - $this->_msgs[$key][] = array('error', _("There was an error deleting the event:") . ' ' . $event->getMessage()); - } else { - $this->_msgs[$key][] = array('success', _("Event successfully deleted.")); - } - } else { - $this->_msgs[$key][] = array('warning', _("This action is not supported.")); - } - break; - - case 'update': - // vEvent reply. - if ($registry->hasMethod('calendar/updateAttendee')) { - $event = $registry->call('calendar/updateAttendee', array('response' => $components[$key], 'sender' => $params[0]->getFromAddress())); - if (is_a($event, 'PEAR_Error')) { - $this->_msgs[$key][] = array('error', _("There was an error updating the event:") . ' ' . $event->getMessage()); - } else { - $this->_msgs[$key][] = array('success', _("Respondent Status Updated.")); - } - } else { - $this->_msgs[$key][] = array('warning', _("This action is not supported.")); - } - break; - - case 'import': - case 'accept-import': - // vFreebusy reply. - // vFreebusy publish. - // vEvent request. - // vEvent publish. - // vTodo publish. - // vJournal publish. - switch ($components[$key]->getType()) { - case 'vEvent': - $handled = false; - $guid = $components[$key]->getAttribute('UID'); - // Check if this is an update. - if ($registry->hasMethod('calendar/export') && - !is_a($registry->call('calendar/export', array($guid, 'text/calendar')), 'PEAR_Error')) { - // Update in Kronolith. - if ($registry->hasMethod('calendar/replace')) { - $handled = true; - $result = $registry->call('calendar/replace', array('uid' => $guid, 'content' => $components[$key], 'contentType' => $this->mime_part->getType())); - if (is_a($result, 'PEAR_Error')) { - $this->_msgs[$key][] = array('error', _("There was an error updating the event:") . ' ' . $result->getMessage()); - } else { - $url = Horde::url($registry->link('calendar/show', array('uid' => $guid))); - $this->_msgs[$key][] = array('success', _("The event was updated in your calendar.") . - ' ' . Horde::link($url, _("View event"), null, '_blank') . Horde::img('mime/icalendar.png', _("View event"), null, $registry->getImageDir('horde')) . ''); - } - } - } else { - // Import into Kronolith. - if ($registry->hasMethod('calendar/import')) { - $handled = true; - $guid = $registry->call('calendar/import', array('content' => $components[$key], 'contentType' => $this->mime_part->getType())); - if (is_a($guid, 'PEAR_Error')) { - $this->_msgs[$key][] = array('error', _("There was an error importing the event:") . ' ' . $guid->getMessage()); - } else { - $url = Horde::url($registry->link('calendar/show', array('uid' => $guid))); - $this->_msgs[$key][] = array('success', _("The event was added to your calendar.") . - ' ' . Horde::link($url, _("View event"), null, '_blank') . Horde::img('mime/icalendar.png', _("View event"), null, $registry->getImageDir('horde')) . ''); - } - } - } - if (!$handled) { - $this->_msgs[$key][] = array('warning', _("This action is not supported.")); - } - break; - - case 'vFreebusy': - // Import into Kronolith. - if ($registry->hasMethod('calendar/import_vfreebusy')) { - $res = $registry->call('calendar/import_vfreebusy', array($components[$key])); - if (is_a($res, 'PEAR_Error')) { - $this->_msgs[$key][] = array('error', _("There was an error importing user's free/busy information:") . ' ' . $res->getMessage()); - } else { - $this->_msgs[$key][] = array('success', _("The user's free/busy information was sucessfully stored.")); - } - } else { - $this->_msgs[$key][] = array('warning', _("This action is not supported.")); - } - break; - - case 'vTodo': - // Import into Nag. - if ($registry->hasMethod('tasks/import')) { - $guid = $registry->call('tasks/import', array($components[$key], $this->mime_part->getType())); - if (is_a($guid, 'PEAR_Error')) { - $this->_msgs[$key][] = array('error', _("There was an error importing the task:") . ' ' . $guid->getMessage()); - } else { - $url = Horde::url($registry->link('tasks/show', array('uid' => $guid))); - $this->_msgs[$key][] = array('success', _("The task has been added to your tasklist.") . - ' ' . Horde::link($url, _("View task"), null, '_blank') . Horde::img('mime/icalendar.png', _("View task"), null, $registry->getImageDir('horde')) . ''); - } - } else { - $this->_msgs[$key][] = array('warning', _("This action is not supported.")); - } - break; - - case 'vJournal': - default: - $this->_msgs[$key][] = array('warning', _("This action is not yet implemented.")); - } - - if ($action != 'accept-import') { - break; - } - - case 'accept': - case 'accept-import': - case 'deny': - case 'tentative': - // vEvent request. - if (isset($components[$key]) && - $components[$key]->getType() == 'vEvent') { - $vEvent = $components[$key]; - - // Get the organizer details. - $organizer = $vEvent->getAttribute('ORGANIZER'); - if (is_a($organizer, 'PEAR_Error')) { - break; - } - $organizer = parse_url($organizer); - $organizerEmail = $organizer['path']; - $organizer = $vEvent->getAttribute('ORGANIZER', true); - $organizerName = isset($organizer['cn']) ? $organizer['cn'] : ''; - - require_once 'Horde/Identity.php'; - - // Build the reply. - $vCal = new Horde_iCalendar(); - $vCal->setAttribute('PRODID', '-//The Horde Project//' . HORDE_AGENT_HEADER . '//EN'); - $vCal->setAttribute('METHOD', 'REPLY'); - - $vEvent_reply = &Horde_iCalendar::newComponent('vevent', $vCal); - $vEvent_reply->setAttribute('UID', $vEvent->getAttribute('UID')); - if (!is_a($vEvent->getAttribute('SUMMARY'), 'PEAR_error')) { - $vEvent_reply->setAttribute('SUMMARY', $vEvent->getAttribute('SUMMARY')); - } - if (!is_a($vEvent->getAttribute('DESCRIPTION'), 'PEAR_error')) { - $vEvent_reply->setAttribute('DESCRIPTION', $vEvent->getAttribute('DESCRIPTION')); - } - $dtstart = $vEvent->getAttribute('DTSTART', true); - $vEvent_reply->setAttribute('DTSTART', $vEvent->getAttribute('DTSTART'), array_pop($dtstart)); - if (!is_a($vEvent->getAttribute('DTEND'), 'PEAR_error')) { - $dtend = $vEvent->getAttribute('DTEND', true); - $vEvent_reply->setAttribute('DTEND', $vEvent->getAttribute('DTEND'), array_pop($dtend)); - } else { - $duration = $vEvent->getAttribute('DURATION', true); - $vEvent_reply->setAttribute('DURATION', $vEvent->getAttribute('DURATION'), array_pop($duration)); - } - if (!is_a($vEvent->getAttribute('SEQUENCE'), 'PEAR_error')) { - $vEvent_reply->setAttribute('SEQUENCE', $vEvent->getAttribute('SEQUENCE')); - } - $vEvent_reply->setAttribute('ORGANIZER', $vEvent->getAttribute('ORGANIZER'), array_pop($organizer)); - - // Find out who we are and update status. - $identity = &Identity::singleton(array('imp', 'imp')); - $attendees = $vEvent->getAttribute('ATTENDEE'); - if (!is_array($attendees)) { - $attendees = array($attendees); - } - foreach ($attendees as $attendee) { - $attendee = preg_replace('/mailto:/i', '', $attendee); - if (!is_null($id = $identity->getMatchingIdentity($attendee))) { - $identity->setDefault($id); - break; - } - } - $name = $email = $identity->getFromAddress(); - $params = array(); - $cn = $identity->getValue('fullname'); - if (!empty($cn)) { - $name = $params['CN'] = $cn; - } - - switch ($action) { - case 'accept': - case 'accept-import': - $message = sprintf(_("%s has accepted."), $name); - $subject = _("Accepted: ") . $vEvent->getAttribute('SUMMARY'); - $params['PARTSTAT'] = 'ACCEPTED'; - break; - - case 'deny': - $message = sprintf(_("%s has declined."), $name); - $subject = _("Declined: ") . $vEvent->getAttribute('SUMMARY'); - $params['PARTSTAT'] = 'DECLINED'; - break; - - case 'tentative': - $message = sprintf(_("%s has tentatively accepted."), $name); - $subject = _("Tentative: ") . $vEvent->getAttribute('SUMMARY'); - $params['PARTSTAT'] = 'TENTATIVE'; - break; - } - - $vEvent_reply->setAttribute('ATTENDEE', 'mailto:' . $email, $params); - $vCal->addComponent($vEvent_reply); - - $mime = new Horde_Mime_Part('multipart/alternative'); - $body = new Horde_Mime_Part('text/plain', - String::wrap($message, 76, "\n"), - NLS::getCharset()); - - $ics = new Horde_Mime_Part('text/calendar', $vCal->exportvCalendar()); - $ics->setName('event-reply.ics'); - $ics->setContentTypeParameter('METHOD', 'REPLY'); - $ics->setCharset(NLS::getCharset()); - - $mime->addPart($body); - $mime->addPart($ics); - $mime = &Horde_Mime_Message::convertMimePart($mime); - - // Build the reply headers. - $msg_headers = new Horde_Mime_Headers(); - $msg_headers->addReceivedHeader(); - $msg_headers->addMessageIdHeader(); - $msg_headers->addHeader('Date', date('r')); - $msg_headers->addHeader('From', $email); - $msg_headers->addHeader('To', $organizerEmail); - - $identity->setDefault(Util::getFormData('identity')); - $replyto = $identity->getValue('replyto_addr'); - if (!empty($replyto) && ($replyto != $email)) { - $msg_headers->addHeader('Reply-to', $replyto); - } - $msg_headers->addHeader('Subject', Horde_Mime::encode($subject, NLS::getCharset())); - - // Send the reply. - $status = $mime->send($organizerEmail, $msg_headers, - $GLOBALS['conf']['mailer']['type'], - $GLOBALS['conf']['mailer']['params']); - if (is_a($status, 'PEAR_Error')) { - $this->_msgs[$key][] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage())); - } else { - $this->_msgs[$key][] = array('success', _("Reply Sent.")); - } - } else { - $this->_msgs[$key][] = array('warning', _("This action is not supported.")); - } - break; - - case 'send': - // vEvent refresh. - if (isset($components[$key]) && - $components[$key]->getType() == 'vEvent') { - $vEvent = $components[$key]; - } - - // vTodo refresh. - case 'reply': - case 'reply2m': - // vfreebusy request. - if (isset($components[$key]) && - $components[$key]->getType() == 'vFreebusy') { - $vFb = $components[$key]; - - // Get the organizer details. - $organizer = $vFb->getAttribute('ORGANIZER'); - if (is_a($organizer, 'PEAR_Error')) { - break; - } - $organizer = parse_url($organizer); - $organizerEmail = $organizer['path']; - $organizer = $vFb->getAttribute('ORGANIZER', true); - $organizerName = isset($organizer['cn']) ? $organizer['cn'] : ''; - - if ($action == 'reply2m') { - $startStamp = time(); - $endStamp = $startStamp + (60 * 24 * 3600); - } else { - $startStamp = $vFb->getAttribute('DTSTART'); - if (is_a($startStamp, 'PEAR_Error')) { - $startStamp = time(); - } - $endStamp = $vFb->getAttribute('DTEND'); - if (is_a($endStamp, 'PEAR_Error')) { - $duration = $vFb->getAttribute('DURATION'); - if (is_a($duration, 'PEAR_Error')) { - $endStamp = $startStamp + (60 * 24 * 3600); - } else { - $endStamp = $startStamp + $duration; - } - } - } - $vfb_reply = $registry->call('calendar/getFreeBusy', - array('startStamp' => $startStamp, - 'endStamp' => $endStamp)); - require_once 'Horde/Identity.php'; - - // Find out who we are and update status. - $identity = &Identity::singleton(); - $email = $identity->getFromAddress(); - - // Build the reply. - $vCal = new Horde_iCalendar(); - $vCal->setAttribute('PRODID', '-//The Horde Project//' . HORDE_AGENT_HEADER . '//EN'); - $vCal->setAttribute('METHOD', 'REPLY'); - $vCal->addComponent($vfb_reply); - - $mime = new Horde_Mime_Message(); - $message = _("Attached is a reply to a calendar request you sent."); - $body = new Horde_Mime_Part('text/plain', - String::wrap($message, 76, "\n"), - NLS::getCharset()); - - $ics = new Horde_Mime_Part('text/calendar', $vCal->exportvCalendar()); - $ics->setName('icalendar.ics'); - $ics->setContentTypeParameter('METHOD', 'REPLY'); - $ics->setCharset(NLS::getCharset()); - - $mime->addPart($body); - $mime->addPart($ics); - - // Build the reply headers. - $msg_headers = new Horde_Mime_Headers(); - $msg_headers->addReceivedHeader(); - $msg_headers->addMessageIdHeader(); - $msg_headers->addHeader('Date', date('r')); - $msg_headers->addHeader('From', $email); - $msg_headers->addHeader('To', $organizerEmail); - - $identity->setDefault(Util::getFormData('identity')); - $replyto = $identity->getValue('replyto_addr'); - if (!empty($replyto) && ($replyto != $email)) { - $msg_headers->addHeader('Reply-to', $replyto); - } - $msg_headers->addHeader('Subject', Horde_Mime::encode(_("Free/Busy Request Response"), NLS::getCharset())); - - // Send the reply. - $status = $mime->send($organizerEmail, $msg_headers, - $GLOBALS['conf']['mailer']['type'], - $GLOBALS['conf']['mailer']['params']); - if (is_a($status, 'PEAR_Error')) { - $this->_msgs[$key][] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage())); - } else { - $this->_msgs[$key][] = array('success', _("Reply Sent.")); - } - } else { - $this->_msgs[$key][] = array('warning', _("Invalid Action selected for this component.")); - } - break; - - case 'nosup': - // vFreebusy request. - default: - $this->_msgs[$key][] = array('warning', _("This action is not yet implemented.")); - break; - } - } - - // Create the HTML to display the iCal file. - $html = ''; - if ($this->viewAsAttachment()) { - $html .= Util::bufferOutput('require', $registry->get('templates', 'horde') . '/common-header.inc'); - } - if ($_SESSION['imp']['viewmode'] == 'imp') { - $html .= '
'; - } - - foreach ($components as $key => $component) { - switch ($component->getType()) { - case 'vEvent': - $html .= $this->_vEvent($component, $key); - break; - - case 'vTodo': - $html .= $this->_vTodo($component, $key); - break; - - case 'vTimeZone': - // Ignore them. - break; - - case 'vFreebusy': - $html .= $this->_vFreebusy($component, $key); - break; - - // @todo: handle stray vcards here as well. - default: - $html .= sprintf(_("Unhandled component of type: %s"), $component->getType()); - } - } - - // Need to work out if we are inline and actually need this. - if ($_SESSION['imp']['viewmode'] == 'imp') { - $html .= '
'; - } - if ($this->viewAsAttachment()) { - $html .= Util::bufferOutput('require', $registry->get('templates', 'horde') . '/common-footer.inc'); - } - - return $html; - } - - /** - * Return text/html as the content-type. - * - * @return string "text/html" constant - */ - public function getType() - { - return 'text/html; charset=' . NLS::getCharset(); - } - - /** - * Return the html for a vFreebusy. - */ - protected function _vFreebusy($vfb, $id) - { - global $registry, $prefs; - - $html = ''; - $desc = ''; - $sender = $vfb->getName(); - switch ($this->_method) { - case 'PUBLISH': - $desc = _("%s has sent you free/busy information."); - break; - - case 'REQUEST': - $sender = $this->_headers->getValue('From'); - $desc = _("%s requests your free/busy information."); - break; - - case 'REPLY': - $desc = _("%s has replied to a free/busy request."); - break; - } - - $html .= '

' . sprintf($desc, $sender) . '

'; - - if ($this->_msgs) { - foreach ($this->_msgs[$id] as $msg) { - $html .= '

' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '

'; - } - } - - $start = $vfb->getAttribute('DTSTART'); - if (!is_a($start, 'PEAR_Error')) { - if (is_array($start)) { - $html .= '

' . _("Start") . ': ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '

'; - } else { - $html .= '

' . _("Start") . ': ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '

'; - } - } - - $end = $vfb->getAttribute('DTEND'); - if (!is_a($end, 'PEAR_Error')) { - if (is_array($end)) { - $html .= '

' . _("End") . ': ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '

'; - } else { - $html .= '

' . _("End") . ': ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '

'; - } - } - - if ($_SESSION['imp']['viewmode'] != 'imp') { - return $html; - } - - $html .= '

' . _("Actions") . '

' . - ' '; - } - - /** - * Return the html for a vEvent. - */ - protected function _vEvent($vevent, $id) - { - global $registry, $prefs; - - $html = ''; - $desc = ''; - $sender = $vevent->organizerName(); - $options = array(); - - $attendees = $vevent->getAttribute('ATTENDEE'); - if (!is_a($attendees, 'PEAR_Error') && - !empty($attendees) && - !is_array($attendees)) { - $attendees = array($attendees); - } - $attendee_params = $vevent->getAttribute('ATTENDEE', true); - - switch ($this->_method) { - case 'PUBLISH': - $desc = _("%s wishes to make you aware of \"%s\"."); - if ($registry->hasMethod('calendar/import')) { - $options[] = ''; - } - break; - - case 'REQUEST': - // Check if this is an update. - if ($registry->hasMethod('calendar/export') && - !is_a($registry->call('calendar/export', array($vevent->getAttribute('UID'), 'text/calendar')), 'PEAR_Error')) { - $is_update = true; - $desc = _("%s wants to notify you about changes of \"%s\"."); - } else { - $is_update = false; - - // Check that you are one of the attendees here. - $is_attendee = false; - if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) { - require_once 'Horde/Identity.php'; - $identity = &Identity::singleton(array('imp', 'imp')); - for ($i = 0, $c = count($attendees); $i < $c; ++$i) { - $attendee = parse_url($attendees[$i]); - if (!empty($attendee['path']) && - $identity->hasAddress($attendee['path'])) { - $is_attendee = true; - break; - } - } - } - - $desc = $is_attendee - ? _("%s requests your presence at \"%s\".") - : _("%s wishes to make you aware of \"%s\"."); - } - if ($is_update && $registry->hasMethod('calendar/replace')) { - $options[] = ''; - $options[] = ''; - } elseif ($registry->hasMethod('calendar/import')) { - $options[] = ''; - $options[] = ''; - } - $options[] = ''; - $options[] = ''; - $options[] = ''; - // $options[] = ''; - break; - - case 'ADD': - $desc = _("%s wishes to ammend \"%s\"."); - if ($registry->hasMethod('calendar/import')) { - $options[] = ''; - } - break; - - case 'REFRESH': - $desc = _("%s wishes to receive the latest information about \"%s\"."); - $options[] = ''; - break; - - case 'REPLY': - $desc = _("%s has replied to the invitation to \"%s\"."); - $sender = $this->_headers->getValue('From'); - if ($registry->hasMethod('calendar/updateAttendee')) { - $options[] = ''; - } - break; - - case 'CANCEL': - if (is_a($instance = $vevent->getAttribute('RECURRENCE-ID'), 'PEAR_Error')) { - $desc = _("%s has cancelled \"%s\"."); - if ($registry->hasMethod('calendar/delete')) { - $options[] = ''; - } - } else { - $desc = _("%s has cancelled an instance of the recurring \"%s\"."); - if ($registry->hasMethod('calendar/replace')) { - $options[] = ''; - } - } - break; - } - - $summary = $vevent->getAttribute('SUMMARY'); - if (is_a($summary, 'PEAR_Error')) { - $desc = sprintf($desc, htmlspecialchars($sender), _("Unknown Meeting")); - } else { - $desc = sprintf($desc, htmlspecialchars($sender), htmlspecialchars($summary)); - } - - $html .= '

' . $desc . '

'; - - if ($this->_msgs) { - foreach ($this->_msgs[$id] as $msg) { - $html .= '

' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '

'; - } - } - - $start = $vevent->getAttribute('DTSTART'); - if (!is_a($start, 'PEAR_Error')) { - if (is_array($start)) { - $html .= '

' . _("Start") . ': ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '

'; - } else { - $html .= '

' . _("Start") . ': ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '

'; - } - } - - $end = $vevent->getAttribute('DTEND'); - if (!is_a($end, 'PEAR_Error')) { - if (is_array($end)) { - $html .= '

' . _("End") . ': ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '

'; - } else { - $html .= '

' . _("End") . ': ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '

'; - } - } - - $sum = $vevent->getAttribute('SUMMARY'); - if (!is_a($sum, 'PEAR_Error')) { - $html .= '

' . _("Summary") . ': ' . htmlspecialchars($sum) . '

'; - } else { - $html .= '

' . _("Summary") . ': ' . _("None") . '

'; - } - - $desc = $vevent->getAttribute('DESCRIPTION'); - if (!is_a($desc, 'PEAR_Error')) { - $html .= '

' . _("Description") . ': ' . nl2br(htmlspecialchars($desc)) . '

'; - } - - $loc = $vevent->getAttribute('LOCATION'); - if (!is_a($loc, 'PEAR_Error')) { - $html .= '

' . _("Location") . ': ' . htmlspecialchars($loc) . '

'; - } - - if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) { - $html .= '

' . _("Attendees") . '

'; - - $html .= ''; - foreach ($attendees as $key => $attendee) { - $attendee = parse_url($attendee); - $attendee = empty($attendee['path']) ? _("Unknown") : $attendee['path']; - - if (!empty($attendee_params[$key]['CN'])) { - $attendee = $attendee_params[$key]['CN']; - } - - $role = _("Required Participant"); - if (isset($attendee_params[$key]['ROLE'])) { - switch ($attendee_params[$key]['ROLE']) { - case 'CHAIR': - $role = _("Chair Person"); - break; - - case 'OPT-PARTICIPANT': - $role = _("Optional Participant"); - break; - - case 'NON-PARTICIPANT': - $role = _("Non Participant"); - break; - - case 'REQ-PARTICIPANT': - default: - // Already set above. - break; - } - } - - $status = _("Awaiting Response"); - if (isset($attendee_params[$key]['PARTSTAT'])) { - $status = $this->_partstatToString($attendee_params[$key]['PARTSTAT'], $status); - } - - $html .= ''; - } - $html .= '
' . _("Name") . '' . _("Role") . '' . _("Status") . '
' . htmlspecialchars($attendee) . '' . htmlspecialchars($role) . '' . htmlspecialchars($status) . '
'; - } - - if ($_SESSION['imp']['viewmode'] != 'imp') { - return $html; - } - - if ($options) { - $html .= '

' . _("Actions") . '

' . - '' . - ' '; - } - - return $html; - } - - /** - * Returns the html for a vEvent. - * - * @todo IMP 5: move organizerName() from Horde_iCalendar_vevent to - * Horde_iCalendar - */ - protected function _vTodo($vtodo, $id) - { - global $registry, $prefs; - - $html = ''; - $desc = ''; - $options = array(); - - $organizer = $vtodo->getAttribute('ORGANIZER', true); - if (is_a($organizer, 'PEAR_Error')) { - $sender = _("An unknown person"); - } else { - if (isset($organizer[0]['CN'])) { - $sender = $organizer[0]['CN']; - } else { - $organizer = parse_url($vtodo->getAttribute('ORGANIZER')); - $sender = $organizer['path']; - } - } - - switch ($this->_method) { - case 'PUBLISH': - $desc = _("%s wishes to make you aware of \"%s\"."); - if ($registry->hasMethod('tasks/import')) { - $options[] = ''; - } - break; - } - - $summary = $vtodo->getAttribute('SUMMARY'); - if (is_a($summary, 'PEAR_Error')) { - $desc = sprintf($desc, htmlspecialchars($sender), _("Unknown Task")); - } else { - $desc = sprintf($desc, htmlspecialchars($sender), htmlspecialchars($summary)); - } - - $html .= '

' . $desc . '

'; - - if ($this->_msgs) { - foreach ($this->_msgs[$id] as $msg) { - $html .= '

' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '

'; - } - } - - $priority = $vtodo->getAttribute('PRIORITY'); - if (!is_a($priority, 'PEAR_Error')) { - $html .= '

' . _("Priority") . ': ' . (int)$priority . '

'; - } - - $sum = $vtodo->getAttribute('SUMMARY'); - if (!is_a($sum, 'PEAR_Error')) { - $html .= '

' . _("Summary") . ': ' . htmlspecialchars($sum) . '

'; - } else { - $html .= '

' . _("Summary") . ': ' . _("None") . '

'; - } - - $desc = $vtodo->getAttribute('DESCRIPTION'); - if (!is_a($desc, 'PEAR_Error')) { - $html .= '

' . _("Description") . ': ' . nl2br(htmlspecialchars($desc)) . '

'; - } - - $attendees = $vtodo->getAttribute('ATTENDEE'); - $params = $vtodo->getAttribute('ATTENDEE', true); - - if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) { - $html .= '

' . _("Attendees") . '

'; - if (!is_array($attendees)) { - $attendees = array($attendees); - } - - $html .= ''; - foreach ($attendees as $key => $attendee) { - $attendee = parse_url($attendee); - $attendee = $attendee['path']; - - if (isset($params[$key]['CN'])) { - $attendee = $params[$key]['CN']; - } - - $role = _("Required Participant"); - if (isset($params[$key]['ROLE'])) { - switch ($params[$key]['ROLE']) { - case 'CHAIR': - $role = _("Chair Person"); - break; - - case 'OPT-PARTICIPANT': - $role = _("Optional Participant"); - break; - - case 'NON-PARTICIPANT': - $role = _("Non Participant"); - break; - - case 'REQ-PARTICIPANT': - default: - // Already set above. - break; - } - } - - $status = _("Awaiting Response"); - if (isset($params[$key]['PARTSTAT'])) { - $status = $this->_partstatToString($params[$key]['PARTSTAT'], $status); - } - - $html .= ''; - } - $html .= '
' . _("Name") . '' . _("Role") . '' . _("Status") . '
' . htmlspecialchars($attendee) . '' . htmlspecialchars($role) . '' . htmlspecialchars($status) . '
'; - } - - if ($_SESSION['imp']['viewmode'] != 'imp') { - return $html; - } - - if ($options) { - $html .= '

' . _("Actions") . '

' . - ' '; - } - - return $html; - } - - /** - * Translate the Participation status to string. - * - * @param string $value The value of PARTSTAT. - * @param string $default The value to return as default. - * - * @return string The translated string. - */ - protected function _partstatToString($value, $default = null) - { - switch ($value) { - case 'ACCEPTED': - return _("Accepted"); - break; - - case 'DECLINED': - return _("Declined"); - break; - - case 'TENTATIVE': - return _("Tentatively Accepted"); - break; - - case 'DELEGATED': - return _("Delegated"); - break; - - case 'COMPLETED': - return _("Completed"); - break; - - case 'IN-PROCESS': - return _("In Process"); - break; - - case 'NEEDS-ACTION': - default: - return is_null($default) ? _("Needs Action") : $default; - } - } -} diff --git a/imp/lib/MIME/Viewer/notification.php b/imp/lib/MIME/Viewer/notification.php deleted file mode 100644 index 216e3e62f..000000000 --- a/imp/lib/MIME/Viewer/notification.php +++ /dev/null @@ -1,82 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_notification extends Horde_Mime_Viewer_Driver -{ - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - $contents = &$params[0]; - - /* If this is a straight message/disposition-notification part, just - output the text. */ - if ($this->mime_part->getType() == 'message/disposition-notification') { - $part = new MIME_Part('text/plain'); - $part->setContents($this->mime_part->getContents()); - return '
' . htmlspecialchars($contents->renderMIMEPart($part)) . '
'; - } - - global $registry; - - /* RFC 2298 [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) */ - - /* Print the human readable message. */ - $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(1)); - $status = array( - _("A message you have sent has resulted in a return notification from the recipient."), - _("The mail server generated the following informational message:") - ); - $statusimg = Horde::img('alerts/message.png', _("Attention"), 'height="16" width="16"', $registry->getImageDir('horde')); - $text = $this->formatStatusMsg($status, $statusimg) . - '
' . $contents->renderMIMEPart($part) . '
' . "\n"; - - /* Display a link to more detailed message. */ - $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(2)); - if ($part) { - $statusimg = Horde::img('info_icon.png', _("Info"), 'height="16" width="16"', $registry->getImageDir('horde')); - $status = array(sprintf(_("Additional information can be viewed %s."), $contents->linkViewJS($part, 'view_attach', _("HERE"), _("Additional information details")))); - } - - /* Display a link to the sent message. Try to download the text of - the message/rfc822 part first, if it exists. */ - if (($part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId('3.0'))) || - ($part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(3)))) { - $status[] = sprintf(_("The text of the sent message can be viewed %s."), $contents->linkViewJS($part, 'view_attach', _("HERE"), _("The text of the sent message"))); - } - - $text .= $this->formatStatusMsg($status, $statusimg, false); - - return $text; - } - - /** - * Return the content-type. - * - * @return string The content-type of the message. - */ - public function getType() - { - return 'text/html; charset=' . NLS::getCharset(); - } -} diff --git a/imp/lib/MIME/Viewer/partial.php b/imp/lib/MIME/Viewer/partial.php deleted file mode 100644 index e744371f6..000000000 --- a/imp/lib/MIME/Viewer/partial.php +++ /dev/null @@ -1,76 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_partial extends Horde_Mime_Viewer_Driver -{ - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - $contents = &$params[0]; - - $base_ob = &$contents->getBaseObjectPtr(); - $curr_index = $base_ob->getMessageIndex(); - $id = $this->mime_part->getContentTypeParameter('id'); - $parts = array(); - - /* Perform the search to find the other parts of the message. */ - $query = new Horde_Imap_Client_Search_Query(); - $query->header('Content-Type', $id); - - $indices = $GLOBALS['imp_search']->runSearchQuery($query, $GLOBALS['imp_mbox']['thismailbox']); - - /* If not able to find the other parts of the message, print error. */ - if (count($indices) != $this->mime_part->getContentTypeParameter('total')) { - return $this->formatStatusMsg(sprintf(_("Cannot display - found only %s of %s parts of this message in the current mailbox."), count($indices), $this->mime_part->getContentTypeParameter('total'))); - } - - /* Get the contents of each of the parts. */ - foreach ($indices as $val) { - /* No need to fetch the current part again. */ - if ($val == $curr_index) { - $parts[$this->mime_part->getContentTypeParameter('number')] = $this->mime_part->getContents(); - } else { - $imp_contents = &IMP_Contents::singleton($val . IMP::IDX_SEP . $GLOBALS['imp_mbox']['thismailbox']); - $part = &$imp_contents->getMIMEPart(0); - $parts[$part->getContentTypeParameter('number')] = $imp_contents->getBody(); - } - } - - /* Sort the parts in numerical order. */ - ksort($parts, SORT_NUMERIC); - - /* Combine the parts and render the underlying data. */ - $mime_message = &MIME_Message::parseMessage(implode('', $parts)); - $mc = new MIME_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$contents)); - $mc->buildMessage(); - - return '' . $mc->getMessage(true) . '
'; - } - - /** - * Return the content-type of the rendered output. - * - * @return string The content-type of the output. - */ - public function getType() - { - return 'text/html; charset=' . NLS::getCharset(); - } -} diff --git a/imp/lib/MIME/Viewer/pdf.php b/imp/lib/MIME/Viewer/pdf.php deleted file mode 100644 index 6ee5b36aa..000000000 --- a/imp/lib/MIME/Viewer/pdf.php +++ /dev/null @@ -1,141 +0,0 @@ - - * @package Horde_Mime_Viewer - */ -class IMP_Horde_Mime_Viewer_pdf extends Horde_Mime_Viewer_Driver -{ - /** - * The content-type of the generated data. - * - * @var string - */ - protected $_contentType; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered information. - */ - public function render($params) - { - /* Create the thumbnail and display. */ - if (Util::getFormData('images_view_thumbnail')) { - $mime = $this->mime_part; - $img = $this->_getHordeImageOb(); - - if ($img) { - $img->resize(96, 96, true); - $type = $img->getContentType(); - $data = $img->raw(true); - } - - if (!$img || !$data) { - $type = 'image/png'; - $data = file_get_contents(IMP_BASE . '/themes/graphics/mini-error.png'); - } - - $mime->setType($type); - $this->_contentType = $type; - $mime->setContents($data); - - return $mime->getContents(); - } - - return parent::render($params); - } - - /** - * Render out attachment information. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function renderAttachmentInfo($params) - { - $contents = &$params[0]; - - if (is_a($contents, 'IMP_Contents')) { - $this->mime_part = &$contents->getDecodedMIMEPart($this->mime_part->getMIMEId(), true); - } - - /* Check to see if convert utility is available. */ - if (!$this->_getHordeImageOb(false)) { - return ''; - } - - $status = array( - sprintf(_("A PDF file named %s is attached to this message. A thumbnail is below."), - $this->mime_part->getName(true)), - ); - - if (!$GLOBALS['browser']->hasFeature('javascript')) { - $status[] = Horde::link($contents->urlView($this->mime_part, - 'view_attach')) . - Horde::img($contents->urlView($this->mime_part, - 'view_attach', array('images_view_thumbnail' => 1), false), - _("View Attachment"), null, '') . ''; - } else { - $status[] = $contents->linkViewJS($this->mime_part, 'view_attach', - Horde::img($contents->urlView($this->mime_part, - 'view_attach', array('images_view_thumbnail' => 1), - false), _("View Attachment"), null, ''), null, null, - null); - } - - return $this->formatStatusMsg($status, Horde::img('mime/image.png', - _("Thumbnail of attached PDF file"), null, $GLOBALS['registry']->getImageDir('horde')), false); - } - - /** - * Return a Horde_Image object. - * - * @param boolean $load Whether to load the image data. - * - * @return Horde_Image The requested object. - */ - protected function _getHordeImageOb($load = true) - { - if (empty($GLOBALS['conf']['image']['convert'])) { - return false; - } - - include_once 'Horde/Image.php'; - $img = &Horde_Image::singleton('im', array('temp' => Horde::getTempdir())); - if (is_a($img, 'PEAR_Error')) { - return false; - } - - if ($load) { - $ret = $img->loadString(1, $this->mime_part->getContents()); - if (is_a($ret, 'PEAR_Error')) { - return false; - } - } - - return $img; - } - - /** - * Return the content-type - * - * @return string The content-type of the output. - */ - public function getType() - { - return ($this->_contentType) ? $this->_contentType : parent::getType(); - } -} diff --git a/imp/lib/MIME/Viewer/pgp.php b/imp/lib/MIME/Viewer/pgp.php deleted file mode 100644 index bcebf764d..000000000 --- a/imp/lib/MIME/Viewer/pgp.php +++ /dev/null @@ -1,484 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver -{ - /** - * IMP_PGP object. - * - * @var IMP_PGP - */ - protected $_imp_pgp; - - /** - * The address of the sender. - * - * @var string - */ - protected $_address; - - /** - * Pointer to the MIME_Contents item. - * - * @var MIME_Contents - */ - protected $_contents = null; - - /** - * Classwide cache for icons for status messages. - * - * @var string - */ - protected $_icon = null; - - /** - * Classwide cache for status messages. - * - * @var array - */ - protected $_status = array(); - - /** - * The MIME content-type of this part. - * - * @var string - */ - protected $_type = 'text/html'; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - global $conf, $prefs; - - /* Set the MIME_Contents class variable. */ - $this->_contents = &$params[0]; - - $msg = ''; - - if (empty($this->_imp_pgp) && !empty($conf['utils']['gnupg'])) { - $this->_imp_pgp = new IMP_PGP(); - } - - /* Determine the address of the sender. */ - if (empty($this->_address)) { - $base_ob = &$this->_contents->getBaseObjectPtr(); - $this->_address = $base_ob->getFromAddress(); - } - - /* We need to insert JavaScript code now if PGP support is active. */ - if (!empty($conf['utils']['gnupg']) && - $prefs->getValue('use_pgp') && - !Util::getFormData('rawpgpkey')) { - $msg = Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true); - $msg .= Util::bufferOutput(array('Horde', 'addScriptFile'), 'popup.js', 'imp', true); - } - - /* For RFC 2015/3156, there are 3 types of messages: - + multipart/encrypted - + multipart/signed - + application/pgp-keys */ - switch ($this->mime_part->getType()) { - case 'application/pgp-keys': - $msg .= $this->_outputPGPKey(); - break; - - case 'multipart/signed': - case 'application/pgp-signature': - $msg .= $this->_outputPGPSigned(); - break; - - case 'multipart/encrypted': - case 'application/pgp-encrypted': - $msg .= $this->_outputPGPEncrypted(); - break; - } - - return $msg; - } - - /** - * Return the content-type of the output. - * - * @return string The MIME content type of the output. - */ - public function getType() - { - return $this->_type; - } - - /** - * Generates HTML output for 'application/pgp-keys' MIME_Parts. - * - * @return string The HTML output. - */ - protected function _outputPGPKey() - { - global $conf, $prefs; - - $mime = &$this->mime_part; - $part = $this->_contents->getDecodedMIMEPart($mime->getMIMEId()); - - if (empty($conf['utils']['gnupg'])) { - $text = '
' . $part->getContents() . '
'; - } elseif (Util::getFormData('rawpgpkey')) { - $text = $part->getContents(); - $this->_type = 'text/plain'; - } else { - require_once 'Horde/Text.php'; - - $pgp_key = $mime->getContents(); - - /* Initialize status message. */ - $this->_initStatus($this->getIcon($mime->getType()), _("PGP")); - $msg = _("This PGP Public Key was attached to the message."); - if ($prefs->getValue('use_pgp') && - $GLOBALS['registry']->hasMethod('contacts/addField') && - $prefs->getValue('add_source')) { - $msg .= ' ' . Horde::link('#', '', '', '', $this->_imp_pgp->savePublicKeyURL($mime) . ' return false;') . _("[Save the key to your Address book]") . ''; - } - $this->_status[] = $msg . ' ' . $this->_contents->linkViewJS($part, 'view_attach', _("[View the raw key]"), '', null, array('rawpgpkey' => 1)); - - $text = $this->_outputStatus(false) . - '' . nl2br(str_replace(' ', ' ', $this->_imp_pgp->pgpPrettyKey($pgp_key))) . ''; - } - - return $text; - } - - /** - * Generates HTML output for 'multipart/signed' and - * 'application/pgp-signature' MIME_Parts. - * - * @return string The HTML output. - */ - protected function _outputPGPSigned() - { - global $conf, $prefs; - - $active = ($prefs->getValue('use_pgp') && !empty($conf['utils']['gnupg'])); - $mime = &$this->mime_part; - $mimetype = $mime->getType(); - $text = ''; - - $signenc = $mime->getInformation('pgp_signenc'); - if (!$active) { - if ($signenc) { - $this->_status[] = _("The message below has been digitally signed and encrypted with PGP, but the signature cannot be verified."); - } else { - $this->_status[] = _("The message below has been digitally signed with PGP, but the signature cannot be verified."); - } - } else { - if ($signenc) { - $this->_status[] = _("The message below has been digitally signed and encrypted with PGP."); - } else { - $this->_status[] = _("The message below has been digitally signed with PGP."); - } - } - - $this->_initStatus($this->getIcon($mimetype), _("PGP")); - - /* Store PGP results in $sig_result; store text in $data. */ - $sig_result = null; - if ($mimetype == 'multipart/signed') { - /* If the MIME ID is 0, we need to store the body of the message - in the MIME_Part object. */ - if (!$signenc) { - if (($mimeID = $mime->getMIMEId())) { - $mime->setContents($this->_contents->getBodyPart($mimeID)); - } else { - $mime->setContents($this->_contents->getBody()); - } - $mime->splitContents(); - } - - /* Data that is signed appears in the first MIME subpart. */ - $subpart = $mime->getPart($mime->getRelativeMIMEId(1)); - $signature_data = rtrim($subpart->getCanonicalContents(), "\r"); - - $mime_message = Horde_Mime_Message::parseMessage($signature_data); - - /* The PGP signature appears in the second MIME subpart. */ - $subpart = $mime->getPart($mime->getRelativeMIMEId(2)); - if ($subpart && $subpart->getType() == 'application/pgp-signature') { - if ($active) { - if ($GLOBALS['prefs']->getValue('pgp_verify') || - Util::getFormData('pgp_verify_msg')) { - $subpart->transferDecodeContents(); - $sig_result = $this->_imp_pgp->verifySignature($signature_data, $this->_address, $subpart->getContents()); - } elseif (isset($_SESSION['imp']['viewmode']) && - ($_SESSION['imp']['viewmode'] == 'imp')) { - // TODO: Fix to work with DIMP - $base_ob = &$this->_contents->getBaseObjectPtr(); - $this->_status[] = Horde::link(Util::addParameter(IMP::generateIMPUrl(Horde::selfUrl(), $GLOBALS['imp_mbox']['mailbox'], $GLOBALS['imp_mbox']['index'], $GLOBALS['imp_mbox']['thismailbox']), 'pgp_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; - } - } - } else { - $this->_status[] = _("The message below does not appear to be in the correct PGP format (according to RFC 2015)."); - } - } elseif ($mimetype == 'application/pgp-signature') { - /* Get the signed message to output. */ - $mime_message = new Horde_Mime_Message(); - $mime_message->setType('text/plain'); - $mime->transferDecodeContents(); - - if (empty($this->_imp_pgp)) { - $mime_message->setContents($mime->getContents()); - } else { - $mime_message->setContents($this->_imp_pgp->getSignedMessage($mime)); - } - - /* Pass the signed text straight through to PGP program */ - if ($active) { - if ($GLOBALS['prefs']->getValue('pgp_verify') || - Util::getFormData('pgp_verify_msg')) { - $sig_result = $this->_imp_pgp->verifySignature($mime->getContents(), $this->_address); - } elseif (isset($_SESSION['imp']['viewmode']) && - ($_SESSION['imp']['viewmode'] == 'imp')) { - // TODO: Fix to work with DIMP - $this->_status[] = Horde::link(Util::addParameter(Horde::selfUrl(true), 'pgp_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; - } - } - } - - $text = $this->_outputStatus(); - - if ($sig_result !== null) { - $text .= $this->_outputPGPSignatureTest($sig_result); - } - - /* We need to stick the output into a MIME_Contents object. */ - $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); - $mc->buildMessage(); - - return $text . '' . $mc->getMessage(true) . '
'; - } - - /** - * Generates HTML output for 'multipart/encrypted' and - * 'application/pgp-encrypted' MIME_Parts. - * - * @return string The HTML output. - */ - protected function _outputPGPEncrypted() - { - global $conf, $prefs; - - $mime = &$this->mime_part; - $mimetype = $mime->getType(); - $text = ''; - - $this->_initStatus($this->getIcon($mimetype), _("PGP")); - - /* Print out message now if PGP is not active. */ - if (empty($conf['utils']['gnupg']) || !$prefs->getValue('use_pgp')) { - $this->_status[] = _("The message below has been encrypted with PGP, however, PGP support is disabled so the message cannot be decrypted."); - return $this->_outputStatus(); - } - - if ($mimetype == 'multipart/encrypted') { - /* PGP control information appears in the first MIME subpart. We - don't currently need to do anything with this information. The - encrypted data appears in the second MIME subpart. */ - $subpart = $mime->getPart($mime->getRelativeMIMEId(2)); - if (!$subpart) { - return $text; - } - $encrypted_data = $this->_contents->getBodyPart($subpart->getMIMEId()); - } elseif ($mimetype == 'application/pgp-encrypted') { - $encrypted_data = $mime->getContents(); - } else { - return $text; - } - - /* Check if this is a literal compressed message. */ - $info = $this->_imp_pgp->pgpPacketInformation($encrypted_data); - $literal = !empty($info['literal']); - - /* Check if this a symmetrically encrypted message. */ - $symmetric = $this->_imp_pgp->encryptedSymmetrically($encrypted_data); - - if ($symmetric && !$this->_imp_pgp->getSymmetricPassphrase()) { - if (isset($_SESSION['imp']['viewmode']) && - ($_SESSION['imp']['viewmode'] == 'imp')) { - // TODO: Fix to work with DIMP - /* Ask for the correct passphrase if this is encrypted - * symmetrically. */ - $url = $this->_imp_pgp->getJSOpenWinCode('open_symmetric_passphrase_dialog'); - $this->_status[] = Horde::link('#', _("The message below has been encrypted with PGP. You must enter the passphrase that was used to encrypt this message."), null, null, $url . ' return false;') . _("You must enter the passphrase that was used to encrypt this message.") . ''; - $text .= $this->_outputStatus() . - Util::bufferOutput(array('IMP', 'addInlineScript'), $url); - } - } elseif (!$literal && !$symmetric && - !$this->_imp_pgp->getPersonalPrivateKey()) { - /* Output if there is no personal private key to decrypt with. */ - $this->_status[] = _("The message below has been encrypted with PGP, however, no personal private key exists so the message cannot be decrypted."); - return $this->_outputStatus(); - } elseif (!$literal && !$symmetric && - !$this->_imp_pgp->getPassphrase()) { - if (isset($_SESSION['imp']['viewmode']) && - ($_SESSION['imp']['viewmode'] == 'imp')) { - // TODO: Fix to work with DIMP - /* Ask for the private key's passphrase if this is encrypted - * asymmetrically. */ - $url = $this->_imp_pgp->getJSOpenWinCode('open_passphrase_dialog'); - $this->_status[] = Horde::link('#', _("The message below has been encrypted with PGP. You must enter the passphrase for your PGP private key before it can be decrypted."), null, null, $url . ' return false;') . _("You must enter the passphrase for your PGP private key to view this message.") . ''; - $text .= $this->_outputStatus() . - Util::bufferOutput(array('IMP', 'addInlineScript'), $url); - } - } else { - /* Decrypt this message. */ - $this->_status[] = $literal ? _("The message below has been compressed with PGP.") : _("The message below has been encrypted with PGP."); - if ($mimetype == 'multipart/encrypted') { - if ($subpart->getType() == 'application/octet-stream') { - $decrypted_data = $this->_imp_pgp->decryptMessage($encrypted_data, $symmetric, !$literal); - if (is_a($decrypted_data, 'PEAR_Error')) { - $this->_status[] = _("The message below does not appear to be a valid PGP encrypted message. Error: ") . $decrypted_data->getMessage(); - $text .= $this->_outputStatus(); - } else { - /* We need to check if this is a signed/encrypted - message. */ - $mime_message = Horde_Mime_Message::parseMessage($decrypted_data->message); - if (!$mime_message) { - require_once 'Horde/Text/Filter.php'; - $text .= $this->_signedOutput($decrypted_data->sig_result); - $decrypted_message = String::convertCharset($decrypted_data->message, $subpart->getCharset()); - $text .= '' . Text_Filter::filter($decrypted_message, 'text2html', array('parselevel' => TEXT_HTML_SYNTAX)) . ''; - } else { - $mimetype = $mime_message->getType(); - if (($mimetype == 'multipart/signed') || - ($mimetype == 'application/pgp-signature')) { - $mime_message->setInformation('pgp_signenc', true); - $mime_message->setContents($decrypted_data->message); - $mime_message->splitContents(); - } else { - $text .= $this->_signedOutput($decrypted_data->sig_result); - } - - /* We need to stick the output into a - MIME_Contents object. */ - $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); - $mc->buildMessage(); - if ($mime_message->getInformation('pgp_signenc')) { - $text .= $mc->getMessage(true); - } else { - $text .= '' . $mc->getMessage(true) . '
'; - } - } - } - } else { - $this->_status[] = _("The message below does not appear to be in the correct PGP format (according to RFC 2015)."); - $text .= $this->_outputStatus(); - } - } elseif ($mimetype == 'application/pgp-encrypted') { - $decrypted_data = $this->_imp_pgp->decryptMessage($encrypted_data, $symmetric, !$literal); - if (is_a($decrypted_data, 'PEAR_Error')) { - $decrypted_message = $decrypted_data->getMessage(); - $text .= $this->_outputStatus(); - } else { - $text .= $this->_signedOutput($decrypted_data->sig_result); - $decrypted_message = String::convertCharset($decrypted_data->message, $mime->getCharset()); - } - - require_once 'Horde/Text/Filter.php'; - $text .= '' . Text_Filter::filter($decrypted_message, 'text2html', array('parselevel' => TEXT_HTML_SYNTAX)) . ''; - } - } - $this->_imp_pgp->unsetSymmetricPassphrase(); - - return $text; - } - - /** - * Generates HTML output for the PGP signature test. - * - * @param string $result Result string of the PGP output concerning the - * signature test. - * - * @return string The HTML output. - */ - protected function _outputPGPSignatureTest($result) - { - $text = ''; - - if (is_a($result, 'PEAR_Error')) { - $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/error.png', _("Error")); - $result = $result->getMessage(); - } else { - $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/success.png', _("Success")); - /* This message has been verified but there was no output from the - PGP program. */ - if (empty($result)) { - $result = _("The message below has been verified."); - } - } - - require_once 'Horde/Text/Filter.php'; - $this->_status[] = Text_Filter::filter($result, 'text2html', array('parselevel' => TEXT_HTML_NOHTML)); - - return $this->_outputStatus(); - } - - /** - * Output signed message status. - * - * @param string $result The signature result. - * - * @return string HTML output. - */ - protected function _signedOutput($result) - { - if (!empty($result)) { - return $this->_outputPGPSignatureTest($result); - } else { - return $this->_outputStatus(); - } - } - - /* Various formatting helper functions */ - protected function _initStatus($src, $alt = '') - { - if ($this->_icon === null) { - $this->_icon = Horde::img($src, $alt, 'height="16" width="16"', ''); - } - } - - protected function _outputStatus($printable = true) - { - $output = ''; - if (!empty($this->_status)) { - $output = $this->formatStatusMsg($this->_status, $this->_icon, $printable); - } - $this->_icon = null; - $this->_status = array(); - return $output; - } -} diff --git a/imp/lib/MIME/Viewer/pkcs7.php b/imp/lib/MIME/Viewer/pkcs7.php deleted file mode 100644 index 168a048e4..000000000 --- a/imp/lib/MIME/Viewer/pkcs7.php +++ /dev/null @@ -1,495 +0,0 @@ - - * @author Michael Slusarz - * @package Horde_Mime_Viewer - */ -class IMP_Horde_Mime_Viewer_pkcs7 extends Horde_Mime_Viewer_Driver -{ - /** - * IMP_SMIME object. - * - * @var IMP_SMIME - */ - protected $_impSmime; - - /** - * Classwide cache for icons for status messages. - * - * @var string - */ - protected $_icon = null; - - /** - * Pointer to the IMP_Contents item. - * - * @var IMP_Contents - */ - protected $_contents = null; - - /** - * Classwide cache for status messages. - * - * @var array - */ - protected $_status = array(); - - /** - * The MIME_Headers object for the message data. - * - * @var MIME_Headers - */ - protected $_headers; - - /** - * Some mailers set S/MIME messages to always be attachments. However, - * most of the time S/MIME is used to secure the contents of the message, - * so displaying as an attachment makes no sense. Therefore, force - * viewing inline (or at least let Horde_Mime_Viewer/IMP_Contents make the - * determination on whether the data can be viewed inline or not). - * - * @var boolean - */ - protected $_forceinline = true; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a IMP_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - /* Set the IMP_Contents class variable. */ - $this->_contents = &$params[0]; - - $msg = ''; - - if (empty($this->_impSmime)) { - $this->_impSmime = new IMP_SMIME(); - } - - /* Check to see if S/MIME support is available. */ - $openssl_check = $this->_impSmime->checkForOpenSSL(); - if ($GLOBALS['prefs']->getValue('use_smime') && - !is_a($openssl_check, 'PEAR_Error')) { - /* We need to insert JavaScript code now if S/MIME support is - active. */ - $msg = Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true); - $msg .= Util::bufferOutput(array('Horde', 'addScriptFile'), 'popup.js', 'imp', true); - } - - /* Get the type of message now. */ - $type = $this->_getSMIMEType(); - switch ($type) { - case 'signed': - $msg .= $this->_outputSMIMESigned(); - break; - - case 'encrypted': - $msg .= $this->_outputSMIMEEncrypted(); - break; - } - - return $msg; - } - - /** - * Generates HTML output for the S/MIME key in - * 'application/pkcs7-signature' MIME_Parts. - * - * @return string The HTML output. - */ - protected function _outputSMIMEKey() - { - if (!$GLOBALS['prefs']->getValue('use_smime')) { - return _("S/MIME support is not enabled."); - } else { - $mime = &$this->mime_part; - $signenc = $mime->getInformation('smime_signenc'); - $raw_text = $this->_getRawSMIMEText(); - if ($signenc && $mime->getInformation('smime_from')) { - $smime_from = $mime->getInformation('smime_from'); - $raw_text = "From: $smime_from\n" . $raw_text; - } - $sig_result = $this->_impSmime->verifySignature($raw_text); - return $this->_impSmime->certToHTML($sig_result->cert); - } - } - - /** - * Generates HTML output for 'multipart/signed', - * 'application/pkcs7-signature' and - * 'application/x-pkcs7-signature' MIME_Parts. - * - * @return string The HTML output. - */ - protected function _outputSMIMESigned() - { - if (Util::getFormData('viewkey')) { - return $this->_outputSMIMEKey(); - } - - $cert = $text = ''; - $mime = &$this->mime_part; - $mimetype = $mime->getType(); - $active = $GLOBALS['prefs']->getValue('use_smime'); - - $signenc = $mime->getInformation('smime_signenc'); - if ($signenc) { - $this->_status[] = _("This message has been encrypted via S/MIME."); - } - - $this->_initStatus($this->getIcon($mimetype), _("S/MIME")); - $this->_status[] = _("This message has been digitally signed via S/MIME."); - - if (!$active) { - $this->_status[] = _("S/MIME support is not enabled so the digital signature is unable to be verified."); - } - - /* Store S/MIME results in $sig_result. */ - $sig_result = null; - if ($mimetype == 'multipart/signed') { - if (!$signenc) { - if (($mimeID = $mime->getMIMEId())) { - $mime->setContents($this->_contents->getBodyPart($mimeID)); - } else { - $mime->setContents($this->_contents->getBody()); - } - $mime->splitContents(); - } - - /* Data that is signed appears in the first MIME subpart. */ - $signed_part = $mime->getPart($mime->getRelativeMIMEId(1)); - $signed_data = rtrim($signed_part->getCanonicalContents(), "\r"); - $mime_message = Horde_Mime_Message::parseMessage($signed_data); - - /* The S/MIME signature appears in the second MIME subpart. */ - $subpart = $mime->getPart($mime->getRelativeMIMEId(2)); - if (!$subpart || - !in_array($subpart->getType(), array('application/pkcs7-signature', 'application/x-pkcs7-signature'))) { - $this->_status[] = _("This message does not appear to be in the correct S/MIME format."); - } - } elseif (!$active) { - $this->_status[] = _("S/MIME support is not enabled so the contents of this signed message cannot be displayed."); - } - - if ($active) { - $raw_text = $this->_getRawSMIMEText(); - if ($signenc && $mime->getInformation('smime_from')) { - $smime_from = $mime->getInformation('smime_from'); - $raw_text = "From: $smime_from\n" . $raw_text; - } - - if ($GLOBALS['prefs']->getValue('smime_verify') || - Util::getFormData('smime_verify_msg')) { - $sig_result = $this->_impSmime->verifySignature($raw_text); - } elseif (isset($_SESSION['imp']['viewmode']) && - ($_SESSION['imp']['viewmode'] == 'imp')) { - // TODO: Fix to work with DIMP - $this->_status[] = Horde::link(Util::addParameter(Horde::selfUrl(true), 'smime_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; - } - - if (!isset($subpart)) { - $msg_data = $this->_impSmime->extractSignedContents($raw_text); - if (is_a($msg_data, 'PEAR_Error')) { - $this->_status[] = $msg_data->getMessage(); - $mime_message = $mime; - } else { - $mime_message = Horde_Mime_Message::parseMessage($msg_data); - } - } - - $text = $this->_outputStatus(); - if ($sig_result !== null) { - $text .= $this->_outputSMIMESignatureTest($sig_result->result, $sig_result->email); - if (!empty($sig_result->cert)) { - $cert_details = $this->_impSmime->parseCert($sig_result->cert); - if (isset($cert_details['certificate']['subject']['CommonName'])) { - $subject = $cert_details['certificate']['subject']['CommonName']; - } elseif (isset($cert_details['certificate']['subject']['Email'])) { - $subject = $cert_details['certificate']['subject']['Email']; - } elseif (isset($sig_result->email)) { - $subject = $sig_result->email; - } elseif (isset($smime_from)) { - $subject = $smime_from; - } elseif (($from = $this->_headers->getValue('from'))) { - $subject = $from; - } else { - $subject = null; - } - if (isset($subpart) && - !empty($subject) && - $GLOBALS['registry']->hasMethod('contacts/addField') && - $GLOBALS['prefs']->getValue('add_source')) { - $this->_status[] = sprintf(_("The S/MIME certificate of %s: "), @htmlspecialchars($subject, ENT_COMPAT, NLS::getCharset())) . - $this->_contents->linkViewJS($subpart, 'view_attach', _("View"), '', null, array('viewkey' => 1)) . '/' . - Horde::link('#', '', null, null, $this->_impSmime->savePublicKeyURL($sig_result->cert) . ' return false;') . _("Save in your Address Book") . ''; - $text .= $this->_outputStatus(); - } - } - } - } - - if (isset($mime_message)) { - /* We need to stick the output into a IMP_Contents object. */ - $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); - $mc->buildMessage(); - - $text .= '' . $mc->getMessage(true) . '
'; - } else { - $text = $this->_outputStatus(); - } - - return $text; - } - - /** - * Generates HTML output for 'multipart/encrypted', - * 'application/pkcs7-mime' and - * 'application/x-pkcs7-mime' MIME_Parts. - * - * @return string The HTML output. - */ - protected function _outputSMIMEEncrypted() - { - $active = $GLOBALS['prefs']->getValue('use_smime'); - $mime = &$this->mime_part; - $mimetype = $mime->getType(); - $msg = ''; - - $this->_initStatus($this->getIcon($mime->getType()), _("S/MIME")); - $this->_status[] = _("This message has been encrypted via S/MIME."); - - if (!$active) { - $this->_status[] = _("S/MIME support is not currently enabled so the message is unable to be decrypted."); - return $this->_outputStatus(); - } - - if (!$this->_impSmime->getPersonalPrivateKey()) { - $this->_status[] = _("No personal private key exists so the message is unable to be decrypted."); - return $this->_outputStatus(); - } - - /* Make sure we have a passphrase. */ - $passphrase = $this->_impSmime->getPassphrase(); - if ($passphrase === false) { - if (isset($_SESSION['imp']['viewmode']) && - ($_SESSION['imp']['viewmode'] == 'imp')) { - // TODO: Fix to work with DIMP - $url = $this->_impSmime->getJSOpenWinCode('open_passphrase_dialog'); - $this->_status[] = Horde::link('#', _("You must enter the passphrase for your S/MIME private key to view this message"), null, null, $url . ' return false;') . '' . _("You must enter the passphrase for your S/MIME private key to view this message") . '.'; - $msg .= $this->_outputStatus() . - ''; - } - return $msg; - } - - $raw_text = $this->_getRawSMIMEText(); - $decrypted_data = $this->_impSmime->decryptMessage($raw_text); - - if (is_a($decrypted_data, 'PEAR_Error')) { - $this->_status[] = $decrypted_data->getMessage(); - return $this->_outputStatus(); - } - - /* We need to check if this is a signed/encrypted message. */ - $mime_message = Horde_Mime_Message::parseMessage($decrypted_data); - if ($mime_message) { - /* Check for signed and encoded data. */ - if (in_array($mime_message->getType(), array('multipart/signed', 'application/pkcs7-mime', 'application/x-pkcs7-mime'))) { - $mime_message->setContents($decrypted_data); - $mime_message->splitContents(); - $mime_message->setInformation('smime_signenc', true); - if (($from = $this->_headers->getValue('from'))) { - $mime_message->setInformation('smime_from', $from); - } - } else { - $msg .= $this->_outputStatus(); - } - - /* We need to stick the output into a IMP_Contents object. */ - $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); - $mc->buildMessage(); - $msg .= '' . $mc->getMessage(true) . '
'; - } else { - require_once 'Horde/Text/Filter.php'; - $msg .= $this->_outputStatus() . - '' . Text_Filter::filter($decrypted_data, 'text2html', array('parselevel' => TEXT_HTML_SYNTAX)) . ''; - } - - return $msg; - } - - /** - * Return text/html as the content-type. - * - * @return string "text/html" constant. - */ - public function getType() - { - return 'text/html; charset=' . NLS::getCharset(); - } - - /** - * Get the headers of the S/MIME message. - */ - protected function _getRawSMIMEText() - { - $mime = &$this->mime_part; - - $mime->setContents($this->_contents->getBody()); - if (is_a($this->_contents, 'IMP_Contents') && - (($mime->getMIMEId() == 0) || - ($mime->splitContents() == false))) { - $this->_headers = $this->_contents->getHeaderOb(); - return $this->_contents->fullMessageText(); - } else { - $header_text = $mime->getCanonicalContents(); - $header_text = substr($header_text, 0, strpos($header_text, "\r\n\r\n")); - $this->_headers = MIME_Headers::parseHeaders($header_text); - - $mime_headers = new MIME_Headers(); - foreach (array('Content-Type', 'From', 'To') as $val) { - $tmp = $this->_headers->getValue($val); - if (!empty($tmp)) { - $mime_headers->addHeader($val, $tmp); - } - } - - return $mime_headers->toString() . $mime->toCanonicalString(); - } - } - - /* Various formatting helper functions. */ - protected function _initStatus($src, $alt = '') - { - if ($this->_icon === null) { - $this->_icon = Horde::img($src, $alt, 'height="16" width="16"', ''); - } - } - - protected function _outputStatus() - { - $output = ''; - if (!empty($this->_status)) { - $output = $this->formatStatusMsg($this->_status, $this->_icon); - } - $this->_icon = null; - $this->_status = array(); - return $output; - } - - /** - * Generates HTML output for the S/MIME signature test. - * - * @param string $result Result string of the S/MIME output concerning - * the signature test. - * @param string $email The email of the sender. - * - * @return string The HTML output. - */ - protected function _outputSMIMESignatureTest($result, $email) - { - $text = ''; - - if (is_a($result, 'PEAR_Error')) { - if ($result->getCode() == 'horde.warning') { - $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/warning.png', _("Warning")); - } else { - $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/error.png', _("Error")); - } - $result = $result->getMessage(); - } else { - $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/success.png', _("Success")); - /* This message has been verified but there was no output - from the PGP program. */ - if (empty($result) || ($result === true)) { - $email = (is_array($email)) ? implode(', ', $email): $email; - $result = sprintf(_("The message has been verified. Sender: %s."), htmlspecialchars($email)); - } - } - - require_once 'Horde/Text/Filter.php'; - - $this->_status[] = Text_Filter::filter($result, 'text2html', array('parselevel' => TEXT_HTML_NOHTML)); - - return $this->_outputStatus(); - } - - /** - * Render out attachment information. - * - * @param array $params An array with a reference to a IMP_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function renderAttachmentInfo($params) - { - $this->_contents = &$params[0]; - - $type = $this->_getSMIMEType(); - switch ($type) { - case 'signed': - $this->_status[] = _("This message contains an attachment that has been digitally signed via S/MIME."); - break; - - case 'encrypted': - $this->_status[] = _("This message contains an attachment that has been encrypted via S/MIME."); - break; - } - - $this->_status[] = sprintf(_("Click %s to view the attachment in a separate window."), $this->_contents->linkViewJS($this->mime_part, 'view_attach', _("HERE"), _("View attachment in a separate window"))); - $this->_initStatus($this->getIcon($this->mime_part->getType()), _("S/MIME")); - return $this->_outputStatus(); - } - - /** - * Deterimne the S/MIME type of the message. - * - * @return string Either 'encrypted' or 'signed'. - */ - protected function _getSMIMEType() - { - $type = $this->mime_part->getType(); - if (in_array($type, array('application/pkcs7-mime', 'application/x-pkcs7-mime'))) { - $smime_type = $this->mime_part->getContentTypeParameter('smime-type'); - if ($smime_type == 'signed-data') { - return 'signed'; - } elseif (!$smime_type || ($smime_type == 'enveloped-data')) { - return 'encrypted'; - } - } - - switch ($type) { - case 'multipart/signed': - case 'application/pkcs7-signature': - case 'application/x-pkcs7-signature': - return 'signed'; - } - } -} diff --git a/imp/lib/MIME/Viewer/plain.php b/imp/lib/MIME/Viewer/plain.php deleted file mode 100644 index f5887b851..000000000 --- a/imp/lib/MIME/Viewer/plain.php +++ /dev/null @@ -1,115 +0,0 @@ - - * @author Michael Slusarz - * @package Horde_Mime_Viewer - */ -class IMP_Horde_Mime_Viewer_plain extends Horde_Mime_Viewer_plain -{ - /** - * TODO - */ - protected function _renderInline() - { - global $conf, $prefs; - - // Trim extra whitespace in the text. - $text = rtrim($this->_mimepart->getContents()); - if ($text == '') { - return array( - 'data' => '', - 'status' => array() - ); - } - - // If requested, scan the message for PGP data. - if (!empty($conf['utils']['gnupg']) && - $prefs->getValue('pgp_scan_body') && - preg_match('/-----BEGIN PGP ([^-]+)-----/', $text)) { - require_once IMP_BASE . '/lib/Crypt/PGP.php'; - $imp_pgp = new IMP_PGP(); - if (($out = $imp_pgp->parseMessageOutput($this->_mimepart, $this->_params['contents']))) { - return array( - 'data' => $out, - 'status' => array() - ); - } - } - - // 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); - } - - // Build filter stack. Starts with HTML markup and tab expansion. - require_once 'Horde/Text/Filter.php'; - $filters = array( - 'text2html' => array( - 'parselevel' => TEXT_HTML_MICRO, - 'charset' => $this->_mimepart->getCharset() - ), - 'tabs2spaces' => array() - ); - - // Highlight quoted parts of an email. - if ($prefs->getValue('highlight_text')) { - $show = $prefs->getValue('show_quoteblocks'); - $hideBlocks = ($show == 'hidden') || - (($show == 'thread') && (basename(Horde::selfUrl()) == 'thread.php')); - if (!$hideBlocks && in_array($show, array('list', 'listthread'))) { - $header = $this->_params['contents']->getHeaderOb(); - $imp_ui = new IMP_UI_Message(); - $list_info = $imp_ui->getListInformation($header); - $hideBlocks = $list_info['exists']; - } - $filters['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(); - } - - // Filter bad language. - if ($prefs->getValue('filtering')) { - $filters['words'] = array( - 'words_file' => $conf['msgsettings']['filtering']['words'], - 'replacement' => $conf['msgsettings']['filtering']['replacement'] - ); - } - - // Run filters. - $text = Text_Filter::filter($text, array_keys($filters), array_values($filters)); - - // Wordwrap. - $text = str_replace(array(' ', "\n "), array('  ', "\n "), $text); - if (!strncmp($text, ' ', 1)) { - $text = ' ' . substr($text, 1); - } - - return array( - 'data' => '
' . "\n" . $text . '
', - 'status' => array() - ); - } -} diff --git a/imp/lib/MIME/Viewer/related.php b/imp/lib/MIME/Viewer/related.php deleted file mode 100644 index 2879b6d13..000000000 --- a/imp/lib/MIME/Viewer/related.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_related extends Horde_Mime_Viewer_Driver -{ - /** - * The character set of the rendered HTML part. - * - * @var string - */ - protected $_charset = null; - - /** - * The mime type of the message part that has been chosen to be displayed. - * - * @var string - */ - protected $_viewerType = 'text/html'; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - $contents = &$params[0]; - - /* Look at the 'start' parameter to determine which part to start - with. If no 'start' parameter, use the first part. - RFC 2387 [3.1] */ - if ($this->mime_part->getContentTypeParameter('start') && - ($key = array_search($this->mime_part->getContentTypeParameter('start'), $this->mime_part->getCIDList()))) { - if (($pos = strrpos($key, '.'))) { - $id = substr($key, $pos + 1); - } else { - $id = $key; - } - } else { - $id = 1; - } - $start = $this->mime_part->getPart($this->mime_part->getRelativeMimeID($id)); - - /* Only display if the start part (normally text/html) can be displayed - inline -OR- we are viewing this part as an attachment. */ - if ($contents->canDisplayInline($start) || - $this->viewAsAttachment()) { - $text = $contents->renderMIMEPart($start); - $this->_viewerType = $contents->getMIMEViewerType($start); - $this->_charset = $start->getCharset(); - } else { - $text = ''; - } - - return $text; - } - - /** - * Render out attachment information. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function renderAttachmentInfo($params) - { - $contents = &$params[0]; - - $msg = sprintf(_("Click %s to view this multipart/related part in a separate window."), $contents->linkViewJS($this->mime_part, 'view_attach', _("HERE"), _("View content in a separate window"))); - return $this->formatStatusMsg($msg, Horde::img('mime/html.png', _("HTML")), false); - } - - /** - * Return the content-type. - * - * @return string The content-type of the message. - */ - public function getType() - { - return $this->_viewerType . '; charset=' . $this->getCharset(); - } - - /** - * Returns the character set used for the Viewer. - * - * @return string The character set used by this Viewer. - */ - public function getCharset() - { - return ($this->_charset === null) ? NLS::getCharset() : $this->_charset; - } -} diff --git a/imp/lib/MIME/Viewer/rfc822.php b/imp/lib/MIME/Viewer/rfc822.php deleted file mode 100644 index d15dbd342..000000000 --- a/imp/lib/MIME/Viewer/rfc822.php +++ /dev/null @@ -1,62 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_rfc822 extends Horde_Mime_Viewer_rfc822 -{ - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - $contents = &$params[0]; - - /* Get the entire body part of the message/rfc822 contents. */ - if (!$this->mime_part->getInformation('imp_contents_set') && - is_a($contents, 'IMP_Contents') && - $this->mime_part->getMIMEId()) { - $this->mime_part = &$contents->getDecodedMIMEPart($this->mime_part->getMIMEId(), true); - } - - return parent::render(); - } - - /** - * Render out attachment information. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function renderAttachmentInfo($params) - { - $contents = &$params[0]; - - if (is_a($contents, 'IMP_Contents') && - !$this->mime_part->getContents()) { - $id = $this->mime_part->getMIMEId(); - $hdr_id = substr($id, -2); - if ($hdr_id != '.0') { - $id .= '.0'; - } - $this->mime_part = &$contents->getDecodedMIMEPart($id); - } - - return parent::renderAttachmentInfo(); - } -} diff --git a/imp/lib/MIME/Viewer/smil.php b/imp/lib/MIME/Viewer/smil.php deleted file mode 100644 index 76b039b85..000000000 --- a/imp/lib/MIME/Viewer/smil.php +++ /dev/null @@ -1,101 +0,0 @@ - - * @author Michael Slusarz - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_smil extends Horde_Mime_Viewer_smil -{ - /** - * The MIME_Contents object, needed for the _callback() function. - * - * @var MIME_Contents - */ - protected $_contents; - - /** - * The list of related parts to the current part. - * - * @var array - */ - protected $_related = null; - - /** - * Renders out the contents. - * - * @param array $params Any parameters the Viewer may need. - * - * @return string The rendered contents. - */ - public function render($params) - { - $this->_contents = &$params[0]; - return parent::render($params); - } - - /** - * User-defined function callback for start elements. - * - * @param object $parser Handle to the parser instance. - * @param string $name The name of this XML element. - * @param array $attrs List of this element's attributes. - */ - protected function _startElement($parser, $name, $attrs) - { - switch ($name) { - case 'IMG': - if (isset($attrs['SRC'])) { - $rp = $this->_getRelatedLink($attrs['SRC']); - if ($rp !== false) { - $this->_content .= '
'; - } - } - break; - - case 'TEXT': - if (isset($attrs['SRC'])) { - $rp = $this->_getRelatedLink($attrs['SRC']); - if ($rp !== false) { - $this->_content .= htmlspecialchars($rp->getContents()) . '
'; - } - } - break; - } - } - - /** - * Get related parts. - * - * @param string $cid The CID to search for. - * - * @return mixed Either the related MIME_Part or false. - */ - protected function _getRelatedLink($cid) - { - if ($this->_related === null) { - $this->_related = false; - $related = $this->mime_part->getInformation('related_part'); - if ($related !== false) { - $relatedPart = $this->_contents->getMIMEPart($related); - $this->_related = $relatedPart->getCIDList(); - } - } - - if ($this->_related) { - $key = array_search('<' . $cid . '>', $this->_related); - if ($key !== false) { - $cid_part = $this->_contents->getDecodedMIMEPart($key); - return $cid_part; - } - } - - return false; - } -} diff --git a/imp/lib/MIME/Viewer/status.php b/imp/lib/MIME/Viewer/status.php deleted file mode 100644 index 17ab23099..000000000 --- a/imp/lib/MIME/Viewer/status.php +++ /dev/null @@ -1,179 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_status extends Horde_Mime_Viewer_Driver -{ - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function render($params) - { - $contents = &$params[0]; - - /* If this is a straight message/delivery-status part, just output - the text. */ - if ($this->mime_part->getType() == 'message/delivery-status') { - $part = new Horde_Mime_Part('text/plain'); - $part->setContents($this->mime_part->getContents()); - return '
' . $contents->renderMIMEPart($part) . '
'; - } - - global $registry; - - /* 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 2464 [2.3.3]). It can be either 'failed', - 'delayed', 'delivered', 'relayed', or 'expanded'. */ - $action = null; - $part2 = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(2)); - if (empty($part2)) { - return $this->_errorMsg($contents); - } - - foreach (explode("\n", $part2->getContents()) as $line) { - if (strstr($line, 'Action:') !== false) { - $pos = strpos($line, ':') + 1; - $action = strtolower(trim(substr($line, $pos))); - break; - } - } - if (strpos($action, ' ') !== false) { - $action = substr($action, 0, strpos($action, ' ')); - } - - /* Get the correct text strings for the action type. */ - switch ($action) { - case 'failed': - case 'delayed': - $graphic = 'alerts/error.png'; - $alt = _("Error"); - $msg = array( - _("ERROR: Your message could not be delivered."), - _("The mail server generated the following error message:") - ); - $detail_msg = _("Additional message error details can be viewed %s."); - $detail_msg_status = _("Additional message error details"); - $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': - $graphic = 'alerts/success.png'; - $alt = _("Success"); - $msg = array( - _("Your message was successfully delivered."), - _("The mail server generated the following message:") - ); - $detail_msg = _("Additional message details can be viewed %s."); - $detail_msg_status = _("Additional message details"); - $msg_link = _("The text of the message can be viewed %s."); - $msg_link_status = _("The text of the message"); - break; - - default: - $graphic = ''; - $alt = ''; - $msg = ''; - $detail_msg = ''; - $detail_msg_status = ''; - $msg_link = ''; - $msg_link_status = ''; - } - - /* Print the human readable message. */ - $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(1)); - if (empty($part)) { - return $this->_errorMsg($contents); - } - - $statusimg = Horde::img($graphic, $alt, 'height="16" width="16"', $registry->getImageDir('horde')); - $text = $this->formatStatusMsg($msg, $statusimg); - $text .= '
' . $contents->renderMIMEPart($part) . '
' . "\n"; - - /* Display a link to more detailed error message. */ - $detailed_msg = array( - sprintf($detail_msg, $contents->linkViewJS($part2, 'view_attach', _("HERE"), $detail_msg_status)) - ); - - /* Display a link to the returned message. Try to download the - text of the message/rfc822 part first, if it exists. - TODO: Retrieving by part ID 3.0 is deprecated. Remove this once - Horde 4.0 is released. */ - if (($part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(3))) || - ($part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId('3.0')))) { - $detailed_msg[] = sprintf($msg_link, $contents->linkViewJS($part, 'view_attach', _("HERE"), $msg_link_status, null, array('ctype' => 'message/rfc822'))); - } - - $infoimg = Horde::img('info_icon.png', _("Info"), 'height="16" width="16"', $registry->getImageDir('horde')); - $text .= $this->formatStatusMsg($detailed_msg, $infoimg, false); - - return $text; - } - - - /** - * Return the content-type. - * - * @return string The content-type of the message. - */ - public function getType() - { - return 'text/html; charset=' . NLS::getCharset(); - } - - /** - * Returns an error string for a malformed RFC 3464 message. - * - * @param MIME_Contents &$contents The MIME_Contents object for this - * message. - * - * @return string The error message string. - */ - protected function _errorMsg(&$contents) - { - $err_msg = array( - _("This message contains mail delivery status information, but the format of this message is unknown."), - _("Below is the raw text of the status information message.") - ); - $img = Horde::img('alerts/error.png', _("Error"), 'height="16" width="16"', $GLOBALS['registry']->getImageDir('horde')); - - $text = $this->formatStatusMsg($err_msg, $img); - - /* There is currently no BC way to obtain all parts from a message - * and display the summary of each part. So instead, display the - * entire raw contents of the message. See Bug 3757. */ - $text .= '
';
-        if (is_a($contents, 'IMP_Contents')) {
-            $contents->rebuildMessage();
-            $message = $contents->getMIMEMessage();
-            $text .= $contents->toString($message, true);
-        } else {
-            $text .= htmlspecialchars($this->mime_part->getContents());
-        }
-
-        return $text . '
'; - } - -} diff --git a/imp/lib/MIME/Viewer/tnef.php b/imp/lib/MIME/Viewer/tnef.php deleted file mode 100644 index 2f3061579..000000000 --- a/imp/lib/MIME/Viewer/tnef.php +++ /dev/null @@ -1,136 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_tnef extends Horde_Mime_Viewer_tnef -{ - /** - * The contentType of the attachment. - * - * @var string - */ - protected $_contentType = 'application/octet-stream'; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string Either the list of tnef files or the data of an - * individual tnef file. - */ - public function render($params) - { - $contents = &$params[0]; - - $text = ''; - - /* Get the data from the attachment. */ - $tnefData = $this->_getSubparts(); - - /* Display the requested file. Its position in the $tnefData - array can be found in 'tnef_attachment'. */ - if (Util::getFormData('tnef_attachment')) { - $tnefKey = Util::getFormData('tnef_attachment') - 1; - /* Verify that the requested file exists. */ - if (isset($tnefData[$tnefKey])) { - $text = $tnefData[$tnefKey]['stream']; - if (empty($text)) { - $text = $this->formatStatusMsg(_("Could not extract the requested file from the MS-TNEF attachment.")); - } else { - $this->mime_part->setName($tnefData[$tnefKey]['name']); - $this->mime_part->setType($tnefData[$tnefKey]['type'] . '/' . $tnefData[$tnefKey]['subtype']); - } - } else { - $text = $this->formatStatusMsg(_("The requested file does not exist in the MS-TNEF attachment.")); - } - } else { - $text = $this->renderAttachmentInfo(array($params[0])); - } - - return $text; - } - - /** - * Render out TNEF attachment information. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string The rendered text in HTML. - */ - public function renderAttachmentInfo($params) - { - $contents = &$params[0]; - - $text = ''; - - /* Make sure the contents are in the MIME_Part object. */ - if (!$this->mime_part->getContents()) { - $this->mime_part->setContents($contents->getBodyPart($this->mime_part->getMIMEId())); - } - - /* Get the data from the attachment. */ - $tnefData = $this->_getSubparts(); - - if (!count($tnefData)) { - $text = $this->formatStatusMsg(_("No attachments found.")); - } else { - $text = $this->formatStatusMsg(_("The following files were attached to this part:")) . '
'; - foreach ($tnefData as $key => $data) { - $temp_part = $this->mime_part; - $temp_part->setName($data['name']); - $temp_part->setDescription($data['name']); - - /* Short-circuit MIME-type guessing for winmail.dat parts; - we're showing enough entries for them already. */ - $type = $data['type'] . '/' . $data['subtype']; - if (($type == 'application/octet-stream') || - ($type == 'application/base64')) { - $type = Horde_Mime_Magic::filenameToMIME($data['name']); - } - $temp_part->setType($type); - - $link = $contents->linkView($temp_part, 'view_attach', htmlspecialchars($data['name']), array('jstext' => sprintf(_("View %s"), $data['name']), 'viewparams' => array('tnef_attachment' => ($key + 1)))); - $text .= _("Attached File:") . '  ' . $link . '  (' . $data['type'] . '/' . $data['subtype'] . ")
\n"; - } - } - - return $text; - } - - /** - * List any embedded attachments in the TNEF part. - * - * @return array An array of any embedded attachments. - */ - protected function _getSubparts() - { - $tnef = &Horde_Compress::singleton('tnef'); - return $tnef->decompress($this->mime_part->transferDecode()); - } - - /** - * Return the content-type. - * - * @return string The content-type of the output. - */ - public function getType() - { - if (Util::getFormData('tnef_attachment')) { - return $this->_contentType; - } else { - return 'text/html; charset=' . NLS::getCharset(); - } - } -} diff --git a/imp/lib/MIME/Viewer/zip.php b/imp/lib/MIME/Viewer/zip.php deleted file mode 100644 index a2faf0111..000000000 --- a/imp/lib/MIME/Viewer/zip.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @author Michael Slusarz - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_zip extends Horde_Mime_Viewer_zip -{ - /** - * The IMP_Contents object, needed for the _callback() function. - * - * @var IMP_Contents - */ - protected $_contents; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. - * - * @return string Either the list of zip files or the data of an - * individual zip file. - */ - public function render($params) - { - $contents = &$params[0]; - - $data = $this->mime_part->getContents(); - $text = ''; - - /* Send the requested file. Its position in the zip archive is located - * in 'zip_attachment'. */ - if (Util::getFormData('zip_attachment')) { - $zip = &Horde_Compress::singleton('zip'); - $fileKey = Util::getFormData('zip_attachment') - 1; - $zipInfo = $zip->decompress( - $data, array('action' => HORDE_COMPRESS_ZIP_LIST)); - /* Verify that the requested file exists. */ - if (isset($zipInfo[$fileKey])) { - $text = $zip->decompress( - $data, - array('action' => HORDE_COMPRESS_ZIP_DATA, - 'info' => &$zipInfo, - 'key' => $fileKey)); - if (empty($text)) { - $text = '
' . _("Could not extract the requested file from the Zip archive.") . '
'; - } else { - $this->mime_part->setType('application/octet-stream'); - $this->mime_part->setName(basename($zipInfo[$fileKey]['name'])); - } - } else { - $text = '
' . _("The requested file does not exist in the Zip attachment.") . '
'; - } - } else { - $this->_contents = $contents; - $text = parent::_render($data, array($this, '_callback')); - } - - return $text; - } - - /** - * The function to use as a callback to parent::_render(). - * - * @param integer $key The position of the file in the zip archive. - * @param array $val The information array for the archived file. - * - * @return string The content-type of the output. - */ - protected function _callback($key, $val) - { - $name = preg_replace('/( )+$/', '', $val['name']); - if (!empty($val['size']) && (strstr($val['attr'], 'D') === false) && - ((($val['method'] == 0x8) && Util::extensionExists('zlib')) || - ($val['method'] == 0x0))) { - $old_name = $this->mime_part->getName(); - $this->mime_part->setName(basename($name)); - $val['name'] = str_replace( - $name, - $this->_contents->linkView( - $this->mime_part, 'download_render', $name, - array('jstext' => sprintf(_("View %s"), - str_replace(' ', ' ', $name)), - 'class' => 'fixed', - 'viewparams' => array( - 'ctype' => 'application/zip', - 'zip_attachment' => (urlencode($key) + 1)))), - $val['name']); - $this->mime_part->setName($old_name); - } - - return $val; - } -} diff --git a/imp/lib/Mime/Viewer/alternative.php b/imp/lib/Mime/Viewer/alternative.php new file mode 100644 index 000000000..83844e809 --- /dev/null +++ b/imp/lib/Mime/Viewer/alternative.php @@ -0,0 +1,128 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_alternative extends Horde_Mime_Viewer_Driver +{ + /** + * The content-type of the preferred part. + * Default: application/octet-stream + * + * @var string + */ + protected $_contentType = 'application/octet-stream'; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + $contents = &$params[0]; + + $display_id = null; + $summaryList = array(); + $text = ''; + + /* Look for a displayable part. + * RFC 2046: We show the LAST choice that can be displayed inline. */ + $partList = $this->mime_part->getParts(); + foreach ($partList as $part) { + if ($contents->canDisplayInline($part)) { + $text = $contents->renderMIMEPart($part); + $this->_contentType = $part->getType(); + $display_id = $part->getMIMEId(); + } + } + + /* Show links to alternative parts. */ + if (($text === null) || (count($partList) > 1)) { + if ($text === null) { + $text = '' . _("There are no alternative parts that can be displayed.") . ''; + } + + /* Generate the list of summaries to use. */ + foreach ($partList as $part) { + $id = $part->getMIMEId(); + if ($id && $id != $display_id) { + $summary = $contents->partSummary($part); + /* We don't want to show the MIME ID for alt parts. */ + if (!empty($summary)) { + array_splice($summary, 1, 1); + $summaryList[] = $summary; + } + } + } + + /* Make sure there is at least one summary before showing the + * alternative parts. */ + $alternative_display = $GLOBALS['prefs']->getValue('alternative_display'); + if (!empty($summaryList) && + !$this->viewAsAttachment() && + $alternative_display != 'none') { + $status_array = array(); + $status = _("Alternative parts for this section:"); + if ($contents->showSummaryLinks()) { + require_once 'Horde/Help.php'; + $status .= '  ' . Help::link('imp', 'alternative-msg'); + } + $status_array[] = $status; + $status = ''; + foreach ($summaryList as $summary) { + $status .= ''; + foreach ($summary as $val) { + if (!empty($val)) { + $status .= "\n"; + } + } + $status .= "\n"; + } + $status .= '
$val 
'; + $status_array[] = $status; + $status_msg = $this->formatStatusMsg($status_array, Horde::img('mime/binary.png', _("Multipart/alternative"), null, $GLOBALS['registry']->getImageDir('horde')), false); + switch ($alternative_display) { + case 'above': + $text = $status_msg . $text; + break; + + case 'below': + $text .= $status_msg; + break; + } + } + } + + /* Remove attachment information for the displayed part if we + * can. */ + if (is_callable(array($contents, 'removeAtcEntry'))) { + $contents->removeAtcEntry($this->mime_part->getMIMEId()); + } + + return $text; + } + + /** + * Return the content-type. + * + * @return string The content-type of the message. + * Returns 'application/octet-stream' until actual + * content type of the message can be determined. + */ + public function getType() + { + return $this->_contentType; + } +} diff --git a/imp/lib/Mime/Viewer/appledouble.php b/imp/lib/Mime/Viewer/appledouble.php new file mode 100644 index 000000000..7b89bb65a --- /dev/null +++ b/imp/lib/Mime/Viewer/appledouble.php @@ -0,0 +1,73 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_appledouble extends Horde_Mime_Viewer_Driver +{ + /** + * Force viewing of a part inline, regardless of the Content-Disposition + * of the MIME Part. + * + * @var boolean + */ + protected $_forceinline = true; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + $contents = &$params[0]; + $text = ''; + + /* RFC 1740 [4]: There are two parts to an appledouble message: + (1) application/applefile + (2) Data embedded in the Mac file */ + + /* Display the macintosh download link. */ + $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(1)); + if ($part) { + $status = array( + _("This message contains a Macintosh file."), + sprintf(_("The Macintosh resource fork can be downloaded %s."), $contents->linkViewJS($part, 'download_attach', _("HERE"), _("The Macintosh resource fork"))), + _("The contents of the Macintosh file are below.") + ); + $text .= $this->formatStatusMsg($status, Horde::img('apple.png', _("Macintosh File")), false); + } + + /* Display the content of the file. */ + $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(2)); + if ($part) { + $mime_message = Horde_Mime_Message::convertMIMEPart($part); + $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$contents)); + $mc->buildMessage(); + $text .= '' . $mc->getMessage(true) . '
'; + } + + return $text; + } + + /** + * Return the content-type. + * + * @return string The content-type of the message. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/imp/lib/Mime/Viewer/enriched.php b/imp/lib/Mime/Viewer/enriched.php new file mode 100644 index 000000000..d3e4cc1b9 --- /dev/null +++ b/imp/lib/Mime/Viewer/enriched.php @@ -0,0 +1,66 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_enriched extends Horde_Mime_Viewer_enriched +{ + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + $contents = &$params[0]; + + global $prefs; + + if (($text = $this->mime_part->getContents()) === false) { + return $this->formatPartError(_("There was an error displaying this message part")); + } + + if (trim($text) == '') { + return $text; + } + + $text = parent::render(); + + // Highlight quoted parts of an email. + if ($prefs->getValue('highlight_text')) { + $text = implode("\n", preg_replace('|^(\s*>.+)$|', '\1', explode("\n", $text))); + $indent = 1; + while (preg_match('|>(\s?>){' . $indent . '}|', $text)) { + $text = implode("\n", preg_replace('|^(\s*>(\s?>){' . $indent . '}.+)$|', '\1', explode("\n", $text))); + $indent++; + } + } + + // Dim signatures. + if ($prefs->getValue('dim_signature')) { + $parts = preg_split('|(\n--\s*\n)|', $text, 2, PREG_SPLIT_DELIM_CAPTURE); + $text = array_shift($parts); + if (count($parts)) { + $text .= '' . $parts[0] . + preg_replace('|class="[^"]+"|', 'class="signature-fixed"', $parts[1]) . + ''; + } + } + + // Filter bad language. + $text = IMP::filterText($text); + + return $text; + } +} diff --git a/imp/lib/Mime/Viewer/html.php b/imp/lib/Mime/Viewer/html.php new file mode 100644 index 000000000..b98565cd4 --- /dev/null +++ b/imp/lib/Mime/Viewer/html.php @@ -0,0 +1,275 @@ + + * @author Jon Parise + * @author Michael Slusarz + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_html extends Horde_Mime_Viewer_html +{ + /** + * Cached block image. + * + * @var string + */ + protected $_blockimg = null; + + /** + * The regular expression to catch any tags and attributes that load + * external images. + * + * @var string + */ + protected $_img_regex = '/ + # match 1 + ( + # tags + ]+src= + # tags + |]*src= + # "background" attributes + |]*background=|]*background=|]*background= + # "style" attributes; match 2; quotes: match 3 + |(style=\s*("|\')?[^>]*background(?:-image)?:(?(3)[^"\']|[^>])*?url\s*\() + ) + # whitespace + \s* + # opening quotes, parenthesis; match 4 + ("|\')? + # the image url; match 5 + ((?(2) + # matched a "style" attribute + (?(4)[^"\')>]*|[^\s)>]*) + # did not match a "style" attribute + |(?(4)[^"\'>]*|[^\s>]*) + )) + # closing quotes + (?(4)\\4) + # matched a "style" attribute? + (?(2) + # closing parenthesis + \s*\) + # remainder of the "style" attribute; match 5 + ((?(3)[^"\'>]*|[^\s>]*)) + ) + /isx'; + + /** + * TODO + */ + protected function _render() + { + $render = $this->_IMPrender(false); + + return array( + 'data' => $render['html'], + 'status' => $render['status'], + 'type' => $this->_mimepart->getType(true) + ); + } + + /** + * TODO + */ + protected function _renderInline() + { + $render = $this->_IMPrender(true); + + return array( + 'data' => $render['html'], + 'status' => $render['status'] + ); + } + + /** + * Render out the currently set contents. + * + * @param boolean $inline Are we viewing inline? + * + * @return array Two elements: html and status. + */ + protected function _IMPrender($inline) + { + $data = $this->_mimepart->getContents(); + $charset = NLS::getCharset(); + $msg_charset = $this->_mimepart->getCharset(); + + if ($inline) { + $data = String::convertCharset($data, $msg_charset); + $msg_charset = $charset; + } + + /* Run tidy on the HTML. */ + if ($this->getConfigParam('tidy') && + ($tidy_config = IMP::getTidyConfig(String::length($data)))) { + if ($msg_charset == 'us-ascii') { + $tidy = tidy_parse_string($data, $tidy_config, 'ascii'); + $tidy->cleanRepair(); + $data = tidy_get_output($tidy); + } else { + $tidy = tidy_parse_string(String::convertCharset($data, $msg_charset, 'UTF-8'), $tidy_config, 'utf8'); + $tidy->cleanRepair(); + $data = String::convertCharset(tidy_get_output($tidy), 'UTF-8', $msg_charset); + } + } + + /* Sanitize the HTML. */ + $cleanhtml = $this->_cleanHTML($data, $inline); + $data = $cleanhtml['html']; + + /* Reset absolutely positioned elements. */ + if ($inline) { + $data = preg_replace('/(style\s*=\s*)(["\'])?([^>"\']*)position\s*:\s*absolute([^>"\']*)\2/i', '$1"$3$4"', $data); + } + + /* Search for inlined images that we can display. */ + // TODO + $related = $this->_mimepart->getInformation('related_part'); + if ($related !== false) { + $relatedPart = $this->_params['contents']->getMIMEPart($related); + foreach ($relatedPart->getCIDList() as $ref => $id) { + $id = trim($id, '<>'); + $cid_part = $this->_params['contents']->getDecodedMIMEPart($ref); + $data = str_replace("cid:$id", $this->_params['contents']->urlView($cid_part, 'view_attach'), $data); + } + } + + /* Convert links to open in new windows. First we hide all + * mailto: links, links that have an "#xyz" anchor and ignore + * all links that already have a target. */ + $data = preg_replace( + array('/]*\s*href=["\']?(#|mailto:))/i', + '/]*)\s*target=["\']?[^>"\'\s]*["\']?/i', + '/]*\s*href=["\']?(#|mailto:))/i', + '/]*)\s*target=["\']?[^>"\'\s]*["\']?/i', + '/hasMethod('mail/compose')) { + $data = preg_replace_callback('/href\s*=\s*(["\'])?mailto:((?(1)[^\1]*?|[^\s>]+))(?(1)\1|)/i', array($this, '_mailtoCallback'), $data); + } + + /* Filter bad language. */ + $data = IMP::filterText($data); + + if ($inline) { + /* Put div around message. */ + $data = '
' . $data . '
'; + } + + /* Only display images if specifically allowed by user. */ + $msg = $script = ''; + if (!IMP::printMode() && + $GLOBALS['prefs']->getValue('html_image_replacement') && + preg_match($this->_img_regex, $data)) { + /* Make sure the URL parameters are correct for the current + * message. */ + $url = Util::removeParameter(Horde::selfUrl(true), array('index')); + if ($inline) { + $url = Util::removeParameter($url, array('actionID')); + } + $url = Util::addParameter($url, 'index', $this->_params['contents']->getMessageIndex()); + + $view_img = Util::getFormData('view_html_images'); + $addr_check = ($GLOBALS['prefs']->getValue('html_image_addrbook') && $this->_inAddressBook()); + + if (!$view_img && !$addr_check) { + $script = Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true) . + Util::bufferOutput(array('Horde', 'addScriptFile'), 'unblockImages.js', 'imp', true); + + $url = Util::addParameter($url, 'view_html_images', 1); + $attributes = $inline ? array() : array('style' => 'color:blue'); + $msg = Horde::img('mime/image.png') . ' ' . String::convertCharset(_("Images have been blocked to protect your privacy."), $charset, $msg_charset) . ' ' . Horde::link($url, '', '', '', 'return IMP.unblockImages(' . (!$inline ? 'document.body' : '$(\'html-message\')') . ', \'block-images\');', '', '', $attributes) . String::convertCharset(_("Show Images?"), $charset, $msg_charset) . ''; + $data = preg_replace_callback($this->_img_regex, array($this, '_blockImages'), $data); + if (!$inline) { + $msg = '' . nl2br($msg) . '
'; + } + $msg = '' . $msg . ''; + } + } + + /* If we are viewing inline, give option to view in separate window. */ + if ($inline && $this->getConfigParam('external')) { + $cleanhtml['status'][] = array( + 'data' => $this->_params['contents']->linkViewJS($this->mime_part, 'view_attach', _("Show this HTML in a new window?")), + 'type' => 'info' + ); + } + + return array( + 'html' => $data, + 'status' => $cleanhtml['status'] + ); + } + + /** + * TODO + */ + protected function _mailtoCallback($m) + { + return 'href="' . $GLOBALS['registry']->call('mail/compose', array(String::convertCharset(html_entity_decode($m[2]), 'ISO-8859-1', NLS::getCharset()))) . '"'; + } + + /** + * Called from the image-blocking regexp to construct the new image tags. + * + * @param array $matches + * + * @return string The new image tag. + */ + protected function _blockImages($matches) + { + if (is_null($this->_blockimg)) { + $this->_blockimg = Horde::url($GLOBALS['registry']->getImageDir('imp') . '/spacer_red.png', false, -1); + } + + return empty($matches[2]) + ? $matches[1] . '"' . $this->_blockimg . '" blocked="' . rawurlencode(str_replace('&', '&', trim($matches[5], '\'" '))) . '"' + : $matches[1] . "'" . $this->_blockimg . '\')' . $matches[6] . '" blocked="' . rawurlencode(str_replace('&', '&', trim($matches[5], '\'" '))); + } + + /** + * Determine whether the sender appears in an available addressbook. + * + * @return boolean Does the sender appear in an addressbook? + */ + protected function _inAddressBook() + { + /* If we don't have a contacts provider available, give up. */ + if (!$GLOBALS['registry']->hasMethod('contacts/getField')) { + return false; + } + + $params = IMP_Compose::getAddressSearchParams(); + + // TODO + // get mime_message. + + /* Try to get back a result from the search. */ + $result = $GLOBALS['registry']->call('contacts/getField', array($base_ob->getFromAddress(), '__key', $params['sources'], false, true)); + + return is_a($result, 'PEAR_Error') + ? false + : (count($result) > 0); + } +} diff --git a/imp/lib/Mime/Viewer/images.php b/imp/lib/Mime/Viewer/images.php new file mode 100644 index 000000000..a8c787204 --- /dev/null +++ b/imp/lib/Mime/Viewer/images.php @@ -0,0 +1,265 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_images extends Horde_Mime_Viewer_images +{ + /** + * The content-type of the generated data. + * + * @var string + */ + protected $_contentType; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered information. + */ + public function render($params) + { + $contents = $params[0]; + + global $browser; + + /* If calling page is asking us to output data, do that without any + * further delay and exit. */ + if (Util::getFormData('img_data')) { + return parent::render(); + } + + /* Convert the image to browser-viewable format and display. */ + if (Util::getFormData('images_view_convert')) { + return $this->_viewConvert(); + } + + /* Create the thumbnail and display. */ + if (Util::getFormData('images_view_thumbnail')) { + return $this->_viewConvert(true); + } + + if (Util::getFormData('images_load_convert')) { + return $this->_popupImageWindow(); + } + + if ($this->viewAsAttachment()) { + if (!$browser->hasFeature('javascript')) { + /* If the browser doesn't support javascript then simply + render the image data. */ + return parent::render(); + } elseif ($browser->isViewable(parent::getType())) { + /* The browser can display the image type directly - just + output the javascript code to render the auto resize popup + image window. */ + return $this->_popupImageWindow(); + } + } + + if ($browser->isViewable($this->mime_part->getType())) { + /* If we are viewing inline, and the browser can handle the image + type directly, output an tag to load the image. */ + $alt = $this->mime_part->getName(false, true); + return Horde::img($contents->urlView($this->mime_part, 'view_attach'), $alt, null, ''); + } else { + /* If we have made it this far, than the browser cannot view this + image inline. Inform the user of this and, possibly, ask user + if we should convert to another image type. */ + $msg = _("Your browser does not support inline display of this image type."); + + if ($this->viewAsAttachment()) { + $msg .= '
' . sprintf(_("Click %s to download the image."), $contents->linkView($this->mime_part, 'download_attach', _("HERE"), array('viewparams' => array('img_data' => 1)), true)); + } + + /* See if we can convert to an inline browser viewable form. */ + $img = $this->_getHordeImageOb(false); + if ($img && $browser->isViewable($img->getContentType())) { + if ($this->viewAsAttachment()) { + $convert_link = Horde::link($contents->urlView($this->mime_part, 'view_attach', array('images_load_convert' => 1))) . _("HERE") . ''; + } else { + $convert_link = $contents->linkViewJS($this->mime_part, 'view_attach', _("HERE"), null, null, array('images_load_convert' => 1)); + } + $msg .= '
' . sprintf(_("Click %s to convert the image file into a format your browser can view."), $convert_link); + } + + $this->_contentType = 'text/html; charset=' . NLS::getCharset(); + return $this->formatStatusMsg($msg); + } + } + + /** + * Return the content-type + * + * @return string The content-type of the output. + */ + public function getType() + { + return ($this->_contentType) ? $this->_contentType : parent::getType(); + } + + /** + * Render out attachment information. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function renderAttachmentInfo($params) + { + $contents = &$params[0]; + + /* Display the thumbnail link only if we show thumbs for all images or + if image is over 50 KB. */ + if (!$this->getConfigParam('allthumbs') && + ($this->mime_part->getBytes() < 51200)) { + return ''; + } + + if (is_a($contents, 'IMP_Contents')) { + $this->mime_part = &$contents->getDecodedMIMEPart($this->mime_part->getMIMEId(), true); + } + + /* Check to see if convert utility is available. */ + if (!$this->_getHordeImageOb(false)) { + return ''; + } + + $status = array( + sprintf(_("An image named %s is attached to this message. A thumbnail is below."), + $this->mime_part->getName(true)), + ); + + if (!$GLOBALS['browser']->hasFeature('javascript')) { + $status[] = Horde::link($contents->urlView($this->mime_part, + 'view_attach')) . + Horde::img($contents->urlView($this->mime_part, + 'view_attach', array('images_view_thumbnail' => 1), false), + _("View Attachment"), null, '') . ''; + } else { + $status[] = $contents->linkViewJS($this->mime_part, 'view_attach', + Horde::img($contents->urlView($this->mime_part, + 'view_attach', array('images_view_thumbnail' => 1), + false), _("View Attachment"), null, ''), null, null, + null); + } + + return $this->formatStatusMsg($status, Horde::img('mime/image.png', + _("Thumbnail of attached image"), null, $GLOBALS['registry']->getImageDir('horde')), false); + } + + /** + * Generate the HTML output for the JS auto-resize view window. + * + * @return string The HTML output. + */ + protected function _popupImageWindow() + { + $params = $remove_params = array(); + if (Util::getFormData('images_load_convert')) { + $params['images_view_convert'] = 1; + $remove_params[] = 'images_load_convert'; + } else { + $params['img_data'] = 1; + } + $self_url = Util::addParameter(Util::removeParameter(Horde::selfUrl(true), $remove_params), $params); + $title = MIME::decode($this->mime_part->getName(false, true)); + $this->_contentType = 'text/html; charset=' . NLS::getCharset(); + return parent::_popupImageWindow($self_url, $title); + } + + /** + * View thumbnail sized image. + * + * @param boolean $thumb View thumbnail size? + * + * @return string The image data. + */ + protected function _viewConvert($thumb = false) + { + $mime = $this->mime_part; + $img = $this->_getHordeImageOb(); + + if ($img) { + if ($thumb) { + $img->resize(96, 96, true); + } + $type = $img->getContentType(); + $data = $img->raw(true); + } + + if (!$img || !$data) { + $type = 'image/png'; + $data = file_get_contents(IMP_BASE . '/themes/graphics/mini-error.png'); + } + + $mime->setType($type); + $this->_contentType = $type; + $mime->setContents($data); + + return $mime->getContents(); + } + + /** + * Return a Horde_Image object. + * + * @param boolean $load Whether to load the image data. + * + * @return Horde_Image The requested object. + */ + protected function _getHordeImageOb($load = true) + { + include_once 'Horde/Image.php'; + $params = array('temp' => Horde::getTempdir()); + if (!empty($GLOBALS['conf']['image']['convert'])) { + $img = &Horde_Image::singleton('im', $params); + } elseif (Util::extensionExists('gd')) { + $img = &Horde_Image::singleton('gd', $params); + } else { + return false; + } + + if (is_a($img, 'PEAR_Error')) { + return false; + } + + if ($load) { + $ret = $img->loadString(1, $this->mime_part->getContents()); + if (is_a($ret, 'PEAR_Error')) { + return false; + } + } + + return $img; + } + + /** + * Can this driver render the the data inline? + * + * @return boolean True if the driver can display inline. + */ + public function canDisplayInline() + { + /* Only display the image inline if the configuration parameter is set, + the browser can actually display it, and the size of the image is + small. */ + global $browser; + if (($this->getConfigParam('inline')) && ($browser->isViewable($this->mime_part->getType())) && + ($this->mime_part->getBytes() < 51200)) { + return true; + } else { + return false; + } + } +} diff --git a/imp/lib/Mime/Viewer/itip.php b/imp/lib/Mime/Viewer/itip.php new file mode 100644 index 000000000..6d7ebdaaa --- /dev/null +++ b/imp/lib/Mime/Viewer/itip.php @@ -0,0 +1,978 @@ + + * @author Mike Cochrane + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_itip extends Horde_Mime_Viewer_Driver +{ + /** + * Force viewing of a part inline, regardless of the Content-Disposition + * of the MIME Part. + * + * @var boolean + */ + protected $_forceinline = true; + + /** + * The messages to output to the user. + * + * @var array + */ + protected $_msgs = array(); + + /** + * The method as marked in either the iCal structure or message header. + * + * @var string + */ + protected $_method = 'PUBLISH'; + + /** + * The headers of the message. + * + * @var string + */ + protected $_headers; + + /** + * Render out the currently set iCalendar contents. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + global $registry; + + // Extract the data. + $data = $this->mime_part->getContents(); + if (empty($this->_headers) && is_a($params[0], 'IMP_Contents')) { + $this->_headers = $params[0]->getHeaderOb(); + } + + // Parse the iCal file. + $vCal = new Horde_iCalendar(); + if (!$vCal->parsevCalendar($data, 'VCALENDAR', $this->mime_part->getCharset())) { + return '

' . _("The calendar data is invalid") . '

' . + '
' . htmlspecialchars($data) . '
'; + } + + // Check if we got vcard data with the wrong vcalendar mime type. + $c = $vCal->getComponentClasses(); + if (count($c) == 1 && !empty($c['horde_icalendar_vcard'])) { + $vcard_renderer = &Horde_Mime_Viewer::factory($this->mime_part, 'text/x-vcard'); + return $vcard_renderer->render($params); + } + + // Get the method type. + $this->_method = $vCal->getAttribute('METHOD'); + if (is_a($this->_method, 'PEAR_Error')) { + $this->_method = ''; + } + + // Get the iCalendar file components. + $components = $vCal->getComponents(); + + // Handle the action requests. + $actions = Util::getFormData('action', array()); + foreach ($actions as $key => $action) { + $this->_msgs[$key] = array(); + switch ($action) { + case 'delete': + // vEvent cancellation. + if ($registry->hasMethod('calendar/delete')) { + $guid = $components[$key]->getAttribute('UID'); + $event = $registry->call('calendar/delete', array('guid' => $guid)); + if (is_a($event, 'PEAR_Error')) { + $this->_msgs[$key][] = array('error', _("There was an error deleting the event:") . ' ' . $event->getMessage()); + } else { + $this->_msgs[$key][] = array('success', _("Event successfully deleted.")); + } + } else { + $this->_msgs[$key][] = array('warning', _("This action is not supported.")); + } + break; + + case 'update': + // vEvent reply. + if ($registry->hasMethod('calendar/updateAttendee')) { + $event = $registry->call('calendar/updateAttendee', array('response' => $components[$key], 'sender' => $params[0]->getFromAddress())); + if (is_a($event, 'PEAR_Error')) { + $this->_msgs[$key][] = array('error', _("There was an error updating the event:") . ' ' . $event->getMessage()); + } else { + $this->_msgs[$key][] = array('success', _("Respondent Status Updated.")); + } + } else { + $this->_msgs[$key][] = array('warning', _("This action is not supported.")); + } + break; + + case 'import': + case 'accept-import': + // vFreebusy reply. + // vFreebusy publish. + // vEvent request. + // vEvent publish. + // vTodo publish. + // vJournal publish. + switch ($components[$key]->getType()) { + case 'vEvent': + $handled = false; + $guid = $components[$key]->getAttribute('UID'); + // Check if this is an update. + if ($registry->hasMethod('calendar/export') && + !is_a($registry->call('calendar/export', array($guid, 'text/calendar')), 'PEAR_Error')) { + // Update in Kronolith. + if ($registry->hasMethod('calendar/replace')) { + $handled = true; + $result = $registry->call('calendar/replace', array('uid' => $guid, 'content' => $components[$key], 'contentType' => $this->mime_part->getType())); + if (is_a($result, 'PEAR_Error')) { + $this->_msgs[$key][] = array('error', _("There was an error updating the event:") . ' ' . $result->getMessage()); + } else { + $url = Horde::url($registry->link('calendar/show', array('uid' => $guid))); + $this->_msgs[$key][] = array('success', _("The event was updated in your calendar.") . + ' ' . Horde::link($url, _("View event"), null, '_blank') . Horde::img('mime/icalendar.png', _("View event"), null, $registry->getImageDir('horde')) . ''); + } + } + } else { + // Import into Kronolith. + if ($registry->hasMethod('calendar/import')) { + $handled = true; + $guid = $registry->call('calendar/import', array('content' => $components[$key], 'contentType' => $this->mime_part->getType())); + if (is_a($guid, 'PEAR_Error')) { + $this->_msgs[$key][] = array('error', _("There was an error importing the event:") . ' ' . $guid->getMessage()); + } else { + $url = Horde::url($registry->link('calendar/show', array('uid' => $guid))); + $this->_msgs[$key][] = array('success', _("The event was added to your calendar.") . + ' ' . Horde::link($url, _("View event"), null, '_blank') . Horde::img('mime/icalendar.png', _("View event"), null, $registry->getImageDir('horde')) . ''); + } + } + } + if (!$handled) { + $this->_msgs[$key][] = array('warning', _("This action is not supported.")); + } + break; + + case 'vFreebusy': + // Import into Kronolith. + if ($registry->hasMethod('calendar/import_vfreebusy')) { + $res = $registry->call('calendar/import_vfreebusy', array($components[$key])); + if (is_a($res, 'PEAR_Error')) { + $this->_msgs[$key][] = array('error', _("There was an error importing user's free/busy information:") . ' ' . $res->getMessage()); + } else { + $this->_msgs[$key][] = array('success', _("The user's free/busy information was sucessfully stored.")); + } + } else { + $this->_msgs[$key][] = array('warning', _("This action is not supported.")); + } + break; + + case 'vTodo': + // Import into Nag. + if ($registry->hasMethod('tasks/import')) { + $guid = $registry->call('tasks/import', array($components[$key], $this->mime_part->getType())); + if (is_a($guid, 'PEAR_Error')) { + $this->_msgs[$key][] = array('error', _("There was an error importing the task:") . ' ' . $guid->getMessage()); + } else { + $url = Horde::url($registry->link('tasks/show', array('uid' => $guid))); + $this->_msgs[$key][] = array('success', _("The task has been added to your tasklist.") . + ' ' . Horde::link($url, _("View task"), null, '_blank') . Horde::img('mime/icalendar.png', _("View task"), null, $registry->getImageDir('horde')) . ''); + } + } else { + $this->_msgs[$key][] = array('warning', _("This action is not supported.")); + } + break; + + case 'vJournal': + default: + $this->_msgs[$key][] = array('warning', _("This action is not yet implemented.")); + } + + if ($action != 'accept-import') { + break; + } + + case 'accept': + case 'accept-import': + case 'deny': + case 'tentative': + // vEvent request. + if (isset($components[$key]) && + $components[$key]->getType() == 'vEvent') { + $vEvent = $components[$key]; + + // Get the organizer details. + $organizer = $vEvent->getAttribute('ORGANIZER'); + if (is_a($organizer, 'PEAR_Error')) { + break; + } + $organizer = parse_url($organizer); + $organizerEmail = $organizer['path']; + $organizer = $vEvent->getAttribute('ORGANIZER', true); + $organizerName = isset($organizer['cn']) ? $organizer['cn'] : ''; + + require_once 'Horde/Identity.php'; + + // Build the reply. + $vCal = new Horde_iCalendar(); + $vCal->setAttribute('PRODID', '-//The Horde Project//' . HORDE_AGENT_HEADER . '//EN'); + $vCal->setAttribute('METHOD', 'REPLY'); + + $vEvent_reply = &Horde_iCalendar::newComponent('vevent', $vCal); + $vEvent_reply->setAttribute('UID', $vEvent->getAttribute('UID')); + if (!is_a($vEvent->getAttribute('SUMMARY'), 'PEAR_error')) { + $vEvent_reply->setAttribute('SUMMARY', $vEvent->getAttribute('SUMMARY')); + } + if (!is_a($vEvent->getAttribute('DESCRIPTION'), 'PEAR_error')) { + $vEvent_reply->setAttribute('DESCRIPTION', $vEvent->getAttribute('DESCRIPTION')); + } + $dtstart = $vEvent->getAttribute('DTSTART', true); + $vEvent_reply->setAttribute('DTSTART', $vEvent->getAttribute('DTSTART'), array_pop($dtstart)); + if (!is_a($vEvent->getAttribute('DTEND'), 'PEAR_error')) { + $dtend = $vEvent->getAttribute('DTEND', true); + $vEvent_reply->setAttribute('DTEND', $vEvent->getAttribute('DTEND'), array_pop($dtend)); + } else { + $duration = $vEvent->getAttribute('DURATION', true); + $vEvent_reply->setAttribute('DURATION', $vEvent->getAttribute('DURATION'), array_pop($duration)); + } + if (!is_a($vEvent->getAttribute('SEQUENCE'), 'PEAR_error')) { + $vEvent_reply->setAttribute('SEQUENCE', $vEvent->getAttribute('SEQUENCE')); + } + $vEvent_reply->setAttribute('ORGANIZER', $vEvent->getAttribute('ORGANIZER'), array_pop($organizer)); + + // Find out who we are and update status. + $identity = &Identity::singleton(array('imp', 'imp')); + $attendees = $vEvent->getAttribute('ATTENDEE'); + if (!is_array($attendees)) { + $attendees = array($attendees); + } + foreach ($attendees as $attendee) { + $attendee = preg_replace('/mailto:/i', '', $attendee); + if (!is_null($id = $identity->getMatchingIdentity($attendee))) { + $identity->setDefault($id); + break; + } + } + $name = $email = $identity->getFromAddress(); + $params = array(); + $cn = $identity->getValue('fullname'); + if (!empty($cn)) { + $name = $params['CN'] = $cn; + } + + switch ($action) { + case 'accept': + case 'accept-import': + $message = sprintf(_("%s has accepted."), $name); + $subject = _("Accepted: ") . $vEvent->getAttribute('SUMMARY'); + $params['PARTSTAT'] = 'ACCEPTED'; + break; + + case 'deny': + $message = sprintf(_("%s has declined."), $name); + $subject = _("Declined: ") . $vEvent->getAttribute('SUMMARY'); + $params['PARTSTAT'] = 'DECLINED'; + break; + + case 'tentative': + $message = sprintf(_("%s has tentatively accepted."), $name); + $subject = _("Tentative: ") . $vEvent->getAttribute('SUMMARY'); + $params['PARTSTAT'] = 'TENTATIVE'; + break; + } + + $vEvent_reply->setAttribute('ATTENDEE', 'mailto:' . $email, $params); + $vCal->addComponent($vEvent_reply); + + $mime = new Horde_Mime_Part('multipart/alternative'); + $body = new Horde_Mime_Part('text/plain', + String::wrap($message, 76, "\n"), + NLS::getCharset()); + + $ics = new Horde_Mime_Part('text/calendar', $vCal->exportvCalendar()); + $ics->setName('event-reply.ics'); + $ics->setContentTypeParameter('METHOD', 'REPLY'); + $ics->setCharset(NLS::getCharset()); + + $mime->addPart($body); + $mime->addPart($ics); + $mime = &Horde_Mime_Message::convertMimePart($mime); + + // Build the reply headers. + $msg_headers = new Horde_Mime_Headers(); + $msg_headers->addReceivedHeader(); + $msg_headers->addMessageIdHeader(); + $msg_headers->addHeader('Date', date('r')); + $msg_headers->addHeader('From', $email); + $msg_headers->addHeader('To', $organizerEmail); + + $identity->setDefault(Util::getFormData('identity')); + $replyto = $identity->getValue('replyto_addr'); + if (!empty($replyto) && ($replyto != $email)) { + $msg_headers->addHeader('Reply-to', $replyto); + } + $msg_headers->addHeader('Subject', Horde_Mime::encode($subject, NLS::getCharset())); + + // Send the reply. + $status = $mime->send($organizerEmail, $msg_headers, + $GLOBALS['conf']['mailer']['type'], + $GLOBALS['conf']['mailer']['params']); + if (is_a($status, 'PEAR_Error')) { + $this->_msgs[$key][] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage())); + } else { + $this->_msgs[$key][] = array('success', _("Reply Sent.")); + } + } else { + $this->_msgs[$key][] = array('warning', _("This action is not supported.")); + } + break; + + case 'send': + // vEvent refresh. + if (isset($components[$key]) && + $components[$key]->getType() == 'vEvent') { + $vEvent = $components[$key]; + } + + // vTodo refresh. + case 'reply': + case 'reply2m': + // vfreebusy request. + if (isset($components[$key]) && + $components[$key]->getType() == 'vFreebusy') { + $vFb = $components[$key]; + + // Get the organizer details. + $organizer = $vFb->getAttribute('ORGANIZER'); + if (is_a($organizer, 'PEAR_Error')) { + break; + } + $organizer = parse_url($organizer); + $organizerEmail = $organizer['path']; + $organizer = $vFb->getAttribute('ORGANIZER', true); + $organizerName = isset($organizer['cn']) ? $organizer['cn'] : ''; + + if ($action == 'reply2m') { + $startStamp = time(); + $endStamp = $startStamp + (60 * 24 * 3600); + } else { + $startStamp = $vFb->getAttribute('DTSTART'); + if (is_a($startStamp, 'PEAR_Error')) { + $startStamp = time(); + } + $endStamp = $vFb->getAttribute('DTEND'); + if (is_a($endStamp, 'PEAR_Error')) { + $duration = $vFb->getAttribute('DURATION'); + if (is_a($duration, 'PEAR_Error')) { + $endStamp = $startStamp + (60 * 24 * 3600); + } else { + $endStamp = $startStamp + $duration; + } + } + } + $vfb_reply = $registry->call('calendar/getFreeBusy', + array('startStamp' => $startStamp, + 'endStamp' => $endStamp)); + require_once 'Horde/Identity.php'; + + // Find out who we are and update status. + $identity = &Identity::singleton(); + $email = $identity->getFromAddress(); + + // Build the reply. + $vCal = new Horde_iCalendar(); + $vCal->setAttribute('PRODID', '-//The Horde Project//' . HORDE_AGENT_HEADER . '//EN'); + $vCal->setAttribute('METHOD', 'REPLY'); + $vCal->addComponent($vfb_reply); + + $mime = new Horde_Mime_Message(); + $message = _("Attached is a reply to a calendar request you sent."); + $body = new Horde_Mime_Part('text/plain', + String::wrap($message, 76, "\n"), + NLS::getCharset()); + + $ics = new Horde_Mime_Part('text/calendar', $vCal->exportvCalendar()); + $ics->setName('icalendar.ics'); + $ics->setContentTypeParameter('METHOD', 'REPLY'); + $ics->setCharset(NLS::getCharset()); + + $mime->addPart($body); + $mime->addPart($ics); + + // Build the reply headers. + $msg_headers = new Horde_Mime_Headers(); + $msg_headers->addReceivedHeader(); + $msg_headers->addMessageIdHeader(); + $msg_headers->addHeader('Date', date('r')); + $msg_headers->addHeader('From', $email); + $msg_headers->addHeader('To', $organizerEmail); + + $identity->setDefault(Util::getFormData('identity')); + $replyto = $identity->getValue('replyto_addr'); + if (!empty($replyto) && ($replyto != $email)) { + $msg_headers->addHeader('Reply-to', $replyto); + } + $msg_headers->addHeader('Subject', Horde_Mime::encode(_("Free/Busy Request Response"), NLS::getCharset())); + + // Send the reply. + $status = $mime->send($organizerEmail, $msg_headers, + $GLOBALS['conf']['mailer']['type'], + $GLOBALS['conf']['mailer']['params']); + if (is_a($status, 'PEAR_Error')) { + $this->_msgs[$key][] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage())); + } else { + $this->_msgs[$key][] = array('success', _("Reply Sent.")); + } + } else { + $this->_msgs[$key][] = array('warning', _("Invalid Action selected for this component.")); + } + break; + + case 'nosup': + // vFreebusy request. + default: + $this->_msgs[$key][] = array('warning', _("This action is not yet implemented.")); + break; + } + } + + // Create the HTML to display the iCal file. + $html = ''; + if ($this->viewAsAttachment()) { + $html .= Util::bufferOutput('require', $registry->get('templates', 'horde') . '/common-header.inc'); + } + if ($_SESSION['imp']['viewmode'] == 'imp') { + $html .= '
'; + } + + foreach ($components as $key => $component) { + switch ($component->getType()) { + case 'vEvent': + $html .= $this->_vEvent($component, $key); + break; + + case 'vTodo': + $html .= $this->_vTodo($component, $key); + break; + + case 'vTimeZone': + // Ignore them. + break; + + case 'vFreebusy': + $html .= $this->_vFreebusy($component, $key); + break; + + // @todo: handle stray vcards here as well. + default: + $html .= sprintf(_("Unhandled component of type: %s"), $component->getType()); + } + } + + // Need to work out if we are inline and actually need this. + if ($_SESSION['imp']['viewmode'] == 'imp') { + $html .= '
'; + } + if ($this->viewAsAttachment()) { + $html .= Util::bufferOutput('require', $registry->get('templates', 'horde') . '/common-footer.inc'); + } + + return $html; + } + + /** + * Return text/html as the content-type. + * + * @return string "text/html" constant + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } + + /** + * Return the html for a vFreebusy. + */ + protected function _vFreebusy($vfb, $id) + { + global $registry, $prefs; + + $html = ''; + $desc = ''; + $sender = $vfb->getName(); + switch ($this->_method) { + case 'PUBLISH': + $desc = _("%s has sent you free/busy information."); + break; + + case 'REQUEST': + $sender = $this->_headers->getValue('From'); + $desc = _("%s requests your free/busy information."); + break; + + case 'REPLY': + $desc = _("%s has replied to a free/busy request."); + break; + } + + $html .= '

' . sprintf($desc, $sender) . '

'; + + if ($this->_msgs) { + foreach ($this->_msgs[$id] as $msg) { + $html .= '

' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '

'; + } + } + + $start = $vfb->getAttribute('DTSTART'); + if (!is_a($start, 'PEAR_Error')) { + if (is_array($start)) { + $html .= '

' . _("Start") . ': ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '

'; + } else { + $html .= '

' . _("Start") . ': ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '

'; + } + } + + $end = $vfb->getAttribute('DTEND'); + if (!is_a($end, 'PEAR_Error')) { + if (is_array($end)) { + $html .= '

' . _("End") . ': ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '

'; + } else { + $html .= '

' . _("End") . ': ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '

'; + } + } + + if ($_SESSION['imp']['viewmode'] != 'imp') { + return $html; + } + + $html .= '

' . _("Actions") . '

' . + ' '; + } + + /** + * Return the html for a vEvent. + */ + protected function _vEvent($vevent, $id) + { + global $registry, $prefs; + + $html = ''; + $desc = ''; + $sender = $vevent->organizerName(); + $options = array(); + + $attendees = $vevent->getAttribute('ATTENDEE'); + if (!is_a($attendees, 'PEAR_Error') && + !empty($attendees) && + !is_array($attendees)) { + $attendees = array($attendees); + } + $attendee_params = $vevent->getAttribute('ATTENDEE', true); + + switch ($this->_method) { + case 'PUBLISH': + $desc = _("%s wishes to make you aware of \"%s\"."); + if ($registry->hasMethod('calendar/import')) { + $options[] = ''; + } + break; + + case 'REQUEST': + // Check if this is an update. + if ($registry->hasMethod('calendar/export') && + !is_a($registry->call('calendar/export', array($vevent->getAttribute('UID'), 'text/calendar')), 'PEAR_Error')) { + $is_update = true; + $desc = _("%s wants to notify you about changes of \"%s\"."); + } else { + $is_update = false; + + // Check that you are one of the attendees here. + $is_attendee = false; + if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) { + require_once 'Horde/Identity.php'; + $identity = &Identity::singleton(array('imp', 'imp')); + for ($i = 0, $c = count($attendees); $i < $c; ++$i) { + $attendee = parse_url($attendees[$i]); + if (!empty($attendee['path']) && + $identity->hasAddress($attendee['path'])) { + $is_attendee = true; + break; + } + } + } + + $desc = $is_attendee + ? _("%s requests your presence at \"%s\".") + : _("%s wishes to make you aware of \"%s\"."); + } + if ($is_update && $registry->hasMethod('calendar/replace')) { + $options[] = ''; + $options[] = ''; + } elseif ($registry->hasMethod('calendar/import')) { + $options[] = ''; + $options[] = ''; + } + $options[] = ''; + $options[] = ''; + $options[] = ''; + // $options[] = ''; + break; + + case 'ADD': + $desc = _("%s wishes to ammend \"%s\"."); + if ($registry->hasMethod('calendar/import')) { + $options[] = ''; + } + break; + + case 'REFRESH': + $desc = _("%s wishes to receive the latest information about \"%s\"."); + $options[] = ''; + break; + + case 'REPLY': + $desc = _("%s has replied to the invitation to \"%s\"."); + $sender = $this->_headers->getValue('From'); + if ($registry->hasMethod('calendar/updateAttendee')) { + $options[] = ''; + } + break; + + case 'CANCEL': + if (is_a($instance = $vevent->getAttribute('RECURRENCE-ID'), 'PEAR_Error')) { + $desc = _("%s has cancelled \"%s\"."); + if ($registry->hasMethod('calendar/delete')) { + $options[] = ''; + } + } else { + $desc = _("%s has cancelled an instance of the recurring \"%s\"."); + if ($registry->hasMethod('calendar/replace')) { + $options[] = ''; + } + } + break; + } + + $summary = $vevent->getAttribute('SUMMARY'); + if (is_a($summary, 'PEAR_Error')) { + $desc = sprintf($desc, htmlspecialchars($sender), _("Unknown Meeting")); + } else { + $desc = sprintf($desc, htmlspecialchars($sender), htmlspecialchars($summary)); + } + + $html .= '

' . $desc . '

'; + + if ($this->_msgs) { + foreach ($this->_msgs[$id] as $msg) { + $html .= '

' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '

'; + } + } + + $start = $vevent->getAttribute('DTSTART'); + if (!is_a($start, 'PEAR_Error')) { + if (is_array($start)) { + $html .= '

' . _("Start") . ': ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '

'; + } else { + $html .= '

' . _("Start") . ': ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '

'; + } + } + + $end = $vevent->getAttribute('DTEND'); + if (!is_a($end, 'PEAR_Error')) { + if (is_array($end)) { + $html .= '

' . _("End") . ': ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '

'; + } else { + $html .= '

' . _("End") . ': ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '

'; + } + } + + $sum = $vevent->getAttribute('SUMMARY'); + if (!is_a($sum, 'PEAR_Error')) { + $html .= '

' . _("Summary") . ': ' . htmlspecialchars($sum) . '

'; + } else { + $html .= '

' . _("Summary") . ': ' . _("None") . '

'; + } + + $desc = $vevent->getAttribute('DESCRIPTION'); + if (!is_a($desc, 'PEAR_Error')) { + $html .= '

' . _("Description") . ': ' . nl2br(htmlspecialchars($desc)) . '

'; + } + + $loc = $vevent->getAttribute('LOCATION'); + if (!is_a($loc, 'PEAR_Error')) { + $html .= '

' . _("Location") . ': ' . htmlspecialchars($loc) . '

'; + } + + if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) { + $html .= '

' . _("Attendees") . '

'; + + $html .= ''; + foreach ($attendees as $key => $attendee) { + $attendee = parse_url($attendee); + $attendee = empty($attendee['path']) ? _("Unknown") : $attendee['path']; + + if (!empty($attendee_params[$key]['CN'])) { + $attendee = $attendee_params[$key]['CN']; + } + + $role = _("Required Participant"); + if (isset($attendee_params[$key]['ROLE'])) { + switch ($attendee_params[$key]['ROLE']) { + case 'CHAIR': + $role = _("Chair Person"); + break; + + case 'OPT-PARTICIPANT': + $role = _("Optional Participant"); + break; + + case 'NON-PARTICIPANT': + $role = _("Non Participant"); + break; + + case 'REQ-PARTICIPANT': + default: + // Already set above. + break; + } + } + + $status = _("Awaiting Response"); + if (isset($attendee_params[$key]['PARTSTAT'])) { + $status = $this->_partstatToString($attendee_params[$key]['PARTSTAT'], $status); + } + + $html .= ''; + } + $html .= '
' . _("Name") . '' . _("Role") . '' . _("Status") . '
' . htmlspecialchars($attendee) . '' . htmlspecialchars($role) . '' . htmlspecialchars($status) . '
'; + } + + if ($_SESSION['imp']['viewmode'] != 'imp') { + return $html; + } + + if ($options) { + $html .= '

' . _("Actions") . '

' . + '' . + ' '; + } + + return $html; + } + + /** + * Returns the html for a vEvent. + * + * @todo IMP 5: move organizerName() from Horde_iCalendar_vevent to + * Horde_iCalendar + */ + protected function _vTodo($vtodo, $id) + { + global $registry, $prefs; + + $html = ''; + $desc = ''; + $options = array(); + + $organizer = $vtodo->getAttribute('ORGANIZER', true); + if (is_a($organizer, 'PEAR_Error')) { + $sender = _("An unknown person"); + } else { + if (isset($organizer[0]['CN'])) { + $sender = $organizer[0]['CN']; + } else { + $organizer = parse_url($vtodo->getAttribute('ORGANIZER')); + $sender = $organizer['path']; + } + } + + switch ($this->_method) { + case 'PUBLISH': + $desc = _("%s wishes to make you aware of \"%s\"."); + if ($registry->hasMethod('tasks/import')) { + $options[] = ''; + } + break; + } + + $summary = $vtodo->getAttribute('SUMMARY'); + if (is_a($summary, 'PEAR_Error')) { + $desc = sprintf($desc, htmlspecialchars($sender), _("Unknown Task")); + } else { + $desc = sprintf($desc, htmlspecialchars($sender), htmlspecialchars($summary)); + } + + $html .= '

' . $desc . '

'; + + if ($this->_msgs) { + foreach ($this->_msgs[$id] as $msg) { + $html .= '

' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '

'; + } + } + + $priority = $vtodo->getAttribute('PRIORITY'); + if (!is_a($priority, 'PEAR_Error')) { + $html .= '

' . _("Priority") . ': ' . (int)$priority . '

'; + } + + $sum = $vtodo->getAttribute('SUMMARY'); + if (!is_a($sum, 'PEAR_Error')) { + $html .= '

' . _("Summary") . ': ' . htmlspecialchars($sum) . '

'; + } else { + $html .= '

' . _("Summary") . ': ' . _("None") . '

'; + } + + $desc = $vtodo->getAttribute('DESCRIPTION'); + if (!is_a($desc, 'PEAR_Error')) { + $html .= '

' . _("Description") . ': ' . nl2br(htmlspecialchars($desc)) . '

'; + } + + $attendees = $vtodo->getAttribute('ATTENDEE'); + $params = $vtodo->getAttribute('ATTENDEE', true); + + if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) { + $html .= '

' . _("Attendees") . '

'; + if (!is_array($attendees)) { + $attendees = array($attendees); + } + + $html .= ''; + foreach ($attendees as $key => $attendee) { + $attendee = parse_url($attendee); + $attendee = $attendee['path']; + + if (isset($params[$key]['CN'])) { + $attendee = $params[$key]['CN']; + } + + $role = _("Required Participant"); + if (isset($params[$key]['ROLE'])) { + switch ($params[$key]['ROLE']) { + case 'CHAIR': + $role = _("Chair Person"); + break; + + case 'OPT-PARTICIPANT': + $role = _("Optional Participant"); + break; + + case 'NON-PARTICIPANT': + $role = _("Non Participant"); + break; + + case 'REQ-PARTICIPANT': + default: + // Already set above. + break; + } + } + + $status = _("Awaiting Response"); + if (isset($params[$key]['PARTSTAT'])) { + $status = $this->_partstatToString($params[$key]['PARTSTAT'], $status); + } + + $html .= ''; + } + $html .= '
' . _("Name") . '' . _("Role") . '' . _("Status") . '
' . htmlspecialchars($attendee) . '' . htmlspecialchars($role) . '' . htmlspecialchars($status) . '
'; + } + + if ($_SESSION['imp']['viewmode'] != 'imp') { + return $html; + } + + if ($options) { + $html .= '

' . _("Actions") . '

' . + ' '; + } + + return $html; + } + + /** + * Translate the Participation status to string. + * + * @param string $value The value of PARTSTAT. + * @param string $default The value to return as default. + * + * @return string The translated string. + */ + protected function _partstatToString($value, $default = null) + { + switch ($value) { + case 'ACCEPTED': + return _("Accepted"); + break; + + case 'DECLINED': + return _("Declined"); + break; + + case 'TENTATIVE': + return _("Tentatively Accepted"); + break; + + case 'DELEGATED': + return _("Delegated"); + break; + + case 'COMPLETED': + return _("Completed"); + break; + + case 'IN-PROCESS': + return _("In Process"); + break; + + case 'NEEDS-ACTION': + default: + return is_null($default) ? _("Needs Action") : $default; + } + } +} diff --git a/imp/lib/Mime/Viewer/notification.php b/imp/lib/Mime/Viewer/notification.php new file mode 100644 index 000000000..216e3e62f --- /dev/null +++ b/imp/lib/Mime/Viewer/notification.php @@ -0,0 +1,82 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_notification extends Horde_Mime_Viewer_Driver +{ + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + $contents = &$params[0]; + + /* If this is a straight message/disposition-notification part, just + output the text. */ + if ($this->mime_part->getType() == 'message/disposition-notification') { + $part = new MIME_Part('text/plain'); + $part->setContents($this->mime_part->getContents()); + return '
' . htmlspecialchars($contents->renderMIMEPart($part)) . '
'; + } + + global $registry; + + /* RFC 2298 [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) */ + + /* Print the human readable message. */ + $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(1)); + $status = array( + _("A message you have sent has resulted in a return notification from the recipient."), + _("The mail server generated the following informational message:") + ); + $statusimg = Horde::img('alerts/message.png', _("Attention"), 'height="16" width="16"', $registry->getImageDir('horde')); + $text = $this->formatStatusMsg($status, $statusimg) . + '
' . $contents->renderMIMEPart($part) . '
' . "\n"; + + /* Display a link to more detailed message. */ + $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(2)); + if ($part) { + $statusimg = Horde::img('info_icon.png', _("Info"), 'height="16" width="16"', $registry->getImageDir('horde')); + $status = array(sprintf(_("Additional information can be viewed %s."), $contents->linkViewJS($part, 'view_attach', _("HERE"), _("Additional information details")))); + } + + /* Display a link to the sent message. Try to download the text of + the message/rfc822 part first, if it exists. */ + if (($part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId('3.0'))) || + ($part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(3)))) { + $status[] = sprintf(_("The text of the sent message can be viewed %s."), $contents->linkViewJS($part, 'view_attach', _("HERE"), _("The text of the sent message"))); + } + + $text .= $this->formatStatusMsg($status, $statusimg, false); + + return $text; + } + + /** + * Return the content-type. + * + * @return string The content-type of the message. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/imp/lib/Mime/Viewer/partial.php b/imp/lib/Mime/Viewer/partial.php new file mode 100644 index 000000000..e744371f6 --- /dev/null +++ b/imp/lib/Mime/Viewer/partial.php @@ -0,0 +1,76 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_partial extends Horde_Mime_Viewer_Driver +{ + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + $contents = &$params[0]; + + $base_ob = &$contents->getBaseObjectPtr(); + $curr_index = $base_ob->getMessageIndex(); + $id = $this->mime_part->getContentTypeParameter('id'); + $parts = array(); + + /* Perform the search to find the other parts of the message. */ + $query = new Horde_Imap_Client_Search_Query(); + $query->header('Content-Type', $id); + + $indices = $GLOBALS['imp_search']->runSearchQuery($query, $GLOBALS['imp_mbox']['thismailbox']); + + /* If not able to find the other parts of the message, print error. */ + if (count($indices) != $this->mime_part->getContentTypeParameter('total')) { + return $this->formatStatusMsg(sprintf(_("Cannot display - found only %s of %s parts of this message in the current mailbox."), count($indices), $this->mime_part->getContentTypeParameter('total'))); + } + + /* Get the contents of each of the parts. */ + foreach ($indices as $val) { + /* No need to fetch the current part again. */ + if ($val == $curr_index) { + $parts[$this->mime_part->getContentTypeParameter('number')] = $this->mime_part->getContents(); + } else { + $imp_contents = &IMP_Contents::singleton($val . IMP::IDX_SEP . $GLOBALS['imp_mbox']['thismailbox']); + $part = &$imp_contents->getMIMEPart(0); + $parts[$part->getContentTypeParameter('number')] = $imp_contents->getBody(); + } + } + + /* Sort the parts in numerical order. */ + ksort($parts, SORT_NUMERIC); + + /* Combine the parts and render the underlying data. */ + $mime_message = &MIME_Message::parseMessage(implode('', $parts)); + $mc = new MIME_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$contents)); + $mc->buildMessage(); + + return '' . $mc->getMessage(true) . '
'; + } + + /** + * Return the content-type of the rendered output. + * + * @return string The content-type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/imp/lib/Mime/Viewer/pdf.php b/imp/lib/Mime/Viewer/pdf.php new file mode 100644 index 000000000..6ee5b36aa --- /dev/null +++ b/imp/lib/Mime/Viewer/pdf.php @@ -0,0 +1,141 @@ + + * @package Horde_Mime_Viewer + */ +class IMP_Horde_Mime_Viewer_pdf extends Horde_Mime_Viewer_Driver +{ + /** + * The content-type of the generated data. + * + * @var string + */ + protected $_contentType; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered information. + */ + public function render($params) + { + /* Create the thumbnail and display. */ + if (Util::getFormData('images_view_thumbnail')) { + $mime = $this->mime_part; + $img = $this->_getHordeImageOb(); + + if ($img) { + $img->resize(96, 96, true); + $type = $img->getContentType(); + $data = $img->raw(true); + } + + if (!$img || !$data) { + $type = 'image/png'; + $data = file_get_contents(IMP_BASE . '/themes/graphics/mini-error.png'); + } + + $mime->setType($type); + $this->_contentType = $type; + $mime->setContents($data); + + return $mime->getContents(); + } + + return parent::render($params); + } + + /** + * Render out attachment information. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function renderAttachmentInfo($params) + { + $contents = &$params[0]; + + if (is_a($contents, 'IMP_Contents')) { + $this->mime_part = &$contents->getDecodedMIMEPart($this->mime_part->getMIMEId(), true); + } + + /* Check to see if convert utility is available. */ + if (!$this->_getHordeImageOb(false)) { + return ''; + } + + $status = array( + sprintf(_("A PDF file named %s is attached to this message. A thumbnail is below."), + $this->mime_part->getName(true)), + ); + + if (!$GLOBALS['browser']->hasFeature('javascript')) { + $status[] = Horde::link($contents->urlView($this->mime_part, + 'view_attach')) . + Horde::img($contents->urlView($this->mime_part, + 'view_attach', array('images_view_thumbnail' => 1), false), + _("View Attachment"), null, '') . ''; + } else { + $status[] = $contents->linkViewJS($this->mime_part, 'view_attach', + Horde::img($contents->urlView($this->mime_part, + 'view_attach', array('images_view_thumbnail' => 1), + false), _("View Attachment"), null, ''), null, null, + null); + } + + return $this->formatStatusMsg($status, Horde::img('mime/image.png', + _("Thumbnail of attached PDF file"), null, $GLOBALS['registry']->getImageDir('horde')), false); + } + + /** + * Return a Horde_Image object. + * + * @param boolean $load Whether to load the image data. + * + * @return Horde_Image The requested object. + */ + protected function _getHordeImageOb($load = true) + { + if (empty($GLOBALS['conf']['image']['convert'])) { + return false; + } + + include_once 'Horde/Image.php'; + $img = &Horde_Image::singleton('im', array('temp' => Horde::getTempdir())); + if (is_a($img, 'PEAR_Error')) { + return false; + } + + if ($load) { + $ret = $img->loadString(1, $this->mime_part->getContents()); + if (is_a($ret, 'PEAR_Error')) { + return false; + } + } + + return $img; + } + + /** + * Return the content-type + * + * @return string The content-type of the output. + */ + public function getType() + { + return ($this->_contentType) ? $this->_contentType : parent::getType(); + } +} diff --git a/imp/lib/Mime/Viewer/pgp.php b/imp/lib/Mime/Viewer/pgp.php new file mode 100644 index 000000000..bcebf764d --- /dev/null +++ b/imp/lib/Mime/Viewer/pgp.php @@ -0,0 +1,484 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver +{ + /** + * IMP_PGP object. + * + * @var IMP_PGP + */ + protected $_imp_pgp; + + /** + * The address of the sender. + * + * @var string + */ + protected $_address; + + /** + * Pointer to the MIME_Contents item. + * + * @var MIME_Contents + */ + protected $_contents = null; + + /** + * Classwide cache for icons for status messages. + * + * @var string + */ + protected $_icon = null; + + /** + * Classwide cache for status messages. + * + * @var array + */ + protected $_status = array(); + + /** + * The MIME content-type of this part. + * + * @var string + */ + protected $_type = 'text/html'; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + global $conf, $prefs; + + /* Set the MIME_Contents class variable. */ + $this->_contents = &$params[0]; + + $msg = ''; + + if (empty($this->_imp_pgp) && !empty($conf['utils']['gnupg'])) { + $this->_imp_pgp = new IMP_PGP(); + } + + /* Determine the address of the sender. */ + if (empty($this->_address)) { + $base_ob = &$this->_contents->getBaseObjectPtr(); + $this->_address = $base_ob->getFromAddress(); + } + + /* We need to insert JavaScript code now if PGP support is active. */ + if (!empty($conf['utils']['gnupg']) && + $prefs->getValue('use_pgp') && + !Util::getFormData('rawpgpkey')) { + $msg = Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true); + $msg .= Util::bufferOutput(array('Horde', 'addScriptFile'), 'popup.js', 'imp', true); + } + + /* For RFC 2015/3156, there are 3 types of messages: + + multipart/encrypted + + multipart/signed + + application/pgp-keys */ + switch ($this->mime_part->getType()) { + case 'application/pgp-keys': + $msg .= $this->_outputPGPKey(); + break; + + case 'multipart/signed': + case 'application/pgp-signature': + $msg .= $this->_outputPGPSigned(); + break; + + case 'multipart/encrypted': + case 'application/pgp-encrypted': + $msg .= $this->_outputPGPEncrypted(); + break; + } + + return $msg; + } + + /** + * Return the content-type of the output. + * + * @return string The MIME content type of the output. + */ + public function getType() + { + return $this->_type; + } + + /** + * Generates HTML output for 'application/pgp-keys' MIME_Parts. + * + * @return string The HTML output. + */ + protected function _outputPGPKey() + { + global $conf, $prefs; + + $mime = &$this->mime_part; + $part = $this->_contents->getDecodedMIMEPart($mime->getMIMEId()); + + if (empty($conf['utils']['gnupg'])) { + $text = '
' . $part->getContents() . '
'; + } elseif (Util::getFormData('rawpgpkey')) { + $text = $part->getContents(); + $this->_type = 'text/plain'; + } else { + require_once 'Horde/Text.php'; + + $pgp_key = $mime->getContents(); + + /* Initialize status message. */ + $this->_initStatus($this->getIcon($mime->getType()), _("PGP")); + $msg = _("This PGP Public Key was attached to the message."); + if ($prefs->getValue('use_pgp') && + $GLOBALS['registry']->hasMethod('contacts/addField') && + $prefs->getValue('add_source')) { + $msg .= ' ' . Horde::link('#', '', '', '', $this->_imp_pgp->savePublicKeyURL($mime) . ' return false;') . _("[Save the key to your Address book]") . ''; + } + $this->_status[] = $msg . ' ' . $this->_contents->linkViewJS($part, 'view_attach', _("[View the raw key]"), '', null, array('rawpgpkey' => 1)); + + $text = $this->_outputStatus(false) . + '' . nl2br(str_replace(' ', ' ', $this->_imp_pgp->pgpPrettyKey($pgp_key))) . ''; + } + + return $text; + } + + /** + * Generates HTML output for 'multipart/signed' and + * 'application/pgp-signature' MIME_Parts. + * + * @return string The HTML output. + */ + protected function _outputPGPSigned() + { + global $conf, $prefs; + + $active = ($prefs->getValue('use_pgp') && !empty($conf['utils']['gnupg'])); + $mime = &$this->mime_part; + $mimetype = $mime->getType(); + $text = ''; + + $signenc = $mime->getInformation('pgp_signenc'); + if (!$active) { + if ($signenc) { + $this->_status[] = _("The message below has been digitally signed and encrypted with PGP, but the signature cannot be verified."); + } else { + $this->_status[] = _("The message below has been digitally signed with PGP, but the signature cannot be verified."); + } + } else { + if ($signenc) { + $this->_status[] = _("The message below has been digitally signed and encrypted with PGP."); + } else { + $this->_status[] = _("The message below has been digitally signed with PGP."); + } + } + + $this->_initStatus($this->getIcon($mimetype), _("PGP")); + + /* Store PGP results in $sig_result; store text in $data. */ + $sig_result = null; + if ($mimetype == 'multipart/signed') { + /* If the MIME ID is 0, we need to store the body of the message + in the MIME_Part object. */ + if (!$signenc) { + if (($mimeID = $mime->getMIMEId())) { + $mime->setContents($this->_contents->getBodyPart($mimeID)); + } else { + $mime->setContents($this->_contents->getBody()); + } + $mime->splitContents(); + } + + /* Data that is signed appears in the first MIME subpart. */ + $subpart = $mime->getPart($mime->getRelativeMIMEId(1)); + $signature_data = rtrim($subpart->getCanonicalContents(), "\r"); + + $mime_message = Horde_Mime_Message::parseMessage($signature_data); + + /* The PGP signature appears in the second MIME subpart. */ + $subpart = $mime->getPart($mime->getRelativeMIMEId(2)); + if ($subpart && $subpart->getType() == 'application/pgp-signature') { + if ($active) { + if ($GLOBALS['prefs']->getValue('pgp_verify') || + Util::getFormData('pgp_verify_msg')) { + $subpart->transferDecodeContents(); + $sig_result = $this->_imp_pgp->verifySignature($signature_data, $this->_address, $subpart->getContents()); + } elseif (isset($_SESSION['imp']['viewmode']) && + ($_SESSION['imp']['viewmode'] == 'imp')) { + // TODO: Fix to work with DIMP + $base_ob = &$this->_contents->getBaseObjectPtr(); + $this->_status[] = Horde::link(Util::addParameter(IMP::generateIMPUrl(Horde::selfUrl(), $GLOBALS['imp_mbox']['mailbox'], $GLOBALS['imp_mbox']['index'], $GLOBALS['imp_mbox']['thismailbox']), 'pgp_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; + } + } + } else { + $this->_status[] = _("The message below does not appear to be in the correct PGP format (according to RFC 2015)."); + } + } elseif ($mimetype == 'application/pgp-signature') { + /* Get the signed message to output. */ + $mime_message = new Horde_Mime_Message(); + $mime_message->setType('text/plain'); + $mime->transferDecodeContents(); + + if (empty($this->_imp_pgp)) { + $mime_message->setContents($mime->getContents()); + } else { + $mime_message->setContents($this->_imp_pgp->getSignedMessage($mime)); + } + + /* Pass the signed text straight through to PGP program */ + if ($active) { + if ($GLOBALS['prefs']->getValue('pgp_verify') || + Util::getFormData('pgp_verify_msg')) { + $sig_result = $this->_imp_pgp->verifySignature($mime->getContents(), $this->_address); + } elseif (isset($_SESSION['imp']['viewmode']) && + ($_SESSION['imp']['viewmode'] == 'imp')) { + // TODO: Fix to work with DIMP + $this->_status[] = Horde::link(Util::addParameter(Horde::selfUrl(true), 'pgp_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; + } + } + } + + $text = $this->_outputStatus(); + + if ($sig_result !== null) { + $text .= $this->_outputPGPSignatureTest($sig_result); + } + + /* We need to stick the output into a MIME_Contents object. */ + $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); + $mc->buildMessage(); + + return $text . '' . $mc->getMessage(true) . '
'; + } + + /** + * Generates HTML output for 'multipart/encrypted' and + * 'application/pgp-encrypted' MIME_Parts. + * + * @return string The HTML output. + */ + protected function _outputPGPEncrypted() + { + global $conf, $prefs; + + $mime = &$this->mime_part; + $mimetype = $mime->getType(); + $text = ''; + + $this->_initStatus($this->getIcon($mimetype), _("PGP")); + + /* Print out message now if PGP is not active. */ + if (empty($conf['utils']['gnupg']) || !$prefs->getValue('use_pgp')) { + $this->_status[] = _("The message below has been encrypted with PGP, however, PGP support is disabled so the message cannot be decrypted."); + return $this->_outputStatus(); + } + + if ($mimetype == 'multipart/encrypted') { + /* PGP control information appears in the first MIME subpart. We + don't currently need to do anything with this information. The + encrypted data appears in the second MIME subpart. */ + $subpart = $mime->getPart($mime->getRelativeMIMEId(2)); + if (!$subpart) { + return $text; + } + $encrypted_data = $this->_contents->getBodyPart($subpart->getMIMEId()); + } elseif ($mimetype == 'application/pgp-encrypted') { + $encrypted_data = $mime->getContents(); + } else { + return $text; + } + + /* Check if this is a literal compressed message. */ + $info = $this->_imp_pgp->pgpPacketInformation($encrypted_data); + $literal = !empty($info['literal']); + + /* Check if this a symmetrically encrypted message. */ + $symmetric = $this->_imp_pgp->encryptedSymmetrically($encrypted_data); + + if ($symmetric && !$this->_imp_pgp->getSymmetricPassphrase()) { + if (isset($_SESSION['imp']['viewmode']) && + ($_SESSION['imp']['viewmode'] == 'imp')) { + // TODO: Fix to work with DIMP + /* Ask for the correct passphrase if this is encrypted + * symmetrically. */ + $url = $this->_imp_pgp->getJSOpenWinCode('open_symmetric_passphrase_dialog'); + $this->_status[] = Horde::link('#', _("The message below has been encrypted with PGP. You must enter the passphrase that was used to encrypt this message."), null, null, $url . ' return false;') . _("You must enter the passphrase that was used to encrypt this message.") . ''; + $text .= $this->_outputStatus() . + Util::bufferOutput(array('IMP', 'addInlineScript'), $url); + } + } elseif (!$literal && !$symmetric && + !$this->_imp_pgp->getPersonalPrivateKey()) { + /* Output if there is no personal private key to decrypt with. */ + $this->_status[] = _("The message below has been encrypted with PGP, however, no personal private key exists so the message cannot be decrypted."); + return $this->_outputStatus(); + } elseif (!$literal && !$symmetric && + !$this->_imp_pgp->getPassphrase()) { + if (isset($_SESSION['imp']['viewmode']) && + ($_SESSION['imp']['viewmode'] == 'imp')) { + // TODO: Fix to work with DIMP + /* Ask for the private key's passphrase if this is encrypted + * asymmetrically. */ + $url = $this->_imp_pgp->getJSOpenWinCode('open_passphrase_dialog'); + $this->_status[] = Horde::link('#', _("The message below has been encrypted with PGP. You must enter the passphrase for your PGP private key before it can be decrypted."), null, null, $url . ' return false;') . _("You must enter the passphrase for your PGP private key to view this message.") . ''; + $text .= $this->_outputStatus() . + Util::bufferOutput(array('IMP', 'addInlineScript'), $url); + } + } else { + /* Decrypt this message. */ + $this->_status[] = $literal ? _("The message below has been compressed with PGP.") : _("The message below has been encrypted with PGP."); + if ($mimetype == 'multipart/encrypted') { + if ($subpart->getType() == 'application/octet-stream') { + $decrypted_data = $this->_imp_pgp->decryptMessage($encrypted_data, $symmetric, !$literal); + if (is_a($decrypted_data, 'PEAR_Error')) { + $this->_status[] = _("The message below does not appear to be a valid PGP encrypted message. Error: ") . $decrypted_data->getMessage(); + $text .= $this->_outputStatus(); + } else { + /* We need to check if this is a signed/encrypted + message. */ + $mime_message = Horde_Mime_Message::parseMessage($decrypted_data->message); + if (!$mime_message) { + require_once 'Horde/Text/Filter.php'; + $text .= $this->_signedOutput($decrypted_data->sig_result); + $decrypted_message = String::convertCharset($decrypted_data->message, $subpart->getCharset()); + $text .= '' . Text_Filter::filter($decrypted_message, 'text2html', array('parselevel' => TEXT_HTML_SYNTAX)) . ''; + } else { + $mimetype = $mime_message->getType(); + if (($mimetype == 'multipart/signed') || + ($mimetype == 'application/pgp-signature')) { + $mime_message->setInformation('pgp_signenc', true); + $mime_message->setContents($decrypted_data->message); + $mime_message->splitContents(); + } else { + $text .= $this->_signedOutput($decrypted_data->sig_result); + } + + /* We need to stick the output into a + MIME_Contents object. */ + $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); + $mc->buildMessage(); + if ($mime_message->getInformation('pgp_signenc')) { + $text .= $mc->getMessage(true); + } else { + $text .= '' . $mc->getMessage(true) . '
'; + } + } + } + } else { + $this->_status[] = _("The message below does not appear to be in the correct PGP format (according to RFC 2015)."); + $text .= $this->_outputStatus(); + } + } elseif ($mimetype == 'application/pgp-encrypted') { + $decrypted_data = $this->_imp_pgp->decryptMessage($encrypted_data, $symmetric, !$literal); + if (is_a($decrypted_data, 'PEAR_Error')) { + $decrypted_message = $decrypted_data->getMessage(); + $text .= $this->_outputStatus(); + } else { + $text .= $this->_signedOutput($decrypted_data->sig_result); + $decrypted_message = String::convertCharset($decrypted_data->message, $mime->getCharset()); + } + + require_once 'Horde/Text/Filter.php'; + $text .= '' . Text_Filter::filter($decrypted_message, 'text2html', array('parselevel' => TEXT_HTML_SYNTAX)) . ''; + } + } + $this->_imp_pgp->unsetSymmetricPassphrase(); + + return $text; + } + + /** + * Generates HTML output for the PGP signature test. + * + * @param string $result Result string of the PGP output concerning the + * signature test. + * + * @return string The HTML output. + */ + protected function _outputPGPSignatureTest($result) + { + $text = ''; + + if (is_a($result, 'PEAR_Error')) { + $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/error.png', _("Error")); + $result = $result->getMessage(); + } else { + $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/success.png', _("Success")); + /* This message has been verified but there was no output from the + PGP program. */ + if (empty($result)) { + $result = _("The message below has been verified."); + } + } + + require_once 'Horde/Text/Filter.php'; + $this->_status[] = Text_Filter::filter($result, 'text2html', array('parselevel' => TEXT_HTML_NOHTML)); + + return $this->_outputStatus(); + } + + /** + * Output signed message status. + * + * @param string $result The signature result. + * + * @return string HTML output. + */ + protected function _signedOutput($result) + { + if (!empty($result)) { + return $this->_outputPGPSignatureTest($result); + } else { + return $this->_outputStatus(); + } + } + + /* Various formatting helper functions */ + protected function _initStatus($src, $alt = '') + { + if ($this->_icon === null) { + $this->_icon = Horde::img($src, $alt, 'height="16" width="16"', ''); + } + } + + protected function _outputStatus($printable = true) + { + $output = ''; + if (!empty($this->_status)) { + $output = $this->formatStatusMsg($this->_status, $this->_icon, $printable); + } + $this->_icon = null; + $this->_status = array(); + return $output; + } +} diff --git a/imp/lib/Mime/Viewer/pkcs7.php b/imp/lib/Mime/Viewer/pkcs7.php new file mode 100644 index 000000000..168a048e4 --- /dev/null +++ b/imp/lib/Mime/Viewer/pkcs7.php @@ -0,0 +1,495 @@ + + * @author Michael Slusarz + * @package Horde_Mime_Viewer + */ +class IMP_Horde_Mime_Viewer_pkcs7 extends Horde_Mime_Viewer_Driver +{ + /** + * IMP_SMIME object. + * + * @var IMP_SMIME + */ + protected $_impSmime; + + /** + * Classwide cache for icons for status messages. + * + * @var string + */ + protected $_icon = null; + + /** + * Pointer to the IMP_Contents item. + * + * @var IMP_Contents + */ + protected $_contents = null; + + /** + * Classwide cache for status messages. + * + * @var array + */ + protected $_status = array(); + + /** + * The MIME_Headers object for the message data. + * + * @var MIME_Headers + */ + protected $_headers; + + /** + * Some mailers set S/MIME messages to always be attachments. However, + * most of the time S/MIME is used to secure the contents of the message, + * so displaying as an attachment makes no sense. Therefore, force + * viewing inline (or at least let Horde_Mime_Viewer/IMP_Contents make the + * determination on whether the data can be viewed inline or not). + * + * @var boolean + */ + protected $_forceinline = true; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a IMP_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + /* Set the IMP_Contents class variable. */ + $this->_contents = &$params[0]; + + $msg = ''; + + if (empty($this->_impSmime)) { + $this->_impSmime = new IMP_SMIME(); + } + + /* Check to see if S/MIME support is available. */ + $openssl_check = $this->_impSmime->checkForOpenSSL(); + if ($GLOBALS['prefs']->getValue('use_smime') && + !is_a($openssl_check, 'PEAR_Error')) { + /* We need to insert JavaScript code now if S/MIME support is + active. */ + $msg = Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true); + $msg .= Util::bufferOutput(array('Horde', 'addScriptFile'), 'popup.js', 'imp', true); + } + + /* Get the type of message now. */ + $type = $this->_getSMIMEType(); + switch ($type) { + case 'signed': + $msg .= $this->_outputSMIMESigned(); + break; + + case 'encrypted': + $msg .= $this->_outputSMIMEEncrypted(); + break; + } + + return $msg; + } + + /** + * Generates HTML output for the S/MIME key in + * 'application/pkcs7-signature' MIME_Parts. + * + * @return string The HTML output. + */ + protected function _outputSMIMEKey() + { + if (!$GLOBALS['prefs']->getValue('use_smime')) { + return _("S/MIME support is not enabled."); + } else { + $mime = &$this->mime_part; + $signenc = $mime->getInformation('smime_signenc'); + $raw_text = $this->_getRawSMIMEText(); + if ($signenc && $mime->getInformation('smime_from')) { + $smime_from = $mime->getInformation('smime_from'); + $raw_text = "From: $smime_from\n" . $raw_text; + } + $sig_result = $this->_impSmime->verifySignature($raw_text); + return $this->_impSmime->certToHTML($sig_result->cert); + } + } + + /** + * Generates HTML output for 'multipart/signed', + * 'application/pkcs7-signature' and + * 'application/x-pkcs7-signature' MIME_Parts. + * + * @return string The HTML output. + */ + protected function _outputSMIMESigned() + { + if (Util::getFormData('viewkey')) { + return $this->_outputSMIMEKey(); + } + + $cert = $text = ''; + $mime = &$this->mime_part; + $mimetype = $mime->getType(); + $active = $GLOBALS['prefs']->getValue('use_smime'); + + $signenc = $mime->getInformation('smime_signenc'); + if ($signenc) { + $this->_status[] = _("This message has been encrypted via S/MIME."); + } + + $this->_initStatus($this->getIcon($mimetype), _("S/MIME")); + $this->_status[] = _("This message has been digitally signed via S/MIME."); + + if (!$active) { + $this->_status[] = _("S/MIME support is not enabled so the digital signature is unable to be verified."); + } + + /* Store S/MIME results in $sig_result. */ + $sig_result = null; + if ($mimetype == 'multipart/signed') { + if (!$signenc) { + if (($mimeID = $mime->getMIMEId())) { + $mime->setContents($this->_contents->getBodyPart($mimeID)); + } else { + $mime->setContents($this->_contents->getBody()); + } + $mime->splitContents(); + } + + /* Data that is signed appears in the first MIME subpart. */ + $signed_part = $mime->getPart($mime->getRelativeMIMEId(1)); + $signed_data = rtrim($signed_part->getCanonicalContents(), "\r"); + $mime_message = Horde_Mime_Message::parseMessage($signed_data); + + /* The S/MIME signature appears in the second MIME subpart. */ + $subpart = $mime->getPart($mime->getRelativeMIMEId(2)); + if (!$subpart || + !in_array($subpart->getType(), array('application/pkcs7-signature', 'application/x-pkcs7-signature'))) { + $this->_status[] = _("This message does not appear to be in the correct S/MIME format."); + } + } elseif (!$active) { + $this->_status[] = _("S/MIME support is not enabled so the contents of this signed message cannot be displayed."); + } + + if ($active) { + $raw_text = $this->_getRawSMIMEText(); + if ($signenc && $mime->getInformation('smime_from')) { + $smime_from = $mime->getInformation('smime_from'); + $raw_text = "From: $smime_from\n" . $raw_text; + } + + if ($GLOBALS['prefs']->getValue('smime_verify') || + Util::getFormData('smime_verify_msg')) { + $sig_result = $this->_impSmime->verifySignature($raw_text); + } elseif (isset($_SESSION['imp']['viewmode']) && + ($_SESSION['imp']['viewmode'] == 'imp')) { + // TODO: Fix to work with DIMP + $this->_status[] = Horde::link(Util::addParameter(Horde::selfUrl(true), 'smime_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; + } + + if (!isset($subpart)) { + $msg_data = $this->_impSmime->extractSignedContents($raw_text); + if (is_a($msg_data, 'PEAR_Error')) { + $this->_status[] = $msg_data->getMessage(); + $mime_message = $mime; + } else { + $mime_message = Horde_Mime_Message::parseMessage($msg_data); + } + } + + $text = $this->_outputStatus(); + if ($sig_result !== null) { + $text .= $this->_outputSMIMESignatureTest($sig_result->result, $sig_result->email); + if (!empty($sig_result->cert)) { + $cert_details = $this->_impSmime->parseCert($sig_result->cert); + if (isset($cert_details['certificate']['subject']['CommonName'])) { + $subject = $cert_details['certificate']['subject']['CommonName']; + } elseif (isset($cert_details['certificate']['subject']['Email'])) { + $subject = $cert_details['certificate']['subject']['Email']; + } elseif (isset($sig_result->email)) { + $subject = $sig_result->email; + } elseif (isset($smime_from)) { + $subject = $smime_from; + } elseif (($from = $this->_headers->getValue('from'))) { + $subject = $from; + } else { + $subject = null; + } + if (isset($subpart) && + !empty($subject) && + $GLOBALS['registry']->hasMethod('contacts/addField') && + $GLOBALS['prefs']->getValue('add_source')) { + $this->_status[] = sprintf(_("The S/MIME certificate of %s: "), @htmlspecialchars($subject, ENT_COMPAT, NLS::getCharset())) . + $this->_contents->linkViewJS($subpart, 'view_attach', _("View"), '', null, array('viewkey' => 1)) . '/' . + Horde::link('#', '', null, null, $this->_impSmime->savePublicKeyURL($sig_result->cert) . ' return false;') . _("Save in your Address Book") . ''; + $text .= $this->_outputStatus(); + } + } + } + } + + if (isset($mime_message)) { + /* We need to stick the output into a IMP_Contents object. */ + $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); + $mc->buildMessage(); + + $text .= '' . $mc->getMessage(true) . '
'; + } else { + $text = $this->_outputStatus(); + } + + return $text; + } + + /** + * Generates HTML output for 'multipart/encrypted', + * 'application/pkcs7-mime' and + * 'application/x-pkcs7-mime' MIME_Parts. + * + * @return string The HTML output. + */ + protected function _outputSMIMEEncrypted() + { + $active = $GLOBALS['prefs']->getValue('use_smime'); + $mime = &$this->mime_part; + $mimetype = $mime->getType(); + $msg = ''; + + $this->_initStatus($this->getIcon($mime->getType()), _("S/MIME")); + $this->_status[] = _("This message has been encrypted via S/MIME."); + + if (!$active) { + $this->_status[] = _("S/MIME support is not currently enabled so the message is unable to be decrypted."); + return $this->_outputStatus(); + } + + if (!$this->_impSmime->getPersonalPrivateKey()) { + $this->_status[] = _("No personal private key exists so the message is unable to be decrypted."); + return $this->_outputStatus(); + } + + /* Make sure we have a passphrase. */ + $passphrase = $this->_impSmime->getPassphrase(); + if ($passphrase === false) { + if (isset($_SESSION['imp']['viewmode']) && + ($_SESSION['imp']['viewmode'] == 'imp')) { + // TODO: Fix to work with DIMP + $url = $this->_impSmime->getJSOpenWinCode('open_passphrase_dialog'); + $this->_status[] = Horde::link('#', _("You must enter the passphrase for your S/MIME private key to view this message"), null, null, $url . ' return false;') . '' . _("You must enter the passphrase for your S/MIME private key to view this message") . '.'; + $msg .= $this->_outputStatus() . + ''; + } + return $msg; + } + + $raw_text = $this->_getRawSMIMEText(); + $decrypted_data = $this->_impSmime->decryptMessage($raw_text); + + if (is_a($decrypted_data, 'PEAR_Error')) { + $this->_status[] = $decrypted_data->getMessage(); + return $this->_outputStatus(); + } + + /* We need to check if this is a signed/encrypted message. */ + $mime_message = Horde_Mime_Message::parseMessage($decrypted_data); + if ($mime_message) { + /* Check for signed and encoded data. */ + if (in_array($mime_message->getType(), array('multipart/signed', 'application/pkcs7-mime', 'application/x-pkcs7-mime'))) { + $mime_message->setContents($decrypted_data); + $mime_message->splitContents(); + $mime_message->setInformation('smime_signenc', true); + if (($from = $this->_headers->getValue('from'))) { + $mime_message->setInformation('smime_from', $from); + } + } else { + $msg .= $this->_outputStatus(); + } + + /* We need to stick the output into a IMP_Contents object. */ + $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); + $mc->buildMessage(); + $msg .= '' . $mc->getMessage(true) . '
'; + } else { + require_once 'Horde/Text/Filter.php'; + $msg .= $this->_outputStatus() . + '' . Text_Filter::filter($decrypted_data, 'text2html', array('parselevel' => TEXT_HTML_SYNTAX)) . ''; + } + + return $msg; + } + + /** + * Return text/html as the content-type. + * + * @return string "text/html" constant. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } + + /** + * Get the headers of the S/MIME message. + */ + protected function _getRawSMIMEText() + { + $mime = &$this->mime_part; + + $mime->setContents($this->_contents->getBody()); + if (is_a($this->_contents, 'IMP_Contents') && + (($mime->getMIMEId() == 0) || + ($mime->splitContents() == false))) { + $this->_headers = $this->_contents->getHeaderOb(); + return $this->_contents->fullMessageText(); + } else { + $header_text = $mime->getCanonicalContents(); + $header_text = substr($header_text, 0, strpos($header_text, "\r\n\r\n")); + $this->_headers = MIME_Headers::parseHeaders($header_text); + + $mime_headers = new MIME_Headers(); + foreach (array('Content-Type', 'From', 'To') as $val) { + $tmp = $this->_headers->getValue($val); + if (!empty($tmp)) { + $mime_headers->addHeader($val, $tmp); + } + } + + return $mime_headers->toString() . $mime->toCanonicalString(); + } + } + + /* Various formatting helper functions. */ + protected function _initStatus($src, $alt = '') + { + if ($this->_icon === null) { + $this->_icon = Horde::img($src, $alt, 'height="16" width="16"', ''); + } + } + + protected function _outputStatus() + { + $output = ''; + if (!empty($this->_status)) { + $output = $this->formatStatusMsg($this->_status, $this->_icon); + } + $this->_icon = null; + $this->_status = array(); + return $output; + } + + /** + * Generates HTML output for the S/MIME signature test. + * + * @param string $result Result string of the S/MIME output concerning + * the signature test. + * @param string $email The email of the sender. + * + * @return string The HTML output. + */ + protected function _outputSMIMESignatureTest($result, $email) + { + $text = ''; + + if (is_a($result, 'PEAR_Error')) { + if ($result->getCode() == 'horde.warning') { + $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/warning.png', _("Warning")); + } else { + $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/error.png', _("Error")); + } + $result = $result->getMessage(); + } else { + $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/success.png', _("Success")); + /* This message has been verified but there was no output + from the PGP program. */ + if (empty($result) || ($result === true)) { + $email = (is_array($email)) ? implode(', ', $email): $email; + $result = sprintf(_("The message has been verified. Sender: %s."), htmlspecialchars($email)); + } + } + + require_once 'Horde/Text/Filter.php'; + + $this->_status[] = Text_Filter::filter($result, 'text2html', array('parselevel' => TEXT_HTML_NOHTML)); + + return $this->_outputStatus(); + } + + /** + * Render out attachment information. + * + * @param array $params An array with a reference to a IMP_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function renderAttachmentInfo($params) + { + $this->_contents = &$params[0]; + + $type = $this->_getSMIMEType(); + switch ($type) { + case 'signed': + $this->_status[] = _("This message contains an attachment that has been digitally signed via S/MIME."); + break; + + case 'encrypted': + $this->_status[] = _("This message contains an attachment that has been encrypted via S/MIME."); + break; + } + + $this->_status[] = sprintf(_("Click %s to view the attachment in a separate window."), $this->_contents->linkViewJS($this->mime_part, 'view_attach', _("HERE"), _("View attachment in a separate window"))); + $this->_initStatus($this->getIcon($this->mime_part->getType()), _("S/MIME")); + return $this->_outputStatus(); + } + + /** + * Deterimne the S/MIME type of the message. + * + * @return string Either 'encrypted' or 'signed'. + */ + protected function _getSMIMEType() + { + $type = $this->mime_part->getType(); + if (in_array($type, array('application/pkcs7-mime', 'application/x-pkcs7-mime'))) { + $smime_type = $this->mime_part->getContentTypeParameter('smime-type'); + if ($smime_type == 'signed-data') { + return 'signed'; + } elseif (!$smime_type || ($smime_type == 'enveloped-data')) { + return 'encrypted'; + } + } + + switch ($type) { + case 'multipart/signed': + case 'application/pkcs7-signature': + case 'application/x-pkcs7-signature': + return 'signed'; + } + } +} diff --git a/imp/lib/Mime/Viewer/plain.php b/imp/lib/Mime/Viewer/plain.php new file mode 100644 index 000000000..f5887b851 --- /dev/null +++ b/imp/lib/Mime/Viewer/plain.php @@ -0,0 +1,115 @@ + + * @author Michael Slusarz + * @package Horde_Mime_Viewer + */ +class IMP_Horde_Mime_Viewer_plain extends Horde_Mime_Viewer_plain +{ + /** + * TODO + */ + protected function _renderInline() + { + global $conf, $prefs; + + // Trim extra whitespace in the text. + $text = rtrim($this->_mimepart->getContents()); + if ($text == '') { + return array( + 'data' => '', + 'status' => array() + ); + } + + // If requested, scan the message for PGP data. + if (!empty($conf['utils']['gnupg']) && + $prefs->getValue('pgp_scan_body') && + preg_match('/-----BEGIN PGP ([^-]+)-----/', $text)) { + require_once IMP_BASE . '/lib/Crypt/PGP.php'; + $imp_pgp = new IMP_PGP(); + if (($out = $imp_pgp->parseMessageOutput($this->_mimepart, $this->_params['contents']))) { + return array( + 'data' => $out, + 'status' => array() + ); + } + } + + // 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); + } + + // Build filter stack. Starts with HTML markup and tab expansion. + require_once 'Horde/Text/Filter.php'; + $filters = array( + 'text2html' => array( + 'parselevel' => TEXT_HTML_MICRO, + 'charset' => $this->_mimepart->getCharset() + ), + 'tabs2spaces' => array() + ); + + // Highlight quoted parts of an email. + if ($prefs->getValue('highlight_text')) { + $show = $prefs->getValue('show_quoteblocks'); + $hideBlocks = ($show == 'hidden') || + (($show == 'thread') && (basename(Horde::selfUrl()) == 'thread.php')); + if (!$hideBlocks && in_array($show, array('list', 'listthread'))) { + $header = $this->_params['contents']->getHeaderOb(); + $imp_ui = new IMP_UI_Message(); + $list_info = $imp_ui->getListInformation($header); + $hideBlocks = $list_info['exists']; + } + $filters['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(); + } + + // Filter bad language. + if ($prefs->getValue('filtering')) { + $filters['words'] = array( + 'words_file' => $conf['msgsettings']['filtering']['words'], + 'replacement' => $conf['msgsettings']['filtering']['replacement'] + ); + } + + // Run filters. + $text = Text_Filter::filter($text, array_keys($filters), array_values($filters)); + + // Wordwrap. + $text = str_replace(array(' ', "\n "), array('  ', "\n "), $text); + if (!strncmp($text, ' ', 1)) { + $text = ' ' . substr($text, 1); + } + + return array( + 'data' => '
' . "\n" . $text . '
', + 'status' => array() + ); + } +} diff --git a/imp/lib/Mime/Viewer/related.php b/imp/lib/Mime/Viewer/related.php new file mode 100644 index 000000000..2879b6d13 --- /dev/null +++ b/imp/lib/Mime/Viewer/related.php @@ -0,0 +1,106 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_related extends Horde_Mime_Viewer_Driver +{ + /** + * The character set of the rendered HTML part. + * + * @var string + */ + protected $_charset = null; + + /** + * The mime type of the message part that has been chosen to be displayed. + * + * @var string + */ + protected $_viewerType = 'text/html'; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + $contents = &$params[0]; + + /* Look at the 'start' parameter to determine which part to start + with. If no 'start' parameter, use the first part. + RFC 2387 [3.1] */ + if ($this->mime_part->getContentTypeParameter('start') && + ($key = array_search($this->mime_part->getContentTypeParameter('start'), $this->mime_part->getCIDList()))) { + if (($pos = strrpos($key, '.'))) { + $id = substr($key, $pos + 1); + } else { + $id = $key; + } + } else { + $id = 1; + } + $start = $this->mime_part->getPart($this->mime_part->getRelativeMimeID($id)); + + /* Only display if the start part (normally text/html) can be displayed + inline -OR- we are viewing this part as an attachment. */ + if ($contents->canDisplayInline($start) || + $this->viewAsAttachment()) { + $text = $contents->renderMIMEPart($start); + $this->_viewerType = $contents->getMIMEViewerType($start); + $this->_charset = $start->getCharset(); + } else { + $text = ''; + } + + return $text; + } + + /** + * Render out attachment information. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function renderAttachmentInfo($params) + { + $contents = &$params[0]; + + $msg = sprintf(_("Click %s to view this multipart/related part in a separate window."), $contents->linkViewJS($this->mime_part, 'view_attach', _("HERE"), _("View content in a separate window"))); + return $this->formatStatusMsg($msg, Horde::img('mime/html.png', _("HTML")), false); + } + + /** + * Return the content-type. + * + * @return string The content-type of the message. + */ + public function getType() + { + return $this->_viewerType . '; charset=' . $this->getCharset(); + } + + /** + * Returns the character set used for the Viewer. + * + * @return string The character set used by this Viewer. + */ + public function getCharset() + { + return ($this->_charset === null) ? NLS::getCharset() : $this->_charset; + } +} diff --git a/imp/lib/Mime/Viewer/rfc822.php b/imp/lib/Mime/Viewer/rfc822.php new file mode 100644 index 000000000..d15dbd342 --- /dev/null +++ b/imp/lib/Mime/Viewer/rfc822.php @@ -0,0 +1,62 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_rfc822 extends Horde_Mime_Viewer_rfc822 +{ + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + $contents = &$params[0]; + + /* Get the entire body part of the message/rfc822 contents. */ + if (!$this->mime_part->getInformation('imp_contents_set') && + is_a($contents, 'IMP_Contents') && + $this->mime_part->getMIMEId()) { + $this->mime_part = &$contents->getDecodedMIMEPart($this->mime_part->getMIMEId(), true); + } + + return parent::render(); + } + + /** + * Render out attachment information. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function renderAttachmentInfo($params) + { + $contents = &$params[0]; + + if (is_a($contents, 'IMP_Contents') && + !$this->mime_part->getContents()) { + $id = $this->mime_part->getMIMEId(); + $hdr_id = substr($id, -2); + if ($hdr_id != '.0') { + $id .= '.0'; + } + $this->mime_part = &$contents->getDecodedMIMEPart($id); + } + + return parent::renderAttachmentInfo(); + } +} diff --git a/imp/lib/Mime/Viewer/smil.php b/imp/lib/Mime/Viewer/smil.php new file mode 100644 index 000000000..76b039b85 --- /dev/null +++ b/imp/lib/Mime/Viewer/smil.php @@ -0,0 +1,101 @@ + + * @author Michael Slusarz + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_smil extends Horde_Mime_Viewer_smil +{ + /** + * The MIME_Contents object, needed for the _callback() function. + * + * @var MIME_Contents + */ + protected $_contents; + + /** + * The list of related parts to the current part. + * + * @var array + */ + protected $_related = null; + + /** + * Renders out the contents. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params) + { + $this->_contents = &$params[0]; + return parent::render($params); + } + + /** + * User-defined function callback for start elements. + * + * @param object $parser Handle to the parser instance. + * @param string $name The name of this XML element. + * @param array $attrs List of this element's attributes. + */ + protected function _startElement($parser, $name, $attrs) + { + switch ($name) { + case 'IMG': + if (isset($attrs['SRC'])) { + $rp = $this->_getRelatedLink($attrs['SRC']); + if ($rp !== false) { + $this->_content .= '
'; + } + } + break; + + case 'TEXT': + if (isset($attrs['SRC'])) { + $rp = $this->_getRelatedLink($attrs['SRC']); + if ($rp !== false) { + $this->_content .= htmlspecialchars($rp->getContents()) . '
'; + } + } + break; + } + } + + /** + * Get related parts. + * + * @param string $cid The CID to search for. + * + * @return mixed Either the related MIME_Part or false. + */ + protected function _getRelatedLink($cid) + { + if ($this->_related === null) { + $this->_related = false; + $related = $this->mime_part->getInformation('related_part'); + if ($related !== false) { + $relatedPart = $this->_contents->getMIMEPart($related); + $this->_related = $relatedPart->getCIDList(); + } + } + + if ($this->_related) { + $key = array_search('<' . $cid . '>', $this->_related); + if ($key !== false) { + $cid_part = $this->_contents->getDecodedMIMEPart($key); + return $cid_part; + } + } + + return false; + } +} diff --git a/imp/lib/Mime/Viewer/status.php b/imp/lib/Mime/Viewer/status.php new file mode 100644 index 000000000..17ab23099 --- /dev/null +++ b/imp/lib/Mime/Viewer/status.php @@ -0,0 +1,179 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_status extends Horde_Mime_Viewer_Driver +{ + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function render($params) + { + $contents = &$params[0]; + + /* If this is a straight message/delivery-status part, just output + the text. */ + if ($this->mime_part->getType() == 'message/delivery-status') { + $part = new Horde_Mime_Part('text/plain'); + $part->setContents($this->mime_part->getContents()); + return '
' . $contents->renderMIMEPart($part) . '
'; + } + + global $registry; + + /* 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 2464 [2.3.3]). It can be either 'failed', + 'delayed', 'delivered', 'relayed', or 'expanded'. */ + $action = null; + $part2 = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(2)); + if (empty($part2)) { + return $this->_errorMsg($contents); + } + + foreach (explode("\n", $part2->getContents()) as $line) { + if (strstr($line, 'Action:') !== false) { + $pos = strpos($line, ':') + 1; + $action = strtolower(trim(substr($line, $pos))); + break; + } + } + if (strpos($action, ' ') !== false) { + $action = substr($action, 0, strpos($action, ' ')); + } + + /* Get the correct text strings for the action type. */ + switch ($action) { + case 'failed': + case 'delayed': + $graphic = 'alerts/error.png'; + $alt = _("Error"); + $msg = array( + _("ERROR: Your message could not be delivered."), + _("The mail server generated the following error message:") + ); + $detail_msg = _("Additional message error details can be viewed %s."); + $detail_msg_status = _("Additional message error details"); + $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': + $graphic = 'alerts/success.png'; + $alt = _("Success"); + $msg = array( + _("Your message was successfully delivered."), + _("The mail server generated the following message:") + ); + $detail_msg = _("Additional message details can be viewed %s."); + $detail_msg_status = _("Additional message details"); + $msg_link = _("The text of the message can be viewed %s."); + $msg_link_status = _("The text of the message"); + break; + + default: + $graphic = ''; + $alt = ''; + $msg = ''; + $detail_msg = ''; + $detail_msg_status = ''; + $msg_link = ''; + $msg_link_status = ''; + } + + /* Print the human readable message. */ + $part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(1)); + if (empty($part)) { + return $this->_errorMsg($contents); + } + + $statusimg = Horde::img($graphic, $alt, 'height="16" width="16"', $registry->getImageDir('horde')); + $text = $this->formatStatusMsg($msg, $statusimg); + $text .= '
' . $contents->renderMIMEPart($part) . '
' . "\n"; + + /* Display a link to more detailed error message. */ + $detailed_msg = array( + sprintf($detail_msg, $contents->linkViewJS($part2, 'view_attach', _("HERE"), $detail_msg_status)) + ); + + /* Display a link to the returned message. Try to download the + text of the message/rfc822 part first, if it exists. + TODO: Retrieving by part ID 3.0 is deprecated. Remove this once + Horde 4.0 is released. */ + if (($part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId(3))) || + ($part = $contents->getDecodedMIMEPart($this->mime_part->getRelativeMIMEId('3.0')))) { + $detailed_msg[] = sprintf($msg_link, $contents->linkViewJS($part, 'view_attach', _("HERE"), $msg_link_status, null, array('ctype' => 'message/rfc822'))); + } + + $infoimg = Horde::img('info_icon.png', _("Info"), 'height="16" width="16"', $registry->getImageDir('horde')); + $text .= $this->formatStatusMsg($detailed_msg, $infoimg, false); + + return $text; + } + + + /** + * Return the content-type. + * + * @return string The content-type of the message. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } + + /** + * Returns an error string for a malformed RFC 3464 message. + * + * @param MIME_Contents &$contents The MIME_Contents object for this + * message. + * + * @return string The error message string. + */ + protected function _errorMsg(&$contents) + { + $err_msg = array( + _("This message contains mail delivery status information, but the format of this message is unknown."), + _("Below is the raw text of the status information message.") + ); + $img = Horde::img('alerts/error.png', _("Error"), 'height="16" width="16"', $GLOBALS['registry']->getImageDir('horde')); + + $text = $this->formatStatusMsg($err_msg, $img); + + /* There is currently no BC way to obtain all parts from a message + * and display the summary of each part. So instead, display the + * entire raw contents of the message. See Bug 3757. */ + $text .= '
';
+        if (is_a($contents, 'IMP_Contents')) {
+            $contents->rebuildMessage();
+            $message = $contents->getMIMEMessage();
+            $text .= $contents->toString($message, true);
+        } else {
+            $text .= htmlspecialchars($this->mime_part->getContents());
+        }
+
+        return $text . '
'; + } + +} diff --git a/imp/lib/Mime/Viewer/tnef.php b/imp/lib/Mime/Viewer/tnef.php new file mode 100644 index 000000000..2f3061579 --- /dev/null +++ b/imp/lib/Mime/Viewer/tnef.php @@ -0,0 +1,136 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_tnef extends Horde_Mime_Viewer_tnef +{ + /** + * The contentType of the attachment. + * + * @var string + */ + protected $_contentType = 'application/octet-stream'; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string Either the list of tnef files or the data of an + * individual tnef file. + */ + public function render($params) + { + $contents = &$params[0]; + + $text = ''; + + /* Get the data from the attachment. */ + $tnefData = $this->_getSubparts(); + + /* Display the requested file. Its position in the $tnefData + array can be found in 'tnef_attachment'. */ + if (Util::getFormData('tnef_attachment')) { + $tnefKey = Util::getFormData('tnef_attachment') - 1; + /* Verify that the requested file exists. */ + if (isset($tnefData[$tnefKey])) { + $text = $tnefData[$tnefKey]['stream']; + if (empty($text)) { + $text = $this->formatStatusMsg(_("Could not extract the requested file from the MS-TNEF attachment.")); + } else { + $this->mime_part->setName($tnefData[$tnefKey]['name']); + $this->mime_part->setType($tnefData[$tnefKey]['type'] . '/' . $tnefData[$tnefKey]['subtype']); + } + } else { + $text = $this->formatStatusMsg(_("The requested file does not exist in the MS-TNEF attachment.")); + } + } else { + $text = $this->renderAttachmentInfo(array($params[0])); + } + + return $text; + } + + /** + * Render out TNEF attachment information. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string The rendered text in HTML. + */ + public function renderAttachmentInfo($params) + { + $contents = &$params[0]; + + $text = ''; + + /* Make sure the contents are in the MIME_Part object. */ + if (!$this->mime_part->getContents()) { + $this->mime_part->setContents($contents->getBodyPart($this->mime_part->getMIMEId())); + } + + /* Get the data from the attachment. */ + $tnefData = $this->_getSubparts(); + + if (!count($tnefData)) { + $text = $this->formatStatusMsg(_("No attachments found.")); + } else { + $text = $this->formatStatusMsg(_("The following files were attached to this part:")) . '
'; + foreach ($tnefData as $key => $data) { + $temp_part = $this->mime_part; + $temp_part->setName($data['name']); + $temp_part->setDescription($data['name']); + + /* Short-circuit MIME-type guessing for winmail.dat parts; + we're showing enough entries for them already. */ + $type = $data['type'] . '/' . $data['subtype']; + if (($type == 'application/octet-stream') || + ($type == 'application/base64')) { + $type = Horde_Mime_Magic::filenameToMIME($data['name']); + } + $temp_part->setType($type); + + $link = $contents->linkView($temp_part, 'view_attach', htmlspecialchars($data['name']), array('jstext' => sprintf(_("View %s"), $data['name']), 'viewparams' => array('tnef_attachment' => ($key + 1)))); + $text .= _("Attached File:") . '  ' . $link . '  (' . $data['type'] . '/' . $data['subtype'] . ")
\n"; + } + } + + return $text; + } + + /** + * List any embedded attachments in the TNEF part. + * + * @return array An array of any embedded attachments. + */ + protected function _getSubparts() + { + $tnef = &Horde_Compress::singleton('tnef'); + return $tnef->decompress($this->mime_part->transferDecode()); + } + + /** + * Return the content-type. + * + * @return string The content-type of the output. + */ + public function getType() + { + if (Util::getFormData('tnef_attachment')) { + return $this->_contentType; + } else { + return 'text/html; charset=' . NLS::getCharset(); + } + } +} diff --git a/imp/lib/Mime/Viewer/zip.php b/imp/lib/Mime/Viewer/zip.php new file mode 100644 index 000000000..a2faf0111 --- /dev/null +++ b/imp/lib/Mime/Viewer/zip.php @@ -0,0 +1,103 @@ + + * @author Michael Slusarz + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_zip extends Horde_Mime_Viewer_zip +{ + /** + * The IMP_Contents object, needed for the _callback() function. + * + * @var IMP_Contents + */ + protected $_contents; + + /** + * Render out the currently set contents. + * + * @param array $params An array with a reference to a MIME_Contents + * object. + * + * @return string Either the list of zip files or the data of an + * individual zip file. + */ + public function render($params) + { + $contents = &$params[0]; + + $data = $this->mime_part->getContents(); + $text = ''; + + /* Send the requested file. Its position in the zip archive is located + * in 'zip_attachment'. */ + if (Util::getFormData('zip_attachment')) { + $zip = &Horde_Compress::singleton('zip'); + $fileKey = Util::getFormData('zip_attachment') - 1; + $zipInfo = $zip->decompress( + $data, array('action' => HORDE_COMPRESS_ZIP_LIST)); + /* Verify that the requested file exists. */ + if (isset($zipInfo[$fileKey])) { + $text = $zip->decompress( + $data, + array('action' => HORDE_COMPRESS_ZIP_DATA, + 'info' => &$zipInfo, + 'key' => $fileKey)); + if (empty($text)) { + $text = '
' . _("Could not extract the requested file from the Zip archive.") . '
'; + } else { + $this->mime_part->setType('application/octet-stream'); + $this->mime_part->setName(basename($zipInfo[$fileKey]['name'])); + } + } else { + $text = '
' . _("The requested file does not exist in the Zip attachment.") . '
'; + } + } else { + $this->_contents = $contents; + $text = parent::_render($data, array($this, '_callback')); + } + + return $text; + } + + /** + * The function to use as a callback to parent::_render(). + * + * @param integer $key The position of the file in the zip archive. + * @param array $val The information array for the archived file. + * + * @return string The content-type of the output. + */ + protected function _callback($key, $val) + { + $name = preg_replace('/( )+$/', '', $val['name']); + if (!empty($val['size']) && (strstr($val['attr'], 'D') === false) && + ((($val['method'] == 0x8) && Util::extensionExists('zlib')) || + ($val['method'] == 0x0))) { + $old_name = $this->mime_part->getName(); + $this->mime_part->setName(basename($name)); + $val['name'] = str_replace( + $name, + $this->_contents->linkView( + $this->mime_part, 'download_render', $name, + array('jstext' => sprintf(_("View %s"), + str_replace(' ', ' ', $name)), + 'class' => 'fixed', + 'viewparams' => array( + 'ctype' => 'application/zip', + 'zip_attachment' => (urlencode($key) + 1)))), + $val['name']); + $this->mime_part->setName($old_name); + } + + return $val; + } +} diff --git a/imp/message.php b/imp/message.php index 463b5349c..9592231fa 100644 --- a/imp/message.php +++ b/imp/message.php @@ -240,19 +240,32 @@ $flags = $flags_ret[$index]['flags']; $mime_headers = $fetch_ret[$index]['headertext'][0]; $use_pop = ($_SESSION['imp']['protocol'] == 'pop'); -/* Parse MIME info and create the body of the message. */ +/* Parse the message. */ $imp_contents = &IMP_Contents::singleton($index . IMP::IDX_SEP . $mailbox_name); if (is_a($imp_contents, 'PEAR_Error')) { _returnToMailbox(null, 'message_missing'); require IMP_BASE . '/mailbox.php'; exit; } -$summary = $imp_contents->getSummary(array( - 'message_token' => $message_token, - 'show_links' => !$printer_friendly, - 'strip' => !$readonly && $prefs->getValue('strip_attachments') -)); -$inline_parts = $imp_contents->getInlineParts(); + +$contents_mask = IMP_Contents::SUMMARY_RENDER | + IMP_Contents::SUMMARY_BYTES | + IMP_Contents::SUMMARY_SIZE | + IMP_Contents::SUMMARY_ICON; +if ($printer_friendly) { + $contents_mask |= IMP_Contents::SUMMARY_DESCRIP_NOLINK; +} else { + $contents_mask |= IMP_Contents::SUMMARY_DESCRIP_LINK | + IMP_Contents::SUMMARY_DOWNLOAD | + IMP_Contents::SUMMARY_DOWNLOAD_ZIP | + IMP_Contents::SUMMARY_IMAGE_SAVE | + IMP_Contents::SUMMARY_DOWNLOAD_ALL; + if (!$readonly && $prefs->getValue('strip_attachments')) { + $contents_mask |= IMP_Contents::SUMMARY_STRIP_LINK; + } +} + +$summary = $imp_contents->getSummary($contents_mask); /* Get the title/mailbox label of the mailbox page. */ $page_label = IMP::getLabel($imp_mbox['mailbox']);