Compare commits

...

18 Commits

Author SHA1 Message Date
ratatine 507509c2b8
Merge 01897c9622 into ebf1655bd5 2026-01-09 18:35:15 +00:00
Marcus Bointon ebf1655bd5
7.0.2 2026-01-09 19:02:33 +01:00
Marcus Bointon b938991866
Merge pull request #3283 from SirLouen/patch/3282
Fixing troubles with mailSend and Sender enabled
2026-01-09 17:34:10 +00:00
SirLouen 0d4c5ee8da
fix: Correct sendmail_path validation to ensure proper formatting for sender address 2025-12-09 01:37:34 +01:00
SirLouen 14e1efe293
docs: Fixing array multiline syntax for sendmailPathProvider 2025-12-08 16:54:13 +01:00
SirLouen dca8e03946
fix: Add more causitry to testParseSendmailPath and nclude -oi with related tests 2025-12-08 16:48:09 +01:00
SirLouen 2b240b44e9
docs Update parseSendmailPath documentation to clarify parameter handling 2025-12-08 01:42:13 +01:00
SirLouen 4c06f874c9
fix: Refactor sendmail parameter handling and update related tests 2025-12-08 01:35:57 +01:00
SirLouen 4e8292f43c
fix: Refactor to fix linting issues in PHP 5.5 2025-12-07 19:11:59 +01:00
SirLouen 8d27fb6b7c
fix: Enhance sendmail parameter parsing, sendmailSend and add related unit tests 2025-12-07 19:05:45 +01:00
SirLouen 34b209f864
fix: Improve sendmail formatting logic and enhance related tests 2025-12-07 03:13:29 +01:00
SirLouen 31e3d491aa
fix: Correct sendmail path check logic in PHPMailer class 2025-12-07 02:00:54 +01:00
SirLouen 96d82b913a
fix: Update testMailSendWithSendmailParams to use example.org in sendmail path 2025-12-07 01:27:56 +01:00
SirLouen 8ff00047a8
docs: Add doc block to the testMailSendWithSendmailParams test 2025-12-06 17:44:11 +01:00
SirLouen a767d446c2
fix: Remove useless sendmail path check in testMailSend method 2025-12-06 17:05:27 +01:00
SirLouen b6729d7bd2
Fixing troubles with mailSend and Sender enabled 2025-12-06 16:56:00 +01:00
ratatine 01897c9622 Formatting fixes 2018-11-30 13:06:02 -06:00
ratatine ce0eeb7c45 Support for SMIME for digital signing and encryption of message 2018-11-30 12:30:34 -06:00
6 changed files with 290 additions and 33 deletions

View File

@ -1 +1 @@
7.0.1
7.0.2

View File

@ -1,5 +1,9 @@
# PHPMailer Change Log
## Version 7.0.2 (January 9th, 2026)
* Fixes for sendmail parameter problems in WordPress, thanks to @SirLouen
* Reduce memory consumption when sending large attachments by @RobinvanderVliet
## Version 7.0.1 (November 25th, 2025)
* Use From domain when generating CIDs in msgHTML.
* Update to PHPCompatibility 10, resolve numerous PHPCS issues in PHP 8.5.

View File

@ -749,6 +749,20 @@ class PHPMailer
*/
protected $sign_key_pass = '';
/**
* An array of public PEM encoded certificates for each recipient
*
* @var array
*/
protected $encrypt_recipcerts = [];
/**
* Used if body should be S/MIME encrypted
*
* @var bool
*/
protected $encrypt_body = false;
/**
* Whether to throw exceptions for errors.
*
@ -768,7 +782,7 @@ class PHPMailer
*
* @var string
*/
const VERSION = '7.0.1';
const VERSION = '7.0.2';
/**
* Error severity: message only, continue processing.
@ -988,6 +1002,54 @@ class PHPMailer
$this->Mailer = 'mail';
}
/**
* Extract sendmail path and parse to deal with known parameters.
*
* @param string $sendmailPath The sendmail path as set in php.ini
*
* @return string The sendmail path without the known parameters
*/
private function parseSendmailPath($sendmailPath)
{
$sendmailPath = trim((string)$sendmailPath);
if ($sendmailPath === '') {
return $sendmailPath;
}
$parts = preg_split('/\s+/', $sendmailPath);
if (empty($parts)) {
return $sendmailPath;
}
$command = array_shift($parts);
$remainder = [];
// Parse only -t, -i, -oi and -f parameters.
for ($i = 0; $i < count($parts); ++$i) {
$part = $parts[$i];
if (preg_match('/^-(i|oi|t)$/', $part, $matches)) {
continue;
}
if (preg_match('/^-f(.*)$/', $part, $matches)) {
$address = $matches[1];
if ($address === '' && isset($parts[$i + 1]) && strpos($parts[$i + 1], '-') !== 0) {
$address = $parts[++$i];
}
$this->Sender = $address;
continue;
}
$remainder[] = $part;
}
// The params that are not parsed are added back to the command.
if (!empty($remainder)) {
$command .= ' ' . implode(' ', $remainder);
}
return $command;
}
/**
* Send messages using $Sendmail.
*/
@ -996,10 +1058,9 @@ class PHPMailer
$ini_sendmail_path = ini_get('sendmail_path');
if (false === stripos($ini_sendmail_path, 'sendmail')) {
$this->Sendmail = '/usr/sbin/sendmail';
} else {
$this->Sendmail = $ini_sendmail_path;
$ini_sendmail_path = '/usr/sbin/sendmail';
}
$this->Sendmail = $this->parseSendmailPath($ini_sendmail_path);
$this->Mailer = 'sendmail';
}
@ -1011,10 +1072,9 @@ class PHPMailer
$ini_sendmail_path = ini_get('sendmail_path');
if (false === stripos($ini_sendmail_path, 'qmail')) {
$this->Sendmail = '/var/qmail/bin/qmail-inject';
} else {
$this->Sendmail = $ini_sendmail_path;
$ini_sendmail_path = '/var/qmail/bin/qmail-inject';
}
$this->Sendmail = $this->parseSendmailPath($ini_sendmail_path);
$this->Mailer = 'qmail';
}
@ -1860,25 +1920,27 @@ class PHPMailer
//PHP config has a sender address we can use
$this->Sender = ini_get('sendmail_from');
}
//CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
$sendmailArgs = [];
// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
// Also don't add the -f automatically unless it has been set either via Sender
// or sendmail_path. Otherwise it can introduce new problems.
// @see http://github.com/PHPMailer/PHPMailer/issues/2298
if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) {
if ($this->Mailer === 'qmail') {
$sendmailFmt = '%s -f%s';
} else {
$sendmailFmt = '%s -oi -f%s -t';
}
} elseif ($this->Mailer === 'qmail') {
$sendmailFmt = '%s';
} else {
//Allow sendmail to choose a default envelope sender. It may
//seem preferable to force it to use the From header as with
//SMTP, but that introduces new problems (see
//<https://github.com/PHPMailer/PHPMailer/issues/2298>), and
//it has historically worked this way.
$sendmailFmt = '%s -oi -t';
$sendmailArgs[] = '-f' . $this->Sender;
}
$sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
// Qmail doesn't accept all the sendmail parameters
// @see https://github.com/PHPMailer/PHPMailer/issues/3189
if ($this->Mailer !== 'qmail') {
$sendmailArgs[] = '-i';
$sendmailArgs[] = '-t';
}
$resultArgs = (empty($sendmailArgs) ? '' : ' ' . implode(' ', $sendmailArgs));
$sendmail = trim(escapeshellcmd($this->Sendmail) . $resultArgs);
$this->edebug('Sendmail path: ' . $this->Sendmail);
$this->edebug('Sendmail command: ' . $sendmail);
$this->edebug('Envelope sender: ' . $this->Sender);
@ -2062,7 +2124,8 @@ class PHPMailer
$this->Sender = ini_get('sendmail_from');
}
if (!empty($this->Sender) && static::validateAddress($this->Sender)) {
if (self::isShellSafe($this->Sender)) {
$phpmailer_path = ini_get('sendmail_path');
if (self::isShellSafe($this->Sender) && strpos($phpmailer_path, ' -f') === false) {
$params = sprintf('-f%s', $this->Sender);
}
$old_from = ini_get('sendmail_from');
@ -3259,6 +3322,30 @@ class PHPMailer
@unlink($signed);
throw new Exception(self::lang('signing') . openssl_error_string());
}
if ($this->encrypt_body) {
// Write out the encrypted message
$file = tempnam(sys_get_temp_dir(), 'mail');
if (false === file_put_contents($file, $this->MIMEHeader . static::$LE . static::$LE . $body)) {
throw new phpmailerException($this->lang('encrypting') . ' Could not write temp file');
}
$encrypted = tempnam(sys_get_temp_dir(), 'encrypted');
$encrypt = openssl_pkcs7_encrypt($file, $encrypted, $this->encrypt_recipcerts, []);
if ($encrypt) {
@unlink($file);
$body = file_get_contents($encrypted);
// As with signing, the headers get rewriting after encrypting
$parts = explode("\n\n", $body, 2);
$this->MIMEHeader = $parts[0] . static::$LE . static::$LE;
$body = $parts[1];
@unlink($encrypted);
} else {
@unlink($file);
@unlink($encrypted);
throw new phpmailerException($this->lang('encrypting') . openssl_error_string());
}
}
} catch (Exception $exc) {
$body = '';
if ($this->exceptions) {
@ -5068,6 +5155,19 @@ class PHPMailer
$this->sign_extracerts_file = $extracerts_filename;
}
/**
* Set the certificates, keys and passwords to encrypt via S/MIME
*
* @param array $recipcerts Array of certificates used for recipients in PEM format
* @param mixed $recipcert_file
*/
public function add_encryption($recipcert_file)
{
$this->encrypt_body = true;
$cert = file_get_contents($recipcert_file);
array_push($this->encrypt_recipcerts, $cert);
}
/**
* Quoted-Printable-encode a DKIM header.
*

View File

@ -47,7 +47,7 @@ class POP3
* @var string
* @deprecated This constant will be removed in PHPMailer 8.0. Use `PHPMailer::VERSION` instead.
*/
const VERSION = '7.0.1';
const VERSION = '7.0.2';
/**
* Default POP3 port number.

View File

@ -36,7 +36,7 @@ class SMTP
* @var string
* @deprecated This constant will be removed in PHPMailer 8.0. Use `PHPMailer::VERSION` instead.
*/
const VERSION = '7.0.1';
const VERSION = '7.0.2';
/**
* SMTP line break constant.

View File

@ -20,6 +20,23 @@ use PHPMailer\Test\SendTestCase;
*/
final class MailTransportTest extends SendTestCase
{
/** @var string */
private $originalSendmailFrom = '';
protected function set_up()
{
parent::set_up();
$from = ini_get('sendmail_from');
$this->originalSendmailFrom = $from === false ? '' : $from;
}
protected function tear_down()
{
ini_set('sendmail_from', $this->originalSendmailFrom);
parent::tear_down();
}
/**
* Test sending using SendMail.
*
@ -65,12 +82,6 @@ final class MailTransportTest extends SendTestCase
*/
public function testMailSend()
{
$sendmail = ini_get('sendmail_path');
// No path in sendmail_path.
if (strpos($sendmail, '/') === false) {
ini_set('sendmail_path', '/usr/sbin/sendmail -t -i ');
}
$this->Mail->Body = 'Sending via mail()';
$this->buildBody();
$this->Mail->Subject = $this->Mail->Subject . ': mail()';
@ -105,4 +116,146 @@ final class MailTransportTest extends SendTestCase
$msg = $this->Mail->getSentMIMEMessage();
self::assertStringNotContainsString("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers');
}
/**
* Test sending using PHP mail() function with Sender address
* and explicit sendmail_from ini set.
* Test running required with:
* php -d sendmail_path="/usr/sbin/sendmail -t -i -frpath@example.org" ./vendor/bin/phpunit
*
* @group sendmailparams
* @covers \PHPMailer\PHPMailer\PHPMailer::isMail
*/
public function testMailSendWithSendmailParams()
{
$sender = 'rpath@example.org';
if (strpos(ini_get('sendmail_path'), $sender) === false) {
self::markTestSkipped('Custom Sendmail php.ini not available');
}
$this->Mail->Body = 'Sending via mail()';
$this->buildBody();
$this->Mail->Subject = $this->Mail->Subject . ': mail()';
$this->Mail->clearAddresses();
$this->setAddress('testmailsend@example.com', 'totest');
ini_set('sendmail_from', $sender);
$this->Mail->createHeader();
$this->Mail->isMail();
self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);
}
/**
* Test sending using SendMail with Sender address
* and explicit sendmail_from ini set.
* Test running required with:
* php -d sendmail_path="/usr/sbin/sendmail -t -i -frpath@example.org" ./vendor/bin/phpunit
*
* @group sendmailparams
* @covers \PHPMailer\PHPMailer\PHPMailer::isSendmail
*/
public function testSendmailSendWithSendmailParams()
{
$sender = 'rpath@example.org';
if (strpos(ini_get('sendmail_path'), $sender) === false) {
self::markTestSkipped('Custom Sendmail php.ini not available');
}
$this->Mail->Body = 'Sending via sendmail';
$this->buildBody();
$subject = $this->Mail->Subject;
$this->Mail->Subject = $subject . ': sendmail';
ini_set('sendmail_from', $sender);
$this->Mail->isSendmail();
self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);
}
/**
* Test parsing of sendmail path and with certain parameters.
*
* @group sendmailparams
* @covers \PHPMailer\PHPMailer\PHPMailer::parseSendmailPath
* @dataProvider sendmailPathProvider
*
* @param string $sendmailPath The sendmail path to parse.
* @param string $expectedCommand The expected command after parsing.
* @param string $expectedSender The expected Sender (-f parameter) after parsing.
*/
public function testParseSendmailPath($sendmailPath, $expectedCommand, $expectedSender)
{
$mailer = $this->Mail;
$parseSendmailPath = \Closure::bind(
function ($path) {
return $this->{'parseSendmailPath'}($path);
},
$mailer,
\PHPMailer\PHPMailer\PHPMailer::class
);
$command = $parseSendmailPath($sendmailPath);
self::assertSame($expectedCommand, $command, 'Sendmail command not parsed correctly');
self::assertSame($expectedSender, $mailer->Sender, 'Sender property not set correctly');
}
/**
* Data provider for testParseSendmailPath.
*
* @return array{
* 0: string, // The sendmail path to parse.
* 1: string, // The expected command after parsing.
* 2: string // The expected Sender (-f parameter) after parsing.
* }
*/
public function sendmailPathProvider()
{
return [
'path only' => [
'/usr/sbin/sendmail',
'/usr/sbin/sendmail',
''
],
'with i and t' => [
'/usr/sbin/sendmail -i -t',
'/usr/sbin/sendmail',
''
],
'with f concatenated' => [
'/usr/sbin/sendmail -frpath@example.org -i',
'/usr/sbin/sendmail',
'rpath@example.org'
],
'with f separated' => [
'/usr/sbin/sendmail -f rpath@example.org -t',
'/usr/sbin/sendmail',
'rpath@example.org',
],
'with extra flags preserved' => [
'/opt/sendmail -x -y -fuser@example.org',
'/opt/sendmail -x -y',
'user@example.org',
],
"extra flags with values preserved" => [
'/opt/sendmail -X /path/to/logfile -fuser@example.org',
'/opt/sendmail -X /path/to/logfile',
'user@example.org',
],
"extra flags concatenated preserved" => [
'/opt/sendmail -X/path/to/logfile -t -i',
'/opt/sendmail -X/path/to/logfile',
'',
],
"option values with regular parameters" => [
'/opt/sendmail -oi -t',
'/opt/sendmail',
'',
],
];
}
}