MDL-57474 mailer: Make sure that our exlicit MessageID is trimmed
[moodle.git] / lib / phpmailer / class.phpmailer.php
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.
18  */
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)
27  */
28 class PHPMailer
29 {
30     /**
31      * The PHPMailer Version number.
32      * @var string
33      */
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
41      */
42     public $Priority = null;
44     /**
45      * The character set of the message.
46      * @var string
47      */
48     public $CharSet = 'iso-8859-1';
50     /**
51      * The MIME Content-type of the message.
52      * @var string
53      */
54     public $ContentType = 'text/plain';
56     /**
57      * The message encoding.
58      * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59      * @var string
60      */
61     public $Encoding = '8bit';
63     /**
64      * Holds the most recent mailer error message.
65      * @var string
66      */
67     public $ErrorInfo = '';
69     /**
70      * The From email address for the message.
71      * @var string
72      */
73     public $From = 'root@localhost';
75     /**
76      * The From name of the message.
77      * @var string
78      */
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
85      */
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
95      */
96     public $ReturnPath = '';
98     /**
99      * The Subject of the message.
100      * @var string
101      */
102     public $Subject = '';
104     /**
105      * An HTML or plain text message body.
106      * If HTML then call isHTML(true).
107      * @var string
108      */
109     public $Body = '';
111     /**
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
117      */
118     public $AltBody = '';
120     /**
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
127      */
128     public $Ical = '';
130     /**
131      * The complete compiled MIME message body.
132      * @access protected
133      * @var string
134      */
135     protected $MIMEBody = '';
137     /**
138      * The complete compiled MIME message headers.
139      * @var string
140      * @access protected
141      */
142     protected $MIMEHeader = '';
144     /**
145      * Extra headers that createHeader() doesn't fold in.
146      * @var string
147      * @access protected
148      */
149     protected $mailHeader = '';
151     /**
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
155      */
156     public $WordWrap = 0;
158     /**
159      * Which method to use to send mail.
160      * Options: "mail", "sendmail", or "smtp".
161      * @var string
162      */
163     public $Mailer = 'mail';
165     /**
166      * The path to the sendmail program.
167      * @var string
168      */
169     public $Sendmail = '/usr/sbin/sendmail';
171     /**
172      * Whether mail() uses a fully sendmail-compatible MTA.
173      * One which supports sendmail's "-oi -f" options.
174      * @var boolean
175      */
176     public $UseSendmailOptions = true;
178     /**
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.
183      */
184     public $PluginDir = '';
186     /**
187      * The email address that a reading confirmation should be sent to, also known as read receipt.
188      * @var string
189      */
190     public $ConfirmReadingTo = '';
192     /**
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
198      */
199     public $Hostname = '';
201     /**
202      * An ID to be used in the Message-ID header.
203      * If empty, a unique id will be generated.
204      * @var string
205      */
206     public $MessageID = '';
208     /**
209      * The message Date to be used in the Date header.
210      * If empty, the current date will be added.
211      * @var string
212      */
213     public $MessageDate = '';
215     /**
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
225      */
226     public $Host = 'localhost';
228     /**
229      * The default SMTP server port.
230      * @var integer
231      * @TODO Why is this needed when the SMTP class takes care of it?
232      */
233     public $Port = 25;
235     /**
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
241      */
242     public $Helo = '';
244     /**
245      * What kind of encryption to use on the SMTP connection.
246      * Options: '', 'ssl' or 'tls'
247      * @var string
248      */
249     public $SMTPSecure = '';
251     /**
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
256      */
257     public $SMTPAutoTLS = true;
259     /**
260      * Whether to use SMTP authentication.
261      * Uses the Username and Password properties.
262      * @var boolean
263      * @see PHPMailer::$Username
264      * @see PHPMailer::$Password
265      */
266     public $SMTPAuth = false;
268     /**
269      * Options array passed to stream_context_create when connecting via SMTP.
270      * @var array
271      */
272     public $SMTPOptions = array();
274     /**
275      * SMTP username.
276      * @var string
277      */
278     public $Username = '';
280     /**
281      * SMTP password.
282      * @var string
283      */
284     public $Password = '';
286     /**
287      * SMTP auth type.
288      * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified
289      * @var string
290      */
291     public $AuthType = '';
293     /**
294      * SMTP realm.
295      * Used for NTLM auth
296      * @var string
297      */
298     public $Realm = '';
300     /**
301      * SMTP workstation.
302      * Used for NTLM auth
303      * @var string
304      */
305     public $Workstation = '';
307     /**
308      * The SMTP server timeout in seconds.
309      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
310      * @var integer
311      */
312     public $Timeout = 300;
314     /**
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
325      */
326     public $SMTPDebug = 0;
328     /**
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
334      *
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
341      */
342     public $Debugoutput = 'echo';
344     /**
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
349      */
350     public $SMTPKeepAlive = false;
352     /**
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
357      */
358     public $SingleTo = false;
360     /**
361      * Storage for addresses when SingleTo is enabled.
362      * @var array
363      * @TODO This should really not be public
364      */
365     public $SingleToArray = array();
367     /**
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
373      */
374     public $do_verp = false;
376     /**
377      * Whether to allow sending messages with an empty body.
378      * @var boolean
379      */
380     public $AllowEmpty = false;
382     /**
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
387      */
388     public $LE = "\n";
390     /**
391      * DKIM selector.
392      * @var string
393      */
394     public $DKIM_selector = '';
396     /**
397      * DKIM Identity.
398      * Usually the email address used as the source of the email.
399      * @var string
400      */
401     public $DKIM_identity = '';
403     /**
404      * DKIM passphrase.
405      * Used if your key is encrypted.
406      * @var string
407      */
408     public $DKIM_passphrase = '';
410     /**
411      * DKIM signing domain name.
412      * @example 'example.com'
413      * @var string
414      */
415     public $DKIM_domain = '';
417     /**
418      * DKIM private key file path.
419      * @var string
420      */
421     public $DKIM_private = '';
423     /**
424      * Callback Action function name.
425      *
426      * The function that handles the result of the send email action.
427      * It is called out by send() for each email sent.
428      *
429      * Value can be any php callable: http://www.php.net/is_callable
430      *
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
440      */
441     public $action_function = '';
443     /**
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
447      */
448     public $XMailer = '';
450     /**
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
456      */
457     public static $validator = 'auto';
459     /**
460      * An instance of the SMTP sender class.
461      * @var SMTP
462      * @access protected
463      */
464     protected $smtp = null;
466     /**
467      * The array of 'to' names and addresses.
468      * @var array
469      * @access protected
470      */
471     protected $to = array();
473     /**
474      * The array of 'cc' names and addresses.
475      * @var array
476      * @access protected
477      */
478     protected $cc = array();
480     /**
481      * The array of 'bcc' names and addresses.
482      * @var array
483      * @access protected
484      */
485     protected $bcc = array();
487     /**
488      * The array of reply-to names and addresses.
489      * @var array
490      * @access protected
491      */
492     protected $ReplyTo = array();
494     /**
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
500      */
501     protected $all_recipients = array();
503     /**
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
512      */
513     protected $RecipientsQueue = array();
515     /**
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
522      */
523     protected $ReplyToQueue = array();
525     /**
526      * The array of attachments.
527      * @var array
528      * @access protected
529      */
530     protected $attachment = array();
532     /**
533      * The array of custom headers.
534      * @var array
535      * @access protected
536      */
537     protected $CustomHeader = array();
539     /**
540      * The most recent Message-ID (including angular brackets).
541      * @var string
542      * @access protected
543      */
544     protected $lastMessageID = '';
546     /**
547      * The message's MIME type.
548      * @var string
549      * @access protected
550      */
551     protected $message_type = '';
553     /**
554      * The array of MIME boundary strings.
555      * @var array
556      * @access protected
557      */
558     protected $boundary = array();
560     /**
561      * The array of available languages.
562      * @var array
563      * @access protected
564      */
565     protected $language = array();
567     /**
568      * The number of errors encountered.
569      * @var integer
570      * @access protected
571      */
572     protected $error_count = 0;
574     /**
575      * The S/MIME certificate file path.
576      * @var string
577      * @access protected
578      */
579     protected $sign_cert_file = '';
581     /**
582      * The S/MIME key file path.
583      * @var string
584      * @access protected
585      */
586     protected $sign_key_file = '';
588     /**
589      * The optional S/MIME extra certificates ("CA Chain") file path.
590      * @var string
591      * @access protected
592      */
593     protected $sign_extracerts_file = '';
595     /**
596      * The S/MIME password for the key.
597      * Used only if the key is encrypted.
598      * @var string
599      * @access protected
600      */
601     protected $sign_key_pass = '';
603     /**
604      * Whether to throw exceptions for errors.
605      * @var boolean
606      * @access protected
607      */
608     protected $exceptions = false;
610     /**
611      * Unique ID used for message ID and boundaries.
612      * @var string
613      * @access protected
614      */
615     protected $uniqueid = '';
617     /**
618      * Error severity: message only, continue processing.
619      */
620     const STOP_MESSAGE = 0;
622     /**
623      * Error severity: message, likely ok to continue processing.
624      */
625     const STOP_CONTINUE = 1;
627     /**
628      * Error severity: message, plus full stop, critical error reached.
629      */
630     const STOP_CRITICAL = 2;
632     /**
633      * SMTP RFC standard line ending.
634      */
635     const CRLF = "\r\n";
637     /**
638      * The maximum line length allowed by RFC 2822 section 2.1.1
639      * @var integer
640      */
641     const MAX_LINE_LENGTH = 998;
643     /**
644      * Constructor.
645      * @param boolean $exceptions Should we throw external exceptions?
646      */
647     public function __construct($exceptions = null)
648     {
649         if ($exceptions !== null) {
650             $this->exceptions = (boolean)$exceptions;
651         }
652     }
654     /**
655      * Destructor.
656      */
657     public function __destruct()
658     {
659         //Close any open SMTP connection nicely
660         $this->smtpClose();
661     }
663     /**
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
675      */
676     private function mailPassthru($to, $subject, $body, $header, $params)
677     {
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));
683         }
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);
690         }
691         return $result;
692     }
694     /**
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
700      */
701     protected function edebug($str)
702     {
703         if ($this->SMTPDebug <= 0) {
704             return;
705         }
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;
710         }
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'
722                 )
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";
734         }
735     }
737     /**
738      * Sets message type to HTML or plain.
739      * @param boolean $isHtml True for HTML mode.
740      * @return void
741      */
742     public function isHTML($isHtml = true)
743     {
744         if ($isHtml) {
745             $this->ContentType = 'text/html';
746         } else {
747             $this->ContentType = 'text/plain';
748         }
749     }
751     /**
752      * Send messages using SMTP.
753      * @return void
754      */
755     public function isSMTP()
756     {
757         $this->Mailer = 'smtp';
758     }
760     /**
761      * Send messages using PHP's mail() function.
762      * @return void
763      */
764     public function isMail()
765     {
766         $this->Mailer = 'mail';
767     }
769     /**
770      * Send messages using $Sendmail.
771      * @return void
772      */
773     public function isSendmail()
774     {
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;
781         }
782         $this->Mailer = 'sendmail';
783     }
785     /**
786      * Send messages using qmail.
787      * @return void
788      */
789     public function isQmail()
790     {
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;
797         }
798         $this->Mailer = 'qmail';
799     }
801     /**
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
806      */
807     public function addAddress($address, $name = '')
808     {
809         return $this->addOrEnqueueAnAddress('to', $address, $name);
810     }
812     /**
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
818      */
819     public function addCC($address, $name = '')
820     {
821         return $this->addOrEnqueueAnAddress('cc', $address, $name);
822     }
824     /**
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
830      */
831     public function addBCC($address, $name = '')
832     {
833         return $this->addOrEnqueueAnAddress('bcc', $address, $name);
834     }
836     /**
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
841      */
842     public function addReplyTo($address, $name = '')
843     {
844         return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
845     }
847     /**
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
858      */
859     protected function addOrEnqueueAnAddress($kind, $address, $name)
860     {
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);
870             }
871             return false;
872         }
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;
880                 }
881             } else {
882                 if (!array_key_exists($address, $this->ReplyToQueue)) {
883                     $this->ReplyToQueue[$address] = $params;
884                     return true;
885                 }
886             }
887             return false;
888         }
889         // Immediately add standard addresses without IDN.
890         return call_user_func_array(array($this, 'addAnAddress'), $params);
891     }
893     /**
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
902      */
903     protected function addAnAddress($kind, $address, $name = '')
904     {
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);
911             }
912             return false;
913         }
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);
920             }
921             return false;
922         }
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;
928             }
929         } else {
930             if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
931                 $this->ReplyTo[strtolower($address)] = array($address, $name);
932                 return true;
933             }
934         }
935         return false;
936     }
938     /**
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
947      */
948     public function parseAddresses($addrstr, $useimap = true)
949     {
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
960                         );
961                     }
962                 }
963             }
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
976                         );
977                     }
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
985                         );
986                     }
987                 }
988             }
989         }
990         return $addresses;
991     }
993     /**
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
1000      */
1001     public function setFrom($address, $name = '', $auto = true)
1002     {
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);
1014             }
1015             return false;
1016         }
1017         $this->From = $address;
1018         $this->FromName = $name;
1019         if ($auto) {
1020             if (empty($this->Sender)) {
1021                 $this->Sender = $address;
1022             }
1023         }
1024         return true;
1025     }
1027     /**
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
1033      */
1034     public function getLastMessageID()
1035     {
1036         return $this->lastMessageID;
1037     }
1039     /**
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
1057      */
1058     public static function validateAddress($address, $patternselect = null)
1059     {
1060         if (is_null($patternselect)) {
1061             $patternselect = self::$validator;
1062         }
1063         if (is_callable($patternselect)) {
1064             return call_user_func($patternselect, $address);
1065         }
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;
1069         }
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';
1079                 }
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';
1089                 }
1090             }
1091         }
1092         switch ($patternselect) {
1093             case 'pcre8':
1094                 /**
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.
1099                  */
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
1111                 );
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
1126                 );
1127             case 'html5':
1128                 /**
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)
1131                  */
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
1136                 );
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);
1146         }
1147     }
1149     /**
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
1153      */
1154     public function idnSupported()
1155     {
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');
1158     }
1160     /**
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
1170      */
1171     public function punyencodeAddress($address)
1172     {
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;
1185                 }
1186             }
1187         }
1188         return $address;
1189     }
1191     /**
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.
1196      */
1197     public function send()
1198     {
1199         try {
1200             if (!$this->preSend()) {
1201                 return false;
1202             }
1203             return $this->postSend();
1204         } catch (phpmailerException $exc) {
1205             $this->mailHeader = '';
1206             $this->setError($exc->getMessage());
1207             if ($this->exceptions) {
1208                 throw $exc;
1209             }
1210             return false;
1211         }
1212     }
1214     /**
1215      * Prepare a message for sending.
1216      * @throws phpmailerException
1217      * @return boolean
1218      */
1219     public function preSend()
1220     {
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);
1229             }
1230             if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1231                 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1232             }
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;
1239                 }
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);
1247                     }
1248                     return false;
1249                 }
1250             }
1252             // Set whether the message is multipart/alternative
1253             if ($this->alternativeExists()) {
1254                 $this->ContentType = 'multipart/alternative';
1255             }
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);
1261             }
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:;');
1278                 }
1279                 $this->mailHeader .= $this->headerLine(
1280                     'Subject',
1281                     $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1282                 );
1283             }
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
1294                 );
1295                 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1296                     str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1297             }
1298             return true;
1299         } catch (phpmailerException $exc) {
1300             $this->setError($exc->getMessage());
1301             if ($this->exceptions) {
1302                 throw $exc;
1303             }
1304             return false;
1305         }
1306     }
1308     /**
1309      * Actually send a message.
1310      * Send the email via the selected mechanism
1311      * @throws phpmailerException
1312      * @return boolean
1313      */
1314     public function postSend()
1315     {
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);
1330                     }
1332                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1333             }
1334         } catch (phpmailerException $exc) {
1335             $this->setError($exc->getMessage());
1336             $this->edebug($exc->getMessage());
1337             if ($this->exceptions) {
1338                 throw $exc;
1339             }
1340         }
1341         return false;
1342     }
1344     /**
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
1352      */
1353     protected function sendmailSend($header, $body)
1354     {
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));
1360             }
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));
1366             }
1367         }
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);
1372                 }
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
1385                 );
1386                 if ($result != 0) {
1387                     throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1388                 }
1389             }
1390         } else {
1391             if (!@$mail = popen($sendmail, 'w')) {
1392                 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1393             }
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
1405             );
1406             if ($result != 0) {
1407                 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1408             }
1409         }
1410         return true;
1411     }
1413     /**
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
1421      */
1422     protected function mailSend($header, $body)
1423     {
1424         $toArr = array();
1425         foreach ($this->to as $toaddr) {
1426             $toArr[] = $this->addrFormat($toaddr);
1427         }
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);
1434         }
1435         if ($this->Sender != '' and !ini_get('safe_mode')) {
1436             $old_from = ini_get('sendmail_from');
1437             ini_set('sendmail_from', $this->Sender);
1438         }
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);
1444             }
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);
1448         }
1449         if (isset($old_from)) {
1450             ini_set('sendmail_from', $old_from);
1451         }
1452         if (!$result) {
1453             throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1454         }
1455         return true;
1456     }
1458     /**
1459      * Get an instance to use for SMTP operations.
1460      * Override this function to load your own SMTP implementation
1461      * @return SMTP
1462      */
1463     public function getSMTPInstance()
1464     {
1465         if (!is_object($this->smtp)) {
1466             $this->smtp = new SMTP;
1467         }
1468         return $this->smtp;
1469     }
1471     /**
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
1482      */
1483     protected function smtpSend($header, $body)
1484     {
1485         $bad_rcpt = array();
1486         if (!$this->smtpConnect($this->SMTPOptions)) {
1487             throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1488         }
1489         if ('' == $this->Sender) {
1490             $smtp_from = $this->From;
1491         } else {
1492             $smtp_from = $this->Sender;
1493         }
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);
1497         }
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;
1508                 }
1509                 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1510             }
1511         }
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);
1516         }
1517         if ($this->SMTPKeepAlive) {
1518             $this->smtp->reset();
1519         } else {
1520             $this->smtp->quit();
1521             $this->smtp->close();
1522         }
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'];
1528             }
1529             throw new phpmailerException(
1530                 $this->lang('recipients_failed') . $errstr,
1531                 self::STOP_CONTINUE
1532             );
1533         }
1534         return true;
1535     }
1537     /**
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
1545      */
1546     public function smtpConnect($options = null)
1547     {
1548         if (is_null($this->smtp)) {
1549             $this->smtp = $this->getSMTPInstance();
1550         }
1552         //If no options are provided, use whatever is set in the instance
1553         if (is_null($options)) {
1554             $options = $this->SMTPOptions;
1555         }
1557         // Already connected?
1558         if ($this->smtp->connected()) {
1559             return true;
1560         }
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;
1574             }
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';
1591             }
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);
1598                 }
1599             }
1600             $host = $hostinfo[3];
1601             $port = $this->Port;
1602             $tport = (integer)$hostinfo[4];
1603             if ($tport > 0 and $tport < 65536) {
1604                 $port = $tport;
1605             }
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();
1612                     }
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;
1621                     }
1622                     if ($tls) {
1623                         if (!$this->smtp->startTLS()) {
1624                             throw new phpmailerException($this->lang('connect_host'));
1625                         }
1626                         // We must resend EHLO after TLS negotiation
1627                         $this->smtp->hello($hello);
1628                     }
1629                     if ($this->SMTPAuth) {
1630                         if (!$this->smtp->authenticate(
1631                             $this->Username,
1632                             $this->Password,
1633                             $this->AuthType,
1634                             $this->Realm,
1635                             $this->Workstation
1636                         )
1637                         ) {
1638                             throw new phpmailerException($this->lang('authenticate'));
1639                         }
1640                     }
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();
1647                 }
1648             }
1649         }
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;
1655         }
1656         return false;
1657     }
1659     /**
1660      * Close the active SMTP session if one exists.
1661      * @return void
1662      */
1663     public function smtpClose()
1664     {
1665         if (is_a($this->smtp, 'SMTP')) {
1666             if ($this->smtp->connected()) {
1667                 $this->smtp->quit();
1668                 $this->smtp->close();
1669             }
1670         }
1671     }
1673     /**
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
1681      */
1682     public function setLanguage($langcode = 'en', $lang_path = '')
1683     {
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: '
1705         );
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;
1709         }
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;
1721             }
1722         }
1723         $this->language = $PHPMAILER_LANG;
1724         return (boolean)$foundlang; // Returns false if language not found
1725     }
1727     /**
1728      * Get the array of strings for the current language.
1729      * @return array
1730      */
1731     public function getTranslations()
1732     {
1733         return $this->language;
1734     }
1736     /**
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
1745      */
1746     public function addrAppend($type, $addr)
1747     {
1748         $addresses = array();
1749         foreach ($addr as $address) {
1750             $addresses[] = $this->addrFormat($address);
1751         }
1752         return $type . ': ' . implode(', ', $addresses) . $this->LE;
1753     }
1755     /**
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
1761      */
1762     public function addrFormat($addr)
1763     {
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             ) . '>';
1770         }
1771     }
1773     /**
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
1783      */
1784     public function wrapText($message, $length, $qp_mode = false)
1785     {
1786         if ($qp_mode) {
1787             $soft_break = sprintf(' =%s', $this->LE);
1788         } else {
1789             $soft_break = $this->LE;
1790         }
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);
1801         }
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;
1823                             }
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;
1830                         }
1831                         $buf = '';
1832                     }
1833                     while (strlen($word) > 0) {
1834                         if ($length <= 0) {
1835                             break;
1836                         }
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;
1844                         }
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;
1852                         }
1853                     }
1854                 } else {
1855                     $buf_o = $buf;
1856                     if (!$firstword) {
1857                         $buf .= ' ';
1858                     }
1859                     $buf .= $word;
1861                     if (strlen($buf) > $length and $buf_o != '') {
1862                         $message .= $buf_o . $soft_break;
1863                         $buf = $word;
1864                     }
1865                 }
1866                 $firstword = false;
1867             }
1868             $message .= $buf . self::CRLF;
1869         }
1871         return $message;
1872     }
1874     /**
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
1882      */
1883     public function utf8CharBoundary($encodedText, $maxLength)
1884     {
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);
1901                     }
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;
1911                 }
1912             } else {
1913                 // No encoded character found
1914                 $foundSplitPos = true;
1915             }
1916         }
1917         return $maxLength;
1918     }
1920     /**
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
1927      */
1928     public function setWordWrap()
1929     {
1930         if ($this->WordWrap < 1) {
1931             return;
1932         }
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;
1944         }
1945     }
1947     /**
1948      * Assemble message headers.
1949      * @access public
1950      * @return string The assembled headers
1951      */
1952     public function createHeader()
1953     {
1954         $result = '';
1956         if ($this->MessageDate == '') {
1957             $this->MessageDate = self::rfcDate();
1958         }
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);
1966                 }
1967             }
1968         } else {
1969             if (count($this->to) > 0) {
1970                 if ($this->Mailer != 'mail') {
1971                     $result .= $this->addrAppend('To', $this->to);
1972                 }
1973             } elseif (count($this->cc) == 0) {
1974                 $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1975             }
1976         }
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);
1983         }
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'
1988             )
1989             and count($this->bcc) > 0
1990         ) {
1991             $result .= $this->addrAppend('Bcc', $this->bcc);
1992         }
1994         if (count($this->ReplyTo) > 0) {
1995             $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1996         }
1998         // mail() sets the subject itself
1999         if ($this->Mailer != 'mail') {
2000             $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2001         }
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());
2007         }
2008         $result .= $this->headerLine('Message-ID', $this->lastMessageID);
2009         if (!is_null($this->Priority)) {
2010             $result .= $this->headerLine('X-Priority', $this->Priority);
2011         }
2012         if ($this->XMailer == '') {
2013             $result .= $this->headerLine(
2014                 'X-Mailer',
2015                 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
2016             );
2017         } else {
2018             $myXmailer = trim($this->XMailer);
2019             if ($myXmailer) {
2020                 $result .= $this->headerLine('X-Mailer', $myXmailer);
2021             }
2022         }
2024         if ($this->ConfirmReadingTo != '') {
2025             $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
2026         }
2028         // Add custom headers
2029         foreach ($this->CustomHeader as $header) {
2030             $result .= $this->headerLine(
2031                 trim($header[0]),
2032                 $this->encodeHeader(trim($header[1]))
2033             );
2034         }
2035         if (!$this->sign_key_file) {
2036             $result .= $this->headerLine('MIME-Version', '1.0');
2037             $result .= $this->getMailMIME();
2038         }
2040         return $result;
2041     }
2043     /**
2044      * Get the message MIME type headers.
2045      * @access public
2046      * @return string
2047      */
2048     public function getMailMIME()
2049     {
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;
2074         }
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');
2081                 }
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);
2085             }
2086         }
2088         if ($this->Mailer != 'mail') {
2089             $result .= $this->LE;
2090         }
2092         return $result;
2093     }
2095     /**
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
2102      */
2103     public function getSentMIMEMessage()
2104     {
2105         return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
2106     }
2108     /**
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
2114      */
2115     public function createBody()
2116     {
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;
2126         }
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';
2137         }
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';
2142         }
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';
2151         }
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';
2156         }
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;
2199                 }
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;
2262         }
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');
2270                 }
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');
2275                 }
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
2285                     );
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
2295                     );
2296                 }
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());
2309                 }
2310             } catch (phpmailerException $exc) {
2311                 $body = '';
2312                 if ($this->exceptions) {
2313                     throw $exc;
2314                 }
2315             }
2316         }
2317         return $body;
2318     }
2320     /**
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
2328      */
2329     protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2330     {
2331         $result = '';
2332         if ($charSet == '') {
2333             $charSet = $this->CharSet;
2334         }
2335         if ($contentType == '') {
2336             $contentType = $this->ContentType;
2337         }
2338         if ($encoding == '') {
2339             $encoding = $this->Encoding;
2340         }
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);
2347         }
2348         $result .= $this->LE;
2350         return $result;
2351     }
2353     /**
2354      * Return the end of a message boundary.
2355      * @access protected
2356      * @param string $boundary
2357      * @return string
2358      */
2359     protected function endBoundary($boundary)
2360     {
2361         return $this->LE . '--' . $boundary . '--' . $this->LE;
2362     }
2364     /**
2365      * Set the message type.
2366      * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2367      * @access protected
2368      * @return void
2369      */
2370     protected function setMessageType()
2371     {
2372         $type = array();
2373         if ($this->alternativeExists()) {
2374             $type[] = 'alt';
2375         }
2376         if ($this->inlineImageExists()) {
2377             $type[] = 'inline';
2378         }
2379         if ($this->attachmentExists()) {
2380             $type[] = 'attach';
2381         }
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';
2386         }
2387     }
2389     /**
2390      * Format a header line.
2391      * @access public
2392      * @param string $name
2393      * @param string $value
2394      * @return string
2395      */
2396     public function headerLine($name, $value)
2397     {
2398         return $name . ': ' . $value . $this->LE;
2399     }
2401     /**
2402      * Return a formatted mail line.
2403      * @access public
2404      * @param string $value
2405      * @return string
2406      */
2407     public function textLine($value)
2408     {
2409         return $value . $this->LE;
2410     }
2412     /**
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
2422      */
2423     public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2424     {
2425         try {
2426             if (!@is_file($path)) {
2427                 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2428             }
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);
2433             }
2435             $filename = basename($path);
2436             if ($name == '') {
2437                 $name = $filename;
2438             }
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
2449             );
2451         } catch (phpmailerException $exc) {
2452             $this->setError($exc->getMessage());
2453             $this->edebug($exc->getMessage());
2454             if ($this->exceptions) {
2455                 throw $exc;
2456             }
2457             return false;
2458         }
2459         return true;
2460     }
2462     /**
2463      * Return the array of attachments.
2464      * @return array
2465      */
2466     public function getAttachments()
2467     {
2468         return $this->attachment;
2469     }
2471     /**
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
2478      */
2479     protected function attachAll($disposition_type, $boundary)
2480     {
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];
2498                 }
2500                 $inclhash = md5(serialize($attachment));
2501                 if (in_array($inclhash, $incl)) {
2502                     continue;
2503                 }
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;
2512                 }
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
2523                     );
2524                 } else {
2525                     $mime[] = sprintf(
2526                         'Content-Type: %s%s',
2527                         $type,
2528                         $this->LE
2529                     );
2530                 }
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);
2534                 }
2536                 if ($disposition == 'inline') {
2537                     $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2538                 }
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
2552                         );
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
2560                             );
2561                         } else {
2562                             $mime[] = sprintf(
2563                                 'Content-Disposition: %s%s',
2564                                 $disposition,
2565                                 $this->LE . $this->LE
2566                             );
2567                         }
2568                     }
2569                 } else {
2570                     $mime[] = $this->LE;
2571                 }
2573                 // Encode as string attachment
2574                 if ($bString) {
2575                     $mime[] = $this->encodeString($string, $encoding);
2576                     if ($this->isError()) {
2577                         return '';
2578                     }
2579                     $mime[] = $this->LE . $this->LE;
2580                 } else {
2581                     $mime[] = $this->encodeFile($path, $encoding);
2582                     if ($this->isError()) {
2583                         return '';
2584                     }
2585                     $mime[] = $this->LE . $this->LE;
2586                 }
2587             }
2588         }
2590         $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2592         return implode('', $mime);
2593     }
2595     /**
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
2603      */
2604     protected function encodeFile($path, $encoding = 'base64')
2605     {
2606         try {
2607             if (!is_readable($path)) {
2608                 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2609             }
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);
2619                 }
2620             }
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);
2628                 }
2629             }
2630             return $file_buffer;
2631         } catch (Exception $exc) {
2632             $this->setError($exc->getMessage());
2633             return '';
2634         }
2635     }
2637     /**
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
2644      */
2645     public function encodeString($str, $encoding = 'base64')
2646     {
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;
2658                 }
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;
2669         }
2670         return $encoded;
2671     }
2673     /**
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
2680      */
2681     public function encodeHeader($str, $position = 'text')
2682     {
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\"");
2693                     }
2694                 }
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;
2705         }
2707         //There are no chars that need encoding
2708         if ($matchcount == 0) {
2709             return ($str);
2710         }
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"));
2725             }
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));
2731         }
2733         $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2734         $encoded = trim(str_replace("\n", $this->LE, $encoded));
2736         return $encoded;
2737     }
2739     /**
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
2744      */
2745     public function hasMultiBytes($str)
2746     {
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;
2751         }
2752     }
2754     /**
2755      * Does a string contain any 8-bit chars (in any charset)?
2756      * @param string $text
2757      * @return boolean
2758      */
2759     public function has8bitChars($text)
2760     {
2761         return (boolean)preg_match('/[\x80-\xFF]/', $text);
2762     }
2764     /**
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
2773      */
2774     public function base64EncodeWrapMB($str, $linebreak = null)
2775     {
2776         $start = '=?' . $this->CharSet . '?B?';
2777         $end = '?=';
2778         $encoded = '';
2779         if ($linebreak === null) {
2780             $linebreak = $this->LE;
2781         }
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;
2800         }
2802         // Chomp the last linefeed
2803         $encoded = substr($encoded, 0, -strlen($linebreak));
2804         return $encoded;
2805     }
2807     /**
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
2815      */
2816     public function encodeQP($string, $line_max = 76)
2817     {
2818         // Use native function if it's available (>= PHP5.3)
2819         if (function_exists('quoted_printable_encode')) {
2820             return quoted_printable_encode($string);
2821         }
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)
2827         );
2828         return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2829     }
2831     /**
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.
2840      */
2841     public function encodeQPphp(
2842         $string,
2843         $line_max = 76,
2844         /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2845     ) {
2846         return $this->encodeQP($string, $line_max);
2847     }
2849     /**
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
2856      */
2857     public function encodeQ($str, $position = 'text')
2858     {
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;
2879         }
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], '=');
2888             }
2889             foreach (array_unique($matches[0]) as $char) {
2890                 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2891             }
2892         }
2893         // Replace every spaces to _ (more readable than =20)
2894         return str_replace(' ', '_', $encoded);
2895     }
2897     /**
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
2907      */
2908     public function addStringAttachment(
2909         $string,
2910         $filename,
2911         $encoding = 'base64',
2912         $type = '',
2913         $disposition = 'attachment'
2914     ) {
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);
2918         }
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
2929         );
2930     }
2932     /**
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
2947      */
2948     public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2949     {
2950         if (!@is_file($path)) {
2951             $this->setError($this->lang('file_access') . $path);
2952             return false;
2953         }
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);
2958         }
2960         $filename = basename($path);
2961         if ($name == '') {
2962             $name = $filename;
2963         }
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
2975         );
2976         return true;
2977     }
2979     /**
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
2992      */
2993     public function addStringEmbeddedImage(
2994         $string,
2995         $cid,
2996         $name = '',
2997         $encoding = 'base64',
2998         $type = '',
2999         $disposition = 'inline'
3000     ) {
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);
3004         }
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
3016         );
3017         return true;
3018     }
3020     /**
3021      * Check if an inline attachment is present.
3022      * @access public
3023      * @return boolean
3024      */
3025     public function inlineImageExists()
3026     {
3027         foreach ($this->attachment as $attachment) {
3028             if ($attachment[6] == 'inline') {
3029                 return true;
3030             }
3031         }
3032         return false;
3033     }
3035     /**
3036      * Check if an attachment (non-inline) is present.
3037      * @return boolean
3038      */
3039     public function attachmentExists()
3040     {
3041         foreach ($this->attachment as $attachment) {
3042             if ($attachment[6] == 'attachment') {
3043                 return true;
3044             }
3045         }
3046         return false;
3047     }
3049     /**
3050      * Check if this message has an alternative body set.
3051      * @return boolean
3052      */
3053     public function alternativeExists()
3054     {
3055         return !empty($this->AltBody);
3056     }
3058     /**
3059      * Clear queued addresses of given kind.
3060      * @access protected
3061      * @param string $kind 'to', 'cc', or 'bcc'
3062      * @return void
3063      */
3064     public function clearQueuedAddresses($kind)
3065     {
3066         $RecipientsQueue = $this->RecipientsQueue;
3067         foreach ($RecipientsQueue as $address => $params) {
3068             if ($params[0] == $kind) {
3069                 unset($this->RecipientsQueue[$address]);
3070             }
3071         }
3072     }
3074     /**
3075      * Clear all To recipients.
3076      * @return void
3077      */
3078     public function clearAddresses()
3079     {
3080         foreach ($this->to as $to) {
3081             unset($this->all_recipients[strtolower($to[0])]);
3082         }
3083         $this->to = array();
3084         $this->clearQueuedAddresses('to');
3085     }
3087     /**
3088      * Clear all CC recipients.
3089      * @return void
3090      */
3091     public function clearCCs()
3092     {
3093         foreach ($this->cc as $cc) {
3094             unset($this->all_recipients[strtolower($cc[0])]);
3095         }
3096         $this->cc = array();
3097         $this->clearQueuedAddresses('cc');
3098     }
3100     /**
3101      * Clear all BCC recipients.
3102      * @return void
3103      */
3104     public function clearBCCs()
3105     {
3106         foreach ($this->bcc as $bcc) {
3107             unset($this->all_recipients[strtolower($bcc[0])]);
3108         }
3109         $this->bcc = array();
3110         $this->clearQueuedAddresses('bcc');
3111     }
3113     /**
3114      * Clear all ReplyTo recipients.
3115      * @return void
3116      */
3117     public function clearReplyTos()
3118     {
3119         $this->ReplyTo = array();
3120         $this->ReplyToQueue = array();
3121     }
3123     /**
3124      * Clear all recipient types.
3125      * @return void
3126      */
3127     public function clearAllRecipients()
3128     {
3129         $this->to = array();
3130         $this->cc = array();
3131         $this->bcc = array();
3132         $this->all_recipients = array();
3133         $this->RecipientsQueue = array();
3134     }
3136     /**
3137      * Clear all filesystem, string, and binary attachments.
3138      * @return void
3139      */
3140     public function clearAttachments()
3141     {
3142         $this->attachment = array();
3143     }
3145     /**
3146      * Clear all custom headers.
3147      * @return void
3148      */
3149     public function clearCustomHeaders()
3150     {
3151         $this->CustomHeader = array();
3152     }
3154     /**
3155      * Add an error message to the error container.
3156      * @access protected
3157      * @param string $msg
3158      * @return void
3159      */
3160     protected function setError($msg)
3161     {
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'];
3169                 }
3170                 if (!empty($lasterror['smtp_code'])) {
3171                     $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3172                 }
3173                 if (!empty($lasterror['smtp_code_ex'])) {
3174                     $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3175                 }
3176             }
3177         }
3178         $this->ErrorInfo = $msg;
3179     }
3181     /**
3182      * Return an RFC 822 formatted date.
3183      * @access public
3184      * @return string
3185      * @static
3186      */
3187     public static function rfcDate()
3188     {
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');
3193     }
3195     /**
3196      * Get the server hostname.
3197      * Returns 'localhost.localdomain' if unknown.
3198      * @access protected
3199      * @return string
3200      */
3201     protected function serverHostname()
3202     {
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');
3212         }
3213         return $result;
3214     }
3216     /**
3217      * Get an error message in the current language.
3218      * @access protected
3219      * @param string $key
3220      * @return string
3221      */
3222     protected function lang($key)
3223     {
3224         if (count($this->language) < 1) {
3225             $this->setLanguage('en'); // set the default language
3226         }
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';
3234             }
3235             return $this->language[$key];
3236         } else {
3237             //Return the key as a fallback
3238             return $key;
3239         }
3240     }
3242     /**
3243      * Check if an error occurred.
3244      * @access public
3245      * @return boolean True if an error did occur.
3246      */
3247     public function isError()
3248     {
3249         return ($this->error_count > 0);
3250     }
3252     /**
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
3258      */
3259     public function fixEOL($str)
3260     {
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);
3266         }
3267         return $nstr;
3268     }
3270     /**
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
3278      */
3279