* @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: mimeDecode.php,v 1.48 2006/12/03 13:43:33 cipri Exp $
+ * @version CVS: $Id: mimeDecode.php 305875 2010-12-01 07:17:10Z alan_k $
* @link http://pear.php.net/package/Mail_mime
*/
*/
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) {
+ if (($pos = strpos($input, ';')) === false) {
+ $input = $this->_decode_headers ? $this->_decodeHeader($input) : $input;
+ $return['value'] = trim($input);
+ return $return;
+ }
+
+
- $return['value'] = trim(substr($input, 0, $pos));
- $input = trim(substr($input, $pos+1));
+ $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) {
+ 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));
- // 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];
+ $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;
}
- $parameters[] = $param;
+ $val = false;
+ $key = '';
+ $lq = false;
+ $i++;
+ continue;
}
- 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);
+ $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;
}
- $return['other'][$param_name] = $param_value;
- $return['other'][strtolower($param_name)] = $param_value;
+ $added = true;
}
+ // continue and use standard setters..
+ }
+ if (!$added) {
+ $return['other'][$key] = $val;
+ $return['other'][strtolower($key)] = $val;
}
- } else {
- $return['value'] = trim($input);
}
-
+ // 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;
}