From 3412e50b54e3daac8745234e21ab6e72be0ed165 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Wed, 04 Jun 2014 11:20:33 -0400
Subject: [PATCH] Fix attachment menu structure and aria-attributes
---
program/lib/Mail/mimeDecode.php | 439 +++++++++++++++++++++++++++++++++++++-----------------
1 files changed, 300 insertions(+), 139 deletions(-)
diff --git a/program/lib/Mail/mimeDecode.php b/program/lib/Mail/mimeDecode.php
index 92827b7..9f45894 100644
--- a/program/lib/Mail/mimeDecode.php
+++ b/program/lib/Mail/mimeDecode.php
@@ -1,139 +1,160 @@
<?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: mimeDecode.php 305875 2010-12-01 07:17:10Z alan_k $
+ * @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 Implement multipart/appledouble
-* 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: 1.5.5
+ * @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.
@@ -153,6 +174,7 @@
$this->_body = $body;
$this->_decode_bodies = false;
$this->_include_bodies = true;
+ $this->_rfc822_bodies = false;
}
/**
@@ -175,7 +197,7 @@
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.
@@ -196,6 +218,8 @@
$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) {
@@ -223,6 +247,7 @@
$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'];
@@ -235,8 +260,8 @@
}
}
- reset($headers);
- while (list($key, $value) = each($headers)) {
+
+ foreach ($headers as $key => $value) {
$headers[$key]['name'] = strtolower($headers[$key]['name']);
switch ($headers[$key]['name']) {
@@ -249,7 +274,7 @@
}
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;
}
}
@@ -259,7 +284,7 @@
$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;
}
}
@@ -284,18 +309,21 @@
break;
case 'multipart/parallel':
+ case 'multipart/appledouble': // Appledouble mail
case 'multipart/report': // RFC1892
case 'multipart/signed': // PGP
case 'multipart/digest':
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;
}
-
+
$default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
+
$parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
for ($i = 0; $i < count($parts); $i++) {
list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
@@ -307,7 +335,11 @@
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));
@@ -387,6 +419,11 @@
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;
}
@@ -405,7 +442,12 @@
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) {
@@ -416,7 +458,7 @@
$return[] = array(
'name' => $hdr_name,
- 'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
+ 'value' => $hdr_value
);
}
} else {
@@ -440,41 +482,161 @@
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;
}
@@ -496,19 +658,19 @@
if ($boundary == $bs_check) {
$boundary = $bs_possible;
}
+ $tmp = preg_split("/--".preg_quote($boundary, '/')."((?=\s)|--)/", $input);
- $tmp = explode('--' . $boundary, $input);
- $count = count($tmp);
-
- // when boundaries are set correctly we should have at least 3 parts;
- // if not, return the last one (tbr)
- if ($count<3)
- return array($tmp[$count-1]);
-
- for ($i = 1; $i < $count - 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;
}
@@ -711,7 +873,7 @@
case "to":
case "cc":
case "bcc":
- $to = ",".$item['value'];
+ $to .= ",".$item['value'];
default:
break;
}
@@ -839,4 +1001,3 @@
}
} // End of class
-?>
--
Gitblit v1.9.1