From 69f06e9cde5f5f0e4c556ceffe986dc37b7e1c33 Mon Sep 17 00:00:00 2001 From: Erwann Philippe Date: Tue, 10 Mar 2026 17:38:47 +0100 Subject: [PATCH] =?UTF-8?q?Modifs=20divers=20+d=C3=A9but=20du=20syst=C3=A8?= =?UTF-8?q?me=20de=20mail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Logos/nothing.jpg | Bin 0 -> 65846 bytes Assets/Logos/octime.png | Bin 0 -> 12060 bytes Assets/functions.php | 52 +- Assets/sendMail.php | 67 + Mailer/Exception.php | 40 + Mailer/PHPMailer.php | 5525 ++++++++++++++++++++++++++ Mailer/SMTP.php | 1617 ++++++++ activate.php | 24 + admin/admin.php | 2 +- admin/modifyEvent.php | 29 +- diapo.php | 2 +- intranet v1.sql | 134 +- login.php | 1 + styles-scripts/admin.modifyEvent.css | 15 + styles-scripts/editableCards.js | 3 +- 15 files changed, 7450 insertions(+), 61 deletions(-) create mode 100644 Assets/Logos/nothing.jpg create mode 100644 Assets/Logos/octime.png create mode 100644 Assets/sendMail.php create mode 100644 Mailer/Exception.php create mode 100644 Mailer/PHPMailer.php create mode 100644 Mailer/SMTP.php create mode 100644 activate.php create mode 100644 styles-scripts/admin.modifyEvent.css diff --git a/Assets/Logos/nothing.jpg b/Assets/Logos/nothing.jpg new file mode 100644 index 0000000000000000000000000000000000000000..82e0a57623df4f597b3e1bb0899213b76025c5bf GIT binary patch literal 65846 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

;TmP<9ZMS=`a^ae?aPXU!n z#Drxlcxk9#y!1&lrzL&baBmF1&)hjaS9}e>?Rmn20RC2{eO&bPj+n_d)wbc*fXR4G zB^%JGYm3R-4S)oDN5Mn&tWIT;ZddmE^Rv%qQ}>F^s^qf#uz%>MhkG;Au?FzCIm zhnMUNCY|ZmFA?IJ1R>mL<_Lcgj?>dIdaRP``q420lLoTIfp;a2E9zzb17l|9u6x^% zOQ(ND%RZBl5t5mSfbwIn;K4n?~;y<9lGU;@-%M5`wC z`}U$`kB9|7KA%N8=WHBUKOHI0Jzf z+tA_KP?@`djMsUbWn>;4Qm8iw-{IfqCJlOn>g30{c{iharn;QP*hz2J4r)^6Qna-Nr1ROoG4Q`i;OzYGmo3 zuRSbJ&@j`|E3m5AQ(-W-^WJET&kWDF4YK%}d7eIvx$Z`aHK$qA1(%6Y%*^rxny2r! zfdnN+(J$e6T_UdKD);}y_aSp_CR&^ zTMLtnbe8fq1pfW!^;#A$I*Jjs-&|z%>CL3gSl1ic*r%UNti;X~-Tfta{%kGl5C@+n z_ZIXWY5%Xl2uKf5Q^o}g8c9ig=rJqPTNIKLwXNz)0Z?r8Ot`-^9O1sy&O#13v0xMC zko7S><|#hS?AZgl+0YhwS$t-B6g~cZadpJ%FK8$$whriqeyh+|<%leK&{@Jgp1 zn=wlNd{d%KXrN&I?dD~OoRPGV8X;v24V9J?fsaW7jG(l{r>9Fun28jXIrE>Wmrcp# zf8`&(NJeIVK0p=jo>w@f|HbR5e&vH_Wff(SmC?r=C0ZM!v_;;B@t!9T-{WmFv98gd zzQ;UjQ7sxS3=j#_Py)7ie)K4LVBPd1Az&RY$of+#Yg`f}+&uigs46#q zqwEN(@?(jjVxTHNm)PG1wXh0shJ`d+;S39Fwn7X}obJ3tp@^syy)& zv{+l5?oC3Ah16jb1__oeysHyQL}|Xo19YJOle^g?2py2Eb;LUIKj33vQG^+XqVFP07pe zpi;O?qCl?Pe`qhyym{VHi?y!5&I8UMVU=7K(@+M%+wm8J46;j?H5z1)Ox?@xt1ba! zGKE90!yvrjt4_DX2MBM7XMCu0{}sR+*4#xk_q;6xJcsSmBzN;TTetztH8nBSgc`sA z1HERENEwi@IMLP^xU!)oEIjkO3@RJ7vz`%9W%F@|@p*oq8SY0eU~@t11^SjHDkE4g z(6{W9PMC!HmLn~4%IT_g_YxP45TNU48-^1a+i1lSl+YACa>Jm6CUEPVz?iw=8IK7C z5H9smPzM24?I4&gQ6!rAXHFjqi?4d@GJwCv6r^%O*P@*@L$x3tbg(zU3ozR+#hYi? z!aduY3j-G#pq?!dvTXH}!E_YeLf1$18^Jw<^a^(LA=E>#j|ewIJ;df%PS8Va7UR!& ziW+4OOThqiprGShg%X4gSo;lh49G(0z`o3IZ4uw#Rq96GKpr$`;6+zhx�p$D08v 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 literal 0 HcmV?d00001 diff --git a/Assets/Logos/octime.png b/Assets/Logos/octime.png new file mode 100644 index 0000000000000000000000000000000000000000..f48b2916d6f173bd723dae1cb31b62ee59669a43 GIT binary patch literal 12060 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 0; + + mysqli_stmt_close($stmt); + + return $success; +} + +function getEventDate($bdd, $event) +{ + $stmt = mysqli_prepare( + $bdd, + "SELECT date FROM evenements WHERE id = ? LIMIT 1" + ); + + mysqli_stmt_bind_param($stmt, "s", $event); + mysqli_stmt_execute($stmt); + + $result = mysqli_stmt_get_result($stmt); + $row = mysqli_fetch_assoc($result); + + mysqli_stmt_close($stmt); + + return $row ? $row['date'] : null; +} diff --git a/Assets/sendMail.php b/Assets/sendMail.php new file mode 100644 index 0000000..29c85ce --- /dev/null +++ b/Assets/sendMail.php @@ -0,0 +1,67 @@ +Validation de création de compte +

L'utilisateur {user} a demandé la création de son compte.

+Le mail de validation sera envoyé à l'adresse : {mail}
+Si vous voulez accepter, cliquez
ici
+

Sinon, vous pouvez simplement ignorer ce message


+

Des bisous

+"," +

Activation de votre compte

+

Cliquez sur le lien ci-dessous afin de créer votre mot de passe;
+Ce lien expirera dans 7 jours
+Merci de ne pas répondre à cet email. +

+{lien} +"]; +if(isset($_GET['type'])){ + $type = $_GET['type']; +}else{ + die("pas de type"); +} + +if($type == 0){ + $user = $_GET['user']; + $email = $_GET['email']; + $message[0] = str_replace("{user}", $user, $message[0]); + $message[0] = str_replace('{mail}', $email, $message[0]); +} + +$mail = new PHPMailer(true); + +try { + + $mail->isSMTP(); + $mail->Host = 'smtp.office365.com'; + $mail->SMTPAuth = true; + $mail->Username = 'intranet-apei@apeimbge.fr'; + $mail->Password = 'Asdb0789!'; + $mail->SMTPSecure = 'tls'; + $mail->Port = 587; + $mail->CharSet = 'UTF-8'; + + $mail->isHTML(true); + + $mail->setFrom('intranet-apei@apeimbge.fr', 'Demande de connexion'); + //$mail->addAddress('blemaire@apeimbge.fr'); + $mail->addAddress('erwann.philippe2@gmail.com'); + + $mail->Subject = $objet[$type]; + $mail->Body = $message[$type]; + + $mail->send(); + + header('location: ../index.php'); + +} catch (Exception $e) { + echo "Erreur lors de l'envoi : {$mail->ErrorInfo}"; +} \ No newline at end of file diff --git a/Mailer/Exception.php b/Mailer/Exception.php new file mode 100644 index 0000000..09c1a2c --- /dev/null +++ b/Mailer/Exception.php @@ -0,0 +1,40 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer exception handler. + * + * @author Marcus Bointon + */ +class Exception extends \Exception +{ + /** + * Prettify error message output. + * + * @return string + */ + public function errorMessage() + { + return '' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "
\n"; + } +} diff --git a/Mailer/PHPMailer.php b/Mailer/PHPMailer.php new file mode 100644 index 0000000..2bb3578 --- /dev/null +++ b/Mailer/PHPMailer.php @@ -0,0 +1,5525 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer - PHP email creation and transport class. + * + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + */ +class PHPMailer +{ + const CHARSET_ASCII = 'us-ascii'; + const CHARSET_ISO88591 = 'iso-8859-1'; + const CHARSET_UTF8 = 'utf-8'; + + const CONTENT_TYPE_PLAINTEXT = 'text/plain'; + const CONTENT_TYPE_TEXT_CALENDAR = 'text/calendar'; + const CONTENT_TYPE_TEXT_HTML = 'text/html'; + const CONTENT_TYPE_MULTIPART_ALTERNATIVE = 'multipart/alternative'; + const CONTENT_TYPE_MULTIPART_MIXED = 'multipart/mixed'; + const CONTENT_TYPE_MULTIPART_RELATED = 'multipart/related'; + + const ENCODING_7BIT = '7bit'; + const ENCODING_8BIT = '8bit'; + const ENCODING_BASE64 = 'base64'; + const ENCODING_BINARY = 'binary'; + const ENCODING_QUOTED_PRINTABLE = 'quoted-printable'; + + const ENCRYPTION_STARTTLS = 'tls'; + const ENCRYPTION_SMTPS = 'ssl'; + + const ICAL_METHOD_REQUEST = 'REQUEST'; + const ICAL_METHOD_PUBLISH = 'PUBLISH'; + const ICAL_METHOD_REPLY = 'REPLY'; + const ICAL_METHOD_ADD = 'ADD'; + const ICAL_METHOD_CANCEL = 'CANCEL'; + const ICAL_METHOD_REFRESH = 'REFRESH'; + const ICAL_METHOD_COUNTER = 'COUNTER'; + const ICAL_METHOD_DECLINECOUNTER = 'DECLINECOUNTER'; + + /** + * Email priority. + * Options: null (default), 1 = High, 3 = Normal, 5 = low. + * When null, the header is not set at all. + * + * @var int|null + */ + public $Priority; + + /** + * The character set of the message. + * + * @var string + */ + public $CharSet = self::CHARSET_ISO88591; + + /** + * The MIME Content-type of the message. + * + * @var string + */ + public $ContentType = self::CONTENT_TYPE_PLAINTEXT; + + /** + * The message encoding. + * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". + * + * @var string + */ + public $Encoding = self::ENCODING_8BIT; + + /** + * Holds the most recent mailer error message. + * + * @var string + */ + public $ErrorInfo = ''; + + /** + * The From email address for the message. + * + * @var string + */ + public $From = ''; + + /** + * The From name of the message. + * + * @var string + */ + public $FromName = ''; + + /** + * The envelope sender of the message. + * This will usually be turned into a Return-Path header by the receiver, + * and is the address that bounces will be sent to. + * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP. + * + * @var string + */ + public $Sender = ''; + + /** + * The Subject of the message. + * + * @var string + */ + public $Subject = ''; + + /** + * An HTML or plain text message body. + * If HTML then call isHTML(true). + * + * @var string + */ + public $Body = ''; + + /** + * The plain-text message body. + * This body can be read by mail clients that do not have HTML email + * capability such as mutt & Eudora. + * Clients that can read HTML will view the normal Body. + * + * @var string + */ + public $AltBody = ''; + + /** + * An iCal message part body. + * Only supported in simple alt or alt_inline message types + * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator. + * + * @see https://kigkonsult.se/iCalcreator/ + * + * @var string + */ + public $Ical = ''; + + /** + * Value-array of "method" in Contenttype header "text/calendar" + * + * @var string[] + */ + protected static $IcalMethods = [ + self::ICAL_METHOD_REQUEST, + self::ICAL_METHOD_PUBLISH, + self::ICAL_METHOD_REPLY, + self::ICAL_METHOD_ADD, + self::ICAL_METHOD_CANCEL, + self::ICAL_METHOD_REFRESH, + self::ICAL_METHOD_COUNTER, + self::ICAL_METHOD_DECLINECOUNTER, + ]; + + /** + * The complete compiled MIME message body. + * + * @var string + */ + protected $MIMEBody = ''; + + /** + * The complete compiled MIME message headers. + * + * @var string + */ + protected $MIMEHeader = ''; + + /** + * Extra headers that createHeader() doesn't fold in. + * + * @var string + */ + protected $mailHeader = ''; + + /** + * Word-wrap the message body to this number of chars. + * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. + * + * @see static::STD_LINE_LENGTH + * + * @var int + */ + public $WordWrap = 0; + + /** + * Which method to use to send mail. + * Options: "mail", "sendmail", or "smtp". + * + * @var string + */ + public $Mailer = 'mail'; + + /** + * The path to the sendmail program. + * + * @var string + */ + public $Sendmail = '/usr/sbin/sendmail'; + + /** + * Whether mail() uses a fully sendmail-compatible MTA. + * One which supports sendmail's "-oi -f" options. + * + * @var bool + */ + public $UseSendmailOptions = true; + + /** + * The email address that a reading confirmation should be sent to, also known as read receipt. + * + * @var string + */ + public $ConfirmReadingTo = ''; + + /** + * The hostname to use in the Message-ID header and as default HELO string. + * If empty, PHPMailer attempts to find one with, in order, + * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value + * 'localhost.localdomain'. + * + * @see PHPMailer::$Helo + * + * @var string + */ + public $Hostname = ''; + + /** + * An ID to be used in the Message-ID header. + * If empty, a unique id will be generated. + * You can set your own, but it must be in the format "", + * as defined in RFC5322 section 3.6.4 or it will be ignored. + * + * @see https://www.rfc-editor.org/rfc/rfc5322#section-3.6.4 + * + * @var string + */ + public $MessageID = ''; + + /** + * The message Date to be used in the Date header. + * If empty, the current date will be added. + * + * @var string + */ + public $MessageDate = ''; + + /** + * SMTP hosts. + * Either a single hostname or multiple semicolon-delimited hostnames. + * You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * You can also specify encryption type, for example: + * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). + * Hosts will be tried in order. + * + * @var string + */ + public $Host = 'localhost'; + + /** + * The default SMTP server port. + * + * @var int + */ + public $Port = 25; + + /** + * The SMTP HELO/EHLO name used for the SMTP connection. + * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find + * one with the same method described above for $Hostname. + * + * @see PHPMailer::$Hostname + * + * @var string + */ + public $Helo = ''; + + /** + * What kind of encryption to use on the SMTP connection. + * Options: '', static::ENCRYPTION_STARTTLS, or static::ENCRYPTION_SMTPS. + * + * @var string + */ + public $SMTPSecure = ''; + + /** + * Whether to enable TLS encryption automatically if a server supports it, + * even if `SMTPSecure` is not set to 'tls'. + * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. + * + * @var bool + */ + public $SMTPAutoTLS = true; + + /** + * Whether to use SMTP authentication. + * Uses the Username and Password properties. + * + * @see PHPMailer::$Username + * @see PHPMailer::$Password + * + * @var bool + */ + public $SMTPAuth = false; + + /** + * Options array passed to stream_context_create when connecting via SMTP. + * + * @var array + */ + public $SMTPOptions = []; + + /** + * SMTP username. + * + * @var string + */ + public $Username = ''; + + /** + * SMTP password. + * + * @var string + */ + public $Password = ''; + + /** + * SMTP authentication type. Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2. + * If not specified, the first one from that list that the server supports will be selected. + * + * @var string + */ + public $AuthType = ''; + + /** + * SMTP SMTPXClient command attributes + * + * @var array + */ + protected $SMTPXClient = []; + + /** + * An implementation of the PHPMailer OAuthTokenProvider interface. + * + * @var OAuthTokenProvider + */ + protected $oauth; + + /** + * The SMTP server timeout in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. + * + * @var int + */ + public $Timeout = 300; + + /** + * Comma separated list of DSN notifications + * 'NEVER' under no circumstances a DSN must be returned to the sender. + * If you use NEVER all other notifications will be ignored. + * 'SUCCESS' will notify you when your mail has arrived at its destination. + * 'FAILURE' will arrive if an error occurred during delivery. + * 'DELAY' will notify you if there is an unusual delay in delivery, but the actual + * delivery's outcome (success or failure) is not yet decided. + * + * @see https://www.rfc-editor.org/rfc/rfc3461.html#section-4.1 for more information about NOTIFY + */ + public $dsn = ''; + + /** + * SMTP class debug output mode. + * Debug output level. + * Options: + * @see SMTP::DEBUG_OFF: No output + * @see SMTP::DEBUG_CLIENT: Client messages + * @see SMTP::DEBUG_SERVER: Client and server messages + * @see SMTP::DEBUG_CONNECTION: As SERVER plus connection status + * @see SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed + * + * @see SMTP::$do_debug + * + * @var int + */ + public $SMTPDebug = 0; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise. + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * ```php + * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * ``` + * + * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug` + * level output is used: + * + * ```php + * $mail->Debugoutput = new myPsr3Logger; + * ``` + * + * @see SMTP::$Debugoutput + * + * @var string|callable|\Psr\Log\LoggerInterface + */ + public $Debugoutput = 'echo'; + + /** + * Whether to keep the SMTP connection open after each message. + * If this is set to true then the connection will remain open after a send, + * and closing the connection will require an explicit call to smtpClose(). + * It's a good idea to use this if you are sending multiple messages as it reduces overhead. + * See the mailing list example for how to use it. + * + * @var bool + */ + public $SMTPKeepAlive = false; + + /** + * Whether to split multiple to addresses into multiple messages + * or send them all in one message. + * Only supported in `mail` and `sendmail` transports, not in SMTP. + * + * @var bool + * + * @deprecated 6.0.0 PHPMailer isn't a mailing list manager! + */ + public $SingleTo = false; + + /** + * Storage for addresses when SingleTo is enabled. + * + * @var array + */ + protected $SingleToArray = []; + + /** + * Whether to generate VERP addresses on send. + * Only applicable when sending via SMTP. + * + * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path + * @see https://www.postfix.org/VERP_README.html Postfix VERP info + * + * @var bool + */ + public $do_verp = false; + + /** + * Whether to allow sending messages with an empty body. + * + * @var bool + */ + public $AllowEmpty = false; + + /** + * DKIM selector. + * + * @var string + */ + public $DKIM_selector = ''; + + /** + * DKIM Identity. + * Usually the email address used as the source of the email. + * + * @var string + */ + public $DKIM_identity = ''; + + /** + * DKIM passphrase. + * Used if your key is encrypted. + * + * @var string + */ + public $DKIM_passphrase = ''; + + /** + * DKIM signing domain name. + * + * @example 'example.com' + * + * @var string + */ + public $DKIM_domain = ''; + + /** + * DKIM Copy header field values for diagnostic use. + * + * @var bool + */ + public $DKIM_copyHeaderFields = true; + + /** + * DKIM Extra signing headers. + * + * @example ['List-Unsubscribe', 'List-Help'] + * + * @var array + */ + public $DKIM_extraHeaders = []; + + /** + * DKIM private key file path. + * + * @var string + */ + public $DKIM_private = ''; + + /** + * DKIM private key string. + * + * If set, takes precedence over `$DKIM_private`. + * + * @var string + */ + public $DKIM_private_string = ''; + + /** + * Callback Action function name. + * + * The function that handles the result of the send email action. + * It is called out by send() for each email sent. + * + * Value can be any php callable: https://www.php.net/is_callable + * + * Parameters: + * bool $result result of the send action + * array $to email addresses of the recipients + * array $cc cc email addresses + * array $bcc bcc email addresses + * string $subject the subject + * string $body the email body + * string $from email address of sender + * string $extra extra information of possible use + * 'smtp_transaction_id' => last smtp transaction id + * + * @var callable|callable-string + */ + public $action_function = ''; + + /** + * What to put in the X-Mailer header. + * Options: An empty string for PHPMailer default, whitespace/null for none, or a string to use. + * + * @var string|null + */ + public $XMailer = ''; + + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option. + * + * If CharSet is UTF8, the validator is left at the default value, + * and you send to addresses that use non-ASCII local parts, then + * PHPMailer automatically changes to the 'eai' validator. + * + * @see PHPMailer::validateAddress() + * + * @var string|callable + */ + public static $validator = 'php'; + + /** + * An instance of the SMTP sender class. + * + * @var SMTP + */ + protected $smtp; + + /** + * The array of 'to' names and addresses. + * + * @var array + */ + protected $to = []; + + /** + * The array of 'cc' names and addresses. + * + * @var array + */ + protected $cc = []; + + /** + * The array of 'bcc' names and addresses. + * + * @var array + */ + protected $bcc = []; + + /** + * The array of reply-to names and addresses. + * + * @var array + */ + protected $ReplyTo = []; + + /** + * An array of all kinds of addresses. + * Includes all of $to, $cc, $bcc. + * + * @see PHPMailer::$to + * @see PHPMailer::$cc + * @see PHPMailer::$bcc + * + * @var array + */ + protected $all_recipients = []; + + /** + * An array of names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $all_recipients + * and one of $to, $cc, or $bcc. + * This array is used only for addresses with IDN. + * + * @see PHPMailer::$to + * @see PHPMailer::$cc + * @see PHPMailer::$bcc + * @see PHPMailer::$all_recipients + * + * @var array + */ + protected $RecipientsQueue = []; + + /** + * An array of reply-to names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $ReplyTo. + * This array is used only for addresses with IDN. + * + * @see PHPMailer::$ReplyTo + * + * @var array + */ + protected $ReplyToQueue = []; + + /** + * Whether the need for SMTPUTF8 has been detected. Set by + * preSend() if necessary. + * + * @var bool + */ + public $UseSMTPUTF8 = false; + + /** + * The array of attachments. + * + * @var array + */ + protected $attachment = []; + + /** + * The array of custom headers. + * + * @var array + */ + protected $CustomHeader = []; + + /** + * The most recent Message-ID (including angular brackets). + * + * @var string + */ + protected $lastMessageID = ''; + + /** + * The message's MIME type. + * + * @var string + */ + protected $message_type = ''; + + /** + * The array of MIME boundary strings. + * + * @var array + */ + protected $boundary = []; + + /** + * The array of available text strings for the current language. + * + * @var array + */ + protected static $language = []; + + /** + * The number of errors encountered. + * + * @var int + */ + protected $error_count = 0; + + /** + * The S/MIME certificate file path. + * + * @var string + */ + protected $sign_cert_file = ''; + + /** + * The S/MIME key file path. + * + * @var string + */ + protected $sign_key_file = ''; + + /** + * The optional S/MIME extra certificates ("CA Chain") file path. + * + * @var string + */ + protected $sign_extracerts_file = ''; + + /** + * The S/MIME password for the key. + * Used only if the key is encrypted. + * + * @var string + */ + protected $sign_key_pass = ''; + + /** + * Whether to throw exceptions for errors. + * + * @var bool + */ + protected $exceptions = false; + + /** + * Unique ID used for message ID and boundaries. + * + * @var string + */ + protected $uniqueid = ''; + + /** + * The PHPMailer Version number. + * + * @var string + */ + const VERSION = '7.0.2'; + + /** + * Error severity: message only, continue processing. + * + * @var int + */ + const STOP_MESSAGE = 0; + + /** + * Error severity: message, likely ok to continue processing. + * + * @var int + */ + const STOP_CONTINUE = 1; + + /** + * Error severity: message, plus full stop, critical error reached. + * + * @var int + */ + const STOP_CRITICAL = 2; + + /** + * The SMTP standard CRLF line break. + * If you want to change line break format, change static::$LE, not this. + */ + const CRLF = "\r\n"; + + /** + * "Folding White Space" a white space string used for line folding. + */ + const FWS = ' '; + + /** + * SMTP RFC standard line ending; Carriage Return, Line Feed. + * + * @var string + */ + protected static $LE = self::CRLF; + + /** + * The maximum line length supported by mail(). + * + * Background: mail() will sometimes corrupt messages + * with headers longer than 65 chars, see #818. + * + * @var int + */ + const MAIL_MAX_LINE_LENGTH = 63; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1. + * + * @var int + */ + const MAX_LINE_LENGTH = 998; + + /** + * The lower maximum line length allowed by RFC 2822 section 2.1.1. + * This length does NOT include the line break + * 76 means that lines will be 77 or 78 chars depending on whether + * the line break format is LF or CRLF; both are valid. + * + * @var int + */ + const STD_LINE_LENGTH = 76; + + /** + * Constructor. + * + * @param bool $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = null) + { + if (null !== $exceptions) { + $this->exceptions = (bool) $exceptions; + } + //Pick an appropriate debug output format automatically + $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); + } + + /** + * Destructor. + */ + public function __destruct() + { + //Close any open SMTP connection nicely + $this->smtpClose(); + } + + /** + * Call mail() in a safe_mode-aware fashion. + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do). + * + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string|null $params Params + * + * @return bool + */ + private function mailPassthru($to, $subject, $body, $header, $params) + { + //Check overloading of mail function to avoid double-encoding + // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecatedRemoved + if ((int)ini_get('mbstring.func_overload') & 1) { + $subject = $this->secureHeader($subject); + } else { + $subject = $this->encodeHeader($this->secureHeader($subject)); + } + //Calling mail() with null params breaks + $this->edebug('Sending with mail()'); + $this->edebug('Sendmail path: ' . ini_get('sendmail_path')); + $this->edebug("Envelope sender: {$this->Sender}"); + $this->edebug("To: {$to}"); + $this->edebug("Subject: {$subject}"); + $this->edebug("Headers: {$header}"); + if (!$this->UseSendmailOptions || null === $params) { + $result = @mail($to, $subject, $body, $header); + } else { + $this->edebug("Additional params: {$params}"); + $result = @mail($to, $subject, $body, $header, $params); + } + $this->edebug('Result: ' . ($result ? 'true' : 'false')); + return $result; + } + + /** + * Output debugging info via a user-defined method. + * Only generates output if debug output is enabled. + * + * @see PHPMailer::$Debugoutput + * @see PHPMailer::$SMTPDebug + * + * @param string $str + */ + protected function edebug($str) + { + if ($this->SMTPDebug <= 0) { + return; + } + //Is this a PSR-3 logger? + if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { + $this->Debugoutput->debug(rtrim($str, "\r\n")); + + return; + } + //Avoid clash with built-in function names + if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) { + call_user_func($this->Debugoutput, $str, $this->SMTPDebug); + + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + /** @noinspection ForgottenDebugOutputInspection */ + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ), "
\n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/\r\n|\r/m', "\n", $str); + echo gmdate('Y-m-d H:i:s'), + "\t", + //Trim trailing space + trim( + //Indent for readability, except for trailing break + str_replace( + "\n", + "\n \t ", + trim($str) + ) + ), + "\n"; + } + } + + /** + * Sets message type to HTML or plain. + * + * @param bool $isHtml True for HTML mode + */ + public function isHTML($isHtml = true) + { + if ($isHtml) { + $this->ContentType = static::CONTENT_TYPE_TEXT_HTML; + } else { + $this->ContentType = static::CONTENT_TYPE_PLAINTEXT; + } + } + + /** + * Send messages using SMTP. + */ + public function isSMTP() + { + $this->Mailer = 'smtp'; + } + + /** + * Send messages using PHP's mail() function. + */ + public function isMail() + { + $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. + */ + public function isSendmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (false === stripos($ini_sendmail_path, 'sendmail')) { + $ini_sendmail_path = '/usr/sbin/sendmail'; + } + $this->Sendmail = $this->parseSendmailPath($ini_sendmail_path); + $this->Mailer = 'sendmail'; + } + + /** + * Send messages using qmail. + */ + public function isQmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (false === stripos($ini_sendmail_path, 'qmail')) { + $ini_sendmail_path = '/var/qmail/bin/qmail-inject'; + } + $this->Sendmail = $this->parseSendmailPath($ini_sendmail_path); + $this->Mailer = 'qmail'; + } + + /** + * Add a "To" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addAddress($address, $name = '') + { + return $this->addOrEnqueueAnAddress('to', $address, $name); + } + + /** + * Add a "CC" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('cc', $address, $name); + } + + /** + * Add a "BCC" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addBCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('bcc', $address, $name); + } + + /** + * Add a "Reply-To" address. + * + * @param string $address The email address to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addReplyTo($address, $name = '') + { + return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer + * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still + * be modified after calling this function), addition of such addresses is delayed until send(). + * Addresses that have been added already return false, but do not throw exceptions. + * + * @param string $kind One of 'to', 'cc', 'bcc', or 'Reply-To' + * @param string $address The email address + * @param string $name An optional username associated with the address + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + protected function addOrEnqueueAnAddress($kind, $address, $name) + { + $pos = false; + if ($address !== null) { + $address = trim($address); + $pos = strrpos($address, '@'); + } + if (false === $pos) { + //At-sign is missing. + $error_message = sprintf( + '%s (%s): %s', + self::lang('invalid_address'), + $kind, + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if ($name !== null && is_string($name)) { + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + } else { + $name = ''; + } + $params = [$kind, $address, $name]; + //Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + //Domain is assumed to be whatever is after the last @ symbol in the address + if ($this->has8bitChars(substr($address, ++$pos))) { + if (static::idnSupported()) { + if ('Reply-To' !== $kind) { + if (!array_key_exists($address, $this->RecipientsQueue)) { + $this->RecipientsQueue[$address] = $params; + + return true; + } + } elseif (!array_key_exists($address, $this->ReplyToQueue)) { + $this->ReplyToQueue[$address] = $params; + + return true; + } + } + //We have an 8-bit domain, but we are missing the necessary extensions to support it + //Or we are already sending to this address + return false; + } + + //Immediately add standard addresses without IDN. + return call_user_func_array([$this, 'addAnAddress'], $params); + } + + /** + * Set the boundaries to use for delimiting MIME parts. + * If you override this, ensure you set all 3 boundaries to unique values. + * The default boundaries include a "=_" sequence which cannot occur in quoted-printable bodies, + * as suggested by https://www.rfc-editor.org/rfc/rfc2045#section-6.7 + * + * @return void + */ + public function setBoundaries() + { + $this->uniqueid = $this->generateId(); + $this->boundary[1] = 'b1=_' . $this->uniqueid; + $this->boundary[2] = 'b2=_' . $this->uniqueid; + $this->boundary[3] = 'b3=_' . $this->uniqueid; + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. + * Addresses that have been added already return false, but do not throw exceptions. + * + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + protected function addAnAddress($kind, $address, $name = '') + { + if ( + self::$validator === 'php' && + ((bool) preg_match('/[\x80-\xFF]/', $address)) + ) { + //The caller has not altered the validator and is sending to an address + //with UTF-8, so assume that they want UTF-8 support instead of failing + $this->CharSet = self::CHARSET_UTF8; + self::$validator = 'eai'; + } + if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) { + $error_message = sprintf( + '%s: %s', + self::lang('Invalid recipient kind'), + $kind + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if (!static::validateAddress($address)) { + $error_message = sprintf( + '%s (%s): %s', + self::lang('invalid_address'), + $kind, + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if ('Reply-To' !== $kind) { + if (!array_key_exists(strtolower($address), $this->all_recipients)) { + $this->{$kind}[] = [$address, $name]; + $this->all_recipients[strtolower($address)] = true; + + return true; + } + } else { + foreach ($this->ReplyTo as $replyTo) { + if (0 === strcasecmp($replyTo[0], $address)) { + return false; + } + } + $this->ReplyTo[] = [$address, $name]; + + return true; + } + return false; + } + + /** + * Parse and validate a string containing one or more RFC822-style comma-separated email addresses + * of the form "display name
" into an array of name/address pairs. + * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. + * Note that quotes in the name part are removed. + * + * @see https://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation + * + * @param string $addrstr The address list string + * @param null $useimap Unused. Argument has been deprecated in PHPMailer 6.11.0. + * Previously this argument determined whether to use + * the IMAP extension to parse the list and accepted a boolean value. + * @param string $charset The charset to use when decoding the address list string. + * + * @return array + */ + public static function parseAddresses($addrstr, $useimap = null, $charset = self::CHARSET_ISO88591) + { + if ($useimap !== null) { + trigger_error(self::lang('deprecated_argument') . '$useimap', E_USER_DEPRECATED); + } + $addresses = []; + if (function_exists('imap_rfc822_parse_adrlist')) { + //Use this built-in parser if it's available + // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.imap_rfc822_parse_adrlistRemoved -- wrapped in function_exists() + $list = imap_rfc822_parse_adrlist($addrstr, ''); + // Clear any potential IMAP errors to get rid of notices being thrown at end of script. + // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.imap_errorsRemoved -- wrapped in function_exists() + imap_errors(); + foreach ($list as $address) { + if ( + '.SYNTAX-ERROR.' !== $address->host && + static::validateAddress($address->mailbox . '@' . $address->host) + ) { + //Decode the name part if it's present and maybe encoded + if ( + property_exists($address, 'personal') + && is_string($address->personal) + && $address->personal !== '' + ) { + $address->personal = static::decodeHeader($address->personal, $charset); + } + + $addresses[] = [ + 'name' => (property_exists($address, 'personal') ? $address->personal : ''), + 'address' => $address->mailbox . '@' . $address->host, + ]; + } + } + } else { + //Use this simpler parser + $addresses = static::parseSimplerAddresses($addrstr, $charset); + } + + return $addresses; + } + + /** + * Parse a string containing one or more RFC822-style comma-separated email addresses + * with the form "display name
" into an array of name/address pairs. + * Uses a simpler parser that does not require the IMAP extension but doesnt support + * the full RFC822 spec. For full RFC822 support, use the PHP IMAP extension. + * + * @param string $addrstr The address list string + * @param string $charset The charset to use when decoding the address list string. + * + * @return array + */ + protected static function parseSimplerAddresses($addrstr, $charset) + { + // Emit a runtime notice to recommend using the IMAP extension for full RFC822 parsing + trigger_error(self::lang('imap_recommended'), E_USER_NOTICE); + + $addresses = []; + $list = explode(',', $addrstr); + foreach ($list as $address) { + $address = trim($address); + //Is there a separate name part? + if (strpos($address, '<') === false) { + //No separate name, just use the whole thing + if (static::validateAddress($address)) { + $addresses[] = [ + 'name' => '', + 'address' => $address, + ]; + } + } else { + $parsed = static::parseEmailString($address); + $email = $parsed['email']; + if (static::validateAddress($email)) { + $name = static::decodeHeader($parsed['name'], $charset); + $addresses[] = [ + //Remove any surrounding quotes and spaces from the name + 'name' => trim($name, '\'" '), + 'address' => $email, + ]; + } + } + } + + return $addresses; + } + + /** + * Parse a string containing an email address with an optional name + * and divide it into a name and email address. + * + * @param string $input The email with name. + * + * @return array{name: string, email: string} + */ + private static function parseEmailString($input) + { + $input = trim((string)$input); + + if ($input === '') { + return ['name' => '', 'email' => '']; + } + + $pattern = '/^\s*(?:(?:"([^"]*)"|\'([^\']*)\'|([^<]*?))\s*)?<\s*([^>]+)\s*>\s*$/'; + if (preg_match($pattern, $input, $matches)) { + $name = ''; + // Double quotes including special scenarios. + if (isset($matches[1]) && $matches[1] !== '') { + $name = $matches[1]; + // Single quotes including special scenarios. + } elseif (isset($matches[2]) && $matches[2] !== '') { + $name = $matches[2]; + // Simplest scenario, name and email are in the format "Name ". + } elseif (isset($matches[3])) { + $name = trim($matches[3]); + } + + return ['name' => $name, 'email' => trim($matches[4])]; + } + + return ['name' => '', 'email' => $input]; + } + + /** + * Set the From and FromName properties. + * + * @param string $address + * @param string $name + * @param bool $auto Whether to also set the Sender address, defaults to true + * + * @throws Exception + * + * @return bool + */ + public function setFrom($address, $name = '', $auto = true) + { + if (is_null($name)) { + //Helps avoid a deprecation warning in the preg_replace() below + $name = ''; + } + $address = trim((string)$address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + //Don't validate now addresses with IDN. Will be done in send(). + $pos = strrpos($address, '@'); + if ( + (false === $pos) + || ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported()) + && !static::validateAddress($address)) + ) { + $error_message = sprintf( + '%s (From): %s', + self::lang('invalid_address'), + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + $this->From = $address; + $this->FromName = $name; + if ($auto && empty($this->Sender)) { + $this->Sender = $address; + } + + return true; + } + + /** + * Return the Message-ID header of the last email. + * Technically this is the value from the last time the headers were created, + * but it's also the message ID of the last sent message except in + * pathological cases. + * + * @return string + */ + public function getLastMessageID() + { + return $this->lastMessageID; + } + + /** + * Check that a string looks like an email address. + * Validation patterns supported: + * * `auto` Pick best pattern automatically; + * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0; + * * `pcre` Use old PCRE implementation; + * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; + * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. + * * `eai` Use a pattern similar to the HTML5 spec for 'email' and to firefox, extended to support EAI (RFC6530). + * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * + * ```php + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * ``` + * + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. + * + * @param string $address The email address to check + * @param string|callable $patternselect Which pattern to use + * + * @return bool + */ + public static function validateAddress($address, $patternselect = null) + { + if (null === $patternselect) { + $patternselect = static::$validator; + } + //Don't allow strings as callables, see SECURITY.md and CVE-2021-3603 + if (is_callable($patternselect) && !is_string($patternselect)) { + return call_user_func($patternselect, $address); + } + //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 + if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) { + return false; + } + switch ($patternselect) { + case 'pcre': //Kept for BC + case 'pcre8': + /* + * A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL + * is based. + * In addition to the addresses allowed by filter_var, also permits: + * * dotless domains: `a@b` + * * comments: `1234 @ local(blah) .machine .example` + * * quoted elements: `'"test blah"@example.org'` + * * numeric TLDs: `a@b.123` + * * unbracketed IPv4 literals: `a@192.168.0.1` + * * IPv6 literals: 'first.last@[IPv6:a1::]' + * Not all of these will necessarily work for sending! + * + * @copyright 2009-2010 Michael Rushton + * Feel free to use and redistribute this code. But please keep this copyright notice. + */ + return (bool) preg_match( + '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . + '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . + '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . + '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . + '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . + '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . + '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . + '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', + $address + ); + case 'html5': + /* + * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. + * + * @see https://html.spec.whatwg.org/#e-mail-state-(type=email) + */ + return (bool) preg_match( + '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . + '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', + $address + ); + case 'eai': + /* + * This is the pattern used in the HTML5 spec for validation of 'email' type + * form input elements (as above), modified to accept Unicode email addresses. + * This is also more lenient than Firefox' html5 spec, in order to make the regex faster. + * 'eai' is an acronym for Email Address Internationalization. + * This validator is selected automatically if you attempt to use recipient addresses + * that contain Unicode characters in the local part. + * + * @see https://html.spec.whatwg.org/#e-mail-state-(type=email) + * @see https://en.wikipedia.org/wiki/International_email + */ + return (bool) preg_match( + '/^[-\p{L}\p{N}\p{M}.!#$%&\'*+\/=?^_`{|}~]+@[\p{L}\p{N}\p{M}](?:[\p{L}\p{N}\p{M}-]{0,61}' . + '[\p{L}\p{N}\p{M}])?(?:\.[\p{L}\p{N}\p{M}]' . + '(?:[-\p{L}\p{N}\p{M}]{0,61}[\p{L}\p{N}\p{M}])?)*$/usD', + $address + ); + case 'php': + default: + return filter_var($address, FILTER_VALIDATE_EMAIL) !== false; + } + } + + /** + * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the + * `intl` and `mbstring` PHP extensions. + * + * @return bool `true` if required functions for IDN support are present + */ + public static function idnSupported() + { + return function_exists('idn_to_ascii') && function_exists('mb_convert_encoding'); + } + + /** + * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. + * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. + * This function silently returns unmodified address if: + * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) + * - Conversion to punycode is impossible (e.g. required PHP functions are not available) + * or fails for any reason (e.g. domain contains characters not allowed in an IDN). + * + * @see PHPMailer::$CharSet + * + * @param string $address The email address to convert + * + * @return string The encoded address in ASCII form + */ + public function punyencodeAddress($address) + { + //Verify we have required functions, CharSet, and at-sign. + $pos = strrpos($address, '@'); + if ( + !empty($this->CharSet) && + false !== $pos && + static::idnSupported() + ) { + $domain = substr($address, ++$pos); + //Verify CharSet string is a valid one, and domain properly encoded in this CharSet. + if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) { + //Convert the domain from whatever charset it's in to UTF-8 + $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet); + //Ignore IDE complaints about this line - method signature changed in PHP 5.4 + $errorcode = 0; + if (defined('INTL_IDNA_VARIANT_UTS46')) { + //Use the current punycode standard (appeared in PHP 7.2) + $punycode = idn_to_ascii( + $domain, + \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI | + \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII, + \INTL_IDNA_VARIANT_UTS46 + ); + } elseif (defined('INTL_IDNA_VARIANT_2003')) { + //Fall back to this old, deprecated/removed encoding + // phpcs:ignore PHPCompatibility.Constants.RemovedConstants.intl_idna_variant_2003DeprecatedRemoved + $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003); + } else { + //Fall back to a default we don't know about + // phpcs:ignore PHPCompatibility.ParameterValues.NewIDNVariantDefault.NotSet + $punycode = idn_to_ascii($domain, $errorcode); + } + if (false !== $punycode) { + return substr($address, 0, $pos) . $punycode; + } + } + } + + return $address; + } + + /** + * Create a message and send it. + * Uses the sending method specified by $Mailer. + * + * @throws Exception + * + * @return bool false on error - See the ErrorInfo property for details of the error + */ + public function send() + { + try { + if (!$this->preSend()) { + return false; + } + + return $this->postSend(); + } catch (Exception $exc) { + $this->mailHeader = ''; + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + } + + /** + * Prepare a message for sending. + * + * @throws Exception + * + * @return bool + */ + public function preSend() + { + if ( + 'smtp' === $this->Mailer + || ('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); + } else { + //Maintain backward compatibility with legacy Linux command line mailers + static::setLE(PHP_EOL); + } + //Check for buggy PHP versions that add a header with an incorrect line break + if ( + 'mail' === $this->Mailer + && ((\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(self::lang('buggy_php'), E_USER_WARNING); + } + + try { + $this->error_count = 0; //Reset errors + $this->mailHeader = ''; + + //The code below tries to support full use of Unicode, + //while remaining compatible with legacy SMTP servers to + //the greatest degree possible: If the message uses + //Unicode in the local parts of any addresses, it is sent + //using SMTPUTF8. If not, it it sent using + //punycode-encoded domains and plain SMTP. + if ( + static::CHARSET_UTF8 === strtolower($this->CharSet) && + ($this->anyAddressHasUnicodeLocalPart($this->RecipientsQueue) || + $this->anyAddressHasUnicodeLocalPart(array_keys($this->all_recipients)) || + $this->anyAddressHasUnicodeLocalPart($this->ReplyToQueue) || + $this->addressHasUnicodeLocalPart($this->From)) + ) { + $this->UseSMTPUTF8 = true; + } + //Dequeue recipient and Reply-To addresses with IDN + foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { + if (!$this->UseSMTPUTF8) { + $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(self::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} = ''; + continue; + } + $this->{$address_kind} = trim($this->{$address_kind}); + if (empty($this->{$address_kind})) { + continue; + } + $this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind}); + if (!static::validateAddress($this->{$address_kind})) { + $error_message = sprintf( + '%s (%s): %s', + self::lang('invalid_address'), + $address_kind, + $this->{$address_kind} + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + } + + //Set whether the message is multipart/alternative + if ($this->alternativeExists()) { + $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE; + } + + $this->setMessageType(); + //Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty && empty($this->Body)) { + throw new Exception(self::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->MIMEHeader = ''; + $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); + } else { + $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + $this->mailHeader .= $this->headerLine( + 'Subject', + $this->encodeHeader($this->secureHeader($this->Subject)) + ); + } + + //Sign with DKIM if enabled + if ( + !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->MIMEBody + ); + $this->MIMEHeader = static::stripTrailingWSP($this->MIMEHeader) . static::$LE . + static::normalizeBreaks($header_dkim) . static::$LE; + } + + return true; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + } + + /** + * Actually send a message via the selected mechanism. + * + * @throws Exception + * + * @return bool + */ + public function postSend() + { + try { + //Choose the mailer and send through it + switch ($this->Mailer) { + case 'sendmail': + case 'qmail': + return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + default: + $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()) { + $this->smtp->reset(); + } + if ($this->exceptions) { + throw $exc; + } + } + + return false; + } + + /** + * Send mail using the $Sendmail program. + * + * @see PHPMailer::$Sendmail + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function sendmailSend($header, $body) + { + if ($this->Mailer === 'qmail') { + $this->edebug('Sending with qmail'); + } else { + $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: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: https://www.sendmail.org/~ca/email/man/sendmail.html + //Example problem: https://www.drupal.org/node/1057954 + + //PHP 5.6 workaround + $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'); + } + + $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)) { + $sendmailArgs[] = '-f' . $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); + $this->edebug("Headers: {$header}"); + + if ($this->SingleTo) { + foreach ($this->SingleToArray as $toAddr) { + $mail = @popen($sendmail, 'w'); + if (!$mail) { + throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + $this->edebug("To: {$toAddr}"); + fwrite($mail, 'To: ' . $toAddr . "\n"); + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $addrinfo = static::parseAddresses($toAddr, null, $this->CharSet); + foreach ($addrinfo as $addr) { + $this->doCallback( + ($result === 0), + [[$addr['address'], $addr['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + } + $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); + if (0 !== $result) { + throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + $mail = @popen($sendmail, 'w'); + if (!$mail) { + throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result === 0), + $this->to, + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); + if (0 !== $result) { + throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + + return true; + } + + /** + * 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 + * + * @return bool + */ + 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, + //so we don't. + if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) { + return false; + } + + if ( + escapeshellcmd($string) !== $string + || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""]) + ) { + return false; + } + + $length = strlen($string); + + for ($i = 0; $i < $length; ++$i) { + $c = $string[$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) { + return false; + } + } + + return true; + } + + /** + * Check whether a file path is of a permitted type. + * Used to reject URLs and phar files from functions that access local file paths, + * such as addAttachment. + * + * @param string $path A relative or absolute path to a file + * + * @return bool + */ + protected static function isPermittedPath($path) + { + //Matches scheme definition from https://www.rfc-editor.org/rfc/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 + * + * @return bool + */ + protected static function fileIsAccessible($path) + { + if (!static::isPermittedPath($path)) { + return false; + } + $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); + } + return $readable; + } + + /** + * Send mail using the PHP mail() function. + * + * @see https://www.php.net/manual/en/book.mail.php + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function mailSend($header, $body) + { + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + + $toArr = []; + 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() + if ($to === '') { + $to = 'undisclosed-recipients:;'; + } + + $params = null; + //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: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: https://www.sendmail.org/~ca/email/man/sendmail.html + //Example problem: https://www.drupal.org/node/1057954 + //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + + //PHP 5.6 workaround + $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)) { + $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'); + ini_set('sendmail_from', $this->Sender); + } + $result = false; + if ($this->SingleTo && count($toArr) > 1) { + foreach ($toArr as $toAddr) { + $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); + $addrinfo = static::parseAddresses($toAddr, null, $this->CharSet); + foreach ($addrinfo as $addr) { + $this->doCallback( + $result, + [[$addr['address'], $addr['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + } + } + } else { + $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); + $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); + } + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if (!$result) { + throw new Exception(self::lang('instantiate'), self::STOP_CRITICAL); + } + + return true; + } + + /** + * Get an instance to use for SMTP operations. + * Override this function to load your own SMTP implementation, + * or set one with setSMTPInstance. + * + * @return SMTP + */ + public function getSMTPInstance() + { + if (!is_object($this->smtp)) { + $this->smtp = new SMTP(); + } + + return $this->smtp; + } + + /** + * Provide an instance to use for SMTP operations. + * + * @return SMTP + */ + public function setSMTPInstance(SMTP $smtp) + { + $this->smtp = $smtp; + + return $this->smtp; + } + + /** + * Provide SMTP XCLIENT attributes + * + * @param string $name Attribute name + * @param ?string $value Attribute value + * + * @return bool + */ + public function setSMTPXclientAttribute($name, $value) + { + if (!in_array($name, SMTP::$xclient_allowed_attributes)) { + return false; + } + if (isset($this->SMTPXClient[$name]) && $value === null) { + unset($this->SMTPXClient[$name]); + } elseif ($value !== null) { + $this->SMTPXClient[$name] = $value; + } + + return true; + } + + /** + * Get SMTP XCLIENT attributes + * + * @return array + */ + public function getSMTPXclientAttributes() + { + return $this->SMTPXClient; + } + + /** + * Send mail via SMTP. + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * + * @see PHPMailer::setSMTPInstance() to use a different class. + * + * @uses \PHPMailer\PHPMailer\SMTP + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function smtpSend($header, $body) + { + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + $bad_rcpt = []; + if (!$this->smtpConnect($this->SMTPOptions)) { + throw new Exception(self::lang('smtp_connect_failed'), self::STOP_CRITICAL); + } + //If we have recipient addresses that need Unicode support, + //but the server doesn't support it, stop here + if ($this->UseSMTPUTF8 && !$this->smtp->getServerExt('SMTPUTF8')) { + throw new Exception(self::lang('no_smtputf8'), self::STOP_CRITICAL); + } + //Sender already validated in preSend() + if ('' === $this->Sender) { + $smtp_from = $this->From; + } else { + $smtp_from = $this->Sender; + } + if (count($this->SMTPXClient)) { + $this->smtp->xclient($this->SMTPXClient); + } + if (!$this->smtp->mail($smtp_from)) { + $this->setError(self::lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); + throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); + } + + $callbacks = []; + //Attempt to send to all recipients + foreach ([$this->to, $this->cc, $this->bcc] as $togroup) { + foreach ($togroup as $to) { + if (!$this->smtp->recipient($to[0], $this->dsn)) { + $error = $this->smtp->getError(); + $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']]; + $isSent = false; + } else { + $isSent = true; + } + + $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]]; + } + } + + //Only send the DATA command if we have viable recipients + if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) { + throw new Exception(self::lang('data_not_accepted'), self::STOP_CRITICAL); + } + + $smtp_transaction_id = $this->smtp->getLastTransactionID(); + + if ($this->SMTPKeepAlive) { + $this->smtp->reset(); + } else { + $this->smtp->quit(); + $this->smtp->close(); + } + + foreach ($callbacks as $cb) { + $this->doCallback( + $cb['issent'], + [[$cb['to'], $cb['name']]], + [], + [], + $this->Subject, + $body, + $this->From, + ['smtp_transaction_id' => $smtp_transaction_id] + ); + } + + //Create error message for any bad addresses + if (count($bad_rcpt) > 0) { + $errstr = ''; + foreach ($bad_rcpt as $bad) { + $errstr .= $bad['to'] . ': ' . $bad['error']; + } + throw new Exception(self::lang('recipients_failed') . $errstr, self::STOP_CONTINUE); + } + + return true; + } + + /** + * Initiate a connection to an SMTP server. + * Returns false if the operation failed. + * + * @param array $options An array of options compatible with stream_context_create() + * + * @throws Exception + * + * @uses \PHPMailer\PHPMailer\SMTP + * + * @return bool + */ + public function smtpConnect($options = null) + { + if (null === $this->smtp) { + $this->smtp = $this->getSMTPInstance(); + } + + //If no options are provided, use whatever is set in the instance + if (null === $options) { + $options = $this->SMTPOptions; + } + + //Already connected? + if ($this->smtp->connected()) { + return true; + } + + $this->smtp->setTimeout($this->Timeout); + $this->smtp->setDebugLevel($this->SMTPDebug); + $this->smtp->setDebugOutput($this->Debugoutput); + $this->smtp->setVerp($this->do_verp); + $this->smtp->setSMTPUTF8($this->UseSMTPUTF8); + if ($this->Host === null) { + $this->Host = 'localhost'; + } + $hosts = explode(';', $this->Host); + $lastexception = null; + + foreach ($hosts as $hostentry) { + $hostinfo = []; + if ( + !preg_match( + '/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/', + trim($hostentry), + $hostinfo + ) + ) { + $this->edebug(self::lang('invalid_hostentry') . ' ' . trim($hostentry)); + //Not a valid host entry + continue; + } + //$hostinfo[1]: optional ssl or tls prefix + //$hostinfo[2]: the hostname + //$hostinfo[3]: optional port number + //The host string prefix can temporarily override the current setting for SMTPSecure + //If it's not specified, the default value is used + + //Check the host name is a valid name or IP address before trying to use it + if (!static::isValidHost($hostinfo[2])) { + $this->edebug(self::lang('invalid_host') . ' ' . $hostinfo[2]); + continue; + } + $prefix = ''; + $secure = $this->SMTPSecure; + $tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure); + if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) { + $prefix = 'ssl://'; + $tls = false; //Can't have SSL and TLS at the same time + $secure = static::ENCRYPTION_SMTPS; + } elseif ('tls' === $hostinfo[1]) { + $tls = true; + //TLS doesn't use a prefix + $secure = static::ENCRYPTION_STARTTLS; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA256'); + if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new Exception(self::lang('extension_missing') . 'openssl', self::STOP_CRITICAL); + } + } + $host = $hostinfo[2]; + $port = $this->Port; + if ( + array_key_exists(3, $hostinfo) && + is_numeric($hostinfo[3]) && + $hostinfo[3] > 0 && + $hostinfo[3] < 65536 + ) { + $port = (int) $hostinfo[3]; + } + if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); + } + $this->smtp->hello($hello); + //Automatically enable TLS encryption if: + //* it's not disabled + //* we are not connecting to localhost + //* we have openssl extension + //* we are not already using SSL + //* the server offers STARTTLS + if ( + $this->SMTPAutoTLS && + $this->Host !== 'localhost' && + $sslext && + $secure !== 'ssl' && + $this->smtp->getServerExt('STARTTLS') + ) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + $message = $this->getSmtpErrorMessage('connect_host'); + throw new Exception($message); + } + //We must resend EHLO after TLS negotiation + $this->smtp->hello($hello); + } + if ( + $this->SMTPAuth && !$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->oauth + ) + ) { + throw new Exception(self::lang('authenticate')); + } + + return true; + } catch (Exception $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + //We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); + } + } + } + //If we get here, all connection attempts have failed, so close connection hard + $this->smtp->close(); + //As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions && null !== $lastexception) { + throw $lastexception; + } + if ($this->exceptions) { + // no exception was thrown, likely $this->smtp->connect() failed + $message = $this->getSmtpErrorMessage('connect_host'); + throw new Exception($message); + } + + return false; + } + + /** + * Close the active SMTP session if one exists. + */ + public function smtpClose() + { + if ((null !== $this->smtp) && $this->smtp->connected()) { + $this->smtp->quit(); + $this->smtp->close(); + } + } + + /** + * Set the language for error messages. + * The default language is English. + * + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * Optionally, the language code can be enhanced with a 4-character + * script annotation and/or a 2-character country annotation. + * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * Do not set this from user input! + * + * @return bool Returns true if the requested language was loaded, false otherwise. + */ + public static function setLanguage($langcode = 'en', $lang_path = '') + { + //Backwards compatibility for renamed language codes + $renamed_langcodes = [ + 'br' => 'pt_br', + 'cz' => 'cs', + 'dk' => 'da', + 'no' => 'nb', + 'se' => 'sv', + 'rs' => 'sr', + 'tg' => 'tl', + 'am' => 'hy', + ]; + + if (array_key_exists($langcode, $renamed_langcodes)) { + $langcode = $renamed_langcodes[$langcode]; + } + + //Define full set of translatable strings in English + $PHPMAILER_LANG = [ + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' . + ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . + ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'extension_missing' => 'Extension missing: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address: ', + 'invalid_header' => 'Invalid header name or value', + 'invalid_hostentry' => 'Invalid hostentry: ', + 'invalid_host' => 'Invalid host: ', + 'mailer_not_supported' => ' mailer is not supported.', + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_code' => 'SMTP code: ', + 'smtp_code_ex' => 'Additional SMTP info: ', + 'smtp_connect_failed' => 'SMTP connect() failed.', + 'smtp_detail' => 'Detail: ', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ', + 'no_smtputf8' => 'Server does not support SMTPUTF8 needed to send to Unicode addresses', + 'imap_recommended' => 'Using simplified address parser is not recommended. ' . + 'Install the PHP IMAP extension for full RFC822 parsing.', + 'deprecated_argument' => 'Deprecated Argument: ', + ]; + if (empty($lang_path)) { + //Calculate an absolute path so it can work if CWD is not here + $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR; + } + + //Validate $langcode + $foundlang = true; + $langcode = strtolower($langcode); + if ( + !preg_match('/^(?P[a-z]{2})(?P