: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if (false !== $punycode) {
return substr($address, 0, $pos) . $punycode;
* Create a message and send it.
* Uses the sending method specified by $Mailer.
* @return bool false on error - See the ErrorInfo property for details of the error
return $this->postSend();
} catch (Exception $exc) {
$this->setError($exc->getMessage());
* Prepare a message for sending.
public function preSend()
|| ('mail' === $this->Mailer && (\PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0))
//SMTP mandates RFC-compliant line endings
//and it's also used with mail() on Windows
static::setLE(self::CRLF);
//Maintain backward compatibility with legacy Linux command line mailers
//Check for buggy PHP versions that add a header with an incorrect line break
&& ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017)
|| (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103))
&& ini_get('mail.add_x_header') === '1'
&& stripos(PHP_OS, 'WIN') === 0
trigger_error($this->lang('buggy_php'), E_USER_WARNING);
$this->error_count = 0; //Reset errors
//Dequeue recipient and Reply-To addresses with IDN
foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
$params[1] = $this->punyencodeAddress($params[1]);
call_user_func_array([$this, 'addAnAddress'], $params);
if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
//Validate From, Sender, and ConfirmReadingTo addresses
foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
if ($this->{$address_kind} === null) {
$this->{$address_kind} = '';
$this->{$address_kind} = trim($this->{$address_kind});
if (empty($this->{$address_kind})) {
$this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind});
if (!static::validateAddress($this->{$address_kind})) {
$error_message = sprintf(
$this->lang('invalid_address'),
$this->setError($error_message);
$this->edebug($error_message);
throw new Exception($error_message);
//Set whether the message is multipart/alternative
if ($this->alternativeExists()) {
$this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE;
//Refuse to send an empty message unless we are specifically allowing it
if (!$this->AllowEmpty && empty($this->Body)) {
throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
//Trim subject consistently
$this->Subject = trim($this->Subject);
//Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
$this->MIMEBody = $this->createBody();
//createBody may have added some headers, so retain them
$tempheaders = $this->MIMEHeader;
$this->MIMEHeader = $this->createHeader();
$this->MIMEHeader .= $tempheaders;
//To capture the complete message when using mail(), create
//an extra header list which createHeader() doesn't fold in
if ('mail' === $this->Mailer) {
if (count($this->to) > 0) {
$this->mailHeader .= $this->addrAppend('To', $this->to);
$this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
$this->mailHeader .= $this->headerLine(
$this->encodeHeader($this->secureHeader($this->Subject))
//Sign with DKIM if enabled
!empty($this->DKIM_domain)
&& !empty($this->DKIM_selector)
&& (!empty($this->DKIM_private_string)
|| (!empty($this->DKIM_private)
&& static::isPermittedPath($this->DKIM_private)
&& file_exists($this->DKIM_private)
$header_dkim = $this->DKIM_Add(
$this->MIMEHeader . $this->mailHeader,
$this->encodeHeader($this->secureHeader($this->Subject)),
$this->MIMEHeader = static::stripTrailingWSP($this->MIMEHeader) . static::$LE .
static::normalizeBreaks($header_dkim) . static::$LE;
} catch (Exception $exc) {
$this->setError($exc->getMessage());
* Actually send a message via the selected mechanism.
public function postSend()
//Choose the mailer and send through it
return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
$sendMethod = $this->Mailer . 'Send';
if (method_exists($this, $sendMethod)) {
return $this->{$sendMethod}($this->MIMEHeader, $this->MIMEBody);
return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
} catch (Exception $exc) {
$this->setError($exc->getMessage());
$this->edebug($exc->getMessage());
if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true && $this->smtp->connected()) {
* Send mail using the $Sendmail program.
* @see PHPMailer::$Sendmail
* @param string $header The message headers
* @param string $body The message body
protected function sendmailSend($header, $body)
if ($this->Mailer === 'qmail') {
$this->edebug('Sending with qmail');
$this->edebug('Sending with sendmail');
$header = static::stripTrailingWSP($header) . static::$LE . static::$LE;
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
//A space after `-f` is optional, but there is a long history of its presence
//causing problems, so we don't use one
//Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
//Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
//Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
//Example problem: https://www.drupal.org/node/1057954
$sendmail_from_value = ini_get('sendmail_from');
if (empty($this->Sender) && !empty($sendmail_from_value)) {
//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.
if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) {
if ($this->Mailer === 'qmail') {
$sendmailFmt = '%s -f%s';
$sendmailFmt = '%s -oi -f%s -t';
//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';
$sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
$this->edebug('Sendmail path: ' . $this->Sendmail);
$this->edebug('Sendmail command: ' . $sendmail);
$this->edebug('Envelope sender: ' . $this->Sender);
$this->edebug("Headers: {$header}");
foreach ($this->SingleToArray as $toAddr) {
$mail = @popen($sendmail, 'w');
throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
$this->edebug("To: {$toAddr}");
fwrite($mail, 'To: ' . $toAddr . "\n");
$addrinfo = static::parseAddresses($toAddr, true, $this->CharSet);
[[$addrinfo['address'], $addrinfo['name']]],
$this->edebug("Result: " . ($result === 0 ? 'true' : 'false'));
throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
$mail = @popen($sendmail, 'w');
throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
$this->edebug("Result: " . ($result === 0 ? 'true' : 'false'));
throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
* Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
* Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
* @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
* @param string $string The string to be validated
protected static function isShellSafe($string)
//It's not possible to use shell commands safely (which includes the mail() function) without escapeshellarg,
//but some hosting providers disable it, creating a security problem that we don't want to have to deal with,
if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) {
escapeshellcmd($string) !== $string
|| !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
$length = strlen($string);
for ($i = 0; $i < $length; ++$i) {
//All other characters have a special meaning in at least one common shell, including = and +.
//Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
//Note that this does permit non-Latin alphanumeric characters based on the current locale.
if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
* Check whether a file path is of a permitted type.
* Used to reject URLs and phar files from functions that access local file paths,
* @param string $path A relative or absolute path to a file
protected static function isPermittedPath($path)
//Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1
return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path);
* Check whether a file path is safe, accessible, and readable.
* @param string $path A relative or absolute path to a file
protected static function fileIsAccessible($path)
if (!static::isPermittedPath($path)) {
$readable = is_file($path);
//If not a UNC path (expected to start with \\), check read permission, see #2069
if (strpos($path, '\\\\') !== 0) {
$readable = $readable && is_readable($path);
* Send mail using the PHP mail() function.
* @see http://www.php.net/manual/en/book.mail.php
* @param string $header The message headers
* @param string $body The message body
protected function mailSend($header, $body)
$header = static::stripTrailingWSP($header) . static::$LE . static::$LE;
foreach ($this->to as $toaddr) {
$toArr[] = $this->addrFormat($toaddr);
$to = trim(implode(', ', $toArr));
//If there are no To-addresses (e.g. when sending only to BCC-addresses)
//the following should be added to get a correct DKIM-signature.
//Compare with $this->preSend()
$to = 'undisclosed-recipients:;';
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
//A space after `-f` is optional, but there is a long history of its presence
//causing problems, so we don't use one
//Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
//Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
//Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
//Example problem: https://www.drupal.org/node/1057954
//CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
$sendmail_from_value = ini_get('sendmail_from');
if (empty($this->Sender) && !empty($sendmail_from_value)) {
//PHP config has a sender address we can use
$this->Sender = ini_get('sendmail_from');
if (!empty($this->Sender) && static::validateAddress($this->Sender)) {
if (self::isShellSafe($this->Sender)) {
$params = sprintf('-f%s', $this->Sender);
$old_from = ini_get('sendmail_from');
ini_set('sendmail_from', $this->Sender);
if ($this->SingleTo && count($toArr) > 1) {
foreach ($toArr as $toAddr) {
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
$addrinfo = static::parseAddresses($toAddr, true, $this->CharSet);
[[$addrinfo['address'], $addrinfo['name']]],
$result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
$this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
ini_set('sendmail_from', $old_from);
throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL);
* Get an instance to use for SMTP operations.
* Override this function to load your own SMTP implementation,
* or set one with setSMTPInstance.
public function getSMTPInstance()
if (!is_object($this->smtp)) {
$this->smtp = new SMTP();