Ticket #41750: 41750.2.diff
File 41750.2.diff, 242.1 KB (added by , 5 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 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'; 35 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. 242 * @var string264 * 243 265 * @see PHPMailer::$Hostname 266 * 267 * @var string 244 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 auth 299 * @var string 300 */ 301 public $Realm = ''; 302 303 /** 304 * SMTP workstation. 305 * Used for NTLM auth 306 * @var string 329 * An instance of the PHPMailer OAuth class. 330 * 331 * @var OAuth 307 332 */ 308 p ublic $Workstation = '';333 protected $oauth; 309 334 310 335 /** 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 337 * 365 * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise. 338 366 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 339 * <code> 367 * 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. 518 * @var array 519 * @access protected 520 * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc 586 * 587 * @see PHPMailer::$to 588 * @see PHPMailer::$cc 589 * @see PHPMailer::$bcc 521 590 * @see PHPMailer::$all_recipients 591 * 592 * @var array 522 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. 529 * @var array 530 * @access protected 600 * 531 601 * @see PHPMailer::$ReplyTo 602 * 603 * @var array 532 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); 1394 } 1395 fputs($mail, 'To: ' . $toAddr . "\n"); 1396 fputs($mail, $header); 1397 fputs($mail, $body); 1571 $mail = @popen($sendmail, 'w'); 1572 if (!$mail) { 1573 throw new \Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1574 } 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. 1437 *1438 1621 * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. 1439 * @param string $string The string to be validated1622 * 1440 1623 * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report 1441 * @access protected 1442 * @return boolean 1624 * 1625 * @param string $string The string to be validated 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 1739 return $this->smtp; 1740 } 1741 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 1540 1753 return $this->smtp; 1541 1754 } 1542 1755 1543 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)) { 1562 $smtp_from = $this->Sender; 1563 } else { 1777 //Sender already validated in preSend() 1778 if ('' == $this->Sender) { 1564 1779 $smtp_from = $this->From; 1780 } else { 1781 $smtp_from = $this->Sender; 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 (!self::isPermittedPath($path) or !file_exists($path)) { 2713 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); 2714 } 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 } 2994 if (!static::isPermittedPath($path) || !file_exists($path)) { 2995 throw new \Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); 2725 2996 } 2726 2997 $file_buffer = file_get_contents($path); 2727 $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 } 2998 if (false === $file_buffer) { 2999 throw new \Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); 2734 3000 } 3001 $file_buffer = $this->encodeString($file_buffer, $encoding); 3002 2735 3003 return $file_buffer; 2736 } catch ( Exception $exc) {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); 2934 } 2935 2936 /** 2937 * Backward compatibility wrapper for an old QP encoding function that was removed. 2938 * @see PHPMailer::encodeQP() 2939 * @access public 2940 * @param string $string 2941 * @param integer $line_max 2942 * @param boolean $space_conv 2943 * @return string 2944 * @deprecated Use encodeQP instead. 2945 */ 2946 public function encodeQPphp( 2947 $string, 2948 $line_max = 76, 2949 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false 2950 ) { 2951 return $this->encodeQP($string, $line_max); 3222 return static::normalizeBreaks(quoted_printable_encode($string)); 2952 3223 } 2953 3224 2954 3225 /** 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); 3356 } 3357 3358 /** 3359 * Ensure consistent line endings in a string. 3360 * Changes every end of line from CRLF, CR or LF to $this->LE. 3361 * @access public 3362 * @param string $str String to fixEOL 3363 * @return string 3364 */ 3365 public function fixEOL($str) 3366 { 3367 // Normalise to \n 3368 $nstr = str_replace(array("\r\n", "\r"), "\n", $str); 3369 // Now convert LE as needed 3370 if ($this->LE !== "\n") { 3371 $nstr = str_replace("\n", $this->LE, $nstr); 3372 } 3373 return $nstr; 3694 return $this->error_count > 0; 3374 3695 } 3375 3696 3376 3697 /** 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' 3537 'js' 3538 'hqx' 3539 'cpt' 3540 'bin' 3541 'doc' 3542 'word' 3543 'xlsx' 3544 'xltx' 3545 'potx' 3546 'ppsx' 3547 'pptx' 3548 'sldx' 3549 'docx' 3550 'dotx' 3551 'xlam' 3552 'xlsb' 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' 3555 'dms' 3556 'exe' 3557 'lha' 3558 'lzh' 3559 'psd' 3560 'sea' 3561 'so' 3562 'oda' 3563 'pdf' 3564 'ai' 3565 'eps' 3566 'ps' 3567 'smi' 3568 'smil' 3569 'mif' 3570 'xls' 3571 'ppt' 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' 3574 'dcr' 3575 'dir' 3576 'dxr' 3577 'dvi' 3578 'gtar' 3579 'php3' 3580 'php4' 3581 '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' 3584 'swf' 3585 'sit' 3586 'tar' 3587 'tgz' 3588 'xht' 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' 3618 'text' 3619 'txt' 3620 'rtx' 3621 'rtf' 3622 'vcf' 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 * 4107 * @return string 4108 */ 4109 public static function normalizeBreaks($text, $breaktype = null) 4110 { 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; 4122 } 4123 4124 /** 4125 * Return the current line break format string. 4126 * 3748 4127 * @return string 3749 * @access public3750 * @static3751 4128 */ 3752 public static function normalizeBreaks($text, $breaktype = "\r\n") 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) 3753 4140 { 3754 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);4141 static::$LE = $le; 3755 4142 } 3756 4143 3757 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); 3830 3831 if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { 3832 openssl_pkey_free($privKey); 3833 return base64_encode($signature); 3834 } 4209 if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { 4210 openssl_pkey_free($privKey); 4211 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 4487 */ 4488 public function getOAuth() 4489 { 4490 return $this->oauth; 4491 } 4492 4493 /** 4494 * Set an OAuth instance. 4495 * 4496 * @param OAuth $oauth 4056 4497 */ 4057 public function errorMessage()4498 public function setOAuth(OAuth $oauth) 4058 4499 { 4059 $errorMsg = '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n"; 4060 return $errorMsg; 4500 $this->oauth = $oauth; 4061 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 120 *121 104 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 122 * <code> 105 * 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 -
tests/phpunit/includes/mock-mailer.php
1 1 <?php 2 2 require_once( ABSPATH . '/wp-includes/class-phpmailer.php' ); 3 3 4 class MockPHPMailer extends PHPMailer {4 class MockPHPMailer extends PHPMailer\PHPMailer\PHPMailer { 5 5 var $mock_sent = array(); 6 6 7 7 function preSend() {