From a8435bdd12437c5b655859d2ad194832921fcd0d Mon Sep 17 00:00:00 2001 From: svncommit Date: Fri, 9 Jun 2006 16:47:21 +0000 Subject: Updated to latest version. Resolves issues with non-ISO-8859-1 characters in attachment filenames and headers --- program/lib/Mail/mime.php | 155 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 139 insertions(+), 16 deletions(-) (limited to 'program/lib/Mail') diff --git a/program/lib/Mail/mime.php b/program/lib/Mail/mime.php index de2bba0a9..42842e3a4 100644 --- a/program/lib/Mail/mime.php +++ b/program/lib/Mail/mime.php @@ -114,6 +114,7 @@ class Mail_mime { $this->_setEOL($crlf); $this->_build_params = array( + 'head_encoding' => 'quoted-printable', 'text_encoding' => '7bit', 'html_encoding' => 'quoted-printable', '7bit_wrap' => 998, @@ -247,13 +248,15 @@ class Mail_mime * @param string $disposition The content-disposition of this file * Defaults to attachment. * Possible values: attachment, inline. + * @param string $charset The character set used in the filename + * of this attachment. * @return mixed true on success or PEAR_Error object * @access public */ function addAttachment($file, $c_type = 'application/octet-stream', $name = '', $isfilename = true, $encoding = 'base64', - $disposition = 'attachment') + $disposition = 'attachment', $charset = '') { $filedata = ($isfilename === true) ? $this->_file2str($file) : $file; @@ -279,6 +282,7 @@ class Mail_mime 'name' => $filename, 'c_type' => $c_type, 'encoding' => $encoding, + 'charset' => $charset, 'disposition' => $disposition ); return true; @@ -433,7 +437,7 @@ class Mail_mime function &_addHtmlImagePart(&$obj, $value) { $params['content_type'] = $value['c_type'] . '; ' . - 'name="' . $params['dfilename'] . '"'; + 'name="' . $value['name'] . '"'; $params['encoding'] = 'base64'; $params['disposition'] = 'inline'; $params['dfilename'] = $value['name']; @@ -454,12 +458,20 @@ class Mail_mime */ function &_addAttachmentPart(&$obj, $value) { + $params['dfilename'] = $value['name']; + $params['encoding'] = $value['encoding']; + if ($value['disposition'] != "inline") { + $fname = array("fname" => $value['name']); + $fname_enc = $this->_encodeHeaders($fname); + $params['dfilename'] = $fname_enc['fname']; + } + if ($value['charset']) { + $params['charset'] = $value['charset']; + } $params['content_type'] = $value['c_type'] . '; ' . 'name="' . $params['dfilename'] . '"'; - $params['encoding'] = $value['encoding']; $params['disposition'] = isset($value['disposition']) ? $value['disposition'] : 'attachment'; - $params['dfilename'] = $value['name']; $ret = $obj->addSubpart($value['body'], $params); return $ret; } @@ -500,9 +512,14 @@ class Mail_mime * * @param array Build parameters that change the way the email * is built. Should be associative. Can contain: + * head_encoding - What encoding to use for the headers. + * Options: quoted-printable or base64 + * Default is quoted-printable * text_encoding - What encoding to use for plain text + * Options: 7bit, 8bit, base64, or quoted-printable * Default is 7bit * html_encoding - What encoding to use for html + * Options: 7bit, 8bit, base64, or quoted-printable * Default is quoted-printable * 7bit_wrap - Number of characters before text is * wrapped in 7bit encoding @@ -742,26 +759,131 @@ class Mail_mime } } + /** + * Since the PHP send function requires you to specifiy + * recipients (To: header) separately from the other + * headers, the To: header is not properly encoded. + * To fix this, you can use this public method to + * encode your recipients before sending to the send + * function + * + * @param string $recipients A comma-delimited list of recipients + * @return string Encoded data + * @access public + */ + function encodeRecipients($recipients) + { + $input = array("To" => $recipients); + $retval = $this->_encodeHeaders($input); + return $retval["To"] ; + } + /** * Encodes a header as per RFC2047 * - * @param string $input The header data to encode - * @return string Encoded data + * @param array $input The header data to encode + * @return array Encoded data * @access private */ function _encodeHeaders($input) { foreach ($input as $hdr_name => $hdr_value) { - preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $hdr_value, $matches); - foreach ($matches[1] as $value) { - $replacement = preg_replace('/([\x80-\xFF])/e', - '"=" . - strtoupper(dechex(ord("\1")))', - $value); - $hdr_value = str_replace($value, '=?' . - $this->_build_params['head_charset'] . - '?Q?' . $replacement . '?=', - $hdr_value); + if (function_exists('iconv_mime_encode') && preg_match('#[\x80-\xFF]{1}#', $hdr_value)){ + $imePref = array(); + if ($this->_build_params['head_encoding'] == 'base64'){ + $imePrefs['scheme'] = 'B'; + }else{ + $imePrefs['scheme'] = 'Q'; + } + $imePrefs['input-charset'] = $this->_build_params['head_charset']; + $imePrefs['output-charset'] = $this->_build_params['head_charset']; + $hdr_value = iconv_mime_encode($hdr_name, $hdr_value, $imePrefs); + $hdr_value = preg_replace("#^{$hdr_name}\:\ #", "", $hdr_value); + }elseif (preg_match('#[\x80-\xFF]{1}#', $hdr_value)){ + //This header contains non ASCII chars and should be encoded. + switch ($this->_build_params['head_encoding']) { + case 'base64': + //Base64 encoding has been selected. + + //Generate the header using the specified params and dynamicly + //determine the maximum length of such strings. + //75 is the value specified in the RFC. The -2 is there so + //the later regexp doesn't break any of the translated chars. + $prefix = '=?' . $this->_build_params['head_charset'] . '?B?'; + $suffix = '?='; + $maxLength = 75 - strlen($prefix . $suffix) - 2; + $maxLength1stLine = $maxLength - strlen($hdr_name); + + //Base64 encode the entire string + $hdr_value = base64_encode($hdr_value); + + //This regexp will break base64-encoded text at every + //$maxLength but will not break any encoded letters. + $reg1st = "|.{0,$maxLength1stLine}[^\=][^\=]|"; + $reg2nd = "|.{0,$maxLength}[^\=][^\=]|"; + break; + case 'quoted-printable': + default: + //quoted-printable encoding has been selected + + //Generate the header using the specified params and dynamicly + //determine the maximum length of such strings. + //75 is the value specified in the RFC. The -2 is there so + //the later regexp doesn't break any of the translated chars. + $prefix = '=?' . $this->_build_params['head_charset'] . '?Q?'; + $suffix = '?='; + $maxLength = 75 - strlen($prefix . $suffix) - 2; + $maxLength1stLine = $maxLength - strlen($hdr_name); + + //Replace all special characters used by the encoder. + $search = array("=", "_", "?", " "); + $replace = array("=3D", "=5F", "=3F", "_"); + $hdr_value = str_replace($search, $replace, $hdr_value); + + //Replace all extended characters (\x80-xFF) with their + //ASCII values. + $hdr_value = preg_replace( + '#([\x80-\xFF])#e', + '"=" . strtoupper(dechex(ord("\1")))', + $hdr_value + ); + //This regexp will break QP-encoded text at every $maxLength + //but will not break any encoded letters. + $reg1st = "|(.{0,$maxLength})[^\=]|"; + $reg2nd = "|(.{0,$maxLength})[^\=]|"; + break; + } + //Begin with the regexp for the first line. + $reg = $reg1st; + $output = ""; + while ($hdr_value) { + //Split translated string at every $maxLength + //But make sure not to break any translated chars. + $found = preg_match($reg, $hdr_value, $matches); + + //After this first line, we need to use a different + //regexp for the first line. + $reg = $reg2nd; + + //Save the found part and encapsulate it in the + //prefix & suffix. Then remove the part from the + //$hdr_value variable. + if ($found){ + $part = $matches[0]; + $hdr_value = substr($hdr_value, strlen($matches[0])); + }else{ + $part = $hdr_value; + $hdr_value = ""; + } + + //RFC 2047 specifies that any split header should be seperated + //by a CRLF SPACE. + if ($output){ + $output .= "\r\n "; + } + $output .= $prefix . $part . $suffix; + } + $hdr_value = $output; } $input[$hdr_name] = $hdr_value; } @@ -787,3 +909,4 @@ class Mail_mime } // End of class ?> + -- cgit v1.2.3