This commit is contained in:
Vincent JARDIN 2023-11-22 13:25:47 +00:00 committed by GitHub
commit bdcdf72a64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 161 additions and 3 deletions

View File

@ -372,6 +372,17 @@ class PHPMailer
*/
public $Timeout = 300;
/**
* DSN Sender Extensions
* 'RET=FULL' shall return the complete message in case of any error
* xor
* 'RET=HDRS' shall return only the headers of the failed e-mail.
* 'ENVID=xyz' shall include the ID xyz on the way back of this e-mail if any error.
*
* @see https://tools.ietf.org/html/rfc3461 See section 4.3 and 4.4 regarding RET and ENVID
*/
public $dsn_ret = '';
/**
* Comma separated list of DSN notifications
* 'NEVER' under no circumstances a DSN must be returned to the sender.
@ -2029,7 +2040,7 @@ class PHPMailer
} else {
$smtp_from = $this->Sender;
}
if (!$this->smtp->mail($smtp_from)) {
if (!$this->smtp->mail($smtp_from, $this->dsn_ret)) {
$this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
}

View File

@ -887,6 +887,87 @@ class SMTP
}
}
/**
* Enforse the content of the DSN to be compliant with the RFC 3461, section 4.
*
* @param string $s eg. câfé
*/
protected static function xtext(string $s): string
{
$r = '';
for ($i = 0, $len = strlen($s); $i < $len; ++$i) {
$o = ord($s[$i]);
if (($o >= 0x21) && ($o < 0x7e) && ($o !== ord('+')) && ($o !== ord('='))) {
$r .= $s[$i];
continue;
}
$r .= sprintf('+%02X', $o & 0xff);
}
return $r;
}
/**
* Enforce the content of the DSN to be compliant with the RFC 3461, section 4.
*
* @param string $s eg. câfé
*
* @return false|string
*/
protected static function dsnize(string $str)
{
$r = [];
$args = explode(' ', $str);
switch (count($args)) {
case 1: /* likely RET=HDRS|FULL */
case 2: /* likely RET=HDRS|FULL ENVID=xtext */
/* place holder if more arguments get required */
break;
default:
return false;
}
foreach ($args as $i => $arg) {
$tokens = explode('=', $arg);
if ($i === 0) {
if (count($tokens) !== 2) {
return false;
}
if (strcasecmp($tokens[0], 'RET') !== 0) {
return false;
}
if ((strcasecmp($tokens[1], 'FULL') !== 0) &&
(strcasecmp($tokens[1], 'HDRS') !== 0)) {
return false;
}
$r[] = sprintf('RET=%s', strtoupper($tokens[1])); /* FULL or HDRS */
continue;
}
if ($i == 1) {
/* support any cases including ENVID=ab=de */
if (count($tokens) <= 1) {
return false;
}
if (strcasecmp($tokens[0], 'ENVID') !== 0) {
return false;
}
array_shift($tokens); // pop ENVID
$id = implode('=', $tokens);
$r[] = sprintf('ENVID=%s', self::xtext($id));
continue;
}
}
return implode(' ', $r);
}
/**
* Send an SMTP MAIL command.
* Starts a mail transaction from the email address specified in
@ -896,16 +977,27 @@ class SMTP
* Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF>.
*
* @param string $from Source address of this message
* @param string $ret DSN argument
*
* @return bool
*/
public function mail($from)
public function mail($from, $ret = '')
{
$useVerp = ($this->do_verp ? ' XVERP' : '');
$useRet = '';
if ($this->getServerExt('DSN') && !empty($ret)) {
$useRet = self::dsnize($ret);
if (!is_string($useRet)) {
$useRet = ''; // bad one
} else {
$useRet = ' ' . $useRet;
}
}
return $this->sendCommand(
'MAIL FROM',
'MAIL FROM:<' . $from . '>' . $useVerp,
'MAIL FROM:<' . $from . '>' . $useVerp . $useRet,
250
);
}

55
test/dsn.php Normal file
View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/*
*
* The syntax for "esmtp-value" in [4] does not allow SP, "=", control
* characters, or characters outside the traditional ASCII range of 1- 127
* decimal to be transmitted in an esmtp-value. Because the ENVID and ORCPT
* parameters may need to convey values outside this range, the esmtp-values for
* these parameters are encoded as "xtext". "xtext" is formally defined as
* follows:
*
* xtext = *( xchar / hexchar )
*
* xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive, except for
* "+" and "=".
*
* ; "hexchar"s are intended to encode octets that cannot appear
* ; as ASCII characters within an esmtp-value.
*
* hexchar = ASCII "+" immediately followed by two upper case hexadecimal digits
*
* When encoding an octet sequence as xtext:
*
* + Any ASCII CHAR between "!" and "~" inclusive, except for "+" and "=",
* MAY be encoded as itself. (A CHAR in this range MAY instead be encoded
* as a "hexchar", at the implementor's discretion.)
*
* + ASCII CHARs that fall outside the range above must be encoded as "hexchar".
*/
include '../src/SMTP.php';
class SMTPTestDsn extends PHPMailer\PHPMailer\SMTP
{
public function TestDsn()
{
$strs = [];
$strs[] = 'RET=FULL ENVID=xyz';
$strs[] = 'RET=FUll ENVID=xyz';
$strs[] = 'ENVID=xyz';
$strs[] = 'RET=ful ENVID=xyz';
$strs[] = 'RET=Hdrs';
$strs[] = 'RET=Hdrs ENVID=abcde+ée';
$strs[] = 'RET=Hdrs ENVID=abc=de';
$strs[] = 'RET=FULL ENVID=+Vincent+=+Jardin+=';
foreach ($strs as $str) {
printf("%s -> %s\n", $str, \PHPMailer\PHPMailer\SMTP::dsnize($str));
}
}
}
SMTPTestDsn::TestDsn();