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