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