--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Alternative class renders out messages from
+ * multipart/alternative content types (RFC 2046 [5.1.4]).
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Appledouble class handles multipart/appledouble
+ * messages conforming to RFC 1740.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Enriched class renders out plain text from
+ * enriched content tags, ala RFC 1896
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Eric Rostetter <eric.rostetter@physics.utexas.edu>
+ * @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*>.+)$|', '<span class="quoted1">\1</span>', explode("\n", $text)));
+ $indent = 1;
+ while (preg_match('|>(\s?>){' . $indent . '}|', $text)) {
+ $text = implode("\n", preg_replace('|^<span class="quoted' . ((($indent - 1) % 5) + 1) . '">(\s*>(\s?>){' . $indent . '}.+)$|', '<span class="quoted' . (($indent % 5) + 1) . '">\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 .= '<span class="signature">' . $parts[0] .
+ preg_replace('|class="[^"]+"|', 'class="signature-fixed"', $parts[1]) .
+ '</span>';
+ }
+ }
+
+ // Filter bad language.
+ return IMP::filterText($text);
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Html class renders out HTML text with an effort
+ * to remove potentially malicious code.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Anil Madhavapeddy <anil@recoil.org>
+ * @author Jon Parise <jon@horde.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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
+ (
+ # <img> tags
+ <img[^>]+src=
+ # <input> tags
+ |<input[^>]*src=
+ # "background" attributes
+ |<body[^>]*background=|<td[^>]*background=|<table[^>]*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('/<a\s([^>]*\s*href=["\']?(#|mailto:))/i',
+ '/<a\s([^>]*)\s*target=["\']?[^>"\'\s]*["\']?/i',
+ '/<a\s/i',
+ '/<area\s([^>]*\s*href=["\']?(#|mailto:))/i',
+ '/<area\s([^>]*)\s*target=["\']?[^>"\'\s]*["\']?/i',
+ '/<area\s/i',
+ "/\x01/",
+ "/\x02/"),
+ array("<\x01\\1",
+ "<\x01 \\1 target=\"_blank\"",
+ '<a target="_blank" ',
+ "<\x02\\1",
+ "<\x02 \\1 target=\"_blank\"",
+ '<area target="_blank" ',
+ 'a ',
+ 'area '),
+ $data);
+
+ /* Turn mailto: links into our own compose links. */
+ if ($inline && $GLOBALS['registry']->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 = '<div class="htmlMessage">' . $data . '</div>';
+ }
+
+ /* 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) . '</a>'
+ )
+ );
+
+ $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);
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Images class allows display of images attached
+ * to a message.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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:
+ * <pre>
+ * 'imp_img_view' - (string) One of the following:
+ * 'data' - Output the image directly.
+ * 'load_convert' - TODO
+ * 'view_convert' - TODO
+ * 'view_thumbnail' - TODO
+ * </pre>
+ *
+ * @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 <img> 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, '') . '</a>';
+ }
+
+ 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 = <<<EOD
+<html>
+<head>
+<title>$title</title>
+<style type="text/css"><!-- body { margin:0px; padding:0px; } --></style>
+EOD;
+
+ /* Only use javascript if we are using a DOM capable browser. */
+ if ($GLOBALS['browser']->getFeature('dom')) {
+ /* Javascript display. */
+ $loading = _("Loading...");
+ $str .= <<<EOD
+<script type="text/javascript">
+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();
+}
+</script></head>
+<body onload="resizeWindow();"><span id="splash" style="color:gray;font-family:sans-serif;padding:2px;">$loading</span><img id="disp_image" style="display:none;" src="$self_url" /></body></html>
+EOD;
+ } else {
+ /* Non-javascript display. */
+ $img_txt = _("Image");
+ $str .= <<<EOD
+</head>
+<body bgcolor="#ffffff">
+<img border="0" src="$self_url" alt="$img_txt" />
+</body>
+</html>
+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;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Itip class displays vCalendar/iCalendar data
+ * and provides an option to import the data into a calendar source,
+ * if one is available.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @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:
+ * <pre>
+ * 'identity' - (integer) TODO
+ * 'itip_action' - (array) TODO
+ * </pre>
+ *
+ * @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' => '<h1>' . _("The calendar data is invalid") . '</h1>' . '<pre>' . htmlspecialchars($data) . '</pre>',
+ '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')) . '</a>');
+ }
+ }
+ }
+ 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')) . '</a>');
+ }
+ }
+ 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')) . '</a>');
+ }
+ } 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 .= '<form method="post" name="iCal" action="' . (IMP::selfUrl()) . '">';
+ }
+
+ 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 .= '</form>';
+ }
+
+ 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 .= '<h1 class="header">' . sprintf($desc, $sender) . '</h1>';
+
+ foreach ($msgs as $msg) {
+ $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '</p>';
+ }
+
+ $start = $vfb->getAttribute('DTSTART');
+ if (!is_a($start, 'PEAR_Error')) {
+ if (is_array($start)) {
+ $html .= '<p><strong>' . _("Start") . ':</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '</p>';
+ } else {
+ $html .= '<p><strong>' . _("Start") . ':</strong> ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '</p>';
+ }
+ }
+
+ $end = $vfb->getAttribute('DTEND');
+ if (!is_a($end, 'PEAR_Error')) {
+ if (is_array($end)) {
+ $html .= '<p><strong>' . _("End") . ':</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '</p>';
+ } else {
+ $html .= '<p><strong>' . _("End") . ':</strong> ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '</p>';
+ }
+ }
+
+ if ($_SESSION['imp']['view'] != 'imp') {
+ return $html;
+ }
+
+ $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>' .
+ '<select name="itip_action[' . $id . ']">';
+
+ switch ($method) {
+ case 'PUBLISH':
+ if ($registry->hasMethod('calendar/import_vfreebusy')) {
+ $html .= '<option value="import">' . _("Remember the free/busy information.") . '</option>';
+ } else {
+ $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . '</option>';
+ }
+ break;
+
+ case 'REQUEST':
+ if ($registry->hasMethod('calendar/getFreeBusy')) {
+ $html .= '<option value="reply">' . _("Reply with requested free/busy information.") . '</option>' .
+ '<option value="reply2m">' . _("Reply with free/busy for next 2 months.") . '</option>';
+ } else {
+ $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . '</option>';
+ }
+
+ $html .= '<option value="deny">' . _("Deny request for free/busy information") . '</option>';
+ break;
+
+ case 'REPLY':
+ if ($registry->hasMethod('calendar/import_vfreebusy')) {
+ $html .= '<option value="import">' . _("Remember the free/busy information.") . '</option>';
+ } else {
+ $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . '</option>';
+ }
+ break;
+ }
+
+ return $html . '</select> <input type="submit" class="button" value="' . _("Go") . '/>';
+ }
+
+ /**
+ * 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[] = '<option value="import">' . _("Add this to my calendar") . '</option>';
+ }
+ 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[] = '<option value="accept-import">' . _("Accept and update in my calendar") . '</option>';
+ $options[] = '<option value="import">' . _("Update in my calendar") . '</option>';
+ } elseif ($registry->hasMethod('calendar/import')) {
+ $options[] = '<option value="accept-import">' . _("Accept and add to my calendar") . '</option>';
+ $options[] = '<option value="import">' . _("Add to my calendar") . '</option>';
+ }
+ $options[] = '<option value="accept">' . _("Accept request") . '</option>';
+ $options[] = '<option value="tentative">' . _("Tentatively Accept request") . '</option>';
+ $options[] = '<option value="deny">' . _("Deny request") . '</option>';
+ // $options[] = '<option value="delegate">' . _("Delegate position") . '</option>';
+ break;
+
+ case 'ADD':
+ $desc = _("%s wishes to ammend \"%s\".");
+ if ($registry->hasMethod('calendar/import')) {
+ $options[] = '<option value="import">' . _("Update this event on my calendar") . '</option>';
+ }
+ break;
+
+ case 'REFRESH':
+ $desc = _("%s wishes to receive the latest information about \"%s\".");
+ $options[] = '<option value="send">' . _("Send Latest Information") . '</option>';
+ break;
+
+ case 'REPLY':
+ $desc = _("%s has replied to the invitation to \"%s\".");
+ $sender = $this->_headers->getValue('From');
+ if ($registry->hasMethod('calendar/updateAttendee')) {
+ $options[] = '<option value="update">' . _("Update respondent status") . '</option>';
+ }
+ break;
+
+ case 'CANCEL':
+ if (is_a($instance = $vevent->getAttribute('RECURRENCE-ID'), 'PEAR_Error')) {
+ $desc = _("%s has cancelled \"%s\".");
+ if ($registry->hasMethod('calendar/delete')) {
+ $options[] = '<option value="delete">' . _("Delete from my calendar") . '</option>';
+ }
+ } else {
+ $desc = _("%s has cancelled an instance of the recurring \"%s\".");
+ if ($registry->hasMethod('calendar/replace')) {
+ $options[] = '<option value="import">' . _("Update in my calendar") . '</option>';
+ }
+ }
+ 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 .= '<h2 class="header">' . $desc . '</h2>';
+
+ foreach ($msgs as $msg) {
+ $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '</p>';
+ }
+
+ $start = $vevent->getAttribute('DTSTART');
+ if (!is_a($start, 'PEAR_Error')) {
+ if (is_array($start)) {
+ $html .= '<p><strong>' . _("Start") . ':</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '</p>';
+ } else {
+ $html .= '<p><strong>' . _("Start") . ':</strong> ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '</p>';
+ }
+ }
+
+ $end = $vevent->getAttribute('DTEND');
+ if (!is_a($end, 'PEAR_Error')) {
+ if (is_array($end)) {
+ $html .= '<p><strong>' . _("End") . ':</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '</p>';
+ } else {
+ $html .= '<p><strong>' . _("End") . ':</strong> ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '</p>';
+ }
+ }
+
+ $sum = $vevent->getAttribute('SUMMARY');
+ if (!is_a($sum, 'PEAR_Error')) {
+ $html .= '<p><strong>' . _("Summary") . ':</strong> ' . htmlspecialchars($sum) . '</p>';
+ } else {
+ $html .= '<p><strong>' . _("Summary") . ':</strong> <em>' . _("None") . '</em></p>';
+ }
+
+ $desc = $vevent->getAttribute('DESCRIPTION');
+ if (!is_a($desc, 'PEAR_Error')) {
+ $html .= '<p><strong>' . _("Description") . ':</strong> ' . nl2br(htmlspecialchars($desc)) . '</p>';
+ }
+
+ $loc = $vevent->getAttribute('LOCATION');
+ if (!is_a($loc, 'PEAR_Error')) {
+ $html .= '<p><strong>' . _("Location") . ':</strong> ' . htmlspecialchars($loc) . '</p>';
+ }
+
+ if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) {
+ $html .= '<h2 class="smallheader">' . _("Attendees") . '</h2>';
+
+ $html .= '<table><thead class="leftAlign"><tr><th>' . _("Name") . '</th><th>' . _("Role") . '</th><th>' . _("Status") . '</th></tr></thead><tbody>';
+ 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 .= '<tr><td>' . htmlspecialchars($attendee) . '</td><td>' . htmlspecialchars($role) . '</td><td>' . htmlspecialchars($status) . '</td></tr>';
+ }
+ $html .= '</tbody></table>';
+ }
+
+ if ($_SESSION['imp']['view'] != 'imp') {
+ return $html;
+ }
+
+ if ($options) {
+ $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>' .
+ '<label for="action_' . $id . '" class="hidden">' . _("Actions") . '</label>' .
+ '<select id="action_' . $id . '" name="itip_action[' . $id . ']">' .
+ implode("\n", $options) .
+ '</select> <input type="submit" class="button" value="' . _("Go") . '" />';
+ }
+
+ 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[] = '<option value="import">' . _("Add this to my tasklist") . '</option>';
+ }
+ 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 .= '<h2 class="header">' . $desc . '</h2>';
+
+ foreach ($msgs as $msg) {
+ $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '</p>';
+ }
+
+ $priority = $vtodo->getAttribute('PRIORITY');
+ if (!is_a($priority, 'PEAR_Error')) {
+ $html .= '<p><strong>' . _("Priority") . ':</strong> ' . (int)$priority . '</p>';
+ }
+
+ $sum = $vtodo->getAttribute('SUMMARY');
+ if (!is_a($sum, 'PEAR_Error')) {
+ $html .= '<p><strong>' . _("Summary") . ':</strong> ' . htmlspecialchars($sum) . '</p>';
+ } else {
+ $html .= '<p><strong>' . _("Summary") . ':</strong> <em>' . _("None") . '</em></p>';
+ }
+
+ $desc = $vtodo->getAttribute('DESCRIPTION');
+ if (!is_a($desc, 'PEAR_Error')) {
+ $html .= '<p><strong>' . _("Description") . ':</strong> ' . nl2br(htmlspecialchars($desc)) . '</p>';
+ }
+
+ $attendees = $vtodo->getAttribute('ATTENDEE');
+ $params = $vtodo->getAttribute('ATTENDEE', true);
+
+ if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) {
+ $html .= '<h2 class="smallheader">' . _("Attendees") . '</h2>';
+ if (!is_array($attendees)) {
+ $attendees = array($attendees);
+ }
+
+ $html .= '<table><thead class="leftAlign"><tr><th>' . _("Name") . '</th><th>' . _("Role") . '</th><th>' . _("Status") . '</th></tr></thead><tbody>';
+ 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 .= '<tr><td>' . htmlspecialchars($attendee) . '</td><td>' . htmlspecialchars($role) . '</td><td>' . htmlspecialchars($status) . '</td></tr>';
+ }
+ $html .= '</tbody></table>';
+ }
+
+ if ($_SESSION['imp']['view'] != 'imp') {
+ return $html;
+ }
+
+ if ($options) {
+ $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>' .
+ '<select name="itip_action[' . $id . ']">' .
+ implode("\n", $options) .
+ '</select> <input type="submit" class="button" value="' . _("Go") . '" />';
+ }
+
+ 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;
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Mdn class handles multipart/report messages that
+ * that refer to message disposition notification (MDN) messages (RFC 3798).
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Partial class allows message/partial messages
+ * to be displayed (RFC 2046 [5.2.2]).
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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);
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Pdf class enables generation of thumbnails for
+ * PDF attachments.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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:
+ * <pre>
+ * 'pdf_view_thumbnail' - (boolean) Output the thumbnail info.
+ * </pre>
+ *
+ * @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, '') . '</a>';
+ }
+
+ 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;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Pgp class allows viewing/decrypting of PGP
+ * formatted messages. This class implements RFC 3156.
+ *
+ * This class handles the following MIME types:
+ * application/pgp-encryption (in multipart/encrypted part)
+ * application/pgp-keys
+ * application/pgp-signature (in multipart/signed part)
+ *
+ * This class may add the following parameters to the URL:
+ * 'pgp_verify_msg' - (boolean) Do verification of PGP signed data.
+ * 'rawpgpkey' - (boolean) Display the PGP Public Key in raw, text format
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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.") . '</a>';
+ 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.") . '</a>';
+ 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]") . '</a>';
+ }
+ $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 = '<span class="fixed">' . nl2br(str_replace(' ', ' ', $this->_imppgp->pgpPrettyKey($this->_mimepart->getContents()))) . '</span>';
+ } 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.") . '</a>';
+ break;
+
+ case 'dimp':
+ $status[] = Horde::link('#', '', 'pgpVerifyMsg') . _("Click HERE to verify the message.") . '</a>';
+ 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());
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Plain class renders out text/plain MIME parts
+ * with URLs made into hyperlinks.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Anil Madhavapeddy <anil@recoil.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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' => '<div class="fixed leftAlign">' . "\n" . $text . '</div>',
+ '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);
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Related class handles multipart/related
+ * (RFC 2387) messages.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Smil renders SMIL documents to very basic HTML.
+ *
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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 .= '<img src="' . $this->_params['contents']->urlView($rp, 'view_attach') . '" alt="" /><br />';
+ }
+ break;
+
+ case 'TEXT':
+ if (isset($attrs['SRC']) &&
+ (($rp = $this->_getRelatedLink($attrs['SRC'])) !== false)) {
+ $this->_content .= htmlspecialchars($rp->getContents()) . '<br />';
+ }
+ 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;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Smime class allows viewing/decrypting of S/MIME
+ * messages.
+ * This class implements parts of RFC 2630, RFC 2632, and RFC 2633.
+ *
+ * This class handles the following MIME types:
+ * application/pkcs7-mime
+ * application/x-pkcs7-mime
+ * application/pkcs7-signature (in multipart/signed part)
+ * application/x-pkcs7-signature (in multipart/signed part)
+ *
+ * This class may add the following parameters to the URL:
+ * 'smime_verify_msg' - (boolean) Do verification of S.
+ * 'view_smime_key' - (boolean) Display the S/MIME Key.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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.") . '</a>';
+ 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.") . '</a>';
+ break;
+
+ case 'dimp':
+ $status[] = Horde::link('#', '', 'smimeVerifyMsg') . _("Click HERE to verify the message.") . '</a>';
+ 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") . '</a>';
+ }
+ }
+
+ 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();
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Status class handles multipart/report messages
+ * that refer to mail system administrative messages (RFC 3464).
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Tnef class allows MS-TNEF attachments to be
+ * displayed.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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:
+ * <pre>
+ * 'tnef_attachment' - (integer) The TNEF attachment to download.
+ * </pre>
+ *
+ * @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'] = '<html><body>' . $ret[key($ret)]['data'] . '</body></html>';
+ 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'] . ")<br />\n";
+ }
+ }
+
+ return array(
+ $this->_mimepart->getMimeId() => array(
+ 'data' => $text,
+ 'status' => array($status),
+ 'type' => 'text/html; charset=' . NLS::getCharset()
+ )
+ );
+ }
+}
--- /dev/null
+<?php
+/**
+ * The IMP_Horde_Mime_Viewer_Zip class renders out the contents of ZIP files
+ * in HTML format and allows downloading of extractable files.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @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:
+ * <pre>
+ * 'zip_attachment' - (integer) The ZIP attachment to download.
+ * </pre>
+ *
+ * @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;
+ }
+}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_alternative class renders out messages from
- * multipart/alternative content types (RFC 2046 [5.1.4]).
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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;
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_appledouble class handles multipart/appledouble
- * messages conforming to RFC 1740.
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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;
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_enriched class renders out plain text from
- * enriched content tags, ala RFC 1896
- *
- * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Eric Rostetter <eric.rostetter@physics.utexas.edu>
- * @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*>.+)$|', '<span class="quoted1">\1</span>', explode("\n", $text)));
- $indent = 1;
- while (preg_match('|>(\s?>){' . $indent . '}|', $text)) {
- $text = implode("\n", preg_replace('|^<span class="quoted' . ((($indent - 1) % 5) + 1) . '">(\s*>(\s?>){' . $indent . '}.+)$|', '<span class="quoted' . (($indent % 5) + 1) . '">\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 .= '<span class="signature">' . $parts[0] .
- preg_replace('|class="[^"]+"|', 'class="signature-fixed"', $parts[1]) .
- '</span>';
- }
- }
-
- // Filter bad language.
- return IMP::filterText($text);
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_html class renders out HTML text with an effort
- * to remove potentially malicious code.
- *
- * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Anil Madhavapeddy <anil@recoil.org>
- * @author Jon Parise <jon@horde.org>
- * @author Michael Slusarz <slusarz@horde.org>
- * @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
- (
- # <img> tags
- <img[^>]+src=
- # <input> tags
- |<input[^>]*src=
- # "background" attributes
- |<body[^>]*background=|<td[^>]*background=|<table[^>]*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('/<a\s([^>]*\s*href=["\']?(#|mailto:))/i',
- '/<a\s([^>]*)\s*target=["\']?[^>"\'\s]*["\']?/i',
- '/<a\s/i',
- '/<area\s([^>]*\s*href=["\']?(#|mailto:))/i',
- '/<area\s([^>]*)\s*target=["\']?[^>"\'\s]*["\']?/i',
- '/<area\s/i',
- "/\x01/",
- "/\x02/"),
- array("<\x01\\1",
- "<\x01 \\1 target=\"_blank\"",
- '<a target="_blank" ',
- "<\x02\\1",
- "<\x02 \\1 target=\"_blank\"",
- '<area target="_blank" ',
- 'a ',
- 'area '),
- $data);
-
- /* Turn mailto: links into our own compose links. */
- if ($inline && $GLOBALS['registry']->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 = '<div class="htmlMessage">' . $data . '</div>';
- }
-
- /* 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) . '</a>'
- )
- );
-
- $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);
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_images class allows display of images attached
- * to a message.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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:
- * <pre>
- * 'imp_img_view' - (string) One of the following:
- * 'data' - Output the image directly.
- * 'load_convert' - TODO
- * 'view_convert' - TODO
- * 'view_thumbnail' - TODO
- * </pre>
- *
- * @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 <img> 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, '') . '</a>';
- }
-
- 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 = <<<EOD
-<html>
-<head>
-<title>$title</title>
-<style type="text/css"><!-- body { margin:0px; padding:0px; } --></style>
-EOD;
-
- /* Only use javascript if we are using a DOM capable browser. */
- if ($GLOBALS['browser']->getFeature('dom')) {
- /* Javascript display. */
- $loading = _("Loading...");
- $str .= <<<EOD
-<script type="text/javascript">
-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();
-}
-</script></head>
-<body onload="resizeWindow();"><span id="splash" style="color:gray;font-family:sans-serif;padding:2px;">$loading</span><img id="disp_image" style="display:none;" src="$self_url" /></body></html>
-EOD;
- } else {
- /* Non-javascript display. */
- $img_txt = _("Image");
- $str .= <<<EOD
-</head>
-<body bgcolor="#ffffff">
-<img border="0" src="$self_url" alt="$img_txt" />
-</body>
-</html>
-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;
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_itip class displays vCalendar/iCalendar data
- * and provides an option to import the data into a calendar source,
- * if one is available.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @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:
- * <pre>
- * 'identity' - (integer) TODO
- * 'itip_action' - (array) TODO
- * </pre>
- *
- * @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' => '<h1>' . _("The calendar data is invalid") . '</h1>' . '<pre>' . htmlspecialchars($data) . '</pre>',
- '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')) . '</a>');
- }
- }
- }
- 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')) . '</a>');
- }
- }
- 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')) . '</a>');
- }
- } 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 .= '<form method="post" name="iCal" action="' . (IMP::selfUrl()) . '">';
- }
-
- 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 .= '</form>';
- }
-
- 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 .= '<h1 class="header">' . sprintf($desc, $sender) . '</h1>';
-
- foreach ($msgs as $msg) {
- $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '</p>';
- }
-
- $start = $vfb->getAttribute('DTSTART');
- if (!is_a($start, 'PEAR_Error')) {
- if (is_array($start)) {
- $html .= '<p><strong>' . _("Start") . ':</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '</p>';
- } else {
- $html .= '<p><strong>' . _("Start") . ':</strong> ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '</p>';
- }
- }
-
- $end = $vfb->getAttribute('DTEND');
- if (!is_a($end, 'PEAR_Error')) {
- if (is_array($end)) {
- $html .= '<p><strong>' . _("End") . ':</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '</p>';
- } else {
- $html .= '<p><strong>' . _("End") . ':</strong> ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '</p>';
- }
- }
-
- if ($_SESSION['imp']['view'] != 'imp') {
- return $html;
- }
-
- $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>' .
- '<select name="itip_action[' . $id . ']">';
-
- switch ($method) {
- case 'PUBLISH':
- if ($registry->hasMethod('calendar/import_vfreebusy')) {
- $html .= '<option value="import">' . _("Remember the free/busy information.") . '</option>';
- } else {
- $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . '</option>';
- }
- break;
-
- case 'REQUEST':
- if ($registry->hasMethod('calendar/getFreeBusy')) {
- $html .= '<option value="reply">' . _("Reply with requested free/busy information.") . '</option>' .
- '<option value="reply2m">' . _("Reply with free/busy for next 2 months.") . '</option>';
- } else {
- $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . '</option>';
- }
-
- $html .= '<option value="deny">' . _("Deny request for free/busy information") . '</option>';
- break;
-
- case 'REPLY':
- if ($registry->hasMethod('calendar/import_vfreebusy')) {
- $html .= '<option value="import">' . _("Remember the free/busy information.") . '</option>';
- } else {
- $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . '</option>';
- }
- break;
- }
-
- return $html . '</select> <input type="submit" class="button" value="' . _("Go") . '/>';
- }
-
- /**
- * 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[] = '<option value="import">' . _("Add this to my calendar") . '</option>';
- }
- 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[] = '<option value="accept-import">' . _("Accept and update in my calendar") . '</option>';
- $options[] = '<option value="import">' . _("Update in my calendar") . '</option>';
- } elseif ($registry->hasMethod('calendar/import')) {
- $options[] = '<option value="accept-import">' . _("Accept and add to my calendar") . '</option>';
- $options[] = '<option value="import">' . _("Add to my calendar") . '</option>';
- }
- $options[] = '<option value="accept">' . _("Accept request") . '</option>';
- $options[] = '<option value="tentative">' . _("Tentatively Accept request") . '</option>';
- $options[] = '<option value="deny">' . _("Deny request") . '</option>';
- // $options[] = '<option value="delegate">' . _("Delegate position") . '</option>';
- break;
-
- case 'ADD':
- $desc = _("%s wishes to ammend \"%s\".");
- if ($registry->hasMethod('calendar/import')) {
- $options[] = '<option value="import">' . _("Update this event on my calendar") . '</option>';
- }
- break;
-
- case 'REFRESH':
- $desc = _("%s wishes to receive the latest information about \"%s\".");
- $options[] = '<option value="send">' . _("Send Latest Information") . '</option>';
- break;
-
- case 'REPLY':
- $desc = _("%s has replied to the invitation to \"%s\".");
- $sender = $this->_headers->getValue('From');
- if ($registry->hasMethod('calendar/updateAttendee')) {
- $options[] = '<option value="update">' . _("Update respondent status") . '</option>';
- }
- break;
-
- case 'CANCEL':
- if (is_a($instance = $vevent->getAttribute('RECURRENCE-ID'), 'PEAR_Error')) {
- $desc = _("%s has cancelled \"%s\".");
- if ($registry->hasMethod('calendar/delete')) {
- $options[] = '<option value="delete">' . _("Delete from my calendar") . '</option>';
- }
- } else {
- $desc = _("%s has cancelled an instance of the recurring \"%s\".");
- if ($registry->hasMethod('calendar/replace')) {
- $options[] = '<option value="import">' . _("Update in my calendar") . '</option>';
- }
- }
- 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 .= '<h2 class="header">' . $desc . '</h2>';
-
- foreach ($msgs as $msg) {
- $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '</p>';
- }
-
- $start = $vevent->getAttribute('DTSTART');
- if (!is_a($start, 'PEAR_Error')) {
- if (is_array($start)) {
- $html .= '<p><strong>' . _("Start") . ':</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '</p>';
- } else {
- $html .= '<p><strong>' . _("Start") . ':</strong> ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '</p>';
- }
- }
-
- $end = $vevent->getAttribute('DTEND');
- if (!is_a($end, 'PEAR_Error')) {
- if (is_array($end)) {
- $html .= '<p><strong>' . _("End") . ':</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '</p>';
- } else {
- $html .= '<p><strong>' . _("End") . ':</strong> ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '</p>';
- }
- }
-
- $sum = $vevent->getAttribute('SUMMARY');
- if (!is_a($sum, 'PEAR_Error')) {
- $html .= '<p><strong>' . _("Summary") . ':</strong> ' . htmlspecialchars($sum) . '</p>';
- } else {
- $html .= '<p><strong>' . _("Summary") . ':</strong> <em>' . _("None") . '</em></p>';
- }
-
- $desc = $vevent->getAttribute('DESCRIPTION');
- if (!is_a($desc, 'PEAR_Error')) {
- $html .= '<p><strong>' . _("Description") . ':</strong> ' . nl2br(htmlspecialchars($desc)) . '</p>';
- }
-
- $loc = $vevent->getAttribute('LOCATION');
- if (!is_a($loc, 'PEAR_Error')) {
- $html .= '<p><strong>' . _("Location") . ':</strong> ' . htmlspecialchars($loc) . '</p>';
- }
-
- if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) {
- $html .= '<h2 class="smallheader">' . _("Attendees") . '</h2>';
-
- $html .= '<table><thead class="leftAlign"><tr><th>' . _("Name") . '</th><th>' . _("Role") . '</th><th>' . _("Status") . '</th></tr></thead><tbody>';
- 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 .= '<tr><td>' . htmlspecialchars($attendee) . '</td><td>' . htmlspecialchars($role) . '</td><td>' . htmlspecialchars($status) . '</td></tr>';
- }
- $html .= '</tbody></table>';
- }
-
- if ($_SESSION['imp']['view'] != 'imp') {
- return $html;
- }
-
- if ($options) {
- $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>' .
- '<label for="action_' . $id . '" class="hidden">' . _("Actions") . '</label>' .
- '<select id="action_' . $id . '" name="itip_action[' . $id . ']">' .
- implode("\n", $options) .
- '</select> <input type="submit" class="button" value="' . _("Go") . '" />';
- }
-
- 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[] = '<option value="import">' . _("Add this to my tasklist") . '</option>';
- }
- 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 .= '<h2 class="header">' . $desc . '</h2>';
-
- foreach ($msgs as $msg) {
- $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png', '', null, $registry->getImageDir('horde')) . $msg[1] . '</p>';
- }
-
- $priority = $vtodo->getAttribute('PRIORITY');
- if (!is_a($priority, 'PEAR_Error')) {
- $html .= '<p><strong>' . _("Priority") . ':</strong> ' . (int)$priority . '</p>';
- }
-
- $sum = $vtodo->getAttribute('SUMMARY');
- if (!is_a($sum, 'PEAR_Error')) {
- $html .= '<p><strong>' . _("Summary") . ':</strong> ' . htmlspecialchars($sum) . '</p>';
- } else {
- $html .= '<p><strong>' . _("Summary") . ':</strong> <em>' . _("None") . '</em></p>';
- }
-
- $desc = $vtodo->getAttribute('DESCRIPTION');
- if (!is_a($desc, 'PEAR_Error')) {
- $html .= '<p><strong>' . _("Description") . ':</strong> ' . nl2br(htmlspecialchars($desc)) . '</p>';
- }
-
- $attendees = $vtodo->getAttribute('ATTENDEE');
- $params = $vtodo->getAttribute('ATTENDEE', true);
-
- if (!is_a($attendees, 'PEAR_Error') && !empty($attendees)) {
- $html .= '<h2 class="smallheader">' . _("Attendees") . '</h2>';
- if (!is_array($attendees)) {
- $attendees = array($attendees);
- }
-
- $html .= '<table><thead class="leftAlign"><tr><th>' . _("Name") . '</th><th>' . _("Role") . '</th><th>' . _("Status") . '</th></tr></thead><tbody>';
- 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 .= '<tr><td>' . htmlspecialchars($attendee) . '</td><td>' . htmlspecialchars($role) . '</td><td>' . htmlspecialchars($status) . '</td></tr>';
- }
- $html .= '</tbody></table>';
- }
-
- if ($_SESSION['imp']['view'] != 'imp') {
- return $html;
- }
-
- if ($options) {
- $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>' .
- '<select name="itip_action[' . $id . ']">' .
- implode("\n", $options) .
- '</select> <input type="submit" class="button" value="' . _("Go") . '" />';
- }
-
- 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;
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_mdn class handles multipart/report messages that
- * that refer to message disposition notification (MDN) messages (RFC 3798).
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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;
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_partial class allows message/partial messages
- * to be displayed (RFC 2046 [5.2.2]).
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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);
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_pdf class enables generation of thumbnails for
- * PDF attachments.
- *
- * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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:
- * <pre>
- * 'pdf_view_thumbnail' - (boolean) Output the thumbnail info.
- * </pre>
- *
- * @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, '') . '</a>';
- }
-
- 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;
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_pgp class allows viewing/decrypting of PGP
- * formatted messages. This class implements RFC 3156.
- *
- * This class handles the following MIME types:
- * application/pgp-encryption (in multipart/encrypted part)
- * application/pgp-keys
- * application/pgp-signature (in multipart/signed part)
- *
- * This class may add the following parameters to the URL:
- * 'pgp_verify_msg' - (boolean) Do verification of PGP signed data.
- * 'rawpgpkey' - (boolean) Display the PGP Public Key in raw, text format
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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.") . '</a>';
- 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.") . '</a>';
- 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]") . '</a>';
- }
- $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 = '<span class="fixed">' . nl2br(str_replace(' ', ' ', $this->_imppgp->pgpPrettyKey($this->_mimepart->getContents()))) . '</span>';
- } 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.") . '</a>';
- break;
-
- case 'dimp':
- $status[] = Horde::link('#', '', 'pgpVerifyMsg') . _("Click HERE to verify the message.") . '</a>';
- 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());
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_plain class renders out text/plain MIME parts
- * with URLs made into hyperlinks.
- *
- * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Anil Madhavapeddy <anil@recoil.org>
- * @author Michael Slusarz <slusarz@horde.org>
- * @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' => '<div class="fixed leftAlign">' . "\n" . $text . '</div>',
- '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);
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_related class handles multipart/related
- * (RFC 2387) messages.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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;
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_smil renders SMIL documents to very basic HTML.
- *
- * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Jan Schneider <jan@horde.org>
- * @author Michael Slusarz <slusarz@horde.org>
- * @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 .= '<img src="' . $this->_params['contents']->urlView($rp, 'view_attach') . '" alt="" /><br />';
- }
- break;
-
- case 'TEXT':
- if (isset($attrs['SRC']) &&
- (($rp = $this->_getRelatedLink($attrs['SRC'])) !== false)) {
- $this->_content .= htmlspecialchars($rp->getContents()) . '<br />';
- }
- 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;
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_smime class allows viewing/decrypting of S/MIME
- * messages.
- * This class implements parts of RFC 2630, RFC 2632, and RFC 2633.
- *
- * This class handles the following MIME types:
- * application/pkcs7-mime
- * application/x-pkcs7-mime
- * application/pkcs7-signature (in multipart/signed part)
- * application/x-pkcs7-signature (in multipart/signed part)
- *
- * This class may add the following parameters to the URL:
- * 'smime_verify_msg' - (boolean) Do verification of S.
- * 'view_smime_key' - (boolean) Display the S/MIME Key.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @author Michael Slusarz <slusarz@horde.org>
- * @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.") . '</a>';
- 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.") . '</a>';
- break;
-
- case 'dimp':
- $status[] = Horde::link('#', '', 'smimeVerifyMsg') . _("Click HERE to verify the message.") . '</a>';
- 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") . '</a>';
- }
- }
-
- 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();
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_status class handles multipart/report messages
- * that refer to mail system administrative messages (RFC 3464).
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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;
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_tnef class allows MS-TNEF attachments to be
- * displayed.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @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:
- * <pre>
- * 'tnef_attachment' - (integer) The TNEF attachment to download.
- * </pre>
- *
- * @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'] = '<html><body>' . $ret[key($ret)]['data'] . '</body></html>';
- 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'] . ")<br />\n";
- }
- }
-
- return array(
- $this->_mimepart->getMimeId() => array(
- 'data' => $text,
- 'status' => array($status),
- 'type' => 'text/html; charset=' . NLS::getCharset()
- )
- );
- }
-}
+++ /dev/null
-<?php
-/**
- * The IMP_Horde_Mime_Viewer_zip class renders out the contents of ZIP files
- * in HTML format and allows downloading of extractable files.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @author Michael Slusarz <slusarz@horde.org>
- * @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:
- * <pre>
- * 'zip_attachment' - (integer) The ZIP attachment to download.
- * </pre>
- *
- * @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;
- }
-}