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