From b941cf4566810d40d8fca12844913a614b089671 Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Tue, 7 Jul 2009 02:30:03 -0600 Subject: [PATCH] Remove dependency on Mail_mimeDecode. Previously, we were using Mail_mimeDecode to parse MIME message text and to parse MIME headers with parameter data. This code is more robust, cleaner, probably faster, and doesn't thrown errors in PHP 5 strict mode. In transition, added ability to get params and base of MIME headers directly. --- framework/Mime/lib/Horde/Mime.php | 93 ++++++- framework/Mime/lib/Horde/Mime/Headers.php | 71 +++-- framework/Mime/lib/Horde/Mime/Part.php | 265 ++++++++++--------- framework/Mime/package.xml | 11 +- .../Mime/test/Horde/Mime/fixtures/sample_msg.txt | 75 ++++++ framework/Mime/test/Horde/Mime/parse_001.phpt | 294 +++++++++++++++++++++ framework/Mime/test/Horde/Mime/parse_002.phpt | 20 ++ framework/Mime/test/Horde/Mime/parse_003.phpt | 26 ++ 8 files changed, 693 insertions(+), 162 deletions(-) create mode 100644 framework/Mime/test/Horde/Mime/fixtures/sample_msg.txt create mode 100644 framework/Mime/test/Horde/Mime/parse_001.phpt create mode 100644 framework/Mime/test/Horde/Mime/parse_002.phpt create mode 100644 framework/Mime/test/Horde/Mime/parse_003.phpt diff --git a/framework/Mime/lib/Horde/Mime.php b/framework/Mime/lib/Horde/Mime.php index d58ab0f76..c3e2f7e47 100644 --- a/framework/Mime/lib/Horde/Mime.php +++ b/framework/Mime/lib/Horde/Mime.php @@ -3,6 +3,48 @@ * The Horde_Mime:: class provides methods for dealing with various MIME (see, * e.g., RFC 2045-2049; 2183; 2231) standards. * + * ----- + * + * This file contains code adapted from PEAR's Mail_mimeDecode library (v1.5). + * + * http://pear.php.net/package/Mail_mime + * + * This code appears in Horde_Mime::decodeParam(). + * + * This code was originally released under this license: + * + * LICENSE: This LICENSE is in the BSD license style. + * Copyright (c) 2002-2003, Richard Heyes + * Copyright (c) 2003-2006, PEAR + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of the authors, nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * ----- + * * Copyright 1999-2009 The Horde Project (http://www.horde.org/) * * See the enclosed file COPYING for license information (LGPL). If you @@ -432,29 +474,52 @@ class Horde_Mime { $convert = array(); $ret = array('params' => array(), 'val' => ''); + $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; + $type = Horde_String::lower($type); if (is_array($data)) { // Use dummy base values - $ret['val'] = (Horde_String::lower($type) == 'content-type') + $ret['val'] = ($type == 'content-type') ? 'text/plain' : 'attachment'; $params = $data; } else { - /* Give $string a bogus body part or else decode() will - * complain. */ - $mime_decode = new Mail_mimeDecode($type . ': ' . $data . "\n\nA"); - $res = $mime_decode->decode(); - - /* Are we dealing with content-type or content-disposition? */ - if (isset($res->disposition)) { - $ret['val'] = $res->disposition; - $params = isset($res->d_parameters) ? $res->d_parameters : array(); - } elseif (isset($res->ctype_primary)) { - $ret['val'] = $res->ctype_primary . '/' . $res->ctype_secondary; - $params = isset($res->ctype_parameters) ? $res->ctype_parameters : array(); - } else { + /* This code was adapted from PEAR's Mail_mimeDecode::. */ + if (($pos = strpos($data, ';')) === false) { + $ret['val'] = trim($data); return $ret; } + + $ret['val'] = trim(substr($data, 0, $pos)); + $data = trim(substr($data, ++$pos)); + + if (strlen($data) > 0) { + $params = $tmp = array(); + + /* This splits on a semi-colon, if there's no preceeding + * backslash. */ + preg_match_all($splitRegex, $data, $matches); + + for ($i = 0, $cnt = count($matches[0]); $i < $cnt; ++$i) { + $param = $matches[0][$i]; + while (substr($param, -2) == '\;') { + $param .= $matches[0][++$i]; + } + $tmp[] = $param; + } + + for ($i = 0, $cnt = count($tmp); $i < $cnt; ++$i) { + $pos = strpos($tmp[$i], '='); + $p_name = trim(substr($tmp[$i], 0, $pos), "'\";\t\\ "); + $p_val = trim(str_replace('\;', ';', substr($tmp[$i], $pos + 1)), "'\";\t\\ "); + if ($p_val[0] == '"') { + $p_val = substr($param_value, 1, -1); + } + + $params[$p_name] = $p_val; + } + } + /* End of code adapted from PEAR's Mail_mimeDecode::. */ } /* Sort the params list. Prevents us from having to manually keep diff --git a/framework/Mime/lib/Horde/Mime/Headers.php b/framework/Mime/lib/Horde/Mime/Headers.php index 294a1c482..f578d0924 100644 --- a/framework/Mime/lib/Horde/Mime/Headers.php +++ b/framework/Mime/lib/Horde/Mime/Headers.php @@ -13,6 +13,11 @@ */ class Horde_Mime_Headers { + /* Constants for getValue(). */ + const VALUE_STRING = 1; + const VALUE_BASE = 2; + const VALUE_PARAMS = 3; + /** * The default charset to use when parsing text parts with no charset * information. @@ -387,28 +392,47 @@ class Horde_Mime_Headers * The values are not MIME encoded. * * @param string $header The header to search for. + * @param integer $type The type of return: + *
+     * VALUE_STRING - Returns a string representation of the entire header.
+     * VALUE_BASE - Returns a string representation of the base value of the
+     *              header. If this is not a header that allows parameters,
+     *              this will be equivalent to VALUE_BASE.
+     * VALUE_PARAMS - Returns the list of parameters for this header. If this
+     *                is not a header that allows parameters, this will be
+     *                an empty array.
+     * 
* * @return mixed The value for the given header. * If the header is not found, returns null. */ - public function getValue($header) + public function getValue($header, $type = self::VALUE_STRING) { - $entry = null; $header = Horde_String::lower($header); - if (isset($this->_headers[$header])) { - $ptr = &$this->_headers[$header]; - $entry = (is_array($ptr['value']) && in_array($header, $this->singleFields(true))) - ? $ptr['value'][0] - : $ptr['value']; - if (isset($ptr['params'])) { - foreach ($ptr['params'] as $key => $val) { - $entry .= '; ' . $key . '=' . $val; - } - } + if (!isset($this->_headers[$header])) { + return null; } - return $entry; + $ptr = &$this->_headers[$header]; + $base = (is_array($ptr['value']) && in_array($header, $this->singleFields(true))) + ? $ptr['value'][0] + : $ptr['value']; + $params = isset($ptr['params']) ? $ptr['params'] : array(); + + switch ($type) { + case self::VALUE_BASE: + return $base; + + case self::VALUE_PARAMS: + return $params; + + case self::VALUE_STRING: + foreach ($params as $key => $val) { + $base .= '; ' . $key . '=' . $val; + } + return $base; + } } /** @@ -551,19 +575,18 @@ class Horde_Mime_Headers $currtext .= ' ' . ltrim($val); } else { if (!is_null($currheader)) { - if (in_array(Horde_String::lower($currheader), $mime)) { - $res = Horde_Mime::decodeParam($currheader, $currtext); - $to_process[] = array($currheader, $res['val'], array('decode' => true, 'params' => $res['params'])); - } else { - $to_process[] = array($currheader, $currtext, array('decode' => true)); - } + $to_process[] = array($currheader, $currtext); } + $pos = strpos($val, ':'); $currheader = substr($val, 0, $pos); $currtext = ltrim(substr($val, $pos + 1)); } } - $to_process[] = array($currheader, $currtext, array('decode' => true)); + + if (!is_null($currheader)) { + $to_process[] = array($currheader, $currtext); + } $headers = new Horde_Mime_Headers(); $eightbit_check = (self::$defaultCharset != 'us-ascii'); @@ -573,7 +596,13 @@ class Horde_Mime_Headers if ($eightbit_check && Horde_Mime::is8bit($val[1])) { $val[1] = Horde_String::convertCharset($val[1], self::$defaultCharset); } - $headers->addHeader($val[0], $val[1], $val[2]); + + if (in_array(Horde_String::lower($val[0]), $mime)) { + $res = Horde_Mime::decodeParam($val[0], $val[1]); + $headers->addHeader($val[0], $res['val'], array('decode' => true, 'params' => $res['params'])); + } else { + $headers->addHeader($val[0], $val[1], array('decode' => true)); + } } return $headers; diff --git a/framework/Mime/lib/Horde/Mime/Part.php b/framework/Mime/lib/Horde/Mime/Part.php index 8d7b6063c..9fe8d64d9 100644 --- a/framework/Mime/lib/Horde/Mime/Part.php +++ b/framework/Mime/lib/Horde/Mime/Part.php @@ -1734,19 +1734,10 @@ class Horde_Mime_Part */ static public function parseMessage($text, $options = array()) { - /* Set up the options for the mimeDecode class. */ - $decode_args = array( - 'include_bodies' => true, - 'decode_bodies' => false, - 'decode_headers' => false - ); + /* Find the header. */ + list($hdr_pos, $eol) = self::_findHeader($text); - $mimeDecode = new Mail_mimeDecode($text, Horde_Mime_Part::EOL); - if (!($ob = $mimeDecode->decode($decode_args))) { - throw new Horde_Mime_Exception('Could not decode MIME message.'); - } - - $ob = self::_convertMimeDecodeData($ob); + $ob = self::_getStructure(substr($text, 0, $hdr_pos), substr($text, $hdr_pos + $eol)); return empty($options['structure']) ? self::parseStructure($ob) @@ -1754,87 +1745,85 @@ class Horde_Mime_Part } /** - * Convert the output from Mail_mimeDecode::decode() into a structure that - * parseStructure() can handle. + * Creates a structure object from the text of one part of a MIME message. * - * @param stdClass $ob The output from Mail_mimeDecode::decode(). + * @param string $header The header text. + * @param string $body The body text. + * @param string $ctype The default content-type. * - * @return array An array of structure information. + * @return array See Horde_Mime_Part::parseStructure(). */ - static protected function _convertMimeDecodeData($ob) + static protected function _getStructure($header, $body, + $ctype = 'application/octet-stream') { - /* 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' - ); + $part = array('parts' => array()); + + /* Parse headers text into a Horde_Mime_Headers object. */ + $hdrs = Horde_Mime_Headers::parseHeaders($header); + + /* Content type. */ + $tmp = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_BASE); + if (!$tmp) { + $tmp = $ctype; } + list($part['type'], $part['subtype']) = explode('/', strtolower($tmp), 2); /* Content transfer encoding. */ - if (isset($ob->headers['content-transfer-encoding'])) { - $part['encoding'] = strtolower($ob->headers['content-transfer-encoding']); + $tmp = $hdrs->getValue('content-transfer-encoding'); + if ($tmp) { + $part['encoding'] = strtolower($tmp); } - /* 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-Type and Disposition parameters. */ + $part['dparameters'] = $hdrs->getValue('content-disposition', Horde_Mime_Headers::VALUE_PARAMS); + $part['parameters'] = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_PARAMS); /* Content-Description. */ - if (isset($ob->headers['content-description'])) { - $part['description'] = $ob->headers['content-description']; + $tmp = $hdrs->getValue('content-description'); + if ($tmp) { + $part['description'] = $tmp; } /* 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); + $tmp = $hdrs->getValue('content-disposition', Horde_Mime_Headers::VALUE_BASE); + if ($tmp) { + $part['disposition'] = strtolower($tmp); } /* Content-ID. */ - if (isset($ob->headers['content-id'])) { - $part['id'] = $ob->headers['content-id']; + $tmp = $hdrs->getValue('content-id'); + if ($tmp) { + $part['id'] = $tmp; } /* Get file size (if 'body' text is set). */ - if (isset($ob->body)) { - $part['contents'] = $ob->body; + if (!empty($body)) { + $part['contents'] = $body; if (($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; + $part['size'] = strlen(str_replace(array("\r\n", "\n"), array("\n", "\r\n"), $body)); } } - /* 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]); + /* Process subparts. */ + switch ($part['type']) { + case 'message': + if ($part['subtype'] == 'rfc822') { + $part['parts'][] = self::parseMessage($body, array('structure' => true)); + } + break; + + case 'multipart': + $tmp = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_PARAMS); + if (isset($tmp['boundary'])) { + $b_find = self::_findBoundary($body, 0, $tmp['boundary']); + foreach ($b_find as $val) { + $subpart = substr($body, $val['start'], $val['length']); + list($hdr_pos, $eol) = self::_findHeader($subpart); + $part['parts'][] = self::_getStructure(substr($subpart, 0, $hdr_pos), substr($subpart, $hdr_pos + $eol), ($part['subtype'] == 'digest') ? 'message/rfc822' : 'text/plain'); + } } + break; } return $part; @@ -1854,90 +1843,122 @@ class Horde_Mime_Part */ static public function getRawPartText($text, $type, $id) { - return self::_getRawPartText($text, $type, $id, null); - } - - /** - * Obtain the raw text of a MIME part. - * - * @param string $text The full text of the MIME message. - * @param string $type Either 'header' or 'body'. - * @param string $id The MIME ID. - * @param string $boundary The boundary string. - * - * @return string The raw text. - * @throws Horde_Mime_Exception - */ - static protected function _getRawPartText($text, $type, $id, - $boundary = null) - { /* We need to carry around the trailing "\n" because this is needed * to correctly find the boundary string. */ - $hdr_pos = strpos($text, "\n\n"); - if ($hdr_pos === false) { - $hdr_pos = strpos($text, "\r\n\r\n"); - $curr_pos = $hdr_pos + 3; - } else { - $curr_pos = $hdr_pos + 1; - } + list($hdr_pos, $eol) = self::_findHeader($text); + $curr_pos = $hdr_pos + $eol - 1; if ($id == 0) { switch ($type) { case 'body': - if (is_null($boundary)) { - return substr($text, $curr_pos + 1); - } - $end_boundary = strpos($text, "\n--" . $boundary, $curr_pos); - if ($end_boundary === false) { - throw new Horde_Mime_Exception('Could not find MIME part.'); - } - return substr($text, $curr_pos + 1, $end_boundary - $curr_pos); + return substr($text, $curr_pos + 1); case 'header': return trim(substr($text, 0, $hdr_pos)); } } + $hdr_ob = Horde_Mime_Headers::parseHeaders(trim(substr($text, 0, $hdr_pos))); + + /* If this is a message/rfc822, pass the body into the next loop. + * Don't decrement the ID here. */ + if ($hdr_ob->getValue('Content-Type', Horde_Mime_Headers::VALUE_BASE) == 'message/rfc822') { + return self::getRawPartText(substr($text, $curr_pos + 1), $type, $id); + } + $base_pos = strpos($id, '.'); if ($base_pos !== false) { $base_pos = substr($id, 0, $base_pos); - $id = substr($id, $base_pos + 1); + $id = substr($id, $base_pos); } else { $base_pos = $id; $id = 0; } - $hdr_ob = Horde_Mime_Headers::parseHeaders(trim(substr($text, 0, $hdr_pos))); - $params = Horde_Mime::decodeParam('content-type', $hdr_ob->getValue('Content-Type')); - if (!isset($params['params']['boundary'])) { + $params = $hdr_ob->getValue('Content-Type', Horde_Mime_Headers::VALUE_PARAMS); + if (!isset($params['boundary'])) { throw new Horde_Mime_Exception('Could not find MIME part.'); } - $search = "\n--" . $params['params']['boundary']; + $b_find = self::_findBoundary($text, $curr_pos, $params['boundary'], $base_pos); + + if (!isset($b_find[$base_pos])) { + throw new Horde_Mime_Exception('Could not find MIME part.'); + } + + return self::getRawPartText(substr($text, $b_find[$base_pos]['start'], $b_find[$base_pos]['length']), $type, $id); + } + + /** + * Find the location of the end of the header text. + * + * @param string $text The text to search. + * + * @return array 1st element: Header position, 2nd element: Lenght of + * trailing EOL. + */ + static protected function _findHeader($text) + { + $hdr_pos = strpos($text, "\r\n\r\n"); + if ($hdr_pos !== false) { + return array($hdr_pos, 4); + } + + $hdr_pos = strpos($text, "\n\n"); + return ($hdr_pos === false) + ? array(strlen($text), 0) + : array($hdr_pos, 2); + } + + /** + * Find the location of the next boundary string. + * + * @param string $text The text to search. + * @param integer $pos The current position in $text. + * @param string $boundary The boundary string. + * @param integer $end If set, return after matching this many + * boundaries. + * + * @return array Keys are the boundary number, values are an array with + * two elements: 'start' and 'length'. + */ + static protected function _findBoundary($text, $pos, $boundary, + $end = null) + { + $i = 0; + $out = array(); + + $search = "\n--" . $boundary; $search_len = strlen($search); - for ($i = 0; $i < $base_pos; ++$i) { - $new_pos = strpos($text, $search, $curr_pos); - if ($new_pos !== false) { - $curr_pos = $new_pos + $search_len; - if (isset($text[$curr_pos + 1])) { - switch ($text[$curr_pos + 1]) { - case "\r": - ++$curr_pos; - break; - - case "\n": - // noop - break; - - case '-': - throw new Horde_Mime_Exception('Could not find MIME part.'); - } + while (($pos = strpos($text, $search, $pos)) !== false) { + if (isset($out[$i])) { + $out[$i]['length'] = $pos - $out[$i]['start']; + } + + if (!is_null($end) && ($end == $i)) { + break; + } + + $pos += $search_len; + if (isset($text[$pos])) { + switch ($text[$pos]) { + case "\r": + $pos += 2; + $out[++$i] = array('start' => $pos); + break; + + case "\n": + $out[++$i] = array('start' => ++$pos); + break; + + case '-': + return $out; } } } - return self::_getRawPartText(substr($text, $curr_pos), $type, $id, $params['params']['boundary']); + return $out; } } diff --git a/framework/Mime/package.xml b/framework/Mime/package.xml index 55d73637d..b24cb6dcb 100644 --- a/framework/Mime/package.xml +++ b/framework/Mime/package.xml @@ -31,7 +31,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> alpha LGPL - * Added Horde_Mime::uudecode(). + * Remove dependence on PEAR's Mail_mimeDecode::. + * Added Horde_Mime::uudecode(). * Remove support for deprecated mime_magic module. * Use Gnumeric to display MS Excel documents. * Use AbiWord to display MS Word documents (Request #8011). @@ -109,6 +110,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + @@ -130,6 +132,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + @@ -151,10 +156,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> pear.horde.org - Mail_mimeDecode - pear.php.net - - Util pear.horde.org diff --git a/framework/Mime/test/Horde/Mime/fixtures/sample_msg.txt b/framework/Mime/test/Horde/Mime/fixtures/sample_msg.txt new file mode 100644 index 000000000..31d82e453 --- /dev/null +++ b/framework/Mime/test/Horde/Mime/fixtures/sample_msg.txt @@ -0,0 +1,75 @@ +Return-Path: +Delivered-To: foo@example.com +Received: from localhost (localhost [127.0.0.1]) + by example.com (Postfix) with ESMTP id 87C5B4F220 + for ; Tue, 7 Jul 2009 11:51:18 -0600 (MDT) +Message-ID: <20090707115118.ooc4wo40oc88@example.com> +Date: Tue, 07 Jul 2009 11:51:18 -0600 +From: "Test Q. User" +To: foo@example.com +Subject: Fwd: Test +User-Agent: Internet Messaging Program (IMP) H4 (5.0-git) +Content-Type: multipart/mixed; boundary="=_k4kgcwkwggwc" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +This message is in MIME format. + +--=_k4kgcwkwggwc +Content-Type: text/plain; charset=UTF-8; format=flowed; DelSp=Yes +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +Test. + + +--=_k4kgcwkwggwc +Content-Type: message/rfc822; name="Forwarded Message" + +Return-Path: +Delivered-To: test@example.com +Received: from localhost (localhost [127.0.0.1]) + by example.com (Postfix) with ESMTP id B09464F220 + for ; Tue, 7 Jul 2009 11:49:00 -0600 (MDT) +Message-ID: <20090707114900.4w4ksggowkc4@example.com> +Date: Tue, 07 Jul 2009 11:49:00 -0600 +From: Foo +To: test@example.com +Subject: Test +User-Agent: Internet Messaging Program (IMP) H4 (5.0-git) +Content-Type: multipart/mixed; boundary="=_8w0gkwkgk44o" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +This message is in MIME format. + +--=_8w0gkwkgk44o +Content-Type: text/plain; charset=UTF-8; format=flowed; DelSp=Yes +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +Test text. + + +--=_8w0gkwkgk44o +Content-Type: text/plain; charset=UTF-8; name=test.txt +Content-Disposition: attachment; filename=test.txt +Content-Transfer-Encoding: 7bit + +Test. + +--=_8w0gkwkgk44o-- + + +--=_k4kgcwkwggwc +Content-Type: image/png; name=index.png +Content-Disposition: attachment; filename=index.png +Content-Transfer-Encoding: base64 + +iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0 +U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKO +giihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQI +V2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4tr +RCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0 +gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC +--=_k4kgcwkwggwc-- diff --git a/framework/Mime/test/Horde/Mime/parse_001.phpt b/framework/Mime/test/Horde/Mime/parse_001.phpt new file mode 100644 index 000000000..97230d9b6 --- /dev/null +++ b/framework/Mime/test/Horde/Mime/parse_001.phpt @@ -0,0 +1,294 @@ +--TEST-- +Horde_Mime_Part::parseMessage() [Structure array] test +--FILE-- + true))); + +?> +--EXPECTF-- +Array +( + [parts] => Array + ( + [0] => Array + ( + [parts] => Array + ( + ) + + [subtype] => plain + [type] => text + [encoding] => 7bit + [dparameters] => Array + ( + ) + + [parameters] => Array + ( + [charset] => UTF-8 + [DelSp] => Yes + [format] => flowed + ) + + [disposition] => inline + [contents] => Test. + + + [size] => 9 + ) + + [1] => Array + ( + [parts] => Array + ( + [0] => Array + ( + [parts] => Array + ( + [0] => Array + ( + [parts] => Array + ( + ) + + [subtype] => plain + [type] => text + [encoding] => 7bit + [dparameters] => Array + ( + ) + + [parameters] => Array + ( + [charset] => UTF-8 + [DelSp] => Yes + [format] => flowed + ) + + [disposition] => inline + [contents] => Test text. + + + [size] => 14 + ) + + [1] => Array + ( + [parts] => Array + ( + ) + + [subtype] => plain + [type] => text + [encoding] => 7bit + [dparameters] => Array + ( + [filename] => test.txt + ) + + [parameters] => Array + ( + [charset] => UTF-8 + [name] => test.txt + ) + + [disposition] => attachment + [contents] => Test. + + [size] => 7 + ) + + ) + + [subtype] => mixed + [type] => multipart + [encoding] => 7bit + [dparameters] => + [parameters] => Array + ( + [boundary] => =_8w0gkwkgk44o + ) + + [contents] => This message is in MIME format. + +--=_8w0gkwkgk44o +Content-Type: text/plain; charset=UTF-8; format=flowed; DelSp=Yes +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +Test text. + + +--=_8w0gkwkgk44o +Content-Type: text/plain; charset=UTF-8; name=test.txt +Content-Disposition: attachment; filename=test.txt +Content-Transfer-Encoding: 7bit + +Test. + +--=_8w0gkwkgk44o-- + + + [size] => 392 + ) + + ) + + [subtype] => rfc822 + [type] => message + [dparameters] => + [parameters] => Array + ( + [name] => Forwarded Message + ) + + [contents] => Return-Path: +Delivered-To: test@example.com +Received: from localhost (localhost [127.0.0.1]) + by example.com (Postfix) with ESMTP id B09464F220 + for ; Tue, 7 Jul 2009 11:49:00 -0600 (MDT) +Message-ID: <20090707114900.4w4ksggowkc4@example.com> +Date: Tue, 07 Jul 2009 11:49:00 -0600 +From: Foo +To: test@example.com +Subject: Test +User-Agent: Internet Messaging Program (IMP) H4 (5.0-git) +Content-Type: multipart/mixed; boundary="=_8w0gkwkgk44o" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +This message is in MIME format. + +--=_8w0gkwkgk44o +Content-Type: text/plain; charset=UTF-8; format=flowed; DelSp=Yes +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +Test text. + + +--=_8w0gkwkgk44o +Content-Type: text/plain; charset=UTF-8; name=test.txt +Content-Disposition: attachment; filename=test.txt +Content-Transfer-Encoding: 7bit + +Test. + +--=_8w0gkwkgk44o-- + + + ) + + [2] => Array + ( + [parts] => Array + ( + ) + + [subtype] => png + [type] => image + [encoding] => base64 + [dparameters] => Array + ( + [filename] => index.png + ) + + [parameters] => Array + ( + [name] => index.png + ) + + [disposition] => attachment + [contents] => iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0 +U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKO +giihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQI +V2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4tr +RCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0 +gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC + [size] => 466 + ) + + ) + + [subtype] => mixed + [type] => multipart + [encoding] => 7bit + [dparameters] => + [parameters] => Array + ( + [boundary] => =_k4kgcwkwggwc + ) + + [contents] => This message is in MIME format. + +--=_k4kgcwkwggwc +Content-Type: text/plain; charset=UTF-8; format=flowed; DelSp=Yes +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +Test. + + +--=_k4kgcwkwggwc +Content-Type: message/rfc822; name="Forwarded Message" + +Return-Path: +Delivered-To: test@example.com +Received: from localhost (localhost [127.0.0.1]) + by example.com (Postfix) with ESMTP id B09464F220 + for ; Tue, 7 Jul 2009 11:49:00 -0600 (MDT) +Message-ID: <20090707114900.4w4ksggowkc4@example.com> +Date: Tue, 07 Jul 2009 11:49:00 -0600 +From: Foo +To: test@example.com +Subject: Test +User-Agent: Internet Messaging Program (IMP) H4 (5.0-git) +Content-Type: multipart/mixed; boundary="=_8w0gkwkgk44o" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +This message is in MIME format. + +--=_8w0gkwkgk44o +Content-Type: text/plain; charset=UTF-8; format=flowed; DelSp=Yes +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +Test text. + + +--=_8w0gkwkgk44o +Content-Type: text/plain; charset=UTF-8; name=test.txt +Content-Disposition: attachment; filename=test.txt +Content-Transfer-Encoding: 7bit + +Test. + +--=_8w0gkwkgk44o-- + + +--=_k4kgcwkwggwc +Content-Type: image/png; name=index.png +Content-Disposition: attachment; filename=index.png +Content-Transfer-Encoding: base64 + +iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0 +U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKO +giihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQI +V2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4tr +RCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0 +gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC +--=_k4kgcwkwggwc-- + + [size] => 1869 +) diff --git a/framework/Mime/test/Horde/Mime/parse_002.phpt b/framework/Mime/test/Horde/Mime/parse_002.phpt new file mode 100644 index 000000000..53061ac86 --- /dev/null +++ b/framework/Mime/test/Horde/Mime/parse_002.phpt @@ -0,0 +1,20 @@ +--TEST-- +Horde_Mime_Part::parseMessage() [Horde_Mime_Part] test +--FILE-- +getType()); + +?> +--EXPECTF-- +multipart/mixed diff --git a/framework/Mime/test/Horde/Mime/parse_003.phpt b/framework/Mime/test/Horde/Mime/parse_003.phpt new file mode 100644 index 000000000..fd9500706 --- /dev/null +++ b/framework/Mime/test/Horde/Mime/parse_003.phpt @@ -0,0 +1,26 @@ +--TEST-- +Horde_Mime_Part::getRawPartText() test +--FILE-- + +--EXPECTF-- +Test text. + +--- +Content-Type: image/png; name=index.png +Content-Disposition: attachment; filename=index.png +Content-Transfer-Encoding: base64 -- 2.11.0