diff --git a/README.md b/README.md index de8f354cc..f24ddba52 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Build status: [![Build Status](https://travis-ci.org/PHPMailer/PHPMailer.svg)](h ## Class Features - Probably the world's most popular code for sending email from PHP! -- Used by many open-source projects: Wordpress, Drupal, 1CRM, SugarCRM, Yii, Joomla! and many more +- Used by many open-source projects: WordPress, Drupal, 1CRM, SugarCRM, Yii, Joomla! and many more - Integrated SMTP support - send without a local mail server - Send emails with multiple TOs, CCs, BCCs and REPLY-TOs - Multipart/alternative emails for mail clients that do not read HTML email @@ -40,7 +40,15 @@ software availability and distribution. PHPMailer is available via [Composer/Packagist](https://packagist.org/packages/phpmailer/phpmailer) (using semantic versioning), so just add this line to your `composer.json` file: - "phpmailer/phpmailer": "~5.2" +```json +"phpmailer/phpmailer": "~5.2" +``` + +or + +```sh +composer require phpmailer/phpmailer +``` Alternatively, copy the contents of the PHPMailer folder into somewhere that's in your PHP `include_path` setting. If you don't speak git or just want a tarball, click the 'zip' button at the top of the page in GitHub. @@ -138,7 +146,9 @@ We're particularly interested in fixing edge-cases, expanding test coverage and With the move to the PHPMailer GitHub organisation, you'll need to update any remote URLs referencing the old GitHub location with a command like this from within your clone: -`git remote set-url upstream https://github.com/PHPMailer/PHPMailer.git` +```sh +git remote set-url upstream https://github.com/PHPMailer/PHPMailer.git +``` Please *don't* use the SourceForge or Google Code projects any more. diff --git a/changelog.md b/changelog.md index 604043c51..31155e0f0 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,10 @@ * Fixed Travis-CI config when run on PHP 7 * Added Google XOAUTH2 authentication mechanism, thanks to @sherryl4george * Add address parser for RFC822-format addresses +* Update MS Office MIME types +* Don't convert line breaks when using quoted-printable encoding +* Handle MS Exchange returning an invalid empty AUTH-type list in EHLO +* Don't set name or filename properties on MIME parts that don't have one ## Version 5.2.10 (May 4th 2015) * Add custom header getter diff --git a/class.phpmailer.php b/class.phpmailer.php index 6445ff5b0..93398b21f 100644 --- a/class.phpmailer.php +++ b/class.phpmailer.php @@ -35,10 +35,11 @@ class PHPMailer /** * Email priority. - * Options: 1 = High, 3 = Normal, 5 = low. + * Options: null (default), 1 = High, 3 = Normal, 5 = low. + * When null, the header is not set at all. * @type integer */ - public $Priority = 3; + public $Priority = null; /** * The character set of the message. @@ -1837,7 +1838,9 @@ public function createHeader() $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->ServerHostname()); } $result .= $this->headerLine('Message-ID', $this->lastMessageID); - $result .= $this->headerLine('X-Priority', $this->Priority); + if (!is_null($this->Priority)) { + $result .= $this->headerLine('X-Priority', $this->Priority); + } if ($this->XMailer == '') { $result .= $this->headerLine( 'X-Mailer', @@ -2338,12 +2341,21 @@ protected function attachAll($disposition_type, $boundary) $cidUniq[$cid] = true; $mime[] = sprintf('--%s%s', $boundary, $this->LE); - $mime[] = sprintf( - 'Content-Type: %s; name="%s"%s', - $type, - $this->encodeHeader($this->secureHeader($name)), - $this->LE - ); + //Only include a filename property if we have one + if (!empty($name)) { + $mime[] = sprintf( + 'Content-Type: %s; name="%s"%s', + $type, + $this->encodeHeader($this->secureHeader($name)), + $this->LE + ); + } else { + $mime[] = sprintf( + 'Content-Type: %s%s', + $type, + $this->LE + ); + } // RFC1341 part 5 says 7bit is assumed if not specified if ($encoding != '7bit') { $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); @@ -2367,12 +2379,20 @@ protected function attachAll($disposition_type, $boundary) $this->LE . $this->LE ); } else { - $mime[] = sprintf( - 'Content-Disposition: %s; filename=%s%s', - $disposition, - $encoded_name, - $this->LE . $this->LE - ); + if (!empty($encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename=%s%s', + $disposition, + $encoded_name, + $this->LE . $this->LE + ); + } else { + $mime[] = sprintf( + 'Content-Disposition: %s%s', + $disposition, + $this->LE . $this->LE + ); + } } } else { $mime[] = $this->LE; @@ -2626,7 +2646,7 @@ public function encodeQP($string, $line_max = 76) { // Use native function if it's available (>= PHP5.3) if (function_exists('quoted_printable_encode')) { - return $this->fixEOL(quoted_printable_encode($string)); + return quoted_printable_encode($string); } // Fall back to a pure PHP implementation $string = str_replace( @@ -2634,8 +2654,7 @@ public function encodeQP($string, $line_max = 76) array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string) ); - $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); - return $this->fixEOL($string); + return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); } /** @@ -2810,7 +2829,7 @@ public function addStringEmbeddedImage( $disposition = 'inline' ) { // If a MIME type is not specified, try to work it out from the name - if ($type == '') { + if ($type == '' and !empty($name)) { $type = self::filenameToType($name); } @@ -3112,7 +3131,7 @@ public function msgHTML($message, $basedir = '', $advanced = false) $data = rawurldecode($data); } $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 - if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) { + if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { $message = str_replace( $images[0][$imgindex], $images[1][$imgindex] . '="cid:' . $cid . '"', @@ -3210,6 +3229,16 @@ public static function _mime_types($ext = '') 'bin' => 'application/macbinary', 'doc' => 'application/msword', 'word' => 'application/msword', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'class' => 'application/octet-stream', 'dll' => 'application/octet-stream', 'dms' => 'application/octet-stream', diff --git a/class.smtp.php b/class.smtp.php index 03d624ccf..ff557a209 100644 --- a/class.smtp.php +++ b/class.smtp.php @@ -737,9 +737,11 @@ protected function parseHelloFields($type) { $this->server_caps = array(); $lines = explode("\n", $this->last_reply); + foreach ($lines as $n => $s) { + //First 4 chars contain response code followed by - or space $s = trim(substr($s, 4)); - if (!$s) { + if (empty($s)) { continue; } $fields = explode(' ', $s); @@ -749,11 +751,20 @@ protected function parseHelloFields($type) $fields = $fields[0]; } else { $name = array_shift($fields); - if ($name == 'SIZE') { - $fields = ($fields) ? $fields[0] : 0; + switch ($name) { + case 'SIZE': + $fields = ($fields ? $fields[0] : 0); + break; + case 'AUTH': + if (!is_array($fields)) { + $fields = array(); + } + break; + default: + $fields = true; } } - $this->server_caps[$name] = ($fields ? $fields : true); + $this->server_caps[$name] = $fields; } } } @@ -1045,10 +1056,9 @@ protected function get_lines() } while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { $str = @fgets($this->smtp_conn, 515); - $this->edebug("SMTP -> get_lines(): \$data was \"$data\"", self::DEBUG_LOWLEVEL); - $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); - $data .= $str; $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); + $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); + $data .= $str; // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen if ((isset($str[3]) and $str[3] == ' ')) { break; diff --git a/examples/gmail.phps b/examples/gmail.phps index b020f3380..b3cc02d53 100644 --- a/examples/gmail.phps +++ b/examples/gmail.phps @@ -26,6 +26,9 @@ $mail->Debugoutput = 'html'; //Set the hostname of the mail server $mail->Host = 'smtp.gmail.com'; +// use +// $mail->Host = gethostbyname('smtp.gmail.com'); +// if your network does not support SMTP over IPv6 //Set the SMTP port number - 587 for authenticated TLS, a.k.a. RFC4409 SMTP submission $mail->Port = 587; diff --git a/examples/signed-mail.phps b/examples/signed-mail.phps new file mode 100644 index 000000000..a9c3140a1 --- /dev/null +++ b/examples/signed-mail.phps @@ -0,0 +1,82 @@ +setFrom('from@example.com', 'First Last'); +//Set an alternative reply-to address +$mail->addReplyTo('replyto@example.com', 'First Last'); +//Set who the message is to be sent to +$mail->addAddress('whoto@example.com', 'John Doe'); +//Set the subject line +$mail->Subject = 'PHPMailer mail() test'; +//Read an HTML message body from an external file, convert referenced images to embedded, +//convert HTML into a basic plain-text alternative body +$mail->msgHTML(file_get_contents('contents.html'), dirname(__FILE__)); +//Replace the plain text body with one created manually +$mail->AltBody = 'This is a plain-text message body'; +//Attach an image file +$mail->addAttachment('images/phpmailer_mini.png'); + +//signing the email +$mail->sign('/path/to/cert.crt', //the location of your certificate file + '/path/to/cert.key', //the location of your private key file + 'yourSecretPrivateKeyPassword'); //the password you protected your private key with (may be empty but parameter can not mit omitted!) + //!!!! This is not the Import Password !!!! + +//send the message, check for errors +if (!$mail->send()) { + echo "Mailer Error: " . $mail->ErrorInfo; +} else { + echo "Message sent!"; +} + +/** + * REMARKS: + * If your email client does not support S/MIME it will most likely just show an attachment smime.p7s which is the signature contained in the email. + * Other clients, such as Thunderbird support S/MIME natively and will validate the signature automatically and report the result in some way. + */ +?> \ No newline at end of file diff --git a/language/phpmailer.lang-hr.php b/language/phpmailer.lang-hr.php index 55ed6a001..5fedae02c 100644 --- a/language/phpmailer.lang-hr.php +++ b/language/phpmailer.lang-hr.php @@ -23,4 +23,4 @@ $PHPMAILER_LANG['smtp_connect_failed'] = 'Spajanje na SMTP poslužitelj nije uspjelo.'; $PHPMAILER_LANG['smtp_error'] = 'Greška SMTP poslužitelja: '; $PHPMAILER_LANG['variable_set'] = 'Ne mogu postaviti varijablu niti ju vratiti nazad: '; -//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; +$PHPMAILER_LANG['extension_missing'] = 'Nedostaje proširenje: '; diff --git a/language/phpmailer.lang-uk.php b/language/phpmailer.lang-uk.php index 64ede4460..9a7b34674 100644 --- a/language/phpmailer.lang-uk.php +++ b/language/phpmailer.lang-uk.php @@ -3,10 +3,11 @@ * Ukrainian PHPMailer language file: refer to English translation for definitive list * @package PHPMailer * @author Yuriy Rudyy + * @fixed by Boris Yurchenko */ $PHPMAILER_LANG['authenticate'] = 'Помилка SMTP: помилка авторизації.'; -$PHPMAILER_LANG['connect_host'] = 'Помилка SMTP: не вдається підєднатися до серверу SMTP.'; +$PHPMAILER_LANG['connect_host'] = 'Помилка SMTP: не вдається під\'єднатися до серверу SMTP.'; $PHPMAILER_LANG['data_not_accepted'] = 'Помилка SMTP: дані не прийняті.'; $PHPMAILER_LANG['encoding'] = 'Невідомий тип кодування: '; $PHPMAILER_LANG['execute'] = 'Неможливо виконати команду: '; @@ -16,11 +17,11 @@ $PHPMAILER_LANG['instantiate'] = 'Неможливо запустити функцію mail.'; $PHPMAILER_LANG['provide_address'] = 'Будь-ласка, введіть хоча б одну адресу e-mail отримувача.'; $PHPMAILER_LANG['mailer_not_supported'] = ' - поштовий сервер не підтримується.'; -$PHPMAILER_LANG['recipients_failed'] = 'Помилка SMTP: відправти наступним отрмувачам не вдалася: '; +$PHPMAILER_LANG['recipients_failed'] = 'Помилка SMTP: відправлення наступним отримувачам не вдалося: '; $PHPMAILER_LANG['empty_message'] = 'Пусте тіло повідомлення'; -$PHPMAILER_LANG['invalid_address'] = 'Не відправлено, невірний формат email адреси: '; +$PHPMAILER_LANG['invalid_address'] = 'Не відправлено, невірний формат адреси e-mail: '; $PHPMAILER_LANG['signing'] = 'Помилка підпису: '; -$PHPMAILER_LANG['smtp_connect_failed'] = 'Помилка зєднання із SMTP-сервером'; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Помилка з\'єднання із SMTP-сервером'; $PHPMAILER_LANG['smtp_error'] = 'Помилка SMTP-сервера: '; $PHPMAILER_LANG['variable_set'] = 'Неможливо встановити або перевстановити змінну: '; //$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/test/phpmailerTest.php b/test/phpmailerTest.php index eb2061468..4d9153a30 100644 --- a/test/phpmailerTest.php +++ b/test/phpmailerTest.php @@ -761,6 +761,8 @@ public function testQuotedPrintable() //Check that a quoted printable encode and decode results in the same as went in $t = file_get_contents(__FILE__); //Use this file as test content + //Force line breaks to UNIX-style + $t = str_replace(array("\r\n", "\r"), "\n", $t); $this->assertEquals( $t, quoted_printable_decode($this->Mail->encodeQP($t)), @@ -771,6 +773,13 @@ public function testQuotedPrintable() $this->Mail->encodeQPphp($t), 'Quoted-Printable BC wrapper failed' ); + //Force line breaks to Windows-style + $t = str_replace("\n", "\r\n", $t); + $this->assertEquals( + $t, + quoted_printable_decode($this->Mail->encodeQP($t)), + 'Quoted-Printable encoding round-trip failed (Windows line breaks)' + ); } /** @@ -966,6 +975,31 @@ public function testHTMLAttachment() $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); } + /** + * Test embedded image without a name + */ + public function testHTMLStringEmbedNoName() + { + $this->Mail->Body = 'This is the HTML part of the email.'; + $this->Mail->Subject .= ': HTML + unnamed embedded image'; + $this->Mail->isHTML(true); + + if (!$this->Mail->addStringEmbeddedImage( + file_get_contents('../examples/images/phpmailer_mini.png'), + md5('phpmailer_mini.png').'@phpmailer.0', + '', //intentionally empty name + 'base64', + 'image/png', + 'inline') + ) { + $this->assertTrue(false, $this->Mail->ErrorInfo); + return; + } + + $this->buildBody(); + $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); + } + /** * Simple HTML and multiple attachment test */