fix php 5.6 in docker dev env (#1740)
[openemr.git] / vendor / phpmailer / phpmailer / class.phpmailer.php
blobf9013ebb1744a9f43cd8a1bde0e9f3793d16d7ed
1 <?php
2 /**
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
12 * @copyright 2010 - 2012 Jim Jagielski
13 * @copyright 2004 - 2009 Andy Prevost
14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15 * @note This program is distributed in the hope that it will be useful - WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE.
20 /**
21 * PHPMailer - PHP email creation and transport class.
22 * @package PHPMailer
23 * @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)
28 class PHPMailer
30 /**
31 * The PHPMailer Version number.
32 * @var string
34 public $Version = '5.2.16';
36 /**
37 * Email priority.
38 * Options: null (default), 1 = High, 3 = Normal, 5 = low.
39 * When null, the header is not set at all.
40 * @var integer
42 public $Priority = null;
44 /**
45 * The character set of the message.
46 * @var string
48 public $CharSet = 'iso-8859-1';
50 /**
51 * The MIME Content-type of the message.
52 * @var string
54 public $ContentType = 'text/plain';
56 /**
57 * The message encoding.
58 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59 * @var string
61 public $Encoding = '8bit';
63 /**
64 * Holds the most recent mailer error message.
65 * @var string
67 public $ErrorInfo = '';
69 /**
70 * The From email address for the message.
71 * @var string
73 public $From = 'root@localhost';
75 /**
76 * The From name of the message.
77 * @var string
79 public $FromName = 'Root User';
81 /**
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.
84 * @var string
86 public $Sender = '';
88 /**
89 * The Return-Path of the message.
90 * If empty, it will be set to either From or Sender.
91 * @var string
92 * @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 reference
96 public $ReturnPath = '';
98 /**
99 * The Subject of the message.
100 * @var string
102 public $Subject = '';
105 * An HTML or plain text message body.
106 * If HTML then call isHTML(true).
107 * @var string
109 public $Body = '';
112 * The plain-text message body.
113 * This body can be read by mail clients that do not have HTML email
114 * capability such as mutt & Eudora.
115 * Clients that can read HTML will view the normal Body.
116 * @var string
118 public $AltBody = '';
121 * An iCal message part body.
122 * 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/
126 * @var string
128 public $Ical = '';
131 * The complete compiled MIME message body.
132 * @access protected
133 * @var string
135 protected $MIMEBody = '';
138 * The complete compiled MIME message headers.
139 * @var string
140 * @access protected
142 protected $MIMEHeader = '';
145 * Extra headers that createHeader() doesn't fold in.
146 * @var string
147 * @access protected
149 protected $mailHeader = '';
152 * Word-wrap the message body to this number of chars.
153 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
154 * @var integer
156 public $WordWrap = 0;
159 * Which method to use to send mail.
160 * Options: "mail", "sendmail", or "smtp".
161 * @var string
163 public $Mailer = 'mail';
166 * The path to the sendmail program.
167 * @var string
169 public $Sendmail = '/usr/sbin/sendmail';
172 * Whether mail() uses a fully sendmail-compatible MTA.
173 * One which supports sendmail's "-oi -f" options.
174 * @var boolean
176 public $UseSendmailOptions = true;
179 * Path to PHPMailer plugins.
180 * Useful if the SMTP class is not in the PHP include path.
181 * @var string
182 * @deprecated Should not be needed now there is an autoloader.
184 public $PluginDir = '';
187 * The email address that a reading confirmation should be sent to, also known as read receipt.
188 * @var string
190 public $ConfirmReadingTo = '';
193 * The hostname to use in the Message-ID header and as default HELO string.
194 * If empty, PHPMailer attempts to find one with, in order,
195 * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
196 * 'localhost.localdomain'.
197 * @var string
199 public $Hostname = '';
202 * An ID to be used in the Message-ID header.
203 * If empty, a unique id will be generated.
204 * @var string
206 public $MessageID = '';
209 * The message Date to be used in the Date header.
210 * If empty, the current date will be added.
211 * @var string
213 public $MessageDate = '';
216 * SMTP hosts.
217 * Either a single hostname or multiple semicolon-delimited hostnames.
218 * You can also specify a different port
219 * for each host by using this format: [hostname:port]
220 * (e.g. "smtp1.example.com:25;smtp2.example.com").
221 * You can also specify encryption type, for example:
222 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
223 * Hosts will be tried in order.
224 * @var string
226 public $Host = 'localhost';
229 * The default SMTP server port.
230 * @var integer
231 * @TODO Why is this needed when the SMTP class takes care of it?
233 public $Port = 25;
236 * The SMTP HELO of the message.
237 * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
238 * one with the same method described above for $Hostname.
239 * @var string
240 * @see PHPMailer::$Hostname
242 public $Helo = '';
245 * What kind of encryption to use on the SMTP connection.
246 * Options: '', 'ssl' or 'tls'
247 * @var string
249 public $SMTPSecure = '';
252 * Whether to enable TLS encryption automatically if a server supports it,
253 * even if `SMTPSecure` is not set to 'tls'.
254 * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
255 * @var boolean
257 public $SMTPAutoTLS = true;
260 * Whether to use SMTP authentication.
261 * Uses the Username and Password properties.
262 * @var boolean
263 * @see PHPMailer::$Username
264 * @see PHPMailer::$Password
266 public $SMTPAuth = false;
269 * Options array passed to stream_context_create when connecting via SMTP.
270 * @var array
272 public $SMTPOptions = array();
275 * SMTP username.
276 * @var string
278 public $Username = '';
281 * SMTP password.
282 * @var string
284 public $Password = '';
287 * SMTP auth type.
288 * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified
289 * @var string
291 public $AuthType = '';
294 * SMTP realm.
295 * Used for NTLM auth
296 * @var string
298 public $Realm = '';
301 * SMTP workstation.
302 * Used for NTLM auth
303 * @var string
305 public $Workstation = '';
308 * The SMTP server timeout in seconds.
309 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
310 * @var integer
312 public $Timeout = 300;
315 * SMTP class debug output mode.
316 * Debug output level.
317 * Options:
318 * * `0` No output
319 * * `1` Commands
320 * * `2` Data and commands
321 * * `3` As 2 plus connection status
322 * * `4` Low-level data output
323 * @var integer
324 * @see SMTP::$do_debug
326 public $SMTPDebug = 0;
329 * How to handle debug output.
330 * Options:
331 * * `echo` Output plain-text as-is, appropriate for CLI
332 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
333 * * `error_log` Output to error log as configured in php.ini
335 * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
336 * <code>
337 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
338 * </code>
339 * @var string|callable
340 * @see SMTP::$Debugoutput
342 public $Debugoutput = 'echo';
345 * Whether to keep SMTP connection open after each message.
346 * If this is set to true then to close the connection
347 * requires an explicit call to smtpClose().
348 * @var boolean
350 public $SMTPKeepAlive = false;
353 * Whether to split multiple to addresses into multiple messages
354 * or send them all in one message.
355 * Only supported in `mail` and `sendmail` transports, not in SMTP.
356 * @var boolean
358 public $SingleTo = false;
361 * Storage for addresses when SingleTo is enabled.
362 * @var array
363 * @TODO This should really not be public
365 public $SingleToArray = array();
368 * Whether to generate VERP addresses on send.
369 * Only applicable when sending via SMTP.
370 * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
371 * @link http://www.postfix.org/VERP_README.html Postfix VERP info
372 * @var boolean
374 public $do_verp = false;
377 * Whether to allow sending messages with an empty body.
378 * @var boolean
380 public $AllowEmpty = false;
383 * The default line ending.
384 * @note The default remains "\n". We force CRLF where we know
385 * it must be used via self::CRLF.
386 * @var string
388 public $LE = "\n";
391 * DKIM selector.
392 * @var string
394 public $DKIM_selector = '';
397 * DKIM Identity.
398 * Usually the email address used as the source of the email.
399 * @var string
401 public $DKIM_identity = '';
404 * DKIM passphrase.
405 * Used if your key is encrypted.
406 * @var string
408 public $DKIM_passphrase = '';
411 * DKIM signing domain name.
412 * @example 'example.com'
413 * @var string
415 public $DKIM_domain = '';
418 * DKIM private key file path.
419 * @var string
421 public $DKIM_private = '';
424 * Callback Action function name.
426 * The function that handles the result of the send email action.
427 * It is called out by send() for each email sent.
429 * Value can be any php callable: http://www.php.net/is_callable
431 * Parameters:
432 * boolean $result result of the send action
433 * string $to email address of the recipient
434 * string $cc cc email addresses
435 * string $bcc bcc email addresses
436 * string $subject the subject
437 * string $body the email body
438 * string $from email address of sender
439 * @var string
441 public $action_function = '';
444 * What to put in the X-Mailer header.
445 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
446 * @var string
448 public $XMailer = '';
451 * Which validator to use by default when validating email addresses.
452 * May be a callable to inject your own validator, but there are several built-in validators.
453 * @see PHPMailer::validateAddress()
454 * @var string|callable
455 * @static
457 public static $validator = 'auto';
460 * An instance of the SMTP sender class.
461 * @var SMTP
462 * @access protected
464 protected $smtp = null;
467 * The array of 'to' names and addresses.
468 * @var array
469 * @access protected
471 protected $to = array();
474 * The array of 'cc' names and addresses.
475 * @var array
476 * @access protected
478 protected $cc = array();
481 * The array of 'bcc' names and addresses.
482 * @var array
483 * @access protected
485 protected $bcc = array();
488 * The array of reply-to names and addresses.
489 * @var array
490 * @access protected
492 protected $ReplyTo = array();
495 * An array of all kinds of addresses.
496 * Includes all of $to, $cc, $bcc
497 * @var array
498 * @access protected
499 * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
501 protected $all_recipients = array();
504 * An array of names and addresses queued for validation.
505 * In send(), valid and non duplicate entries are moved to $all_recipients
506 * and one of $to, $cc, or $bcc.
507 * This array is used only for addresses with IDN.
508 * @var array
509 * @access protected
510 * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
511 * @see PHPMailer::$all_recipients
513 protected $RecipientsQueue = array();
516 * An array of reply-to names and addresses queued for validation.
517 * In send(), valid and non duplicate entries are moved to $ReplyTo.
518 * This array is used only for addresses with IDN.
519 * @var array
520 * @access protected
521 * @see PHPMailer::$ReplyTo
523 protected $ReplyToQueue = array();
526 * The array of attachments.
527 * @var array
528 * @access protected
530 protected $attachment = array();
533 * The array of custom headers.
534 * @var array
535 * @access protected
537 protected $CustomHeader = array();
540 * The most recent Message-ID (including angular brackets).
541 * @var string
542 * @access protected
544 protected $lastMessageID = '';
547 * The message's MIME type.
548 * @var string
549 * @access protected
551 protected $message_type = '';
554 * The array of MIME boundary strings.
555 * @var array
556 * @access protected
558 protected $boundary = array();
561 * The array of available languages.
562 * @var array
563 * @access protected
565 protected $language = array();
568 * The number of errors encountered.
569 * @var integer
570 * @access protected
572 protected $error_count = 0;
575 * The S/MIME certificate file path.
576 * @var string
577 * @access protected
579 protected $sign_cert_file = '';
582 * The S/MIME key file path.
583 * @var string
584 * @access protected
586 protected $sign_key_file = '';
589 * The optional S/MIME extra certificates ("CA Chain") file path.
590 * @var string
591 * @access protected
593 protected $sign_extracerts_file = '';
596 * The S/MIME password for the key.
597 * Used only if the key is encrypted.
598 * @var string
599 * @access protected
601 protected $sign_key_pass = '';
604 * Whether to throw exceptions for errors.
605 * @var boolean
606 * @access protected
608 protected $exceptions = false;
611 * Unique ID used for message ID and boundaries.
612 * @var string
613 * @access protected
615 protected $uniqueid = '';
618 * Error severity: message only, continue processing.
620 const STOP_MESSAGE = 0;
623 * Error severity: message, likely ok to continue processing.
625 const STOP_CONTINUE = 1;
628 * Error severity: message, plus full stop, critical error reached.
630 const STOP_CRITICAL = 2;
633 * SMTP RFC standard line ending.
635 const CRLF = "\r\n";
638 * The maximum line length allowed by RFC 2822 section 2.1.1
639 * @var integer
641 const MAX_LINE_LENGTH = 998;
644 * Constructor.
645 * @param boolean $exceptions Should we throw external exceptions?
647 public function __construct($exceptions = null)
649 if ($exceptions !== null) {
650 $this->exceptions = (boolean)$exceptions;
655 * Destructor.
657 public function __destruct()
659 //Close any open SMTP connection nicely
660 $this->smtpClose();
664 * Call mail() in a safe_mode-aware fashion.
665 * Also, unless sendmail_path points to sendmail (or something that
666 * claims to be sendmail), don't pass params (not a perfect fix,
667 * but it will do)
668 * @param string $to To
669 * @param string $subject Subject
670 * @param string $body Message Body
671 * @param string $header Additional Header(s)
672 * @param string $params Params
673 * @access private
674 * @return boolean
676 private function mailPassthru($to, $subject, $body, $header, $params)
678 //Check overloading of mail function to avoid double-encoding
679 if (ini_get('mbstring.func_overload') & 1) {
680 $subject = $this->secureHeader($subject);
681 } else {
682 $subject = $this->encodeHeader($this->secureHeader($subject));
684 //Can't use additional_parameters in safe_mode
685 //@link http://php.net/manual/en/function.mail.php
686 if (ini_get('safe_mode') or !$this->UseSendmailOptions) {
687 $result = @mail($to, $subject, $body, $header);
688 } else {
689 $result = @mail($to, $subject, $body, $header, $params);
691 return $result;
695 * Output debugging info via user-defined method.
696 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
697 * @see PHPMailer::$Debugoutput
698 * @see PHPMailer::$SMTPDebug
699 * @param string $str
701 protected function edebug($str)
703 if ($this->SMTPDebug <= 0) {
704 return;
706 //Avoid clash with built-in function names
707 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
708 call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
709 return;
711 switch ($this->Debugoutput) {
712 case 'error_log':
713 //Don't output, just log
714 error_log($str);
715 break;
716 case 'html':
717 //Cleans up output a bit for a better looking, HTML-safe output
718 echo htmlentities(
719 preg_replace('/[\r\n]+/', '', $str),
720 ENT_QUOTES,
721 'UTF-8'
723 . "<br>\n";
724 break;
725 case 'echo':
726 default:
727 //Normalize line breaks
728 $str = preg_replace('/\r\n?/ms', "\n", $str);
729 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
730 "\n",
731 "\n \t ",
732 trim($str)
733 ) . "\n";
738 * Sets message type to HTML or plain.
739 * @param boolean $isHtml True for HTML mode.
740 * @return void
742 public function isHTML($isHtml = true)
744 if ($isHtml) {
745 $this->ContentType = 'text/html';
746 } else {
747 $this->ContentType = 'text/plain';
752 * Send messages using SMTP.
753 * @return void
755 public function isSMTP()
757 $this->Mailer = 'smtp';
761 * Send messages using PHP's mail() function.
762 * @return void
764 public function isMail()
766 $this->Mailer = 'mail';
770 * Send messages using $Sendmail.
771 * @return void
773 public function isSendmail()
775 $ini_sendmail_path = ini_get('sendmail_path');
777 if (!stristr($ini_sendmail_path, 'sendmail')) {
778 $this->Sendmail = '/usr/sbin/sendmail';
779 } else {
780 $this->Sendmail = $ini_sendmail_path;
782 $this->Mailer = 'sendmail';
786 * Send messages using qmail.
787 * @return void
789 public function isQmail()
791 $ini_sendmail_path = ini_get('sendmail_path');
793 if (!stristr($ini_sendmail_path, 'qmail')) {
794 $this->Sendmail = '/var/qmail/bin/qmail-inject';
795 } else {
796 $this->Sendmail = $ini_sendmail_path;
798 $this->Mailer = 'qmail';
802 * Add a "To" address.
803 * @param string $address The email address to send to
804 * @param string $name
805 * @return boolean true on success, false if address already used or invalid in some way
807 public function addAddress($address, $name = '')
809 return $this->addOrEnqueueAnAddress('to', $address, $name);
813 * Add a "CC" address.
814 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
815 * @param string $address The email address to send to
816 * @param string $name
817 * @return boolean true on success, false if address already used or invalid in some way
819 public function addCC($address, $name = '')
821 return $this->addOrEnqueueAnAddress('cc', $address, $name);
825 * Add a "BCC" address.
826 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
827 * @param string $address The email address to send to
828 * @param string $name
829 * @return boolean true on success, false if address already used or invalid in some way
831 public function addBCC($address, $name = '')
833 return $this->addOrEnqueueAnAddress('bcc', $address, $name);
837 * Add a "Reply-To" address.
838 * @param string $address The email address to reply to
839 * @param string $name
840 * @return boolean true on success, false if address already used or invalid in some way
842 public function addReplyTo($address, $name = '')
844 return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
848 * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
849 * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
850 * be modified after calling this function), addition of such addresses is delayed until send().
851 * Addresses that have been added already return false, but do not throw exceptions.
852 * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
853 * @param string $address The email address to send, resp. to reply to
854 * @param string $name
855 * @throws phpmailerException
856 * @return boolean true on success, false if address already used or invalid in some way
857 * @access protected
859 protected function addOrEnqueueAnAddress($kind, $address, $name)
861 $address = trim($address);
862 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
863 if (($pos = strrpos($address, '@')) === false) {
864 // At-sign is misssing.
865 $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
866 $this->setError($error_message);
867 $this->edebug($error_message);
868 if ($this->exceptions) {
869 throw new phpmailerException($error_message);
871 return false;
873 $params = array($kind, $address, $name);
874 // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
875 if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
876 if ($kind != 'Reply-To') {
877 if (!array_key_exists($address, $this->RecipientsQueue)) {
878 $this->RecipientsQueue[$address] = $params;
879 return true;
881 } else {
882 if (!array_key_exists($address, $this->ReplyToQueue)) {
883 $this->ReplyToQueue[$address] = $params;
884 return true;
887 return false;
889 // Immediately add standard addresses without IDN.
890 return call_user_func_array(array($this, 'addAnAddress'), $params);
894 * Add an address to one of the recipient arrays or to the ReplyTo array.
895 * Addresses that have been added already return false, but do not throw exceptions.
896 * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
897 * @param string $address The email address to send, resp. to reply to
898 * @param string $name
899 * @throws phpmailerException
900 * @return boolean true on success, false if address already used or invalid in some way
901 * @access protected
903 protected function addAnAddress($kind, $address, $name = '')
905 if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
906 $error_message = $this->lang('Invalid recipient kind: ') . $kind;
907 $this->setError($error_message);
908 $this->edebug($error_message);
909 if ($this->exceptions) {
910 throw new phpmailerException($error_message);
912 return false;
914 if (!$this->validateAddress($address)) {
915 $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
916 $this->setError($error_message);
917 $this->edebug($error_message);
918 if ($this->exceptions) {
919 throw new phpmailerException($error_message);
921 return false;
923 if ($kind != 'Reply-To') {
924 if (!array_key_exists(strtolower($address), $this->all_recipients)) {
925 array_push($this->$kind, array($address, $name));
926 $this->all_recipients[strtolower($address)] = true;
927 return true;
929 } else {
930 if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
931 $this->ReplyTo[strtolower($address)] = array($address, $name);
932 return true;
935 return false;
939 * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
940 * of the form "display name <address>" into an array of name/address pairs.
941 * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
942 * Note that quotes in the name part are removed.
943 * @param string $addrstr The address list string
944 * @param bool $useimap Whether to use the IMAP extension to parse the list
945 * @return array
946 * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
948 public function parseAddresses($addrstr, $useimap = true)
950 $addresses = array();
951 if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
952 //Use this built-in parser if it's available
953 $list = imap_rfc822_parse_adrlist($addrstr, '');
954 foreach ($list as $address) {
955 if ($address->host != '.SYNTAX-ERROR.') {
956 if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
957 $addresses[] = array(
958 'name' => (property_exists($address, 'personal') ? $address->personal : ''),
959 'address' => $address->mailbox . '@' . $address->host
964 } else {
965 //Use this simpler parser
966 $list = explode(',', $addrstr);
967 foreach ($list as $address) {
968 $address = trim($address);
969 //Is there a separate name part?
970 if (strpos($address, '<') === false) {
971 //No separate name, just use the whole thing
972 if ($this->validateAddress($address)) {
973 $addresses[] = array(
974 'name' => '',
975 'address' => $address
978 } else {
979 list($name, $email) = explode('<', $address);
980 $email = trim(str_replace('>', '', $email));
981 if ($this->validateAddress($email)) {
982 $addresses[] = array(
983 'name' => trim(str_replace(array('"', "'"), '', $name)),
984 'address' => $email
990 return $addresses;
994 * Set the From and FromName properties.
995 * @param string $address
996 * @param string $name
997 * @param boolean $auto Whether to also set the Sender address, defaults to true
998 * @throws phpmailerException
999 * @return boolean
1001 public function setFrom($address, $name = '', $auto = true)
1003 $address = trim($address);
1004 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
1005 // Don't validate now addresses with IDN. Will be done in send().
1006 if (($pos = strrpos($address, '@')) === false or
1007 (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
1008 !$this->validateAddress($address)) {
1009 $error_message = $this->lang('invalid_address') . " (setFrom) $address";
1010 $this->setError($error_message);
1011 $this->edebug($error_message);
1012 if ($this->exceptions) {
1013 throw new phpmailerException($error_message);
1015 return false;
1017 $this->From = $address;
1018 $this->FromName = $name;
1019 if ($auto) {
1020 if (empty($this->Sender)) {
1021 $this->Sender = $address;
1024 return true;
1028 * Return the Message-ID header of the last email.
1029 * Technically this is the value from the last time the headers were created,
1030 * but it's also the message ID of the last sent message except in
1031 * pathological cases.
1032 * @return string
1034 public function getLastMessageID()
1036 return $this->lastMessageID;
1040 * Check that a string looks like an email address.
1041 * @param string $address The email address to check
1042 * @param string|callable $patternselect A selector for the validation pattern to use :
1043 * * `auto` Pick best pattern automatically;
1044 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
1045 * * `pcre` Use old PCRE implementation;
1046 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1047 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1048 * * `noregex` Don't use a regex: super fast, really dumb.
1049 * Alternatively you may pass in a callable to inject your own validator, for example:
1050 * PHPMailer::validateAddress('user@example.com', function($address) {
1051 * return (strpos($address, '@') !== false);
1052 * });
1053 * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
1054 * @return boolean
1055 * @static
1056 * @access public
1058 public static function validateAddress($address, $patternselect = null)
1060 if (is_null($patternselect)) {
1061 $patternselect = self::$validator;
1063 if (is_callable($patternselect)) {
1064 return call_user_func($patternselect, $address);
1066 //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1067 if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1068 return false;
1070 if (!$patternselect or $patternselect == 'auto') {
1071 //Check this constant first so it works when extension_loaded() is disabled by safe mode
1072 //Constant was added in PHP 5.2.4
1073 if (defined('PCRE_VERSION')) {
1074 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
1075 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
1076 $patternselect = 'pcre8';
1077 } else {
1078 $patternselect = 'pcre';
1080 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
1081 //Fall back to older PCRE
1082 $patternselect = 'pcre';
1083 } else {
1084 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1085 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1086 $patternselect = 'php';
1087 } else {
1088 $patternselect = 'noregex';
1092 switch ($patternselect) {
1093 case 'pcre8':
1095 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1096 * @link http://squiloople.com/2009/12/20/email-address-validation/
1097 * @copyright 2009-2010 Michael Rushton
1098 * Feel free to use and redistribute this code. But please keep this copyright notice.
1100 return (boolean)preg_match(
1101 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1102 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1103 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1104 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1105 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1106 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1107 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1108 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1109 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1110 $address
1112 case 'pcre':
1113 //An older regex that doesn't need a recent PCRE
1114 return (boolean)preg_match(
1115 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1116 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1117 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1118 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1119 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1120 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1121 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1122 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1123 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1124 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1125 $address
1127 case 'html5':
1129 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1130 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1132 return (boolean)preg_match(
1133 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1134 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1135 $address
1137 case 'noregex':
1138 //No PCRE! Do something _very_ approximate!
1139 //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1140 return (strlen($address) >= 3
1141 and strpos($address, '@') >= 1
1142 and strpos($address, '@') != strlen($address) - 1);
1143 case 'php':
1144 default:
1145 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
1150 * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1151 * "intl" and "mbstring" PHP extensions.
1152 * @return bool "true" if required functions for IDN support are present
1154 public function idnSupported()
1156 // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1157 return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1161 * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1162 * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1163 * This function silently returns unmodified address if:
1164 * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1165 * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1166 * or fails for any reason (e.g. domain has characters not allowed in an IDN)
1167 * @see PHPMailer::$CharSet
1168 * @param string $address The email address to convert
1169 * @return string The encoded address in ASCII form
1171 public function punyencodeAddress($address)
1173 // Verify we have required functions, CharSet, and at-sign.
1174 if ($this->idnSupported() and
1175 !empty($this->CharSet) and
1176 ($pos = strrpos($address, '@')) !== false) {
1177 $domain = substr($address, ++$pos);
1178 // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1179 if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1180 $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1181 if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1182 idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
1183 idn_to_ascii($domain)) !== false) {
1184 return substr($address, 0, $pos) . $punycode;
1188 return $address;
1192 * Create a message and send it.
1193 * Uses the sending method specified by $Mailer.
1194 * @throws phpmailerException
1195 * @return boolean false on error - See the ErrorInfo property for details of the error.
1197 public function send()
1199 try {
1200 if (!$this->preSend()) {
1201 return false;
1203 return $this->postSend();
1204 } catch (phpmailerException $exc) {
1205 $this->mailHeader = '';
1206 $this->setError($exc->getMessage());
1207 if ($this->exceptions) {
1208 throw $exc;
1210 return false;
1215 * Prepare a message for sending.
1216 * @throws phpmailerException
1217 * @return boolean
1219 public function preSend()
1221 try {
1222 $this->error_count = 0; // Reset errors
1223 $this->mailHeader = '';
1225 // Dequeue recipient and Reply-To addresses with IDN
1226 foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1227 $params[1] = $this->punyencodeAddress($params[1]);
1228 call_user_func_array(array($this, 'addAnAddress'), $params);
1230 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1231 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1234 // Validate From, Sender, and ConfirmReadingTo addresses
1235 foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
1236 $this->$address_kind = trim($this->$address_kind);
1237 if (empty($this->$address_kind)) {
1238 continue;
1240 $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1241 if (!$this->validateAddress($this->$address_kind)) {
1242 $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
1243 $this->setError($error_message);
1244 $this->edebug($error_message);
1245 if ($this->exceptions) {
1246 throw new phpmailerException($error_message);
1248 return false;
1252 // Set whether the message is multipart/alternative
1253 if ($this->alternativeExists()) {
1254 $this->ContentType = 'multipart/alternative';
1257 $this->setMessageType();
1258 // Refuse to send an empty message unless we are specifically allowing it
1259 if (!$this->AllowEmpty and empty($this->Body)) {
1260 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1263 // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1264 $this->MIMEHeader = '';
1265 $this->MIMEBody = $this->createBody();
1266 // createBody may have added some headers, so retain them
1267 $tempheaders = $this->MIMEHeader;
1268 $this->MIMEHeader = $this->createHeader();
1269 $this->MIMEHeader .= $tempheaders;
1271 // To capture the complete message when using mail(), create
1272 // an extra header list which createHeader() doesn't fold in
1273 if ($this->Mailer == 'mail') {
1274 if (count($this->to) > 0) {
1275 $this->mailHeader .= $this->addrAppend('To', $this->to);
1276 } else {
1277 $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1279 $this->mailHeader .= $this->headerLine(
1280 'Subject',
1281 $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1285 // Sign with DKIM if enabled
1286 if (!empty($this->DKIM_domain)
1287 && !empty($this->DKIM_private)
1288 && !empty($this->DKIM_selector)
1289 && file_exists($this->DKIM_private)) {
1290 $header_dkim = $this->DKIM_Add(
1291 $this->MIMEHeader . $this->mailHeader,
1292 $this->encodeHeader($this->secureHeader($this->Subject)),
1293 $this->MIMEBody
1295 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1296 str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1298 return true;
1299 } catch (phpmailerException $exc) {
1300 $this->setError($exc->getMessage());
1301 if ($this->exceptions) {
1302 throw $exc;
1304 return false;
1309 * Actually send a message.
1310 * Send the email via the selected mechanism
1311 * @throws phpmailerException
1312 * @return boolean
1314 public function postSend()
1316 try {
1317 // Choose the mailer and send through it
1318 switch ($this->Mailer) {
1319 case 'sendmail':
1320 case 'qmail':
1321 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1322 case 'smtp':
1323 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1324 case 'mail':
1325 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1326 default:
1327 $sendMethod = $this->Mailer.'Send';
1328 if (method_exists($this, $sendMethod)) {
1329 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1332 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1334 } catch (phpmailerException $exc) {
1335 $this->setError($exc->getMessage());
1336 $this->edebug($exc->getMessage());
1337 if ($this->exceptions) {
1338 throw $exc;
1341 return false;
1345 * Send mail using the $Sendmail program.
1346 * @param string $header The message headers
1347 * @param string $body The message body
1348 * @see PHPMailer::$Sendmail
1349 * @throws phpmailerException
1350 * @access protected
1351 * @return boolean
1353 protected function sendmailSend($header, $body)
1355 if ($this->Sender != '') {
1356 if ($this->Mailer == 'qmail') {
1357 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1358 } else {
1359 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1361 } else {
1362 if ($this->Mailer == 'qmail') {
1363 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1364 } else {
1365 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1368 if ($this->SingleTo) {
1369 foreach ($this->SingleToArray as $toAddr) {
1370 if (!@$mail = popen($sendmail, 'w')) {
1371 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1373 fputs($mail, 'To: ' . $toAddr . "\n");
1374 fputs($mail, $header);
1375 fputs($mail, $body);
1376 $result = pclose($mail);
1377 $this->doCallback(
1378 ($result == 0),
1379 array($toAddr),
1380 $this->cc,
1381 $this->bcc,
1382 $this->Subject,
1383 $body,
1384 $this->From
1386 if ($result != 0) {
1387 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1390 } else {
1391 if (!@$mail = popen($sendmail, 'w')) {
1392 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1394 fputs($mail, $header);
1395 fputs($mail, $body);
1396 $result = pclose($mail);
1397 $this->doCallback(
1398 ($result == 0),
1399 $this->to,
1400 $this->cc,
1401 $this->bcc,
1402 $this->Subject,
1403 $body,
1404 $this->From
1406 if ($result != 0) {
1407 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1410 return true;
1414 * Send mail using the PHP mail() function.
1415 * @param string $header The message headers
1416 * @param string $body The message body
1417 * @link http://www.php.net/manual/en/book.mail.php
1418 * @throws phpmailerException
1419 * @access protected
1420 * @return boolean
1422 protected function mailSend($header, $body)
1424 $toArr = array();
1425 foreach ($this->to as $toaddr) {
1426 $toArr[] = $this->addrFormat($toaddr);
1428 $to = implode(', ', $toArr);
1430 $params = null;
1431 //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
1432 if (!empty($this->Sender)) {
1433 $params = sprintf('-f%s', $this->Sender);
1435 if ($this->Sender != '' and !ini_get('safe_mode')) {
1436 $old_from = ini_get('sendmail_from');
1437 ini_set('sendmail_from', $this->Sender);
1439 $result = false;
1440 if ($this->SingleTo and count($toArr) > 1) {
1441 foreach ($toArr as $toAddr) {
1442 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1443 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1445 } else {
1446 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1447 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1449 if (isset($old_from)) {
1450 ini_set('sendmail_from', $old_from);
1452 if (!$result) {
1453 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1455 return true;
1459 * Get an instance to use for SMTP operations.
1460 * Override this function to load your own SMTP implementation
1461 * @return SMTP
1463 public function getSMTPInstance()
1465 if (!is_object($this->smtp)) {
1466 $this->smtp = new SMTP;
1468 return $this->smtp;
1472 * Send mail via SMTP.
1473 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1474 * Uses the PHPMailerSMTP class by default.
1475 * @see PHPMailer::getSMTPInstance() to use a different class.
1476 * @param string $header The message headers
1477 * @param string $body The message body
1478 * @throws phpmailerException
1479 * @uses SMTP
1480 * @access protected
1481 * @return boolean
1483 protected function smtpSend($header, $body)
1485 $bad_rcpt = array();
1486 if (!$this->smtpConnect($this->SMTPOptions)) {
1487 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1489 if ('' == $this->Sender) {
1490 $smtp_from = $this->From;
1491 } else {
1492 $smtp_from = $this->Sender;
1494 if (!$this->smtp->mail($smtp_from)) {
1495 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1496 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1499 // Attempt to send to all recipients
1500 foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1501 foreach ($togroup as $to) {
1502 if (!$this->smtp->recipient($to[0])) {
1503 $error = $this->smtp->getError();
1504 $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1505 $isSent = false;
1506 } else {
1507 $isSent = true;
1509 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1513 // Only send the DATA command if we have viable recipients
1514 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1515 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1517 if ($this->SMTPKeepAlive) {
1518 $this->smtp->reset();
1519 } else {
1520 $this->smtp->quit();
1521 $this->smtp->close();
1523 //Create error message for any bad addresses
1524 if (count($bad_rcpt) > 0) {
1525 $errstr = '';
1526 foreach ($bad_rcpt as $bad) {
1527 $errstr .= $bad['to'] . ': ' . $bad['error'];
1529 throw new phpmailerException(
1530 $this->lang('recipients_failed') . $errstr,
1531 self::STOP_CONTINUE
1534 return true;
1538 * Initiate a connection to an SMTP server.
1539 * Returns false if the operation failed.
1540 * @param array $options An array of options compatible with stream_context_create()
1541 * @uses SMTP
1542 * @access public
1543 * @throws phpmailerException
1544 * @return boolean
1546 public function smtpConnect($options = null)
1548 if (is_null($this->smtp)) {
1549 $this->smtp = $this->getSMTPInstance();
1552 //If no options are provided, use whatever is set in the instance
1553 if (is_null($options)) {
1554 $options = $this->SMTPOptions;
1557 // Already connected?
1558 if ($this->smtp->connected()) {
1559 return true;
1562 $this->smtp->setTimeout($this->Timeout);
1563 $this->smtp->setDebugLevel($this->SMTPDebug);
1564 $this->smtp->setDebugOutput($this->Debugoutput);
1565 $this->smtp->setVerp($this->do_verp);
1566 $hosts = explode(';', $this->Host);
1567 $lastexception = null;
1569 foreach ($hosts as $hostentry) {
1570 $hostinfo = array();
1571 if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1572 // Not a valid host entry
1573 continue;
1575 // $hostinfo[2]: optional ssl or tls prefix
1576 // $hostinfo[3]: the hostname
1577 // $hostinfo[4]: optional port number
1578 // The host string prefix can temporarily override the current setting for SMTPSecure
1579 // If it's not specified, the default value is used
1580 $prefix = '';
1581 $secure = $this->SMTPSecure;
1582 $tls = ($this->SMTPSecure == 'tls');
1583 if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1584 $prefix = 'ssl://';
1585 $tls = false; // Can't have SSL and TLS at the same time
1586 $secure = 'ssl';
1587 } elseif ($hostinfo[2] == 'tls') {
1588 $tls = true;
1589 // tls doesn't use a prefix
1590 $secure = 'tls';
1592 //Do we need the OpenSSL extension?
1593 $sslext = defined('OPENSSL_ALGO_SHA1');
1594 if ('tls' === $secure or 'ssl' === $secure) {
1595 //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1596 if (!$sslext) {
1597 throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1600 $host = $hostinfo[3];
1601 $port = $this->Port;
1602 $tport = (integer)$hostinfo[4];
1603 if ($tport > 0 and $tport < 65536) {
1604 $port = $tport;
1606 if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1607 try {
1608 if ($this->Helo) {
1609 $hello = $this->Helo;
1610 } else {
1611 $hello = $this->serverHostname();
1613 $this->smtp->hello($hello);
1614 //Automatically enable TLS encryption if:
1615 // * it's not disabled
1616 // * we have openssl extension
1617 // * we are not already using SSL
1618 // * the server offers STARTTLS
1619 if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1620 $tls = true;
1622 if ($tls) {
1623 if (!$this->smtp->startTLS()) {
1624 throw new phpmailerException($this->lang('connect_host'));
1626 // We must resend EHLO after TLS negotiation
1627 $this->smtp->hello($hello);
1629 if ($this->SMTPAuth) {
1630 if (!$this->smtp->authenticate(
1631 $this->Username,
1632 $this->Password,
1633 $this->AuthType,
1634 $this->Realm,
1635 $this->Workstation
1638 throw new phpmailerException($this->lang('authenticate'));
1641 return true;
1642 } catch (phpmailerException $exc) {
1643 $lastexception = $exc;
1644 $this->edebug($exc->getMessage());
1645 // We must have connected, but then failed TLS or Auth, so close connection nicely
1646 $this->smtp->quit();
1650 // If we get here, all connection attempts have failed, so close connection hard
1651 $this->smtp->close();
1652 // As we've caught all exceptions, just report whatever the last one was
1653 if ($this->exceptions and !is_null($lastexception)) {
1654 throw $lastexception;
1656 return false;
1660 * Close the active SMTP session if one exists.
1661 * @return void
1663 public function smtpClose()
1665 if (is_a($this->smtp, 'SMTP')) {
1666 if ($this->smtp->connected()) {
1667 $this->smtp->quit();
1668 $this->smtp->close();
1674 * Set the language for error messages.
1675 * Returns false if it cannot load the language file.
1676 * The default language is English.
1677 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1678 * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1679 * @return boolean
1680 * @access public
1682 public function setLanguage($langcode = 'en', $lang_path = '')
1684 // Define full set of translatable strings in English
1685 $PHPMAILER_LANG = array(
1686 'authenticate' => 'SMTP Error: Could not authenticate.',
1687 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1688 'data_not_accepted' => 'SMTP Error: data not accepted.',
1689 'empty_message' => 'Message body empty',
1690 'encoding' => 'Unknown encoding: ',
1691 'execute' => 'Could not execute: ',
1692 'file_access' => 'Could not access file: ',
1693 'file_open' => 'File Error: Could not open file: ',
1694 'from_failed' => 'The following From address failed: ',
1695 'instantiate' => 'Could not instantiate mail function.',
1696 'invalid_address' => 'Invalid address: ',
1697 'mailer_not_supported' => ' mailer is not supported.',
1698 'provide_address' => 'You must provide at least one recipient email address.',
1699 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1700 'signing' => 'Signing Error: ',
1701 'smtp_connect_failed' => 'SMTP connect() failed.',
1702 'smtp_error' => 'SMTP server error: ',
1703 'variable_set' => 'Cannot set or reset variable: ',
1704 'extension_missing' => 'Extension missing: '
1706 if (empty($lang_path)) {
1707 // Calculate an absolute path so it can work if CWD is not here
1708 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1710 $foundlang = true;
1711 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1712 // There is no English translation file
1713 if ($langcode != 'en') {
1714 // Make sure language file path is readable
1715 if (!is_readable($lang_file)) {
1716 $foundlang = false;
1717 } else {
1718 // Overwrite language-specific strings.
1719 // This way we'll never have missing translation keys.
1720 $foundlang = include $lang_file;
1723 $this->language = $PHPMAILER_LANG;
1724 return (boolean)$foundlang; // Returns false if language not found
1728 * Get the array of strings for the current language.
1729 * @return array
1731 public function getTranslations()
1733 return $this->language;
1737 * Create recipient headers.
1738 * @access public
1739 * @param string $type
1740 * @param array $addr An array of recipient,
1741 * where each recipient is a 2-element indexed array with element 0 containing an address
1742 * and element 1 containing a name, like:
1743 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
1744 * @return string
1746 public function addrAppend($type, $addr)
1748 $addresses = array();
1749 foreach ($addr as $address) {
1750 $addresses[] = $this->addrFormat($address);
1752 return $type . ': ' . implode(', ', $addresses) . $this->LE;
1756 * Format an address for use in a message header.
1757 * @access public
1758 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1759 * like array('joe@example.com', 'Joe User')
1760 * @return string
1762 public function addrFormat($addr)
1764 if (empty($addr[1])) { // No name provided
1765 return $this->secureHeader($addr[0]);
1766 } else {
1767 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1768 $addr[0]
1769 ) . '>';
1774 * Word-wrap message.
1775 * For use with mailers that do not automatically perform wrapping
1776 * and for quoted-printable encoded messages.
1777 * Original written by philippe.
1778 * @param string $message The message to wrap
1779 * @param integer $length The line length to wrap to
1780 * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1781 * @access public
1782 * @return string
1784 public function wrapText($message, $length, $qp_mode = false)
1786 if ($qp_mode) {
1787 $soft_break = sprintf(' =%s', $this->LE);
1788 } else {
1789 $soft_break = $this->LE;
1791 // If utf-8 encoding is used, we will need to make sure we don't
1792 // split multibyte characters when we wrap
1793 $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1794 $lelen = strlen($this->LE);
1795 $crlflen = strlen(self::CRLF);
1797 $message = $this->fixEOL($message);
1798 //Remove a trailing line break
1799 if (substr($message, -$lelen) == $this->LE) {
1800 $message = substr($message, 0, -$lelen);
1803 //Split message into lines
1804 $lines = explode($this->LE, $message);
1805 //Message will be rebuilt in here
1806 $message = '';
1807 foreach ($lines as $line) {
1808 $words = explode(' ', $line);
1809 $buf = '';
1810 $firstword = true;
1811 foreach ($words as $word) {
1812 if ($qp_mode and (strlen($word) > $length)) {
1813 $space_left = $length - strlen($buf) - $crlflen;
1814 if (!$firstword) {
1815 if ($space_left > 20) {
1816 $len = $space_left;
1817 if ($is_utf8) {
1818 $len = $this->utf8CharBoundary($word, $len);
1819 } elseif (substr($word, $len - 1, 1) == '=') {
1820 $len--;
1821 } elseif (substr($word, $len - 2, 1) == '=') {
1822 $len -= 2;
1824 $part = substr($word, 0, $len);
1825 $word = substr($word, $len);
1826 $buf .= ' ' . $part;
1827 $message .= $buf . sprintf('=%s', self::CRLF);
1828 } else {
1829 $message .= $buf . $soft_break;
1831 $buf = '';
1833 while (strlen($word) > 0) {
1834 if ($length <= 0) {
1835 break;
1837 $len = $length;
1838 if ($is_utf8) {
1839 $len = $this->utf8CharBoundary($word, $len);
1840 } elseif (substr($word, $len - 1, 1) == '=') {
1841 $len--;
1842 } elseif (substr($word, $len - 2, 1) == '=') {
1843 $len -= 2;
1845 $part = substr($word, 0, $len);
1846 $word = substr($word, $len);
1848 if (strlen($word) > 0) {
1849 $message .= $part . sprintf('=%s', self::CRLF);
1850 } else {
1851 $buf = $part;
1854 } else {
1855 $buf_o = $buf;
1856 if (!$firstword) {
1857 $buf .= ' ';
1859 $buf .= $word;
1861 if (strlen($buf) > $length and $buf_o != '') {
1862 $message .= $buf_o . $soft_break;
1863 $buf = $word;
1866 $firstword = false;
1868 $message .= $buf . self::CRLF;
1871 return $message;
1875 * Find the last character boundary prior to $maxLength in a utf-8
1876 * quoted-printable encoded string.
1877 * Original written by Colin Brown.
1878 * @access public
1879 * @param string $encodedText utf-8 QP text
1880 * @param integer $maxLength Find the last character boundary prior to this length
1881 * @return integer
1883 public function utf8CharBoundary($encodedText, $maxLength)
1885 $foundSplitPos = false;
1886 $lookBack = 3;
1887 while (!$foundSplitPos) {
1888 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1889 $encodedCharPos = strpos($lastChunk, '=');
1890 if (false !== $encodedCharPos) {
1891 // Found start of encoded character byte within $lookBack block.
1892 // Check the encoded byte value (the 2 chars after the '=')
1893 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1894 $dec = hexdec($hex);
1895 if ($dec < 128) {
1896 // Single byte character.
1897 // If the encoded char was found at pos 0, it will fit
1898 // otherwise reduce maxLength to start of the encoded char
1899 if ($encodedCharPos > 0) {
1900 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1902 $foundSplitPos = true;
1903 } elseif ($dec >= 192) {
1904 // First byte of a multi byte character
1905 // Reduce maxLength to split at start of character
1906 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1907 $foundSplitPos = true;
1908 } elseif ($dec < 192) {
1909 // Middle byte of a multi byte character, look further back
1910 $lookBack += 3;
1912 } else {
1913 // No encoded character found
1914 $foundSplitPos = true;
1917 return $maxLength;
1921 * Apply word wrapping to the message body.
1922 * Wraps the message body to the number of chars set in the WordWrap property.
1923 * You should only do this to plain-text bodies as wrapping HTML tags may break them.
1924 * This is called automatically by createBody(), so you don't need to call it yourself.
1925 * @access public
1926 * @return void
1928 public function setWordWrap()
1930 if ($this->WordWrap < 1) {
1931 return;
1934 switch ($this->message_type) {
1935 case 'alt':
1936 case 'alt_inline':
1937 case 'alt_attach':
1938 case 'alt_inline_attach':
1939 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1940 break;
1941 default:
1942 $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1943 break;
1948 * Assemble message headers.
1949 * @access public
1950 * @return string The assembled headers
1952 public function createHeader()
1954 $result = '';
1956 if ($this->MessageDate == '') {
1957 $this->MessageDate = self::rfcDate();
1959 $result .= $this->headerLine('Date', $this->MessageDate);
1961 // To be created automatically by mail()
1962 if ($this->SingleTo) {
1963 if ($this->Mailer != 'mail') {
1964 foreach ($this->to as $toaddr) {
1965 $this->SingleToArray[] = $this->addrFormat($toaddr);
1968 } else {
1969 if (count($this->to) > 0) {
1970 if ($this->Mailer != 'mail') {
1971 $result .= $this->addrAppend('To', $this->to);
1973 } elseif (count($this->cc) == 0) {
1974 $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1978 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1980 // sendmail and mail() extract Cc from the header before sending
1981 if (count($this->cc) > 0) {
1982 $result .= $this->addrAppend('Cc', $this->cc);
1985 // sendmail and mail() extract Bcc from the header before sending
1986 if ((
1987 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1989 and count($this->bcc) > 0
1991 $result .= $this->addrAppend('Bcc', $this->bcc);
1994 if (count($this->ReplyTo) > 0) {
1995 $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1998 // mail() sets the subject itself
1999 if ($this->Mailer != 'mail') {
2000 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2003 if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
2004 $this->lastMessageID = $this->MessageID;
2005 } else {
2006 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
2008 $result .= $this->headerLine('Message-ID', $this->lastMessageID);
2009 if (!is_null($this->Priority)) {
2010 $result .= $this->headerLine('X-Priority', $this->Priority);
2012 if ($this->XMailer == '') {
2013 $result .= $this->headerLine(
2014 'X-Mailer',
2015 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
2017 } else {
2018 $myXmailer = trim($this->XMailer);
2019 if ($myXmailer) {
2020 $result .= $this->headerLine('X-Mailer', $myXmailer);
2024 if ($this->ConfirmReadingTo != '') {
2025 $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
2028 // Add custom headers
2029 foreach ($this->CustomHeader as $header) {
2030 $result .= $this->headerLine(
2031 trim($header[0]),
2032 $this->encodeHeader(trim($header[1]))
2035 if (!$this->sign_key_file) {
2036 $result .= $this->headerLine('MIME-Version', '1.0');
2037 $result .= $this->getMailMIME();
2040 return $result;
2044 * Get the message MIME type headers.
2045 * @access public
2046 * @return string
2048 public function getMailMIME()
2050 $result = '';
2051 $ismultipart = true;
2052 switch ($this->message_type) {
2053 case 'inline':
2054 $result .= $this->headerLine('Content-Type', 'multipart/related;');
2055 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2056 break;
2057 case 'attach':
2058 case 'inline_attach':
2059 case 'alt_attach':
2060 case 'alt_inline_attach':
2061 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2062 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2063 break;
2064 case 'alt':
2065 case 'alt_inline':
2066 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2067 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2068 break;
2069 default:
2070 // Catches case 'plain': and case '':
2071 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2072 $ismultipart = false;
2073 break;
2075 // RFC1341 part 5 says 7bit is assumed if not specified
2076 if ($this->Encoding != '7bit') {
2077 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2078 if ($ismultipart) {
2079 if ($this->Encoding == '8bit') {
2080 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2082 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2083 } else {
2084 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2088 if ($this->Mailer != 'mail') {
2089 $result .= $this->LE;
2092 return $result;
2096 * Returns the whole MIME message.
2097 * Includes complete headers and body.
2098 * Only valid post preSend().
2099 * @see PHPMailer::preSend()
2100 * @access public
2101 * @return string
2103 public function getSentMIMEMessage()
2105 return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
2109 * Assemble the message body.
2110 * Returns an empty string on failure.
2111 * @access public
2112 * @throws phpmailerException
2113 * @return string The assembled message body
2115 public function createBody()
2117 $body = '';
2118 //Create unique IDs and preset boundaries
2119 $this->uniqueid = md5(uniqid(time()));
2120 $this->boundary[1] = 'b1_' . $this->uniqueid;
2121 $this->boundary[2] = 'b2_' . $this->uniqueid;
2122 $this->boundary[3] = 'b3_' . $this->uniqueid;
2124 if ($this->sign_key_file) {
2125 $body .= $this->getMailMIME() . $this->LE;
2128 $this->setWordWrap();
2130 $bodyEncoding = $this->Encoding;
2131 $bodyCharSet = $this->CharSet;
2132 //Can we do a 7-bit downgrade?
2133 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2134 $bodyEncoding = '7bit';
2135 //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2136 $bodyCharSet = 'us-ascii';
2138 //If lines are too long, and we're not already using an encoding that will shorten them,
2139 //change to quoted-printable transfer encoding for the body part only
2140 if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
2141 $bodyEncoding = 'quoted-printable';
2144 $altBodyEncoding = $this->Encoding;
2145 $altBodyCharSet = $this->CharSet;
2146 //Can we do a 7-bit downgrade?
2147 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2148 $altBodyEncoding = '7bit';
2149 //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2150 $altBodyCharSet = 'us-ascii';
2152 //If lines are too long, and we're not already using an encoding that will shorten them,
2153 //change to quoted-printable transfer encoding for the alt body part only
2154 if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
2155 $altBodyEncoding = 'quoted-printable';
2157 //Use this as a preamble in all multipart message types
2158 $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
2159 switch ($this->message_type) {
2160 case 'inline':
2161 $body .= $mimepre;
2162 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2163 $body .= $this->encodeString($this->Body, $bodyEncoding);
2164 $body .= $this->LE . $this->LE;
2165 $body .= $this->attachAll('inline', $this->boundary[1]);
2166 break;
2167 case 'attach':
2168 $body .= $mimepre;
2169 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2170 $body .= $this->encodeString($this->Body, $bodyEncoding);
2171 $body .= $this->LE . $this->LE;
2172 $body .= $this->attachAll('attachment', $this->boundary[1]);
2173 break;
2174 case 'inline_attach':
2175 $body .= $mimepre;
2176 $body .= $this->textLine('--' . $this->boundary[1]);
2177 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2178 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2179 $body .= $this->LE;
2180 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2181 $body .= $this->encodeString($this->Body, $bodyEncoding);
2182 $body .= $this->LE . $this->LE;
2183 $body .= $this->attachAll('inline', $this->boundary[2]);
2184 $body .= $this->LE;
2185 $body .= $this->attachAll('attachment', $this->boundary[1]);
2186 break;
2187 case 'alt':
2188 $body .= $mimepre;
2189 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2190 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2191 $body .= $this->LE . $this->LE;
2192 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2193 $body .= $this->encodeString($this->Body, $bodyEncoding);
2194 $body .= $this->LE . $this->LE;
2195 if (!empty($this->Ical)) {
2196 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2197 $body .= $this->encodeString($this->Ical, $this->Encoding);
2198 $body .= $this->LE . $this->LE;
2200 $body .= $this->endBoundary($this->boundary[1]);
2201 break;
2202 case 'alt_inline':
2203 $body .= $mimepre;
2204 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2205 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2206 $body .= $this->LE . $this->LE;
2207 $body .= $this->textLine('--' . $this->boundary[1]);
2208 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2209 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2210 $body .= $this->LE;
2211 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2212 $body .= $this->encodeString($this->Body, $bodyEncoding);
2213 $body .= $this->LE . $this->LE;
2214 $body .= $this->attachAll('inline', $this->boundary[2]);
2215 $body .= $this->LE;
2216 $body .= $this->endBoundary($this->boundary[1]);
2217 break;
2218 case 'alt_attach':
2219 $body .= $mimepre;
2220 $body .= $this->textLine('--' . $this->boundary[1]);
2221 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2222 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2223 $body .= $this->LE;
2224 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2225 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2226 $body .= $this->LE . $this->LE;
2227 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2228 $body .= $this->encodeString($this->Body, $bodyEncoding);
2229 $body .= $this->LE . $this->LE;
2230 $body .= $this->endBoundary($this->boundary[2]);
2231 $body .= $this->LE;
2232 $body .= $this->attachAll('attachment', $this->boundary[1]);
2233 break;
2234 case 'alt_inline_attach':
2235 $body .= $mimepre;
2236 $body .= $this->textLine('--' . $this->boundary[1]);
2237 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2238 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2239 $body .= $this->LE;
2240 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2241 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2242 $body .= $this->LE . $this->LE;
2243 $body .= $this->textLine('--' . $this->boundary[2]);
2244 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2245 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2246 $body .= $this->LE;
2247 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2248 $body .= $this->encodeString($this->Body, $bodyEncoding);
2249 $body .= $this->LE . $this->LE;
2250 $body .= $this->attachAll('inline', $this->boundary[3]);
2251 $body .= $this->LE;
2252 $body .= $this->endBoundary($this->boundary[2]);
2253 $body .= $this->LE;
2254 $body .= $this->attachAll('attachment', $this->boundary[1]);
2255 break;
2256 default:
2257 // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
2258 //Reset the `Encoding` property in case we changed it for line length reasons
2259 $this->Encoding = $bodyEncoding;
2260 $body .= $this->encodeString($this->Body, $this->Encoding);
2261 break;
2264 if ($this->isError()) {
2265 $body = '';
2266 } elseif ($this->sign_key_file) {
2267 try {
2268 if (!defined('PKCS7_TEXT')) {
2269 throw new phpmailerException($this->lang('extension_missing') . 'openssl');
2271 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2272 $file = tempnam(sys_get_temp_dir(), 'mail');
2273 if (false === file_put_contents($file, $body)) {
2274 throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2276 $signed = tempnam(sys_get_temp_dir(), 'signed');
2277 //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2278 if (empty($this->sign_extracerts_file)) {
2279 $sign = @openssl_pkcs7_sign(
2280 $file,
2281 $signed,
2282 'file://' . realpath($this->sign_cert_file),
2283 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2284 null
2286 } else {
2287 $sign = @openssl_pkcs7_sign(
2288 $file,
2289 $signed,
2290 'file://' . realpath($this->sign_cert_file),
2291 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2292 null,
2293 PKCS7_DETACHED,
2294 $this->sign_extracerts_file
2297 if ($sign) {
2298 @unlink($file);
2299 $body = file_get_contents($signed);
2300 @unlink($signed);
2301 //The message returned by openssl contains both headers and body, so need to split them up
2302 $parts = explode("\n\n", $body, 2);
2303 $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2304 $body = $parts[1];
2305 } else {
2306 @unlink($file);
2307 @unlink($signed);
2308 throw new phpmailerException($this->lang('signing') . openssl_error_string());
2310 } catch (phpmailerException $exc) {
2311 $body = '';
2312 if ($this->exceptions) {
2313 throw $exc;
2317 return $body;
2321 * Return the start of a message boundary.
2322 * @access protected
2323 * @param string $boundary
2324 * @param string $charSet
2325 * @param string $contentType
2326 * @param string $encoding
2327 * @return string
2329 protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2331 $result = '';
2332 if ($charSet == '') {
2333 $charSet = $this->CharSet;
2335 if ($contentType == '') {
2336 $contentType = $this->ContentType;
2338 if ($encoding == '') {
2339 $encoding = $this->Encoding;
2341 $result .= $this->textLine('--' . $boundary);
2342 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2343 $result .= $this->LE;
2344 // RFC1341 part 5 says 7bit is assumed if not specified
2345 if ($encoding != '7bit') {
2346 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2348 $result .= $this->LE;
2350 return $result;
2354 * Return the end of a message boundary.
2355 * @access protected
2356 * @param string $boundary
2357 * @return string
2359 protected function endBoundary($boundary)
2361 return $this->LE . '--' . $boundary . '--' . $this->LE;
2365 * Set the message type.
2366 * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2367 * @access protected
2368 * @return void
2370 protected function setMessageType()
2372 $type = array();
2373 if ($this->alternativeExists()) {
2374 $type[] = 'alt';
2376 if ($this->inlineImageExists()) {
2377 $type[] = 'inline';
2379 if ($this->attachmentExists()) {
2380 $type[] = 'attach';
2382 $this->message_type = implode('_', $type);
2383 if ($this->message_type == '') {
2384 //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2385 $this->message_type = 'plain';
2390 * Format a header line.
2391 * @access public
2392 * @param string $name
2393 * @param string $value
2394 * @return string
2396 public function headerLine($name, $value)
2398 return $name . ': ' . $value . $this->LE;
2402 * Return a formatted mail line.
2403 * @access public
2404 * @param string $value
2405 * @return string
2407 public function textLine($value)
2409 return $value . $this->LE;
2413 * Add an attachment from a path on the filesystem.
2414 * Returns false if the file could not be found or read.
2415 * @param string $path Path to the attachment.
2416 * @param string $name Overrides the attachment name.
2417 * @param string $encoding File encoding (see $Encoding).
2418 * @param string $type File extension (MIME) type.
2419 * @param string $disposition Disposition to use
2420 * @throws phpmailerException
2421 * @return boolean
2423 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2425 try {
2426 if (!@is_file($path)) {
2427 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2430 // If a MIME type is not specified, try to work it out from the file name
2431 if ($type == '') {
2432 $type = self::filenameToType($path);
2435 $filename = basename($path);
2436 if ($name == '') {
2437 $name = $filename;
2440 $this->attachment[] = array(
2441 0 => $path,
2442 1 => $filename,
2443 2 => $name,
2444 3 => $encoding,
2445 4 => $type,
2446 5 => false, // isStringAttachment
2447 6 => $disposition,
2448 7 => 0
2451 } catch (phpmailerException $exc) {
2452 $this->setError($exc->getMessage());
2453 $this->edebug($exc->getMessage());
2454 if ($this->exceptions) {
2455 throw $exc;
2457 return false;
2459 return true;
2463 * Return the array of attachments.
2464 * @return array
2466 public function getAttachments()
2468 return $this->attachment;
2472 * Attach all file, string, and binary attachments to the message.
2473 * Returns an empty string on failure.
2474 * @access protected
2475 * @param string $disposition_type
2476 * @param string $boundary
2477 * @return string
2479 protected function attachAll($disposition_type, $boundary)
2481 // Return text of body
2482 $mime = array();
2483 $cidUniq = array();
2484 $incl = array();
2486 // Add all attachments
2487 foreach ($this->attachment as $attachment) {
2488 // Check if it is a valid disposition_filter
2489 if ($attachment[6] == $disposition_type) {
2490 // Check for string attachment
2491 $string = '';
2492 $path = '';
2493 $bString = $attachment[5];
2494 if ($bString) {
2495 $string = $attachment[0];
2496 } else {
2497 $path = $attachment[0];
2500 $inclhash = md5(serialize($attachment));
2501 if (in_array($inclhash, $incl)) {
2502 continue;
2504 $incl[] = $inclhash;
2505 $name = $attachment[2];
2506 $encoding = $attachment[3];
2507 $type = $attachment[4];
2508 $disposition = $attachment[6];
2509 $cid = $attachment[7];
2510 if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
2511 continue;
2513 $cidUniq[$cid] = true;
2515 $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2516 //Only include a filename property if we have one
2517 if (!empty($name)) {
2518 $mime[] = sprintf(
2519 'Content-Type: %s; name="%s"%s',
2520 $type,
2521 $this->encodeHeader($this->secureHeader($name)),
2522 $this->LE
2524 } else {
2525 $mime[] = sprintf(
2526 'Content-Type: %s%s',
2527 $type,
2528 $this->LE
2531 // RFC1341 part 5 says 7bit is assumed if not specified
2532 if ($encoding != '7bit') {
2533 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2536 if ($disposition == 'inline') {
2537 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2540 // If a filename contains any of these chars, it should be quoted,
2541 // but not otherwise: RFC2183 & RFC2045 5.1
2542 // Fixes a warning in IETF's msglint MIME checker
2543 // Allow for bypassing the Content-Disposition header totally
2544 if (!(empty($disposition))) {
2545 $encoded_name = $this->encodeHeader($this->secureHeader($name));
2546 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2547 $mime[] = sprintf(
2548 'Content-Disposition: %s; filename="%s"%s',
2549 $disposition,
2550 $encoded_name,
2551 $this->LE . $this->LE
2553 } else {
2554 if (!empty($encoded_name)) {
2555 $mime[] = sprintf(
2556 'Content-Disposition: %s; filename=%s%s',
2557 $disposition,
2558 $encoded_name,
2559 $this->LE . $this->LE
2561 } else {
2562 $mime[] = sprintf(
2563 'Content-Disposition: %s%s',
2564 $disposition,
2565 $this->LE . $this->LE
2569 } else {
2570 $mime[] = $this->LE;
2573 // Encode as string attachment
2574 if ($bString) {
2575 $mime[] = $this->encodeString($string, $encoding);
2576 if ($this->isError()) {
2577 return '';
2579 $mime[] = $this->LE . $this->LE;
2580 } else {
2581 $mime[] = $this->encodeFile($path, $encoding);
2582 if ($this->isError()) {
2583 return '';
2585 $mime[] = $this->LE . $this->LE;
2590 $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2592 return implode('', $mime);
2596 * Encode a file attachment in requested format.
2597 * Returns an empty string on failure.
2598 * @param string $path The full path to the file
2599 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2600 * @throws phpmailerException
2601 * @access protected
2602 * @return string
2604 protected function encodeFile($path, $encoding = 'base64')
2606 try {
2607 if (!is_readable($path)) {
2608 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2610 $magic_quotes = get_magic_quotes_runtime();
2611 if ($magic_quotes) {
2612 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2613 set_magic_quotes_runtime(false);
2614 } else {
2615 //Doesn't exist in PHP 5.4, but we don't need to check because
2616 //get_magic_quotes_runtime always returns false in 5.4+
2617 //so it will never get here
2618 ini_set('magic_quotes_runtime', false);
2621 $file_buffer = file_get_contents($path);
2622 $file_buffer = $this->encodeString($file_buffer, $encoding);
2623 if ($magic_quotes) {
2624 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2625 set_magic_quotes_runtime($magic_quotes);
2626 } else {
2627 ini_set('magic_quotes_runtime', $magic_quotes);
2630 return $file_buffer;
2631 } catch (Exception $exc) {
2632 $this->setError($exc->getMessage());
2633 return '';
2638 * Encode a string in requested format.
2639 * Returns an empty string on failure.
2640 * @param string $str The text to encode
2641 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2642 * @access public
2643 * @return string
2645 public function encodeString($str, $encoding = 'base64')
2647 $encoded = '';
2648 switch (strtolower($encoding)) {
2649 case 'base64':
2650 $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2651 break;
2652 case '7bit':
2653 case '8bit':
2654 $encoded = $this->fixEOL($str);
2655 // Make sure it ends with a line break
2656 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2657 $encoded .= $this->LE;
2659 break;
2660 case 'binary':
2661 $encoded = $str;
2662 break;
2663 case 'quoted-printable':
2664 $encoded = $this->encodeQP($str);
2665 break;
2666 default:
2667 $this->setError($this->lang('encoding') . $encoding);
2668 break;
2670 return $encoded;
2674 * Encode a header string optimally.
2675 * Picks shortest of Q, B, quoted-printable or none.
2676 * @access public
2677 * @param string $str
2678 * @param string $position
2679 * @return string
2681 public function encodeHeader($str, $position = 'text')
2683 $matchcount = 0;
2684 switch (strtolower($position)) {
2685 case 'phrase':
2686 if (!preg_match('/[\200-\377]/', $str)) {
2687 // Can't use addslashes as we don't know the value of magic_quotes_sybase
2688 $encoded = addcslashes($str, "\0..\37\177\\\"");
2689 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2690 return ($encoded);
2691 } else {
2692 return ("\"$encoded\"");
2695 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2696 break;
2697 /** @noinspection PhpMissingBreakStatementInspection */
2698 case 'comment':
2699 $matchcount = preg_match_all('/[()"]/', $str, $matches);
2700 // Intentional fall-through
2701 case 'text':
2702 default:
2703 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2704 break;
2707 //There are no chars that need encoding
2708 if ($matchcount == 0) {
2709 return ($str);
2712 $maxlen = 75 - 7 - strlen($this->CharSet);
2713 // Try to select the encoding which should produce the shortest output
2714 if ($matchcount > strlen($str) / 3) {
2715 // More than a third of the content will need encoding, so B encoding will be most efficient
2716 $encoding = 'B';
2717 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2718 // Use a custom function which correctly encodes and wraps long
2719 // multibyte strings without breaking lines within a character
2720 $encoded = $this->base64EncodeWrapMB($str, "\n");
2721 } else {
2722 $encoded = base64_encode($str);
2723 $maxlen -= $maxlen % 4;
2724 $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2726 } else {
2727 $encoding = 'Q';
2728 $encoded = $this->encodeQ($str, $position);
2729 $encoded = $this->wrapText($encoded, $maxlen, true);
2730 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2733 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2734 $encoded = trim(str_replace("\n", $this->LE, $encoded));
2736 return $encoded;
2740 * Check if a string contains multi-byte characters.
2741 * @access public
2742 * @param string $str multi-byte text to wrap encode
2743 * @return boolean
2745 public function hasMultiBytes($str)
2747 if (function_exists('mb_strlen')) {
2748 return (strlen($str) > mb_strlen($str, $this->CharSet));
2749 } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2750 return false;
2755 * Does a string contain any 8-bit chars (in any charset)?
2756 * @param string $text
2757 * @return boolean
2759 public function has8bitChars($text)
2761 return (boolean)preg_match('/[\x80-\xFF]/', $text);
2765 * Encode and wrap long multibyte strings for mail headers
2766 * without breaking lines within a character.
2767 * Adapted from a function by paravoid
2768 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2769 * @access public
2770 * @param string $str multi-byte text to wrap encode
2771 * @param string $linebreak string to use as linefeed/end-of-line
2772 * @return string
2774 public function base64EncodeWrapMB($str, $linebreak = null)
2776 $start = '=?' . $this->CharSet . '?B?';
2777 $end = '?=';
2778 $encoded = '';
2779 if ($linebreak === null) {
2780 $linebreak = $this->LE;
2783 $mb_length = mb_strlen($str, $this->CharSet);
2784 // Each line must have length <= 75, including $start and $end
2785 $length = 75 - strlen($start) - strlen($end);
2786 // Average multi-byte ratio
2787 $ratio = $mb_length / strlen($str);
2788 // Base64 has a 4:3 ratio
2789 $avgLength = floor($length * $ratio * .75);
2791 for ($i = 0; $i < $mb_length; $i += $offset) {
2792 $lookBack = 0;
2793 do {
2794 $offset = $avgLength - $lookBack;
2795 $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2796 $chunk = base64_encode($chunk);
2797 $lookBack++;
2798 } while (strlen($chunk) > $length);
2799 $encoded .= $chunk . $linebreak;
2802 // Chomp the last linefeed
2803 $encoded = substr($encoded, 0, -strlen($linebreak));
2804 return $encoded;
2808 * Encode a string in quoted-printable format.
2809 * According to RFC2045 section 6.7.
2810 * @access public
2811 * @param string $string The text to encode
2812 * @param integer $line_max Number of chars allowed on a line before wrapping
2813 * @return string
2814 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2816 public function encodeQP($string, $line_max = 76)
2818 // Use native function if it's available (>= PHP5.3)
2819 if (function_exists('quoted_printable_encode')) {
2820 return quoted_printable_encode($string);
2822 // Fall back to a pure PHP implementation
2823 $string = str_replace(
2824 array('%20', '%0D%0A.', '%0D%0A', '%'),
2825 array(' ', "\r\n=2E", "\r\n", '='),
2826 rawurlencode($string)
2828 return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2832 * Backward compatibility wrapper for an old QP encoding function that was removed.
2833 * @see PHPMailer::encodeQP()
2834 * @access public
2835 * @param string $string
2836 * @param integer $line_max
2837 * @param boolean $space_conv
2838 * @return string
2839 * @deprecated Use encodeQP instead.
2841 public function encodeQPphp(
2842 $string,
2843 $line_max = 76,
2844 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2846 return $this->encodeQP($string, $line_max);
2850 * Encode a string using Q encoding.
2851 * @link http://tools.ietf.org/html/rfc2047
2852 * @param string $str the text to encode
2853 * @param string $position Where the text is going to be used, see the RFC for what that means
2854 * @access public
2855 * @return string
2857 public function encodeQ($str, $position = 'text')
2859 // There should not be any EOL in the string
2860 $pattern = '';
2861 $encoded = str_replace(array("\r", "\n"), '', $str);
2862 switch (strtolower($position)) {
2863 case 'phrase':
2864 // RFC 2047 section 5.3
2865 $pattern = '^A-Za-z0-9!*+\/ -';
2866 break;
2867 /** @noinspection PhpMissingBreakStatementInspection */
2868 case 'comment':
2869 // RFC 2047 section 5.2
2870 $pattern = '\(\)"';
2871 // intentional fall-through
2872 // for this reason we build the $pattern without including delimiters and []
2873 case 'text':
2874 default:
2875 // RFC 2047 section 5.1
2876 // Replace every high ascii, control, =, ? and _ characters
2877 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2878 break;
2880 $matches = array();
2881 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2882 // If the string contains an '=', make sure it's the first thing we replace
2883 // so as to avoid double-encoding
2884 $eqkey = array_search('=', $matches[0]);
2885 if (false !== $eqkey) {
2886 unset($matches[0][$eqkey]);
2887 array_unshift($matches[0], '=');
2889 foreach (array_unique($matches[0]) as $char) {
2890 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2893 // Replace every spaces to _ (more readable than =20)
2894 return str_replace(' ', '_', $encoded);
2898 * Add a string or binary attachment (non-filesystem).
2899 * This method can be used to attach ascii or binary data,
2900 * such as a BLOB record from a database.
2901 * @param string $string String attachment data.
2902 * @param string $filename Name of the attachment.
2903 * @param string $encoding File encoding (see $Encoding).
2904 * @param string $type File extension (MIME) type.
2905 * @param string $disposition Disposition to use
2906 * @return void
2908 public function addStringAttachment(
2909 $string,
2910 $filename,
2911 $encoding = 'base64',
2912 $type = '',
2913 $disposition = 'attachment'
2915 // If a MIME type is not specified, try to work it out from the file name
2916 if ($type == '') {
2917 $type = self::filenameToType($filename);
2919 // Append to $attachment array
2920 $this->attachment[] = array(
2921 0 => $string,
2922 1 => $filename,
2923 2 => basename($filename),
2924 3 => $encoding,
2925 4 => $type,
2926 5 => true, // isStringAttachment
2927 6 => $disposition,
2928 7 => 0
2933 * Add an embedded (inline) attachment from a file.
2934 * This can include images, sounds, and just about any other document type.
2935 * These differ from 'regular' attachments in that they are intended to be
2936 * displayed inline with the message, not just attached for download.
2937 * This is used in HTML messages that embed the images
2938 * the HTML refers to using the $cid value.
2939 * @param string $path Path to the attachment.
2940 * @param string $cid Content ID of the attachment; Use this to reference
2941 * the content when using an embedded image in HTML.
2942 * @param string $name Overrides the attachment name.
2943 * @param string $encoding File encoding (see $Encoding).
2944 * @param string $type File MIME type.
2945 * @param string $disposition Disposition to use
2946 * @return boolean True on successfully adding an attachment
2948 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2950 if (!@is_file($path)) {
2951 $this->setError($this->lang('file_access') . $path);
2952 return false;
2955 // If a MIME type is not specified, try to work it out from the file name
2956 if ($type == '') {
2957 $type = self::filenameToType($path);
2960 $filename = basename($path);
2961 if ($name == '') {
2962 $name = $filename;
2965 // Append to $attachment array
2966 $this->attachment[] = array(
2967 0 => $path,
2968 1 => $filename,
2969 2 => $name,
2970 3 => $encoding,
2971 4 => $type,
2972 5 => false, // isStringAttachment
2973 6 => $disposition,
2974 7 => $cid
2976 return true;
2980 * Add an embedded stringified attachment.
2981 * This can include images, sounds, and just about any other document type.
2982 * Be sure to set the $type to an image type for images:
2983 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2984 * @param string $string The attachment binary data.
2985 * @param string $cid Content ID of the attachment; Use this to reference
2986 * the content when using an embedded image in HTML.
2987 * @param string $name
2988 * @param string $encoding File encoding (see $Encoding).
2989 * @param string $type MIME type.
2990 * @param string $disposition Disposition to use
2991 * @return boolean True on successfully adding an attachment
2993 public function addStringEmbeddedImage(
2994 $string,
2995 $cid,
2996 $name = '',
2997 $encoding = 'base64',
2998 $type = '',
2999 $disposition = 'inline'
3001 // If a MIME type is not specified, try to work it out from the name
3002 if ($type == '' and !empty($name)) {
3003 $type = self::filenameToType($name);
3006 // Append to $attachment array
3007 $this->attachment[] = array(
3008 0 => $string,
3009 1 => $name,
3010 2 => $name,
3011 3 => $encoding,
3012 4 => $type,
3013 5 => true, // isStringAttachment
3014 6 => $disposition,
3015 7 => $cid
3017 return true;
3021 * Check if an inline attachment is present.
3022 * @access public
3023 * @return boolean
3025 public function inlineImageExists()
3027 foreach ($this->attachment as $attachment) {
3028 if ($attachment[6] == 'inline') {
3029 return true;
3032 return false;
3036 * Check if an attachment (non-inline) is present.
3037 * @return boolean
3039 public function attachmentExists()
3041 foreach ($this->attachment as $attachment) {
3042 if ($attachment[6] == 'attachment') {
3043 return true;
3046 return false;
3050 * Check if this message has an alternative body set.
3051 * @return boolean
3053 public function alternativeExists()
3055 return !empty($this->AltBody);
3059 * Clear queued addresses of given kind.
3060 * @access protected
3061 * @param string $kind 'to', 'cc', or 'bcc'
3062 * @return void
3064 public function clearQueuedAddresses($kind)
3066 $RecipientsQueue = $this->RecipientsQueue;
3067 foreach ($RecipientsQueue as $address => $params) {
3068 if ($params[0] == $kind) {
3069 unset($this->RecipientsQueue[$address]);
3075 * Clear all To recipients.
3076 * @return void
3078 public function clearAddresses()
3080 foreach ($this->to as $to) {
3081 unset($this->all_recipients[strtolower($to[0])]);
3083 $this->to = array();
3084 $this->clearQueuedAddresses('to');
3088 * Clear all CC recipients.
3089 * @return void
3091 public function clearCCs()
3093 foreach ($this->cc as $cc) {
3094 unset($this->all_recipients[strtolower($cc[0])]);
3096 $this->cc = array();
3097 $this->clearQueuedAddresses('cc');
3101 * Clear all BCC recipients.
3102 * @return void
3104 public function clearBCCs()
3106 foreach ($this->bcc as $bcc) {
3107 unset($this->all_recipients[strtolower($bcc[0])]);
3109 $this->bcc = array();
3110 $this->clearQueuedAddresses('bcc');
3114 * Clear all ReplyTo recipients.
3115 * @return void
3117 public function clearReplyTos()
3119 $this->ReplyTo = array();
3120 $this->ReplyToQueue = array();
3124 * Clear all recipient types.
3125 * @return void
3127 public function clearAllRecipients()
3129 $this->to = array();
3130 $this->cc = array();
3131 $this->bcc = array();
3132 $this->all_recipients = array();
3133 $this->RecipientsQueue = array();
3137 * Clear all filesystem, string, and binary attachments.
3138 * @return void
3140 public function clearAttachments()
3142 $this->attachment = array();
3146 * Clear all custom headers.
3147 * @return void
3149 public function clearCustomHeaders()
3151 $this->CustomHeader = array();
3155 * Add an error message to the error container.
3156 * @access protected
3157 * @param string $msg
3158 * @return void
3160 protected function setError($msg)
3162 $this->error_count++;
3163 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
3164 $lasterror = $this->smtp->getError();
3165 if (!empty($lasterror['error'])) {
3166 $msg .= $this->lang('smtp_error') . $lasterror['error'];
3167 if (!empty($lasterror['detail'])) {
3168 $msg .= ' Detail: '. $lasterror['detail'];
3170 if (!empty($lasterror['smtp_code'])) {
3171 $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3173 if (!empty($lasterror['smtp_code_ex'])) {
3174 $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3178 $this->ErrorInfo = $msg;
3182 * Return an RFC 822 formatted date.
3183 * @access public
3184 * @return string
3185 * @static
3187 public static function rfcDate()
3189 // Set the time zone to whatever the default is to avoid 500 errors
3190 // Will default to UTC if it's not set properly in php.ini
3191 date_default_timezone_set(@date_default_timezone_get());
3192 return date('D, j M Y H:i:s O');
3196 * Get the server hostname.
3197 * Returns 'localhost.localdomain' if unknown.
3198 * @access protected
3199 * @return string
3201 protected function serverHostname()
3203 $result = 'localhost.localdomain';
3204 if (!empty($this->Hostname)) {
3205 $result = $this->Hostname;
3206 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
3207 $result = $_SERVER['SERVER_NAME'];
3208 } elseif (function_exists('gethostname') && gethostname() !== false) {
3209 $result = gethostname();
3210 } elseif (php_uname('n') !== false) {
3211 $result = php_uname('n');
3213 return $result;
3217 * Get an error message in the current language.
3218 * @access protected
3219 * @param string $key
3220 * @return string
3222 protected function lang($key)
3224 if (count($this->language) < 1) {
3225 $this->setLanguage('en'); // set the default language
3228 if (array_key_exists($key, $this->language)) {
3229 if ($key == 'smtp_connect_failed') {
3230 //Include a link to troubleshooting docs on SMTP connection failure
3231 //this is by far the biggest cause of support questions
3232 //but it's usually not PHPMailer's fault.
3233 return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3235 return $this->language[$key];
3236 } else {
3237 //Return the key as a fallback
3238 return $key;
3243 * Check if an error occurred.
3244 * @access public
3245 * @return boolean True if an error did occur.
3247 public function isError()
3249 return ($this->error_count > 0);
3253 * Ensure consistent line endings in a string.
3254 * Changes every end of line from CRLF, CR or LF to $this->LE.
3255 * @access public
3256 * @param string $str String to fixEOL
3257 * @return string
3259 public function fixEOL($str)
3261 // Normalise to \n
3262 $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
3263 // Now convert LE as needed
3264 if ($this->LE !== "\n") {
3265 $nstr = str_replace("\n", $this->LE, $nstr);
3267 return $nstr;
3271 * Add a custom header.
3272 * $name value can be overloaded to contain
3273 * both header name and value (name:value)
3274 * @access public
3275 * @param string $name Custom header name
3276 * @param string $value Header value
3277 * @return void
3279 public function addCustomHeader($name, $value = null)
3281 if ($value === null) {
3282 // Value passed in as name:value
3283 $this->CustomHeader[] = explode(':', $name, 2);
3284 } else {
3285 $this->CustomHeader[] = array($name, $value);
3290 * Returns all custom headers.
3291 * @return array
3293 public function getCustomHeaders()
3295 return $this->CustomHeader;
3299 * Create a message from an HTML string.
3300 * Automatically makes modifications for inline images and backgrounds
3301 * and creates a plain-text version by converting the HTML.
3302 * Overwrites any existing values in $this->Body and $this->AltBody
3303 * @access public
3304 * @param string $message HTML message string
3305 * @param string $basedir baseline directory for path
3306 * @param boolean|callable $advanced Whether to use the internal HTML to text converter
3307 * or your own custom converter @see PHPMailer::html2text()
3308 * @return string $message
3310 public function msgHTML($message, $basedir = '', $advanced = false)
3312 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3313 if (array_key_exists(2, $images)) {
3314 foreach ($images[2] as $imgindex => $url) {
3315 // Convert data URIs into embedded images
3316 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3317 $data = substr($url, strpos($url, ','));
3318 if ($match[2]) {
3319 $data = base64_decode($data);
3320 } else {
3321 $data = rawurldecode($data);
3323 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3324 if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
3325 $message = str_replace(
3326 $images[0][$imgindex],
3327 $images[1][$imgindex] . '="cid:' . $cid . '"',
3328 $message
3331 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) {
3332 // Do not change urls for absolute images (thanks to corvuscorax)
3333 // Do not change urls that are already inline images
3334 $filename = basename($url);
3335 $directory = dirname($url);
3336 if ($directory == '.') {
3337 $directory = '';
3339 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3340 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3341 $basedir .= '/';
3343 if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3344 $directory .= '/';
3346 if ($this->addEmbeddedImage(
3347 $basedir . $directory . $filename,
3348 $cid,
3349 $filename,
3350 'base64',
3351 self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
3354 $message = preg_replace(
3355 '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3356 $images[1][$imgindex] . '="cid:' . $cid . '"',
3357 $message
3363 $this->isHTML(true);
3364 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3365 $this->Body = $this->normalizeBreaks($message);
3366 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3367 if (!$this->alternativeExists()) {
3368 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
3369 self::CRLF . self::CRLF;
3371 return $this->Body;
3375 * Convert an HTML string into plain text.
3376 * This is used by msgHTML().
3377 * Note - older versions of this function used a bundled advanced converter
3378 * which was been removed for license reasons in #232
3379 * Example usage:
3380 * <code>
3381 * // Use default conversion
3382 * $plain = $mail->html2text($html);
3383 * // Use your own custom converter
3384 * $plain = $mail->html2text($html, function($html) {
3385 * $converter = new MyHtml2text($html);
3386 * return $converter->get_text();
3387 * });
3388 * </code>
3389 * @param string $html The HTML text to convert
3390 * @param boolean|callable $advanced Any boolean value to use the internal converter,
3391 * or provide your own callable for custom conversion.
3392 * @return string
3394 public function html2text($html, $advanced = false)
3396 if (is_callable($advanced)) {
3397 return call_user_func($advanced, $html);
3399 return html_entity_decode(
3400 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3401 ENT_QUOTES,
3402 $this->CharSet
3407 * Get the MIME type for a file extension.
3408 * @param string $ext File extension
3409 * @access public
3410 * @return string MIME type of file.
3411 * @static
3413 public static function _mime_types($ext = '')
3415 $mimes = array(
3416 'xl' => 'application/excel',
3417 'js' => 'application/javascript',
3418 'hqx' => 'application/mac-binhex40',
3419 'cpt' => 'application/mac-compactpro',
3420 'bin' => 'application/macbinary',
3421 'doc' => 'application/msword',
3422 'word' => 'application/msword',
3423 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3424 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3425 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3426 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3427 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3428 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3429 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3430 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3431 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
3432 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3433 'class' => 'application/octet-stream',
3434 'dll' => 'application/octet-stream',
3435 'dms' => 'application/octet-stream',
3436 'exe' => 'application/octet-stream',
3437 'lha' => 'application/octet-stream',
3438 'lzh' => 'application/octet-stream',
3439 'psd' => 'application/octet-stream',
3440 'sea' => 'application/octet-stream',
3441 'so' => 'application/octet-stream',
3442 'oda' => 'application/oda',
3443 'pdf' => 'application/pdf',
3444 'ai' => 'application/postscript',
3445 'eps' => 'application/postscript',
3446 'ps' => 'application/postscript',
3447 'smi' => 'application/smil',
3448 'smil' => 'application/smil',
3449 'mif' => 'application/vnd.mif',
3450 'xls' => 'application/vnd.ms-excel',
3451 'ppt' => 'application/vnd.ms-powerpoint',
3452 'wbxml' => 'application/vnd.wap.wbxml',
3453 'wmlc' => 'application/vnd.wap.wmlc',
3454 'dcr' => 'application/x-director',
3455 'dir' => 'application/x-director',
3456 'dxr' => 'application/x-director',
3457 'dvi' => 'application/x-dvi',
3458 'gtar' => 'application/x-gtar',
3459 'php3' => 'application/x-httpd-php',
3460 'php4' => 'application/x-httpd-php',
3461 'php' => 'application/x-httpd-php',
3462 'phtml' => 'application/x-httpd-php',
3463 'phps' => 'application/x-httpd-php-source',
3464 'swf' => 'application/x-shockwave-flash',
3465 'sit' => 'application/x-stuffit',
3466 'tar' => 'application/x-tar',
3467 'tgz' => 'application/x-tar',
3468 'xht' => 'application/xhtml+xml',
3469 'xhtml' => 'application/xhtml+xml',
3470 'zip' => 'application/zip',
3471 'mid' => 'audio/midi',
3472 'midi' => 'audio/midi',
3473 'mp2' => 'audio/mpeg',
3474 'mp3' => 'audio/mpeg',
3475 'mpga' => 'audio/mpeg',
3476 'aif' => 'audio/x-aiff',
3477 'aifc' => 'audio/x-aiff',
3478 'aiff' => 'audio/x-aiff',
3479 'ram' => 'audio/x-pn-realaudio',
3480 'rm' => 'audio/x-pn-realaudio',
3481 'rpm' => 'audio/x-pn-realaudio-plugin',
3482 'ra' => 'audio/x-realaudio',
3483 'wav' => 'audio/x-wav',
3484 'bmp' => 'image/bmp',
3485 'gif' => 'image/gif',
3486 'jpeg' => 'image/jpeg',
3487 'jpe' => 'image/jpeg',
3488 'jpg' => 'image/jpeg',
3489 'png' => 'image/png',
3490 'tiff' => 'image/tiff',
3491 'tif' => 'image/tiff',
3492 'eml' => 'message/rfc822',
3493 'css' => 'text/css',
3494 'html' => 'text/html',
3495 'htm' => 'text/html',
3496 'shtml' => 'text/html',
3497 'log' => 'text/plain',
3498 'text' => 'text/plain',
3499 'txt' => 'text/plain',
3500 'rtx' => 'text/richtext',
3501 'rtf' => 'text/rtf',
3502 'vcf' => 'text/vcard',
3503 'vcard' => 'text/vcard',
3504 'xml' => 'text/xml',
3505 'xsl' => 'text/xml',
3506 'mpeg' => 'video/mpeg',
3507 'mpe' => 'video/mpeg',
3508 'mpg' => 'video/mpeg',
3509 'mov' => 'video/quicktime',
3510 'qt' => 'video/quicktime',
3511 'rv' => 'video/vnd.rn-realvideo',
3512 'avi' => 'video/x-msvideo',
3513 'movie' => 'video/x-sgi-movie'
3515 if (array_key_exists(strtolower($ext), $mimes)) {
3516 return $mimes[strtolower($ext)];
3518 return 'application/octet-stream';
3522 * Map a file name to a MIME type.
3523 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3524 * @param string $filename A file name or full path, does not need to exist as a file
3525 * @return string
3526 * @static
3528 public static function filenameToType($filename)
3530 // In case the path is a URL, strip any query string before getting extension
3531 $qpos = strpos($filename, '?');
3532 if (false !== $qpos) {
3533 $filename = substr($filename, 0, $qpos);
3535 $pathinfo = self::mb_pathinfo($filename);
3536 return self::_mime_types($pathinfo['extension']);
3540 * Multi-byte-safe pathinfo replacement.
3541 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3542 * Works similarly to the one in PHP >= 5.2.0
3543 * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3544 * @param string $path A filename or path, does not need to exist as a file
3545 * @param integer|string $options Either a PATHINFO_* constant,
3546 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3547 * @return string|array
3548 * @static
3550 public static function mb_pathinfo($path, $options = null)
3552 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3553 $pathinfo = array();
3554 if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3555 if (array_key_exists(1, $pathinfo)) {
3556 $ret['dirname'] = $pathinfo[1];
3558 if (array_key_exists(2, $pathinfo)) {
3559 $ret['basename'] = $pathinfo[2];
3561 if (array_key_exists(5, $pathinfo)) {
3562 $ret['extension'] = $pathinfo[5];
3564 if (array_key_exists(3, $pathinfo)) {
3565 $ret['filename'] = $pathinfo[3];
3568 switch ($options) {
3569 case PATHINFO_DIRNAME:
3570 case 'dirname':
3571 return $ret['dirname'];
3572 case PATHINFO_BASENAME:
3573 case 'basename':
3574 return $ret['basename'];
3575 case PATHINFO_EXTENSION:
3576 case 'extension':
3577 return $ret['extension'];
3578 case PATHINFO_FILENAME:
3579 case 'filename':
3580 return $ret['filename'];
3581 default:
3582 return $ret;
3587 * Set or reset instance properties.
3588 * You should avoid this function - it's more verbose, less efficient, more error-prone and
3589 * harder to debug than setting properties directly.
3590 * Usage Example:
3591 * `$mail->set('SMTPSecure', 'tls');`
3592 * is the same as:
3593 * `$mail->SMTPSecure = 'tls';`
3594 * @access public
3595 * @param string $name The property name to set
3596 * @param mixed $value The value to set the property to
3597 * @return boolean
3598 * @TODO Should this not be using the __set() magic function?
3600 public function set($name, $value = '')
3602 if (property_exists($this, $name)) {
3603 $this->$name = $value;
3604 return true;
3605 } else {
3606 $this->setError($this->lang('variable_set') . $name);
3607 return false;
3612 * Strip newlines to prevent header injection.
3613 * @access public
3614 * @param string $str
3615 * @return string
3617 public function secureHeader($str)
3619 return trim(str_replace(array("\r", "\n"), '', $str));
3623 * Normalize line breaks in a string.
3624 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3625 * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3626 * @param string $text
3627 * @param string $breaktype What kind of line break to use, defaults to CRLF
3628 * @return string
3629 * @access public
3630 * @static
3632 public static function normalizeBreaks($text, $breaktype = "\r\n")
3634 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3638 * Set the public and private key files and password for S/MIME signing.
3639 * @access public
3640 * @param string $cert_filename
3641 * @param string $key_filename
3642 * @param string $key_pass Password for private key
3643 * @param string $extracerts_filename Optional path to chain certificate
3645 public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3647 $this->sign_cert_file = $cert_filename;
3648 $this->sign_key_file = $key_filename;
3649 $this->sign_key_pass = $key_pass;
3650 $this->sign_extracerts_file = $extracerts_filename;
3654 * Quoted-Printable-encode a DKIM header.
3655 * @access public
3656 * @param string $txt
3657 * @return string
3659 public function DKIM_QP($txt)
3661 $line = '';
3662 for ($i = 0; $i < strlen($txt); $i++) {
3663 $ord = ord($txt[$i]);
3664 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3665 $line .= $txt[$i];
3666 } else {
3667 $line .= '=' . sprintf('%02X', $ord);
3670 return $line;
3674 * Generate a DKIM signature.
3675 * @access public
3676 * @param string $signHeader
3677 * @throws phpmailerException
3678 * @return string
3680 public function DKIM_Sign($signHeader)
3682 if (!defined('PKCS7_TEXT')) {
3683 if ($this->exceptions) {
3684 throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3686 return '';
3688 $privKeyStr = file_get_contents($this->DKIM_private);
3689 if ($this->DKIM_passphrase != '') {
3690 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3691 } else {
3692 $privKey = openssl_pkey_get_private($privKeyStr);
3694 if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { //sha1WithRSAEncryption
3695 openssl_pkey_free($privKey);
3696 return base64_encode($signature);
3698 openssl_pkey_free($privKey);
3699 return '';
3703 * Generate a DKIM canonicalization header.
3704 * @access public
3705 * @param string $signHeader Header
3706 * @return string
3708 public function DKIM_HeaderC($signHeader)
3710 $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3711 $lines = explode("\r\n", $signHeader);
3712 foreach ($lines as $key => $line) {
3713 list($heading, $value) = explode(':', $line, 2);
3714 $heading = strtolower($heading);
3715 $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
3716 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3718 $signHeader = implode("\r\n", $lines);
3719 return $signHeader;
3723 * Generate a DKIM canonicalization body.
3724 * @access public
3725 * @param string $body Message Body
3726 * @return string
3728 public function DKIM_BodyC($body)
3730 if ($body == '') {
3731 return "\r\n";
3733 // stabilize line endings
3734 $body = str_replace("\r\n", "\n", $body);
3735 $body = str_replace("\n", "\r\n", $body);
3736 // END stabilize line endings
3737 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3738 $body = substr($body, 0, strlen($body) - 2);
3740 return $body;
3744 * Create the DKIM header and body in a new message header.
3745 * @access public
3746 * @param string $headers_line Header lines
3747 * @param string $subject Subject
3748 * @param string $body Body
3749 * @return string
3751 public function DKIM_Add($headers_line, $subject, $body)
3753 $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
3754 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3755 $DKIMquery = 'dns/txt'; // Query method
3756 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3757 $subject_header = "Subject: $subject";
3758 $headers = explode($this->LE, $headers_line);
3759 $from_header = '';
3760 $to_header = '';
3761 $date_header = '';
3762 $current = '';
3763 foreach ($headers as $header) {
3764 if (strpos($header, 'From:') === 0) {
3765 $from_header = $header;
3766 $current = 'from_header';
3767 } elseif (strpos($header, 'To:') === 0) {
3768 $to_header = $header;
3769 $current = 'to_header';
3770 } elseif (strpos($header, 'Date:') === 0) {
3771 $date_header = $header;
3772 $current = 'date_header';
3773 } else {
3774 if (!empty($$current) && strpos($header, ' =?') === 0) {
3775 $$current .= $header;
3776 } else {
3777 $current = '';
3781 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3782 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3783 $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
3784 $subject = str_replace(
3785 '|',
3786 '=7C',
3787 $this->DKIM_QP($subject_header)
3788 ); // Copied header fields (dkim-quoted-printable)
3789 $body = $this->DKIM_BodyC($body);
3790 $DKIMlen = strlen($body); // Length of body
3791 $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
3792 if ('' == $this->DKIM_identity) {
3793 $ident = '';
3794 } else {
3795 $ident = ' i=' . $this->DKIM_identity . ';';
3797 $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3798 $DKIMsignatureType . '; q=' .
3799 $DKIMquery . '; l=' .
3800 $DKIMlen . '; s=' .
3801 $this->DKIM_selector .
3802 ";\r\n" .
3803 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3804 "\th=From:To:Date:Subject;\r\n" .
3805 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3806 "\tz=$from\r\n" .
3807 "\t|$to\r\n" .
3808 "\t|$date\r\n" .
3809 "\t|$subject;\r\n" .
3810 "\tbh=" . $DKIMb64 . ";\r\n" .
3811 "\tb=";
3812 $toSign = $this->DKIM_HeaderC(
3813 $from_header . "\r\n" .
3814 $to_header . "\r\n" .
3815 $date_header . "\r\n" .
3816 $subject_header . "\r\n" .
3817 $dkimhdrs
3819 $signed = $this->DKIM_Sign($toSign);
3820 return $dkimhdrs . $signed . "\r\n";
3824 * Detect if a string contains a line longer than the maximum line length allowed.
3825 * @param string $str
3826 * @return boolean
3827 * @static
3829 public static function hasLineLongerThanMax($str)
3831 //+2 to include CRLF line break for a 1000 total
3832 return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
3836 * Allows for public read access to 'to' property.
3837 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3838 * @access public
3839 * @return array
3841 public function getToAddresses()
3843 return $this->to;
3847 * Allows for public read access to 'cc' property.
3848 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3849 * @access public
3850 * @return array
3852 public function getCcAddresses()
3854 return $this->cc;
3858 * Allows for public read access to 'bcc' property.
3859 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3860 * @access public
3861 * @return array
3863 public function getBccAddresses()
3865 return $this->bcc;
3869 * Allows for public read access to 'ReplyTo' property.
3870 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3871 * @access public
3872 * @return array
3874 public function getReplyToAddresses()
3876 return $this->ReplyTo;
3880 * Allows for public read access to 'all_recipients' property.
3881 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3882 * @access public
3883 * @return array
3885 public function getAllRecipientAddresses()
3887 return $this->all_recipients;
3891 * Perform a callback.
3892 * @param boolean $isSent
3893 * @param array $to
3894 * @param array $cc
3895 * @param array $bcc
3896 * @param string $subject
3897 * @param string $body
3898 * @param string $from
3900 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3902 if (!empty($this->action_function) && is_callable($this->action_function)) {
3903 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3904 call_user_func_array($this->action_function, $params);
3910 * PHPMailer exception handler
3911 * @package PHPMailer
3913 class phpmailerException extends Exception
3916 * Prettify error message output
3917 * @return string
3919 public function errorMessage()
3921 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3922 return $errorMsg;