Ticket #41750: 41750.diff
| File 41750.diff, 243.6 KB (added by , 6 years ago) |
|---|
-
src/wp-includes/class-phpmailer.php
1 1 <?php 2 2 /** 3 3 * PHPMailer - PHP email creation and transport class. 4 * PHP Version 5 5 * @package PHPMailer 6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project 7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 10 * @author Brent R. Matzelle (original founder) 11 * @copyright 2012 - 2014 Marcus Bointon 4 * PHP Version 5.5. 5 * 6 * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project 7 * 8 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 9 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 10 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 11 * @author Brent R. Matzelle (original founder) 12 * @copyright 2012 - 2017 Marcus Bointon 12 13 * @copyright 2010 - 2012 Jim Jagielski 13 14 * @copyright 2004 - 2009 Andy Prevost 14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License15 * @note This program is distributed in the hope that it will be useful - WITHOUT15 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 16 * @note This program is distributed in the hope that it will be useful - WITHOUT 16 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 18 * FITNESS FOR A PARTICULAR PURPOSE. 18 19 */ 19 20 21 namespace PHPMailer\PHPMailer; 22 20 23 /** 21 24 * PHPMailer - PHP email creation and transport class. 22 * @package PHPMailer23 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>24 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>25 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>26 * @author Brent R. Matzelle (original founder)25 * 26 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 27 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 28 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 29 * @author Brent R. Matzelle (original founder) 27 30 */ 28 31 class PHPMailer 29 32 { 30 /** 31 * The PHPMailer Version number. 32 * @var string 33 */ 34 public $Version = '5.2.27'; 33 const CHARSET_ISO88591 = 'iso-8859-1'; 34 const CHARSET_UTF8 = 'utf-8'; 35 35 36 const CONTENT_TYPE_PLAINTEXT = 'text/plain'; 37 const CONTENT_TYPE_TEXT_CALENDAR = 'text/calendar'; 38 const CONTENT_TYPE_TEXT_HTML = 'text/html'; 39 const CONTENT_TYPE_MULTIPART_ALTERNATIVE = 'multipart/alternative'; 40 const CONTENT_TYPE_MULTIPART_MIXED = 'multipart/mixed'; 41 const CONTENT_TYPE_MULTIPART_RELATED = 'multipart/related'; 42 43 const ENCODING_7BIT = '7bit'; 44 const ENCODING_8BIT = '8bit'; 45 const ENCODING_BASE64 = 'base64'; 46 const ENCODING_BINARY = 'binary'; 47 const ENCODING_QUOTED_PRINTABLE = 'quoted-printable'; 48 36 49 /** 37 50 * Email priority. 38 51 * Options: null (default), 1 = High, 3 = Normal, 5 = low. 39 52 * When null, the header is not set at all. 40 * @var integer 53 * 54 * @var int 41 55 */ 42 public $Priority = null;56 public $Priority; 43 57 44 58 /** 45 59 * The character set of the message. 60 * 46 61 * @var string 47 62 */ 48 public $CharSet = 'iso-8859-1';63 public $CharSet = self::CHARSET_ISO88591; 49 64 50 65 /** 51 66 * The MIME Content-type of the message. 67 * 52 68 * @var string 53 69 */ 54 public $ContentType = 'text/plain';70 public $ContentType = self::CONTENT_TYPE_PLAINTEXT; 55 71 56 72 /** 57 73 * The message encoding. 58 74 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". 75 * 59 76 * @var string 60 77 */ 61 public $Encoding = '8bit';78 public $Encoding = self::ENCODING_8BIT; 62 79 63 80 /** 64 81 * Holds the most recent mailer error message. 82 * 65 83 * @var string 66 84 */ 67 85 public $ErrorInfo = ''; … … 68 86 69 87 /** 70 88 * The From email address for the message. 89 * 71 90 * @var string 72 91 */ 73 92 public $From = 'root@localhost'; … … 74 93 75 94 /** 76 95 * The From name of the message. 96 * 77 97 * @var string 78 98 */ 79 99 public $FromName = 'Root User'; 80 100 81 101 /** 82 * The Sender email (Return-Path) of the message. 83 * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. 102 * The envelope sender of the message. 103 * This will usually be turned into a Return-Path header by the receiver, 104 * and is the address that bounces will be sent to. 105 * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP. 106 * 84 107 * @var string 85 108 */ 86 109 public $Sender = ''; 87 110 88 111 /** 89 * The Return-Path of the message.90 * If empty, it will be set to either From or Sender.91 * @var string92 * @deprecated Email senders should never set a return-path header;93 * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.94 * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference95 */96 public $ReturnPath = '';97 98 /**99 112 * The Subject of the message. 113 * 100 114 * @var string 101 115 */ 102 116 public $Subject = ''; … … 104 118 /** 105 119 * An HTML or plain text message body. 106 120 * If HTML then call isHTML(true). 121 * 107 122 * @var string 108 123 */ 109 124 public $Body = ''; … … 113 128 * This body can be read by mail clients that do not have HTML email 114 129 * capability such as mutt & Eudora. 115 130 * Clients that can read HTML will view the normal Body. 131 * 116 132 * @var string 117 133 */ 118 134 public $AltBody = ''; … … 120 136 /** 121 137 * An iCal message part body. 122 138 * Only supported in simple alt or alt_inline message types 123 * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator 124 * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ 125 * @link http://kigkonsult.se/iCalcreator/ 139 * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator. 140 * 141 * @see http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ 142 * @see http://kigkonsult.se/iCalcreator/ 143 * 126 144 * @var string 127 145 */ 128 146 public $Ical = ''; … … 129 147 130 148 /** 131 149 * The complete compiled MIME message body. 132 * @access protected150 * 133 151 * @var string 134 152 */ 135 153 protected $MIMEBody = ''; … … 136 154 137 155 /** 138 156 * The complete compiled MIME message headers. 157 * 139 158 * @var string 140 * @access protected141 159 */ 142 160 protected $MIMEHeader = ''; 143 161 144 162 /** 145 163 * Extra headers that createHeader() doesn't fold in. 164 * 146 165 * @var string 147 * @access protected148 166 */ 149 167 protected $mailHeader = ''; 150 168 … … 151 169 /** 152 170 * Word-wrap the message body to this number of chars. 153 171 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. 154 * @var integer 172 * 173 * @see static::STD_LINE_LENGTH 174 * 175 * @var int 155 176 */ 156 177 public $WordWrap = 0; 157 178 … … 158 179 /** 159 180 * Which method to use to send mail. 160 181 * Options: "mail", "sendmail", or "smtp". 182 * 161 183 * @var string 162 184 */ 163 185 public $Mailer = 'mail'; … … 164 186 165 187 /** 166 188 * The path to the sendmail program. 189 * 167 190 * @var string 168 191 */ 169 192 public $Sendmail = '/usr/sbin/sendmail'; … … 171 194 /** 172 195 * Whether mail() uses a fully sendmail-compatible MTA. 173 196 * One which supports sendmail's "-oi -f" options. 174 * @var boolean 197 * 198 * @var bool 175 199 */ 176 200 public $UseSendmailOptions = true; 177 201 178 202 /** 179 * Path to PHPMailer plugins.180 * Useful if the SMTP class is not in the PHP include path.181 * @var string182 * @deprecated Should not be needed now there is an autoloader.183 */184 public $PluginDir = '';185 186 /**187 203 * The email address that a reading confirmation should be sent to, also known as read receipt. 204 * 188 205 * @var string 189 206 */ 190 207 public $ConfirmReadingTo = ''; … … 194 211 * If empty, PHPMailer attempts to find one with, in order, 195 212 * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value 196 213 * 'localhost.localdomain'. 214 * 197 215 * @var string 198 216 */ 199 217 public $Hostname = ''; … … 203 221 * If empty, a unique id will be generated. 204 222 * You can set your own, but it must be in the format "<id@domain>", 205 223 * as defined in RFC5322 section 3.6.4 or it will be ignored. 224 * 206 225 * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 226 * 207 227 * @var string 208 228 */ 209 229 public $MessageID = ''; … … 211 231 /** 212 232 * The message Date to be used in the Date header. 213 233 * If empty, the current date will be added. 234 * 214 235 * @var string 215 236 */ 216 237 public $MessageDate = ''; … … 224 245 * You can also specify encryption type, for example: 225 246 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). 226 247 * Hosts will be tried in order. 248 * 227 249 * @var string 228 250 */ 229 251 public $Host = 'localhost'; … … 230 252 231 253 /** 232 254 * The default SMTP server port. 233 * @var integer234 * @ TODO Why is this needed when the SMTP class takes care of it?255 * 256 * @var int 235 257 */ 236 258 public $Port = 25; 237 259 … … 239 261 * The SMTP HELO of the message. 240 262 * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find 241 263 * one with the same method described above for $Hostname. 264 * 265 * @see PHPMailer::$Hostname 266 * 242 267 * @var string 243 * @see PHPMailer::$Hostname244 268 */ 245 269 public $Helo = ''; 246 270 247 271 /** 248 272 * What kind of encryption to use on the SMTP connection. 249 * Options: '', 'ssl' or 'tls' 273 * Options: '', 'ssl' or 'tls'. 274 * 250 275 * @var string 251 276 */ 252 277 public $SMTPSecure = ''; … … 255 280 * Whether to enable TLS encryption automatically if a server supports it, 256 281 * even if `SMTPSecure` is not set to 'tls'. 257 282 * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. 258 * @var boolean 283 * 284 * @var bool 259 285 */ 260 286 public $SMTPAutoTLS = true; 261 287 … … 262 288 /** 263 289 * Whether to use SMTP authentication. 264 290 * Uses the Username and Password properties. 265 * @var boolean291 * 266 292 * @see PHPMailer::$Username 267 293 * @see PHPMailer::$Password 294 * 295 * @var bool 268 296 */ 269 297 public $SMTPAuth = false; 270 298 271 299 /** 272 300 * Options array passed to stream_context_create when connecting via SMTP. 301 * 273 302 * @var array 274 303 */ 275 public $SMTPOptions = array();304 public $SMTPOptions = []; 276 305 277 306 /** 278 307 * SMTP username. 308 * 279 309 * @var string 280 310 */ 281 311 public $Username = ''; … … 282 312 283 313 /** 284 314 * SMTP password. 315 * 285 316 * @var string 286 317 */ 287 318 public $Password = ''; … … 288 319 289 320 /** 290 321 * SMTP auth type. 291 * Options are CRAM-MD5, LOGIN, PLAIN, attempted in that order if not specified 322 * Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2, attempted in that order if not specified. 323 * 292 324 * @var string 293 325 */ 294 326 public $AuthType = ''; 295 327 296 328 /** 297 * SMTP realm.298 * Used for NTLM auth299 * @var string329 * An instance of the PHPMailer OAuth class. 330 * 331 * @var OAuth 300 332 */ 301 p ublic $Realm = '';333 protected $oauth; 302 334 303 335 /** 304 * SMTP workstation.305 * Used for NTLM auth306 * @var string307 */308 public $Workstation = '';309 310 /**311 336 * The SMTP server timeout in seconds. 312 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 313 * @var integer 337 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. 338 * 339 * @var int 314 340 */ 315 341 public $Timeout = 300; 316 342 … … 322 348 * * `1` Commands 323 349 * * `2` Data and commands 324 350 * * `3` As 2 plus connection status 325 * * `4` Low-level data output 326 * @var integer351 * * `4` Low-level data output. 352 * 327 353 * @see SMTP::$do_debug 354 * 355 * @var int 328 356 */ 329 357 public $SMTPDebug = 0; 330 358 … … 334 362 * * `echo` Output plain-text as-is, appropriate for CLI 335 363 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output 336 364 * * `error_log` Output to error log as configured in php.ini 365 * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise. 366 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 337 367 * 338 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 339 * <code> 368 * ```php 340 369 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; 341 * </code> 342 * @var string|callable 370 * ``` 371 * 372 * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug` 373 * level output is used: 374 * 375 * ```php 376 * $mail->Debugoutput = new myPsr3Logger; 377 * ``` 378 * 343 379 * @see SMTP::$Debugoutput 380 * 381 * @var string|callable|\Psr\Log\LoggerInterface 344 382 */ 345 383 public $Debugoutput = 'echo'; 346 384 … … 348 386 * Whether to keep SMTP connection open after each message. 349 387 * If this is set to true then to close the connection 350 388 * requires an explicit call to smtpClose(). 351 * @var boolean 389 * 390 * @var bool 352 391 */ 353 392 public $SMTPKeepAlive = false; 354 393 … … 356 395 * Whether to split multiple to addresses into multiple messages 357 396 * or send them all in one message. 358 397 * Only supported in `mail` and `sendmail` transports, not in SMTP. 359 * @var boolean 398 * 399 * @var bool 360 400 */ 361 401 public $SingleTo = false; 362 402 363 403 /** 364 404 * Storage for addresses when SingleTo is enabled. 405 * 365 406 * @var array 366 * @TODO This should really not be public367 407 */ 368 p ublic $SingleToArray = array();408 protected $SingleToArray = []; 369 409 370 410 /** 371 411 * Whether to generate VERP addresses on send. 372 412 * Only applicable when sending via SMTP. 373 * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path 374 * @link http://www.postfix.org/VERP_README.html Postfix VERP info 375 * @var boolean 413 * 414 * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path 415 * @see http://www.postfix.org/VERP_README.html Postfix VERP info 416 * 417 * @var bool 376 418 */ 377 419 public $do_verp = false; 378 420 379 421 /** 380 422 * Whether to allow sending messages with an empty body. 381 * @var boolean 423 * 424 * @var bool 382 425 */ 383 426 public $AllowEmpty = false; 384 427 385 428 /** 386 * The default line ending.387 * @note The default remains "\n". We force CRLF where we know388 * it must be used via self::CRLF.389 * @var string390 */391 public $LE = "\n";392 393 /**394 429 * DKIM selector. 430 * 395 431 * @var string 396 432 */ 397 433 public $DKIM_selector = ''; … … 399 435 /** 400 436 * DKIM Identity. 401 437 * Usually the email address used as the source of the email. 438 * 402 439 * @var string 403 440 */ 404 441 public $DKIM_identity = ''; … … 406 443 /** 407 444 * DKIM passphrase. 408 445 * Used if your key is encrypted. 446 * 409 447 * @var string 410 448 */ 411 449 public $DKIM_passphrase = ''; … … 412 450 413 451 /** 414 452 * DKIM signing domain name. 453 * 415 454 * @example 'example.com' 455 * 416 456 * @var string 417 457 */ 418 458 public $DKIM_domain = ''; 419 459 420 460 /** 461 * DKIM Copy header field values for diagnostic use. 462 * 463 * @var bool 464 */ 465 public $DKIM_copyHeaderFields = true; 466 467 /** 468 * DKIM Extra signing headers. 469 * 470 * @example ['List-Unsubscribe', 'List-Help'] 471 * 472 * @var array 473 */ 474 public $DKIM_extraHeaders = []; 475 476 /** 421 477 * DKIM private key file path. 478 * 422 479 * @var string 423 480 */ 424 481 public $DKIM_private = ''; … … 425 482 426 483 /** 427 484 * DKIM private key string. 485 * 428 486 * If set, takes precedence over `$DKIM_private`. 487 * 429 488 * @var string 430 489 */ 431 490 public $DKIM_private_string = ''; … … 439 498 * Value can be any php callable: http://www.php.net/is_callable 440 499 * 441 500 * Parameters: 442 * bool ean$result result of the send action501 * bool $result result of the send action 443 502 * array $to email addresses of the recipients 444 503 * array $cc cc email addresses 445 504 * array $bcc bcc email addresses … … 446 505 * string $subject the subject 447 506 * string $body the email body 448 507 * string $from email address of sender 508 * string $extra extra information of possible use 509 * "smtp_transaction_id' => last smtp transaction id 510 * 449 511 * @var string 450 512 */ 451 513 public $action_function = ''; … … 452 514 453 515 /** 454 516 * What to put in the X-Mailer header. 455 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use 517 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use. 518 * 456 519 * @var string 457 520 */ 458 521 public $XMailer = ''; … … 460 523 /** 461 524 * Which validator to use by default when validating email addresses. 462 525 * May be a callable to inject your own validator, but there are several built-in validators. 526 * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option. 527 * 463 528 * @see PHPMailer::validateAddress() 529 * 464 530 * @var string|callable 465 * @static466 531 */ 467 public static $validator = ' auto';532 public static $validator = 'php'; 468 533 469 534 /** 470 535 * An instance of the SMTP sender class. 536 * 471 537 * @var SMTP 472 * @access protected473 538 */ 474 protected $smtp = null;539 protected $smtp; 475 540 476 541 /** 477 542 * The array of 'to' names and addresses. 543 * 478 544 * @var array 479 * @access protected480 545 */ 481 protected $to = array();546 protected $to = []; 482 547 483 548 /** 484 549 * The array of 'cc' names and addresses. 550 * 485 551 * @var array 486 * @access protected487 552 */ 488 protected $cc = array();553 protected $cc = []; 489 554 490 555 /** 491 556 * The array of 'bcc' names and addresses. 557 * 492 558 * @var array 493 * @access protected494 559 */ 495 protected $bcc = array();560 protected $bcc = []; 496 561 497 562 /** 498 563 * The array of reply-to names and addresses. 564 * 499 565 * @var array 500 * @access protected501 566 */ 502 protected $ReplyTo = array();567 protected $ReplyTo = []; 503 568 504 569 /** 505 570 * An array of all kinds of addresses. 506 * Includes all of $to, $cc, $bcc 571 * Includes all of $to, $cc, $bcc. 572 * 573 * @see PHPMailer::$to 574 * @see PHPMailer::$cc 575 * @see PHPMailer::$bcc 576 * 507 577 * @var array 508 * @access protected509 * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc510 578 */ 511 protected $all_recipients = array();579 protected $all_recipients = []; 512 580 513 581 /** 514 582 * An array of names and addresses queued for validation. … … 515 583 * In send(), valid and non duplicate entries are moved to $all_recipients 516 584 * and one of $to, $cc, or $bcc. 517 585 * This array is used only for addresses with IDN. 586 * 587 * @see PHPMailer::$to 588 * @see PHPMailer::$cc 589 * @see PHPMailer::$bcc 590 * @see PHPMailer::$all_recipients 591 * 518 592 * @var array 519 * @access protected520 * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc521 * @see PHPMailer::$all_recipients522 593 */ 523 protected $RecipientsQueue = array();594 protected $RecipientsQueue = []; 524 595 525 596 /** 526 597 * An array of reply-to names and addresses queued for validation. 527 598 * In send(), valid and non duplicate entries are moved to $ReplyTo. 528 599 * This array is used only for addresses with IDN. 600 * 601 * @see PHPMailer::$ReplyTo 602 * 529 603 * @var array 530 * @access protected531 * @see PHPMailer::$ReplyTo532 604 */ 533 protected $ReplyToQueue = array();605 protected $ReplyToQueue = []; 534 606 535 607 /** 536 608 * The array of attachments. 609 * 537 610 * @var array 538 * @access protected539 611 */ 540 protected $attachment = array();612 protected $attachment = []; 541 613 542 614 /** 543 615 * The array of custom headers. 616 * 544 617 * @var array 545 * @access protected546 618 */ 547 protected $CustomHeader = array();619 protected $CustomHeader = []; 548 620 549 621 /** 550 622 * The most recent Message-ID (including angular brackets). 623 * 551 624 * @var string 552 * @access protected553 625 */ 554 626 protected $lastMessageID = ''; 555 627 556 628 /** 557 629 * The message's MIME type. 630 * 558 631 * @var string 559 * @access protected560 632 */ 561 633 protected $message_type = ''; 562 634 563 635 /** 564 636 * The array of MIME boundary strings. 637 * 565 638 * @var array 566 * @access protected567 639 */ 568 protected $boundary = array();640 protected $boundary = []; 569 641 570 642 /** 571 643 * The array of available languages. 644 * 572 645 * @var array 573 * @access protected574 646 */ 575 protected $language = array();647 protected $language = []; 576 648 577 649 /** 578 650 * The number of errors encountered. 579 * @var integer580 * @ access protected651 * 652 * @var int 581 653 */ 582 654 protected $error_count = 0; 583 655 584 656 /** 585 657 * The S/MIME certificate file path. 658 * 586 659 * @var string 587 * @access protected588 660 */ 589 661 protected $sign_cert_file = ''; 590 662 591 663 /** 592 664 * The S/MIME key file path. 665 * 593 666 * @var string 594 * @access protected595 667 */ 596 668 protected $sign_key_file = ''; 597 669 598 670 /** 599 671 * The optional S/MIME extra certificates ("CA Chain") file path. 672 * 600 673 * @var string 601 * @access protected602 674 */ 603 675 protected $sign_extracerts_file = ''; 604 676 … … 605 677 /** 606 678 * The S/MIME password for the key. 607 679 * Used only if the key is encrypted. 680 * 608 681 * @var string 609 * @access protected610 682 */ 611 683 protected $sign_key_pass = ''; 612 684 613 685 /** 614 686 * Whether to throw exceptions for errors. 615 * @var boolean616 * @ access protected687 * 688 * @var bool 617 689 */ 618 690 protected $exceptions = false; 619 691 620 692 /** 621 693 * Unique ID used for message ID and boundaries. 694 * 622 695 * @var string 623 * @access protected624 696 */ 625 697 protected $uniqueid = ''; 626 698 627 699 /** 700 * The PHPMailer Version number. 701 * 702 * @var string 703 */ 704 const VERSION = '6.0.7'; 705 706 /** 628 707 * Error severity: message only, continue processing. 708 * 709 * @var int 629 710 */ 630 711 const STOP_MESSAGE = 0; 631 712 632 713 /** 633 714 * Error severity: message, likely ok to continue processing. 715 * 716 * @var int 634 717 */ 635 718 const STOP_CONTINUE = 1; 636 719 637 720 /** 638 721 * Error severity: message, plus full stop, critical error reached. 722 * 723 * @var int 639 724 */ 640 725 const STOP_CRITICAL = 2; 641 726 642 727 /** 643 728 * SMTP RFC standard line ending. 729 * 730 * @var string 644 731 */ 645 const CRLF= "\r\n";732 protected static $LE = "\r\n"; 646 733 647 734 /** 648 * The maximum line length allowed by RFC 2822 section 2.1.1 649 * @var integer 735 * The maximum line length allowed by RFC 2822 section 2.1.1. 736 * 737 * @var int 650 738 */ 651 739 const MAX_LINE_LENGTH = 998; 652 740 653 741 /** 742 * The lower maximum line length allowed by RFC 2822 section 2.1.1. 743 * This length does NOT include the line break 744 * 76 means that lines will be 77 or 78 chars depending on whether 745 * the line break format is LF or CRLF; both are valid. 746 * 747 * @var int 748 */ 749 const STD_LINE_LENGTH = 76; 750 751 /** 654 752 * Constructor. 655 * @param boolean $exceptions Should we throw external exceptions? 753 * 754 * @param bool $exceptions Should we throw external exceptions? 656 755 */ 657 756 public function __construct($exceptions = null) 658 757 { 659 if ( $exceptions !== null) {660 $this->exceptions = (bool ean)$exceptions;758 if (null !== $exceptions) { 759 $this->exceptions = (bool) $exceptions; 661 760 } 662 761 //Pick an appropriate debug output format automatically 663 762 $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); … … 676 775 * Call mail() in a safe_mode-aware fashion. 677 776 * Also, unless sendmail_path points to sendmail (or something that 678 777 * claims to be sendmail), don't pass params (not a perfect fix, 679 * but it will do) 680 * @param string $to To 681 * @param string $subject Subject 682 * @param string $body Message Body 683 * @param string $header Additional Header(s) 684 * @param string $params Params 685 * @access private 686 * @return boolean 778 * but it will do). 779 * 780 * @param string $to To 781 * @param string $subject Subject 782 * @param string $body Message Body 783 * @param string $header Additional Header(s) 784 * @param string|null $params Params 785 * 786 * @return bool 687 787 */ 688 788 private function mailPassthru($to, $subject, $body, $header, $params) 689 789 { … … 693 793 } else { 694 794 $subject = $this->encodeHeader($this->secureHeader($subject)); 695 795 } 696 697 //Can't use additional_parameters in safe_mode, calling mail() with null params breaks 698 //@link http://php.net/manual/en/function.mail.php 699 if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { 796 //Calling mail() with null params breaks 797 if (!$this->UseSendmailOptions or null === $params) { 700 798 $result = @mail($to, $subject, $body, $header); 701 799 } else { 702 800 $result = @mail($to, $subject, $body, $header, $params); 703 801 } 802 704 803 return $result; 705 804 } 805 706 806 /** 707 807 * Output debugging info via user-defined method. 708 808 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). 809 * 709 810 * @see PHPMailer::$Debugoutput 710 811 * @see PHPMailer::$SMTPDebug 812 * 711 813 * @param string $str 712 814 */ 713 815 protected function edebug($str) … … 715 817 if ($this->SMTPDebug <= 0) { 716 818 return; 717 819 } 820 //Is this a PSR-3 logger? 821 if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { 822 $this->Debugoutput->debug($str); 823 824 return; 825 } 718 826 //Avoid clash with built-in function names 719 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {827 if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) { 720 828 call_user_func($this->Debugoutput, $str, $this->SMTPDebug); 829 721 830 return; 722 831 } 723 832 switch ($this->Debugoutput) { … … 731 840 preg_replace('/[\r\n]+/', '', $str), 732 841 ENT_QUOTES, 733 842 'UTF-8' 734 ) 735 . "<br>\n"; 843 ), "<br>\n"; 736 844 break; 737 845 case 'echo': 738 846 default: 739 847 //Normalize line breaks 740 $str = preg_replace('/\r\n?/ms', "\n", $str); 741 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 742 "\n", 743 "\n \t ", 744 trim($str) 745 ) . "\n"; 848 $str = preg_replace('/\r\n|\r/ms', "\n", $str); 849 echo gmdate('Y-m-d H:i:s'), 850 "\t", 851 //Trim trailing space 852 trim( 853 //Indent for readability, except for trailing break 854 str_replace( 855 "\n", 856 "\n \t ", 857 trim($str) 858 ) 859 ), 860 "\n"; 746 861 } 747 862 } 748 863 749 864 /** 750 865 * Sets message type to HTML or plain. 751 * @param boolean $isHtml True for HTML mode.752 * @ return void866 * 867 * @param bool $isHtml True for HTML mode 753 868 */ 754 869 public function isHTML($isHtml = true) 755 870 { 756 871 if ($isHtml) { 757 $this->ContentType = 'text/html';872 $this->ContentType = static::CONTENT_TYPE_TEXT_HTML; 758 873 } else { 759 $this->ContentType = 'text/plain';874 $this->ContentType = static::CONTENT_TYPE_PLAINTEXT; 760 875 } 761 876 } 762 877 763 878 /** 764 879 * Send messages using SMTP. 765 * @return void766 880 */ 767 881 public function isSMTP() 768 882 { … … 771 885 772 886 /** 773 887 * Send messages using PHP's mail() function. 774 * @return void775 888 */ 776 889 public function isMail() 777 890 { … … 780 893 781 894 /** 782 895 * Send messages using $Sendmail. 783 * @return void784 896 */ 785 897 public function isSendmail() 786 898 { 787 899 $ini_sendmail_path = ini_get('sendmail_path'); 788 900 789 if ( !stristr($ini_sendmail_path, 'sendmail')) {901 if (false === stripos($ini_sendmail_path, 'sendmail')) { 790 902 $this->Sendmail = '/usr/sbin/sendmail'; 791 903 } else { 792 904 $this->Sendmail = $ini_sendmail_path; … … 796 908 797 909 /** 798 910 * Send messages using qmail. 799 * @return void800 911 */ 801 912 public function isQmail() 802 913 { 803 914 $ini_sendmail_path = ini_get('sendmail_path'); 804 915 805 if ( !stristr($ini_sendmail_path, 'qmail')) {916 if (false === stripos($ini_sendmail_path, 'qmail')) { 806 917 $this->Sendmail = '/var/qmail/bin/qmail-inject'; 807 918 } else { 808 919 $this->Sendmail = $ini_sendmail_path; … … 812 923 813 924 /** 814 925 * Add a "To" address. 926 * 815 927 * @param string $address The email address to send to 816 928 * @param string $name 817 * @return boolean true on success, false if address already used or invalid in some way 929 * 930 * @return bool true on success, false if address already used or invalid in some way 818 931 */ 819 932 public function addAddress($address, $name = '') 820 933 { … … 823 936 824 937 /** 825 938 * Add a "CC" address. 826 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.939 * 827 940 * @param string $address The email address to send to 828 941 * @param string $name 829 * @return boolean true on success, false if address already used or invalid in some way 942 * 943 * @return bool true on success, false if address already used or invalid in some way 830 944 */ 831 945 public function addCC($address, $name = '') 832 946 { … … 835 949 836 950 /** 837 951 * Add a "BCC" address. 838 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.952 * 839 953 * @param string $address The email address to send to 840 954 * @param string $name 841 * @return boolean true on success, false if address already used or invalid in some way 955 * 956 * @return bool true on success, false if address already used or invalid in some way 842 957 */ 843 958 public function addBCC($address, $name = '') 844 959 { … … 847 962 848 963 /** 849 964 * Add a "Reply-To" address. 965 * 850 966 * @param string $address The email address to reply to 851 967 * @param string $name 852 * @return boolean true on success, false if address already used or invalid in some way 968 * 969 * @return bool true on success, false if address already used or invalid in some way 853 970 */ 854 971 public function addReplyTo($address, $name = '') 855 972 { … … 861 978 * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still 862 979 * be modified after calling this function), addition of such addresses is delayed until send(). 863 980 * Addresses that have been added already return false, but do not throw exceptions. 864 * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' 981 * 982 * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' 865 983 * @param string $address The email address to send, resp. to reply to 866 984 * @param string $name 867 * @throws phpmailerException 868 * @return boolean true on success, false if address already used or invalid in some way 869 * @access protected 985 * 986 * @throws Exception 987 * 988 * @return bool true on success, false if address already used or invalid in some way 870 989 */ 871 990 protected function addOrEnqueueAnAddress($kind, $address, $name) 872 991 { 873 992 $address = trim($address); 874 993 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 875 if (($pos = strrpos($address, '@')) === false) { 876 // At-sign is misssing. 877 $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; 994 $pos = strrpos($address, '@'); 995 if (false === $pos) { 996 // At-sign is missing. 997 $error_message = sprintf('%s (%s): %s', 998 $this->lang('invalid_address'), 999 $kind, 1000 $address); 878 1001 $this->setError($error_message); 879 1002 $this->edebug($error_message); 880 1003 if ($this->exceptions) { 881 throw new phpmailerException($error_message);1004 throw new Exception($error_message); 882 1005 } 1006 883 1007 return false; 884 1008 } 885 $params = array($kind, $address, $name);1009 $params = [$kind, $address, $name]; 886 1010 // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. 887 if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {888 if ( $kind != 'Reply-To') {1011 if ($this->has8bitChars(substr($address, ++$pos)) and static::idnSupported()) { 1012 if ('Reply-To' != $kind) { 889 1013 if (!array_key_exists($address, $this->RecipientsQueue)) { 890 1014 $this->RecipientsQueue[$address] = $params; 1015 891 1016 return true; 892 1017 } 893 1018 } else { 894 1019 if (!array_key_exists($address, $this->ReplyToQueue)) { 895 1020 $this->ReplyToQueue[$address] = $params; 1021 896 1022 return true; 897 1023 } 898 1024 } 1025 899 1026 return false; 900 1027 } 1028 901 1029 // Immediately add standard addresses without IDN. 902 return call_user_func_array( array($this, 'addAnAddress'), $params);1030 return call_user_func_array([$this, 'addAnAddress'], $params); 903 1031 } 904 1032 905 1033 /** 906 1034 * Add an address to one of the recipient arrays or to the ReplyTo array. 907 1035 * Addresses that have been added already return false, but do not throw exceptions. 908 * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' 1036 * 1037 * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' 909 1038 * @param string $address The email address to send, resp. to reply to 910 1039 * @param string $name 911 * @throws phpmailerException 912 * @return boolean true on success, false if address already used or invalid in some way 913 * @access protected 1040 * 1041 * @throws Exception 1042 * 1043 * @return bool true on success, false if address already used or invalid in some way 914 1044 */ 915 1045 protected function addAnAddress($kind, $address, $name = '') 916 1046 { 917 if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { 918 $error_message = $this->lang('Invalid recipient kind: ') . $kind; 1047 if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) { 1048 $error_message = sprintf('%s: %s', 1049 $this->lang('Invalid recipient kind'), 1050 $kind); 919 1051 $this->setError($error_message); 920 1052 $this->edebug($error_message); 921 1053 if ($this->exceptions) { 922 throw new phpmailerException($error_message);1054 throw new Exception($error_message); 923 1055 } 1056 924 1057 return false; 925 1058 } 926 if (!$this->validateAddress($address)) { 927 $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; 1059 if (!static::validateAddress($address)) { 1060 $error_message = sprintf('%s (%s): %s', 1061 $this->lang('invalid_address'), 1062 $kind, 1063 $address); 928 1064 $this->setError($error_message); 929 1065 $this->edebug($error_message); 930 1066 if ($this->exceptions) { 931 throw new phpmailerException($error_message);1067 throw new Exception($error_message); 932 1068 } 1069 933 1070 return false; 934 1071 } 935 if ( $kind != 'Reply-To') {1072 if ('Reply-To' != $kind) { 936 1073 if (!array_key_exists(strtolower($address), $this->all_recipients)) { 937 array_push($this->$kind, array($address, $name));1074 $this->{$kind}[] = [$address, $name]; 938 1075 $this->all_recipients[strtolower($address)] = true; 1076 939 1077 return true; 940 1078 } 941 1079 } else { 942 1080 if (!array_key_exists(strtolower($address), $this->ReplyTo)) { 943 $this->ReplyTo[strtolower($address)] = array($address, $name); 1081 $this->ReplyTo[strtolower($address)] = [$address, $name]; 1082 944 1083 return true; 945 1084 } 946 1085 } 1086 947 1087 return false; 948 1088 } 949 1089 … … 952 1092 * of the form "display name <address>" into an array of name/address pairs. 953 1093 * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. 954 1094 * Note that quotes in the name part are removed. 1095 * 1096 * @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation 1097 * 955 1098 * @param string $addrstr The address list string 956 * @param bool $useimap Whether to use the IMAP extension to parse the list 1099 * @param bool $useimap Whether to use the IMAP extension to parse the list 1100 * 957 1101 * @return array 958 * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation959 1102 */ 960 public function parseAddresses($addrstr, $useimap = true)1103 public static function parseAddresses($addrstr, $useimap = true) 961 1104 { 962 $addresses = array();1105 $addresses = []; 963 1106 if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { 964 1107 //Use this built-in parser if it's available 965 1108 $list = imap_rfc822_parse_adrlist($addrstr, ''); 966 1109 foreach ($list as $address) { 967 if ( $address->host != '.SYNTAX-ERROR.') {968 if ( $this->validateAddress($address->mailbox . '@' . $address->host)) {969 $addresses[] = array(1110 if ('.SYNTAX-ERROR.' != $address->host) { 1111 if (static::validateAddress($address->mailbox . '@' . $address->host)) { 1112 $addresses[] = [ 970 1113 'name' => (property_exists($address, 'personal') ? $address->personal : ''), 971 'address' => $address->mailbox . '@' . $address->host 972 );1114 'address' => $address->mailbox . '@' . $address->host, 1115 ]; 973 1116 } 974 1117 } 975 1118 } … … 981 1124 //Is there a separate name part? 982 1125 if (strpos($address, '<') === false) { 983 1126 //No separate name, just use the whole thing 984 if ( $this->validateAddress($address)) {985 $addresses[] = array(1127 if (static::validateAddress($address)) { 1128 $addresses[] = [ 986 1129 'name' => '', 987 'address' => $address 988 );1130 'address' => $address, 1131 ]; 989 1132 } 990 1133 } else { 991 1134 list($name, $email) = explode('<', $address); 992 1135 $email = trim(str_replace('>', '', $email)); 993 if ( $this->validateAddress($email)) {994 $addresses[] = array(995 'name' => trim(str_replace( array('"', "'"), '', $name)),996 'address' => $email 997 );1136 if (static::validateAddress($email)) { 1137 $addresses[] = [ 1138 'name' => trim(str_replace(['"', "'"], '', $name)), 1139 'address' => $email, 1140 ]; 998 1141 } 999 1142 } 1000 1143 } 1001 1144 } 1145 1002 1146 return $addresses; 1003 1147 } 1004 1148 1005 1149 /** 1006 1150 * Set the From and FromName properties. 1151 * 1007 1152 * @param string $address 1008 1153 * @param string $name 1009 * @param boolean $auto Whether to also set the Sender address, defaults to true 1010 * @throws phpmailerException 1011 * @return boolean 1154 * @param bool $auto Whether to also set the Sender address, defaults to true 1155 * 1156 * @throws Exception 1157 * 1158 * @return bool 1012 1159 */ 1013 1160 public function setFrom($address, $name = '', $auto = true) 1014 1161 { … … 1015 1162 $address = trim($address); 1016 1163 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 1017 1164 // Don't validate now addresses with IDN. Will be done in send(). 1018 if (($pos = strrpos($address, '@')) === false or 1019 (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and 1020 !$this->validateAddress($address)) { 1021 $error_message = $this->lang('invalid_address') . " (setFrom) $address"; 1165 $pos = strrpos($address, '@'); 1166 if (false === $pos or 1167 (!$this->has8bitChars(substr($address, ++$pos)) or !static::idnSupported()) and 1168 !static::validateAddress($address)) { 1169 $error_message = sprintf('%s (From): %s', 1170 $this->lang('invalid_address'), 1171 $address); 1022 1172 $this->setError($error_message); 1023 1173 $this->edebug($error_message); 1024 1174 if ($this->exceptions) { 1025 throw new phpmailerException($error_message);1175 throw new Exception($error_message); 1026 1176 } 1177 1027 1178 return false; 1028 1179 } 1029 1180 $this->From = $address; … … 1033 1184 $this->Sender = $address; 1034 1185 } 1035 1186 } 1187 1036 1188 return true; 1037 1189 } 1038 1190 … … 1041 1193 * Technically this is the value from the last time the headers were created, 1042 1194 * but it's also the message ID of the last sent message except in 1043 1195 * pathological cases. 1196 * 1044 1197 * @return string 1045 1198 */ 1046 1199 public function getLastMessageID() … … 1050 1203 1051 1204 /** 1052 1205 * Check that a string looks like an email address. 1053 * @param string $address The email address to check 1054 * @param string|callable $patternselect A selector for the validation pattern to use : 1206 * Validation patterns supported: 1055 1207 * * `auto` Pick best pattern automatically; 1056 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0 , PHP >= 5.3.2, 5.2.14;1208 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0; 1057 1209 * * `pcre` Use old PCRE implementation; 1058 1210 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; 1059 1211 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. 1060 1212 * * `noregex` Don't use a regex: super fast, really dumb. 1061 1213 * Alternatively you may pass in a callable to inject your own validator, for example: 1214 * 1215 * ```php 1062 1216 * PHPMailer::validateAddress('user@example.com', function($address) { 1063 1217 * return (strpos($address, '@') !== false); 1064 1218 * }); 1219 * ``` 1220 * 1065 1221 * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. 1066 * @return boolean 1067 * @static 1068 * @access public 1222 * 1223 * @param string $address The email address to check 1224 * @param string|callable $patternselect Which pattern to use 1225 * 1226 * @return bool 1069 1227 */ 1070 1228 public static function validateAddress($address, $patternselect = null) 1071 1229 { 1072 if ( is_null($patternselect)) {1073 $patternselect = s elf::$validator;1230 if (null === $patternselect) { 1231 $patternselect = static::$validator; 1074 1232 } 1075 1233 if (is_callable($patternselect)) { 1076 1234 return call_user_func($patternselect, $address); … … 1079 1237 if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { 1080 1238 return false; 1081 1239 } 1082 if (!$patternselect or $patternselect == 'auto') {1083 //Check this constant first so it works when extension_loaded() is disabled by safe mode1084 //Constant was added in PHP 5.2.41085 if (defined('PCRE_VERSION')) {1086 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.21087 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {1088 $patternselect = 'pcre8';1089 } else {1090 $patternselect = 'pcre';1091 }1092 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {1093 //Fall back to older PCRE1094 $patternselect = 'pcre';1095 } else {1096 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension1097 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {1098 $patternselect = 'php';1099 } else {1100 $patternselect = 'noregex';1101 }1102 }1103 }1104 1240 switch ($patternselect) { 1241 case 'pcre': //Kept for BC 1105 1242 case 'pcre8': 1106 /** 1107 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. 1108 * @link http://squiloople.com/2009/12/20/email-address-validation/ 1243 /* 1244 * A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL 1245 * is based. 1246 * In addition to the addresses allowed by filter_var, also permits: 1247 * * dotless domains: `a@b` 1248 * * comments: `1234 @ local(blah) .machine .example` 1249 * * quoted elements: `'"test blah"@example.org'` 1250 * * numeric TLDs: `a@b.123` 1251 * * unbracketed IPv4 literals: `a@192.168.0.1` 1252 * * IPv6 literals: 'first.last@[IPv6:a1::]' 1253 * Not all of these will necessarily work for sending! 1254 * 1255 * @see http://squiloople.com/2009/12/20/email-address-validation/ 1109 1256 * @copyright 2009-2010 Michael Rushton 1110 1257 * Feel free to use and redistribute this code. But please keep this copyright notice. 1111 1258 */ 1112 return (bool ean)preg_match(1259 return (bool) preg_match( 1113 1260 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . 1114 1261 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . 1115 1262 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . … … 1121 1268 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', 1122 1269 $address 1123 1270 ); 1124 case 'pcre':1125 //An older regex that doesn't need a recent PCRE1126 return (boolean)preg_match(1127 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .1128 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .1129 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .1130 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .1131 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .1132 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .1133 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .1134 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .1135 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .1136 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',1137 $address1138 );1139 1271 case 'html5': 1140 /* *1272 /* 1141 1273 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. 1142 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) 1274 * 1275 * @see http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) 1143 1276 */ 1144 return (bool ean)preg_match(1277 return (bool) preg_match( 1145 1278 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . 1146 1279 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', 1147 1280 $address 1148 1281 ); 1149 case 'noregex':1150 //No PCRE! Do something _very_ approximate!1151 //Check the address is 3 chars or longer and contains an @ that's not the first or last char1152 return (strlen($address) >= 31153 and strpos($address, '@') >= 11154 and strpos($address, '@') != strlen($address) - 1);1155 1282 case 'php': 1156 1283 default: 1157 return (bool ean)filter_var($address, FILTER_VALIDATE_EMAIL);1284 return (bool) filter_var($address, FILTER_VALIDATE_EMAIL); 1158 1285 } 1159 1286 } 1160 1287 1161 1288 /** 1162 1289 * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the 1163 * "intl" and "mbstring" PHP extensions. 1164 * @return bool "true" if required functions for IDN support are present 1290 * `intl` and `mbstring` PHP extensions. 1291 * 1292 * @return bool `true` if required functions for IDN support are present 1165 1293 */ 1166 public function idnSupported()1294 public static function idnSupported() 1167 1295 { 1168 // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.1169 1296 return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); 1170 1297 } 1171 1298 … … 1175 1302 * This function silently returns unmodified address if: 1176 1303 * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) 1177 1304 * - Conversion to punycode is impossible (e.g. required PHP functions are not available) 1178 * or fails for any reason (e.g. domain has characters not allowed in an IDN) 1179 * @see PHPMailer::$CharSet 1305 * or fails for any reason (e.g. domain contains characters not allowed in an IDN). 1306 * 1307 * @see PHPMailer::$CharSet 1308 * 1180 1309 * @param string $address The email address to convert 1310 * 1181 1311 * @return string The encoded address in ASCII form 1182 1312 */ 1183 1313 public function punyencodeAddress($address) 1184 1314 { 1185 1315 // Verify we have required functions, CharSet, and at-sign. 1186 if ($this->idnSupported() and 1316 $pos = strrpos($address, '@'); 1317 if (static::idnSupported() and 1187 1318 !empty($this->CharSet) and 1188 ($pos = strrpos($address, '@')) !== false) { 1319 false !== $pos 1320 ) { 1189 1321 $domain = substr($address, ++$pos); 1190 1322 // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. 1191 1323 if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { 1192 1324 $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); 1193 if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? 1194 idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : 1195 idn_to_ascii($domain)) !== false) { 1325 //Ignore IDE complaints about this line - method signature changed in PHP 5.4 1326 $errorcode = 0; 1327 $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_UTS46); 1328 if (false !== $punycode) { 1196 1329 return substr($address, 0, $pos) . $punycode; 1197 1330 } 1198 1331 } 1199 1332 } 1333 1200 1334 return $address; 1201 1335 } 1202 1336 … … 1203 1337 /** 1204 1338 * Create a message and send it. 1205 1339 * Uses the sending method specified by $Mailer. 1206 * @throws phpmailerException 1207 * @return boolean false on error - See the ErrorInfo property for details of the error. 1340 * 1341 * @throws Exception 1342 * 1343 * @return bool false on error - See the ErrorInfo property for details of the error 1208 1344 */ 1209 1345 public function send() 1210 1346 { … … 1212 1348 if (!$this->preSend()) { 1213 1349 return false; 1214 1350 } 1351 1215 1352 return $this->postSend(); 1216 } catch ( phpmailerException $exc) {1353 } catch (Exception $exc) { 1217 1354 $this->mailHeader = ''; 1218 1355 $this->setError($exc->getMessage()); 1219 1356 if ($this->exceptions) { 1220 1357 throw $exc; 1221 1358 } 1359 1222 1360 return false; 1223 1361 } 1224 1362 } … … 1225 1363 1226 1364 /** 1227 1365 * Prepare a message for sending. 1228 * @throws phpmailerException 1229 * @return boolean 1366 * 1367 * @throws Exception 1368 * 1369 * @return bool 1230 1370 */ 1231 1371 public function preSend() 1232 1372 { 1373 if ('smtp' == $this->Mailer or 1374 ('mail' == $this->Mailer and stripos(PHP_OS, 'WIN') === 0) 1375 ) { 1376 //SMTP mandates RFC-compliant line endings 1377 //and it's also used with mail() on Windows 1378 static::setLE("\r\n"); 1379 } else { 1380 //Maintain backward compatibility with legacy Linux command line mailers 1381 static::setLE(PHP_EOL); 1382 } 1383 //Check for buggy PHP versions that add a header with an incorrect line break 1384 if (ini_get('mail.add_x_header') == 1 1385 and 'mail' == $this->Mailer 1386 and stripos(PHP_OS, 'WIN') === 0 1387 and ((version_compare(PHP_VERSION, '7.0.0', '>=') 1388 and version_compare(PHP_VERSION, '7.0.17', '<')) 1389 or (version_compare(PHP_VERSION, '7.1.0', '>=') 1390 and version_compare(PHP_VERSION, '7.1.3', '<'))) 1391 ) { 1392 trigger_error( 1393 'Your version of PHP is affected by a bug that may result in corrupted messages.' . 1394 ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . 1395 ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', 1396 E_USER_WARNING 1397 ); 1398 } 1399 1233 1400 try { 1234 1401 $this->error_count = 0; // Reset errors 1235 1402 $this->mailHeader = ''; … … 1237 1404 // Dequeue recipient and Reply-To addresses with IDN 1238 1405 foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { 1239 1406 $params[1] = $this->punyencodeAddress($params[1]); 1240 call_user_func_array( array($this, 'addAnAddress'), $params);1407 call_user_func_array([$this, 'addAnAddress'], $params); 1241 1408 } 1242 if ( (count($this->to) + count($this->cc) + count($this->bcc)) < 1) {1243 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);1409 if (count($this->to) + count($this->cc) + count($this->bcc) < 1) { 1410 throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL); 1244 1411 } 1245 1412 1246 1413 // Validate From, Sender, and ConfirmReadingTo addresses 1247 foreach ( array('From', 'Sender', 'ConfirmReadingTo')as $address_kind) {1414 foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) { 1248 1415 $this->$address_kind = trim($this->$address_kind); 1249 1416 if (empty($this->$address_kind)) { 1250 1417 continue; 1251 1418 } 1252 1419 $this->$address_kind = $this->punyencodeAddress($this->$address_kind); 1253 if (!$this->validateAddress($this->$address_kind)) { 1254 $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; 1420 if (!static::validateAddress($this->$address_kind)) { 1421 $error_message = sprintf('%s (%s): %s', 1422 $this->lang('invalid_address'), 1423 $address_kind, 1424 $this->$address_kind); 1255 1425 $this->setError($error_message); 1256 1426 $this->edebug($error_message); 1257 1427 if ($this->exceptions) { 1258 throw new phpmailerException($error_message);1428 throw new Exception($error_message); 1259 1429 } 1430 1260 1431 return false; 1261 1432 } 1262 1433 } … … 1263 1434 1264 1435 // Set whether the message is multipart/alternative 1265 1436 if ($this->alternativeExists()) { 1266 $this->ContentType = 'multipart/alternative';1437 $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE; 1267 1438 } 1268 1439 1269 1440 $this->setMessageType(); 1270 1441 // Refuse to send an empty message unless we are specifically allowing it 1271 1442 if (!$this->AllowEmpty and empty($this->Body)) { 1272 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);1443 throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); 1273 1444 } 1274 1445 1446 //Trim subject consistently 1447 $this->Subject = trim($this->Subject); 1275 1448 // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) 1276 1449 $this->MIMEHeader = ''; 1277 1450 $this->MIMEBody = $this->createBody(); … … 1282 1455 1283 1456 // To capture the complete message when using mail(), create 1284 1457 // an extra header list which createHeader() doesn't fold in 1285 if ( $this->Mailer == 'mail') {1458 if ('mail' == $this->Mailer) { 1286 1459 if (count($this->to) > 0) { 1287 1460 $this->mailHeader .= $this->addrAppend('To', $this->to); 1288 1461 } else { … … 1290 1463 } 1291 1464 $this->mailHeader .= $this->headerLine( 1292 1465 'Subject', 1293 $this->encodeHeader($this->secureHeader( trim($this->Subject)))1466 $this->encodeHeader($this->secureHeader($this->Subject)) 1294 1467 ); 1295 1468 } 1296 1469 … … 1299 1472 and !empty($this->DKIM_selector) 1300 1473 and (!empty($this->DKIM_private_string) 1301 1474 or (!empty($this->DKIM_private) 1302 and s elf::isPermittedPath($this->DKIM_private)1475 and static::isPermittedPath($this->DKIM_private) 1303 1476 and file_exists($this->DKIM_private) 1304 1477 ) 1305 1478 ) … … 1309 1482 $this->encodeHeader($this->secureHeader($this->Subject)), 1310 1483 $this->MIMEBody 1311 1484 ); 1312 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . s elf::CRLF.1313 st r_replace("\r\n", "\n", $header_dkim) . self::CRLF;1485 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . static::$LE . 1486 static::normalizeBreaks($header_dkim) . static::$LE; 1314 1487 } 1488 1315 1489 return true; 1316 } catch ( phpmailerException $exc) {1490 } catch (Exception $exc) { 1317 1491 $this->setError($exc->getMessage()); 1318 1492 if ($this->exceptions) { 1319 1493 throw $exc; 1320 1494 } 1495 1321 1496 return false; 1322 1497 } 1323 1498 } 1324 1499 1325 1500 /** 1326 * Actually send a message. 1327 * Send the email via the selected mechanism 1328 * @throws phpmailerException 1329 * @return boolean 1501 * Actually send a message via the selected mechanism. 1502 * 1503 * @throws Exception 1504 * 1505 * @return bool 1330 1506 */ 1331 1507 public function postSend() 1332 1508 { … … 1341 1517 case 'mail': 1342 1518 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1343 1519 default: 1344 $sendMethod = $this->Mailer .'Send';1520 $sendMethod = $this->Mailer . 'Send'; 1345 1521 if (method_exists($this, $sendMethod)) { 1346 1522 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); 1347 1523 } … … 1348 1524 1349 1525 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1350 1526 } 1351 } catch ( phpmailerException $exc) {1527 } catch (Exception $exc) { 1352 1528 $this->setError($exc->getMessage()); 1353 1529 $this->edebug($exc->getMessage()); 1354 1530 if ($this->exceptions) { … … 1355 1531 throw $exc; 1356 1532 } 1357 1533 } 1534 1358 1535 return false; 1359 1536 } 1360 1537 1361 1538 /** 1362 1539 * Send mail using the $Sendmail program. 1540 * 1541 * @see PHPMailer::$Sendmail 1542 * 1363 1543 * @param string $header The message headers 1364 * @param string $body The message body1365 * @see PHPMailer::$Sendmail1366 * @throws phpmailerException1367 * @access protected1368 * @return bool ean1544 * @param string $body The message body 1545 * 1546 * @throws Exception 1547 * 1548 * @return bool 1369 1549 */ 1370 1550 protected function sendmailSend($header, $body) 1371 1551 { 1372 1552 // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. 1373 1553 if (!empty($this->Sender) and self::isShellSafe($this->Sender)) { 1374 if ( $this->Mailer == 'qmail') {1554 if ('qmail' == $this->Mailer) { 1375 1555 $sendmailFmt = '%s -f%s'; 1376 1556 } else { 1377 1557 $sendmailFmt = '%s -oi -f%s -t'; 1378 1558 } 1379 1559 } else { 1380 if ( $this->Mailer == 'qmail') {1560 if ('qmail' == $this->Mailer) { 1381 1561 $sendmailFmt = '%s'; 1382 1562 } else { 1383 1563 $sendmailFmt = '%s -oi -t'; … … 1384 1564 } 1385 1565 } 1386 1566 1387 // TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing.1388 1567 $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); 1389 1568 1390 1569 if ($this->SingleTo) { 1391 1570 foreach ($this->SingleToArray as $toAddr) { 1392 if (!@$mail = popen($sendmail, 'w')) { 1393 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1571 $mail = @popen($sendmail, 'w'); 1572 if (!$mail) { 1573 throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1394 1574 } 1395 f puts($mail, 'To: ' . $toAddr . "\n");1396 f puts($mail, $header);1397 f puts($mail, $body);1575 fwrite($mail, 'To: ' . $toAddr . "\n"); 1576 fwrite($mail, $header); 1577 fwrite($mail, $body); 1398 1578 $result = pclose($mail); 1399 1579 $this->doCallback( 1400 1580 ($result == 0), 1401 array($toAddr),1581 [$toAddr], 1402 1582 $this->cc, 1403 1583 $this->bcc, 1404 1584 $this->Subject, 1405 1585 $body, 1406 $this->From 1586 $this->From, 1587 [] 1407 1588 ); 1408 if ( $result != 0) {1409 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);1589 if (0 !== $result) { 1590 throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1410 1591 } 1411 1592 } 1412 1593 } else { 1413 if (!@$mail = popen($sendmail, 'w')) { 1414 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1594 $mail = @popen($sendmail, 'w'); 1595 if (!$mail) { 1596 throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1415 1597 } 1416 f puts($mail, $header);1417 f puts($mail, $body);1598 fwrite($mail, $header); 1599 fwrite($mail, $body); 1418 1600 $result = pclose($mail); 1419 1601 $this->doCallback( 1420 1602 ($result == 0), … … 1423 1605 $this->bcc, 1424 1606 $this->Subject, 1425 1607 $body, 1426 $this->From 1608 $this->From, 1609 [] 1427 1610 ); 1428 if ( $result != 0) {1429 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);1611 if (0 !== $result) { 1612 throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1430 1613 } 1431 1614 } 1615 1432 1616 return true; 1433 1617 } 1434 1618 1435 1619 /** 1436 1620 * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters. 1621 * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. 1437 1622 * 1438 * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. 1623 * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report 1624 * 1439 1625 * @param string $string The string to be validated 1440 * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report 1441 * @access protected 1442 * @return boolean 1626 * 1627 * @return bool 1443 1628 */ 1444 1629 protected static function isShellSafe($string) 1445 1630 { 1446 1631 // Future-proof 1447 1632 if (escapeshellcmd($string) !== $string 1448 or !in_array(escapeshellarg($string), array("'$string'", "\"$string\""))1633 or !in_array(escapeshellarg($string), ["'$string'", "\"$string\""]) 1449 1634 ) { 1450 1635 return false; 1451 1636 } … … 1452 1637 1453 1638 $length = strlen($string); 1454 1639 1455 for ($i = 0; $i < $length; $i++) {1640 for ($i = 0; $i < $length; ++$i) { 1456 1641 $c = $string[$i]; 1457 1642 1458 1643 // All other characters have a special meaning in at least one common shell, including = and +. … … 1470 1655 * Check whether a file path is of a permitted type. 1471 1656 * Used to reject URLs and phar files from functions that access local file paths, 1472 1657 * such as addAttachment. 1473 * @param string $path A relative or absolute path to a file. 1658 * 1659 * @param string $path A relative or absolute path to a file 1660 * 1474 1661 * @return bool 1475 1662 */ 1476 1663 protected static function isPermittedPath($path) … … 1480 1667 1481 1668 /** 1482 1669 * Send mail using the PHP mail() function. 1670 * 1671 * @see http://www.php.net/manual/en/book.mail.php 1672 * 1483 1673 * @param string $header The message headers 1484 * @param string $body The message body1485 * @link http://www.php.net/manual/en/book.mail.php1486 * @throws phpmailerException1487 * @access protected1488 * @return bool ean1674 * @param string $body The message body 1675 * 1676 * @throws Exception 1677 * 1678 * @return bool 1489 1679 */ 1490 1680 protected function mailSend($header, $body) 1491 1681 { 1492 $toArr = array();1682 $toArr = []; 1493 1683 foreach ($this->to as $toaddr) { 1494 1684 $toArr[] = $this->addrFormat($toaddr); 1495 1685 } … … 1497 1687 1498 1688 $params = null; 1499 1689 //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver 1500 if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { 1690 if (!empty($this->Sender) and static::validateAddress($this->Sender)) { 1691 //A space after `-f` is optional, but there is a long history of its presence 1692 //causing problems, so we don't use one 1693 //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html 1694 //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html 1695 //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html 1696 //Example problem: https://www.drupal.org/node/1057954 1501 1697 // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. 1502 1698 if (self::isShellSafe($this->Sender)) { 1503 1699 $params = sprintf('-f%s', $this->Sender); 1504 1700 } 1505 1701 } 1506 if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {1702 if (!empty($this->Sender) and static::validateAddress($this->Sender)) { 1507 1703 $old_from = ini_get('sendmail_from'); 1508 1704 ini_set('sendmail_from', $this->Sender); 1509 1705 } … … 1511 1707 if ($this->SingleTo and count($toArr) > 1) { 1512 1708 foreach ($toArr as $toAddr) { 1513 1709 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); 1514 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);1710 $this->doCallback($result, [$toAddr], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); 1515 1711 } 1516 1712 } else { 1517 1713 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); 1518 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From );1714 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); 1519 1715 } 1520 1716 if (isset($old_from)) { 1521 1717 ini_set('sendmail_from', $old_from); 1522 1718 } 1523 1719 if (!$result) { 1524 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);1720 throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL); 1525 1721 } 1722 1526 1723 return true; 1527 1724 } 1528 1725 1529 1726 /** 1530 1727 * Get an instance to use for SMTP operations. 1531 * Override this function to load your own SMTP implementation 1728 * Override this function to load your own SMTP implementation, 1729 * or set one with setSMTPInstance. 1730 * 1532 1731 * @return SMTP 1533 1732 */ 1534 1733 public function getSMTPInstance() 1535 1734 { 1536 1735 if (!is_object($this->smtp)) { 1537 require_once( 'class-smtp.php' ); 1538 $this->smtp = new SMTP; 1736 $this->smtp = new SMTP(); 1539 1737 } 1738 1540 1739 return $this->smtp; 1541 1740 } 1542 1741 1543 1742 /** 1743 * Provide an instance to use for SMTP operations. 1744 * 1745 * @param SMTP $smtp 1746 * 1747 * @return SMTP 1748 */ 1749 public function setSMTPInstance(SMTP $smtp) 1750 { 1751 $this->smtp = $smtp; 1752 1753 return $this->smtp; 1754 } 1755 1756 /** 1544 1757 * Send mail via SMTP. 1545 1758 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. 1546 * Uses the PHPMailerSMTP class by default. 1547 * @see PHPMailer::getSMTPInstance() to use a different class. 1759 * 1760 * @see PHPMailer::setSMTPInstance() to use a different class. 1761 * 1762 * @uses \PHPMailer\PHPMailer\SMTP 1763 * 1548 1764 * @param string $header The message headers 1549 * @param string $body The message body1550 * @throws phpmailerException1551 * @ uses SMTP1552 * @access protected1553 * @return bool ean1765 * @param string $body The message body 1766 * 1767 * @throws Exception 1768 * 1769 * @return bool 1554 1770 */ 1555 1771 protected function smtpSend($header, $body) 1556 1772 { 1557 $bad_rcpt = array();1773 $bad_rcpt = []; 1558 1774 if (!$this->smtpConnect($this->SMTPOptions)) { 1559 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);1775 throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); 1560 1776 } 1561 if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { 1777 //Sender already validated in preSend() 1778 if ('' == $this->Sender) { 1779 $smtp_from = $this->From; 1780 } else { 1562 1781 $smtp_from = $this->Sender; 1563 } else {1564 $smtp_from = $this->From;1565 1782 } 1566 1783 if (!$this->smtp->mail($smtp_from)) { 1567 1784 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); 1568 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);1785 throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); 1569 1786 } 1570 1787 1788 $callbacks = []; 1571 1789 // Attempt to send to all recipients 1572 foreach ( array($this->to, $this->cc, $this->bcc)as $togroup) {1790 foreach ([$this->to, $this->cc, $this->bcc] as $togroup) { 1573 1791 foreach ($togroup as $to) { 1574 1792 if (!$this->smtp->recipient($to[0])) { 1575 1793 $error = $this->smtp->getError(); 1576 $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);1794 $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']]; 1577 1795 $isSent = false; 1578 1796 } else { 1579 1797 $isSent = true; 1580 1798 } 1581 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); 1799 1800 $callbacks[] = ['issent'=>$isSent, 'to'=>$to[0]]; 1582 1801 } 1583 1802 } 1584 1803 1585 1804 // Only send the DATA command if we have viable recipients 1586 1805 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { 1587 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);1806 throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL); 1588 1807 } 1808 1809 $smtp_transaction_id = $this->smtp->getLastTransactionID(); 1810 1589 1811 if ($this->SMTPKeepAlive) { 1590 1812 $this->smtp->reset(); 1591 1813 } else { … … 1592 1814 $this->smtp->quit(); 1593 1815 $this->smtp->close(); 1594 1816 } 1817 1818 foreach ($callbacks as $cb) { 1819 $this->doCallback( 1820 $cb['issent'], 1821 [$cb['to']], 1822 [], 1823 [], 1824 $this->Subject, 1825 $body, 1826 $this->From, 1827 ['smtp_transaction_id' => $smtp_transaction_id] 1828 ); 1829 } 1830 1595 1831 //Create error message for any bad addresses 1596 1832 if (count($bad_rcpt) > 0) { 1597 1833 $errstr = ''; … … 1598 1834 foreach ($bad_rcpt as $bad) { 1599 1835 $errstr .= $bad['to'] . ': ' . $bad['error']; 1600 1836 } 1601 throw new phpmailerException(1837 throw new Exception( 1602 1838 $this->lang('recipients_failed') . $errstr, 1603 1839 self::STOP_CONTINUE 1604 1840 ); 1605 1841 } 1842 1606 1843 return true; 1607 1844 } 1608 1845 … … 1609 1846 /** 1610 1847 * Initiate a connection to an SMTP server. 1611 1848 * Returns false if the operation failed. 1849 * 1612 1850 * @param array $options An array of options compatible with stream_context_create() 1613 * @uses SMTP 1614 * @access public 1615 * @throws phpmailerException 1616 * @return boolean 1851 * 1852 * @throws Exception 1853 * 1854 * @uses \PHPMailer\PHPMailer\SMTP 1855 * 1856 * @return bool 1617 1857 */ 1618 1858 public function smtpConnect($options = null) 1619 1859 { 1620 if ( is_null($this->smtp)) {1860 if (null === $this->smtp) { 1621 1861 $this->smtp = $this->getSMTPInstance(); 1622 1862 } 1623 1863 1624 1864 //If no options are provided, use whatever is set in the instance 1625 if ( is_null($options)) {1865 if (null === $options) { 1626 1866 $options = $this->SMTPOptions; 1627 1867 } 1628 1868 … … 1639 1879 $lastexception = null; 1640 1880 1641 1881 foreach ($hosts as $hostentry) { 1642 $hostinfo = array();1882 $hostinfo = []; 1643 1883 if (!preg_match( 1644 1884 '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/', 1645 1885 trim($hostentry), 1646 1886 $hostinfo 1647 1887 )) { 1888 static::edebug($this->lang('connect_host') . ' ' . $hostentry); 1648 1889 // Not a valid host entry 1649 $this->edebug('Ignoring invalid host: ' . $hostentry);1650 1890 continue; 1651 1891 } 1652 1892 // $hostinfo[2]: optional ssl or tls prefix … … 1654 1894 // $hostinfo[4]: optional port number 1655 1895 // The host string prefix can temporarily override the current setting for SMTPSecure 1656 1896 // If it's not specified, the default value is used 1897 1898 //Check the host name is a valid name or IP address before trying to use it 1899 if (!static::isValidHost($hostinfo[3])) { 1900 static::edebug($this->lang('connect_host') . ' ' . $hostentry); 1901 continue; 1902 } 1657 1903 $prefix = ''; 1658 1904 $secure = $this->SMTPSecure; 1659 $tls = ( $this->SMTPSecure == 'tls');1905 $tls = ('tls' == $this->SMTPSecure); 1660 1906 if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { 1661 1907 $prefix = 'ssl://'; 1662 1908 $tls = false; // Can't have SSL and TLS at the same time 1663 1909 $secure = 'ssl'; 1664 } elseif ( $hostinfo[2] == 'tls') {1910 } elseif ('tls' == $hostinfo[2]) { 1665 1911 $tls = true; 1666 1912 // tls doesn't use a prefix 1667 1913 $secure = 'tls'; 1668 1914 } 1669 1915 //Do we need the OpenSSL extension? 1670 $sslext = defined('OPENSSL_ALGO_SHA 1');1916 $sslext = defined('OPENSSL_ALGO_SHA256'); 1671 1917 if ('tls' === $secure or 'ssl' === $secure) { 1672 1918 //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled 1673 1919 if (!$sslext) { 1674 throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);1920 throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL); 1675 1921 } 1676 1922 } 1677 1923 $host = $hostinfo[3]; 1678 1924 $port = $this->Port; 1679 $tport = (int eger)$hostinfo[4];1925 $tport = (int) $hostinfo[4]; 1680 1926 if ($tport > 0 and $tport < 65536) { 1681 1927 $port = $tport; 1682 1928 } … … 1693 1939 // * we have openssl extension 1694 1940 // * we are not already using SSL 1695 1941 // * the server offers STARTTLS 1696 if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl'and $this->smtp->getServerExt('STARTTLS')) {1942 if ($this->SMTPAutoTLS and $sslext and 'ssl' != $secure and $this->smtp->getServerExt('STARTTLS')) { 1697 1943 $tls = true; 1698 1944 } 1699 1945 if ($tls) { 1700 1946 if (!$this->smtp->startTLS()) { 1701 throw new phpmailerException($this->lang('connect_host'));1947 throw new Exception($this->lang('connect_host')); 1702 1948 } 1703 1949 // We must resend EHLO after TLS negotiation 1704 1950 $this->smtp->hello($hello); … … 1708 1954 $this->Username, 1709 1955 $this->Password, 1710 1956 $this->AuthType, 1711 $this->Realm, 1712 $this->Workstation 1957 $this->oauth 1713 1958 ) 1714 1959 ) { 1715 throw new phpmailerException($this->lang('authenticate'));1960 throw new Exception($this->lang('authenticate')); 1716 1961 } 1717 1962 } 1963 1718 1964 return true; 1719 } catch ( phpmailerException $exc) {1965 } catch (Exception $exc) { 1720 1966 $lastexception = $exc; 1721 1967 $this->edebug($exc->getMessage()); 1722 1968 // We must have connected, but then failed TLS or Auth, so close connection nicely … … 1727 1973 // If we get here, all connection attempts have failed, so close connection hard 1728 1974 $this->smtp->close(); 1729 1975 // As we've caught all exceptions, just report whatever the last one was 1730 if ($this->exceptions and !is_null($lastexception)) {1976 if ($this->exceptions and null !== $lastexception) { 1731 1977 throw $lastexception; 1732 1978 } 1979 1733 1980 return false; 1734 1981 } 1735 1982 1736 1983 /** 1737 1984 * Close the active SMTP session if one exists. 1738 * @return void1739 1985 */ 1740 1986 public function smtpClose() 1741 1987 { 1742 if ( is_a($this->smtp, 'SMTP')) {1988 if (null !== $this->smtp) { 1743 1989 if ($this->smtp->connected()) { 1744 1990 $this->smtp->quit(); 1745 1991 $this->smtp->close(); … … 1751 1997 * Set the language for error messages. 1752 1998 * Returns false if it cannot load the language file. 1753 1999 * The default language is English. 1754 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") 2000 * 2001 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") 1755 2002 * @param string $lang_path Path to the language file directory, with trailing separator (slash) 1756 * @return boolean1757 * @ access public2003 * 2004 * @return bool 1758 2005 */ 1759 2006 public function setLanguage($langcode = 'en', $lang_path = '') 1760 2007 { 1761 2008 // Backwards compatibility for renamed language codes 1762 $renamed_langcodes = array(2009 $renamed_langcodes = [ 1763 2010 'br' => 'pt_br', 1764 2011 'cz' => 'cs', 1765 2012 'dk' => 'da', 1766 2013 'no' => 'nb', 1767 2014 'se' => 'sv', 1768 'sr' => 'rs' 1769 ); 2015 'rs' => 'sr', 2016 'tg' => 'tl', 2017 ]; 1770 2018 1771 2019 if (isset($renamed_langcodes[$langcode])) { 1772 2020 $langcode = $renamed_langcodes[$langcode]; … … 1773 2021 } 1774 2022 1775 2023 // Define full set of translatable strings in English 1776 $PHPMAILER_LANG = array(2024 $PHPMAILER_LANG = [ 1777 2025 'authenticate' => 'SMTP Error: Could not authenticate.', 1778 2026 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', 1779 2027 'data_not_accepted' => 'SMTP Error: data not accepted.', … … 1792 2040 'smtp_connect_failed' => 'SMTP connect() failed.', 1793 2041 'smtp_error' => 'SMTP server error: ', 1794 2042 'variable_set' => 'Cannot set or reset variable: ', 1795 'extension_missing' => 'Extension missing: ' 1796 );2043 'extension_missing' => 'Extension missing: ', 2044 ]; 1797 2045 if (empty($lang_path)) { 1798 2046 // Calculate an absolute path so it can work if CWD is not here 1799 $lang_path = dirname(__ FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;2047 $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR; 1800 2048 } 1801 2049 //Validate $langcode 1802 2050 if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { … … 1805 2053 $foundlang = true; 1806 2054 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; 1807 2055 // There is no English translation file 1808 if ( $langcode != 'en') {2056 if ('en' != $langcode) { 1809 2057 // Make sure language file path is readable 1810 if (!s elf::isPermittedPath($lang_file) or !is_readable($lang_file)) {2058 if (!static::isPermittedPath($lang_file) || !file_exists($lang_file)) { 1811 2059 $foundlang = false; 1812 2060 } else { 1813 2061 // Overwrite language-specific strings. … … 1816 2064 } 1817 2065 } 1818 2066 $this->language = $PHPMAILER_LANG; 1819 return (boolean)$foundlang; // Returns false if language not found 2067 2068 return (bool) $foundlang; // Returns false if language not found 1820 2069 } 1821 2070 1822 2071 /** 1823 2072 * Get the array of strings for the current language. 2073 * 1824 2074 * @return array 1825 2075 */ 1826 2076 public function getTranslations() … … 1830 2080 1831 2081 /** 1832 2082 * Create recipient headers. 1833 * @access public2083 * 1834 2084 * @param string $type 1835 * @param array $addr An array of recipient, 1836 * where each recipient is a 2-element indexed array with element 0 containing an address 1837 * and element 1 containing a name, like: 1838 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) 2085 * @param array $addr An array of recipients, 2086 * where each recipient is a 2-element indexed array with element 0 containing an address 2087 * and element 1 containing a name, like: 2088 * [['joe@example.com', 'Joe User'], ['zoe@example.com', 'Zoe User']] 2089 * 1839 2090 * @return string 1840 2091 */ 1841 2092 public function addrAppend($type, $addr) 1842 2093 { 1843 $addresses = array();2094 $addresses = []; 1844 2095 foreach ($addr as $address) { 1845 2096 $addresses[] = $this->addrFormat($address); 1846 2097 } 1847 return $type . ': ' . implode(', ', $addresses) . $this->LE; 2098 2099 return $type . ': ' . implode(', ', $addresses) . static::$LE; 1848 2100 } 1849 2101 1850 2102 /** 1851 2103 * Format an address for use in a message header. 1852 * @access public 1853 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name 1854 * like array('joe@example.com', 'Joe User') 2104 * 2105 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name like 2106 * ['joe@example.com', 'Joe User'] 2107 * 1855 2108 * @return string 1856 2109 */ 1857 2110 public function addrFormat($addr) … … 1858 2111 { 1859 2112 if (empty($addr[1])) { // No name provided 1860 2113 return $this->secureHeader($addr[0]); 1861 } else { 1862 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( 2114 } 2115 2116 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( 1863 2117 $addr[0] 1864 2118 ) . '>'; 1865 }1866 2119 } 1867 2120 1868 2121 /** … … 1870 2123 * For use with mailers that do not automatically perform wrapping 1871 2124 * and for quoted-printable encoded messages. 1872 2125 * Original written by philippe. 2126 * 1873 2127 * @param string $message The message to wrap 1874 * @param int eger $lengthThe line length to wrap to1875 * @param bool ean$qp_mode Whether to run in Quoted-Printable mode1876 * @access public2128 * @param int $length The line length to wrap to 2129 * @param bool $qp_mode Whether to run in Quoted-Printable mode 2130 * 1877 2131 * @return string 1878 2132 */ 1879 2133 public function wrapText($message, $length, $qp_mode = false) 1880 2134 { 1881 2135 if ($qp_mode) { 1882 $soft_break = sprintf(' =%s', $this->LE);2136 $soft_break = sprintf(' =%s', static::$LE); 1883 2137 } else { 1884 $soft_break = $this->LE;2138 $soft_break = static::$LE; 1885 2139 } 1886 2140 // If utf-8 encoding is used, we will need to make sure we don't 1887 2141 // split multibyte characters when we wrap 1888 $is_utf8 = (strtolower($this->CharSet) == 'utf-8');1889 $lelen = strlen( $this->LE);1890 $crlflen = strlen(s elf::CRLF);2142 $is_utf8 = static::CHARSET_UTF8 === strtolower($this->CharSet); 2143 $lelen = strlen(static::$LE); 2144 $crlflen = strlen(static::$LE); 1891 2145 1892 $message = $this->fixEOL($message);2146 $message = static::normalizeBreaks($message); 1893 2147 //Remove a trailing line break 1894 if (substr($message, -$lelen) == $this->LE) {2148 if (substr($message, -$lelen) == static::$LE) { 1895 2149 $message = substr($message, 0, -$lelen); 1896 2150 } 1897 2151 1898 2152 //Split message into lines 1899 $lines = explode( $this->LE, $message);2153 $lines = explode(static::$LE, $message); 1900 2154 //Message will be rebuilt in here 1901 2155 $message = ''; 1902 2156 foreach ($lines as $line) { … … 1911 2165 $len = $space_left; 1912 2166 if ($is_utf8) { 1913 2167 $len = $this->utf8CharBoundary($word, $len); 1914 } elseif ( substr($word, $len - 1, 1) == '=') {1915 $len--;1916 } elseif ( substr($word, $len - 2, 1) == '=') {2168 } elseif ('=' == substr($word, $len - 1, 1)) { 2169 --$len; 2170 } elseif ('=' == substr($word, $len - 2, 1)) { 1917 2171 $len -= 2; 1918 2172 } 1919 2173 $part = substr($word, 0, $len); 1920 2174 $word = substr($word, $len); 1921 2175 $buf .= ' ' . $part; 1922 $message .= $buf . sprintf('=%s', s elf::CRLF);2176 $message .= $buf . sprintf('=%s', static::$LE); 1923 2177 } else { 1924 2178 $message .= $buf . $soft_break; 1925 2179 } … … 1932 2186 $len = $length; 1933 2187 if ($is_utf8) { 1934 2188 $len = $this->utf8CharBoundary($word, $len); 1935 } elseif ( substr($word, $len - 1, 1) == '=') {1936 $len--;1937 } elseif ( substr($word, $len - 2, 1) == '=') {2189 } elseif ('=' == substr($word, $len - 1, 1)) { 2190 --$len; 2191 } elseif ('=' == substr($word, $len - 2, 1)) { 1938 2192 $len -= 2; 1939 2193 } 1940 2194 $part = substr($word, 0, $len); … … 1941 2195 $word = substr($word, $len); 1942 2196 1943 2197 if (strlen($word) > 0) { 1944 $message .= $part . sprintf('=%s', s elf::CRLF);2198 $message .= $part . sprintf('=%s', static::$LE); 1945 2199 } else { 1946 2200 $buf = $part; 1947 2201 } … … 1953 2207 } 1954 2208 $buf .= $word; 1955 2209 1956 if (strlen($buf) > $length and $buf_o != '') {2210 if (strlen($buf) > $length and '' != $buf_o) { 1957 2211 $message .= $buf_o . $soft_break; 1958 2212 $buf = $word; 1959 2213 } … … 1960 2214 } 1961 2215 $firstword = false; 1962 2216 } 1963 $message .= $buf . s elf::CRLF;2217 $message .= $buf . static::$LE; 1964 2218 } 1965 2219 1966 2220 return $message; … … 1970 2224 * Find the last character boundary prior to $maxLength in a utf-8 1971 2225 * quoted-printable encoded string. 1972 2226 * Original written by Colin Brown. 1973 * @access public2227 * 1974 2228 * @param string $encodedText utf-8 QP text 1975 * @param integer $maxLength Find the last character boundary prior to this length 1976 * @return integer 2229 * @param int $maxLength Find the last character boundary prior to this length 2230 * 2231 * @return int 1977 2232 */ 1978 2233 public function utf8CharBoundary($encodedText, $maxLength) 1979 2234 { … … 1992 2247 // If the encoded char was found at pos 0, it will fit 1993 2248 // otherwise reduce maxLength to start of the encoded char 1994 2249 if ($encodedCharPos > 0) { 1995 $maxLength = $maxLength - ($lookBack - $encodedCharPos);2250 $maxLength -= $lookBack - $encodedCharPos; 1996 2251 } 1997 2252 $foundSplitPos = true; 1998 2253 } elseif ($dec >= 192) { 1999 2254 // First byte of a multi byte character 2000 2255 // Reduce maxLength to split at start of character 2001 $maxLength = $maxLength - ($lookBack - $encodedCharPos);2256 $maxLength -= $lookBack - $encodedCharPos; 2002 2257 $foundSplitPos = true; 2003 2258 } elseif ($dec < 192) { 2004 2259 // Middle byte of a multi byte character, look further back … … 2009 2264 $foundSplitPos = true; 2010 2265 } 2011 2266 } 2267 2012 2268 return $maxLength; 2013 2269 } 2014 2270 … … 2017 2273 * Wraps the message body to the number of chars set in the WordWrap property. 2018 2274 * You should only do this to plain-text bodies as wrapping HTML tags may break them. 2019 2275 * This is called automatically by createBody(), so you don't need to call it yourself. 2020 * @access public2021 * @return void2022 2276 */ 2023 2277 public function setWordWrap() 2024 2278 { … … 2041 2295 2042 2296 /** 2043 2297 * Assemble message headers. 2044 * @access public2298 * 2045 2299 * @return string The assembled headers 2046 2300 */ 2047 2301 public function createHeader() … … 2048 2302 { 2049 2303 $result = ''; 2050 2304 2051 $result .= $this->headerLine('Date', $this->MessageDate == ''? self::rfcDate() : $this->MessageDate);2305 $result .= $this->headerLine('Date', '' == $this->MessageDate ? self::rfcDate() : $this->MessageDate); 2052 2306 2053 2307 // To be created automatically by mail() 2054 2308 if ($this->SingleTo) { 2055 if ( $this->Mailer != 'mail') {2309 if ('mail' != $this->Mailer) { 2056 2310 foreach ($this->to as $toaddr) { 2057 2311 $this->SingleToArray[] = $this->addrFormat($toaddr); 2058 2312 } … … 2059 2313 } 2060 2314 } else { 2061 2315 if (count($this->to) > 0) { 2062 if ( $this->Mailer != 'mail') {2316 if ('mail' != $this->Mailer) { 2063 2317 $result .= $this->addrAppend('To', $this->to); 2064 2318 } 2065 2319 } elseif (count($this->cc) == 0) { … … 2067 2321 } 2068 2322 } 2069 2323 2070 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));2324 $result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]); 2071 2325 2072 2326 // sendmail and mail() extract Cc from the header before sending 2073 2327 if (count($this->cc) > 0) { … … 2076 2330 2077 2331 // sendmail and mail() extract Bcc from the header before sending 2078 2332 if (( 2079 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'2333 'sendmail' == $this->Mailer or 'qmail' == $this->Mailer or 'mail' == $this->Mailer 2080 2334 ) 2081 2335 and count($this->bcc) > 0 2082 2336 ) { … … 2088 2342 } 2089 2343 2090 2344 // mail() sets the subject itself 2091 if ( $this->Mailer != 'mail') {2345 if ('mail' != $this->Mailer) { 2092 2346 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); 2093 2347 } 2094 2348 … … 2100 2354 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); 2101 2355 } 2102 2356 $result .= $this->headerLine('Message-ID', $this->lastMessageID); 2103 if ( !is_null($this->Priority)) {2357 if (null !== $this->Priority) { 2104 2358 $result .= $this->headerLine('X-Priority', $this->Priority); 2105 2359 } 2106 if ( $this->XMailer == '') {2360 if ('' == $this->XMailer) { 2107 2361 $result .= $this->headerLine( 2108 2362 'X-Mailer', 2109 'PHPMailer ' . $this->Version. ' (https://github.com/PHPMailer/PHPMailer)'2363 'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)' 2110 2364 ); 2111 2365 } else { 2112 2366 $myXmailer = trim($this->XMailer); … … 2115 2369 } 2116 2370 } 2117 2371 2118 if ( $this->ConfirmReadingTo != '') {2372 if ('' != $this->ConfirmReadingTo) { 2119 2373 $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); 2120 2374 } 2121 2375 … … 2136 2390 2137 2391 /** 2138 2392 * Get the message MIME type headers. 2139 * @access public2393 * 2140 2394 * @return string 2141 2395 */ 2142 2396 public function getMailMIME() … … 2145 2399 $ismultipart = true; 2146 2400 switch ($this->message_type) { 2147 2401 case 'inline': 2148 $result .= $this->headerLine('Content-Type', 'multipart/related;');2402 $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); 2149 2403 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 2150 2404 break; 2151 2405 case 'attach': … … 2152 2406 case 'inline_attach': 2153 2407 case 'alt_attach': 2154 2408 case 'alt_inline_attach': 2155 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');2409 $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_MIXED . ';'); 2156 2410 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 2157 2411 break; 2158 2412 case 'alt': 2159 2413 case 'alt_inline': 2160 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');2414 $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); 2161 2415 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 2162 2416 break; 2163 2417 default: … … 2167 2421 break; 2168 2422 } 2169 2423 // RFC1341 part 5 says 7bit is assumed if not specified 2170 if ( $this->Encoding != '7bit') {2424 if (static::ENCODING_7BIT != $this->Encoding) { 2171 2425 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE 2172 2426 if ($ismultipart) { 2173 if ( $this->Encoding == '8bit') {2174 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');2427 if (static::ENCODING_8BIT == $this->Encoding) { 2428 $result .= $this->headerLine('Content-Transfer-Encoding', static::ENCODING_8BIT); 2175 2429 } 2176 2430 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible 2177 2431 } else { … … 2179 2433 } 2180 2434 } 2181 2435 2182 if ( $this->Mailer != 'mail') {2183 $result .= $this->LE;2436 if ('mail' != $this->Mailer) { 2437 $result .= static::$LE; 2184 2438 } 2185 2439 2186 2440 return $result; … … 2190 2444 * Returns the whole MIME message. 2191 2445 * Includes complete headers and body. 2192 2446 * Only valid post preSend(). 2447 * 2193 2448 * @see PHPMailer::preSend() 2194 * @access public2449 * 2195 2450 * @return string 2196 2451 */ 2197 2452 public function getSentMIMEMessage() 2198 2453 { 2199 return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . s elf::CRLF . self::CRLF. $this->MIMEBody;2454 return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . static::$LE . static::$LE . $this->MIMEBody; 2200 2455 } 2201 2456 2202 2457 /** 2203 * Create unique ID 2458 * Create a unique ID to use for boundaries. 2459 * 2204 2460 * @return string 2205 2461 */ 2206 protected function generateId() { 2207 return md5(uniqid(time())); 2462 protected function generateId() 2463 { 2464 $len = 32; //32 bytes = 256 bits 2465 if (function_exists('random_bytes')) { 2466 $bytes = random_bytes($len); 2467 } elseif (function_exists('openssl_random_pseudo_bytes')) { 2468 $bytes = openssl_random_pseudo_bytes($len); 2469 } else { 2470 //Use a hash to force the length to the same as the other methods 2471 $bytes = hash('sha256', uniqid((string) mt_rand(), true), true); 2472 } 2473 2474 //We don't care about messing up base64 format here, just want a random string 2475 return str_replace(['=', '+', '/'], '', base64_encode(hash('sha256', $bytes, true))); 2208 2476 } 2209 2477 2210 2478 /** 2211 2479 * Assemble the message body. 2212 2480 * Returns an empty string on failure. 2213 * @access public 2214 * @throws phpmailerException 2481 * 2482 * @throws Exception 2483 * 2215 2484 * @return string The assembled message body 2216 2485 */ 2217 2486 public function createBody() … … 2224 2493 $this->boundary[3] = 'b3_' . $this->uniqueid; 2225 2494 2226 2495 if ($this->sign_key_file) { 2227 $body .= $this->getMailMIME() . $this->LE;2496 $body .= $this->getMailMIME() . static::$LE; 2228 2497 } 2229 2498 2230 2499 $this->setWordWrap(); … … 2232 2501 $bodyEncoding = $this->Encoding; 2233 2502 $bodyCharSet = $this->CharSet; 2234 2503 //Can we do a 7-bit downgrade? 2235 if ( $bodyEncoding == '8bit'and !$this->has8bitChars($this->Body)) {2236 $bodyEncoding = '7bit';2504 if (static::ENCODING_8BIT == $bodyEncoding and !$this->has8bitChars($this->Body)) { 2505 $bodyEncoding = static::ENCODING_7BIT; 2237 2506 //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit 2238 2507 $bodyCharSet = 'us-ascii'; 2239 2508 } 2240 2509 //If lines are too long, and we're not already using an encoding that will shorten them, 2241 2510 //change to quoted-printable transfer encoding for the body part only 2242 if ( 'base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {2243 $bodyEncoding = 'quoted-printable';2511 if (static::ENCODING_BASE64 != $this->Encoding and static::hasLineLongerThanMax($this->Body)) { 2512 $bodyEncoding = static::ENCODING_QUOTED_PRINTABLE; 2244 2513 } 2245 2514 2246 2515 $altBodyEncoding = $this->Encoding; 2247 2516 $altBodyCharSet = $this->CharSet; 2248 2517 //Can we do a 7-bit downgrade? 2249 if ( $altBodyEncoding == '8bit'and !$this->has8bitChars($this->AltBody)) {2250 $altBodyEncoding = '7bit';2518 if (static::ENCODING_8BIT == $altBodyEncoding and !$this->has8bitChars($this->AltBody)) { 2519 $altBodyEncoding = static::ENCODING_7BIT; 2251 2520 //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit 2252 2521 $altBodyCharSet = 'us-ascii'; 2253 2522 } 2254 2523 //If lines are too long, and we're not already using an encoding that will shorten them, 2255 2524 //change to quoted-printable transfer encoding for the alt body part only 2256 if ( 'base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {2257 $altBodyEncoding = 'quoted-printable';2525 if (static::ENCODING_BASE64 != $altBodyEncoding and static::hasLineLongerThanMax($this->AltBody)) { 2526 $altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE; 2258 2527 } 2259 2528 //Use this as a preamble in all multipart message types 2260 $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;2529 $mimepre = 'This is a multi-part message in MIME format.' . static::$LE; 2261 2530 switch ($this->message_type) { 2262 2531 case 'inline': 2263 2532 $body .= $mimepre; 2264 2533 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); 2265 2534 $body .= $this->encodeString($this->Body, $bodyEncoding); 2266 $body .= $this->LE . $this->LE;2535 $body .= static::$LE; 2267 2536 $body .= $this->attachAll('inline', $this->boundary[1]); 2268 2537 break; 2269 2538 case 'attach': … … 2270 2539 $body .= $mimepre; 2271 2540 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); 2272 2541 $body .= $this->encodeString($this->Body, $bodyEncoding); 2273 $body .= $this->LE . $this->LE;2542 $body .= static::$LE; 2274 2543 $body .= $this->attachAll('attachment', $this->boundary[1]); 2275 2544 break; 2276 2545 case 'inline_attach': 2277 2546 $body .= $mimepre; 2278 2547 $body .= $this->textLine('--' . $this->boundary[1]); 2279 $body .= $this->headerLine('Content-Type', 'multipart/related;');2548 $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); 2280 2549 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 2281 $body .= $this->LE;2550 $body .= static::$LE; 2282 2551 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); 2283 2552 $body .= $this->encodeString($this->Body, $bodyEncoding); 2284 $body .= $this->LE . $this->LE;2553 $body .= static::$LE; 2285 2554 $body .= $this->attachAll('inline', $this->boundary[2]); 2286 $body .= $this->LE;2555 $body .= static::$LE; 2287 2556 $body .= $this->attachAll('attachment', $this->boundary[1]); 2288 2557 break; 2289 2558 case 'alt': 2290 2559 $body .= $mimepre; 2291 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);2560 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding); 2292 2561 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 2293 $body .= $this->LE . $this->LE;2294 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);2562 $body .= static::$LE; 2563 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding); 2295 2564 $body .= $this->encodeString($this->Body, $bodyEncoding); 2296 $body .= $this->LE . $this->LE;2565 $body .= static::$LE; 2297 2566 if (!empty($this->Ical)) { 2298 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');2567 $body .= $this->getBoundary($this->boundary[1], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=REQUEST', ''); 2299 2568 $body .= $this->encodeString($this->Ical, $this->Encoding); 2300 $body .= $this->LE . $this->LE;2569 $body .= static::$LE; 2301 2570 } 2302 2571 $body .= $this->endBoundary($this->boundary[1]); 2303 2572 break; 2304 2573 case 'alt_inline': 2305 2574 $body .= $mimepre; 2306 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);2575 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding); 2307 2576 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 2308 $body .= $this->LE . $this->LE;2577 $body .= static::$LE; 2309 2578 $body .= $this->textLine('--' . $this->boundary[1]); 2310 $body .= $this->headerLine('Content-Type', 'multipart/related;');2579 $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); 2311 2580 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 2312 $body .= $this->LE;2313 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);2581 $body .= static::$LE; 2582 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding); 2314 2583 $body .= $this->encodeString($this->Body, $bodyEncoding); 2315 $body .= $this->LE . $this->LE;2584 $body .= static::$LE; 2316 2585 $body .= $this->attachAll('inline', $this->boundary[2]); 2317 $body .= $this->LE;2586 $body .= static::$LE; 2318 2587 $body .= $this->endBoundary($this->boundary[1]); 2319 2588 break; 2320 2589 case 'alt_attach': 2321 2590 $body .= $mimepre; 2322 2591 $body .= $this->textLine('--' . $this->boundary[1]); 2323 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');2592 $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); 2324 2593 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 2325 $body .= $this->LE;2326 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);2594 $body .= static::$LE; 2595 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding); 2327 2596 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 2328 $body .= $this->LE . $this->LE;2329 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);2597 $body .= static::$LE; 2598 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding); 2330 2599 $body .= $this->encodeString($this->Body, $bodyEncoding); 2331 $body .= $this->LE . $this->LE; 2600 $body .= static::$LE; 2601 if (!empty($this->Ical)) { 2602 $body .= $this->getBoundary($this->boundary[2], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=REQUEST', ''); 2603 $body .= $this->encodeString($this->Ical, $this->Encoding); 2604 } 2332 2605 $body .= $this->endBoundary($this->boundary[2]); 2333 $body .= $this->LE;2606 $body .= static::$LE; 2334 2607 $body .= $this->attachAll('attachment', $this->boundary[1]); 2335 2608 break; 2336 2609 case 'alt_inline_attach': 2337 2610 $body .= $mimepre; 2338 2611 $body .= $this->textLine('--' . $this->boundary[1]); 2339 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');2612 $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); 2340 2613 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 2341 $body .= $this->LE;2342 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);2614 $body .= static::$LE; 2615 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding); 2343 2616 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 2344 $body .= $this->LE . $this->LE;2617 $body .= static::$LE; 2345 2618 $body .= $this->textLine('--' . $this->boundary[2]); 2346 $body .= $this->headerLine('Content-Type', 'multipart/related;');2619 $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); 2347 2620 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); 2348 $body .= $this->LE;2349 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);2621 $body .= static::$LE; 2622 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding); 2350 2623 $body .= $this->encodeString($this->Body, $bodyEncoding); 2351 $body .= $this->LE . $this->LE;2624 $body .= static::$LE; 2352 2625 $body .= $this->attachAll('inline', $this->boundary[3]); 2353 $body .= $this->LE;2626 $body .= static::$LE; 2354 2627 $body .= $this->endBoundary($this->boundary[2]); 2355 $body .= $this->LE;2628 $body .= static::$LE; 2356 2629 $body .= $this->attachAll('attachment', $this->boundary[1]); 2357 2630 break; 2358 2631 default: … … 2365 2638 2366 2639 if ($this->isError()) { 2367 2640 $body = ''; 2641 if ($this->exceptions) { 2642 throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); 2643 } 2368 2644 } elseif ($this->sign_key_file) { 2369 2645 try { 2370 2646 if (!defined('PKCS7_TEXT')) { 2371 throw new phpmailerException($this->lang('extension_missing') . 'openssl');2647 throw new Exception($this->lang('extension_missing') . 'openssl'); 2372 2648 } 2373 // @TODO would be nice to use php://temp streams here , but need to wrap for PHP < 5.12649 // @TODO would be nice to use php://temp streams here 2374 2650 $file = tempnam(sys_get_temp_dir(), 'mail'); 2375 2651 if (false === file_put_contents($file, $body)) { 2376 throw new phpmailerException($this->lang('signing') . ' Could not write temp file');2652 throw new Exception($this->lang('signing') . ' Could not write temp file'); 2377 2653 } 2378 2654 $signed = tempnam(sys_get_temp_dir(), 'signed'); 2379 2655 //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 … … 2382 2658 $file, 2383 2659 $signed, 2384 2660 'file://' . realpath($this->sign_cert_file), 2385 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),2386 null2661 ['file://' . realpath($this->sign_key_file), $this->sign_key_pass], 2662 [] 2387 2663 ); 2388 2664 } else { 2389 2665 $sign = @openssl_pkcs7_sign( … … 2390 2666 $file, 2391 2667 $signed, 2392 2668 'file://' . realpath($this->sign_cert_file), 2393 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),2394 null,2669 ['file://' . realpath($this->sign_key_file), $this->sign_key_pass], 2670 [], 2395 2671 PKCS7_DETACHED, 2396 2672 $this->sign_extracerts_file 2397 2673 ); 2398 2674 } 2675 @unlink($file); 2399 2676 if ($sign) { 2400 @unlink($file);2401 2677 $body = file_get_contents($signed); 2402 2678 @unlink($signed); 2403 2679 //The message returned by openssl contains both headers and body, so need to split them up 2404 2680 $parts = explode("\n\n", $body, 2); 2405 $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;2681 $this->MIMEHeader .= $parts[0] . static::$LE . static::$LE; 2406 2682 $body = $parts[1]; 2407 2683 } else { 2408 @unlink($file);2409 2684 @unlink($signed); 2410 throw new phpmailerException($this->lang('signing') . openssl_error_string());2685 throw new Exception($this->lang('signing') . openssl_error_string()); 2411 2686 } 2412 } catch ( phpmailerException $exc) {2687 } catch (Exception $exc) { 2413 2688 $body = ''; 2414 2689 if ($this->exceptions) { 2415 2690 throw $exc; … … 2416 2691 } 2417 2692 } 2418 2693 } 2694 2419 2695 return $body; 2420 2696 } 2421 2697 2422 2698 /** 2423 2699 * Return the start of a message boundary. 2424 * @access protected2700 * 2425 2701 * @param string $boundary 2426 2702 * @param string $charSet 2427 2703 * @param string $contentType 2428 2704 * @param string $encoding 2705 * 2429 2706 * @return string 2430 2707 */ 2431 2708 protected function getBoundary($boundary, $charSet, $contentType, $encoding) 2432 2709 { 2433 2710 $result = ''; 2434 if ( $charSet == '') {2711 if ('' == $charSet) { 2435 2712 $charSet = $this->CharSet; 2436 2713 } 2437 if ( $contentType == '') {2714 if ('' == $contentType) { 2438 2715 $contentType = $this->ContentType; 2439 2716 } 2440 if ( $encoding == '') {2717 if ('' == $encoding) { 2441 2718 $encoding = $this->Encoding; 2442 2719 } 2443 2720 $result .= $this->textLine('--' . $boundary); 2444 2721 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); 2445 $result .= $this->LE;2722 $result .= static::$LE; 2446 2723 // RFC1341 part 5 says 7bit is assumed if not specified 2447 if ( $encoding != '7bit') {2724 if (static::ENCODING_7BIT != $encoding) { 2448 2725 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); 2449 2726 } 2450 $result .= $this->LE;2727 $result .= static::$LE; 2451 2728 2452 2729 return $result; 2453 2730 } … … 2454 2731 2455 2732 /** 2456 2733 * Return the end of a message boundary. 2457 * @access protected2734 * 2458 2735 * @param string $boundary 2736 * 2459 2737 * @return string 2460 2738 */ 2461 2739 protected function endBoundary($boundary) 2462 2740 { 2463 return $this->LE . '--' . $boundary . '--' . $this->LE;2741 return static::$LE . '--' . $boundary . '--' . static::$LE; 2464 2742 } 2465 2743 2466 2744 /** 2467 2745 * Set the message type. 2468 2746 * PHPMailer only supports some preset message types, not arbitrary MIME structures. 2469 * @access protected2470 * @return void2471 2747 */ 2472 2748 protected function setMessageType() 2473 2749 { 2474 $type = array();2750 $type = []; 2475 2751 if ($this->alternativeExists()) { 2476 2752 $type[] = 'alt'; 2477 2753 } … … 2482 2758 $type[] = 'attach'; 2483 2759 } 2484 2760 $this->message_type = implode('_', $type); 2485 if ( $this->message_type == '') {2761 if ('' == $this->message_type) { 2486 2762 //The 'plain' message_type refers to the message having a single body element, not that it is plain-text 2487 2763 $this->message_type = 'plain'; 2488 2764 } … … 2490 2766 2491 2767 /** 2492 2768 * Format a header line. 2493 * @access public 2494 * @param string $name 2495 * @param string $value 2769 * 2770 * @param string $name 2771 * @param string|int $value 2772 * 2496 2773 * @return string 2497 2774 */ 2498 2775 public function headerLine($name, $value) 2499 2776 { 2500 return $name . ': ' . $value . $this->LE;2777 return $name . ': ' . $value . static::$LE; 2501 2778 } 2502 2779 2503 2780 /** 2504 2781 * Return a formatted mail line. 2505 * @access public2782 * 2506 2783 * @param string $value 2784 * 2507 2785 * @return string 2508 2786 */ 2509 2787 public function textLine($value) 2510 2788 { 2511 return $value . $this->LE;2789 return $value . static::$LE; 2512 2790 } 2513 2791 2514 2792 /** … … 2517 2795 * Returns false if the file could not be found or read. 2518 2796 * Explicitly *does not* support passing URLs; PHPMailer is not an HTTP client. 2519 2797 * If you need to do that, fetch the resource yourself and pass it in via a local file or string. 2520 * @param string $path Path to the attachment. 2521 * @param string $name Overrides the attachment name. 2522 * @param string $encoding File encoding (see $Encoding). 2523 * @param string $type File extension (MIME) type. 2798 * 2799 * @param string $path Path to the attachment 2800 * @param string $name Overrides the attachment name 2801 * @param string $encoding File encoding (see $Encoding) 2802 * @param string $type File extension (MIME) type 2524 2803 * @param string $disposition Disposition to use 2525 * @throws phpmailerException 2526 * @return boolean 2804 * 2805 * @throws Exception 2806 * 2807 * @return bool 2527 2808 */ 2528 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')2809 public function addAttachment($path, $name = '', $encoding = self::ENCODING_BASE64, $type = '', $disposition = 'attachment') 2529 2810 { 2530 2811 try { 2531 if (!s elf::isPermittedPath($path) or!@is_file($path)) {2532 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);2812 if (!static::isPermittedPath($path) || !@is_file($path)) { 2813 throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE); 2533 2814 } 2534 2815 2535 2816 // If a MIME type is not specified, try to work it out from the file name 2536 if ( $type == '') {2537 $type = s elf::filenameToType($path);2817 if ('' == $type) { 2818 $type = static::filenameToType($path); 2538 2819 } 2539 2820 2540 2821 $filename = basename($path); 2541 if ( $name == '') {2822 if ('' == $name) { 2542 2823 $name = $filename; 2543 2824 } 2544 2825 2545 $this->attachment[] = array(2826 $this->attachment[] = [ 2546 2827 0 => $path, 2547 2828 1 => $filename, 2548 2829 2 => $name, … … 2550 2831 4 => $type, 2551 2832 5 => false, // isStringAttachment 2552 2833 6 => $disposition, 2553 7 => 0 2554 ); 2555 2556 } catch (phpmailerException $exc) { 2834 7 => $name, 2835 ]; 2836 } catch (Exception $exc) { 2557 2837 $this->setError($exc->getMessage()); 2558 2838 $this->edebug($exc->getMessage()); 2559 2839 if ($this->exceptions) { 2560 2840 throw $exc; 2561 2841 } 2842 2562 2843 return false; 2563 2844 } 2845 2564 2846 return true; 2565 2847 } 2566 2848 2567 2849 /** 2568 2850 * Return the array of attachments. 2851 * 2569 2852 * @return array 2570 2853 */ 2571 2854 public function getAttachments() … … 2576 2859 /** 2577 2860 * Attach all file, string, and binary attachments to the message. 2578 2861 * Returns an empty string on failure. 2579 * @access protected2862 * 2580 2863 * @param string $disposition_type 2581 2864 * @param string $boundary 2865 * 2582 2866 * @return string 2583 2867 */ 2584 2868 protected function attachAll($disposition_type, $boundary) 2585 2869 { 2586 2870 // Return text of body 2587 $mime = array();2588 $cidUniq = array();2589 $incl = array();2871 $mime = []; 2872 $cidUniq = []; 2873 $incl = []; 2590 2874 2591 2875 // Add all attachments 2592 2876 foreach ($this->attachment as $attachment) { … … 2602 2886 $path = $attachment[0]; 2603 2887 } 2604 2888 2605 $inclhash = md5(serialize($attachment));2889 $inclhash = hash('sha256', serialize($attachment)); 2606 2890 if (in_array($inclhash, $incl)) { 2607 2891 continue; 2608 2892 } … … 2612 2896 $type = $attachment[4]; 2613 2897 $disposition = $attachment[6]; 2614 2898 $cid = $attachment[7]; 2615 if ( $disposition == 'inline' &&array_key_exists($cid, $cidUniq)) {2899 if ('inline' == $disposition and array_key_exists($cid, $cidUniq)) { 2616 2900 continue; 2617 2901 } 2618 2902 $cidUniq[$cid] = true; 2619 2903 2620 $mime[] = sprintf('--%s%s', $boundary, $this->LE);2904 $mime[] = sprintf('--%s%s', $boundary, static::$LE); 2621 2905 //Only include a filename property if we have one 2622 2906 if (!empty($name)) { 2623 2907 $mime[] = sprintf( … … 2624 2908 'Content-Type: %s; name="%s"%s', 2625 2909 $type, 2626 2910 $this->encodeHeader($this->secureHeader($name)), 2627 $this->LE2911 static::$LE 2628 2912 ); 2629 2913 } else { 2630 2914 $mime[] = sprintf( 2631 2915 'Content-Type: %s%s', 2632 2916 $type, 2633 $this->LE2917 static::$LE 2634 2918 ); 2635 2919 } 2636 2920 // RFC1341 part 5 says 7bit is assumed if not specified 2637 if ( $encoding != '7bit') {2638 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);2921 if (static::ENCODING_7BIT != $encoding) { 2922 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, static::$LE); 2639 2923 } 2640 2924 2641 if ( $disposition == 'inline') {2642 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);2925 if (!empty($cid)) { 2926 $mime[] = sprintf('Content-ID: <%s>%s', $cid, static::$LE); 2643 2927 } 2644 2928 2645 2929 // If a filename contains any of these chars, it should be quoted, … … 2653 2937 'Content-Disposition: %s; filename="%s"%s', 2654 2938 $disposition, 2655 2939 $encoded_name, 2656 $this->LE . $this->LE2940 static::$LE . static::$LE 2657 2941 ); 2658 2942 } else { 2659 2943 if (!empty($encoded_name)) { … … 2661 2945 'Content-Disposition: %s; filename=%s%s', 2662 2946 $disposition, 2663 2947 $encoded_name, 2664 $this->LE . $this->LE2948 static::$LE . static::$LE 2665 2949 ); 2666 2950 } else { 2667 2951 $mime[] = sprintf( 2668 2952 'Content-Disposition: %s%s', 2669 2953 $disposition, 2670 $this->LE . $this->LE2954 static::$LE . static::$LE 2671 2955 ); 2672 2956 } 2673 2957 } 2674 2958 } else { 2675 $mime[] = $this->LE;2959 $mime[] = static::$LE; 2676 2960 } 2677 2961 2678 2962 // Encode as string attachment 2679 2963 if ($bString) { 2680 2964 $mime[] = $this->encodeString($string, $encoding); 2681 if ($this->isError()) {2682 return '';2683 }2684 $mime[] = $this->LE . $this->LE;2685 2965 } else { 2686 2966 $mime[] = $this->encodeFile($path, $encoding); 2687 if ($this->isError()) {2688 return '';2689 }2690 $mime[] = $this->LE . $this->LE;2691 2967 } 2968 if ($this->isError()) { 2969 return ''; 2970 } 2971 $mime[] = static::$LE; 2692 2972 } 2693 2973 } 2694 2974 2695 $mime[] = sprintf('--%s--%s', $boundary, $this->LE);2975 $mime[] = sprintf('--%s--%s', $boundary, static::$LE); 2696 2976 2697 2977 return implode('', $mime); 2698 2978 } … … 2700 2980 /** 2701 2981 * Encode a file attachment in requested format. 2702 2982 * Returns an empty string on failure. 2703 * @param string $path The full path to the file 2983 * 2984 * @param string $path The full path to the file 2704 2985 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 2705 * @throws phpmailerException 2706 * @access protected 2986 * 2987 * @throws Exception 2988 * 2707 2989 * @return string 2708 2990 */ 2709 protected function encodeFile($path, $encoding = 'base64')2991 protected function encodeFile($path, $encoding = self::ENCODING_BASE64) 2710 2992 { 2711 2993 try { 2712 if (!s elf::isPermittedPath($path) or!file_exists($path)) {2713 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);2994 if (!static::isPermittedPath($path) || !file_exists($path)) { 2995 throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); 2714 2996 } 2715 $magic_quotes = get_magic_quotes_runtime(); 2716 if ($magic_quotes) { 2717 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 2718 set_magic_quotes_runtime(false); 2719 } else { 2720 //Doesn't exist in PHP 5.4, but we don't need to check because 2721 //get_magic_quotes_runtime always returns false in 5.4+ 2722 //so it will never get here 2723 ini_set('magic_quotes_runtime', false); 2724 } 2997 $file_buffer = file_get_contents($path); 2998 if (false === $file_buffer) { 2999 throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); 2725 3000 } 2726 $file_buffer = file_get_contents($path);2727 3001 $file_buffer = $this->encodeString($file_buffer, $encoding); 2728 if ($magic_quotes) { 2729 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 2730 set_magic_quotes_runtime($magic_quotes); 2731 } else { 2732 ini_set('magic_quotes_runtime', $magic_quotes); 2733 } 2734 } 3002 2735 3003 return $file_buffer; 2736 3004 } catch (Exception $exc) { 2737 3005 $this->setError($exc->getMessage()); 3006 2738 3007 return ''; 2739 3008 } 2740 3009 } … … 2742 3011 /** 2743 3012 * Encode a string in requested format. 2744 3013 * Returns an empty string on failure. 2745 * @param string $str The text to encode 3014 * 3015 * @param string $str The text to encode 2746 3016 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 2747 * @access public3017 * 2748 3018 * @return string 2749 3019 */ 2750 public function encodeString($str, $encoding = 'base64')3020 public function encodeString($str, $encoding = self::ENCODING_BASE64) 2751 3021 { 2752 3022 $encoded = ''; 2753 3023 switch (strtolower($encoding)) { 2754 case 'base64': 2755 $encoded = chunk_split(base64_encode($str), 76, $this->LE); 3024 case static::ENCODING_BASE64: 3025 $encoded = chunk_split( 3026 base64_encode($str), 3027 static::STD_LINE_LENGTH, 3028 static::$LE 3029 ); 2756 3030 break; 2757 case '7bit':2758 case '8bit':2759 $encoded = $this->fixEOL($str);3031 case static::ENCODING_7BIT: 3032 case static::ENCODING_8BIT: 3033 $encoded = static::normalizeBreaks($str); 2760 3034 // Make sure it ends with a line break 2761 if (substr($encoded, -(strlen( $this->LE))) != $this->LE) {2762 $encoded .= $this->LE;3035 if (substr($encoded, -(strlen(static::$LE))) != static::$LE) { 3036 $encoded .= static::$LE; 2763 3037 } 2764 3038 break; 2765 case 'binary':3039 case static::ENCODING_BINARY: 2766 3040 $encoded = $str; 2767 3041 break; 2768 case 'quoted-printable':3042 case static::ENCODING_QUOTED_PRINTABLE: 2769 3043 $encoded = $this->encodeQP($str); 2770 3044 break; 2771 3045 default: … … 2772 3046 $this->setError($this->lang('encoding') . $encoding); 2773 3047 break; 2774 3048 } 3049 2775 3050 return $encoded; 2776 3051 } 2777 3052 2778 3053 /** 2779 * Encode a header string optimally. 2780 * Picks shortest of Q, B, quoted-printable or none. 2781 * @access public 2782 * @param string $str 2783 * @param string $position 3054 * Encode a header value (not including its label) optimally. 3055 * Picks shortest of Q, B, or none. Result includes folding if needed. 3056 * See RFC822 definitions for phrase, comment and text positions. 3057 * 3058 * @param string $str The header value to encode 3059 * @param string $position What context the string will be used in 3060 * 2784 3061 * @return string 2785 3062 */ 2786 3063 public function encodeHeader($str, $position = 'text') … … 2791 3068 if (!preg_match('/[\200-\377]/', $str)) { 2792 3069 // Can't use addslashes as we don't know the value of magic_quotes_sybase 2793 3070 $encoded = addcslashes($str, "\0..\37\177\\\""); 2794 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { 2795 return ($encoded); 2796 } else { 2797 return ("\"$encoded\""); 3071 if (($str == $encoded) and !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { 3072 return $encoded; 2798 3073 } 3074 3075 return "\"$encoded\""; 2799 3076 } 2800 3077 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); 2801 3078 break; 2802 /* *@noinspection PhpMissingBreakStatementInspection */3079 /* @noinspection PhpMissingBreakStatementInspection */ 2803 3080 case 'comment': 2804 3081 $matchcount = preg_match_all('/[()"]/', $str, $matches); 2805 // Intentional fall-through3082 //fallthrough 2806 3083 case 'text': 2807 3084 default: 2808 3085 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); … … 2809 3086 break; 2810 3087 } 2811 3088 2812 //There are no chars that need encoding 2813 if ($matchcount == 0) { 2814 return ($str); 2815 } 2816 2817 $maxlen = 75 - 7 - strlen($this->CharSet); 3089 //RFCs specify a maximum line length of 78 chars, however mail() will sometimes 3090 //corrupt messages with headers longer than 65 chars. See #818 3091 $lengthsub = 'mail' == $this->Mailer ? 13 : 0; 3092 $maxlen = static::STD_LINE_LENGTH - $lengthsub; 2818 3093 // Try to select the encoding which should produce the shortest output 2819 3094 if ($matchcount > strlen($str) / 3) { 2820 3095 // More than a third of the content will need encoding, so B encoding will be most efficient 2821 3096 $encoding = 'B'; 2822 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { 3097 //This calculation is: 3098 // max line length 3099 // - shorten to avoid mail() corruption 3100 // - Q/B encoding char overhead ("` =?<charset>?[QB]?<content>?=`") 3101 // - charset name length 3102 $maxlen = static::STD_LINE_LENGTH - $lengthsub - 8 - strlen($this->CharSet); 3103 if ($this->hasMultiBytes($str)) { 2823 3104 // Use a custom function which correctly encodes and wraps long 2824 3105 // multibyte strings without breaking lines within a character 2825 3106 $encoded = $this->base64EncodeWrapMB($str, "\n"); … … 2828 3109 $maxlen -= $maxlen % 4; 2829 3110 $encoded = trim(chunk_split($encoded, $maxlen, "\n")); 2830 3111 } 2831 } else { 3112 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); 3113 } elseif ($matchcount > 0) { 3114 //1 or more chars need encoding, use Q-encode 2832 3115 $encoding = 'Q'; 3116 //Recalc max line length for Q encoding - see comments on B encode 3117 $maxlen = static::STD_LINE_LENGTH - $lengthsub - 8 - strlen($this->CharSet); 2833 3118 $encoded = $this->encodeQ($str, $position); 2834 3119 $encoded = $this->wrapText($encoded, $maxlen, true); 2835 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); 3120 $encoded = str_replace('=' . static::$LE, "\n", trim($encoded)); 3121 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); 3122 } elseif (strlen($str) > $maxlen) { 3123 //No chars need encoding, but line is too long, so fold it 3124 $encoded = trim($this->wrapText($str, $maxlen, false)); 3125 if ($str == $encoded) { 3126 //Wrapping nicely didn't work, wrap hard instead 3127 $encoded = trim(chunk_split($str, static::STD_LINE_LENGTH, static::$LE)); 3128 } 3129 $encoded = str_replace(static::$LE, "\n", trim($encoded)); 3130 $encoded = preg_replace('/^(.*)$/m', ' \\1', $encoded); 3131 } else { 3132 //No reformatting needed 3133 return $str; 2836 3134 } 2837 3135 2838 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); 2839 $encoded = trim(str_replace("\n", $this->LE, $encoded)); 2840 2841 return $encoded; 3136 return trim(static::normalizeBreaks($encoded)); 2842 3137 } 2843 3138 2844 3139 /** 2845 3140 * Check if a string contains multi-byte characters. 2846 * @access public3141 * 2847 3142 * @param string $str multi-byte text to wrap encode 2848 * @return boolean 3143 * 3144 * @return bool 2849 3145 */ 2850 3146 public function hasMultiBytes($str) 2851 3147 { 2852 3148 if (function_exists('mb_strlen')) { 2853 return (strlen($str) > mb_strlen($str, $this->CharSet)); 2854 } else { // Assume no multibytes (we can't handle without mbstring functions anyway) 2855 return false; 3149 return strlen($str) > mb_strlen($str, $this->CharSet); 2856 3150 } 3151 3152 // Assume no multibytes (we can't handle without mbstring functions anyway) 3153 return false; 2857 3154 } 2858 3155 2859 3156 /** 2860 3157 * Does a string contain any 8-bit chars (in any charset)? 3158 * 2861 3159 * @param string $text 2862 * @return boolean 3160 * 3161 * @return bool 2863 3162 */ 2864 3163 public function has8bitChars($text) 2865 3164 { 2866 return (bool ean)preg_match('/[\x80-\xFF]/', $text);3165 return (bool) preg_match('/[\x80-\xFF]/', $text); 2867 3166 } 2868 3167 2869 3168 /** 2870 3169 * Encode and wrap long multibyte strings for mail headers 2871 3170 * without breaking lines within a character. 2872 * Adapted from a function by paravoid 2873 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 2874 * @access public 2875 * @param string $str multi-byte text to wrap encode 3171 * Adapted from a function by paravoid. 3172 * 3173 * @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 3174 * 3175 * @param string $str multi-byte text to wrap encode 2876 3176 * @param string $linebreak string to use as linefeed/end-of-line 3177 * 2877 3178 * @return string 2878 3179 */ 2879 3180 public function base64EncodeWrapMB($str, $linebreak = null) … … 2881 3182 $start = '=?' . $this->CharSet . '?B?'; 2882 3183 $end = '?='; 2883 3184 $encoded = ''; 2884 if ( $linebreak === null) {2885 $linebreak = $this->LE;3185 if (null === $linebreak) { 3186 $linebreak = static::$LE; 2886 3187 } 2887 3188 2888 3189 $mb_length = mb_strlen($str, $this->CharSet); … … 2899 3200 $offset = $avgLength - $lookBack; 2900 3201 $chunk = mb_substr($str, $i, $offset, $this->CharSet); 2901 3202 $chunk = base64_encode($chunk); 2902 $lookBack++;3203 ++$lookBack; 2903 3204 } while (strlen($chunk) > $length); 2904 3205 $encoded .= $chunk . $linebreak; 2905 3206 } 2906 3207 2907 3208 // Chomp the last linefeed 2908 $encoded = substr($encoded, 0, -strlen($linebreak)); 2909 return $encoded; 3209 return substr($encoded, 0, -strlen($linebreak)); 2910 3210 } 2911 3211 2912 3212 /** 2913 3213 * Encode a string in quoted-printable format. 2914 3214 * According to RFC2045 section 6.7. 2915 * @access public3215 * 2916 3216 * @param string $string The text to encode 2917 * @param integer $line_max Number of chars allowed on a line before wrapping3217 * 2918 3218 * @return string 2919 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment2920 3219 */ 2921 public function encodeQP($string , $line_max = 76)3220 public function encodeQP($string) 2922 3221 { 2923 // Use native function if it's available (>= PHP5.3) 2924 if (function_exists('quoted_printable_encode')) { 2925 return quoted_printable_encode($string); 2926 } 2927 // Fall back to a pure PHP implementation 2928 $string = str_replace( 2929 array('%20', '%0D%0A.', '%0D%0A', '%'), 2930 array(' ', "\r\n=2E", "\r\n", '='), 2931 rawurlencode($string) 2932 ); 2933 return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); 3222 return static::normalizeBreaks(quoted_printable_encode($string)); 2934 3223 } 2935 3224 2936 3225 /** 2937 * Backward compatibility wrapper for an old QP encoding function that was removed.2938 * @see PHPMailer::encodeQP()2939 * @access public2940 * @param string $string2941 * @param integer $line_max2942 * @param boolean $space_conv2943 * @return string2944 * @deprecated Use encodeQP instead.2945 */2946 public function encodeQPphp(2947 $string,2948 $line_max = 76,2949 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false2950 ) {2951 return $this->encodeQP($string, $line_max);2952 }2953 2954 /**2955 3226 * Encode a string using Q encoding. 2956 * @link http://tools.ietf.org/html/rfc2047 2957 * @param string $str the text to encode 3227 * 3228 * @see http://tools.ietf.org/html/rfc2047#section-4.2 3229 * 3230 * @param string $str the text to encode 2958 3231 * @param string $position Where the text is going to be used, see the RFC for what that means 2959 * @access public3232 * 2960 3233 * @return string 2961 3234 */ 2962 3235 public function encodeQ($str, $position = 'text') … … 2963 3236 { 2964 3237 // There should not be any EOL in the string 2965 3238 $pattern = ''; 2966 $encoded = str_replace( array("\r", "\n"), '', $str);3239 $encoded = str_replace(["\r", "\n"], '', $str); 2967 3240 switch (strtolower($position)) { 2968 3241 case 'phrase': 2969 3242 // RFC 2047 section 5.3 2970 3243 $pattern = '^A-Za-z0-9!*+\/ -'; 2971 3244 break; 2972 /** @noinspection PhpMissingBreakStatementInspection */ 3245 /* 3246 * RFC 2047 section 5.2. 3247 * Build $pattern without including delimiters and [] 3248 */ 3249 /* @noinspection PhpMissingBreakStatementInspection */ 2973 3250 case 'comment': 2974 // RFC 2047 section 5.22975 3251 $pattern = '\(\)"'; 2976 // intentional fall-through 2977 // for this reason we build the $pattern without including delimiters and [] 3252 /* Intentional fall through */ 2978 3253 case 'text': 2979 3254 default: 2980 3255 // RFC 2047 section 5.1 2981 3256 // Replace every high ascii, control, =, ? and _ characters 3257 /** @noinspection SuspiciousAssignmentsInspection */ 2982 3258 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; 2983 3259 break; 2984 3260 } 2985 $matches = array();3261 $matches = []; 2986 3262 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { 2987 3263 // If the string contains an '=', make sure it's the first thing we replace 2988 3264 // so as to avoid double-encoding … … 2995 3271 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); 2996 3272 } 2997 3273 } 2998 // Replace every spaces to _ (more readable than =20) 3274 // Replace spaces with _ (more readable than =20) 3275 // RFC 2047 section 4.2(2) 2999 3276 return str_replace(' ', '_', $encoded); 3000 3277 } 3001 3278 … … 3003 3280 * Add a string or binary attachment (non-filesystem). 3004 3281 * This method can be used to attach ascii or binary data, 3005 3282 * such as a BLOB record from a database. 3006 * @param string $string String attachment data. 3007 * @param string $filename Name of the attachment. 3008 * @param string $encoding File encoding (see $Encoding). 3009 * @param string $type File extension (MIME) type. 3283 * 3284 * @param string $string String attachment data 3285 * @param string $filename Name of the attachment 3286 * @param string $encoding File encoding (see $Encoding) 3287 * @param string $type File extension (MIME) type 3010 3288 * @param string $disposition Disposition to use 3011 * @return void3012 3289 */ 3013 3290 public function addStringAttachment( 3014 3291 $string, 3015 3292 $filename, 3016 $encoding = 'base64',3293 $encoding = self::ENCODING_BASE64, 3017 3294 $type = '', 3018 3295 $disposition = 'attachment' 3019 3296 ) { 3020 3297 // If a MIME type is not specified, try to work it out from the file name 3021 if ( $type == '') {3022 $type = s elf::filenameToType($filename);3298 if ('' == $type) { 3299 $type = static::filenameToType($filename); 3023 3300 } 3024 3301 // Append to $attachment array 3025 $this->attachment[] = array(3302 $this->attachment[] = [ 3026 3303 0 => $string, 3027 3304 1 => $filename, 3028 3305 2 => basename($filename), … … 3030 3307 4 => $type, 3031 3308 5 => true, // isStringAttachment 3032 3309 6 => $disposition, 3033 7 => 0 3034 );3310 7 => 0, 3311 ]; 3035 3312 } 3036 3313 3037 3314 /** … … 3042 3319 * This is used in HTML messages that embed the images 3043 3320 * the HTML refers to using the $cid value. 3044 3321 * Never use a user-supplied path to a file! 3045 * @param string $path Path to the attachment. 3046 * @param string $cid Content ID of the attachment; Use this to reference 3047 * the content when using an embedded image in HTML. 3048 * @param string $name Overrides the attachment name. 3049 * @param string $encoding File encoding (see $Encoding). 3050 * @param string $type File MIME type. 3322 * 3323 * @param string $path Path to the attachment 3324 * @param string $cid Content ID of the attachment; Use this to reference 3325 * the content when using an embedded image in HTML 3326 * @param string $name Overrides the attachment name 3327 * @param string $encoding File encoding (see $Encoding) 3328 * @param string $type File MIME type 3051 3329 * @param string $disposition Disposition to use 3052 * @return boolean True on successfully adding an attachment 3330 * 3331 * @return bool True on successfully adding an attachment 3053 3332 */ 3054 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')3333 public function addEmbeddedImage($path, $cid, $name = '', $encoding = self::ENCODING_BASE64, $type = '', $disposition = 'inline') 3055 3334 { 3056 if (!s elf::isPermittedPath($path) or!@is_file($path)) {3335 if (!static::isPermittedPath($path) || !@is_file($path)) { 3057 3336 $this->setError($this->lang('file_access') . $path); 3337 3058 3338 return false; 3059 3339 } 3060 3340 3061 3341 // If a MIME type is not specified, try to work it out from the file name 3062 if ( $type == '') {3063 $type = s elf::filenameToType($path);3342 if ('' == $type) { 3343 $type = static::filenameToType($path); 3064 3344 } 3065 3345 3066 3346 $filename = basename($path); 3067 if ( $name == '') {3347 if ('' == $name) { 3068 3348 $name = $filename; 3069 3349 } 3070 3350 3071 3351 // Append to $attachment array 3072 $this->attachment[] = array(3352 $this->attachment[] = [ 3073 3353 0 => $path, 3074 3354 1 => $filename, 3075 3355 2 => $name, … … 3077 3357 4 => $type, 3078 3358 5 => false, // isStringAttachment 3079 3359 6 => $disposition, 3080 7 => $cid 3081 ); 3360 7 => $cid, 3361 ]; 3362 3082 3363 return true; 3083 3364 } 3084 3365 … … 3085 3366 /** 3086 3367 * Add an embedded stringified attachment. 3087 3368 * This can include images, sounds, and just about any other document type. 3088 * Be sure to set the $type to an image type for images: 3089 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. 3090 * @param string $string The attachment binary data. 3091 * @param string $cid Content ID of the attachment; Use this to reference 3092 * the content when using an embedded image in HTML. 3093 * @param string $name 3094 * @param string $encoding File encoding (see $Encoding). 3095 * @param string $type MIME type. 3369 * If your filename doesn't contain an extension, be sure to set the $type to an appropriate MIME type. 3370 * 3371 * @param string $string The attachment binary data 3372 * @param string $cid Content ID of the attachment; Use this to reference 3373 * the content when using an embedded image in HTML 3374 * @param string $name A filename for the attachment. If this contains an extension, 3375 * PHPMailer will attempt to set a MIME type for the attachment. 3376 * For example 'file.jpg' would get an 'image/jpeg' MIME type. 3377 * @param string $encoding File encoding (see $Encoding), defaults to 'base64' 3378 * @param string $type MIME type - will be used in preference to any automatically derived type 3096 3379 * @param string $disposition Disposition to use 3097 * @return boolean True on successfully adding an attachment 3380 * 3381 * @return bool True on successfully adding an attachment 3098 3382 */ 3099 3383 public function addStringEmbeddedImage( 3100 3384 $string, 3101 3385 $cid, 3102 3386 $name = '', 3103 $encoding = 'base64',3387 $encoding = self::ENCODING_BASE64, 3104 3388 $type = '', 3105 3389 $disposition = 'inline' 3106 3390 ) { 3107 3391 // If a MIME type is not specified, try to work it out from the name 3108 if ( $type == ''and !empty($name)) {3109 $type = s elf::filenameToType($name);3392 if ('' == $type and !empty($name)) { 3393 $type = static::filenameToType($name); 3110 3394 } 3111 3395 3112 3396 // Append to $attachment array 3113 $this->attachment[] = array(3397 $this->attachment[] = [ 3114 3398 0 => $string, 3115 3399 1 => $name, 3116 3400 2 => $name, … … 3118 3402 4 => $type, 3119 3403 5 => true, // isStringAttachment 3120 3404 6 => $disposition, 3121 7 => $cid 3122 ); 3405 7 => $cid, 3406 ]; 3407 3123 3408 return true; 3124 3409 } 3125 3410 3126 3411 /** 3412 * Check if an embedded attachment is present with this cid. 3413 * 3414 * @param string $cid 3415 * 3416 * @return bool 3417 */ 3418 protected function cidExists($cid) 3419 { 3420 foreach ($this->attachment as $attachment) { 3421 if ('inline' == $attachment[6] and $cid == $attachment[7]) { 3422 return true; 3423 } 3424 } 3425 3426 return false; 3427 } 3428 3429 /** 3127 3430 * Check if an inline attachment is present. 3128 * @access public3129 * @return bool ean3431 * 3432 * @return bool 3130 3433 */ 3131 3434 public function inlineImageExists() 3132 3435 { 3133 3436 foreach ($this->attachment as $attachment) { 3134 if ( $attachment[6] == 'inline') {3437 if ('inline' == $attachment[6]) { 3135 3438 return true; 3136 3439 } 3137 3440 } 3441 3138 3442 return false; 3139 3443 } 3140 3444 3141 3445 /** 3142 3446 * Check if an attachment (non-inline) is present. 3143 * @return boolean 3447 * 3448 * @return bool 3144 3449 */ 3145 3450 public function attachmentExists() 3146 3451 { 3147 3452 foreach ($this->attachment as $attachment) { 3148 if ( $attachment[6] == 'attachment') {3453 if ('attachment' == $attachment[6]) { 3149 3454 return true; 3150 3455 } 3151 3456 } 3457 3152 3458 return false; 3153 3459 } 3154 3460 3155 3461 /** 3156 3462 * Check if this message has an alternative body set. 3157 * @return boolean 3463 * 3464 * @return bool 3158 3465 */ 3159 3466 public function alternativeExists() 3160 3467 { … … 3163 3470 3164 3471 /** 3165 3472 * Clear queued addresses of given kind. 3166 * @access protected3473 * 3167 3474 * @param string $kind 'to', 'cc', or 'bcc' 3168 * @return void3169 3475 */ 3170 3476 public function clearQueuedAddresses($kind) 3171 3477 { 3172 $ RecipientsQueue = $this->RecipientsQueue;3173 foreach ($RecipientsQueue as $address => $params) {3174 if ($params[0] ==$kind) {3175 unset($this->RecipientsQueue[$address]);3478 $this->RecipientsQueue = array_filter( 3479 $this->RecipientsQueue, 3480 function ($params) use ($kind) { 3481 return $params[0] != $kind; 3176 3482 } 3177 }3483 ); 3178 3484 } 3179 3485 3180 3486 /** 3181 3487 * Clear all To recipients. 3182 * @return void3183 3488 */ 3184 3489 public function clearAddresses() 3185 3490 { … … 3186 3491 foreach ($this->to as $to) { 3187 3492 unset($this->all_recipients[strtolower($to[0])]); 3188 3493 } 3189 $this->to = array();3494 $this->to = []; 3190 3495 $this->clearQueuedAddresses('to'); 3191 3496 } 3192 3497 3193 3498 /** 3194 3499 * Clear all CC recipients. 3195 * @return void3196 3500 */ 3197 3501 public function clearCCs() 3198 3502 { … … 3199 3503 foreach ($this->cc as $cc) { 3200 3504 unset($this->all_recipients[strtolower($cc[0])]); 3201 3505 } 3202 $this->cc = array();3506 $this->cc = []; 3203 3507 $this->clearQueuedAddresses('cc'); 3204 3508 } 3205 3509 3206 3510 /** 3207 3511 * Clear all BCC recipients. 3208 * @return void3209 3512 */ 3210 3513 public function clearBCCs() 3211 3514 { … … 3212 3515 foreach ($this->bcc as $bcc) { 3213 3516 unset($this->all_recipients[strtolower($bcc[0])]); 3214 3517 } 3215 $this->bcc = array();3518 $this->bcc = []; 3216 3519 $this->clearQueuedAddresses('bcc'); 3217 3520 } 3218 3521 3219 3522 /** 3220 3523 * Clear all ReplyTo recipients. 3221 * @return void3222 3524 */ 3223 3525 public function clearReplyTos() 3224 3526 { 3225 $this->ReplyTo = array();3226 $this->ReplyToQueue = array();3527 $this->ReplyTo = []; 3528 $this->ReplyToQueue = []; 3227 3529 } 3228 3530 3229 3531 /** 3230 3532 * Clear all recipient types. 3231 * @return void3232 3533 */ 3233 3534 public function clearAllRecipients() 3234 3535 { 3235 $this->to = array();3236 $this->cc = array();3237 $this->bcc = array();3238 $this->all_recipients = array();3239 $this->RecipientsQueue = array();3536 $this->to = []; 3537 $this->cc = []; 3538 $this->bcc = []; 3539 $this->all_recipients = []; 3540 $this->RecipientsQueue = []; 3240 3541 } 3241 3542 3242 3543 /** 3243 3544 * Clear all filesystem, string, and binary attachments. 3244 * @return void3245 3545 */ 3246 3546 public function clearAttachments() 3247 3547 { 3248 $this->attachment = array();3548 $this->attachment = []; 3249 3549 } 3250 3550 3251 3551 /** 3252 3552 * Clear all custom headers. 3253 * @return void3254 3553 */ 3255 3554 public function clearCustomHeaders() 3256 3555 { 3257 $this->CustomHeader = array();3556 $this->CustomHeader = []; 3258 3557 } 3259 3558 3260 3559 /** 3261 3560 * Add an error message to the error container. 3262 * @access protected3561 * 3263 3562 * @param string $msg 3264 * @return void3265 3563 */ 3266 3564 protected function setError($msg) 3267 3565 { 3268 $this->error_count++;3269 if ( $this->Mailer == 'smtp' and !is_null($this->smtp)) {3566 ++$this->error_count; 3567 if ('smtp' == $this->Mailer and null !== $this->smtp) { 3270 3568 $lasterror = $this->smtp->getError(); 3271 3569 if (!empty($lasterror['error'])) { 3272 3570 $msg .= $this->lang('smtp_error') . $lasterror['error']; 3273 3571 if (!empty($lasterror['detail'])) { 3274 $msg .= ' Detail: ' . $lasterror['detail'];3572 $msg .= ' Detail: ' . $lasterror['detail']; 3275 3573 } 3276 3574 if (!empty($lasterror['smtp_code'])) { 3277 3575 $msg .= ' SMTP code: ' . $lasterror['smtp_code']; … … 3286 3584 3287 3585 /** 3288 3586 * Return an RFC 822 formatted date. 3289 * @access public3587 * 3290 3588 * @return string 3291 * @static3292 3589 */ 3293 3590 public static function rfcDate() 3294 3591 { … … 3295 3592 // Set the time zone to whatever the default is to avoid 500 errors 3296 3593 // Will default to UTC if it's not set properly in php.ini 3297 3594 date_default_timezone_set(@date_default_timezone_get()); 3595 3298 3596 return date('D, j M Y H:i:s O'); 3299 3597 } 3300 3598 … … 3301 3599 /** 3302 3600 * Get the server hostname. 3303 3601 * Returns 'localhost.localdomain' if unknown. 3304 * @access protected3602 * 3305 3603 * @return string 3306 3604 */ 3307 3605 protected function serverHostname() 3308 3606 { 3309 $result = ' localhost.localdomain';3607 $result = ''; 3310 3608 if (!empty($this->Hostname)) { 3311 3609 $result = $this->Hostname; 3312 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {3610 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER)) { 3313 3611 $result = $_SERVER['SERVER_NAME']; 3314 } elseif (function_exists('gethostname') &&gethostname() !== false) {3612 } elseif (function_exists('gethostname') and gethostname() !== false) { 3315 3613 $result = gethostname(); 3316 3614 } elseif (php_uname('n') !== false) { 3317 3615 $result = php_uname('n'); 3318 3616 } 3617 if (!static::isValidHost($result)) { 3618 return 'localhost.localdomain'; 3619 } 3620 3319 3621 return $result; 3320 3622 } 3321 3623 3322 3624 /** 3625 * Validate whether a string contains a valid value to use as a hostname or IP address. 3626 * IPv6 addresses must include [], e.g. `[::1]`, not just `::1`. 3627 * 3628 * @param string $host The host name or IP address to check 3629 * 3630 * @return bool 3631 */ 3632 public static function isValidHost($host) 3633 { 3634 //Simple syntax limits 3635 if (empty($host) 3636 or !is_string($host) 3637 or strlen($host) > 256 3638 ) { 3639 return false; 3640 } 3641 //Looks like a bracketed IPv6 address 3642 if (trim($host, '[]') != $host) { 3643 return (bool) filter_var(trim($host, '[]'), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); 3644 } 3645 //If removing all the dots results in a numeric string, it must be an IPv4 address. 3646 //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names 3647 if (is_numeric(str_replace('.', '', $host))) { 3648 //Is it a valid IPv4 address? 3649 return (bool) filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); 3650 } 3651 if (filter_var('http://' . $host, FILTER_VALIDATE_URL)) { 3652 //Is it a syntactically valid hostname? 3653 return true; 3654 } 3655 3656 return false; 3657 } 3658 3659 /** 3323 3660 * Get an error message in the current language. 3324 * @access protected3661 * 3325 3662 * @param string $key 3663 * 3326 3664 * @return string 3327 3665 */ 3328 3666 protected function lang($key) … … 3332 3670 } 3333 3671 3334 3672 if (array_key_exists($key, $this->language)) { 3335 if ( $key == 'smtp_connect_failed') {3673 if ('smtp_connect_failed' == $key) { 3336 3674 //Include a link to troubleshooting docs on SMTP connection failure 3337 3675 //this is by far the biggest cause of support questions 3338 3676 //but it's usually not PHPMailer's fault. 3339 3677 return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; 3340 3678 } 3679 3341 3680 return $this->language[$key]; 3342 } else {3343 //Return the key as a fallback3344 return $key;3345 3681 } 3682 3683 //Return the key as a fallback 3684 return $key; 3346 3685 } 3347 3686 3348 3687 /** 3349 3688 * Check if an error occurred. 3350 * @access public3351 * @return bool ean True if an error did occur.3689 * 3690 * @return bool True if an error did occur 3352 3691 */ 3353 3692 public function isError() 3354 3693 { 3355 return ($this->error_count > 0);3694 return $this->error_count > 0; 3356 3695 } 3357 3696 3358 3697 /** 3359 * Ensure consistent line endings in a string.3360 * Changes every end of line from CRLF, CR or LF to $this->LE.3361 * @access public3362 * @param string $str String to fixEOL3363 * @return string3364 */3365 public function fixEOL($str)3366 {3367 // Normalise to \n3368 $nstr = str_replace(array("\r\n", "\r"), "\n", $str);3369 // Now convert LE as needed3370 if ($this->LE !== "\n") {3371 $nstr = str_replace("\n", $this->LE, $nstr);3372 }3373 return $nstr;3374 }3375 3376 /**3377 3698 * Add a custom header. 3378 3699 * $name value can be overloaded to contain 3379 * both header name and value (name:value) 3380 * @access public 3381 * @param string $name Custom header name 3382 * @param string $value Header value 3383 * @return void 3700 * both header name and value (name:value). 3701 * 3702 * @param string $name Custom header name 3703 * @param string|null $value Header value 3384 3704 */ 3385 3705 public function addCustomHeader($name, $value = null) 3386 3706 { 3387 if ( $value === null) {3707 if (null === $value) { 3388 3708 // Value passed in as name:value 3389 3709 $this->CustomHeader[] = explode(':', $name, 2); 3390 3710 } else { 3391 $this->CustomHeader[] = array($name, $value);3711 $this->CustomHeader[] = [$name, $value]; 3392 3712 } 3393 3713 } 3394 3714 3395 3715 /** 3396 3716 * Returns all custom headers. 3717 * 3397 3718 * @return array 3398 3719 */ 3399 3720 public function getCustomHeaders() … … 3409 3730 * $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty 3410 3731 * will look for an image file in $basedir/images/a.png and convert it to inline. 3411 3732 * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email) 3733 * Converts data-uri images into embedded attachments. 3412 3734 * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly. 3413 * @access public 3414 * @param string $message HTML message string 3415 * @param string $basedir Absolute path to a base directory to prepend to relative paths to images 3416 * @param boolean|callable $advanced Whether to use the internal HTML to text converter 3417 * or your own custom converter @see PHPMailer::html2text() 3735 * 3736 * @param string $message HTML message string 3737 * @param string $basedir Absolute path to a base directory to prepend to relative paths to images 3738 * @param bool|callable $advanced Whether to use the internal HTML to text converter 3739 * or your own custom converter @see PHPMailer::html2text() 3740 * 3418 3741 * @return string $message The transformed message Body 3419 3742 */ 3420 3743 public function msgHTML($message, $basedir = '', $advanced = false) … … 3421 3744 { 3422 3745 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); 3423 3746 if (array_key_exists(2, $images)) { 3424 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {3747 if (strlen($basedir) > 1 && '/' != substr($basedir, -1)) { 3425 3748 // Ensure $basedir has a trailing / 3426 3749 $basedir .= '/'; 3427 3750 } 3428 3751 foreach ($images[2] as $imgindex => $url) { 3429 3752 // Convert data URIs into embedded images 3430 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { 3431 $data = substr($url, strpos($url, ',')); 3432 if ($match[2]) { 3433 $data = base64_decode($data); 3753 //e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" 3754 if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) { 3755 if (count($match) == 4 and static::ENCODING_BASE64 == $match[2]) { 3756 $data = base64_decode($match[3]); 3757 } elseif ('' == $match[2]) { 3758 $data = rawurldecode($match[3]); 3434 3759 } else { 3435 $data = rawurldecode($data); 3760 //Not recognised so leave it alone 3761 continue; 3436 3762 } 3437 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 3438 if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { 3439 $message = str_replace( 3440 $images[0][$imgindex], 3441 $images[1][$imgindex] . '="cid:' . $cid . '"', 3442 $message 3443 ); 3763 //Hash the decoded data, not the URL so that the same data-URI image used in multiple places 3764 //will only be embedded once, even if it used a different encoding 3765 $cid = hash('sha256', $data) . '@phpmailer.0'; // RFC2392 S 2 3766 3767 if (!$this->cidExists($cid)) { 3768 $this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, static::ENCODING_BASE64, $match[1]); 3444 3769 } 3770 $message = str_replace( 3771 $images[0][$imgindex], 3772 $images[1][$imgindex] . '="cid:' . $cid . '"', 3773 $message 3774 ); 3445 3775 continue; 3446 3776 } 3447 if ( 3448 // Only process relative URLs if a basedir is provided (i.e. no absolute local paths) 3777 if (// Only process relative URLs if a basedir is provided (i.e. no absolute local paths) 3449 3778 !empty($basedir) 3450 3779 // Ignore URLs containing parent dir traversal (..) 3451 &&(strpos($url, '..') === false)3780 and (strpos($url, '..') === false) 3452 3781 // Do not change urls that are already inline images 3453 && substr($url, 0, 4) !== 'cid:'3782 and 0 !== strpos($url, 'cid:') 3454 3783 // Do not change absolute URLs, including anonymous protocol 3455 &&!preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)3784 and !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url) 3456 3785 ) { 3457 3786 $filename = basename($url); 3458 3787 $directory = dirname($url); 3459 if ( $directory == '.') {3788 if ('.' == $directory) { 3460 3789 $directory = ''; 3461 3790 } 3462 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 3463 if (strlen($directory) > 1 && substr($directory, -1) != '/') { 3791 $cid = hash('sha256', $url) . '@phpmailer.0'; // RFC2392 S 2 3792 if (strlen($basedir) > 1 and '/' != substr($basedir, -1)) { 3793 $basedir .= '/'; 3794 } 3795 if (strlen($directory) > 1 and '/' != substr($directory, -1)) { 3464 3796 $directory .= '/'; 3465 3797 } 3466 3798 if ($this->addEmbeddedImage( … … 3467 3799 $basedir . $directory . $filename, 3468 3800 $cid, 3469 3801 $filename, 3470 'base64',3471 s elf::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))3802 static::ENCODING_BASE64, 3803 static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION)) 3472 3804 ) 3473 3805 ) { 3474 3806 $message = preg_replace( … … 3481 3813 } 3482 3814 } 3483 3815 $this->isHTML(true); 3484 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better3485 $this->Body = $this->normalizeBreaks($message);3486 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));3816 // Convert all message body line breaks to LE, makes quoted-printable encoding work much better 3817 $this->Body = static::normalizeBreaks($message); 3818 $this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced)); 3487 3819 if (!$this->alternativeExists()) { 3488 $this->AltBody = 'T o view this email message, open it in a program that understands HTML!' .3489 self::CRLF . self::CRLF;3820 $this->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.' 3821 . static::$LE; 3490 3822 } 3823 3491 3824 return $this->Body; 3492 3825 } 3493 3826 … … 3495 3828 * Convert an HTML string into plain text. 3496 3829 * This is used by msgHTML(). 3497 3830 * Note - older versions of this function used a bundled advanced converter 3498 * which was beenremoved for license reasons in #232.3831 * which was removed for license reasons in #232. 3499 3832 * Example usage: 3500 * <code> 3833 * 3834 * ```php 3501 3835 * // Use default conversion 3502 3836 * $plain = $mail->html2text($html); 3503 3837 * // Use your own custom converter … … 3505 3839 * $converter = new MyHtml2text($html); 3506 3840 * return $converter->get_text(); 3507 3841 * }); 3508 * </code> 3509 * @param string $html The HTML text to convert 3510 * @param boolean|callable $advanced Any boolean value to use the internal converter, 3511 * or provide your own callable for custom conversion. 3842 * ``` 3843 * 3844 * @param string $html The HTML text to convert 3845 * @param bool|callable $advanced Any boolean value to use the internal converter, 3846 * or provide your own callable for custom conversion 3847 * 3512 3848 * @return string 3513 3849 */ 3514 3850 public function html2text($html, $advanced = false) … … 3516 3852 if (is_callable($advanced)) { 3517 3853 return call_user_func($advanced, $html); 3518 3854 } 3855 3519 3856 return html_entity_decode( 3520 3857 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), 3521 3858 ENT_QUOTES, … … 3525 3862 3526 3863 /** 3527 3864 * Get the MIME type for a file extension. 3865 * 3528 3866 * @param string $ext File extension 3529 * @access public 3530 * @return string MIME type of file. 3531 * @static 3867 * 3868 * @return string MIME type of file 3532 3869 */ 3533 3870 public static function _mime_types($ext = '') 3534 3871 { 3535 $mimes = array(3536 'xl' => 'application/excel',3537 'js' => 'application/javascript',3538 'hqx' => 'application/mac-binhex40',3539 'cpt' => 'application/mac-compactpro',3540 'bin' => 'application/macbinary',3541 'doc' => 'application/msword',3542 'word' => 'application/msword',3543 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',3544 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',3545 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',3546 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',3547 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',3548 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',3549 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',3550 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',3551 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',3552 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',3872 $mimes = [ 3873 'xl' => 'application/excel', 3874 'js' => 'application/javascript', 3875 'hqx' => 'application/mac-binhex40', 3876 'cpt' => 'application/mac-compactpro', 3877 'bin' => 'application/macbinary', 3878 'doc' => 'application/msword', 3879 'word' => 'application/msword', 3880 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 3881 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 3882 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 3883 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 3884 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 3885 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', 3886 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 3887 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 3888 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 3889 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 3553 3890 'class' => 'application/octet-stream', 3554 'dll' => 'application/octet-stream',3555 'dms' => 'application/octet-stream',3556 'exe' => 'application/octet-stream',3557 'lha' => 'application/octet-stream',3558 'lzh' => 'application/octet-stream',3559 'psd' => 'application/octet-stream',3560 'sea' => 'application/octet-stream',3561 'so' => 'application/octet-stream',3562 'oda' => 'application/oda',3563 'pdf' => 'application/pdf',3564 'ai' => 'application/postscript',3565 'eps' => 'application/postscript',3566 'ps' => 'application/postscript',3567 'smi' => 'application/smil',3568 'smil' => 'application/smil',3569 'mif' => 'application/vnd.mif',3570 'xls' => 'application/vnd.ms-excel',3571 'ppt' => 'application/vnd.ms-powerpoint',3891 'dll' => 'application/octet-stream', 3892 'dms' => 'application/octet-stream', 3893 'exe' => 'application/octet-stream', 3894 'lha' => 'application/octet-stream', 3895 'lzh' => 'application/octet-stream', 3896 'psd' => 'application/octet-stream', 3897 'sea' => 'application/octet-stream', 3898 'so' => 'application/octet-stream', 3899 'oda' => 'application/oda', 3900 'pdf' => 'application/pdf', 3901 'ai' => 'application/postscript', 3902 'eps' => 'application/postscript', 3903 'ps' => 'application/postscript', 3904 'smi' => 'application/smil', 3905 'smil' => 'application/smil', 3906 'mif' => 'application/vnd.mif', 3907 'xls' => 'application/vnd.ms-excel', 3908 'ppt' => 'application/vnd.ms-powerpoint', 3572 3909 'wbxml' => 'application/vnd.wap.wbxml', 3573 'wmlc' => 'application/vnd.wap.wmlc',3574 'dcr' => 'application/x-director',3575 'dir' => 'application/x-director',3576 'dxr' => 'application/x-director',3577 'dvi' => 'application/x-dvi',3578 'gtar' => 'application/x-gtar',3579 'php3' => 'application/x-httpd-php',3580 'php4' => 'application/x-httpd-php',3581 'php' => 'application/x-httpd-php',3910 'wmlc' => 'application/vnd.wap.wmlc', 3911 'dcr' => 'application/x-director', 3912 'dir' => 'application/x-director', 3913 'dxr' => 'application/x-director', 3914 'dvi' => 'application/x-dvi', 3915 'gtar' => 'application/x-gtar', 3916 'php3' => 'application/x-httpd-php', 3917 'php4' => 'application/x-httpd-php', 3918 'php' => 'application/x-httpd-php', 3582 3919 'phtml' => 'application/x-httpd-php', 3583 'phps' => 'application/x-httpd-php-source',3584 'swf' => 'application/x-shockwave-flash',3585 'sit' => 'application/x-stuffit',3586 'tar' => 'application/x-tar',3587 'tgz' => 'application/x-tar',3588 'xht' => 'application/xhtml+xml',3920 'phps' => 'application/x-httpd-php-source', 3921 'swf' => 'application/x-shockwave-flash', 3922 'sit' => 'application/x-stuffit', 3923 'tar' => 'application/x-tar', 3924 'tgz' => 'application/x-tar', 3925 'xht' => 'application/xhtml+xml', 3589 3926 'xhtml' => 'application/xhtml+xml', 3590 'zip' => 'application/zip', 3591 'mid' => 'audio/midi', 3592 'midi' => 'audio/midi', 3593 'mp2' => 'audio/mpeg', 3594 'mp3' => 'audio/mpeg', 3595 'mpga' => 'audio/mpeg', 3596 'aif' => 'audio/x-aiff', 3597 'aifc' => 'audio/x-aiff', 3598 'aiff' => 'audio/x-aiff', 3599 'ram' => 'audio/x-pn-realaudio', 3600 'rm' => 'audio/x-pn-realaudio', 3601 'rpm' => 'audio/x-pn-realaudio-plugin', 3602 'ra' => 'audio/x-realaudio', 3603 'wav' => 'audio/x-wav', 3604 'bmp' => 'image/bmp', 3605 'gif' => 'image/gif', 3606 'jpeg' => 'image/jpeg', 3607 'jpe' => 'image/jpeg', 3608 'jpg' => 'image/jpeg', 3609 'png' => 'image/png', 3610 'tiff' => 'image/tiff', 3611 'tif' => 'image/tiff', 3612 'eml' => 'message/rfc822', 3613 'css' => 'text/css', 3614 'html' => 'text/html', 3615 'htm' => 'text/html', 3927 'zip' => 'application/zip', 3928 'mid' => 'audio/midi', 3929 'midi' => 'audio/midi', 3930 'mp2' => 'audio/mpeg', 3931 'mp3' => 'audio/mpeg', 3932 'm4a' => 'audio/mp4', 3933 'mpga' => 'audio/mpeg', 3934 'aif' => 'audio/x-aiff', 3935 'aifc' => 'audio/x-aiff', 3936 'aiff' => 'audio/x-aiff', 3937 'ram' => 'audio/x-pn-realaudio', 3938 'rm' => 'audio/x-pn-realaudio', 3939 'rpm' => 'audio/x-pn-realaudio-plugin', 3940 'ra' => 'audio/x-realaudio', 3941 'wav' => 'audio/x-wav', 3942 'mka' => 'audio/x-matroska', 3943 'bmp' => 'image/bmp', 3944 'gif' => 'image/gif', 3945 'jpeg' => 'image/jpeg', 3946 'jpe' => 'image/jpeg', 3947 'jpg' => 'image/jpeg', 3948 'png' => 'image/png', 3949 'tiff' => 'image/tiff', 3950 'tif' => 'image/tiff', 3951 'webp' => 'image/webp', 3952 'heif' => 'image/heif', 3953 'heifs' => 'image/heif-sequence', 3954 'heic' => 'image/heic', 3955 'heics' => 'image/heic-sequence', 3956 'eml' => 'message/rfc822', 3957 'css' => 'text/css', 3958 'html' => 'text/html', 3959 'htm' => 'text/html', 3616 3960 'shtml' => 'text/html', 3617 'log' => 'text/plain',3618 'text' => 'text/plain',3619 'txt' => 'text/plain',3620 'rtx' => 'text/richtext',3621 'rtf' => 'text/rtf',3622 'vcf' => 'text/vcard',3961 'log' => 'text/plain', 3962 'text' => 'text/plain', 3963 'txt' => 'text/plain', 3964 'rtx' => 'text/richtext', 3965 'rtf' => 'text/rtf', 3966 'vcf' => 'text/vcard', 3623 3967 'vcard' => 'text/vcard', 3624 'xml' => 'text/xml', 3625 'xsl' => 'text/xml', 3626 'mpeg' => 'video/mpeg', 3627 'mpe' => 'video/mpeg', 3628 'mpg' => 'video/mpeg', 3629 'mov' => 'video/quicktime', 3630 'qt' => 'video/quicktime', 3631 'rv' => 'video/vnd.rn-realvideo', 3632 'avi' => 'video/x-msvideo', 3633 'movie' => 'video/x-sgi-movie' 3634 ); 3635 if (array_key_exists(strtolower($ext), $mimes)) { 3636 return $mimes[strtolower($ext)]; 3968 'ics' => 'text/calendar', 3969 'xml' => 'text/xml', 3970 'xsl' => 'text/xml', 3971 'wmv' => 'video/x-ms-wmv', 3972 'mpeg' => 'video/mpeg', 3973 'mpe' => 'video/mpeg', 3974 'mpg' => 'video/mpeg', 3975 'mp4' => 'video/mp4', 3976 'm4v' => 'video/mp4', 3977 'mov' => 'video/quicktime', 3978 'qt' => 'video/quicktime', 3979 'rv' => 'video/vnd.rn-realvideo', 3980 'avi' => 'video/x-msvideo', 3981 'movie' => 'video/x-sgi-movie', 3982 'webm' => 'video/webm', 3983 'mkv' => 'video/x-matroska', 3984 ]; 3985 $ext = strtolower($ext); 3986 if (array_key_exists($ext, $mimes)) { 3987 return $mimes[$ext]; 3637 3988 } 3989 3638 3990 return 'application/octet-stream'; 3639 3991 } 3640 3992 … … 3641 3993 /** 3642 3994 * Map a file name to a MIME type. 3643 3995 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. 3996 * 3644 3997 * @param string $filename A file name or full path, does not need to exist as a file 3998 * 3645 3999 * @return string 3646 * @static3647 4000 */ 3648 4001 public static function filenameToType($filename) 3649 4002 { … … 3652 4005 if (false !== $qpos) { 3653 4006 $filename = substr($filename, 0, $qpos); 3654 4007 } 3655 $pathinfo = self::mb_pathinfo($filename); 3656 return self::_mime_types($pathinfo['extension']); 4008 $ext = static::mb_pathinfo($filename, PATHINFO_EXTENSION); 4009 4010 return static::_mime_types($ext); 3657 4011 } 3658 4012 3659 4013 /** 3660 4014 * Multi-byte-safe pathinfo replacement. 3661 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. 3662 * Works similarly to the one in PHP >= 5.2.0 3663 * @link http://www.php.net/manual/en/function.pathinfo.php#107461 3664 * @param string $path A filename or path, does not need to exist as a file 3665 * @param integer|string $options Either a PATHINFO_* constant, 3666 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 4015 * Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe. 4016 * 4017 * @see http://www.php.net/manual/en/function.pathinfo.php#107461 4018 * 4019 * @param string $path A filename or path, does not need to exist as a file 4020 * @param int|string $options Either a PATHINFO_* constant, 4021 * or a string name to return only the specified piece 4022 * 3667 4023 * @return string|array 3668 * @static3669 4024 */ 3670 4025 public static function mb_pathinfo($path, $options = null) 3671 4026 { 3672 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');3673 $pathinfo = array();3674 if (preg_match(' %^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {4027 $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '']; 4028 $pathinfo = []; 4029 if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$#im', $path, $pathinfo)) { 3675 4030 if (array_key_exists(1, $pathinfo)) { 3676 4031 $ret['dirname'] = $pathinfo[1]; 3677 4032 } … … 3710 4065 * Usage Example: 3711 4066 * `$mail->set('SMTPSecure', 'tls');` 3712 4067 * is the same as: 3713 * `$mail->SMTPSecure = 'tls';` 3714 * @access public3715 * @param string $name The property name to set3716 * @param mixed $value The value to set the property to3717 * @return boolean3718 * @ TODO Should this not be using the __set() magic function?4068 * `$mail->SMTPSecure = 'tls';`. 4069 * 4070 * @param string $name The property name to set 4071 * @param mixed $value The value to set the property to 4072 * 4073 * @return bool 3719 4074 */ 3720 4075 public function set($name, $value = '') 3721 4076 { 3722 4077 if (property_exists($this, $name)) { 3723 4078 $this->$name = $value; 4079 3724 4080 return true; 3725 } else {3726 $this->setError($this->lang('variable_set') . $name);3727 return false;3728 4081 } 4082 $this->setError($this->lang('variable_set') . $name); 4083 4084 return false; 3729 4085 } 3730 4086 3731 4087 /** 3732 4088 * Strip newlines to prevent header injection. 3733 * @access public4089 * 3734 4090 * @param string $str 4091 * 3735 4092 * @return string 3736 4093 */ 3737 4094 public function secureHeader($str) 3738 4095 { 3739 return trim(str_replace( array("\r", "\n"), '', $str));4096 return trim(str_replace(["\r", "\n"], '', $str)); 3740 4097 } 3741 4098 3742 4099 /** … … 3743 4100 * Normalize line breaks in a string. 3744 4101 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. 3745 4102 * Defaults to CRLF (for message bodies) and preserves consecutive breaks. 4103 * 3746 4104 * @param string $text 3747 * @param string $breaktype What kind of line break to use, defaults to CRLF 4105 * @param string $breaktype What kind of line break to use; defaults to static::$LE 4106 * 3748 4107 * @return string 3749 * @access public3750 * @static3751 4108 */ 3752 public static function normalizeBreaks($text, $breaktype = "\r\n")4109 public static function normalizeBreaks($text, $breaktype = null) 3753 4110 { 3754 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); 4111 if (null === $breaktype) { 4112 $breaktype = static::$LE; 4113 } 4114 // Normalise to \n 4115 $text = str_replace(["\r\n", "\r"], "\n", $text); 4116 // Now convert LE as needed 4117 if ("\n" !== $breaktype) { 4118 $text = str_replace("\n", $breaktype, $text); 4119 } 4120 4121 return $text; 3755 4122 } 3756 4123 3757 4124 /** 4125 * Return the current line break format string. 4126 * 4127 * @return string 4128 */ 4129 public static function getLE() 4130 { 4131 return static::$LE; 4132 } 4133 4134 /** 4135 * Set the line break format string, e.g. "\r\n". 4136 * 4137 * @param string $le 4138 */ 4139 protected static function setLE($le) 4140 { 4141 static::$LE = $le; 4142 } 4143 4144 /** 3758 4145 * Set the public and private key files and password for S/MIME signing. 3759 * @access public4146 * 3760 4147 * @param string $cert_filename 3761 4148 * @param string $key_filename 3762 * @param string $key_pass Password for private key4149 * @param string $key_pass Password for private key 3763 4150 * @param string $extracerts_filename Optional path to chain certificate 3764 4151 */ 3765 4152 public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') … … 3772 4159 3773 4160 /** 3774 4161 * Quoted-Printable-encode a DKIM header. 3775 * @access public4162 * 3776 4163 * @param string $txt 4164 * 3777 4165 * @return string 3778 4166 */ 3779 4167 public function DKIM_QP($txt) 3780 4168 { 3781 4169 $line = ''; 3782 for ($i = 0; $i < strlen($txt); $i++) { 4170 $len = strlen($txt); 4171 for ($i = 0; $i < $len; ++$i) { 3783 4172 $ord = ord($txt[$i]); 3784 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) &&($ord <= 0x7E))) {4173 if (((0x21 <= $ord) and ($ord <= 0x3A)) or $ord == 0x3C or ((0x3E <= $ord) and ($ord <= 0x7E))) { 3785 4174 $line .= $txt[$i]; 3786 4175 } else { 3787 4176 $line .= '=' . sprintf('%02X', $ord); 3788 4177 } 3789 4178 } 4179 3790 4180 return $line; 3791 4181 } 3792 4182 3793 4183 /** 3794 4184 * Generate a DKIM signature. 3795 * @access public4185 * 3796 4186 * @param string $signHeader 3797 * @throws phpmailerException 4187 * 4188 * @throws Exception 4189 * 3798 4190 * @return string The DKIM signature value 3799 4191 */ 3800 4192 public function DKIM_Sign($signHeader) … … 3801 4193 { 3802 4194 if (!defined('PKCS7_TEXT')) { 3803 4195 if ($this->exceptions) { 3804 throw new phpmailerException($this->lang('extension_missing') . 'openssl');4196 throw new Exception($this->lang('extension_missing') . 'openssl'); 3805 4197 } 4198 3806 4199 return ''; 3807 4200 } 3808 $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private); 4201 $privKeyStr = !empty($this->DKIM_private_string) ? 4202 $this->DKIM_private_string : 4203 file_get_contents($this->DKIM_private); 3809 4204 if ('' != $this->DKIM_passphrase) { 3810 4205 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); 3811 4206 } else { 3812 4207 $privKey = openssl_pkey_get_private($privKeyStr); 3813 4208 } 3814 //Workaround for missing digest algorithms in old PHP & OpenSSL versions 3815 //@link http://stackoverflow.com/a/11117338/333340 3816 if (version_compare(PHP_VERSION, '5.3.0') >= 0 and 3817 in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { 3818 if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { 3819 openssl_pkey_free($privKey); 3820 return base64_encode($signature); 3821 } 3822 } else { 3823 $pinfo = openssl_pkey_get_details($privKey); 3824 $hash = hash('sha256', $signHeader); 3825 //'Magic' constant for SHA256 from RFC3447 3826 //@link https://tools.ietf.org/html/rfc3447#page-43 3827 $t = '3031300d060960864801650304020105000420' . $hash; 3828 $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); 3829 $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); 4209 if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { 4210 openssl_pkey_free($privKey); 3830 4211 3831 if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { 3832 openssl_pkey_free($privKey); 3833 return base64_encode($signature); 3834 } 4212 return base64_encode($signature); 3835 4213 } 3836 4214 openssl_pkey_free($privKey); 4215 3837 4216 return ''; 3838 4217 } 3839 4218 3840 4219 /** 3841 4220 * Generate a DKIM canonicalization header. 3842 * @access public 4221 * Uses the 'relaxed' algorithm from RFC6376 section 3.4.2. 4222 * Canonicalized headers should *always* use CRLF, regardless of mailer setting. 4223 * 4224 * @see https://tools.ietf.org/html/rfc6376#section-3.4.2 4225 * 3843 4226 * @param string $signHeader Header 4227 * 3844 4228 * @return string 3845 4229 */ 3846 4230 public function DKIM_HeaderC($signHeader) 3847 4231 { 3848 $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); 4232 //Unfold all header continuation lines 4233 //Also collapses folded whitespace. 4234 //Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]` 4235 //@see https://tools.ietf.org/html/rfc5322#section-2.2 4236 //That means this may break if you do something daft like put vertical tabs in your headers. 4237 $signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader); 3849 4238 $lines = explode("\r\n", $signHeader); 3850 4239 foreach ($lines as $key => $line) { 4240 //If the header is missing a :, skip it as it's invalid 4241 //This is likely to happen because the explode() above will also split 4242 //on the trailing LE, leaving an empty line 4243 if (strpos($line, ':') === false) { 4244 continue; 4245 } 3851 4246 list($heading, $value) = explode(':', $line, 2); 4247 //Lower-case header name 3852 4248 $heading = strtolower($heading); 3853 $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces 3854 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value 4249 //Collapse white space within the value 4250 $value = preg_replace('/[ \t]{2,}/', ' ', $value); 4251 //RFC6376 is slightly unclear here - it says to delete space at the *end* of each value 4252 //But then says to delete space before and after the colon. 4253 //Net result is the same as trimming both ends of the value. 4254 //by elimination, the same applies to the field name 4255 $lines[$key] = trim($heading, " \t") . ':' . trim($value, " \t"); 3855 4256 } 3856 $signHeader = implode("\r\n", $lines); 3857 return $signHeader;4257 4258 return implode("\r\n", $lines); 3858 4259 } 3859 4260 3860 4261 /** 3861 4262 * Generate a DKIM canonicalization body. 3862 * @access public 4263 * Uses the 'simple' algorithm from RFC6376 section 3.4.3. 4264 * Canonicalized bodies should *always* use CRLF, regardless of mailer setting. 4265 * 4266 * @see https://tools.ietf.org/html/rfc6376#section-3.4.3 4267 * 3863 4268 * @param string $body Message Body 4269 * 3864 4270 * @return string 3865 4271 */ 3866 4272 public function DKIM_BodyC($body) 3867 4273 { 3868 if ( $body == '') {4274 if (empty($body)) { 3869 4275 return "\r\n"; 3870 4276 } 3871 // stabilize line endings 3872 $body = str_replace("\r\n", "\n", $body); 3873 $body = str_replace("\n", "\r\n", $body); 3874 // END stabilize line endings 3875 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { 3876 $body = substr($body, 0, strlen($body) - 2); 3877 } 3878 return $body; 4277 // Normalize line endings to CRLF 4278 $body = static::normalizeBreaks($body, "\r\n"); 4279 4280 //Reduce multiple trailing line breaks to a single one 4281 return rtrim($body, "\r\n") . "\r\n"; 3879 4282 } 3880 4283 3881 4284 /** 3882 4285 * Create the DKIM header and body in a new message header. 3883 * @access public4286 * 3884 4287 * @param string $headers_line Header lines 3885 * @param string $subject Subject 3886 * @param string $body Body 4288 * @param string $subject Subject 4289 * @param string $body Body 4290 * 3887 4291 * @return string 3888 4292 */ 3889 4293 public function DKIM_Add($headers_line, $subject, $body) … … 3893 4297 $DKIMquery = 'dns/txt'; // Query method 3894 4298 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) 3895 4299 $subject_header = "Subject: $subject"; 3896 $headers = explode( $this->LE, $headers_line);4300 $headers = explode(static::$LE, $headers_line); 3897 4301 $from_header = ''; 3898 4302 $to_header = ''; 3899 4303 $date_header = ''; 3900 4304 $current = ''; 4305 $copiedHeaderFields = ''; 4306 $foundExtraHeaders = []; 4307 $extraHeaderKeys = ''; 4308 $extraHeaderValues = ''; 4309 $extraCopyHeaderFields = ''; 3901 4310 foreach ($headers as $header) { 3902 4311 if (strpos($header, 'From:') === 0) { 3903 4312 $from_header = $header; … … 3908 4317 } elseif (strpos($header, 'Date:') === 0) { 3909 4318 $date_header = $header; 3910 4319 $current = 'date_header'; 4320 } elseif (!empty($this->DKIM_extraHeaders)) { 4321 foreach ($this->DKIM_extraHeaders as $extraHeader) { 4322 if (strpos($header, $extraHeader . ':') === 0) { 4323 $headerValue = $header; 4324 foreach ($this->CustomHeader as $customHeader) { 4325 if ($customHeader[0] === $extraHeader) { 4326 $headerValue = trim($customHeader[0]) . 4327 ': ' . 4328 $this->encodeHeader(trim($customHeader[1])); 4329 break; 4330 } 4331 } 4332 $foundExtraHeaders[$extraHeader] = $headerValue; 4333 $current = ''; 4334 break; 4335 } 4336 } 3911 4337 } else { 3912 if (!empty($$current) &&strpos($header, ' =?') === 0) {4338 if (!empty($$current) and strpos($header, ' =?') === 0) { 3913 4339 $$current .= $header; 3914 4340 } else { 3915 4341 $current = ''; … … 3916 4342 } 3917 4343 } 3918 4344 } 3919 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 3920 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 3921 $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); 3922 $subject = str_replace( 3923 '|', 3924 '=7C', 3925 $this->DKIM_QP($subject_header) 3926 ); // Copied header fields (dkim-quoted-printable) 4345 foreach ($foundExtraHeaders as $key => $value) { 4346 $extraHeaderKeys .= ':' . $key; 4347 $extraHeaderValues .= $value . "\r\n"; 4348 if ($this->DKIM_copyHeaderFields) { 4349 $extraCopyHeaderFields .= "\t|" . str_replace('|', '=7C', $this->DKIM_QP($value)) . ";\r\n"; 4350 } 4351 } 4352 if ($this->DKIM_copyHeaderFields) { 4353 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 4354 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 4355 $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); 4356 $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)); 4357 $copiedHeaderFields = "\tz=$from\r\n" . 4358 "\t|$to\r\n" . 4359 "\t|$date\r\n" . 4360 "\t|$subject;\r\n" . 4361 $extraCopyHeaderFields; 4362 } 3927 4363 $body = $this->DKIM_BodyC($body); 3928 4364 $DKIMlen = strlen($body); // Length of body 3929 4365 $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body … … 3939 4375 $this->DKIM_selector . 3940 4376 ";\r\n" . 3941 4377 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . 3942 "\th=From:To:Date:Subject ;\r\n" .4378 "\th=From:To:Date:Subject" . $extraHeaderKeys . ";\r\n" . 3943 4379 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . 3944 "\tz=$from\r\n" . 3945 "\t|$to\r\n" . 3946 "\t|$date\r\n" . 3947 "\t|$subject;\r\n" . 4380 $copiedHeaderFields . 3948 4381 "\tbh=" . $DKIMb64 . ";\r\n" . 3949 4382 "\tb="; 3950 4383 $toSign = $this->DKIM_HeaderC( … … 3952 4385 $to_header . "\r\n" . 3953 4386 $date_header . "\r\n" . 3954 4387 $subject_header . "\r\n" . 4388 $extraHeaderValues . 3955 4389 $dkimhdrs 3956 4390 ); 3957 4391 $signed = $this->DKIM_Sign($toSign); 3958 return $dkimhdrs . $signed . "\r\n"; 4392 4393 return static::normalizeBreaks($dkimhdrs . $signed) . static::$LE; 3959 4394 } 3960 4395 3961 4396 /** 3962 * Detect if a string contains a line longer than the maximum line length allowed. 4397 * Detect if a string contains a line longer than the maximum line length 4398 * allowed by RFC 2822 section 2.1.1. 4399 * 3963 4400 * @param string $str 3964 * @return boolean3965 * @ static4401 * 4402 * @return bool 3966 4403 */ 3967 4404 public static function hasLineLongerThanMax($str) 3968 4405 { 3969 //+2 to include CRLF line break for a 1000 total 3970 return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); 4406 return (bool) preg_match('/^(.{' . (self::MAX_LINE_LENGTH + strlen(static::$LE)) . ',})/m', $str); 3971 4407 } 3972 4408 3973 4409 /** 3974 4410 * Allows for public read access to 'to' property. 3975 * @note:Before the send() call, queued addresses (i.e. with IDN) are not yet included.3976 * @access public4411 * Before the send() call, queued addresses (i.e. with IDN) are not yet included. 4412 * 3977 4413 * @return array 3978 4414 */ 3979 4415 public function getToAddresses() … … 3983 4419 3984 4420 /** 3985 4421 * Allows for public read access to 'cc' property. 3986 * @note:Before the send() call, queued addresses (i.e. with IDN) are not yet included.3987 * @access public4422 * Before the send() call, queued addresses (i.e. with IDN) are not yet included. 4423 * 3988 4424 * @return array 3989 4425 */ 3990 4426 public function getCcAddresses() … … 3994 4430 3995 4431 /** 3996 4432 * Allows for public read access to 'bcc' property. 3997 * @note:Before the send() call, queued addresses (i.e. with IDN) are not yet included.3998 * @access public4433 * Before the send() call, queued addresses (i.e. with IDN) are not yet included. 4434 * 3999 4435 * @return array 4000 4436 */ 4001 4437 public function getBccAddresses() … … 4005 4441 4006 4442 /** 4007 4443 * Allows for public read access to 'ReplyTo' property. 4008 * @note:Before the send() call, queued addresses (i.e. with IDN) are not yet included.4009 * @access public4444 * Before the send() call, queued addresses (i.e. with IDN) are not yet included. 4445 * 4010 4446 * @return array 4011 4447 */ 4012 4448 public function getReplyToAddresses() … … 4016 4452 4017 4453 /** 4018 4454 * Allows for public read access to 'all_recipients' property. 4019 * @note:Before the send() call, queued addresses (i.e. with IDN) are not yet included.4020 * @access public4455 * Before the send() call, queued addresses (i.e. with IDN) are not yet included. 4456 * 4021 4457 * @return array 4022 4458 */ 4023 4459 public function getAllRecipientAddresses() … … 4027 4463 4028 4464 /** 4029 4465 * Perform a callback. 4030 * @param boolean $isSent 4031 * @param array $to 4032 * @param array $cc 4033 * @param array $bcc 4466 * 4467 * @param bool $isSent 4468 * @param array $to 4469 * @param array $cc 4470 * @param array $bcc 4034 4471 * @param string $subject 4035 4472 * @param string $body 4036 4473 * @param string $from 4474 * @param array $extra 4037 4475 */ 4038 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from )4476 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from, $extra) 4039 4477 { 4040 if (!empty($this->action_function) && is_callable($this->action_function)) { 4041 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); 4042 call_user_func_array($this->action_function, $params); 4478 if (!empty($this->action_function) and is_callable($this->action_function)) { 4479 call_user_func($this->action_function, $isSent, $to, $cc, $bcc, $subject, $body, $from, $extra); 4043 4480 } 4044 4481 } 4045 }4046 4482 4047 /**4048 * PHPMailer exception handler4049 * @package PHPMailer4050 */4051 class phpmailerException extends Exception4052 {4053 4483 /** 4054 * Prettify error message output 4055 * @return string 4484 * Get the OAuth instance. 4485 * 4486 * @return OAuth 4056 4487 */ 4057 public function errorMessage()4488 public function getOAuth() 4058 4489 { 4059 $errorMsg = '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n"; 4060 return $errorMsg; 4490 return $this->oauth; 4061 4491 } 4492 4493 /** 4494 * Set an OAuth instance. 4495 * 4496 * @param OAuth $oauth 4497 */ 4498 public function setOAuth(OAuth $oauth) 4499 { 4500 $this->oauth = $oauth; 4501 } 4062 4502 } -
src/wp-includes/class-smtp.php
1 1 <?php 2 2 /** 3 3 * PHPMailer RFC821 SMTP email transport class. 4 * PHP Version 5 5 * @package PHPMailer 6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project 7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 10 * @author Brent R. Matzelle (original founder) 11 * @copyright 2014 Marcus Bointon 4 * PHP Version 5.5. 5 * 6 * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project 7 * 8 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 9 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 10 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 11 * @author Brent R. Matzelle (original founder) 12 * @copyright 2012 - 2017 Marcus Bointon 12 13 * @copyright 2010 - 2012 Jim Jagielski 13 14 * @copyright 2004 - 2009 Andy Prevost 14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License15 * @note This program is distributed in the hope that it will be useful - WITHOUT15 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 16 * @note This program is distributed in the hope that it will be useful - WITHOUT 16 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 18 * FITNESS FOR A PARTICULAR PURPOSE. 18 19 */ 19 20 21 namespace PHPMailer\PHPMailer; 22 20 23 /** 21 24 * PHPMailer RFC821 SMTP email transport class. 22 25 * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. 23 * @package PHPMailer24 * @author Chris Ryan25 * @author Marcus Bointon <phpmailer@synchromedia.co.uk>26 * 27 * @author Chris Ryan 28 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> 26 29 */ 27 30 class SMTP 28 31 { 29 32 /** 30 33 * The PHPMailer SMTP version number. 34 * 31 35 * @var string 32 36 */ 33 const VERSION = ' 5.2.27';37 const VERSION = '6.0.7'; 34 38 35 39 /** 36 40 * SMTP line break constant. 41 * 37 42 * @var string 38 43 */ 39 const CRLF= "\r\n";44 const LE = "\r\n"; 40 45 41 46 /** 42 47 * The SMTP port to use if one is not specified. 43 * @var integer 48 * 49 * @var int 44 50 */ 45 const DEFAULT_ SMTP_PORT = 25;51 const DEFAULT_PORT = 25; 46 52 47 53 /** 48 * The maximum line length allowed by RFC 2822 section 2.1.1 49 * @var integer 54 * The maximum line length allowed by RFC 2822 section 2.1.1. 55 * 56 * @var int 50 57 */ 51 58 const MAX_LINE_LENGTH = 998; 52 59 53 60 /** 54 * Debug level for no output 61 * Debug level for no output. 55 62 */ 56 63 const DEBUG_OFF = 0; 57 64 58 65 /** 59 * Debug level to show client -> server messages 66 * Debug level to show client -> server messages. 60 67 */ 61 68 const DEBUG_CLIENT = 1; 62 69 63 70 /** 64 * Debug level to show client -> server and server -> client messages 71 * Debug level to show client -> server and server -> client messages. 65 72 */ 66 73 const DEBUG_SERVER = 2; 67 74 68 75 /** 69 * Debug level to show connection status, client -> server and server -> client messages 76 * Debug level to show connection status, client -> server and server -> client messages. 70 77 */ 71 78 const DEBUG_CONNECTION = 3; 72 79 73 80 /** 74 * Debug level to show all messages 81 * Debug level to show all messages. 75 82 */ 76 83 const DEBUG_LOWLEVEL = 4; 77 84 78 85 /** 79 * The PHPMailer SMTP Version number.80 * @var string81 * @deprecated Use the `VERSION` constant instead82 * @see SMTP::VERSION83 */84 public $Version = '5.2.27';85 86 /**87 * SMTP server port number.88 * @var integer89 * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead90 * @see SMTP::DEFAULT_SMTP_PORT91 */92 public $SMTP_PORT = 25;93 94 /**95 * SMTP reply line ending.96 * @var string97 * @deprecated Use the `CRLF` constant instead98 * @see SMTP::CRLF99 */100 public $CRLF = "\r\n";101 102 /**103 86 * Debug output level. 104 87 * Options: 105 88 * * self::DEBUG_OFF (`0`) No debug output, default … … 106 89 * * self::DEBUG_CLIENT (`1`) Client commands 107 90 * * self::DEBUG_SERVER (`2`) Client commands and server responses 108 91 * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status 109 * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages 110 * @var integer 92 * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages. 93 * 94 * @var int 111 95 */ 112 96 public $do_debug = self::DEBUG_OFF; 113 97 … … 117 101 * * `echo` Output plain-text as-is, appropriate for CLI 118 102 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output 119 103 * * `error_log` Output to error log as configured in php.ini 104 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 120 105 * 121 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 122 * <code> 106 * ```php 123 107 * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; 124 * </code> 125 * @var string|callable 108 * ``` 109 * 110 * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug` 111 * level output is used: 112 * 113 * ```php 114 * $mail->Debugoutput = new myPsr3Logger; 115 * ``` 116 * 117 * @var string|callable|\Psr\Log\LoggerInterface 126 118 */ 127 119 public $Debugoutput = 'echo'; 128 120 129 121 /** 130 122 * Whether to use VERP. 131 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path 132 * @link http://www.postfix.org/VERP_README.html Info on VERP 133 * @var boolean 123 * 124 * @see http://en.wikipedia.org/wiki/Variable_envelope_return_path 125 * @see http://www.postfix.org/VERP_README.html Info on VERP 126 * 127 * @var bool 134 128 */ 135 129 public $do_verp = false; 136 130 137 131 /** 138 132 * The timeout value for connection, in seconds. 139 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 133 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. 140 134 * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. 141 * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 142 * @var integer 135 * 136 * @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2 137 * 138 * @var int 143 139 */ 144 140 public $Timeout = 300; 145 141 146 142 /** 147 143 * How long to wait for commands to complete, in seconds. 148 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 149 * @var integer 144 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. 145 * 146 * @var int 150 147 */ 151 148 public $Timelimit = 300; 152 149 153 150 /** 154 * @var arrayPatterns to extract an SMTP transaction id from reply to a DATA command.151 * Patterns to extract an SMTP transaction id from reply to a DATA command. 155 152 * The first capture group in each regex will be used as the ID. 153 * MS ESMTP returns the message ID, which may not be correct for internal tracking. 154 * 155 * @var string[] 156 156 */ 157 protected $smtp_transaction_id_patterns = array( 158 'exim' => '/[0-9]{3} OK id=(.*)/', 159 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', 160 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' 161 ); 157 protected $smtp_transaction_id_patterns = [ 158 'exim' => '/[\d]{3} OK id=(.*)/', 159 'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/', 160 'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/', 161 'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/', 162 'Amazon_SES' => '/[\d]{3} Ok (.*)/', 163 'SendGrid' => '/[\d]{3} Ok: queued as (.*)/', 164 'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/', 165 ]; 162 166 163 167 /** 164 * @var string The last transaction ID issued in response to a DATA command, 165 * if one was detected 168 * The last transaction ID issued in response to a DATA command, 169 * if one was detected. 170 * 171 * @var string|bool|null 166 172 */ 167 173 protected $last_smtp_transaction_id; 168 174 169 175 /** 170 176 * The socket for the server connection. 171 * @var resource 177 * 178 * @var ?resource 172 179 */ 173 180 protected $smtp_conn; 174 181 175 182 /** 176 183 * Error information, if any, for the last SMTP command. 184 * 177 185 * @var array 178 186 */ 179 protected $error = array(187 protected $error = [ 180 188 'error' => '', 181 189 'detail' => '', 182 190 'smtp_code' => '', 183 'smtp_code_ex' => '' 184 );191 'smtp_code_ex' => '', 192 ]; 185 193 186 194 /** 187 195 * The reply the server sent to us for HELO. 188 196 * If null, no HELO string has yet been received. 197 * 189 198 * @var string|null 190 199 */ 191 200 protected $helo_rply = null; … … 197 206 * represents the server name. In case of HELO it is the only element of the array. 198 207 * Other values can be boolean TRUE or an array containing extension options. 199 208 * If null, no HELO/EHLO string has yet been received. 209 * 200 210 * @var array|null 201 211 */ 202 212 protected $server_caps = null; … … 203 213 204 214 /** 205 215 * The most recent reply received from the server. 216 * 206 217 * @var string 207 218 */ 208 219 protected $last_reply = ''; … … 209 220 210 221 /** 211 222 * Output debugging info via a user-selected method. 223 * 224 * @param string $str Debug string to output 225 * @param int $level The debug level of this message; see DEBUG_* constants 226 * 212 227 * @see SMTP::$Debugoutput 213 228 * @see SMTP::$do_debug 214 * @param string $str Debug string to output215 * @param integer $level The debug level of this message; see DEBUG_* constants216 * @return void217 229 */ 218 230 protected function edebug($str, $level = 0) 219 231 { … … 220 232 if ($level > $this->do_debug) { 221 233 return; 222 234 } 235 //Is this a PSR-3 logger? 236 if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { 237 $this->Debugoutput->debug($str); 238 239 return; 240 } 223 241 //Avoid clash with built-in function names 224 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {242 if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) { 225 243 call_user_func($this->Debugoutput, $str, $level); 244 226 245 return; 227 246 } 228 247 switch ($this->Debugoutput) { … … 232 251 break; 233 252 case 'html': 234 253 //Cleans up output a bit for a better looking, HTML-safe output 235 echo gmdate('Y-m-d H:i:s') . ' ' .htmlentities(254 echo gmdate('Y-m-d H:i:s'), ' ', htmlentities( 236 255 preg_replace('/[\r\n]+/', '', $str), 237 256 ENT_QUOTES, 238 257 'UTF-8' 239 ) ."<br>\n";258 ), "<br>\n"; 240 259 break; 241 260 case 'echo': 242 261 default: 243 262 //Normalize line breaks 244 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 245 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 246 "\n", 247 "\n \t ", 248 trim($str) 249 ) . "\n"; 263 $str = preg_replace('/\r\n|\r/ms', "\n", $str); 264 echo gmdate('Y-m-d H:i:s'), 265 "\t", 266 //Trim trailing space 267 trim( 268 //Indent for readability, except for trailing break 269 str_replace( 270 "\n", 271 "\n \t ", 272 trim($str) 273 ) 274 ), 275 "\n"; 250 276 } 251 277 } 252 278 253 279 /** 254 280 * Connect to an SMTP server. 255 * @param string $host SMTP server IP or host name 256 * @param integer $port The port number to connect to 257 * @param integer $timeout How long to wait for the connection to open 258 * @param array $options An array of options for stream_context_create() 259 * @access public 260 * @return boolean 281 * 282 * @param string $host SMTP server IP or host name 283 * @param int $port The port number to connect to 284 * @param int $timeout How long to wait for the connection to open 285 * @param array $options An array of options for stream_context_create() 286 * 287 * @return bool 261 288 */ 262 public function connect($host, $port = null, $timeout = 30, $options = array())289 public function connect($host, $port = null, $timeout = 30, $options = []) 263 290 { 264 291 static $streamok; 265 292 //This is enabled by default since 5.0.0 but some providers disable it 266 293 //Check this once and cache the result 267 if ( is_null($streamok)) {294 if (null === $streamok) { 268 295 $streamok = function_exists('stream_socket_client'); 269 296 } 270 297 // Clear errors to avoid confusion … … 273 300 if ($this->connected()) { 274 301 // Already connected, generate error 275 302 $this->setError('Already connected to a server'); 303 276 304 return false; 277 305 } 278 306 if (empty($port)) { 279 $port = self::DEFAULT_ SMTP_PORT;307 $port = self::DEFAULT_PORT; 280 308 } 281 309 // Connect to the SMTP server 282 310 $this->edebug( 283 311 "Connection: opening to $host:$port, timeout=$timeout, options=" . 284 var_export($options, true),312 (count($options) > 0 ? var_export($options, true) : 'array()'), 285 313 self::DEBUG_CONNECTION 286 314 ); 287 315 $errno = 0; … … 288 316 $errstr = ''; 289 317 if ($streamok) { 290 318 $socket_context = stream_context_create($options); 291 set_error_handler( array($this, 'errorHandler'));319 set_error_handler([$this, 'errorHandler']); 292 320 $this->smtp_conn = stream_socket_client( 293 $host . ":". $port,321 $host . ':' . $port, 294 322 $errno, 295 323 $errstr, 296 324 $timeout, … … 301 329 } else { 302 330 //Fall back to fsockopen which should work in more places, but is missing some features 303 331 $this->edebug( 304 "Connection: stream_socket_client not available, falling back to fsockopen",332 'Connection: stream_socket_client not available, falling back to fsockopen', 305 333 self::DEBUG_CONNECTION 306 334 ); 307 set_error_handler( array($this, 'errorHandler'));335 set_error_handler([$this, 'errorHandler']); 308 336 $this->smtp_conn = fsockopen( 309 337 $host, 310 338 $port, … … 318 346 if (!is_resource($this->smtp_conn)) { 319 347 $this->setError( 320 348 'Failed to connect to server', 321 $errno, 322 $errstr 349 '', 350 (string) $errno, 351 (string) $errstr 323 352 ); 324 353 $this->edebug( 325 354 'SMTP ERROR: ' . $this->error['error'] … … 326 355 . ": $errstr ($errno)", 327 356 self::DEBUG_CLIENT 328 357 ); 358 329 359 return false; 330 360 } 331 361 $this->edebug('Connection: opened', self::DEBUG_CONNECTION); … … 334 364 if (substr(PHP_OS, 0, 3) != 'WIN') { 335 365 $max = ini_get('max_execution_time'); 336 366 // Don't bother if unlimited 337 if ( $max != 0 &&$timeout > $max) {367 if (0 != $max and $timeout > $max) { 338 368 @set_time_limit($timeout); 339 369 } 340 370 stream_set_timeout($this->smtp_conn, $timeout, 0); … … 342 372 // Get any announcement 343 373 $announce = $this->get_lines(); 344 374 $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); 375 345 376 return true; 346 377 } 347 378 348 379 /** 349 380 * Initiate a TLS (encrypted) session. 350 * @access public351 * @return bool ean381 * 382 * @return bool 352 383 */ 353 384 public function startTLS() 354 385 { … … 367 398 } 368 399 369 400 // Begin encrypted connection 370 set_error_handler( array($this, 'errorHandler'));401 set_error_handler([$this, 'errorHandler']); 371 402 $crypto_ok = stream_socket_enable_crypto( 372 403 $this->smtp_conn, 373 404 true, … … 374 405 $crypto_method 375 406 ); 376 407 restore_error_handler(); 377 return $crypto_ok; 408 409 return (bool) $crypto_ok; 378 410 } 379 411 380 412 /** 381 413 * Perform SMTP authentication. 382 414 * Must be run after hello(). 383 * @see hello() 415 * 416 * @see hello() 417 * 384 418 * @param string $username The user name 385 419 * @param string $password The password 386 * @param string $authtype The auth type (PLAIN, LOGIN, CRAM-MD5) 387 * @param string $realm The auth realm for NTLM 388 * @param string $workstation The auth workstation for NTLM 389 * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) 390 * @return bool True if successfully authenticated.* @access public 420 * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2) 421 * @param OAuth $OAuth An optional OAuth instance for XOAUTH2 authentication 422 * 423 * @return bool True if successfully authenticated 391 424 */ 392 425 public function authenticate( 393 426 $username, 394 427 $password, 395 428 $authtype = null, 396 $realm = '',397 $workstation = '',398 429 $OAuth = null 399 430 ) { 400 431 if (!$this->server_caps) { 401 432 $this->setError('Authentication is not allowed before HELO/EHLO'); 433 402 434 return false; 403 435 } 404 436 … … 408 440 $this->setError('Authentication is not allowed at this stage'); 409 441 // 'at this stage' means that auth may be allowed after the stage changes 410 442 // e.g. after STARTTLS 443 411 444 return false; 412 445 } 413 446 414 self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);415 self::edebug(447 $this->edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNSPECIFIED'), self::DEBUG_LOWLEVEL); 448 $this->edebug( 416 449 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), 417 450 self::DEBUG_LOWLEVEL 418 451 ); 419 452 453 //If we have requested a specific auth type, check the server supports it before trying others 454 if (null !== $authtype and !in_array($authtype, $this->server_caps['AUTH'])) { 455 $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL); 456 $authtype = null; 457 } 458 420 459 if (empty($authtype)) { 421 foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN') as $method) { 460 //If no auth mechanism is specified, attempt to use these, in this order 461 //Try CRAM-MD5 first as it's more secure than the others 462 foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) { 422 463 if (in_array($method, $this->server_caps['AUTH'])) { 423 464 $authtype = $method; 424 465 break; … … 426 467 } 427 468 if (empty($authtype)) { 428 469 $this->setError('No supported authentication methods found'); 470 429 471 return false; 430 472 } 431 473 self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL); … … 433 475 434 476 if (!in_array($authtype, $this->server_caps['AUTH'])) { 435 477 $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); 478 436 479 return false; 437 480 } 438 481 } elseif (empty($authtype)) { … … 459 502 if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { 460 503 return false; 461 504 } 462 if (!$this->sendCommand( "Username", base64_encode($username), 334)) {505 if (!$this->sendCommand('Username', base64_encode($username), 334)) { 463 506 return false; 464 507 } 465 if (!$this->sendCommand( "Password", base64_encode($password), 235)) {508 if (!$this->sendCommand('Password', base64_encode($password), 235)) { 466 509 return false; 467 510 } 468 511 break; … … 479 522 480 523 // send encoded credentials 481 524 return $this->sendCommand('Username', base64_encode($response), 235); 525 case 'XOAUTH2': 526 //The OAuth instance must be set up prior to requesting auth. 527 if (null === $OAuth) { 528 return false; 529 } 530 $oauth = $OAuth->getOauth64(); 531 532 // Start authentication 533 if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { 534 return false; 535 } 536 break; 482 537 default: 483 538 $this->setError("Authentication method \"$authtype\" is not supported"); 539 484 540 return false; 485 541 } 542 486 543 return true; 487 544 } 488 545 … … 489 546 /** 490 547 * Calculate an MD5 HMAC hash. 491 548 * Works like hash_hmac('md5', $data, $key) 492 * in case that function is not available 549 * in case that function is not available. 550 * 493 551 * @param string $data The data to hash 494 * @param string $key The key to hash with495 * @access protected552 * @param string $key The key to hash with 553 * 496 554 * @return string 497 555 */ 498 556 protected function hmac($data, $key) … … 524 582 525 583 /** 526 584 * Check connection state. 527 * @access public528 * @return bool ean True if connected.585 * 586 * @return bool True if connected 529 587 */ 530 588 public function connected() 531 589 { … … 538 596 self::DEBUG_CLIENT 539 597 ); 540 598 $this->close(); 599 541 600 return false; 542 601 } 602 543 603 return true; // everything looks good 544 604 } 605 545 606 return false; 546 607 } 547 608 … … 548 609 /** 549 610 * Close the socket and clean up the state of the class. 550 611 * Don't use this function without first trying to use QUIT. 612 * 551 613 * @see quit() 552 * @access public553 * @return void554 614 */ 555 615 public function close() 556 616 { … … 571 631 * finializing the mail transaction. $msg_data is the message 572 632 * that is to be send with the headers. Each header needs to be 573 633 * on a single line followed by a <CRLF> with the message headers 574 * and the message body being separated by and additional <CRLF>. 575 * Implements rfc 821: DATA <CRLF> 634 * and the message body being separated by an additional <CRLF>. 635 * Implements RFC 821: DATA <CRLF>. 636 * 576 637 * @param string $msg_data Message data to send 577 * @access public578 * @return bool ean638 * 639 * @return bool 579 640 */ 580 641 public function data($msg_data) 581 642 { … … 585 646 } 586 647 587 648 /* The server is ready to accept data! 588 * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)649 * According to rfc821 we should not send more than 1000 characters on a single line (including the LE) 589 650 * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into 590 651 * smaller lines to fit within the limit. 591 652 * We will also look for lines that start with a '.' and prepend an additional '.'. … … 593 654 */ 594 655 595 656 // Normalize line breaks before exploding 596 $lines = explode("\n", str_replace( array("\r\n", "\r"), "\n", $msg_data));657 $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data)); 597 658 598 659 /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field 599 660 * of the first line (':' separated) does not contain a space then it _should_ be a header and we will … … 602 663 603 664 $field = substr($lines[0], 0, strpos($lines[0], ':')); 604 665 $in_headers = false; 605 if (!empty($field) &&strpos($field, ' ') === false) {666 if (!empty($field) and strpos($field, ' ') === false) { 606 667 $in_headers = true; 607 668 } 608 669 609 670 foreach ($lines as $line) { 610 $lines_out = array();671 $lines_out = []; 611 672 if ($in_headers and $line == '') { 612 673 $in_headers = false; 613 674 } … … 642 703 if (!empty($line_out) and $line_out[0] == '.') { 643 704 $line_out = '.' . $line_out; 644 705 } 645 $this->client_send($line_out . s elf::CRLF);706 $this->client_send($line_out . static::LE, 'DATA'); 646 707 } 647 708 } 648 709 … … 654 715 $this->recordLastTransactionID(); 655 716 //Restore timelimit 656 717 $this->Timelimit = $savetimelimit; 718 657 719 return $result; 658 720 } 659 721 … … 663 725 * This makes sure that client and server are in a known state. 664 726 * Implements RFC 821: HELO <SP> <domain> <CRLF> 665 727 * and RFC 2821 EHLO. 728 * 666 729 * @param string $host The host name or IP to connect to 667 * @access public668 * @return bool ean730 * 731 * @return bool 669 732 */ 670 733 public function hello($host = '') 671 734 { 672 735 //Try extended hello first (RFC 2821) 673 return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));736 return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host); 674 737 } 675 738 676 739 /** 677 740 * Send an SMTP HELO or EHLO command. 678 * Low-level implementation used by hello() 679 * @see hello()741 * Low-level implementation used by hello(). 742 * 680 743 * @param string $hello The HELO string 681 * @param string $host The hostname to say we are 682 * @access protected 683 * @return boolean 744 * @param string $host The hostname to say we are 745 * 746 * @return bool 747 * 748 * @see hello() 684 749 */ 685 750 protected function sendHello($hello, $host) 686 751 { … … 691 756 } else { 692 757 $this->server_caps = null; 693 758 } 759 694 760 return $noerror; 695 761 } 696 762 … … 697 763 /** 698 764 * Parse a reply to HELO/EHLO command to discover server extensions. 699 765 * In case of HELO, the only parameter that can be discovered is a server name. 700 * @access protected701 * @param string $type - 'HELO' or 'EHLO'766 * 767 * @param string $type `HELO` or `EHLO` 702 768 */ 703 769 protected function parseHelloFields($type) 704 770 { 705 $this->server_caps = array();771 $this->server_caps = []; 706 772 $lines = explode("\n", $this->helo_rply); 707 773 708 774 foreach ($lines as $n => $s) { … … 724 790 break; 725 791 case 'AUTH': 726 792 if (!is_array($fields)) { 727 $fields = array();793 $fields = []; 728 794 } 729 795 break; 730 796 default: … … 742 808 * $from. Returns true if successful or false otherwise. If True 743 809 * the mail transaction is started and then one or more recipient 744 810 * commands may be called followed by a data command. 745 * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> 811 * Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF>. 812 * 746 813 * @param string $from Source address of this message 747 * @access public748 * @return bool ean814 * 815 * @return bool 749 816 */ 750 817 public function mail($from) 751 818 { 752 819 $useVerp = ($this->do_verp ? ' XVERP' : ''); 820 753 821 return $this->sendCommand( 754 822 'MAIL FROM', 755 823 'MAIL FROM:<' . $from . '>' . $useVerp, … … 760 828 /** 761 829 * Send an SMTP QUIT command. 762 830 * Closes the socket if there is no error or the $close_on_error argument is true. 763 * Implements from rfc 821: QUIT <CRLF> 764 * @param boolean $close_on_error Should the connection close if an error occurs? 765 * @access public 766 * @return boolean 831 * Implements from RFC 821: QUIT <CRLF>. 832 * 833 * @param bool $close_on_error Should the connection close if an error occurs? 834 * 835 * @return bool 767 836 */ 768 837 public function quit($close_on_error = true) 769 838 { … … 773 842 $this->close(); 774 843 $this->error = $err; //Restore any error from the quit command 775 844 } 845 776 846 return $noerror; 777 847 } 778 848 … … 780 850 * Send an SMTP RCPT command. 781 851 * Sets the TO argument to $toaddr. 782 852 * Returns true if the recipient was accepted false if it was rejected. 783 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> 853 * Implements from RFC 821: RCPT <SP> TO:<forward-path> <CRLF>. 854 * 784 855 * @param string $address The address the message is being sent to 785 * @access public786 * @return bool ean856 * 857 * @return bool 787 858 */ 788 859 public function recipient($address) 789 860 { … … 790 861 return $this->sendCommand( 791 862 'RCPT TO', 792 863 'RCPT TO:<' . $address . '>', 793 array(250, 251)864 [250, 251] 794 865 ); 795 866 } 796 867 … … 797 868 /** 798 869 * Send an SMTP RSET command. 799 870 * Abort any transaction that is currently in progress. 800 * Implements rfc 821: RSET <CRLF>801 * @access public802 * @return bool ean True on success.871 * Implements RFC 821: RSET <CRLF>. 872 * 873 * @return bool True on success 803 874 */ 804 875 public function reset() 805 876 { … … 808 879 809 880 /** 810 881 * Send a command to an SMTP server and check its return code. 811 * @param string $command The command name - not sent to the server 812 * @param string $commandstring The actual command to send 813 * @param integer|array $expect One or more expected integer success codes 814 * @access protected 815 * @return boolean True on success. 882 * 883 * @param string $command The command name - not sent to the server 884 * @param string $commandstring The actual command to send 885 * @param int|array $expect One or more expected integer success codes 886 * 887 * @return bool True on success 816 888 */ 817 889 protected function sendCommand($command, $commandstring, $expect) 818 890 { 819 891 if (!$this->connected()) { 820 892 $this->setError("Called $command without being connected"); 893 821 894 return false; 822 895 } 823 896 //Reject line breaks in all commands 824 897 if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { 825 898 $this->setError("Command '$command' contained line breaks"); 899 826 900 return false; 827 901 } 828 $this->client_send($commandstring . s elf::CRLF);902 $this->client_send($commandstring . static::LE, $command); 829 903 830 904 $this->last_reply = $this->get_lines(); 831 905 // Fetch SMTP code and possible error code explanation 832 $matches = array();833 if (preg_match( "/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) {906 $matches = []; 907 if (preg_match('/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/', $this->last_reply, $matches)) { 834 908 $code = $matches[1]; 835 909 $code_ex = (count($matches) > 2 ? $matches[2] : null); 836 910 // Cut off error code from each response line 837 911 $detail = preg_replace( 838 912 "/{$code}[ -]" . 839 ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . "/m",913 ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m', 840 914 '', 841 915 $this->last_reply 842 916 ); … … 849 923 850 924 $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); 851 925 852 if (!in_array($code, (array) $expect)) {926 if (!in_array($code, (array) $expect)) { 853 927 $this->setError( 854 928 "$command command failed", 855 929 $detail, … … 860 934 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, 861 935 self::DEBUG_CLIENT 862 936 ); 937 863 938 return false; 864 939 } 865 940 866 941 $this->setError(''); 942 867 943 return true; 868 944 } 869 945 … … 875 951 * commands may be called followed by a data command. This command 876 952 * will send the message to the users terminal if they are logged 877 953 * in and send them an email. 878 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> 954 * Implements RFC 821: SAML <SP> FROM:<reverse-path> <CRLF>. 955 * 879 956 * @param string $from The address the message is from 880 * @access public881 * @return bool ean957 * 958 * @return bool 882 959 */ 883 960 public function sendAndMail($from) 884 961 { … … 887 964 888 965 /** 889 966 * Send an SMTP VRFY command. 967 * 890 968 * @param string $name The name to verify 891 * @access public892 * @return bool ean969 * 970 * @return bool 893 971 */ 894 972 public function verify($name) 895 973 { 896 return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));974 return $this->sendCommand('VRFY', "VRFY $name", [250, 251]); 897 975 } 898 976 899 977 /** 900 978 * Send an SMTP NOOP command. 901 * Used to keep keep-alives alive, doesn't actually do anything 902 * @access public903 * @return bool ean979 * Used to keep keep-alives alive, doesn't actually do anything. 980 * 981 * @return bool 904 982 */ 905 983 public function noop() 906 984 { … … 911 989 * Send an SMTP TURN command. 912 990 * This is an optional command for SMTP that this class does not support. 913 991 * This method is here to make the RFC821 Definition complete for this class 914 * and _may_ be implemented in future 915 * Implements from rfc 821: TURN <CRLF>916 * @access public917 * @return bool ean992 * and _may_ be implemented in future. 993 * Implements from RFC 821: TURN <CRLF>. 994 * 995 * @return bool 918 996 */ 919 997 public function turn() 920 998 { 921 999 $this->setError('The SMTP TURN command is not implemented'); 922 1000 $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); 1001 923 1002 return false; 924 1003 } 925 1004 926 1005 /** 927 1006 * Send raw data to the server. 928 * @param string $data The data to send 929 * @access public 930 * @return integer|boolean The number of bytes sent to the server or false on error 1007 * 1008 * @param string $data The data to send 1009 * @param string $command Optionally, the command this is part of, used only for controlling debug output 1010 * 1011 * @return int|bool The number of bytes sent to the server or false on error 931 1012 */ 932 public function client_send($data )1013 public function client_send($data, $command = '') 933 1014 { 934 $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); 935 set_error_handler(array($this, 'errorHandler')); 1015 //If SMTP transcripts are left enabled, or debug output is posted online 1016 //it can leak credentials, so hide credentials in all but lowest level 1017 if (self::DEBUG_LOWLEVEL > $this->do_debug and 1018 in_array($command, ['User & Password', 'Username', 'Password'], true)) { 1019 $this->edebug('CLIENT -> SERVER: <credentials hidden>', self::DEBUG_CLIENT); 1020 } else { 1021 $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT); 1022 } 1023 set_error_handler([$this, 'errorHandler']); 936 1024 $result = fwrite($this->smtp_conn, $data); 937 1025 restore_error_handler(); 1026 938 1027 return $result; 939 1028 } 940 1029 941 1030 /** 942 1031 * Get the latest error. 943 * @access public1032 * 944 1033 * @return array 945 1034 */ 946 1035 public function getError() … … 949 1038 } 950 1039 951 1040 /** 952 * Get SMTP extensions available on the server 953 * @access public1041 * Get SMTP extensions available on the server. 1042 * 954 1043 * @return array|null 955 1044 */ 956 1045 public function getServerExtList() … … 959 1048 } 960 1049 961 1050 /** 962 * A multipurpose method 963 * The method works in three ways, dependent on argument value and current state 964 * 1. HELO/EHLO was not sent - returns null and set up $this->error 965 * 2. HELO was sent 966 * $name = 'HELO': returns server name 967 * $name = 'EHLO': returns boolean false 968 * $name = any string: returns null and set up $this->error 969 * 3. EHLO was sent 970 * $name = 'HELO'|'EHLO': returns server name 971 * $name = any string: if extension $name exists, returns boolean True 972 * or its options. Otherwise returns boolean False 973 * In other words, one can use this method to detect 3 conditions: 974 * - null returned: handshake was not or we don't know about ext (refer to $this->error) 975 * - false returned: the requested feature exactly not exists 976 * - positive value returned: the requested feature exists 1051 * Get metadata about the SMTP server from its HELO/EHLO response. 1052 * The method works in three ways, dependent on argument value and current state: 1053 * 1. HELO/EHLO has not been sent - returns null and populates $this->error. 1054 * 2. HELO has been sent - 1055 * $name == 'HELO': returns server name 1056 * $name == 'EHLO': returns boolean false 1057 * $name == any other string: returns null and populates $this->error 1058 * 3. EHLO has been sent - 1059 * $name == 'HELO'|'EHLO': returns the server name 1060 * $name == any other string: if extension $name exists, returns True 1061 * or its options (e.g. AUTH mechanisms supported). Otherwise returns False. 1062 * 977 1063 * @param string $name Name of SMTP extension or 'HELO'|'EHLO' 1064 * 978 1065 * @return mixed 979 1066 */ 980 1067 public function getServerExt($name) … … 981 1068 { 982 1069 if (!$this->server_caps) { 983 1070 $this->setError('No HELO/EHLO was sent'); 984 return null; 1071 1072 return; 985 1073 } 986 1074 987 // the tight logic knot ;)988 1075 if (!array_key_exists($name, $this->server_caps)) { 989 if ( $name == 'HELO') {1076 if ('HELO' == $name) { 990 1077 return $this->server_caps['EHLO']; 991 1078 } 992 if ( $name == 'EHLO'|| array_key_exists('EHLO', $this->server_caps)) {1079 if ('EHLO' == $name || array_key_exists('EHLO', $this->server_caps)) { 993 1080 return false; 994 1081 } 995 $this->setError('HELO handshake was used. Client knows nothing about server extensions'); 996 return null; 1082 $this->setError('HELO handshake was used; No information about server extensions available'); 1083 1084 return; 997 1085 } 998 1086 999 1087 return $this->server_caps[$name]; … … 1001 1089 1002 1090 /** 1003 1091 * Get the last reply from the server. 1004 * @access public1092 * 1005 1093 * @return string 1006 1094 */ 1007 1095 public function getLastReply() … … 1015 1103 * With SMTP we can tell if we have more lines to read if the 1016 1104 * 4th character is '-' symbol. If it is a space then we don't 1017 1105 * need to read anything else. 1018 * @access protected1106 * 1019 1107 * @return string 1020 1108 */ 1021 1109 protected function get_lines() … … 1030 1118 if ($this->Timelimit > 0) { 1031 1119 $endtime = time() + $this->Timelimit; 1032 1120 } 1033 while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 1121 $selR = [$this->smtp_conn]; 1122 $selW = null; 1123 while (is_resource($this->smtp_conn) and !feof($this->smtp_conn)) { 1124 //Must pass vars in here as params are by reference 1125 if (!stream_select($selR, $selW, $selW, $this->Timelimit)) { 1126 $this->edebug( 1127 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', 1128 self::DEBUG_LOWLEVEL 1129 ); 1130 break; 1131 } 1132 //Deliberate noise suppression - errors are handled afterwards 1034 1133 $str = @fgets($this->smtp_conn, 515); 1035 $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); 1036 $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); 1134 $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL); 1037 1135 $data .= $str; 1038 1136 // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled), 1039 1137 // or 4th character is a space, we are done reading, break the loop, … … 1060 1158 break; 1061 1159 } 1062 1160 } 1161 1063 1162 return $data; 1064 1163 } 1065 1164 1066 1165 /** 1067 1166 * Enable or disable VERP address generation. 1068 * @param boolean $enabled 1167 * 1168 * @param bool $enabled 1069 1169 */ 1070 1170 public function setVerp($enabled = false) 1071 1171 { … … 1074 1174 1075 1175 /** 1076 1176 * Get VERP address generation mode. 1077 * @return boolean 1177 * 1178 * @return bool 1078 1179 */ 1079 1180 public function getVerp() 1080 1181 { … … 1083 1184 1084 1185 /** 1085 1186 * Set error messages and codes. 1086 * @param string $message The error message 1087 * @param string $detail Further detail on the error 1088 * @param string $smtp_code An associated SMTP error code 1187 * 1188 * @param string $message The error message 1189 * @param string $detail Further detail on the error 1190 * @param string $smtp_code An associated SMTP error code 1089 1191 * @param string $smtp_code_ex Extended SMTP code 1090 1192 */ 1091 1193 protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') 1092 1194 { 1093 $this->error = array(1195 $this->error = [ 1094 1196 'error' => $message, 1095 1197 'detail' => $detail, 1096 1198 'smtp_code' => $smtp_code, 1097 'smtp_code_ex' => $smtp_code_ex 1098 );1199 'smtp_code_ex' => $smtp_code_ex, 1200 ]; 1099 1201 } 1100 1202 1101 1203 /** 1102 1204 * Set debug output method. 1103 * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. 1205 * 1206 * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it 1104 1207 */ 1105 1208 public function setDebugOutput($method = 'echo') 1106 1209 { … … 1109 1212 1110 1213 /** 1111 1214 * Get debug output method. 1215 * 1112 1216 * @return string 1113 1217 */ 1114 1218 public function getDebugOutput() … … 1118 1222 1119 1223 /** 1120 1224 * Set debug output level. 1121 * @param integer $level 1225 * 1226 * @param int $level 1122 1227 */ 1123 1228 public function setDebugLevel($level = 0) 1124 1229 { … … 1127 1232 1128 1233 /** 1129 1234 * Get debug output level. 1130 * @return integer 1235 * 1236 * @return int 1131 1237 */ 1132 1238 public function getDebugLevel() 1133 1239 { … … 1136 1242 1137 1243 /** 1138 1244 * Set SMTP timeout. 1139 * @param integer $timeout 1245 * 1246 * @param int $timeout The timeout duration in seconds 1140 1247 */ 1141 1248 public function setTimeout($timeout = 0) 1142 1249 { … … 1145 1252 1146 1253 /** 1147 1254 * Get SMTP timeout. 1148 * @return integer 1255 * 1256 * @return int 1149 1257 */ 1150 1258 public function getTimeout() 1151 1259 { … … 1154 1262 1155 1263 /** 1156 1264 * Reports an error number and string. 1157 * @param integer $errno The error number returned by PHP. 1158 * @param string $errmsg The error message returned by PHP. 1265 * 1266 * @param int $errno The error number returned by PHP 1267 * @param string $errmsg The error message returned by PHP 1159 1268 * @param string $errfile The file the error occurred in 1160 * @param int eger$errline The line number the error occurred on1269 * @param int $errline The line number the error occurred on 1161 1270 */ 1162 1271 protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0) 1163 1272 { … … 1164 1273 $notice = 'Connection failed.'; 1165 1274 $this->setError( 1166 1275 $notice, 1167 $err no,1168 $errmsg1276 $errmsg, 1277 (string) $errno 1169 1278 ); 1170 1279 $this->edebug( 1171 $notice . ' Error #' . $errno . ': ' . $errmsg . "[$errfile line $errline]",1280 "$notice Error #$errno: $errmsg [$errfile line $errline]", 1172 1281 self::DEBUG_CONNECTION 1173 1282 ); 1174 1283 } … … 1179 1288 * Relies on the host providing the ID in response to a DATA command. 1180 1289 * If no reply has been received yet, it will return null. 1181 1290 * If no pattern was matched, it will return false. 1291 * 1182 1292 * @return bool|null|string 1183 1293 */ 1184 1294 protected function recordLastTransactionID() … … 1191 1301 $this->last_smtp_transaction_id = false; 1192 1302 foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { 1193 1303 if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) { 1194 $this->last_smtp_transaction_id = $matches[1]; 1304 $this->last_smtp_transaction_id = trim($matches[1]); 1305 break; 1195 1306 } 1196 1307 } 1197 1308 } … … 1203 1314 * Get the queue/transaction ID of the last SMTP transaction 1204 1315 * If no reply has been received yet, it will return null. 1205 1316 * If no pattern was matched, it will return false. 1317 * 1206 1318 * @return bool|null|string 1319 * 1207 1320 * @see recordLastTransactionID() 1208 1321 */ 1209 1322 public function getLastTransactionID() -
src/wp-includes/pluggable.php
210 210 global $phpmailer; 211 211 212 212 // (Re)create it, if it's gone missing 213 if ( ! ( $phpmailer instanceof PHPMailer ) ) {213 if ( ! ( $phpmailer instanceof PHPMailer\PHPMailer\PHPMailer ) ) { 214 214 require_once ABSPATH . WPINC . '/class-phpmailer.php'; 215 215 require_once ABSPATH . WPINC . '/class-smtp.php'; 216 $phpmailer = new PHPMailer ( true );216 $phpmailer = new PHPMailer\PHPMailer\PHPMailer( true ); 217 217 } 218 218 219 219 // Headers