From: Michael M Slusarz Date: Tue, 10 Mar 2009 21:59:41 +0000 (-0600) Subject: Rename Mime drivers X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=7dc3f0b944c0bebc4d64f09f749ebf6929f998fc;p=horde.git Rename Mime drivers --- diff --git a/imp/lib/Mime/Viewer/Alternative.php b/imp/lib/Mime/Viewer/Alternative.php new file mode 100644 index 000000000..e62860ac9 --- /dev/null +++ b/imp/lib/Mime/Viewer/Alternative.php @@ -0,0 +1,96 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Alternative extends Horde_Mime_Viewer_Driver +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => true, + 'full' => false, + 'info' => false, + 'inline' => true, + ); + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + $base_id = $this->_mimepart->getMimeId(); + $subparts = $this->_mimepart->contentTypeMap(); + + $base_ids = $display_ids = $ret = array(); + + /* Look for a displayable part. RFC: show the LAST choice that can be + * displayed inline. If an alternative is itself a multipart, the user + * agent is allowed to show that alternative, an earlier alternative, + * or both. If we find a multipart alternative that contains at least + * one viewable part, we will display all viewable subparts of that + * alternative. */ + foreach (array_keys($subparts) as $mime_id) { + $ret[$mime_id] = null; + if ((strcmp($base_id, $mime_id) !== 0) && + $this->_params['contents']->canDisplay($mime_id, IMP_Contents::RENDER_INLINE)) { + $display_ids[strval($mime_id)] = true; + } + } + + /* If we found no IDs, return now. */ + if (empty($display_ids)) { + $ret[$base_id] = array( + 'data' => '', + 'status' => array( + array( + 'text' => array(_("There are no alternative parts that can be displayed inline.")), + 'type' => 'info' + ) + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ); + return $ret; + } + + /* If the last viewable message exists in a subpart, back up to the + * base multipart and display all viewable parts in that multipart. + * Else, display the single part. */ + end($display_ids); + $curr_id = key($display_ids); + while (!is_null($curr_id) && (strcmp($base_id, $curr_id) !== 0)) { + if (isset($subparts[$curr_id])) { + $disp_id = $curr_id; + } + $curr_id = Horde_Mime::mimeIdArithmetic($curr_id, 'up'); + } + + /* Now grab all keys under this ID. */ + $render_part = $this->_mimepart->getPart($disp_id); + foreach (array_keys($render_part->contentTypeMap()) as $val) { + if (isset($display_ids[$val])) { + $render = $this->_params['contents']->renderMIMEPart($val, IMP_Contents::RENDER_INLINE, array('params' => $this->_params)); + foreach (array_keys($render) as $id) { + $ret[$id] = $render[$id]; + unset($display_ids[$id]); + } + } + } + + return $ret; + } +} diff --git a/imp/lib/Mime/Viewer/Appledouble.php b/imp/lib/Mime/Viewer/Appledouble.php new file mode 100644 index 000000000..19f1eb24b --- /dev/null +++ b/imp/lib/Mime/Viewer/Appledouble.php @@ -0,0 +1,112 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Appledouble extends Horde_Mime_Viewer_Driver +{ + /** + * This driver's capabilities. + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => true, + 'full' => false, + 'info' => true, + 'inline' => true + ); + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + return $this->_IMPrender(true); + } + + /** + * Return the rendered information about the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInfo() + { + return $this->_IMPrender(false); + } + + /** + * Render the part based on the view mode. + * + * @param boolean $inline True if viewing inline. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _IMPrender($inline) + { + /* RFC 1740 [4]: There are two parts to an appledouble message: + * (1) application/applefile + * (2) Data embedded in the Mac file + * Since the resource fork is not very useful to us, only provide a + * means to download. */ + + /* Display the resource fork download link. */ + $mime_id = $this->_mimepart->getMimeId(); + $parts_list = array_keys($this->_mimepart->contentTypeMap()); + reset($parts_list); + $applefile_id = next($parts_list); + $data_id = Horde_Mime::mimeIdArithmetic($applefile_id, 'next'); + + $applefile_part = $this->_mimepart->getPart($applefile_id); + $data_part = $this->_mimepart->getPart($data_id); + + $data_name = $data_part->getName(true); + if (empty($data_name)) { + $data_name = _("unnamed"); + } + + $status = array( + 'icon' => Horde::img('apple.png', _("Macintosh File")), + 'text' => array( + sprintf(_("This message contains a Macintosh file (named \"%s\")."), $data_name), + sprintf(_("The Macintosh resource fork can be downloaded %s."), $this->_params['contents']->linkViewJS($applefile_part, 'download_attach', _("HERE"), array('jstext' => _("The Macintosh resource fork")))) + ) + ); + + /* For inline viewing, attempt to display the data inline. */ + $ret = array(); + if ($inline && (($disp = $this->_params['contents']->canDisplay($data_part, IMP_Contents::RENDER_INLINE | IMP_Contents::RENDER_INFO)))) { + $ret = $this->_params['contents']->renderMIMEPart($data_id, $disp, array('params' => $this->_params)); + $status['text'][] = _("The contents of the Macintosh file are below."); + } else { + $status['text'][] = sprintf(_("The contents of the Macintosh file can be downloaded %s."), $this->_params['contents']->linkViewJS($data_part, 'download_attach', _("HERE"), array('jstext' => _("The Macintosh file")))); + } + + foreach ($parts_list as $val) { + if (!isset($ret[$val]) && (strcmp($val, $data_id) !== 0)) { + $ret[$val] = (strcmp($val, $mime_id) === 0) + ? array( + 'data' => '', + 'status' => array($status), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + : null; + } + } + + ksort($ret); + + return $ret; + } +} diff --git a/imp/lib/Mime/Viewer/Enriched.php b/imp/lib/Mime/Viewer/Enriched.php new file mode 100644 index 000000000..ca3413b3f --- /dev/null +++ b/imp/lib/Mime/Viewer/Enriched.php @@ -0,0 +1,79 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Enriched extends Horde_Mime_Viewer_Enriched +{ + /** + * Return the full rendered version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _render() + { + $ret = parent::_render(); + if (!empty($ret)) { + reset($ret); + $ret[key($ret)]['data'] = $this->_IMPformat($ret[key($ret)]['data']); + } + return $ret; + } + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + $ret = parent::_renderInline(); + if (!empty($ret)) { + reset($ret); + $ret[key($ret)]['data'] = $this->_IMPformat($ret[key($ret)]['data']); + } + return $ret; + } + + /** + * Format output text with IMP additions. + * + * @param string $text The HTML text. + * + * @return string The text with extra IMP formatting applied. + */ + protected function _IMPformat($text) + { + // Highlight quoted parts of an email. + if ($GLOBALS['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 ($GLOBALS['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. + return IMP::filterText($text); + } +} diff --git a/imp/lib/Mime/Viewer/Html.php b/imp/lib/Mime/Viewer/Html.php new file mode 100644 index 000000000..69000c64a --- /dev/null +++ b/imp/lib/Mime/Viewer/Html.php @@ -0,0 +1,290 @@ + + * @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'; + + /** + * Return the full rendered version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _render() + { + $render = $this->_IMPrender(false); + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $render['html'], + 'status' => $render['status'], + 'type' => $this->_mimepart->getType(true) + ) + ); + } + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + $render = $this->_IMPrender(true); + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $render['html'], + 'status' => $render['status'], + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + } + + /** + * 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']; + + /* We are done processing if in mimp mode. */ + if ($_SESSION['imp']['view'] == 'mimp') { + require_once 'Horde/Text/Filter.php'; + $data = Text_Filter::filter($data, 'html2text'); + + // Filter bad language. + return array('html' => IMP::filterText($data), 'status' => array()); + } + + /* 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 links that we can display (multipart/related + * parts). */ + if (isset($this->_params['related_id'])) { + $cid_replace = array(); + + foreach ($this->_params['related_cids'] as $mime_id => $cid) { + $cid = trim($cid, '<>'); + if ($cid) { + $cid_part = $this->_params['contents']->getMIMEPart($mime_id); + $cid_replace['cid:' . $cid] = $this->_params['contents']->urlView($cid_part, 'view_attach', array('params' => array('img_data' => 1))); + } + } + + if (!empty($cid_replace)) { + $data = str_replace(array_keys($cid_replace), array_values($cid_replace), $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. */ + if ($inline && + !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(IMP::selfUrl(), array('actionID', 'index')); + $url = Util::addParameter($url, 'index', $this->_params['contents']->getIndex()); + + $view_img = Util::getFormData('view_html_images'); + $addr_check = ($GLOBALS['prefs']->getValue('html_image_addrbook') && $this->_inAddressBook()); + + if (!$view_img && !$addr_check) { + $data .= Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true) . + Util::bufferOutput(array('Horde', 'addScriptFile'), 'imp.js', 'imp', true); + + // Unblock javascript code in js/src/imp.js + $cleanhtml['status'][] = array( + 'icon' => Horde::img('mime/image.png'), + 'text' => array( + String::convertCharset(_("Images have been blocked to protect your privacy."), $charset, $msg_charset), + Horde::link(Util::addParameter($url, 'view_html_images', 1), '', 'unblockImageLink') . String::convertCharset(_("Show Images?"), $charset, $msg_charset) . '' + ) + ); + + $data = preg_replace_callback($this->_img_regex, array($this, '_blockImages'), $data); + } + } + + require_once 'Horde/Text/Filter.php'; + if ($GLOBALS['prefs']->getValue('emoticons')) { + $data = Text_Filter::filter($data, array('emoticons'), array(array('emoticons' => true))); + } + + 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(); + $headers = $this->_params['contents']->getHeaderOb(); + + /* Try to get back a result from the search. */ + $res = $GLOBALS['registry']->call('contacts/getField', array(Horde_Mime_Address::bareAddress($headers->getValue('from')), '__key', $params['sources'], false, true)); + return is_a($res, 'PEAR_Error') ? false : count($res); + } +} diff --git a/imp/lib/Mime/Viewer/Images.php b/imp/lib/Mime/Viewer/Images.php new file mode 100644 index 000000000..21f7cf6df --- /dev/null +++ b/imp/lib/Mime/Viewer/Images.php @@ -0,0 +1,282 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Images extends Horde_Mime_Viewer_Images +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => false, + 'full' => true, + 'info' => true, + 'inline' => true + ); + + /** + * Return the full rendered version of the Horde_Mime_Part object. + * + * URL parameters used by this function: + *
+     * 'imp_img_view' - (string) One of the following:
+     *   'data' - Output the image directly.
+     *   'load_convert' - TODO
+     *   'view_convert' - TODO
+     *   'view_thumbnail' - TODO
+     * 
+ * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _render() + { + switch (Util::getFormData('imp_img_view')) { + case 'data': + /* If calling page is asking us to output data, do that without + * any further delay and exit. */ + return parent::_render(); + + case 'view_convert': + /* Convert the image to browser-viewable format and display. */ + return $this->_viewConvert(false); + + case 'view_thumbnail': + /* Create the thumbnail and display. */ + return $this->_viewConvert(true); + + case 'load_convert': + /* The browser can display the image type directly - output the JS + * code to render the auto resize popup image window. */ + return $this->_popupImageWindow(); + } + + return parent::_render(); + } + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + /* Only display the image inline if the browser can display it and the + * size of the image is below the config value. */ + if ($GLOBALS['browser']->isViewable($this->_getType())) { + if (isset($this->_conf['inlinesize']) && + ($this->_mimepart->getBytes() < $this->_conf['inlinesize'])) { + /* Viewing inline, and the browser can handle the image type + * directly. So output an tag to load the image. */ + return array( + $this->_mimepart->getMimeId() => array( + 'data' => Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('imp_img_view' => 'data'))), $this->_mimepart->getName(true), null, ''), + 'status' => array(), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + } else { + return $this->_renderInfo(); + } + } + + /* The browser cannot view this image. Inform the user of this and + * ask user if we should convert to another image type. */ + $status = array(_("Your browser does not support inline display of this image type.")); + + /* See if we can convert to an inline browser viewable form. */ + if ($GLOBALS['browser']->hasFeature('javascript')) { + $img = $this->_getHordeImageOb(false); + if ($img && + $GLOBALS['browser']->isViewable($img->getContentType())) { + $convert_link = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("HERE"), array('params' => array('imp_img_view' => 'load_convert'))); + $status[] = sprintf(_("Click %s to convert the image file into a format your browser can attempt to view."), $convert_link); + } + } + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => '', + 'status' => array( + array( + 'text' => $status + ) + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + } + + /** + * Return the rendered information about the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInfo() + { + /* Display the thumbnail link only if we show thumbs for all images or + * if image is over 50 KB. Also, check to see if convert utility is + * available. */ + if ((!$this->getConfigParam('allthumbs') && + ($this->_mimepart->getBytes() < 51200)) || + !$this->_getHordeImageOb(false)) { + return array(); + } + + $status = array(sprintf(_("An image named %s is attached to this message. A thumbnail is below."), $this->_mimepart->getName(true))); + + if ($GLOBALS['browser']->hasFeature('javascript')) { + $status[] = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('imp_img_view' => 'view_thumbnail')), false), _("View Attachment"), null, ''), null, null, null); + } else { + $status[] = Horde::link($this->_params['contents']->urlView($this->_mimepart, 'view_attach')) . Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('imp_img_view' => 'view_thumbnail')), false), _("View Attachment"), null, '') . ''; + } + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => '', + 'status' => array( + array( + 'icon' => Horde::img('mime/image.png', _("Thumbnail of attached image")), + 'text' => $status + ) + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + } + + /** + * Generate the HTML output for the JS auto-resize view window. + * + * @return string The HTML output. + */ + protected function _popupImageWindow() + { + $self_url = Util::addParameter(IMP::selfUrl(), array('imp_img_view' => ((Util::getFormData('imp_img_view') == 'load_convert') ? 'view_convert' : 'data'))); + $title = $this->_mimepart->getName(true); + + $str = << + +$title + +EOD; + + /* Only use javascript if we are using a DOM capable browser. */ + if ($GLOBALS['browser']->getFeature('dom')) { + /* Javascript display. */ + $loading = _("Loading..."); + $str .= << +function resizeWindow() +{ + + var h, img = document.getElementById('disp_image'), w; + document.getElementById('splash').style.display = 'none'; + img.style.display = 'block'; + window.moveTo(0, 0); + h = img.height - (self.innerHeight ? self.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight)); + w = img.width - (self.innerWidth ? self.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth)); + window.resizeBy(w, h); + self.focus(); +} + +$loading +EOD; + } else { + /* Non-javascript display. */ + $img_txt = _("Image"); + $str .= << + +$img_txt + + +EOD; + } + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $str, + 'status' => array(), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + } + + /** + * Convert image. + * + * @param boolean $thumb View image in thumbnail size? + * + * @return string The image data. + */ + protected function _viewConvert($thumb) + { + $img = $this->_getHordeImageOb(true); + + 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'); + } + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $data, + 'status' => array(), + 'type' => $type + ) + ); + } + + /** + * Return a Horde_Image object. + * + * @param boolean $load Whether to load the image data. + * + * @return mixed The Horde_Image object, or false on error. + */ + protected function _getHordeImageOb($load) + { + $img = null; + $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); + } + + if (!$img || is_a($img, 'PEAR_Error')) { + return false; + } + + if ($load) { + $ret = $img->loadString(1, $this->_mimepart->getContents()); + if (is_a($ret, 'PEAR_Error')) { + return false; + } + } + + return $img; + } +} diff --git a/imp/lib/Mime/Viewer/Itip.php b/imp/lib/Mime/Viewer/Itip.php new file mode 100644 index 000000000..0a1fb154a --- /dev/null +++ b/imp/lib/Mime/Viewer/Itip.php @@ -0,0 +1,967 @@ + + * @author Mike Cochrane + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Itip extends Horde_Mime_Viewer_Driver +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => false, + 'full' => true, + 'info' => false, + 'inline' => true + ); + + /** + * Return the full rendered version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _render() + { + $ret = $this->_renderInline(); + if (!empty($ret)) { + reset($ret); + $ret[key($ret)]['data'] = Util::bufferOutput('include', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') . + $ret[key($ret)]['data'] . + Util::bufferOutput('include', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc'); + } + return $ret; + } + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * URL parameters used by this function: + *
+     * 'identity' - (integer) TODO
+     * 'itip_action' - (array) TODO
+     * 
+ * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + global $registry; + + $charset = NLS::getCharset(); + $data = $this->_mimepart->getContents(); + $mime_id = $this->_mimepart->getMimeId(); + + // Parse the iCal file. + $vCal = new Horde_iCalendar(); + if (!$vCal->parsevCalendar($data, 'VCALENDAR', $this->_mimepart->getCharset())) { + return array( + $mime_id => array( + 'data' => '

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

' . '
' . htmlspecialchars($data) . '
', + 'status' => array(), + 'type' => 'text/html; charset=' . $charset + ) + ); + } + + // Check if we got vcard data with the wrong vcalendar mime type. + $c = $vCal->getComponentClasses(); + if ((count($c) == 1) && !empty($c['horde_icalendar_vcard'])) { + return $this->_params['contents']->renderMIMEPart($mime_id, IMP_Contents::RENDER_INLINE, array('type' => 'text/x-vcard')); + } + + // Get the method type. + $method = $vCal->getAttribute('METHOD'); + if (is_a($method, 'PEAR_Error')) { + $method = ''; + } + + // Get the iCalendar file components. + $components = $vCal->getComponents(); + $msgs = array(); + + // Handle the action requests. + $actions = Util::getFormData('itip_action', array()); + foreach ($actions as $key => $action) { + 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')) { + $msgs[] = array('error', _("There was an error deleting the event:") . ' ' . $event->getMessage()); + } else { + $msgs[] = array('success', _("Event successfully deleted.")); + } + } else { + $msgs[] = 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')) { + $msgs[] = array('error', _("There was an error updating the event:") . ' ' . $event->getMessage()); + } else { + $msgs[] = array('success', _("Respondent Status Updated.")); + } + } else { + $msgs[] = 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')) { + // Try to update in calendar. + if ($registry->hasMethod('calendar/replace')) { + $result = $registry->call('calendar/replace', array('uid' => $guid, 'content' => $components[$key], 'contentType' => $this->mime_part->getType())); + if (is_a($result, 'PEAR_Error')) { + // Could be a missing permission. + $msgs[] = array('warning', _("There was an error updating the event:") . ' ' . $result->getMessage() . '. ' . _("Trying to import the event instead.")); + } else { + $handled = true; + $url = Horde::url($registry->link('calendar/show', array('uid' => $guid))); + $msgs[] = 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')) . ''); + } + } + } + if (!$handled && $registry->hasMethod('calendar/import')) { + // Import into calendar. + $handled = true; + $guid = $registry->call('calendar/import', array('content' => $components[$key], 'contentType' => $this->mime_part->getType())); + if (is_a($guid, 'PEAR_Error')) { + $msgs[] = array('error', _("There was an error importing the event:") . ' ' . $guid->getMessage()); + } else { + $url = Horde::url($registry->link('calendar/show', array('uid' => $guid))); + $msgs[] = 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) { + $msgs[] = 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')) { + $msgs[] = array('error', _("There was an error importing user's free/busy information:") . ' ' . $res->getMessage()); + } else { + $msgs[] = array('success', _("The user's free/busy information was sucessfully stored.")); + } + } else { + $msgs[] = 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')) { + $msgs[] = array('error', _("There was an error importing the task:") . ' ' . $guid->getMessage()); + } else { + $url = Horde::url($registry->link('tasks/show', array('uid' => $guid))); + $msgs[] = 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 { + $msgs[] = array('warning', _("This action is not supported.")); + } + break; + + case 'vJournal': + default: + $msgs[] = 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(); + $mime->setType('multipart/alternative'); + + $body = new Horde_Mime_Part(); + $body->setType('text/plain'); + $body->setCharset($charset); + $body->setContents(String::wrap($message, 76, "\n")); + + $ics = new Horde_Mime_Part(); + $ics->setType('text/calendar'); + $ics->setCharset($charset); + $ics->setContents($vCal->exportvCalendar()); + $ics->setName('event-reply.ics'); + $ics->setContentTypeParameter('METHOD', 'REPLY'); + + $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($subject, $charset)); + + // Send the reply. + $mail_driver = IMP_Compose::getMailDriver(); + try { + $mime->send($organizerEmail, $msg_headers, + $mail_driver['driver'], + $mail_driver['params']); + $msgs[] = array('success', _("Reply Sent.")); + } catch (Horde_Mime_Exception $e) { + $msgs[] = array('error', sprintf(_("Error sending reply: %s."), $e->getMessage())); + } + } else { + $msgs[] = 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); + + $message = _("Attached is a reply to a calendar request you sent."); + $body = new Horde_Mime_Part('text/plain', + String::wrap($message, 76, "\n"), + $charset); + + $ics = new Horde_Mime_Part('text/calendar', $vCal->exportvCalendar()); + $ics->setName('icalendar.ics'); + $ics->setContentTypeParameter('METHOD', 'REPLY'); + $ics->setCharset($charset); + + $mime = new Horde_Mime_Part(); + $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"), $charset)); + + // Send the reply. + $mail_driver = IMP_Compose::getMailDriver(); + try { + $mime->send($organizerEmail, $msg_headers, + $mail_driver['driver'], + $mail_driver['params']); + $msgs[] = array('success', _("Reply Sent.")); + } catch (Horde_Mime_Exception $e) { + $msgs[] = array('error', sprintf(_("Error sending reply: %s."), $e->getMessage())); + } + } else { + $msgs[] = array('warning', _("Invalid Action selected for this component.")); + } + break; + + case 'nosup': + // vFreebusy request. + default: + $msgs[] = array('warning', _("This action is not yet implemented.")); + break; + } + } + + // Create the HTML to display the iCal file. + $html = ''; + if ($_SESSION['imp']['view'] == 'imp') { + $html .= '
'; + } + + foreach ($components as $key => $component) { + switch ($component->getType()) { + case 'vEvent': + $html .= $this->_vEvent($component, $key, $method, $msgs); + break; + + case 'vTodo': + $html .= $this->_vTodo($component, $key, $method, $msgs); + break; + + case 'vTimeZone': + // Ignore them. + break; + + case 'vFreebusy': + $html .= $this->_vFreebusy($component, $key, $method, $msgs); + 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']['view'] == 'imp') { + $html .= '
'; + } + + return array( + $mime_id = array( + 'data' => $html, + 'status' => array(), + 'type' => 'text/html; charset=' . $charset + ) + ); + } + + /** + * Return the html for a vFreebusy. + */ + protected function _vFreebusy($vfb, $id, $method, $msgs) + { + global $registry, $prefs; + + $desc = $html = ''; + $sender = $vfb->getName(); + + switch ($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) . '

'; + + foreach ($msgs 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']['view'] != 'imp') { + return $html; + } + + $html .= '

' . _("Actions") . '

' . + ' '; + } + + /** + * Return the html for a vEvent. + */ + protected function _vEvent($vevent, $id, $method, $msgs) + { + global $registry, $prefs; + + $desc = $html = ''; + $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 ($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 . '

'; + + foreach ($msgs 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']['view'] != '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, $method, $msgs) + { + global $registry, $prefs; + + $desc = $html = ''; + $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 ($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 . '

'; + + foreach ($msgs 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']['view'] != '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"); + + case 'DECLINED': + return _("Declined"); + + case 'TENTATIVE': + return _("Tentatively Accepted"); + + case 'DELEGATED': + return _("Delegated"); + + case 'COMPLETED': + return _("Completed"); + + case 'IN-PROCESS': + return _("In Process"); + + case 'NEEDS-ACTION': + default: + return is_null($default) ? _("Needs Action") : $default; + } + } +} diff --git a/imp/lib/Mime/Viewer/Mdn.php b/imp/lib/Mime/Viewer/Mdn.php new file mode 100644 index 000000000..5755bfbd2 --- /dev/null +++ b/imp/lib/Mime/Viewer/Mdn.php @@ -0,0 +1,105 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Mdn extends Horde_Mime_Viewer_Driver +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => true, + 'full' => false, + 'info' => true, + 'inline' => true, + ); + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + /* If this is a straight message/disposition-notification part, just + * output the text. */ + if ($this->_mimepart->getType() == 'message/disposition-notification') { + return $this->_params['contents']->renderMIMEPart($this->_mimepart->getMIMEId(), IMP_Contents::RENDER_FULL, array('type' => 'text/plain', 'params' => $this->_params)); + } + + return $this->_renderInfo(); + } + + /** + * Return the rendered information about the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInfo() + { + $mdn_id = $this->_mimepart->getMimeId(); + $parts = array_keys($this->_mimepart->contentTypeMap()); + + $status = array( + array( + 'icon' => Horde::img('info_icon.png', _("Info"), null, $GLOBALS['registry']->getImageDir('horde')), + 'text' => array(_("A message you have sent has resulted in a return notification from the recipient.")) + ) + ); + + /* RFC 3798 [3]: There are three parts to a delivery status + * multipart/report message: + * (1) Human readable message + * (2) Machine parsable body part (message/disposition-notification) + * (3) Original message (optional) */ + + /* Print the human readable message. */ + reset($parts); + $curr_id = $first_id = next($parts); + $first_part = $this->_params['contents']->renderMIMEPart($curr_id, IMP_Contents::RENDER_INLINE_AUTO, array('params' => $this->_params)); + + /* Display a link to more detailed message. */ + $curr_id = Horde_Mime::mimeIdArithmetic($curr_id, 'next'); + $part = $this->_params['contents']->getMIMEPart($curr_id); + if ($part) { + $status[0]['text'][] = sprintf(_("Additional information can be viewed %s."), $this->_params['contents']->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("Additional information details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE)))); + } + + /* Display a link to the sent message. Try to download the text of + the message/rfc822 part first, if it exists. */ + $curr_id = Horde_Mime::mimeIdArithmetic($curr_id, 'next'); + $part = $this->_params['contents']->getMIMEPart($curr_id); + if ($part) { + $status[0]['text'][] = sprintf(_("The text of the sent message can be viewed %s."), $this->_params['contents']->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("The text of the sent message")))); + } + + if (empty($first_part)) { + $data = ''; + } else { + $status[0]['text'][] = _("The mail server generated the following informational message:"); + $status = array_merge($status, $first_part[$first_id]['status']); + $data = $first_part[$first_id]['data']; + } + + $ret = array_combine($parts, array_fill(0, count($parts), null)); + $ret[$mdn_id] = array( + 'data' => $data, + 'status' => $status, + 'type' => 'text/html; charset=' . NLS::getCharset() + ); + + return $ret; + } +} diff --git a/imp/lib/Mime/Viewer/Partial.php b/imp/lib/Mime/Viewer/Partial.php new file mode 100644 index 000000000..4572198f1 --- /dev/null +++ b/imp/lib/Mime/Viewer/Partial.php @@ -0,0 +1,84 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Partial extends Horde_Mime_Viewer_Driver +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => true, + 'forceinline' => true, + 'full' => false, + 'info' => false, + 'inline' => false, + ); + + /** + * If this MIME part can contain embedded MIME parts, and those embedded + * MIME parts exist, return a list of MIME parts that contain the embedded + * MIME part information. + * + * @return array An array of Horde_Mime_Part objects, with the key as + * the ID, or null if no embedded MIME parts exist. + */ + protected function _getEmbeddedMimeParts() + { + $id = $this->_mimepart->getContentTypeParameter('id'); + $number = $this->_mimepart->getContentTypeParameter('number'); + $total = $this->_mimepart->getContentTypeParameter('total'); + + if (is_null($id) || is_null($number) || is_null($total)) { + return null; + } + + $mbox = $this->_params['contents']->getMailbox(); + + /* Perform the search to find the other parts of the message. */ + $query = new Horde_Imap_Client_Search_Query(); + $query->headerText('Content-Type', $id); + $indices = $GLOBALS['imp_search']->runSearchQuery($query, $mbox); + + /* If not able to find the other parts of the message, print error. */ + if (count($indices) != $total) { + $mime_part = new Horde_Mime_Part(); + $mime_part->setType('text/plain'); + $mime_part->setCharset(NLS::getCharset()); + $mime_part->setContents(sprintf(_("[Cannot display message - found only %s of %s parts of this message in the current mailbox.]"), count($indices), $total)); + return array($this->_mimepart->getMimeId() => $mime_part); + } + + /* Get the contents of each of the parts. */ + $parts = array(); + foreach ($indices as $val) { + /* No need to fetch the current part again. */ + if ($val == $number) { + $parts[$number] = $this->_mimepart->getContents(); + } else { + $ic = &IMP_Contents::singleton($val . IMP::IDX_SEP . $mbox); + $parts[$ic->getMIMEMessage()->getContentTypeParameter('number')] = $ic->getBody(); + } + } + + /* Sort the parts in numerical order. */ + ksort($parts, SORT_NUMERIC); + + /* Combine the parts. */ + $mime_part = Horde_Mime_Part::parseMessage(implode('', $parts)); + return ($mime_part === false) + ? null + : array($this->_mimepart->getMimeId() => $mime_part); + } +} diff --git a/imp/lib/Mime/Viewer/Pdf.php b/imp/lib/Mime/Viewer/Pdf.php new file mode 100644 index 000000000..a2e9a7492 --- /dev/null +++ b/imp/lib/Mime/Viewer/Pdf.php @@ -0,0 +1,131 @@ + + * @package Horde_Mime_Viewer + */ +class IMP_Horde_Mime_Viewer_Pdf extends Horde_Mime_Viewer_Pdf +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => false, + 'full' => true, + 'info' => true, + 'inline' => false + ); + + /** + * Return the full rendered version of the Horde_Mime_Part object. + * + * URL parameters used by this function: + *
+     * 'pdf_view_thumbnail' - (boolean) Output the thumbnail info.
+     * 
+ * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _render() + { + /* Create the thumbnail and display. */ + if (!Util::getFormData('pdf_view_thumbnail')) { + return parent::_render(); + } + + $img = $this->_getHordeImageOb(true); + + 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'); + } + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $data, + 'status' => array(), + 'type' => $type + ) + ); + } + + /** + * Return the rendered information about the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInfo() + { + /* Check to see if convert utility is available. */ + if (!$this->_getHordeImageOb(false)) { + return array(); + } + + $status = array( + sprintf(_("A PDF file named %s is attached to this message. A thumbnail is below."), $this->_mimepart->getName(true)), + ); + + if ($GLOBALS['browser']->hasFeature('javascript')) { + $status[] = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('pdf_view_thumbnail' => 1)), false), _("View Attachment"), null, ''), null, null, null); + } else { + $status[] = Horde::link($this->_params['contents']->urlView($this->_mimepart, 'view_attach')) . Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('pdf_view_thumbnail' => 1)), false), _("View Attachment"), null, '') . ''; + } + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => '', + 'status' => array( + array( + 'icon' => Horde::img('mime/image.png', _("Thumbnail of attached PDF file")), + 'text' => $status + ) + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + } + + /** + * Return a Horde_Image object. + * + * @param boolean $load Whether to load the image data. + * + * @return mixed The Hore_Image object, or false on error. + */ + protected function _getHordeImageOb($load) + { + if (empty($GLOBALS['conf']['image']['convert'])) { + return false; + } + + $img = &Horde_Image::singleton('im', array('temp' => Horde::getTempdir())); + if (is_a($img, 'PEAR_Error')) { + return false; + } + + if ($load) { + $ret = $img->loadString(1, $this->_mimepart->getContents()); + if (is_a($ret, 'PEAR_Error')) { + return false; + } + } + + return $img; + } +} diff --git a/imp/lib/Mime/Viewer/Pgp.php b/imp/lib/Mime/Viewer/Pgp.php new file mode 100644 index 000000000..d5e78fd2c --- /dev/null +++ b/imp/lib/Mime/Viewer/Pgp.php @@ -0,0 +1,408 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => true, + 'forceinline' => true, + 'full' => false, + 'info' => false, + 'inline' => true + ); + + /** + * IMP_Crypt_Pgp object. + * + * @var IMP_Crypt_Pgp + */ + protected $_imppgp; + + /** + * The address of the sender. + * + * @var string + */ + protected $_address = null; + + /** + * Cache for inline data. + * + * @var array + */ + static protected $_inlinecache = array(); + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + if (empty($this->_imppgp) && + !empty($GLOBALS['conf']['utils']['gnupg'])) { + $this->_imppgp = Horde_Crypt::singleton(array('IMP', 'Pgp')); + } + + if (Util::getFormData('rawpgpkey')) { + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $this->_mimepart->getContents(), + 'status' => array(), + 'type' => 'text/plain; charset=' . $this->_mimepart->getCharset() + ) + ); + } + + /* Determine the address of the sender. */ + if (is_null($this->_address)) { + $headers = $this->_params['contents']->getHeaderOb(); + $this->_address = Horde_Mime_Address::bareAddress($headers->getValue('from')); + } + + switch ($this->_mimepart->getType()) { + case 'application/pgp-keys': + return $this->_outputPGPKey(); + + case 'multipart/signed': + return $this->_outputPGPSigned(); + + case 'multipart/encrypted': + return $this->_outputPGPEncrypted(); + + case 'application/pgp-encrypted': + case 'application/pgp-signature': + default: + return array(); + } + } + + /** + * If this MIME part can contain embedded MIME parts, and those embedded + * MIME parts exist, return an altered version of the Horde_Mime_Part that + * contains the embedded MIME part information. + * + * @return mixed A Horde_Mime_Part with the embedded MIME part information + * or null if no embedded MIME parts exist. + */ + protected function _getEmbeddedMimeParts() + { + if ($this->_mimepart->getType() != 'multipart/encrypted') { + return null; + } + + $partlist = array_keys($this->_mimepart->contentTypeMap()); + $base_id = reset($partlist); + $version_id = next($partlist); + $data_id = Horde_Mime::mimeIdArithmetic($version_id, 'next'); + + /* Initialize inline data. */ + $resymmetric = isset(self::$_inlinecache[$base_id]); + self::$_inlinecache[$base_id] = array( + $base_id => array( + 'data' => '', + 'status' => array( + array( + 'icon' => Horde::img('mime/encryption.png', 'PGP'), + 'text' => $resymmetric ? self::$_inlinecache[$base_id][$base_id]['status'][0]['text'] : array() + ) + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ), + $version_id => null, + $data_id => null + ); + $status = &self::$_inlinecache[$base_id][$base_id]['status'][0]['text']; + + /* Is PGP active? */ + if (empty($GLOBALS['conf']['utils']['gnupg']) || + !$GLOBALS['prefs']->getValue('use_pgp')) { + $status[] = _("The message below has been encrypted via PGP, however, PGP support is disabled so the message cannot be decrypted."); + return null; + } + + if (empty($this->_imppgp)) { + $this->_imppgp = Horde_Crypt::singleton(array('IMP', 'Pgp')); + } + + /* PGP version 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. */ + $encrypted_part = $this->_params['contents']->getMIMEPart($data_id); + $encrypted_data = $encrypted_part->getContents(); + + $symmetric_pass = $personal_pass = null; + + /* Check if this a symmetrically encrypted message. */ + try { + $symmetric = $this->_imppgp->encryptedSymmetrically($encrypted_data); + if ($symmetric) { + $symmetric_id = $this->_getSymmetricID(); + $symmetric_pass = $this->_imppgp->getPassphrase('symmetric', $symmetric_id); + + if (is_null($symmetric_pass)) { + $js_action = ''; + if (!$resymmetric) { + $status[] = _("The message has been encrypted via PGP."); + } + + switch ($_SESSION['imp']['view']) { + case 'dimp': + $js_action = 'DimpCore.reloadMessage({});'; + // Fall through + + case 'imp': + /* Ask for the correct passphrase if this is encrypted + * symmetrically. */ + $status[] = Horde::link('#', '', '', '', IMP::passphraseDialogJS('PGPSymmetric', $js_action, array('symmetricid' => $symmetric_id)) . ';return false;') . _("You must enter the passphrase used to encrypt this message to view it.") . ''; + break; + } + return null; + } + } + } catch (Horde_Exception $e) { + Horde::logMessage($e, __FILE__, __LINE__); + unset(self::$_inlinecache[$base_id]); + return null; + } + + /* Check if this is a literal compressed message. */ + try { + $info = $this->_imppgp->pgpPacketInformation($encrypted_data); + } catch (Horde_Exception $e) { + Horde::logMessage($e, __FILE__, __LINE__); + unset(self::$_inlinecache[$base_id]); + return null; + } + $literal = !empty($info['literal']); + + if ($literal) { + $status[] = _("The message below has been compressed via PGP."); + } else { + $status[] = _("The message below has been encrypted via PGP."); + if (!$symmetric) { + if (!$this->_imppgp->getPersonalPrivateKey()) { + /* Output if there is no personal private key to decrypt + * with. */ + $status[] = _("The message below has been encrypted via PGP, however, no personal private key exists so the message cannot be decrypted."); + return null; + } else { + $personal_pass = $this->_imppgp->getPassphrase('personal'); + + if (is_null($personal_pass)) { + $js_action = ''; + $status[] = _("The message has been encrypted via PGP."); + + switch ($_SESSION['imp']['view']) { + case 'dimp': + $js_action = 'DimpCore.reloadMessage({});'; + // Fall through + + case 'imp': + /* Ask for the private key's passphrase if this is + * encrypted asymmetrically. */ + $status[] = Horde::link('#', '', '', '', IMP::passphraseDialogJS('PGPPersonal', $js_action) . ';return false;') . _("You must enter the passphrase for your PGP private key to view this message.") . ''; + break; + } + return null; + } + } + } + } + + try { + if (!is_null($symmetric_pass)) { + $decrypted_data = $this->_imppgp->decryptMessage($encrypted_data, 'symmetric', $symmetric_pass); + } elseif (!is_null($personal_pass)) { + $decrypted_data = $this->_imppgp->decryptMessage($encrypted_data, 'personal', $personal_pass); + } else { + $decrypted_data = $this->_imppgp->decryptMessage($encrypted_data, 'literal'); + } + } catch (Horde_Exception $e) { + $status[] = _("The message below does not appear to be a valid PGP encrypted message. Error: ") . $e->getMessage(); + if (!is_null($symmetric_pass)) { + $this->_imppgp->unsetPassphrase('symmetric', $this->_getSymmetricID()); + return $this->_getEmbeddedMimeParts(); + } + return null; + } + + unset(self::$_inlinecache[$base_id][$data_id]); + + $msg = Horde_Mime_Part::parseMessage($decrypted_data->message); + $msg->buildMimeIds($data_id); + + return array($data_id => $msg); + } + + /** + * Generates output for 'application/pgp-keys' MIME_Parts. + * + * @return string The HTML output. + */ + protected function _outputPGPKey() + { + /* Initialize status message. */ + $status = array( + 'icon' => Horde::img('mime/encryption.png', 'PGP'), + 'text' => array( + _("A PGP Public Key was attached to the message.") + ) + ); + + $mime_id = $this->_mimepart->getMimeId(); + + if ($GLOBALS['prefs']->getValue('use_pgp') && + $GLOBALS['prefs']->getValue('add_source') && + $GLOBALS['registry']->hasMethod('contacts/addField')) { + $status['text'][] = Horde::link('#', '', '', '', $this->_imppgp->savePublicKeyURL($this->_params['contents']->getMailbox(), $this->_params['contents']->getIndex(), $mime_id) . 'return false;') . _("[Save the key to your Address book]") . ''; + } + $status['text'][] = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View the raw text of the Public Key."), array('params' => array('mode' => IMP_Contents::RENDER_INLINE, 'rawpgpkey' => 1))); + + try { + $data = '' . nl2br(str_replace(' ', ' ', $this->_imppgp->pgpPrettyKey($this->_mimepart->getContents()))) . ''; + } catch (Horde_Exception $e) { + $data = $e->getMessage(); + } + + return array( + $mime_id => array( + 'data' => $data, + 'status' => array($status), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + } + + /** + * Generates HTML output for 'multipart/signed' MIME parts. + * + * @return string The HTML output. + */ + protected function _outputPGPSigned() + { + $partlist = array_keys($this->_mimepart->contentTypeMap()); + $base_id = reset($partlist); + $signed_id = next($partlist); + $sig_id = Horde_Mime::mimeIdArithmetic($signed_id, 'next'); + + $ret = array( + $base_id => array( + 'data' => '', + 'status' => array( + array( + 'icon' => Horde::img('mime/encryption.png', 'PGP'), + 'text' => array() + ) + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ), + $sig_id => null + ); + $status = &$ret[$base_id]['status'][0]['text']; + + if (!$GLOBALS['prefs']->getValue('use_pgp') || + empty($GLOBALS['conf']['utils']['gnupg'])) { + /* If PGP not active, hide signature data and output status + * information. */ + $status[] = _("The message below has been digitally signed via PGP, but the signature cannot be verified."); + return $ret; + } + + $status[] = _("The message below has been digitally signed via PGP."); + + if ($GLOBALS['prefs']->getValue('pgp_verify') || + Util::getFormData('pgp_verify_msg')) { + $signed_data = $GLOBALS['imp_imap']->utils->removeBareNewlines($this->_params['contents']->getBodyPart($signed_id, array('mimeheaders' => true))); + $sig_part = $this->_params['contents']->getMIMEPart($sig_id); + + /* Check for the 'x-imp-pgp-signature' param. This is set by the + * plain driver when parsing PGP armor text. */ + $graphicsdir = $GLOBALS['registry']->getImageDir('horde'); + try { + $sig_result = $sig_part->getContentTypeParameter('x-imp-pgp-signature') + ? $this->_imppgp->verifySignature($signed_data, $this->_address) + : $this->_imppgp->verifySignature($signed_data, $this->_address, $sig_part->getContents()); + + if ($sig_result->result) { + $icon = Horde::img('alerts/success.png', _("Success"), null, $graphicsdir); + $sig_text = $sig_result->message; + } else { + $icon = Horde::img('alerts/warning.png', _("Warning"), null, $graphicsdir); + $sig_text = _("The signature could not be checked because the sender's key could not be found."); + } + } catch (Horde_Exception $e) { + $icon = Horde::img('alerts/error.png', _("Error"), null, $graphicsdir); + $sig_text = $e->getMessage(); + } + + require_once 'Horde/Text/Filter.php'; + $ret[$base_id]['status'][] = array( + 'icon' => $icon, + 'text' => array( + Text_Filter::filter($sig_text, 'text2html', array('parselevel' => TEXT_HTML_NOHTML)) + ) + ); + } else { + switch ($_SESSION['imp']['view']) { + case 'imp': + $status[] = Horde::link(Util::addParameter(IMP::selfUrl(), array('pgp_verify_msg' => 1))) . _("Click HERE to verify the message.") . ''; + break; + + case 'dimp': + $status[] = Horde::link('#', '', 'pgpVerifyMsg') . _("Click HERE to verify the message.") . ''; + break; + } + } + + return $ret; + } + + /** + * Generates HTML output for 'multipart/encrypted' MIME parts. + * + * @return string The HTML output. + */ + protected function _outputPGPEncrypted() + { + $id = $this->_mimepart->getMimeId(); + return isset(self::$_inlinecache[$id]) + ? self::$_inlinecache[$id] + : array(); + } + + /** + * Generates the symmetric ID for this message. + * + * @return string Symmetric ID. + */ + protected function _getSymmetricID() + { + return $this->_imppgp->getSymmetricID($this->_params['contents']->getMailbox(), $this->_params['contents']->getIndex(), $this->_mimepart->getMimeId()); + } + +} diff --git a/imp/lib/Mime/Viewer/Plain.php b/imp/lib/Mime/Viewer/Plain.php new file mode 100644 index 000000000..4c83cd0e9 --- /dev/null +++ b/imp/lib/Mime/Viewer/Plain.php @@ -0,0 +1,293 @@ + + * @author Michael Slusarz + * @package Horde_Mime_Viewer + */ +class IMP_Horde_Mime_Viewer_Plain extends Horde_Mime_Viewer_Plain +{ + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + global $conf, $prefs; + + $mime_id = $this->_mimepart->getMimeId(); + $type = 'text/html; charset=' . NLS::getCharset(); + + // Trim extra whitespace in the text. + $text = trim($this->_mimepart->getContents()); + if ($text == '') { + return array( + $mime_id => array( + 'data' => '', + 'status' => array(), + 'type' => $type + ) + ); + } + + // Convert to the local charset. + $text = String::convertCharset($text, $this->_mimepart->getCharset()); + + // Check for 'flowed' text data. + if ($this->_mimepart->getContentTypeParameter('format') == 'flowed') { + $text = $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp')); + } else { + /* A "From" located at the beginning of a line in the body text + * will be escaped with a '>' by the IMAP server. Remove this + * escape character or else the line will display as being + * quoted. Flowed conversion would have already taken care of this + * for us. */ + $text = preg_replace('/(\n+)> ?From(\s+)/', "$1From$2", $text); + } + + $text = IMP::filterText($text); + + /* Done processing if in mimp mode. */ + if ($_SESSION['imp']['view'] == 'mimp') { + return array( + $mime_id => array( + 'data' => $text, + 'status' => array(), + 'type' => $type + ) + ); + } + + // 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' => NLS::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, 'outputJS' => false); + } + + // Highlight simple markup of an email. + if ($prefs->getValue('highlight_simple_markup')) { + $filters['simplemarkup'] = array(); + } + + // Dim signatures. + if ($prefs->getValue('dim_signature')) { + $filters['dimsignature'] = array(); + } + + if ($prefs->getValue('emoticons')) { + $filters['emoticons'] = array('entities' => true); + } + + // Run filters. + $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( + $mime_id => array( + 'data' => '
' . "\n" . $text . '
', + 'status' => array(), + 'type' => $type + ) + ); + } + + /** + * Does this MIME part possibly contain embedded MIME parts? + * + * @return boolean True if this driver supports parsing embedded MIME + * parts. + */ + public function embeddedMimeParts() + { + return (!empty($GLOBALS['conf']['utils']['gnupg']) && $GLOBALS['prefs']->getValue('pgp_scan_body')) || $this->getConfigParam('uudecode'); + } + + /** + * If this MIME part can contain embedded MIME parts, and those embedded + * MIME parts exist, return a list of MIME parts that contain the embedded + * MIME part information. + * + * @return mixed An array of Horde_Mime_Part objects, with the key as + * the ID, or null if no embedded MIME parts exist. + */ + public function getEmbeddedMimeParts() + { + $ret = null; + + if (!empty($GLOBALS['conf']['utils']['gnupg']) && + $GLOBALS['prefs']->getValue('pgp_scan_body')) { + $ret = $this->_parsePGP(); + } + + if (is_null($ret) && $this->getConfigParam('uudecode')) { + $ret = $this->_parseUUencode(); + } + + return $ret; + } + + /** + * Scan text for armored PGP blocks and, if they exist, convert the part + * to the embedded MIME representation. + * + * @return mixed See self::_getEmbeddedMimeParts(). + */ + protected function _parsePGP() + { + /* Avoid infinite loop. */ + $imp_pgp = Horde_Crypt::singleton(array('IMP', 'Pgp')); + $parts = $imp_pgp->parsePGPData($this->_mimepart->getContents()); + if (empty($parts) || + ((count($parts) == 1) && + ($parts[0]['type'] == Horde_Crypt_Pgp::ARMOR_TEXT))) { + return null; + } + + $new_part = new Horde_Mime_Part(); + $new_part->setType('multipart/mixed'); + $charset = $this->_mimepart->getCharset(); + $mime_id = $this->_mimepart->getMimeId(); + + while (list(,$val) = each($parts)) { + switch ($val['type']) { + case Horde_Crypt_Pgp::ARMOR_TEXT: + $part = new Horde_Mime_Part(); + $part->setType('text/plain'); + $part->setCharset($charset); + $part->setContents(implode("\n", $val['data'])); + $new_part->addPart($part); + break; + + case Horde_Crypt_Pgp::ARMOR_PUBLIC_KEY: + $part = new Horde_Mime_Part(); + $part->setType('application/pgp-keys'); + $part->setContents(implode("\n", $val['data'])); + $new_part->addPart($part); + break; + + case Horde_Crypt_Pgp::ARMOR_MESSAGE: + $part = new Horde_Mime_Part(); + $part->setType('multipart/signed'); + // TODO: add micalg parameter + $part->setContentTypeParameter('protocol', 'application/pgp-encrypted'); + + $part1 = new Horde_Mime_Part(); + $part1->setType('application/pgp-encrypted'); + $part1->setContents("Version: 1\n"); + + $part2 = new Horde_Mime_Part(); + $part2->setType('application/octet-stream'); + $part2->setContents($message_encrypt); + $part2->setDisposition('inline'); + + $part->addPart($part1); + $part->addPart($part2); + + $new_part->addPart($part); + break; + + case Horde_Crypt_Pgp::ARMOR_SIGNED_MESSAGE: + if (($sig = current($parts)) && + ($sig['type'] == Horde_Crypt_Pgp::ARMOR_SIGNATURE)) { + $part = new Horde_Mime_Part(); + $part->setType('multipart/signed'); + // TODO: add micalg parameter + $part->setContentTypeParameter('protocol', 'application/pgp-signature'); + + $part1 = new Horde_Mime_Part(); + $part1->setType('text/plain'); + $part1->setCharset($charset); + + $part1_data = implode("\n", $val['data']); + $part1->setContents(substr($part1_data, strpos($part1_data, "\n\n") + 2)); + + $part2 = new Horde_Mime_Part(); + $part2->setType('application/x-imp-pgp-signature'); + $part2->setContents(String::convertCharset(implode("\n", $val['data']) . "\n" . implode("\n", $sig['data']), $charset)); + + $part->addPart($part1); + $part->addPart($part2); + $new_part->addPart($part); + + next($parts); + } + } + } + + $new_part->buildMimeIds($mime_id); + + return array($mime_id => $new_part); + } + + /** + * Scan text for UUencode data an, if it exists, convert the part to the + * embedded MIME representation. + * + * @return mixed See self::_getEmbeddedMimeParts(). + */ + protected function _parseUUencode() + { + $text = String::convertCharset($this->_mimepart->getContents(), $this->_mimepart->getCharset()); + + /* Don't want to use convert_uudecode() here as there may be multiple + * files residing in the text. */ + $files = &Mail_mimeDecode::uudecode($text); + if (empty($files)) { + return null; + } + + $new_part = new Horde_Mime_Part(); + $new_part->setType('multipart/mixed'); + $mime_id = $this->_mimepart->getMimeId(); + + $text_part = new Horde_Mime_Part(); + $text_part->setType('text/plain'); + $text_part->setCharset(NLS::getCharset()); + $text_part->setContents(preg_replace("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", "\n", $text)); + $new_part->addPart($text_part); + + reset($files); + while (list(,$file) = each($files)) { + $uupart = new Horde_Mime_Part(); + $uupart->setType('application/octet-stream'); + $uupart->setContents($file['filedata']); + $uupart->setName(strip_tags($file['filename'])); + $new_part->addPart($uupart); + } + + $new_part->buildMimeIds($mime_id); + + return array($mime_id => $new_part); + } +} diff --git a/imp/lib/Mime/Viewer/Related.php b/imp/lib/Mime/Viewer/Related.php new file mode 100644 index 000000000..4a491a0ca --- /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 +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => true, + 'full' => true, + 'info' => false, + 'inline' => true, + ); + + /** + * Return the full rendered version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _render() + { + return $this->_IMPrender(false); + } + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + return $this->_IMPrender(true); + } + + /** + * Render out the currently set contents. + * + * @param boolean $inline Are we viewing inline? + * + * @return array See self::render(). + */ + protected function _IMPrender($inline) + { + $ids = array_keys($this->_mimepart->contentTypeMap()); + $related_id = $this->_mimepart->getMimeId(); + + $cids = $ret = array(); + $id = null; + + /* Build a list of parts -> CIDs. */ + foreach ($ids as $val) { + $ret[$val] = null; + if (strcmp($related_id, $val) !== 0) { + $part = $this->_mimepart->getPart($val); + $cids[$val] = $part->getContentId(); + } + } + + /* Look at the 'start' parameter to determine which part to start + * with. If no 'start' parameter, use the first part. RFC 2387 + * [3.1] */ + $start = $this->_mimepart->getContentTypeParameter('start'); + if (!empty($start)) { + $id = array_search($id, $cids); + } + + if (empty($id)) { + reset($ids); + $id = next($ids); + } + + /* Only display if the start part (normally text/html) can be + * displayed inline -OR- we are viewing this part as an attachment. */ + if ($inline && + !$this->_params['contents']->canDisplay($id, IMP_Contents::RENDER_INLINE)) { + return array(); + } + + + $render = $this->_params['contents']->renderMIMEPart($id, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL, array('params' => array_merge($this->_params, array('related_id' => $id, 'related_cids' => $cids)))); + + if (!$inline) { + return $render; + } + + foreach (array_keys($render) as $val) { + $ret[$val] = $render[$val]; + } + + return $ret; + } +} diff --git a/imp/lib/Mime/Viewer/Smil.php b/imp/lib/Mime/Viewer/Smil.php new file mode 100644 index 000000000..5abd83f2f --- /dev/null +++ b/imp/lib/Mime/Viewer/Smil.php @@ -0,0 +1,58 @@ + + * @author Michael Slusarz + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Smil extends Horde_Mime_Viewer_Smil +{ + /** + * 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'])) !== false)) { + $this->_content .= '
'; + } + break; + + case 'TEXT': + if (isset($attrs['SRC']) && + (($rp = $this->_getRelatedLink($attrs['SRC'])) !== 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 (isset($this->_params['related_id']) && + (($key = array_search(trim($cid, '<>', $this->_params['related_cids']))) !== false)) { + return $this->_param['contents']->getMIMEPart($key); + } + + return false; + } +} diff --git a/imp/lib/Mime/Viewer/Smime.php b/imp/lib/Mime/Viewer/Smime.php new file mode 100644 index 000000000..448cd5e08 --- /dev/null +++ b/imp/lib/Mime/Viewer/Smime.php @@ -0,0 +1,331 @@ + + * @author Michael Slusarz + * @package Horde_Mime_Viewer + */ +class IMP_Horde_Mime_Viewer_Smime extends Horde_Mime_Viewer_Driver +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => true, + 'forceinline' => true, + 'full' => false, + 'info' => false, + 'inline' => true + ); + + /** + * IMP_Crypt_Smime object. + * + * @var IMP_Crypt_Smime + */ + protected $_impsmime = null; + + /** + * Cache for inline data. + * + * @var array + */ + static protected $_inlinecache = array(); + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + /* Check to see if S/MIME support is available. */ + $this->_initSMIME(); + + if (Util::getFormData('view_smime_key')) { + return $this->_outputSMIMEKey(); + } + + if (is_null($this->_impsmime)) { + $this->_impsmime = false; + } else { + /* We need to insert JavaScript code now if S/MIME support is + * active. */ + Horde::addScriptFile('prototype.js', 'horde', true); + Horde::addScriptFile('imp.js', 'imp', true); + } + + switch ($this->_mimepart->getType()) { + case 'multipart/signed': + return $this->_outputSMIMESigned(); + + case 'application/pkcs7-mime': + case 'application/x-pkcs7-mime': + return $this->_outputSMIMEEncrypted(); + } + } + + /** + * If this MIME part can contain embedded MIME parts, and those embedded + * MIME parts exist, return an altered version of the Horde_Mime_Part that + * contains the embedded MIME part information. + * + * @return mixed A Horde_Mime_Part with the embedded MIME part information + * or null if no embedded MIME parts exist. + */ + protected function _getEmbeddedMimeParts() + { + if (!in_array($this->_mimepart->getType(), array('application/pkcs7-mime', 'application/x-pkcs7-mime'))) { + return null; + } + + // 'smime-type' must be empty or 'enveloped-data' + $smime_type = $this->_mimepart->getContentTypeParameter('smime-type'); + if ($smime_type == 'signed-data') { + // TODO + return null; + } + + $base_id = $this->_mimepart->getMimeId(); + + /* Initialize inline data. */ + self::$_inlinecache[$base_id] = array( + $base_id => array( + 'data' => '', + 'status' => array( + array( + 'icon' => Horde::img('mime/encryption.png', 'S/MIME'), + 'text' => array(_("This message has been encrypted via S/MIME.")) + ) + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + $status = &self::$_inlinecache[$base_id][$base_id]['status'][0]['text']; + + /* Is PGP active? */ + $this->_initSMIME(); + if (empty($this->_impsmime)) { + $status[] = _("S/MIME support is not currently enabled so the message is unable to be decrypted."); + return null; + } + + if (!$this->_impsmime->getPersonalPrivateKey()) { + $status[] = _("No personal private key exists so the message is unable to be decrypted."); + return null; + } + + /* Make sure we have a passphrase. */ + $passphrase = $this->_impsmime->getPassphrase(); + if ($passphrase === false) { + $js_action = ''; + + switch ($_SESSION['imp']['view'] == 'imp') { + case 'dimp': + $js_action = 'DimpCore.reloadMessage({});'; + // Fall through + + case 'imp': + $status[] = Horde::link('#', '', '', '', IMP::passphraseDialogJS('SMIMEPersonal', $js_action) . ';return false;') . _("You must enter the passphrase for your S/MIME private key to view this message.") . ''; + break; + } + return null; + } + + $raw_text = $GLOBALS['imp_imap']->utils->removeBareNewlines($this->_params['contents']->getBodyPart($this->_mimepart->getMimeId(), array('mimeheaders' => true))); + + try { + $decrypted_data = $this->_impsmime->decryptMessage($raw_text); + } catch (Horde_Exception $e) { + $status[] = $e->getMessage(); + return null; + } + + return array($base_id => Horde_Mime_Part::parseMessage($decrypted_data)); + } + + /** + * Generates HTML output for the S/MIME key. + * + * @return string The HTML output. + */ + protected function _outputSMIMEKey() + { + if (empty($this->_impsmime)) { + return array(); + } + + $raw_text = $GLOBALS['imp_imap']->utils->removeBareNewlines($this->_params['contents']->getBodyPart($this->_mimepart->getMimeId(), array('mimeheaders' => true))); + + try { + $sig_result = $this->_impsmime->verifySignature($raw_text); + } catch (Horde_Exception $e) {} + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $this->_impsmime->certToHTML($sig_result->cert), + 'status' => array(), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); + } + + /** + * Init the S/MIME Horde_Crypt object. + */ + protected function _initSMIME() + { + if (is_null($this->_impsmime) && + $GLOBALS['prefs']->getValue('use_smime')) { + try { + $this->_impsmime = Horde_Crypt::singleton(array('IMP', 'Smime')); + $this->_impsmime->checkForOpenSSL(); + } catch (Horde_Exception $e) { + $this->_impsmime = null; + } + } + } + + /** + * Generates HTML output for 'multipart/signed' MIME parts. + * + * @return array TODO + */ + protected function _outputSMIMESigned() + { + $partlist = array_keys($this->_mimepart->contentTypeMap()); + $base_id = reset($partlist); + $sig_id = Horde_Mime::mimeIdArithmetic(next($partlist), 'next'); + + $ret = array( + $base_id => array( + 'data' => '', + 'status' => array( + array( + 'icon' => Horde::img('mime/encryption.png', 'S/MIME'), + 'text' => array(_("This message has been digitally signed via S/MIME.")) + ) + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ), + $sig_id => null + ); + $status = &$ret[$base_id]['status'][0]['text']; + + if (!$GLOBALS['prefs']->getValue('use_smime')) { + $status[] = _("S/MIME support is not enabled so the digital signature is unable to be verified."); + return $ret; + } + + $raw_text = $base_id + ? $this->_params['contents']->getBodyPart($base_id, array('mimeheaders' => true)) + : $this->_params['contents']->fullMessageText(); + $raw_text = $GLOBALS['imp_imap']->utils->removeBareNewlines($raw_text); + + $sig_result = null; + + if ($GLOBALS['prefs']->getValue('smime_verify') || + Util::getFormData('smime_verify_msg')) { + try { + $sig_result = $this->_impsmime->verifySignature($raw_text); + } catch (Horde_Exception $e) { + $ret[$base_id]['status'][0]['icon'] = ($e->getCode() == 'horde.warning') + ? Horde::img('alerts/warning.png', _("Warning"), null, $graphicsdir) + : Horde::img('alerts/error.png', _("Error"), null, $graphicsdir); + $status[] = $e->getMessage(); + return $ret; + } + } else { + switch ($_SESSION['imp']['view']) { + case 'imp': + $status[] = Horde::link(Util::addParameter(IMP::selfUrl(), 'smime_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; + break; + + case 'dimp': + $status[] = Horde::link('#', '', 'smimeVerifyMsg') . _("Click HERE to verify the message.") . ''; + break; + } + return $ret; + } + + $subpart = $this->_params['contents']->getMIMEPart($sig_id); + if (!isset($subpart)) { + try { + $msg_data = $this->_impsmime->extractSignedContents($raw_text); + $subpart = Horde_Mime_Part::parseMessage($msg_data); + } catch (Horde_Exception $e) { + $this->_status[] = $e->getMessage(); + $subpart = $this->_mimepart; + } + } + + $graphicsdir = $GLOBALS['registry']->getImageDir('horde'); + + $ret[$base_id]['status'][0]['icon'] = Horde::img('alerts/success.png', _("Success"), null, $graphicsdir); + + /* This message has been verified but there was no output + * from the PGP program. */ + if (empty($sig_result->result) || ($sig_result->result === true)) { + $email = (is_array($sig_result->email)) + ? implode(', ', $sig_result->email) + : $sig_result->email; + $status[] = sprintf(_("The message has been verified. Sender: %s."), htmlspecialchars($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; + } else { + $subject = null; + } + + if (!empty($subject) && + $GLOBALS['registry']->hasMethod('contacts/addField') && + $GLOBALS['prefs']->getValue('add_source')) { + $status[] = sprintf(_("The S/MIME certificate of %s: "), @htmlspecialchars($subject, ENT_COMPAT, NLS::getCharset())) . $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View"), array('params' => array('mode' => IMP_Contents::RENDER_INLINE, 'view_smime_key' => 1))) . '/' . Horde::link('#', '', null, null, $this->_impsmime->savePublicKeyURL($sig_result->cert, $this->_params['contents']->getIndex(), $sig_id) . ' return false;') . _("Save in your Address Book") . ''; + } + } + + return $ret; + } + + /** + * Generates output for encrypted S/MIME parts. + * + * @return array TODO + */ + protected function _outputSMIMEEncrypted() + { + $id = $this->_mimepart->getMimeId(); + return isset(self::$_inlinecache[$id]) + ? self::$_inlinecache[$id] + : array(); + } +} diff --git a/imp/lib/Mime/Viewer/Status.php b/imp/lib/Mime/Viewer/Status.php new file mode 100644 index 000000000..2516df4bd --- /dev/null +++ b/imp/lib/Mime/Viewer/Status.php @@ -0,0 +1,148 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Status extends Horde_Mime_Viewer_Driver +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => true, + 'full' => false, + 'info' => true, + 'inline' => true, + ); + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + /* If this is a straight message/disposition-notification part, just + * output the text. */ + if ($this->_mimepart->getType() == 'message/delivery-status') { + return $this->_params['contents']->renderMIMEPart($this->_mimepart->getMIMEId(), IMP_Contents::RENDER_FULL, array('type' => 'text/plain', 'params' => $this->_params)); + } + + return $this->_renderInfo(); + } + + /** + * Return the rendered information about the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInfo() + { + $parts = array_keys($this->_mimepart->contentTypeMap()); + + reset($parts); + $part1_id = next($parts); + $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); + $part3_id = Horde_Mime::mimeIdArithmetic($part2_id, 'next'); + + /* RFC 3464 [2]: There are three parts to a delivery status + * multipart/report message: + * (1) Human readable message + * (2) Machine parsable body part (message/delivery-status) + * (3) Returned message (optional) + * + * Information on the message status is found in the 'Action' field + * located in part #2 (RFC 3464 [2.3.3]). It can be either 'failed', + * 'delayed', 'delivered', 'relayed', or 'expanded'. */ + + /* Get the action first - it appears in the second part. */ + $action = null; + $part2 = $this->_params['contents']->getMIMEPart($part2_id); + + foreach (explode("\n", $part2->getContents()) as $line) { + if (stristr($line, 'Action:') !== false) { + $action = strtolower(trim(substr($line, strpos($line, ':') + 1))); + if (strpos($action, ' ') !== false) { + $action = substr($action, 0, strpos($action, ' ')); + } + break; + } + } + + if (is_null($action)) { + return array(); + } + + /* Get the correct text strings for the action type. */ + switch ($action) { + case 'failed': + case 'delayed': + $status = array( + array( + 'icon' => Horde::img('alerts/error.png', _("Error"), null, $GLOBALS['registry']->getImageDir('horde')), + 'text' => array( + _("ERROR: Your message could not be delivered."), + sprintf(_("Additional error message details can be viewed %s."), $this->_params['contents']->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Additional message details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE)))) + ) + ) + ); + $msg_link = _("The text of the returned message can be viewed %s."); + $msg_link_status = _("The text of the returned message"); + break; + + case 'delivered': + case 'expanded': + case 'relayed': + $status = array( + array( + 'icon' => Horde::img('alerts/success.png', _("Success"), null, $GLOBALS['registry']->getImageDir('horde')), + 'text' => array( + _("Your message was successfully delivered."), + sprintf(_("Additional message details can be viewed %s."), $this->_params['contents']->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Additional message details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE)))) + ) + ) + ); + $msg_link = _("The text of the message can be viewed %s."); + $msg_link_status = _("The text of the message"); + break; + } + + /* Print the human readable message. */ + $first_part = $this->_params['contents']->renderMIMEPart($part1_id, IMP_Contents::RENDER_INLINE_AUTO, array('params' => $this->_params)); + + /* Display a link to the returned message, if it exists. */ + $part3 = $this->_params['contents']->getMIMEPart($part3_id); + if ($part3) { + $status[0]['text'][] = sprintf($msg_link, $this->_params['contents']->linkViewJS($part3, 'view_attach', _("HERE"), array('jstext' => $msg_link_status, 'ctype' => 'message/rfc822'))); + } + + if (empty($first_part)) { + $data = ''; + } else { + $status[0]['text'][] = _("The mail server generated the following informational message:"); + $status = array_merge($status, $first_part[$part1_id]['status']); + $data = $first_part[$part1_id]['data']; + } + + $ret = array_combine($parts, array_fill(0, count($parts), null)); + + $ret[$this->_mimepart->getMimeId()] = array( + 'data' => $data, + 'status' => $status, + 'type' => 'text/html; charset=' . NLS::getCharset() + ); + + return $ret; + } +} diff --git a/imp/lib/Mime/Viewer/Tnef.php b/imp/lib/Mime/Viewer/Tnef.php new file mode 100644 index 000000000..faf68e754 --- /dev/null +++ b/imp/lib/Mime/Viewer/Tnef.php @@ -0,0 +1,124 @@ + + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Tnef extends Horde_Mime_Viewer_Tnef +{ + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_capability = array( + 'embedded' => false, + 'forceinline' => true, + 'full' => true, + 'info' => true, + 'inline' => false + ); + + /** + * Return the full rendered version of the Horde_Mime_Part object. + * + * URL parameters used by this function: + *
+     * 'tnef_attachment' - (integer) The TNEF attachment to download.
+     * 
+ * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _render() + { + if (!Util::getFormData('tnef_attachment')) { + $ret = $this->_renderInfo(); + reset($ret); + $ret[key($ret)]['data'] = '' . $ret[key($ret)]['data'] . ''; + return $ret; + } + + /* Get the data from the attachment. */ + $tnef = &Horde_Compress::singleton('tnef'); + $tnefData = $tnef->decompress($this->_mimepart->getContents()); + + /* Display the requested file. Its position in the $tnefData + * array can be found in '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)) { + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $text, + 'name' => $tnefData[$tnefKey]['name'], + 'status' => array(), + 'type' => $tnefData[$tnefKey]['type'] . '/' . $tnefData[$tnefKey]['subtype'] + ) + ); + } + } + + // TODO: Error reporting + return array(); + } + + /** + * Return the rendered information about the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInfo() + { + /* Get the data from the attachment. */ + $tnef = &Horde_Compress::singleton('tnef'); + $tnefData = $tnef->decompress($this->_mimepart->getContents()); + + $text = ''; + + if (!count($tnefData)) { + $status = array( + 'text' => array(_("No attachments found.")) + ); + } else { + $status = array( + 'text' => array(_("The following files were attached to this part:")) + ); + + reset($tnefData); + while (list($key, $data) = each($tnefData)) { + $temp_part = $this->_mimepart; + $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 (in_array($type, array('application/octet-stream', 'application/base64'))) { + $type = Horde_Mime_Magic::filenameToMIME($data['name']); + } + $temp_part->setType($type); + + $link = $this->_params['contents']->linkView($temp_part, 'view_attach', htmlspecialchars($data['name']), array('jstext' => sprintf(_("View %s"), $data['name']), 'params' => array('tnef_attachment' => $key + 1))); + $text .= _("Attached File:") . '  ' . $link . '  (' . $data['type'] . '/' . $data['subtype'] . ")
\n"; + } + } + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $text, + 'status' => array($status), + 'type' => '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..504dcd23e --- /dev/null +++ b/imp/lib/Mime/Viewer/Zip.php @@ -0,0 +1,93 @@ + + * @author Michael Slusarz + * @package Horde_Mime + */ +class IMP_Horde_Mime_Viewer_Zip extends Horde_Mime_Viewer_Zip +{ + /** + * Return the full rendered version of the Horde_Mime_Part object. + * + * URL parameters used by this function: + *
+     * 'zip_attachment' - (integer) The ZIP attachment to download.
+     * 
+ * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _render() + { + if (!Util::getFormData('zip_attachment')) { + $this->_callback = array(&$this, '_IMPcallback'); + return parent::_render(); + } + + /* Send the requested file. Its position in the zip archive is located + * in 'zip_attachment'. */ + $data = $this->_mimepart->getContents(); + $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)) { + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $text, + 'name' => basename($zipInfo[$fileKey]['name']), + 'status' => array(), + 'type' => 'application/octet-stream' + ) + ); + } + } + + // TODO: Error reporting + return array(); + } + + /** + * Return the rendered inline version of the Horde_Mime_Part object. + * + * @return array See Horde_Mime_Viewer_Driver::render(). + */ + protected function _renderInline() + { + $this->_callback = array(&$this, '_IMPcallback'); + return parent::_renderInline(); + } + + /** + * The function to use as a callback to _toHTML(). + * + * @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 _IMPcallback($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))) { + $mime_part = $this->_mimepart; + $mime_part->setName(basename($name)); + $val['name'] = str_replace($name, $this->_params['contents']->linkView($mime_part, 'download_render', $name, array('jstext' => sprintf(_("View %s"), str_replace(' ', ' ', $name)), 'class' => 'fixed', 'params' => array('zip_attachment' => urlencode($key) + 1))), $val['name']); + } + + return $val; + } +} diff --git a/imp/lib/Mime/Viewer/alternative.php b/imp/lib/Mime/Viewer/alternative.php deleted file mode 100644 index 265d8efd9..000000000 --- a/imp/lib/Mime/Viewer/alternative.php +++ /dev/null @@ -1,96 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_alternative extends Horde_Mime_Viewer_Driver -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => true, - 'full' => false, - 'info' => false, - 'inline' => true, - ); - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - $base_id = $this->_mimepart->getMimeId(); - $subparts = $this->_mimepart->contentTypeMap(); - - $base_ids = $display_ids = $ret = array(); - - /* Look for a displayable part. RFC: show the LAST choice that can be - * displayed inline. If an alternative is itself a multipart, the user - * agent is allowed to show that alternative, an earlier alternative, - * or both. If we find a multipart alternative that contains at least - * one viewable part, we will display all viewable subparts of that - * alternative. */ - foreach (array_keys($subparts) as $mime_id) { - $ret[$mime_id] = null; - if ((strcmp($base_id, $mime_id) !== 0) && - $this->_params['contents']->canDisplay($mime_id, IMP_Contents::RENDER_INLINE)) { - $display_ids[strval($mime_id)] = true; - } - } - - /* If we found no IDs, return now. */ - if (empty($display_ids)) { - $ret[$base_id] = array( - 'data' => '', - 'status' => array( - array( - 'text' => array(_("There are no alternative parts that can be displayed inline.")), - 'type' => 'info' - ) - ), - 'type' => 'text/html; charset=' . NLS::getCharset() - ); - return $ret; - } - - /* If the last viewable message exists in a subpart, back up to the - * base multipart and display all viewable parts in that multipart. - * Else, display the single part. */ - end($display_ids); - $curr_id = key($display_ids); - while (!is_null($curr_id) && (strcmp($base_id, $curr_id) !== 0)) { - if (isset($subparts[$curr_id])) { - $disp_id = $curr_id; - } - $curr_id = Horde_Mime::mimeIdArithmetic($curr_id, 'up'); - } - - /* Now grab all keys under this ID. */ - $render_part = $this->_mimepart->getPart($disp_id); - foreach (array_keys($render_part->contentTypeMap()) as $val) { - if (isset($display_ids[$val])) { - $render = $this->_params['contents']->renderMIMEPart($val, IMP_Contents::RENDER_INLINE, array('params' => $this->_params)); - foreach (array_keys($render) as $id) { - $ret[$id] = $render[$id]; - unset($display_ids[$id]); - } - } - } - - return $ret; - } -} diff --git a/imp/lib/Mime/Viewer/appledouble.php b/imp/lib/Mime/Viewer/appledouble.php deleted file mode 100644 index ca8e8cc5e..000000000 --- a/imp/lib/Mime/Viewer/appledouble.php +++ /dev/null @@ -1,112 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_appledouble extends Horde_Mime_Viewer_Driver -{ - /** - * This driver's capabilities. - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => true, - 'full' => false, - 'info' => true, - 'inline' => true - ); - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - return $this->_IMPrender(true); - } - - /** - * Return the rendered information about the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInfo() - { - return $this->_IMPrender(false); - } - - /** - * Render the part based on the view mode. - * - * @param boolean $inline True if viewing inline. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _IMPrender($inline) - { - /* RFC 1740 [4]: There are two parts to an appledouble message: - * (1) application/applefile - * (2) Data embedded in the Mac file - * Since the resource fork is not very useful to us, only provide a - * means to download. */ - - /* Display the resource fork download link. */ - $mime_id = $this->_mimepart->getMimeId(); - $parts_list = array_keys($this->_mimepart->contentTypeMap()); - reset($parts_list); - $applefile_id = next($parts_list); - $data_id = Horde_Mime::mimeIdArithmetic($applefile_id, 'next'); - - $applefile_part = $this->_mimepart->getPart($applefile_id); - $data_part = $this->_mimepart->getPart($data_id); - - $data_name = $data_part->getName(true); - if (empty($data_name)) { - $data_name = _("unnamed"); - } - - $status = array( - 'icon' => Horde::img('apple.png', _("Macintosh File")), - 'text' => array( - sprintf(_("This message contains a Macintosh file (named \"%s\")."), $data_name), - sprintf(_("The Macintosh resource fork can be downloaded %s."), $this->_params['contents']->linkViewJS($applefile_part, 'download_attach', _("HERE"), array('jstext' => _("The Macintosh resource fork")))) - ) - ); - - /* For inline viewing, attempt to display the data inline. */ - $ret = array(); - if ($inline && (($disp = $this->_params['contents']->canDisplay($data_part, IMP_Contents::RENDER_INLINE | IMP_Contents::RENDER_INFO)))) { - $ret = $this->_params['contents']->renderMIMEPart($data_id, $disp, array('params' => $this->_params)); - $status['text'][] = _("The contents of the Macintosh file are below."); - } else { - $status['text'][] = sprintf(_("The contents of the Macintosh file can be downloaded %s."), $this->_params['contents']->linkViewJS($data_part, 'download_attach', _("HERE"), array('jstext' => _("The Macintosh file")))); - } - - foreach ($parts_list as $val) { - if (!isset($ret[$val]) && (strcmp($val, $data_id) !== 0)) { - $ret[$val] = (strcmp($val, $mime_id) === 0) - ? array( - 'data' => '', - 'status' => array($status), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - : null; - } - } - - ksort($ret); - - return $ret; - } -} diff --git a/imp/lib/Mime/Viewer/enriched.php b/imp/lib/Mime/Viewer/enriched.php deleted file mode 100644 index 4fc1940f4..000000000 --- a/imp/lib/Mime/Viewer/enriched.php +++ /dev/null @@ -1,79 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_enriched extends Horde_Mime_Viewer_enriched -{ - /** - * Return the full rendered version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _render() - { - $ret = parent::_render(); - if (!empty($ret)) { - reset($ret); - $ret[key($ret)]['data'] = $this->_IMPformat($ret[key($ret)]['data']); - } - return $ret; - } - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - $ret = parent::_renderInline(); - if (!empty($ret)) { - reset($ret); - $ret[key($ret)]['data'] = $this->_IMPformat($ret[key($ret)]['data']); - } - return $ret; - } - - /** - * Format output text with IMP additions. - * - * @param string $text The HTML text. - * - * @return string The text with extra IMP formatting applied. - */ - protected function _IMPformat($text) - { - // Highlight quoted parts of an email. - if ($GLOBALS['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 ($GLOBALS['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. - return IMP::filterText($text); - } -} diff --git a/imp/lib/Mime/Viewer/html.php b/imp/lib/Mime/Viewer/html.php deleted file mode 100644 index ebb72b12b..000000000 --- a/imp/lib/Mime/Viewer/html.php +++ /dev/null @@ -1,290 +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'; - - /** - * Return the full rendered version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _render() - { - $render = $this->_IMPrender(false); - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $render['html'], - 'status' => $render['status'], - 'type' => $this->_mimepart->getType(true) - ) - ); - } - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - $render = $this->_IMPrender(true); - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $render['html'], - 'status' => $render['status'], - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - } - - /** - * 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']; - - /* We are done processing if in mimp mode. */ - if ($_SESSION['imp']['view'] == 'mimp') { - require_once 'Horde/Text/Filter.php'; - $data = Text_Filter::filter($data, 'html2text'); - - // Filter bad language. - return array('html' => IMP::filterText($data), 'status' => array()); - } - - /* 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 links that we can display (multipart/related - * parts). */ - if (isset($this->_params['related_id'])) { - $cid_replace = array(); - - foreach ($this->_params['related_cids'] as $mime_id => $cid) { - $cid = trim($cid, '<>'); - if ($cid) { - $cid_part = $this->_params['contents']->getMIMEPart($mime_id); - $cid_replace['cid:' . $cid] = $this->_params['contents']->urlView($cid_part, 'view_attach', array('params' => array('img_data' => 1))); - } - } - - if (!empty($cid_replace)) { - $data = str_replace(array_keys($cid_replace), array_values($cid_replace), $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. */ - if ($inline && - !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(IMP::selfUrl(), array('actionID', 'index')); - $url = Util::addParameter($url, 'index', $this->_params['contents']->getIndex()); - - $view_img = Util::getFormData('view_html_images'); - $addr_check = ($GLOBALS['prefs']->getValue('html_image_addrbook') && $this->_inAddressBook()); - - if (!$view_img && !$addr_check) { - $data .= Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true) . - Util::bufferOutput(array('Horde', 'addScriptFile'), 'imp.js', 'imp', true); - - // Unblock javascript code in js/src/imp.js - $cleanhtml['status'][] = array( - 'icon' => Horde::img('mime/image.png'), - 'text' => array( - String::convertCharset(_("Images have been blocked to protect your privacy."), $charset, $msg_charset), - Horde::link(Util::addParameter($url, 'view_html_images', 1), '', 'unblockImageLink') . String::convertCharset(_("Show Images?"), $charset, $msg_charset) . '' - ) - ); - - $data = preg_replace_callback($this->_img_regex, array($this, '_blockImages'), $data); - } - } - - require_once 'Horde/Text/Filter.php'; - if ($GLOBALS['prefs']->getValue('emoticons')) { - $data = Text_Filter::filter($data, array('emoticons'), array(array('emoticons' => true))); - } - - 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(); - $headers = $this->_params['contents']->getHeaderOb(); - - /* Try to get back a result from the search. */ - $res = $GLOBALS['registry']->call('contacts/getField', array(Horde_Mime_Address::bareAddress($headers->getValue('from')), '__key', $params['sources'], false, true)); - return is_a($res, 'PEAR_Error') ? false : count($res); - } -} diff --git a/imp/lib/Mime/Viewer/images.php b/imp/lib/Mime/Viewer/images.php deleted file mode 100644 index 8c6816a5a..000000000 --- a/imp/lib/Mime/Viewer/images.php +++ /dev/null @@ -1,282 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_images extends Horde_Mime_Viewer_images -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => false, - 'full' => true, - 'info' => true, - 'inline' => true - ); - - /** - * Return the full rendered version of the Horde_Mime_Part object. - * - * URL parameters used by this function: - *
-     * 'imp_img_view' - (string) One of the following:
-     *   'data' - Output the image directly.
-     *   'load_convert' - TODO
-     *   'view_convert' - TODO
-     *   'view_thumbnail' - TODO
-     * 
- * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _render() - { - switch (Util::getFormData('imp_img_view')) { - case 'data': - /* If calling page is asking us to output data, do that without - * any further delay and exit. */ - return parent::_render(); - - case 'view_convert': - /* Convert the image to browser-viewable format and display. */ - return $this->_viewConvert(false); - - case 'view_thumbnail': - /* Create the thumbnail and display. */ - return $this->_viewConvert(true); - - case 'load_convert': - /* The browser can display the image type directly - output the JS - * code to render the auto resize popup image window. */ - return $this->_popupImageWindow(); - } - - return parent::_render(); - } - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - /* Only display the image inline if the browser can display it and the - * size of the image is below the config value. */ - if ($GLOBALS['browser']->isViewable($this->_getType())) { - if (isset($this->_conf['inlinesize']) && - ($this->_mimepart->getBytes() < $this->_conf['inlinesize'])) { - /* Viewing inline, and the browser can handle the image type - * directly. So output an tag to load the image. */ - return array( - $this->_mimepart->getMimeId() => array( - 'data' => Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('imp_img_view' => 'data'))), $this->_mimepart->getName(true), null, ''), - 'status' => array(), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - } else { - return $this->_renderInfo(); - } - } - - /* The browser cannot view this image. Inform the user of this and - * ask user if we should convert to another image type. */ - $status = array(_("Your browser does not support inline display of this image type.")); - - /* See if we can convert to an inline browser viewable form. */ - if ($GLOBALS['browser']->hasFeature('javascript')) { - $img = $this->_getHordeImageOb(false); - if ($img && - $GLOBALS['browser']->isViewable($img->getContentType())) { - $convert_link = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("HERE"), array('params' => array('imp_img_view' => 'load_convert'))); - $status[] = sprintf(_("Click %s to convert the image file into a format your browser can attempt to view."), $convert_link); - } - } - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => '', - 'status' => array( - array( - 'text' => $status - ) - ), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - } - - /** - * Return the rendered information about the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInfo() - { - /* Display the thumbnail link only if we show thumbs for all images or - * if image is over 50 KB. Also, check to see if convert utility is - * available. */ - if ((!$this->getConfigParam('allthumbs') && - ($this->_mimepart->getBytes() < 51200)) || - !$this->_getHordeImageOb(false)) { - return array(); - } - - $status = array(sprintf(_("An image named %s is attached to this message. A thumbnail is below."), $this->_mimepart->getName(true))); - - if ($GLOBALS['browser']->hasFeature('javascript')) { - $status[] = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('imp_img_view' => 'view_thumbnail')), false), _("View Attachment"), null, ''), null, null, null); - } else { - $status[] = Horde::link($this->_params['contents']->urlView($this->_mimepart, 'view_attach')) . Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('imp_img_view' => 'view_thumbnail')), false), _("View Attachment"), null, '') . ''; - } - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => '', - 'status' => array( - array( - 'icon' => Horde::img('mime/image.png', _("Thumbnail of attached image")), - 'text' => $status - ) - ), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - } - - /** - * Generate the HTML output for the JS auto-resize view window. - * - * @return string The HTML output. - */ - protected function _popupImageWindow() - { - $self_url = Util::addParameter(IMP::selfUrl(), array('imp_img_view' => ((Util::getFormData('imp_img_view') == 'load_convert') ? 'view_convert' : 'data'))); - $title = $this->_mimepart->getName(true); - - $str = << - -$title - -EOD; - - /* Only use javascript if we are using a DOM capable browser. */ - if ($GLOBALS['browser']->getFeature('dom')) { - /* Javascript display. */ - $loading = _("Loading..."); - $str .= << -function resizeWindow() -{ - - var h, img = document.getElementById('disp_image'), w; - document.getElementById('splash').style.display = 'none'; - img.style.display = 'block'; - window.moveTo(0, 0); - h = img.height - (self.innerHeight ? self.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight)); - w = img.width - (self.innerWidth ? self.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth)); - window.resizeBy(w, h); - self.focus(); -} - -$loading -EOD; - } else { - /* Non-javascript display. */ - $img_txt = _("Image"); - $str .= << - -$img_txt - - -EOD; - } - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $str, - 'status' => array(), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - } - - /** - * Convert image. - * - * @param boolean $thumb View image in thumbnail size? - * - * @return string The image data. - */ - protected function _viewConvert($thumb) - { - $img = $this->_getHordeImageOb(true); - - 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'); - } - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $data, - 'status' => array(), - 'type' => $type - ) - ); - } - - /** - * Return a Horde_Image object. - * - * @param boolean $load Whether to load the image data. - * - * @return mixed The Horde_Image object, or false on error. - */ - protected function _getHordeImageOb($load) - { - $img = null; - $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); - } - - if (!$img || is_a($img, 'PEAR_Error')) { - return false; - } - - if ($load) { - $ret = $img->loadString(1, $this->_mimepart->getContents()); - if (is_a($ret, 'PEAR_Error')) { - return false; - } - } - - return $img; - } -} diff --git a/imp/lib/Mime/Viewer/itip.php b/imp/lib/Mime/Viewer/itip.php deleted file mode 100644 index ec35afb83..000000000 --- a/imp/lib/Mime/Viewer/itip.php +++ /dev/null @@ -1,967 +0,0 @@ - - * @author Mike Cochrane - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_itip extends Horde_Mime_Viewer_Driver -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => false, - 'full' => true, - 'info' => false, - 'inline' => true - ); - - /** - * Return the full rendered version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _render() - { - $ret = $this->_renderInline(); - if (!empty($ret)) { - reset($ret); - $ret[key($ret)]['data'] = Util::bufferOutput('include', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') . - $ret[key($ret)]['data'] . - Util::bufferOutput('include', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc'); - } - return $ret; - } - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * URL parameters used by this function: - *
-     * 'identity' - (integer) TODO
-     * 'itip_action' - (array) TODO
-     * 
- * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - global $registry; - - $charset = NLS::getCharset(); - $data = $this->_mimepart->getContents(); - $mime_id = $this->_mimepart->getMimeId(); - - // Parse the iCal file. - $vCal = new Horde_iCalendar(); - if (!$vCal->parsevCalendar($data, 'VCALENDAR', $this->_mimepart->getCharset())) { - return array( - $mime_id => array( - 'data' => '

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

' . '
' . htmlspecialchars($data) . '
', - 'status' => array(), - 'type' => 'text/html; charset=' . $charset - ) - ); - } - - // Check if we got vcard data with the wrong vcalendar mime type. - $c = $vCal->getComponentClasses(); - if ((count($c) == 1) && !empty($c['horde_icalendar_vcard'])) { - return $this->_params['contents']->renderMIMEPart($mime_id, IMP_Contents::RENDER_INLINE, array('type' => 'text/x-vcard')); - } - - // Get the method type. - $method = $vCal->getAttribute('METHOD'); - if (is_a($method, 'PEAR_Error')) { - $method = ''; - } - - // Get the iCalendar file components. - $components = $vCal->getComponents(); - $msgs = array(); - - // Handle the action requests. - $actions = Util::getFormData('itip_action', array()); - foreach ($actions as $key => $action) { - 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')) { - $msgs[] = array('error', _("There was an error deleting the event:") . ' ' . $event->getMessage()); - } else { - $msgs[] = array('success', _("Event successfully deleted.")); - } - } else { - $msgs[] = 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')) { - $msgs[] = array('error', _("There was an error updating the event:") . ' ' . $event->getMessage()); - } else { - $msgs[] = array('success', _("Respondent Status Updated.")); - } - } else { - $msgs[] = 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')) { - // Try to update in calendar. - if ($registry->hasMethod('calendar/replace')) { - $result = $registry->call('calendar/replace', array('uid' => $guid, 'content' => $components[$key], 'contentType' => $this->mime_part->getType())); - if (is_a($result, 'PEAR_Error')) { - // Could be a missing permission. - $msgs[] = array('warning', _("There was an error updating the event:") . ' ' . $result->getMessage() . '. ' . _("Trying to import the event instead.")); - } else { - $handled = true; - $url = Horde::url($registry->link('calendar/show', array('uid' => $guid))); - $msgs[] = 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')) . ''); - } - } - } - if (!$handled && $registry->hasMethod('calendar/import')) { - // Import into calendar. - $handled = true; - $guid = $registry->call('calendar/import', array('content' => $components[$key], 'contentType' => $this->mime_part->getType())); - if (is_a($guid, 'PEAR_Error')) { - $msgs[] = array('error', _("There was an error importing the event:") . ' ' . $guid->getMessage()); - } else { - $url = Horde::url($registry->link('calendar/show', array('uid' => $guid))); - $msgs[] = 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) { - $msgs[] = 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')) { - $msgs[] = array('error', _("There was an error importing user's free/busy information:") . ' ' . $res->getMessage()); - } else { - $msgs[] = array('success', _("The user's free/busy information was sucessfully stored.")); - } - } else { - $msgs[] = 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')) { - $msgs[] = array('error', _("There was an error importing the task:") . ' ' . $guid->getMessage()); - } else { - $url = Horde::url($registry->link('tasks/show', array('uid' => $guid))); - $msgs[] = 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 { - $msgs[] = array('warning', _("This action is not supported.")); - } - break; - - case 'vJournal': - default: - $msgs[] = 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(); - $mime->setType('multipart/alternative'); - - $body = new Horde_Mime_Part(); - $body->setType('text/plain'); - $body->setCharset($charset); - $body->setContents(String::wrap($message, 76, "\n")); - - $ics = new Horde_Mime_Part(); - $ics->setType('text/calendar'); - $ics->setCharset($charset); - $ics->setContents($vCal->exportvCalendar()); - $ics->setName('event-reply.ics'); - $ics->setContentTypeParameter('METHOD', 'REPLY'); - - $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($subject, $charset)); - - // Send the reply. - $mail_driver = IMP_Compose::getMailDriver(); - try { - $mime->send($organizerEmail, $msg_headers, - $mail_driver['driver'], - $mail_driver['params']); - $msgs[] = array('success', _("Reply Sent.")); - } catch (Horde_Mime_Exception $e) { - $msgs[] = array('error', sprintf(_("Error sending reply: %s."), $e->getMessage())); - } - } else { - $msgs[] = 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); - - $message = _("Attached is a reply to a calendar request you sent."); - $body = new Horde_Mime_Part('text/plain', - String::wrap($message, 76, "\n"), - $charset); - - $ics = new Horde_Mime_Part('text/calendar', $vCal->exportvCalendar()); - $ics->setName('icalendar.ics'); - $ics->setContentTypeParameter('METHOD', 'REPLY'); - $ics->setCharset($charset); - - $mime = new Horde_Mime_Part(); - $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"), $charset)); - - // Send the reply. - $mail_driver = IMP_Compose::getMailDriver(); - try { - $mime->send($organizerEmail, $msg_headers, - $mail_driver['driver'], - $mail_driver['params']); - $msgs[] = array('success', _("Reply Sent.")); - } catch (Horde_Mime_Exception $e) { - $msgs[] = array('error', sprintf(_("Error sending reply: %s."), $e->getMessage())); - } - } else { - $msgs[] = array('warning', _("Invalid Action selected for this component.")); - } - break; - - case 'nosup': - // vFreebusy request. - default: - $msgs[] = array('warning', _("This action is not yet implemented.")); - break; - } - } - - // Create the HTML to display the iCal file. - $html = ''; - if ($_SESSION['imp']['view'] == 'imp') { - $html .= '
'; - } - - foreach ($components as $key => $component) { - switch ($component->getType()) { - case 'vEvent': - $html .= $this->_vEvent($component, $key, $method, $msgs); - break; - - case 'vTodo': - $html .= $this->_vTodo($component, $key, $method, $msgs); - break; - - case 'vTimeZone': - // Ignore them. - break; - - case 'vFreebusy': - $html .= $this->_vFreebusy($component, $key, $method, $msgs); - 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']['view'] == 'imp') { - $html .= '
'; - } - - return array( - $mime_id = array( - 'data' => $html, - 'status' => array(), - 'type' => 'text/html; charset=' . $charset - ) - ); - } - - /** - * Return the html for a vFreebusy. - */ - protected function _vFreebusy($vfb, $id, $method, $msgs) - { - global $registry, $prefs; - - $desc = $html = ''; - $sender = $vfb->getName(); - - switch ($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) . '

'; - - foreach ($msgs 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']['view'] != 'imp') { - return $html; - } - - $html .= '

' . _("Actions") . '

' . - ' '; - } - - /** - * Return the html for a vEvent. - */ - protected function _vEvent($vevent, $id, $method, $msgs) - { - global $registry, $prefs; - - $desc = $html = ''; - $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 ($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 . '

'; - - foreach ($msgs 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']['view'] != '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, $method, $msgs) - { - global $registry, $prefs; - - $desc = $html = ''; - $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 ($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 . '

'; - - foreach ($msgs 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']['view'] != '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"); - - case 'DECLINED': - return _("Declined"); - - case 'TENTATIVE': - return _("Tentatively Accepted"); - - case 'DELEGATED': - return _("Delegated"); - - case 'COMPLETED': - return _("Completed"); - - case 'IN-PROCESS': - return _("In Process"); - - case 'NEEDS-ACTION': - default: - return is_null($default) ? _("Needs Action") : $default; - } - } -} diff --git a/imp/lib/Mime/Viewer/mdn.php b/imp/lib/Mime/Viewer/mdn.php deleted file mode 100644 index e0abd2531..000000000 --- a/imp/lib/Mime/Viewer/mdn.php +++ /dev/null @@ -1,105 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_mdn extends Horde_Mime_Viewer_Driver -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => true, - 'full' => false, - 'info' => true, - 'inline' => true, - ); - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - /* If this is a straight message/disposition-notification part, just - * output the text. */ - if ($this->_mimepart->getType() == 'message/disposition-notification') { - return $this->_params['contents']->renderMIMEPart($this->_mimepart->getMIMEId(), IMP_Contents::RENDER_FULL, array('type' => 'text/plain', 'params' => $this->_params)); - } - - return $this->_renderInfo(); - } - - /** - * Return the rendered information about the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInfo() - { - $mdn_id = $this->_mimepart->getMimeId(); - $parts = array_keys($this->_mimepart->contentTypeMap()); - - $status = array( - array( - 'icon' => Horde::img('info_icon.png', _("Info"), null, $GLOBALS['registry']->getImageDir('horde')), - 'text' => array(_("A message you have sent has resulted in a return notification from the recipient.")) - ) - ); - - /* RFC 3798 [3]: There are three parts to a delivery status - * multipart/report message: - * (1) Human readable message - * (2) Machine parsable body part (message/disposition-notification) - * (3) Original message (optional) */ - - /* Print the human readable message. */ - reset($parts); - $curr_id = $first_id = next($parts); - $first_part = $this->_params['contents']->renderMIMEPart($curr_id, IMP_Contents::RENDER_INLINE_AUTO, array('params' => $this->_params)); - - /* Display a link to more detailed message. */ - $curr_id = Horde_Mime::mimeIdArithmetic($curr_id, 'next'); - $part = $this->_params['contents']->getMIMEPart($curr_id); - if ($part) { - $status[0]['text'][] = sprintf(_("Additional information can be viewed %s."), $this->_params['contents']->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("Additional information details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE)))); - } - - /* Display a link to the sent message. Try to download the text of - the message/rfc822 part first, if it exists. */ - $curr_id = Horde_Mime::mimeIdArithmetic($curr_id, 'next'); - $part = $this->_params['contents']->getMIMEPart($curr_id); - if ($part) { - $status[0]['text'][] = sprintf(_("The text of the sent message can be viewed %s."), $this->_params['contents']->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("The text of the sent message")))); - } - - if (empty($first_part)) { - $data = ''; - } else { - $status[0]['text'][] = _("The mail server generated the following informational message:"); - $status = array_merge($status, $first_part[$first_id]['status']); - $data = $first_part[$first_id]['data']; - } - - $ret = array_combine($parts, array_fill(0, count($parts), null)); - $ret[$mdn_id] = array( - 'data' => $data, - 'status' => $status, - 'type' => 'text/html; charset=' . NLS::getCharset() - ); - - return $ret; - } -} diff --git a/imp/lib/Mime/Viewer/partial.php b/imp/lib/Mime/Viewer/partial.php deleted file mode 100644 index 7bf2c0c60..000000000 --- a/imp/lib/Mime/Viewer/partial.php +++ /dev/null @@ -1,84 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_partial extends Horde_Mime_Viewer_Driver -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => true, - 'forceinline' => true, - 'full' => false, - 'info' => false, - 'inline' => false, - ); - - /** - * If this MIME part can contain embedded MIME parts, and those embedded - * MIME parts exist, return a list of MIME parts that contain the embedded - * MIME part information. - * - * @return array An array of Horde_Mime_Part objects, with the key as - * the ID, or null if no embedded MIME parts exist. - */ - protected function _getEmbeddedMimeParts() - { - $id = $this->_mimepart->getContentTypeParameter('id'); - $number = $this->_mimepart->getContentTypeParameter('number'); - $total = $this->_mimepart->getContentTypeParameter('total'); - - if (is_null($id) || is_null($number) || is_null($total)) { - return null; - } - - $mbox = $this->_params['contents']->getMailbox(); - - /* Perform the search to find the other parts of the message. */ - $query = new Horde_Imap_Client_Search_Query(); - $query->headerText('Content-Type', $id); - $indices = $GLOBALS['imp_search']->runSearchQuery($query, $mbox); - - /* If not able to find the other parts of the message, print error. */ - if (count($indices) != $total) { - $mime_part = new Horde_Mime_Part(); - $mime_part->setType('text/plain'); - $mime_part->setCharset(NLS::getCharset()); - $mime_part->setContents(sprintf(_("[Cannot display message - found only %s of %s parts of this message in the current mailbox.]"), count($indices), $total)); - return array($this->_mimepart->getMimeId() => $mime_part); - } - - /* Get the contents of each of the parts. */ - $parts = array(); - foreach ($indices as $val) { - /* No need to fetch the current part again. */ - if ($val == $number) { - $parts[$number] = $this->_mimepart->getContents(); - } else { - $ic = &IMP_Contents::singleton($val . IMP::IDX_SEP . $mbox); - $parts[$ic->getMIMEMessage()->getContentTypeParameter('number')] = $ic->getBody(); - } - } - - /* Sort the parts in numerical order. */ - ksort($parts, SORT_NUMERIC); - - /* Combine the parts. */ - $mime_part = Horde_Mime_Part::parseMessage(implode('', $parts)); - return ($mime_part === false) - ? null - : array($this->_mimepart->getMimeId() => $mime_part); - } -} diff --git a/imp/lib/Mime/Viewer/pdf.php b/imp/lib/Mime/Viewer/pdf.php deleted file mode 100644 index 81b8dd061..000000000 --- a/imp/lib/Mime/Viewer/pdf.php +++ /dev/null @@ -1,131 +0,0 @@ - - * @package Horde_Mime_Viewer - */ -class IMP_Horde_Mime_Viewer_pdf extends Horde_Mime_Viewer_pdf -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => false, - 'full' => true, - 'info' => true, - 'inline' => false - ); - - /** - * Return the full rendered version of the Horde_Mime_Part object. - * - * URL parameters used by this function: - *
-     * 'pdf_view_thumbnail' - (boolean) Output the thumbnail info.
-     * 
- * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _render() - { - /* Create the thumbnail and display. */ - if (!Util::getFormData('pdf_view_thumbnail')) { - return parent::_render(); - } - - $img = $this->_getHordeImageOb(true); - - 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'); - } - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $data, - 'status' => array(), - 'type' => $type - ) - ); - } - - /** - * Return the rendered information about the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInfo() - { - /* Check to see if convert utility is available. */ - if (!$this->_getHordeImageOb(false)) { - return array(); - } - - $status = array( - sprintf(_("A PDF file named %s is attached to this message. A thumbnail is below."), $this->_mimepart->getName(true)), - ); - - if ($GLOBALS['browser']->hasFeature('javascript')) { - $status[] = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('pdf_view_thumbnail' => 1)), false), _("View Attachment"), null, ''), null, null, null); - } else { - $status[] = Horde::link($this->_params['contents']->urlView($this->_mimepart, 'view_attach')) . Horde::img($this->_params['contents']->urlView($this->_mimepart, 'view_attach', array('params' => array('pdf_view_thumbnail' => 1)), false), _("View Attachment"), null, '') . ''; - } - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => '', - 'status' => array( - array( - 'icon' => Horde::img('mime/image.png', _("Thumbnail of attached PDF file")), - 'text' => $status - ) - ), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - } - - /** - * Return a Horde_Image object. - * - * @param boolean $load Whether to load the image data. - * - * @return mixed The Hore_Image object, or false on error. - */ - protected function _getHordeImageOb($load) - { - if (empty($GLOBALS['conf']['image']['convert'])) { - return false; - } - - $img = &Horde_Image::singleton('im', array('temp' => Horde::getTempdir())); - if (is_a($img, 'PEAR_Error')) { - return false; - } - - if ($load) { - $ret = $img->loadString(1, $this->_mimepart->getContents()); - if (is_a($ret, 'PEAR_Error')) { - return false; - } - } - - return $img; - } -} diff --git a/imp/lib/Mime/Viewer/pgp.php b/imp/lib/Mime/Viewer/pgp.php deleted file mode 100644 index 26d5f3f07..000000000 --- a/imp/lib/Mime/Viewer/pgp.php +++ /dev/null @@ -1,408 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => true, - 'forceinline' => true, - 'full' => false, - 'info' => false, - 'inline' => true - ); - - /** - * IMP_Crypt_Pgp object. - * - * @var IMP_Crypt_Pgp - */ - protected $_imppgp; - - /** - * The address of the sender. - * - * @var string - */ - protected $_address = null; - - /** - * Cache for inline data. - * - * @var array - */ - static protected $_inlinecache = array(); - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - if (empty($this->_imppgp) && - !empty($GLOBALS['conf']['utils']['gnupg'])) { - $this->_imppgp = Horde_Crypt::singleton(array('IMP', 'Pgp')); - } - - if (Util::getFormData('rawpgpkey')) { - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $this->_mimepart->getContents(), - 'status' => array(), - 'type' => 'text/plain; charset=' . $this->_mimepart->getCharset() - ) - ); - } - - /* Determine the address of the sender. */ - if (is_null($this->_address)) { - $headers = $this->_params['contents']->getHeaderOb(); - $this->_address = Horde_Mime_Address::bareAddress($headers->getValue('from')); - } - - switch ($this->_mimepart->getType()) { - case 'application/pgp-keys': - return $this->_outputPGPKey(); - - case 'multipart/signed': - return $this->_outputPGPSigned(); - - case 'multipart/encrypted': - return $this->_outputPGPEncrypted(); - - case 'application/pgp-encrypted': - case 'application/pgp-signature': - default: - return array(); - } - } - - /** - * If this MIME part can contain embedded MIME parts, and those embedded - * MIME parts exist, return an altered version of the Horde_Mime_Part that - * contains the embedded MIME part information. - * - * @return mixed A Horde_Mime_Part with the embedded MIME part information - * or null if no embedded MIME parts exist. - */ - protected function _getEmbeddedMimeParts() - { - if ($this->_mimepart->getType() != 'multipart/encrypted') { - return null; - } - - $partlist = array_keys($this->_mimepart->contentTypeMap()); - $base_id = reset($partlist); - $version_id = next($partlist); - $data_id = Horde_Mime::mimeIdArithmetic($version_id, 'next'); - - /* Initialize inline data. */ - $resymmetric = isset(self::$_inlinecache[$base_id]); - self::$_inlinecache[$base_id] = array( - $base_id => array( - 'data' => '', - 'status' => array( - array( - 'icon' => Horde::img('mime/encryption.png', 'PGP'), - 'text' => $resymmetric ? self::$_inlinecache[$base_id][$base_id]['status'][0]['text'] : array() - ) - ), - 'type' => 'text/html; charset=' . NLS::getCharset() - ), - $version_id => null, - $data_id => null - ); - $status = &self::$_inlinecache[$base_id][$base_id]['status'][0]['text']; - - /* Is PGP active? */ - if (empty($GLOBALS['conf']['utils']['gnupg']) || - !$GLOBALS['prefs']->getValue('use_pgp')) { - $status[] = _("The message below has been encrypted via PGP, however, PGP support is disabled so the message cannot be decrypted."); - return null; - } - - if (empty($this->_imppgp)) { - $this->_imppgp = Horde_Crypt::singleton(array('IMP', 'Pgp')); - } - - /* PGP version 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. */ - $encrypted_part = $this->_params['contents']->getMIMEPart($data_id); - $encrypted_data = $encrypted_part->getContents(); - - $symmetric_pass = $personal_pass = null; - - /* Check if this a symmetrically encrypted message. */ - try { - $symmetric = $this->_imppgp->encryptedSymmetrically($encrypted_data); - if ($symmetric) { - $symmetric_id = $this->_getSymmetricID(); - $symmetric_pass = $this->_imppgp->getPassphrase('symmetric', $symmetric_id); - - if (is_null($symmetric_pass)) { - $js_action = ''; - if (!$resymmetric) { - $status[] = _("The message has been encrypted via PGP."); - } - - switch ($_SESSION['imp']['view']) { - case 'dimp': - $js_action = 'DimpCore.reloadMessage({});'; - // Fall through - - case 'imp': - /* Ask for the correct passphrase if this is encrypted - * symmetrically. */ - $status[] = Horde::link('#', '', '', '', IMP::passphraseDialogJS('PGPSymmetric', $js_action, array('symmetricid' => $symmetric_id)) . ';return false;') . _("You must enter the passphrase used to encrypt this message to view it.") . ''; - break; - } - return null; - } - } - } catch (Horde_Exception $e) { - Horde::logMessage($e, __FILE__, __LINE__); - unset(self::$_inlinecache[$base_id]); - return null; - } - - /* Check if this is a literal compressed message. */ - try { - $info = $this->_imppgp->pgpPacketInformation($encrypted_data); - } catch (Horde_Exception $e) { - Horde::logMessage($e, __FILE__, __LINE__); - unset(self::$_inlinecache[$base_id]); - return null; - } - $literal = !empty($info['literal']); - - if ($literal) { - $status[] = _("The message below has been compressed via PGP."); - } else { - $status[] = _("The message below has been encrypted via PGP."); - if (!$symmetric) { - if (!$this->_imppgp->getPersonalPrivateKey()) { - /* Output if there is no personal private key to decrypt - * with. */ - $status[] = _("The message below has been encrypted via PGP, however, no personal private key exists so the message cannot be decrypted."); - return null; - } else { - $personal_pass = $this->_imppgp->getPassphrase('personal'); - - if (is_null($personal_pass)) { - $js_action = ''; - $status[] = _("The message has been encrypted via PGP."); - - switch ($_SESSION['imp']['view']) { - case 'dimp': - $js_action = 'DimpCore.reloadMessage({});'; - // Fall through - - case 'imp': - /* Ask for the private key's passphrase if this is - * encrypted asymmetrically. */ - $status[] = Horde::link('#', '', '', '', IMP::passphraseDialogJS('PGPPersonal', $js_action) . ';return false;') . _("You must enter the passphrase for your PGP private key to view this message.") . ''; - break; - } - return null; - } - } - } - } - - try { - if (!is_null($symmetric_pass)) { - $decrypted_data = $this->_imppgp->decryptMessage($encrypted_data, 'symmetric', $symmetric_pass); - } elseif (!is_null($personal_pass)) { - $decrypted_data = $this->_imppgp->decryptMessage($encrypted_data, 'personal', $personal_pass); - } else { - $decrypted_data = $this->_imppgp->decryptMessage($encrypted_data, 'literal'); - } - } catch (Horde_Exception $e) { - $status[] = _("The message below does not appear to be a valid PGP encrypted message. Error: ") . $e->getMessage(); - if (!is_null($symmetric_pass)) { - $this->_imppgp->unsetPassphrase('symmetric', $this->_getSymmetricID()); - return $this->_getEmbeddedMimeParts(); - } - return null; - } - - unset(self::$_inlinecache[$base_id][$data_id]); - - $msg = Horde_Mime_Part::parseMessage($decrypted_data->message); - $msg->buildMimeIds($data_id); - - return array($data_id => $msg); - } - - /** - * Generates output for 'application/pgp-keys' MIME_Parts. - * - * @return string The HTML output. - */ - protected function _outputPGPKey() - { - /* Initialize status message. */ - $status = array( - 'icon' => Horde::img('mime/encryption.png', 'PGP'), - 'text' => array( - _("A PGP Public Key was attached to the message.") - ) - ); - - $mime_id = $this->_mimepart->getMimeId(); - - if ($GLOBALS['prefs']->getValue('use_pgp') && - $GLOBALS['prefs']->getValue('add_source') && - $GLOBALS['registry']->hasMethod('contacts/addField')) { - $status['text'][] = Horde::link('#', '', '', '', $this->_imppgp->savePublicKeyURL($this->_params['contents']->getMailbox(), $this->_params['contents']->getIndex(), $mime_id) . 'return false;') . _("[Save the key to your Address book]") . ''; - } - $status['text'][] = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View the raw text of the Public Key."), array('params' => array('mode' => IMP_Contents::RENDER_INLINE, 'rawpgpkey' => 1))); - - try { - $data = '' . nl2br(str_replace(' ', ' ', $this->_imppgp->pgpPrettyKey($this->_mimepart->getContents()))) . ''; - } catch (Horde_Exception $e) { - $data = $e->getMessage(); - } - - return array( - $mime_id => array( - 'data' => $data, - 'status' => array($status), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - } - - /** - * Generates HTML output for 'multipart/signed' MIME parts. - * - * @return string The HTML output. - */ - protected function _outputPGPSigned() - { - $partlist = array_keys($this->_mimepart->contentTypeMap()); - $base_id = reset($partlist); - $signed_id = next($partlist); - $sig_id = Horde_Mime::mimeIdArithmetic($signed_id, 'next'); - - $ret = array( - $base_id => array( - 'data' => '', - 'status' => array( - array( - 'icon' => Horde::img('mime/encryption.png', 'PGP'), - 'text' => array() - ) - ), - 'type' => 'text/html; charset=' . NLS::getCharset() - ), - $sig_id => null - ); - $status = &$ret[$base_id]['status'][0]['text']; - - if (!$GLOBALS['prefs']->getValue('use_pgp') || - empty($GLOBALS['conf']['utils']['gnupg'])) { - /* If PGP not active, hide signature data and output status - * information. */ - $status[] = _("The message below has been digitally signed via PGP, but the signature cannot be verified."); - return $ret; - } - - $status[] = _("The message below has been digitally signed via PGP."); - - if ($GLOBALS['prefs']->getValue('pgp_verify') || - Util::getFormData('pgp_verify_msg')) { - $signed_data = $GLOBALS['imp_imap']->utils->removeBareNewlines($this->_params['contents']->getBodyPart($signed_id, array('mimeheaders' => true))); - $sig_part = $this->_params['contents']->getMIMEPart($sig_id); - - /* Check for the 'x-imp-pgp-signature' param. This is set by the - * plain driver when parsing PGP armor text. */ - $graphicsdir = $GLOBALS['registry']->getImageDir('horde'); - try { - $sig_result = $sig_part->getContentTypeParameter('x-imp-pgp-signature') - ? $this->_imppgp->verifySignature($signed_data, $this->_address) - : $this->_imppgp->verifySignature($signed_data, $this->_address, $sig_part->getContents()); - - if ($sig_result->result) { - $icon = Horde::img('alerts/success.png', _("Success"), null, $graphicsdir); - $sig_text = $sig_result->message; - } else { - $icon = Horde::img('alerts/warning.png', _("Warning"), null, $graphicsdir); - $sig_text = _("The signature could not be checked because the sender's key could not be found."); - } - } catch (Horde_Exception $e) { - $icon = Horde::img('alerts/error.png', _("Error"), null, $graphicsdir); - $sig_text = $e->getMessage(); - } - - require_once 'Horde/Text/Filter.php'; - $ret[$base_id]['status'][] = array( - 'icon' => $icon, - 'text' => array( - Text_Filter::filter($sig_text, 'text2html', array('parselevel' => TEXT_HTML_NOHTML)) - ) - ); - } else { - switch ($_SESSION['imp']['view']) { - case 'imp': - $status[] = Horde::link(Util::addParameter(IMP::selfUrl(), array('pgp_verify_msg' => 1))) . _("Click HERE to verify the message.") . ''; - break; - - case 'dimp': - $status[] = Horde::link('#', '', 'pgpVerifyMsg') . _("Click HERE to verify the message.") . ''; - break; - } - } - - return $ret; - } - - /** - * Generates HTML output for 'multipart/encrypted' MIME parts. - * - * @return string The HTML output. - */ - protected function _outputPGPEncrypted() - { - $id = $this->_mimepart->getMimeId(); - return isset(self::$_inlinecache[$id]) - ? self::$_inlinecache[$id] - : array(); - } - - /** - * Generates the symmetric ID for this message. - * - * @return string Symmetric ID. - */ - protected function _getSymmetricID() - { - return $this->_imppgp->getSymmetricID($this->_params['contents']->getMailbox(), $this->_params['contents']->getIndex(), $this->_mimepart->getMimeId()); - } - -} diff --git a/imp/lib/Mime/Viewer/plain.php b/imp/lib/Mime/Viewer/plain.php deleted file mode 100644 index 123136271..000000000 --- a/imp/lib/Mime/Viewer/plain.php +++ /dev/null @@ -1,293 +0,0 @@ - - * @author Michael Slusarz - * @package Horde_Mime_Viewer - */ -class IMP_Horde_Mime_Viewer_plain extends Horde_Mime_Viewer_plain -{ - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - global $conf, $prefs; - - $mime_id = $this->_mimepart->getMimeId(); - $type = 'text/html; charset=' . NLS::getCharset(); - - // Trim extra whitespace in the text. - $text = trim($this->_mimepart->getContents()); - if ($text == '') { - return array( - $mime_id => array( - 'data' => '', - 'status' => array(), - 'type' => $type - ) - ); - } - - // Convert to the local charset. - $text = String::convertCharset($text, $this->_mimepart->getCharset()); - - // Check for 'flowed' text data. - if ($this->_mimepart->getContentTypeParameter('format') == 'flowed') { - $text = $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp')); - } else { - /* A "From" located at the beginning of a line in the body text - * will be escaped with a '>' by the IMAP server. Remove this - * escape character or else the line will display as being - * quoted. Flowed conversion would have already taken care of this - * for us. */ - $text = preg_replace('/(\n+)> ?From(\s+)/', "$1From$2", $text); - } - - $text = IMP::filterText($text); - - /* Done processing if in mimp mode. */ - if ($_SESSION['imp']['view'] == 'mimp') { - return array( - $mime_id => array( - 'data' => $text, - 'status' => array(), - 'type' => $type - ) - ); - } - - // 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' => NLS::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, 'outputJS' => false); - } - - // Highlight simple markup of an email. - if ($prefs->getValue('highlight_simple_markup')) { - $filters['simplemarkup'] = array(); - } - - // Dim signatures. - if ($prefs->getValue('dim_signature')) { - $filters['dimsignature'] = array(); - } - - if ($prefs->getValue('emoticons')) { - $filters['emoticons'] = array('entities' => true); - } - - // Run filters. - $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( - $mime_id => array( - 'data' => '
' . "\n" . $text . '
', - 'status' => array(), - 'type' => $type - ) - ); - } - - /** - * Does this MIME part possibly contain embedded MIME parts? - * - * @return boolean True if this driver supports parsing embedded MIME - * parts. - */ - public function embeddedMimeParts() - { - return (!empty($GLOBALS['conf']['utils']['gnupg']) && $GLOBALS['prefs']->getValue('pgp_scan_body')) || $this->getConfigParam('uudecode'); - } - - /** - * If this MIME part can contain embedded MIME parts, and those embedded - * MIME parts exist, return a list of MIME parts that contain the embedded - * MIME part information. - * - * @return mixed An array of Horde_Mime_Part objects, with the key as - * the ID, or null if no embedded MIME parts exist. - */ - public function getEmbeddedMimeParts() - { - $ret = null; - - if (!empty($GLOBALS['conf']['utils']['gnupg']) && - $GLOBALS['prefs']->getValue('pgp_scan_body')) { - $ret = $this->_parsePGP(); - } - - if (is_null($ret) && $this->getConfigParam('uudecode')) { - $ret = $this->_parseUUencode(); - } - - return $ret; - } - - /** - * Scan text for armored PGP blocks and, if they exist, convert the part - * to the embedded MIME representation. - * - * @return mixed See self::_getEmbeddedMimeParts(). - */ - protected function _parsePGP() - { - /* Avoid infinite loop. */ - $imp_pgp = Horde_Crypt::singleton(array('IMP', 'Pgp')); - $parts = $imp_pgp->parsePGPData($this->_mimepart->getContents()); - if (empty($parts) || - ((count($parts) == 1) && - ($parts[0]['type'] == Horde_Crypt_Pgp::ARMOR_TEXT))) { - return null; - } - - $new_part = new Horde_Mime_Part(); - $new_part->setType('multipart/mixed'); - $charset = $this->_mimepart->getCharset(); - $mime_id = $this->_mimepart->getMimeId(); - - while (list(,$val) = each($parts)) { - switch ($val['type']) { - case Horde_Crypt_Pgp::ARMOR_TEXT: - $part = new Horde_Mime_Part(); - $part->setType('text/plain'); - $part->setCharset($charset); - $part->setContents(implode("\n", $val['data'])); - $new_part->addPart($part); - break; - - case Horde_Crypt_Pgp::ARMOR_PUBLIC_KEY: - $part = new Horde_Mime_Part(); - $part->setType('application/pgp-keys'); - $part->setContents(implode("\n", $val['data'])); - $new_part->addPart($part); - break; - - case Horde_Crypt_Pgp::ARMOR_MESSAGE: - $part = new Horde_Mime_Part(); - $part->setType('multipart/signed'); - // TODO: add micalg parameter - $part->setContentTypeParameter('protocol', 'application/pgp-encrypted'); - - $part1 = new Horde_Mime_Part(); - $part1->setType('application/pgp-encrypted'); - $part1->setContents("Version: 1\n"); - - $part2 = new Horde_Mime_Part(); - $part2->setType('application/octet-stream'); - $part2->setContents($message_encrypt); - $part2->setDisposition('inline'); - - $part->addPart($part1); - $part->addPart($part2); - - $new_part->addPart($part); - break; - - case Horde_Crypt_Pgp::ARMOR_SIGNED_MESSAGE: - if (($sig = current($parts)) && - ($sig['type'] == Horde_Crypt_Pgp::ARMOR_SIGNATURE)) { - $part = new Horde_Mime_Part(); - $part->setType('multipart/signed'); - // TODO: add micalg parameter - $part->setContentTypeParameter('protocol', 'application/pgp-signature'); - - $part1 = new Horde_Mime_Part(); - $part1->setType('text/plain'); - $part1->setCharset($charset); - - $part1_data = implode("\n", $val['data']); - $part1->setContents(substr($part1_data, strpos($part1_data, "\n\n") + 2)); - - $part2 = new Horde_Mime_Part(); - $part2->setType('application/x-imp-pgp-signature'); - $part2->setContents(String::convertCharset(implode("\n", $val['data']) . "\n" . implode("\n", $sig['data']), $charset)); - - $part->addPart($part1); - $part->addPart($part2); - $new_part->addPart($part); - - next($parts); - } - } - } - - $new_part->buildMimeIds($mime_id); - - return array($mime_id => $new_part); - } - - /** - * Scan text for UUencode data an, if it exists, convert the part to the - * embedded MIME representation. - * - * @return mixed See self::_getEmbeddedMimeParts(). - */ - protected function _parseUUencode() - { - $text = String::convertCharset($this->_mimepart->getContents(), $this->_mimepart->getCharset()); - - /* Don't want to use convert_uudecode() here as there may be multiple - * files residing in the text. */ - $files = &Mail_mimeDecode::uudecode($text); - if (empty($files)) { - return null; - } - - $new_part = new Horde_Mime_Part(); - $new_part->setType('multipart/mixed'); - $mime_id = $this->_mimepart->getMimeId(); - - $text_part = new Horde_Mime_Part(); - $text_part->setType('text/plain'); - $text_part->setCharset(NLS::getCharset()); - $text_part->setContents(preg_replace("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", "\n", $text)); - $new_part->addPart($text_part); - - reset($files); - while (list(,$file) = each($files)) { - $uupart = new Horde_Mime_Part(); - $uupart->setType('application/octet-stream'); - $uupart->setContents($file['filedata']); - $uupart->setName(strip_tags($file['filename'])); - $new_part->addPart($uupart); - } - - $new_part->buildMimeIds($mime_id); - - return array($mime_id => $new_part); - } -} diff --git a/imp/lib/Mime/Viewer/related.php b/imp/lib/Mime/Viewer/related.php deleted file mode 100644 index ec0be5e32..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 -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => true, - 'full' => true, - 'info' => false, - 'inline' => true, - ); - - /** - * Return the full rendered version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _render() - { - return $this->_IMPrender(false); - } - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - return $this->_IMPrender(true); - } - - /** - * Render out the currently set contents. - * - * @param boolean $inline Are we viewing inline? - * - * @return array See self::render(). - */ - protected function _IMPrender($inline) - { - $ids = array_keys($this->_mimepart->contentTypeMap()); - $related_id = $this->_mimepart->getMimeId(); - - $cids = $ret = array(); - $id = null; - - /* Build a list of parts -> CIDs. */ - foreach ($ids as $val) { - $ret[$val] = null; - if (strcmp($related_id, $val) !== 0) { - $part = $this->_mimepart->getPart($val); - $cids[$val] = $part->getContentId(); - } - } - - /* Look at the 'start' parameter to determine which part to start - * with. If no 'start' parameter, use the first part. RFC 2387 - * [3.1] */ - $start = $this->_mimepart->getContentTypeParameter('start'); - if (!empty($start)) { - $id = array_search($id, $cids); - } - - if (empty($id)) { - reset($ids); - $id = next($ids); - } - - /* Only display if the start part (normally text/html) can be - * displayed inline -OR- we are viewing this part as an attachment. */ - if ($inline && - !$this->_params['contents']->canDisplay($id, IMP_Contents::RENDER_INLINE)) { - return array(); - } - - - $render = $this->_params['contents']->renderMIMEPart($id, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL, array('params' => array_merge($this->_params, array('related_id' => $id, 'related_cids' => $cids)))); - - if (!$inline) { - return $render; - } - - foreach (array_keys($render) as $val) { - $ret[$val] = $render[$val]; - } - - return $ret; - } -} diff --git a/imp/lib/Mime/Viewer/smil.php b/imp/lib/Mime/Viewer/smil.php deleted file mode 100644 index 0f903329f..000000000 --- a/imp/lib/Mime/Viewer/smil.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @author Michael Slusarz - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_smil extends Horde_Mime_Viewer_smil -{ - /** - * 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'])) !== false)) { - $this->_content .= '
'; - } - break; - - case 'TEXT': - if (isset($attrs['SRC']) && - (($rp = $this->_getRelatedLink($attrs['SRC'])) !== 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 (isset($this->_params['related_id']) && - (($key = array_search(trim($cid, '<>', $this->_params['related_cids']))) !== false)) { - return $this->_param['contents']->getMIMEPart($key); - } - - return false; - } -} diff --git a/imp/lib/Mime/Viewer/smime.php b/imp/lib/Mime/Viewer/smime.php deleted file mode 100644 index ff42f87f6..000000000 --- a/imp/lib/Mime/Viewer/smime.php +++ /dev/null @@ -1,331 +0,0 @@ - - * @author Michael Slusarz - * @package Horde_Mime_Viewer - */ -class IMP_Horde_Mime_Viewer_smime extends Horde_Mime_Viewer_Driver -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => true, - 'forceinline' => true, - 'full' => false, - 'info' => false, - 'inline' => true - ); - - /** - * IMP_Crypt_Smime object. - * - * @var IMP_Crypt_Smime - */ - protected $_impsmime = null; - - /** - * Cache for inline data. - * - * @var array - */ - static protected $_inlinecache = array(); - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - /* Check to see if S/MIME support is available. */ - $this->_initSMIME(); - - if (Util::getFormData('view_smime_key')) { - return $this->_outputSMIMEKey(); - } - - if (is_null($this->_impsmime)) { - $this->_impsmime = false; - } else { - /* We need to insert JavaScript code now if S/MIME support is - * active. */ - Horde::addScriptFile('prototype.js', 'horde', true); - Horde::addScriptFile('imp.js', 'imp', true); - } - - switch ($this->_mimepart->getType()) { - case 'multipart/signed': - return $this->_outputSMIMESigned(); - - case 'application/pkcs7-mime': - case 'application/x-pkcs7-mime': - return $this->_outputSMIMEEncrypted(); - } - } - - /** - * If this MIME part can contain embedded MIME parts, and those embedded - * MIME parts exist, return an altered version of the Horde_Mime_Part that - * contains the embedded MIME part information. - * - * @return mixed A Horde_Mime_Part with the embedded MIME part information - * or null if no embedded MIME parts exist. - */ - protected function _getEmbeddedMimeParts() - { - if (!in_array($this->_mimepart->getType(), array('application/pkcs7-mime', 'application/x-pkcs7-mime'))) { - return null; - } - - // 'smime-type' must be empty or 'enveloped-data' - $smime_type = $this->_mimepart->getContentTypeParameter('smime-type'); - if ($smime_type == 'signed-data') { - // TODO - return null; - } - - $base_id = $this->_mimepart->getMimeId(); - - /* Initialize inline data. */ - self::$_inlinecache[$base_id] = array( - $base_id => array( - 'data' => '', - 'status' => array( - array( - 'icon' => Horde::img('mime/encryption.png', 'S/MIME'), - 'text' => array(_("This message has been encrypted via S/MIME.")) - ) - ), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - $status = &self::$_inlinecache[$base_id][$base_id]['status'][0]['text']; - - /* Is PGP active? */ - $this->_initSMIME(); - if (empty($this->_impsmime)) { - $status[] = _("S/MIME support is not currently enabled so the message is unable to be decrypted."); - return null; - } - - if (!$this->_impsmime->getPersonalPrivateKey()) { - $status[] = _("No personal private key exists so the message is unable to be decrypted."); - return null; - } - - /* Make sure we have a passphrase. */ - $passphrase = $this->_impsmime->getPassphrase(); - if ($passphrase === false) { - $js_action = ''; - - switch ($_SESSION['imp']['view'] == 'imp') { - case 'dimp': - $js_action = 'DimpCore.reloadMessage({});'; - // Fall through - - case 'imp': - $status[] = Horde::link('#', '', '', '', IMP::passphraseDialogJS('SMIMEPersonal', $js_action) . ';return false;') . _("You must enter the passphrase for your S/MIME private key to view this message.") . ''; - break; - } - return null; - } - - $raw_text = $GLOBALS['imp_imap']->utils->removeBareNewlines($this->_params['contents']->getBodyPart($this->_mimepart->getMimeId(), array('mimeheaders' => true))); - - try { - $decrypted_data = $this->_impsmime->decryptMessage($raw_text); - } catch (Horde_Exception $e) { - $status[] = $e->getMessage(); - return null; - } - - return array($base_id => Horde_Mime_Part::parseMessage($decrypted_data)); - } - - /** - * Generates HTML output for the S/MIME key. - * - * @return string The HTML output. - */ - protected function _outputSMIMEKey() - { - if (empty($this->_impsmime)) { - return array(); - } - - $raw_text = $GLOBALS['imp_imap']->utils->removeBareNewlines($this->_params['contents']->getBodyPart($this->_mimepart->getMimeId(), array('mimeheaders' => true))); - - try { - $sig_result = $this->_impsmime->verifySignature($raw_text); - } catch (Horde_Exception $e) {} - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $this->_impsmime->certToHTML($sig_result->cert), - 'status' => array(), - 'type' => 'text/html; charset=' . NLS::getCharset() - ) - ); - } - - /** - * Init the S/MIME Horde_Crypt object. - */ - protected function _initSMIME() - { - if (is_null($this->_impsmime) && - $GLOBALS['prefs']->getValue('use_smime')) { - try { - $this->_impsmime = Horde_Crypt::singleton(array('IMP', 'Smime')); - $this->_impsmime->checkForOpenSSL(); - } catch (Horde_Exception $e) { - $this->_impsmime = null; - } - } - } - - /** - * Generates HTML output for 'multipart/signed' MIME parts. - * - * @return array TODO - */ - protected function _outputSMIMESigned() - { - $partlist = array_keys($this->_mimepart->contentTypeMap()); - $base_id = reset($partlist); - $sig_id = Horde_Mime::mimeIdArithmetic(next($partlist), 'next'); - - $ret = array( - $base_id => array( - 'data' => '', - 'status' => array( - array( - 'icon' => Horde::img('mime/encryption.png', 'S/MIME'), - 'text' => array(_("This message has been digitally signed via S/MIME.")) - ) - ), - 'type' => 'text/html; charset=' . NLS::getCharset() - ), - $sig_id => null - ); - $status = &$ret[$base_id]['status'][0]['text']; - - if (!$GLOBALS['prefs']->getValue('use_smime')) { - $status[] = _("S/MIME support is not enabled so the digital signature is unable to be verified."); - return $ret; - } - - $raw_text = $base_id - ? $this->_params['contents']->getBodyPart($base_id, array('mimeheaders' => true)) - : $this->_params['contents']->fullMessageText(); - $raw_text = $GLOBALS['imp_imap']->utils->removeBareNewlines($raw_text); - - $sig_result = null; - - if ($GLOBALS['prefs']->getValue('smime_verify') || - Util::getFormData('smime_verify_msg')) { - try { - $sig_result = $this->_impsmime->verifySignature($raw_text); - } catch (Horde_Exception $e) { - $ret[$base_id]['status'][0]['icon'] = ($e->getCode() == 'horde.warning') - ? Horde::img('alerts/warning.png', _("Warning"), null, $graphicsdir) - : Horde::img('alerts/error.png', _("Error"), null, $graphicsdir); - $status[] = $e->getMessage(); - return $ret; - } - } else { - switch ($_SESSION['imp']['view']) { - case 'imp': - $status[] = Horde::link(Util::addParameter(IMP::selfUrl(), 'smime_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; - break; - - case 'dimp': - $status[] = Horde::link('#', '', 'smimeVerifyMsg') . _("Click HERE to verify the message.") . ''; - break; - } - return $ret; - } - - $subpart = $this->_params['contents']->getMIMEPart($sig_id); - if (!isset($subpart)) { - try { - $msg_data = $this->_impsmime->extractSignedContents($raw_text); - $subpart = Horde_Mime_Part::parseMessage($msg_data); - } catch (Horde_Exception $e) { - $this->_status[] = $e->getMessage(); - $subpart = $this->_mimepart; - } - } - - $graphicsdir = $GLOBALS['registry']->getImageDir('horde'); - - $ret[$base_id]['status'][0]['icon'] = Horde::img('alerts/success.png', _("Success"), null, $graphicsdir); - - /* This message has been verified but there was no output - * from the PGP program. */ - if (empty($sig_result->result) || ($sig_result->result === true)) { - $email = (is_array($sig_result->email)) - ? implode(', ', $sig_result->email) - : $sig_result->email; - $status[] = sprintf(_("The message has been verified. Sender: %s."), htmlspecialchars($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; - } else { - $subject = null; - } - - if (!empty($subject) && - $GLOBALS['registry']->hasMethod('contacts/addField') && - $GLOBALS['prefs']->getValue('add_source')) { - $status[] = sprintf(_("The S/MIME certificate of %s: "), @htmlspecialchars($subject, ENT_COMPAT, NLS::getCharset())) . $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View"), array('params' => array('mode' => IMP_Contents::RENDER_INLINE, 'view_smime_key' => 1))) . '/' . Horde::link('#', '', null, null, $this->_impsmime->savePublicKeyURL($sig_result->cert, $this->_params['contents']->getIndex(), $sig_id) . ' return false;') . _("Save in your Address Book") . ''; - } - } - - return $ret; - } - - /** - * Generates output for encrypted S/MIME parts. - * - * @return array TODO - */ - protected function _outputSMIMEEncrypted() - { - $id = $this->_mimepart->getMimeId(); - return isset(self::$_inlinecache[$id]) - ? self::$_inlinecache[$id] - : array(); - } -} diff --git a/imp/lib/Mime/Viewer/status.php b/imp/lib/Mime/Viewer/status.php deleted file mode 100644 index 99c277213..000000000 --- a/imp/lib/Mime/Viewer/status.php +++ /dev/null @@ -1,148 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_status extends Horde_Mime_Viewer_Driver -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => true, - 'full' => false, - 'info' => true, - 'inline' => true, - ); - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - /* If this is a straight message/disposition-notification part, just - * output the text. */ - if ($this->_mimepart->getType() == 'message/delivery-status') { - return $this->_params['contents']->renderMIMEPart($this->_mimepart->getMIMEId(), IMP_Contents::RENDER_FULL, array('type' => 'text/plain', 'params' => $this->_params)); - } - - return $this->_renderInfo(); - } - - /** - * Return the rendered information about the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInfo() - { - $parts = array_keys($this->_mimepart->contentTypeMap()); - - reset($parts); - $part1_id = next($parts); - $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); - $part3_id = Horde_Mime::mimeIdArithmetic($part2_id, 'next'); - - /* RFC 3464 [2]: There are three parts to a delivery status - * multipart/report message: - * (1) Human readable message - * (2) Machine parsable body part (message/delivery-status) - * (3) Returned message (optional) - * - * Information on the message status is found in the 'Action' field - * located in part #2 (RFC 3464 [2.3.3]). It can be either 'failed', - * 'delayed', 'delivered', 'relayed', or 'expanded'. */ - - /* Get the action first - it appears in the second part. */ - $action = null; - $part2 = $this->_params['contents']->getMIMEPart($part2_id); - - foreach (explode("\n", $part2->getContents()) as $line) { - if (stristr($line, 'Action:') !== false) { - $action = strtolower(trim(substr($line, strpos($line, ':') + 1))); - if (strpos($action, ' ') !== false) { - $action = substr($action, 0, strpos($action, ' ')); - } - break; - } - } - - if (is_null($action)) { - return array(); - } - - /* Get the correct text strings for the action type. */ - switch ($action) { - case 'failed': - case 'delayed': - $status = array( - array( - 'icon' => Horde::img('alerts/error.png', _("Error"), null, $GLOBALS['registry']->getImageDir('horde')), - 'text' => array( - _("ERROR: Your message could not be delivered."), - sprintf(_("Additional error message details can be viewed %s."), $this->_params['contents']->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Additional message details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE)))) - ) - ) - ); - $msg_link = _("The text of the returned message can be viewed %s."); - $msg_link_status = _("The text of the returned message"); - break; - - case 'delivered': - case 'expanded': - case 'relayed': - $status = array( - array( - 'icon' => Horde::img('alerts/success.png', _("Success"), null, $GLOBALS['registry']->getImageDir('horde')), - 'text' => array( - _("Your message was successfully delivered."), - sprintf(_("Additional message details can be viewed %s."), $this->_params['contents']->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Additional message details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE)))) - ) - ) - ); - $msg_link = _("The text of the message can be viewed %s."); - $msg_link_status = _("The text of the message"); - break; - } - - /* Print the human readable message. */ - $first_part = $this->_params['contents']->renderMIMEPart($part1_id, IMP_Contents::RENDER_INLINE_AUTO, array('params' => $this->_params)); - - /* Display a link to the returned message, if it exists. */ - $part3 = $this->_params['contents']->getMIMEPart($part3_id); - if ($part3) { - $status[0]['text'][] = sprintf($msg_link, $this->_params['contents']->linkViewJS($part3, 'view_attach', _("HERE"), array('jstext' => $msg_link_status, 'ctype' => 'message/rfc822'))); - } - - if (empty($first_part)) { - $data = ''; - } else { - $status[0]['text'][] = _("The mail server generated the following informational message:"); - $status = array_merge($status, $first_part[$part1_id]['status']); - $data = $first_part[$part1_id]['data']; - } - - $ret = array_combine($parts, array_fill(0, count($parts), null)); - - $ret[$this->_mimepart->getMimeId()] = array( - 'data' => $data, - 'status' => $status, - 'type' => 'text/html; charset=' . NLS::getCharset() - ); - - return $ret; - } -} diff --git a/imp/lib/Mime/Viewer/tnef.php b/imp/lib/Mime/Viewer/tnef.php deleted file mode 100644 index 5405e88fc..000000000 --- a/imp/lib/Mime/Viewer/tnef.php +++ /dev/null @@ -1,124 +0,0 @@ - - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_tnef extends Horde_Mime_Viewer_tnef -{ - /** - * Can this driver render various views? - * - * @var boolean - */ - protected $_capability = array( - 'embedded' => false, - 'forceinline' => true, - 'full' => true, - 'info' => true, - 'inline' => false - ); - - /** - * Return the full rendered version of the Horde_Mime_Part object. - * - * URL parameters used by this function: - *
-     * 'tnef_attachment' - (integer) The TNEF attachment to download.
-     * 
- * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _render() - { - if (!Util::getFormData('tnef_attachment')) { - $ret = $this->_renderInfo(); - reset($ret); - $ret[key($ret)]['data'] = '' . $ret[key($ret)]['data'] . ''; - return $ret; - } - - /* Get the data from the attachment. */ - $tnef = &Horde_Compress::singleton('tnef'); - $tnefData = $tnef->decompress($this->_mimepart->getContents()); - - /* Display the requested file. Its position in the $tnefData - * array can be found in '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)) { - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $text, - 'name' => $tnefData[$tnefKey]['name'], - 'status' => array(), - 'type' => $tnefData[$tnefKey]['type'] . '/' . $tnefData[$tnefKey]['subtype'] - ) - ); - } - } - - // TODO: Error reporting - return array(); - } - - /** - * Return the rendered information about the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInfo() - { - /* Get the data from the attachment. */ - $tnef = &Horde_Compress::singleton('tnef'); - $tnefData = $tnef->decompress($this->_mimepart->getContents()); - - $text = ''; - - if (!count($tnefData)) { - $status = array( - 'text' => array(_("No attachments found.")) - ); - } else { - $status = array( - 'text' => array(_("The following files were attached to this part:")) - ); - - reset($tnefData); - while (list($key, $data) = each($tnefData)) { - $temp_part = $this->_mimepart; - $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 (in_array($type, array('application/octet-stream', 'application/base64'))) { - $type = Horde_Mime_Magic::filenameToMIME($data['name']); - } - $temp_part->setType($type); - - $link = $this->_params['contents']->linkView($temp_part, 'view_attach', htmlspecialchars($data['name']), array('jstext' => sprintf(_("View %s"), $data['name']), 'params' => array('tnef_attachment' => $key + 1))); - $text .= _("Attached File:") . '  ' . $link . '  (' . $data['type'] . '/' . $data['subtype'] . ")
\n"; - } - } - - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $text, - 'status' => array($status), - 'type' => '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 e7d5db12e..000000000 --- a/imp/lib/Mime/Viewer/zip.php +++ /dev/null @@ -1,93 +0,0 @@ - - * @author Michael Slusarz - * @package Horde_Mime - */ -class IMP_Horde_Mime_Viewer_zip extends Horde_Mime_Viewer_zip -{ - /** - * Return the full rendered version of the Horde_Mime_Part object. - * - * URL parameters used by this function: - *
-     * 'zip_attachment' - (integer) The ZIP attachment to download.
-     * 
- * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _render() - { - if (!Util::getFormData('zip_attachment')) { - $this->_callback = array(&$this, '_IMPcallback'); - return parent::_render(); - } - - /* Send the requested file. Its position in the zip archive is located - * in 'zip_attachment'. */ - $data = $this->_mimepart->getContents(); - $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)) { - return array( - $this->_mimepart->getMimeId() => array( - 'data' => $text, - 'name' => basename($zipInfo[$fileKey]['name']), - 'status' => array(), - 'type' => 'application/octet-stream' - ) - ); - } - } - - // TODO: Error reporting - return array(); - } - - /** - * Return the rendered inline version of the Horde_Mime_Part object. - * - * @return array See Horde_Mime_Viewer_Driver::render(). - */ - protected function _renderInline() - { - $this->_callback = array(&$this, '_IMPcallback'); - return parent::_renderInline(); - } - - /** - * The function to use as a callback to _toHTML(). - * - * @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 _IMPcallback($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))) { - $mime_part = $this->_mimepart; - $mime_part->setName(basename($name)); - $val['name'] = str_replace($name, $this->_params['contents']->linkView($mime_part, 'download_render', $name, array('jstext' => sprintf(_("View %s"), str_replace(' ', ' ', $name)), 'class' => 'fixed', 'params' => array('zip_attachment' => urlencode($key) + 1))), $val['name']); - } - - return $val; - } -}