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

Last change on this file since 101345 was 101345, checked in by brunobergot@…, 3 years ago

version 3.4.6 : sécurité, maj de la lib PHPMailer en version 5.2.19

on en profite pour ajouter PHPMailer aux crédits du plugin

astuce en cadeau, les commandes svn pour retirer les fichiers absents ou ajouteur les nouveaux :

svn rm $( svn status | sed -e '/^!/!d' -e 's/^!//' ) + svn add $( svn status | sed -e '/^?/!d' -e 's/^?//' )

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