From: Michael M Slusarz Date: Tue, 2 Dec 2008 20:20:26 +0000 (-0700) Subject: Finish cleanup/fixes of RFC 2231 related stuff. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=297a3a6cb5d348a0732172371d59e8d638cde395;p=horde.git Finish cleanup/fixes of RFC 2231 related stuff. --- diff --git a/framework/Mime/lib/Horde/Mime.php b/framework/Mime/lib/Horde/Mime.php index 55fd4f984..3d54935f6 100644 --- a/framework/Mime/lib/Horde/Mime.php +++ b/framework/Mime/lib/Horde/Mime.php @@ -371,59 +371,81 @@ class Horde_Mime $output[$name . (($wrap) ? ('*' . $i++) : '') . (($encode) ? '*' : '')] = $line; } - if (self::$brokenRFC2231 && !isset($output[$name])) { - $output[$name] = self::encode($val, $charset); - } - - return $output; + return (self::$brokenRFC2231 && !isset($output[$name])) + ? array_merge(array($name => self::encode($val, $charset)), $output) + : $output; } /** - * Decodes a header string encoded pursuant to RFC 2231. + * Decodes a MIME parameter string pursuant to RFC 2183 & 2231 + * (Content-Type and Content-Disposition headers). * - * @param string $string The string to decode. - * @param string $to_charset The charset the text should be decoded to. + * @param string $string The full header to decode (including the header + * name). + * @param string $charset The charset the text should be decoded to. + * Defaults to system charset. * * @return array An array with the following entries: *
+     * 'params' - (array) The header's parameter values.
+     * 'val' - (string) The header's "base" value.
      * 
*/ - static public function decodeParamString($string, $to_charset = null) + static public function decodeParam($string, $charset = null) { - if (($pos = strpos($string, '*')) === false) { - return false; + $convert = array(); + $ret = array('params' => array(), 'val' => ''); + + /* Give $string a bogus body part or else decode() will complain. */ + require 'Mail/mimeDecode.php'; + $mime_decode = new Mail_mimeDecode($string . "\n\nA"); + $res = $mime_decode->decode(); + + /* Are we dealing with content-type or content-disposition? */ + if ($res->ctype_primary) { + $ret['val'] = $res->ctype_primary . '/' . $res->ctype_secondary; + $params = $res->ctype_parameters; + } elseif ($res->disposition) { + $ret['val'] = $res->disposition; + $params = $res->d_parameters; + } else { + return $ret; } - $attribute = substr($string, 0, $pos); - $charset = $lang = null; - $output = ''; + /* Sort the params list. Prevents us from having to manually keep + * track of continuation values below. */ + uksort($params, 'strnatcasecmp'); - /* 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); - } + foreach ($params as $name => $val) { + /* Asterisk at end indicates encoded value. */ + if (($encode = substr($name, -1)) == '*') { + $name = substr($name, 0, -1); + $val = urldecode($val); + } - $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))); + /* This asterisk indicates continuation parameter. */ + if (($pos = strrpos($name, '*')) === false) { + $ret['params'][$name] = $val; } else { - $output .= substr($line, 1); + $first_part = ($encode && (substr($name, -2) == '*0')); + $name = substr($name, 0, $pos); + if ($first_part) { + $quote = strpos($val, "'"); + $convert[$name] = substr($val, 0, $quote); + /* Ignore language. */ + $quote = strpos($val, "'", $quote + 1); + $ret['params'][$name] = substr($val, $quote + 1); + } else { + $ret['params'][$name] .= $val; + } } } - /* RFC 2231 uses quoted printable encoding. */ - if (!is_null($charset)) { - $output = String::convertCharset($output, $charset, $to_charset); + foreach ($convert as $key => $val) { + $ret['params'][$key] = String::convertCharset($ret['params'][$key], $val, $charset); } - return array( - 'attribute' => $attribute, - 'value' => $output - ); + return $ret; } /** diff --git a/framework/Mime/lib/Horde/Mime/Headers.php b/framework/Mime/lib/Horde/Mime/Headers.php index ceb521adb..6f9c41174 100644 --- a/framework/Mime/lib/Horde/Mime/Headers.php +++ b/framework/Mime/lib/Horde/Mime/Headers.php @@ -57,6 +57,7 @@ class Horde_Mime_Headers { $charset = empty($options['charset']) ? null : $options['charset']; $address_keys = $charset ? array() : $this->addressFields(); + $mime = $this->mimeParamFields(); $ret = array(); foreach ($this->_headers as $header => $ob) { @@ -69,23 +70,21 @@ class Horde_Mime_Headers if (is_a($text, 'PEAR_Error')) { $text = $val[$key]; } + } elseif (in_array($header, $mime) && !empty($ob['params'])) { + /* MIME encoded headers (RFC 2231). */ + $text = $val[$key]; + foreach ($ob['params'] as $name => $param) { + foreach (Horde_Mime::encodeParam($name, $param, $charset) as $name2 => $param2) { + /* MIME parameter quoting is identical to RFC 822 + * quoted-string encoding. See RFC 2045 [Appendix + * A]. */ + $text .= '; ' . $name2 . '=' . Horde_Mime_Address::encode($param2, null); + } + } } else { $text = $charset ? Horde_Mime::encode($val[$key], $charset) : $val[$key]; - - /* MIME encoded headers (RFC 2231). */ - if (in_array($header, array('content-type', 'content-disposition')) && - !empty($ob['params'])) { - foreach ($ob['params'] as $name => $param) { - foreach (Horde_Mime::encodeParam($name, $param, $charset) as $name2 => $param2) { - /* MIME parameter quoting is identical to RFC - * 822 quoted-string encoding. See RFC 2045 - * [Appendix A]. */ - $text .= '; ' . $name2 . '=' . Horde_Mime_Address::encode($param2, null); - } - } - } } if (empty($options['nowrap'])) { @@ -363,6 +362,7 @@ class Horde_Mime_Headers * * 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]) + * The values are not MIME encoded. * * @param string $header The header to search for. * @@ -373,15 +373,22 @@ class Horde_Mime_Headers { require_once 'Horde/String.php'; + $entry = null; $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']; + $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; + } + } } - return null; + return $entry; } /** @@ -421,6 +428,17 @@ class Horde_Mime_Headers } /** + * Returns the list of RFC defined MIME header fields that may contain + * parameter info. + * + * @return array The list of headers, in lowercase. + */ + public function mimeParamFields() + { + return array('content-type', 'content-disposition'); + } + + /** * Returns the list of valid mailing list headers. * * @return array The list of valid mailing list headers. @@ -498,6 +516,9 @@ class Horde_Mime_Headers { $headers = new Horde_Mime_Headers(); $currheader = $currtext = null; + $mime = $this->mimeParamFields(); + + require_once 'Horde/String.php'; foreach (explode("\n", $text) as $val) { $val = rtrim($val); @@ -509,8 +530,12 @@ class Horde_Mime_Headers $currtext .= ' ' . ltrim($val); } else { if (!is_null($currheader)) { - // TODO: RFC 2231 - $headers->addHeader($currheader, $currtext, array('decode' => true)); + if (in_array(String::lower($currheader), $mime)) { + $res = Horde_Mime::decodeParam($currtext); + $headers->addHeader($currheader, $res['val'], array('decode' => true, 'params' => $res['params'])); + } else { + $headers->addHeader($currheader, $currtext, array('decode' => true)); + } } $pos = strpos($val, ':'); $currheader = substr($val, 0, $pos);