source: spip-zone/_plugins_/facteur/trunk/phpmailer-php5/class.phpmailer.php @ 92830

Last change on this file since 92830 was 92830, checked in by rastapopoulos@…, 4 years ago

Mettons à jour PHPMailer, qui a eu 4 versions de plus, dont de grosses choses, au moins : ajout de la méthode Oauth pour Gmail (qui a l'air compliqué cela dit, et pas encore intégré dans la config de Facteur !) + de la sécurité importante.

  • Property svn:executable set to *
File size: 135.9 KB
Line 
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 */
28class 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     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
928     * of the form "display name <address>" into an array of name/address pairs.
929     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
930     * Note that quotes in the name part are removed.
931     * @param string $addrstr The address list string
932     * @param bool $useimap Whether to use the IMAP extension to parse the list
933     * @return array
934     * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
935     */
936    public function parseAddresses($addrstr, $useimap = true)
937    {
938        $addresses = array();
939        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
940            //Use this built-in parser if it's available
941            $list = imap_rfc822_parse_adrlist($addrstr, '');
942            foreach ($list as $address) {
943                if ($address->host != '.SYNTAX-ERROR.') {
944                    if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
945                        $addresses[] = array(
946                            'name' => (property_exists($address, 'personal') ? $address->personal : ''),
947                            'address' => $address->mailbox . '@' . $address->host
948                        );
949                    }
950                }
951            }
952        } else {
953            //Use this simpler parser
954            $list = explode(',', $addrstr);
955            foreach ($list as $address) {
956                $address = trim($address);
957                //Is there a separate name part?
958                if (strpos($address, '<') === false) {
959                    //No separate name, just use the whole thing
960                    if ($this->validateAddress($address)) {
961                        $addresses[] = array(
962                            'name' => '',
963                            'address' => $address
964                        );
965                    }
966                } else {
967                    list($name, $email) = explode('<', $address);
968                    $email = trim(str_replace('>', '', $email));
969                    if ($this->validateAddress($email)) {
970                        $addresses[] = array(
971                            'name' => trim(str_replace(array('"', "'"), '', $name)),
972                            'address' => $email
973                        );
974                    }
975                }
976            }
977        }
978        return $addresses;
979    }
980
981    /**
982     * Set the From and FromName properties.
983     * @param string $address
984     * @param string $name
985     * @param boolean $auto Whether to also set the Sender address, defaults to true
986     * @throws phpmailerException
987     * @return boolean
988     */
989    public function setFrom($address, $name = '', $auto = true)
990    {
991        $address = trim($address);
992        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
993        // Don't validate now addresses with IDN. Will be done in send().
994        if (($pos = strrpos($address, '@')) === false or
995            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
996            !$this->validateAddress($address)) {
997            $error_message = $this->lang('invalid_address') . $address;
998            $this->setError($error_message);
999            $this->edebug($error_message);
1000            if ($this->exceptions) {
1001                throw new phpmailerException($error_message);
1002            }
1003            return false;
1004        }
1005        $this->From = $address;
1006        $this->FromName = $name;
1007        if ($auto) {
1008            if (empty($this->Sender)) {
1009                $this->Sender = $address;
1010            }
1011        }
1012        return true;
1013    }
1014
1015    /**
1016     * Return the Message-ID header of the last email.
1017     * Technically this is the value from the last time the headers were created,
1018     * but it's also the message ID of the last sent message except in
1019     * pathological cases.
1020     * @return string
1021     */
1022    public function getLastMessageID()
1023    {
1024        return $this->lastMessageID;
1025    }
1026
1027    /**
1028     * Check that a string looks like an email address.
1029     * @param string $address The email address to check
1030     * @param string $patternselect A selector for the validation pattern to use :
1031     * * `auto` Pick best pattern automatically;
1032     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
1033     * * `pcre` Use old PCRE implementation;
1034     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1035     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1036     * * `noregex` Don't use a regex: super fast, really dumb.
1037     * @return boolean
1038     * @static
1039     * @access public
1040     */
1041    public static function validateAddress($address, $patternselect = 'auto')
1042    {
1043        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1044        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1045            return false;
1046        }
1047        if (!$patternselect or $patternselect == 'auto') {
1048            //Check this constant first so it works when extension_loaded() is disabled by safe mode
1049            //Constant was added in PHP 5.2.4
1050            if (defined('PCRE_VERSION')) {
1051                //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
1052                if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
1053                    $patternselect = 'pcre8';
1054                } else {
1055                    $patternselect = 'pcre';
1056                }
1057            } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
1058                //Fall back to older PCRE
1059                $patternselect = 'pcre';
1060            } else {
1061                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1062                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1063                    $patternselect = 'php';
1064                } else {
1065                    $patternselect = 'noregex';
1066                }
1067            }
1068        }
1069        switch ($patternselect) {
1070            case 'pcre8':
1071                /**
1072                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1073                 * @link http://squiloople.com/2009/12/20/email-address-validation/
1074                 * @copyright 2009-2010 Michael Rushton
1075                 * Feel free to use and redistribute this code. But please keep this copyright notice.
1076                 */
1077                return (boolean)preg_match(
1078                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1079                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1080                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1081                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1082                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1083                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1084                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1085                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1086                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1087                    $address
1088                );
1089            case 'pcre':
1090                //An older regex that doesn't need a recent PCRE
1091                return (boolean)preg_match(
1092                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1093                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1094                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1095                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1096                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1097                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1098                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1099                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1100                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1101                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1102                    $address
1103                );
1104            case 'html5':
1105                /**
1106                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1107                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1108                 */
1109                return (boolean)preg_match(
1110                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1111                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1112                    $address
1113                );
1114            case 'noregex':
1115                //No PCRE! Do something _very_ approximate!
1116                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1117                return (strlen($address) >= 3
1118                    and strpos($address, '@') >= 1
1119                    and strpos($address, '@') != strlen($address) - 1);
1120            case 'php':
1121            default:
1122                return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
1123        }
1124    }
1125
1126    /**
1127     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1128     * "intl" and "mbstring" PHP extensions.
1129     * @return bool "true" if required functions for IDN support are present
1130     */
1131    public function idnSupported()
1132    {
1133        // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1134        return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1135    }
1136
1137    /**
1138     * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1139     * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1140     * This function silently returns unmodified address if:
1141     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1142     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1143     *   or fails for any reason (e.g. domain has characters not allowed in an IDN)
1144     * @see PHPMailer::$CharSet
1145     * @param string $address The email address to convert
1146     * @return string The encoded address in ASCII form
1147     */
1148    public function punyencodeAddress($address)
1149    {
1150        // Verify we have required functions, CharSet, and at-sign.
1151        if ($this->idnSupported() and
1152            !empty($this->CharSet) and
1153            ($pos = strrpos($address, '@')) !== false) {
1154            $domain = substr($address, ++$pos);
1155            // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1156            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1157                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1158                if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1159                    idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
1160                    idn_to_ascii($domain)) !== false) {
1161                    return substr($address, 0, $pos) . $punycode;
1162                }
1163            }
1164        }
1165        return $address;
1166    }
1167
1168    /**
1169     * Create a message and send it.
1170     * Uses the sending method specified by $Mailer.
1171     * @throws phpmailerException
1172     * @return boolean false on error - See the ErrorInfo property for details of the error.
1173     */
1174    public function send()
1175    {
1176        try {
1177            if (!$this->preSend()) {
1178                return false;
1179            }
1180            return $this->postSend();
1181        } catch (phpmailerException $exc) {
1182            $this->mailHeader = '';
1183            $this->setError($exc->getMessage());
1184            if ($this->exceptions) {
1185                throw $exc;
1186            }
1187            return false;
1188        }
1189    }
1190
1191    /**
1192     * Prepare a message for sending.
1193     * @throws phpmailerException
1194     * @return boolean
1195     */
1196    public function preSend()
1197    {
1198        try {
1199            $this->error_count = 0; // Reset errors
1200            $this->mailHeader = '';
1201
1202            // Dequeue recipient and Reply-To addresses with IDN
1203            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1204                $params[1] = $this->punyencodeAddress($params[1]);
1205                call_user_func_array(array($this, 'addAnAddress'), $params);
1206            }
1207            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1208                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1209            }
1210
1211            // Validate From, Sender, and ConfirmReadingTo addresses
1212            foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
1213                $this->$address_kind = trim($this->$address_kind);
1214                if (empty($this->$address_kind)) {
1215                    continue;
1216                }
1217                $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1218                if (!$this->validateAddress($this->$address_kind)) {
1219                    $error_message = $this->lang('invalid_address') . $this->$address_kind;
1220                    $this->setError($error_message);
1221                    $this->edebug($error_message);
1222                    if ($this->exceptions) {
1223                        throw new phpmailerException($error_message);
1224                    }
1225                    return false;
1226                }
1227            }
1228
1229            // Set whether the message is multipart/alternative
1230            if (!empty($this->AltBody)) {
1231                $this->ContentType = 'multipart/alternative';
1232            }
1233
1234            $this->setMessageType();
1235            // Refuse to send an empty message unless we are specifically allowing it
1236            if (!$this->AllowEmpty and empty($this->Body)) {
1237                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1238            }
1239
1240            // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1241            $this->MIMEHeader = '';
1242            $this->MIMEBody = $this->createBody();
1243            // createBody may have added some headers, so retain them
1244            $tempheaders = $this->MIMEHeader;
1245            $this->MIMEHeader = $this->createHeader();
1246            $this->MIMEHeader .= $tempheaders;
1247
1248            // To capture the complete message when using mail(), create
1249            // an extra header list which createHeader() doesn't fold in
1250            if ($this->Mailer == 'mail') {
1251                if (count($this->to) > 0) {
1252                    $this->mailHeader .= $this->addrAppend('To', $this->to);
1253                } else {
1254                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1255                }
1256                $this->mailHeader .= $this->headerLine(
1257                    'Subject',
1258                    $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1259                );
1260            }
1261
1262            // Sign with DKIM if enabled
1263            if (!empty($this->DKIM_domain)
1264                && !empty($this->DKIM_private)
1265                && !empty($this->DKIM_selector)
1266                && file_exists($this->DKIM_private)) {
1267                $header_dkim = $this->DKIM_Add(
1268                    $this->MIMEHeader . $this->mailHeader,
1269                    $this->encodeHeader($this->secureHeader($this->Subject)),
1270                    $this->MIMEBody
1271                );
1272                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1273                    str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1274            }
1275            return true;
1276        } catch (phpmailerException $exc) {
1277            $this->setError($exc->getMessage());
1278            if ($this->exceptions) {
1279                throw $exc;
1280            }
1281            return false;
1282        }
1283    }
1284
1285    /**
1286     * Actually send a message.
1287     * Send the email via the selected mechanism
1288     * @throws phpmailerException
1289     * @return boolean
1290     */
1291    public function postSend()
1292    {
1293        try {
1294            // Choose the mailer and send through it
1295            switch ($this->Mailer) {
1296                case 'sendmail':
1297                case 'qmail':
1298                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1299                case 'smtp':
1300                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1301                case 'mail':
1302                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1303                default:
1304                    $sendMethod = $this->Mailer.'Send';
1305                    if (method_exists($this, $sendMethod)) {
1306                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1307                    }
1308
1309                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1310            }
1311        } catch (phpmailerException $exc) {
1312            $this->setError($exc->getMessage());
1313            $this->edebug($exc->getMessage());
1314            if ($this->exceptions) {
1315                throw $exc;
1316            }
1317        }
1318        return false;
1319    }
1320
1321    /**
1322     * Send mail using the $Sendmail program.
1323     * @param string $header The message headers
1324     * @param string $body The message body
1325     * @see PHPMailer::$Sendmail
1326     * @throws phpmailerException
1327     * @access protected
1328     * @return boolean
1329     */
1330    protected function sendmailSend($header, $body)
1331    {
1332        if ($this->Sender != '') {
1333            if ($this->Mailer == 'qmail') {
1334                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1335            } else {
1336                $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1337            }
1338        } else {
1339            if ($this->Mailer == 'qmail') {
1340                $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1341            } else {
1342                $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1343            }
1344        }
1345        if ($this->SingleTo) {
1346            foreach ($this->SingleToArray as $toAddr) {
1347                if (!@$mail = popen($sendmail, 'w')) {
1348                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1349                }
1350                fputs($mail, 'To: ' . $toAddr . "\n");
1351                fputs($mail, $header);
1352                fputs($mail, $body);
1353                $result = pclose($mail);
1354                $this->doCallback(
1355                    ($result == 0),
1356                    array($toAddr),
1357                    $this->cc,
1358                    $this->bcc,
1359                    $this->Subject,
1360                    $body,
1361                    $this->From
1362                );
1363                if ($result != 0) {
1364                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1365                }
1366            }
1367        } else {
1368            if (!@$mail = popen($sendmail, 'w')) {
1369                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1370            }
1371            fputs($mail, $header);
1372            fputs($mail, $body);
1373            $result = pclose($mail);
1374            $this->doCallback(
1375                ($result == 0),
1376                $this->to,
1377                $this->cc,
1378                $this->bcc,
1379                $this->Subject,
1380                $body,
1381                $this->From
1382            );
1383            if ($result != 0) {
1384                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1385            }
1386        }
1387        return true;
1388    }
1389
1390    /**
1391     * Send mail using the PHP mail() function.
1392     * @param string $header The message headers
1393     * @param string $body The message body
1394     * @link http://www.php.net/manual/en/book.mail.php
1395     * @throws phpmailerException
1396     * @access protected
1397     * @return boolean
1398     */
1399    protected function mailSend($header, $body)
1400    {
1401        $toArr = array();
1402        foreach ($this->to as $toaddr) {
1403            $toArr[] = $this->addrFormat($toaddr);
1404        }
1405        $to = implode(', ', $toArr);
1406
1407        if (empty($this->Sender)) {
1408            $params = ' ';
1409        } else {
1410            $params = sprintf('-f%s', $this->Sender);
1411        }
1412        if ($this->Sender != '' and !ini_get('safe_mode')) {
1413            $old_from = ini_get('sendmail_from');
1414            ini_set('sendmail_from', $this->Sender);
1415        }
1416        $result = false;
1417        if ($this->SingleTo && count($toArr) > 1) {
1418            foreach ($toArr as $toAddr) {
1419                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1420                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1421            }
1422        } else {
1423            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1424            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1425        }
1426        if (isset($old_from)) {
1427            ini_set('sendmail_from', $old_from);
1428        }
1429        if (!$result) {
1430            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1431        }
1432        return true;
1433    }
1434
1435    /**
1436     * Get an instance to use for SMTP operations.
1437     * Override this function to load your own SMTP implementation
1438     * @return SMTP
1439     */
1440    public function getSMTPInstance()
1441    {
1442        if (!is_object($this->smtp)) {
1443            $this->smtp = new SMTP;
1444        }
1445        return $this->smtp;
1446    }
1447
1448    /**
1449     * Send mail via SMTP.
1450     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1451     * Uses the PHPMailerSMTP class by default.
1452     * @see PHPMailer::getSMTPInstance() to use a different class.
1453     * @param string $header The message headers
1454     * @param string $body The message body
1455     * @throws phpmailerException
1456     * @uses SMTP
1457     * @access protected
1458     * @return boolean
1459     */
1460    protected function smtpSend($header, $body)
1461    {
1462        $bad_rcpt = array();
1463        if (!$this->smtpConnect($this->SMTPOptions)) {
1464            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1465        }
1466        if ('' == $this->Sender) {
1467            $smtp_from = $this->From;
1468        } else {
1469            $smtp_from = $this->Sender;
1470        }
1471        if (!$this->smtp->mail($smtp_from)) {
1472            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1473            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1474        }
1475
1476        // Attempt to send to all recipients
1477        foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1478            foreach ($togroup as $to) {
1479                if (!$this->smtp->recipient($to[0])) {
1480                    $error = $this->smtp->getError();
1481                    $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1482                    $isSent = false;
1483                } else {
1484                    $isSent = true;
1485                }
1486                $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1487            }
1488        }
1489
1490        // Only send the DATA command if we have viable recipients
1491        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1492            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1493        }
1494        if ($this->SMTPKeepAlive) {
1495            $this->smtp->reset();
1496        } else {
1497            $this->smtp->quit();
1498            $this->smtp->close();
1499        }
1500        //Create error message for any bad addresses
1501        if (count($bad_rcpt) > 0) {
1502            $errstr = '';
1503            foreach ($bad_rcpt as $bad) {
1504                $errstr .= $bad['to'] . ': ' . $bad['error'];
1505            }
1506            throw new phpmailerException(
1507                $this->lang('recipients_failed') . $errstr,
1508                self::STOP_CONTINUE
1509            );
1510        }
1511        return true;
1512    }
1513
1514    /**
1515     * Initiate a connection to an SMTP server.
1516     * Returns false if the operation failed.
1517     * @param array $options An array of options compatible with stream_context_create()
1518     * @uses SMTP
1519     * @access public
1520     * @throws phpmailerException
1521     * @return boolean
1522     */
1523    public function smtpConnect($options = array())
1524    {
1525        if (is_null($this->smtp)) {
1526            $this->smtp = $this->getSMTPInstance();
1527        }
1528
1529        // Already connected?
1530        if ($this->smtp->connected()) {
1531            return true;
1532        }
1533
1534        $this->smtp->setTimeout($this->Timeout);
1535        $this->smtp->setDebugLevel($this->SMTPDebug);
1536        $this->smtp->setDebugOutput($this->Debugoutput);
1537        $this->smtp->setVerp($this->do_verp);
1538        $hosts = explode(';', $this->Host);
1539        $lastexception = null;
1540
1541        foreach ($hosts as $hostentry) {
1542            $hostinfo = array();
1543            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1544                // Not a valid host entry
1545                continue;
1546            }
1547            // $hostinfo[2]: optional ssl or tls prefix
1548            // $hostinfo[3]: the hostname
1549            // $hostinfo[4]: optional port number
1550            // The host string prefix can temporarily override the current setting for SMTPSecure
1551            // If it's not specified, the default value is used
1552            $prefix = '';
1553            $secure = $this->SMTPSecure;
1554            $tls = ($this->SMTPSecure == 'tls');
1555            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1556                $prefix = 'ssl://';
1557                $tls = false; // Can't have SSL and TLS at the same time
1558                $secure = 'ssl';
1559            } elseif ($hostinfo[2] == 'tls') {
1560                $tls = true;
1561                // tls doesn't use a prefix
1562                $secure = 'tls';
1563            }
1564            //Do we need the OpenSSL extension?
1565            $sslext = defined('OPENSSL_ALGO_SHA1');
1566            if ('tls' === $secure or 'ssl' === $secure) {
1567                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1568                if (!$sslext) {
1569                    throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1570                }
1571            }
1572            $host = $hostinfo[3];
1573            $port = $this->Port;
1574            $tport = (integer)$hostinfo[4];
1575            if ($tport > 0 and $tport < 65536) {
1576                $port = $tport;
1577            }
1578            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1579                try {
1580                    if ($this->Helo) {
1581                        $hello = $this->Helo;
1582                    } else {
1583                        $hello = $this->serverHostname();
1584                    }
1585                    $this->smtp->hello($hello);
1586                    //Automatically enable TLS encryption if:
1587                    // * it's not disabled
1588                    // * we have openssl extension
1589                    // * we are not already using SSL
1590                    // * the server offers STARTTLS
1591                    if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1592                        $tls = true;
1593                    }
1594                    if ($tls) {
1595                        if (!$this->smtp->startTLS()) {
1596                            throw new phpmailerException($this->lang('connect_host'));
1597                        }
1598                        // We must resend HELO after tls negotiation
1599                        $this->smtp->hello($hello);
1600                    }
1601                    if ($this->SMTPAuth) {
1602                        if (!$this->smtp->authenticate(
1603                            $this->Username,
1604                            $this->Password,
1605                            $this->AuthType,
1606                            $this->Realm,
1607                            $this->Workstation
1608                        )
1609                        ) {
1610                            throw new phpmailerException($this->lang('authenticate'));
1611                        }
1612                    }
1613                    return true;
1614                } catch (phpmailerException $exc) {
1615                    $lastexception = $exc;
1616                    $this->edebug($exc->getMessage());
1617                    // We must have connected, but then failed TLS or Auth, so close connection nicely
1618                    $this->smtp->quit();
1619                }
1620            }
1621        }
1622        // If we get here, all connection attempts have failed, so close connection hard
1623        $this->smtp->close();
1624        // As we've caught all exceptions, just report whatever the last one was
1625        if ($this->exceptions and !is_null($lastexception)) {
1626            throw $lastexception;
1627        }
1628        return false;
1629    }
1630
1631    /**
1632     * Close the active SMTP session if one exists.
1633     * @return void
1634     */
1635    public function smtpClose()
1636    {
1637        if ($this->smtp !== null) {
1638            if ($this->smtp->connected()) {
1639                $this->smtp->quit();
1640                $this->smtp->close();
1641            }
1642        }
1643    }
1644
1645    /**
1646     * Set the language for error messages.
1647     * Returns false if it cannot load the language file.
1648     * The default language is English.
1649     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1650     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1651     * @return boolean
1652     * @access public
1653     */
1654    public function setLanguage($langcode = 'en', $lang_path = '')
1655    {
1656        // Define full set of translatable strings in English
1657        $PHPMAILER_LANG = array(
1658            'authenticate' => 'SMTP Error: Could not authenticate.',
1659            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1660            'data_not_accepted' => 'SMTP Error: data not accepted.',
1661            'empty_message' => 'Message body empty',
1662            'encoding' => 'Unknown encoding: ',
1663            'execute' => 'Could not execute: ',
1664            'file_access' => 'Could not access file: ',
1665            'file_open' => 'File Error: Could not open file: ',
1666            'from_failed' => 'The following From address failed: ',
1667            'instantiate' => 'Could not instantiate mail function.',
1668            'invalid_address' => 'Invalid address: ',
1669            'mailer_not_supported' => ' mailer is not supported.',
1670            'provide_address' => 'You must provide at least one recipient email address.',
1671            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1672            'signing' => 'Signing Error: ',
1673            'smtp_connect_failed' => 'SMTP connect() failed.',
1674            'smtp_error' => 'SMTP server error: ',
1675            'variable_set' => 'Cannot set or reset variable: ',
1676            'extension_missing' => 'Extension missing: '
1677        );
1678        if (empty($lang_path)) {
1679            // Calculate an absolute path so it can work if CWD is not here
1680            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1681        }
1682        $foundlang = true;
1683        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1684        // There is no English translation file
1685        if ($langcode != 'en') {
1686            // Make sure language file path is readable
1687            if (!is_readable($lang_file)) {
1688                $foundlang = false;
1689            } else {
1690                // Overwrite language-specific strings.
1691                // This way we'll never have missing translation keys.
1692                $foundlang = include $lang_file;
1693            }
1694        }
1695        $this->language = $PHPMAILER_LANG;
1696        return (boolean)$foundlang; // Returns false if language not found
1697    }
1698
1699    /**
1700     * Get the array of strings for the current language.
1701     * @return array
1702     */
1703    public function getTranslations()
1704    {
1705        return $this->language;
1706    }
1707
1708    /**
1709     * Create recipient headers.
1710     * @access public
1711     * @param string $type
1712     * @param array $addr An array of recipient,
1713     * where each recipient is a 2-element indexed array with element 0 containing an address
1714     * and element 1 containing a name, like:
1715     * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
1716     * @return string
1717     */
1718    public function addrAppend($type, $addr)
1719    {
1720        $addresses = array();
1721        foreach ($addr as $address) {
1722            $addresses[] = $this->addrFormat($address);
1723        }
1724        return $type . ': ' . implode(', ', $addresses) . $this->LE;
1725    }
1726
1727    /**
1728     * Format an address for use in a message header.
1729     * @access public
1730     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1731     *      like array('joe@example.com', 'Joe User')
1732     * @return string
1733     */
1734    public function addrFormat($addr)
1735    {
1736        if (empty($addr[1])) { // No name provided
1737            return $this->secureHeader($addr[0]);
1738        } else {
1739            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1740                $addr[0]
1741            ) . '>';
1742        }
1743    }
1744
1745    /**
1746     * Word-wrap message.
1747     * For use with mailers that do not automatically perform wrapping
1748     * and for quoted-printable encoded messages.
1749     * Original written by philippe.
1750     * @param string $message The message to wrap
1751     * @param integer $length The line length to wrap to
1752     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1753     * @access public
1754     * @return string
1755     */
1756    public function wrapText($message, $length, $qp_mode = false)
1757    {
1758        if ($qp_mode) {
1759            $soft_break = sprintf(' =%s', $this->LE);
1760        } else {
1761            $soft_break = $this->LE;
1762        }
1763        // If utf-8 encoding is used, we will need to make sure we don't
1764        // split multibyte characters when we wrap
1765        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1766        $lelen = strlen($this->LE);
1767        $crlflen = strlen(self::CRLF);
1768
1769        $message = $this->fixEOL($message);
1770        //Remove a trailing line break
1771        if (substr($message, -$lelen) == $this->LE) {
1772            $message = substr($message, 0, -$lelen);
1773        }
1774
1775        //Split message into lines
1776        $lines = explode($this->LE, $message);
1777        //Message will be rebuilt in here
1778        $message = '';
1779        foreach ($lines as $line) {
1780            $words = explode(' ', $line);
1781            $buf = '';
1782            $firstword = true;
1783            foreach ($words as $word) {
1784                if ($qp_mode and (strlen($word) > $length)) {
1785                    $space_left = $length - strlen($buf) - $crlflen;
1786                    if (!$firstword) {
1787                        if ($space_left > 20) {
1788                            $len = $space_left;
1789                            if ($is_utf8) {
1790                                $len = $this->utf8CharBoundary($word, $len);
1791                            } elseif (substr($word, $len - 1, 1) == '=') {
1792                                $len--;
1793                            } elseif (substr($word, $len - 2, 1) == '=') {
1794                                $len -= 2;
1795                            }
1796                            $part = substr($word, 0, $len);
1797                            $word = substr($word, $len);
1798                            $buf .= ' ' . $part;
1799                            $message .= $buf . sprintf('=%s', self::CRLF);
1800                        } else {
1801                            $message .= $buf . $soft_break;
1802                        }
1803                        $buf = '';
1804                    }
1805                    while (strlen($word) > 0) {
1806                        if ($length <= 0) {
1807                            break;
1808                        }
1809                        $len = $length;
1810                        if ($is_utf8) {
1811                            $len = $this->utf8CharBoundary($word, $len);
1812                        } elseif (substr($word, $len - 1, 1) == '=') {
1813                            $len--;
1814                        } elseif (substr($word, $len - 2, 1) == '=') {
1815                            $len -= 2;
1816                        }
1817                        $part = substr($word, 0, $len);
1818                        $word = substr($word, $len);
1819
1820                        if (strlen($word) > 0) {
1821                            $message .= $part . sprintf('=%s', self::CRLF);
1822                        } else {
1823                            $buf = $part;
1824                        }
1825                    }
1826                } else {
1827                    $buf_o = $buf;
1828                    if (!$firstword) {
1829                        $buf .= ' ';
1830                    }
1831                    $buf .= $word;
1832
1833                    if (strlen($buf) > $length and $buf_o != '') {
1834                        $message .= $buf_o . $soft_break;
1835                        $buf = $word;
1836                    }
1837                }
1838                $firstword = false;
1839            }
1840            $message .= $buf . self::CRLF;
1841        }
1842
1843        return $message;
1844    }
1845
1846    /**
1847     * Find the last character boundary prior to $maxLength in a utf-8
1848     * quoted-printable encoded string.
1849     * Original written by Colin Brown.
1850     * @access public
1851     * @param string $encodedText utf-8 QP text
1852     * @param integer $maxLength Find the last character boundary prior to this length
1853     * @return integer
1854     */
1855    public function utf8CharBoundary($encodedText, $maxLength)
1856    {
1857        $foundSplitPos = false;
1858        $lookBack = 3;
1859        while (!$foundSplitPos) {
1860            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1861            $encodedCharPos = strpos($lastChunk, '=');
1862            if (false !== $encodedCharPos) {
1863                // Found start of encoded character byte within $lookBack block.
1864                // Check the encoded byte value (the 2 chars after the '=')
1865                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1866                $dec = hexdec($hex);
1867                if ($dec < 128) {
1868                    // Single byte character.
1869                    // If the encoded char was found at pos 0, it will fit
1870                    // otherwise reduce maxLength to start of the encoded char
1871                    if ($encodedCharPos > 0) {
1872                        $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1873                    }
1874                    $foundSplitPos = true;
1875                } elseif ($dec >= 192) {
1876                    // First byte of a multi byte character
1877                    // Reduce maxLength to split at start of character
1878                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1879                    $foundSplitPos = true;
1880                } elseif ($dec < 192) {
1881                    // Middle byte of a multi byte character, look further back
1882                    $lookBack += 3;
1883                }
1884            } else {
1885                // No encoded character found
1886                $foundSplitPos = true;
1887            }
1888        }
1889        return $maxLength;
1890    }
1891
1892    /**
1893     * Apply word wrapping to the message body.
1894     * Wraps the message body to the number of chars set in the WordWrap property.
1895     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
1896     * This is called automatically by createBody(), so you don't need to call it yourself.
1897     * @access public
1898     * @return void
1899     */
1900    public function setWordWrap()
1901    {
1902        if ($this->WordWrap < 1) {
1903            return;
1904        }
1905
1906        switch ($this->message_type) {
1907            case 'alt':
1908            case 'alt_inline':
1909            case 'alt_attach':
1910            case 'alt_inline_attach':
1911                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1912                break;
1913            default:
1914                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1915                break;
1916        }
1917    }
1918
1919    /**
1920     * Assemble message headers.
1921     * @access public
1922     * @return string The assembled headers
1923     */
1924    public function createHeader()
1925    {
1926        $result = '';
1927
1928        if ($this->MessageDate == '') {
1929            $this->MessageDate = self::rfcDate();
1930        }
1931        $result .= $this->headerLine('Date', $this->MessageDate);
1932
1933        // To be created automatically by mail()
1934        if ($this->SingleTo) {
1935            if ($this->Mailer != 'mail') {
1936                foreach ($this->to as $toaddr) {
1937                    $this->SingleToArray[] = $this->addrFormat($toaddr);
1938                }
1939            }
1940        } else {
1941            if (count($this->to) > 0) {
1942                if ($this->Mailer != 'mail') {
1943                    $result .= $this->addrAppend('To', $this->to);
1944                }
1945            } elseif (count($this->cc) == 0) {
1946                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1947            }
1948        }
1949
1950        $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1951
1952        // sendmail and mail() extract Cc from the header before sending
1953        if (count($this->cc) > 0) {
1954            $result .= $this->addrAppend('Cc', $this->cc);
1955        }
1956
1957        // sendmail and mail() extract Bcc from the header before sending
1958        if ((
1959                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1960            )
1961            and count($this->bcc) > 0
1962        ) {
1963            $result .= $this->addrAppend('Bcc', $this->bcc);
1964        }
1965
1966        if (count($this->ReplyTo) > 0) {
1967            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1968        }
1969
1970        // mail() sets the subject itself
1971        if ($this->Mailer != 'mail') {
1972            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1973        }
1974
1975        if ($this->MessageID != '') {
1976            $this->lastMessageID = $this->MessageID;
1977        } else {
1978            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
1979        }
1980        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
1981        if (!is_null($this->Priority)) {
1982            $result .= $this->headerLine('X-Priority', $this->Priority);
1983        }
1984        if ($this->XMailer == '') {
1985            $result .= $this->headerLine(
1986                'X-Mailer',
1987                'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
1988            );
1989        } else {
1990            $myXmailer = trim($this->XMailer);
1991            if ($myXmailer) {
1992                $result .= $this->headerLine('X-Mailer', $myXmailer);
1993            }
1994        }
1995
1996        if ($this->ConfirmReadingTo != '') {
1997            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
1998        }
1999
2000        // Add custom headers
2001        foreach ($this->CustomHeader as $header) {
2002            $result .= $this->headerLine(
2003                trim($header[0]),
2004                $this->encodeHeader(trim($header[1]))
2005            );
2006        }
2007        if (!$this->sign_key_file) {
2008            $result .= $this->headerLine('MIME-Version', '1.0');
2009            $result .= $this->getMailMIME();
2010        }
2011
2012        return $result;
2013    }
2014
2015    /**
2016     * Get the message MIME type headers.
2017     * @access public
2018     * @return string
2019     */
2020    public function getMailMIME()
2021    {
2022        $result = '';
2023        $ismultipart = true;
2024        switch ($this->message_type) {
2025            case 'inline':
2026                $result .= $this->headerLine('Content-Type', 'multipart/related;');
2027                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2028                break;
2029            case 'attach':
2030            case 'inline_attach':
2031            case 'alt_attach':
2032            case 'alt_inline_attach':
2033                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2034                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2035                break;
2036            case 'alt':
2037            case 'alt_inline':
2038                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2039                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2040                break;
2041            default:
2042                // Catches case 'plain': and case '':
2043                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2044                $ismultipart = false;
2045                break;
2046        }
2047        // RFC1341 part 5 says 7bit is assumed if not specified
2048        if ($this->Encoding != '7bit') {
2049            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2050            if ($ismultipart) {
2051                if ($this->Encoding == '8bit') {
2052                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2053                }
2054                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2055            } else {
2056                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2057            }
2058        }
2059
2060        if ($this->Mailer != 'mail') {
2061            $result .= $this->LE;
2062        }
2063
2064        return $result;
2065    }
2066
2067    /**
2068     * Returns the whole MIME message.
2069     * Includes complete headers and body.
2070     * Only valid post preSend().
2071     * @see PHPMailer::preSend()
2072     * @access public
2073     * @return string
2074     */
2075    public function getSentMIMEMessage()
2076    {
2077        return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
2078    }
2079
2080    /**
2081     * Assemble the message body.
2082     * Returns an empty string on failure.
2083     * @access public
2084     * @throws phpmailerException
2085     * @return string The assembled message body
2086     */
2087    public function createBody()
2088    {
2089        $body = '';
2090        //Create unique IDs and preset boundaries
2091        $this->uniqueid = md5(uniqid(time()));
2092        $this->boundary[1] = 'b1_' . $this->uniqueid;
2093        $this->boundary[2] = 'b2_' . $this->uniqueid;
2094        $this->boundary[3] = 'b3_' . $this->uniqueid;
2095
2096        if ($this->sign_key_file) {
2097            $body .= $this->getMailMIME() . $this->LE;
2098        }
2099
2100        $this->setWordWrap();
2101
2102        $bodyEncoding = $this->Encoding;
2103        $bodyCharSet = $this->CharSet;
2104        //Can we do a 7-bit downgrade?
2105        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2106            $bodyEncoding = '7bit';
2107            $bodyCharSet = 'us-ascii';
2108        }
2109        //If lines are too long, and we're not already using an encoding that will shorten them,
2110        //change to quoted-printable transfer encoding
2111        if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
2112            $this->Encoding = 'quoted-printable';
2113            $bodyEncoding = 'quoted-printable';
2114        }
2115
2116        $altBodyEncoding = $this->Encoding;
2117        $altBodyCharSet = $this->CharSet;
2118        //Can we do a 7-bit downgrade?
2119        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2120            $altBodyEncoding = '7bit';
2121            $altBodyCharSet = 'us-ascii';
2122        }
2123        //If lines are too long, change to quoted-printable transfer encoding
2124        if (self::hasLineLongerThanMax($this->AltBody)) {
2125            $altBodyEncoding = 'quoted-printable';
2126        }
2127        //Use this as a preamble in all multipart message types
2128        $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
2129        switch ($this->message_type) {
2130            case 'inline':
2131                $body .= $mimepre;
2132                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2133                $body .= $this->encodeString($this->Body, $bodyEncoding);
2134                $body .= $this->LE . $this->LE;
2135                $body .= $this->attachAll('inline', $this->boundary[1]);
2136                break;
2137            case 'attach':
2138                $body .= $mimepre;
2139                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2140                $body .= $this->encodeString($this->Body, $bodyEncoding);
2141                $body .= $this->LE . $this->LE;
2142                $body .= $this->attachAll('attachment', $this->boundary[1]);
2143                break;
2144            case 'inline_attach':
2145                $body .= $mimepre;
2146                $body .= $this->textLine('--' . $this->boundary[1]);
2147                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2148                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2149                $body .= $this->LE;
2150                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2151                $body .= $this->encodeString($this->Body, $bodyEncoding);
2152                $body .= $this->LE . $this->LE;
2153                $body .= $this->attachAll('inline', $this->boundary[2]);
2154                $body .= $this->LE;
2155                $body .= $this->attachAll('attachment', $this->boundary[1]);
2156                break;
2157            case 'alt':
2158                $body .= $mimepre;
2159                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2160                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2161                $body .= $this->LE . $this->LE;
2162                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2163                $body .= $this->encodeString($this->Body, $bodyEncoding);
2164                $body .= $this->LE . $this->LE;
2165                if (!empty($this->Ical)) {
2166                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2167                    $body .= $this->encodeString($this->Ical, $this->Encoding);
2168                    $body .= $this->LE . $this->LE;
2169                }
2170                $body .= $this->endBoundary($this->boundary[1]);
2171                break;
2172            case 'alt_inline':
2173                $body .= $mimepre;
2174                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2175                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2176                $body .= $this->LE . $this->LE;
2177                $body .= $this->textLine('--' . $this->boundary[1]);
2178                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2179                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2180                $body .= $this->LE;
2181                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2182                $body .= $this->encodeString($this->Body, $bodyEncoding);
2183                $body .= $this->LE . $this->LE;
2184                $body .= $this->attachAll('inline', $this->boundary[2]);
2185                $body .= $this->LE;
2186                $body .= $this->endBoundary($this->boundary[1]);
2187                break;
2188            case 'alt_attach':
2189                $body .= $mimepre;
2190                $body .= $this->textLine('--' . $this->boundary[1]);
2191                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2192                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2193                $body .= $this->LE;
2194                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2195                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2196                $body .= $this->LE . $this->LE;
2197                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2198                $body .= $this->encodeString($this->Body, $bodyEncoding);
2199                $body .= $this->LE . $this->LE;
2200                $body .= $this->endBoundary($this->boundary[2]);
2201                $body .= $this->LE;
2202                $body .= $this->attachAll('attachment', $this->boundary[1]);
2203                break;
2204            case 'alt_inline_attach':
2205                $body .= $mimepre;
2206                $body .= $this->textLine('--' . $this->boundary[1]);
2207                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2208                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2209                $body .= $this->LE;
2210                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2211                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2212                $body .= $this->LE . $this->LE;
2213                $body .= $this->textLine('--' . $this->boundary[2]);
2214                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2215                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2216                $body .= $this->LE;
2217                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2218                $body .= $this->encodeString($this->Body, $bodyEncoding);
2219                $body .= $this->LE . $this->LE;
2220                $body .= $this->attachAll('inline', $this->boundary[3]);
2221                $body .= $this->LE;
2222                $body .= $this->endBoundary($this->boundary[2]);
2223                $body .= $this->LE;
2224                $body .= $this->attachAll('attachment', $this->boundary[1]);
2225                break;
2226            default:
2227                // catch case 'plain' and case ''
2228                $body .= $this->encodeString($this->Body, $bodyEncoding);
2229                break;
2230        }
2231
2232        if ($this->isError()) {
2233            $body = '';
2234        } elseif ($this->sign_key_file) {
2235            try {
2236                if (!defined('PKCS7_TEXT')) {
2237                    throw new phpmailerException($this->lang('extension_missing') . 'openssl');
2238                }
2239                // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2240                $file = tempnam(sys_get_temp_dir(), 'mail');
2241                if (false === file_put_contents($file, $body)) {
2242                    throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2243                }
2244                $signed = tempnam(sys_get_temp_dir(), 'signed');
2245                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2246                if (empty($this->sign_extracerts_file)) {
2247                    $sign = @openssl_pkcs7_sign(
2248                        $file,
2249                        $signed,
2250                        'file://' . realpath($this->sign_cert_file),
2251                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2252                        null
2253                    );
2254                } else {
2255                    $sign = @openssl_pkcs7_sign(
2256                        $file,
2257                        $signed,
2258                        'file://' . realpath($this->sign_cert_file),
2259                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2260                        null,
2261                        PKCS7_DETACHED,
2262                        $this->sign_extracerts_file
2263                    );
2264                }
2265                if ($sign) {
2266                    @unlink($file);
2267                    $body = file_get_contents($signed);
2268                    @unlink($signed);
2269                    //The message returned by openssl contains both headers and body, so need to split them up
2270                    $parts = explode("\n\n", $body, 2);
2271                    $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2272                    $body = $parts[1];
2273                } else {
2274                    @unlink($file);
2275                    @unlink($signed);
2276                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
2277                }
2278            } catch (phpmailerException $exc) {
2279                $body = '';
2280                if ($this->exceptions) {
2281                    throw $exc;
2282                }
2283            }
2284        }
2285        return $body;
2286    }
2287
2288    /**
2289     * Return the start of a message boundary.
2290     * @access protected
2291     * @param string $boundary
2292     * @param string $charSet
2293     * @param string $contentType
2294     * @param string $encoding
2295     * @return string
2296     */
2297    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2298    {
2299        $result = '';
2300        if ($charSet == '') {
2301            $charSet = $this->CharSet;
2302        }
2303        if ($contentType == '') {
2304            $contentType = $this->ContentType;
2305        }
2306        if ($encoding == '') {
2307            $encoding = $this->Encoding;
2308        }
2309        $result .= $this->textLine('--' . $boundary);
2310        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2311        $result .= $this->LE;
2312        // RFC1341 part 5 says 7bit is assumed if not specified
2313        if ($encoding != '7bit') {
2314            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2315        }
2316        $result .= $this->LE;
2317
2318        return $result;
2319    }
2320
2321    /**
2322     * Return the end of a message boundary.
2323     * @access protected
2324     * @param string $boundary
2325     * @return string
2326     */
2327    protected function endBoundary($boundary)
2328    {
2329        return $this->LE . '--' . $boundary . '--' . $this->LE;
2330    }
2331
2332    /**
2333     * Set the message type.
2334     * PHPMailer only supports some preset message types,
2335     * not arbitrary MIME structures.
2336     * @access protected
2337     * @return void
2338     */
2339    protected function setMessageType()
2340    {
2341        $type = array();
2342        if ($this->alternativeExists()) {
2343            $type[] = 'alt';
2344        }
2345        if ($this->inlineImageExists()) {
2346            $type[] = 'inline';
2347        }
2348        if ($this->attachmentExists()) {
2349            $type[] = 'attach';
2350        }
2351        $this->message_type = implode('_', $type);
2352        if ($this->message_type == '') {
2353            $this->message_type = 'plain';
2354        }
2355    }
2356
2357    /**
2358     * Format a header line.
2359     * @access public
2360     * @param string $name
2361     * @param string $value
2362     * @return string
2363     */
2364    public function headerLine($name, $value)
2365    {
2366        return $name . ': ' . $value . $this->LE;
2367    }
2368
2369    /**
2370     * Return a formatted mail line.
2371     * @access public
2372     * @param string $value
2373     * @return string
2374     */
2375    public function textLine($value)
2376    {
2377        return $value . $this->LE;
2378    }
2379
2380    /**
2381     * Add an attachment from a path on the filesystem.
2382     * Returns false if the file could not be found or read.
2383     * @param string $path Path to the attachment.
2384     * @param string $name Overrides the attachment name.
2385     * @param string $encoding File encoding (see $Encoding).
2386     * @param string $type File extension (MIME) type.
2387     * @param string $disposition Disposition to use
2388     * @throws phpmailerException
2389     * @return boolean
2390     */
2391    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2392    {
2393        try {
2394            if (!@is_file($path)) {
2395                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2396            }
2397
2398            // If a MIME type is not specified, try to work it out from the file name
2399            if ($type == '') {
2400                $type = self::filenameToType($path);
2401            }
2402
2403            $filename = basename($path);
2404            if ($name == '') {
2405                $name = $filename;
2406            }
2407
2408            $this->attachment[] = array(
2409                0 => $path,
2410                1 => $filename,
2411                2 => $name,
2412                3 => $encoding,
2413                4 => $type,
2414                5 => false, // isStringAttachment
2415                6 => $disposition,
2416                7 => 0
2417            );
2418
2419        } catch (phpmailerException $exc) {
2420            $this->setError($exc->getMessage());
2421            $this->edebug($exc->getMessage());
2422            if ($this->exceptions) {
2423                throw $exc;
2424            }
2425            return false;
2426        }
2427        return true;
2428    }
2429
2430    /**
2431     * Return the array of attachments.
2432     * @return array
2433     */
2434    public function getAttachments()
2435    {
2436        return $this->attachment;
2437    }
2438
2439    /**
2440     * Attach all file, string, and binary attachments to the message.
2441     * Returns an empty string on failure.
2442     * @access protected
2443     * @param string $disposition_type
2444     * @param string $boundary
2445     * @return string
2446     */
2447    protected function attachAll($disposition_type, $boundary)
2448    {
2449        // Return text of body
2450        $mime = array();
2451        $cidUniq = array();
2452        $incl = array();
2453
2454        // Add all attachments
2455        foreach ($this->attachment as $attachment) {
2456            // Check if it is a valid disposition_filter
2457            if ($attachment[6] == $disposition_type) {
2458                // Check for string attachment
2459                $string = '';
2460                $path = '';
2461                $bString = $attachment[5];
2462                if ($bString) {
2463                    $string = $attachment[0];
2464                } else {
2465                    $path = $attachment[0];
2466                }
2467
2468                $inclhash = md5(serialize($attachment));
2469                if (in_array($inclhash, $incl)) {
2470                    continue;
2471                }
2472                $incl[] = $inclhash;
2473                $name = $attachment[2];
2474                $encoding = $attachment[3];
2475                $type = $attachment[4];
2476                $disposition = $attachment[6];
2477                $cid = $attachment[7];
2478                if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
2479                    continue;
2480                }
2481                $cidUniq[$cid] = true;
2482
2483                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2484                //Only include a filename property if we have one
2485                if (!empty($name)) {
2486                    $mime[] = sprintf(
2487                        'Content-Type: %s; name="%s"%s',
2488                        $type,
2489                        $this->encodeHeader($this->secureHeader($name)),
2490                        $this->LE
2491                    );
2492                } else {
2493                    $mime[] = sprintf(
2494                        'Content-Type: %s%s',
2495                        $type,
2496                        $this->LE
2497                    );
2498                }
2499                // RFC1341 part 5 says 7bit is assumed if not specified
2500                if ($encoding != '7bit') {
2501                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2502                }
2503
2504                if ($disposition == 'inline') {
2505                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2506                }
2507
2508                // If a filename contains any of these chars, it should be quoted,
2509                // but not otherwise: RFC2183 & RFC2045 5.1
2510                // Fixes a warning in IETF's msglint MIME checker
2511                // Allow for bypassing the Content-Disposition header totally
2512                if (!(empty($disposition))) {
2513                    $encoded_name = $this->encodeHeader($this->secureHeader($name));
2514                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2515                        $mime[] = sprintf(
2516                            'Content-Disposition: %s; filename="%s"%s',
2517                            $disposition,
2518                            $encoded_name,
2519                            $this->LE . $this->LE
2520                        );
2521                    } else {
2522                        if (!empty($encoded_name)) {
2523                            $mime[] = sprintf(
2524                                'Content-Disposition: %s; filename=%s%s',
2525                                $disposition,
2526                                $encoded_name,
2527                                $this->LE . $this->LE
2528                            );
2529                        } else {
2530                            $mime[] = sprintf(
2531                                'Content-Disposition: %s%s',
2532                                $disposition,
2533                                $this->LE . $this->LE
2534                            );
2535                        }
2536                    }
2537                } else {
2538                    $mime[] = $this->LE;
2539                }
2540
2541                // Encode as string attachment
2542                if ($bString) {
2543                    $mime[] = $this->encodeString($string, $encoding);
2544                    if ($this->isError()) {
2545                        return '';
2546                    }
2547                    $mime[] = $this->LE . $this->LE;
2548                } else {
2549                    $mime[] = $this->encodeFile($path, $encoding);
2550                    if ($this->isError()) {
2551                        return '';
2552                    }
2553                    $mime[] = $this->LE . $this->LE;
2554                }
2555            }
2556        }
2557
2558        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2559
2560        return implode('', $mime);
2561    }
2562
2563    /**
2564     * Encode a file attachment in requested format.
2565     * Returns an empty string on failure.
2566     * @param string $path The full path to the file
2567     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2568     * @throws phpmailerException
2569     * @access protected
2570     * @return string
2571     */
2572    protected function encodeFile($path, $encoding = 'base64')
2573    {
2574        try {
2575            if (!is_readable($path)) {
2576                throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2577            }
2578            $magic_quotes = get_magic_quotes_runtime();
2579            if ($magic_quotes) {
2580                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2581                    set_magic_quotes_runtime(false);
2582                } else {
2583                    //Doesn't exist in PHP 5.4, but we don't need to check because
2584                    //get_magic_quotes_runtime always returns false in 5.4+
2585                    //so it will never get here
2586                    ini_set('magic_quotes_runtime', false);
2587                }
2588            }
2589            $file_buffer = file_get_contents($path);
2590            $file_buffer = $this->encodeString($file_buffer, $encoding);
2591            if ($magic_quotes) {
2592                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2593                    set_magic_quotes_runtime($magic_quotes);
2594                } else {
2595                    ini_set('magic_quotes_runtime', $magic_quotes);
2596                }
2597            }
2598            return $file_buffer;
2599        } catch (Exception $exc) {
2600            $this->setError($exc->getMessage());
2601            return '';
2602        }
2603    }
2604
2605    /**
2606     * Encode a string in requested format.
2607     * Returns an empty string on failure.
2608     * @param string $str The text to encode
2609     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2610     * @access public
2611     * @return string
2612     */
2613    public function encodeString($str, $encoding = 'base64')
2614    {
2615        $encoded = '';
2616        switch (strtolower($encoding)) {
2617            case 'base64':
2618                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2619                break;
2620            case '7bit':
2621            case '8bit':
2622                $encoded = $this->fixEOL($str);
2623                // Make sure it ends with a line break
2624                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2625                    $encoded .= $this->LE;
2626                }
2627                break;
2628            case 'binary':
2629                $encoded = $str;
2630                break;
2631            case 'quoted-printable':
2632                $encoded = $this->encodeQP($str);
2633                break;
2634            default:
2635                $this->setError($this->lang('encoding') . $encoding);
2636                break;
2637        }
2638        return $encoded;
2639    }
2640
2641    /**
2642     * Encode a header string optimally.
2643     * Picks shortest of Q, B, quoted-printable or none.
2644     * @access public
2645     * @param string $str
2646     * @param string $position
2647     * @return string
2648     */
2649    public function encodeHeader($str, $position = 'text')
2650    {
2651        $matchcount = 0;
2652        switch (strtolower($position)) {
2653            case 'phrase':
2654                if (!preg_match('/[\200-\377]/', $str)) {
2655                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
2656                    $encoded = addcslashes($str, "\0..\37\177\\\"");
2657                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2658                        return ($encoded);
2659                    } else {
2660                        return ("\"$encoded\"");
2661                    }
2662                }
2663                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2664                break;
2665            /** @noinspection PhpMissingBreakStatementInspection */
2666            case 'comment':
2667                $matchcount = preg_match_all('/[()"]/', $str, $matches);
2668                // Intentional fall-through
2669            case 'text':
2670            default:
2671                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2672                break;
2673        }
2674
2675        //There are no chars that need encoding
2676        if ($matchcount == 0) {
2677            return ($str);
2678        }
2679
2680        $maxlen = 75 - 7 - strlen($this->CharSet);
2681        // Try to select the encoding which should produce the shortest output
2682        if ($matchcount > strlen($str) / 3) {
2683            // More than a third of the content will need encoding, so B encoding will be most efficient
2684            $encoding = 'B';
2685            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2686                // Use a custom function which correctly encodes and wraps long
2687                // multibyte strings without breaking lines within a character
2688                $encoded = $this->base64EncodeWrapMB($str, "\n");
2689            } else {
2690                $encoded = base64_encode($str);
2691                $maxlen -= $maxlen % 4;
2692                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2693            }
2694        } else {
2695            $encoding = 'Q';
2696            $encoded = $this->encodeQ($str, $position);
2697            $encoded = $this->wrapText($encoded, $maxlen, true);
2698            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2699        }
2700
2701        $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2702        $encoded = trim(str_replace("\n", $this->LE, $encoded));
2703
2704        return $encoded;
2705    }
2706
2707    /**
2708     * Check if a string contains multi-byte characters.
2709     * @access public
2710     * @param string $str multi-byte text to wrap encode
2711     * @return boolean
2712     */
2713    public function hasMultiBytes($str)
2714    {
2715        if (function_exists('mb_strlen')) {
2716            return (strlen($str) > mb_strlen($str, $this->CharSet));
2717        } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2718            return false;
2719        }
2720    }
2721
2722    /**
2723     * Does a string contain any 8-bit chars (in any charset)?
2724     * @param string $text
2725     * @return boolean
2726     */
2727    public function has8bitChars($text)
2728    {
2729        return (boolean)preg_match('/[\x80-\xFF]/', $text);
2730    }
2731
2732    /**
2733     * Encode and wrap long multibyte strings for mail headers
2734     * without breaking lines within a character.
2735     * Adapted from a function by paravoid
2736     * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2737     * @access public
2738     * @param string $str multi-byte text to wrap encode
2739     * @param string $linebreak string to use as linefeed/end-of-line
2740     * @return string
2741     */
2742    public function base64EncodeWrapMB($str, $linebreak = null)
2743    {
2744        $start = '=?' . $this->CharSet . '?B?';
2745        $end = '?=';
2746        $encoded = '';
2747        if ($linebreak === null) {
2748            $linebreak = $this->LE;
2749        }
2750
2751        $mb_length = mb_strlen($str, $this->CharSet);
2752        // Each line must have length <= 75, including $start and $end
2753        $length = 75 - strlen($start) - strlen($end);
2754        // Average multi-byte ratio
2755        $ratio = $mb_length / strlen($str);
2756        // Base64 has a 4:3 ratio
2757        $avgLength = floor($length * $ratio * .75);
2758
2759        for ($i = 0; $i < $mb_length; $i += $offset) {
2760            $lookBack = 0;
2761            do {
2762                $offset = $avgLength - $lookBack;
2763                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2764                $chunk = base64_encode($chunk);
2765                $lookBack++;
2766            } while (strlen($chunk) > $length);
2767            $encoded .= $chunk . $linebreak;
2768        }
2769
2770        // Chomp the last linefeed
2771        $encoded = substr($encoded, 0, -strlen($linebreak));
2772        return $encoded;
2773    }
2774
2775    /**
2776     * Encode a string in quoted-printable format.
2777     * According to RFC2045 section 6.7.
2778     * @access public
2779     * @param string $string The text to encode
2780     * @param integer $line_max Number of chars allowed on a line before wrapping
2781     * @return string
2782     * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2783     */
2784    public function encodeQP($string, $line_max = 76)
2785    {
2786        // Use native function if it's available (>= PHP5.3)
2787        if (function_exists('quoted_printable_encode')) {
2788            return quoted_printable_encode($string);
2789        }
2790        // Fall back to a pure PHP implementation
2791        $string = str_replace(
2792            array('%20', '%0D%0A.', '%0D%0A', '%'),
2793            array(' ', "\r\n=2E", "\r\n", '='),
2794            rawurlencode($string)
2795        );
2796        return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2797    }
2798
2799    /**
2800     * Backward compatibility wrapper for an old QP encoding function that was removed.
2801     * @see PHPMailer::encodeQP()
2802     * @access public
2803     * @param string $string
2804     * @param integer $line_max
2805     * @param boolean $space_conv
2806     * @return string
2807     * @deprecated Use encodeQP instead.
2808     */
2809    public function encodeQPphp(
2810        $string,
2811        $line_max = 76,
2812        /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2813    ) {
2814        return $this->encodeQP($string, $line_max);
2815    }
2816
2817    /**
2818     * Encode a string using Q encoding.
2819     * @link http://tools.ietf.org/html/rfc2047
2820     * @param string $str the text to encode
2821     * @param string $position Where the text is going to be used, see the RFC for what that means
2822     * @access public
2823     * @return string
2824     */
2825    public function encodeQ($str, $position = 'text')
2826    {
2827        // There should not be any EOL in the string
2828        $pattern = '';
2829        $encoded = str_replace(array("\r", "\n"), '', $str);
2830        switch (strtolower($position)) {
2831            case 'phrase':
2832                // RFC 2047 section 5.3
2833                $pattern = '^A-Za-z0-9!*+\/ -';
2834                break;
2835            /** @noinspection PhpMissingBreakStatementInspection */
2836            case 'comment':
2837                // RFC 2047 section 5.2
2838                $pattern = '\(\)"';
2839                // intentional fall-through
2840                // for this reason we build the $pattern without including delimiters and []
2841            case 'text':
2842            default:
2843                // RFC 2047 section 5.1
2844                // Replace every high ascii, control, =, ? and _ characters
2845                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2846                break;
2847        }
2848        $matches = array();
2849        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2850            // If the string contains an '=', make sure it's the first thing we replace
2851            // so as to avoid double-encoding
2852            $eqkey = array_search('=', $matches[0]);
2853            if (false !== $eqkey) {
2854                unset($matches[0][$eqkey]);
2855                array_unshift($matches[0], '=');
2856            }
2857            foreach (array_unique($matches[0]) as $char) {
2858                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2859            }
2860        }
2861        // Replace every spaces to _ (more readable than =20)
2862        return str_replace(' ', '_', $encoded);
2863    }
2864
2865    /**
2866     * Add a string or binary attachment (non-filesystem).
2867     * This method can be used to attach ascii or binary data,
2868     * such as a BLOB record from a database.
2869     * @param string $string String attachment data.
2870     * @param string $filename Name of the attachment.
2871     * @param string $encoding File encoding (see $Encoding).
2872     * @param string $type File extension (MIME) type.
2873     * @param string $disposition Disposition to use
2874     * @return void
2875     */
2876    public function addStringAttachment(
2877        $string,
2878        $filename,
2879        $encoding = 'base64',
2880        $type = '',
2881        $disposition = 'attachment'
2882    ) {
2883        // If a MIME type is not specified, try to work it out from the file name
2884        if ($type == '') {
2885            $type = self::filenameToType($filename);
2886        }
2887        // Append to $attachment array
2888        $this->attachment[] = array(
2889            0 => $string,
2890            1 => $filename,
2891            2 => basename($filename),
2892            3 => $encoding,
2893            4 => $type,
2894            5 => true, // isStringAttachment
2895            6 => $disposition,
2896            7 => 0
2897        );
2898    }
2899
2900    /**
2901     * Add an embedded (inline) attachment from a file.
2902     * This can include images, sounds, and just about any other document type.
2903     * These differ from 'regular' attachments in that they are intended to be
2904     * displayed inline with the message, not just attached for download.
2905     * This is used in HTML messages that embed the images
2906     * the HTML refers to using the $cid value.
2907     * @param string $path Path to the attachment.
2908     * @param string $cid Content ID of the attachment; Use this to reference
2909     *        the content when using an embedded image in HTML.
2910     * @param string $name Overrides the attachment name.
2911     * @param string $encoding File encoding (see $Encoding).
2912     * @param string $type File MIME type.
2913     * @param string $disposition Disposition to use
2914     * @return boolean True on successfully adding an attachment
2915     */
2916    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2917    {
2918        if (!@is_file($path)) {
2919            $this->setError($this->lang('file_access') . $path);
2920            return false;
2921        }
2922
2923        // If a MIME type is not specified, try to work it out from the file name
2924        if ($type == '') {
2925            $type = self::filenameToType($path);
2926        }
2927
2928        $filename = basename($path);
2929        if ($name == '') {
2930            $name = $filename;
2931        }
2932
2933        // Append to $attachment array
2934        $this->attachment[] = array(
2935            0 => $path,
2936            1 => $filename,
2937            2 => $name,
2938            3 => $encoding,
2939            4 => $type,
2940            5 => false, // isStringAttachment
2941            6 => $disposition,
2942            7 => $cid
2943        );
2944        return true;
2945    }
2946
2947    /**
2948     * Add an embedded stringified attachment.
2949     * This can include images, sounds, and just about any other document type.
2950     * Be sure to set the $type to an image type for images:
2951     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2952     * @param string $string The attachment binary data.
2953     * @param string $cid Content ID of the attachment; Use this to reference
2954     *        the content when using an embedded image in HTML.
2955     * @param string $name
2956     * @param string $encoding File encoding (see $Encoding).
2957     * @param string $type MIME type.
2958     * @param string $disposition Disposition to use
2959     * @return boolean True on successfully adding an attachment
2960     */
2961    public function addStringEmbeddedImage(
2962        $string,
2963        $cid,
2964        $name = '',
2965        $encoding = 'base64',
2966        $type = '',
2967        $disposition = 'inline'
2968    ) {
2969        // If a MIME type is not specified, try to work it out from the name
2970        if ($type == '' and !empty($name)) {
2971            $type = self::filenameToType($name);
2972        }
2973
2974        // Append to $attachment array
2975        $this->attachment[] = array(
2976            0 => $string,
2977            1 => $name,
2978            2 => $name,
2979            3 => $encoding,
2980            4 => $type,
2981            5 => true, // isStringAttachment
2982            6 => $disposition,
2983            7 => $cid
2984        );
2985        return true;
2986    }
2987
2988    /**
2989     * Check if an inline attachment is present.
2990     * @access public
2991     * @return boolean
2992     */
2993    public function inlineImageExists()
2994    {
2995        foreach ($this->attachment as $attachment) {
2996            if ($attachment[6] == 'inline') {
2997                return true;
2998            }
2999        }
3000        return false;
3001    }
3002
3003    /**
3004     * Check if an attachment (non-inline) is present.
3005     * @return boolean
3006     */
3007    public function attachmentExists()
3008    {
3009        foreach ($this->attachment as $attachment) {
3010            if ($attachment[6] == 'attachment') {
3011                return true;
3012            }
3013        }
3014        return false;
3015    }
3016
3017    /**
3018     * Check if this message has an alternative body set.
3019     * @return boolean
3020     */
3021    public function alternativeExists()
3022    {
3023        return !empty($this->AltBody);
3024    }
3025
3026    /**
3027     * Clear queued addresses of given kind.
3028     * @access protected
3029     * @param string $kind 'to', 'cc', or 'bcc'
3030     * @return void
3031     */
3032    public function clearQueuedAddresses($kind)
3033    {
3034        $RecipientsQueue = $this->RecipientsQueue;
3035        foreach ($RecipientsQueue as $address => $params) {
3036            if ($params[0] == $kind) {
3037                unset($this->RecipientsQueue[$address]);
3038            }
3039        }
3040    }
3041
3042    /**
3043     * Clear all To recipients.
3044     * @return void
3045     */
3046    public function clearAddresses()
3047    {
3048        foreach ($this->to as $to) {
3049            unset($this->all_recipients[strtolower($to[0])]);
3050        }
3051        $this->to = array();
3052        $this->clearQueuedAddresses('to');
3053    }
3054
3055    /**
3056     * Clear all CC recipients.
3057     * @return void
3058     */
3059    public function clearCCs()
3060    {
3061        foreach ($this->cc as $cc) {
3062            unset($this->all_recipients[strtolower($cc[0])]);
3063        }
3064        $this->cc = array();
3065        $this->clearQueuedAddresses('cc');
3066    }
3067
3068    /**
3069     * Clear all BCC recipients.
3070     * @return void
3071     */
3072    public function clearBCCs()
3073    {
3074        foreach ($this->bcc as $bcc) {
3075            unset($this->all_recipients[strtolower($bcc[0])]);
3076        }
3077        $this->bcc = array();
3078        $this->clearQueuedAddresses('bcc');
3079    }
3080
3081    /**
3082     * Clear all ReplyTo recipients.
3083     * @return void
3084     */
3085    public function clearReplyTos()
3086    {
3087        $this->ReplyTo = array();
3088        $this->ReplyToQueue = array();
3089    }
3090
3091    /**
3092     * Clear all recipient types.
3093     * @return void
3094     */
3095    public function clearAllRecipients()
3096    {
3097        $this->to = array();
3098        $this->cc = array();
3099        $this->bcc = array();
3100        $this->all_recipients = array();
3101        $this->RecipientsQueue = array();
3102    }
3103
3104    /**
3105     * Clear all filesystem, string, and binary attachments.
3106     * @return void
3107     */
3108    public function clearAttachments()
3109    {
3110        $this->attachment = array();
3111    }
3112
3113    /**
3114     * Clear all custom headers.
3115     * @return void
3116     */
3117    public function clearCustomHeaders()
3118    {
3119        $this->CustomHeader = array();
3120    }
3121
3122    /**
3123     * Add an error message to the error container.
3124     * @access protected
3125     * @param string $msg
3126     * @return void
3127     */
3128    protected function setError($msg)
3129    {
3130        $this->error_count++;
3131        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
3132            $lasterror = $this->smtp->getError();
3133            if (!empty($lasterror['error'])) {
3134                $msg .= $this->lang('smtp_error') . $lasterror['error'];
3135                if (!empty($lasterror['detail'])) {
3136                    $msg .= ' Detail: '. $lasterror['detail'];
3137                }
3138                if (!empty($lasterror['smtp_code'])) {
3139                    $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3140                }
3141                if (!empty($lasterror['smtp_code_ex'])) {
3142                    $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3143                }
3144            }
3145        }
3146        $this->ErrorInfo = $msg;
3147    }
3148
3149    /**
3150     * Return an RFC 822 formatted date.
3151     * @access public
3152     * @return string
3153     * @static
3154     */
3155    public static function rfcDate()
3156    {
3157        // Set the time zone to whatever the default is to avoid 500 errors
3158        // Will default to UTC if it's not set properly in php.ini
3159        date_default_timezone_set(@date_default_timezone_get());
3160        return date('D, j M Y H:i:s O');
3161    }
3162
3163    /**
3164     * Get the server hostname.
3165     * Returns 'localhost.localdomain' if unknown.
3166     * @access protected
3167     * @return string
3168     */
3169    protected function serverHostname()
3170    {
3171        $result = 'localhost.localdomain';
3172        if (!empty($this->Hostname)) {
3173            $result = $this->Hostname;
3174        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
3175            $result = $_SERVER['SERVER_NAME'];
3176        } elseif (function_exists('gethostname') && gethostname() !== false) {
3177            $result = gethostname();
3178        } elseif (php_uname('n') !== false) {
3179            $result = php_uname('n');
3180        }
3181        return $result;
3182    }
3183
3184    /**
3185     * Get an error message in the current language.
3186     * @access protected
3187     * @param string $key
3188     * @return string
3189     */
3190    protected function lang($key)
3191    {
3192        if (count($this->language) < 1) {
3193            $this->setLanguage('en'); // set the default language
3194        }
3195
3196        if (array_key_exists($key, $this->language)) {
3197            if ($key == 'smtp_connect_failed') {
3198                //Include a link to troubleshooting docs on SMTP connection failure
3199                //this is by far the biggest cause of support questions
3200                //but it's usually not PHPMailer's fault.
3201                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3202            }
3203            return $this->language[$key];
3204        } else {
3205            //Return the key as a fallback
3206            return $key;
3207        }
3208    }
3209
3210    /**
3211     * Check if an error occurred.
3212     * @access public
3213     * @return boolean True if an error did occur.
3214     */
3215    public function isError()
3216    {
3217        return ($this->error_count > 0);
3218    }
3219
3220    /**
3221     * Ensure consistent line endings in a string.
3222     * Changes every end of line from CRLF, CR or LF to $this->LE.
3223     * @access public
3224     * @param string $str String to fixEOL
3225     * @return string
3226     */
3227    public function fixEOL($str)
3228    {
3229        // Normalise to \n
3230        $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
3231        // Now convert LE as needed
3232        if ($this->LE !== "\n") {
3233            $nstr = str_replace("\n", $this->LE, $nstr);
3234        }
3235        return $nstr;
3236    }
3237
3238    /**
3239     * Add a custom header.
3240     * $name value can be overloaded to contain
3241     * both header name and value (name:value)
3242     * @access public
3243     * @param string $name Custom header name
3244     * @param string $value Header value
3245     * @return void
3246     */
3247    public function addCustomHeader($name, $value = null)
3248    {
3249        if ($value === null) {
3250            // Value passed in as name:value
3251            $this->CustomHeader[] = explode(':', $name, 2);
3252        } else {
3253            $this->CustomHeader[] = array($name, $value);
3254        }
3255    }
3256
3257    /**
3258     * Returns all custom headers.
3259     * @return array
3260     */
3261    public function getCustomHeaders()
3262    {
3263        return $this->CustomHeader;
3264    }
3265
3266    /**
3267     * Create a message from an HTML string.
3268     * Automatically makes modifications for inline images and backgrounds
3269     * and creates a plain-text version by converting the HTML.
3270     * Overwrites any existing values in $this->Body and $this->AltBody
3271     * @access public
3272     * @param string $message HTML message string
3273     * @param string $basedir baseline directory for path
3274     * @param boolean|callable $advanced Whether to use the internal HTML to text converter
3275     *    or your own custom converter @see PHPMailer::html2text()
3276     * @return string $message
3277     */
3278    public function msgHTML($message, $basedir = '', $advanced = false)
3279    {
3280        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3281        if (array_key_exists(2, $images)) {
3282            foreach ($images[2] as $imgindex => $url) {
3283                // Convert data URIs into embedded images
3284                if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3285                    $data = substr($url, strpos($url, ','));
3286                    if ($match[2]) {
3287                        $data = base64_decode($data);
3288                    } else {
3289                        $data = rawurldecode($data);
3290                    }
3291                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3292                    if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
3293                        $message = str_replace(
3294                            $images[0][$imgindex],
3295                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3296                            $message
3297                        );
3298                    }
3299                } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) {
3300                    // Do not change urls for absolute images (thanks to corvuscorax)
3301                    // Do not change urls that are already inline images
3302                    $filename = basename($url);
3303                    $directory = dirname($url);
3304                    if ($directory == '.') {
3305                        $directory = '';
3306                    }
3307                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3308                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3309                        $basedir .= '/';
3310                    }
3311                    if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3312                        $directory .= '/';
3313                    }
3314                    if ($this->addEmbeddedImage(
3315                        $basedir . $directory . $filename,
3316                        $cid,
3317                        $filename,
3318                        'base64',
3319                        self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
3320                    )
3321                    ) {
3322                        $message = preg_replace(
3323                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3324                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3325                            $message
3326                        );
3327                    }
3328                }
3329            }
3330        }
3331        $this->isHTML(true);
3332        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3333        $this->Body = $this->normalizeBreaks($message);
3334        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3335        if (empty($this->AltBody)) {
3336            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
3337                self::CRLF . self::CRLF;
3338        }
3339        return $this->Body;
3340    }
3341
3342    /**
3343     * Convert an HTML string into plain text.
3344     * This is used by msgHTML().
3345     * Note - older versions of this function used a bundled advanced converter
3346     * which was been removed for license reasons in #232
3347     * Example usage:
3348     * <code>
3349     * // Use default conversion
3350     * $plain = $mail->html2text($html);
3351     * // Use your own custom converter
3352     * $plain = $mail->html2text($html, function($html) {
3353     *     $converter = new MyHtml2text($html);
3354     *     return $converter->get_text();
3355     * });
3356     * </code>
3357     * @param string $html The HTML text to convert
3358     * @param boolean|callable $advanced Any boolean value to use the internal converter,
3359     *   or provide your own callable for custom conversion.
3360     * @return string
3361     */
3362    public function html2text($html, $advanced = false)
3363    {
3364        if (is_callable($advanced)) {
3365            return call_user_func($advanced, $html);
3366        }
3367        return html_entity_decode(
3368            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3369            ENT_QUOTES,
3370            $this->CharSet
3371        );
3372    }
3373
3374    /**
3375     * Get the MIME type for a file extension.
3376     * @param string $ext File extension
3377     * @access public
3378     * @return string MIME type of file.
3379     * @static
3380     */
3381    public static function _mime_types($ext = '')
3382    {
3383        $mimes = array(
3384            'xl'    => 'application/excel',
3385            'js'    => 'application/javascript',
3386            'hqx'   => 'application/mac-binhex40',
3387            'cpt'   => 'application/mac-compactpro',
3388            'bin'   => 'application/macbinary',
3389            'doc'   => 'application/msword',
3390            'word'  => 'application/msword',
3391            'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3392            'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3393            'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3394            'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3395            'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3396            'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3397            'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3398            'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3399            'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
3400            'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3401            'class' => 'application/octet-stream',
3402            'dll'   => 'application/octet-stream',
3403            'dms'   => 'application/octet-stream',
3404            'exe'   => 'application/octet-stream',
3405            'lha'   => 'application/octet-stream',
3406            'lzh'   => 'application/octet-stream',
3407            'psd'   => 'application/octet-stream',
3408            'sea'   => 'application/octet-stream',
3409            'so'    => 'application/octet-stream',
3410            'oda'   => 'application/oda',
3411            'pdf'   => 'application/pdf',
3412            'ai'    => 'application/postscript',
3413            'eps'   => 'application/postscript',
3414            'ps'    => 'application/postscript',
3415            'smi'   => 'application/smil',
3416            'smil'  => 'application/smil',
3417            'mif'   => 'application/vnd.mif',
3418            'xls'   => 'application/vnd.ms-excel',
3419            'ppt'   => 'application/vnd.ms-powerpoint',
3420            'wbxml' => 'application/vnd.wap.wbxml',
3421            'wmlc'  => 'application/vnd.wap.wmlc',
3422            'dcr'   => 'application/x-director',
3423            'dir'   => 'application/x-director',
3424            'dxr'   => 'application/x-director',
3425            'dvi'   => 'application/x-dvi',
3426            'gtar'  => 'application/x-gtar',
3427            'php3'  => 'application/x-httpd-php',
3428            'php4'  => 'application/x-httpd-php',
3429            'php'   => 'application/x-httpd-php',
3430            'phtml' => 'application/x-httpd-php',
3431            'phps'  => 'application/x-httpd-php-source',
3432            'swf'   => 'application/x-shockwave-flash',
3433            'sit'   => 'application/x-stuffit',
3434            'tar'   => 'application/x-tar',
3435            'tgz'   => 'application/x-tar',
3436            'xht'   => 'application/xhtml+xml',
3437            'xhtml' => 'application/xhtml+xml',
3438            'zip'   => 'application/zip',
3439            'mid'   => 'audio/midi',
3440            'midi'  => 'audio/midi',
3441            'mp2'   => 'audio/mpeg',
3442            'mp3'   => 'audio/mpeg',
3443            'mpga'  => 'audio/mpeg',
3444            'aif'   => 'audio/x-aiff',
3445            'aifc'  => 'audio/x-aiff',
3446            'aiff'  => 'audio/x-aiff',
3447            'ram'   => 'audio/x-pn-realaudio',
3448            'rm'    => 'audio/x-pn-realaudio',
3449            'rpm'   => 'audio/x-pn-realaudio-plugin',
3450            'ra'    => 'audio/x-realaudio',
3451            'wav'   => 'audio/x-wav',
3452            'bmp'   => 'image/bmp',
3453            'gif'   => 'image/gif',
3454            'jpeg'  => 'image/jpeg',
3455            'jpe'   => 'image/jpeg',
3456            'jpg'   => 'image/jpeg',
3457            'png'   => 'image/png',
3458            'tiff'  => 'image/tiff',
3459            'tif'   => 'image/tiff',
3460            'eml'   => 'message/rfc822',
3461            'css'   => 'text/css',
3462            'html'  => 'text/html',
3463            'htm'   => 'text/html',
3464            'shtml' => 'text/html',
3465            'log'   => 'text/plain',
3466            'text'  => 'text/plain',
3467            'txt'   => 'text/plain',
3468            'rtx'   => 'text/richtext',
3469            'rtf'   => 'text/rtf',
3470            'vcf'   => 'text/vcard',
3471            'vcard' => 'text/vcard',
3472            'xml'   => 'text/xml',
3473            'xsl'   => 'text/xml',
3474            'mpeg'  => 'video/mpeg',
3475            'mpe'   => 'video/mpeg',
3476            'mpg'   => 'video/mpeg',
3477            'mov'   => 'video/quicktime',
3478            'qt'    => 'video/quicktime',
3479            'rv'    => 'video/vnd.rn-realvideo',
3480            'avi'   => 'video/x-msvideo',
3481            'movie' => 'video/x-sgi-movie'
3482        );
3483        if (array_key_exists(strtolower($ext), $mimes)) {
3484            return $mimes[strtolower($ext)];
3485        }
3486        return 'application/octet-stream';
3487    }
3488
3489    /**
3490     * Map a file name to a MIME type.
3491     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3492     * @param string $filename A file name or full path, does not need to exist as a file
3493     * @return string
3494     * @static
3495     */
3496    public static function filenameToType($filename)
3497    {
3498        // In case the path is a URL, strip any query string before getting extension
3499        $qpos = strpos($filename, '?');
3500        if (false !== $qpos) {
3501            $filename = substr($filename, 0, $qpos);
3502        }
3503        $pathinfo = self::mb_pathinfo($filename);
3504        return self::_mime_types($pathinfo['extension']);
3505    }
3506
3507    /**
3508     * Multi-byte-safe pathinfo replacement.
3509     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3510     * Works similarly to the one in PHP >= 5.2.0
3511     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3512     * @param string $path A filename or path, does not need to exist as a file
3513     * @param integer|string $options Either a PATHINFO_* constant,
3514     *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3515     * @return string|array
3516     * @static
3517     */
3518    public static function mb_pathinfo($path, $options = null)
3519    {
3520        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3521        $pathinfo = array();
3522        if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3523            if (array_key_exists(1, $pathinfo)) {
3524                $ret['dirname'] = $pathinfo[1];
3525            }
3526            if (array_key_exists(2, $pathinfo)) {
3527                $ret['basename'] = $pathinfo[2];
3528            }
3529            if (array_key_exists(5, $pathinfo)) {
3530                $ret['extension'] = $pathinfo[5];
3531            }
3532            if (array_key_exists(3, $pathinfo)) {
3533                $ret['filename'] = $pathinfo[3];
3534            }
3535        }
3536        switch ($options) {
3537            case PATHINFO_DIRNAME:
3538            case 'dirname':
3539                return $ret['dirname'];
3540            case PATHINFO_BASENAME:
3541            case 'basename':
3542                return $ret['basename'];
3543            case PATHINFO_EXTENSION:
3544            case 'extension':
3545                return $ret['extension'];
3546            case PATHINFO_FILENAME:
3547            case 'filename':
3548                return $ret['filename'];
3549            default:
3550                return $ret;
3551        }
3552    }
3553
3554    /**
3555     * Set or reset instance properties.
3556     * You should avoid this function - it's more verbose, less efficient, more error-prone and
3557     * harder to debug than setting properties directly.
3558     * Usage Example:
3559     * `$mail->set('SMTPSecure', 'tls');`
3560     *   is the same as:
3561     * `$mail->SMTPSecure = 'tls';`
3562     * @access public
3563     * @param string $name The property name to set
3564     * @param mixed $value The value to set the property to
3565     * @return boolean
3566     * @TODO Should this not be using the __set() magic function?
3567     */
3568    public function set($name, $value = '')
3569    {
3570        if (property_exists($this, $name)) {
3571            $this->$name = $value;
3572            return true;
3573        } else {
3574            $this->setError($this->lang('variable_set') . $name);
3575            return false;
3576        }
3577    }
3578
3579    /**
3580     * Strip newlines to prevent header injection.
3581     * @access public
3582     * @param string $str
3583     * @return string
3584     */
3585    public function secureHeader($str)
3586    {
3587        return trim(str_replace(array("\r", "\n"), '', $str));
3588    }
3589
3590    /**
3591     * Normalize line breaks in a string.
3592     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3593     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3594     * @param string $text
3595     * @param string $breaktype What kind of line break to use, defaults to CRLF
3596     * @return string
3597     * @access public
3598     * @static
3599     */
3600    public static function normalizeBreaks($text, $breaktype = "\r\n")
3601    {
3602        return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3603    }
3604
3605    /**
3606     * Set the public and private key files and password for S/MIME signing.
3607     * @access public
3608     * @param string $cert_filename
3609     * @param string $key_filename
3610     * @param string $key_pass Password for private key
3611     * @param string $extracerts_filename Optional path to chain certificate
3612     */
3613    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3614    {
3615        $this->sign_cert_file = $cert_filename;
3616        $this->sign_key_file = $key_filename;
3617        $this->sign_key_pass = $key_pass;
3618        $this->sign_extracerts_file = $extracerts_filename;
3619    }
3620
3621    /**
3622     * Quoted-Printable-encode a DKIM header.
3623     * @access public
3624     * @param string $txt
3625     * @return string
3626     */
3627    public function DKIM_QP($txt)
3628    {
3629        $line = '';
3630        for ($i = 0; $i < strlen($txt); $i++) {
3631            $ord = ord($txt[$i]);
3632            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3633                $line .= $txt[$i];
3634            } else {
3635                $line .= '=' . sprintf('%02X', $ord);
3636            }
3637        }
3638        return $line;
3639    }
3640
3641    /**
3642     * Generate a DKIM signature.
3643     * @access public
3644     * @param string $signHeader
3645     * @throws phpmailerException
3646     * @return string
3647     */
3648    public function DKIM_Sign($signHeader)
3649    {
3650        if (!defined('PKCS7_TEXT')) {
3651            if ($this->exceptions) {
3652                throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3653            }
3654            return '';
3655        }
3656        $privKeyStr = file_get_contents($this->DKIM_private);
3657        if ($this->DKIM_passphrase != '') {
3658            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3659        } else {
3660            $privKey = $privKeyStr;
3661        }
3662        if (openssl_sign($signHeader, $signature, $privKey)) {
3663            return base64_encode($signature);
3664        }
3665        return '';
3666    }
3667
3668    /**
3669     * Generate a DKIM canonicalization header.
3670     * @access public
3671     * @param string $signHeader Header
3672     * @return string
3673     */
3674    public function DKIM_HeaderC($signHeader)
3675    {
3676        $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3677        $lines = explode("\r\n", $signHeader);
3678        foreach ($lines as $key => $line) {
3679            list($heading, $value) = explode(':', $line, 2);
3680            $heading = strtolower($heading);
3681            $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
3682            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3683        }
3684        $signHeader = implode("\r\n", $lines);
3685        return $signHeader;
3686    }
3687
3688    /**
3689     * Generate a DKIM canonicalization body.
3690     * @access public
3691     * @param string $body Message Body
3692     * @return string
3693     */
3694    public function DKIM_BodyC($body)
3695    {
3696        if ($body == '') {
3697            return "\r\n";
3698        }
3699        // stabilize line endings
3700        $body = str_replace("\r\n", "\n", $body);
3701        $body = str_replace("\n", "\r\n", $body);
3702        // END stabilize line endings
3703        while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3704            $body = substr($body, 0, strlen($body) - 2);
3705        }
3706        return $body;
3707    }
3708
3709    /**
3710     * Create the DKIM header and body in a new message header.
3711     * @access public
3712     * @param string $headers_line Header lines
3713     * @param string $subject Subject
3714     * @param string $body Body
3715     * @return string
3716     */
3717    public function DKIM_Add($headers_line, $subject, $body)
3718    {
3719        $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
3720        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3721        $DKIMquery = 'dns/txt'; // Query method
3722        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3723        $subject_header = "Subject: $subject";
3724        $headers = explode($this->LE, $headers_line);
3725        $from_header = '';
3726        $to_header = '';
3727        $current = '';
3728        foreach ($headers as $header) {
3729            if (strpos($header, 'From:') === 0) {
3730                $from_header = $header;
3731                $current = 'from_header';
3732            } elseif (strpos($header, 'To:') === 0) {
3733                $to_header = $header;
3734                $current = 'to_header';
3735            } else {
3736                if (!empty($$current) && strpos($header, ' =?') === 0) {
3737                    $$current .= $header;
3738                } else {
3739                    $current = '';
3740                }
3741            }
3742        }
3743        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3744        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3745        $subject = str_replace(
3746            '|',
3747            '=7C',
3748            $this->DKIM_QP($subject_header)
3749        ); // Copied header fields (dkim-quoted-printable)
3750        $body = $this->DKIM_BodyC($body);
3751        $DKIMlen = strlen($body); // Length of body
3752        $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
3753        if ('' == $this->DKIM_identity) {
3754            $ident = '';
3755        } else {
3756            $ident = ' i=' . $this->DKIM_identity . ';';
3757        }
3758        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3759            $DKIMsignatureType . '; q=' .
3760            $DKIMquery . '; l=' .
3761            $DKIMlen . '; s=' .
3762            $this->DKIM_selector .
3763            ";\r\n" .
3764            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3765            "\th=From:To:Subject;\r\n" .
3766            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3767            "\tz=$from\r\n" .
3768            "\t|$to\r\n" .
3769            "\t|$subject;\r\n" .
3770            "\tbh=" . $DKIMb64 . ";\r\n" .
3771            "\tb=";
3772        $toSign = $this->DKIM_HeaderC(
3773            $from_header . "\r\n" .
3774            $to_header . "\r\n" .
3775            $subject_header . "\r\n" .
3776            $dkimhdrs
3777        );
3778        $signed = $this->DKIM_Sign($toSign);
3779        return $dkimhdrs . $signed . "\r\n";
3780    }
3781
3782    /**
3783     * Detect if a string contains a line longer than the maximum line length allowed.
3784     * @param string $str
3785     * @return boolean
3786     * @static
3787     */
3788    public static function hasLineLongerThanMax($str)
3789    {
3790        //+2 to include CRLF line break for a 1000 total
3791        return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
3792    }
3793
3794    /**
3795     * Allows for public read access to 'to' property.
3796     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3797     * @access public
3798     * @return array
3799     */
3800    public function getToAddresses()
3801    {
3802        return $this->to;
3803    }
3804
3805    /**
3806     * Allows for public read access to 'cc' property.
3807     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3808     * @access public
3809     * @return array
3810     */
3811    public function getCcAddresses()
3812    {
3813        return $this->cc;
3814    }
3815
3816    /**
3817     * Allows for public read access to 'bcc' property.
3818     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3819     * @access public
3820     * @return array
3821     */
3822    public function getBccAddresses()
3823    {
3824        return $this->bcc;
3825    }
3826
3827    /**
3828     * Allows for public read access to 'ReplyTo' property.
3829     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3830     * @access public
3831     * @return array
3832     */
3833    public function getReplyToAddresses()
3834    {
3835        return $this->ReplyTo;
3836    }
3837
3838    /**
3839     * Allows for public read access to 'all_recipients' property.
3840     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3841     * @access public
3842     * @return array
3843     */
3844    public function getAllRecipientAddresses()
3845    {
3846        return $this->all_recipients;
3847    }
3848
3849    /**
3850     * Perform a callback.
3851     * @param boolean $isSent
3852     * @param array $to
3853     * @param array $cc
3854     * @param array $bcc
3855     * @param string $subject
3856     * @param string $body
3857     * @param string $from
3858     */
3859    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3860    {
3861        if (!empty($this->action_function) && is_callable($this->action_function)) {
3862            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3863            call_user_func_array($this->action_function, $params);
3864        }
3865    }
3866}
3867
3868/**
3869 * PHPMailer exception handler
3870 * @package PHPMailer
3871 */
3872class phpmailerException extends Exception
3873{
3874    /**
3875     * Prettify error message output
3876     * @return string
3877     */
3878    public function errorMessage()
3879    {
3880        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3881        return $errorMsg;
3882    }
3883}
Note: See TracBrowser for help on using the repository browser.