~canonical-sysadmins/wordpress/4.8.3

« back to all changes in this revision

Viewing changes to wp-includes/class-phpmailer.php

  • Committer: Ryan Finnie
  • Date: 2015-08-31 16:09:47 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: ryan.finnie@canonical.com-20150831160947-1h6rfxby9z1ec62u
Merge WP4.3 from upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
<?php
2
2
/**
3
3
 * PHPMailer - PHP email creation and transport class.
4
 
 * PHP Version 5.0.0
5
 
 * Version 5.2.7
 
4
 * PHP Version 5
6
5
 * @package PHPMailer
7
 
 * @link https://github.com/PHPMailer/PHPMailer/
8
 
 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
 
6
 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
 
7
 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
9
8
 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
10
9
 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
11
10
 * @author Brent R. Matzelle (original founder)
12
 
 * @copyright 2013 Marcus Bointon
 
11
 * @copyright 2012 - 2014 Marcus Bointon
13
12
 * @copyright 2010 - 2012 Jim Jagielski
14
13
 * @copyright 2004 - 2009 Andy Prevost
15
14
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
18
17
 * FITNESS FOR A PARTICULAR PURPOSE.
19
18
 */
20
19
 
21
 
if (version_compare(PHP_VERSION, '5.0.0', '<')) {
22
 
    exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n");
23
 
}
24
 
 
25
20
/**
26
21
 * PHPMailer - PHP email creation and transport class.
27
 
 * PHP Version 5.0.0
28
22
 * @package PHPMailer
29
 
 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
 
23
 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
30
24
 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
31
25
 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
32
26
 * @author Brent R. Matzelle (original founder)
33
 
 * @copyright 2013 Marcus Bointon
34
 
 * @copyright 2010 - 2012 Jim Jagielski
35
 
 * @copyright 2004 - 2009 Andy Prevost
36
27
 */
37
28
class PHPMailer
38
29
{
40
31
     * The PHPMailer Version number.
41
32
     * @type string
42
33
     */
43
 
    public $Version = '5.2.7';
 
34
    public $Version = '5.2.10';
44
35
 
45
36
    /**
46
37
     * Email priority.
47
38
     * Options: 1 = High, 3 = Normal, 5 = low.
48
 
     * @type int
 
39
     * @type integer
49
40
     */
50
41
    public $Priority = 3;
51
42
 
97
88
     * The Return-Path of the message.
98
89
     * If empty, it will be set to either From or Sender.
99
90
     * @type string
 
91
     * @deprecated Email senders should never set a return-path header;
 
92
     * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
 
93
     * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
100
94
     */
101
95
    public $ReturnPath = '';
102
96
 
155
149
 
156
150
    /**
157
151
     * Word-wrap the message body to this number of chars.
158
 
     * @type int
 
152
     * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
 
153
     * @type integer
159
154
     */
160
155
    public $WordWrap = 0;
161
156
 
175
170
    /**
176
171
     * Whether mail() uses a fully sendmail-compatible MTA.
177
172
     * One which supports sendmail's "-oi -f" options.
178
 
     * @type bool
 
173
     * @type boolean
179
174
     */
180
175
    public $UseSendmailOptions = true;
181
176
 
222
217
     * You can also specify a different port
223
218
     * for each host by using this format: [hostname:port]
224
219
     * (e.g. "smtp1.example.com:25;smtp2.example.com").
 
220
     * You can also specify encryption type, for example:
 
221
     * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
225
222
     * Hosts will be tried in order.
226
223
     * @type string
227
224
     */
229
226
 
230
227
    /**
231
228
     * The default SMTP server port.
232
 
     * @type int
233
 
     * @Todo Why is this needed when the SMTP class takes care of it?
 
229
     * @type integer
 
230
     * @TODO Why is this needed when the SMTP class takes care of it?
234
231
     */
235
232
    public $Port = 25;
236
233
 
243
240
    public $Helo = '';
244
241
 
245
242
    /**
246
 
     * The secure connection prefix.
247
 
     * Options: "", "ssl" or "tls"
 
243
     * What kind of encryption to use on the SMTP connection.
 
244
     * Options: '', 'ssl' or 'tls'
248
245
     * @type string
249
246
     */
250
247
    public $SMTPSecure = '';
251
248
 
252
249
    /**
 
250
     * Whether to enable TLS encryption automatically if a server supports it,
 
251
     * even if `SMTPSecure` is not set to 'tls'.
 
252
     * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
 
253
     * @type boolean
 
254
     */
 
255
    public $SMTPAutoTLS = true;
 
256
 
 
257
    /**
253
258
     * Whether to use SMTP authentication.
254
259
     * Uses the Username and Password properties.
255
 
     * @type bool
 
260
     * @type boolean
256
261
     * @see PHPMailer::$Username
257
262
     * @see PHPMailer::$Password
258
263
     */
259
264
    public $SMTPAuth = false;
260
265
 
261
266
    /**
 
267
     * Options array passed to stream_context_create when connecting via SMTP.
 
268
     * @type array
 
269
     */
 
270
    public $SMTPOptions = array();
 
271
 
 
272
    /**
262
273
     * SMTP username.
263
274
     * @type string
264
275
     */
293
304
 
294
305
    /**
295
306
     * The SMTP server timeout in seconds.
296
 
     * @type int
 
307
     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
 
308
     * @type integer
297
309
     */
298
 
    public $Timeout = 10;
 
310
    public $Timeout = 300;
299
311
 
300
312
    /**
301
313
     * SMTP class debug output mode.
302
 
     * Options: 0 = off, 1 = commands, 2 = commands and data
303
 
     * @type int
 
314
     * Debug output level.
 
315
     * Options:
 
316
     * * `0` No output
 
317
     * * `1` Commands
 
318
     * * `2` Data and commands
 
319
     * * `3` As 2 plus connection status
 
320
     * * `4` Low-level data output
 
321
     * @type integer
304
322
     * @see SMTP::$do_debug
305
323
     */
306
324
    public $SMTPDebug = 0;
307
325
 
308
326
    /**
309
 
     * The function/method to use for debugging output.
310
 
     * Options: "echo" or "error_log"
311
 
     * @type string
 
327
     * How to handle debug output.
 
328
     * Options:
 
329
     * * `echo` Output plain-text as-is, appropriate for CLI
 
330
     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
 
331
     * * `error_log` Output to error log as configured in php.ini
 
332
     *
 
333
     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
 
334
     * <code>
 
335
     * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
 
336
     * </code>
 
337
     * @type string|callable
312
338
     * @see SMTP::$Debugoutput
313
339
     */
314
 
    public $Debugoutput = "echo";
 
340
    public $Debugoutput = 'echo';
315
341
 
316
342
    /**
317
343
     * Whether to keep SMTP connection open after each message.
318
344
     * If this is set to true then to close the connection
319
345
     * requires an explicit call to smtpClose().
320
 
     * @type bool
 
346
     * @type boolean
321
347
     */
322
348
    public $SMTPKeepAlive = false;
323
349
 
324
350
    /**
325
351
     * Whether to split multiple to addresses into multiple messages
326
352
     * or send them all in one message.
327
 
     * @type bool
 
353
     * @type boolean
328
354
     */
329
355
    public $SingleTo = false;
330
356
 
331
357
    /**
332
358
     * Storage for addresses when SingleTo is enabled.
333
359
     * @type array
334
 
     * @todo This should really not be public
 
360
     * @TODO This should really not be public
335
361
     */
336
362
    public $SingleToArray = array();
337
363
 
339
365
     * Whether to generate VERP addresses on send.
340
366
     * Only applicable when sending via SMTP.
341
367
     * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
342
 
     * @type bool
 
368
     * @link http://www.postfix.org/VERP_README.html Postfix VERP info
 
369
     * @type boolean
343
370
     */
344
371
    public $do_verp = false;
345
372
 
346
373
    /**
347
374
     * Whether to allow sending messages with an empty body.
348
 
     * @type bool
 
375
     * @type boolean
349
376
     */
350
377
    public $AllowEmpty = false;
351
378
 
396
423
     * The function that handles the result of the send email action.
397
424
     * It is called out by send() for each email sent.
398
425
     *
399
 
     * Value can be:
400
 
     * - 'function_name' for function names
401
 
     * - 'Class::Method' for static method calls
402
 
     * - array($object, 'Method') for calling methods on $object
403
 
     * See http://php.net/is_callable manual page for more details.
 
426
     * Value can be any php callable: http://www.php.net/is_callable
404
427
     *
405
428
     * Parameters:
406
 
     *   bool    $result        result of the send action
 
429
     *   boolean $result        result of the send action
407
430
     *   string  $to            email address of the recipient
408
431
     *   string  $cc            cc email addresses
409
432
     *   string  $bcc           bcc email addresses
410
433
     *   string  $subject       the subject
411
434
     *   string  $body          the email body
412
435
     *   string  $from          email address of sender
413
 
     * 
414
436
     * @type string
415
437
     */
416
438
    public $action_function = '';
417
439
 
418
440
    /**
419
 
     * What to use in the X-Mailer header.
420
 
     * Options: null for default, whitespace for none, or a string to use
 
441
     * What to put in the X-Mailer header.
 
442
     * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
421
443
     * @type string
422
444
     */
423
445
    public $XMailer = '';
459
481
 
460
482
    /**
461
483
     * An array of all kinds of addresses.
462
 
     * Includes all of $to, $cc, $bcc, $replyto
 
484
     * Includes all of $to, $cc, $bcc
463
485
     * @type array
464
486
     * @access protected
465
487
     */
529
551
    protected $sign_key_file = '';
530
552
 
531
553
    /**
 
554
     * The optional S/MIME extra certificates ("CA Chain") file path.
 
555
     * @type string
 
556
     * @access protected
 
557
     */
 
558
    protected $sign_extracerts_file = '';
 
559
 
 
560
    /**
532
561
     * The S/MIME password for the key.
533
562
     * Used only if the key is encrypted.
534
563
     * @type string
538
567
 
539
568
    /**
540
569
     * Whether to throw exceptions for errors.
541
 
     * @type bool
 
570
     * @type boolean
542
571
     * @access protected
543
572
     */
544
573
    protected $exceptions = false;
545
574
 
546
575
    /**
547
 
     * Error severity: message only, continue processing
 
576
     * Unique ID used for message ID and boundaries.
 
577
     * @type string
 
578
     * @access protected
 
579
     */
 
580
    protected $uniqueid = '';
 
581
 
 
582
    /**
 
583
     * Error severity: message only, continue processing.
548
584
     */
549
585
    const STOP_MESSAGE = 0;
550
586
 
551
587
    /**
552
 
     * Error severity: message, likely ok to continue processing
 
588
     * Error severity: message, likely ok to continue processing.
553
589
     */
554
590
    const STOP_CONTINUE = 1;
555
591
 
556
592
    /**
557
 
     * Error severity: message, plus full stop, critical error reached
 
593
     * Error severity: message, plus full stop, critical error reached.
558
594
     */
559
595
    const STOP_CRITICAL = 2;
560
596
 
561
597
    /**
562
 
     * SMTP RFC standard line ending
 
598
     * SMTP RFC standard line ending.
563
599
     */
564
600
    const CRLF = "\r\n";
565
601
 
566
602
    /**
567
 
     * Constructor
568
 
     * @param bool $exceptions Should we throw external exceptions?
 
603
     * The maximum line length allowed by RFC 2822 section 2.1.1
 
604
     * @type integer
 
605
     */
 
606
    const MAX_LINE_LENGTH = 998;
 
607
 
 
608
    /**
 
609
     * Constructor.
 
610
     * @param boolean $exceptions Should we throw external exceptions?
569
611
     */
570
612
    public function __construct($exceptions = false)
571
613
    {
572
 
        $this->exceptions = ($exceptions == true);
 
614
        $this->exceptions = (boolean)$exceptions;
573
615
    }
574
616
 
575
617
    /**
577
619
     */
578
620
    public function __destruct()
579
621
    {
580
 
        if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
 
622
        //Close any open SMTP connection nicely
 
623
        if ($this->Mailer == 'smtp') {
581
624
            $this->smtpClose();
582
625
        }
583
626
    }
593
636
     * @param string $header Additional Header(s)
594
637
     * @param string $params Params
595
638
     * @access private
596
 
     * @return bool
 
639
     * @return boolean
597
640
     */
598
641
    private function mailPassthru($to, $subject, $body, $header, $params)
599
642
    {
 
643
        //Check overloading of mail function to avoid double-encoding
 
644
        if (ini_get('mbstring.func_overload') & 1) {
 
645
            $subject = $this->secureHeader($subject);
 
646
        } else {
 
647
            $subject = $this->encodeHeader($this->secureHeader($subject));
 
648
        }
600
649
        if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
601
 
            $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header);
 
650
            $result = @mail($to, $subject, $body, $header);
602
651
        } else {
603
 
            $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header, $params);
 
652
            $result = @mail($to, $subject, $body, $header, $params);
604
653
        }
605
 
        return $rt;
 
654
        return $result;
606
655
    }
607
656
 
608
657
    /**
609
658
     * Output debugging info via user-defined method.
610
 
     * Only if debug output is enabled.
 
659
     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
611
660
     * @see PHPMailer::$Debugoutput
612
661
     * @see PHPMailer::$SMTPDebug
613
662
     * @param string $str
614
663
     */
615
664
    protected function edebug($str)
616
665
    {
617
 
        if (!$this->SMTPDebug) {
 
666
        if ($this->SMTPDebug <= 0) {
 
667
            return;
 
668
        }
 
669
        //Avoid clash with built-in function names
 
670
        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
 
671
            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
618
672
            return;
619
673
        }
620
674
        switch ($this->Debugoutput) {
621
675
            case 'error_log':
 
676
                //Don't output, just log
622
677
                error_log($str);
623
678
                break;
624
679
            case 'html':
625
 
                //Cleans up output a bit for a better looking display that's HTML-safe
626
 
                echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . "<br>\n";
 
680
                //Cleans up output a bit for a better looking, HTML-safe output
 
681
                echo htmlentities(
 
682
                    preg_replace('/[\r\n]+/', '', $str),
 
683
                    ENT_QUOTES,
 
684
                    'UTF-8'
 
685
                )
 
686
                . "<br>\n";
627
687
                break;
628
688
            case 'echo':
629
689
            default:
630
 
                //Just echoes exactly what was received
631
 
                echo $str;
 
690
                //Normalize line breaks
 
691
                $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
 
692
                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
 
693
                    "\n",
 
694
                    "\n                   \t                  ",
 
695
                    trim($str)
 
696
                ) . "\n";
632
697
        }
633
698
    }
634
699
 
635
700
    /**
636
701
     * Sets message type to HTML or plain.
637
 
     * @param bool $ishtml True for HTML mode.
 
702
     * @param boolean $isHtml True for HTML mode.
638
703
     * @return void
639
704
     */
640
 
    public function isHTML($ishtml = true)
 
705
    public function isHTML($isHtml = true)
641
706
    {
642
 
        if ($ishtml) {
 
707
        if ($isHtml) {
643
708
            $this->ContentType = 'text/html';
644
709
        } else {
645
710
            $this->ContentType = 'text/plain';
670
735
     */
671
736
    public function isSendmail()
672
737
    {
673
 
        if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
674
 
            $this->Sendmail = '/var/qmail/bin/sendmail';
 
738
        $ini_sendmail_path = ini_get('sendmail_path');
 
739
 
 
740
        if (!stristr($ini_sendmail_path, 'sendmail')) {
 
741
            $this->Sendmail = '/usr/sbin/sendmail';
 
742
        } else {
 
743
            $this->Sendmail = $ini_sendmail_path;
675
744
        }
676
745
        $this->Mailer = 'sendmail';
677
746
    }
682
751
     */
683
752
    public function isQmail()
684
753
    {
685
 
        if (stristr(ini_get('sendmail_path'), 'qmail')) {
686
 
            $this->Sendmail = '/var/qmail/bin/sendmail';
 
754
        $ini_sendmail_path = ini_get('sendmail_path');
 
755
 
 
756
        if (!stristr($ini_sendmail_path, 'qmail')) {
 
757
            $this->Sendmail = '/var/qmail/bin/qmail-inject';
 
758
        } else {
 
759
            $this->Sendmail = $ini_sendmail_path;
687
760
        }
688
 
        $this->Mailer = 'sendmail';
 
761
        $this->Mailer = 'qmail';
689
762
    }
690
763
 
691
764
    /**
692
765
     * Add a "To" address.
693
766
     * @param string $address
694
767
     * @param string $name
695
 
     * @return bool true on success, false if address already used
 
768
     * @return boolean true on success, false if address already used
696
769
     */
697
770
    public function addAddress($address, $name = '')
698
771
    {
704
777
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
705
778
     * @param string $address
706
779
     * @param string $name
707
 
     * @return bool true on success, false if address already used
 
780
     * @return boolean true on success, false if address already used
708
781
     */
709
782
    public function addCC($address, $name = '')
710
783
    {
716
789
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
717
790
     * @param string $address
718
791
     * @param string $name
719
 
     * @return bool true on success, false if address already used
 
792
     * @return boolean true on success, false if address already used
720
793
     */
721
794
    public function addBCC($address, $name = '')
722
795
    {
727
800
     * Add a "Reply-to" address.
728
801
     * @param string $address
729
802
     * @param string $name
730
 
     * @return bool
 
803
     * @return boolean
731
804
     */
732
805
    public function addReplyTo($address, $name = '')
733
806
    {
741
814
     * @param string $address The email address to send to
742
815
     * @param string $name
743
816
     * @throws phpmailerException
744
 
     * @return bool true on success, false if address already used or invalid in some way
 
817
     * @return boolean true on success, false if address already used or invalid in some way
745
818
     * @access protected
746
819
     */
747
820
    protected function addAnAddress($kind, $address, $name = '')
748
821
    {
749
822
        if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
750
823
            $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
 
824
            $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
751
825
            if ($this->exceptions) {
752
826
                throw new phpmailerException('Invalid recipient array: ' . $kind);
753
827
            }
754
 
            $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
755
828
            return false;
756
829
        }
757
830
        $address = trim($address);
758
831
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
759
832
        if (!$this->validateAddress($address)) {
760
833
            $this->setError($this->lang('invalid_address') . ': ' . $address);
 
834
            $this->edebug($this->lang('invalid_address') . ': ' . $address);
761
835
            if ($this->exceptions) {
762
836
                throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
763
837
            }
764
 
            $this->edebug($this->lang('invalid_address') . ': ' . $address);
765
838
            return false;
766
839
        }
767
840
        if ($kind != 'Reply-To') {
783
856
     * Set the From and FromName properties.
784
857
     * @param string $address
785
858
     * @param string $name
786
 
     * @param bool $auto Whether to also set the Sender address, defaults to true
 
859
     * @param boolean $auto Whether to also set the Sender address, defaults to true
787
860
     * @throws phpmailerException
788
 
     * @return bool
 
861
     * @return boolean
789
862
     */
790
863
    public function setFrom($address, $name = '', $auto = true)
791
864
    {
793
866
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
794
867
        if (!$this->validateAddress($address)) {
795
868
            $this->setError($this->lang('invalid_address') . ': ' . $address);
 
869
            $this->edebug($this->lang('invalid_address') . ': ' . $address);
796
870
            if ($this->exceptions) {
797
871
                throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
798
872
            }
799
 
            $this->edebug($this->lang('invalid_address') . ': ' . $address);
800
873
            return false;
801
874
        }
802
875
        $this->From = $address;
825
898
     * Check that a string looks like an email address.
826
899
     * @param string $address The email address to check
827
900
     * @param string $patternselect A selector for the validation pattern to use :
828
 
     *   'auto' - pick best one automatically;
829
 
     *   'pcre8' - use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
830
 
     *   'pcre' - use old PCRE implementation;
831
 
     *   'php' - use PHP built-in FILTER_VALIDATE_EMAIL; faster, less thorough;
832
 
     *   'noregex' - super fast, really dumb.
833
 
     * @return bool
 
901
     * * `auto` Pick strictest one automatically;
 
902
     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
 
903
     * * `pcre` Use old PCRE implementation;
 
904
     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
 
905
     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
 
906
     * * `noregex` Don't use a regex: super fast, really dumb.
 
907
     * @return boolean
834
908
     * @static
835
909
     * @access public
836
910
     */
837
911
    public static function validateAddress($address, $patternselect = 'auto')
838
912
    {
839
 
        if ($patternselect == 'auto') {
840
 
            if (defined(
841
 
                'PCRE_VERSION'
842
 
            )
843
 
            ) { //Check this instead of extension_loaded so it works when that function is disabled
844
 
                if (version_compare(PCRE_VERSION, '8.0') >= 0) {
 
913
        if (!$patternselect or $patternselect == 'auto') {
 
914
            //Check this constant first so it works when extension_loaded() is disabled by safe mode
 
915
            //Constant was added in PHP 5.2.4
 
916
            if (defined('PCRE_VERSION')) {
 
917
                //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
 
918
                if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
845
919
                    $patternselect = 'pcre8';
846
920
                } else {
847
921
                    $patternselect = 'pcre';
848
922
                }
 
923
            } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
 
924
                //Fall back to older PCRE
 
925
                $patternselect = 'pcre';
849
926
            } else {
850
927
                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
851
928
                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
858
935
        switch ($patternselect) {
859
936
            case 'pcre8':
860
937
                /**
861
 
                 * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is
862
 
                 * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to
863
 
                 * not allow a@b type valid addresses :(
 
938
                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
864
939
                 * @link http://squiloople.com/2009/12/20/email-address-validation/
865
940
                 * @copyright 2009-2010 Michael Rushton
866
941
                 * Feel free to use and redistribute this code. But please keep this copyright notice.
867
942
                 */
868
 
                return (bool)preg_match(
 
943
                return (boolean)preg_match(
869
944
                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
870
945
                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
871
946
                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
877
952
                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
878
953
                    $address
879
954
                );
880
 
                break;
881
955
            case 'pcre':
882
956
                //An older regex that doesn't need a recent PCRE
883
 
                return (bool)preg_match(
 
957
                return (boolean)preg_match(
884
958
                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
885
959
                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
886
960
                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
893
967
                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
894
968
                    $address
895
969
                );
896
 
                break;
897
 
            case 'php':
898
 
            default:
899
 
                return (bool)filter_var($address, FILTER_VALIDATE_EMAIL);
900
 
                break;
 
970
            case 'html5':
 
971
                /**
 
972
                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
 
973
                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
 
974
                 */
 
975
                return (boolean)preg_match(
 
976
                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
 
977
                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
 
978
                    $address
 
979
                );
901
980
            case 'noregex':
902
981
                //No PCRE! Do something _very_ approximate!
903
982
                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
904
983
                return (strlen($address) >= 3
905
984
                    and strpos($address, '@') >= 1
906
985
                    and strpos($address, '@') != strlen($address) - 1);
907
 
                break;
 
986
            case 'php':
 
987
            default:
 
988
                return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
908
989
        }
909
990
    }
910
991
 
911
992
    /**
912
993
     * Create a message and send it.
913
994
     * Uses the sending method specified by $Mailer.
914
 
     * Returns false on error - Use the ErrorInfo variable to view description of the error.
915
995
     * @throws phpmailerException
916
 
     * @return bool
 
996
     * @return boolean false on error - See the ErrorInfo property for details of the error.
917
997
     */
918
998
    public function send()
919
999
    {
922
1002
                return false;
923
1003
            }
924
1004
            return $this->postSend();
925
 
        } catch (phpmailerException $e) {
 
1005
        } catch (phpmailerException $exc) {
926
1006
            $this->mailHeader = '';
927
 
            $this->setError($e->getMessage());
 
1007
            $this->setError($exc->getMessage());
928
1008
            if ($this->exceptions) {
929
 
                throw $e;
 
1009
                throw $exc;
930
1010
            }
931
1011
            return false;
932
1012
        }
935
1015
    /**
936
1016
     * Prepare a message for sending.
937
1017
     * @throws phpmailerException
938
 
     * @return bool
 
1018
     * @return boolean
939
1019
     */
940
1020
    public function preSend()
941
1021
    {
942
1022
        try {
943
 
            $this->mailHeader = "";
 
1023
            $this->mailHeader = '';
944
1024
            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
945
1025
                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
946
1026
            }
950
1030
                $this->ContentType = 'multipart/alternative';
951
1031
            }
952
1032
 
953
 
            $this->error_count = 0; // reset errors
 
1033
            $this->error_count = 0; // Reset errors
954
1034
            $this->setMessageType();
955
1035
            // Refuse to send an empty message unless we are specifically allowing it
956
1036
            if (!$this->AllowEmpty and empty($this->Body)) {
957
1037
                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
958
1038
            }
959
1039
 
 
1040
            // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
 
1041
            $this->MIMEHeader = '';
 
1042
            $this->MIMEBody = $this->createBody();
 
1043
            // createBody may have added some headers, so retain them
 
1044
            $tempheaders = $this->MIMEHeader;
960
1045
            $this->MIMEHeader = $this->createHeader();
961
 
            $this->MIMEBody = $this->createBody();
 
1046
            $this->MIMEHeader .= $tempheaders;
962
1047
 
963
1048
            // To capture the complete message when using mail(), create
964
1049
            // an extra header list which createHeader() doesn't fold in
965
1050
            if ($this->Mailer == 'mail') {
966
1051
                if (count($this->to) > 0) {
967
 
                    $this->mailHeader .= $this->addrAppend("To", $this->to);
 
1052
                    $this->mailHeader .= $this->addrAppend('To', $this->to);
968
1053
                } else {
969
 
                    $this->mailHeader .= $this->headerLine("To", "undisclosed-recipients:;");
 
1054
                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
970
1055
                }
971
1056
                $this->mailHeader .= $this->headerLine(
972
1057
                    'Subject',
978
1063
            if (!empty($this->DKIM_domain)
979
1064
                && !empty($this->DKIM_private)
980
1065
                && !empty($this->DKIM_selector)
981
 
                && !empty($this->DKIM_domain)
982
1066
                && file_exists($this->DKIM_private)) {
983
1067
                $header_dkim = $this->DKIM_Add(
984
1068
                    $this->MIMEHeader . $this->mailHeader,
989
1073
                    str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
990
1074
            }
991
1075
            return true;
992
 
 
993
 
        } catch (phpmailerException $e) {
994
 
            $this->setError($e->getMessage());
 
1076
        } catch (phpmailerException $exc) {
 
1077
            $this->setError($exc->getMessage());
995
1078
            if ($this->exceptions) {
996
 
                throw $e;
 
1079
                throw $exc;
997
1080
            }
998
1081
            return false;
999
1082
        }
1003
1086
     * Actually send a message.
1004
1087
     * Send the email via the selected mechanism
1005
1088
     * @throws phpmailerException
1006
 
     * @return bool
 
1089
     * @return boolean
1007
1090
     */
1008
1091
    public function postSend()
1009
1092
    {
1011
1094
            // Choose the mailer and send through it
1012
1095
            switch ($this->Mailer) {
1013
1096
                case 'sendmail':
 
1097
                case 'qmail':
1014
1098
                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1015
1099
                case 'smtp':
1016
1100
                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1017
1101
                case 'mail':
1018
1102
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1019
1103
                default:
 
1104
                    $sendMethod = $this->Mailer.'Send';
 
1105
                    if (method_exists($this, $sendMethod)) {
 
1106
                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
 
1107
                    }
 
1108
 
1020
1109
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1021
1110
            }
1022
 
        } catch (phpmailerException $e) {
1023
 
            $this->setError($e->getMessage());
 
1111
        } catch (phpmailerException $exc) {
 
1112
            $this->setError($exc->getMessage());
 
1113
            $this->edebug($exc->getMessage());
1024
1114
            if ($this->exceptions) {
1025
 
                throw $e;
 
1115
                throw $exc;
1026
1116
            }
1027
 
            $this->edebug($e->getMessage() . "\n");
1028
1117
        }
1029
1118
        return false;
1030
1119
    }
1036
1125
     * @see PHPMailer::$Sendmail
1037
1126
     * @throws phpmailerException
1038
1127
     * @access protected
1039
 
     * @return bool
 
1128
     * @return boolean
1040
1129
     */
1041
1130
    protected function sendmailSend($header, $body)
1042
1131
    {
1043
1132
        if ($this->Sender != '') {
1044
 
            $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
 
1133
            if ($this->Mailer == 'qmail') {
 
1134
                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
 
1135
            } else {
 
1136
                $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
 
1137
            }
1045
1138
        } else {
1046
 
            $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
 
1139
            if ($this->Mailer == 'qmail') {
 
1140
                $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
 
1141
            } else {
 
1142
                $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
 
1143
            }
1047
1144
        }
1048
 
        if ($this->SingleTo === true) {
1049
 
            foreach ($this->SingleToArray as $val) {
 
1145
        if ($this->SingleTo) {
 
1146
            foreach ($this->SingleToArray as $toAddr) {
1050
1147
                if (!@$mail = popen($sendmail, 'w')) {
1051
1148
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1052
1149
                }
1053
 
                fputs($mail, "To: " . $val . "\n");
 
1150
                fputs($mail, 'To: ' . $toAddr . "\n");
1054
1151
                fputs($mail, $header);
1055
1152
                fputs($mail, $body);
1056
1153
                $result = pclose($mail);
1057
 
                // implement call back function if it exists
1058
 
                $isSent = ($result == 0) ? 1 : 0;
1059
 
                $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
 
1154
                $this->doCallback(
 
1155
                    ($result == 0),
 
1156
                    array($toAddr),
 
1157
                    $this->cc,
 
1158
                    $this->bcc,
 
1159
                    $this->Subject,
 
1160
                    $body,
 
1161
                    $this->From
 
1162
                );
1060
1163
                if ($result != 0) {
1061
1164
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1062
1165
                }
1068
1171
            fputs($mail, $header);
1069
1172
            fputs($mail, $body);
1070
1173
            $result = pclose($mail);
1071
 
            // implement call back function if it exists
1072
 
            $isSent = ($result == 0) ? 1 : 0;
1073
 
            $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
 
1174
            $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1074
1175
            if ($result != 0) {
1075
1176
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1076
1177
            }
1085
1186
     * @link http://www.php.net/manual/en/book.mail.php
1086
1187
     * @throws phpmailerException
1087
1188
     * @access protected
1088
 
     * @return bool
 
1189
     * @return boolean
1089
1190
     */
1090
1191
    protected function mailSend($header, $body)
1091
1192
    {
1092
1193
        $toArr = array();
1093
 
        foreach ($this->to as $t) {
1094
 
            $toArr[] = $this->addrFormat($t);
 
1194
        foreach ($this->to as $toaddr) {
 
1195
            $toArr[] = $this->addrFormat($toaddr);
1095
1196
        }
1096
1197
        $to = implode(', ', $toArr);
1097
1198
 
1098
1199
        if (empty($this->Sender)) {
1099
 
            $params = " ";
 
1200
            $params = ' ';
1100
1201
        } else {
1101
 
            $params = sprintf("-f%s", $this->Sender);
 
1202
            $params = sprintf('-f%s', $this->Sender);
1102
1203
        }
1103
1204
        if ($this->Sender != '' and !ini_get('safe_mode')) {
1104
1205
            $old_from = ini_get('sendmail_from');
1105
1206
            ini_set('sendmail_from', $this->Sender);
1106
1207
        }
1107
 
        $rt = false;
1108
 
        if ($this->SingleTo === true && count($toArr) > 1) {
1109
 
            foreach ($toArr as $val) {
1110
 
                $rt = $this->mailPassthru($val, $this->Subject, $body, $header, $params);
1111
 
                // implement call back function if it exists
1112
 
                $isSent = ($rt == 1) ? 1 : 0;
1113
 
                $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
 
1208
        $result = false;
 
1209
        if ($this->SingleTo && count($toArr) > 1) {
 
1210
            foreach ($toArr as $toAddr) {
 
1211
                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
 
1212
                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1114
1213
            }
1115
1214
        } else {
1116
 
            $rt = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1117
 
            // implement call back function if it exists
1118
 
            $isSent = ($rt == 1) ? 1 : 0;
1119
 
            $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
 
1215
            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
 
1216
            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1120
1217
        }
1121
1218
        if (isset($old_from)) {
1122
1219
            ini_set('sendmail_from', $old_from);
1123
1220
        }
1124
 
        if (!$rt) {
 
1221
        if (!$result) {
1125
1222
            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1126
1223
        }
1127
1224
        return true;
1135
1232
    public function getSMTPInstance()
1136
1233
    {
1137
1234
        if (!is_object($this->smtp)) {
1138
 
            require_once 'class-smtp.php';
 
1235
                require_once( 'class-smtp.php' );
1139
1236
            $this->smtp = new SMTP;
1140
1237
        }
1141
1238
        return $this->smtp;
1151
1248
     * @throws phpmailerException
1152
1249
     * @uses SMTP
1153
1250
     * @access protected
1154
 
     * @return bool
 
1251
     * @return boolean
1155
1252
     */
1156
1253
    protected function smtpSend($header, $body)
1157
1254
    {
1158
1255
        $bad_rcpt = array();
1159
 
 
1160
 
        if (!$this->smtpConnect()) {
 
1256
        if (!$this->smtpConnect($this->SMTPOptions)) {
1161
1257
            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1162
1258
        }
1163
 
        $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
 
1259
        if ('' == $this->Sender) {
 
1260
            $smtp_from = $this->From;
 
1261
        } else {
 
1262
            $smtp_from = $this->Sender;
 
1263
        }
1164
1264
        if (!$this->smtp->mail($smtp_from)) {
1165
1265
            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1166
1266
            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1167
1267
        }
1168
1268
 
1169
 
        // Attempt to send attach all recipients
1170
 
        foreach ($this->to as $to) {
1171
 
            if (!$this->smtp->recipient($to[0])) {
1172
 
                $bad_rcpt[] = $to[0];
1173
 
                $isSent = 0;
1174
 
            } else {
1175
 
                $isSent = 1;
1176
 
            }
1177
 
            $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From);
1178
 
        }
1179
 
        foreach ($this->cc as $cc) {
1180
 
            if (!$this->smtp->recipient($cc[0])) {
1181
 
                $bad_rcpt[] = $cc[0];
1182
 
                $isSent = 0;
1183
 
            } else {
1184
 
                $isSent = 1;
1185
 
            }
1186
 
            $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From);
1187
 
        }
1188
 
        foreach ($this->bcc as $bcc) {
1189
 
            if (!$this->smtp->recipient($bcc[0])) {
1190
 
                $bad_rcpt[] = $bcc[0];
1191
 
                $isSent = 0;
1192
 
            } else {
1193
 
                $isSent = 1;
1194
 
            }
1195
 
            $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From);
 
1269
        // Attempt to send to all recipients
 
1270
        foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
 
1271
            foreach ($togroup as $to) {
 
1272
                if (!$this->smtp->recipient($to[0])) {
 
1273
                    $error = $this->smtp->getError();
 
1274
                    $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
 
1275
                    $isSent = false;
 
1276
                } else {
 
1277
                    $isSent = true;
 
1278
                }
 
1279
                $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
 
1280
            }
1196
1281
        }
1197
1282
 
1198
 
        if (count($bad_rcpt) > 0) { //Create error message for any bad addresses
1199
 
            throw new phpmailerException($this->lang('recipients_failed') . implode(', ', $bad_rcpt));
1200
 
        }
1201
 
        if (!$this->smtp->data($header . $body)) {
 
1283
        // Only send the DATA command if we have viable recipients
 
1284
        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1202
1285
            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1203
1286
        }
1204
 
        if ($this->SMTPKeepAlive == true) {
 
1287
        if ($this->SMTPKeepAlive) {
1205
1288
            $this->smtp->reset();
1206
1289
        } else {
1207
1290
            $this->smtp->quit();
1208
1291
            $this->smtp->close();
1209
1292
        }
 
1293
        //Create error message for any bad addresses
 
1294
        if (count($bad_rcpt) > 0) {
 
1295
            $errstr = '';
 
1296
            foreach ($bad_rcpt as $bad) {
 
1297
                $errstr .= $bad['to'] . ': ' . $bad['error'];
 
1298
            }
 
1299
            throw new phpmailerException(
 
1300
                $this->lang('recipients_failed') . $errstr,
 
1301
                self::STOP_CONTINUE
 
1302
            );
 
1303
        }
1210
1304
        return true;
1211
1305
    }
1212
1306
 
1217
1311
     * @uses SMTP
1218
1312
     * @access public
1219
1313
     * @throws phpmailerException
1220
 
     * @return bool
 
1314
     * @return boolean
1221
1315
     */
1222
1316
    public function smtpConnect($options = array())
1223
1317
    {
1225
1319
            $this->smtp = $this->getSMTPInstance();
1226
1320
        }
1227
1321
 
1228
 
        //Already connected?
 
1322
        // Already connected?
1229
1323
        if ($this->smtp->connected()) {
1230
1324
            return true;
1231
1325
        }
1234
1328
        $this->smtp->setDebugLevel($this->SMTPDebug);
1235
1329
        $this->smtp->setDebugOutput($this->Debugoutput);
1236
1330
        $this->smtp->setVerp($this->do_verp);
1237
 
        $tls = ($this->SMTPSecure == 'tls');
1238
 
        $ssl = ($this->SMTPSecure == 'ssl');
1239
1331
        $hosts = explode(';', $this->Host);
1240
1332
        $lastexception = null;
1241
1333
 
1242
1334
        foreach ($hosts as $hostentry) {
1243
1335
            $hostinfo = array();
1244
 
            $host = $hostentry;
 
1336
            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
 
1337
                // Not a valid host entry
 
1338
                continue;
 
1339
            }
 
1340
            // $hostinfo[2]: optional ssl or tls prefix
 
1341
            // $hostinfo[3]: the hostname
 
1342
            // $hostinfo[4]: optional port number
 
1343
            // The host string prefix can temporarily override the current setting for SMTPSecure
 
1344
            // If it's not specified, the default value is used
 
1345
            $prefix = '';
 
1346
            $secure = $this->SMTPSecure;
 
1347
            $tls = ($this->SMTPSecure == 'tls');
 
1348
            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
 
1349
                $prefix = 'ssl://';
 
1350
                $tls = false; // Can't have SSL and TLS at the same time
 
1351
                $secure = 'ssl';
 
1352
            } elseif ($hostinfo[2] == 'tls') {
 
1353
                $tls = true;
 
1354
                // tls doesn't use a prefix
 
1355
                $secure = 'tls';
 
1356
            }
 
1357
            //Do we need the OpenSSL extension?
 
1358
            $sslext = defined('OPENSSL_ALGO_SHA1');
 
1359
            if ('tls' === $secure or 'ssl' === $secure) {
 
1360
                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
 
1361
                if (!$sslext) {
 
1362
                    throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
 
1363
                }
 
1364
            }
 
1365
            $host = $hostinfo[3];
1245
1366
            $port = $this->Port;
1246
 
            if (preg_match(
1247
 
                '/^(.+):([0-9]+)$/',
1248
 
                $hostentry,
1249
 
                $hostinfo
1250
 
            )
1251
 
            ) { //If $hostentry contains 'address:port', override default
1252
 
                $host = $hostinfo[1];
1253
 
                $port = $hostinfo[2];
 
1367
            $tport = (integer)$hostinfo[4];
 
1368
            if ($tport > 0 and $tport < 65536) {
 
1369
                $port = $tport;
1254
1370
            }
1255
 
            if ($this->smtp->connect(($ssl ? 'ssl://' : '') . $host, $port, $this->Timeout, $options)) {
 
1371
            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1256
1372
                try {
1257
1373
                    if ($this->Helo) {
1258
1374
                        $hello = $this->Helo;
1260
1376
                        $hello = $this->serverHostname();
1261
1377
                    }
1262
1378
                    $this->smtp->hello($hello);
1263
 
 
 
1379
                    //Automatically enable TLS encryption if:
 
1380
                    // * it's not disabled
 
1381
                    // * we have openssl extension
 
1382
                    // * we are not already using SSL
 
1383
                    // * the server offers STARTTLS
 
1384
                    if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
 
1385
                        $tls = true;
 
1386
                    }
1264
1387
                    if ($tls) {
1265
1388
                        if (!$this->smtp->startTLS()) {
1266
1389
                            throw new phpmailerException($this->lang('connect_host'));
1267
1390
                        }
1268
 
                        //We must resend HELO after tls negotiation
 
1391
                        // We must resend HELO after tls negotiation
1269
1392
                        $this->smtp->hello($hello);
1270
1393
                    }
1271
1394
                    if ($this->SMTPAuth) {
1281
1404
                        }
1282
1405
                    }
1283
1406
                    return true;
1284
 
                } catch (phpmailerException $e) {
1285
 
                    $lastexception = $e;
1286
 
                    //We must have connected, but then failed TLS or Auth, so close connection nicely
 
1407
                } catch (phpmailerException $exc) {
 
1408
                    $lastexception = $exc;
 
1409
                    $this->edebug($exc->getMessage());
 
1410
                    // We must have connected, but then failed TLS or Auth, so close connection nicely
1287
1411
                    $this->smtp->quit();
1288
1412
                }
1289
1413
            }
1290
1414
        }
1291
 
        //If we get here, all connection attempts have failed, so close connection hard
 
1415
        // If we get here, all connection attempts have failed, so close connection hard
1292
1416
        $this->smtp->close();
1293
 
        //As we've caught all exceptions, just report whatever the last one was
 
1417
        // As we've caught all exceptions, just report whatever the last one was
1294
1418
        if ($this->exceptions and !is_null($lastexception)) {
1295
1419
            throw $lastexception;
1296
1420
        }
1317
1441
     * The default language is English.
1318
1442
     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1319
1443
     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1320
 
     * @return bool
 
1444
     * @return boolean
1321
1445
     * @access public
1322
1446
     */
1323
 
    public function setLanguage($langcode = 'en', $lang_path = 'language/')
 
1447
    public function setLanguage($langcode = 'en', $lang_path = '')
1324
1448
    {
1325
 
        //Define full set of translatable strings
 
1449
        // Define full set of translatable strings in English
1326
1450
        $PHPMAILER_LANG = array(
1327
1451
            'authenticate' => 'SMTP Error: Could not authenticate.',
1328
1452
            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1341
1465
            'signing' => 'Signing Error: ',
1342
1466
            'smtp_connect_failed' => 'SMTP connect() failed.',
1343
1467
            'smtp_error' => 'SMTP server error: ',
1344
 
            'variable_set' => 'Cannot set or reset variable: '
 
1468
            'variable_set' => 'Cannot set or reset variable: ',
 
1469
            'extension_missing' => 'Extension missing: '
1345
1470
        );
1346
 
        //Overwrite language-specific strings.
1347
 
        //This way we'll never have missing translations - no more "language string failed to load"!
1348
 
        $l = true;
1349
 
        if ($langcode != 'en') { //There is no English translation file
1350
 
            $l = @include $lang_path . 'phpmailer.lang-' . $langcode . '.php';
 
1471
        if (empty($lang_path)) {
 
1472
            // Calculate an absolute path so it can work if CWD is not here
 
1473
            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
 
1474
        }
 
1475
        $foundlang = true;
 
1476
        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
 
1477
        // There is no English translation file
 
1478
        if ($langcode != 'en') {
 
1479
            // Make sure language file path is readable
 
1480
            if (!is_readable($lang_file)) {
 
1481
                $foundlang = false;
 
1482
            } else {
 
1483
                // Overwrite language-specific strings.
 
1484
                // This way we'll never have missing translation keys.
 
1485
                $foundlang = include $lang_file;
 
1486
            }
1351
1487
        }
1352
1488
        $this->language = $PHPMAILER_LANG;
1353
 
        return ($l == true); //Returns false if language not found
 
1489
        return (boolean)$foundlang; // Returns false if language not found
1354
1490
    }
1355
1491
 
1356
1492
    /**
1375
1511
    public function addrAppend($type, $addr)
1376
1512
    {
1377
1513
        $addresses = array();
1378
 
        foreach ($addr as $a) {
1379
 
            $addresses[] = $this->addrFormat($a);
 
1514
        foreach ($addr as $address) {
 
1515
            $addresses[] = $this->addrFormat($address);
1380
1516
        }
1381
1517
        return $type . ': ' . implode(', ', $addresses) . $this->LE;
1382
1518
    }
1393
1529
        if (empty($addr[1])) { // No name provided
1394
1530
            return $this->secureHeader($addr[0]);
1395
1531
        } else {
1396
 
            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <" . $this->secureHeader(
 
1532
            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1397
1533
                $addr[0]
1398
 
            ) . ">";
 
1534
            ) . '>';
1399
1535
        }
1400
1536
    }
1401
1537
 
1406
1542
     * Original written by philippe.
1407
1543
     * @param string $message The message to wrap
1408
1544
     * @param integer $length The line length to wrap to
1409
 
     * @param bool $qp_mode Whether to run in Quoted-Printable mode
 
1545
     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1410
1546
     * @access public
1411
1547
     * @return string
1412
1548
     */
1413
1549
    public function wrapText($message, $length, $qp_mode = false)
1414
1550
    {
1415
 
        $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
 
1551
        if ($qp_mode) {
 
1552
            $soft_break = sprintf(' =%s', $this->LE);
 
1553
        } else {
 
1554
            $soft_break = $this->LE;
 
1555
        }
1416
1556
        // If utf-8 encoding is used, we will need to make sure we don't
1417
1557
        // split multibyte characters when we wrap
1418
 
        $is_utf8 = (strtolower($this->CharSet) == "utf-8");
 
1558
        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1419
1559
        $lelen = strlen($this->LE);
1420
1560
        $crlflen = strlen(self::CRLF);
1421
1561
 
1422
1562
        $message = $this->fixEOL($message);
 
1563
        //Remove a trailing line break
1423
1564
        if (substr($message, -$lelen) == $this->LE) {
1424
1565
            $message = substr($message, 0, -$lelen);
1425
1566
        }
1426
1567
 
1427
 
        $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
 
1568
        //Split message into lines
 
1569
        $lines = explode($this->LE, $message);
 
1570
        //Message will be rebuilt in here
1428
1571
        $message = '';
1429
 
        for ($i = 0; $i < count($line); $i++) {
1430
 
            $line_part = explode(' ', $line[$i]);
 
1572
        foreach ($lines as $line) {
 
1573
            $words = explode(' ', $line);
1431
1574
            $buf = '';
1432
 
            for ($e = 0; $e < count($line_part); $e++) {
1433
 
                $word = $line_part[$e];
 
1575
            $firstword = true;
 
1576
            foreach ($words as $word) {
1434
1577
                if ($qp_mode and (strlen($word) > $length)) {
1435
1578
                    $space_left = $length - strlen($buf) - $crlflen;
1436
 
                    if ($e != 0) {
 
1579
                    if (!$firstword) {
1437
1580
                        if ($space_left > 20) {
1438
1581
                            $len = $space_left;
1439
1582
                            if ($is_utf8) {
1440
1583
                                $len = $this->utf8CharBoundary($word, $len);
1441
 
                            } elseif (substr($word, $len - 1, 1) == "=") {
 
1584
                            } elseif (substr($word, $len - 1, 1) == '=') {
1442
1585
                                $len--;
1443
 
                            } elseif (substr($word, $len - 2, 1) == "=") {
 
1586
                            } elseif (substr($word, $len - 2, 1) == '=') {
1444
1587
                                $len -= 2;
1445
1588
                            }
1446
1589
                            $part = substr($word, 0, $len);
1447
1590
                            $word = substr($word, $len);
1448
1591
                            $buf .= ' ' . $part;
1449
 
                            $message .= $buf . sprintf("=%s", self::CRLF);
 
1592
                            $message .= $buf . sprintf('=%s', self::CRLF);
1450
1593
                        } else {
1451
1594
                            $message .= $buf . $soft_break;
1452
1595
                        }
1459
1602
                        $len = $length;
1460
1603
                        if ($is_utf8) {
1461
1604
                            $len = $this->utf8CharBoundary($word, $len);
1462
 
                        } elseif (substr($word, $len - 1, 1) == "=") {
 
1605
                        } elseif (substr($word, $len - 1, 1) == '=') {
1463
1606
                            $len--;
1464
 
                        } elseif (substr($word, $len - 2, 1) == "=") {
 
1607
                        } elseif (substr($word, $len - 2, 1) == '=') {
1465
1608
                            $len -= 2;
1466
1609
                        }
1467
1610
                        $part = substr($word, 0, $len);
1468
1611
                        $word = substr($word, $len);
1469
1612
 
1470
1613
                        if (strlen($word) > 0) {
1471
 
                            $message .= $part . sprintf("=%s", self::CRLF);
 
1614
                            $message .= $part . sprintf('=%s', self::CRLF);
1472
1615
                        } else {
1473
1616
                            $buf = $part;
1474
1617
                        }
1475
1618
                    }
1476
1619
                } else {
1477
1620
                    $buf_o = $buf;
1478
 
                    $buf .= ($e == 0) ? $word : (' ' . $word);
 
1621
                    if (!$firstword) {
 
1622
                        $buf .= ' ';
 
1623
                    }
 
1624
                    $buf .= $word;
1479
1625
 
1480
1626
                    if (strlen($buf) > $length and $buf_o != '') {
1481
1627
                        $message .= $buf_o . $soft_break;
1482
1628
                        $buf = $word;
1483
1629
                    }
1484
1630
                }
 
1631
                $firstword = false;
1485
1632
            }
1486
1633
            $message .= $buf . self::CRLF;
1487
1634
        }
1491
1638
 
1492
1639
    /**
1493
1640
     * Find the last character boundary prior to $maxLength in a utf-8
1494
 
     * quoted (printable) encoded string.
 
1641
     * quoted-printable encoded string.
1495
1642
     * Original written by Colin Brown.
1496
1643
     * @access public
1497
1644
     * @param string $encodedText utf-8 QP text
1498
 
     * @param int $maxLength   find last character boundary prior to this length
1499
 
     * @return int
 
1645
     * @param integer $maxLength Find the last character boundary prior to this length
 
1646
     * @return integer
1500
1647
     */
1501
1648
    public function utf8CharBoundary($encodedText, $maxLength)
1502
1649
    {
1504
1651
        $lookBack = 3;
1505
1652
        while (!$foundSplitPos) {
1506
1653
            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1507
 
            $encodedCharPos = strpos($lastChunk, "=");
1508
 
            if ($encodedCharPos !== false) {
 
1654
            $encodedCharPos = strpos($lastChunk, '=');
 
1655
            if (false !== $encodedCharPos) {
1509
1656
                // Found start of encoded character byte within $lookBack block.
1510
1657
                // Check the encoded byte value (the 2 chars after the '=')
1511
1658
                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1512
1659
                $dec = hexdec($hex);
1513
 
                if ($dec < 128) { // Single byte character.
 
1660
                if ($dec < 128) {
 
1661
                    // Single byte character.
1514
1662
                    // If the encoded char was found at pos 0, it will fit
1515
1663
                    // otherwise reduce maxLength to start of the encoded char
1516
 
                    $maxLength = ($encodedCharPos == 0) ? $maxLength :
1517
 
                        $maxLength - ($lookBack - $encodedCharPos);
 
1664
                    if ($encodedCharPos > 0) {
 
1665
                        $maxLength = $maxLength - ($lookBack - $encodedCharPos);
 
1666
                    }
1518
1667
                    $foundSplitPos = true;
1519
 
                } elseif ($dec >= 192) { // First byte of a multi byte character
 
1668
                } elseif ($dec >= 192) {
 
1669
                    // First byte of a multi byte character
1520
1670
                    // Reduce maxLength to split at start of character
1521
1671
                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1522
1672
                    $foundSplitPos = true;
1523
 
                } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
 
1673
                } elseif ($dec < 192) {
 
1674
                    // Middle byte of a multi byte character, look further back
1524
1675
                    $lookBack += 3;
1525
1676
                }
1526
1677
            } else {
1531
1682
        return $maxLength;
1532
1683
    }
1533
1684
 
1534
 
 
1535
1685
    /**
1536
 
     * Set the body wrapping.
 
1686
     * Apply word wrapping to the message body.
 
1687
     * Wraps the message body to the number of chars set in the WordWrap property.
 
1688
     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
 
1689
     * This is called automatically by createBody(), so you don't need to call it yourself.
1537
1690
     * @access public
1538
1691
     * @return void
1539
1692
     */
1565
1718
    {
1566
1719
        $result = '';
1567
1720
 
1568
 
        // Set the boundaries
1569
 
        $uniq_id = md5(uniqid(time()));
1570
 
        $this->boundary[1] = 'b1_' . $uniq_id;
1571
 
        $this->boundary[2] = 'b2_' . $uniq_id;
1572
 
        $this->boundary[3] = 'b3_' . $uniq_id;
1573
 
 
1574
1721
        if ($this->MessageDate == '') {
1575
 
            $result .= $this->headerLine('Date', self::rfcDate());
1576
 
        } else {
1577
 
            $result .= $this->headerLine('Date', $this->MessageDate);
 
1722
            $this->MessageDate = self::rfcDate();
1578
1723
        }
 
1724
        $result .= $this->headerLine('Date', $this->MessageDate);
1579
1725
 
1580
 
        if ($this->ReturnPath) {
1581
 
            $result .= $this->headerLine('Return-Path', '<' . trim($this->ReturnPath) . '>');
1582
 
        } elseif ($this->Sender == '') {
1583
 
            $result .= $this->headerLine('Return-Path', '<' . trim($this->From) . '>');
1584
 
        } else {
1585
 
            $result .= $this->headerLine('Return-Path', '<' . trim($this->Sender) . '>');
1586
 
        }
1587
1726
 
1588
1727
        // To be created automatically by mail()
1589
 
        if ($this->Mailer != 'mail') {
1590
 
            if ($this->SingleTo === true) {
1591
 
                foreach ($this->to as $t) {
1592
 
                    $this->SingleToArray[] = $this->addrFormat($t);
 
1728
        if ($this->SingleTo) {
 
1729
            if ($this->Mailer != 'mail') {
 
1730
                foreach ($this->to as $toaddr) {
 
1731
                    $this->SingleToArray[] = $this->addrFormat($toaddr);
1593
1732
                }
1594
 
            } else {
1595
 
                if (count($this->to) > 0) {
 
1733
            }
 
1734
        } else {
 
1735
            if (count($this->to) > 0) {
 
1736
                if ($this->Mailer != 'mail') {
1596
1737
                    $result .= $this->addrAppend('To', $this->to);
1597
 
                } elseif (count($this->cc) == 0) {
1598
 
                    $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1599
1738
                }
 
1739
            } elseif (count($this->cc) == 0) {
 
1740
                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1600
1741
            }
1601
1742
        }
1602
1743
 
1608
1749
        }
1609
1750
 
1610
1751
        // sendmail and mail() extract Bcc from the header before sending
1611
 
        if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
 
1752
        if ((
 
1753
                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
 
1754
            )
 
1755
            and count($this->bcc) > 0
 
1756
        ) {
1612
1757
            $result .= $this->addrAppend('Bcc', $this->bcc);
1613
1758
        }
1614
1759
 
1624
1769
        if ($this->MessageID != '') {
1625
1770
            $this->lastMessageID = $this->MessageID;
1626
1771
        } else {
1627
 
            $this->lastMessageID = sprintf("<%s@%s>", $uniq_id, $this->ServerHostname());
 
1772
            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->ServerHostname());
1628
1773
        }
1629
 
        $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
 
1774
        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
1630
1775
        $result .= $this->headerLine('X-Priority', $this->Priority);
1631
1776
        if ($this->XMailer == '') {
1632
1777
            $result .= $this->headerLine(
1645
1790
        }
1646
1791
 
1647
1792
        // Add custom headers
1648
 
        for ($index = 0; $index < count($this->CustomHeader); $index++) {
 
1793
        foreach ($this->CustomHeader as $header) {
1649
1794
            $result .= $this->headerLine(
1650
 
                trim($this->CustomHeader[$index][0]),
1651
 
                $this->encodeHeader(trim($this->CustomHeader[$index][1]))
 
1795
                trim($header[0]),
 
1796
                $this->encodeHeader(trim($header[1]))
1652
1797
            );
1653
1798
        }
1654
1799
        if (!$this->sign_key_file) {
1667
1812
    public function getMailMIME()
1668
1813
    {
1669
1814
        $result = '';
 
1815
        $ismultipart = true;
1670
1816
        switch ($this->message_type) {
1671
1817
            case 'inline':
1672
1818
                $result .= $this->headerLine('Content-Type', 'multipart/related;');
1687
1833
            default:
1688
1834
                // Catches case 'plain': and case '':
1689
1835
                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
 
1836
                $ismultipart = false;
1690
1837
                break;
1691
1838
        }
1692
 
        //RFC1341 part 5 says 7bit is assumed if not specified
 
1839
        // RFC1341 part 5 says 7bit is assumed if not specified
1693
1840
        if ($this->Encoding != '7bit') {
1694
 
            $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
 
1841
            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
 
1842
            if ($ismultipart) {
 
1843
                if ($this->Encoding == '8bit') {
 
1844
                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
 
1845
                }
 
1846
                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
 
1847
            } else {
 
1848
                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
 
1849
            }
1695
1850
        }
1696
1851
 
1697
1852
        if ($this->Mailer != 'mail') {
1704
1859
    /**
1705
1860
     * Returns the whole MIME message.
1706
1861
     * Includes complete headers and body.
1707
 
     * Only valid post PreSend().
1708
 
     * @see PHPMailer::PreSend()
 
1862
     * Only valid post preSend().
 
1863
     * @see PHPMailer::preSend()
1709
1864
     * @access public
1710
1865
     * @return string
1711
1866
     */
1714
1869
        return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
1715
1870
    }
1716
1871
 
1717
 
 
1718
1872
    /**
1719
1873
     * Assemble the message body.
1720
1874
     * Returns an empty string on failure.
1725
1879
    public function createBody()
1726
1880
    {
1727
1881
        $body = '';
 
1882
        //Create unique IDs and preset boundaries
 
1883
        $this->uniqueid = md5(uniqid(time()));
 
1884
        $this->boundary[1] = 'b1_' . $this->uniqueid;
 
1885
        $this->boundary[2] = 'b2_' . $this->uniqueid;
 
1886
        $this->boundary[3] = 'b3_' . $this->uniqueid;
1728
1887
 
1729
1888
        if ($this->sign_key_file) {
1730
1889
            $body .= $this->getMailMIME() . $this->LE;
1732
1891
 
1733
1892
        $this->setWordWrap();
1734
1893
 
 
1894
        $bodyEncoding = $this->Encoding;
 
1895
        $bodyCharSet = $this->CharSet;
 
1896
        //Can we do a 7-bit downgrade?
 
1897
        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
 
1898
            $bodyEncoding = '7bit';
 
1899
            $bodyCharSet = 'us-ascii';
 
1900
        }
 
1901
        //If lines are too long, change to quoted-printable transfer encoding
 
1902
        if (self::hasLineLongerThanMax($this->Body)) {
 
1903
            $this->Encoding = 'quoted-printable';
 
1904
            $bodyEncoding = 'quoted-printable';
 
1905
        }
 
1906
 
 
1907
        $altBodyEncoding = $this->Encoding;
 
1908
        $altBodyCharSet = $this->CharSet;
 
1909
        //Can we do a 7-bit downgrade?
 
1910
        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
 
1911
            $altBodyEncoding = '7bit';
 
1912
            $altBodyCharSet = 'us-ascii';
 
1913
        }
 
1914
        //If lines are too long, change to quoted-printable transfer encoding
 
1915
        if (self::hasLineLongerThanMax($this->AltBody)) {
 
1916
            $altBodyEncoding = 'quoted-printable';
 
1917
        }
 
1918
        //Use this as a preamble in all multipart message types
 
1919
        $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
1735
1920
        switch ($this->message_type) {
1736
1921
            case 'inline':
1737
 
                $body .= $this->getBoundary($this->boundary[1], '', '', '');
1738
 
                $body .= $this->encodeString($this->Body, $this->Encoding);
 
1922
                $body .= $mimepre;
 
1923
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
 
1924
                $body .= $this->encodeString($this->Body, $bodyEncoding);
1739
1925
                $body .= $this->LE . $this->LE;
1740
1926
                $body .= $this->attachAll('inline', $this->boundary[1]);
1741
1927
                break;
1742
1928
            case 'attach':
1743
 
                $body .= $this->getBoundary($this->boundary[1], '', '', '');
1744
 
                $body .= $this->encodeString($this->Body, $this->Encoding);
 
1929
                $body .= $mimepre;
 
1930
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
 
1931
                $body .= $this->encodeString($this->Body, $bodyEncoding);
1745
1932
                $body .= $this->LE . $this->LE;
1746
1933
                $body .= $this->attachAll('attachment', $this->boundary[1]);
1747
1934
                break;
1748
1935
            case 'inline_attach':
 
1936
                $body .= $mimepre;
1749
1937
                $body .= $this->textLine('--' . $this->boundary[1]);
1750
1938
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
1751
1939
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1752
1940
                $body .= $this->LE;
1753
 
                $body .= $this->getBoundary($this->boundary[2], '', '', '');
1754
 
                $body .= $this->encodeString($this->Body, $this->Encoding);
 
1941
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
 
1942
                $body .= $this->encodeString($this->Body, $bodyEncoding);
1755
1943
                $body .= $this->LE . $this->LE;
1756
1944
                $body .= $this->attachAll('inline', $this->boundary[2]);
1757
1945
                $body .= $this->LE;
1758
1946
                $body .= $this->attachAll('attachment', $this->boundary[1]);
1759
1947
                break;
1760
1948
            case 'alt':
1761
 
                $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
1762
 
                $body .= $this->encodeString($this->AltBody, $this->Encoding);
 
1949
                $body .= $mimepre;
 
1950
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
 
1951
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1763
1952
                $body .= $this->LE . $this->LE;
1764
 
                $body .= $this->getBoundary($this->boundary[1], '', 'text/html', '');
1765
 
                $body .= $this->encodeString($this->Body, $this->Encoding);
 
1953
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
 
1954
                $body .= $this->encodeString($this->Body, $bodyEncoding);
1766
1955
                $body .= $this->LE . $this->LE;
1767
1956
                if (!empty($this->Ical)) {
1768
1957
                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
1772
1961
                $body .= $this->endBoundary($this->boundary[1]);
1773
1962
                break;
1774
1963
            case 'alt_inline':
1775
 
                $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
1776
 
                $body .= $this->encodeString($this->AltBody, $this->Encoding);
 
1964
                $body .= $mimepre;
 
1965
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
 
1966
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1777
1967
                $body .= $this->LE . $this->LE;
1778
1968
                $body .= $this->textLine('--' . $this->boundary[1]);
1779
1969
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
1780
1970
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1781
1971
                $body .= $this->LE;
1782
 
                $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
1783
 
                $body .= $this->encodeString($this->Body, $this->Encoding);
 
1972
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
 
1973
                $body .= $this->encodeString($this->Body, $bodyEncoding);
1784
1974
                $body .= $this->LE . $this->LE;
1785
1975
                $body .= $this->attachAll('inline', $this->boundary[2]);
1786
1976
                $body .= $this->LE;
1787
1977
                $body .= $this->endBoundary($this->boundary[1]);
1788
1978
                break;
1789
1979
            case 'alt_attach':
 
1980
                $body .= $mimepre;
1790
1981
                $body .= $this->textLine('--' . $this->boundary[1]);
1791
1982
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1792
1983
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1793
1984
                $body .= $this->LE;
1794
 
                $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
1795
 
                $body .= $this->encodeString($this->AltBody, $this->Encoding);
 
1985
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
 
1986
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1796
1987
                $body .= $this->LE . $this->LE;
1797
 
                $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
1798
 
                $body .= $this->encodeString($this->Body, $this->Encoding);
 
1988
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
 
1989
                $body .= $this->encodeString($this->Body, $bodyEncoding);
1799
1990
                $body .= $this->LE . $this->LE;
1800
1991
                $body .= $this->endBoundary($this->boundary[2]);
1801
1992
                $body .= $this->LE;
1802
1993
                $body .= $this->attachAll('attachment', $this->boundary[1]);
1803
1994
                break;
1804
1995
            case 'alt_inline_attach':
 
1996
                $body .= $mimepre;
1805
1997
                $body .= $this->textLine('--' . $this->boundary[1]);
1806
1998
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1807
1999
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1808
2000
                $body .= $this->LE;
1809
 
                $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
1810
 
                $body .= $this->encodeString($this->AltBody, $this->Encoding);
 
2001
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
 
2002
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1811
2003
                $body .= $this->LE . $this->LE;
1812
2004
                $body .= $this->textLine('--' . $this->boundary[2]);
1813
2005
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
1814
2006
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
1815
2007
                $body .= $this->LE;
1816
 
                $body .= $this->getBoundary($this->boundary[3], '', 'text/html', '');
1817
 
                $body .= $this->encodeString($this->Body, $this->Encoding);
 
2008
                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
 
2009
                $body .= $this->encodeString($this->Body, $bodyEncoding);
1818
2010
                $body .= $this->LE . $this->LE;
1819
2011
                $body .= $this->attachAll('inline', $this->boundary[3]);
1820
2012
                $body .= $this->LE;
1824
2016
                break;
1825
2017
            default:
1826
2018
                // catch case 'plain' and case ''
1827
 
                $body .= $this->encodeString($this->Body, $this->Encoding);
 
2019
                $body .= $this->encodeString($this->Body, $bodyEncoding);
1828
2020
                break;
1829
2021
        }
1830
2022
 
1833
2025
        } elseif ($this->sign_key_file) {
1834
2026
            try {
1835
2027
                if (!defined('PKCS7_TEXT')) {
1836
 
                    throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
 
2028
                    throw new phpmailerException($this->lang('extension_missing') . 'openssl');
1837
2029
                }
 
2030
                // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
1838
2031
                $file = tempnam(sys_get_temp_dir(), 'mail');
1839
 
                file_put_contents($file, $body); //TODO check this worked
 
2032
                if (false === file_put_contents($file, $body)) {
 
2033
                    throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
 
2034
                }
1840
2035
                $signed = tempnam(sys_get_temp_dir(), 'signed');
1841
 
                if (@openssl_pkcs7_sign(
1842
 
                    $file,
1843
 
                    $signed,
1844
 
                    'file://' . realpath($this->sign_cert_file),
1845
 
                    array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
1846
 
                    null
1847
 
                )
1848
 
                ) {
 
2036
                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
 
2037
                if (empty($this->sign_extracerts_file)) {
 
2038
                    $sign = @openssl_pkcs7_sign(
 
2039
                        $file,
 
2040
                        $signed,
 
2041
                        'file://' . realpath($this->sign_cert_file),
 
2042
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
 
2043
                        null
 
2044
                    );
 
2045
                } else {
 
2046
                    $sign = @openssl_pkcs7_sign(
 
2047
                        $file,
 
2048
                        $signed,
 
2049
                        'file://' . realpath($this->sign_cert_file),
 
2050
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
 
2051
                        null,
 
2052
                        PKCS7_DETACHED,
 
2053
                        $this->sign_extracerts_file
 
2054
                    );
 
2055
                }
 
2056
                if ($sign) {
1849
2057
                    @unlink($file);
1850
2058
                    $body = file_get_contents($signed);
1851
2059
                    @unlink($signed);
 
2060
                    //The message returned by openssl contains both headers and body, so need to split them up
 
2061
                    $parts = explode("\n\n", $body, 2);
 
2062
                    $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
 
2063
                    $body = $parts[1];
1852
2064
                } else {
1853
2065
                    @unlink($file);
1854
2066
                    @unlink($signed);
1855
2067
                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
1856
2068
                }
1857
 
            } catch (phpmailerException $e) {
 
2069
            } catch (phpmailerException $exc) {
1858
2070
                $body = '';
1859
2071
                if ($this->exceptions) {
1860
 
                    throw $e;
 
2072
                    throw $exc;
1861
2073
                }
1862
2074
            }
1863
2075
        }
1886
2098
            $encoding = $this->Encoding;
1887
2099
        }
1888
2100
        $result .= $this->textLine('--' . $boundary);
1889
 
        $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet);
 
2101
        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
1890
2102
        $result .= $this->LE;
1891
 
        $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
 
2103
        // RFC1341 part 5 says 7bit is assumed if not specified
 
2104
        if ($encoding != '7bit') {
 
2105
            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
 
2106
        }
1892
2107
        $result .= $this->LE;
1893
2108
 
1894
2109
        return $result;
1914
2129
     */
1915
2130
    protected function setMessageType()
1916
2131
    {
1917
 
        $this->message_type = array();
 
2132
        $type = array();
1918
2133
        if ($this->alternativeExists()) {
1919
 
            $this->message_type[] = "alt";
 
2134
            $type[] = 'alt';
1920
2135
        }
1921
2136
        if ($this->inlineImageExists()) {
1922
 
            $this->message_type[] = "inline";
 
2137
            $type[] = 'inline';
1923
2138
        }
1924
2139
        if ($this->attachmentExists()) {
1925
 
            $this->message_type[] = "attach";
 
2140
            $type[] = 'attach';
1926
2141
        }
1927
 
        $this->message_type = implode("_", $this->message_type);
1928
 
        if ($this->message_type == "") {
1929
 
            $this->message_type = "plain";
 
2142
        $this->message_type = implode('_', $type);
 
2143
        if ($this->message_type == '') {
 
2144
            $this->message_type = 'plain';
1930
2145
        }
1931
2146
    }
1932
2147
 
1962
2177
     * @param string $type File extension (MIME) type.
1963
2178
     * @param string $disposition Disposition to use
1964
2179
     * @throws phpmailerException
1965
 
     * @return bool
 
2180
     * @return boolean
1966
2181
     */
1967
2182
    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
1968
2183
    {
1971
2186
                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
1972
2187
            }
1973
2188
 
1974
 
            //If a MIME type is not specified, try to work it out from the file name
 
2189
            // If a MIME type is not specified, try to work it out from the file name
1975
2190
            if ($type == '') {
1976
2191
                $type = self::filenameToType($path);
1977
2192
            }
1992
2207
                7 => 0
1993
2208
            );
1994
2209
 
1995
 
        } catch (phpmailerException $e) {
1996
 
            $this->setError($e->getMessage());
 
2210
        } catch (phpmailerException $exc) {
 
2211
            $this->setError($exc->getMessage());
 
2212
            $this->edebug($exc->getMessage());
1997
2213
            if ($this->exceptions) {
1998
 
                throw $e;
 
2214
                throw $exc;
1999
2215
            }
2000
 
            $this->edebug($e->getMessage() . "\n");
2001
2216
            return false;
2002
2217
        }
2003
2218
        return true;
2056
2271
                }
2057
2272
                $cidUniq[$cid] = true;
2058
2273
 
2059
 
                $mime[] = sprintf("--%s%s", $boundary, $this->LE);
 
2274
                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2060
2275
                $mime[] = sprintf(
2061
 
                    "Content-Type: %s; name=\"%s\"%s",
 
2276
                    'Content-Type: %s; name="%s"%s',
2062
2277
                    $type,
2063
2278
                    $this->encodeHeader($this->secureHeader($name)),
2064
2279
                    $this->LE
2065
2280
                );
2066
 
                $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
 
2281
                // RFC1341 part 5 says 7bit is assumed if not specified
 
2282
                if ($encoding != '7bit') {
 
2283
                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
 
2284
                }
2067
2285
 
2068
2286
                if ($disposition == 'inline') {
2069
 
                    $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
 
2287
                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2070
2288
                }
2071
2289
 
2072
2290
                // If a filename contains any of these chars, it should be quoted,
2074
2292
                // Fixes a warning in IETF's msglint MIME checker
2075
2293
                // Allow for bypassing the Content-Disposition header totally
2076
2294
                if (!(empty($disposition))) {
2077
 
                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
 
2295
                    $encoded_name = $this->encodeHeader($this->secureHeader($name));
 
2296
                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2078
2297
                        $mime[] = sprintf(
2079
 
                            "Content-Disposition: %s; filename=\"%s\"%s",
 
2298
                            'Content-Disposition: %s; filename="%s"%s',
2080
2299
                            $disposition,
2081
 
                            $this->encodeHeader($this->secureHeader($name)),
 
2300
                            $encoded_name,
2082
2301
                            $this->LE . $this->LE
2083
2302
                        );
2084
2303
                    } else {
2085
2304
                        $mime[] = sprintf(
2086
 
                            "Content-Disposition: %s; filename=%s%s",
 
2305
                            'Content-Disposition: %s; filename=%s%s',
2087
2306
                            $disposition,
2088
 
                            $this->encodeHeader($this->secureHeader($name)),
 
2307
                            $encoded_name,
2089
2308
                            $this->LE . $this->LE
2090
2309
                        );
2091
2310
                    }
2110
2329
            }
2111
2330
        }
2112
2331
 
2113
 
        $mime[] = sprintf("--%s--%s", $boundary, $this->LE);
 
2332
        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2114
2333
 
2115
 
        return implode("", $mime);
 
2334
        return implode('', $mime);
2116
2335
    }
2117
2336
 
2118
2337
    /**
2134
2353
            $magic_quotes = get_magic_quotes_runtime();
2135
2354
            if ($magic_quotes) {
2136
2355
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2137
 
                    set_magic_quotes_runtime(0);
 
2356
                    set_magic_quotes_runtime(false);
2138
2357
                } else {
2139
 
                    ini_set('magic_quotes_runtime', 0);
 
2358
                    //Doesn't exist in PHP 5.4, but we don't need to check because
 
2359
                    //get_magic_quotes_runtime always returns false in 5.4+
 
2360
                    //so it will never get here
 
2361
                    ini_set('magic_quotes_runtime', false);
2140
2362
                }
2141
2363
            }
2142
2364
            $file_buffer = file_get_contents($path);
2149
2371
                }
2150
2372
            }
2151
2373
            return $file_buffer;
2152
 
        } catch (Exception $e) {
2153
 
            $this->setError($e->getMessage());
 
2374
        } catch (Exception $exc) {
 
2375
            $this->setError($exc->getMessage());
2154
2376
            return '';
2155
2377
        }
2156
2378
    }
2173
2395
            case '7bit':
2174
2396
            case '8bit':
2175
2397
                $encoded = $this->fixEOL($str);
2176
 
                //Make sure it ends with a line break
 
2398
                // Make sure it ends with a line break
2177
2399
                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2178
2400
                    $encoded .= $this->LE;
2179
2401
                }
2201
2423
     */
2202
2424
    public function encodeHeader($str, $position = 'text')
2203
2425
    {
2204
 
        $x = 0;
 
2426
        $matchcount = 0;
2205
2427
        switch (strtolower($position)) {
2206
2428
            case 'phrase':
2207
2429
                if (!preg_match('/[\200-\377]/', $str)) {
2208
 
                    // Can't use addslashes as we don't know what value has magic_quotes_sybase
 
2430
                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
2209
2431
                    $encoded = addcslashes($str, "\0..\37\177\\\"");
2210
2432
                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2211
2433
                        return ($encoded);
2213
2435
                        return ("\"$encoded\"");
2214
2436
                    }
2215
2437
                }
2216
 
                $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
 
2438
                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2217
2439
                break;
2218
2440
            /** @noinspection PhpMissingBreakStatementInspection */
2219
2441
            case 'comment':
2220
 
                $x = preg_match_all('/[()"]/', $str, $matches);
 
2442
                $matchcount = preg_match_all('/[()"]/', $str, $matches);
2221
2443
                // Intentional fall-through
2222
2444
            case 'text':
2223
2445
            default:
2224
 
                $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
 
2446
                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2225
2447
                break;
2226
2448
        }
2227
2449
 
2228
 
        if ($x == 0) { //There are no chars that need encoding
 
2450
        //There are no chars that need encoding
 
2451
        if ($matchcount == 0) {
2229
2452
            return ($str);
2230
2453
        }
2231
2454
 
2232
2455
        $maxlen = 75 - 7 - strlen($this->CharSet);
2233
2456
        // Try to select the encoding which should produce the shortest output
2234
 
        if ($x > strlen($str) / 3) {
2235
 
            //More than a third of the content will need encoding, so B encoding will be most efficient
 
2457
        if ($matchcount > strlen($str) / 3) {
 
2458
            // More than a third of the content will need encoding, so B encoding will be most efficient
2236
2459
            $encoding = 'B';
2237
2460
            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2238
2461
                // Use a custom function which correctly encodes and wraps long
2250
2473
            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2251
2474
        }
2252
2475
 
2253
 
        $encoded = preg_replace('/^(.*)$/m', " =?" . $this->CharSet . "?$encoding?\\1?=", $encoded);
 
2476
        $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2254
2477
        $encoded = trim(str_replace("\n", $this->LE, $encoded));
2255
2478
 
2256
2479
        return $encoded;
2260
2483
     * Check if a string contains multi-byte characters.
2261
2484
     * @access public
2262
2485
     * @param string $str multi-byte text to wrap encode
2263
 
     * @return bool
 
2486
     * @return boolean
2264
2487
     */
2265
2488
    public function hasMultiBytes($str)
2266
2489
    {
2272
2495
    }
2273
2496
 
2274
2497
    /**
 
2498
     * Does a string contain any 8-bit chars (in any charset)?
 
2499
     * @param string $text
 
2500
     * @return boolean
 
2501
     */
 
2502
    public function has8bitChars($text)
 
2503
    {
 
2504
        return (boolean)preg_match('/[\x80-\xFF]/', $text);
 
2505
    }
 
2506
 
 
2507
    /**
2275
2508
     * Encode and wrap long multibyte strings for mail headers
2276
2509
     * without breaking lines within a character.
2277
 
     * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
 
2510
     * Adapted from a function by paravoid
 
2511
     * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2278
2512
     * @access public
2279
2513
     * @param string $str multi-byte text to wrap encode
2280
 
     * @param string $lf string to use as linefeed/end-of-line
 
2514
     * @param string $linebreak string to use as linefeed/end-of-line
2281
2515
     * @return string
2282
2516
     */
2283
 
    public function base64EncodeWrapMB($str, $lf = null)
 
2517
    public function base64EncodeWrapMB($str, $linebreak = null)
2284
2518
    {
2285
 
        $start = "=?" . $this->CharSet . "?B?";
2286
 
        $end = "?=";
2287
 
        $encoded = "";
2288
 
        if ($lf === null) {
2289
 
            $lf = $this->LE;
 
2519
        $start = '=?' . $this->CharSet . '?B?';
 
2520
        $end = '?=';
 
2521
        $encoded = '';
 
2522
        if ($linebreak === null) {
 
2523
            $linebreak = $this->LE;
2290
2524
        }
2291
2525
 
2292
2526
        $mb_length = mb_strlen($str, $this->CharSet);
2305
2539
                $chunk = base64_encode($chunk);
2306
2540
                $lookBack++;
2307
2541
            } while (strlen($chunk) > $length);
2308
 
            $encoded .= $chunk . $lf;
 
2542
            $encoded .= $chunk . $linebreak;
2309
2543
        }
2310
2544
 
2311
2545
        // Chomp the last linefeed
2312
 
        $encoded = substr($encoded, 0, -strlen($lf));
 
2546
        $encoded = substr($encoded, 0, -strlen($linebreak));
2313
2547
        return $encoded;
2314
2548
    }
2315
2549
 
2320
2554
     * @param string $string The text to encode
2321
2555
     * @param integer $line_max Number of chars allowed on a line before wrapping
2322
2556
     * @return string
2323
 
     * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417
 
2557
     * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2324
2558
     */
2325
2559
    public function encodeQP($string, $line_max = 76)
2326
2560
    {
2327
 
        if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
2328
 
            return quoted_printable_encode($string);
 
2561
        // Use native function if it's available (>= PHP5.3)
 
2562
        if (function_exists('quoted_printable_encode')) {
 
2563
            return $this->fixEOL(quoted_printable_encode($string));
2329
2564
        }
2330
 
        //Fall back to a pure PHP implementation
 
2565
        // Fall back to a pure PHP implementation
2331
2566
        $string = str_replace(
2332
2567
            array('%20', '%0D%0A.', '%0D%0A', '%'),
2333
2568
            array(' ', "\r\n=2E", "\r\n", '='),
2334
2569
            rawurlencode($string)
2335
2570
        );
2336
2571
        $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2337
 
        return $string;
 
2572
        return $this->fixEOL($string);
2338
2573
    }
2339
2574
 
2340
2575
    /**
2343
2578
     * @access public
2344
2579
     * @param string $string
2345
2580
     * @param integer $line_max
2346
 
     * @param bool $space_conv
 
2581
     * @param boolean $space_conv
2347
2582
     * @return string
2348
2583
     * @deprecated Use encodeQP instead.
2349
2584
     */
2365
2600
     */
2366
2601
    public function encodeQ($str, $position = 'text')
2367
2602
    {
2368
 
        //There should not be any EOL in the string
 
2603
        // There should not be any EOL in the string
2369
2604
        $pattern = '';
2370
2605
        $encoded = str_replace(array("\r", "\n"), '', $str);
2371
2606
        switch (strtolower($position)) {
2372
2607
            case 'phrase':
2373
 
                //RFC 2047 section 5.3
 
2608
                // RFC 2047 section 5.3
2374
2609
                $pattern = '^A-Za-z0-9!*+\/ -';
2375
2610
                break;
2376
2611
            /** @noinspection PhpMissingBreakStatementInspection */
2377
2612
            case 'comment':
2378
 
                //RFC 2047 section 5.2
 
2613
                // RFC 2047 section 5.2
2379
2614
                $pattern = '\(\)"';
2380
 
                //intentional fall-through
2381
 
                //for this reason we build the $pattern without including delimiters and []
 
2615
                // intentional fall-through
 
2616
                // for this reason we build the $pattern without including delimiters and []
2382
2617
            case 'text':
2383
2618
            default:
2384
 
                //RFC 2047 section 5.1
2385
 
                //Replace every high ascii, control, =, ? and _ characters
 
2619
                // RFC 2047 section 5.1
 
2620
                // Replace every high ascii, control, =, ? and _ characters
2386
2621
                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2387
2622
                break;
2388
2623
        }
2389
2624
        $matches = array();
2390
2625
        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2391
 
            //If the string contains an '=', make sure it's the first thing we replace
2392
 
            //so as to avoid double-encoding
2393
 
            $s = array_search('=', $matches[0]);
2394
 
            if ($s !== false) {
2395
 
                unset($matches[0][$s]);
 
2626
            // If the string contains an '=', make sure it's the first thing we replace
 
2627
            // so as to avoid double-encoding
 
2628
            $eqkey = array_search('=', $matches[0]);
 
2629
            if (false !== $eqkey) {
 
2630
                unset($matches[0][$eqkey]);
2396
2631
                array_unshift($matches[0], '=');
2397
2632
            }
2398
2633
            foreach (array_unique($matches[0]) as $char) {
2399
2634
                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2400
2635
            }
2401
2636
        }
2402
 
        //Replace every spaces to _ (more readable than =20)
 
2637
        // Replace every spaces to _ (more readable than =20)
2403
2638
        return str_replace(' ', '_', $encoded);
2404
2639
    }
2405
2640
 
2422
2657
        $type = '',
2423
2658
        $disposition = 'attachment'
2424
2659
    ) {
2425
 
        //If a MIME type is not specified, try to work it out from the file name
 
2660
        // If a MIME type is not specified, try to work it out from the file name
2426
2661
        if ($type == '') {
2427
2662
            $type = self::filenameToType($filename);
2428
2663
        }
2442
2677
    /**
2443
2678
     * Add an embedded (inline) attachment from a file.
2444
2679
     * This can include images, sounds, and just about any other document type.
2445
 
     * These differ from 'regular' attachmants in that they are intended to be
 
2680
     * These differ from 'regular' attachments in that they are intended to be
2446
2681
     * displayed inline with the message, not just attached for download.
2447
2682
     * This is used in HTML messages that embed the images
2448
2683
     * the HTML refers to using the $cid value.
2453
2688
     * @param string $encoding File encoding (see $Encoding).
2454
2689
     * @param string $type File MIME type.
2455
2690
     * @param string $disposition Disposition to use
2456
 
     * @return bool True on successfully adding an attachment
 
2691
     * @return boolean True on successfully adding an attachment
2457
2692
     */
2458
2693
    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2459
2694
    {
2462
2697
            return false;
2463
2698
        }
2464
2699
 
2465
 
        //If a MIME type is not specified, try to work it out from the file name
 
2700
        // If a MIME type is not specified, try to work it out from the file name
2466
2701
        if ($type == '') {
2467
2702
            $type = self::filenameToType($path);
2468
2703
        }
2498
2733
     * @param string $encoding File encoding (see $Encoding).
2499
2734
     * @param string $type MIME type.
2500
2735
     * @param string $disposition Disposition to use
2501
 
     * @return bool True on successfully adding an attachment
 
2736
     * @return boolean True on successfully adding an attachment
2502
2737
     */
2503
2738
    public function addStringEmbeddedImage(
2504
2739
        $string,
2508
2743
        $type = '',
2509
2744
        $disposition = 'inline'
2510
2745
    ) {
2511
 
        //If a MIME type is not specified, try to work it out from the name
 
2746
        // If a MIME type is not specified, try to work it out from the name
2512
2747
        if ($type == '') {
2513
2748
            $type = self::filenameToType($name);
2514
2749
        }
2530
2765
    /**
2531
2766
     * Check if an inline attachment is present.
2532
2767
     * @access public
2533
 
     * @return bool
 
2768
     * @return boolean
2534
2769
     */
2535
2770
    public function inlineImageExists()
2536
2771
    {
2544
2779
 
2545
2780
    /**
2546
2781
     * Check if an attachment (non-inline) is present.
2547
 
     * @return bool
 
2782
     * @return boolean
2548
2783
     */
2549
2784
    public function attachmentExists()
2550
2785
    {
2558
2793
 
2559
2794
    /**
2560
2795
     * Check if this message has an alternative body set.
2561
 
     * @return bool
 
2796
     * @return boolean
2562
2797
     */
2563
2798
    public function alternativeExists()
2564
2799
    {
2651
2886
        $this->error_count++;
2652
2887
        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2653
2888
            $lasterror = $this->smtp->getError();
2654
 
            if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
2655
 
                $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
 
2889
            if (!empty($lasterror['error'])) {
 
2890
                $msg .= $this->lang('smtp_error') . $lasterror['error'];
 
2891
                if (!empty($lasterror['detail'])) {
 
2892
                    $msg .= ' Detail: '. $lasterror['detail'];
 
2893
                }
 
2894
                if (!empty($lasterror['smtp_code'])) {
 
2895
                    $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
 
2896
                }
 
2897
                if (!empty($lasterror['smtp_code_ex'])) {
 
2898
                    $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
 
2899
                }
2656
2900
            }
2657
2901
        }
2658
2902
        $this->ErrorInfo = $msg;
2666
2910
     */
2667
2911
    public static function rfcDate()
2668
2912
    {
2669
 
        //Set the time zone to whatever the default is to avoid 500 errors
2670
 
        //Will default to UTC if it's not set properly in php.ini
 
2913
        // Set the time zone to whatever the default is to avoid 500 errors
 
2914
        // Will default to UTC if it's not set properly in php.ini
2671
2915
        date_default_timezone_set(@date_default_timezone_get());
2672
2916
        return date('D, j M Y H:i:s O');
2673
2917
    }
2680
2924
     */
2681
2925
    protected function serverHostname()
2682
2926
    {
 
2927
        $result = 'localhost.localdomain';
2683
2928
        if (!empty($this->Hostname)) {
2684
2929
            $result = $this->Hostname;
2685
 
        } elseif (isset($_SERVER['SERVER_NAME'])) {
 
2930
        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
2686
2931
            $result = $_SERVER['SERVER_NAME'];
2687
 
        } else {
2688
 
            $result = 'localhost.localdomain';
 
2932
        } elseif (function_exists('gethostname') && gethostname() !== false) {
 
2933
            $result = gethostname();
 
2934
        } elseif (php_uname('n') !== false) {
 
2935
            $result = php_uname('n');
2689
2936
        }
2690
 
 
2691
2937
        return $result;
2692
2938
    }
2693
2939
 
2703
2949
            $this->setLanguage('en'); // set the default language
2704
2950
        }
2705
2951
 
2706
 
        if (isset($this->language[$key])) {
 
2952
        if (array_key_exists($key, $this->language)) {
 
2953
            if ($key == 'smtp_connect_failed') {
 
2954
                //Include a link to troubleshooting docs on SMTP connection failure
 
2955
                //this is by far the biggest cause of support questions
 
2956
                //but it's usually not PHPMailer's fault.
 
2957
                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
 
2958
            }
2707
2959
            return $this->language[$key];
2708
2960
        } else {
2709
 
            return 'Language string failed to load: ' . $key;
 
2961
            //Return the key as a fallback
 
2962
            return $key;
2710
2963
        }
2711
2964
    }
2712
2965
 
2713
2966
    /**
2714
2967
     * Check if an error occurred.
2715
2968
     * @access public
2716
 
     * @return bool True if an error did occur.
 
2969
     * @return boolean True if an error did occur.
2717
2970
     */
2718
2971
    public function isError()
2719
2972
    {
2758
3011
    }
2759
3012
 
2760
3013
    /**
 
3014
     * Returns all custom headers
 
3015
     *
 
3016
     * @return array
 
3017
     */
 
3018
    public function getCustomHeaders()
 
3019
    {
 
3020
        return $this->CustomHeader;
 
3021
    }
 
3022
 
 
3023
    /**
2761
3024
     * Create a message from an HTML string.
2762
3025
     * Automatically makes modifications for inline images and backgrounds
2763
3026
     * and creates a plain-text version by converting the HTML.
2765
3028
     * @access public
2766
3029
     * @param string $message HTML message string
2767
3030
     * @param string $basedir baseline directory for path
2768
 
     * @param bool $advanced Whether to use the advanced HTML to text converter
 
3031
     * @param boolean|callable $advanced Whether to use the internal HTML to text converter
 
3032
     *    or your own custom converter @see html2text()
2769
3033
     * @return string $message
2770
3034
     */
2771
3035
    public function msgHTML($message, $basedir = '', $advanced = false)
2772
3036
    {
2773
 
        preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);
 
3037
        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
2774
3038
        if (isset($images[2])) {
2775
 
            foreach ($images[2] as $i => $url) {
2776
 
                // do not change urls for absolute images (thanks to corvuscorax)
2777
 
                if (!preg_match('#^[A-z]+://#', $url)) {
 
3039
            foreach ($images[2] as $imgindex => $url) {
 
3040
                // Convert data URIs into embedded images
 
3041
                if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
 
3042
                    $data = substr($url, strpos($url, ','));
 
3043
                    if ($match[2]) {
 
3044
                        $data = base64_decode($data);
 
3045
                    } else {
 
3046
                        $data = rawurldecode($data);
 
3047
                    }
 
3048
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
 
3049
                    if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) {
 
3050
                        $message = str_replace(
 
3051
                            $images[0][$imgindex],
 
3052
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
 
3053
                            $message
 
3054
                        );
 
3055
                    }
 
3056
                } elseif (!preg_match('#^[A-z]+://#', $url)) {
 
3057
                    // Do not change urls for absolute images (thanks to corvuscorax)
2778
3058
                    $filename = basename($url);
2779
3059
                    $directory = dirname($url);
2780
3060
                    if ($directory == '.') {
2781
3061
                        $directory = '';
2782
3062
                    }
2783
 
                    $cid = md5($url) . '@phpmailer.0'; //RFC2392 S 2
 
3063
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
2784
3064
                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
2785
3065
                        $basedir .= '/';
2786
3066
                    }
2792
3072
                        $cid,
2793
3073
                        $filename,
2794
3074
                        'base64',
2795
 
                        self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION))
 
3075
                        self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
2796
3076
                    )
2797
3077
                    ) {
2798
3078
                        $message = preg_replace(
2799
 
                            "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui",
2800
 
                            $images[1][$i] . "=\"cid:" . $cid . "\"",
 
3079
                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
 
3080
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
2801
3081
                            $message
2802
3082
                        );
2803
3083
                    }
2805
3085
            }
2806
3086
        }
2807
3087
        $this->isHTML(true);
2808
 
        if (empty($this->AltBody)) {
2809
 
            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
2810
 
        }
2811
 
        //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
 
3088
        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
2812
3089
        $this->Body = $this->normalizeBreaks($message);
2813
3090
        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
 
3091
        if (empty($this->AltBody)) {
 
3092
            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
 
3093
                self::CRLF . self::CRLF;
 
3094
        }
2814
3095
        return $this->Body;
2815
3096
    }
2816
3097
 
2817
3098
    /**
2818
3099
     * Convert an HTML string into plain text.
 
3100
     * This is used by msgHTML().
 
3101
     * Note - older versions of this function used a bundled advanced converter
 
3102
     * which was been removed for license reasons in #232
 
3103
     * Example usage:
 
3104
     * <code>
 
3105
     * // Use default conversion
 
3106
     * $plain = $mail->html2text($html);
 
3107
     * // Use your own custom converter
 
3108
     * $plain = $mail->html2text($html, function($html) {
 
3109
     *     $converter = new MyHtml2text($html);
 
3110
     *     return $converter->get_text();
 
3111
     * });
 
3112
     * </code>
2819
3113
     * @param string $html The HTML text to convert
2820
 
     * @param bool $advanced Should this use the more complex html2text converter or just a simple one?
 
3114
     * @param boolean|callable $advanced Any boolean value to use the internal converter,
 
3115
     *   or provide your own callable for custom conversion.
2821
3116
     * @return string
2822
3117
     */
2823
3118
    public function html2text($html, $advanced = false)
2824
3119
    {
2825
 
        if ($advanced) {
2826
 
            require_once 'extras/class.html2text.php';
2827
 
            $h = new html2text($html);
2828
 
            return $h->get_text();
 
3120
        if (is_callable($advanced)) {
 
3121
            return call_user_func($advanced, $html);
2829
3122
        }
2830
3123
        return html_entity_decode(
2831
3124
            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
2844
3137
    public static function _mime_types($ext = '')
2845
3138
    {
2846
3139
        $mimes = array(
2847
 
            'xl' => 'application/excel',
2848
 
            'hqx' => 'application/mac-binhex40',
2849
 
            'cpt' => 'application/mac-compactpro',
2850
 
            'bin' => 'application/macbinary',
2851
 
            'doc' => 'application/msword',
2852
 
            'word' => 'application/msword',
 
3140
            'xl'    => 'application/excel',
 
3141
            'js'    => 'application/javascript',
 
3142
            'hqx'   => 'application/mac-binhex40',
 
3143
            'cpt'   => 'application/mac-compactpro',
 
3144
            'bin'   => 'application/macbinary',
 
3145
            'doc'   => 'application/msword',
 
3146
            'word'  => 'application/msword',
2853
3147
            'class' => 'application/octet-stream',
2854
 
            'dll' => 'application/octet-stream',
2855
 
            'dms' => 'application/octet-stream',
2856
 
            'exe' => 'application/octet-stream',
2857
 
            'lha' => 'application/octet-stream',
2858
 
            'lzh' => 'application/octet-stream',
2859
 
            'psd' => 'application/octet-stream',
2860
 
            'sea' => 'application/octet-stream',
2861
 
            'so' => 'application/octet-stream',
2862
 
            'oda' => 'application/oda',
2863
 
            'pdf' => 'application/pdf',
2864
 
            'ai' => 'application/postscript',
2865
 
            'eps' => 'application/postscript',
2866
 
            'ps' => 'application/postscript',
2867
 
            'smi' => 'application/smil',
2868
 
            'smil' => 'application/smil',
2869
 
            'mif' => 'application/vnd.mif',
2870
 
            'xls' => 'application/vnd.ms-excel',
2871
 
            'ppt' => 'application/vnd.ms-powerpoint',
 
3148
            'dll'   => 'application/octet-stream',
 
3149
            'dms'   => 'application/octet-stream',
 
3150
            'exe'   => 'application/octet-stream',
 
3151
            'lha'   => 'application/octet-stream',
 
3152
            'lzh'   => 'application/octet-stream',
 
3153
            'psd'   => 'application/octet-stream',
 
3154
            'sea'   => 'application/octet-stream',
 
3155
            'so'    => 'application/octet-stream',
 
3156
            'oda'   => 'application/oda',
 
3157
            'pdf'   => 'application/pdf',
 
3158
            'ai'    => 'application/postscript',
 
3159
            'eps'   => 'application/postscript',
 
3160
            'ps'    => 'application/postscript',
 
3161
            'smi'   => 'application/smil',
 
3162
            'smil'  => 'application/smil',
 
3163
            'mif'   => 'application/vnd.mif',
 
3164
            'xls'   => 'application/vnd.ms-excel',
 
3165
            'ppt'   => 'application/vnd.ms-powerpoint',
2872
3166
            'wbxml' => 'application/vnd.wap.wbxml',
2873
 
            'wmlc' => 'application/vnd.wap.wmlc',
2874
 
            'dcr' => 'application/x-director',
2875
 
            'dir' => 'application/x-director',
2876
 
            'dxr' => 'application/x-director',
2877
 
            'dvi' => 'application/x-dvi',
2878
 
            'gtar' => 'application/x-gtar',
2879
 
            'php3' => 'application/x-httpd-php',
2880
 
            'php4' => 'application/x-httpd-php',
2881
 
            'php' => 'application/x-httpd-php',
 
3167
            'wmlc'  => 'application/vnd.wap.wmlc',
 
3168
            'dcr'   => 'application/x-director',
 
3169
            'dir'   => 'application/x-director',
 
3170
            'dxr'   => 'application/x-director',
 
3171
            'dvi'   => 'application/x-dvi',
 
3172
            'gtar'  => 'application/x-gtar',
 
3173
            'php3'  => 'application/x-httpd-php',
 
3174
            'php4'  => 'application/x-httpd-php',
 
3175
            'php'   => 'application/x-httpd-php',
2882
3176
            'phtml' => 'application/x-httpd-php',
2883
 
            'phps' => 'application/x-httpd-php-source',
2884
 
            'js' => 'application/x-javascript',
2885
 
            'swf' => 'application/x-shockwave-flash',
2886
 
            'sit' => 'application/x-stuffit',
2887
 
            'tar' => 'application/x-tar',
2888
 
            'tgz' => 'application/x-tar',
2889
 
            'xht' => 'application/xhtml+xml',
 
3177
            'phps'  => 'application/x-httpd-php-source',
 
3178
            'swf'   => 'application/x-shockwave-flash',
 
3179
            'sit'   => 'application/x-stuffit',
 
3180
            'tar'   => 'application/x-tar',
 
3181
            'tgz'   => 'application/x-tar',
 
3182
            'xht'   => 'application/xhtml+xml',
2890
3183
            'xhtml' => 'application/xhtml+xml',
2891
 
            'zip' => 'application/zip',
2892
 
            'mid' => 'audio/midi',
2893
 
            'midi' => 'audio/midi',
2894
 
            'mp2' => 'audio/mpeg',
2895
 
            'mp3' => 'audio/mpeg',
2896
 
            'mpga' => 'audio/mpeg',
2897
 
            'aif' => 'audio/x-aiff',
2898
 
            'aifc' => 'audio/x-aiff',
2899
 
            'aiff' => 'audio/x-aiff',
2900
 
            'ram' => 'audio/x-pn-realaudio',
2901
 
            'rm' => 'audio/x-pn-realaudio',
2902
 
            'rpm' => 'audio/x-pn-realaudio-plugin',
2903
 
            'ra' => 'audio/x-realaudio',
2904
 
            'wav' => 'audio/x-wav',
2905
 
            'bmp' => 'image/bmp',
2906
 
            'gif' => 'image/gif',
2907
 
            'jpeg' => 'image/jpeg',
2908
 
            'jpe' => 'image/jpeg',
2909
 
            'jpg' => 'image/jpeg',
2910
 
            'png' => 'image/png',
2911
 
            'tiff' => 'image/tiff',
2912
 
            'tif' => 'image/tiff',
2913
 
            'eml' => 'message/rfc822',
2914
 
            'css' => 'text/css',
2915
 
            'html' => 'text/html',
2916
 
            'htm' => 'text/html',
 
3184
            'zip'   => 'application/zip',
 
3185
            'mid'   => 'audio/midi',
 
3186
            'midi'  => 'audio/midi',
 
3187
            'mp2'   => 'audio/mpeg',
 
3188
            'mp3'   => 'audio/mpeg',
 
3189
            'mpga'  => 'audio/mpeg',
 
3190
            'aif'   => 'audio/x-aiff',
 
3191
            'aifc'  => 'audio/x-aiff',
 
3192
            'aiff'  => 'audio/x-aiff',
 
3193
            'ram'   => 'audio/x-pn-realaudio',
 
3194
            'rm'    => 'audio/x-pn-realaudio',
 
3195
            'rpm'   => 'audio/x-pn-realaudio-plugin',
 
3196
            'ra'    => 'audio/x-realaudio',
 
3197
            'wav'   => 'audio/x-wav',
 
3198
            'bmp'   => 'image/bmp',
 
3199
            'gif'   => 'image/gif',
 
3200
            'jpeg'  => 'image/jpeg',
 
3201
            'jpe'   => 'image/jpeg',
 
3202
            'jpg'   => 'image/jpeg',
 
3203
            'png'   => 'image/png',
 
3204
            'tiff'  => 'image/tiff',
 
3205
            'tif'   => 'image/tiff',
 
3206
            'eml'   => 'message/rfc822',
 
3207
            'css'   => 'text/css',
 
3208
            'html'  => 'text/html',
 
3209
            'htm'   => 'text/html',
2917
3210
            'shtml' => 'text/html',
2918
 
            'log' => 'text/plain',
2919
 
            'text' => 'text/plain',
2920
 
            'txt' => 'text/plain',
2921
 
            'rtx' => 'text/richtext',
2922
 
            'rtf' => 'text/rtf',
2923
 
            'xml' => 'text/xml',
2924
 
            'xsl' => 'text/xml',
2925
 
            'mpeg' => 'video/mpeg',
2926
 
            'mpe' => 'video/mpeg',
2927
 
            'mpg' => 'video/mpeg',
2928
 
            'mov' => 'video/quicktime',
2929
 
            'qt' => 'video/quicktime',
2930
 
            'rv' => 'video/vnd.rn-realvideo',
2931
 
            'avi' => 'video/x-msvideo',
 
3211
            'log'   => 'text/plain',
 
3212
            'text'  => 'text/plain',
 
3213
            'txt'   => 'text/plain',
 
3214
            'rtx'   => 'text/richtext',
 
3215
            'rtf'   => 'text/rtf',
 
3216
            'vcf'   => 'text/vcard',
 
3217
            'vcard' => 'text/vcard',
 
3218
            'xml'   => 'text/xml',
 
3219
            'xsl'   => 'text/xml',
 
3220
            'mpeg'  => 'video/mpeg',
 
3221
            'mpe'   => 'video/mpeg',
 
3222
            'mpg'   => 'video/mpeg',
 
3223
            'mov'   => 'video/quicktime',
 
3224
            'qt'    => 'video/quicktime',
 
3225
            'rv'    => 'video/vnd.rn-realvideo',
 
3226
            'avi'   => 'video/x-msvideo',
2932
3227
            'movie' => 'video/x-sgi-movie'
2933
3228
        );
2934
 
        return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
 
3229
        if (array_key_exists(strtolower($ext), $mimes)) {
 
3230
            return $mimes[strtolower($ext)];
 
3231
        }
 
3232
        return 'application/octet-stream';
2935
3233
    }
2936
3234
 
2937
3235
    /**
2943
3241
     */
2944
3242
    public static function filenameToType($filename)
2945
3243
    {
2946
 
        //In case the path is a URL, strip any query string before getting extension
 
3244
        // In case the path is a URL, strip any query string before getting extension
2947
3245
        $qpos = strpos($filename, '?');
2948
 
        if ($qpos !== false) {
 
3246
        if (false !== $qpos) {
2949
3247
            $filename = substr($filename, 0, $qpos);
2950
3248
        }
2951
3249
        $pathinfo = self::mb_pathinfo($filename);
2966
3264
    public static function mb_pathinfo($path, $options = null)
2967
3265
    {
2968
3266
        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
2969
 
        $m = array();
2970
 
        preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
2971
 
        if (array_key_exists(1, $m)) {
2972
 
            $ret['dirname'] = $m[1];
2973
 
        }
2974
 
        if (array_key_exists(2, $m)) {
2975
 
            $ret['basename'] = $m[2];
2976
 
        }
2977
 
        if (array_key_exists(5, $m)) {
2978
 
            $ret['extension'] = $m[5];
2979
 
        }
2980
 
        if (array_key_exists(3, $m)) {
2981
 
            $ret['filename'] = $m[3];
 
3267
        $pathinfo = array();
 
3268
        if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
 
3269
            if (array_key_exists(1, $pathinfo)) {
 
3270
                $ret['dirname'] = $pathinfo[1];
 
3271
            }
 
3272
            if (array_key_exists(2, $pathinfo)) {
 
3273
                $ret['basename'] = $pathinfo[2];
 
3274
            }
 
3275
            if (array_key_exists(5, $pathinfo)) {
 
3276
                $ret['extension'] = $pathinfo[5];
 
3277
            }
 
3278
            if (array_key_exists(3, $pathinfo)) {
 
3279
                $ret['filename'] = $pathinfo[3];
 
3280
            }
2982
3281
        }
2983
3282
        switch ($options) {
2984
3283
            case PATHINFO_DIRNAME:
2985
3284
            case 'dirname':
2986
3285
                return $ret['dirname'];
2987
 
                break;
2988
3286
            case PATHINFO_BASENAME:
2989
3287
            case 'basename':
2990
3288
                return $ret['basename'];
2991
 
                break;
2992
3289
            case PATHINFO_EXTENSION:
2993
3290
            case 'extension':
2994
3291
                return $ret['extension'];
2995
 
                break;
2996
3292
            case PATHINFO_FILENAME:
2997
3293
            case 'filename':
2998
3294
                return $ret['filename'];
2999
 
                break;
3000
3295
            default:
3001
3296
                return $ret;
3002
3297
        }
3004
3299
 
3005
3300
    /**
3006
3301
     * Set or reset instance properties.
3007
 
     *
 
3302
     * You should avoid this function - it's more verbose, less efficient, more error-prone and
 
3303
     * harder to debug than setting properties directly.
3008
3304
     * Usage Example:
3009
 
     * $page->set('X-Priority', '3');
3010
 
     *
 
3305
     * `$mail->set('SMTPSecure', 'tls');`
 
3306
     *   is the same as:
 
3307
     * `$mail->SMTPSecure = 'tls';`
3011
3308
     * @access public
3012
 
     * @param string $name
3013
 
     * @param mixed $value
3014
 
     * NOTE: will not work with arrays, there are no arrays to set/reset
3015
 
     * @throws phpmailerException
3016
 
     * @return bool
3017
 
     * @todo Should this not be using __set() magic function?
 
3309
     * @param string $name The property name to set
 
3310
     * @param mixed $value The value to set the property to
 
3311
     * @return boolean
 
3312
     * @TODO Should this not be using the __set() magic function?
3018
3313
     */
3019
3314
    public function set($name, $value = '')
3020
3315
    {
3021
 
        try {
3022
 
            if (isset($this->$name)) {
3023
 
                $this->$name = $value;
3024
 
            } else {
3025
 
                throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
3026
 
            }
3027
 
        } catch (Exception $e) {
3028
 
            $this->setError($e->getMessage());
3029
 
            if ($e->getCode() == self::STOP_CRITICAL) {
3030
 
                return false;
3031
 
            }
 
3316
        if (property_exists($this, $name)) {
 
3317
            $this->$name = $value;
 
3318
            return true;
 
3319
        } else {
 
3320
            $this->setError($this->lang('variable_set') . $name);
 
3321
            return false;
3032
3322
        }
3033
 
        return true;
3034
3323
    }
3035
3324
 
3036
3325
    /**
3061
3350
 
3062
3351
 
3063
3352
    /**
3064
 
     * Set the private key file and password for S/MIME signing.
 
3353
     * Set the public and private key files and password for S/MIME signing.
3065
3354
     * @access public
3066
3355
     * @param string $cert_filename
3067
3356
     * @param string $key_filename
3068
3357
     * @param string $key_pass Password for private key
 
3358
     * @param string $extracerts_filename Optional path to chain certificate
3069
3359
     */
3070
 
    public function sign($cert_filename, $key_filename, $key_pass)
 
3360
    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3071
3361
    {
3072
3362
        $this->sign_cert_file = $cert_filename;
3073
3363
        $this->sign_key_file = $key_filename;
3074
3364
        $this->sign_key_pass = $key_pass;
 
3365
        $this->sign_extracerts_file = $extracerts_filename;
3075
3366
    }
3076
3367
 
3077
3368
    /**
3088
3379
            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3089
3380
                $line .= $txt[$i];
3090
3381
            } else {
3091
 
                $line .= "=" . sprintf("%02X", $ord);
 
3382
                $line .= '=' . sprintf('%02X', $ord);
3092
3383
            }
3093
3384
        }
3094
3385
        return $line;
3097
3388
    /**
3098
3389
     * Generate a DKIM signature.
3099
3390
     * @access public
3100
 
     * @param string $s Header
 
3391
     * @param string $signHeader
3101
3392
     * @throws phpmailerException
3102
3393
     * @return string
3103
3394
     */
3104
 
    public function DKIM_Sign($s)
 
3395
    public function DKIM_Sign($signHeader)
3105
3396
    {
3106
3397
        if (!defined('PKCS7_TEXT')) {
3107
3398
            if ($this->exceptions) {
3108
 
                throw new phpmailerException($this->lang("signing") . ' OpenSSL extension missing.');
 
3399
                throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3109
3400
            }
3110
3401
            return '';
3111
3402
        }
3115
3406
        } else {
3116
3407
            $privKey = $privKeyStr;
3117
3408
        }
3118
 
        if (openssl_sign($s, $signature, $privKey)) {
 
3409
        if (openssl_sign($signHeader, $signature, $privKey)) {
3119
3410
            return base64_encode($signature);
3120
3411
        }
3121
3412
        return '';
3124
3415
    /**
3125
3416
     * Generate a DKIM canonicalization header.
3126
3417
     * @access public
3127
 
     * @param string $s Header
 
3418
     * @param string $signHeader Header
3128
3419
     * @return string
3129
3420
     */
3130
 
    public function DKIM_HeaderC($s)
 
3421
    public function DKIM_HeaderC($signHeader)
3131
3422
    {
3132
 
        $s = preg_replace("/\r\n\s+/", " ", $s);
3133
 
        $lines = explode("\r\n", $s);
 
3423
        $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
 
3424
        $lines = explode("\r\n", $signHeader);
3134
3425
        foreach ($lines as $key => $line) {
3135
 
            list($heading, $value) = explode(":", $line, 2);
 
3426
            list($heading, $value) = explode(':', $line, 2);
3136
3427
            $heading = strtolower($heading);
3137
 
            $value = preg_replace("/\s+/", " ", $value); // Compress useless spaces
3138
 
            $lines[$key] = $heading . ":" . trim($value); // Don't forget to remove WSP around the value
 
3428
            $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
 
3429
            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3139
3430
        }
3140
 
        $s = implode("\r\n", $lines);
3141
 
        return $s;
 
3431
        $signHeader = implode("\r\n", $lines);
 
3432
        return $signHeader;
3142
3433
    }
3143
3434
 
3144
3435
    /**
3189
3480
                $to_header = $header;
3190
3481
                $current = 'to_header';
3191
3482
            } else {
3192
 
                if ($current && strpos($header, ' =?') === 0) {
3193
 
                    $current .= $header;
 
3483
                if (!empty($$current) && strpos($header, ' =?') === 0) {
 
3484
                    $$current .= $header;
3194
3485
                } else {
3195
3486
                    $current = '';
3196
3487
                }
3205
3496
        ); // Copied header fields (dkim-quoted-printable)
3206
3497
        $body = $this->DKIM_BodyC($body);
3207
3498
        $DKIMlen = strlen($body); // Length of body
3208
 
        $DKIMb64 = base64_encode(pack("H*", sha1($body))); // Base64 of packed binary SHA-1 hash of body
3209
 
        $ident = ($this->DKIM_identity == '') ? '' : " i=" . $this->DKIM_identity . ";";
3210
 
        $dkimhdrs = "DKIM-Signature: v=1; a=" .
3211
 
            $DKIMsignatureType . "; q=" .
3212
 
            $DKIMquery . "; l=" .
3213
 
            $DKIMlen . "; s=" .
 
3499
        $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
 
3500
        if ('' == $this->DKIM_identity) {
 
3501
            $ident = '';
 
3502
        } else {
 
3503
            $ident = ' i=' . $this->DKIM_identity . ';';
 
3504
        }
 
3505
        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
 
3506
            $DKIMsignatureType . '; q=' .
 
3507
            $DKIMquery . '; l=' .
 
3508
            $DKIMlen . '; s=' .
3214
3509
            $this->DKIM_selector .
3215
3510
            ";\r\n" .
3216
 
            "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n" .
 
3511
            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3217
3512
            "\th=From:To:Subject;\r\n" .
3218
 
            "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n" .
 
3513
            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3219
3514
            "\tz=$from\r\n" .
3220
3515
            "\t|$to\r\n" .
3221
3516
            "\t|$subject;\r\n" .
3229
3524
    }
3230
3525
 
3231
3526
    /**
 
3527
     * Detect if a string contains a line longer than the maximum line length allowed.
 
3528
     * @param string $str
 
3529
     * @return boolean
 
3530
     * @static
 
3531
     */
 
3532
    public static function hasLineLongerThanMax($str)
 
3533
    {
 
3534
        //+2 to include CRLF line break for a 1000 total
 
3535
        return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
 
3536
    }
 
3537
 
 
3538
    /**
 
3539
     * Allows for public read access to 'to' property.
 
3540
     * @access public
 
3541
     * @return array
 
3542
     */
 
3543
    public function getToAddresses()
 
3544
    {
 
3545
        return $this->to;
 
3546
    }
 
3547
 
 
3548
    /**
 
3549
     * Allows for public read access to 'cc' property.
 
3550
     * @access public
 
3551
     * @return array
 
3552
     */
 
3553
    public function getCcAddresses()
 
3554
    {
 
3555
        return $this->cc;
 
3556
    }
 
3557
 
 
3558
    /**
 
3559
     * Allows for public read access to 'bcc' property.
 
3560
     * @access public
 
3561
     * @return array
 
3562
     */
 
3563
    public function getBccAddresses()
 
3564
    {
 
3565
        return $this->bcc;
 
3566
    }
 
3567
 
 
3568
    /**
 
3569
     * Allows for public read access to 'ReplyTo' property.
 
3570
     * @access public
 
3571
     * @return array
 
3572
     */
 
3573
    public function getReplyToAddresses()
 
3574
    {
 
3575
        return $this->ReplyTo;
 
3576
    }
 
3577
 
 
3578
    /**
 
3579
     * Allows for public read access to 'all_recipients' property.
 
3580
     * @access public
 
3581
     * @return array
 
3582
     */
 
3583
    public function getAllRecipientAddresses()
 
3584
    {
 
3585
        return $this->all_recipients;
 
3586
    }
 
3587
 
 
3588
    /**
3232
3589
     * Perform a callback.
3233
 
     * @param bool $isSent
3234
 
     * @param string $to
3235
 
     * @param string $cc
3236
 
     * @param string $bcc
 
3590
     * @param boolean $isSent
 
3591
     * @param array $to
 
3592
     * @param array $cc
 
3593
     * @param array $bcc
3237
3594
     * @param string $subject
3238
3595
     * @param string $body
3239
3596
     * @param string $from
3240
3597
     */
3241
 
    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null)
 
3598
    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3242
3599
    {
3243
3600
        if (!empty($this->action_function) && is_callable($this->action_function)) {
3244
3601
            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);