From 877d6ab1c0afe43337ed38d0a3d98c4d0711c95a Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Wed, 5 Nov 2008 22:57:56 -0700 Subject: [PATCH] Import new MIME library. --- framework/MIME/lib/Horde/MIME.php | 443 ++++++ framework/MIME/lib/Horde/MIME/Address.php | 401 +++++ framework/MIME/lib/Horde/MIME/Headers.php | 543 +++++++ framework/MIME/lib/Horde/MIME/MDN.php | 238 +++ framework/MIME/lib/Horde/MIME/Magic.php | 260 +++ framework/MIME/lib/Horde/MIME/Mail.php | 507 ++++++ framework/MIME/lib/Horde/MIME/Message.php | 384 +++++ framework/MIME/lib/Horde/MIME/Part.php | 1324 ++++++++++++++++ framework/MIME/lib/Horde/MIME/Viewer.php | 332 ++++ framework/MIME/lib/Horde/MIME/Viewer/Driver.php | 195 +++ framework/MIME/lib/Horde/MIME/Viewer/audio.php | 25 + framework/MIME/lib/Horde/MIME/Viewer/css.php | 109 ++ framework/MIME/lib/Horde/MIME/Viewer/deb.php | 54 + framework/MIME/lib/Horde/MIME/Viewer/default.php | 17 + framework/MIME/lib/Horde/MIME/Viewer/enriched.php | 158 ++ framework/MIME/lib/Horde/MIME/Viewer/enscript.php | 132 ++ framework/MIME/lib/Horde/MIME/Viewer/html.php | 160 ++ framework/MIME/lib/Horde/MIME/Viewer/images.php | 95 ++ framework/MIME/lib/Horde/MIME/Viewer/msexcel.php | 55 + .../MIME/lib/Horde/MIME/Viewer/mspowerpoint.php | 55 + framework/MIME/lib/Horde/MIME/Viewer/msword.php | 69 + framework/MIME/lib/Horde/MIME/Viewer/ooo.php | 88 + .../MIME/lib/Horde/MIME/Viewer/ooo/common.xsl | 1165 ++++++++++++++ .../lib/Horde/MIME/Viewer/ooo/global_document.xsl | 1674 ++++++++++++++++++++ .../MIME/lib/Horde/MIME/Viewer/ooo/main_html.xsl | 462 ++++++ framework/MIME/lib/Horde/MIME/Viewer/ooo/palm.xsl | 404 +++++ .../lib/Horde/MIME/Viewer/ooo/style_header.xsl | 379 +++++ .../lib/Horde/MIME/Viewer/ooo/style_inlined.xsl | 398 +++++ .../lib/Horde/MIME/Viewer/ooo/style_mapping.xsl | 660 ++++++++ framework/MIME/lib/Horde/MIME/Viewer/ooo/table.xsl | 328 ++++ .../MIME/lib/Horde/MIME/Viewer/ooo/table_cells.xsl | 484 ++++++ .../lib/Horde/MIME/Viewer/ooo/table_columns.xsl | 215 +++ .../MIME/lib/Horde/MIME/Viewer/ooo/table_rows.xsl | 177 +++ framework/MIME/lib/Horde/MIME/Viewer/pdf.php | 26 + framework/MIME/lib/Horde/MIME/Viewer/php.php | 138 ++ framework/MIME/lib/Horde/MIME/Viewer/plain.php | 81 + framework/MIME/lib/Horde/MIME/Viewer/rar.php | 197 +++ framework/MIME/lib/Horde/MIME/Viewer/report.php | 89 ++ framework/MIME/lib/Horde/MIME/Viewer/rfc822.php | 98 ++ framework/MIME/lib/Horde/MIME/Viewer/richtext.php | 152 ++ framework/MIME/lib/Horde/MIME/Viewer/rpm.php | 55 + framework/MIME/lib/Horde/MIME/Viewer/rtf.php | 57 + framework/MIME/lib/Horde/MIME/Viewer/security.php | 88 + framework/MIME/lib/Horde/MIME/Viewer/simple.php | 25 + framework/MIME/lib/Horde/MIME/Viewer/smil.php | 98 ++ framework/MIME/lib/Horde/MIME/Viewer/source.php | 40 + .../MIME/lib/Horde/MIME/Viewer/srchighlite.php | 102 ++ framework/MIME/lib/Horde/MIME/Viewer/tgz.php | 104 ++ framework/MIME/lib/Horde/MIME/Viewer/tnef.php | 54 + framework/MIME/lib/Horde/MIME/Viewer/vcard.php | 372 +++++ framework/MIME/lib/Horde/MIME/Viewer/webcpp.php | 81 + .../MIME/lib/Horde/MIME/Viewer/wordperfect.php | 58 + framework/MIME/lib/Horde/MIME/Viewer/zip.php | 135 ++ framework/MIME/lib/Horde/MIME/mime.magic.php | Bin 0 -> 16939 bytes framework/MIME/lib/Horde/MIME/mime.mapping.php | 894 +++++++++++ framework/MIME/package.xml | 223 +++ framework/MIME/tests/attachment.bin | 1 + framework/MIME/tests/bug_325.phpt | 12 + framework/MIME/tests/bug_325.txt | 6 + framework/MIME/tests/bug_4834.phpt | 11 + framework/MIME/tests/bug_6896.phpt | 16 + framework/MIME/tests/contents.phpt | 67 + framework/MIME/tests/mail_001.phpt | 30 + framework/MIME/tests/mail_002.phpt | 36 + framework/MIME/tests/mail_003.phpt | 33 + framework/MIME/tests/mail_004.phpt | 59 + framework/MIME/tests/mail_005.phpt | 100 ++ framework/MIME/tests/mail_006.phpt | 76 + framework/MIME/tests/mail_007.phpt | 61 + framework/MIME/tests/mail_008.phpt | 39 + framework/MIME/tests/mail_dummy.inc | 15 + framework/MIME/tests/url.phpt | 48 + framework/MIME/tests/url1.html | 1 + framework/MIME/tests/url2.html | 1 + framework/MIME/tests/url3.html | 1 + framework/MIME/tests/url4.html | 1 + framework/MIME/tests/url5.html | 1 + framework/MIME/tests/url6.html | 1 + framework/MIME/tests/url7.html | 1 + framework/MIME/tests/viewer_php.phpt | 21 + 80 files changed, 15795 insertions(+) create mode 100644 framework/MIME/lib/Horde/MIME.php create mode 100644 framework/MIME/lib/Horde/MIME/Address.php create mode 100644 framework/MIME/lib/Horde/MIME/Headers.php create mode 100644 framework/MIME/lib/Horde/MIME/MDN.php create mode 100644 framework/MIME/lib/Horde/MIME/Magic.php create mode 100644 framework/MIME/lib/Horde/MIME/Mail.php create mode 100644 framework/MIME/lib/Horde/MIME/Message.php create mode 100644 framework/MIME/lib/Horde/MIME/Part.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/Driver.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/audio.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/css.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/deb.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/default.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/enriched.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/enscript.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/html.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/images.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/msexcel.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/mspowerpoint.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/msword.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/common.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/global_document.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/main_html.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/palm.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/style_header.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/style_inlined.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/style_mapping.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/table.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/table_cells.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/table_columns.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/ooo/table_rows.xsl create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/pdf.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/php.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/plain.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/rar.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/report.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/rfc822.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/richtext.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/rpm.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/rtf.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/security.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/simple.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/smil.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/source.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/srchighlite.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/tgz.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/tnef.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/vcard.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/webcpp.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/wordperfect.php create mode 100644 framework/MIME/lib/Horde/MIME/Viewer/zip.php create mode 100644 framework/MIME/lib/Horde/MIME/mime.magic.php create mode 100644 framework/MIME/lib/Horde/MIME/mime.mapping.php create mode 100644 framework/MIME/package.xml create mode 100644 framework/MIME/tests/attachment.bin create mode 100644 framework/MIME/tests/bug_325.phpt create mode 100644 framework/MIME/tests/bug_325.txt create mode 100644 framework/MIME/tests/bug_4834.phpt create mode 100644 framework/MIME/tests/bug_6896.phpt create mode 100644 framework/MIME/tests/contents.phpt create mode 100644 framework/MIME/tests/mail_001.phpt create mode 100644 framework/MIME/tests/mail_002.phpt create mode 100644 framework/MIME/tests/mail_003.phpt create mode 100644 framework/MIME/tests/mail_004.phpt create mode 100644 framework/MIME/tests/mail_005.phpt create mode 100644 framework/MIME/tests/mail_006.phpt create mode 100644 framework/MIME/tests/mail_007.phpt create mode 100644 framework/MIME/tests/mail_008.phpt create mode 100644 framework/MIME/tests/mail_dummy.inc create mode 100644 framework/MIME/tests/url.phpt create mode 100644 framework/MIME/tests/url1.html create mode 100644 framework/MIME/tests/url2.html create mode 100644 framework/MIME/tests/url3.html create mode 100644 framework/MIME/tests/url4.html create mode 100644 framework/MIME/tests/url5.html create mode 100644 framework/MIME/tests/url6.html create mode 100644 framework/MIME/tests/url7.html create mode 100644 framework/MIME/tests/viewer_php.phpt diff --git a/framework/MIME/lib/Horde/MIME.php b/framework/MIME/lib/Horde/MIME.php new file mode 100644 index 000000000..9569c78f3 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME.php @@ -0,0 +1,443 @@ + + * @author Michael Slusarz + * @package Horde_MIME + */ +class Horde_MIME +{ + /** + * Determines if a string contains 8-bit (non US-ASCII) characters. + * + * @param string $string The string to check. + * @param string $charset The charset of the string. Defaults to + * US-ASCII. + * + * @return boolean True if string contains non US-ASCII characters. + */ + static public function is8bit($string, $charset = null) + { + /* ISO-2022-JP is a 7bit charset, but it is an 8bit representation so + * it needs to be entirely encoded. */ + return is_string($string) && + ((stristr('iso-2022-jp', $charset) && + (strstr($string, "\x1b\$B"))) || + preg_match('/[\x80-\xff]/', $string)); + } + + /** + * Encodes a string containing non-ASCII characters according to RFC 2047. + * + * @param string $text The text to encode. + * @param string $charset The character set of the text. + * + * @return string The text, encoded only if it contains non-ASCII + * characters. + */ + static public function encode($text, $charset = null) + { + if (is_null($charset)) { + require_once 'Horde/NLS.php'; + $charset = NLS::getCharset(); + } + $charset = String::lower($charset); + + if (($charset == 'us-ascii') || !self::is8bit($text, $charset)) { + return $text; + } + + /* Get the list of elements in the string. */ + $size = preg_match_all('/([^\s]+)([\s]*)/', $text, $matches, PREG_SET_ORDER); + + $line = ''; + + /* Return if nothing needs to be encoded. */ + foreach ($matches as $key => $val) { + if (self::is8bit($val[1], $charset)) { + if ((($key + 1) < $size) && + self::is8bit($matches[$key + 1][1], $charset)) { + $line .= self::_encode($val[1] . $val[2], $charset) . ' '; + } else { + $line .= self::_encode($val[1], $charset) . $val[2]; + } + } else { + $line .= $val[1] . $val[2]; + } + } + + return rtrim($line); + } + + /** + * Internal recursive function to RFC 2047 encode a string. + * + * @param string $text The text to encode. + * @param string $charset The character set of the text. + * + * @return string The text, encoded only if it contains non-ASCII + * characters. + */ + static protected function _encode($text, $charset) + { + $encoded = trim(base64_encode($text)); + $c_size = strlen($charset) + 7; + + if ((strlen($encoded) + $c_size) > 75) { + $parts = explode("\r\n", rtrim(chunk_split($encoded, intval((75 - $c_size) / 4) * 4))); + } else { + $parts[] = $encoded; + } + + $p_size = count($parts); + $out = ''; + + foreach ($parts as $key => $val) { + $out .= '=?' . $charset . '?b?' . $val . '?='; + if ($p_size > $key + 1) { + /* RFC 2047 [2]: no encoded word can be more than 75 + * characters long. If longer, you must split the word with + * CRLF SPACE. */ + $out .= "\r\n "; + } + } + + return $out; + } + + /** + * Encodes a line via quoted-printable encoding. + * + * @param string $text The text to encode. + * @param string $eol The EOL sequence to use. + * @param integer $wrap Wrap a line at this many characters. + * + * @return string The quoted-printable encoded string. + */ + static public function quotedPrintableEncode($text, $eol, $wrap = 76) + { + $line = $output = ''; + $curr_length = 0; + + /* We need to go character by character through the data. */ + for ($i = 0, $length = strlen($text); $i < $length; ++$i) { + $char = $text[$i]; + + /* If we have reached the end of the line, reset counters. */ + if ($char == "\n") { + $output .= $eol; + $curr_length = 0; + continue; + } elseif ($char == "\r") { + continue; + } + + /* Spaces or tabs at the end of the line are NOT allowed. Also, + * ASCII characters below 32 or above 126 AND 61 must be + * encoded. */ + $ascii = ord($char); + if ((($ascii === 32) && + ($i + 1 != $length) && + (($text[$i + 1] == "\n") || ($text[$i + 1] == "\r"))) || + (($ascii < 32) || ($ascii > 126) || ($ascii === 61))) { + $char_len = 3; + $char = '=' . String::upper(sprintf('%02s', dechex($ascii))); + } else { + $char_len = 1; + } + + /* Lines must be $wrap characters or less. */ + $curr_length += $char_len; + if ($curr_length > $wrap) { + $output .= '=' . $eol; + $curr_length = $char_len; + } + $output .= $char; + } + + return $output; + } + + /** + * Encodes a string containing email addresses according to RFC 2047. + * + * This differs from encode() because it keeps email addresses legal, only + * encoding the personal information. + * + * @param mixed $addresses The email addresses to encode (either a + * string or an array of addresses). + * @param string $charset The character set of the text. + * @param string $defserver The default domain to append to mailboxes. + * + * @return string The text, encoded only if it contains non-ASCII + * characters, or PEAR_Error on error. + */ + static public function encodeAddress($addresses, $charset = null, + $defserver = null) + { + if (!is_array($addresses)) { + /* parseAddressList() does not process the null entry + * 'undisclosed-recipients:;' correctly. */ + $addresses = trim($addresses); + if (preg_match('/undisclosed-recipients:\s*;/i', $addresses)) { + return $addresses; + } + + $addresses = Horde_MIME_Address::parseAddressList($addresses, array('defserver' => $defserver, 'nestgroups' => true)); + if (is_a($addresses, 'PEAR_Error')) { + return $addresses; + } + } + + $text = ''; + foreach ($addresses as $addr) { + // Check for groups. + if (empty($addr['groupname'])) { + if (empty($addr['personal'])) { + $personal = ''; + } else { + if (($addr['personal'][0] == '"') && + (substr($addr['personal'], -1) == '"')) { + $addr['personal'] = stripslashes(substr($addr['personal'], 1, -1)); + } + $personal = self::encode($addr['personal'], $charset); + } + $text .= Horde_MIME_Address::writeAddress($addr['mailbox'], $addr['host'], $personal) . ', '; + } else { + $text .= Horde_MIME_Address::writeGroupAddress($addr['groupname'], $addr['addresses']) . ' '; + } + } + + return rtrim($text, ' ,'); + } + + /** + * Decodes an RFC 2047-encoded string. + * + * @param string $string The text to decode. + * @param string $to_charset The charset that the text should be decoded + * to. + * + * @return string The decoded text. + */ + static public function decode($string, $to_charset = null) + { + if (($pos = strpos($string, '=?')) === false) { + return $string; + } + + /* Take out any spaces between multiple encoded words. */ + $string = preg_replace('|\?=\s+=\?|', '?==?', $string); + + /* Save any preceding text. */ + $preceding = substr($string, 0, $pos); + + $search = substr($string, $pos + 2); + $d1 = strpos($search, '?'); + if ($d1 === false) { + return $string; + } + + $charset = substr($string, $pos + 2, $d1); + $search = substr($search, $d1 + 1); + + $d2 = strpos($search, '?'); + if ($d2 === false) { + return $string; + } + + $encoding = substr($search, 0, $d2); + $search = substr($search, $d2 + 1); + + $end = strpos($search, '?='); + if ($end === false) { + $end = strlen($search); + } + + $encoded_text = substr($search, 0, $end); + $rest = substr($string, (strlen($preceding . $charset . $encoding . $encoded_text) + 6)); + + if (is_null($to_charset)) { + require_once 'Horde/NLS.php'; + $to_charset = NLS::getCharset(); + } + + switch ($encoding) { + case 'Q': + case 'q': + $decoded = preg_replace('/=([0-9a-f]{2})/ie', 'chr(0x\1)', str_replace('_', ' ', $encoded_text)); + $decoded = String::convertCharset($decoded, $charset, $to_charset); + break; + + case 'B': + case 'b': + $decoded = String::convertCharset(base64_decode($encoded_text), $charset, $to_charset); + break; + + default: + $decoded = '=?' . $charset . '?' . $encoding . '?' . $encoded_text . '?='; + break; + } + + return $preceding . $decoded . self::decode($rest, $to_charset); + } + + /** + * Decodes an RFC 2047-encoded address string. + * + * @param string $string The text to decode. + * @param string $to_charset The charset that the text should be decoded + * to. + * + * @return string The decoded text. + */ + static public function decodeAddrString($string, $to_charset = null) + { + $addr_list = array(); + foreach (Horde_MIME_Address::parseAddressList($string) as $ob) { + $ob['personal'] = isset($ob['personal']) + ? self::decode($ob['personal'], $to_charset) + : ''; + $addr_list[] = $ob; + } + + return Horde_MIME_Address::addrArray2String($addr_list); + } + + /** + * Encodes a parameter string pursuant to RFC 2231. + * + * @param string $name The parameter name. + * @param string $string The string to encode. + * @param string $charset The charset the text should be encoded with. + * @param string $lang The language to use when encoding. + * + * @return array The encoded parameter string. + */ + static public function encodeParamString($name, $string, $charset, + $lang = null) + { + $encode = $wrap = false; + $output = array(); + + if (self::is8bit($string, $charset)) { + $string = String::lower($charset) . '\'' . (is_null($lang) ? '' : String::lower($lang)) . '\'' . rawurlencode($string); + $encode = true; + } + + // 4 = '*', 2x '"', ';' + $pre_len = strlen($name) + 4 + (($encode) ? 1 : 0); + if (($pre_len + strlen($string)) > 76) { + while ($string) { + $chunk = 76 - $pre_len; + $pos = min($chunk, strlen($string) - 1); + if (($chunk == $pos) && ($pos > 2)) { + for ($i = 0; $i <= 2; $i++) { + if ($string[$pos-$i] == '%') { + $pos -= $i + 1; + break; + } + } + } + $lines[] = substr($string, 0, $pos + 1); + $string = substr($string, $pos + 1); + } + $wrap = true; + } else { + $lines = array($string); + } + + $i = 0; + foreach ($lines as $val) { + $output[] = + $name . + (($wrap) ? ('*' . $i++) : '') . + (($encode) ? '*' : '') . + '="' . $val . '"'; + } + + return implode('; ', $output); + } + + /** + * Decodes a parameter string encoded pursuant to RFC 2231. + * + * @param string $string The entire string to decode, including the + * parameter name. + * @param string $to_charset The charset the text should be decoded to. + * + * @return array The decoded text, or the original string if it was not + * encoded. + */ + static public function decodeParamString($string, $to_charset = null) + { + if (($pos = strpos($string, '*')) === false) { + return false; + } + + if (!isset($to_charset)) { + require_once 'Horde/NLS.php'; + $to_charset = NLS::getCharset(); + } + + $attribute = substr($string, 0, $pos); + $charset = $lang = null; + $output = ''; + + /* Get the character set and language used in the encoding, if + * any. */ + if (preg_match("/^[^=]+\*\=([^']*)'([^']*)'/", $string, $matches)) { + $charset = $matches[1]; + $lang = $matches[2]; + $string = str_replace($charset . "'" . $lang . "'", '', $string); + } + + $lines = preg_split('/\s*' . preg_quote($attribute) . '(?:\*\d)*/', $string); + foreach ($lines as $line) { + if (strpos($line, '*=') === 0) { + $output .= urldecode(str_replace(array('_', '='), array('%20', '%'), substr($line, 2))); + } else { + $output .= substr($line, 1); + } + } + + /* RFC 2231 uses quoted printable encoding. */ + if (!is_null($charset)) { + $output = String::convertCharset($output, $charset, $to_charset); + } + + return array( + 'attribute' => $attribute, + 'value' => $output + ); + } + + /** + * Generates a Message-ID string conforming to RFC 2822 [3.6.4] and the + * standards outlined in 'draft-ietf-usefor-message-id-01.txt'. + * + * @param string A message ID string. + */ + static public function generateMessageID() + { + return '<' . date('YmdHis') . '.' . self::generateRandomID() . '@' . $_SERVER['SERVER_NAME'] . '>'; + } + + /** + * Generates a Random-ID string suitable for use with MIME features that + * require a random string. + * + * @return string A random string. + */ + static public function generateRandomID() + { + return base_convert(dechex(strtr(microtime(), array('0.' => '', ' ' => ''))) . uniqid(), 16, 36); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Address.php b/framework/MIME/lib/Horde/MIME/Address.php new file mode 100644 index 000000000..1ca31fdc0 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Address.php @@ -0,0 +1,401 @@ + + * @author Michael Slusarz + * @package Horde_MIME + */ +class Horde_MIME_Address +{ + /** + * Builds an RFC compliant email address. + * + * @param string $mailbox Mailbox name. + * @param string $host Domain name of mailbox's host. + * @param string $personal Personal name phrase. + * + * @return string The correctly escaped and quoted + * "$personal <$mailbox@$host>" string. + */ + static public function writeAddress($mailbox, $host, $personal = '') + { + $address = ''; + + if (strlen($personal)) { + $address .= self::encode($personal, 'personal') . ' <'; + } + + $address .= self::encode($mailbox, 'address') . '@' . ltrim($host, '@'); + + if (strlen($personal)) { + $address .= '>'; + } + + return $address; + } + + /** + * Write an RFC compliant group address, given the group name and a list + * of email addresses. + * + * @param string $groupname The name of the group. + * @param array $addresses The component email addresses. These e-mail + * addresses must be in RFC format. + * + * @return string The correctly quoted group string. + */ + static public function writeGroupAddress($groupname, $addresses = array()) + { + return self::encode($groupname, 'address') . ':' . (empty($addresses) ? '' : implode(', ', $addresses)) . ';'; + } + + /** + * If an email address has no personal information, get rid of any angle + * brackets (<>) around it. + * + * @param string $address The address to trim. + * + * @return string The trimmed address. + */ + static public function trimAddress($address) + { + $address = trim($address); + + if (($address[0] == '<') && (substr($address, -1) == '>')) { + $address = substr($address, 1, -1); + } + + return $address; + } + + /** + * Explodes an RFC string, ignoring a delimiter if preceded by a "\" + * character, or if the delimiter is inside single or double quotes. + * + * @param string $string The RFC compliant string. + * @param string $delimiters A string containing valid delimiters. + * Defaults to ','. + * + * @return array The exploded string in an array. + */ + static public function explode($string, $delimiters = ',') + { + if (!strlen($string)) { + return array($string); + } + + $emails = array(); + $pos = 0; + $in_group = $in_quote = false; + + for ($i = 0, $iMax = strlen($string); $i < $iMax; ++$i) { + $char = $string[$i]; + if ($char == '"') { + if (!$i || ($prev !== '\\')) { + $in_quote = !$in_quote; + } + } elseif ($in_group) { + if ($char == ';') { + $emails[] = substr($string, $pos, $i - $pos + 1); + $pos = $i + 1; + $in_group = false; + } + } elseif (!$in_quote) { + if ($char == ':') { + $in_group = true; + } elseif ((strpos($delimiters, $char) !== false) && + (!$i || ($prev !== '\\'))) { + $emails[] = $i ? substr($string, $pos, $i - $pos) : ''; + $pos = $i + 1; + } + } + $prev = $char; + } + + if ($pos != $i) { + /* The string ended without a delimiter. */ + $emails[] = substr($string, $pos, $i - $pos); + } + + return $emails; + } + + /** + * Takes an address object array and formats it as a string. + * + * Object array format for the address "John Doe " + * is: + *
+     * 'personal' = Personal name ("John Doe")
+     * 'mailbox' = The user's mailbox ("john_doe")
+     * 'host' = The host the mailbox is on ("example.com")
+     * 
+ * + * @param array $ob The address object to be turned into a string. + * @param mixed $filter A user@example.com style bare address to ignore. + * Either single string or an array of strings. If + * the address matches $filter, an empty string will + * be returned. + * + * @return string The formatted address. + */ + static public function addrObject2String($ob, $filter = '') + { + /* If the personal name is set, decode it. */ + $ob['personal'] = isset($ob['personal']) + ? Horde_MIME::decode($ob['personal']) + : ''; + + /* If both the mailbox and the host are empty, return an empty string. + * If we just let this case fall through, the call to writeAddress() + * will end up return just a '@', which is undesirable. */ + if (empty($ob['mailbox']) && empty($ob['host'])) { + return ''; + } + + /* Make sure these two variables have some sort of value. */ + if (!isset($ob['mailbox'])) { + $ob['mailbox'] = ''; + } elseif ($ob['mailbox'] == 'undisclosed-recipients') { + return ''; + } + if (!isset($ob['host'])) { + $ob['host'] = ''; + } + + /* Filter out unwanted addresses based on the $filter string. */ + if ($filter) { + if (!is_array($filter)) { + $filter = array($filter); + } + foreach ($filter as $f) { + if (strcasecmp($f, $ob['mailbox'] . '@' . $ob['host']) == 0) { + return ''; + } + } + } + + /* Return the formatted email address. */ + return self::writeAddress($ob['mailbox'], $ob['host'], $ob['personal']); + } + + /** + * Takes an array of address object arrays and passes each of them through + * addrObject2String(). + * + * @param array $addresses The array of address objects. + * @param mixed $filter A user@example.com style bare address to + * ignore. If any address matches $filter, it + * will not be included in the final string. + * + * @return string All of the addresses in a comma-delimited string. + * Returns the empty string on error/no addresses found. + */ + static public function addrArray2String($addresses, $filter = '') + { + if (!is_array($addresses)) { + return ''; + } + + $addrList = array(); + + foreach ($addresses as $addr) { + $val = self::addrObject2String($addr, $filter); + if (!empty($val)) { + $addrList[String::lower(self::bareAddress($val))] = $val; + } + } + + return implode(', ', $addrList); + } + + /** + * Return the list of addresses for a header object. + * + * @param array $obs An array of header objects. + * + * @return array An array of address information. Array elements: + *
+     * 'address' - (string) Full address
+     * 'display' - (string) A displayable version of the address
+     * 'groupname' - (string) The group name.
+     * 'host' - (string) Hostname
+     * 'inner' - (string) Trimmed, bare address
+     * 'personal' - (string) Personal string
+     * 
+ */ + static public function getAddressesFromObject($obs) + { + $ret = array(); + + if (!is_array($obs) || empty($obs)) { + return $ret; + } + + foreach ($obs as $ob) { + if (isset($ob['groupname'])) { + $ret[] = array( + 'addresses' => self::getAddressesFromObject($ob['addresses']), + 'groupname' => $ob['groupname'] + ); + continue; + } + + $ob = array_merge(array( + 'host' => '', + 'mailbox' => '', + 'personal' => '' + ), $ob); + + /* Ensure we're working with initialized values. */ + if (!empty($ob['personal'])) { + $ob['personal'] = stripslashes(trim(Horde_MIME::decode($ob['personal']), '"')); + } + + $inner = self::writeAddress($ob['mailbox'], $ob['host']); + + /* Generate the new object. */ + $ret[] = array( + 'address' => self::addrObject2String($ob), + 'display' => (empty($ob['personal']) ? '' : $ob['personal'] . ' <') . $inner . (empty($ob['personal']) ? '' : '>'), + 'host' => $ob['host'], + 'inner' => $inner, + 'personal' => $ob['personal'] + ); + } + + return $ret; + } + + /** + * Returns the bare address. + * + * @param string $address The address string. + * @param string $defserver The default domain to append to mailboxes. + * @param boolean $multiple Should we return multiple results? + * + * @return mixed If $multiple is false, returns the mailbox@host e-mail + * address. If $multiple is true, returns an array of + * these addresses. + */ + static public function bareAddress($address, $defserver = null, + $multiple = false) + { + $addressList = array(); + + $from = self::parseAddressList($address, array('defserver' => $defserver)); + if (is_a($from, 'PEAR_Error')) { + return $multiple ? array() : ''; + } + + foreach ($from as $entry) { + if (!empty($entry['mailbox'])) { + $addressList[] = $entry['mailbox'] . (isset($entry['host']) ? '@' . $entry['host'] : ''); + } + } + + return $multiple ? $addressList : array_pop($addressList); + } + + /** + * Parses a list of email addresses into its parts. Handles distribution + * lists. + * + * @param string $address The address string. + * @param array $options Additional options: + *
+     * 'defserver' - (string) The default domain to append to mailboxes.
+     *               DEFAULT: No domain appended.
+     * 'nestgroups' - (boolean) Nest the groups? (Will appear under the
+     *                'groupname' key)
+     *                DEFAULT: No.
+     * 'reterror' - (boolean) Return a PEAR_Error object on error?
+     *              DEFAULT: Returns an empty array on error.
+     * 'validate' - (boolean) Validate the address(es)?
+     *              DEFAULT: No.
+     * 
+ * + * @return mixed If 'reterror' is true, returns a PEAR_Error object on + * error. Otherwise, a list of arrays with the possible + * keys: 'mailbox', 'host', 'personal', 'adl', 'groupname', + * and 'comment'. + */ + static public function parseAddressList($address, $options = array()) + { + if (preg_match('/undisclosed-recipients:\s*;/i', trim($address))) { + return array(); + } + + $options = array_merge(array( + 'defserver' => null, + 'nestgroups' => false, + 'reterror' => false, + 'validate' => false + ), $options); + + static $parser; + if (!isset($parser)) { + require_once 'Mail/RFC822.php'; + $parser = new Mail_RFC822(); + } + + $ret = $parser->parseAddressList($address, $options['defserver'], $options['nestgroups'], $options['validate']); + if (is_a($ret, 'PEAR_Error')) { + return empty($options['reterror']) ? array() : $ret; + } + + /* Convert objects to arrays. */ + foreach (array_keys($ret) as $key) { + $ret[$key] = (array) $ret[$key]; + } + + return $ret; + } + + /** + * Quotes and escapes the given string if necessary using rules contained + * in RFC 2822 [3.2.5]. + * + * @param string $str The string to be quoted and escaped. + * @param string $type Either 'address' or 'personal'. + * + * @return string The correctly quoted and escaped string. + */ + static public function encode($str, $type = 'address') + { + // Excluded (in ASCII): 0-8, 10-31, 34, 40-41, 44, 58-60, 62, 64, + // 91-93, 127 + $filter = "\0\1\2\3\4\5\6\7\10\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\"(),:;<>@[\\]\177"; + + switch ($type) { + case 'address': + // RFC 2822 [3.4.1]: (HTAB, SPACE) not allowed in address + $filter .= "\11\40"; + break; + + case 'personal': + // RFC 2822 [3.4]: Period not allowed in display name + $filter .= '.'; + break; + } + + // Strip double quotes if they are around the string already. + // If quoted, we know that the contents are already escaped, so + // unescape now. + $str = trim($str); + if ($str && ($str[0] == '"') && (substr($str, -1) == '"')) { + $str = stripslashes(substr($str, 1, -1)); + } + + return (strcspn($str, $filter) != strlen($str)) + ? '"' . addcslashes($str, '\\"') . '"' + : $str; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Headers.php b/framework/MIME/lib/Horde/MIME/Headers.php new file mode 100644 index 000000000..164f7de10 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Headers.php @@ -0,0 +1,543 @@ + + * @package Horde_MIME + */ +class Horde_MIME_Headers +{ + /** + * The internal headers array. + * + * @var array + */ + protected $_headers = array(); + + /** + * The sequence to use as EOL for the headers. + * The default is currently to output the EOL sequence internally as + * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045. + * To be RFC complaint, the full EOL combination should be used + * when sending a message. + * + * @var string + */ + protected $_eol = "\n"; + + /** + * The User-Agent string to use. + * + * @var string + */ + protected $_agent = null; + + /** + * Returns the internal header array in array format. + * + * @param array $options Optional parameters: + *
+     * 'charset' => (string) Encodes the headers using this charset.
+     *              DEFAULT: No encoding.
+     * 'defserver' => (string) TODO
+     *              DEFAULT: NO
+     * 'nowrap' => (integer) Don't wrap the headers.
+     *             DEFAULT: Headers are wrapped.
+     * 
+ * + * @return array The headers in array format. + */ + public function toArray($options = array()) + { + $ret = array(); + $address_keys = empty($options['charset']) + ? array() + : $this->addressFields(); + + foreach ($this->_headers as $header => $ob) { + $val = is_array($ob['value']) ? $ob['value'] : array($ob['value']); + + foreach (array_keys($val) as $key) { + if (!empty($address_keys)) { + if (in_array($header, $address_keys)) { + $text = Horde_MIME::encodeAddress($val[$key], $charset, empty($options['defserver']) ? null : $options['defserver']); + if (is_a($text, 'PEAR_Error')) { + $text = $val[$key]; + } + } else { + $text = Horde_MIME::encode($val[$key], $options['charset']); + } + } else { + $text = $val[$key]; + } + + if (empty($options['nowrap'])) { + $text = $this->wrapHeaders($header, $text); + } + + $val[$key] = $text; + } + + $ret[$ob['header']] = (count($val) == 1) ? reset($val) : $val; + } + + return $ret; + } + + /** + * Returns the internal header array in string format. + * + * @param array $options Optional parameters: + *
+     * 'charset' => (string) Encodes the headers using this charset.
+     *              DEFAULT: No encoding.
+     * 'defserver' => (string) TODO
+     *              DEFAULT: NO
+     * 'nowrap' => (integer) Don't wrap the headers.
+     *             DEFAULT: Headers are wrapped.
+     * 
+ * + * @return string The headers in string format. + */ + public function toString($options = array()) + { + $text = ''; + + foreach ($this->toArray($options) as $key => $val) { + if (!is_array($val)) { + $val = array($val); + } + foreach ($val as $entry) { + $text .= $key . ': ' . $entry . $this->_eol; + } + } + + return $text . $this->_eol; + } + + /** + * Generate the 'Received' header for the Web browser->Horde hop + * (attempts to conform to guidelines in RFC 5321 [4.4]). + */ + public function addReceivedHeader() + { + $old_error = error_reporting(0); + if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { + /* This indicates the user is connecting through a proxy. */ + $remote_path = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); + $remote_addr = $remote_path[0]; + $remote = gethostbyaddr($remote_addr); + } else { + $remote_addr = $_SERVER['REMOTE_ADDR']; + $remote = empty($_SERVER['REMOTE_HOST']) + ? gethostbyaddr($remote_addr) + : $_SERVER['REMOTE_HOST']; + } + error_reporting($old_error); + + if (!empty($_SERVER['REMOTE_IDENT'])) { + $remote_ident = $_SERVER['REMOTE_IDENT'] . '@' . $remote . ' '; + } elseif ($remote != $_SERVER['REMOTE_ADDR']) { + $remote_ident = $remote . ' '; + } else { + $remote_ident = ''; + } + + if (!empty($GLOBALS['conf']['server']['name'])) { + $server_name = $GLOBALS['conf']['server']['name']; + } elseif (!empty($_SERVER['SERVER_NAME'])) { + $server_name = $_SERVER['SERVER_NAME']; + } elseif (!empty($_SERVER['HTTP_HOST'])) { + $server_name = $_SERVER['HTTP_HOST']; + } else { + $server_name = 'unknown'; + } + + $received = 'from ' . $remote . ' (' . $remote_ident . + '[' . $remote_addr . ']) ' . + 'by ' . $server_name . ' (Horde Framework) with HTTP; ' . + date('r'); + + $this->addHeader('Received', $received); + } + + /** + * Generate the 'Message-ID' header. + */ + public function addMessageIdHeader() + { + require_once dirname(__FILE__) . '/../MIME.php'; + $this->addHeader('Message-ID', Horde_MIME::generateMessageID()); + } + + /** + * Generate the 'Resent' headers (conforms to guidelines in + * RFC 2822 [3.6.6]). + * + * @param string $from The address to use for 'Resent-From'. + * @param string $to The address to use for 'Resent-To'. + */ + public function addResentHeaders($from, $to) + { + require_once dirname(__FILE__) . '/../MIME.php'; + + /* We don't set Resent-Sender, Resent-Cc, or Resent-Bcc. */ + $this->addHeader('Resent-Date', date('r')); + $this->addHeader('Resent-From', $from); + $this->addHeader('Resent-To', $to); + $this->addHeader('Resent-Message-ID', Horde_MIME::generateMessageID()); + } + + /** + * Generate the user agent description header. + */ + public function addUserAgentHeader() + { + $this->addHeader('User-Agent', $this->getUserAgent()); + } + + /** + * Returns the user agent description header. + * + * @return string The user agent header. + */ + public function getUserAgent() + { + if (is_null($this->_agent)) { + $this->_agent = 'Horde Application Framework 4.0'; + } + return $this->_agent; + } + + /** + * Explicitly sets the User-Agent string. + * + * @param string $agent The User-Agent string to use. + */ + public function setUserAgent($agent) + { + $this->_agent = $agent; + } + + /** + * Add a header to the header array. + * + * @param string $header The header name. + * @param string $value The header value. + * @param boolean $decode MIME decode the value? + */ + public function addHeader($header, $value, $decode = false) + { + require_once 'Horde/String.php'; + + $header = trim($header); + $lcHeader = String::lower($header); + + if (!isset($this->_headers[$lcHeader])) { + $this->_headers[$lcHeader] = array(); + $this->_headers[$lcHeader]['header'] = $header; + } + $ptr = &$this->_headers[$lcHeader]; + + if ($decode) { + require_once dirname(__FILE__) . '/../MIME.php'; + + // Fields defined in RFC 2822 that contain address information + if (in_array($lcHeader, $this->addressFields())) { + $value = Horde_MIME::decodeAddrString($value); + } else { + $value = Horde_MIME::decode($value); + } + } + + if (isset($ptr['value'])) { + if (!is_array($ptr['value'])) { + $ptr['value'] = array($ptr['value']); + } + $ptr['value'][] = $value; + } else { + $ptr['value'] = $value; + } + } + + /** + * Remove a header from the header array. + * + * @param string $header The header name. + */ + public function removeHeader($header) + { + require_once 'Horde/String.php'; + unset($this->_headers[String::lower(trim($header))]); + } + + /** + * Set a value for a particular header ONLY if that header is set. + * + * @param string $header The header name. + * @param string $value The header value. + * @param boolean $decode MIME decode the value? + * + * @return boolean True if value was set. + */ + public function setValue($header, $value, $decode = false) + { + require_once 'Horde/String.php'; + + if (isset($this->_headers[String::lower($header)])) { + $this->addHeader($header, $value, $decode); + return true; + } + + return false; + } + + /** + * Attempts to return the header in the correct case. + * + * @param string $header The header to search for. + * + * @return string The value for the given header. + * If the header is not found, returns null. + */ + public function getString($header) + { + require_once 'Horde/String.php'; + + $lcHeader = String::lower($header); + return (isset($this->_headers[$lcHeader])) + ? $this->_headers[$lcHeader]['header'] + : null; + } + + /** + * Attempt to return the value for a given header. + * The following header fields can only have 1 entry, so if duplicate + * entries exist, the first value will be used: + * * To, From, Cc, Bcc, Date, Sender, Reply-to, Message-ID, In-Reply-To, + * References, Subject (RFC 2822 [3.6]) + * * All List Headers (RFC 2369 [3]) + * + * @param string $header The header to search for. + * + * @return mixed The value for the given header. + * If the header is not found, returns null. + */ + public function getValue($header) + { + require_once 'Horde/String.php'; + + $header = String::lower($header); + + if (isset($this->_headers[$header])) { + return (is_array($this->_headers[$header]['value']) && in_array($header, $this->singleFields(true))) + ? $this->_headers[$header]['value'][0] + : $this->_headers[$header]['value']; + } + + return null; + } + + /** + * Returns the list of RFC defined header fields that contain address + * info. + * + * @return array The list of headers, in lowercase. + */ + public function addressFields() + { + return array( + 'from', 'to', 'cc', 'bcc', 'reply-to', 'resent-to', 'resent-cc', + 'resent-bcc', 'resent-from', 'sender' + ); + } + + /** + * Returns the list of RFC defined header fields that can only contain + * a single value. + * + * @param boolean $list Return list-related headers also? + * + * @return array The list of headers, in lowercase. + */ + public function singleFields($list = true) + { + $single = array( + 'to', 'from', 'cc', 'bcc', 'date', 'sender', 'reply-to', + 'message-id', 'in-reply-to', 'references', 'subject', 'x-priority' + ); + + if ($list) { + $single = array_merge($single, array_keys($this->listHeaders())); + } + + return $single; + } + + /** + * Returns the list of valid mailing list headers. + * + * @return array The list of valid mailing list headers. + */ + public function listHeaders() + { + return array( + /* RFC 2369 */ + 'list-help' => _("List-Help"), + 'list-unsubscribe' => _("List-Unsubscribe"), + 'list-subscribe' => _("List-Subscribe"), + 'list-owner' => _("List-Owner"), + 'list-post' => _("List-Post"), + 'list-archive' => _("List-Archive"), + /* RFC 2919 */ + 'list-id' => _("List-Id") + ); + } + + /** + * Do any mailing list headers exist? + * + * @return boolean True if any mailing list headers exist. + */ + public function listHeadersExist() + { + return (bool) count(array_intersect(array_keys($this->listHeaders()), array_keys($this->_headers))); + } + + /** + * Sets a new string to use for EOLs. + * + * @param string $eol The string to use for EOLs. + */ + public function setEOL($eol) + { + $this->_eol = $eol; + } + + /** + * Get the string to use for EOLs. + * + * @return string The string to use for EOLs. + */ + public function getEOL() + { + return $this->_eol; + } + + /** + * Returns a header from the header object. + * + * @param string $field The header to return as an object. + * + * @return array The object for the field requested. + */ + public function getOb($field) + { + $val = $this->getValue($field); + return is_null($val) + ? array() + : Horde_MIME_Address::parseAddressList($val); + } + + /** + * Adds proper linebreaks to a header string. + * RFC 2822 says headers SHOULD only be 78 characters a line, but also + * says that a header line MUST not be more than 998 characters. + * + * @param string $header The header name. + * @param string $text The text of the header field. + * + * @return string The header value, with linebreaks inserted. + */ + public function wrapHeaders($header, $text) + { + $eol = $this->_eol; + $header_text = rtrim($header) . ': '; + + /* Remove any existing linebreaks. */ + $text = $header_text . preg_replace("/\r?\n\s?/", ' ', rtrim($text)); + + if (!in_array(strtolower($header), array('content-type', 'content-disposition'))) { + /* Wrap the line. */ + $line = wordwrap($text, 75, $eol . ' '); + + /* Make sure there are no empty lines. */ + $line = preg_replace('/' . $eol . ' ' . $eol . ' /', '/' . $eol . ' /', $line); + + return substr($line, strlen($header_text)); + } + + /* Split the line by the RFC parameter separator ';'. */ + $params = preg_split("/\s*;\s*/", $text); + + $line = ''; + $eollength = strlen($eol); + $length = 1000 - $eollength; + $paramcount = count($params); + + reset($params); + while (list($count, $val) = each($params)) { + /* If longer than RFC allows, then simply chop off the excess. */ + $moreparams = (($count + 1) != $paramcount); + $maxlength = $length - (!empty($line) ? 1 : 0) - (($moreparams) ? 1 : 0); + if (strlen($val) > $maxlength) { + $val = substr($val, 0, $maxlength); + + /* If we have an opening quote, add a closing quote after + * chopping the rest of the text. */ + if (strpos($val, '"') !== false) { + $val = substr($val, 0, -1) . '"'; + } + } + + if (!empty($line)) { + $line .= ' '; + } + $line .= $val . (($moreparams) ? ';' : '') . $eol; + } + + return substr($line, strlen($header_text), ($eollength * -1)); + } + + /** + * Builds a Horde_MIME_Headers object from header text. + * This function can be called statically: + * $headers = Horde_MIME_Headers::parseHeaders(). + * + * @param string $text A text string containing the headers. + * + * @return Horde_MIME_Headers A new Horde_MIME_Headers object. + */ + static public function parseHeaders($text) + { + $headers = new Horde_MIME_Headers(); + $currheader = $currtext = null; + + foreach (explode("\n", $text) as $val) { + $val = rtrim($val); + if (empty($val)) { + break; + } + + if (($val[0] == ' ') || ($val[0] == "\t")) { + $currtext .= ' ' . ltrim($val); + } else { + if (!is_null($currheader)) { + $headers->addHeader($currheader, $currtext, true); + } + $pos = strpos($val, ':'); + $currheader = substr($val, 0, $pos); + $currtext = ltrim(substr($val, $pos + 1)); + } + } + $headers->addHeader($currheader, $currtext, true); + + return $headers; + } +} diff --git a/framework/MIME/lib/Horde/MIME/MDN.php b/framework/MIME/lib/Horde/MIME/MDN.php new file mode 100644 index 000000000..d567c88ac --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/MDN.php @@ -0,0 +1,238 @@ + + * @package Horde_MIME + */ +class Horde_MIME_MDN +{ + /** + * The Horde_MIME_Headers object. + * + * @var Horde_MIME_Headers + */ + protected $_headers; + + /** + * The text of the original message. + * + * @var string + */ + protected $_msgtext = false; + + /** + * Constructor. + * + * @param Horde_MIME_Headers $mime_headers A Horde_MIME_Headers object. + */ + function __construct($headers = null) + { + $this->_headers = $headers; + } + + /** + * Returns the address to return the MDN to. + * + * @return string The address to send the MDN to. Returns null if no + * MDN is requested. + */ + public function getMDNReturnAddr() + { + /* RFC 3798 [2.1] requires the Disposition-Notificaion-To header + * for an MDN to be created. */ + return $this->_headers->getValue('Disposition-Notification-To'); + } + + /** + * Is user input required to send the MDN? + * Explicit confirmation is needed in some cases to prevent mail loops + * and the use of MDNs for mail bombing. + * + * @return boolean Is explicit user input required to send the MDN? + */ + public function userConfirmationNeeded() + { + $return_path = $this->_headers->getValue('Return-Path'); + + /* RFC 3798 [2.1]: Explicit confirmation is needed if there is no + * Return-Path in the header. Also, "if the message contains more + * than one Return-Path header, the implementation may [] treat the + * situation as a failure of the comparison." */ + if (empty($return_path) || is_array($return_path)) { + return true; + } + + require_once dirname(__FILE__) . '/Address.php'; + + /* RFC 3798 [2.1]: Explicit confirmation is needed if there is more + * than one distinct address in the Disposition-Notification-To + * header. */ + $addr_arr = Horde_MIME_Address::parseAddressList($this->getMDNReturnAddr()); + if (count($addr_arr) > 1) { + return true; + } + + /* RFC 3798 [2.1] states that "MDNs SHOULD NOT be sent automatically + * if the address in the Disposition-Notification-To header differs + * from the address in the Return-Path header." This comparison is + * case-sensitive for the mailbox part and case-insensitive for the + * host part. */ + $ret_arr = Horde_MIME_Address::parseAddressList($return_path); + return ($addr_arr[0]['mailbox'] == $ret_arr[0]['mailbox']) && + (String::lower($addr_arr[0]['host']) == String::lower($ret_arr[0]['host'])); + } + + /** + * When generating the MDN, should we return the enitre text of the + * original message? The default is no - we only return the headers of + * the original message. If the text is passed in via this method, we + * will return the entire message. + * + * @param string $text The text of the original message. + */ + public function originalMessageText($text) + { + $this->_msgtext = $text; + } + + /** + * Generate the MDN according to the specifications listed in RFC + * 3798 [3]. + * + * @param boolean $action Was this MDN type a result of a manual action + * on part of the user? + * @param boolean $sending Was this MDN sent as a result of a manual + * action on part of the user? + * @param string $type The type of action performed by the user. + *
+     * Per RFC 3798 [3.2.6.2] the following types are valid:
+     * =====================================================
+     * 'displayed'
+     * 'deleted'
+     * 
+ * @param array $mod The list of modifications. + *
+     * Per RFC 3798 [3.2.6.3] the following modifications are valid:
+     * =============================================================
+     * 'error'
+     * 
+ * @param array $err If $mod is 'error', the additional information + * to provide. Key is the type of modification, + * value is the text. + * + * @return mixed True on success, PEAR_Error object on error. + */ + public function generate($action, $sending, $type, $mod = array(), + $err = array()) + { + require_once dirname(__FILE__) . '/Headers.php'; + require_once dirname(__FILE__) . '/Message.php'; + require_once 'Horde/Identity.php'; + require_once 'Horde/Text.php'; + + /* Set up some variables we use later. */ + $identity = &Identity::singleton(); + $from_addr = $identity->getDefaultFromAddress(); + + $to = $this->getMDNReturnAddr(); + $ua = $this->_headers->getAgentHeader(); + + $orig_recip = $this->_headers->getValue('Original-Recipient'); + if (!empty($orig_recip) && is_array($orig_recip)) { + $orig_recip = $orig_recip[0]; + } + + $msg_id = $this->_headers->getValue('Message-ID'); + + /* Create the Disposition field now (RFC 3798 [3.2.6]). */ + $dispo = 'Disposition: ' . + (($action) ? 'manual-action' : 'automatic-action') . + '/' . + (($sending) ? 'MDN-sent-manually' : 'MDN-sent-automatically') . + '; ' . + $type; + if (!empty($mod)) { + $dispo .= '/' . implode(', ', $mod); + } + + /* Set up the mail headers. */ + $msg_headers = new Horde_MIME_Headers(); + $msg_headers->addMessageIdHeader(); + $msg_headers->addAgentHeader($ua); + $msg_headers->addHeader('Date', date('r')); + $msg_headers->addHeader('From', $from_addr); + $msg_headers->addHeader('To', $this->getMDNReturnAddr()); + $msg_headers->addHeader('Subject', _("Disposition Notification")); + + /* MDNs are a subtype of 'multipart/report'. */ + $msg = new Horde_MIME_Message(); + $msg->setType('multipart/report'); + $msg->setContentTypeParameter('report-type', 'disposition-notification'); + + $charset = NLS::getCharset(); + + /* The first part is a human readable message. */ + $part_one = new Horde_MIME_Part('text/plain'); + $part_one->setCharset($charset); + if ($type == 'displayed') { + $contents = sprintf(_("The message sent on %s to %s with subject \"%s\" has been displayed.\n\nThis is no guarantee that the message has been read or understood."), $this->_headers->getValue('Date'), $this->_headers->getValue('To'), $this->_headers->getValue('Subject')); + require_once 'Text/Flowed.php'; + $flowed = new Text_Flowed($contents, $charset); + $flowed->setDelSp(true); + $part_one->setContentTypeParameter('format', 'flowed'); + $part_one->setContentTypeParameter('DelSp', 'Yes'); + $part_one->setContents($flowed->toFlowed()); + } + // TODO: Messages for other notification types. + $msg->addPart($part_one); + + /* The second part is a machine-parseable description. */ + $part_two = new Horde_MIME_Part('message/disposition-notification'); + $part_two->setContents('Reporting-UA: ' . $GLOBALS['conf']['server']['name'] . '; ' . $ua . "\n"); + if (!empty($orig_recip)) { + $part_two->appendContents('Original-Recipient: rfc822;' . $orig_recip . "\n"); + } + $part_two->appendContents('Final-Recipient: rfc822;' . $from_addr . "\n"); + if (!empty($msg_id)) { + $part_two->appendContents('Original-Message-ID: rfc822;' . $msg_id . "\n"); + } + $part_two->appendContents($dispo . "\n"); + if (in_array('error', $mod) && isset($err['error'])) { + $part_two->appendContents('Error: ' . $err['error'] . "\n"); + } + $msg->addPart($part_two); + + /* The third part is the text of the original message. RFC 3798 [3] + * allows us to return only a portion of the entire message - this + * is left up to the user. */ + $part_three = new Horde_MIME_Part('message/rfc822'); + $part_three->setContents($this->_headers->toString()); + if (!empty($this->_msgtext)) { + $part_three->appendContents($part_three->getEOL() . $this->_msgtext); + } + $msg->addPart($part_three); + + return $msg->send($to, $msg_headers); + } + + /** + * Add a MDN (read receipt) request headers to the Horde_MIME_Headers:: + * object. + * + * @param Horde_MIME_Headers &$ob The object to add the headers to. + * @param string $to The address the receipt should be + * mailed to. + */ + public function addMDNRequestHeaders(&$ob, $to) + { + /* This is the RFC 3798 way of requesting a receipt. */ + $ob->addHeader('Disposition-Notification-To', $to); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Magic.php b/framework/MIME/lib/Horde/MIME/Magic.php new file mode 100644 index 000000000..ef93b459d --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Magic.php @@ -0,0 +1,260 @@ + + * @author Michael Slusarz + * @package Horde_MIME + */ +class Horde_MIME_Magic +{ + /** + * Returns a copy of the MIME extension map. + * + * @return array The MIME extension map. + */ + static protected function _getMimeExtensionMap() + { + static $mime_extension_map; + + if (!isset($mime_extension_map)) { + require dirname(__FILE__) . '/mime.mapping.php'; + } + + return $mime_extension_map; + } + + /** + * Returns a copy of the MIME magic file. + * + * @return array The MIME magic file. + */ + static protected function _getMimeMagicFile() + { + static $mime_magic; + + if (!isset($mime_magic)) { + require dirname(__FILE__) . '/mime.magic.php'; + } + + return $mime_magic; + } + + /** + * Attempt to convert a file extension to a MIME type, based + * on the global Horde and application specific config files. + * + * If we cannot map the file extension to a specific type, then + * we fall back to a custom MIME handler 'x-extension/$ext', which + * can be used as a normal MIME type internally throughout Horde. + * + * @param string $ext The file extension to be mapped to a MIME type. + * + * @return string The MIME type of the file extension. + */ + static public function extToMIME($ext) + { + if (empty($ext)) { + return 'application/octet-stream'; + } + + $ext = String::lower($ext); + $map = self::_getMimeExtensionMap(); + $pos = 0; + + while (!isset($map[$ext])) { + if (($pos = strpos($ext, '.')) === false) { + break; + } + $ext = substr($ext, $pos + 1); + } + + return isset($map[$ext]) + ? $map[$ext] + : 'x-extension/' . $ext; + } + + /** + * Attempt to convert a filename to a MIME type, based on the global Horde + * and application specific config files. + * + * @param string $filename The filename to be mapped to a MIME type. + * @param boolean $unknown How should unknown extensions be handled? If + * true, will return 'x-extension/*' types. If + * false, will return 'application/octet-stream'. + * + * @return string The MIME type of the filename. + */ + static public function filenameToMIME($filename, $unknown = true) + { + $pos = strlen($filename) + 1; + $type = ''; + + $map = self::_getMimeExtensionMap(); + for ($i = 0; $i <= $map['__MAXPERIOD__']; ++$i) { + $pos = strrpos(substr($filename, 0, $pos - 1), '.') + 1; + if ($pos === false) { + break; + } + } + $type = self::extToMIME(substr($filename, $pos)); + + return (empty($type) || (!$unknown && (strpos($type, 'x-extension') !== false))) + ? 'application/octet-stream' + : $type; + } + + /** + * Attempt to convert a MIME type to a file extension, based + * on the global Horde and application specific config files. + * + * If we cannot map the type to a file extension, we return false. + * + * @param string $type The MIME type to be mapped to a file extension. + * + * @return string The file extension of the MIME type. + */ + static public function MIMEToExt($type) + { + if (empty($type)) { + return false; + } + + if (($key = array_search($type, self::_getMimeExtensionMap())) === false) { + list($major, $minor) = explode('/', $type); + if ($major == 'x-extension') { + return $minor; + } + if (strpos($minor, 'x-') === 0) { + return substr($minor, 2); + } + return false; + } + + return $key; + } + + /** + * Uses variants of the UNIX "file" command to attempt to determine the + * MIME type of an unknown file. + * + * @param string $path The path to the file to analyze. + * @param string $magic_db Path to the mime magic database. + * + * @return string The MIME type of the file. Returns false if the file + * type isn't recognized or an error happened. + */ + static public function analyzeFile($path, $magic_db = null) + { + /* If the PHP Mimetype extension is available, use that. */ + if (Util::extensionExists('fileinfo')) { + $res = empty($magic_db) + ? @finfo_open(FILEINFO_MIME) + : @finfo_open(FILEINFO_MIME, $magic_db); + + if ($res) { + $type = finfo_file($res, $path); + finfo_close($res); + + /* Remove any additional information. */ + foreach (array(';', ',', '\\0') as $separator) { + if (($pos = strpos($type, $separator)) !== false) { + $type = rtrim(substr($type, 0, $pos)); + } + } + + if (preg_match('|^[a-z0-9]+/[.-a-z0-9]+$|i', $type)) { + return $type; + } + } + } + + if (Util::extensionExists('mime_magic')) { + return trim(mime_content_type($path)); + } + + /* Use a built-in magic file. */ + $mime_magic = self::_getMimeMagicFile(); + if (!($fp = @fopen($path, 'rb'))) { + return false; + } + + reset($mime_magic); + while (list($offset, $odata) = each($mime_magic)) { + reset($odata); + while (list($length, $ldata) = each($odata)) { + @fseek($fp, $offset, SEEK_SET); + $lookup = @fread($fp, $length); + if (!empty($ldata[$lookup])) { + fclose($fp); + return $ldata[$lookup]; + } + } + } + fclose($fp); + + return false; + } + + /** + * Uses variants of the UNIX "file" command to attempt to determine the + * MIME type of an unknown byte stream. + * + * @param string $data The file data to analyze. + * @param string $magic_db Path to the mime magic database. + * + * @return string The MIME type of the file. Returns false if the file + * type isn't recognized or an error happened. + */ + static public function analyzeData($data, $magic_db = null) + { + /* If the PHP Mimetype extension is available, use that. */ + if (Util::extensionExists('fileinfo')) { + $res = empty($magic_db) + ? @finfo_open(FILEINFO_MIME) + : @finfo_open(FILEINFO_MIME, $magic_db); + + if (!$res) { + return false; + } + + $type = finfo_buffer($res, $data); + finfo_close($res); + + /* Remove any additional information. */ + if (($pos = strpos($type, ';')) !== false) { + $type = rtrim(substr($type, 0, $pos)); + } + + if (($pos = strpos($type, ',')) !== false) { + $type = rtrim(substr($type, 0, $pos)); + } + + return $type; + } + + /* Use a built-in magic file. */ + $mime_magic = self::_getMimeMagicFile(); + reset($mime_magic); + while (list($offset, $odate) = each($mime_magic)) { + reset($odata); + while (list($length, $ldata) = each($odata)) { + $lookup = substr($data, $offset, $length); + if (!empty($ldata[$lookup])) { + return $ldata[$lookup]; + } + } + } + + return false; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Mail.php b/framework/MIME/lib/Horde/MIME/Mail.php new file mode 100644 index 000000000..289fc9a6c --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Mail.php @@ -0,0 +1,507 @@ + + * @package Horde_MIME + */ +class Horde_MIME_Mail +{ + /** + * The message headers. + * + * @var Horde_MIME_Headers + */ + protected $_headers; + + /** + * The main body part. + * + * @var Horde_MIME_Part + */ + protected $_body; + + /** + * The main HTML body part. + * + * @var Horde_MIME_Part + */ + protected $_htmlBody; + + /** + * The message recipients. + * + * @var array + */ + protected $_recipients = array(); + + /** + * All MIME parts except the main body part. + * + * @var array + */ + protected $_parts = array(); + + /** + * The Mail driver name. + * + * @link http://pear.php.net/Mail + * @var string + */ + protected $_mailer_driver = 'smtp'; + + /** + * The Mail driver parameters. + * + * @link http://pear.php.net/Mail + * @var array + */ + protected $_mailer_params = array(); + + /** + * Constructor. + * + * @param string $subject The message subject. + * @param string $body The message body. + * @param string $to The message recipient(s). + * @param string $from The message sender. + * @param string $charset The character set of the message. + */ + function __construct($subject = null, $body = null, $to = null, + $from = null, $charset = 'iso-8859-1') + { + /* Set SERVER_NAME. */ + if (!isset($_SERVER['SERVER_NAME'])) { + $_SERVER['SERVER_NAME'] = php_uname('n'); + } + + $this->_headers = new Horde_MIME_Headers(); + + if ($subject) { + $this->addHeader('Subject', $subject, $charset); + } + if ($to) { + $this->addHeader('To', $to, $charset); + } + if ($from) { + $this->addHeader('From', $from, $charset); + } + if ($body) { + $this->setBody($body, $charset); + } + } + + /** + * Adds several message headers at once. + * + * @param array $header Hash with header names as keys and header + * contents as values. + * @param string $charset The header value's charset. + */ + public function addHeaders($headers = array(), $charset = 'iso-8859-1') + { + foreach ($headers as $header => $value) { + if (is_a($added = $this->addHeader($header, $value, $charset), 'PEAR_Error')) { + return $added; + } + } + } + + /** + * Adds a message header. + * + * @param string $header The header name. + * @param string $value The header value. + * @param string $charset The header value's charset. + * @param boolean $overwrite If true, an existing header of the same name + * is being overwritten; if false, multiple + * headers are added; if null, the correct + * behaviour is automatically chosen depending + * on the header name. + */ + public function addHeader($header, $value, $charset = 'iso-8859-1', + $overwrite = null) + { + $lc_header = String::lower($header); + + /* Only encode value if charset is explicitly specified, otherwise + * the message's charset will be used when building the message. */ + if (!empty($charset)) { + if (in_array($lc_header, $this->_headers->addressFields())) { + $value = Horde_MIME::encodeAddress($value, $charset); + } else { + $value = Horde_MIME::encode($value, $charset); + } + } + + if (is_null($overwrite)) { + if (in_array($lc_header, $this->_headers->singleFields(true))) { + $overwrite = true; + } + } + + if ($overwrite) { + $this->_headers->removeHeader($header); + } + + if ($lc_header !== 'bcc') { + $this->_headers->addHeader($header, $value); + } + + if (in_array($lc_header, array('to', 'cc', 'bcc'))) { + return $this->addRecipients($value); + } + } + + /** + * Removes a message header. + * + * @param string $header The header name. + */ + public function removeHeader($header) + { + $value = $this->_headers->getValue($header); + $this->_headers->removeHeader($header); + if (in_array(String::lower($header), array('to', 'cc', 'bcc'))) { + $this->removeRecipients($value); + } + } + + /** + * Sets the message body text. + * + * @param string $body The message content. + * @param string $charset The character set of the message. + * @param boolean|integer $wrap If true, wrap the message at column 76; + * If an integer wrap the message at that + * column. Don't use wrapping if sending + * flowed messages. + */ + public function setBody($body, $charset = 'iso-8859-1', $wrap = false) + { + if ($wrap) { + $body = String::wrap($body, $wrap === true ? 76 : $wrap, "\n"); + } + $this->_body = new Horde_MIME_Part('text/plain', $body, $charset); + } + + /** + * Sets the HTML message body text. + * + * @param string $body The message content. + * @param string $charset The character set of the message. + * @param boolean $alternative If true, a multipart/alternative message is + * created and the text/plain part is + * generated automatically. If false, a + * text/html message is generated. + */ + public function setHTMLBody($body, $charset = 'iso-8859-1', + $alternative = true) + { + $this->_htmlBody = new Horde_MIME_Part('text/html', $body, $charset); + if ($alternative) { + require_once 'Horde/Text/Filter.php'; + $body = Text_Filter::filter($body, 'html2text', array('wrap' => false)); + $this->_body = new Horde_MIME_Part('text/plain', $body, $charset); + } + } + + /** + * Adds a message part. + * + * @param string $mime_type The content type of the part. + * @param string $content The content of the part. + * @param string $charset The character set of the part. + * @param string $disposition The content disposition of the part. + * + * @return integer The part number. + */ + public function addPart($mime_type, $content, $charset = 'us-ascii', + $disposition = null) + { + $part = new Horde_MIME_Part($mime_type, $content, $charset, $disposition); + $part->transferEncodeContents(); + $this->_parts[] = $part; + return count($this->_parts) - 1; + } + + /** + * Adds a MIME message part. + * + * @param Horde_MIME_Part $part A Horde_MIME_Part object. + * + * @return integer The part number. + */ + public function addMIMEPart($part) + { + $part->transferEncodeContents(); + $this->_parts[] = $part; + return count($this->_parts) - 1; + } + + /** + * Adds an attachment. + * + * @param string $file The path to the file. + * @param string $name The file name to use for the attachment. + * @param string $type The content type of the file. + * @param string $charset The character set of the part (only relevant for + * text parts. + * + * @return integer The part number. + */ + public function addAttachment($file, $name = null, $type = null, + $charset = 'us-ascii') + { + if (empty($name)) { + $name = basename($file); + } + if (empty($type)) { + require_once dirname(__FILE__) . '/Magic.php'; + $type = Horde_MIME_Magic::filenameToMIME($file, false); + } + + $part = new Horde_MIME_Part($type, file_get_contents($file), $charset, 'attachment'); + $part->setName($name); + $part->transferEncodeContents(); + $this->_parts[] = $part; + + return count($this->_parts) - 1; + } + + /** + * Removes a message part. + * + * @param integer $part The part number. + */ + public function removePart($part) + { + if (isset($this->_parts[$part])) { + unset($this->_parts[$part]); + } + } + + /** + * Adds message recipients. + * + * Recipients specified by To:, Cc:, or Bcc: headers are added + * automatically. + * + * @param string|array List of recipients, either as a comma separated + * list or as an array of email addresses. + */ + public function addRecipients($recipients) + { + $recipients = $this->_buildRecipients($recipients); + if (is_a($recipients, 'PEAR_Error')) { + return $recipients; + } + $this->_recipients = array_merge($this->_recipients, $recipients); + } + + /** + * Removes message recipients. + * + * @param string|array List of recipients, either as a comma separated + * list or as an array of email addresses. + */ + public function removeRecipients($recipients) + { + $recipients = $this->_buildRecipients($recipients); + if (is_a($recipients, 'PEAR_Error')) { + return $recipients; + } + $this->_recipients = array_diff($this->_recipients, $recipients); + } + + /** + * Removes all message recipients. + */ + public function clearRecipients() + { + $this->_recipients = array(); + } + + /** + * Builds a recipients list. + * + * @param string|array List of recipients, either as a comma separated + * list or as an array of email addresses. + * + * @return array Normalized list of recipients or PEAR_Error on failure. + */ + protected function _buildRecipients($recipients) + { + if (is_string($recipients)) { + $recipients = Horde_MIME::explode($recipients, ','); + } + $recipients = array_filter(array_map('trim', $recipients)); + + $addrlist = array(); + foreach ($recipients as $email) { + if (!empty($email)) { + $unique = Horde_MIME::bareAddress($email); + if ($unique) { + $addrlist[$unique] = $email; + } else { + $addrlist[$email] = $email; + } + } + } + + foreach (Horde_MIME::bareAddress(implode(', ', $addrlist), null, true) as $val) { + if (Horde_MIME::is8bit($val)) { + return PEAR::raiseError(sprintf(_("Invalid character in e-mail address: %s."), $val)); + } + } + + return $addrlist; + } + + /** + * Sends this message. + * + * For the possible Mail drivers and parameters see the PEAR Mail + * documentation. + * @link http://pear.php.net/Mail + * + * @param string $driver The Mail driver to use. + * @param array $params Any parameters necessary for the Mail driver. + * @param boolean $resend If true, the message id and date are re-used; + * If false, they will be updated. + * @param boolean $flowed Send message in flowed text format. @since + * Horde 3.2.1 + * + * @return mixed True on success, PEAR_Error on error. + */ + public function send($driver = null, $params = array(), $resend = false, + $flowed = true) + { + /* Add mandatory headers if missing. */ + if (!$resend || !$this->_headers->getString('Message-ID')) { + $this->_headers->addMessageIdHeader(); + } + if (!$this->_headers->getString('User-Agent')) { + $this->_headers->addAgentHeader(); + } + if (!$resend || !$this->_headers->getString('Date')) { + $this->_headers->addHeader('Date', date('r')); + } + + /* Send in flowed format. */ + if ($flowed && !empty($this->_body)) { + require_once 'Text/Flowed.php'; + $flowed = new Text_Flowed($this->_body->getContents(), + $this->_body->getCharset()); + $flowed->setDelSp(true); + $this->_body->setContentTypeParameter('DelSp', 'Yes'); + $this->_body->setContents($flowed->toFlowed()); + $this->_body->setContentTypeParameter('format', 'flowed'); + } + + /* Build mime message. */ + $mime = new Horde_MIME_Message(); + if (!empty($this->_body) && !empty($this->_htmlBody)) { + $basepart = new Horde_MIME_Part('multipart/alternative'); + $this->_body->setDescription(_("Plaintext Version of Message")); + $basepart->addPart($this->_body); + $this->_htmlBody->setDescription(_("HTML Version of Message")); + $basepart->addPart($this->_htmlBody); + $mime->addPart($basepart); + } elseif (!empty($this->_htmlBody)) { + $mime->addPart($this->_htmlBody); + } elseif (!empty($this->_body)) { + $mime->addPart($this->_body); + } + foreach ($this->_parts as $mime_part) { + $mime->addPart($mime_part); + } + + /* Check mailer configuration. */ + if (!empty($driver)) { + $this->_mailer_driver = $driver; + } + if (!empty($params)) { + $this->_mailer_params = $params; + } + + /* Send message. */ + return $mime->send(implode(', ', $this->_recipients), $this->_headers, + $this->_mailer_driver, $this->_mailer_params); + } + + /** + * Return error string corresponding to a sendmail error code. + * + * @param integer $code The error code. + * + * @return string The error string, or null if the code is unknown. + */ + static public function sendmailError($code) + { + switch ($code) { + case 64: // EX_USAGE + return 'sendmail: ' . _("command line usage error") . ' (64)'; + + case 65: // EX_DATAERR + return 'sendmail: ' . _("data format error") . ' (65)'; + + case 66: // EX_NOINPUT + return 'sendmail: ' . _("cannot open input") . ' (66)'; + + case 67: // EX_NOUSER + return 'sendmail: ' . _("addressee unknown") . ' (67)'; + + case 68: // EX_NOHOST + return 'sendmail: ' . _("host name unknown") . ' (68)'; + + case 69: // EX_UNAVAILABLE + return 'sendmail: ' . _("service unavailable") . ' (69)'; + + case 70: // EX_SOFTWARE + return 'sendmail: ' . _("internal software error") . ' (70)'; + + case 71: // EX_OSERR + return 'sendmail: ' . _("system error") . ' (71)'; + + case 72: // EX_OSFILE + return 'sendmail: ' . _("critical system file missing") . ' (72)'; + + case 73: // EX_CANTCREAT + return 'sendmail: ' . _("cannot create output file") . ' (73)'; + + case 74: // EX_IOERR + return 'sendmail: ' . _("input/output error") . ' (74)'; + + case 75: // EX_TEMPFAIL + return 'sendmail: ' . _("temporary failure") . ' (75)'; + + case 76: // EX_PROTOCOL + return 'sendmail: ' . _("remote error in protocol") . ' (76)'; + + case 77: // EX_NOPERM + return 'sendmail: ' . _("permission denied") . ' (77)'; + + case 78: // EX_CONFIG + return 'sendmail: ' . _("configuration error") . ' (78)'; + + case 79: // EX_NOTFOUND + return 'sendmail: ' . _("entry not found") . ' (79)'; + } + + return null; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Message.php b/framework/MIME/lib/Horde/MIME/Message.php new file mode 100644 index 000000000..6844080f2 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Message.php @@ -0,0 +1,384 @@ + + * @author Michael Slusarz + * @package Horde_MIME + */ +class Horde_MIME_Message extends Horde_MIME_Part +{ + /** + * Create a Horde_MIME_Message object from a Horde_MIME_Part object. + * This function can be called statically via: + * $mime_message = Horde_MIME_Message::convertMIMEPart(); + * + * @todo Is this needed? + * + * @param Horde_MIME_Part $mime_part The Horde_MIME_Part object. + * + * @return Horde_MIME_Message The new Horde_MIME_Message object. + */ + static public function convertMIMEPart($mime_part) + { + if (!$mime_part->getMIMEId()) { + $mime_part->setMIMEId(1); + } + + $mime_message = new Horde_MIME_Message(); + $mime_message->addPart($mime_part); + + return $mime_message; + } + + /** + * Sends this message. + * + * @param string $email The address list to send to. + * @param Horde_MIME_Headers $headers The Horde_MIME_Headers object + * holding this message's headers. + * @param string $driver The Mail:: driver to use. + * @param array $params Any parameters necessary for the + * Mail driver. + * + * @return mixed True on success, PEAR_Error on error. + */ + public function send($email, $headers, $driver = null, $params = array()) + { + if (!isset($driver)) { + $driver = $GLOBALS['conf']['mailer']['type']; + $params = $GLOBALS['conf']['mailer']['params']; + } + + require_once 'Mail.php'; + $mailer = Mail::factory($driver, $params); + + /* Add MIME Headers if they don't already exist. */ + if (!$headers->getValue('MIME-Version')) { + $headers = $this->addMIMEHeaders($headers); + } + $headerArray = $headers->toArray($this->getCharset()); + + /* Does the SMTP backend support 8BITMIME (RFC 1652) or + * BINARYMIME (RFC 3030) extensions? Requires PEAR's Mail package + * version 1.2+ and Net_SMTP version 1.3+. */ + if (($driver == 'smtp') && method_exists($mailer, 'getSMTPObject')) { + $net_smtp = $mailer->getSMTPObject(); + if (!is_a($net_smtp, 'PEAR_Error') && + method_exists($net_smtp, 'getServiceExtensions')) { + $smtp_ext = $net_smtp->getServiceExtensions(); + $message->strict7bit(false); + $encoding = $message->getTransferEncoding(); + if (($encoding == '8bit') && + isset($smtp_ext['8BITMIME'])) { + $mailer->addServiceExtensionParameter('BODY', '8BITMIME'); + } elseif (($encoding == 'binary') && + isset($smtp_ext['BINARYMIME'])) { + $mailer->addServiceExtensionParameter('BODY', 'BINARYMIME'); + } else { + $message->strict7bit(true); + $encoding = $message->getTransferEncoding(); + } + $headers->addHeader('Content-Transfer-Encoding', $encoding); + } + } + + /* Make sure the message has a trailing newline. */ + $msg = $this->toString(); + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + + $result = $mailer->send(Horde_MIME::encodeAddress($email), $headerArray, $msg); + + if (is_a($result, 'PEAR_Error') && ($driver == 'sendmail')) { + $error = Horde_MIME_Mail::sendmailError($result->getCode()); + if (is_null($error)) { + $error = $result; + $userinfo = null; + } else { + $userinfo = $result->toString(); + } + return PEAR::raiseError($error, null, null, null, $userinfo); + } + + return $result; + } + + /** + * Get the list of MIME headers for this part in an array. + * + * @return array The full set of MIME headers. + */ + public function getHeaderArray() + { + /* Per RFC 2045 [4], this MUST appear in the message headers. */ + return parent::header(array('MIME-Version' => '1.0')); + } + + /** + * Parse an array of MIME structure information into a Horde_MIME_Message + * object. + * This function can be called statically via: + * $mime_message = Horde_MIME_Message::parseStructure(); + * + * @param array $structure An array of structure information in the + * following format: + *
+     * MANDATORY:
+     *   'type' - (string) The MIME type
+     *   'subtype' - (string) The MIME subtype
+     *
+     * The array MAY contain the following information:
+     *   'disposition' - (string) The disposition type of the part (e.g.
+     *                   'attachment', 'inline').
+     *   'dparameters' - (array) Attribute/value pairs from the part's
+     *                   Content-Disposition header.
+     *   'language' - (array) A list of body language values.
+     *   'location' - (string) The body content URI.
+     *
+     * Depending on the MIME type of the part, the array will also contain
+     * further information. If labeled as [OPTIONAL], the array MAY
+     * contain this information, but only if 'noext' is false and the
+     * server returned the requested information. Else, the value is not
+     * set.
+     *
+     * multipart/* parts:
+     * ==================
+     * 'parts' - (array) An array of subparts (follows the same format as
+     *           the base structure array).
+     * 'parameters' - [OPTIONAL] (array) Attribute/value pairs from the
+     *                part's Content-Type header.
+     *
+     * All other parts:
+     * ================
+     * 'parameters' - (array) Attribute/value pairs from the part's
+     *                Content-Type header.
+     * 'id' - (string) The part's Content-ID value.
+     * 'description' - (string) The part's Content-Description value.
+     * 'encoding' - (string) The part's Content-Transfer-Encoding value.
+     * 'size' - (integer) - The part's size in bytes.
+     * 'envelope' - [ONLY message/rfc822] (array) See 'envelope' response.
+     * 'structure' - [ONLY message/rfc822] (array) See 'structure'
+     *               response.
+     * 'lines' - [ONLY message/rfc822 and text/*] (integer) The size of
+     *           the body in text lines.
+     * 'md5' - [OPTIONAL] (string) The part's MD5 value.
+     * 
+ * + * @return object A Horde_MIME_Message object. + */ + static public function parseStructure($structure) + { + $ob = self::_parseStructure($structure, true); + $ob->buildMIMEIds(); + return $ob; + } + + /** + * Parse a subpart of a MIME message into a + * Horde_MIME_Message/Horde_MIME_Part object. + * + * @param array $data Structure information in the format described + * in parseStructure(). + * @param boolean $rfc822 Force the part to be treated as a + * message/rfc822 part. + * + * @return mixed Returns either a Horde_MIME_Message or a Horde_MIME_Part + * object, depending on the part's MIME type. + */ + static protected function _parseStructure($data, $rfc822 = false) + { + $type = $data['type'] . '/' . $data['subtype']; + + if ($rfc822 || ($type == 'message/rfc822')) { + $ob = new Horde_MIME_Message(); + } else { + $ob = new Horde_MIME_Part(); + } + + $ob->setType($type); + + if (isset($data['encoding'])) { + $ob->setTransferEncoding($data['encoding']); + } + + if (isset($data['disposition'])) { + $ob->setDisposition($data['disposition']); + if (!empty($data['dparameters'])) { + foreach ($data['dparameters'] as $key => $val) { + $ob->setDispositionParameter($key, $val); + } + } + } + + if (isset($data['size'])) { + $ob->setBytes($data['size']); + } + + if (isset($data['id'])) { + $ob->setContentID($data['id']); + } + + if (!empty($data['parameters'])) { + foreach ($data['parameters'] as $key => $val) { + $ob->setContentTypeParameter($key, $val); + } + } + + /* Set the default character set. */ + if (($data['subtype'] == 'text') && + (String::lower($ob->getCharset()) == 'us-ascii') && + isset($GLOBALS['mime_structure']['default_charset'])) { + /* @todo - switch to using static variable for this. */ + //$ob->setCharset($GLOBALS['mime_structure']['default_charset']); + } + + if (isset($data['description'])) { + $ob->setDescription($data['description']); + } + + /* Set the name. */ + if (!$ob->getName()) { + $ob->setName($ob->getDispositionParameter('filename')); + } + + // @todo Handle language, location, md5, lines, envelope + + /* Add multipart parts. */ + if (!empty($data['parts'])) { + reset($data['parts']); + while (list(,$val) = each($data['parts'])) { + $ob->addPart(self::_parseStructure($val)); + } + } elseif (!empty($data['structure'])) { + $ob->addPart(self::_parseStructure($data['structure'])); + } + + return $ob; + } + + /** + * Attempts to build a Horde_MIME_Message object from message text. + * This function can be called statically via: + * $mime_message = Horde_MIME_Message::parseMessage(); + * + * @param string $text The text of the MIME message. + * + * @return Horde_MIME_Message A Horde_MIME_Message object, or false on + * error. + */ + static public function parseMessage($text) + { + /* Set up the options for the mimeDecode class. */ + $decode_args = array( + 'include_bodies' => true, + 'decode_bodies' => false, + 'decode_headers' => false + ); + + require_once 'Mail/mimeDecode.php'; + $mimeDecode = new Mail_mimeDecode($text, Horde_MIME_Part::EOL); + if (!($ob = $mimeDecode->decode($decode_args))) { + return false; + } + + return self::parseStructure(self::_convertMimeDecodeData($ob)); + } + + /** + * Convert the output from Mail_mimeDecode::decode() into a structure that + * parse() can handle. + * + * @param stdClass $ob The output from Mail_mimeDecode::decode(). + * + * @return array An array of structure information. + */ + static protected function _convertMimeDecodeData($ob) + { + /* Primary content-type. */ + if (isset($ob->ctype_primary)) { + $part = array( + 'type' => strtolower($ob->ctype_primary), + 'subtype' => isset($ob->ctype_secondary) ? strtolower($ob->ctype_secondary) : 'x-unknown' + ); + } else { + $part = array( + 'type' => 'application', + 'subtype' => 'octet-stream' + ); + } + + /* Content transfer encoding. */ + if (isset($ob->headers['content-transfer-encoding'])) { + $part['encoding'] = strtolower($ob->headers['content-transfer-encoding']); + } + + /* Content-type and Disposition parameters. */ + $param_types = array( + 'ctype_parameters' => 'parameters', + 'd_parameters' => 'dparameters' + ); + + foreach ($param_types as $param_key => $param_value) { + if (isset($ob->$param_key)) { + $part[$param_value] = array(); + foreach ($ob->$param_key as $key => $val) { + $part[$param_value][strtolower($key)] = $val; + } + } + } + + /* Content-Description. */ + if (isset($ob->headers['content-description'])) { + $part['description'] = $ob->headers['content-description']; + } + + /* Content-Disposition. */ + if (isset($ob->headers['content-disposition'])) { + $hdr = $ob->headers['content-disposition']; + $pos = strpos($hdr, ';'); + if ($pos !== false) { + $hdr = substr($hdr, 0, $pos); + } + $part['disposition'] = strtolower($hdr); + } + + /* Content-ID. */ + if (isset($ob->headers['content-id'])) { + $part['id'] = $ob->headers['content-id']; + } + + /* Get file size (if 'body' text is set). */ + if (isset($ob->body) && + ($part['type'] != 'message') && + ($part['subtype'] != 'rfc822')) { + /* Mail_mimeDecode puts an extra linebreak at the end of body + * text. */ + $size = strlen(str_replace(array("\r\n", "\n"), array("\n", "\r\n"), $ob->body)) - 2; + $part['size'] = ($size < 0) ? 0 : $size; + } + + /* Process parts also. */ + if (isset($ob->parts)) { + $part['parts'] = array(); + reset($ob->parts); + while (list($key,) = each($ob->parts)) { + $part['parts'][] = self::_convertMimeDecodeData($ob->parts[$key]); + } + } + + return $part; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Part.php b/framework/MIME/lib/Horde/MIME/Part.php new file mode 100644 index 000000000..6af6251b6 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Part.php @@ -0,0 +1,1324 @@ + + * @author Michael Slusarz + * @package Horde_MIME + */ +class Horde_MIME_Part +{ + /* The character(s) used internally for EOLs. */ + const EOL = "\n"; + + /* The character string designated by RFC 2045 to designate EOLs in MIME + * messages. */ + const RFC_EOL = "\r\n"; + + /* The default MIME character set. */ + const DEFAULT_CHARSET = 'us-ascii'; + + /* The default MIME disposition. */ + const DEFAULT_DISPOSITION = 'inline'; + + /* The default MIME encoding. */ + const DEFAULT_ENCODING = '7bit'; + + /** + * The type (ex.: text) of this part. + * Per RFC 2045, the default is 'application'. + * + * @var string + */ + protected $_type = 'application'; + + /** + * The subtype (ex.: plain) of this part. + * Per RFC 2045, the default is 'octet-stream'. + * + * @var string + */ + protected $_subtype = 'octet-stream'; + + /** + * The body of the part. + * + * @var string + */ + protected $_contents = ''; + + /** + * The desired transfer encoding of this part. + * + * @var string + */ + protected $_transferEncoding = self::DEFAULT_ENCODING; + + /** + * Should the message be encoded via 7-bit? + * + * @var boolean + */ + protected $_encode7bit = true; + + /** + * The description of this part. + * + * @var string + */ + protected $_description = ''; + + /** + * The disposition of this part (inline or attachment). + * + * @var string + */ + protected $_disposition = self::DEFAULT_DISPOSITION; + + /** + * The disposition parameters of this part. + * + * @var array + */ + protected $_dispParams = array(); + + /** + * The content type parameters of this part. + * + * @var array + */ + protected $_contentTypeParams = array(); + + /** + * The subparts of this part. + * + * @var array + */ + protected $_parts = array(); + + /** + * Information/Statistics on the subpart. + * + * @var array + */ + protected $_information = array(); + + /** + * The list of CIDs for this part. + * + * @var array + */ + protected $_cids = array(); + + /** + * The MIME ID of this part. + * + * @var string + */ + protected $_mimeid = null; + + /** + * The sequence to use as EOL for this part. + * The default is currently to output the EOL sequence internally as + * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045. + * To be RFC complaint, the full EOL combination should be used + * when sending a message. + * It is not crucial here since the PHP/PEAR mailing functions will handle + * the EOL details. + * + * @var string + */ + protected $_eol = self::EOL; + + /** + * Internal class flags. + * + * @var array + */ + protected $_flags = array(); + + /** + * Unique Horde_MIME_Part boundary string. + * + * @var string + */ + protected $_boundary = null; + + /** + * Default value for this Part's size. + * + * @var integer + */ + protected $_bytes = 0; + + /** + * The content-ID for this part. + * + * @var string + */ + protected $_contentid = null; + + /** + * Do we need to reindex the current part. + * + * @var boolean + */ + protected $_reindex = false; + + /** + * TODO + */ + protected $_headers; + + /** + * Set the content-disposition of this part. + * + * @param string $disposition The content-disposition to set (inline or + * attachment). + */ + public function setDisposition($disposition) + { + $disposition = String::lower($disposition); + + if (in_array($disposition, array('inline', 'attachment'))) { + $this->_disposition = $disposition; + } + } + + /** + * Get the content-disposition of this part. + * + * @return string The part's content-disposition. + */ + public function getDisposition() + { + return $this->_disposition; + } + + /** + * Add a disposition parameter to this part. + * + * @param string $label The disposition parameter label. + * @param string $data The disposition parameter data. + */ + public function setDispositionParameter($label, $data) + { + $this->_dispParams[$label] = Horde_MIME::decode($data); + } + + /** + * Get a disposition parameter from this part. + * + * @param string $label The disposition parameter label. + * + * @return string The data requested. + * Returns null if $label is not set. + */ + public function getDispositionParameter($label) + { + return (isset($this->_dispParams[$label])) + ? $this->_dispParams[$label] + : null; + } + + /** + * Get all parameters from the Content-Disposition header. + * + * @return array An array of all the parameters + * Returns the empty array if no parameters set. + */ + public function getAllDispositionParameters() + { + return $this->_dispParams; + } + + /** + * Set the name of this part. + * + * @param string $name The name to set. + */ + public function setName($name) + { + $this->setContentTypeParameter('name', $name); + } + + /** + * Get the name of this part. + * + * @param boolean $default If the name parameter doesn't exist, should we + * use the default name from the description + * parameter? + * + * @return string The name of the part. + */ + public function getName($default = false) + { + $name = $this->getContentTypeParameter('name'); + + if ($default && empty($name)) { + $name = preg_replace('|\W|', '_', $this->getDescription(false)); + } + + return $name; + } + + /** + * Set the body contents of this part. + * + * @param string $contents The part body. + * @param string $encoding The current encoding of the contents. + */ + public function setContents($contents, $encoding = null) + { + $this->_contents = $contents; + $this->_flags['contentsSet'] = true; + $this->_flags['currentEncoding'] = is_null($encoding) ? $this->getCurrentEncoding() : $encoding; + } + + /** + * Add to the body contents of this part. + * + * @param string $contents The contents to append to the current part + * body. + * @param string $encoding The current encoding of the contents. If not + * specified, will try to auto determine the + * encoding. + */ + public function appendContents($contents, $encoding = null) + { + if (empty($this->_flags['contentsSet'])) { + $this->setContents($contents, $encoding); + } else { + if (!is_null($encoding) && + ($encoding != $this->getCurrentEncoding())) { + $this->setTransferEncoding($encoding); + $this->transferDecodeContents(); + } + $this->setContents($this->_contents . $contents, $encoding); + } + } + + /** + * Clears the body contents of this part. + */ + public function clearContents() + { + $this->_contents = ''; + unset($this->_flags['contentsSet'], $this->_flags['currentEncoding']); + } + + /** + * Return the body of the part. + * + * @return string The raw body of the part. + */ + public function getContents() + { + return $this->_contents; + } + + /** + * Returns the contents in strict RFC 822 & 2045 output - namely, all + * newlines end with the canonical sequence. + * + * @return string The raw body of the part, with EOL.. + */ + public function getCanonicalContents() + { + return $this->replaceEOL($this->_contents, self::RFC_EOL); + } + + /** + * Transfer encode the contents (to the transfer encoding identified via + * getTransferEncoding()) and set as the part's new contents. + */ + public function transferEncodeContents() + { + $contents = $this->transferEncode(); + $encode = $this->_flags['currentEncoding'] = $this->_flags['lastTransferEncode']; + $this->setContents($contents, $encode); + $this->setTransferEncoding($encode); + } + + /** + * Transfer decode the contents and set them as the new contents. + */ + public function transferDecodeContents() + { + $contents = $this->transferDecode(); + $encode = $this->_flags['currentEncoding'] = $this->_flags['lastTransferDecode']; + $this->setTransferEncoding($encode); + + /* Don't set contents if they are empty, because this will do stuff + like reset the internal bytes field, even though we shouldn't do + that (the user has their reasons to set the bytes field to a + non-zero value without putting the contents into this part). */ + if (strlen($contents)) { + $this->setContents($contents, $encode); + } + } + + /** + * Set the MIME type of this part. + * + * @param string $mimetype The MIME type to set (ex.: text/plain). + */ + public function setType($mimetype) + { + /* RFC 2045: Any entity with unrecognized encoding must be treated + as if it has a Content-Type of "application/octet-stream" + regardless of what the Content-Type field actually says. */ + if ($this->_transferEncoding == 'x-unknown') { + return; + } + + list($this->_type, $this->_subtype) = explode('/', String::lower($mimetype)); + + /* Known types. */ + $known = array( + 'text', 'multipart', 'message', 'application', 'audio', 'image', + 'video', 'model' + ); + + if (in_array($this->_type, $known)) { + /* Set the boundary string for 'multipart/*' parts. */ + if ($this->_type == 'multipart') { + if (!$this->getContentTypeParameter('boundary')) { + $this->setContentTypeParameter('boundary', $this->_generateBoundary()); + } + } else { + $this->clearContentTypeParameter('boundary'); + } + } else { + $this->_type = 'x-unknown'; + $this->clearContentTypeParameter('boundary'); + } + } + + /** + * Get the full MIME Content-Type of this part. + * + * @param boolean $charset Append character set information to the end + * of the content type if this is a text/* part? + * + * @return string The mimetype of this part + * (ex.: text/plain; charset=us-ascii). + */ + public function getType($charset = false) + { + if (!isset($this->_type) || !isset($this->_subtype)) { + return false; + } + + $ptype = $this->getPrimaryType(); + $type = $ptype . '/' . $this->getSubType(); + if ($charset && ($ptype == 'text')) { + $type .= '; charset=' . $this->getCharset(); + } + + return $type; + } + + /** + * If the subtype of a MIME part is unrecognized by an application, the + * default type should be used instead (See RFC 2046). This method + * returns the default subtype for a particular primary MIME type. + * + * @return string The default MIME type of this part (ex.: text/plain). + */ + public function getDefaultType() + { + switch ($this->getPrimaryType()) { + case 'text': + /* RFC 2046 (4.1.4): text parts default to text/plain. */ + return 'text/plain'; + + case 'multipart': + /* RFC 2046 (5.1.3): multipart parts default to multipart/mixed. */ + return 'multipart/mixed'; + + default: + /* RFC 2046 (4.2, 4.3, 4.4, 4.5.3, 5.2.4): all others default to + application/octet-stream. */ + return 'application/octet-stream'; + } + } + + /** + * Get the primary type of this part. + * + * @return string The primary MIME type of this part. + */ + public function getPrimaryType() + { + return $this->_type; + } + + /** + * Get the subtype of this part. + * + * @return string The MIME subtype of this part. + */ + public function getSubType() + { + return $this->_subtype; + } + + /** + * Set the character set of this part. + * + * @param string $charset The character set of this part. + */ + public function setCharset($charset) + { + $this->setContentTypeParameter('charset', $charset); + } + + /** + * Get the character set to use for of this part. Returns a charset for + * all types (not just 'text/*') since we use this charset to determine + * how to encode text in MIME headers. + * + * @return string The character set of this part. Returns null if there + * is no character set. + */ + public function getCharset() + { + $charset = $this->getContentTypeParameter('charset'); + return empty($charset) ? null : $charset; + } + + /** + * Set the description of this part. + * + * @param string $description The description of this part. + */ + public function setDescription($description) + { + $this->_description = Horde_MIME::decode($description); + } + + /** + * Get the description of this part. + * + * @param boolean $default If the name parameter doesn't exist, should we + * use the default name from the description + * parameter? + * + * @return string The description of this part. + */ + public function getDescription($default = false) + { + $desc = $this->_description; + + if ($default && empty($desc)) { + $desc = $this->getName(); + } + + return $desc; + } + + /** + * Set the transfer encoding to use for this part. + * + * @param string $encoding The transfer encoding to use. + */ + public function setTransferEncoding($encoding) + { + $known = array('7bit', '8bit', 'binary', 'base64', 'quoted-printable'); + $encoding = String::lower($encoding); + + if (in_array($encoding, $known)) { + $this->_transferEncoding = $encoding; + } else { + /* RFC 2045: Any entity with unrecognized encoding must be treated + as if it has a Content-Type of "application/octet-stream" + regardless of what the Content-Type field actually says. */ + $this->setType('application/octet-stream'); + $this->_transferEncoding = 'x-unknown'; + } + } + + /** + * Add a MIME subpart. + * + * @param Horde_MIME_Part $mime_part Add a subpart to the current object. + */ + public function addPart($mime_part) + { + $this->_parts[] = $mime_part; + $this->_reindex = true; + } + + /** + * Get a list of all MIME subparts. + * + * @return array An array of the Horde_MIME_Part subparts. + */ + public function getParts() + { + return $this->_parts; + } + + /** + * Retrieve a specific MIME part. + * + * @param string $id The MIME ID. + * + * @return Horde_MIME_Part A pointer to the part requested, or null if + * the part doesn't exist. + */ + public function &getPart($id) + { + $this_id = $this->getMIMEId(); + if ($id == $this_id) { + return $this; + } + + if ($this->_reindex) { + $this->buildMIMEIds(is_null($this_id) ? '1' : $this_id); + } + + return $this->_partFind($id, $this->_parts); + } + + /** + * Remove a subpart. + * + * @param string $id The MIME part to delete. + */ + public function removePart($id) + { + $ob = &$this->getPart($id); + if ($ob !== null) { + unset($ob); + } + } + + /** + * Alter a current MIME subpart. + * + * @param string $id The MIME part ID to alter. + * @param Horde_MIME_Part $mime_part The MIME part to store. + */ + public function alterPart($id, $mime_part) + { + $ob = &$this->getPart($id); + if ($ob !== null) { + $ob = $mime_part; + } + } + + /** + * Function used to find a specific MIME part by ID. + * + * @param string $id The MIME ID. + * @param array &$parts A list of Horde_MIME_Part objects. + */ + protected function &_partFind($id, &$parts) + { + foreach (array_keys($parts) as $val) { + $ret = $parts[$val]->getPart($id); + if ($ret !== null) { + return $ret; + } + } + + $ret = null; + return $ret; + } + + /** + * Add a content type parameter to this part. + * + * @param string $label The disposition parameter label. + * @param string $data The disposition parameter data. + */ + public function setContentTypeParameter($label, $data) + { + $this->_contentTypeParams[$label] = $data; + } + + /** + * Clears a content type parameter from this part. + * + * @param string $label The disposition parameter label. + * @param string $data The disposition parameter data. + */ + public function clearContentTypeParameter($label) + { + unset($this->_contentTypeParams[$label]); + } + + /** + * Get a content type parameter from this part. + * + * @param string $label The content type parameter label. + * + * @return string The data requested. + * Returns null if $label is not set. + */ + public function getContentTypeParameter($label) + { + return isset($this->_contentTypeParams[$label]) + ? $this->_contentTypeParams[$label] + : null; + } + + /** + * Get all parameters from the Content-Type header. + * + * @return array An array of all the parameters + * Returns the empty array if no parameters set. + */ + public function getAllContentTypeParameters() + { + return $this->_contentTypeParameters; + } + + /** + * Sets a new string to use for EOLs. + * + * @param string $eol The string to use for EOLs. + */ + public function setEOL($eol) + { + $this->_eol = $eol; + } + + /** + * Get the string to use for EOLs. + * + * @return string The string to use for EOLs. + */ + public function getEOL() + { + return $this->_eol; + } + + /** + * Returns a Horde_MIME_Header object containing all MIME headers needed + * for the part. + * + * @param Horde_MIME_Headers $headers The Horde_MIME_Headers object to + * add the MIME headers to. If not + * specified, adds the headers to a + * new object. + * + * @return Horde_MIME_Headers A Horde_MIME_Headers object. + */ + public function addMIMEHeaders($headers = null) + { + if (is_null($headers)) { + $headers = new Horde_MIME_Headers(); + } + + foreach ($this->getHeaderArray() as $key => $val) { + $headers->addHeader($key, $val); + } + + return $headers; + } + + /** + * Get the list of MIME headers for this part in an array. + * + * @return array The full set of MIME headers. + */ + public function getHeaderArray() + { + $ptype = $this->getPrimaryType(); + $stype = $this->getSubType(); + + /* Get the character set for this part. */ + $charset = $this->getCharset(); + + /* Get the Content-Type - this is ALWAYS required. */ + $ctype = $this->getType(true); + + /* Manually encode Content-Type and Disposition parameters in here, + * rather than in Horde_MIME_Headers, since it is easier to do when + * the paramters are broken down. Encoding in the headers object will + * ignore these headers Since they will already be in 7bit. */ + foreach ($this->getAllContentTypeParameters() as $key => $value) { + /* Skip the charset key since that would have already been + * added to $ctype by getType(). */ + if ($key == 'charset') { + continue; + } + + $encode_2231 = Horde_MIME::encodeParamString($key, $value, $charset); + /* Try to work around non RFC 2231-compliant MUAs by sending both + * a RFC 2047-like parameter name and then the correct RFC 2231 + * parameter. See: + * http://lists.horde.org/archives/dev/Week-of-Mon-20040426/014240.html */ + if (!empty($GLOBALS['conf']['mailformat']['brokenrfc2231']) && + (strpos($encode_2231, '*=') !== false)) { + $ctype .= '; ' . $key . '="' . Horde_MIME::encode($value, $charset) . '"'; + } + $ctype .= '; ' . $encode_2231; + } + $headers['Content-Type'] = $ctype; + + /* Get the description, if any. */ + if (($descrip = $this->getDescription())) { + $headers['Content-Description'] = $descrip; + } + + /* RFC 2045 [4] - message/rfc822 and message/partial require the + MIME-Version header only if they themselves claim to be MIME + compliant. */ + if (($ptype == 'message') && + (($stype == 'rfc822') || ($stype == 'partial'))) { + // TODO - Check for "MIME-Version" in message/rfc822 part. + $headers['MIME-Version'] = '1.0'; + } + + /* message/* parts require no additional header information. */ + if ($ptype == 'message') { + return $headers; + } + + /* Don't show Content-Disposition for multipart messages unless + there is a name parameter. */ + $name = $this->getName(); + if (($ptype != 'multipart') || !empty($name)) { + $disp = $this->getDisposition(); + + /* Add any disposition parameter information, if available. */ + if (!empty($name)) { + $encode_2231 = Horde_MIME::encodeParamString('filename', $name, $charset); + /* Same broken RFC 2231 workaround as above. */ + if (!empty($GLOBALS['conf']['mailformat']['brokenrfc2231']) && + (strpos($encode_2231, '*=') !== false)) { + $disp .= '; filename="' . Horde_MIME::encode($name, $charset) . '"'; + } + $disp .= '; ' . $encode_2231; + } + + $headers['Content-Disposition'] = $disp; + } + + /* Add transfer encoding information. */ + $headers['Content-Transfer-Encoding'] = $this->getTransferEncoding(); + + /* Add content ID information. */ + if (!is_null($this->_contentid)) { + $headers['Content-ID'] = $this->_contentid; + } + + return $headers; + } + + /** + * Return the entire part in MIME format. Includes headers on request. + * + * @param boolean $headers Include the MIME headers? + * + * @return string The MIME string. + */ + public function toString($headers = true) + { + $eol = $this->getEOL(); + $ptype = $this->getPrimaryType(); + $text = ''; + + if ($headers) { + $hdr_ob = $this->addMIMEHeaders(); + $hdr_ob->setEOL($eol); + $text = $hdr_ob->toString(array('charset' => $this->getCharset())); + } + + /* Any information about a message/* is embedded in the message + contents themself. Simply output the contents of the part + directly and return. */ + if ($ptype == 'message') { + return $text . $this->_contents; + } + + $text .= $this->transferEncode(); + + /* Deal with multipart messages. */ + if ($ptype == 'multipart') { + $this->_generateBoundary(); + $boundary = trim($this->getContentTypeParameter('boundary'), '"'); + if (!strlen($this->_contents)) { + $text .= 'This message is in MIME format.' . $eol; + } + reset($this->_parts); + while (list(,$part) = each($this->_parts)) { + $text .= $eol . '--' . $boundary . $eol; + $oldEOL = $part->getEOL(); + $part->setEOL($eol); + $text .= $part->toString(true); + $part->setEOL($oldEOL); + } + $text .= $eol . '--' . $boundary . '--' . $eol; + } + + return $text; + } + + /** + * Returns the encoded part in strict RFC 822 & 2045 output - namely, all + * newlines end with the canonical sequence. + * + * @param boolean $headers Include the MIME headers? + * + * @return string The entire MIME part. + */ + public function toCanonicalString($headers = true) + { + $string = $this->toString($headers); + return $this->replaceEOL($string, self::RFC_EOL); + } + + /** + * Should we make sure the message is encoded via 7-bit (e.g. to adhere + * to mail delivery standards such as RFC 2821)? + * + * @param boolean $use7bit Use 7-bit encoding? + */ + public function strict7bit($use7bit) + { + $this->_encode7bit = $use7bit; + } + + /** + * Get the transfer encoding for the part based on the user requested + * transfer encoding and the current contents of the part. + * + * @return string The transfer-encoding of this part. + */ + public function getTransferEncoding() + { + /* If there are no contents, return whatever the current value of + $_transferEncoding is. */ + if (empty($this->_contents)) { + return $encoding; + } + + $encoding = $this->_transferEncoding; + $ptype = $this->getPrimaryType(); + + switch ($ptype) { + case 'message': + /* RFC 2046 [5.2.1] - message/rfc822 messages only allow 7bit, + 8bit, and binary encodings. If the current encoding is either + base64 or q-p, switch it to 8bit instead. + RFC 2046 [5.2.2, 5.2.3, 5.2.4] - All other message/* messages + only allow 7bit encodings. */ + $encoding = ($this->getSubType() == 'rfc822') ? '8bit' : '7bit'; + break; + + case 'text': + if (Horde_MIME::is8bit($this->_contents)) { + $encoding = ($this->_encode7bit) ? 'quoted-printable' : '8bit'; + } elseif (preg_match("/(?:\n|^)[^\n]{999,}(?:\n|$)/", $this->_contents)) { + /* If the text is longer than 998 characters between + * linebreaks, use quoted-printable encoding to ensure the + * text will not be chopped (i.e. by sendmail if being sent + * as mail text). */ + $encoding = 'quoted-printable'; + } + break; + + default: + if (Horde_MIME::is8bit($this->_contents)) { + $encoding = ($this->_encode7bit) ? 'base64' : '8bit'; + } + break; + } + + /* Need to do one last check for binary data if encoding is 7bit or + * 8bit. If the message contains a NULL character at all, the message + * MUST be in binary format. RFC 2046 [2.7, 2.8, 2.9]. Q-P and base64 + * can handle binary data fine so no need to switch those encodings. */ + if (in_array($encoding, array('8bit', '7bit')) && + preg_match('/\x00/', $this->_encoding)) { + $encoding = ($this->_encode7bit) ? 'base64' : 'binary'; + } + + return $encoding; + } + + /** + * Retrieves the current encoding of the contents in the object. + * + * @return string The current encoding. + */ + public function getCurrentEncoding() + { + return empty($this->_flags['currentEncoding']) + ? $this->_transferEncoding + : $this->_flags['currentEncoding']; + } + + /** + * Encodes the contents with the part's transfer encoding. + * + * @return string The encoded text. + */ + public function transferEncode() + { + $encoding = $this->getTransferEncoding(); + $eol = $this->getEOL(); + + /* Set the 'lastTransferEncode' flag so that transferEncodeContents() + can save a call to getTransferEncoding(). */ + $this->_flags['lastTransferEncode'] = $encoding; + + /* If contents are empty, or contents are already encoded to the + correct encoding, return now. */ + if (!strlen($this->_contents) || + ($encoding == $this->_flags['currentEncoding'])) { + return $this->_contents; + } + + switch ($encoding) { + /* Base64 Encoding: See RFC 2045, section 6.8 */ + case 'base64': + /* Keeping these two lines separate seems to use much less + memory than combining them (as of PHP 4.3). */ + $encoded_contents = base64_encode($this->_contents); + return chunk_split($encoded_contents, 76, $eol); + + /* Quoted-Printable Encoding: See RFC 2045, section 6.7 */ + case 'quoted-printable': + $output = Horde_MIME::quotedPrintableEncode($this->_contents, $eol); + if (($eollength = String::length($eol)) && + (substr($output, $eollength * -1) == $eol)) { + return substr($output, 0, $eollength * -1); + } + return $output; + + default: + return $this->replaceEOL($this->_contents); + } + } + + /** + * Decodes the contents of the part to either a 7bit or 8bit encoding. + * + * @return string The decoded text. + * Returns the empty string if there is no text to decode. + */ + public function transferDecode() + { + $encoding = $this->getCurrentEncoding(); + + /* If the contents are empty, return now. */ + if (!strlen($this->_contents)) { + $this->_flags['lastTransferDecode'] = $encoding; + return $this->_contents; + } + + switch ($encoding) { + case 'base64': + $this->_flags['lastTransferDecode'] = '8bit'; + return base64_decode($this->_contents); + + case 'quoted-printable': + $message = preg_replace("/=\r?\n/", '', $this->_contents); + $message = quoted_printable_decode($this->replaceEOL($message)); + $this->_flags['lastTransferDecode'] = (Horde_MIME::is8bit($message)) ? '8bit' : '7bit'; + return $message; + + /* Support for uuencoded encoding - although not required by RFCs, + some mailers may still encode this way. */ + case 'uuencode': + case 'x-uuencode': + case 'x-uue': + $this->_flags['lastTransferDecode'] = '8bit'; + return convert_uuencode($this->_contents); + + default: + if (isset($this->_flags['lastTransferDecode']) && + ($this->_flags['lastTransferDecode'] != $encoding)) { + $message = $this->replaceEOL($this->_contents); + } else { + $message = $this->_contents; + } + $this->_flags['lastTransferDecode'] = $encoding; + return $message; + } + } + + /** + * Split the contents of the current Part into its respective subparts, + * if it is multipart MIME encoding. + * + * The boundary Content-Type parameter must be set for this function to + * work correctly. + * + * @return boolean True if the contents were successfully split. + * False if any error occurred. + */ + public function splitContents() + { + if ((!($boundary = $this->getContentTypeParameter('boundary'))) || + !strlen($this->_contents)) { + return false; + } + + $eol = $this->getEOL(); + $retvalue = false; + + $boundary = '--' . $boundary; + if (substr($this->_contents, 0, strlen($boundary)) == $boundary) { + $pos1 = 0; + } else { + $pos1 = strpos($this->_contents, $eol . $boundary); + if ($pos1 === false) { + return false; + } + } + + $pos1 = strpos($this->_contents, $eol, $pos1 + 1); + if ($pos1 === false) { + return false; + } + $pos1 += strlen($eol); + + reset($this->_parts); + $part_ptr = key($this->_parts); + + while ($pos2 = strpos($this->_contents, $eol . $boundary, $pos1)) { + $this->_parts[$part_ptr]->setContents(substr($this->_contents, $pos1, $pos2 - $pos1)); + $this->_parts[$part_ptr]->splitContents(); + next($this->_parts); + $part_ptr = key($this->_parts); + if (is_null($part_ptr)) { + return false; + } + $pos1 = strpos($this->_contents, $eol, $pos2 + 1); + if ($pos1 === false) { + return true; + } + $pos1 += strlen($eol); + } + + return true; + } + + /** + * Replace newlines in this part's contents with those specified by either + * the given newline sequence or the part's current EOL setting. + * + * @param string $text The text to replace. + * @param string $eol The EOL sequence to use. If not present, uses the + * part's current EOL setting. + * + * @return string The text with the newlines replaced by the desired + * newline sequence. + */ + public function replaceEOL($text, $eol = null) + { + if (is_null($eol)) { + $eol = $this->getEOL(); + } + return preg_replace("/\r?\n/", $eol, $text); + } + + /** + * Determine the size of this MIME part and its child members. + * + * @return integer Size of the part, in bytes. + */ + public function getBytes() + { + $bytes = 0; + + if (empty($this->_flags['contentsSet']) && $this->_bytes) { + $bytes = $this->_bytes; + } elseif ($this->getPrimaryType() == 'multipart') { + reset($this->_parts); + while (list(,$part) = each($this->_parts)) { + /* Skip multipart entries (since this may result in double + counting). */ + if ($part->getPrimaryType() != 'multipart') { + $bytes += $part->getBytes(); + } + } + } else { + $bytes = ($this->getPrimaryType() == 'text') + ? String::length($this->_contents, $this->getCharset()) + : strlen($this->_contents); + } + + return $bytes; + } + + /** + * Explicitly set the size (in bytes) of this part. This value will only + * be returned (via getBytes()) if there are no contents currently set. + * This function is useful for setting the size of the part when the + * contents of the part are not fully loaded (i.e. creating a + * Horde_MIME_Part object from IMAP header information without loading the + * data of the part). + * + * @param integer $bytes The size of this part in bytes. + */ + public function setBytes($bytes) + { + $this->_bytes = $bytes; + } + + /** + * Output the size of this MIME part in KB. + * + * @return string Size of the part, in string format. + */ + public function getSize() + { + $bytes = $this->getBytes(); + if (empty($bytes)) { + return $bytes; + } + + $localeinfo = NLS::getLocaleInfo(); + return number_format($bytes / 1024, 2, $localeinfo['decimal_point'], $localeinfo['thousands_sep']); + } + + /** + * Add to the list of CIDs for this part. + * + * @param array $cids A list of MIME IDs of the part. + * Key - MIME ID + * Value - CID for the part + */ + public function addCID($cids = array()) + { + $this->_cids += $cids; + asort($this->_cids, SORT_STRING); + } + + /** + * Returns the list of CIDs for this part. + * + * @return array The list of CIDs for this part. + */ + public function getCIDList() + { + return $this->_cids; + } + + /** + * Sets the Content-ID header for this part. + * + * @param string $cid Use this CID (if not already set). Else, generate + * a random CID. + */ + public function setContentID($cid = null) + { + if (is_null($this->_contentid)) { + $this->_contentid = (is_null($cid)) ? (Horde_MIME::generateRandomID() . '@' . $_SERVER['SERVER_NAME']) : $cid; + } + return $this->_contentid; + } + + /** + * Returns the Content-ID for this part. + * + * @return string The Content-ID for this part. + */ + public function getContentID() + { + return $this->_contentid; + } + + /** + * Alter the MIME ID of this part. + * + * @param string $mimeid The MIME ID. + */ + public function setMIMEId($mimeid) + { + $this->_mimeid = $mimeid; + } + + /** + * Returns the MIME ID of this part. + * + * @return string The MIME ID. + */ + public function getMIMEId() + { + return $this->_mimeid; + } + + /** + * Build the MIME IDs for this part and all subparts. + * + * @param string $id The ID of this part. + */ + public function buildMIMEIds($id = null) + { + if (is_null($id)) { + if (empty($this->_parts)) { + $this->setMIMEId('1'); + } else { + $this->setMIMEId('0'); + $i = 1; + foreach (array_keys($this->_parts) as $val) { + $this->_parts[$val]->buildMIMEIds($i++); + } + } + } else { + $this->setMIMEId($id . (($this->getType() == 'message/rfc822') ? '.0' : '')); + + if (!empty($this->_parts)) { + $i = 1; + foreach (array_keys($this->_parts) as $val) { + $this->_parts[$val]->buildMIMEIds($id . '.' . $i++); + } + } + } + + $this->_reindex = false; + } + + /** + * Generate the unique boundary string (if not already done). + * + * @return string The boundary string. + */ + protected function _generateBoundary() + { + if (is_null($this->_boundary)) { + $this->_boundary = '=_' . Horde_MIME::generateRandomID(); + } + return $this->_boundary; + } + + /** + * Returns a mapping of all MIME IDs to their content-types. + * + * @param boolean $sort Sort by MIME ID? + * + * @return array Keys: MIME ID; values: content type. + */ + public function contentTypeMap($sort = true) + { + $map = array($this->getMIMEId() => $this->getType()); + foreach ($this->_parts as $val) { + $map += $val->contentTypeMap(false); + } + + if ($sort) { + uksort($map, 'strnatcasecmp'); + } + + return $map; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer.php b/framework/MIME/lib/Horde/MIME/Viewer.php new file mode 100644 index 000000000..cfc47fc87 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer.php @@ -0,0 +1,332 @@ + + * @author Michael Slusarz + * @package Horde_MIME + */ +class Horde_MIME_Viewer +{ + /** + * The config array. This array is shared between all instances of + * Horde_MIME_Viewer. + * + * @var array + */ + static protected $_config = array(); + + /** + * The driver cache array. + * + * @var array + */ + static protected $_drivercache = array(); + + /** + * Attempts to return a concrete Horde_MIME_Viewer_* object based on the + * MIME type. + * + * @param string $mime_type The MIME type. + * + * @return Horde_MIME_Viewer The Horde_MIME_Viewer object, or false on + * error. + */ + static final public function factory($mime_type) + { + /* Spawn the relevant driver, and return it (or false on failure). */ + if (($ob = self::_getDriver($mime_type, $GLOBALS['registry']->getApp())) && + self::_resolveDriver($ob['driver'], $ob['app']) && + class_exists($ob['class'])) { + return new $ob['class'](self::$_config['mime_drivers'][$ob['app']][$ob['driver']]); + } + + return false; + } + + /** + * Given a MIME type and an app name, determine which driver can best + * handle the data. + * + * @param string $mime_type MIME type to resolve. + * @param string $app App in which to search for the driver. + * + * @return mixed Returns false if driver could not be found. Otherwise, + * an array with the following elements: + *
+     * 'app' - (string) The app containing the driver (e.g. 'horde')
+     * 'driver' - (string) Name of driver (e.g. 'enscript')
+     * 'exact' - (boolean) Was the driver and exact match?
+     * 
+ */ + static final protected function _getDriver($mime_type, $app = 'horde') + { + $sig = $mime_type . '|' . $app; + if (isset(self::$_drivercache[$sig])) { + return self::$_drivercache[$sig]; + } + + /* Make sure 'horde' mime_drivers config is loaded. */ + if (empty(self::$_config['mime_drivers']['horde'])) { + $res = Horde::loadConfiguration('mime_drivers.php', array('mime_drivers', 'mime_drivers_map'), 'horde'); + if (is_a($res, 'PEAR_Error')) { + return false; + } + self::$_config = $res; + } + + /* Make sure app's' mime_drivers config is loaded. */ + if (($app != 'horde') && empty(self::$_config['mime_drivers'][$app])) { + $res = Horde::loadConfiguration('mime_drivers.php', array('mime_drivers', 'mime_drivers_map'), $app); + if (is_a($res, 'PEAR_Error')) { + return false; + } + + require_once 'Horde/Array.php'; + self::$_config = Horde_Array::array_merge_recursive_overwrite(self::$_config, $res); + } + + $driver = ''; + $exact = false; + + list($primary_type,) = explode('/', $mime_type, 2); + $allSub = $primary_type . '/*'; + + /* If the app doesn't exist in $mime_drivers_map, check for Horde-wide + * viewers. */ + if (!isset(self::$_config['mime_drivers_map'][$app]) && + ($app != 'horde')) { + return self::_getDriver($mime_type, 'horde'); + } + + $dr = self::$_config['mime_drivers'][$app]; + $map = self::$_config['mime_drivers_map'][$app]; + + /* If an override exists for this MIME type, then use that */ + if (isset($map['overrides'][$mime_type])) { + $driver = $map['overrides'][$mime_type]; + $exact = true; + } elseif (isset($map['overrides'][$allSub])) { + $driver = $map['overrides'][$allSub]; + $exact = true; + } elseif (isset($map['registered'])) { + /* Iterate through the list of registered drivers, and see if + * this MIME type exists in the MIME types that they claim to + * handle. If the driver handles it, then assign it as the + * rendering driver. If we find a generic handler, keep iterating + * to see if we can find a specific handler. */ + foreach ($map['registered'] as $val) { + if (in_array($mime_type, $dr[$val]['handles'])) { + $driver = $val; + $exact = true; + break; + } elseif (in_array($allSub, $dr[$val]['handles'])) { + $driver = $val; + } + } + } + + /* If this is an application specific app, and an exact match was + not found, search for a Horde-wide specific driver. Only use the + Horde-specific driver if it is NOT the 'default' driver AND the + Horde driver is an exact match. */ + if (!$exact && ($app != 'horde')) { + $ob = self::_getDriver($mime_type, 'horde'); + if (empty($driver) || + (($ob['driver'] != 'default') && $ob['exact'])) { + $driver = $ob['driver']; + $app = 'horde'; + } + } + + /* If the 'default' driver exists in this app, fall back to that. */ + if (empty($driver) && self::_resolveDriver('default', $app)) { + $driver = 'default'; + } + + if (empty($driver)) { + return false; + } + + self::$_drivercache[$sig] = array( + 'app' => $app, + 'class' => (($app == 'horde') ? '' : $app . '_') . 'Horde_MIME_Viewer_' . $driver, + 'driver' => $driver, + 'exact' => $exact, + ); + + return self::$_drivercache[$sig]; + } + + /** + * Given a driver and an application, attempts to load the library file. + * + * @param string $driver Driver name. + * @param string $app Application name. + * + * @return boolean True if library file was loaded. + */ + static final protected function _resolveDriver($driver = 'default', + $app = 'horde') + { + $file = ($app == 'horde') + ? dirname(__FILE__) . '/Viewer/' . $driver . '.php' + : $GLOBALS['registry']->applications[$app]['fileroot'] . '/lib/MIME/Viewer/' . $driver . '.php'; + + require_once dirname(__FILE__) . '/Viewer/Driver.php'; + + $old_error = error_reporting(0); + $ret = require_once $file; + error_reporting($old_error); + + return $ret; + } + + /** + * Prints out the status message for a given MIME Part. + * + * @param string $msg The message to output. + * @param string $img An image link to add to the beginning of the + * message. + * @param string $class An optional style for the status box. + * + * @return string The formatted status message string. + */ + static public function formatStatusMsg($msg, $img = null, $class = null) + { + if (empty($msg)) { + return ''; + } + + if (!is_array($msg)) { + $msg = array($msg); + } + + /* If we are viewing as an attachment, don't print HTML code. */ + if (self::viewAsAttachment()) { + return implode("\n", $msg); + } + + if (is_null($class)) { + $class = 'mimeStatusMessage'; + } + $text = ''; + + /* If no image, simply print out the message. */ + if (is_null($img)) { + foreach ($msg as $val) { + $text .= '' . "\n"; + } + } else { + $text .= '' . "\n"; + } + + return $text . '
' . $val . '
' . $img . ''; + if (count($msg) == 1) { + $text .= $msg[0]; + } else { + $text .= ''; + foreach ($msg as $val) { + $text .= '' . "\n"; + } + $text .= '
' . $val . '
'; + } + $text .= '
'; + } + + /** + * Given a MIME type, this function will return an appropriate icon. + * + * @param string $mime_type The MIME type that we need an icon for. + * + * @return string The URL to the appropriate icon. + */ + static final public function getIcon($mime_type) + { + $app = $GLOBALS['registry']->getApp(); + $ob = self::_getIcon($mime_type, $app); + + if (is_null($ob)) { + if ($app == 'horde') { + return null; + } + + $obHorde = self::_getIcon($mime_type, 'horde'); + return is_null($obHorde) ? null : $obHorde['url']; + } elseif (($ob['match'] !== 0) && ($app != 'horde')) { + $obHorde = self::_getIcon($mime_type, 'horde'); + if (!is_null($ob['match']) && + ($obHorde['match'] <= $ob['match'])) { + return $obHorde['url']; + } + } + + return $ob['url']; + } + + /** + * Given an input MIME type and app, this function returns the URL of an + * icon that can be associated with it + * + * @param string $mime_type MIME type to get the icon for. + * + * @return mixed Null if not found, or an array with the following keys: + *
+     * 'exact' - (integer) How exact the match is.
+     *           0 => 'exact', 1 => 'primary', 2 => 'driver',
+     *           3 => 'default', or null.
+     * 'url' - (string) URL to an icon, or null if none could be found.
+     * 
+ */ + static final protected function _getIcon($mime_type, $app = 'horde') + { + if (!($ob = self::_getDriver($mime_type, $app))) { + return null; + } + $driver = $ob['driver']; + + list($primary_type,) = explode('/', $mime_type, 2); + $allSub = $primary_type . '/*'; + $ret = null; + + /* If the app doesn't exist in $mime_drivers, return now. */ + if (!isset(self::$_config['mime_drivers'][$app])) { + return null; + } + + $dr = self::$_config['mime_drivers'][$app]; + + /* If a specific icon for this driver and mimetype is defined, + then use that. */ + if (isset($dr[$driver]['icons'])) { + $icondr = $dr[$driver]['icons']; + $iconList = array($mime_type => 0, $allSub => 1, 'default' => 2); + foreach ($iconList as $key => $val) { + if (isset($icondr[$key])) { + $ret = array('match' => $val, 'url' => $icondr[$key]); + break; + } + } + } + + /* Try to use a default icon if none already obtained. */ + if (is_null($ret['url']) && + isset($dr['default']['icons']['default'])) { + $ret = array('match' => 3, 'url' => $dr['default']['icons']['default']); + } + + if (!is_null($ret)) { + $ret['url'] = $GLOBALS['registry']->getImageDir($app) . '/mime/' . $ret['url']; + } + + return $ret; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/Driver.php b/framework/MIME/lib/Horde/MIME/Viewer/Driver.php new file mode 100644 index 000000000..a7f5abe2d --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/Driver.php @@ -0,0 +1,195 @@ + + * @author Michael Slusarz + * @package Horde_MIME + */ +class Horde_MIME_Viewer_Driver +{ + /** + * Viewer configuration parameters. + * + * @var array + */ + protected $_conf = array(); + + /** + * The Horde_MIME_Part object to render. + * + * @var Horde_MIME_Part + */ + protected $_mimepart = null; + + /** + * The MIME display type for this part. + * + * @var string + */ + protected $_type = null; + + /** + * Can this driver render various views? + * + * @var boolean + */ + protected $_canrender = array( + 'full' => false, + 'info' => false, + 'inline' => false, + ); + + /** + * Constructor. + * + * @param array $conf Configuration specific to the driver. + */ + function __construct($conf = array()) + { + $this->_conf = $conf; + } + + /** + * Sets the Horde_MIME_Part object for the class. + * + * @param Horde_MIME_Part &$mime_part Reference to an object with the + * information to be rendered. + */ + public function setMIMEPart(&$mime_part) + { + $this->_mimepart = $mime_part; + } + + /** + * Return the MIME type of the rendered content. This can be + * overridden by the individual drivers, depending on what format + * they output in. By default, it passes through the MIME type of + * the object, or replaces custom extension types with + * 'text/plain' to let the browser do a best-guess render. + * + * @return string MIME type of the output content. + */ + public function getType() + { + if (is_null($this->_type)) { + return is_null(is_null($this->_mimepart) || ($this->_mimepart->getPrimaryType() == 'x-extension')) + ? 'text/plain' + : $this->_mimepart->getType(true); + } + + return $this->_type; + } + + /** + * Return the rendered version of the Horde_MIME_Part object. + * + * @return string Rendered version of the Horde_MIME_Part object. + */ + public function render() + { + return (is_null($this->_mimepart) || !$this->canDisplay()) + ? '' + : $this->_render(); + } + + /** + * Return the rendered version of the Horde_MIME_Part object. + * + * @return string Rendered version of the Horde_MIME_Part object. + */ + protected function _render() + { + } + + /** + * Return the rendered inline version of the Horde_MIME_Part object. + * + * @return string Rendered version of the Horde_MIME_Part object. + */ + public function renderInline() + { + return (is_null($this->_mimepart) || !$this->canDisplayInline()) + ? '' + : $this->_renderInline(); + } + + /** + * Return the rendered inline version of the Horde_MIME_Part object. + * + * @return string Rendered version of the Horde_MIME_Part object. + */ + protected function _renderInline() + { + } + + /** + * Return the rendered information about the Horde_MIME_Part object. + * + * @return string Rendered information on the Horde_MIME_Part object. + */ + public function renderInfo() + { + return (is_null($this->_mimepart) || !$this->canDisplayInfo()) + ? '' + : $this->_renderInfo(); + } + + /** + * Return the rendered information about the Horde_MIME_Part object. + * + * @return string Rendered information on the Horde_MIME_Part object. + */ + protected function _renderInfo() + { + } + + /** + * Can this driver render the the data? + * + * @return boolean True if the driver can render data. + */ + public function canDisplay() + { + return $this->_canrender['full']; + } + + /** + * Can this driver render the the data inline? + * + * @return boolean True if the driver can display inline. + */ + public function canDisplayInline() + { + return $this->getConfigParam('inline') && $this->_canrender['inline']; + } + + /** + * Can this driver render the the data inline? + * + * @return boolean True if the driver can display inline. + */ + public function canDisplayInfo() + { + return $this->_canrender['info']; + } + + /** + * Return a configuration parameter for the current viewer. + * + * @param string $param The parameter name. + * + * @return mixed The value of the parameter; returns null if the + * parameter doesn't exist. + */ + public function getConfigParam($param) + { + return isset($this->_conf[$param]) ? $this->_conf[$param] : null; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/audio.php b/framework/MIME/lib/Horde/MIME/Viewer/audio.php new file mode 100644 index 000000000..df83fbeb6 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/audio.php @@ -0,0 +1,25 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_audio extends Horde_MIME_Viewer_Driver +{ + /** + * Return the content-type. + * + * @return string The content-type of the output. + */ + public function getType() + { + return $this->mime_part->getType(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/css.php b/framework/MIME/lib/Horde/MIME/Viewer/css.php new file mode 100644 index 000000000..d11ebed79 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/css.php @@ -0,0 +1,109 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_css extends Horde_MIME_Viewer_source +{ + /** + * Render out the currently set contents. + * + * @param array $params Any parameters the viewer may need. + * + * @return string The rendered text. + */ + public function render($params = null) + { + $css = htmlspecialchars($this->mime_part->getContents(), ENT_NOQUOTES); + $css = preg_replace_callback('!(}|\*/).*?({|/\*)!s', array($this, '_handles'), $css); + $css = preg_replace_callback('!{[^}]*}!s', array($this, '_attributes'), $css); + $css = preg_replace_callback('!/\*.*?\*/!s', array($this, '_comments'), $css); + $css = trim($css); + + // Educated Guess at whether we are inline or not. + if (headers_sent() || ob_get_length()) { + return $this->lineNumber($css); + } else { + return Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') . + $this->lineNumber($css) . + Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc'); + } + } + + /** + */ + protected function _comments($matches) + { + $patterns[] = '!(http://[/\w-.]+)!s'; + $replaces[] = '\\1'; + + $comments = preg_replace($patterns, $replaces, $matches[0]); + + return '' . $comments . ''; + } + + /** + */ + protected function _attributes($matches) + { + // Attributes. + $patterns[] = '!([-\w]+\s*):!s'; + $replaces[] = '\\1:'; + + // Values. + $patterns[] = '!:(\s*)(.+?)(\s*;)!s'; + $replaces[] = ':\\1\\2\\3'; + + // URLs. + $patterns[] = '!(url\([\'"]?)(.*?)([\'"]?\))!s'; + $replaces[] = '\\1\\2\\3'; + + // Colors. + $patterns[] = '!(#[[:xdigit:]]{3,6})!s'; + $replaces[] = '\\1'; + + // Parentheses. + $patterns[] = '!({|})!s'; + $replaces[] = '\\1'; + + // Unity. + $patterns[] = '!(em|px|%)\b!s'; + $replaces[] = '\\1'; + + return preg_replace($patterns, $replaces, $matches[0]); + } + + /** + */ + protected function _handles($matches) + { + // HTML Tags. + $patterns[] = '!\b(body|h\d|a|span|div|acronym|small|strong|em|pre|ul|ol|li|p)\b!s'; + $replaces[] = '\\1\\2'; + + // IDs. + $patterns[] = '!(#[-\w]+)!s'; + $replaces[] = '\\1'; + + // Class. + $patterns[] = '!(\.[-\w]+)\b!s'; + $replaces[] = '\\1'; + + // METAs. + $patterns[] = '!(:link|:visited|:hover|:active|:first-letter)!s'; + $replaces[] = '\\1'; + + return preg_replace($patterns, $replaces, $matches[0]); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/deb.php b/framework/MIME/lib/Horde/MIME/Viewer/deb.php new file mode 100644 index 000000000..7afdc97d9 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/deb.php @@ -0,0 +1,54 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_deb extends Horde_MIME_Viewer_Driver +{ + /** + * Render the data. + * + * @param array $params Any parameters the viewer may need. + * + * @return string The rendered data. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['deb']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['deb']['location']) . '
'; + } + + $tmp_deb = Horde::getTempFile('horde_deb'); + + $fh = fopen($tmp_deb, 'w'); + fwrite($fh, $this->mime_part->getContents()); + fclose($fh); + + $fh = popen($GLOBALS['mime_drivers']['horde']['deb']['location'] . " -f $tmp_deb 2>&1", 'r'); + while (($rc = fgets($fh, 8192))) { + $data .= $rc; + } + pclose($fh); + + return '
' . htmlspecialchars($data) . '
'; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/default.php b/framework/MIME/lib/Horde/MIME/Viewer/default.php new file mode 100644 index 000000000..4872f75f2 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/default.php @@ -0,0 +1,17 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_default extends Horde_MIME_Viewer_Driver +{ +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/enriched.php b/framework/MIME/lib/Horde/MIME/Viewer/enriched.php new file mode 100644 index 000000000..a46db587f --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/enriched.php @@ -0,0 +1,158 @@ + command and the next balancing + * removes all other formatting commands (all text enclosed + * in angle brackets), and outside of environments converts + * any series of n CRLFs to n-1 CRLFs, and converts any lone CRLF + * pairs to SPACE. + * + * We don't qualify as we don't currently track the + * environment, that is we do CRLF conversion even if is + * specified in the text, but we're close at least. + * + * Copyright 2001-2008 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Eric Rostetter + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_enriched extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the currently set contents in HTML format. The + * $mime_part class variable has the information to render out, + * encapsulated in a MIME_Part object. + */ + public function render() + { + if (($text = $this->mime_part->getContents()) === false) { + return false; + } + + if (trim($text) == '') { + return $text; + } + + // We add space at the beginning and end of the string as it will + // make some regular expression checks later much easier (so we + // don't have to worry about start/end of line characters) + $text = ' ' . $text . ' '; + + // We need to preserve << tags, so map them to ascii 1 or ascii 255 + // We make the assumption here that there would never be an ascii + // 1 in an email, which may not be valid, but seems reasonable... + // ascii 255 would work if for some reason you don't like ascii 1 + // ascii 0 does NOT seem to work for this, though I'm not sure why + $text = str_replace('<<', chr(1), $text); + + // Remove any unrecognized tags in the text (via RFC minimal specs) + // any tags we just don't want to implement can also be removed here + // Note that this will remove any html links, but this is intended + $implementedTags = '' . + '
' . + ''; + // $unImplementedTags = ''; + $text = strip_tags($text, $implementedTags); + + // restore the << tags as < tags now... + $text = str_replace(chr(1), '<<', $text); + // $text = str_replace(chr(255), '<', $text); + + // Get color parameters into a more useable format. + $text = preg_replace('/([\da-fA-F]+),([\da-fA-F]+),([\da-fA-F]+)<\/param>/Uis', '', $text); + $text = preg_replace('/(red|blue|green|yellow|cyan|magenta|black|white)<\/param>/Uis', '', $text); + + // Get font family parameters into a more useable format. + $text = preg_replace('/(\w+)<\/param>/Uis', '', $text); + + // Just remove any remaining parameters -- we won't use + // them. Any tags with parameters that we want to implement + // will have to come before this Someday we hope to use these + // tags (e.g. for tags) + $text = preg_replace('/.*<\/param>/Uis', '', $text); + + // Single line breaks become spaces, double line breaks are a + // real break. This needs to do tracking to be + // compliant but we don't want to deal with state at this + // time, so we fake it some day we should rewrite this to + // handle correctly. + $text = preg_replace('/([^\n])\r\n([^\r])/', '\1 \2', $text); + $text = preg_replace('/(\r\n)\r\n/', '\1', $text); + + // We try to protect against bad stuff here. + $text = @htmlspecialchars($text, ENT_QUOTES, $this->mime_part->getCharset()); + + // Now convert the known tags to html. Try to remove any tag + // parameters to stop people from trying to pull a fast one + $text = preg_replace('/(?\1', $text); + $text = preg_replace('/(?\1', $text); + $text = preg_replace('/(?\1', $text); + $text = preg_replace_callback('/(?\2', $text); + $text = preg_replace('/(?\2', $text); + $text = preg_replace('/(?', $text); + $text = preg_replace('/(?', $text); + $text = preg_replace('/(?', $text); + $text = preg_replace('/(?', $text); + $text = preg_replace('/(?\1', $text); + $text = preg_replace('/(?\1', $text); + $text = preg_replace('/(?\1', $text); + $text = preg_replace('/(?\1', $text); + $text = preg_replace('/(?\1', $text); + $text = preg_replace('/(?\1', $text); + $text = preg_replace('/(?\1', $text); + + // Replace << with < now (from translated HTML form). + $text = str_replace('<<', '<', $text); + + // Now we remove the leading/trailing space we added at the + // start. + $text = preg_replace('/^ (.*) $/s', '\1', $text); + + // Make URLs clickable. + require_once 'Horde/Text/Filter.php'; + $text = Text_Filter::filter($text, 'linkurls', array('callback' => 'Horde::externalUrl')); + + // Wordwrap -- note this could impact on our above RFC + // compliance *IF* we honored nofill tags (which we don't + // yet). + $text = str_replace("\t", ' ', $text); + $text = str_replace(' ', '  ', $text); + $text = str_replace("\n ", "\n ", $text); + if ($text[0] == ' ') { + $text = ' ' . substr($text, 1); + } + $text = nl2br($text); + $text = '

' . $text . '

'; + + return $text; + } + + /** + */ + public function colorize($colors) + { + for ($i = 1; $i < 4; $i++) { + $colors[$i] = sprintf('%02X', round(hexdec($colors[$i]) / 255)); + } + return '' . $colors[4] . ''; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/enscript.php b/framework/MIME/lib/Horde/MIME/Viewer/enscript.php new file mode 100644 index 000000000..7d3edb18b --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/enscript.php @@ -0,0 +1,132 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_enscript extends Horde_MIME_Viewer_source +{ + /** + * Render out the data using Enscript. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered data. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['enscript']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['enscript']['location']) . '
'; + } + + /* Create temporary files for input to Enscript. Note that we + cannot use a pipe, since enscript must have access to the + whole file to determine its type for coloured syntax + highlighting. */ + $tmpin = Horde::getTempFile('EnscriptIn'); + + /* Write the contents of our buffer to the temporary input file. */ + $contents = $this->mime_part->getContents(); + $fh = fopen($tmpin, 'wb'); + fwrite($fh, $contents, strlen($contents)); + fclose($fh); + + /* Execute the enscript command. */ + $lang = escapeshellarg($this->_typeToLang($this->mime_part->getType())); + $results = shell_exec($GLOBALS['mime_drivers']['horde']['enscript']['location'] . " -E$lang --language=html --color --output=- < $tmpin"); + + /* Strip out the extraneous HTML from Enscript, and output it. */ + $res_arr = preg_split('/\<\/?pre\>/i', $results); + if (count($res_arr) == 3) { + $results = trim($res_arr[1]); + } + + /* Educated Guess at whether we are inline or not. */ + if (headers_sent() || ob_get_length()) { + return $this->lineNumber($results); + } else { + return Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') . + $this->lineNumber($results) . + Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc'); + } + } + + /** + * Attempts to determine what language to use for the enscript program + * from a MIME type. + * + * @param string $type The MIME type. + * + * @return string The enscript 'language' parameter string. + */ + protected function _typeToLang($type) + { + include_once dirname(__FILE__) . '/../Magic.php'; + + $ext = Horde_MIME_Magic::MIMEToExt($type); + + switch ($ext) { + case 'cs': + return 'java'; + + case 'el': + return 'elisp'; + + case 'h': + return 'c'; + + case 'C': + case 'H': + case 'cc': + case 'hh': + case 'c++': + case 'cxx': + case 'cpp': + return 'cpp'; + + case 'htm': + case 'shtml': + case 'xml': + return 'html'; + + case 'js': + return 'javascript'; + + case 'pas': + return 'pascal'; + + case 'al': + case 'cgi': + case 'pl': + case 'pm': + return 'perl'; + + case 'ps': + return 'postscript'; + + case 'vb': + return 'vba'; + + case 'vhd': + return 'vhdl'; + + case 'patch': + case 'diff': + return 'diffu'; + + default: + return $ext; + } + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/html.php b/framework/MIME/lib/Horde/MIME/Viewer/html.php new file mode 100644 index 000000000..e4229dab3 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/html.php @@ -0,0 +1,160 @@ + + * @author Jon Parise + * @author Michael Slusarz + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_html extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the currently set contents. + * + * @param array $params Any parameters the viewer may need. + * + * @return string The rendered text. + */ + public function render($params = null) + { + return $this->_cleanHTML($this->mime_part->getContents()); + } + + /** + * Filters active content, dereferences external links, detects phishing, + * etc. + * + * @todo Use IP checks from + * http://lxr.mozilla.org/mailnews/source/mail/base/content/phishingDetector.js. + * + * @param string $data The HTML data. + * + * @return string The cleaned HTML data. + */ + protected function _cleanHTML($data) + { + global $browser, $prefs; + + $inline = $this->viewInline(); + $phish_warn = false; + + /* Deal with tags in the HTML, since they will screw up our own + * relative paths. */ + if (preg_match('/ ]*)"? ?\/?>/i', $data, $matches)) { + $base = $matches[1]; + if (substr($base, -1, 1) != '/') { + $base .= '/'; + } + + /* Recursively call _cleanHTML() to prevent clever fiends from + * sneaking nasty things into the page via $base. */ + $base = $this->_cleanHTML($base); + } + + /* Attempt to fix paths that were relying on a tag. */ + if (!empty($base)) { + $pattern = array('|src=(["\'])([^:"\']+)\1|i', + '|src=([^: >"\']+)|i', + '|href= *(["\'])([^:"\']+)\1|i', + '|href=([^: >"\']+)|i'); + $replace = array('src=\1' . $base . '\2\1', + 'src=' . $base . '\1', + 'href=\1' . $base . '\2\1', + 'href=' . $base . '\1'); + $data = preg_replace($pattern, $replace, $data); + } + + require_once 'Horde/Text/Filter.php'; + $strip_style_attributes = (($browser->isBrowser('mozilla') && + $browser->getMajor() == 4) || + $browser->isBrowser('msie')); + $strip_styles = $inline || $strip_style_attributes; + $data = Text_Filter::filter($data, 'xss', + array('body_only' => $inline, + 'strip_styles' => $strip_styles, + 'strip_style_attributes' => $strip_style_attributes)); + + /* Check for phishing exploits. */ + if ($this->getConfigParam('phishing_check')) { + if (preg_match('/href\s*=\s*["\']?\s*(http|https|ftp):\/\/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:[^>]*>\s*(?:\\1:\/\/)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})[^<]*<\/a)?/i', $data, $m)) { + /* Check 1: Check for IP address links, but ignore if the link + * text has the same IP address. */ + if (!isset($m[3]) || $m[2] != $m[3]) { + if (isset($m[3])) { + $data = preg_replace('/href\s*=\s*["\']?\s*(http|https|ftp):\/\/' . preg_quote($m[2], '/') . '(?:[^>]*>\s*(?:$1:\/\/)?' . preg_quote($m[3], '/') . '[^<]*<\/a)?/i', 'class="mimeStatusWarning" $0', $data); + } + $phish_warn = true; + } + } elseif (preg_match_all('/href\s*=\s*["\']?\s*(?:http|https|ftp):\/\/([^\s"\'>]+)["\']?[^>]*>\s*(?:(?:http|https|ftp):\/\/)?(.*?)<\/a/is', $data, $m)) { + /* $m[1] = Link; $m[2] = Target + * Check 2: Check for links that point to a different host than + * the target url; if target looks like a domain name, check it + * against the link. */ + $links = count($m[0]); + for ($i = 0; $i < $links; $i++) { + $link = strtolower(urldecode($m[1][$i])); + $target = strtolower(preg_replace('/^(http|https|ftp):\/\//', '', strip_tags($m[2][$i]))); + if (preg_match('/^[-._\da-z]+\.[a-z]{2,}/i', $target) && + strpos($link, $target) !== 0 && + strpos($target, $link) !== 0) { + /* Don't consider the link a phishing link if the + * domain is the same on both links (e.g. + * adtracking.example.com & www.example.com). */ + preg_match('/\.?([^\.\/]+\.[^\.\/]+)[\/?]/', $link, $host1); + preg_match('/\.?([^\.\/]+\.[^\.\/ ]+)([\/ ].*)?$/', $target, $host2); + if (!(count($host1) && count($host2)) || + strcasecmp($host1[1], $host2[1]) !== 0) { + $data = preg_replace('/href\s*=\s*["\']?\s*(?:http|https|ftp):\/\/' . preg_quote($m[1][$i], '/') . '["\']?[^>]*>\s*(?:(?:http|https|ftp):\/\/)?' . preg_quote($m[2][$i], '/') . '<\/a/is', 'class="mimeStatusWarning" $0', $data); + $phish_warn = true; + } + } + } + } + } + + /* Try to derefer all external references. */ + $data = preg_replace_callback('/href\s*=\s*(["\'])?((?(1)[^\1]*?|[^\s>]+))(?(1)\1|)/i', + array($this, '_dereferExternalReferencesCallback'), + $data); + + /* Prepend phishing warning. */ + if ($phish_warn) { + $phish_warning = sprintf(_("%s: This message may not be from whom it claims to be. Beware of following any links in it or of providing the sender with any personal information.") . ' ' . _("The links that caused this warning have the same background color as this message."), _("Warning")); + if (!$inline) { + $phish_warning = '' . String::convertCharset($phish_warning, NLS::getCharset(), $this->mime_part->getCharset()) . '
'; + } + $phish_warning = $this->formatStatusMsg($phish_warning, null, 'mimeStatusWarning'); + if (stristr($data, ')(.*)/i', '$1' . $phish_warning . '$2', $data); + } + } + + return $data; + } + + /** + */ + protected function _dereferExternalReferencesCallback($m) + { + return 'href="' . Horde::externalUrl($m[2]) . '"'; + } + + /** + * Return the content-type of the rendered text. + * + * @return string The MIME Content-Type. + */ + public function getType() + { + return $this->viewAsAttachment() ? $this->mime_part->getType(true) : 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/images.php b/framework/MIME/lib/Horde/MIME/Viewer/images.php new file mode 100644 index 000000000..ced92d7b1 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/images.php @@ -0,0 +1,95 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_images extends Horde_MIME_Viewer_Driver +{ + /** + * Return the content-type. + * + * @return string The content-type of the output. + */ + public function getType() + { + $type = $this->mime_part->getType(); + if ($GLOBALS['browser']->isBrowser('mozilla') && + ($type == 'image/pjpeg')) { + /* image/jpeg and image/pjpeg *appear* to be the same + * entity, but Mozilla don't seem to want to accept the + * latter. For our purposes, we will treat them the + * same. */ + return 'image/jpeg'; + } elseif ($type == 'image/x-png') { + /* image/x-png is equivalent to image/png. */ + return 'image/png'; + } else { + return $type; + } + } + + /** + * Generate HTML output for a javascript auto-resize view window. + * + * @param string $url The URL which contains the actual image data. + * @param string $title The title to use for the page. + * + * @return string The HTML output. + */ + protected function _popupImageWindow($url, $title) + { + global $browser; + + $str = << + +$title + +EOD; + + /* Only use javascript if we are using a DOM capable browser. */ + if ($browser->getFeature('dom')) { + /* Translate '&' entities to '&' for JS URL links. */ + $url = str_replace('&', '&', $url); + + /* Javascript display. */ + $loading = _("Loading..."); + $str .= << +function resizeWindow() +{ + + var h, img = document.getElementById('disp_image'), w; + document.getElementById('splash').style.display = 'none'; + img.style.display = 'block'; + window.moveTo(0, 0); + h = img.height - (window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight)); + w = img.width - (window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth)); + window.resizeBy(w, h); + self.focus(); +} + +$loading +EOD; + } else { + /* Non-javascript display. */ + $img_txt = _("Image"); + $str .= << + +$img_txt + + +EOD; + } + + return $str; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/msexcel.php b/framework/MIME/lib/Horde/MIME/Viewer/msexcel.php new file mode 100644 index 000000000..7b82a6417 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/msexcel.php @@ -0,0 +1,55 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_msexcel extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the currently data using xlhtml. + * + * @param array $params Any params this Viewer may need. + * + * @return string The rendered data. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['msexcel']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['msexcel']['location']) . '
'; + } + + $data = ''; + $tmp_xls = Horde::getTempFile('horde_msexcel'); + + $fh = fopen($tmp_xls, 'w'); + fwrite($fh, $this->mime_part->getContents()); + fclose($fh); + + $fh = popen($GLOBALS['mime_drivers']['horde']['msexcel']['location'] . " -nh $tmp_xls 2>&1", 'r'); + while (($rc = fgets($fh, 8192))) { + $data .= $rc; + } + pclose($fh); + + return $data; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/mspowerpoint.php b/framework/MIME/lib/Horde/MIME/Viewer/mspowerpoint.php new file mode 100644 index 000000000..6bef4dec6 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/mspowerpoint.php @@ -0,0 +1,55 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_mspowerpoint extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the current data using ppthtml. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['mspowerpoint']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['mspowerpoint']['location']) . '
'; + } + + $data = ''; + $tmp_ppt = Horde::getTempFile('horde_mspowerpoint'); + + $fh = fopen($tmp_ppt, 'w'); + fwrite($fh, $this->mime_part->getContents()); + fclose($fh); + + $fh = popen($GLOBALS['mime_drivers']['horde']['mspowerpoint']['location'] . " $tmp_ppt 2>&1", 'r'); + while (($rc = fgets($fh, 8192))) { + $data .= $rc; + } + pclose($fh); + + return $data; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/msword.php b/framework/MIME/lib/Horde/MIME/Viewer/msword.php new file mode 100644 index 000000000..7892fb2dd --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/msword.php @@ -0,0 +1,69 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_msword extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the current data using wvWare. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['msword']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['msword']['location']) . '
'; + } + + $data = ''; + $tmp_word = Horde::getTempFile('msword'); + $tmp_output = Horde::getTempFile('msword'); + $tmp_dir = Horde::getTempDir(); + $tmp_file = str_replace($tmp_dir . '/', '', $tmp_output); + + if (OS_WINDOWS) { + $args = ' -x ' . dirname($GLOBALS['mime_drivers']['horde']['msword']['location']) . "\\wvHtml.xml -d $tmp_dir -1 $tmp_word > $tmp_output"; + } else { + $version = exec($GLOBALS['mime_drivers']['horde']['msword']['location'] . ' --version'); + if (version_compare($version, '0.7.0') >= 0) { + $args = " --charset=" . NLS::getCharset() . " --targetdir=$tmp_dir $tmp_word $tmp_file"; + } else { + $args = " $tmp_word $tmp_output"; + } + } + + $fh = fopen($tmp_word, 'w'); + fwrite($fh, $this->mime_part->getContents()); + fclose($fh); + + exec($GLOBALS['mime_drivers']['horde']['msword']['location'] . $args); + + if (!file_exists($tmp_output)) { + return _("Unable to translate this Word document"); + } + + return file_get_contents($tmp_output); + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo.php b/framework/MIME/lib/Horde/MIME/Viewer/ooo.php new file mode 100644 index 000000000..4c82e2de4 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo.php @@ -0,0 +1,88 @@ + + * @author Jan Schneider + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_ooo extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the current data. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + $use_xslt = Util::extensionExists('xslt') || function_exists('domxml_xslt_stylesheet_file'); + if ($use_xslt) { + $tmpdir = Util::createTempDir(true); + } + + require_once 'Horde/Compress.php'; + $xml_tags = array('text:p', 'table:table ', 'table:table-row', 'table:table-cell', 'table:number-columns-spanned='); + $html_tags = array('p', 'table border="0" cellspacing="1" cellpadding="0" ', 'tr bgcolor="#cccccc"', 'td', 'colspan='); + $zip = &Horde_Compress::singleton('zip'); + $list = $zip->decompress($this->mime_part->getContents(), + array('action' => HORDE_COMPRESS_ZIP_LIST)); + foreach ($list as $key => $file) { + if ($file['name'] == 'content.xml' || + $file['name'] == 'styles.xml' || + $file['name'] == 'meta.xml') { + $content = $zip->decompress($this->mime_part->getContents(), + array('action' => HORDE_COMPRESS_ZIP_DATA, + 'info' => $list, + 'key' => $key)); + if ($use_xslt) { + $fp = fopen($tmpdir . $file['name'], 'w'); + fwrite($fp, $content); + fclose($fp); + } elseif ($file['name'] == 'content.xml') { + $content = str_replace($xml_tags, $html_tags, $content); + return $content; + } + } + } + if (!Util::extensionExists('xslt')) { + return; + } + + if (function_exists('domxml_xslt_stylesheet_file')) { + // Use DOMXML + $xslt = domxml_xslt_stylesheet_file(dirname(__FILE__) . '/ooo/main_html.xsl'); + $dom = domxml_open_file($tmpdir . 'content.xml'); + $result = @$xslt->process($dom, array('metaFileURL' => $tmpdir . 'meta.xml', 'stylesFileURL' => $tmpdir . 'styles.xml', 'disableJava' => true)); + return String::convertCharset($xslt->result_dump_mem($result), 'UTF-8', NLS::getCharset()); + } else { + // Use XSLT + $xslt = xslt_create(); + $result = @xslt_process($xslt, $tmpdir . 'content.xml', + dirname(__FILE__) . '/ooo/main_html.xsl', null, null, + array('metaFileURL' => $tmpdir . 'meta.xml', 'stylesFileURL' => $tmpdir . 'styles.xml', 'disableJava' => true)); + if (!$result) { + $result = xslt_error($xslt); + } + xslt_free($xslt); + return String::convertCharset($result, 'UTF-8', NLS::getCharset()); + } + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/common.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/common.xsl new file mode 100644 index 000000000..943a5b995 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/common.xsl @@ -0,0 +1,1165 @@ + + + + + + + + + + + + + + + + + + = + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + height: ; + + + width: ; + + + height: ; + width: ; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + margin-left:; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +     + + + + + + + + + + + + + + + + + + + + + + + +     + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +     + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + * + + + + + + + + + + + + * + * + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/global_document.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/global_document.xsl new file mode 100644 index 000000000..fc4579ef3 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/global_document.xsl @@ -0,0 +1,1674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Matching child document header No. + absolute-chapter-level: + encodedTitle: + globalDocumentRefToCurrentFile: + *** + + + + + + + + + + + + + + + + + + + + + + + + + Matching global document header No. + absolute-chapter-level: + encodedTitle: + contentTableURL: + *** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creation of global document helper variable for the content table.... + + + + + + + + + + + + + + + + + Finished the Creation of global document helper variable for the content table! + + + + + Creation of global document helper variable for the child documents.... + + + + + + Finished the Creation of global document helper variable for the child documents! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # + level: + title: + encoded-title: + file-url: + header-no: + ** + + ** + ** + + + childrenHeadings/heading-count: + + # + title: + ** + + + + + + + + + + + + + + + + + + Creating global document variable for chapter relations.... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Finished global document variable for chapter relations! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *** new heading + currentChapterID: + currentChapterTitle: + currentChapterID: + currentHeadingNo: + headingTitle: + headingLevel: + headingNo: + newChildURL: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + only a heading, but not a chapter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All child documents have been walked through without finding the chapter name! + childrenHeadings/heading-count: + currentHeadingNo: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Parsing the global document... + + + + + + + + Parsing the child documents... + + + + + + + + + + + + + Starting the child transformations... + + + + + + Contentable data exists as global data! + + + No Contentable global data exists! + + + + + + + + + + + + + Java method transformChildren to transform all children of a global document could not be found. Be sure to use the XT processor. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Previous document + + + | + + + + + + + + + + + + + + + + + + # + + + + + + Content Table + + + + + + | + + + + + + + + + + + + + + + + + + + + + Next document + + + ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + **** THE HEADING VARIABLE **** + content-table-url: + + + **** new heading: + content-table-id: + child-document-no: + file-url: + out-file-url: + level: + title: + encoded-title: + absolute-chapter-level: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + width: + + + + + + + + + + + + + + + + + + + + + + + + + + + + align: right + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/main_html.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/main_html.xsl new file mode 100644 index 000000000..443182a3e --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/main_html.xsl @@ -0,0 +1,462 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CSS helper variable will be created.... + + CSS variable ready, header will be created.... + + + CSS header creation finished! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + description + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creating the inline styles.... + + + + + Time for instantiating style variable: ms + + + + + + Creating the inline styles.... + + + + + Time for instantiating style variable: ms + + + + + + + + + + + Parameter dpi: + Parameter metaFileURL: + Parameter stylesFileURL: + Parameter absoluteSourceDirRef: + Parameter precedingChapterLevel1 : + Parameter precedingChapterLevel2 : + Parameter precedingChapterLevel3 : + Parameter precedingChapterLevel4 : + Parameter precedingChapterLevel5 : + Parameter precedingChapterLevel6 : + Parameter precedingChapterLevel7 : + Parameter precedingChapterLevel8 : + Parameter precedingChapterLevel9 : + Parameter precedingChapterLevel10: + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/palm.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/palm.xsl new file mode 100644 index 000000000..212edb167 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/palm.xsl @@ -0,0 +1,404 @@ + + + + + + + + + + + + + PalmComputingPlatform + true + + + HandheldFriendly + true + + + HistoryListText + Dateimanager : &date &time + + + description + StarPortal + + + keywords + starportal, staroffice, software + + + Content-Type + text/html; charset=iso-8859-1 + + + + + + + + + + + + + + + + + left + + + right + + + center + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + left + + + right + + + center + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #000000 + + + #FFFFFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #000000 + + + #FFFFFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_header.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_header.xsl new file mode 100644 index 000000000..eeb0c204a --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_header.xsl @@ -0,0 +1,379 @@ + + + + + + + + + + + + + The CSS style header method for setting styles + + + text/css + + + + + + + + + + + + + + + + + + + + + + + + + + + // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *.OOo_defaults + + + , + + + + + , + + + + { + margin-top:0cm; margin-bottom:0cm; } + + + + + + + + + + + + + + + + + + + + + + , + + + + + , + + + + + + + + + + + + + + { + + + } + + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_inlined.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_inlined.xsl new file mode 100644 index 000000000..19159958c --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_inlined.xsl @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_mapping.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_mapping.xsl new file mode 100644 index 000000000..a9a858dc0 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/style_mapping.xsl @@ -0,0 +1,660 @@ + + + + + + + + + + + + + + + + + + + + + + + + float: right; + + + float: left; + + + + + + + + align: left; + + + align: right; + + + align: center; + + + + + + + + padding: + + ; + + + + + + + + + border-width:; + border-style:; + border-color:; + + + border-width:; + border-style:; + border-color:; + + + border-width:; + border-style:; + border-color:; + + + + + border-top: ; + + + border-bottom: ; + + + border-left: ; + + + border-right: ; + + + + + + width:; + + + width:; + + + + + + + + height:; + + + height:; + + + + + + + + width:; + + + width:; + + + + + + :; + + + font-family: + + + ; + + font-style:italic; + + + font-weight:bold; + + + + :; + + + :; + + + :; + + + :; + + + :; + + + :; + + + :; + + + :; + + + :; + + + + + + text-align:left ! important; + + + text-align:right ! important; + + + text-align: ! important; + + + + + :; + + + background-color:; + + + background-color:; + + + background-image:url(); + + + background-repeat:repeat; + + + background-repeat:no-repeat; + + + + + + :; + + + + text-decoration:line-through; + + + + + text-decoration:underline; + + + + + vertical-align:sub; + + + vertical-align:sup; + + + + + + + + + + + + + + + + + + + italic, + + + + + + + bold, + + + + + + underline, + + + + + + + align:left, + + + align:right, + + + align:center, + + + + + + + strike, + + + + + size::size, + + + + + + + color:#FFFFFF, + + + color:#000000, + + + + + + + + size::size, + + + + + + width::width, + + + width::width; + + + + + + + + height::height; + + + height::height; + + + + + + + + width::width; + + + width::width; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/table.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/table.xsl new file mode 100644 index 000000000..36339ed73 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/table.xsl @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + left + + + right + + + center + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + maxRowLength: + + numberOfHiddenColumns: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + #000000 + 2 + 0 + page-break-inside:avoid + + + + + + + + + + + + + + + + Time for checking BorderStyle: ms + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_cells.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_cells.xsl new file mode 100644 index 000000000..4671ea96f --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_cells.xsl @@ -0,0 +1,484 @@ + + + + + + + + + + + + + + + + + + +--------------> table:table-cell has been entered with node value: + table:number-columns-repeated: -- + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NEW VALUE: column-position: -- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +++++++++ starting cell writing +++++++++ + number-columns-repeated: -- + maxRowLength: -- + column-position: -- + + + + + + + + + + + + +++++++++ cell repetition +++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WriteTest -> If nothing between '-' write cell -- + + + + + TABLE COLUMN is hidden! + + + + + + + + TABLE COLUMN is hidden! + + + + + + + + + + + th + + + td + + + + + + + + + + + +*****************************************'' element has been added! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text-align:right; + text-align:left; + + + + + + + + + + + + + + + + + + + + text-align:right; + + + text-align:left; + + + + + + + + + +   + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text-align:right; + + + text-align:left; + + + + + +   + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + ; + + + + + + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_columns.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_columns.xsl new file mode 100644 index 000000000..a9a907ff8 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_columns.xsl @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DebugInformation: For each 'column-style-entry' of the 'allColumnStyleEntries' variable the style-name is given out. + In case of 'column-hidden-flag' attribute the text 'column is hidden' is given out. + + + + + column is hidden + + + + = + + + + + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_rows.xsl b/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_rows.xsl new file mode 100644 index 000000000..6f7d17d62 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/ooo/table_rows.xsl @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*************************'tr' element has been added! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/MIME/lib/Horde/MIME/Viewer/pdf.php b/framework/MIME/lib/Horde/MIME/Viewer/pdf.php new file mode 100644 index 000000000..40e46eca7 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/pdf.php @@ -0,0 +1,26 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_pdf extends Horde_MIME_Viewer_Driver +{ + /** + * Return the content-type. + * + * @return string The content-type of the output. + */ + public function getType() + { + return 'application/pdf'; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/php.php b/framework/MIME/lib/Horde/MIME/Viewer/php.php new file mode 100644 index 000000000..0c0095487 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/php.php @@ -0,0 +1,138 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_php extends Horde_MIME_Viewer_source +{ + /** + * Renders out the contents. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + ini_set('highlight.comment', 'comment'); + ini_set('highlight.default', 'default'); + ini_set('highlight.keyword', 'keyword'); + ini_set('highlight.string', 'string'); + ini_set('highlight.html', 'html'); + + $code = $this->mime_part->getContents(); + if (strpos($code, 'lineNumber(str_replace('<?php ', '', highlight_string('lineNumber(highlight_string($code, true)); + } + + // Educated guess at whether we are inline or not. + if (headers_sent() || ob_get_length()) { + return $results; + } else { + return Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') + . $results + . Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc'); + } + } + + /** + * Add line numbers to a block of code. + * + * @param string $code The code to number. + */ + public function lineNumber($code, $linebreak = "\n") + { + // Clean up. + $code = preg_replace(array('/\s*/', + '/\s*/', + '/\s*<\/span>\s*<\/span>\s*<\/code>/', + '/\s*<\/font>\s*<\/font>\s*<\/code>/'), + '', + $code); + $code = str_replace(array(' ', + '&', + '
', + '', + ), + array(' ', + '&', + "\n", + '', + ), + $code); + $code = trim($code); + + // Normalize newlines. + $code = str_replace("\r", '', $code); + $code = preg_replace('/\n\n\n+/', "\n\n", $code); + + $lines = explode("\n", $code); + + $results = array('
    '); + $previous = false; + foreach ($lines as $lineno => $line) { + if (substr($line, 0, 7) == '') { + $previous = false; + $line = substr($line, 7); + } + + if (empty($line)) { + $line = ' '; + } + + if ($previous) { + $line = "" . $line; + } + + // Save the previous style. + if (strpos($line, '') { + $previous = false; + } elseif ($previous) { + $line .= ''; + } + + $results[] = '
  1. ' . $line . '
  2. '; + } + + $results[] = '
'; + return implode("\n", $results); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/plain.php b/framework/MIME/lib/Horde/MIME/Viewer/plain.php new file mode 100644 index 000000000..23c0aa896 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/plain.php @@ -0,0 +1,81 @@ + + * @author Michael Slusarz + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_plain extends Horde_MIME_Viewer_Driver +{ + /** + * Constructor. + * + * @param array $conf Configuration specific to the driver. + */ + function __construct($conf = array()) + { + $this->_canrender['full'] = true; + $this->_canrender['inline'] = true; + $this->_type = 'text/html; charset=' . NLS::getCharset(); + parent::__construct($conf); + } + + /** + * Render out the contents. + * + * @return string The rendered contents. + */ + protected function _render() + { + $text = $this->_mimepart->getContents(); + + /* Check for 'flowed' text data. */ + if ($this->_mimepart->getContentTypeParameter('format') == 'flowed') { + $text = $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp')); + } + + require_once 'Horde/Text/Filter.php'; + return '' . Text_Filter::filter($text, 'text2html', array('parselevel' => TEXT_HTML_MICRO, 'charset' => NLS::getCharset(), 'class' => null)) . ''; + } + + /** + * Render out the contents. + * + * @return string The rendered contents. + */ + protected function _renderInline() + { + $text = $this->_mimepart->getContents(); + + /* Check for 'flowed' text data. */ + return ($this->_mimepart->getContentTypeParameter('format') == 'flowed') + ? $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp')) + : $text; + } + + /** + * Format flowed text for HTML output. + * + * @param string $text The text to format. + * @param boolean $delsp Was text created with DelSp formatting? + * + * @return string The formatted text. + */ + protected function _formatFlowed($text, $delsp = null) + { + require_once 'Text/Flowed.php'; + $flowed = new Text_Flowed($this->_mimepart->replaceEOL($text, "\n"), $this->_mimepart->getCharset()); + $flowed->setMaxLength(0); + if (!is_null($delsp)) { + $flowed->setDelSp($delsp); + } + return $flowed->toFixed(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/rar.php b/framework/MIME/lib/Horde/MIME/Viewer/rar.php new file mode 100644 index 000000000..5fe863aa1 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/rar.php @@ -0,0 +1,197 @@ + + * @author Michael Cochrane + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_rar extends Horde_MIME_Viewer_Driver +{ + /** + * Rar compression methods. + * + * @var array + */ + protected $_methods = array( + 0x30 => 'Store', + 0x31 => 'Fastest', + 0x32 => 'Fast', + 0x33 => 'Normal', + 0x34 => 'Good', + 0x35 => 'Best' + ); + + /** + * Render out the currently set contents using rar. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + $contents = $this->mime_part->getContents(); + + /* Make sure this is a valid rar file. */ + if ($this->checkRarData($contents) === false) { + return '
' . _("This does not appear to be a valid rar archive.") . '
'; + } + + require_once 'Horde/Text.php'; + + $rarData = $this->getRarData($contents); + $fileCount = count($rarData); + + $text = '' . htmlspecialchars(sprintf(_("Contents of \"%s\""), $this->mime_part->getName())) . ':' . "\n"; + $text .= '
'; + $text .= Text::htmlAllSpaces(_("Archive Name") . ': ' . $this->mime_part->getName()) . "\n"; + $text .= Text::htmlAllSpaces(_("Archive File Size") . ': ' . strlen($contents) . ' bytes') . "\n"; + $text .= Text::htmlAllSpaces(sprintf(ngettext("File Count: %d file", "File Count: %d files", $fileCount), $fileCount)); + $text .= "\n\n"; + $text .= Text::htmlAllSpaces( + String::pad(_("File Name"), 50, ' ', STR_PAD_RIGHT) . + String::pad(_("Attributes"), 10, ' ', STR_PAD_LEFT) . + String::pad(_("Size"), 10, ' ', STR_PAD_LEFT) . + String::pad(_("Modified Date"), 19, ' ', STR_PAD_LEFT) . + String::pad(_("Method"), 10, ' ', STR_PAD_LEFT) . + String::pad(_("Ratio"), 10, ' ', STR_PAD_LEFT) + ) . "\n"; + + $text .= str_repeat('-', 109) . "\n"; + + foreach ($rarData as $val) { + $ratio = (empty($val['size'])) ? 0 : 100 * ($val['csize'] / $val['size']); + $text .= Text::htmlAllSpaces( + String::pad($val['name'], 50, ' ', STR_PAD_RIGHT) . + String::pad($val['attr'], 10, ' ', STR_PAD_LEFT) . + String::pad($val['size'], 10, ' ', STR_PAD_LEFT) . + String::pad(strftime("%d-%b-%Y %H:%M", $val['date']), 19, ' ', STR_PAD_LEFT) . + String::pad($val['method'], 10, ' ', STR_PAD_LEFT) . + String::pad(sprintf("%1.1f%%", $ratio), 10, ' ', STR_PAD_LEFT) + ) . "\n"; + } + + $text .= str_repeat('-', 106) . "\n"; + $text .= '
'; + + return nl2br($text); + } + + /** + * Returns the MIME type of this part. + * + * @return string The MIME type of this part. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } + + /** + * Checks to see if the data is a valid Rar archive. + * + * @param string &$data The rar archive data. + * + * @return boolean True if valid, false if invalid. + */ + public function checkRarData(&$data) + { + $fileHeader = "\x52\x61\x72\x21\x1a\x07\x00"; + if (strpos($data, $fileHeader) === false) { + return false; + } else { + return true; + } + } + + /** + * Get the list of files/data from the rar archive. + * + * @param string &$data The rar archive data. + * + * @return array KEY: Position in RAR archive + * VALUES: 'attr' -- File attributes + * 'date' -- File modification time + * 'csize' -- Compressed file size + * 'method' -- Compression method + * 'name' -- Filename + * 'size' -- Original file size + */ + public function getRarData(&$data) + { + $return_array = array(); + + $blockStart = strpos($data, "\x52\x61\x72\x21\x1a\x07\x00"); + $position = $blockStart + 7; + + while ($position < strlen($data)) { + $head_crc = substr($data, $position + 0, 2); + $head_type = ord(substr($data, $position + 2, 1)); + $head_flags = unpack('vFlags', substr($data, $position + 3, 2)); + $head_flags = $head_flags['Flags']; + $head_size = unpack('vSize', substr($data, $position + 5, 2)); + $head_size = $head_size['Size']; + + $position += 7; + $head_size -= 7; + + switch ($head_type) { + + case 0x73: + /* Archive header */ + $position += $head_size; + + break; + + case 0x74: + $file = array(); + + /* File Header */ + $info = unpack('VPacked/VUnpacked/COS/VCRC32/VTime/CVersion/CMethod/vLength/vAttrib', substr($data, $position)); + + $file['name'] = substr($data, $position + 25, $info['Length']); + $file['size'] = $info['Unpacked']; + $file['csize'] = $info['Packed']; + + $file['date'] = mktime((($info['Time'] >> 11) & 0x1f), + (($info['Time'] >> 5) & 0x3f), + (($info['Time'] << 1) & 0x3e), + (($info['Time'] >> 21) & 0x07), + (($info['Time'] >> 16) & 0x1f), + ((($info['Time'] >> 25) & 0x7f) + 80)); + + $file['method'] = $this->_methods[$info['Method']]; + + $file['attr'] = ''; + $file['attr'] .= ($info['Attrib'] & 0x10) ? 'D' : '-'; + $file['attr'] .= ($info['Attrib'] & 0x20) ? 'A' : '-'; + $file['attr'] .= ($info['Attrib'] & 0x03) ? 'S' : '-'; + $file['attr'] .= ($info['Attrib'] & 0x02) ? 'H' : '-'; + $file['attr'] .= ($info['Attrib'] & 0x01) ? 'R' : '-'; + + $return_array[] = $file; + + $position += $head_size; + $position += $info['Packed']; + break; + + default: + $position += $head_size; + if (isset($add_size)) { + $position += $add_size; + } + break; + + } + } + + return $return_array; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/report.php b/framework/MIME/lib/Horde/MIME/Viewer/report.php new file mode 100644 index 000000000..89197e62e --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/report.php @@ -0,0 +1,89 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_report extends Horde_MIME_Viewer_Driver +{ + /** + * Stores the Horde_MIME_Viewer of the specified protocol. + * + * @var Horde_MIME_Viewer + */ + protected $_viewer; + + /** + * Render the multipart/report data. + * + * @param array $params An array of parameters needed. + * + * @return string The rendered data. + */ + public function render($params = array()) + { + /* Get the appropriate Horde_MIME_Viewer for the protocol specified. */ + if (!($this->_resolveViewer())) { + return; + } + + /* Render using the loaded Horde_MIME_Viewer object. */ + return $this->_viewer->render($params); + } + + /** + * Returns the content-type of the Viewer used to view the part. + * + * @return string A content-type string. + */ + public function getType() + { + /* Get the appropriate Horde_MIME_Viewer for the protocol specified. */ + if (!($this->_resolveViewer())) { + return 'application/octet-stream'; + } else { + return $this->_viewer->getType(); + } + } + + /** + * Load a Horde_MIME_Viewer according to the report-type parameter stored + * in the MIME_Part to render. If unsuccessful, try to load a generic + * multipart Horde_MIME_Viewer. + * + * @return boolean True on success, false on failure. + */ + protected function _resolveViewer() + { + $type = $viewer = null; + + if (empty($this->_viewer)) { + if (($type = $this->mime_part->getContentTypeParameter('report-type'))) { + $viewer = &Horde_MIME_Viewer::factory($this->mime_part, 'message/' . String::lower($type)); + $type = $this->mime_part->getPrimaryType(); + } else { + /* If report-type is missing, the message is an improper + * multipart/report message. Attempt to fall back to a + * multipart/mixed viewer instead. */ + $type = 'multipart'; + } + + if (empty($viewer) || + (String::lower(get_class($viewer)) == 'mime_viewer_default')) { + if (!($viewer = &Horde_MIME_Viewer::factory($this->mime_part, $type . '/*'))) { + return false; + } + } + $this->_viewer = $viewer; + } + + return true; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/rfc822.php b/framework/MIME/lib/Horde/MIME/Viewer/rfc822.php new file mode 100644 index 000000000..b8a10be20 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/rfc822.php @@ -0,0 +1,98 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_rfc822 extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the currently set contents. + * + * @param array $params An array with any parameters needed. + * + * @return string The rendered text. + */ + public function render($params = array()) + { + if (!$this->mime_part) { + return $this->formatStatusMsg(_("There was an error displaying this message part")); + } + + $part = &Util::cloneObject($this->mime_part); + $part->transferDecodeContents(); + $text = $part->getContents(); + + return $text + ? $text + : $this->formatStatusMsg(_("There was an error displaying this message part")); + } + + /** + * Render out attachment information. + * + * @param array $params An array with any parameters needed. + * + * @return string The rendered text in HTML. + */ + public function renderAttachmentInfo($params = array()) + { + if (!$this->mime_part) { + return ''; + } + + /* Get the text of the part. Since we need to look for the end of + * the headers by searching for the CRLFCRLF sequence, use + * getCanonicalContents() to make sure we are getting the text with + * CRLF's. */ + $text = $this->mime_part->getCanonicalContents(); + if (empty($text)) { + return ''; + } + + /* Search for the end of the header text (CRLFCRLF). */ + $text = substr($text, 0, strpos($text, "\r\n\r\n")); + + /* Get the list of headers now. */ + require_once 'Horde/MIME/Headers.php'; + $headers = Horde_MIME_Headers::parseHeaders($text); + + $header_array = array( + 'date' => _("Date"), + 'from' => _("From"), + 'to' => _("To"), + 'cc' => _("Cc"), + 'bcc' => _("Bcc"), + 'reply-to' => _("Reply-To"), + 'subject' => _("Subject") + ); + $header_output = array(); + + foreach ($header_array as $key => $val) { + $hdr = $headers->getValue($key); + if (!empty($hdr)) { + $header_output[] = '' . $val . ': ' . htmlspecialchars($hdr); + } + } + + require_once 'Horde/Text/Filter.php'; + return '
' . Text_Filter::filter(implode("
\n", $header_output), 'emails') . '
'; + } + + /** + * Return the MIME content type for the rendered data. + * + * @return string The content type of the data. + */ + public function getType() + { + return 'text/plain; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/richtext.php b/framework/MIME/lib/Horde/MIME/Viewer/richtext.php new file mode 100644 index 000000000..e8c696565 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/richtext.php @@ -0,0 +1,152 @@ +" to + * "<", converts CRLFs to SPACE, converts to a newline according to + * local newline convention, removes everything between a command + * and the next balancing command, and removes all other + * formatting commands (all text enclosed in angle brackets). + * + * We implement the following tags: + * , , , , , ,
, + * , , , , , , + * , , , , + * + * The following tags are implemented differently than described in the RFC + * (due to limitations in HTML output): + * - Output as centered, bold text. + * - Output as centered, bold text. + * - Output as paragraph break. + * + * The following tags are NOT implemented: + * , , , , , + * , + * + * Copyright 2004-2008 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Michael Slusarz + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_richtext extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the currently set contents in HTML format. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + if (($text = $this->mime_part->getContents()) === false) { + return false; + } + + if (trim($text) == '') { + return $text; + } + + /* Use str_ireplace() if using PHP 5.0+. */ + $has_str_ireplace = function_exists('str_ireplace'); + + /* We add space at the beginning and end of the string as it will + * make some regular expression checks later much easier (so we + * don't have to worry about start/end of line characters). */ + $text = ' ' . $text . ' '; + + /* Remove everything between tags. */ + $text = preg_replace('/.*<\/comment>/Uis', '', $text); + + /* Remove any unrecognized tags in the text. We don't need + * in $tags since it doesn't do anything anyway. All tags + * have already been removed. */ + $tags = '
'; + $text = strip_tags($text, $tags); + + /* becomes a '<'. CRLF becomes a SPACE. */ + if ($has_str_ireplace) { + $text = str_ireplace(array('', "\r\n"), array('<', ' '), $text); + } else { + $text = preg_replace(array('//i', "/\r\n/"), array('<', ' '), $text); + } + + /* We try to protect against bad stuff here. */ + $text = @htmlspecialchars($text, ENT_QUOTES, $this->mime_part->getCharset()); + + /* becomes a newline (
); + * becomes a paragraph break (

). */ + if ($has_str_ireplace) { + $text = str_ireplace(array('<nl>', '<np>'), array('
', '

'), $text); + } else { + $text = preg_replace(array('/(?', '

'), $text); + } + + /* Now convert the known tags to html. Try to remove any tag + * parameters to stop people from trying to pull a fast one. */ + $pattern = array( + '/(?\1', + '\1', + '\1', + '\1', + '\1', + '\1', + '

\1
', + '
\1
', + '
\1
', + '
\1
', + '\1', + '\1', + '\1', + '
\1

', + '
\1

', + '

\1

', + '
\1
', + ); + $text = preg_replace($pattern, $replace, $text); + + /* Now we remove the leading/trailing space we added at the start. */ + $text = substr($text, 1, -1); + + /* Wordwrap. */ + $text = str_replace(array("\t", ' ', "\n "), array(' ', '  ', "\n "), $text); + if ($text[0] == ' ') { + $text = ' ' . substr($text, 1); + } + + return '

' . nl2br($text) . '

'; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/rpm.php b/framework/MIME/lib/Horde/MIME/Viewer/rpm.php new file mode 100644 index 000000000..5c12665b6 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/rpm.php @@ -0,0 +1,55 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_rpm extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the RPM contents. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['rpm']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['rpm']['location']) . '
'; + } + + $data = ''; + $tmp_rpm = Horde::getTempFile('horde_rpm'); + + $fh = fopen($tmp_rpm, 'w'); + fwrite($fh, $this->mime_part->getContents()); + fclose($fh); + + $fh = popen($GLOBALS['mime_drivers']['horde']['rpm']['location'] . " -qip $tmp_rpm 2>&1", 'r'); + while (($rc = fgets($fh, 8192))) { + $data .= $rc; + } + pclose($fh); + + return '
' . htmlentities($data) . '
'; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/rtf.php b/framework/MIME/lib/Horde/MIME/Viewer/rtf.php new file mode 100644 index 000000000..e48e63466 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/rtf.php @@ -0,0 +1,57 @@ + + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Duck + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_rtf extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the current data using UnRTF. + * + * @param array $params Any parameters the viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['rtf']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['rtf']['location']) . '
'; + } + + $tmp_rtf = Horde::getTempFile('rtf'); + $tmp_output = Horde::getTempFile('rtf'); + $args = " $tmp_rtf > $tmp_output"; + + $fh = fopen($tmp_rtf, 'w'); + fwrite($fh, $this->mime_part->getContents()); + fclose($fh); + + exec($GLOBALS['mime_drivers']['horde']['rtf']['location'] . $args); + + if (!file_exists($tmp_output)) { + return _("Unable to translate this RTF document"); + } + + return file_get_contents($tmp_output); + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/security.php b/framework/MIME/lib/Horde/MIME/Viewer/security.php new file mode 100644 index 000000000..480b6e441 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/security.php @@ -0,0 +1,88 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_security extends Horde_MIME_Viewer_Driver +{ + /** + * Stores the Horde_MIME_Viewer of the specified security protocol. + * + * @var Horde_MIME_Viewer + */ + protected $_viewer; + + /** + * The $mime_part class variable has the information to render + * out, encapsulated in a Horde_MIME_Part object. + * + * @param $params mixed The parameters (if any) to pass to the underlying + * Horde_MIME_Viewer. + * + * @return string Rendering of the content. + */ + public function render($params = array()) + { + /* Get the appropriate Horde_MIME_Viewer for the protocol specified. */ + if (!($this->_resolveViewer())) { + return; + } + + /* Render using the loaded Horde_MIME_Viewer object. */ + return $this->_viewer->render($params); + } + + /** + * Returns the content-type of the Viewer used to view the part. + * + * @return string A content-type string. + */ + public function getType() + { + /* Get the appropriate Horde_MIME_Viewer for the protocol specified. */ + if (!($this->_resolveViewer())) { + return 'application/octet-stream'; + } else { + return $this->_viewer->getType(); + } + } + + /** + * Load a Horde_MIME_Viewer according to the protocol parameter stored + * in the Horde_MIME_Part to render. If unsuccessful, try to load a generic + * multipart Horde_MIME_Viewer. + * + * @return boolean True on success, false on failure. + */ + protected function _resolveViewer() + { + $viewer = null; + + if (empty($this->_viewer)) { + $protocol = $this->mime_part->getContentTypeParameter('protocol'); + if (empty($protocol)) { + return false; + } + $viewer = &Horde_MIME_Viewer::factory($this->mime_part, $protocol); + if (empty($viewer) || + (String::lower(get_class($viewer)) == 'mime_viewer_default')) { + $viewer = &Horde_MIME_Viewer::factory($this->mime_part, $this->mime_part->getPrimaryType() . '/*'); + if (empty($viewer)) { + return false; + } + } + $this->_viewer = $viewer; + } + + return true; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/simple.php b/framework/MIME/lib/Horde/MIME/Viewer/simple.php new file mode 100644 index 000000000..d2eecd342 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/simple.php @@ -0,0 +1,25 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_simple extends Horde_MIME_Viewer_Driver +{ + /** + * Return the MIME type of the rendered content. + * + * @return string MIME-type of the output content. + */ + public function getType() + { + return 'text/plain'; + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/smil.php b/framework/MIME/lib/Horde/MIME/Viewer/smil.php new file mode 100644 index 000000000..8f521a481 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/smil.php @@ -0,0 +1,98 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_smil extends Horde_MIME_Viewer_Driver +{ + /** + * Handle for the XML parser object. + * + * @var resource + */ + protected $_parser; + + /** + * String buffer to hold the generated content + * + * @var string + */ + protected $_content = ''; + + /** + * Renders out the contents. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + /* Create a new parser and set its default properties. */ + $this->_parser = xml_parser_create(); + xml_set_object($this->_parser, $this); + xml_set_element_handler($this->_parser, '_startElement', '_endElement'); + xml_set_character_data_handler($this->_parser, '_defaultHandler'); + xml_parse($this->_parser, $this->mime_part->getContents(), true); + return $this->_content; + } + + /** + * 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'])) { + $this->_content .= ''; + } + break; + } + } + + /** + * User-defined function callback for end elements. + * + * @param object $parser Handle to the parser instance. + * @param string $name The name of this XML element. + */ + protected function _endElement($parser, $name) + { + } + + /** + * User-defined function callback for character data. + * + * @param object $parser Handle to the parser instance. + * @param string $data String of character data. + */ + protected function _defaultHandler($parser, $data) + { + $data = trim($data); + if (!empty($data)) { + $this->_content .= ' ' . htmlspecialchars($data); + } + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/source.php b/framework/MIME/lib/Horde/MIME/Viewer/source.php new file mode 100644 index 000000000..336946be5 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/source.php @@ -0,0 +1,40 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_source extends Horde_MIME_Viewer_Driver +{ + /** + * Add line numbers to a block of code. + * + * @param string $code The code to number. + */ + public function lineNumber($code, $linebreak = "\n") + { + $lines = substr_count($code, $linebreak) + 1; + $html = '
'; + for ($l = 1; $l <= $lines; $l++) { + $html .= sprintf('%s
', $l, $l, $l) . "\n"; + } + return $html . '
' . $code . '
'; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/srchighlite.php b/framework/MIME/lib/Horde/MIME/Viewer/srchighlite.php new file mode 100644 index 000000000..aa04725a7 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/srchighlite.php @@ -0,0 +1,102 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_srchighlite extends Horde_MIME_Viewer_source +{ + /** + * Render out the currently set contents using Source-highlight + * + * @param array $params Any parameters the viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['srchighlite']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['srchighlite']['location']) . '
'; + } + + /* Create temporary files for Webcpp. */ + $tmpin = Horde::getTempFile('SrcIn'); + $tmpout = Horde::getTempFile('SrcOut', false); + + /* Write the contents of our buffer to the temporary input file. */ + $contents = $this->mime_part->getContents(); + $fh = fopen($tmpin, 'wb'); + fwrite($fh, $contents, strlen($contents)); + fclose($fh); + + /* Determine the language from the mime type. */ + $lang = ''; + switch ($this->mime_part->getType()) { + case 'text/x-java': + $lang = 'java'; + break; + + case 'text/x-csrc': + case 'text/x-c++src': + case 'text/cpp': + $lang = 'cpp'; + break; + + case 'application/x-perl': + $lang = 'perl'; + break; + + case 'application/x-php': + case 'x-extension/phps': + case 'x-extension/php3s': + case 'application/x-httpd-php': + case 'application/x-httpd-php3': + case 'application/x-httpd-phps': + $lang = 'php3'; + break; + + case 'application/x-python': + $lang = 'python'; + break; + + // $lang = 'prolog'; + // break; + + // $lang = 'flex'; + // break; + + // $lang = 'changelog'; + // break; + + // $lang = 'ruby'; + // break; + } + + /* Execute Source-Highlite. */ + exec($GLOBALS['mime_drivers']['horde']['srchighlite']['location'] . " --src-lang $lang --out-format xhtml --input $tmpin --output $tmpout"); + $results = file_get_contents($tmpout); + unlink($tmpout); + + /* Educated Guess at whether we are inline or not. */ + if (headers_sent() || ob_get_length()) { + return $this->lineNumber($results); + } else { + return Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') . + $this->lineNumber($results) . + Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc'); + } + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/tgz.php b/framework/MIME/lib/Horde/MIME/Viewer/tgz.php new file mode 100644 index 000000000..4bd76649d --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/tgz.php @@ -0,0 +1,104 @@ + + * @author Michael Cochrane + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_tgz extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the currently set tar file contents. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + $contents = $this->mime_part->getContents(); + + /* Only decompress gzipped files. */ + $subtype = $this->mime_part->getSubType(); + if (($subtype == 'x-compressed-tar') || + ($subtype == 'tgz') || + ($subtype == 'x-tgz') || + ($subtype == 'gzip') || + ($subtype == 'x-gzip') || + ($subtype == 'x-gzip-compressed') || + ($subtype == 'x-gtar')) { + $gzip = &Horde_Compress::singleton('gzip'); + $contents = $gzip->decompress($contents); + if (empty($contents)) { + return _("Unable to open compressed archive."); + } elseif (is_a($contents, 'PEAR_Error')) { + return $contents->getMessage(); + } + } + + if ($subtype == 'gzip' || + $subtype == 'x-gzip' || + $subtype == 'x-gzip-compressed') { + global $conf; + require_once 'Horde/MIME/Magic.php'; + $mime_type = MIME_Magic::analyzeData($contents, isset($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null); + if (!$mime_type) { + $mime_type = _("Unknown"); + } + return sprintf(_("Content type of compressed file: %s"), $mime_type); + } + + /* Obtain the list of files/data in the tar file. */ + $tar = Horde_Compress::factory('tar'); + $tarData = $tar->decompress($contents); + if (is_a($tarData, 'PEAR_Error')) { + return $tarData->getMessage(); + } + + $fileCount = count($tarData); + $text = '' . htmlspecialchars(sprintf(_("Contents of \"%s\""), $this->mime_part->getName())) . ':' . "\n" . + '
' . + Text::htmlAllSpaces(_("Archive Name") . ': ' . $this->mime_part->getName()) . "\n" . + Text::htmlAllSpaces(_("Archive File Size") . ': ' . strlen($contents) . ' bytes') . "\n" . + Text::htmlAllSpaces(sprintf(ngettext("File Count: %d file", "File Count: %d files", $fileCount), $fileCount)) . + "\n\n" . + Text::htmlAllSpaces( + str_pad(_("File Name"), 62, ' ', STR_PAD_RIGHT) . + str_pad(_("Attributes"), 15, ' ', STR_PAD_LEFT) . + str_pad(_("Size"), 10, ' ', STR_PAD_LEFT) . + str_pad(_("Modified Date"), 19, ' ', STR_PAD_LEFT) + ) . "\n" . + str_repeat('-', 106) . "\n"; + + foreach ($tarData as $val) { + $text .= Text::htmlAllSpaces( + str_pad($val['name'], 62, ' ', STR_PAD_RIGHT) . + str_pad($val['attr'], 15, ' ', STR_PAD_LEFT) . + str_pad($val['size'], 10, ' ', STR_PAD_LEFT) . + str_pad(strftime("%d-%b-%Y %H:%M", $val['date']), 19, ' ', STR_PAD_LEFT) + ) . "\n"; + } + + return nl2br($text . str_repeat('-', 106) . "\n" . + '
'); + } + + /** + * Return the content-type + * + * @return string The content-type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/tnef.php b/framework/MIME/lib/Horde/MIME/Viewer/tnef.php new file mode 100644 index 000000000..64856df00 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/tnef.php @@ -0,0 +1,54 @@ + + * @author Michael Slusarz + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_tnef extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the current tnef data. + * + * @param array $params Any parameters the viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + require_once 'Horde/Compress.php'; + + $tnef = &Horde_Compress::singleton('tnef'); + + $data = ''; + $info = $tnef->decompress($this->mime_part->getContents()); + if (empty($info) || is_a($info, 'PEAR_Error')) { + $data .= ''; + } else { + $data .= ''; + foreach ($info as $part) { + $data .= ''; + } + } + $data .= '
' . _("MS-TNEF Attachment contained no data.") . '
' . _("Name") . '' . _("Mime Type") . '
' . $part['name'] . '' . $part['type'] . '/' . $part['subtype'] . '
'; + + return $data; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/vcard.php b/framework/MIME/lib/Horde/MIME/Viewer/vcard.php new file mode 100644 index 000000000..b116eb8b3 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/vcard.php @@ -0,0 +1,372 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_vcard extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the vcard contents. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = null) + { + global $registry, $prefs, $notification; + + require_once 'Horde/iCalendar.php'; + + $app = false; + $data = $this->mime_part->getContents(); + $html = ''; + $import_msg = null; + $title = _("vCard"); + + $iCal = new Horde_iCalendar(); + if (!$iCal->parsevCalendar($data, 'VCALENDAR', + $this->mime_part->getCharset())) { + $notification->push( + _("There was an error reading the contact data."), + 'horde.error'); + } + + if (Util::getFormData('import') && + Util::getFormData('source') && + $registry->hasMethod('contacts/import')) { + $source = Util::getFormData('source'); + $contacts = $registry->call('contacts/import', + array($data, 'text/x-vcard', $source)); + if (is_a($contacts, 'PEAR_Error')) { + $notification->push( + _("There was an error importing the contact data:") . ' ' + . $contacts->getMessage(), + 'horde.error'); + } else { + $notification->push(sprintf(ngettext( + "%d contact was successfully added to your address book.", + "%d contacts were successfully added to your address book.", + $iCal->getComponentCount()), + $iCal->getComponentCount()), + 'horde.success'); + } + } + + $html .= ''; + + $i = 0; + foreach ($iCal->getComponents() as $vc) { + if ($i > 0) { + $html .= ''; + } + ++$i; + + $html .= ''; + + $n = $vc->printableName(); + if (!empty($n)) { + $html .= $this->_row(_("Name"), $n); + } + + $aliases = $vc->getAttributeValues('ALIAS'); + if (!is_a($aliases, 'PEAR_Error')) { + $html .= $this->_row(_("Alias"), implode("\n", $aliases)); + } + $birthdays = $vc->getAttributeValues('BDAY'); + if (!is_a($birthdays, 'PEAR_Error')) { + $birthday = new Horde_Date($birthdays[0]); + $html .= $this->_row( + _("Birthday"), + $birthday->strftime($prefs->getValue('date_format'))); + } + + $photos = $vc->getAllAttributes('PHOTO'); + foreach ($photos as $photo) { + if (!isset($photo['params']['VALUE']) || + String::upper($photo['params']['VALUE']) != 'URI') { + continue; + } + $html .= $this->_row(_("Photo"), + '', + false); + } + + $labels = $vc->getAllAttributes('LABEL'); + foreach ($labels as $label) { + if (isset($label['params']['TYPE'])) { + if (!is_array($item['params']['TYPE'])) { + $item['params']['TYPE'] = array($item['params']['TYPE']); + } + } else { + $item['params']['TYPE'] = array_keys($item['params']); + } + $types = array(); + foreach ($item['params']['TYPE'] as $type) { + switch(String::upper($type)) { + case 'HOME': + $types[] = _("Home Address"); + break; + case 'WORK': + $types[] = _("Work Address"); + break; + case 'DOM': + $types[] = _("Domestic Address"); + break; + case 'INTL': + $types[] = _("International Address"); + break; + case 'POSTAL': + $types[] = _("Postal Address"); + break; + case 'PARCEL': + $types[] = _("Parcel Address"); + break; + case 'PREF': + $types[] = _("Preferred Address"); + break; + default: + $types[] = _("Address"); + break; + } + } + $html .= $this->_row(implode('/', $types), $label['value']); + } + + $adrs = $vc->getAllAttributes('ADR'); + foreach ($adrs as $item) { + if (isset($item['params']['TYPE'])) { + if (!is_array($item['params']['TYPE'])) { + $item['params']['TYPE'] = array($item['params']['TYPE']); + } + } else { + $item['params']['TYPE'] = array_keys($item['params']); + } + $address = $item['values']; + $a = array(); + if (isset($address[VCARD_ADR_STREET])) { + $a[] = $address[VCARD_ADR_STREET]; + } + if (isset($address[VCARD_ADR_LOCALITY])) { + $a[] = $address[VCARD_ADR_LOCALITY]; + } + if (isset($address[VCARD_ADR_REGION])) { + $a[] = $address[VCARD_ADR_REGION]; + } + if (isset($address[VCARD_ADR_POSTCODE])) { + $a[] = $address[VCARD_ADR_POSTCODE]; + } + if (isset($address[VCARD_ADR_COUNTRY])) { + $a[] = $address[VCARD_ADR_COUNTRY]; + } + $types = array(); + foreach ($item['params']['TYPE'] as $type) { + switch(String::upper($type)) { + case 'HOME': + $types[] = _("Home Address"); + break; + case 'WORK': + $types[] = _("Work Address"); + break; + case 'DOM': + $types[] = _("Domestic Address"); + break; + case 'INTL': + $types[] = _("International Address"); + break; + case 'POSTAL': + $types[] = _("Postal Address"); + break; + case 'PARCEL': + $types[] = _("Parcel Address"); + break; + case 'PREF': + $types[] = _("Preferred Address"); + break; + default: + $types[] = _("Address"); + break; + } + } + $html .= $this->_row(implode('/', $types), implode("\n", $a)); + } + + $numbers = $vc->getAllAttributes('TEL'); + + foreach ($numbers as $number) { + if (isset($number['params']['TYPE'])) { + if (!is_array($number['params']['TYPE'])) { + $number['params']['TYPE'] = array($number['params']['TYPE']); + } + foreach ($number['params']['TYPE'] as $type) { + $number['params'][String::upper($type)] = true; + } + } + if (isset($number['params']['FAX'])) { + $html .= $this->_row(_("Fax"), $number['value']); + } else { + if (isset($number['params']['HOME'])) { + $html .= $this->_row(_("Home Phone"), + $number['value']); + } elseif (isset($number['params']['WORK'])) { + $html .= $this->_row(_("Work Phone"), + $number['value']); + } elseif (isset($number['params']['CELL'])) { + $html .= $this->_row(_("Cell Phone"), + $number['value']); + } else { + $html .= $this->_row(_("Phone"), + $number['value']); + } + } + } + + $addresses = $vc->getAllAttributes('EMAIL'); + $emails = array(); + foreach ($addresses as $address) { + if (isset($address['params']['TYPE'])) { + if (!is_array($address['params']['TYPE'])) { + $address['params']['TYPE'] = array($address['params']['TYPE']); + } + foreach ($address['params']['TYPE'] as $type) { + $address['params'][String::upper($type)] = true; + } + } + $email = '' . htmlspecialchars($address['value']) . ''; + if (isset($address['params']['PREF'])) { + array_unshift($emails, $email); + } else { + $emails[] = $email; + } + } + + if (count($emails)) { + $html .= $this->_row(_("Email"), implode("\n", $emails), false); + } + + $title = $vc->getAttributeValues('TITLE'); + if (!is_a($title, 'PEAR_Error')) { + $html .= $this->_row(_("Title"), $title[0]); + } + + $role = $vc->getAttributeValues('ROLE'); + if (!is_a($role, 'PEAR_Error')) { + $html .= $this->_row(_("Role"), $role[0]); + } + + $org = $vc->getAttributeValues('ORG'); + if (!is_a($org, 'PEAR_Error')) { + $html .= $this->_row(_("Company"), $org[0]); + if (isset($org[1])) { + $html .= $this->_row(_("Department"), $org[1]); + } + } + + $notes = $vc->getAttributeValues('NOTE'); + if (!is_a($notes, 'PEAR_Error')) { + $html .= $this->_row(_("Notes"), $notes[0]); + } + + $url = $vc->getAttributeValues('URL'); + if (!is_a($url, 'PEAR_Error')) { + $html .= $this->_row( + _("URL"), + '' . htmlspecialchars($url[0]) + . '', + false); + } + } + + if ($registry->hasMethod('contacts/import') && + $registry->hasMethod('contacts/sources')) { + $html .= ''; + } + + $html .= '
 
'; + $fullname = $vc->getAttributeDefault('FN', false); + if ($fullname !== false) { + $html .= $fullname; + } + $html .= '
' + . Util::formInput(); + foreach ($_GET as $key => $val) { + $html .= ''; + } + + $sources = $registry->call('contacts/sources', array(true)); + if (count($sources) > 1) { + $html .= + '' + . '' + . '' + . ''; + } + + $html .= '
 
'; + + return + Util::bufferOutput( + 'include', + $registry->get('templates', 'horde') . '/common-header.inc') + . Util::bufferOutput(array($notification, 'notify'), + array('listeners' => 'status')) + . $html + . Util::bufferOutput( + 'include', + $registry->get('templates', 'horde') . '/common-footer.inc'); + } + + function _row($label, $value, $encode = true) + { + if ($encode) { + $label = htmlspecialchars($label); + $value = htmlspecialchars($value); + } + return '' . $label + . '' . nl2br($value) + . "\n"; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/webcpp.php b/framework/MIME/lib/Horde/MIME/Viewer/webcpp.php new file mode 100644 index 000000000..6c4ad0da8 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/webcpp.php @@ -0,0 +1,81 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_webcpp extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the currently set contents using Web C Plus Plus. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + protected function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['webcpp']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['webcpp']['location']) . '
'; + } + + /* Create temporary files for Webcpp. */ + $tmpin = Horde::getTempFile('WebcppIn'); + $tmpout = Horde::getTempFile('WebcppOut'); + + /* Write the contents of our buffer to the temporary input file. */ + $contents = $this->mime_part->getContents(); + $fh = fopen($tmpin, 'wb'); + fwrite($fh, $contents, strlen($contents)); + fclose($fh); + + /* Get the extension for the mime type. */ + include_once 'Horde/MIME/Magic.php'; + $ext = MIME_Magic::MIMEToExt($this->mime_part->getType()); + + /* Execute Web C Plus Plus. Specifying the in and out files didn't + work for me but pipes did. */ + exec($GLOBALS['mime_drivers']['horde']['webcpp']['location'] . " --pipe --pipe -x=$ext -l -a -t < $tmpin > $tmpout"); + $results = file_get_contents($tmpout); + + /* If we are not displaying inline, all the formatting is already + * done for us. */ + if (!$this->viewInline()) { + /* The first 2 lines are the Content-Type line and a blank line + * so we should remove them before outputting. */ + return preg_replace("/.*\n.*\n/", '', $results, 1); + } + + /* Extract the style sheet, removing any global body formatting + * if we're displaying inline. */ + $res = preg_split(';()|(
' . $body . '
'; + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + protected function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/wordperfect.php b/framework/MIME/lib/Horde/MIME/Viewer/wordperfect.php new file mode 100644 index 000000000..c65184cc3 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/wordperfect.php @@ -0,0 +1,58 @@ + + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_wordperfect extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the current data using wpd2html. + * + * @param array $params Any parameters the viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + /* Check to make sure the program actually exists. */ + if (!file_exists($GLOBALS['mime_drivers']['horde']['wordperfect']['location'])) { + return '
' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['wordperfect']['location']) . '
'; + } + + $tmp_wpd = Horde::getTempFile('wpd'); + $tmp_output = Horde::getTempFile('wpd'); + $args = " $tmp_wpd > $tmp_output"; + + $fh = fopen($tmp_wpd, 'w'); + fwrite($fh, $this->mime_part->getContents()); + fclose($fh); + + exec($GLOBALS['mime_drivers']['horde']['wordperfect']['location'] . $args); + + if (!file_exists($tmp_output)) { + return _("Unable to translate this WordPerfect document"); + } + + return file_get_contents($tmp_output); + } + + /** + * Return the MIME content type of the rendered content. + * + * @return string The content type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/Viewer/zip.php b/framework/MIME/lib/Horde/MIME/Viewer/zip.php new file mode 100644 index 000000000..ebdca0b94 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/Viewer/zip.php @@ -0,0 +1,135 @@ + + * @author Michael Cochrane + * @package Horde_MIME_Viewer + */ +class Horde_MIME_Viewer_zip extends Horde_MIME_Viewer_Driver +{ + /** + * Render out the current zip contents. + * + * @param array $params Any parameters the Viewer may need. + * + * @return string The rendered contents. + */ + public function render($params = array()) + { + return $this->_render($this->mime_part->getContents()); + } + + /** + * Output the file list. + * + * @param string $contents The contents of the zip archive. + * @param mixed $callback The callback function to use on the zipfile + * information. + * + * @return string The file list. + */ + protected function _render($contents, $callback = null) + { + require_once 'Horde/Compress.php'; + + $zip = &Horde_Compress::factory('zip'); + + /* Make sure this is a valid zip file. */ + if ($zip->checkZipData($contents) === false) { + return '
' . _("This does not appear to be a valid zip file.")
+                . '
'; + } + + $zipInfo = $zip->decompress( + $contents, + array('action' => HORDE_COMPRESS_ZIP_LIST)); + if (is_a($zipInfo, 'PEAR_Error')) { + return $zipInfo->getMessage(); + } + $fileCount = count($zipInfo); + + /* Determine maximum file name length. */ + $maxlen = 0; + foreach ($zipInfo as $val) { + $maxlen = max($maxlen, strlen($val['name'])); + } + + require_once 'Horde/Text.php'; + + $text = '' + . htmlspecialchars(sprintf(_("Contents of \"%s\""), + $this->mime_part->getName())) + . ':' . "\n" + . '
' + . Text::htmlAllSpaces( + _("Archive Name") . ': ' . $this->mime_part->getName() . "\n" + . _("Archive File Size") . ': ' . strlen($contents) + . ' bytes' . "\n" + . sprintf( + ngettext("File Count: %d file", "File Count: %d files", + $fileCount), + $fileCount) + . "\n\n" + . String::pad(_("File Name"), $maxlen, ' ', STR_PAD_RIGHT) + . String::pad(_("Attributes"), 10, ' ', STR_PAD_LEFT) + . String::pad(_("Size"), 10, ' ', STR_PAD_LEFT) + . String::pad(_("Modified Date"), 19, ' ', STR_PAD_LEFT) + . String::pad(_("Method"), 10, ' ', STR_PAD_LEFT) + . String::pad(_("CRC"), 10, ' ', STR_PAD_LEFT) + . String::pad(_("Ratio"), 10, ' ', STR_PAD_LEFT) + . "\n") + . str_repeat('-', 69 + $maxlen) . "\n"; + + foreach ($zipInfo as $key => $val) { + $ratio = (empty($val['size'])) + ? 0 + : 100 * ($val['csize'] / $val['size']); + + $val['name'] = String::pad($val['name'], + $maxlen, ' ', STR_PAD_RIGHT); + $val['attr'] = String::pad($val['attr'], + 10, ' ', STR_PAD_LEFT); + $val['size'] = String::pad($val['size'], + 10, ' ', STR_PAD_LEFT); + $val['date'] = String::pad(strftime("%d-%b-%Y %H:%M", + $val['date']), + 19, ' ', STR_PAD_LEFT); + $val['method'] = String::pad($val['method'], + 10, ' ', STR_PAD_LEFT); + $val['crc'] = String::pad($val['crc'], + 10, ' ', STR_PAD_LEFT); + $val['ratio'] = String::pad(sprintf("%1.1f%%", $ratio), + 10, ' ', STR_PAD_LEFT); + + $val = array_map(array('Text', 'htmlAllSpaces'), $val); + if (!is_null($callback)) { + $val = call_user_func($callback, $key, $val); + } + + $text .= $val['name'] . $val['attr'] . $val['size'] . $val['date'] + . $val['method'] . $val['crc'] . $val['ratio'] . "\n"; + } + + $text .= str_repeat('-', 69 + $maxlen) . "\n" + . '
'; + + return nl2br($text); + } + + /** + * Return the content-type + * + * @return string The content-type of the output. + */ + public function getType() + { + return 'text/html; charset=' . NLS::getCharset(); + } +} diff --git a/framework/MIME/lib/Horde/MIME/mime.magic.php b/framework/MIME/lib/Horde/MIME/mime.magic.php new file mode 100644 index 0000000000000000000000000000000000000000..d63efbea09f4cc734dce2bf740fd41a496a448b3 GIT binary patch literal 16939 zcmbVUU5p#obzVxgBCljywbcN10DE@jAoZrROLBj-qStPy{SzZmOKWK>@aDS1qRzO_J}3Iu42J{Cn#Cn#eeBg8wG0c} z^lZm6-HO7Dlq<`gIrfMH&C|Q|9Zh*>r)Rm|yMvvfV|%u4_moy^p!Rvn)->-1G`RdW zab70Q*LhWtt=6me_o@}8S#9heH>(et%3ig(&kNj~rZ85uzBVv4wQFjI=`aKY4i8F= zYIFBasaP!5_HN+c#bUY0ED(W_VcB52Gjc<$ho@m)j%hcRUWh2GrMWDRQe?wpReTR4 zSdP!?XirW89;a074U9}v@`_M1&4(ODTq5}lS&B++SE zMG~EtA`&w25)Awn+jl8R_0nEqGClJRCUKTOSeid8%5Ao_qO-ECmG>O*wjz_#b)GaU zUFAu$GH5($S_XxaTi;}bN8EZAKa8`7S#b}cnd~3H5_?`(cdVhuPLJVHou$Ada+h{7 z%V+Ttkva3N0)}6@1vwzlA#mM7IB`rwty5F^z_xT#IY9818)YkbcpqF+uNAJEn zcpUu2F(B?#HFo^8Ky=F1M&c9^cmz*3mLhwaAvM~}Ha5diilXp=b+QQu z?LnXkKP*z1HdW}`)h&xRg;_V2!dgl(BoXevxYRgStA^b&5A1=bmR3b1gFf2$^P2Z` zXqGsPmTrPS&LzktOl>S>A==y)O)j#kU;_JN&m0)YtLnBf>X{Yhe{&K%+g@4i+XjCP z__3!s985d74bf#?ww+J29O-OQ&p4bDol5DZQJ{0o#t&H~WN?3K8>;b;mRZq?d?7!V^gO zn@~Qxs}gh^7Ij*3A@Gx3WyAB4%O+;#C%~smc^aQ4WZFBW&B8@NuF``TX3NqBEVWq1 zZ46I3JOaFb%4jdCTL*k+Y42#a`KYn4+-cVD5fOMT_7`2RFA{u48MaR6T9MyQqljzD z)5Tdw(tTK~H@X563vXWXCw zXWgJcr)-e)PRVFV=$*5h7kIO&<{QGM)c~N*UWBaVWLCW(2N)}u&&GqmX5&F%jZrN}1&kZ%SE<yw@6k9&PRjZQ^gw0>`DPb!A}|MY`Ae=I5hGhAn^d+cROgW6_HhoN^II- zxhcno2hi83MuDd02^O%WoyOk5yGt_?3be|YH2`<&wS&x3pnRXRHmJVpc$V&;X~a7j zxn<4`L}PdF@lm77n}P|9sdLvwk51JM)jlH5qT6 z>O;$>>7d0hk>NlK(+ZEmZt{}zVB+KxEN*S#)SH*yY1W$e_6dw*FqI2}uN}|wgjS~q zAur0=QS?G!$axnE3kwTG18j-1&|{OLq32%z!{23=K`*>B-FDb+fw^71{fN_1s-mUs zphY?usZL+BSaAf~KdjwnQqXCb6jUvmmjLa8Y=J;!(3%EOXM5ynE!4%*&@f>SCGCq$ z6~RbWj6>5jSUCV3z?aPNgh14*$B*CVH4%hlFQGr`c{tXdDJxX~8%MwO-q(382!n-v zIxtrIu8L+pSF?l(Qs9Jxgz>LAW?Qh6JZ@O+_DpYt`QOmyuI1coKBck?Y=sOQ8V<-~t82k*a6 z*r<%9YZKf1ONKyBrm+GvTGr z`C~caZ^mlRYB^|POzY@;&gGDc${hA{@n4zSZ1gVvXRfUo?kdYEa2%-mEKX=?>B1`y zc{H-Lbe#lw4LEoko>i z#T+5mYWGDRf#W7L?ZMIeEY@l(LP<%3_xA3H2Jr0l?>#&afmQlEzpcyruh$n=nExc= z^`j4JJcy7iya4*G9Sbq<)RHH%-t4lYr+Q2%rf(U{5(HQO;V=G867YrJ9jZ^Pfnke& zEim_+4}^Fq>+$bTIlOufZq|JnH11v$emmlt&#a-3c!1=VmkOEB6YW%vYR(dm#*zy- z4Igv--tisA%ob&{x_3+vx{aQW5T0hZY6q=SpV<4m<=0Y(y??*B$&m2ItrHlvIvPj` zF1~#Er4%}ke(+Hc$|>_^1ERjT>S(Bf-T!%;ti-&avZ z3~@h?PCR#sG5pfym*-Y|fPAulk2R7hR|Ed##g{IpY_;&Ur5_|gLO!$5*4ZS;9(|J= zis}qYe5-N)kmtil4t<5uS0DcAKkz6iWa^eYv$&@hx zEOZ)ivtu=$0Rx*)gjaJ)b99NKpQQw~- zh|rOaKM2qhhuNbmxL8OcAIim7R#s-VPe6aw(LN&z^hQSJqC@0Srq<&>(ip+qfyjA- zTr*tQmGmcKjmR!TY?OXH$;INGB6_uWbDLb*YzjMdamSn11O&IKEfv1+<@PAld;7yK z%P>~Hw~%Xd7Zad^>H`81hBJ1*5W`5lIVqarlmo>1`EN6Yd5GUhMSPNiz+qrS{G@;dT(`^~eC~@}3`JAIq3wE;hhHF~ zcGVOfVC%CH=+@CYfGzRn7fpZ#!e+|*;4D?UfVCepA+8?z-$La5$=jCQw^rv5EK&%0#IUo5P|IZ^vOfom5 zCUS^XAL^-uhW>Wutk(-Xlq`bJxHlJnVct9A1O$`*S0H9@NmN9$vH?(--~T5->JtAX zcPCsnwZnXwoWH?Cv)Oy&+YE?Bsm>%yz42#EjMB|eq|xpE(82pSf1x-2JZ+)xr2)R5 z1`x(NXD_LnJx^I8OU?m}JjW3T7kuK_^sK=Mj|p7P*f6}tX$?fmX|q0*5%YOAHO|{Z zf<#Aze&!^uj4I2Q1SocnlAZdc3$&wk9PhAh&@Sz1164Cl@g5RC&%vVb--fX>`3pe)Os z&0WsdT97Ss{0t7lScS!_Ub|0530qruh0mKjC!t|dKi!gpkoX`k%loK6c@i`$u5&&f zS^5bcGRRwtzHJMKI1@3nh76wNbit2&a+C~-?D9vvOPol-P`f9?=Z|=Qp#fbOHvg7@ zc2=T?TH>iG+(E`)GGKLj*~Fp|KMkZuE>sBp@*4k^FY+%ISHvrd%Z7=Nxh1f$e^_Pk zBzjKz9t$7T#GOH8kTm7*B2byQi8%Z6K2w`}Iq+ppkST&DTtu*o@qYn|I*gAs?-i=O zp)|XetKc6^Jf2d*Pt9WS%I#8nhP_HakPM7}uL6bB6LI1;-izSpZ!Kpep7aqEKb~Z0 zM5cHTM32bvb5C4Y4G017XMgjT8-3R;C`VvpZer1i~ z%3qf7RDMYkJtGSXpYok!ymT=}qWlD70P&lJG!UQF-45qA#i2g-yjJPZnx2KE@IVVmiguc7CU81SwP-nMgkZf8NVZ51~HZ3jOUgKf~m&{{*3<`CZ0!%+I+ykA5M z60CSmTFUfbnSDD|vUN*$Y}amk%Klh4d&=f&QF%xYgSw_@+C?3Y|L)EtG#hU@;E9r_ z2|Z9Tlr50kv-MHm|A7mgM$Rs$p6|gl%fT6-%QQK~R-*KzfhQ0lDkbsj81g*9Hffi~ z#0{vXP7fSc;MC#cZsWm7(B%nx%ZM<*!A9{`qO$gmj)T`S36W}RyR^BzF*%)dsyVfQ zVqg7)&j*fQcxh^^hTmcnP*m>ez&Aw|m;oqi)VV;iFFe-0dF$3z;9SB^>6hYFN5m=7 YeJWKS_+Jr}+bG7KJ$v@0x87d-KfkfPtpET3 literal 0 HcmV?d00001 diff --git a/framework/MIME/lib/Horde/MIME/mime.mapping.php b/framework/MIME/lib/Horde/MIME/mime.mapping.php new file mode 100644 index 000000000..a752f9c94 --- /dev/null +++ b/framework/MIME/lib/Horde/MIME/mime.mapping.php @@ -0,0 +1,894 @@ +' where is the unknown file extension. + * + * @package Horde_MIME + * + * $Horde: framework/MIME/MIME/mime.mapping.php,v 1.17 2008/10/31 18:00:04 slusarz Exp $ + * + * Generated: 10/31/08 11:59:02 by slusarz on bigworm.curecanti.org + */ +$mime_extension_map = array( + '__MAXPERIOD__' => '1', + '3dml' => 'text/vnd.in3d.3dml', + '3ds' => 'image/x-3ds', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + 'BLEND' => 'application/x-blender', + 'C' => 'text/x-c++src', + 'CSSL' => 'text/css', + 'NSV' => 'video/x-nsv', + 'PAR2' => 'application/x-par2', + 'XM' => 'audio/x-mod', + 'Z' => 'application/x-compress', + 'a' => 'application/x-archive', + 'abw' => 'application/x-abiword', + 'abw.CRASHED' => 'application/x-abiword', + 'abw.gz' => 'application/x-abiword', + 'ac3' => 'audio/ac3', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adb' => 'text/x-adasrc', + 'ads' => 'text/x-adasrc', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-afm', + 'afp' => 'application/vnd.ibm.modcap', + 'ag' => 'image/x-applix-graphics', + 'ai' => 'application/illustrator', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'al' => 'application/x-perl', + 'ami' => 'application/vnd.amiga.ami', + 'apr' => 'application/vnd.lotus-approach', + 'arj' => 'application/x-arj', + 'as' => 'application/x-applix-spreadsheet', + 'asc' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'asm' => 'text/x-asm', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asp' => 'application/x-asp', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/x-applix-word', + 'bak' => 'application/x-trash', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bib' => 'text/x-bibtex', + 'bin' => 'application/octet-stream', + 'blend' => 'application/x-blender', + 'blender' => 'application/x-blender', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip', + 'c' => 'text/x-csrc', + 'c++' => 'text/x-c++src', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'cc' => 'text/x-c++src', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdr' => 'application/vnd.corel-draw', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/x-x509-ca-cert', + 'cert' => 'application/x-x509-ca-cert', + 'cgi' => 'application/x-cgi', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/x-chm', + 'chrt' => 'application/x-kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/x-java', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cls' => 'text/x-tex', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpio.gz' => 'application/x-cpio-compressed', + 'cpp' => 'text/x-c++src', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'cs' => 'text/x-csharp', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/vnd.commonspace', + 'csv' => 'text/x-comma-separated-values', + 'cur' => 'image/x-win-bitmap', + 'curl' => 'application/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxx' => 'text/x-c++src', + 'd' => 'text/x-dsrc', + 'daf' => 'application/vnd.mobius.daf', + 'dat' => 'video/mpeg', + 'davmount' => 'application/davmount+xml', + 'dbf' => 'application/x-dbase', + 'dc' => 'application/x-dc-rom', + 'dcl' => 'text/x-dcl', + 'dcm' => 'application/dicom', + 'dcr' => 'application/x-director', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-deb', + 'def' => 'text/plain', + 'der' => 'application/x-x509-ca-cert', + 'desktop' => 'application/x-desktop', + 'dfac' => 'application/vnd.dreamfactory', + 'dia' => 'application/x-dia-diagram', + 'dic' => 'text/x-c', + 'diff' => 'text/x-patch', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'dot' => 'application/msword', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dsc' => 'text/prs.lines.tag', + 'dsl' => 'text/x-dsl', + 'dtd' => 'text/x-dtd', + 'dump' => 'application/octet-stream', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'egon' => 'application/x-egon', + 'ei6' => 'application/vnd.pg.osasli', + 'el' => 'text/x-emacs-lisp', + 'elc' => 'application/octet-stream', + 'eml' => 'message/rfc822', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'image/x-eps', + 'epsf' => 'image/x-eps', + 'epsi' => 'image/x-eps', + 'es3' => 'application/vnd.eszigno3+xml', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etheme' => 'application/x-e-theme', + 'etx' => 'text/x-setext', + 'exe' => 'application/x-ms-dos-executable', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fig' => 'image/x-xfig', + 'fits' => 'image/x-fits', + 'flac' => 'audio/x-flac', + 'flc' => 'video/x-flic', + 'fli' => 'video/x-flic', + 'flo' => 'application/vnd.micrografx.flo', + 'flw' => 'application/x-kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'fo' => 'text/x-xslfo', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fzs' => 'application/vnd.fuzzysheet', + 'g3' => 'image/fax-g3', + 'gac' => 'application/vnd.groove-account', + 'gb' => 'application/x-gameboy-rom', + 'gcrd' => 'text/directory', + 'gdl' => 'model/vnd.gdl', + 'gen' => 'application/x-genesis-rom', + 'gg' => 'application/x-sms-rom', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'glade' => 'application/x-glade', + 'gmo' => 'application/x-gettext-translation', + 'gnc' => 'application/x-gnucash', + 'gnucash' => 'application/x-gnucash', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gra' => 'application/x-graphite', + 'gram' => 'application/srgs', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-type1', + 'gsm' => 'audio/x-gsm', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gz' => 'application/x-gzip', + 'h' => 'text/x-chdr', + 'h++' => 'text/x-chdr', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c++hdr', + 'hlp' => 'application/winhlp', + 'hp' => 'text/x-chdr', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'hs' => 'text/x-haskell', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'icb' => 'image/x-icb', + 'ice' => 'x-conference/x-cooltalk', + 'ico' => 'image/x-ico', + 'ics' => 'text/calendar', + 'idl' => 'text/x-idl', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'iff' => 'image/x-iff', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'ilbm' => 'image/x-ilbm', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/x-cd-image', + 'it' => 'audio/x-it', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/x-jar', + 'java' => 'text/x-java', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jng' => 'image/x-jng', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jp2' => 'image/jpeg2000', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'jpr' => 'application/x-jbuilder-project', + 'jpx' => 'application/x-jbuilder-project', + 'js' => 'application/x-javascript', + 'json' => 'application/json', + 'kar' => 'audio/midi', + 'karbon' => 'application/x-karbon', + 'kdelnk' => 'application/x-desktop', + 'kfo' => 'application/x-kformula', + 'kia' => 'application/vnd.kidspiration', + 'kil' => 'application/x-killustrator', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/x-kontour', + 'kpm' => 'application/x-kpovmodeler', + 'kpr' => 'application/x-kpresenter', + 'kpt' => 'application/x-kpresenter', + 'kra' => 'application/x-krita', + 'ksp' => 'application/x-kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktz' => 'application/vnd.kahootz', + 'kud' => 'application/x-kugar', + 'kwd' => 'application/x-kword', + 'kwt' => 'application/x-kword', + 'la' => 'application/x-shared-library-la', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/x-lha', + 'lhs' => 'text/x-literate-haskell', + 'lhz' => 'application/x-lhz', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'log' => 'text/x-log', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'ltx' => 'text/x-tex', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwo' => 'image/x-lwo', + 'lwob' => 'image/x-lwo', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lws' => 'image/x-lws', + 'lyx' => 'application/x-lyx', + 'lzh' => 'application/x-lha', + 'lzo' => 'application/x-lzop', + 'm' => 'text/x-objcsrc', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm15' => 'audio/x-mod', + 'm1v' => 'video/mpeg', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm4a' => 'audio/x-m4a', + 'm4u' => 'video/vnd.mpegurl', + 'ma' => 'application/mathematica', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'application/x-troff-man', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'md' => 'application/x-genesis-rom', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/x-troff-me', + 'mesh' => 'model/mesh', + 'mfm' => 'application/vnd.mfmp', + 'mgp' => 'application/x-magicpoint', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/x-mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mkv' => 'application/x-matroska', + 'mlp' => 'application/vnd.dolby.mlp', + 'mm' => 'text/x-troff-mm', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mml' => 'text/mathml', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mng' => 'video/x-mng', + 'mny' => 'application/x-msmoney', + 'moc' => 'text/x-moc', + 'mod' => 'audio/x-mod', + 'moov' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'video/mpeg', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'ms' => 'text/x-troff-ms', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msod' => 'image/x-msod', + 'msty' => 'application/vnd.muvee.style', + 'msx' => 'application/x-msx-rom', + 'mtm' => 'audio/x-mod', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n64' => 'application/x-n64-rom', + 'nb' => 'application/mathematica', + 'nc' => 'application/x-netcdf', + 'nes' => 'application/x-nes-rom', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsf' => 'application/vnd.lotus-notes', + 'nsv' => 'video/x-nsv', + 'o' => 'application/x-object', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'obj' => 'application/x-tgif', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'ogg' => 'application/ogg', + 'old' => 'application/x-trash', + 'oleo' => 'application/x-oleo', + 'oot' => 'application/vnd.oasis.opendocument.text', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/vnd.oasis.opendocument.formula-template', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otm' => 'application/vnd.oasis.opendocument.text-master', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'par2' => 'application/x-par2', + 'pas' => 'text/x-pascal', + 'patch' => 'text/x-patch', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcd' => 'image/x-photo-cd', + 'pcf' => 'application/x-font-pcf', + 'pcf.Z' => 'application/x-font-type1', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pem' => 'application/x-x509-ca-cert', + 'perl' => 'application/x-perl', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp', + 'php' => 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'pic' => 'image/x-pict', + 'pict' => 'image/x-pict', + 'pict1' => 'image/x-pict', + 'pict2' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'pl' => 'application/x-perl', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'audio/x-scpls', + 'pm' => 'application/x-perl', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'po' => 'text/x-gettext-translation', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'text/x-gettext-translation-template', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppt' => 'application/vnd.ms-powerpoint', + 'ppz' => 'application/vnd.ms-powerpoint', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/vnd.palm', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'ps.gz' => 'application/x-gzpostscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/x-psd', + 'psf' => 'application/x-font-linux-psf', + 'psid' => 'audio/prs.sid', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pw' => 'application/x-pw', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'py' => 'text/x-python', + 'pyc' => 'application/x-python-bytecode', + 'pyo' => 'application/x-python-bytecode', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qif' => 'application/x-qw', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qtvr' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar', + 'ras' => 'image/x-cmu-raster', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'text/rdf', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rej' => 'application/x-reject', + 'rep' => 'application/vnd.businessobjects', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rle' => 'image/rle', + 'rm' => 'audio/x-pn-realaudio', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roff' => 'application/x-troff', + 'rpm' => 'application/x-rpm', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'text/rss', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 's3m' => 'audio/x-s3m', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sam' => 'application/x-amipro', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'text/x-scheme', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/vnd.stardivision.impress', + 'sds' => 'application/vnd.stardivision.chart', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sgi' => 'image/x-sgi', + 'sgl' => 'application/vnd.stardivision.writer', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-shellscript', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'shtml' => 'text/html', + 'siag' => 'application/x-siag', + 'sid' => 'audio/prs.sid', + 'sig' => 'application/pgp-signature', + 'sik' => 'application/x-trash', + 'silo' => 'model/mesh', + 'sit' => 'application/stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'slk' => 'text/spreadsheet', + 'slt' => 'application/vnd.epson.salt', + 'smd' => 'application/vnd.stardivision.mail', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'sml' => 'application/smil', + 'sms' => 'application/x-sms-rom', + 'snd' => 'audio/basic', + 'so' => 'application/x-sharedlib', + 'spc' => 'application/x-pkcs7-certificates', + 'spd' => 'application/x-font-speedo', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'sql' => 'text/x-sql', + 'src' => 'application/x-wais-source', + 'srx' => 'application/sparql-results+xml', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stm' => 'audio/x-stm', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sty' => 'text/x-tex', + 'sun' => 'image/x-sun-raster', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 'sylk' => 'text/spreadsheet', + 't' => 'application/x-troff', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tar.Z' => 'application/x-tarz', + 'tar.bz' => 'application/x-bzip-compressed-tar', + 'tar.bz2' => 'application/x-bzip-compressed-tar', + 'tar.gz' => 'application/x-compressed-tar', + 'tar.lzo' => 'application/x-tzo', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'text/x-tcl', + 'tex' => 'text/x-tex', + 'texi' => 'text/x-texinfo', + 'texinfo' => 'text/x-texinfo', + 'text' => 'text/plain', + 'tga' => 'image/x-tga', + 'tgz' => 'application/x-compressed-tar', + 'theme' => 'application/x-theme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tk' => 'text/x-tcl', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'application/x-troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'ts' => 'application/x-linguist', + 'tsv' => 'text/tab-separated-values', + 'ttf' => 'application/x-font-ttf', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'tzo' => 'application/x-tzo', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'ui' => 'application/x-designer', + 'uil' => 'text/x-uil', + 'ult' => 'audio/x-mod', + 'umj' => 'application/vnd.umajin', + 'uni' => 'audio/x-mod', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/x-uri', + 'uris' => 'text/uri-list', + 'url' => 'text/x-uri', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/directory', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/calendar', + 'vct' => 'text/directory', + 'vcx' => 'application/vnd.vcx', + 'vfb' => 'text/calendar', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vob' => 'video/mpeg', + 'voc' => 'audio/x-voc', + 'vor' => 'application/vnd.stardivision.writer', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wb1' => 'application/x-quattropro', + 'wb2' => 'application/x-quattropro', + 'wb3' => 'application/x-quattropro', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'wk1' => 'application/vnd.lotus-1-2-3', + 'wk3' => 'application/vnd.lotus-1-2-3', + 'wk4' => 'application/vnd.lotus-1-2-3', + 'wks' => 'application/vnd.lotus-1-2-3', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'image/x-wmf', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-ms-wmz', + 'wpd' => 'application/vnd.wordperfect', + 'wpg' => 'application/x-wpg', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x3d' => 'application/vnd.hzn-3d-crossword', + 'xac' => 'application/x-gnucash', + 'xar' => 'application/vnd.xara', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbel' => 'application/x-xbel', + 'xbm' => 'image/x-xbitmap', + 'xcf' => 'image/x-xcf', + 'xcf.bz2' => 'image/x-compressed-xcf', + 'xcf.gz' => 'image/x-compressed-xcf', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xi' => 'audio/x-xi', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlc' => 'application/vnd.ms-excel', + 'xld' => 'application/vnd.ms-excel', + 'xll' => 'application/vnd.ms-excel', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlt' => 'application/vnd.ms-excel', + 'xlw' => 'application/vnd.ms-excel', + 'xm' => 'audio/x-xm', + 'xmi' => 'text/x-xmi', + 'xml' => 'text/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'text/x-xslt', + 'xslfo' => 'text/x-xslfo', + 'xslt' => 'text/x-xslt', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'zabw' => 'application/x-abiword', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zmm' => 'application/vnd.handheld-entertainment+xml', + 'zoo' => 'application/x-zoo', + '123' => 'application/vnd.lotus-1-2-3', + '669' => 'audio/x-mod' +); \ No newline at end of file diff --git a/framework/MIME/package.xml b/framework/MIME/package.xml new file mode 100644 index 000000000..1a78b5c47 --- /dev/null +++ b/framework/MIME/package.xml @@ -0,0 +1,223 @@ + + + Horde_MIME + pear.horde.org + Horde Mime Library + The Horde_MIME:: class provides methods for dealing with MIME (RFC 2045) and related e-mail (RFC 822/2822/5322) standards. + + + Chuck Hagenbuch + chuck + chuck@horde.org + yes + + + Michael Slusarz + slusarz + slusarz@horde.org + yes + + 2008-10-29 + + + 0.1.0 + 0.1.0 + + + alpha + alpha + + LGPL + * Initial package. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.2.0 + + + 1.5.0 + + + Mail_mimeDecode + pear.php.net + + + Util + pear.horde.org + + + gettext + + + + + Auth + pear.horde.org + + + Horde_Compress + pear.horde.org + + + Horde_Framework + pear.horde.org + + + iCalendar + pear.horde.org + + + Horde_Prefs + pear.horde.org + + + Horde_SessionObjects + pear.horde.org + + + Horde_Text_Filter + pear.horde.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/MIME/tests/attachment.bin b/framework/MIME/tests/attachment.bin new file mode 100644 index 000000000..a17f84e7d --- /dev/null +++ b/framework/MIME/tests/attachment.bin @@ -0,0 +1 @@ +Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich. diff --git a/framework/MIME/tests/bug_325.phpt b/framework/MIME/tests/bug_325.phpt new file mode 100644 index 000000000..16fbaf6a1 --- /dev/null +++ b/framework/MIME/tests/bug_325.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug #338 (fileinfo returning charset) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +text/plain diff --git a/framework/MIME/tests/bug_325.txt b/framework/MIME/tests/bug_325.txt new file mode 100644 index 000000000..0dc0fba09 --- /dev/null +++ b/framework/MIME/tests/bug_325.txt @@ -0,0 +1,6 @@ +Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod +tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo +consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse +cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non +proident, sunt in culpa qui officia deserunt mollit anim id est laborum. diff --git a/framework/MIME/tests/bug_4834.phpt b/framework/MIME/tests/bug_4834.phpt new file mode 100644 index 000000000..6230ff573 --- /dev/null +++ b/framework/MIME/tests/bug_4834.phpt @@ -0,0 +1,11 @@ +--TEST-- +Bug #4834 Wrong encoding of email lists with groups. +--FILE-- +, Group: peter@example.com, jane@example.com;'); + +?> +--EXPECT-- +John Doe , Group: peter@example.com, jane@example.com; diff --git a/framework/MIME/tests/bug_6896.phpt b/framework/MIME/tests/bug_6896.phpt new file mode 100644 index 000000000..ca2e0240b --- /dev/null +++ b/framework/MIME/tests/bug_6896.phpt @@ -0,0 +1,16 @@ +--TEST-- +Bug #6896 MIME::rfc822Explode parsing broken +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + string(17) "addr1@example.com" + [1]=> + string(18) " addr2@example.com" +} diff --git a/framework/MIME/tests/contents.phpt b/framework/MIME/tests/contents.phpt new file mode 100644 index 000000000..809f7748a --- /dev/null +++ b/framework/MIME/tests/contents.phpt @@ -0,0 +1,67 @@ +--TEST-- +MIME_Contents tests. +--FILE-- +getDownloadAllList()); +echo "\n"; +var_export($contents->getAttachmentContents()); + +?> +--EXPECT-- +array ( + 0 => '2.0', +) +array ( + 0 => + array ( + 'name' => 'Weitergeleitete Nachricht: Small message', + 'data' => 'Return-Path: +Received: from neo.wg.de ([unix socket]) + by neo (Cyrus v2.2.13) with LMTPA; + Tue, 11 Mar 2008 17:26:11 +0100 +X-Sieve: CMU Sieve 2.2 +Received: from localhost (localhost [127.0.0.1]) + by neo.wg.de (Postfix) with ESMTP id 142BF32B032 + for ; Tue, 11 Mar 2008 17:26:11 +0100 (CET) +Received: from neo.wg.de ([127.0.0.1]) + by localhost (neo.wg.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP + id 02540-02 for ; Tue, 11 Mar 2008 17:26:02 +0100 (CET) +Received: from localhost (localhost [127.0.0.1]) + by neo.wg.de (Postfix) with ESMTP id 21E2532B037 + for ; Tue, 11 Mar 2008 17:26:02 +0100 (CET) +Received: from 192.168.60.101 ([192.168.60.101]) by neo.wg.de (Horde + Framework) with HTTP; Tue, 11 Mar 2008 17:26:02 +0100 +Message-ID: <20080311172602.12293hbhf6ddsza0@neo.wg.de> +X-Priority: 3 (Normal) +Date: Tue, 11 Mar 2008 17:26:02 +0100 +From: Jan Schneider +To: "jan@localhost" +Subject: Small message +MIME-Version: 1.0 +Content-Type: text/plain; + charset=ISO-8859-1; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit +User-Agent: Internet Messaging Program (IMP) H3 (5.0-cvs) +X-Virus-Scanned: amavisd-new at wg.de +X-Spam-Status: No, score=-4.351 required=5 tests=[ALL_TRUSTED=-1.8, AWL=0.048, + BAYES_00=-2.599] +X-Spam-Score: -4.351 +X-Spam-Level: + +Small message text. + + +', + ), +) diff --git a/framework/MIME/tests/mail_001.phpt b/framework/MIME/tests/mail_001.phpt new file mode 100644 index 000000000..dfd7cfcd4 --- /dev/null +++ b/framework/MIME/tests/mail_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +MIME_Mail constructor test +--FILE-- +send('dummy'); + +?> +--EXPECTF-- +Subject: My Subject +To: recipient@example.com +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: text/plain; + charset=iso-8859-15; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +This is +the body diff --git a/framework/MIME/tests/mail_002.phpt b/framework/MIME/tests/mail_002.phpt new file mode 100644 index 000000000..28d1d9da4 --- /dev/null +++ b/framework/MIME/tests/mail_002.phpt @@ -0,0 +1,36 @@ +--TEST-- +MIME_Mail methods test +--FILE-- +addHeader('Subject', 'My Subject'); +$mail->setBody("This is\nthe body", 'iso-8859-15'); +$mail->addHeader('To', 'recipient@example.com'); +$mail->addHeader('Cc', 'null@example.com'); +$mail->addHeader('Bcc', 'invisible@example.com'); +$mail->addHeader('From', 'sender@example.com'); +$mail->removeHeader('Cc'); + +echo $mail->send('dummy'); + +?> +--EXPECTF-- +Subject: My Subject +To: recipient@example.com +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: text/plain; + charset=iso-8859-15; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +This is +the body diff --git a/framework/MIME/tests/mail_003.phpt b/framework/MIME/tests/mail_003.phpt new file mode 100644 index 000000000..a0a3ef1a7 --- /dev/null +++ b/framework/MIME/tests/mail_003.phpt @@ -0,0 +1,33 @@ +--TEST-- +MIME_Mail encoding test +--FILE-- +', + 'sender@example.com', 'iso-8859-1'); +$mail->addHeader('Cc', 'Der schöne Peter ', 'iso-8859-15'); +echo $mail->send('dummy'); + +?> +--EXPECTF-- +Subject: =?iso-8859-1?b?U2No9m5lcg==?= Betreff +To: =?iso-8859-1?b?RW1wZuRuZ2Vy?= +From: sender@example.com +Cc: Der =?iso-8859-15?b?c2No9m5l?= Peter +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: text/plain; + charset=iso-8859-1; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: quoted-printable + +H=FCbsche Umlaute + und Leerzeichen. diff --git a/framework/MIME/tests/mail_004.phpt b/framework/MIME/tests/mail_004.phpt new file mode 100644 index 000000000..23fd74330 --- /dev/null +++ b/framework/MIME/tests/mail_004.phpt @@ -0,0 +1,59 @@ +--TEST-- +MIME_Mail::addPart() test +--FILE-- +addPart('text/plain', 'This is a plain text', 'iso-8859-1', 'inline'); +$mail->addPart('application/octet-stream', + file_get_contents(dirname(__FILE__) . '/attachment.bin'), + null, 'attachment'); + +echo $mail->send('dummy'); + +?> +--EXPECTF-- +Subject: My Subject +To: recipient@example.com +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="=_%s" +Content-Transfer-Encoding: 7bit + +This message is in MIME format. + +--=_%s +Content-Type: text/plain; + charset=iso-8859-15; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +This is +the body + +--=_%s +Content-Type: text/plain; + charset=iso-8859-1 +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +This is a plain text +--=_%s +Content-Type: application/octet-stream +Content-Disposition: attachment +Content-Transfer-Encoding: base64 + +WnfDtmxmIEJveGvDpG1wZmVyIGphZ2VuIFZpa3RvciBxdWVyIMO8YmVyIGRlbiBncm/Dn2VuIFN5 +bHRlciBEZWljaC4K + +--=_%s-- diff --git a/framework/MIME/tests/mail_005.phpt b/framework/MIME/tests/mail_005.phpt new file mode 100644 index 000000000..5683ec464 --- /dev/null +++ b/framework/MIME/tests/mail_005.phpt @@ -0,0 +1,100 @@ +--TEST-- +MIME_Mail HTML test +--FILE-- +setBody("This is\nthe plain text body."); +echo $mail->send('dummy'); + +echo "====================================================================\n"; + +$mail = new MIME_Mail('My Subject', null, 'recipient@example.com', + 'sender@example.com'); +$mail->setHTMLBody("

Header Title

\n

This is
the html text body.

", + 'iso-8859-1', false); +echo $mail->send('dummy'); + +echo "====================================================================\n"; + +$mail = new MIME_Mail('My Subject', null, 'recipient@example.com', + 'sender@example.com'); +$mail->setHTMLBody("

Header Title

\n

This is
the html text body.

"); +echo $mail->send('dummy'); + +?> +--EXPECTF-- +Subject: My Subject +To: recipient@example.com +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: text/plain; + charset=iso-8859-1; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +This is +the plain text body. +==================================================================== +Subject: My Subject +To: recipient@example.com +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: text/html; + charset=iso-8859-1 +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +

Header Title

+

This is
the html text body.

+==================================================================== +Subject: My Subject +To: recipient@example.com +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="=_%s" +Content-Transfer-Encoding: 7bit + +This message is in MIME format. + +--=_%s +Content-Type: text/plain; + charset=iso-8859-1; + DelSp="Yes"; + format="flowed" +Content-Description: Plaintext Version of Message +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + + + +HEADER TITLE + +This is +the html text body. + +--=_%s +Content-Type: text/html; + charset=iso-8859-1 +Content-Description: HTML Version of Message +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +

Header Title

+

This is
the html text body.

+--=_%s-- diff --git a/framework/MIME/tests/mail_006.phpt b/framework/MIME/tests/mail_006.phpt new file mode 100644 index 000000000..8bed4464e --- /dev/null +++ b/framework/MIME/tests/mail_006.phpt @@ -0,0 +1,76 @@ +--TEST-- +MIME_Mail::addAttachment() test +--FILE-- +addAttachment(dirname(__FILE__) . '/attachment.bin'); +$mail->addAttachment(dirname(__FILE__) . '/mail_dummy.inc', 'my_name.html', 'text/html', 'iso-8859-15'); + +echo $mail->send('dummy'); + +?> +--EXPECTF-- +Subject: My Subject +To: recipient@example.com +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="=_%s" +Content-Transfer-Encoding: 7bit + +This message is in MIME format. + +--=_%s +Content-Type: text/plain; + charset=iso-8859-15; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +This is +the body + +--=_%s +Content-Type: application/octet-stream; + name="attachment.bin" +Content-Disposition: attachment; + filename="attachment.bin" +Content-Transfer-Encoding: base64 + +WnfDtmxmIEJveGvDpG1wZmVyIGphZ2VuIFZpa3RvciBxdWVyIMO8YmVyIGRlbiBncm/Dn2VuIFN5 +bHRlciBEZWljaC4K + +--=_%s +Content-Type: text/html; + charset=iso-8859-15; + name="my_name.html" +Content-Disposition: attachment; + filename="my_name.html" +Content-Transfer-Encoding: 7bit + +send('dummy'); +$id = $mail->_headers->getValue('message-id'); + +echo "====================================================================\n"; + +$mail->addHeader('To', 'Änderung ', 'utf-8'); +echo $mail->send('dummy'); + +echo "====================================================================\n"; + +var_dump($id != $mail->_headers->getValue('message-id')); + +?> +--EXPECTF-- +Subject: My Subject +To: recipient@example.com +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: text/plain; + charset=iso-8859-15; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +This is +the body +==================================================================== +Subject: My Subject +From: sender@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: text/plain; + charset=iso-8859-15; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit +To: =?utf-8?b?w4RuZGVydW5n?= + +This is +the body + +==================================================================== +bool(true) + diff --git a/framework/MIME/tests/mail_008.phpt b/framework/MIME/tests/mail_008.phpt new file mode 100644 index 000000000..a086b37f7 --- /dev/null +++ b/framework/MIME/tests/mail_008.phpt @@ -0,0 +1,39 @@ +--TEST-- +MIME_Mail flowed text test +--FILE-- +addHeader('Subject', 'My Subject'); +$mail->addHeader('To', 'recipient@example.com'); +$mail->setBody('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo +consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse +cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'); + +echo $mail->send('dummy'); + +?> +--EXPECTF-- +Subject: My Subject +To: recipient@example.com +Message-ID: <%d.%s@mail.example.com> +User-Agent: Horde Application Framework 3.2 +Date: %s, %d %s %d %d:%d:%d %s%d +MIME-Version: 1.0 +Content-Type: text/plain; + charset=iso-8859-1; + DelSp="Yes"; + format="flowed" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do +eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad +minim veniam, quis nostrud exercitation ullamco laboris nisi ut +aliquip ex ea commodo +consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse +cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat +cupidatat non proident, sunt in culpa qui officia deserunt mollit anim +id est laborum. \ No newline at end of file diff --git a/framework/MIME/tests/mail_dummy.inc b/framework/MIME/tests/mail_dummy.inc new file mode 100644 index 000000000..b32349931 --- /dev/null +++ b/framework/MIME/tests/mail_dummy.inc @@ -0,0 +1,15 @@ +_cleanHTML($data); +} + +?> +--EXPECT-- +link +link +link +link +link +link +link diff --git a/framework/MIME/tests/url1.html b/framework/MIME/tests/url1.html new file mode 100644 index 000000000..0c88c1490 --- /dev/null +++ b/framework/MIME/tests/url1.html @@ -0,0 +1 @@ +link diff --git a/framework/MIME/tests/url2.html b/framework/MIME/tests/url2.html new file mode 100644 index 000000000..94c45a2ca --- /dev/null +++ b/framework/MIME/tests/url2.html @@ -0,0 +1 @@ +link diff --git a/framework/MIME/tests/url3.html b/framework/MIME/tests/url3.html new file mode 100644 index 000000000..4c7eafed9 --- /dev/null +++ b/framework/MIME/tests/url3.html @@ -0,0 +1 @@ +link diff --git a/framework/MIME/tests/url4.html b/framework/MIME/tests/url4.html new file mode 100644 index 000000000..935612578 --- /dev/null +++ b/framework/MIME/tests/url4.html @@ -0,0 +1 @@ +link diff --git a/framework/MIME/tests/url5.html b/framework/MIME/tests/url5.html new file mode 100644 index 000000000..89dfbc68d --- /dev/null +++ b/framework/MIME/tests/url5.html @@ -0,0 +1 @@ +link diff --git a/framework/MIME/tests/url6.html b/framework/MIME/tests/url6.html new file mode 100644 index 000000000..73ad9fc17 --- /dev/null +++ b/framework/MIME/tests/url6.html @@ -0,0 +1 @@ +link diff --git a/framework/MIME/tests/url7.html b/framework/MIME/tests/url7.html new file mode 100644 index 000000000..cb5c80040 --- /dev/null +++ b/framework/MIME/tests/url7.html @@ -0,0 +1 @@ +link diff --git a/framework/MIME/tests/viewer_php.phpt b/framework/MIME/tests/viewer_php.phpt new file mode 100644 index 000000000..cceaa9198 --- /dev/null +++ b/framework/MIME/tests/viewer_php.phpt @@ -0,0 +1,21 @@ +--TEST-- +PHP source viewer +--FILE-- +lineNumber(str_replace('<?php ', '', highlight_string(' +--EXPECT-- +
    +
  1. highlight_file(__FILE__);
  2. +
\ No newline at end of file -- 2.11.0