diff --git a/include/pear/Mail/mime.php b/include/pear/Mail/mime.php index 4522e20cd2eab289a3be6b39126c3aafc81e18a3..98f7206fa456c316e27eb4bc398b4445cddbb39f 100644 --- a/include/pear/Mail/mime.php +++ b/include/pear/Mail/mime.php @@ -1,4 +1,5 @@ <?php + /** * The Mail_Mime class is used to create MIME E-mail messages * @@ -7,7 +8,7 @@ * contain plain-text bodies, HTML bodies, attachments, inline * images and specific headers. * - * Compatible with PHP versions 4 and 5 + * Compatible with PHP >= 5 * * LICENSE: This LICENSE is in the BSD license style. * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org> @@ -48,7 +49,7 @@ * @author Aleksander Machniak <alec@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net> * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version CVS: $Id$ + * @version Release: @package_version@ * @link http://pear.php.net/package/Mail_mime * * This class is based on HTML Mime Mail class from @@ -58,20 +59,7 @@ */ -/** - * require PEAR - * - * This package depends on PEAR to raise errors. - */ require_once 'PEAR.php'; - -/** - * require Mail_mimePart - * - * Mail_mimePart contains the code required to - * create all the different parts a mail can - * consist of. - */ require_once 'Mail/mimePart.php'; @@ -98,49 +86,50 @@ class Mail_mime * Contains the plain text part of the email * * @var string - * @access private */ - var $_txtbody; + protected $txtbody; /** * Contains the html part of the email * * @var string - * @access private */ - var $_htmlbody; + protected $htmlbody; + + /** + * Contains the text/calendar part of the email + * + * @var string + */ + protected $calbody; /** * list of the attached images * * @var array - * @access private */ - var $_html_images = array(); + protected $html_images = array(); /** * list of the attachements * * @var array - * @access private */ - var $_parts = array(); + protected $parts = array(); /** * Headers for the mail * * @var array - * @access private */ - var $_headers = array(); + protected $headers = array(); /** * Build parameters * * @var array - * @access private */ - var $_build_params = array( + protected $build_params = array( // What encoding to use for the headers // Options: quoted-printable or base64 'head_encoding' => 'quoted-printable', @@ -150,18 +139,28 @@ class Mail_mime // What encoding to use for html // Options: 7bit, 8bit, base64, or quoted-printable 'html_encoding' => 'quoted-printable', + // What encoding to use for calendar part + // Options: 7bit, 8bit, base64, or quoted-printable + 'calendar_encoding' => 'quoted-printable', // The character set to use for html 'html_charset' => 'ISO-8859-1', // The character set to use for text 'text_charset' => 'ISO-8859-1', + // The character set to use for calendar part + 'calendar_charset' => 'UTF-8', // The character set to use for headers 'head_charset' => 'ISO-8859-1', // End-of-line sequence 'eol' => "\r\n", // Delay attachment files IO until building the message - 'delay_file_io' => false + 'delay_file_io' => false, + // Default calendar method + 'calendar_method' => 'request', + // multipart part preamble (RFC2046 5.1.1) + 'preamble' => '', ); + /** * Constructor function * @@ -170,21 +169,20 @@ class Mail_mime * See $_build_params. * * @return void - * @access public */ - function Mail_mime($params = array()) + public function __construct($params = array()) { // Backward-compatible EOL setting if (is_string($params)) { - $this->_build_params['eol'] = $params; + $this->build_params['eol'] = $params; } else if (defined('MAIL_MIME_CRLF') && !isset($params['eol'])) { - $this->_build_params['eol'] = MAIL_MIME_CRLF; + $this->build_params['eol'] = MAIL_MIME_CRLF; } // Update build parameters if (!empty($params) && is_array($params)) { while (list($key, $value) = each($params)) { - $this->_build_params[$key] = $value; + $this->build_params[$key] = $value; } } } @@ -196,12 +194,11 @@ class Mail_mime * @param string $value Parameter value * * @return void - * @access public * @since 1.6.0 */ - function setParam($name, $value) + public function setParam($name, $value) { - $this->_build_params[$name] = $value; + $this->build_params[$name] = $value; } /** @@ -210,12 +207,11 @@ class Mail_mime * @param string $name Parameter name * * @return mixed Parameter value - * @access public * @since 1.6.0 */ - function getParam($name) + public function getParam($name) { - return isset($this->_build_params[$name]) ? $this->_build_params[$name] : null; + return isset($this->build_params[$name]) ? $this->build_params[$name] : null; } /** @@ -224,106 +220,116 @@ class Mail_mime * text/plain part that emails clients who don't support * html should show. * - * @param string $data Either a string or - * the file name with the contents + * @param string $data Either a string or the file name with the contents * @param bool $isfile If true the first param should be treated * as a file name, else as a string (default) * @param bool $append If true the text or file is appended to * the existing body, else the old body is * overwritten * - * @return mixed True on success or PEAR_Error object - * @access public + * @return mixed True on success or PEAR_Error object */ - function setTXTBody($data, $isfile = false, $append = false) + public function setTXTBody($data, $isfile = false, $append = false) { - if (!$isfile) { - if (!$append) { - $this->_txtbody = $data; - } else { - $this->_txtbody .= $data; - } - } else { - $cont = $this->_file2str($data); - if (PEAR::isError($cont)) { - return $cont; - } - if (!$append) { - $this->_txtbody = $cont; - } else { - $this->_txtbody .= $cont; - } - } - return true; + return $this->setBody('txtbody', $data, $isfile, $append); } /** * Get message text body * * @return string Text body - * @access public * @since 1.6.0 */ - function getTXTBody() + public function getTXTBody() { - return $this->_txtbody; + return $this->txtbody; } /** * Adds a html part to the mail. * - * @param string $data Either a string or the file name with the - * contents + * @param string $data Either a string or the file name with the contents * @param bool $isfile A flag that determines whether $data is a * filename, or a string(false, default) * - * @return bool True on success - * @access public + * @return bool True on success or PEAR_Error object */ - function setHTMLBody($data, $isfile = false) + public function setHTMLBody($data, $isfile = false) { - if (!$isfile) { - $this->_htmlbody = $data; - } else { - $cont = $this->_file2str($data); - if (PEAR::isError($cont)) { - return $cont; - } - $this->_htmlbody = $cont; - } - - return true; + return $this->setBody('htmlbody', $data, $isfile); } /** * Get message HTML body * * @return string HTML body - * @access public * @since 1.6.0 */ - function getHTMLBody() + public function getHTMLBody() + { + return $this->htmlbody; + } + + /** + * Function to set a body of text/calendar part (not attachment) + * + * @param string $data Either a string or the file name with the contents + * @param bool $isfile If true the first param should be treated + * as a file name, else as a string (default) + * @param bool $append If true the text or file is appended to + * the existing body, else the old body is + * overwritten + * @param string $method iCalendar object method + * @param string $charset iCalendar character set + * @param string $encoding Transfer encoding + * + * @return mixed True on success or PEAR_Error object + * @since 1.9.0 + */ + public function setCalendarBody($data, $isfile = false, $append = false, + $method = 'request', $charset = 'UTF-8', $encoding = 'quoted-printable' + ) { + $result = $this->setBody('calbody', $data, $isfile, $append); + + if ($result === true) { + $this->build_params['calendar_method'] = $method; + $this->build_params['calendar_charset'] = $charset; + $this->build_params['calendar_encoding'] = $encoding; + } + } + + /** + * Get body of calendar part + * + * @return string Calendar part body + * @since 1.9.0 + */ + public function getCalendarBody() { - return $this->_htmlbody; + return $this->calbody; } /** * Adds an image to the list of embedded images. + * Images added this way will be added as related parts of the HTML message. + * + * To correctly match the HTML image with the related attachment + * HTML should refer to it by a filename (specified in $file or $name + * arguments) or by cid:<content-id> (specified in $content_id arg). * * @param string $file The image file name OR image data itself * @param string $c_type The content type - * @param string $name The filename of the image. - * Only used if $file is the image data. + * @param string $name The filename of the image. Used to find + * the image in HTML content. * @param bool $isfile Whether $file is a filename or not. * Defaults to true * @param string $content_id Desired Content-ID of MIME part * Defaults to generated unique ID * - * @return bool True on success - * @access public + * @return bool True on success */ - function addHTMLImage($file, - $c_type='application/octet-stream', + public function addHTMLImage($file, + $c_type = 'application/octet-stream', $name = '', $isfile = true, $content_id = null @@ -332,25 +338,26 @@ class Mail_mime if ($isfile) { // Don't load file into memory - if ($this->_build_params['delay_file_io']) { + if ($this->build_params['delay_file_io']) { $filedata = null; $bodyfile = $file; } else { - if (PEAR::isError($filedata = $this->_file2str($file))) { + if (self::isError($filedata = $this->file2str($file))) { return $filedata; } } - $filename = ($name ? $name : $file); + + $filename = $name ? $name : $file; } else { $filedata = $file; $filename = $name; } if (!$content_id) { - $content_id = md5(uniqid(time())); + $content_id = preg_replace('/[^0-9a-zA-Z]/', '', uniqid(time(), true)); } - $this->_html_images[] = array( + $this->html_images[] = array( 'body' => $filedata, 'body_file' => $bodyfile, 'name' => $filename, @@ -364,8 +371,8 @@ class Mail_mime /** * Adds a file to the list of attachments. * - * @param string $file The file name of the file to attach - * or the file contents itself + * @param mixed $file The file name or the file contents itself, + * it can be also Mail_mimePart object * @param string $c_type The content type * @param string $name The filename of the attachment * Only use if $file is the contents @@ -390,10 +397,9 @@ class Mail_mime * @param array $add_headers Additional part headers. Array keys can be in form * of <header_name>:<parameter_name> * - * @return mixed True on success or PEAR_Error object - * @access public + * @return mixed True on success or PEAR_Error object */ - function addAttachment($file, + public function addAttachment($file, $c_type = 'application/octet-stream', $name = '', $isfile = true, @@ -408,20 +414,25 @@ class Mail_mime $h_charset = null, $add_headers = array() ) { + if ($file instanceof Mail_mimePart) { + $this->parts[] = $file; + return true; + } + $bodyfile = null; if ($isfile) { // Don't load file into memory - if ($this->_build_params['delay_file_io']) { + if ($this->build_params['delay_file_io']) { $filedata = null; $bodyfile = $file; } else { - if (PEAR::isError($filedata = $this->_file2str($file))) { + if (self::isError($filedata = $this->file2str($file))) { return $filedata; } } // Force the name the user supplied, otherwise use $file - $filename = ($name ? $name : $file); + $filename = ($name ? $name : $this->basename($file)); } else { $filedata = $file; $filename = $name; @@ -429,12 +440,10 @@ class Mail_mime if (!strlen($filename)) { $msg = "The supplied filename for the attachment can't be empty"; - $err = PEAR::raiseError($msg); - return $err; + return self::raiseError($msg); } - $filename = $this->_basename($filename); - $this->_parts[] = array( + $this->parts[] = array( 'body' => $filedata, 'body_file' => $bodyfile, 'name' => $filename, @@ -454,35 +463,45 @@ class Mail_mime return true; } + /** + * Checks if the current message has many parts + * + * @return bool True if the message has many parts, False otherwise. + * @since 1.9.0 + */ + public function isMultipart() + { + return count($this->parts) > 0 || count($this->html_images) > 0 + || (strlen($this->htmlbody) > 0 && strlen($this->txtbody) > 0); + } + /** * Get the contents of the given file name as string * * @param string $file_name Path of file to process * - * @return string Contents of $file_name - * @access private + * @return string Contents of $file_name */ - function &_file2str($file_name) + protected function file2str($file_name) { // Check state of file and raise an error properly if (!file_exists($file_name)) { - $err = PEAR::raiseError('File not found: ' . $file_name); - return $err; + return self::raiseError('File not found: ' . $file_name); } if (!is_file($file_name)) { - $err = PEAR::raiseError('Not a regular file: ' . $file_name); - return $err; + return self::raiseError('Not a regular file: ' . $file_name); } if (!is_readable($file_name)) { - $err = PEAR::raiseError('File is not readable: ' . $file_name); - return $err; + return self::raiseError('File is not readable: ' . $file_name); } // Temporarily reset magic_quotes_runtime and read file contents if ($magic_quote_setting = get_magic_quotes_runtime()) { @ini_set('magic_quotes_runtime', 0); } + $cont = file_get_contents($file_name); + if ($magic_quote_setting) { @ini_set('magic_quotes_runtime', $magic_quote_setting); } @@ -494,53 +513,44 @@ class Mail_mime * Adds a text subpart to the mimePart object and * returns it during the build process. * - * @param mixed &$obj The object to add the part to, or - * null if a new object is to be created. - * @param string $text The text to add. + * @param mixed $obj The object to add the part to, or + * anything else if a new object is to be created. * - * @return object The text mimePart object - * @access private + * @return object The text mimePart object */ - function &_addTextPart(&$obj, $text) + protected function addTextPart($obj = null) { - $params['content_type'] = 'text/plain'; - $params['encoding'] = $this->_build_params['text_encoding']; - $params['charset'] = $this->_build_params['text_charset']; - $params['eol'] = $this->_build_params['eol']; - - if (is_object($obj)) { - $ret = $obj->addSubpart($text, $params); - return $ret; - } else { - $ret = new Mail_mimePart($text, $params); - return $ret; - } + return $this->addBodyPart($obj, $this->txtbody, 'text/plain', 'text'); } /** * Adds a html subpart to the mimePart object and * returns it during the build process. * - * @param mixed &$obj The object to add the part to, or - * null if a new object is to be created. + * @param mixed $obj The object to add the part to, or + * anything else if a new object is to be created. * - * @return object The html mimePart object - * @access private + * @return object The html mimePart object */ - function &_addHtmlPart(&$obj) + protected function addHtmlPart($obj = null) { - $params['content_type'] = 'text/html'; - $params['encoding'] = $this->_build_params['html_encoding']; - $params['charset'] = $this->_build_params['html_charset']; - $params['eol'] = $this->_build_params['eol']; + return $this->addBodyPart($obj, $this->htmlbody, 'text/html', 'html'); + } - if (is_object($obj)) { - $ret = $obj->addSubpart($this->_htmlbody, $params); - return $ret; - } else { - $ret = new Mail_mimePart($this->_htmlbody, $params); - return $ret; - } + /** + * Adds a calendar subpart to the mimePart object and + * returns it during the build process. + * + * @param mixed $obj The object to add the part to, or + * anything else if a new object is to be created. + * + * @return object The text mimePart object + */ + protected function addCalendarPart($obj = null) + { + $ctype = 'text/calendar; method='. $this->build_params['calendar_method']; + + return $this->addBodyPart($obj, $this->calbody, $ctype, 'calendar'); } /** @@ -548,18 +558,17 @@ class Mail_mime * the initial content-type and returns it during the * build process. * + * @param array $params Additional part parameters + * * @return object The multipart/mixed mimePart object - * @access private */ - function &_addMixedPart() + protected function addMixedPart($params = array()) { - $params = array(); $params['content_type'] = 'multipart/mixed'; - $params['eol'] = $this->_build_params['eol']; + $params['eol'] = $this->build_params['eol']; // Create empty multipart/mixed Mail_mimePart object to return - $ret = new Mail_mimePart('', $params); - return $ret; + return new Mail_mimePart('', $params); } /** @@ -567,23 +576,23 @@ class Mail_mime * object (or creates one), and returns it during * the build process. * - * @param mixed &$obj The object to add the part to, or - * null if a new object is to be created. + * @param mixed $obj The object to add the part to, or + * anything else if a new object is to be created. * - * @return object The multipart/mixed mimePart object - * @access private + * @return object The multipart/mixed mimePart object */ - function &_addAlternativePart(&$obj) + protected function addAlternativePart($obj = null) { $params['content_type'] = 'multipart/alternative'; - $params['eol'] = $this->_build_params['eol']; + $params['eol'] = $this->build_params['eol']; if (is_object($obj)) { - return $obj->addSubpart('', $params); + $ret = $obj->addSubpart('', $params); } else { $ret = new Mail_mimePart('', $params); - return $ret; } + + return $ret; } /** @@ -591,36 +600,35 @@ class Mail_mime * object (or creates one), and returns it during * the build process. * - * @param mixed &$obj The object to add the part to, or - * null if a new object is to be created + * @param mixed $obj The object to add the part to, or + * anything else if a new object is to be created * - * @return object The multipart/mixed mimePart object - * @access private + * @return object The multipart/mixed mimePart object */ - function &_addRelatedPart(&$obj) + protected function addRelatedPart($obj = null) { $params['content_type'] = 'multipart/related'; - $params['eol'] = $this->_build_params['eol']; + $params['eol'] = $this->build_params['eol']; if (is_object($obj)) { - return $obj->addSubpart('', $params); + $ret = $obj->addSubpart('', $params); } else { $ret = new Mail_mimePart('', $params); - return $ret; } + + return $ret; } /** * Adds an html image subpart to a mimePart object * and returns it during the build process. * - * @param object &$obj The mimePart to add the image to + * @param object $obj The mimePart to add the image to * @param array $value The image information * - * @return object The image mimePart object - * @access private + * @return object The image mimePart object */ - function &_addHtmlImagePart(&$obj, $value) + protected function addHtmlImagePart($obj, $value) { $params['content_type'] = $value['c_type']; $params['encoding'] = 'base64'; @@ -628,7 +636,7 @@ class Mail_mime $params['filename'] = $value['name']; $params['cid'] = $value['cid']; $params['body_file'] = $value['body_file']; - $params['eol'] = $this->_build_params['eol']; + $params['eol'] = $this->build_params['eol']; if (!empty($value['name_encoding'])) { $params['name_encoding'] = $value['name_encoding']; @@ -637,28 +645,30 @@ class Mail_mime $params['filename_encoding'] = $value['filename_encoding']; } - $ret = $obj->addSubpart($value['body'], $params); - return $ret; + return $obj->addSubpart($value['body'], $params); } /** * Adds an attachment subpart to a mimePart object * and returns it during the build process. * - * @param object &$obj The mimePart to add the image to - * @param array $value The attachment information + * @param object $obj The mimePart to add the image to + * @param mixed $value The attachment information array or Mail_mimePart object * - * @return object The image mimePart object - * @access private + * @return object The image mimePart object */ - function &_addAttachmentPart(&$obj, $value) + protected function addAttachmentPart($obj, $value) { - $params['eol'] = $this->_build_params['eol']; + if ($value instanceof Mail_mimePart) { + return $obj->addSubpart($value); + } + + $params['eol'] = $this->build_params['eol']; $params['filename'] = $value['name']; $params['encoding'] = $value['encoding']; $params['content_type'] = $value['c_type']; $params['body_file'] = $value['body_file']; - $params['disposition'] = isset($value['disposition']) ? + $params['disposition'] = isset($value['disposition']) ? $value['disposition'] : 'attachment'; // content charset @@ -688,83 +698,76 @@ class Mail_mime $params['headers'] = $value['add_headers']; } - $ret = $obj->addSubpart($value['body'], $params); - return $ret; + return $obj->addSubpart($value['body'], $params); } /** * Returns the complete e-mail, ready to send using an alternative * mail delivery method. Note that only the mailpart that is made * with Mail_Mime is created. This means that, - * YOU WILL HAVE NO TO: HEADERS UNLESS YOU SET IT YOURSELF + * YOU WILL HAVE NO TO: HEADERS UNLESS YOU SET IT YOURSELF * using the $headers parameter! - * + * * @param string $separation The separation between these two parts. * @param array $params The Build parameters passed to the - * &get() function. See &get for more info. + * get() function. See get() for more info. * @param array $headers The extra headers that should be passed - * to the &headers() function. + * to the headers() method. * See that function for more info. * @param bool $overwrite Overwrite the existing headers with new. * * @return mixed The complete e-mail or PEAR error object - * @access public */ - function getMessage($separation = null, $params = null, $headers = null, + public function getMessage($separation = null, $params = null, $headers = null, $overwrite = false ) { if ($separation === null) { - $separation = $this->_build_params['eol']; + $separation = $this->build_params['eol']; } $body = $this->get($params); - if (PEAR::isError($body)) { + if (self::isError($body)) { return $body; } - $head = $this->txtHeaders($headers, $overwrite); - $mail = $head . $separation . $body; - return $mail; + return $this->txtHeaders($headers, $overwrite) . $separation . $body; } /** * Returns the complete e-mail body, ready to send using an alternative * mail delivery method. - * + * * @param array $params The Build parameters passed to the - * &get() function. See &get for more info. + * get() method. See get() for more info. * * @return mixed The e-mail body or PEAR error object - * @access public * @since 1.6.0 */ - function getMessageBody($params = null) + public function getMessageBody($params = null) { return $this->get($params, null, true); } /** * Writes (appends) the complete e-mail into file. - * + * * @param string $filename Output file location * @param array $params The Build parameters passed to the - * &get() function. See &get for more info. + * get() method. See get() for more info. * @param array $headers The extra headers that should be passed - * to the &headers() function. + * to the headers() function. * See that function for more info. * @param bool $overwrite Overwrite the existing headers with new. * * @return mixed True or PEAR error object - * @access public * @since 1.6.0 */ - function saveMessage($filename, $params = null, $headers = null, $overwrite = false) + public function saveMessage($filename, $params = null, $headers = null, $overwrite = false) { // Check state of file and raise an error properly if (file_exists($filename) && !is_writable($filename)) { - $err = PEAR::raiseError('File is not writable: ' . $filename); - return $err; + return self::raiseError('File is not writable: ' . $filename); } // Temporarily reset magic_quotes_runtime and read file contents @@ -773,15 +776,13 @@ class Mail_mime } if (!($fh = fopen($filename, 'ab'))) { - $err = PEAR::raiseError('Unable to open file: ' . $filename); - return $err; + return self::raiseError('Unable to open file: ' . $filename); } // Write message headers into file (skipping Content-* headers) $head = $this->txtHeaders($headers, $overwrite, true); if (fwrite($fh, $head) === false) { - $err = PEAR::raiseError('Error writing to file: ' . $filename); - return $err; + return self::raiseError('Error writing to file: ' . $filename); } fclose($fh); @@ -797,22 +798,29 @@ class Mail_mime } /** - * Writes (appends) the complete e-mail body into file. - * - * @param string $filename Output file location - * @param array $params The Build parameters passed to the - * &get() function. See &get for more info. + * Writes (appends) the complete e-mail body into file or stream. + * + * @param mixed $filename Output filename or file pointer where to save + * the message instead of returning it + * @param array $params The Build parameters passed to the + * get() method. See get() for more info. * * @return mixed True or PEAR error object - * @access public * @since 1.6.0 */ - function saveMessageBody($filename, $params = null) + public function saveMessageBody($filename, $params = null) { - // Check state of file and raise an error properly - if (file_exists($filename) && !is_writable($filename)) { - $err = PEAR::raiseError('File is not writable: ' . $filename); - return $err; + if (!is_resource($filename)) { + // Check state of file and raise an error properly + if (!file_exists($filename) || !is_writable($filename)) { + return self::raiseError('File is not writable: ' . $filename); + } + + if (!($fh = fopen($filename, 'ab'))) { + return self::raiseError('Unable to open file: ' . $filename); + } + } else { + $fh = $filename; } // Temporarily reset magic_quotes_runtime and read file contents @@ -820,107 +828,128 @@ class Mail_mime @ini_set('magic_quotes_runtime', 0); } - if (!($fh = fopen($filename, 'ab'))) { - $err = PEAR::raiseError('Unable to open file: ' . $filename); - return $err; + // Write the rest of the message into file + $res = $this->get($params, $fh, true); + + if (!is_resource($filename)) { + fclose($fh); } - // Write the rest of the message into file - $res = $this->get($params, $filename, true); + if ($magic_quote_setting) { + @ini_set('magic_quotes_runtime', $magic_quote_setting); + } return $res ? $res : true; } /** - * Builds the multipart message from the list ($this->_parts) and + * Builds the multipart message from the list ($this->parts) and * returns the mime content. * - * @param array $params Build parameters that change the way the email - * is built. Should be associative. See $_build_params. - * @param resource $filename Output file where to save the message instead of - * returning it - * @param boolean $skip_head True if you want to return/save only the message - * without headers + * @param array $params Build parameters that change the way the email + * is built. Should be associative. See $_build_params. + * @param mixed $filename Output filename or file pointer where to save + * the message instead of returning it + * @param boolean $skip_head True if you want to return/save only the message + * without headers * * @return mixed The MIME message content string, null or PEAR error object - * @access public */ - function &get($params = null, $filename = null, $skip_head = false) + public function get($params = null, $filename = null, $skip_head = false) { if (isset($params)) { while (list($key, $value) = each($params)) { - $this->_build_params[$key] = $value; + $this->build_params[$key] = $value; } } - if (isset($this->_headers['From'])) { + if (isset($this->headers['From'])) { // Bug #11381: Illegal characters in domain ID - if (preg_match('#(@[0-9a-zA-Z\-\.]+)#', $this->_headers['From'], $matches)) { + if (preg_match('#(@[0-9a-zA-Z\-\.]+)#', $this->headers['From'], $matches)) { $domainID = $matches[1]; } else { $domainID = '@localhost'; } - foreach ($this->_html_images as $i => $img) { - $cid = $this->_html_images[$i]['cid']; + + foreach ($this->html_images as $i => $img) { + $cid = $this->html_images[$i]['cid']; if (!preg_match('#'.preg_quote($domainID).'$#', $cid)) { - $this->_html_images[$i]['cid'] = $cid . $domainID; + $this->html_images[$i]['cid'] = $cid . $domainID; } } } - if (count($this->_html_images) && isset($this->_htmlbody)) { - foreach ($this->_html_images as $key => $value) { - $regex = array(); - $regex[] = '#(\s)((?i)src|background|href(?-i))\s*=\s*(["\']?)' . - preg_quote($value['name'], '#') . '\3#'; - $regex[] = '#(?i)url(?-i)\(\s*(["\']?)' . - preg_quote($value['name'], '#') . '\1\s*\)#'; + if (count($this->html_images) && isset($this->htmlbody)) { + foreach ($this->html_images as $key => $value) { + $rval = preg_quote($value['name'], '#'); + $regex = array( + '#(\s)((?i)src|background|href(?-i))\s*=\s*(["\']?)' . $rval . '\3#', + '#(?i)url(?-i)\(\s*(["\']?)' . $rval . '\1\s*\)#', + ); - $rep = array(); - $rep[] = '\1\2=\3cid:' . $value['cid'] .'\3'; - $rep[] = 'url(\1cid:' . $value['cid'] . '\1)'; + $rep = array( + '\1\2=\3cid:' . $value['cid'] .'\3', + 'url(\1cid:' . $value['cid'] . '\1)', + ); - $this->_htmlbody = preg_replace($regex, $rep, $this->_htmlbody); - $this->_html_images[$key]['name'] - = $this->_basename($this->_html_images[$key]['name']); + $this->htmlbody = preg_replace($regex, $rep, $this->htmlbody); + $this->html_images[$key]['name'] + = $this->basename($this->html_images[$key]['name']); } } - $this->_checkParams(); + $this->checkParams(); - $null = null; - $attachments = count($this->_parts) ? true : false; - $html_images = count($this->_html_images) ? true : false; - $html = strlen($this->_htmlbody) ? true : false; - $text = (!$html && strlen($this->_txtbody)) ? true : false; + $attachments = count($this->parts) > 0; + $html_images = count($this->html_images) > 0; + $html = strlen($this->htmlbody) > 0; + $calendar = strlen($this->calbody) > 0; + $has_text = strlen($this->txtbody) > 0; + $text = !$html && $has_text; + $mixed_params = array('preamble' => $this->build_params['preamble']); switch (true) { + case $calendar && !$attachments && !$text && !$html: + $message = $this->addCalendarPart(); + break; + + case $calendar && !$attachments: + $message = $this->addAlternativePart($mixed_params); + if ($has_text) { + $this->addTextPart($message); + } + if ($html) { + $this->addHtmlPart($message); + } + $this->addCalendarPart($message); + break; + case $text && !$attachments: - $message =& $this->_addTextPart($null, $this->_txtbody); + $message = $this->addTextPart(); break; case !$text && !$html && $attachments: - $message =& $this->_addMixedPart(); - for ($i = 0; $i < count($this->_parts); $i++) { - $this->_addAttachmentPart($message, $this->_parts[$i]); + $message = $this->addMixedPart($mixed_params); + for ($i = 0; $i < count($this->parts); $i++) { + $this->addAttachmentPart($message, $this->parts[$i]); } break; case $text && $attachments: - $message =& $this->_addMixedPart(); - $this->_addTextPart($message, $this->_txtbody); - for ($i = 0; $i < count($this->_parts); $i++) { - $this->_addAttachmentPart($message, $this->_parts[$i]); + $message = $this->addMixedPart($mixed_params); + $this->addTextPart($message); + for ($i = 0; $i < count($this->parts); $i++) { + $this->addAttachmentPart($message, $this->parts[$i]); } break; case $html && !$attachments && !$html_images: - if (isset($this->_txtbody)) { - $message =& $this->_addAlternativePart($null); - $this->_addTextPart($message, $this->_txtbody); - $this->_addHtmlPart($message); + if (isset($this->txtbody)) { + $message = $this->addAlternativePart(); + $this->addTextPart($message); + $this->addHtmlPart($message); } else { - $message =& $this->_addHtmlPart($null); + $message = $this->addHtmlPart(); } break; @@ -930,23 +959,23 @@ class Mail_mime // * Content-Type: multipart/related; // * html // * image... - if (isset($this->_txtbody)) { - $message =& $this->_addAlternativePart($null); - $this->_addTextPart($message, $this->_txtbody); - - $ht =& $this->_addRelatedPart($message); - $this->_addHtmlPart($ht); - for ($i = 0; $i < count($this->_html_images); $i++) { - $this->_addHtmlImagePart($ht, $this->_html_images[$i]); + if (isset($this->txtbody)) { + $message = $this->addAlternativePart(); + $this->addTextPart($message); + + $ht = $this->addRelatedPart($message); + $this->addHtmlPart($ht); + for ($i = 0; $i < count($this->html_images); $i++) { + $this->addHtmlImagePart($ht, $this->html_images[$i]); } } else { // * Content-Type: multipart/related; // * html // * image... - $message =& $this->_addRelatedPart($null); - $this->_addHtmlPart($message); - for ($i = 0; $i < count($this->_html_images); $i++) { - $this->_addHtmlImagePart($message, $this->_html_images[$i]); + $message = $this->addRelatedPart(); + $this->addHtmlPart($message); + for ($i = 0; $i < count($this->html_images); $i++) { + $this->addHtmlImagePart($message, $this->html_images[$i]); } } /* @@ -956,62 +985,60 @@ class Mail_mime // * text // * html // * image... - $message =& $this->_addRelatedPart($null); - if (isset($this->_txtbody)) { - $alt =& $this->_addAlternativePart($message); - $this->_addTextPart($alt, $this->_txtbody); - $this->_addHtmlPart($alt); + $message = $this->addRelatedPart(); + if (isset($this->txtbody)) { + $alt = $this->addAlternativePart($message); + $this->addTextPart($alt); + $this->addHtmlPart($alt); } else { - $this->_addHtmlPart($message); + $this->addHtmlPart($message); } - for ($i = 0; $i < count($this->_html_images); $i++) { - $this->_addHtmlImagePart($message, $this->_html_images[$i]); + for ($i = 0; $i < count($this->html_images); $i++) { + $this->addHtmlImagePart($message, $this->html_images[$i]); } */ break; case $html && $attachments && !$html_images: - $message =& $this->_addMixedPart(); - if (isset($this->_txtbody)) { - $alt =& $this->_addAlternativePart($message); - $this->_addTextPart($alt, $this->_txtbody); - $this->_addHtmlPart($alt); + $message = $this->addMixedPart($mixed_params); + if (isset($this->txtbody)) { + $alt = $this->addAlternativePart($message); + $this->addTextPart($alt); + $this->addHtmlPart($alt); } else { - $this->_addHtmlPart($message); + $this->addHtmlPart($message); } - for ($i = 0; $i < count($this->_parts); $i++) { - $this->_addAttachmentPart($message, $this->_parts[$i]); + for ($i = 0; $i < count($this->parts); $i++) { + $this->addAttachmentPart($message, $this->parts[$i]); } break; case $html && $attachments && $html_images: - $message =& $this->_addMixedPart(); - if (isset($this->_txtbody)) { - $alt =& $this->_addAlternativePart($message); - $this->_addTextPart($alt, $this->_txtbody); - $rel =& $this->_addRelatedPart($alt); + $message = $this->addMixedPart($mixed_params); + if (isset($this->txtbody)) { + $alt = $this->addAlternativePart($message); + $this->addTextPart($alt); + $rel = $this->addRelatedPart($alt); } else { - $rel =& $this->_addRelatedPart($message); + $rel = $this->addRelatedPart($message); } - $this->_addHtmlPart($rel); - for ($i = 0; $i < count($this->_html_images); $i++) { - $this->_addHtmlImagePart($rel, $this->_html_images[$i]); + $this->addHtmlPart($rel); + for ($i = 0; $i < count($this->html_images); $i++) { + $this->addHtmlImagePart($rel, $this->html_images[$i]); } - for ($i = 0; $i < count($this->_parts); $i++) { - $this->_addAttachmentPart($message, $this->_parts[$i]); + for ($i = 0; $i < count($this->parts); $i++) { + $this->addAttachmentPart($message, $this->parts[$i]); } break; - } if (!isset($message)) { - $ret = null; - return $ret; + return null; } // Use saved boundary - if (!empty($this->_build_params['boundary'])) { - $boundary = $this->_build_params['boundary']; + if (!empty($this->build_params['boundary'])) { + $boundary = $this->build_params['boundary']; } else { $boundary = null; } @@ -1020,20 +1047,18 @@ class Mail_mime if ($filename) { // Append mimePart message headers and body into file $headers = $message->encodeToFile($filename, $boundary, $skip_head); - if (PEAR::isError($headers)) { + if (self::isError($headers)) { return $headers; } - $this->_headers = array_merge($this->_headers, $headers); - $ret = null; - return $ret; + $this->headers = array_merge($this->headers, $headers); + return null; } else { $output = $message->encode($boundary, $skip_head); - if (PEAR::isError($output)) { + if (self::isError($output)) { return $output; } - $this->_headers = array_merge($this->_headers, $output['headers']); - $body = $output['body']; - return $body; + $this->headers = array_merge($this->headers, $output['headers']); + return $output['body']; } } @@ -1047,11 +1072,10 @@ class Mail_mime * @param bool $overwrite Overwrite already existing headers. * @param bool $skip_content Don't return content headers: Content-Type, * Content-Disposition and Content-Transfer-Encoding - * - * @return array Assoc array with the mime headers - * @access public + * + * @return array Assoc array with the mime headers */ - function &headers($xtra_headers = null, $overwrite = false, $skip_content = false) + public function headers($xtra_headers = null, $overwrite = false, $skip_content = false) { // Add mime version header $headers['MIME-Version'] = '1.0'; @@ -1061,7 +1085,7 @@ class Mail_mime // we got them when called before get() or something in the message // has been changed after get() [#14780] if (!$skip_content) { - $headers += $this->_contentHeaders(); + $headers += $this->contentHeaders(); } if (!empty($xtra_headers)) { @@ -1069,22 +1093,22 @@ class Mail_mime } if ($overwrite) { - $this->_headers = array_merge($this->_headers, $headers); + $this->headers = array_merge($this->headers, $headers); } else { - $this->_headers = array_merge($headers, $this->_headers); + $this->headers = array_merge($headers, $this->headers); } - $headers = $this->_headers; + $headers = $this->headers; if ($skip_content) { unset($headers['Content-Type']); unset($headers['Content-Transfer-Encoding']); unset($headers['Content-Disposition']); - } else if (!empty($this->_build_params['ctype'])) { - $headers['Content-Type'] = $this->_build_params['ctype']; + } else if (!empty($this->build_params['ctype'])) { + $headers['Content-Type'] = $this->build_params['ctype']; } - $encodedHeaders = $this->_encodeHeaders($headers); + $encodedHeaders = $this->encodeHeaders($headers); return $encodedHeaders; } @@ -1098,10 +1122,9 @@ class Mail_mime * @param bool $skip_content Don't return content headers: Content-Type, * Content-Disposition and Content-Transfer-Encoding * - * @return string Plain text headers - * @access public + * @return string Plain text headers */ - function txtHeaders($xtra_headers = null, $overwrite = false, $skip_content = false) + public function txtHeaders($xtra_headers = null, $overwrite = false, $skip_content = false) { $headers = $this->headers($xtra_headers, $overwrite, $skip_content); @@ -1114,7 +1137,7 @@ class Mail_mime } $ret = ''; - $eol = $this->_build_params['eol']; + $eol = $this->build_params['eol']; foreach ($headers as $key => $val) { if (is_array($val)) { @@ -1132,29 +1155,29 @@ class Mail_mime /** * Sets message Content-Type header. * Use it to build messages with various content-types e.g. miltipart/raport - * not supported by _contentHeaders() function. + * not supported by contentHeaders() function. * * @param string $type Type name * @param array $params Hash array of header parameters * * @return void - * @access public * @since 1.7.0 */ - function setContentType($type, $params = array()) + public function setContentType($type, $params = array()) { $header = $type; - $eol = !empty($this->_build_params['eol']) - ? $this->_build_params['eol'] : "\r\n"; + $eol = !empty($this->build_params['eol']) + ? $this->build_params['eol'] : "\r\n"; // add parameters $token_regexp = '#([^\x21\x23-\x27\x2A\x2B\x2D' . '\x2E\x30-\x39\x41-\x5A\x5E-\x7E])#'; + if (is_array($params)) { foreach ($params as $name => $value) { if ($name == 'boundary') { - $this->_build_params['boundary'] = $value; + $this->build_params['boundary'] = $value; } if (!preg_match($token_regexp, $value)) { $header .= ";$eol $name=$value"; @@ -1166,15 +1189,15 @@ class Mail_mime } // add required boundary parameter if not defined - if (preg_match('/^multipart\//i', $type)) { - if (empty($this->_build_params['boundary'])) { - $this->_build_params['boundary'] = '=_' . md5(rand() . microtime()); + if (stripos($type, 'multipart/') === 0) { + if (empty($this->build_params['boundary'])) { + $this->build_params['boundary'] = '=_' . md5(rand() . microtime()); } - $header .= ";$eol boundary=\"".$this->_build_params['boundary']."\""; + $header .= ";$eol boundary=\"".$this->build_params['boundary']."\""; } - $this->_build_params['ctype'] = $header; + $this->build_params['ctype'] = $header; } /** @@ -1183,11 +1206,10 @@ class Mail_mime * @param string $subject String to set the subject to. * * @return void - * @access public */ - function setSubject($subject) + public function setSubject($subject) { - $this->_headers['Subject'] = $subject; + $this->headers['Subject'] = $subject; } /** @@ -1196,11 +1218,10 @@ class Mail_mime * @param string $email The email address to use * * @return void - * @access public */ - function setFrom($email) + public function setFrom($email) { - $this->_headers['From'] = $email; + $this->headers['From'] = $email; } /** @@ -1210,14 +1231,13 @@ class Mail_mime * @param string $email The email direction to add * * @return void - * @access public */ - function addTo($email) + public function addTo($email) { - if (isset($this->_headers['To'])) { - $this->_headers['To'] .= ", $email"; + if (isset($this->headers['To'])) { + $this->headers['To'] .= ", $email"; } else { - $this->_headers['To'] = $email; + $this->headers['To'] = $email; } } @@ -1228,14 +1248,13 @@ class Mail_mime * @param string $email The email direction to add * * @return void - * @access public */ - function addCc($email) + public function addCc($email) { - if (isset($this->_headers['Cc'])) { - $this->_headers['Cc'] .= ", $email"; + if (isset($this->headers['Cc'])) { + $this->headers['Cc'] .= ", $email"; } else { - $this->_headers['Cc'] = $email; + $this->headers['Cc'] = $email; } } @@ -1246,14 +1265,13 @@ class Mail_mime * @param string $email The email direction to add * * @return void - * @access public */ - function addBcc($email) + public function addBcc($email) { - if (isset($this->_headers['Bcc'])) { - $this->_headers['Bcc'] .= ", $email"; + if (isset($this->headers['Bcc'])) { + $this->headers['Bcc'] .= ", $email"; } else { - $this->_headers['Bcc'] = $email; + $this->headers['Bcc'] = $email; } } @@ -1261,20 +1279,19 @@ class Mail_mime * Since the PHP send function requires you to specify * 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 + * 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 + * @return string Encoded data */ - function encodeRecipients($recipients) + public function encodeRecipients($recipients) { - $input = array("To" => $recipients); - $retval = $this->_encodeHeaders($input); - return $retval["To"] ; + $input = array('To' => $recipients); + $retval = $this->encodeHeaders($input); + + return $retval['To'] ; } /** @@ -1283,14 +1300,14 @@ class Mail_mime * @param array $input The header data to encode * @param array $params Extra build parameters * - * @return array Encoded data - * @access private + * @return array Encoded data */ - function _encodeHeaders($input, $params = array()) + protected function encodeHeaders($input, $params = array()) { - $build_params = $this->_build_params; - while (list($key, $value) = each($params)) { - $build_params[$key] = $value; + $build_params = $this->build_params; + + if (!empty($params)) { + $build_params = array_merge($build_params, $params); } foreach ($input as $hdr_name => $hdr_value) { @@ -1301,11 +1318,13 @@ class Mail_mime $build_params['head_charset'], $build_params['head_encoding'] ); } - } else { + } else if ($hdr_value !== null) { $input[$hdr_name] = $this->encodeHeader( $hdr_name, $hdr_value, $build_params['head_charset'], $build_params['head_encoding'] ); + } else { + unset($input[$hdr_name]); } } @@ -1320,27 +1339,24 @@ class Mail_mime * @param string $charset Character set name * @param string $encoding Encoding name (base64 or quoted-printable) * - * @return string Encoded header data (without a name) - * @access public + * @return string Encoded header data (without a name) * @since 1.5.3 */ - function encodeHeader($name, $value, $charset, $encoding) + public function encodeHeader($name, $value, $charset, $encoding) { - $mime_part = new Mail_mimePart; - return $mime_part->encodeHeader( - $name, $value, $charset, $encoding, $this->_build_params['eol'] + return Mail_mimePart::encodeHeader( + $name, $value, $charset, $encoding, $this->build_params['eol'] ); } /** - * Get file's basename (locale independent) + * Get file's basename (locale independent) * * @param string $filename Filename * - * @return string Basename - * @access private + * @return string Basename */ - function _basename($filename) + protected function basename($filename) { // basename() is not unicode safe and locale dependent if (stristr(PHP_OS, 'win') || stristr(PHP_OS, 'netware')) { @@ -1354,18 +1370,27 @@ class Mail_mime * Get Content-Type and Content-Transfer-Encoding headers of the message * * @return array Headers array - * @access private */ - function _contentHeaders() + protected function contentHeaders() { - $attachments = count($this->_parts) ? true : false; - $html_images = count($this->_html_images) ? true : false; - $html = strlen($this->_htmlbody) ? true : false; - $text = (!$html && strlen($this->_txtbody)) ? true : false; + $attachments = count($this->parts) > 0; + $html_images = count($this->html_images) > 0; + $html = strlen($this->htmlbody) > 0; + $calendar = strlen($this->calbody) > 0; + $has_text = strlen($this->txtbody) > 0; + $text = !$html && $has_text; $headers = array(); // See get() switch (true) { + case $calendar && !$attachments && !$html && !$has_text: + $headers['Content-Type'] = 'text/calendar'; + break; + + case $calendar && !$attachments: + $headers['Content-Type'] = 'multipart/alternative'; + break; + case $text && !$attachments: $headers['Content-Type'] = 'text/plain'; break; @@ -1377,16 +1402,16 @@ class Mail_mime $headers['Content-Type'] = 'multipart/mixed'; break; - case $html && !$attachments && !$html_images && isset($this->_txtbody): - case $html && !$attachments && $html_images && isset($this->_txtbody): + case $html && !$attachments && !$html_images && $has_text: + case $html && !$attachments && $html_images && $has_text: $headers['Content-Type'] = 'multipart/alternative'; break; - case $html && !$attachments && !$html_images && !isset($this->_txtbody): + case $html && !$attachments && !$html_images && !$has_text: $headers['Content-Type'] = 'text/html'; break; - case $html && !$attachments && $html_images && !isset($this->_txtbody): + case $html && !$attachments && $html_images && !$has_text: $headers['Content-Type'] = 'multipart/related'; break; @@ -1394,41 +1419,61 @@ class Mail_mime return $headers; } - $this->_checkParams(); + $this->checkParams(); - $eol = !empty($this->_build_params['eol']) - ? $this->_build_params['eol'] : "\r\n"; + $eol = !empty($this->build_params['eol']) + ? $this->build_params['eol'] : "\r\n"; if ($headers['Content-Type'] == 'text/plain') { // single-part message: add charset and encoding - $charset = 'charset=' . $this->_build_params['text_charset']; - // place charset parameter in the same line, if possible - // 26 = strlen("Content-Type: text/plain; ") - $headers['Content-Type'] - .= (strlen($charset) + 26 <= 76) ? "; $charset" : ";$eol $charset"; + if ($this->build_params['text_charset']) { + $charset = 'charset=' . $this->build_params['text_charset']; + // place charset parameter in the same line, if possible + // 26 = strlen("Content-Type: text/plain; ") + $headers['Content-Type'] + .= (strlen($charset) + 26 <= 76) ? "; $charset" : ";$eol $charset"; + } + $headers['Content-Transfer-Encoding'] - = $this->_build_params['text_encoding']; + = $this->build_params['text_encoding']; } else if ($headers['Content-Type'] == 'text/html') { // single-part message: add charset and encoding - $charset = 'charset=' . $this->_build_params['html_charset']; - // place charset parameter in the same line, if possible - $headers['Content-Type'] - .= (strlen($charset) + 25 <= 76) ? "; $charset" : ";$eol $charset"; + if ($this->build_params['html_charset']) { + $charset = 'charset=' . $this->build_params['html_charset']; + // place charset parameter in the same line, if possible + $headers['Content-Type'] + .= (strlen($charset) + 25 <= 76) ? "; $charset" : ";$eol $charset"; + } + $headers['Content-Transfer-Encoding'] + = $this->build_params['html_encoding']; + } + else if ($headers['Content-Type'] == 'text/calendar') { + // single-part message: add charset and encoding + if ($this->build_params['calendar_charset']) { + $charset = 'charset=' . $this->build_params['calendar_charset']; + $headers['Content-Type'] .= "; $charset"; + } + + if ($this->build_params['calendar_method']) { + $method = 'method=' . $this->build_params['calendar_method']; + $headers['Content-Type'] .= "; $method"; + } + $headers['Content-Transfer-Encoding'] - = $this->_build_params['html_encoding']; + = $this->build_params['calendar_encoding']; } else { // multipart message: and boundary - if (!empty($this->_build_params['boundary'])) { - $boundary = $this->_build_params['boundary']; - } else if (!empty($this->_headers['Content-Type']) - && preg_match('/boundary="([^"]+)"/', $this->_headers['Content-Type'], $m) + if (!empty($this->build_params['boundary'])) { + $boundary = $this->build_params['boundary']; + } else if (!empty($this->headers['Content-Type']) + && preg_match('/boundary="([^"]+)"/', $this->headers['Content-Type'], $m) ) { $boundary = $m[1]; } else { $boundary = '=_' . md5(rand() . microtime()); } - $this->_build_params['boundary'] = $boundary; + $this->build_params['boundary'] = $boundary; $headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; } @@ -1439,38 +1484,143 @@ class Mail_mime * Validate and set build parameters * * @return void - * @access private */ - function _checkParams() + protected function checkParams() { $encodings = array('7bit', '8bit', 'base64', 'quoted-printable'); - $this->_build_params['text_encoding'] - = strtolower($this->_build_params['text_encoding']); - $this->_build_params['html_encoding'] - = strtolower($this->_build_params['html_encoding']); + $this->build_params['text_encoding'] + = strtolower($this->build_params['text_encoding']); + $this->build_params['html_encoding'] + = strtolower($this->build_params['html_encoding']); + $this->build_params['calendar_encoding'] + = strtolower($this->build_params['calendar_encoding']); - if (!in_array($this->_build_params['text_encoding'], $encodings)) { - $this->_build_params['text_encoding'] = '7bit'; + if (!in_array($this->build_params['text_encoding'], $encodings)) { + $this->build_params['text_encoding'] = '7bit'; + } + if (!in_array($this->build_params['html_encoding'], $encodings)) { + $this->build_params['html_encoding'] = '7bit'; } - if (!in_array($this->_build_params['html_encoding'], $encodings)) { - $this->_build_params['html_encoding'] = '7bit'; + if (!in_array($this->build_params['calendar_encoding'], $encodings)) { + $this->build_params['calendar_encoding'] = '7bit'; } // text body - if ($this->_build_params['text_encoding'] == '7bit' - && !preg_match('/ascii/i', $this->_build_params['text_charset']) - && preg_match('/[^\x00-\x7F]/', $this->_txtbody) + if ($this->build_params['text_encoding'] == '7bit' + && !preg_match('/ascii/i', $this->build_params['text_charset']) + && preg_match('/[^\x00-\x7F]/', $this->txtbody) ) { - $this->_build_params['text_encoding'] = 'quoted-printable'; + $this->build_params['text_encoding'] = 'quoted-printable'; } // html body - if ($this->_build_params['html_encoding'] == '7bit' - && !preg_match('/ascii/i', $this->_build_params['html_charset']) - && preg_match('/[^\x00-\x7F]/', $this->_htmlbody) + if ($this->build_params['html_encoding'] == '7bit' + && !preg_match('/ascii/i', $this->build_params['html_charset']) + && preg_match('/[^\x00-\x7F]/', $this->htmlbody) ) { - $this->_build_params['html_encoding'] = 'quoted-printable'; + $this->build_params['html_encoding'] = 'quoted-printable'; + } + // calendar body + if ($this->build_params['calendar_encoding'] == '7bit' + && !preg_match('/ascii/i', $this->build_params['calendar_charset']) + && preg_match('/[^\x00-\x7F]/', $this->calbody) + ) { + $this->build_params['calendar_encoding'] = 'quoted-printable'; } } -} // End of class + /** + * Set body of specified message part + * + * @param string $type One of: txtbody, calbody, htmlbody + * @param string $data Either a string or the file name with the contents + * @param bool $isfile If true the first param should be treated + * as a file name, else as a string (default) + * @param bool $append If true the text or file is appended to + * the existing body, else the old body is + * overwritten + * + * @return mixed True on success or PEAR_Error object + */ + protected function setBody($type, $data, $isfile = false, $append = false) + { + if (!$isfile) { + if (!$append) { + $this->{$type} = $data; + } else { + $this->{$type} .= $data; + } + } else { + $cont = $this->file2str($data); + if (self::isError($cont)) { + return $cont; + } + + if (!$append) { + $this->{$type} = $cont; + } else { + $this->{$type} .= $cont; + } + } + + return true; + } + + /** + * Adds a subpart to the mimePart object and + * returns it during the build process. + * + * @param mixed $obj The object to add the part to, or + * anything else if a new object is to be created. + * @param string $body Part body + * @param string $ctype Part content type + * @param string $type Internal part type + * + * @return object The mimePart object + */ + protected function addBodyPart($obj, $body, $ctype, $type) + { + $params['content_type'] = $ctype; + $params['encoding'] = $this->build_params[$type . '_encoding']; + $params['charset'] = $this->build_params[$type . '_charset']; + $params['eol'] = $this->build_params['eol']; + + if (is_object($obj)) { + $ret = $obj->addSubpart($body, $params); + } else { + $ret = new Mail_mimePart($body, $params); + } + + return $ret; + } + + /** + * PEAR::isError implementation + * + * @param mixed $data Object + * + * @return bool True if object is an instance of PEAR_Error + */ + public static function isError($data) + { + // PEAR::isError() is not PHP 5.4 compatible (see Bug #19473) + if (is_a($data, 'PEAR_Error')) { + return true; + } + + return false; + } + + /** + * PEAR::raiseError implementation + * + * @param string $message A text error message + * + * @return PEAR_Error Instance of PEAR_Error + */ + public static function raiseError($message) + { + // PEAR::raiseError() is not PHP 5.4 compatible + return new PEAR_Error($message); + } +} diff --git a/include/pear/Mail/mimePart.php b/include/pear/Mail/mimePart.php index a839402f7a6648c1ffe6ef61284770190c8933f1..6ac85a2f8b9b9287754e65af2477ee8d4e4ef686 100644 --- a/include/pear/Mail/mimePart.php +++ b/include/pear/Mail/mimePart.php @@ -8,7 +8,7 @@ * of mime mail. * This class however allows full control over the email. * - * Compatible with PHP versions 4 and 5 + * Compatible with PHP version 5 * * LICENSE: This LICENSE is in the BSD license style. * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org> @@ -24,8 +24,8 @@ * - 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 + * - 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" @@ -48,10 +48,16 @@ * @author Aleksander Machniak <alec@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net> * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version CVS: $Id$ + * @version Release: @package_version@ * @link http://pear.php.net/package/Mail_mime */ +/** + * require PEAR + * + * This package depends on PEAR to raise errors. + */ +require_once 'PEAR.php'; /** * The Mail_mimePart class is used to create MIME E-mail messages @@ -76,98 +82,97 @@ class Mail_mimePart { /** - * The encoding type of this part - * - * @var string - * @access private - */ - var $_encoding; + * The encoding type of this part + * + * @var string + */ + protected $encoding; /** - * An array of subparts - * - * @var array - * @access private - */ - var $_subparts; + * An array of subparts + * + * @var array + */ + protected $subparts; /** - * The output of this part after being built - * - * @var string - * @access private - */ - var $_encoded; + * The output of this part after being built + * + * @var string + */ + protected $encoded; /** - * Headers for this part - * - * @var array - * @access private - */ - var $_headers; + * Headers for this part + * + * @var array + */ + protected $headers; + + /** + * The body of this part (not encoded) + * + * @var string + */ + protected $body; /** - * The body of this part (not encoded) - * - * @var string - * @access private - */ - var $_body; + * The location of file with body of this part (not encoded) + * + * @var string + */ + protected $body_file; /** - * The location of file with body of this part (not encoded) - * - * @var string - * @access private - */ - var $_body_file; + * The short text of multipart part preamble (RFC2046 5.1.1) + * + * @var string + */ + protected $preamble; /** - * The end-of-line sequence - * - * @var string - * @access private - */ - var $_eol = "\r\n"; + * The end-of-line sequence + * + * @var string + */ + protected $eol = "\r\n"; /** - * Constructor. - * - * Sets up the object. - * - * @param string $body The body of the mime part if any. - * @param array $params An associative array of optional parameters: - * content_type - The content type for this part eg multipart/mixed - * encoding - The encoding to use, 7bit, 8bit, - * base64, or quoted-printable - * charset - Content character set - * cid - Content ID to apply - * disposition - Content disposition, inline or attachment - * filename - Filename parameter for content disposition - * description - Content description - * name_encoding - Encoding of the attachment name (Content-Type) - * By default filenames are encoded using RFC2231 - * Here you can set RFC2047 encoding (quoted-printable - * or base64) instead - * filename_encoding - Encoding of the attachment filename (Content-Disposition) - * See 'name_encoding' - * headers_charset - Charset of the headers e.g. filename, description. - * If not set, 'charset' will be used - * eol - End of line sequence. Default: "\r\n" - * headers - Hash array with additional part headers. Array keys can be - * in form of <header_name>:<parameter_name> - * body_file - Location of file with part's body (instead of $body) - * - * @access public - */ - function Mail_mimePart($body = '', $params = array()) + * Constructor. + * + * Sets up the object. + * + * @param string $body The body of the mime part if any. + * @param array $params An associative array of optional parameters: + * content_type - The content type for this part eg multipart/mixed + * encoding - The encoding to use, 7bit, 8bit, + * base64, or quoted-printable + * charset - Content character set + * cid - Content ID to apply + * disposition - Content disposition, inline or attachment + * filename - Filename parameter for content disposition + * description - Content description + * name_encoding - Encoding of the attachment name (Content-Type) + * By default filenames are encoded using RFC2231 + * Here you can set RFC2047 encoding (quoted-printable + * or base64) instead + * filename_encoding - Encoding of the attachment filename (Content-Disposition) + * See 'name_encoding' + * headers_charset - Charset of the headers e.g. filename, description. + * If not set, 'charset' will be used + * eol - End of line sequence. Default: "\r\n" + * headers - Hash array with additional part headers. Array keys can be + * in form of <header_name>:<parameter_name> + * body_file - Location of file with part's body (instead of $body) + * preamble - short text of multipart part preamble (RFC2046 5.1.1) + */ + public function __construct($body = '', $params = array()) { if (!empty($params['eol'])) { - $this->_eol = $params['eol']; + $this->eol = $params['eol']; } else if (defined('MAIL_MIMEPART_CRLF')) { // backward-copat. - $this->_eol = MAIL_MIMEPART_CRLF; + $this->eol = MAIL_MIMEPART_CRLF; } // Additional part headers @@ -178,7 +183,7 @@ class Mail_mimePart foreach ($params as $key => $value) { switch ($key) { case 'encoding': - $this->_encoding = $value; + $this->encoding = $value; $headers['Content-Transfer-Encoding'] = $value; break; @@ -191,7 +196,11 @@ class Mail_mimePart break; case 'body_file': - $this->_body_file = $value; + $this->body_file = $value; + break; + + case 'preamble': + $this->preamble = $value; break; // for backward compatibility @@ -214,7 +223,7 @@ class Mail_mimePart if ((strlen($headers['Content-Type']) + strlen($charset) + 16) <= 76) { $headers['Content-Type'] .= '; '; } else { - $headers['Content-Type'] .= ';' . $this->_eol . ' '; + $headers['Content-Type'] .= ';' . $this->eol . ' '; } $headers['Content-Type'] .= $charset; @@ -229,10 +238,9 @@ class Mail_mimePart $h_language = !empty($params['language']) ? $params['language'] : null; $h_encoding = !empty($params['name_encoding']) ? $params['name_encoding'] : null; - if (!empty($params['filename'])) { - $headers['Content-Type'] .= ';' . $this->_eol; - $headers['Content-Type'] .= $this->_buildHeaderParam( + $headers['Content-Type'] .= ';' . $this->eol; + $headers['Content-Type'] .= $this->buildHeaderParam( 'name', $params['filename'], $h_charset, $h_language, $h_encoding ); } @@ -241,24 +249,24 @@ class Mail_mimePart if (!empty($params['disposition'])) { $headers['Content-Disposition'] = $params['disposition']; if (!empty($params['filename'])) { - $headers['Content-Disposition'] .= ';' . $this->_eol; - $headers['Content-Disposition'] .= $this->_buildHeaderParam( + $headers['Content-Disposition'] .= ';' . $this->eol; + $headers['Content-Disposition'] .= $this->buildHeaderParam( 'filename', $params['filename'], $h_charset, $h_language, !empty($params['filename_encoding']) ? $params['filename_encoding'] : null ); } // add attachment size - $size = $this->_body_file ? filesize($this->_body_file) : strlen($body); + $size = $this->body_file ? filesize($this->body_file) : strlen($body); if ($size) { - $headers['Content-Disposition'] .= ';' . $this->_eol . ' size=' . $size; + $headers['Content-Disposition'] .= ';' . $this->eol . ' size=' . $size; } } if (!empty($params['description'])) { $headers['Content-Description'] = $this->encodeHeader( 'Content-Description', $params['description'], $h_charset, $h_encoding, - $this->_eol + $this->eol ); } @@ -269,9 +277,9 @@ class Mail_mimePart $header = $items[0]; $param = $items[1]; if (isset($headers[$header])) { - $headers[$header] .= ';' . $this->_eol; + $headers[$header] .= ';' . $this->eol; } - $headers[$header] .= $this->_buildHeaderParam( + $headers[$header] .= $this->buildHeaderParam( $param, $value, $h_charset, $h_language, $h_encoding ); unset($headers[$key]); @@ -279,14 +287,14 @@ class Mail_mimePart } // Default encoding - if (!isset($this->_encoding)) { - $this->_encoding = '7bit'; + if (!isset($this->encoding)) { + $this->encoding = '7bit'; } // Assign stuff to member variables - $this->_encoded = array(); - $this->_headers = $headers; - $this->_body = $body; + $this->encoded = array(); + $this->headers = $headers; + $this->body = $body; } /** @@ -298,24 +306,27 @@ class Mail_mimePart * @return An associative array containing two elements, * body and headers. The headers element is itself * an indexed array. On error returns PEAR error object. - * @access public */ - function encode($boundary=null) + public function encode($boundary=null) { - $encoded =& $this->_encoded; + $encoded =& $this->encoded; - if (count($this->_subparts)) { + if (count($this->subparts)) { $boundary = $boundary ? $boundary : '=_' . md5(rand() . microtime()); - $eol = $this->_eol; + $eol = $this->eol; - $this->_headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; + $this->headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; - $encoded['body'] = ''; + $encoded['body'] = ''; - for ($i = 0; $i < count($this->_subparts); $i++) { + if ($this->preamble) { + $encoded['body'] .= $this->preamble . $eol . $eol; + } + + for ($i = 0; $i < count($this->subparts); $i++) { $encoded['body'] .= '--' . $boundary . $eol; - $tmp = $this->_subparts[$i]->encode(); - if (PEAR::isError($tmp)) { + $tmp = $this->subparts[$i]->encode(); + if (is_a($tmp, 'PEAR_Error')) { return $tmp; } foreach ($tmp['headers'] as $key => $value) { @@ -325,20 +336,19 @@ class Mail_mimePart } $encoded['body'] .= '--' . $boundary . '--' . $eol; - - } else if ($this->_body) { - $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding); - } else if ($this->_body_file) { + } else if ($this->body) { + $encoded['body'] = $this->getEncodedData($this->body, $this->encoding); + } else if ($this->body_file) { // Temporarily reset magic_quotes_runtime for file reads and writes if ($magic_quote_setting = get_magic_quotes_runtime()) { @ini_set('magic_quotes_runtime', 0); } - $body = $this->_getEncodedDataFromFile($this->_body_file, $this->_encoding); + $body = $this->getEncodedDataFromFile($this->body_file, $this->encoding); if ($magic_quote_setting) { @ini_set('magic_quotes_runtime', $magic_quote_setting); } - if (PEAR::isError($body)) { + if (is_a($body, 'PEAR_Error')) { return $body; } $encoded['body'] = $body; @@ -347,34 +357,38 @@ class Mail_mimePart } // Add headers to $encoded - $encoded['headers'] =& $this->_headers; + $encoded['headers'] =& $this->headers; return $encoded; } /** - * Encodes and saves the email into file. File must exist. - * Data will be appended to the file. + * Encodes and saves the email into file or stream. + * Data will be appended to the file/stream. * - * @param string $filename Output file location + * @param mixed $filename Existing file location + * or file pointer resource * @param string $boundary Pre-defined boundary string * @param boolean $skip_head True if you don't want to save headers * * @return array An associative array containing message headers * or PEAR error object - * @access public * @since 1.6.0 */ - function encodeToFile($filename, $boundary=null, $skip_head=false) + public function encodeToFile($filename, $boundary = null, $skip_head = false) { - if (file_exists($filename) && !is_writable($filename)) { - $err = PEAR::raiseError('File is not writeable: ' . $filename); - return $err; - } + if (!is_resource($filename)) { + if (file_exists($filename) && !is_writable($filename)) { + $err = self::raiseError('File is not writeable: ' . $filename); + return $err; + } - if (!($fh = fopen($filename, 'ab'))) { - $err = PEAR::raiseError('Unable to open file: ' . $filename); - return $err; + if (!($fh = fopen($filename, 'ab'))) { + $err = self::raiseError('Unable to open file: ' . $filename); + return $err; + } + } else { + $fh = $filename; } // Temporarily reset magic_quotes_runtime for file reads and writes @@ -382,15 +396,17 @@ class Mail_mimePart @ini_set('magic_quotes_runtime', 0); } - $res = $this->_encodePartToFile($fh, $boundary, $skip_head); + $res = $this->encodePartToFile($fh, $boundary, $skip_head); - fclose($fh); + if (!is_resource($filename)) { + fclose($fh); + } if ($magic_quote_setting) { @ini_set('magic_quotes_runtime', $magic_quote_setting); } - return PEAR::isError($res) ? $res : $this->_headers; + return is_a($res, 'PEAR_Error') ? $res : $this->headers; } /** @@ -401,19 +417,18 @@ class Mail_mimePart * @param boolean $skip_head True if you don't want to save headers * * @return array True on sucess or PEAR error object - * @access private */ - function _encodePartToFile($fh, $boundary=null, $skip_head=false) + protected function encodePartToFile($fh, $boundary = null, $skip_head = false) { - $eol = $this->_eol; + $eol = $this->eol; - if (count($this->_subparts)) { + if (count($this->subparts)) { $boundary = $boundary ? $boundary : '=_' . md5(rand() . microtime()); - $this->_headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; + $this->headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; } if (!$skip_head) { - foreach ($this->_headers as $key => $value) { + foreach ($this->headers as $key => $value) { fwrite($fh, $key . ': ' . $value . $eol); } $f_eol = $eol; @@ -421,26 +436,31 @@ class Mail_mimePart $f_eol = ''; } - if (count($this->_subparts)) { - for ($i = 0; $i < count($this->_subparts); $i++) { + if (count($this->subparts)) { + if ($this->preamble) { + fwrite($fh, $f_eol . $this->preamble . $eol); + $f_eol = $eol; + } + + for ($i = 0; $i < count($this->subparts); $i++) { fwrite($fh, $f_eol . '--' . $boundary . $eol); - $res = $this->_subparts[$i]->_encodePartToFile($fh); - if (PEAR::isError($res)) { + $res = $this->subparts[$i]->encodePartToFile($fh); + if (is_a($res, 'PEAR_Error')) { return $res; } $f_eol = $eol; } fwrite($fh, $eol . '--' . $boundary . '--' . $eol); - - } else if ($this->_body) { - fwrite($fh, $f_eol . $this->_getEncodedData($this->_body, $this->_encoding)); - } else if ($this->_body_file) { + } else if ($this->body) { + fwrite($fh, $f_eol); + fwrite($fh, $this->getEncodedData($this->body, $this->encoding)); + } else if ($this->body_file) { fwrite($fh, $f_eol); - $res = $this->_getEncodedDataFromFile( - $this->_body_file, $this->_encoding, $fh + $res = $this->getEncodedDataFromFile( + $this->body_file, $this->encoding, $fh ); - if (PEAR::isError($res)) { + if (is_a($res, 'PEAR_Error')) { return $res; } } @@ -452,20 +472,23 @@ class Mail_mimePart * Adds a subpart to current mime part and returns * a reference to it * - * @param string $body The body of the subpart, if any. - * @param array $params The parameters for the subpart, same - * as the $params argument for constructor. + * @param mixed $body The body of the subpart or Mail_mimePart object + * @param array $params The parameters for the subpart, same + * as the $params argument for constructor * - * @return Mail_mimePart A reference to the part you just added. It is - * crucial if using multipart/* in your subparts that - * you use =& in your script when calling this function, - * otherwise you will not be able to add further subparts. - * @access public + * @return Mail_mimePart A reference to the part you just added. */ - function &addSubpart($body, $params) + public function addSubpart($body, $params = null) { - $this->_subparts[] = new Mail_mimePart($body, $params); - return $this->_subparts[count($this->_subparts) - 1]; + if ($body instanceof Mail_mimePart) { + $part = $body; + } else { + $part = new Mail_mimePart($body, $params); + } + + $this->subparts[] = $part; + + return $part; } /** @@ -475,18 +498,17 @@ class Mail_mimePart * @param string $encoding The encoding type to use, 7bit, base64, * or quoted-printable. * - * @return string - * @access private + * @return string Encoded data string */ - function _getEncodedData($data, $encoding) + protected function getEncodedData($data, $encoding) { switch ($encoding) { case 'quoted-printable': - return $this->_quotedPrintableEncode($data); + return self::quotedPrintableEncode($data, 76, $this->eol); break; case 'base64': - return rtrim(chunk_split(base64_encode($data), 76, $this->_eol)); + return rtrim(chunk_split(base64_encode($data), 76, $this->eol)); break; case '8bit': @@ -506,17 +528,16 @@ class Mail_mimePart * stored into it instead of returning it * * @return string Encoded data or PEAR error object - * @access private */ - function _getEncodedDataFromFile($filename, $encoding, $fh=null) + protected function getEncodedDataFromFile($filename, $encoding, $fh = null) { if (!is_readable($filename)) { - $err = PEAR::raiseError('Unable to read file: ' . $filename); + $err = self::raiseError('Unable to read file: ' . $filename); return $err; } if (!($fd = fopen($filename, 'rb'))) { - $err = PEAR::raiseError('Could not open file: ' . $filename); + $err = self::raiseError('Could not open file: ' . $filename); return $err; } @@ -525,7 +546,7 @@ class Mail_mimePart switch ($encoding) { case 'quoted-printable': while (!feof($fd)) { - $buffer = $this->_quotedPrintableEncode(fgets($fd)); + $buffer = self::quotedPrintableEncode(fgets($fd), 76, $this->eol); if ($fh) { fwrite($fh, $buffer); } else { @@ -541,7 +562,7 @@ class Mail_mimePart // because base64 encoding is memory expensive $buffer = fread($fd, 57 * 9198); // ca. 0.5 MB $buffer = base64_encode($buffer); - $buffer = chunk_split($buffer, 76, $this->_eol); + $buffer = chunk_split($buffer, 76, $this->eol); if (feof($fd)) { $buffer = rtrim($buffer); } @@ -580,14 +601,12 @@ class Mail_mimePart * @param string $input The data to encode * @param int $line_max Optional max line length. Should * not be more than 76 chars + * @param string $eol End-of-line sequence. Default: "\r\n" * * @return string Encoded data - * - * @access private */ - function _quotedPrintableEncode($input , $line_max = 76) + public static function quotedPrintableEncode($input , $line_max = 76, $eol = "\r\n") { - $eol = $this->_eol; /* // imap_8bit() is extremely fast, but doesn't handle properly some characters if (function_exists('imap_8bit') && $line_max == 76) { @@ -648,7 +667,7 @@ class Mail_mimePart } /** - * Encodes the paramater of a header. + * Encodes the parameter of a header. * * @param string $name The name of the header-parameter * @param string $value The value of the paramter @@ -659,11 +678,9 @@ class Mail_mimePart * @param int $maxLength The maximum length of a line. Defauls to 75 * * @return string - * - * @access private */ - function _buildHeaderParam($name, $value, $charset=null, $language=null, - $encoding=null, $maxLength=75 + protected function buildHeaderParam($name, $value, $charset = null, + $language = null, $encoding = null, $maxLength = 75 ) { // RFC 2045: // value needs encoding if contains non-ASCII chars or is longer than 78 chars @@ -686,13 +703,13 @@ class Mail_mimePart // RFC2047: use quoted-printable/base64 encoding if ($encoding == 'quoted-printable' || $encoding == 'base64') { - return $this->_buildRFC2047Param($name, $value, $charset, $encoding); + return $this->buildRFC2047Param($name, $value, $charset, $encoding); } // RFC2231: $encValue = preg_replace_callback( '/([^\x21\x23\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E])/', - array($this, '_encodeReplaceCallback'), $value + array($this, 'encodeReplaceCallback'), $value ); $value = "$charset'$language'$encValue"; @@ -720,7 +737,7 @@ class Mail_mimePart $headCount++; } - $headers = implode(';' . $this->_eol, $headers); + $headers = implode(';' . $this->eol, $headers); return $headers; } @@ -734,10 +751,9 @@ class Mail_mimePart * @param int $maxLength Encoded parameter max length. Default: 76 * * @return string Parameter line - * @access private */ - function _buildRFC2047Param($name, $value, $charset, - $encoding='quoted-printable', $maxLength=76 + protected function buildRFC2047Param($name, $value, $charset, + $encoding = 'quoted-printable', $maxLength = 76 ) { // WARNING: RFC 2047 says: "An 'encoded-word' MUST NOT be used in // parameter of a MIME Content-Type or Content-Disposition field", @@ -759,7 +775,7 @@ class Mail_mimePart $_quote = substr($value, 0, $real_len); $value = substr($value, $real_len); - $quoted .= $prefix . $_quote . $suffix . $this->_eol . ' '; + $quoted .= $prefix . $_quote . $suffix . $this->eol . ' '; $add_len = strlen($prefix . $suffix) + 4; // 2 x SPACE, '"', ';' $len = strlen($value) + $add_len; } @@ -782,7 +798,7 @@ class Mail_mimePart $_quote = $matches[1]; } - $quoted .= $prefix . $_quote . $suffix . $this->_eol . ' '; + $quoted .= $prefix . $_quote . $suffix . $this->eol . ' '; $value = substr($value, strlen($_quote)); $add_len = strlen($prefix . $suffix) + 4; // 2 x SPACE, '"', ';' $len = strlen($value) + $add_len; @@ -803,18 +819,18 @@ class Mail_mimePart * @param string $encoding Encoding name (base64 or quoted-printable) * @param string $eol End-of-line sequence. Default: "\r\n" * - * @return string Encoded header data (without a name) - * @access public + * @return string Encoded header data (without a name) * @since 1.6.1 */ - function encodeHeader($name, $value, $charset='ISO-8859-1', - $encoding='quoted-printable', $eol="\r\n" + public static function encodeHeader($name, $value, $charset = 'ISO-8859-1', + $encoding = 'quoted-printable', $eol = "\r\n" ) { // Structured headers $comma_headers = array( 'from', 'to', 'cc', 'bcc', 'sender', 'reply-to', 'resent-from', 'resent-to', 'resent-cc', 'resent-bcc', 'resent-sender', 'resent-reply-to', + 'mail-reply-to', 'mail-followup-to', 'return-receipt-to', 'disposition-notification-to', ); $other_headers = array( @@ -838,7 +854,7 @@ class Mail_mimePart // Simple e-mail address regexp $email_regexp = '([^\s<]+|("[^\r\n"]+"))@\S+'; - $parts = Mail_mimePart::_explodeQuotedString($separator, $value); + $parts = Mail_mimePart::explodeQuotedString("[\t$separator]", $value); $value = ''; foreach ($parts as $part) { @@ -849,7 +865,7 @@ class Mail_mimePart continue; } if ($value) { - $value .= $separator==',' ? $separator.' ' : ' '; + $value .= $separator == ',' ? $separator . ' ' : ' '; } else { $value = $name . ': '; } @@ -868,7 +884,7 @@ class Mail_mimePart // check if phrase requires quoting if ($word) { // non-ASCII: require encoding - if (preg_match('#([\x80-\xFF]){1}#', $word)) { + if (preg_match('#([^\s\x21-\x7E]){1}#', $word)) { if ($word[0] == '"' && $word[strlen($word)-1] == '"') { // de-quote quoted-string, encoding changes // string to atom @@ -907,11 +923,10 @@ class Mail_mimePart $value = preg_replace( '/^'.$name.':('.preg_quote($eol, '/').')* /', '', $value ); - } else { // Unstructured header // non-ASCII: require encoding - if (preg_match('#([\x80-\xFF]){1}#', $value)) { + if (preg_match('#([^\s\x21-\x7E]){1}#', $value)) { if ($value[0] == '"' && $value[strlen($value)-1] == '"') { // de-quote quoted-string, encoding changes // string to atom @@ -942,10 +957,9 @@ class Mail_mimePart * @param string $delimiter Delimiter expression string for preg_match() * @param string $string Input string * - * @return array String tokens array - * @access private + * @return array String tokens array */ - function _explodeQuotedString($delimiter, $string) + protected static function explodeQuotedString($delimiter, $string) { $result = array(); $strlen = strlen($string); @@ -974,11 +988,10 @@ class Mail_mimePart * @param int $prefix_len Prefix length. Default: 0 * @param string $eol End-of-line sequence. Default: "\r\n" * - * @return string Encoded header data - * @access public + * @return string Encoded header data * @since 1.6.1 */ - function encodeHeaderValue($value, $charset, $encoding, $prefix_len=0, $eol="\r\n") + public static function encodeHeaderValue($value, $charset, $encoding, $prefix_len = 0, $eol = "\r\n") { // #17311: Use multibyte aware method (requires mbstring extension) if ($result = Mail_mimePart::encodeMB($value, $charset, $encoding, $prefix_len, $eol)) { @@ -1012,7 +1025,7 @@ class Mail_mimePart $value = substr($value, $cutpoint); $cutpoint = $maxLength; // RFC 2047 specifies that any split header should - // be seperated by a CRLF SPACE. + // be separated by a CRLF SPACE. if ($output) { $output .= $eol . ' '; } @@ -1054,7 +1067,7 @@ class Mail_mimePart } // RFC 2047 specifies that any split header should - // be seperated by a CRLF SPACE + // be separated by a CRLF SPACE if ($output) { $output .= $eol . ' '; } @@ -1074,11 +1087,10 @@ class Mail_mimePart * * @param string $str String to encode * - * @return string Encoded string - * @access public + * @return string Encoded string * @since 1.6.0 */ - function encodeQP($str) + public static function encodeQP($str) { // Bug #17226 RFC 2047 restricts some characters // if the word is inside a phrase, permitted chars are only: @@ -1087,7 +1099,7 @@ class Mail_mimePart // "=", "_", "?" must be encoded $regexp = '/([\x22-\x29\x2C\x2E\x3A-\x40\x5B-\x60\x7B-\x7E\x80-\xFF])/'; $str = preg_replace_callback( - $regexp, array('Mail_mimePart', '_qpReplaceCallback'), $str + $regexp, array('Mail_mimePart', 'qpReplaceCallback'), $str ); return str_replace(' ', '_', $str); @@ -1104,11 +1116,10 @@ class Mail_mimePart * @param int $prefix_len Prefix length. Default: 0 * @param string $eol End-of-line sequence. Default: "\r\n" * - * @return string Encoded string - * @access public + * @return string Encoded string * @since 1.8.0 */ - function encodeMB($str, $charset, $encoding, $prefix_len=0, $eol="\r\n") + public static function encodeMB($str, $charset, $encoding, $prefix_len=0, $eol="\r\n") { if (!function_exists('mb_substr') || !function_exists('mb_strlen')) { return; @@ -1172,7 +1183,7 @@ class Mail_mimePart $char_len = 1; } else { $char = preg_replace_callback( - $regexp, array('Mail_mimePart', '_qpReplaceCallback'), $char + $regexp, array('Mail_mimePart', 'qpReplaceCallback'), $char ); $char_len = strlen($char); } @@ -1203,10 +1214,9 @@ class Mail_mimePart * * @param array $matches Preg_replace's matches array * - * @return string Encoded character string - * @access private + * @return string Encoded character string */ - function _qpReplaceCallback($matches) + protected static function qpReplaceCallback($matches) { return sprintf('=%02X', ord($matches[1])); } @@ -1217,12 +1227,23 @@ class Mail_mimePart * * @param array $matches Preg_replace's matches array * - * @return string Encoded character string - * @access private + * @return string Encoded character string */ - function _encodeReplaceCallback($matches) + protected static function encodeReplaceCallback($matches) { return sprintf('%%%02X', ord($matches[1])); } -} // End of class + /** + * PEAR::raiseError implementation + * + * @param string $message A text error message + * + * @return PEAR_Error Instance of PEAR_Error + */ + public static function raiseError($message) + { + // PEAR::raiseError() is not PHP 5.4 compatible + return new PEAR_Error($message); + } +}