$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:
* <pre>
+ * 'params' - (array) The header's parameter values.
+ * 'val' - (string) The header's "base" value.
* </pre>
*/
- 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;
}
/**
{
$charset = empty($options['charset']) ? null : $options['charset'];
$address_keys = $charset ? array() : $this->addressFields();
+ $mime = $this->mimeParamFields();
$ret = array();
foreach ($this->_headers as $header => $ob) {
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'])) {
* * 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.
*
{
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;
}
/**
}
/**
+ * 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.
{
$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);
$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);