| | |
| | | <?php |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | // +-----------------------------------------------------------------------+ |
| | | // | Copyright (c) 2002-2003 Richard Heyes | |
| | | // | Copyright (c) 2003-2005 The PHP Group | |
| | | // | All rights reserved. | |
| | | // | | |
| | | // | Redistribution and use in source and binary forms, with or without | |
| | | // | modification, are permitted provided that the following conditions | |
| | | // | are met: | |
| | | // | | |
| | | // | o Redistributions of source code must retain the above copyright | |
| | | // | notice, this list of conditions and the following disclaimer. | |
| | | // | o 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.| |
| | | // | o The names of the authors may not 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. | |
| | | // | | |
| | | // +-----------------------------------------------------------------------+ |
| | | // | Author: Richard Heyes <richard@phpguru.org> | |
| | | // +-----------------------------------------------------------------------+ |
| | | /** |
| | | * The Mail_mimeDecode class is used to decode mail/mime messages |
| | | * |
| | | * This class will parse a raw mime email and return |
| | | * the structure. Returned structure is similar to |
| | | * that returned by imap_fetchstructure(). |
| | | * |
| | | * +----------------------------- IMPORTANT ------------------------------+ |
| | | * | Usage of this class compared to native php extensions such as | |
| | | * | mailparse or imap, is slow and may be feature deficient. If available| |
| | | * | you are STRONGLY recommended to use the php extensions. | |
| | | * +----------------------------------------------------------------------+ |
| | | * |
| | | * Compatible with PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This LICENSE is in the BSD license style. |
| | | * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org> |
| | | * Copyright (c) 2003-2006, PEAR <pear-group@php.net> |
| | | * 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. |
| | | * |
| | | * @category Mail |
| | | * @package Mail_Mime |
| | | * @author Richard Heyes <richard@phpguru.org> |
| | | * @author George Schlossnagle <george@omniti.com> |
| | | * @author Cipriano Groenendal <cipri@php.net> |
| | | * @author Sean Coates <sean@php.net> |
| | | * @copyright 2003-2006 PEAR <pear-group@php.net> |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/Mail_mime |
| | | */ |
| | | |
| | | require_once 'PEAR.php'; |
| | | |
| | | /** |
| | | * +----------------------------- IMPORTANT ------------------------------+ |
| | | * | Usage of this class compared to native php extensions such as | |
| | | * | mailparse or imap, is slow and may be feature deficient. If available| |
| | | * | you are STRONGLY recommended to use the php extensions. | |
| | | * +----------------------------------------------------------------------+ |
| | | * |
| | | * Mime Decoding class |
| | | * |
| | | * This class will parse a raw mime email and return |
| | | * the structure. Returned structure is similar to |
| | | * that returned by imap_fetchstructure(). |
| | | * |
| | | * USAGE: (assume $input is your raw email) |
| | | * |
| | | * $decode = new Mail_mimeDecode($input, "\r\n"); |
| | | * $structure = $decode->decode(); |
| | | * print_r($structure); |
| | | * |
| | | * Or statically: |
| | | * |
| | | * $params['input'] = $input; |
| | | * $structure = Mail_mimeDecode::decode($params); |
| | | * print_r($structure); |
| | | * |
| | | * TODO: |
| | | * o UTF8: ??? |
| | | * require PEAR |
| | | * |
| | | * This package depends on PEAR to raise errors. |
| | | */ |
| | | require_once 'PEAR.php'; |
| | | |
| | | > 4. We have also found a solution for decoding the UTF-8 |
| | | > headers. Therefore I made the following function: |
| | | > |
| | | > function decode_utf8($txt) { |
| | | > $trans=array("Å‘"=>"õ","ű"=>"û","Å"=>"Õ","Å°" |
| | | =>"Û"); |
| | | > $txt=strtr($txt,$trans); |
| | | > return(utf8_decode($txt)); |
| | | > } |
| | | > |
| | | > And I have inserted the following line to the class: |
| | | > |
| | | > if (strtolower($charset)=="utf-8") $text=decode_utf8($text); |
| | | > |
| | | > ... before the following one in the "_decodeHeader" function: |
| | | > |
| | | > $input = str_replace($encoded, $text, $input); |
| | | > |
| | | > This way from now on it can easily decode the UTF-8 headers too. |
| | | |
| | | * |
| | | * @author Richard Heyes <richard@phpguru.org> |
| | | * @version $Revision$ |
| | | * @package Mail |
| | | */ |
| | | /** |
| | | * The Mail_mimeDecode class is used to decode mail/mime messages |
| | | * |
| | | * This class will parse a raw mime email and return the structure. |
| | | * Returned structure is similar to that returned by imap_fetchstructure(). |
| | | * |
| | | * +----------------------------- IMPORTANT ------------------------------+ |
| | | * | Usage of this class compared to native php extensions such as | |
| | | * | mailparse or imap, is slow and may be feature deficient. If available| |
| | | * | you are STRONGLY recommended to use the php extensions. | |
| | | * +----------------------------------------------------------------------+ |
| | | * |
| | | * @category Mail |
| | | * @package Mail_Mime |
| | | * @author Richard Heyes <richard@phpguru.org> |
| | | * @author George Schlossnagle <george@omniti.com> |
| | | * @author Cipriano Groenendal <cipri@php.net> |
| | | * @author Sean Coates <sean@php.net> |
| | | * @copyright 2003-2006 PEAR <pear-group@php.net> |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/Mail_mime |
| | | */ |
| | | class Mail_mimeDecode extends PEAR |
| | | { |
| | | /** |
| | | * The raw email to decode |
| | | * |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_input; |
| | | |
| | | /** |
| | | * The header part of the input |
| | | * |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_header; |
| | | |
| | | /** |
| | | * The body part of the input |
| | | * |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_body; |
| | | |
| | | /** |
| | | * If an error occurs, this is used to store the message |
| | | * |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_error; |
| | | |
| | | /** |
| | | * Flag to determine whether to include bodies in the |
| | | * returned object. |
| | | * |
| | | * @var boolean |
| | | * @access private |
| | | */ |
| | | var $_include_bodies; |
| | | |
| | | /** |
| | | * Flag to determine whether to decode bodies |
| | | * |
| | | * @var boolean |
| | | * @access private |
| | | */ |
| | | var $_decode_bodies; |
| | | |
| | | /** |
| | | * Flag to determine whether to decode headers |
| | | * |
| | | * @var boolean |
| | | * @access private |
| | | */ |
| | | var $_decode_headers; |
| | | |
| | | /** |
| | | * Flag to determine whether to include attached messages |
| | | * as body in the returned object. Depends on $_include_bodies |
| | | * |
| | | * @var boolean |
| | | * @access private |
| | | */ |
| | | var $_rfc822_bodies; |
| | | |
| | | /** |
| | | * Constructor. |
| | |
| | | $this->_body = $body; |
| | | $this->_decode_bodies = false; |
| | | $this->_include_bodies = true; |
| | | $this->_rfc822_bodies = false; |
| | | } |
| | | |
| | | /** |
| | |
| | | function decode($params = null) |
| | | { |
| | | // determine if this method has been called statically |
| | | $isStatic = !(isset($this) && get_class($this) == __CLASS__); |
| | | $isStatic = empty($this) || !is_a($this, __CLASS__); |
| | | |
| | | // Have we been called statically? |
| | | // If so, create an object and pass details to that. |
| | |
| | | $params['decode_bodies'] : false; |
| | | $this->_decode_headers = isset($params['decode_headers']) ? |
| | | $params['decode_headers'] : false; |
| | | $this->_rfc822_bodies = isset($params['rfc_822bodies']) ? |
| | | $params['rfc_822bodies'] : false; |
| | | |
| | | $structure = $this->_decode($this->_header, $this->_body); |
| | | if ($structure === false) { |
| | |
| | | $headers = $this->_parseHeaders($headers); |
| | | |
| | | foreach ($headers as $value) { |
| | | $value['value'] = $this->_decode_headers ? $this->_decodeHeader($value['value']) : $value['value']; |
| | | if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { |
| | | $return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]); |
| | | $return->headers[strtolower($value['name'])][] = $value['value']; |
| | |
| | | } |
| | | } |
| | | |
| | | reset($headers); |
| | | while (list($key, $value) = each($headers)) { |
| | | |
| | | foreach ($headers as $key => $value) { |
| | | $headers[$key]['name'] = strtolower($headers[$key]['name']); |
| | | switch ($headers[$key]['name']) { |
| | | |
| | |
| | | } |
| | | |
| | | if (isset($content_type['other'])) { |
| | | while (list($p_name, $p_value) = each($content_type['other'])) { |
| | | foreach($content_type['other'] as $p_name => $p_value) { |
| | | $return->ctype_parameters[$p_name] = $p_value; |
| | | } |
| | | } |
| | |
| | | $content_disposition = $this->_parseHeaderValue($headers[$key]['value']); |
| | | $return->disposition = $content_disposition['value']; |
| | | if (isset($content_disposition['other'])) { |
| | | while (list($p_name, $p_value) = each($content_disposition['other'])) { |
| | | foreach($content_disposition['other'] as $p_name => $p_value) { |
| | | $return->d_parameters[$p_name] = $p_value; |
| | | } |
| | | } |
| | |
| | | case 'multipart/alternative': |
| | | case 'multipart/related': |
| | | case 'multipart/mixed': |
| | | case 'application/vnd.wap.multipart.related': |
| | | if(!isset($content_type['other']['boundary'])){ |
| | | $this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; |
| | | return false; |
| | |
| | | break; |
| | | |
| | | case 'message/rfc822': |
| | | $obj = &new Mail_mimeDecode($body); |
| | | if ($this->_rfc822_bodies) { |
| | | $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; |
| | | $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body); |
| | | } |
| | | $obj = new Mail_mimeDecode($body); |
| | | $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, |
| | | 'decode_bodies' => $this->_decode_bodies, |
| | | 'decode_headers' => $this->_decode_headers)); |
| | |
| | | if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { |
| | | return array($match[1], $match[2]); |
| | | } |
| | | // bug #17325 - empty bodies are allowed. - we just check that at least one line |
| | | // of headers exist.. |
| | | if (count(explode("\n",$input))) { |
| | | return array($input, ''); |
| | | } |
| | | $this->_error = 'Could not split header and body'; |
| | | return false; |
| | | } |
| | |
| | | if ($input !== '') { |
| | | // Unfold the input |
| | | $input = preg_replace("/\r?\n/", "\r\n", $input); |
| | | //#7065 - wrapping.. with encoded stuff.. - probably not needed, |
| | | // wrapping space should only get removed if the trailing item on previous line is a |
| | | // encoded character |
| | | $input = preg_replace("/=\r\n(\t| )+/", '=', $input); |
| | | $input = preg_replace("/\r\n(\t| )+/", ' ', $input); |
| | | |
| | | $headers = explode("\r\n", trim($input)); |
| | | |
| | | foreach ($headers as $value) { |
| | |
| | | |
| | | $return[] = array( |
| | | 'name' => $hdr_name, |
| | | 'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value |
| | | 'value' => $hdr_value |
| | | ); |
| | | } |
| | | } else { |
| | |
| | | function _parseHeaderValue($input) |
| | | { |
| | | |
| | | if (($pos = strpos($input, ';')) !== false) { |
| | | |
| | | $return['value'] = trim(substr($input, 0, $pos)); |
| | | $input = trim(substr($input, $pos+1)); |
| | | |
| | | if (strlen($input) > 0) { |
| | | |
| | | // This splits on a semi-colon, if there's no preceeding backslash |
| | | // Now works with quoted values; had to glue the \; breaks in PHP |
| | | // the regex is already bordering on incomprehensible |
| | | $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; |
| | | preg_match_all($splitRegex, $input, $matches); |
| | | $parameters = array(); |
| | | for ($i=0; $i<count($matches[0]); $i++) { |
| | | $param = $matches[0][$i]; |
| | | while (substr($param, -2) == '\;') { |
| | | $param .= $matches[0][++$i]; |
| | | } |
| | | $parameters[] = $param; |
| | | } |
| | | |
| | | for ($i = 0; $i < count($parameters); $i++) { |
| | | $param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ "); |
| | | $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ "); |
| | | if ($param_value[0] == '"') { |
| | | $param_value = substr($param_value, 1, -1); |
| | | } |
| | | $return['other'][$param_name] = $param_value; |
| | | $return['other'][strtolower($param_name)] = $param_value; |
| | | } |
| | | } |
| | | } else { |
| | | if (($pos = strpos($input, ';')) === false) { |
| | | $input = $this->_decode_headers ? $this->_decodeHeader($input) : $input; |
| | | $return['value'] = trim($input); |
| | | return $return; |
| | | } |
| | | |
| | | |
| | | |
| | | $value = substr($input, 0, $pos); |
| | | $value = $this->_decode_headers ? $this->_decodeHeader($value) : $value; |
| | | $return['value'] = trim($value); |
| | | $input = trim(substr($input, $pos+1)); |
| | | |
| | | if (!strlen($input) > 0) { |
| | | return $return; |
| | | } |
| | | // at this point input contains xxxx=".....";zzzz="...." |
| | | // since we are dealing with quoted strings, we need to handle this properly.. |
| | | $i = 0; |
| | | $l = strlen($input); |
| | | $key = ''; |
| | | $val = false; // our string - including quotes.. |
| | | $q = false; // in quote.. |
| | | $lq = ''; // last quote.. |
| | | |
| | | while ($i < $l) { |
| | | |
| | | $c = $input[$i]; |
| | | //var_dump(array('i'=>$i,'c'=>$c,'q'=>$q, 'lq'=>$lq, 'key'=>$key, 'val' =>$val)); |
| | | |
| | | $escaped = false; |
| | | if ($c == '\\') { |
| | | $i++; |
| | | if ($i == $l-1) { // end of string. |
| | | break; |
| | | } |
| | | $escaped = true; |
| | | $c = $input[$i]; |
| | | } |
| | | |
| | | |
| | | // state - in key.. |
| | | if ($val === false) { |
| | | if (!$escaped && $c == '=') { |
| | | $val = ''; |
| | | $key = trim($key); |
| | | $i++; |
| | | continue; |
| | | } |
| | | if (!$escaped && $c == ';') { |
| | | if ($key) { // a key without a value.. |
| | | $key= trim($key); |
| | | $return['other'][$key] = ''; |
| | | $return['other'][strtolower($key)] = ''; |
| | | } |
| | | $key = ''; |
| | | } |
| | | $key .= $c; |
| | | $i++; |
| | | continue; |
| | | } |
| | | |
| | | // state - in value.. (as $val is set..) |
| | | |
| | | if ($q === false) { |
| | | // not in quote yet. |
| | | if ((!strlen($val) || $lq !== false) && $c == ' ' || $c == "\t") { |
| | | $i++; |
| | | continue; // skip leading spaces after '=' or after '"' |
| | | } |
| | | if (!$escaped && ($c == '"' || $c == "'")) { |
| | | // start quoted area.. |
| | | $q = $c; |
| | | // in theory should not happen raw text in value part.. |
| | | // but we will handle it as a merged part of the string.. |
| | | $val = !strlen(trim($val)) ? '' : trim($val); |
| | | $i++; |
| | | continue; |
| | | } |
| | | // got end.... |
| | | if (!$escaped && $c == ';') { |
| | | |
| | | $val = trim($val); |
| | | $added = false; |
| | | if (preg_match('/\*[0-9]+$/', $key)) { |
| | | // this is the extended aaa*0=...;aaa*1=.... code |
| | | // it assumes the pieces arrive in order, and are valid... |
| | | $key = preg_replace('/\*[0-9]+$/', '', $key); |
| | | if (isset($return['other'][$key])) { |
| | | $return['other'][$key] .= $val; |
| | | if (strtolower($key) != $key) { |
| | | $return['other'][strtolower($key)] .= $val; |
| | | } |
| | | $added = true; |
| | | } |
| | | // continue and use standard setters.. |
| | | } |
| | | if (!$added) { |
| | | $return['other'][$key] = $val; |
| | | $return['other'][strtolower($key)] = $val; |
| | | } |
| | | $val = false; |
| | | $key = ''; |
| | | $lq = false; |
| | | $i++; |
| | | continue; |
| | | } |
| | | |
| | | $val .= $c; |
| | | $i++; |
| | | continue; |
| | | } |
| | | |
| | | // state - in quote.. |
| | | if (!$escaped && $c == $q) { // potential exit state.. |
| | | |
| | | // end of quoted string.. |
| | | $lq = $q; |
| | | $q = false; |
| | | $i++; |
| | | continue; |
| | | } |
| | | |
| | | // normal char inside of quoted string.. |
| | | $val.= $c; |
| | | $i++; |
| | | } |
| | | |
| | | // do we have anything left.. |
| | | if (strlen(trim($key)) || $val !== false) { |
| | | |
| | | $val = trim($val); |
| | | $added = false; |
| | | if ($val !== false && preg_match('/\*[0-9]+$/', $key)) { |
| | | // no dupes due to our crazy regexp. |
| | | $key = preg_replace('/\*[0-9]+$/', '', $key); |
| | | if (isset($return['other'][$key])) { |
| | | $return['other'][$key] .= $val; |
| | | if (strtolower($key) != $key) { |
| | | $return['other'][strtolower($key)] .= $val; |
| | | } |
| | | $added = true; |
| | | } |
| | | // continue and use standard setters.. |
| | | } |
| | | if (!$added) { |
| | | $return['other'][$key] = $val; |
| | | $return['other'][strtolower($key)] = $val; |
| | | } |
| | | } |
| | | // decode values. |
| | | foreach($return['other'] as $key =>$val) { |
| | | $return['other'][$key] = $this->_decode_headers ? $this->_decodeHeader($val) : $val; |
| | | } |
| | | //print_r($return); |
| | | return $return; |
| | | } |
| | | |
| | |
| | | if ($boundary == $bs_check) { |
| | | $boundary = $bs_possible; |
| | | } |
| | | $tmp = preg_split("/--".preg_quote($boundary, '/')."((?=\s)|--)/", $input); |
| | | |
| | | $tmp = explode('--' . $boundary, $input); |
| | | |
| | | for ($i = 1; $i < count($tmp) - 1; $i++) { |
| | | $parts[] = $tmp[$i]; |
| | | $len = count($tmp) -1; |
| | | for ($i = 1; $i < $len; $i++) { |
| | | if (strlen(trim($tmp[$i]))) { |
| | | $parts[] = $tmp[$i]; |
| | | } |
| | | } |
| | | |
| | | |
| | | // add the last part on if it does not end with the 'closing indicator' |
| | | if (!empty($tmp[$len]) && strlen(trim($tmp[$len])) && $tmp[$len][0] != '-') { |
| | | $parts[] = $tmp[$len]; |
| | | } |
| | | return $parts; |
| | | } |
| | | |
| | |
| | | case "to": |
| | | case "cc": |
| | | case "bcc": |
| | | $to = ",".$item['value']; |
| | | $to .= ",".$item['value']; |
| | | default: |
| | | break; |
| | | } |
| | |
| | | } |
| | | |
| | | } // End of class |
| | | ?> |