Completed
Push — master ( ed4e7c...9e4b8f )
by Marcus
06:10
created

class.phpmailer.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * PHPMailer - PHP email creation and transport class.
4
 * PHP Version 5
5
 * @package PHPMailer
6
 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7
 * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
8
 * @author Jim Jagielski (jimjag) <[email protected]>
9
 * @author Andy Prevost (codeworxtech) <[email protected]>
10
 * @author Brent R. Matzelle (original founder)
11
 * @copyright 2012 - 2014 Marcus Bointon
12
 * @copyright 2010 - 2012 Jim Jagielski
13
 * @copyright 2004 - 2009 Andy Prevost
14
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15
 * @note This program is distributed in the hope that it will be useful - WITHOUT
16
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
 * FITNESS FOR A PARTICULAR PURPOSE.
18
 */
19
20
/**
21
 * PHPMailer - PHP email creation and transport class.
22
 * @package PHPMailer
23
 * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
24
 * @author Jim Jagielski (jimjag) <[email protected]>
25
 * @author Andy Prevost (codeworxtech) <[email protected]>
26
 * @author Brent R. Matzelle (original founder)
27
 */
28
class PHPMailer
29
{
30
    /**
31
     * The PHPMailer Version number.
32
     * @var string
33
     */
34
    public $Version = '5.2.18';
35
36
    /**
37
     * Email priority.
38
     * Options: null (default), 1 = High, 3 = Normal, 5 = low.
39
     * When null, the header is not set at all.
40
     * @var integer
41
     */
42
    public $Priority = null;
43
44
    /**
45
     * The character set of the message.
46
     * @var string
47
     */
48
    public $CharSet = 'iso-8859-1';
49
50
    /**
51
     * The MIME Content-type of the message.
52
     * @var string
53
     */
54
    public $ContentType = 'text/plain';
55
56
    /**
57
     * The message encoding.
58
     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59
     * @var string
60
     */
61
    public $Encoding = '8bit';
62
63
    /**
64
     * Holds the most recent mailer error message.
65
     * @var string
66
     */
67
    public $ErrorInfo = '';
68
69
    /**
70
     * The From email address for the message.
71
     * @var string
72
     */
73
    public $From = 'root@localhost';
74
75
    /**
76
     * The From name of the message.
77
     * @var string
78
     */
79
    public $FromName = 'Root User';
80
81
    /**
82
     * The Sender email (Return-Path) of the message.
83
     * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
84
     * @var string
85
     */
86
    public $Sender = '';
87
88
    /**
89
     * The Return-Path of the message.
90
     * If empty, it will be set to either From or Sender.
91
     * @var string
92
     * @deprecated Email senders should never set a return-path header;
93
     * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
94
     * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
95
     */
96
    public $ReturnPath = '';
97
98
    /**
99
     * The Subject of the message.
100
     * @var string
101
     */
102
    public $Subject = '';
103
104
    /**
105
     * An HTML or plain text message body.
106
     * If HTML then call isHTML(true).
107
     * @var string
108
     */
109
    public $Body = '';
110
111
    /**
112
     * The plain-text message body.
113
     * This body can be read by mail clients that do not have HTML email
114
     * capability such as mutt & Eudora.
115
     * Clients that can read HTML will view the normal Body.
116
     * @var string
117
     */
118
    public $AltBody = '';
119
120
    /**
121
     * An iCal message part body.
122
     * Only supported in simple alt or alt_inline message types
123
     * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
124
     * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
125
     * @link http://kigkonsult.se/iCalcreator/
126
     * @var string
127
     */
128
    public $Ical = '';
129
130
    /**
131
     * The complete compiled MIME message body.
132
     * @access protected
133
     * @var string
134
     */
135
    protected $MIMEBody = '';
136
137
    /**
138
     * The complete compiled MIME message headers.
139
     * @var string
140
     * @access protected
141
     */
142
    protected $MIMEHeader = '';
143
144
    /**
145
     * Extra headers that createHeader() doesn't fold in.
146
     * @var string
147
     * @access protected
148
     */
149
    protected $mailHeader = '';
150
151
    /**
152
     * Word-wrap the message body to this number of chars.
153
     * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
154
     * @var integer
155
     */
156
    public $WordWrap = 0;
157
158
    /**
159
     * Which method to use to send mail.
160
     * Options: "mail", "sendmail", or "smtp".
161
     * @var string
162
     */
163
    public $Mailer = 'mail';
164
165
    /**
166
     * The path to the sendmail program.
167
     * Must contain only a path to an executable, with no parameters or switches
168
     * @var string
169
     */
170
    public $Sendmail = '/usr/sbin/sendmail';
171
172
    /**
173
     * Whether mail() uses a fully sendmail-compatible MTA.
174
     * One which supports sendmail's "-oi -f" options.
175
     * @var boolean
176
     */
177
    public $UseSendmailOptions = true;
178
179
    /**
180
     * Path to PHPMailer plugins.
181
     * Useful if the SMTP class is not in the PHP include path.
182
     * @var string
183
     * @deprecated Should not be needed now there is an autoloader.
184
     */
185
    public $PluginDir = '';
186
187
    /**
188
     * The email address that a reading confirmation should be sent to, also known as read receipt.
189
     * @var string
190
     */
191
    public $ConfirmReadingTo = '';
192
193
    /**
194
     * The hostname to use in the Message-ID header and as default HELO string.
195
     * If empty, PHPMailer attempts to find one with, in order,
196
     * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
197
     * 'localhost.localdomain'.
198
     * @var string
199
     */
200
    public $Hostname = '';
201
202
    /**
203
     * An ID to be used in the Message-ID header.
204
     * If empty, a unique id will be generated.
205
     * You can set your own, but it must be in the format "<id@domain>",
206
     * as defined in RFC5322 section 3.6.4 or it will be ignored.
207
     * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
208
     * @var string
209
     */
210
    public $MessageID = '';
211
212
    /**
213
     * The message Date to be used in the Date header.
214
     * If empty, the current date will be added.
215
     * @var string
216
     */
217
    public $MessageDate = '';
218
219
    /**
220
     * SMTP hosts.
221
     * Either a single hostname or multiple semicolon-delimited hostnames.
222
     * You can also specify a different port
223
     * for each host by using this format: [hostname:port]
224
     * (e.g. "smtp1.example.com:25;smtp2.example.com").
225
     * You can also specify encryption type, for example:
226
     * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
227
     * Hosts will be tried in order.
228
     * @var string
229
     */
230
    public $Host = 'localhost';
231
232
    /**
233
     * The default SMTP server port.
234
     * @var integer
235
     * @TODO Why is this needed when the SMTP class takes care of it?
236
     */
237
    public $Port = 25;
238
239
    /**
240
     * The SMTP HELO of the message.
241
     * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
242
     * one with the same method described above for $Hostname.
243
     * @var string
244
     * @see PHPMailer::$Hostname
245
     */
246
    public $Helo = '';
247
248
    /**
249
     * What kind of encryption to use on the SMTP connection.
250
     * Options: '', 'ssl' or 'tls'
251
     * @var string
252
     */
253
    public $SMTPSecure = '';
254
255
    /**
256
     * Whether to enable TLS encryption automatically if a server supports it,
257
     * even if `SMTPSecure` is not set to 'tls'.
258
     * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
259
     * @var boolean
260
     */
261
    public $SMTPAutoTLS = true;
262
263
    /**
264
     * Whether to use SMTP authentication.
265
     * Uses the Username and Password properties.
266
     * @var boolean
267
     * @see PHPMailer::$Username
268
     * @see PHPMailer::$Password
269
     */
270
    public $SMTPAuth = false;
271
272
    /**
273
     * Options array passed to stream_context_create when connecting via SMTP.
274
     * @var array
275
     */
276
    public $SMTPOptions = array();
277
278
    /**
279
     * SMTP username.
280
     * @var string
281
     */
282
    public $Username = '';
283
284
    /**
285
     * SMTP password.
286
     * @var string
287
     */
288
    public $Password = '';
289
290
    /**
291
     * SMTP auth type.
292
     * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified
293
     * @var string
294
     */
295
    public $AuthType = '';
296
297
    /**
298
     * SMTP realm.
299
     * Used for NTLM auth
300
     * @var string
301
     */
302
    public $Realm = '';
303
304
    /**
305
     * SMTP workstation.
306
     * Used for NTLM auth
307
     * @var string
308
     */
309
    public $Workstation = '';
310
311
    /**
312
     * The SMTP server timeout in seconds.
313
     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
314
     * @var integer
315
     */
316
    public $Timeout = 300;
317
318
    /**
319
     * SMTP class debug output mode.
320
     * Debug output level.
321
     * Options:
322
     * * `0` No output
323
     * * `1` Commands
324
     * * `2` Data and commands
325
     * * `3` As 2 plus connection status
326
     * * `4` Low-level data output
327
     * @var integer
328
     * @see SMTP::$do_debug
329
     */
330
    public $SMTPDebug = 0;
331
332
    /**
333
     * How to handle debug output.
334
     * Options:
335
     * * `echo` Output plain-text as-is, appropriate for CLI
336
     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
337
     * * `error_log` Output to error log as configured in php.ini
338
     *
339
     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
340
     * <code>
341
     * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
342
     * </code>
343
     * @var string|callable
344
     * @see SMTP::$Debugoutput
345
     */
346
    public $Debugoutput = 'echo';
347
348
    /**
349
     * Whether to keep SMTP connection open after each message.
350
     * If this is set to true then to close the connection
351
     * requires an explicit call to smtpClose().
352
     * @var boolean
353
     */
354
    public $SMTPKeepAlive = false;
355
356
    /**
357
     * Whether to split multiple to addresses into multiple messages
358
     * or send them all in one message.
359
     * Only supported in `mail` and `sendmail` transports, not in SMTP.
360
     * @var boolean
361
     */
362
    public $SingleTo = false;
363
364
    /**
365
     * Storage for addresses when SingleTo is enabled.
366
     * @var array
367
     * @TODO This should really not be public
368
     */
369
    public $SingleToArray = array();
370
371
    /**
372
     * Whether to generate VERP addresses on send.
373
     * Only applicable when sending via SMTP.
374
     * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
375
     * @link http://www.postfix.org/VERP_README.html Postfix VERP info
376
     * @var boolean
377
     */
378
    public $do_verp = false;
379
380
    /**
381
     * Whether to allow sending messages with an empty body.
382
     * @var boolean
383
     */
384
    public $AllowEmpty = false;
385
386
    /**
387
     * The default line ending.
388
     * @note The default remains "\n". We force CRLF where we know
389
     *        it must be used via self::CRLF.
390
     * @var string
391
     */
392
    public $LE = "\n";
393
394
    /**
395
     * DKIM selector.
396
     * @var string
397
     */
398
    public $DKIM_selector = '';
399
400
    /**
401
     * DKIM Identity.
402
     * Usually the email address used as the source of the email.
403
     * @var string
404
     */
405
    public $DKIM_identity = '';
406
407
    /**
408
     * DKIM passphrase.
409
     * Used if your key is encrypted.
410
     * @var string
411
     */
412
    public $DKIM_passphrase = '';
413
414
    /**
415
     * DKIM signing domain name.
416
     * @example 'example.com'
417
     * @var string
418
     */
419
    public $DKIM_domain = '';
420
421
    /**
422
     * DKIM private key file path.
423
     * @var string
424
     */
425
    public $DKIM_private = '';
426
427
    /**
428
     * DKIM private key string.
429
     * If set, takes precedence over `$DKIM_private`.
430
     * @var string
431
     */
432
    public $DKIM_private_string = '';
433
434
    /**
435
     * Callback Action function name.
436
     *
437
     * The function that handles the result of the send email action.
438
     * It is called out by send() for each email sent.
439
     *
440
     * Value can be any php callable: http://www.php.net/is_callable
441
     *
442
     * Parameters:
443
     *   boolean $result        result of the send action
444
     *   string  $to            email address of the recipient
445
     *   string  $cc            cc email addresses
446
     *   string  $bcc           bcc email addresses
447
     *   string  $subject       the subject
448
     *   string  $body          the email body
449
     *   string  $from          email address of sender
450
     * @var string
451
     */
452
    public $action_function = '';
453
454
    /**
455
     * What to put in the X-Mailer header.
456
     * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
457
     * @var string
458
     */
459
    public $XMailer = '';
460
461
    /**
462
     * Which validator to use by default when validating email addresses.
463
     * May be a callable to inject your own validator, but there are several built-in validators.
464
     * @see PHPMailer::validateAddress()
465
     * @var string|callable
466
     * @static
467
     */
468
    public static $validator = 'auto';
469
470
    /**
471
     * An instance of the SMTP sender class.
472
     * @var SMTP
473
     * @access protected
474
     */
475
    protected $smtp = null;
476
477
    /**
478
     * The array of 'to' names and addresses.
479
     * @var array
480
     * @access protected
481
     */
482
    protected $to = array();
483
484
    /**
485
     * The array of 'cc' names and addresses.
486
     * @var array
487
     * @access protected
488
     */
489
    protected $cc = array();
490
491
    /**
492
     * The array of 'bcc' names and addresses.
493
     * @var array
494
     * @access protected
495
     */
496
    protected $bcc = array();
497
498
    /**
499
     * The array of reply-to names and addresses.
500
     * @var array
501
     * @access protected
502
     */
503
    protected $ReplyTo = array();
504
505
    /**
506
     * An array of all kinds of addresses.
507
     * Includes all of $to, $cc, $bcc
508
     * @var array
509
     * @access protected
510
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
511
     */
512
    protected $all_recipients = array();
513
514
    /**
515
     * An array of names and addresses queued for validation.
516
     * In send(), valid and non duplicate entries are moved to $all_recipients
517
     * and one of $to, $cc, or $bcc.
518
     * This array is used only for addresses with IDN.
519
     * @var array
520
     * @access protected
521
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
522
     * @see PHPMailer::$all_recipients
523
     */
524
    protected $RecipientsQueue = array();
525
526
    /**
527
     * An array of reply-to names and addresses queued for validation.
528
     * In send(), valid and non duplicate entries are moved to $ReplyTo.
529
     * This array is used only for addresses with IDN.
530
     * @var array
531
     * @access protected
532
     * @see PHPMailer::$ReplyTo
533
     */
534
    protected $ReplyToQueue = array();
535
536
    /**
537
     * The array of attachments.
538
     * @var array
539
     * @access protected
540
     */
541
    protected $attachment = array();
542
543
    /**
544
     * The array of custom headers.
545
     * @var array
546
     * @access protected
547
     */
548
    protected $CustomHeader = array();
549
550
    /**
551
     * The most recent Message-ID (including angular brackets).
552
     * @var string
553
     * @access protected
554
     */
555
    protected $lastMessageID = '';
556
557
    /**
558
     * The message's MIME type.
559
     * @var string
560
     * @access protected
561
     */
562
    protected $message_type = '';
563
564
    /**
565
     * The array of MIME boundary strings.
566
     * @var array
567
     * @access protected
568
     */
569
    protected $boundary = array();
570
571
    /**
572
     * The array of available languages.
573
     * @var array
574
     * @access protected
575
     */
576
    protected $language = array();
577
578
    /**
579
     * The number of errors encountered.
580
     * @var integer
581
     * @access protected
582
     */
583
    protected $error_count = 0;
584
585
    /**
586
     * The S/MIME certificate file path.
587
     * @var string
588
     * @access protected
589
     */
590
    protected $sign_cert_file = '';
591
592
    /**
593
     * The S/MIME key file path.
594
     * @var string
595
     * @access protected
596
     */
597
    protected $sign_key_file = '';
598
599
    /**
600
     * The optional S/MIME extra certificates ("CA Chain") file path.
601
     * @var string
602
     * @access protected
603
     */
604
    protected $sign_extracerts_file = '';
605
606
    /**
607
     * The S/MIME password for the key.
608
     * Used only if the key is encrypted.
609
     * @var string
610
     * @access protected
611
     */
612
    protected $sign_key_pass = '';
613
614
    /**
615
     * Whether to throw exceptions for errors.
616
     * @var boolean
617
     * @access protected
618
     */
619
    protected $exceptions = false;
620
621
    /**
622
     * Unique ID used for message ID and boundaries.
623
     * @var string
624
     * @access protected
625
     */
626
    protected $uniqueid = '';
627
628
    /**
629
     * Error severity: message only, continue processing.
630
     */
631
    const STOP_MESSAGE = 0;
632
633
    /**
634
     * Error severity: message, likely ok to continue processing.
635
     */
636
    const STOP_CONTINUE = 1;
637
638
    /**
639
     * Error severity: message, plus full stop, critical error reached.
640
     */
641
    const STOP_CRITICAL = 2;
642
643
    /**
644
     * SMTP RFC standard line ending.
645
     */
646
    const CRLF = "\r\n";
647 56
648
    /**
649 56
     * The maximum line length allowed by RFC 2822 section 2.1.1
650
     * @var integer
651
     */
652 56
    const MAX_LINE_LENGTH = 998;
653
654
    /**
655
     * Constructor.
656
     * @param boolean $exceptions Should we throw external exceptions?
657 56
     */
658
    public function __construct($exceptions = null)
659
    {
660 56
        if ($exceptions !== null) {
661 56
            $this->exceptions = (boolean)$exceptions;
662
        }
663
    }
664
665
    /**
666
     * Destructor.
667
     */
668
    public function __destruct()
669
    {
670
        //Close any open SMTP connection nicely
671
        $this->smtpClose();
672
    }
673
674
    /**
675
     * Call mail() in a safe_mode-aware fashion.
676 2
     * Also, unless sendmail_path points to sendmail (or something that
677
     * claims to be sendmail), don't pass params (not a perfect fix,
678
     * but it will do)
679 2
     * @param string $to To
680
     * @param string $subject Subject
681
     * @param string $body Message Body
682 2
     * @param string $header Additional Header(s)
683
     * @param string $params Params
684
     * @access private
685
     * @return boolean
686 2
     */
687
    private function mailPassthru($to, $subject, $body, $header, $params)
688
    {
689 2
        //Check overloading of mail function to avoid double-encoding
690
        if (ini_get('mbstring.func_overload') & 1) {
691 2
            $subject = $this->secureHeader($subject);
692
        } else {
693
            $subject = $this->encodeHeader($this->secureHeader($subject));
694
        }
695
696
        //Can't use additional_parameters in safe_mode, calling mail() with null params breaks
697
        //@link http://php.net/manual/en/function.mail.php
698
        if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
699
            $result = @mail($to, $subject, $body, $header);
700
        } else {
701 4
            $result = @mail($to, $subject, $body, $header, $params);
702
        }
703 4
        return $result;
704
    }
705
    /**
706
     * Output debugging info via user-defined method.
707 4
     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
708
     * @see PHPMailer::$Debugoutput
709
     * @see PHPMailer::$SMTPDebug
710
     * @param string $str
711 4
     */
712 4
    protected function edebug($str)
713
    {
714
        if ($this->SMTPDebug <= 0) {
715
            return;
716 4
        }
717
        //Avoid clash with built-in function names
718
        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
719
            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
720
            return;
721
        }
722
        switch ($this->Debugoutput) {
723
            case 'error_log':
724
                //Don't output, just log
725 4
                error_log($str);
726 4
                break;
727
            case 'html':
728 4
                //Cleans up output a bit for a better looking, HTML-safe output
729 4
                echo htmlentities(
730 4
                    preg_replace('/[\r\n]+/', '', $str),
731 4
                    ENT_QUOTES,
732 4
                    'UTF-8'
733 4
                )
734 4
                . "<br>\n";
735 4
                break;
736
            case 'echo':
737
            default:
738
                //Normalize line breaks
739
                $str = preg_replace('/\r\n?/ms', "\n", $str);
740
                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
741
                    "\n",
742 16
                    "\n                   \t                  ",
743
                    trim($str)
744 16
                ) . "\n";
745 13
        }
746 13
    }
747 3
748
    /**
749 16
     * Sets message type to HTML or plain.
750
     * @param boolean $isHtml True for HTML mode.
751
     * @return void
752
     */
753
    public function isHTML($isHtml = true)
754
    {
755 1
        if ($isHtml) {
756
            $this->ContentType = 'text/html';
757 1
        } else {
758 1
            $this->ContentType = 'text/plain';
759
        }
760
    }
761
762
    /**
763
     * Send messages using SMTP.
764 3
     * @return void
765
     */
766 3
    public function isSMTP()
767 3
    {
768
        $this->Mailer = 'smtp';
769
    }
770
771
    /**
772
     * Send messages using PHP's mail() function.
773 2
     * @return void
774
     */
775 2
    public function isMail()
776
    {
777 2
        $this->Mailer = 'mail';
778
    }
779
780 2
    /**
781
     * Send messages using $Sendmail.
782 2
     * @return void
783 2
     */
784
    public function isSendmail()
785
    {
786
        $ini_sendmail_path = ini_get('sendmail_path');
787
788
        if (!stristr($ini_sendmail_path, 'sendmail')) {
789 1
            $this->Sendmail = '/usr/sbin/sendmail';
790
        } else {
791 1
            $this->Sendmail = $ini_sendmail_path;
792
        }
793 1
        $this->Mailer = 'sendmail';
794 1
    }
795 1
796
    /**
797
     * Send messages using qmail.
798 1
     * @return void
799 1
     */
800
    public function isQmail()
801
    {
802
        $ini_sendmail_path = ini_get('sendmail_path');
803
804
        if (!stristr($ini_sendmail_path, 'qmail')) {
805
            $this->Sendmail = '/var/qmail/bin/qmail-inject';
806
        } else {
807 56
            $this->Sendmail = $ini_sendmail_path;
808
        }
809 56
        $this->Mailer = 'qmail';
810
    }
811
812
    /**
813
     * Add a "To" address.
814
     * @param string $address The email address to send to
815
     * @param string $name
816
     * @return boolean true on success, false if address already used or invalid in some way
817
     */
818
    public function addAddress($address, $name = '')
819 56
    {
820
        return $this->addOrEnqueueAnAddress('to', $address, $name);
821 56
    }
822
823
    /**
824
     * Add a "CC" address.
825
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
826
     * @param string $address The email address to send to
827
     * @param string $name
828
     * @return boolean true on success, false if address already used or invalid in some way
829
     */
830
    public function addCC($address, $name = '')
831 3
    {
832
        return $this->addOrEnqueueAnAddress('cc', $address, $name);
833 3
    }
834
835
    /**
836
     * Add a "BCC" address.
837
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
838
     * @param string $address The email address to send to
839
     * @param string $name
840
     * @return boolean true on success, false if address already used or invalid in some way
841
     */
842 56
    public function addBCC($address, $name = '')
843
    {
844 56
        return $this->addOrEnqueueAnAddress('bcc', $address, $name);
845
    }
846
847
    /**
848
     * Add a "Reply-To" address.
849
     * @param string $address The email address to reply to
850
     * @param string $name
851
     * @return boolean true on success, false if address already used or invalid in some way
852
     */
853
    public function addReplyTo($address, $name = '')
854
    {
855
        return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
856
    }
857
858
    /**
859 56
     * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
860
     * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
861 56
     * be modified after calling this function), addition of such addresses is delayed until send().
862 56
     * Addresses that have been added already return false, but do not throw exceptions.
863 56
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
864
     * @param string $address The email address to send, resp. to reply to
865 1
     * @param string $name
866 1
     * @throws phpmailerException
867 1
     * @return boolean true on success, false if address already used or invalid in some way
868 1
     * @access protected
869
     */
870
    protected function addOrEnqueueAnAddress($kind, $address, $name)
871 1
    {
872
        $address = trim($address);
873 56
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
874
        if (($pos = strrpos($address, '@')) === false) {
875 56
            // At-sign is misssing.
876 2
            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
877 2
            $this->setError($error_message);
878 2
            $this->edebug($error_message);
879 2
            if ($this->exceptions) {
880
                throw new phpmailerException($error_message);
881 1
            }
882 2
            return false;
883 2
        }
884 2
        $params = array($kind, $address, $name);
885
        // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
886
        if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
887 1
            if ($kind != 'Reply-To') {
888
                if (!array_key_exists($address, $this->RecipientsQueue)) {
889
                    $this->RecipientsQueue[$address] = $params;
890 56
                    return true;
891
                }
892
            } else {
893
                if (!array_key_exists($address, $this->ReplyToQueue)) {
894
                    $this->ReplyToQueue[$address] = $params;
895
                    return true;
896
                }
897
            }
898
            return false;
899
        }
900
        // Immediately add standard addresses without IDN.
901
        return call_user_func_array(array($this, 'addAnAddress'), $params);
902
    }
903 56
904
    /**
905 56
     * Add an address to one of the recipient arrays or to the ReplyTo array.
906
     * Addresses that have been added already return false, but do not throw exceptions.
907
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
908
     * @param string $address The email address to send, resp. to reply to
909
     * @param string $name
910
     * @throws phpmailerException
911
     * @return boolean true on success, false if address already used or invalid in some way
912
     * @access protected
913
     */
914 56
    protected function addAnAddress($kind, $address, $name = '')
915 2
    {
916 2
        if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
917 2
            $error_message = $this->lang('Invalid recipient kind: ') . $kind;
918 2
            $this->setError($error_message);
919
            $this->edebug($error_message);
920
            if ($this->exceptions) {
921 2
                throw new phpmailerException($error_message);
922
            }
923 56
            return false;
924 56
        }
925 56
        if (!$this->validateAddress($address)) {
926 56
            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
927 56
            $this->setError($error_message);
928
            $this->edebug($error_message);
929 2
            if ($this->exceptions) {
930 56
                throw new phpmailerException($error_message);
931 56
            }
932 56
            return false;
933
        }
934
        if ($kind != 'Reply-To') {
935 2
            if (!array_key_exists(strtolower($address), $this->all_recipients)) {
936
                array_push($this->$kind, array($address, $name));
937
                $this->all_recipients[strtolower($address)] = true;
938
                return true;
939
            }
940
        } else {
941
            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
942
                $this->ReplyTo[strtolower($address)] = array($address, $name);
943
                return true;
944
            }
945
        }
946
        return false;
947
    }
948 1
949
    /**
950 1
     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
951 1
     * of the form "display name <address>" into an array of name/address pairs.
952
     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
953 1
     * Note that quotes in the name part are removed.
954 1
     * @param string $addrstr The address list string
955 1
     * @param bool $useimap Whether to use the IMAP extension to parse the list
956 1
     * @return array
957 1
     * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
958 1
     */
959 1
    public function parseAddresses($addrstr, $useimap = true)
960 1
    {
961 1
        $addresses = array();
962 1
        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
963 1
            //Use this built-in parser if it's available
964 1
            $list = imap_rfc822_parse_adrlist($addrstr, '');
965
            foreach ($list as $address) {
966 1
                if ($address->host != '.SYNTAX-ERROR.') {
967 1
                    if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
968 1
                        $addresses[] = array(
969
                            'name' => (property_exists($address, 'personal') ? $address->personal : ''),
970 1
                            'address' => $address->mailbox . '@' . $address->host
971
                        );
972 1
                    }
973 1
                }
974 1
            }
975
        } else {
976 1
            //Use this simpler parser
977 1
            $list = explode(',', $addrstr);
978 1
            foreach ($list as $address) {
979 1
                $address = trim($address);
980 1
                //Is there a separate name part?
981 1
                if (strpos($address, '<') === false) {
982 1
                    //No separate name, just use the whole thing
983 1
                    if ($this->validateAddress($address)) {
984
                        $addresses[] = array(
985 1
                            'name' => '',
986 1
                            'address' => $address
987
                        );
988 1
                    }
989
                } else {
990 1
                    list($name, $email) = explode('<', $address);
991
                    $email = trim(str_replace('>', '', $email));
992
                    if ($this->validateAddress($email)) {
993
                        $addresses[] = array(
994
                            'name' => trim(str_replace(array('"', "'"), '', $name)),
995
                            'address' => $email
996
                        );
997
                    }
998
                }
999
            }
1000
        }
1001 1
        return $addresses;
1002
    }
1003 1
1004 1
    /**
1005
     * Set the From and FromName properties.
1006 1
     * @param string $address
1007 1
     * @param string $name
1008 1
     * @param boolean $auto Whether to also set the Sender address, defaults to true
1009 1
     * @throws phpmailerException
1010 1
     * @return boolean
1011 1
     */
1012 1
    public function setFrom($address, $name = '', $auto = true)
1013
    {
1014
        $address = trim($address);
1015 1
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
1016
        // Don't validate now addresses with IDN. Will be done in send().
1017 1
        if (($pos = strrpos($address, '@')) === false or
1018 1
            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
1019 1
            !$this->validateAddress($address)) {
1020 1
            $error_message = $this->lang('invalid_address') . " (setFrom) $address";
1021 1
            $this->setError($error_message);
1022 1
            $this->edebug($error_message);
1023 1
            if ($this->exceptions) {
1024 1
                throw new phpmailerException($error_message);
1025
            }
1026
            return false;
1027
        }
1028
        $this->From = $address;
1029
        $this->FromName = $name;
1030
        if ($auto) {
1031
            if (empty($this->Sender)) {
1032
                $this->Sender = $address;
1033
            }
1034 1
        }
1035
        return true;
1036 1
    }
1037
1038
    /**
1039
     * Return the Message-ID header of the last email.
1040
     * Technically this is the value from the last time the headers were created,
1041
     * but it's also the message ID of the last sent message except in
1042
     * pathological cases.
1043
     * @return string
1044
     */
1045
    public function getLastMessageID()
1046
    {
1047
        return $this->lastMessageID;
1048
    }
1049
1050
    /**
1051
     * Check that a string looks like an email address.
1052
     * @param string $address The email address to check
1053
     * @param string|callable $patternselect A selector for the validation pattern to use :
1054
     * * `auto` Pick best pattern automatically;
1055
     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
1056
     * * `pcre` Use old PCRE implementation;
1057
     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1058 56
     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1059
     * * `noregex` Don't use a regex: super fast, really dumb.
1060 56
     * Alternatively you may pass in a callable to inject your own validator, for example:
1061 56
     * PHPMailer::validateAddress('[email protected]', function($address) {
1062 56
     *     return (strpos($address, '@') !== false);
1063 56
     * });
1064 1
     * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
1065
     * @return boolean
1066
     * @static
1067 56
     * @access public
1068 1
     */
1069
    public static function validateAddress($address, $patternselect = null)
1070 56
    {
1071
        if (is_null($patternselect)) {
1072
            $patternselect = self::$validator;
1073 4
        }
1074
        if (is_callable($patternselect)) {
1075 4
            return call_user_func($patternselect, $address);
1076 4
        }
1077 4
        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1078
        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1079
            return false;
1080 4
        }
1081
        if (!$patternselect or $patternselect == 'auto') {
1082
            //Check this constant first so it works when extension_loaded() is disabled by safe mode
1083
            //Constant was added in PHP 5.2.4
1084
            if (defined('PCRE_VERSION')) {
1085
                //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
1086
                if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
1087
                    $patternselect = 'pcre8';
1088
                } else {
1089
                    $patternselect = 'pcre';
1090
                }
1091 4
            } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
1092
                //Fall back to older PCRE
1093 56
                $patternselect = 'pcre';
1094
            } else {
1095
                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1096
                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1097
                    $patternselect = 'php';
1098
                } else {
1099
                    $patternselect = 'noregex';
1100 4
                }
1101
            }
1102 4
        }
1103 4
        switch ($patternselect) {
1104 4
            case 'pcre8':
1105 4
                /**
1106 4
                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1107 4
                 * @link http://squiloople.com/2009/12/20/email-address-validation/
1108 4
                 * @copyright 2009-2010 Michael Rushton
1109 4
                 * Feel free to use and redistribute this code. But please keep this copyright notice.
1110
                 */
1111 4
                return (boolean)preg_match(
1112 54
                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1113
                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1114 1
                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1115
                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1116 1
                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1117 1
                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1118 1
                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1119 1
                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1120 1
                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1121 1
                    $address
1122 1
                );
1123 1
            case 'pcre':
1124 1
                //An older regex that doesn't need a recent PCRE
1125
                return (boolean)preg_match(
1126 1
                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1127 54
                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1128
                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1129
                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1130
                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1131
                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1132
                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1133
                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1134
                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1135
                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1136
                    $address
1137 54
                );
1138
            case 'html5':
1139
                /**
1140 1
                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1141 1
                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1142 1
                 */
1143 54
                return (boolean)preg_match(
1144 54
                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1145 54
                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1146
                    $address
1147
                );
1148
            case 'noregex':
1149
                //No PCRE! Do something _very_ approximate!
1150
                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1151
                return (strlen($address) >= 3
1152
                    and strpos($address, '@') >= 1
1153
                    and strpos($address, '@') != strlen($address) - 1);
1154 41
            case 'php':
1155
            default:
1156
                return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
1157 41
        }
1158
    }
1159
1160
    /**
1161
     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1162
     * "intl" and "mbstring" PHP extensions.
1163
     * @return bool "true" if required functions for IDN support are present
1164
     */
1165
    public function idnSupported()
1166
    {
1167
        // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1168
        return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1169
    }
1170
1171 41
    /**
1172
     * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1173
     * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1174 41
     * This function silently returns unmodified address if:
1175 41
     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1176 41
     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1177 41
     *   or fails for any reason (e.g. domain has characters not allowed in an IDN)
1178
     * @see PHPMailer::$CharSet
1179 41
     * @param string $address The email address to convert
1180 3
     * @return string The encoded address in ASCII form
1181 3
     */
1182 3
    public function punyencodeAddress($address)
1183 3
    {
1184 3
        // Verify we have required functions, CharSet, and at-sign.
1185
        if ($this->idnSupported() and
1186
            !empty($this->CharSet) and
1187 41
            ($pos = strrpos($address, '@')) !== false) {
1188 41
            $domain = substr($address, ++$pos);
1189
            // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1190
            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1191
                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1192
                if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1193
                    idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
1194
                    idn_to_ascii($domain)) !== false) {
1195
                    return substr($address, 0, $pos) . $punycode;
1196
                }
1197 36
            }
1198
        }
1199
        return $address;
1200 36
    }
1201 3
1202
    /**
1203 36
     * Create a message and send it.
1204
     * Uses the sending method specified by $Mailer.
1205
     * @throws phpmailerException
1206
     * @return boolean false on error - See the ErrorInfo property for details of the error.
1207
     */
1208
    public function send()
1209
    {
1210
        try {
1211
            if (!$this->preSend()) {
1212
                return false;
1213
            }
1214
            return $this->postSend();
1215
        } catch (phpmailerException $exc) {
1216
            $this->mailHeader = '';
1217
            $this->setError($exc->getMessage());
1218
            if ($this->exceptions) {
1219 41
                throw $exc;
1220
            }
1221
            return false;
1222 41
        }
1223 41
    }
1224
1225
    /**
1226 41
     * Prepare a message for sending.
1227 2
     * @throws phpmailerException
1228 2
     * @return boolean
1229 41
     */
1230 41
    public function preSend()
1231 1
    {
1232
        try {
1233
            $this->error_count = 0; // Reset errors
1234
            $this->mailHeader = '';
1235 41
1236 41
            // Dequeue recipient and Reply-To addresses with IDN
1237 41
            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1238 40
                $params[1] = $this->punyencodeAddress($params[1]);
1239
                call_user_func_array(array($this, 'addAnAddress'), $params);
1240 41
            }
1241 41
            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1242 1
                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1243 1
            }
1244 1
1245 1
            // Validate From, Sender, and ConfirmReadingTo addresses
1246
            foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
1247
                $this->$address_kind = trim($this->$address_kind);
1248 1
                if (empty($this->$address_kind)) {
1249
                    continue;
1250 41
                }
1251
                $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1252
                if (!$this->validateAddress($this->$address_kind)) {
1253 41
                    $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
1254 7
                    $this->setError($error_message);
1255 7
                    $this->edebug($error_message);
1256
                    if ($this->exceptions) {
1257 41
                        throw new phpmailerException($error_message);
1258
                    }
1259 41
                    return false;
1260 1
                }
1261
            }
1262
1263
            // Set whether the message is multipart/alternative
1264 41
            if ($this->alternativeExists()) {
1265 41
                $this->ContentType = 'multipart/alternative';
1266
            }
1267 41
1268 41
            $this->setMessageType();
1269 41
            // Refuse to send an empty message unless we are specifically allowing it
1270
            if (!$this->AllowEmpty and empty($this->Body)) {
1271
                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1272
            }
1273 41
1274 2
            // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1275 2
            $this->MIMEHeader = '';
1276 2
            $this->MIMEBody = $this->createBody();
1277
            // createBody may have added some headers, so retain them
1278
            $tempheaders = $this->MIMEHeader;
1279 2
            $this->MIMEHeader = $this->createHeader();
1280 2
            $this->MIMEHeader .= $tempheaders;
1281 2
1282 2
            // To capture the complete message when using mail(), create
1283 2
            // an extra header list which createHeader() doesn't fold in
1284
            if ($this->Mailer == 'mail') {
1285
                if (count($this->to) > 0) {
1286 41
                    $this->mailHeader .= $this->addrAppend('To', $this->to);
1287 41
                } else {
1288 41
                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1289 41
                }
1290 1
                $this->mailHeader .= $this->headerLine(
1291 1
                    'Subject',
1292 1
                    $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1293 1
                );
1294 1
            }
1295 1
1296 1
            // Sign with DKIM if enabled
1297 1
            if (!empty($this->DKIM_domain)
1298 41
                && !empty($this->DKIM_selector)
1299 2
                && (!empty($this->DKIM_private_string)
1300 2
                   || (!empty($this->DKIM_private) && file_exists($this->DKIM_private))
1301 2
                )
1302
            ) {
1303
                $header_dkim = $this->DKIM_Add(
1304 2
                    $this->MIMEHeader . $this->mailHeader,
1305
                    $this->encodeHeader($this->secureHeader($this->Subject)),
1306
                    $this->MIMEBody
1307
                );
1308
                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1309
                    str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1310
            }
1311
            return true;
1312
        } catch (phpmailerException $exc) {
1313
            $this->setError($exc->getMessage());
1314 36
            if ($this->exceptions) {
1315
                throw $exc;
1316
            }
1317
            return false;
1318 36
        }
1319 36
    }
1320 36
1321 1
    /**
1322 35
     * Actually send a message.
1323 33
     * Send the email via the selected mechanism
1324 2
     * @throws phpmailerException
1325 2
     * @return boolean
1326
     */
1327
    public function postSend()
1328
    {
1329
        try {
1330
            // Choose the mailer and send through it
1331
            switch ($this->Mailer) {
1332
                case 'sendmail':
1333
                case 'qmail':
1334
                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1335
                case 'smtp':
1336
                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1337
                case 'mail':
1338
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1339
                default:
1340
                    $sendMethod = $this->Mailer.'Send';
1341
                    if (method_exists($this, $sendMethod)) {
1342
                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1343
                    }
1344
1345
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1346
            }
1347
        } catch (phpmailerException $exc) {
1348
            $this->setError($exc->getMessage());
1349
            $this->edebug($exc->getMessage());
1350
            if ($this->exceptions) {
1351
                throw $exc;
1352
            }
1353 1
        }
1354
        return false;
1355 1
    }
1356 1
1357
    /**
1358
     * Send mail using the $Sendmail program.
1359 1
     * @param string $header The message headers
1360
     * @param string $body The message body
1361 1
     * @see PHPMailer::$Sendmail
1362
     * @throws phpmailerException
1363
     * @access protected
1364
     * @return boolean
1365
     */
1366
    protected function sendmailSend($header, $body)
1367
    {
1368 1
        if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1369
            if ($this->Mailer == 'qmail') {
1370
                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1371
            } else {
1372
                $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1373
            }
1374
        } else {
1375
            if ($this->Mailer == 'qmail') {
1376
                $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1377
            } else {
1378
                $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1379
            }
1380
        }
1381
        if ($this->SingleTo) {
1382
            foreach ($this->SingleToArray as $toAddr) {
1383
                if (!@$mail = popen($sendmail, 'w')) {
1384
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1385
                }
1386
                fputs($mail, 'To: ' . $toAddr . "\n");
1387
                fputs($mail, $header);
1388
                fputs($mail, $body);
1389
                $result = pclose($mail);
1390
                $this->doCallback(
1391 1
                    ($result == 0),
1392
                    array($toAddr),
1393
                    $this->cc,
1394 1
                    $this->bcc,
1395 1
                    $this->Subject,
1396 1
                    $body,
1397 1
                    $this->From
1398 1
                );
1399 1
                if ($result != 0) {
1400 1
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1401 1
                }
1402 1
            }
1403 1
        } else {
1404 1
            if (!@$mail = popen($sendmail, 'w')) {
1405 1
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1406 1
            }
1407
            fputs($mail, $header);
1408
            fputs($mail, $body);
1409
            $result = pclose($mail);
1410 1
            $this->doCallback(
1411
                ($result == 0),
1412
                $this->to,
1413
                $this->cc,
1414
                $this->bcc,
1415
                $this->Subject,
1416
                $body,
1417
                $this->From
1418
            );
1419
            if ($result != 0) {
1420
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1421
            }
1422 2
        }
1423
        return true;
1424 2
    }
1425 2
1426 2
    /**
1427 2
     * Send mail using the PHP mail() function.
1428 2
     * @param string $header The message headers
1429
     * @param string $body The message body
1430 2
     * @link http://www.php.net/manual/en/book.mail.php
1431
     * @throws phpmailerException
1432 2
     * @access protected
1433 2
     * @return boolean
1434 2
     */
1435 2
    protected function mailSend($header, $body)
1436 2
    {
1437 2
        $toArr = array();
1438 2
        foreach ($this->to as $toaddr) {
1439 2
            $toArr[] = $this->addrFormat($toaddr);
1440 2
        }
1441
        $to = implode(', ', $toArr);
1442
1443
        $params = null;
1444
        //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
1445
        if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1446 2
            $params = sprintf('-f%s', escapeshellarg($this->Sender));
1447 2
        }
1448
        if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
1449 2
            $old_from = ini_get('sendmail_from');
1450 2
            ini_set('sendmail_from', $this->Sender);
1451 2
        }
1452 2
        $result = false;
1453
        if ($this->SingleTo and count($toArr) > 1) {
1454
            foreach ($toArr as $toAddr) {
1455 2
                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1456
                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1457
            }
1458
        } else {
1459
            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1460
            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1461
        }
1462
        if (isset($old_from)) {
1463 35
            ini_set('sendmail_from', $old_from);
1464
        }
1465 35
        if (!$result) {
1466 35
            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1467 35
        }
1468 35
        return true;
1469
    }
1470
1471
    /**
1472
     * Get an instance to use for SMTP operations.
1473
     * Override this function to load your own SMTP implementation
1474
     * @return SMTP
1475
     */
1476
    public function getSMTPInstance()
1477
    {
1478
        if (!is_object($this->smtp)) {
1479
            $this->smtp = new SMTP;
1480
        }
1481
        return $this->smtp;
1482
    }
1483 33
1484
    /**
1485 33
     * Send mail via SMTP.
1486 33
     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1487
     * Uses the PHPMailerSMTP class by default.
1488
     * @see PHPMailer::getSMTPInstance() to use a different class.
1489 33
     * @param string $header The message headers
1490
     * @param string $body The message body
1491
     * @throws phpmailerException
1492 33
     * @uses SMTP
1493
     * @access protected
1494 33
     * @return boolean
1495
     */
1496
    protected function smtpSend($header, $body)
1497
    {
1498
        $bad_rcpt = array();
1499
        if (!$this->smtpConnect($this->SMTPOptions)) {
1500 33
            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1501 33
        }
1502 33
        if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1503
            $smtp_from = $this->Sender;
1504
        } else {
1505
            $smtp_from = $this->From;
1506
        }
1507 33
        if (!$this->smtp->mail($smtp_from)) {
1508
            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1509 33
            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1510 33
        }
1511 33
1512
        // Attempt to send to all recipients
1513
        foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1514 33
            foreach ($togroup as $to) {
1515
                if (!$this->smtp->recipient($to[0])) {
1516
                    $error = $this->smtp->getError();
1517 33
                    $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1518 1
                    $isSent = false;
1519 1
                } else {
1520 32
                    $isSent = true;
1521 32
                }
1522
                $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1523
            }
1524 33
        }
1525
1526
        // Only send the DATA command if we have viable recipients
1527
        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1528
            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1529
        }
1530
        if ($this->SMTPKeepAlive) {
1531
            $this->smtp->reset();
1532
        } else {
1533
            $this->smtp->quit();
1534 33
            $this->smtp->close();
1535
        }
1536
        //Create error message for any bad addresses
1537
        if (count($bad_rcpt) > 0) {
1538
            $errstr = '';
1539
            foreach ($bad_rcpt as $bad) {
1540
                $errstr .= $bad['to'] . ': ' . $bad['error'];
1541
            }
1542
            throw new phpmailerException(
1543
                $this->lang('recipients_failed') . $errstr,
1544
                self::STOP_CONTINUE
1545
            );
1546 35
        }
1547
        return true;
1548 35
    }
1549 35
1550 35
    /**
1551
     * Initiate a connection to an SMTP server.
1552
     * Returns false if the operation failed.
1553 35
     * @param array $options An array of options compatible with stream_context_create()
1554 2
     * @uses SMTP
1555 2
     * @access public
1556
     * @throws phpmailerException
1557
     * @return boolean
1558 35
     */
1559 1
    public function smtpConnect($options = null)
1560
    {
1561
        if (is_null($this->smtp)) {
1562 35
            $this->smtp = $this->getSMTPInstance();
1563 35
        }
1564 35
1565 35
        //If no options are provided, use whatever is set in the instance
1566 35
        if (is_null($options)) {
1567 35
            $options = $this->SMTPOptions;
1568
        }
1569 35
1570 35
        // Already connected?
1571 35
        if ($this->smtp->connected()) {
1572
            return true;
1573
        }
1574
1575
        $this->smtp->setTimeout($this->Timeout);
1576
        $this->smtp->setDebugLevel($this->SMTPDebug);
1577
        $this->smtp->setDebugOutput($this->Debugoutput);
1578
        $this->smtp->setVerp($this->do_verp);
1579
        $hosts = explode(';', $this->Host);
1580 35
        $lastexception = null;
1581 35
1582 35
        foreach ($hosts as $hostentry) {
1583 35
            $hostinfo = array();
1584 1
            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1585 1
                // Not a valid host entry
1586 1
                continue;
1587 35
            }
1588 1
            // $hostinfo[2]: optional ssl or tls prefix
1589
            // $hostinfo[3]: the hostname
1590 1
            // $hostinfo[4]: optional port number
1591 1
            // The host string prefix can temporarily override the current setting for SMTPSecure
1592
            // If it's not specified, the default value is used
1593 35
            $prefix = '';
1594 35
            $secure = $this->SMTPSecure;
1595
            $tls = ($this->SMTPSecure == 'tls');
1596 1
            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1597
                $prefix = 'ssl://';
1598
                $tls = false; // Can't have SSL and TLS at the same time
1599 1
                $secure = 'ssl';
1600 35
            } elseif ($hostinfo[2] == 'tls') {
1601 35
                $tls = true;
1602 35
                // tls doesn't use a prefix
1603 35
                $secure = 'tls';
1604 1
            }
1605 1
            //Do we need the OpenSSL extension?
1606 35
            $sslext = defined('OPENSSL_ALGO_SHA1');
1607
            if ('tls' === $secure or 'ssl' === $secure) {
1608 35
                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1609 35
                if (!$sslext) {
1610 35
                    throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1611
                }
1612
            }
1613 35
            $host = $hostinfo[3];
1614
            $port = $this->Port;
1615
            $tport = (integer)$hostinfo[4];
1616
            if ($tport > 0 and $tport < 65536) {
1617
                $port = $tport;
1618
            }
1619 35
            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1620
                try {
1621
                    if ($this->Helo) {
1622 35
                        $hello = $this->Helo;
1623
                    } else {
1624
                        $hello = $this->serverHostname();
1625
                    }
1626
                    $this->smtp->hello($hello);
1627
                    //Automatically enable TLS encryption if:
1628
                    // * it's not disabled
1629 35
                    // * we have openssl extension
1630
                    // * we are not already using SSL
1631
                    // * the server offers STARTTLS
1632
                    if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1633
                        $tls = true;
1634
                    }
1635
                    if ($tls) {
1636
                        if (!$this->smtp->startTLS()) {
1637
                            throw new phpmailerException($this->lang('connect_host'));
1638
                        }
1639
                        // We must resend EHLO after TLS negotiation
1640
                        $this->smtp->hello($hello);
1641 35
                    }
1642
                    if ($this->SMTPAuth) {
1643
                        if (!$this->smtp->authenticate(
1644
                            $this->Username,
1645
                            $this->Password,
1646
                            $this->AuthType,
1647
                            $this->Realm,
1648
                            $this->Workstation
1649 1
                        )
1650
                        ) {
1651 1
                            throw new phpmailerException($this->lang('authenticate'));
1652
                        }
1653 1
                    }
1654
                    return true;
1655
                } catch (phpmailerException $exc) {
1656 1
                    $lastexception = $exc;
1657
                    $this->edebug($exc->getMessage());
1658
                    // We must have connected, but then failed TLS or Auth, so close connection nicely
1659
                    $this->smtp->quit();
1660
                }
1661
            }
1662
        }
1663 56
        // If we get here, all connection attempts have failed, so close connection hard
1664
        $this->smtp->close();
1665 56
        // As we've caught all exceptions, just report whatever the last one was
1666 35
        if ($this->exceptions and !is_null($lastexception)) {
1667 3
            throw $lastexception;
1668 3
        }
1669 3
        return false;
1670 35
    }
1671 56
1672
    /**
1673
     * Close the active SMTP session if one exists.
1674
     * @return void
1675
     */
1676
    public function smtpClose()
1677
    {
1678
        if (is_a($this->smtp, 'SMTP')) {
1679
            if ($this->smtp->connected()) {
1680
                $this->smtp->quit();
1681
                $this->smtp->close();
1682 8
            }
1683
        }
1684
    }
1685
1686 8
    /**
1687 8
     * Set the language for error messages.
1688 8
     * Returns false if it cannot load the language file.
1689 8
     * The default language is English.
1690 8
     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1691 8
     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1692 8
     * @return boolean
1693 8
     * @access public
1694 8
     */
1695 8
    public function setLanguage($langcode = 'en', $lang_path = '')
1696 8
    {
1697 8
        // Backwards compatibility for renamed language codes
1698 8
        $renamed_langcodes = array(
1699 8
            'br' => 'pt_br',
1700 8
            'cz' => 'cs',
1701 8
            'dk' => 'da',
1702 8
            'no' => 'nb',
1703 8
            'se' => 'sv',
1704
        );
1705 8
1706 8
        if (isset($renamed_langcodes[$langcode])) {
1707
            $langcode = $renamed_langcodes[$langcode];
1708 8
        }
1709 8
1710 8
        // Define full set of translatable strings in English
1711 8
        $PHPMAILER_LANG = array(
1712
            'authenticate' => 'SMTP Error: Could not authenticate.',
1713 8
            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1714
            'data_not_accepted' => 'SMTP Error: data not accepted.',
1715 1
            'empty_message' => 'Message body empty',
1716
            'encoding' => 'Unknown encoding: ',
1717
            'execute' => 'Could not execute: ',
1718
            'file_access' => 'Could not access file: ',
1719
            'file_open' => 'File Error: Could not open file: ',
1720 1
            'from_failed' => 'The following From address failed: ',
1721
            'instantiate' => 'Could not instantiate mail function.',
1722 1
            'invalid_address' => 'Invalid address: ',
1723 8
            'mailer_not_supported' => ' mailer is not supported.',
1724 8
            'provide_address' => 'You must provide at least one recipient email address.',
1725
            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1726
            'signing' => 'Signing Error: ',
1727
            'smtp_connect_failed' => 'SMTP connect() failed.',
1728
            'smtp_error' => 'SMTP server error: ',
1729
            'variable_set' => 'Cannot set or reset variable: ',
1730
            'extension_missing' => 'Extension missing: '
1731
        );
1732
        if (empty($lang_path)) {
1733
            // Calculate an absolute path so it can work if CWD is not here
1734
            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1735
        }
1736
        //Validate $langcode
1737
        if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
1738
            $langcode = 'en';
1739
        }
1740
        $foundlang = true;
1741
        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1742
        // There is no English translation file
1743
        if ($langcode != 'en') {
1744
            // Make sure language file path is readable
1745
            if (!is_readable($lang_file)) {
1746 42
                $foundlang = false;
1747
            } else {
1748 42
                // Overwrite language-specific strings.
1749 42
                // This way we'll never have missing translation keys.
1750 42
                $foundlang = include $lang_file;
1751 42
            }
1752 42
        }
1753
        $this->language = $PHPMAILER_LANG;
1754
        return (boolean)$foundlang; // Returns false if language not found
1755
    }
1756
1757
    /**
1758
     * Get the array of strings for the current language.
1759
     * @return array
1760
     */
1761
    public function getTranslations()
1762 42
    {
1763
        return $this->language;
1764 42
    }
1765 3
1766
    /**
1767 42
     * Create recipient headers.
1768 42
     * @access public
1769 42
     * @param string $type
1770
     * @param array $addr An array of recipient,
1771
     * where each recipient is a 2-element indexed array with element 0 containing an address
1772
     * and element 1 containing a name, like:
1773
     * array(array('[email protected]', 'Joe User'), array('[email protected]', 'Zoe User'))
1774
     * @return string
1775
     */
1776
    public function addrAppend($type, $addr)
1777
    {
1778
        $addresses = array();
1779
        foreach ($addr as $address) {
1780
            $addresses[] = $this->addrFormat($address);
1781
        }
1782
        return $type . ': ' . implode(', ', $addresses) . $this->LE;
1783
    }
1784 4
1785
    /**
1786 4
     * Format an address for use in a message header.
1787
     * @access public
1788
     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1789 4
     *      like array('[email protected]', 'Joe User')
1790
     * @return string
1791
     */
1792
    public function addrFormat($addr)
1793 4
    {
1794 4
        if (empty($addr[1])) { // No name provided
1795 4
            return $this->secureHeader($addr[0]);
1796
        } else {
1797 4
            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1798
                $addr[0]
1799 4
            ) . '>';
1800 2
        }
1801 2
    }
1802
1803
    /**
1804 4
     * Word-wrap message.
1805
     * For use with mailers that do not automatically perform wrapping
1806 4
     * and for quoted-printable encoded messages.
1807 4
     * Original written by philippe.
1808 4
     * @param string $message The message to wrap
1809 4
     * @param integer $length The line length to wrap to
1810 4
     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1811 4
     * @access public
1812 4
     * @return string
1813
     */
1814
    public function wrapText($message, $length, $qp_mode = false)
1815
    {
1816
        if ($qp_mode) {
1817
            $soft_break = sprintf(' =%s', $this->LE);
1818
        } else {
1819
            $soft_break = $this->LE;
1820
        }
1821
        // If utf-8 encoding is used, we will need to make sure we don't
1822
        // split multibyte characters when we wrap
1823
        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1824
        $lelen = strlen($this->LE);
1825
        $crlflen = strlen(self::CRLF);
1826
1827
        $message = $this->fixEOL($message);
1828
        //Remove a trailing line break
1829
        if (substr($message, -$lelen) == $this->LE) {
1830
            $message = substr($message, 0, -$lelen);
1831
        }
1832
1833
        //Split message into lines
1834
        $lines = explode($this->LE, $message);
1835
        //Message will be rebuilt in here
1836
        $message = '';
1837
        foreach ($lines as $line) {
1838
            $words = explode(' ', $line);
1839
            $buf = '';
1840
            $firstword = true;
1841
            foreach ($words as $word) {
1842
                if ($qp_mode and (strlen($word) > $length)) {
1843
                    $space_left = $length - strlen($buf) - $crlflen;
1844
                    if (!$firstword) {
1845
                        if ($space_left > 20) {
1846
                            $len = $space_left;
1847
                            if ($is_utf8) {
1848
                                $len = $this->utf8CharBoundary($word, $len);
1849
                            } elseif (substr($word, $len - 1, 1) == '=') {
1850
                                $len--;
1851
                            } elseif (substr($word, $len - 2, 1) == '=') {
1852
                                $len -= 2;
1853
                            }
1854
                            $part = substr($word, 0, $len);
1855 4
                            $word = substr($word, $len);
1856 4
                            $buf .= ' ' . $part;
1857 4
                            $message .= $buf . sprintf('=%s', self::CRLF);
1858 4
                        } else {
1859 4
                            $message .= $buf . $soft_break;
1860
                        }
1861 4
                        $buf = '';
1862 4
                    }
1863 4
                    while (strlen($word) > 0) {
1864 4
                        if ($length <= 0) {
1865
                            break;
1866 4
                        }
1867 4
                        $len = $length;
1868 4
                        if ($is_utf8) {
1869 4
                            $len = $this->utf8CharBoundary($word, $len);
1870
                        } elseif (substr($word, $len - 1, 1) == '=') {
1871 4
                            $len--;
1872
                        } elseif (substr($word, $len - 2, 1) == '=') {
1873
                            $len -= 2;
1874
                        }
1875
                        $part = substr($word, 0, $len);
1876
                        $word = substr($word, $len);
1877
1878
                        if (strlen($word) > 0) {
1879
                            $message .= $part . sprintf('=%s', self::CRLF);
1880
                        } else {
1881
                            $buf = $part;
1882
                        }
1883
                    }
1884
                } else {
1885
                    $buf_o = $buf;
1886
                    if (!$firstword) {
1887
                        $buf .= ' ';
1888
                    }
1889
                    $buf .= $word;
1890
1891
                    if (strlen($buf) > $length and $buf_o != '') {
1892
                        $message .= $buf_o . $soft_break;
1893
                        $buf = $word;
1894
                    }
1895
                }
1896
                $firstword = false;
1897
            }
1898
            $message .= $buf . self::CRLF;
1899
        }
1900
1901
        return $message;
1902
    }
1903
1904
    /**
1905
     * Find the last character boundary prior to $maxLength in a utf-8
1906
     * quoted-printable encoded string.
1907
     * Original written by Colin Brown.
1908
     * @access public
1909
     * @param string $encodedText utf-8 QP text
1910
     * @param integer $maxLength Find the last character boundary prior to this length
1911
     * @return integer
1912
     */
1913
    public function utf8CharBoundary($encodedText, $maxLength)
1914
    {
1915
        $foundSplitPos = false;
1916
        $lookBack = 3;
1917
        while (!$foundSplitPos) {
1918
            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1919
            $encodedCharPos = strpos($lastChunk, '=');
1920
            if (false !== $encodedCharPos) {
1921
                // Found start of encoded character byte within $lookBack block.
1922
                // Check the encoded byte value (the 2 chars after the '=')
1923
                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1924
                $dec = hexdec($hex);
1925
                if ($dec < 128) {
1926
                    // Single byte character.
1927
                    // If the encoded char was found at pos 0, it will fit
1928 41
                    // otherwise reduce maxLength to start of the encoded char
1929
                    if ($encodedCharPos > 0) {
1930 41
                        $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1931 38
                    }
1932
                    $foundSplitPos = true;
1933
                } elseif ($dec >= 192) {
1934 3
                    // First byte of a multi byte character
1935 3
                    // Reduce maxLength to split at start of character
1936 3
                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1937 3
                    $foundSplitPos = true;
1938 3
                } elseif ($dec < 192) {
1939 1
                    // Middle byte of a multi byte character, look further back
1940 1
                    $lookBack += 3;
1941 2
                }
1942 2
            } else {
1943 2
                // No encoded character found
1944 3
                $foundSplitPos = true;
1945 3
            }
1946
        }
1947
        return $maxLength;
1948
    }
1949
1950
    /**
1951
     * Apply word wrapping to the message body.
1952 42
     * Wraps the message body to the number of chars set in the WordWrap property.
1953
     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
1954 42
     * This is called automatically by createBody(), so you don't need to call it yourself.
1955
     * @access public
1956 42
     * @return void
1957 42
     */
1958 42
    public function setWordWrap()
1959 42
    {
1960
        if ($this->WordWrap < 1) {
1961
            return;
1962 42
        }
1963
1964
        switch ($this->message_type) {
1965
            case 'alt':
1966
            case 'alt_inline':
1967
            case 'alt_attach':
1968
            case 'alt_inline_attach':
1969 42
                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1970 41
                break;
1971 39
            default:
1972 39
                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1973 42
                break;
1974 1
        }
1975 1
    }
1976
1977
    /**
1978 42
     * Assemble message headers.
1979
     * @access public
1980
     * @return string The assembled headers
1981 42
     */
1982 39
    public function createHeader()
1983 39
    {
1984
        $result = '';
1985
1986
        if ($this->MessageDate == '') {
1987 42
            $this->MessageDate = self::rfcDate();
1988 40
        }
1989 42
        $result .= $this->headerLine('Date', $this->MessageDate);
1990 42
1991
        // To be created automatically by mail()
1992
        if ($this->SingleTo) {
1993
            if ($this->Mailer != 'mail') {
1994 42
                foreach ($this->to as $toaddr) {
1995 42
                    $this->SingleToArray[] = $this->addrFormat($toaddr);
1996 42
                }
1997
            }
1998
        } else {
1999 42
            if (count($this->to) > 0) {
2000 40
                if ($this->Mailer != 'mail') {
2001 40
                    $result .= $this->addrAppend('To', $this->to);
2002
                }
2003 42
            } elseif (count($this->cc) == 0) {
2004 1
                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
2005 1
            }
2006 42
        }
2007
2008 42
        $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
2009 42
2010 42
        // sendmail and mail() extract Cc from the header before sending
2011 42
        if (count($this->cc) > 0) {
2012 42
            $result .= $this->addrAppend('Cc', $this->cc);
2013 42
        }
2014 42
2015 42
        // sendmail and mail() extract Bcc from the header before sending
2016 42
        if ((
2017 42
                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
2018
            )
2019
            and count($this->bcc) > 0
2020
        ) {
2021
            $result .= $this->addrAppend('Bcc', $this->bcc);
2022
        }
2023
2024 42
        if (count($this->ReplyTo) > 0) {
2025 1
            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
2026 1
        }
2027
2028
        // mail() sets the subject itself
2029 42
        if ($this->Mailer != 'mail') {
2030
            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2031
        }
2032
2033
        // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
2034 42
        // https://tools.ietf.org/html/rfc5322#section-3.6.4
2035 42
        if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
2036 40
            $this->lastMessageID = $this->MessageID;
2037 40
        } else {
2038 40
            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
2039
        }
2040 42
        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
2041
        if (!is_null($this->Priority)) {
2042
            $result .= $this->headerLine('X-Priority', $this->Priority);
2043
        }
2044
        if ($this->XMailer == '') {
2045
            $result .= $this->headerLine(
2046
                'X-Mailer',
2047
                'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
2048 42
            );
2049
        } else {
2050 42
            $myXmailer = trim($this->XMailer);
2051 42
            if ($myXmailer) {
2052 42
                $result .= $this->headerLine('X-Mailer', $myXmailer);
2053 42
            }
2054 3
        }
2055 3
2056 3
        if ($this->ConfirmReadingTo != '') {
2057 39
            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
2058 39
        }
2059 39
2060 39
        // Add custom headers
2061 6
        foreach ($this->CustomHeader as $header) {
2062 6
            $result .= $this->headerLine(
2063 6
                trim($header[0]),
2064 33
                $this->encodeHeader(trim($header[1]))
2065 33
            );
2066 6
        }
2067 6
        if (!$this->sign_key_file) {
2068 6
            $result .= $this->headerLine('MIME-Version', '1.0');
2069 27
            $result .= $this->getMailMIME();
2070
        }
2071 27
2072 27
        return $result;
2073 27
    }
2074 42
2075
    /**
2076 42
     * Get the message MIME type headers.
2077
     * @access public
2078 24
     * @return string
2079 15
     */
2080 15
    public function getMailMIME()
2081 15
    {
2082
        $result = '';
2083 15
        $ismultipart = true;
2084 9
        switch ($this->message_type) {
2085
            case 'inline':
2086 24
                $result .= $this->headerLine('Content-Type', 'multipart/related;');
2087
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2088 42
                break;
2089 40
            case 'attach':
2090 40
            case 'inline_attach':
2091
            case 'alt_attach':
2092 42
            case 'alt_inline_attach':
2093
                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2094
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2095
                break;
2096
            case 'alt':
2097
            case 'alt_inline':
2098
                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2099
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2100
                break;
2101
            default:
2102
                // Catches case 'plain': and case '':
2103 9
                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2104
                $ismultipart = false;
2105 9
                break;
2106
        }
2107
        // RFC1341 part 5 says 7bit is assumed if not specified
2108
        if ($this->Encoding != '7bit') {
2109
            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2110
            if ($ismultipart) {
2111
                if ($this->Encoding == '8bit') {
2112
                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2113
                }
2114
                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2115 41
            } else {
2116
                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2117 41
            }
2118
        }
2119 41
2120 41
        if ($this->Mailer != 'mail') {
2121 41
            $result .= $this->LE;
2122 41
        }
2123
2124 41
        return $result;
2125 2
    }
2126 2
2127
    /**
2128 41
     * Returns the whole MIME message.
2129
     * Includes complete headers and body.
2130 41
     * Only valid post preSend().
2131 41
     * @see PHPMailer::preSend()
2132
     * @access public
2133 41
     * @return string
2134 33
     */
2135
    public function getSentMIMEMessage()
2136 33
    {
2137 33
        return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
2138
    }
2139
2140 41
    /**
2141 2
     * Create unique ID
2142 2
     * @return string
2143
     */
2144 41
    protected function generateId() {
2145 41
        return md5(uniqid(time()));
2146
    }
2147 41
2148 37
    /**
2149
     * Assemble the message body.
2150 37
     * Returns an empty string on failure.
2151 37
     * @access public
2152
     * @throws phpmailerException
2153
     * @return string The assembled message body
2154 41
     */
2155 1
    public function createBody()
2156 1
    {
2157
        $body = '';
2158 41
        //Create unique IDs and preset boundaries
2159 41
        $this->uniqueid = $this->generateId();
2160 41
        $this->boundary[1] = 'b1_' . $this->uniqueid;
2161 3
        $this->boundary[2] = 'b2_' . $this->uniqueid;
2162 3
        $this->boundary[3] = 'b3_' . $this->uniqueid;
2163 3
2164 3
        if ($this->sign_key_file) {
2165 3
            $body .= $this->getMailMIME() . $this->LE;
2166 3
        }
2167 38
2168 4
        $this->setWordWrap();
2169 4
2170 4
        $bodyEncoding = $this->Encoding;
2171 4
        $bodyCharSet = $this->CharSet;
2172 4
        //Can we do a 7-bit downgrade?
2173 4
        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2174 34
            $bodyEncoding = '7bit';
2175 1
            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2176 1
            $bodyCharSet = 'us-ascii';
2177 1
        }
2178 1
        //If lines are too long, and we're not already using an encoding that will shorten them,
2179 1
        //change to quoted-printable transfer encoding for the body part only
2180 1
        if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
2181 1
            $bodyEncoding = 'quoted-printable';
2182 1
        }
2183 1
2184 1
        $altBodyEncoding = $this->Encoding;
2185 1
        $altBodyCharSet = $this->CharSet;
2186 1
        //Can we do a 7-bit downgrade?
2187 33
        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2188 4
            $altBodyEncoding = '7bit';
2189 4
            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2190 4
            $altBodyCharSet = 'us-ascii';
2191 4
        }
2192 4
        //If lines are too long, and we're not already using an encoding that will shorten them,
2193 4
        //change to quoted-printable transfer encoding for the alt body part only
2194 4
        if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
2195 4
            $altBodyEncoding = 'quoted-printable';
2196 1
        }
2197 1
        //Use this as a preamble in all multipart message types
2198 1
        $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
2199 1
        switch ($this->message_type) {
2200 4
            case 'inline':
2201 4
                $body .= $mimepre;
2202 29
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2203 2
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2204 2
                $body .= $this->LE . $this->LE;
2205 2
                $body .= $this->attachAll('inline', $this->boundary[1]);
2206 2
                break;
2207 2
            case 'attach':
2208 2
                $body .= $mimepre;
2209 2
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2210 2
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2211 2
                $body .= $this->LE . $this->LE;
2212 2
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2213 2
                break;
2214 2
            case 'inline_attach':
2215 2
                $body .= $mimepre;
2216 2
                $body .= $this->textLine('--' . $this->boundary[1]);
2217 2
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2218 27
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2219 1
                $body .= $this->LE;
2220 1
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2221 1
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2222 1
                $body .= $this->LE . $this->LE;
2223 1
                $body .= $this->attachAll('inline', $this->boundary[2]);
2224 1
                $body .= $this->LE;
2225 1
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2226 1
                break;
2227 1
            case 'alt':
2228 1
                $body .= $mimepre;
2229 1
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2230 1
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2231 1
                $body .= $this->LE . $this->LE;
2232 1
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2233 1
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2234 26
                $body .= $this->LE . $this->LE;
2235
                if (!empty($this->Ical)) {
2236
                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2237
                    $body .= $this->encodeString($this->Ical, $this->Encoding);
2238
                    $body .= $this->LE . $this->LE;
2239
                }
2240
                $body .= $this->endBoundary($this->boundary[1]);
2241
                break;
2242
            case 'alt_inline':
2243
                $body .= $mimepre;
2244
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2245
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2246
                $body .= $this->LE . $this->LE;
2247
                $body .= $this->textLine('--' . $this->boundary[1]);
2248
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2249
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2250
                $body .= $this->LE;
2251
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2252
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2253
                $body .= $this->LE . $this->LE;
2254
                $body .= $this->attachAll('inline', $this->boundary[2]);
2255
                $body .= $this->LE;
2256 26
                $body .= $this->endBoundary($this->boundary[1]);
2257
                break;
2258
            case 'alt_attach':
2259 26
                $body .= $mimepre;
2260 26
                $body .= $this->textLine('--' . $this->boundary[1]);
2261 26
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2262 41
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2263
                $body .= $this->LE;
2264 41
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2265
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2266 41
                $body .= $this->LE . $this->LE;
2267
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2268 2
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2269
                $body .= $this->LE . $this->LE;
2270
                $body .= $this->endBoundary($this->boundary[2]);
2271
                $body .= $this->LE;
2272 2
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2273 2
                break;
2274
            case 'alt_inline_attach':
2275
                $body .= $mimepre;
2276 2
                $body .= $this->textLine('--' . $this->boundary[1]);
2277
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2278 2
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2279 1
                $body .= $this->LE;
2280 1
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2281 1
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2282 1
                $body .= $this->LE . $this->LE;
2283 1
                $body .= $this->textLine('--' . $this->boundary[2]);
2284
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2285 1
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2286 1
                $body .= $this->LE;
2287 1
                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2288 1
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2289 1
                $body .= $this->LE . $this->LE;
2290 1
                $body .= $this->attachAll('inline', $this->boundary[3]);
2291 1
                $body .= $this->LE;
2292 1
                $body .= $this->endBoundary($this->boundary[2]);
2293 1
                $body .= $this->LE;
2294 1
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2295 1
                break;
2296
            default:
2297 2
                // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
2298 2
                //Reset the `Encoding` property in case we changed it for line length reasons
2299 2
                $this->Encoding = $bodyEncoding;
2300 2
                $body .= $this->encodeString($this->Body, $this->Encoding);
2301
                break;
2302 2
        }
2303 2
2304 2
        if ($this->isError()) {
2305 2
            $body = '';
2306
        } elseif ($this->sign_key_file) {
2307
            try {
2308
                if (!defined('PKCS7_TEXT')) {
2309
                    throw new phpmailerException($this->lang('extension_missing') . 'openssl');
2310 2
                }
2311
                // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2312
                $file = tempnam(sys_get_temp_dir(), 'mail');
2313
                if (false === file_put_contents($file, $body)) {
2314
                    throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2315
                }
2316 2
                $signed = tempnam(sys_get_temp_dir(), 'signed');
2317 41
                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2318
                if (empty($this->sign_extracerts_file)) {
2319
                    $sign = @openssl_pkcs7_sign(
2320
                        $file,
2321
                        $signed,
2322
                        'file://' . realpath($this->sign_cert_file),
2323
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2324
                        null
2325
                    );
2326
                } else {
2327
                    $sign = @openssl_pkcs7_sign(
2328
                        $file,
2329 15
                        $signed,
2330
                        'file://' . realpath($this->sign_cert_file),
2331 15
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2332 15
                        null,
2333 1
                        PKCS7_DETACHED,
2334 1
                        $this->sign_extracerts_file
2335 15
                    );
2336 8
                }
2337 8
                if ($sign) {
2338 15
                    @unlink($file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2339 1
                    $body = file_get_contents($signed);
2340 1
                    @unlink($signed);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2341 15
                    //The message returned by openssl contains both headers and body, so need to split them up
2342 15
                    $parts = explode("\n\n", $body, 2);
2343 15
                    $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2344
                    $body = $parts[1];
2345 15
                } else {
2346 5
                    @unlink($file);
2347 5
                    @unlink($signed);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2348 15
                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
2349
                }
2350 15
            } catch (phpmailerException $exc) {
2351
                $body = '';
2352
                if ($this->exceptions) {
2353
                    throw $exc;
2354
                }
2355
            }
2356
        }
2357
        return $body;
2358
    }
2359 7
2360
    /**
2361 7
     * Return the start of a message boundary.
2362
     * @access protected
2363
     * @param string $boundary
2364
     * @param string $charSet
2365
     * @param string $contentType
2366
     * @param string $encoding
2367
     * @return string
2368
     */
2369
    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2370 41
    {
2371
        $result = '';
2372 41
        if ($charSet == '') {
2373 41
            $charSet = $this->CharSet;
2374 7
        }
2375 7
        if ($contentType == '') {
2376 41
            $contentType = $this->ContentType;
2377 6
        }
2378 6
        if ($encoding == '') {
2379 41
            $encoding = $this->Encoding;
2380 6
        }
2381 6
        $result .= $this->textLine('--' . $boundary);
2382 41
        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2383 41
        $result .= $this->LE;
2384
        // RFC1341 part 5 says 7bit is assumed if not specified
2385 26
        if ($encoding != '7bit') {
2386 26
            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2387 41
        }
2388
        $result .= $this->LE;
2389
2390
        return $result;
2391
    }
2392
2393
    /**
2394
     * Return the end of a message boundary.
2395
     * @access protected
2396 42
     * @param string $boundary
2397
     * @return string
2398 42
     */
2399
    protected function endBoundary($boundary)
2400
    {
2401
        return $this->LE . '--' . $boundary . '--' . $this->LE;
2402
    }
2403
2404
    /**
2405
     * Set the message type.
2406
     * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2407 42
     * @access protected
2408
     * @return void
2409 42
     */
2410
    protected function setMessageType()
2411
    {
2412
        $type = array();
2413
        if ($this->alternativeExists()) {
2414
            $type[] = 'alt';
2415
        }
2416
        if ($this->inlineImageExists()) {
2417
            $type[] = 'inline';
2418
        }
2419
        if ($this->attachmentExists()) {
2420
            $type[] = 'attach';
2421
        }
2422
        $this->message_type = implode('_', $type);
2423 5
        if ($this->message_type == '') {
2424
            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2425
            $this->message_type = 'plain';
2426 5
        }
2427 1
    }
2428
2429
    /**
2430
     * Format a header line.
2431 5
     * @access public
2432 5
     * @param string $name
2433 5
     * @param string $value
2434
     * @return string
2435 5
     */
2436 5
    public function headerLine($name, $value)
2437 1
    {
2438 1
        return $name . ': ' . $value . $this->LE;
2439
    }
2440 5
2441 5
    /**
2442 5
     * Return a formatted mail line.
2443 5
     * @access public
2444 5
     * @param string $value
2445 5
     * @return string
2446 5
     */
2447 5
    public function textLine($value)
2448
    {
2449 5
        return $value . $this->LE;
2450
    }
2451 5
2452 1
    /**
2453 1
     * Add an attachment from a path on the filesystem.
2454 1
     * Returns false if the file could not be found or read.
2455
     * @param string $path Path to the attachment.
2456
     * @param string $name Overrides the attachment name.
2457 1
     * @param string $encoding File encoding (see $Encoding).
2458
     * @param string $type File extension (MIME) type.
2459 5
     * @param string $disposition Disposition to use
2460
     * @throws phpmailerException
2461
     * @return boolean
2462
     */
2463
    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2464
    {
2465
        try {
2466 40
            if (!@is_file($path)) {
2467
                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2468 40
            }
2469
2470
            // If a MIME type is not specified, try to work it out from the file name
2471
            if ($type == '') {
2472
                $type = self::filenameToType($path);
2473
            }
2474
2475
            $filename = basename($path);
2476
            if ($name == '') {
2477
                $name = $filename;
2478
            }
2479 11
2480
            $this->attachment[] = array(
2481
                0 => $path,
2482 11
                1 => $filename,
2483 11
                2 => $name,
2484 11
                3 => $encoding,
2485
                4 => $type,
2486
                5 => false, // isStringAttachment
2487 11
                6 => $disposition,
2488
                7 => 0
2489 11
            );
2490
2491 11
        } catch (phpmailerException $exc) {
2492 11
            $this->setError($exc->getMessage());
2493 11
            $this->edebug($exc->getMessage());
2494 11
            if ($this->exceptions) {
2495 2
                throw $exc;
2496 2
            }
2497 9
            return false;
2498
        }
2499
        return true;
2500 11
    }
2501 11
2502 1
    /**
2503
     * Return the array of attachments.
2504 11
     * @return array
2505 11
     */
2506 11
    public function getAttachments()
2507 11
    {
2508 11
        return $this->attachment;
2509 11
    }
2510 11
2511
    /**
2512
     * Attach all file, string, and binary attachments to the message.
2513 11
     * Returns an empty string on failure.
2514
     * @access protected
2515 11
     * @param string $disposition_type
2516
     * @param string $boundary
2517 11
     * @return string
2518 10
     */
2519 10
    protected function attachAll($disposition_type, $boundary)
2520 10
    {
2521 10
        // Return text of body
2522 10
        $mime = array();
2523 10
        $cidUniq = array();
2524 10
        $incl = array();
2525 1
2526 1
        // Add all attachments
2527 1
        foreach ($this->attachment as $attachment) {
2528 1
            // Check if it is a valid disposition_filter
2529 1
            if ($attachment[6] == $disposition_type) {
2530
                // Check for string attachment
2531
                $string = '';
2532 11
                $path = '';
2533 11
                $bString = $attachment[5];
2534 11
                if ($bString) {
2535
                    $string = $attachment[0];
2536 11
                } else {
2537 6
                    $path = $attachment[0];
2538 6
                }
2539
2540
                $inclhash = md5(serialize($attachment));
2541
                if (in_array($inclhash, $incl)) {
2542
                    continue;
2543
                }
2544 11
                $incl[] = $inclhash;
2545 11
                $name = $attachment[2];
2546 11
                $encoding = $attachment[3];
2547
                $type = $attachment[4];
2548
                $disposition = $attachment[6];
2549
                $cid = $attachment[7];
2550
                if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
2551
                    continue;
2552
                }
2553
                $cidUniq[$cid] = true;
2554 11
2555 10
                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2556 10
                //Only include a filename property if we have one
2557 10
                if (!empty($name)) {
2558 10
                    $mime[] = sprintf(
2559 10
                        'Content-Type: %s; name="%s"%s',
2560 10
                        $type,
2561 10
                        $this->encodeHeader($this->secureHeader($name)),
2562 1
                        $this->LE
2563 1
                    );
2564 1
                } else {
2565 1
                    $mime[] = sprintf(
2566 1
                        'Content-Type: %s%s',
2567
                        $type,
2568
                        $this->LE
2569 11
                    );
2570
                }
2571
                // RFC1341 part 5 says 7bit is assumed if not specified
2572
                if ($encoding != '7bit') {
2573
                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2574 11
                }
2575 2
2576 2
                if ($disposition == 'inline') {
2577
                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2578
                }
2579 2
2580 2
                // If a filename contains any of these chars, it should be quoted,
2581 9
                // but not otherwise: RFC2183 & RFC2045 5.1
2582 9
                // Fixes a warning in IETF's msglint MIME checker
2583
                // Allow for bypassing the Content-Disposition header totally
2584
                if (!(empty($disposition))) {
2585 9
                    $encoded_name = $this->encodeHeader($this->secureHeader($name));
2586
                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2587 11
                        $mime[] = sprintf(
2588 11
                            'Content-Disposition: %s; filename="%s"%s',
2589
                            $disposition,
2590 11
                            $encoded_name,
2591
                            $this->LE . $this->LE
2592 11
                        );
2593
                    } else {
2594
                        if (!empty($encoded_name)) {
2595
                            $mime[] = sprintf(
2596
                                'Content-Disposition: %s; filename=%s%s',
2597
                                $disposition,
2598
                                $encoded_name,
2599
                                $this->LE . $this->LE
2600
                            );
2601
                        } else {
2602
                            $mime[] = sprintf(
2603
                                'Content-Disposition: %s%s',
2604 9
                                $disposition,
2605
                                $this->LE . $this->LE
2606
                            );
2607 9
                        }
2608
                    }
2609
                } else {
2610 9
                    $mime[] = $this->LE;
2611 9
                }
2612
2613
                // Encode as string attachment
2614
                if ($bString) {
2615
                    $mime[] = $this->encodeString($string, $encoding);
2616
                    if ($this->isError()) {
2617
                        return '';
2618
                    }
2619
                    $mime[] = $this->LE . $this->LE;
2620
                } else {
2621 9
                    $mime[] = $this->encodeFile($path, $encoding);
2622 9
                    if ($this->isError()) {
2623 9
                        return '';
2624
                    }
2625
                    $mime[] = $this->LE . $this->LE;
2626
                }
2627
            }
2628
        }
2629
2630 9
        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2631
2632
        return implode('', $mime);
2633
    }
2634
2635
    /**
2636
     * Encode a file attachment in requested format.
2637
     * Returns an empty string on failure.
2638
     * @param string $path The full path to the file
2639
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2640
     * @throws phpmailerException
2641
     * @access protected
2642
     * @return string
2643
     */
2644
    protected function encodeFile($path, $encoding = 'base64')
2645 41
    {
2646
        try {
2647 41
            if (!is_readable($path)) {
2648 41
                throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2649 41
            }
2650 12
            $magic_quotes = get_magic_quotes_runtime();
2651 12
            if ($magic_quotes) {
2652 40
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2653 40
                    set_magic_quotes_runtime(false);
2654 37
                } else {
2655
                    //Doesn't exist in PHP 5.4, but we don't need to check because
2656 37
                    //get_magic_quotes_runtime always returns false in 5.4+
2657 6
                    //so it will never get here
2658 6
                    ini_set('magic_quotes_runtime', false);
2659 37
                }
2660 3
            }
2661
            $file_buffer = file_get_contents($path);
2662
            $file_buffer = $this->encodeString($file_buffer, $encoding);
2663 3
            if ($magic_quotes) {
2664 3
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2665 3
                    set_magic_quotes_runtime($magic_quotes);
2666
                } else {
2667
                    ini_set('magic_quotes_runtime', $magic_quotes);
2668
                }
2669 41
            }
2670 41
            return $file_buffer;
2671
        } catch (Exception $exc) {
2672
            $this->setError($exc->getMessage());
2673
            return '';
2674
        }
2675
    }
2676
2677
    /**
2678
     * Encode a string in requested format.
2679
     * Returns an empty string on failure.
2680
     * @param string $str The text to encode
2681 42
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2682
     * @access public
2683 42
     * @return string
2684 42
     */
2685 42
    public function encodeString($str, $encoding = 'base64')
2686 42
    {
2687
        $encoded = '';
2688 42
        switch (strtolower($encoding)) {
2689 42
            case 'base64':
2690 42
                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2691
                break;
2692 2
            case '7bit':
2693
            case '8bit':
2694
                $encoded = $this->fixEOL($str);
2695
                // Make sure it ends with a line break
2696
                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2697
                    $encoded .= $this->LE;
2698 42
                }
2699
                break;
2700
            case 'binary':
2701 42
                $encoded = $str;
2702 42
                break;
2703 42
            case 'quoted-printable':
2704 42
                $encoded = $this->encodeQP($str);
2705 42
                break;
2706
            default:
2707
                $this->setError($this->lang('encoding') . $encoding);
2708 42
                break;
2709 42
        }
2710
        return $encoded;
2711
    }
2712
2713
    /**
2714
     * Encode a header string optimally.
2715
     * Picks shortest of Q, B, quoted-printable or none.
2716
     * @access public
2717
     * @param string $str
2718
     * @param string $position
2719
     * @return string
2720
     */
2721
    public function encodeHeader($str, $position = 'text')
2722
    {
2723
        $matchcount = 0;
2724
        switch (strtolower($position)) {
2725
            case 'phrase':
2726
                if (!preg_match('/[\200-\377]/', $str)) {
2727
                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
2728
                    $encoded = addcslashes($str, "\0..\37\177\\\"");
2729
                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2730
                        return ($encoded);
2731
                    } else {
2732
                        return ("\"$encoded\"");
2733
                    }
2734
                }
2735
                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2736
                break;
2737
            /** @noinspection PhpMissingBreakStatementInspection */
2738
            case 'comment':
2739
                $matchcount = preg_match_all('/[()"]/', $str, $matches);
2740
                // Intentional fall-through
2741
            case 'text':
2742
            default:
2743
                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2744
                break;
2745
        }
2746
2747
        //There are no chars that need encoding
2748
        if ($matchcount == 0) {
2749
            return ($str);
2750
        }
2751
2752
        $maxlen = 75 - 7 - strlen($this->CharSet);
2753
        // Try to select the encoding which should produce the shortest output
2754
        if ($matchcount > strlen($str) / 3) {
2755
            // More than a third of the content will need encoding, so B encoding will be most efficient
2756
            $encoding = 'B';
2757
            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2758
                // Use a custom function which correctly encodes and wraps long
2759 56
                // multibyte strings without breaking lines within a character
2760
                $encoded = $this->base64EncodeWrapMB($str, "\n");
2761 56
            } else {
2762
                $encoded = base64_encode($str);
2763
                $maxlen -= $maxlen % 4;
2764
                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2765
            }
2766
        } else {
2767
            $encoding = 'Q';
2768
            $encoded = $this->encodeQ($str, $position);
2769
            $encoded = $this->wrapText($encoded, $maxlen, true);
2770
            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2771
        }
2772
2773
        $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2774
        $encoded = trim(str_replace("\n", $this->LE, $encoded));
2775
2776
        return $encoded;
2777
    }
2778
2779
    /**
2780
     * Check if a string contains multi-byte characters.
2781
     * @access public
2782
     * @param string $str multi-byte text to wrap encode
2783
     * @return boolean
2784
     */
2785
    public function hasMultiBytes($str)
2786
    {
2787
        if (function_exists('mb_strlen')) {
2788
            return (strlen($str) > mb_strlen($str, $this->CharSet));
2789
        } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2790
            return false;
2791
        }
2792
    }
2793
2794
    /**
2795
     * Does a string contain any 8-bit chars (in any charset)?
2796
     * @param string $text
2797
     * @return boolean
2798
     */
2799
    public function has8bitChars($text)
2800
    {
2801
        return (boolean)preg_match('/[\x80-\xFF]/', $text);
2802
    }
2803
2804
    /**
2805
     * Encode and wrap long multibyte strings for mail headers
2806
     * without breaking lines within a character.
2807
     * Adapted from a function by paravoid
2808
     * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2809
     * @access public
2810
     * @param string $str multi-byte text to wrap encode
2811
     * @param string $linebreak string to use as linefeed/end-of-line
2812
     * @return string
2813
     */
2814
    public function base64EncodeWrapMB($str, $linebreak = null)
2815
    {
2816 3
        $start = '=?' . $this->CharSet . '?B?';
2817
        $end = '?=';
2818
        $encoded = '';
2819 3
        if ($linebreak === null) {
2820 3
            $linebreak = $this->LE;
2821
        }
2822
2823
        $mb_length = mb_strlen($str, $this->CharSet);
2824
        // Each line must have length <= 75, including $start and $end
2825
        $length = 75 - strlen($start) - strlen($end);
2826
        // Average multi-byte ratio
2827
        $ratio = $mb_length / strlen($str);
2828
        // Base64 has a 4:3 ratio
2829
        $avgLength = floor($length * $ratio * .75);
2830
2831
        for ($i = 0; $i < $mb_length; $i += $offset) {
2832
            $lookBack = 0;
2833
            do {
2834
                $offset = $avgLength - $lookBack;
2835
                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2836
                $chunk = base64_encode($chunk);
2837
                $lookBack++;
2838
            } while (strlen($chunk) > $length);
2839
            $encoded .= $chunk . $linebreak;
2840
        }
2841
2842
        // Chomp the last linefeed
2843
        $encoded = substr($encoded, 0, -strlen($linebreak));
2844
        return $encoded;
2845
    }
2846
2847
    /**
2848
     * Encode a string in quoted-printable format.
2849
     * According to RFC2045 section 6.7.
2850
     * @access public
2851
     * @param string $string The text to encode
2852
     * @param integer $line_max Number of chars allowed on a line before wrapping
2853
     * @return string
2854
     * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2855
     */
2856
    public function encodeQP($string, $line_max = 76)
2857 1
    {
2858
        // Use native function if it's available (>= PHP5.3)
2859
        if (function_exists('quoted_printable_encode')) {
2860 1
            return quoted_printable_encode($string);
2861 1
        }
2862 1
        // Fall back to a pure PHP implementation
2863 1
        $string = str_replace(
2864
            array('%20', '%0D%0A.', '%0D%0A', '%'),
2865 1
            array(' ', "\r\n=2E", "\r\n", '='),
2866 1
            rawurlencode($string)
2867
        );
2868 1
        return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2869
    }
2870 1
2871
    /**
2872
     * Backward compatibility wrapper for an old QP encoding function that was removed.
2873 1
     * @see PHPMailer::encodeQP()
2874 1
     * @access public
2875
     * @param string $string
2876
     * @param integer $line_max
2877 1
     * @param boolean $space_conv
2878 1
     * @return string
2879 1
     * @deprecated Use encodeQP instead.
2880 1
     */
2881 1
    public function encodeQPphp(
2882
        $string,
2883
        $line_max = 76,
2884 1
        /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2885 1
    ) {
2886 1
        return $this->encodeQP($string, $line_max);
2887 1
    }
2888 1
2889 1
    /**
2890 1
     * Encode a string using Q encoding.
2891 1
     * @link http://tools.ietf.org/html/rfc2047
2892 1
     * @param string $str the text to encode
2893
     * @param string $position Where the text is going to be used, see the RFC for what that means
2894 1
     * @access public
2895
     * @return string
2896
     */
2897
    public function encodeQ($str, $position = 'text')
2898
    {
2899
        // There should not be any EOL in the string
2900
        $pattern = '';
2901
        $encoded = str_replace(array("\r", "\n"), '', $str);
2902
        switch (strtolower($position)) {
2903
            case 'phrase':
2904
                // RFC 2047 section 5.3
2905
                $pattern = '^A-Za-z0-9!*+\/ -';
2906
                break;
2907
            /** @noinspection PhpMissingBreakStatementInspection */
2908 1
            case 'comment':
2909
                // RFC 2047 section 5.2
2910
                $pattern = '\(\)"';
2911
                // intentional fall-through
2912
                // for this reason we build the $pattern without including delimiters and []
2913
            case 'text':
2914
            default:
2915
                // RFC 2047 section 5.1
2916 1
                // Replace every high ascii, control, =, ? and _ characters
2917 1
                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2918 1
                break;
2919
        }
2920 1
        $matches = array();
2921 1
        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2922 1
            // If the string contains an '=', make sure it's the first thing we replace
2923 1
            // so as to avoid double-encoding
2924 1
            $eqkey = array_search('=', $matches[0]);
2925 1
            if (false !== $eqkey) {
2926 1
                unset($matches[0][$eqkey]);
2927 1
                array_unshift($matches[0], '=');
2928
            }
2929 1
            foreach (array_unique($matches[0]) as $char) {
2930 1
                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2931
            }
2932
        }
2933
        // Replace every spaces to _ (more readable than =20)
2934
        return str_replace(' ', '_', $encoded);
2935
    }
2936
2937
    /**
2938
     * Add a string or binary attachment (non-filesystem).
2939
     * This method can be used to attach ascii or binary data,
2940
     * such as a BLOB record from a database.
2941
     * @param string $string String attachment data.
2942
     * @param string $filename Name of the attachment.
2943
     * @param string $encoding File encoding (see $Encoding).
2944
     * @param string $type File extension (MIME) type.
2945
     * @param string $disposition Disposition to use
2946
     * @return void
2947
     */
2948 5
    public function addStringAttachment(
2949
        $string,
2950 5
        $filename,
2951 1
        $encoding = 'base64',
2952 1
        $type = '',
2953
        $disposition = 'attachment'
2954
    ) {
2955
        // If a MIME type is not specified, try to work it out from the file name
2956 5
        if ($type == '') {
2957 1
            $type = self::filenameToType($filename);
2958 1
        }
2959
        // Append to $attachment array
2960 5
        $this->attachment[] = array(
2961 5
            0 => $string,
2962 1
            1 => $filename,
2963 1
            2 => basename($filename),
2964
            3 => $encoding,
2965
            4 => $type,
2966 5
            5 => true, // isStringAttachment
2967 5
            6 => $disposition,
2968 5
            7 => 0
2969 5
        );
2970 5
    }
2971 5
2972 5
    /**
2973 5
     * Add an embedded (inline) attachment from a file.
2974
     * This can include images, sounds, and just about any other document type.
2975 5
     * These differ from 'regular' attachments in that they are intended to be
2976 5
     * displayed inline with the message, not just attached for download.
2977
     * This is used in HTML messages that embed the images
2978
     * the HTML refers to using the $cid value.
2979
     * @param string $path Path to the attachment.
2980
     * @param string $cid Content ID of the attachment; Use this to reference
2981
     *        the content when using an embedded image in HTML.
2982
     * @param string $name Overrides the attachment name.
2983
     * @param string $encoding File encoding (see $Encoding).
2984
     * @param string $type File MIME type.
2985
     * @param string $disposition Disposition to use
2986
     * @return boolean True on successfully adding an attachment
2987
     */
2988
    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2989
    {
2990
        if (!@is_file($path)) {
2991
            $this->setError($this->lang('file_access') . $path);
2992
            return false;
2993 1
        }
2994
2995
        // If a MIME type is not specified, try to work it out from the file name
2996
        if ($type == '') {
2997
            $type = self::filenameToType($path);
2998
        }
2999
3000
        $filename = basename($path);
3001
        if ($name == '') {
3002 1
            $name = $filename;
3003
        }
3004
3005
        // Append to $attachment array
3006
        $this->attachment[] = array(
3007 1
            0 => $path,
3008 1
            1 => $filename,
3009 1
            2 => $name,
3010 1
            3 => $encoding,
3011 1
            4 => $type,
3012 1
            5 => false, // isStringAttachment
3013 1
            6 => $disposition,
3014 1
            7 => $cid
3015
        );
3016 1
        return true;
3017 1
    }
3018
3019
    /**
3020
     * Add an embedded stringified attachment.
3021
     * This can include images, sounds, and just about any other document type.
3022
     * Be sure to set the $type to an image type for images:
3023
     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
3024
     * @param string $string The attachment binary data.
3025 41
     * @param string $cid Content ID of the attachment; Use this to reference
3026
     *        the content when using an embedded image in HTML.
3027 41
     * @param string $name
3028 11
     * @param string $encoding File encoding (see $Encoding).
3029 6
     * @param string $type MIME type.
3030
     * @param string $disposition Disposition to use
3031 35
     * @return boolean True on successfully adding an attachment
3032 35
     */
3033
    public function addStringEmbeddedImage(
3034
        $string,
3035
        $cid,
3036
        $name = '',
3037
        $encoding = 'base64',
3038
        $type = '',
3039 41
        $disposition = 'inline'
3040
    ) {
3041 41
        // If a MIME type is not specified, try to work it out from the name
3042 11
        if ($type == '' and !empty($name)) {
3043 6
            $type = self::filenameToType($name);
3044
        }
3045 36
3046 35
        // Append to $attachment array
3047
        $this->attachment[] = array(
3048
            0 => $string,
3049
            1 => $name,
3050
            2 => $name,
3051
            3 => $encoding,
3052
            4 => $type,
3053 41
            5 => true, // isStringAttachment
3054
            6 => $disposition,
3055 41
            7 => $cid
3056
        );
3057
        return true;
3058
    }
3059
3060
    /**
3061
     * Check if an inline attachment is present.
3062
     * @access public
3063
     * @return boolean
3064 3
     */
3065
    public function inlineImageExists()
3066 3
    {
3067 3
        foreach ($this->attachment as $attachment) {
3068 1
            if ($attachment[6] == 'inline') {
3069 1
                return true;
3070 1
            }
3071 3
        }
3072 3
        return false;
3073
    }
3074
3075
    /**
3076
     * Check if an attachment (non-inline) is present.
3077
     * @return boolean
3078 1
     */
3079
    public function attachmentExists()
3080 1
    {
3081 1
        foreach ($this->attachment as $attachment) {
3082 1
            if ($attachment[6] == 'attachment') {
3083 1
                return true;
3084 1
            }
3085 1
        }
3086
        return false;
3087
    }
3088
3089
    /**
3090
     * Check if this message has an alternative body set.
3091 1
     * @return boolean
3092
     */
3093 1
    public function alternativeExists()
3094 1
    {
3095 1
        return !empty($this->AltBody);
3096 1
    }
3097 1
3098 1
    /**
3099
     * Clear queued addresses of given kind.
3100
     * @access protected
3101
     * @param string $kind 'to', 'cc', or 'bcc'
3102
     * @return void
3103
     */
3104 2
    public function clearQueuedAddresses($kind)
3105
    {
3106 2
        $RecipientsQueue = $this->RecipientsQueue;
3107 1
        foreach ($RecipientsQueue as $address => $params) {
3108 2
            if ($params[0] == $kind) {
3109 2
                unset($this->RecipientsQueue[$address]);
3110 2
            }
3111 2
        }
3112
    }
3113
3114
    /**
3115
     * Clear all To recipients.
3116
     * @return void
3117 3
     */
3118
    public function clearAddresses()
3119 3
    {
3120 3
        foreach ($this->to as $to) {
3121 3
            unset($this->all_recipients[strtolower($to[0])]);
3122
        }
3123
        $this->to = array();
3124
        $this->clearQueuedAddresses('to');
3125
    }
3126
3127 5
    /**
3128
     * Clear all CC recipients.
3129 5
     * @return void
3130 5
     */
3131 5
    public function clearCCs()
3132 5
    {
3133 5
        foreach ($this->cc as $cc) {
3134 5
            unset($this->all_recipients[strtolower($cc[0])]);
3135
        }
3136
        $this->cc = array();
3137
        $this->clearQueuedAddresses('cc');
3138
    }
3139
3140 1
    /**
3141
     * Clear all BCC recipients.
3142 1
     * @return void
3143 1
     */
3144
    public function clearBCCs()
3145
    {
3146
        foreach ($this->bcc as $bcc) {
3147
            unset($this->all_recipients[strtolower($bcc[0])]);
3148
        }
3149 2
        $this->bcc = array();
3150
        $this->clearQueuedAddresses('bcc');
3151 2
    }
3152 2
3153
    /**
3154
     * Clear all ReplyTo recipients.
3155
     * @return void
3156
     */
3157
    public function clearReplyTos()
3158
    {
3159
        $this->ReplyTo = array();
3160 8
        $this->ReplyToQueue = array();
3161
    }
3162 8
3163 8
    /**
3164 1
     * Clear all recipient types.
3165 1
     * @return void
3166
     */
3167
    public function clearAllRecipients()
3168
    {
3169
        $this->to = array();
3170
        $this->cc = array();
3171
        $this->bcc = array();
3172
        $this->all_recipients = array();
3173
        $this->RecipientsQueue = array();
3174
    }
3175
3176
    /**
3177 1
     * Clear all filesystem, string, and binary attachments.
3178 8
     * @return void
3179 8
     */
3180
    public function clearAttachments()
3181
    {
3182
        $this->attachment = array();
3183
    }
3184
3185
    /**
3186
     * Clear all custom headers.
3187 42
     * @return void
3188
     */
3189
    public function clearCustomHeaders()
3190
    {
3191 42
        $this->CustomHeader = array();
3192 42
    }
3193
3194
    /**
3195
     * Add an error message to the error container.
3196
     * @access protected
3197
     * @param string $msg
3198
     * @return void
3199
     */
3200
    protected function setError($msg)
3201 42
    {
3202
        $this->error_count++;
3203 42
        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
3204 42
            $lasterror = $this->smtp->getError();
3205
            if (!empty($lasterror['error'])) {
3206 42
                $msg .= $this->lang('smtp_error') . $lasterror['error'];
3207
                if (!empty($lasterror['detail'])) {
3208 42
                    $msg .= ' Detail: '. $lasterror['detail'];
3209 42
                }
3210 42
                if (!empty($lasterror['smtp_code'])) {
3211
                    $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3212
                }
3213 42
                if (!empty($lasterror['smtp_code_ex'])) {
3214
                    $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3215
                }
3216
            }
3217
        }
3218
        $this->ErrorInfo = $msg;
3219
    }
3220
3221
    /**
3222 8
     * Return an RFC 822 formatted date.
3223
     * @access public
3224 8
     * @return string
3225 7
     * @static
3226 7
     */
3227
    public static function rfcDate()
3228 8
    {
3229 8
        // Set the time zone to whatever the default is to avoid 500 errors
3230
        // Will default to UTC if it's not set properly in php.ini
3231
        date_default_timezone_set(@date_default_timezone_get());
3232
        return date('D, j M Y H:i:s O');
3233
    }
3234
3235 8
    /**
3236
     * Get the server hostname.
3237
     * Returns 'localhost.localdomain' if unknown.
3238
     * @access protected
3239
     * @return string
3240
     */
3241
    protected function serverHostname()
3242
    {
3243
        $result = 'localhost.localdomain';
3244
        if (!empty($this->Hostname)) {
3245
            $result = $this->Hostname;
3246
        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
3247 41
            $result = $_SERVER['SERVER_NAME'];
3248
        } elseif (function_exists('gethostname') && gethostname() !== false) {
3249 41
            $result = gethostname();
3250
        } elseif (php_uname('n') !== false) {
3251
            $result = php_uname('n');
3252
        }
3253
        return $result;
3254
    }
3255
3256
    /**
3257
     * Get an error message in the current language.
3258
     * @access protected
3259 38
     * @param string $key
3260
     * @return string
3261
     */
3262 38
    protected function lang($key)
3263
    {
3264 38
        if (count($this->language) < 1) {
3265
            $this->setLanguage('en'); // set the default language
3266
        }
3267 38
3268
        if (array_key_exists($key, $this->language)) {
3269
            if ($key == 'smtp_connect_failed') {
3270
                //Include a link to troubleshooting docs on SMTP connection failure
3271
                //this is by far the biggest cause of support questions
3272
                //but it's usually not PHPMailer's fault.
3273
                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3274
            }
3275
            return $this->language[$key];
3276
        } else {
3277
            //Return the key as a fallback
3278
            return $key;
3279 2
        }
3280
    }
3281 2
3282
    /**
3283 2
     * Check if an error occurred.
3284 2
     * @access public
3285 1
     * @return boolean True if an error did occur.
3286
     */
3287 2
    public function isError()
3288
    {
3289
        return ($this->error_count > 0);
3290
    }
3291
3292
    /**
3293 1
     * Ensure consistent line endings in a string.
3294
     * Changes every end of line from CRLF, CR or LF to $this->LE.
3295 1
     * @access public
3296
     * @param string $str String to fixEOL
3297
     * @return string
3298
     */
3299
    public function fixEOL($str)
3300
    {
3301
        // Normalise to \n
3302
        $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
3303
        // Now convert LE as needed
3304
        if ($this->LE !== "\n") {
3305
            $nstr = str_replace("\n", $this->LE, $nstr);
3306
        }
3307
        return $nstr;
3308
    }
3309
3310 2
    /**
3311
     * Add a custom header.
3312 2
     * $name value can be overloaded to contain
3313 2
     * both header name and value (name:value)
3314 2
     * @access public
3315
     * @param string $name Custom header name
3316 2
     * @param string $value Header value
3317
     * @return void
3318
     */
3319
    public function addCustomHeader($name, $value = null)
3320
    {
3321
        if ($value === null) {
3322
            // Value passed in as name:value
3323
            $this->CustomHeader[] = explode(':', $name, 2);
3324
        } else {
3325
            $this->CustomHeader[] = array($name, $value);
3326
        }
3327
    }
3328
3329
    /**
3330
     * Returns all custom headers.
3331 2
     * @return array
3332
     */
3333
    public function getCustomHeaders()
3334 2
    {
3335 2
        return $this->CustomHeader;
3336 2
    }
3337
3338
    /**
3339 2
     * Create a message body from an HTML string.
3340 2
     * Automatically inlines images and creates a plain-text version by converting the HTML,
3341 2
     * overwriting any existing values in Body and AltBody.
3342 2
     * $basedir is used when handling relative image paths, e.g. <img src="images/a.png">
3343 2
     * will look for an image file in $basedir/images/a.png and convert it to inline.
3344 2
     * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself.
3345 2
     * @access public
3346 2
     * @param string $message HTML message string
3347 2
     * @param string $basedir base directory for relative paths to images
3348 2
     * @param boolean|callable $advanced Whether to use the internal HTML to text converter
3349 2
     *    or your own custom converter @see PHPMailer::html2text()
3350 2
     * @return string $message The transformed message Body
3351 2
     */
3352 2
    public function msgHTML($message, $basedir = '', $advanced = false)
3353 2
    {
3354 2
        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3355 2
        if (array_key_exists(2, $images)) {
3356 2
            foreach ($images[2] as $imgindex => $url) {
3357
                // Convert data URIs into embedded images
3358 2
                if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3359 2
                    $data = substr($url, strpos($url, ','));
3360 2
                    if ($match[2]) {
3361 2
                        $data = base64_decode($data);
3362 2
                    } else {
3363 2
                        $data = rawurldecode($data);
3364
                    }
3365 2
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3366 2
                    if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
3367 2
                        $message = str_replace(
3368
                            $images[0][$imgindex],
3369
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3370
                            $message
3371 2
                        );
3372
                    }
3373
                } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) {
3374
                    // Do not change urls for absolute images (thanks to corvuscorax)
3375
                    // Do not change urls that are already inline images
3376
                    $filename = basename($url);
3377
                    $directory = dirname($url);
3378
                    if ($directory == '.') {
3379
                        $directory = '';
3380
                    }
3381
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3382
                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3383
                        $basedir .= '/';
3384
                    }
3385
                    if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3386
                        $directory .= '/';
3387
                    }
3388
                    if ($this->addEmbeddedImage(
3389
                        $basedir . $directory . $filename,
3390
                        $cid,
3391
                        $filename,
3392
                        'base64',
3393
                        self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
3394 2
                    )
3395
                    ) {
3396 2
                        $message = preg_replace(
3397 1
                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3398
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3399 2
                            $message
3400 2
                        );
3401 2
                    }
3402 2
                }
3403 2
            }
3404
        }
3405
        $this->isHTML(true);
3406
        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3407
        $this->Body = $this->normalizeBreaks($message);
3408
        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3409
        if (!$this->alternativeExists()) {
3410
            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
3411
                self::CRLF . self::CRLF;
3412
        }
3413 10
        return $this->Body;
3414
    }
3415
3416 10
    /**
3417 10
     * Convert an HTML string into plain text.
3418 10
     * This is used by msgHTML().
3419 10
     * Note - older versions of this function used a bundled advanced converter
3420 10
     * which was been removed for license reasons in #232.
3421 10
     * Example usage:
3422 10
     * <code>
3423 10
     * // Use default conversion
3424 10
     * $plain = $mail->html2text($html);
3425 10
     * // Use your own custom converter
3426 10
     * $plain = $mail->html2text($html, function($html) {
3427 10
     *     $converter = new MyHtml2text($html);
3428 10
     *     return $converter->get_text();
3429 10
     * });
3430 10
     * </code>
3431 10
     * @param string $html The HTML text to convert
3432 10
     * @param boolean|callable $advanced Any boolean value to use the internal converter,
3433 10
     *   or provide your own callable for custom conversion.
3434 10
     * @return string
3435 10
     */
3436 10
    public function html2text($html, $advanced = false)
3437 10
    {
3438 10
        if (is_callable($advanced)) {
3439 10
            return call_user_func($advanced, $html);
3440 10
        }
3441 10
        return html_entity_decode(
3442 10
            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3443 10
            ENT_QUOTES,
3444 10
            $this->CharSet
3445 10
        );
3446 10
    }
3447 10
3448 10
    /**
3449 10
     * Get the MIME type for a file extension.
3450 10
     * @param string $ext File extension
3451 10
     * @access public
3452 10
     * @return string MIME type of file.
3453 10
     * @static
3454 10
     */
3455 10
    public static function _mime_types($ext = '')
3456 10
    {
3457 10
        $mimes = array(
3458 10
            'xl'    => 'application/excel',
3459 10
            'js'    => 'application/javascript',
3460 10
            'hqx'   => 'application/mac-binhex40',
3461 10
            'cpt'   => 'application/mac-compactpro',
3462 10
            'bin'   => 'application/macbinary',
3463 10
            'doc'   => 'application/msword',
3464 10
            'word'  => 'application/msword',
3465 10
            'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3466 10
            'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3467 10
            'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3468 10
            'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3469 10
            'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3470 10
            'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3471 10
            'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3472 10
            'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3473 10
            'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
3474 10
            'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3475 10
            'class' => 'application/octet-stream',
3476 10
            'dll'   => 'application/octet-stream',
3477 10
            'dms'   => 'application/octet-stream',
3478 10
            'exe'   => 'application/octet-stream',
3479 10
            'lha'   => 'application/octet-stream',
3480 10
            'lzh'   => 'application/octet-stream',
3481 10
            'psd'   => 'application/octet-stream',
3482 10
            'sea'   => 'application/octet-stream',
3483 10
            'so'    => 'application/octet-stream',
3484 10
            'oda'   => 'application/oda',
3485 10
            'pdf'   => 'application/pdf',
3486 10
            'ai'    => 'application/postscript',
3487 10
            'eps'   => 'application/postscript',
3488 10
            'ps'    => 'application/postscript',
3489 10
            'smi'   => 'application/smil',
3490 10
            'smil'  => 'application/smil',
3491 10
            'mif'   => 'application/vnd.mif',
3492 10
            'xls'   => 'application/vnd.ms-excel',
3493 10
            'ppt'   => 'application/vnd.ms-powerpoint',
3494 10
            'wbxml' => 'application/vnd.wap.wbxml',
3495 10
            'wmlc'  => 'application/vnd.wap.wmlc',
3496 10
            'dcr'   => 'application/x-director',
3497 10
            'dir'   => 'application/x-director',
3498 10
            'dxr'   => 'application/x-director',
3499 10
            'dvi'   => 'application/x-dvi',
3500 10
            'gtar'  => 'application/x-gtar',
3501 10
            'php3'  => 'application/x-httpd-php',
3502 10
            'php4'  => 'application/x-httpd-php',
3503 10
            'php'   => 'application/x-httpd-php',
3504 10
            'phtml' => 'application/x-httpd-php',
3505 10
            'phps'  => 'application/x-httpd-php-source',
3506 10
            'swf'   => 'application/x-shockwave-flash',
3507 10
            'sit'   => 'application/x-stuffit',
3508 10
            'tar'   => 'application/x-tar',
3509 10
            'tgz'   => 'application/x-tar',
3510 10
            'xht'   => 'application/xhtml+xml',
3511 10
            'xhtml' => 'application/xhtml+xml',
3512 10
            'zip'   => 'application/zip',
3513
            'mid'   => 'audio/midi',
3514 10
            'midi'  => 'audio/midi',
3515 10
            'mp2'   => 'audio/mpeg',
3516 10
            'mp3'   => 'audio/mpeg',
3517
            'mpga'  => 'audio/mpeg',
3518
            'aif'   => 'audio/x-aiff',
3519
            'aifc'  => 'audio/x-aiff',
3520
            'aiff'  => 'audio/x-aiff',
3521
            'ram'   => 'audio/x-pn-realaudio',
3522
            'rm'    => 'audio/x-pn-realaudio',
3523
            'rpm'   => 'audio/x-pn-realaudio-plugin',
3524
            'ra'    => 'audio/x-realaudio',
3525
            'wav'   => 'audio/x-wav',
3526
            'bmp'   => 'image/bmp',
3527
            'gif'   => 'image/gif',
3528 7
            'jpeg'  => 'image/jpeg',
3529
            'jpe'   => 'image/jpeg',
3530
            'jpg'   => 'image/jpeg',
3531 7
            'png'   => 'image/png',
3532 7
            'tiff'  => 'image/tiff',
3533
            'tif'   => 'image/tiff',
3534
            'eml'   => 'message/rfc822',
3535 7
            'css'   => 'text/css',
3536 7
            'html'  => 'text/html',
3537
            'htm'   => 'text/html',
3538
            'shtml' => 'text/html',
3539
            'log'   => 'text/plain',
3540
            'text'  => 'text/plain',
3541
            'txt'   => 'text/plain',
3542
            'rtx'   => 'text/richtext',
3543
            'rtf'   => 'text/rtf',
3544
            'vcf'   => 'text/vcard',
3545
            'vcard' => 'text/vcard',
3546
            'xml'   => 'text/xml',
3547
            'xsl'   => 'text/xml',
3548
            'mpeg'  => 'video/mpeg',
3549
            'mpe'   => 'video/mpeg',
3550 10
            'mpg'   => 'video/mpeg',
3551
            'mov'   => 'video/quicktime',
3552 10
            'qt'    => 'video/quicktime',
3553 10
            'rv'    => 'video/vnd.rn-realvideo',
3554 10
            'avi'   => 'video/x-msvideo',
3555 10
            'movie' => 'video/x-sgi-movie'
3556 10
        );
3557 10
        if (array_key_exists(strtolower($ext), $mimes)) {
3558 10
            return $mimes[strtolower($ext)];
3559 10
        }
3560 10
        return 'application/octet-stream';
3561 10
    }
3562 10
3563 10
    /**
3564 10
     * Map a file name to a MIME type.
3565 10
     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3566 10
     * @param string $filename A file name or full path, does not need to exist as a file
3567 10
     * @return string
3568
     * @static
3569 10
     */
3570 10
    public static function filenameToType($filename)
3571 1
    {
3572 10
        // In case the path is a URL, strip any query string before getting extension
3573 10
        $qpos = strpos($filename, '?');
3574
        if (false !== $qpos) {
3575 10
            $filename = substr($filename, 0, $qpos);
3576 10
        }
3577 2
        $pathinfo = self::mb_pathinfo($filename);
3578 8
        return self::_mime_types($pathinfo['extension']);
3579 8
    }
3580 1
3581 8
    /**
3582 8
     * Multi-byte-safe pathinfo replacement.
3583 8
     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3584
     * Works similarly to the one in PHP >= 5.2.0
3585
     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3586
     * @param string $path A filename or path, does not need to exist as a file
3587
     * @param integer|string $options Either a PATHINFO_* constant,
3588
     *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3589
     * @return string|array
3590
     * @static
3591
     */
3592
    public static function mb_pathinfo($path, $options = null)
3593
    {
3594
        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3595
        $pathinfo = array();
3596
        if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3597
            if (array_key_exists(1, $pathinfo)) {
3598
                $ret['dirname'] = $pathinfo[1];
3599
            }
3600 1
            if (array_key_exists(2, $pathinfo)) {
3601
                $ret['basename'] = $pathinfo[2];
3602 1
            }
3603 1
            if (array_key_exists(5, $pathinfo)) {
3604 1
                $ret['extension'] = $pathinfo[5];
3605
            }
3606 1
            if (array_key_exists(3, $pathinfo)) {
3607 1
                $ret['filename'] = $pathinfo[3];
3608
            }
3609
        }
3610
        switch ($options) {
3611
            case PATHINFO_DIRNAME:
3612
            case 'dirname':
3613
                return $ret['dirname'];
3614
            case PATHINFO_BASENAME:
3615
            case 'basename':
3616
                return $ret['basename'];
3617 42
            case PATHINFO_EXTENSION:
3618
            case 'extension':
3619 42
                return $ret['extension'];
3620
            case PATHINFO_FILENAME:
3621
            case 'filename':
3622
                return $ret['filename'];
3623
            default:
3624
                return $ret;
3625
        }
3626
    }
3627
3628
    /**
3629
     * Set or reset instance properties.
3630
     * You should avoid this function - it's more verbose, less efficient, more error-prone and
3631
     * harder to debug than setting properties directly.
3632 3
     * Usage Example:
3633
     * `$mail->set('SMTPSecure', 'tls');`
3634 3
     *   is the same as:
3635
     * `$mail->SMTPSecure = 'tls';`
3636
     * @access public
3637
     * @param string $name The property name to set
3638
     * @param mixed $value The value to set the property to
3639
     * @return boolean
3640
     * @TODO Should this not be using the __set() magic function?
3641
     */
3642
    public function set($name, $value = '')
3643
    {
3644
        if (property_exists($this, $name)) {
3645 2
            $this->$name = $value;
3646
            return true;
3647 2
        } else {
3648 2
            $this->setError($this->lang('variable_set') . $name);
3649 2
            return false;
3650 2
        }
3651 2
    }
3652
3653
    /**
3654
     * Strip newlines to prevent header injection.
3655
     * @access public
3656
     * @param string $str
3657
     * @return string
3658
     */
3659 1
    public function secureHeader($str)
3660
    {
3661 1
        return trim(str_replace(array("\r", "\n"), '', $str));
3662 1
    }
3663 1
3664 1
    /**
3665 1
     * Normalize line breaks in a string.
3666 1
     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3667 1
     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3668
     * @param string $text
3669 1
     * @param string $breaktype What kind of line break to use, defaults to CRLF
3670 1
     * @return string
3671
     * @access public
3672
     * @static
3673
     */
3674
    public static function normalizeBreaks($text, $breaktype = "\r\n")
3675
    {
3676
        return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3677
    }
3678
3679
    /**
3680 1
     * Set the public and private key files and password for S/MIME signing.
3681
     * @access public
3682 1
     * @param string $cert_filename
3683
     * @param string $key_filename
3684
     * @param string $key_pass Password for private key
3685
     * @param string $extracerts_filename Optional path to chain certificate
3686
     */
3687
    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3688 1
    {
3689 1
        $this->sign_cert_file = $cert_filename;
3690
        $this->sign_key_file = $key_filename;
3691
        $this->sign_key_pass = $key_pass;
3692 1
        $this->sign_extracerts_file = $extracerts_filename;
3693
    }
3694 1
3695 1
    /**
3696 1
     * Quoted-Printable-encode a DKIM header.
3697
     * @access public
3698
     * @param string $txt
3699
     * @return string
3700
     */
3701
    public function DKIM_QP($txt)
3702
    {
3703
        $line = '';
3704
        for ($i = 0; $i < strlen($txt); $i++) {
3705
            $ord = ord($txt[$i]);
3706
            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3707
                $line .= $txt[$i];
3708 1
            } else {
3709
                $line .= '=' . sprintf('%02X', $ord);
3710 1
            }
3711 1
        }
3712 1
        return $line;
3713 1
    }
3714 1
3715 1
    /**
3716 1
     * Generate a DKIM signature.
3717 1
     * @access public
3718 1
     * @param string $signHeader
3719 1
     * @throws phpmailerException
3720
     * @return string The DKIM signature value
3721
     */
3722
    public function DKIM_Sign($signHeader)
3723
    {
3724
        if (!defined('PKCS7_TEXT')) {
3725
            if ($this->exceptions) {
3726
                throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3727
            }
3728 1
            return '';
3729
        }
3730 1
        $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
3731
        if ('' != $this->DKIM_passphrase) {
3732
            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3733
        } else {
3734 1
            $privKey = openssl_pkey_get_private($privKeyStr);
3735 1
        }
3736
        //Workaround for missing digest algorithms in old PHP & OpenSSL versions
3737 1
        //@link http://stackoverflow.com/a/11117338/333340
3738 1
        if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
3739 1
            in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
3740 1
            if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
3741
                openssl_pkey_free($privKey);
3742
                return base64_encode($signature);
3743
            }
3744
        } else {
3745
            $pinfo = openssl_pkey_get_details($privKey);
3746
            $hash = hash('sha256', $signHeader);
3747
            //'Magic' constant for SHA256 from RFC3447
3748
            //@link https://tools.ietf.org/html/rfc3447#page-43
3749
            $t = '3031300d060960864801650304020105000420' . $hash;
3750
            $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
3751 1
            $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t);
3752
3753 1
            if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
3754 1
                openssl_pkey_free($privKey);
3755 1
                return base64_encode($signature);
3756 1
            }
3757 1
        }
3758 1
        openssl_pkey_free($privKey);
3759 1
        return '';
3760 1
    }
3761 1
3762 1
    /**
3763 1
     * Generate a DKIM canonicalization header.
3764 1
     * @access public
3765 1
     * @param string $signHeader Header
3766 1
     * @return string
3767 1
     */
3768 1
    public function DKIM_HeaderC($signHeader)
3769 1
    {
3770 1
        $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3771 1
        $lines = explode("\r\n", $signHeader);
3772 1
        foreach ($lines as $key => $line) {
3773 1
            list($heading, $value) = explode(':', $line, 2);
3774 1
            $heading = strtolower($heading);
3775
            $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
3776
            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3777 1
        }
3778
        $signHeader = implode("\r\n", $lines);
3779
        return $signHeader;
3780 1
    }
3781 1
3782 1
    /**
3783 1
     * Generate a DKIM canonicalization body.
3784 1
     * @access public
3785 1
     * @param string $body Message Body
3786 1
     * @return string
3787 1
     */
3788 1
    public function DKIM_BodyC($body)
3789 1
    {
3790 1
        if ($body == '') {
3791 1
            return "\r\n";
3792 1
        }
3793 1
        // stabilize line endings
3794 1
        $body = str_replace("\r\n", "\n", $body);
3795
        $body = str_replace("\n", "\r\n", $body);
3796
        // END stabilize line endings
3797
        while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3798 1
            $body = substr($body, 0, strlen($body) - 2);
3799 1
        }
3800 1
        return $body;
3801 1
    }
3802 1
3803 1
    /**
3804 1
     * Create the DKIM header and body in a new message header.
3805 1
     * @access public
3806 1
     * @param string $headers_line Header lines
3807 1
     * @param string $subject Subject
3808 1
     * @param string $body Body
3809 1
     * @return string
3810 1
     */
3811 1
    public function DKIM_Add($headers_line, $subject, $body)
3812 1
    {
3813 1
        $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
3814 1
        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3815 1
        $DKIMquery = 'dns/txt'; // Query method
3816 1
        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3817
        $subject_header = "Subject: $subject";
3818 1
        $headers = explode($this->LE, $headers_line);
3819 1
        $from_header = '';
3820 1
        $to_header = '';
3821
        $date_header = '';
3822
        $current = '';
3823
        foreach ($headers as $header) {
3824
            if (strpos($header, 'From:') === 0) {
3825
                $from_header = $header;
3826
                $current = 'from_header';
3827
            } elseif (strpos($header, 'To:') === 0) {
3828
                $to_header = $header;
3829 40
                $current = 'to_header';
3830
            } elseif (strpos($header, 'Date:') === 0) {
3831
                $date_header = $header;
3832 40
                $current = 'date_header';
3833
            } else {
3834
                if (!empty($$current) && strpos($header, ' =?') === 0) {
3835
                    $$current .= $header;
3836
                } else {
3837
                    $current = '';
3838
                }
3839
            }
3840
        }
3841 2
        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3842
        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3843 2
        $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
3844
        $subject = str_replace(
3845
            '|',
3846
            '=7C',
3847
            $this->DKIM_QP($subject_header)
3848
        ); // Copied header fields (dkim-quoted-printable)
3849
        $body = $this->DKIM_BodyC($body);
3850
        $DKIMlen = strlen($body); // Length of body
3851
        $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
3852 1
        if ('' == $this->DKIM_identity) {
3853
            $ident = '';
3854 1
        } else {
3855
            $ident = ' i=' . $this->DKIM_identity . ';';
3856
        }
3857
        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3858
            $DKIMsignatureType . '; q=' .
3859
            $DKIMquery . '; l=' .
3860
            $DKIMlen . '; s=' .
3861
            $this->DKIM_selector .
3862
            ";\r\n" .
3863 1
            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3864
            "\th=From:To:Date:Subject;\r\n" .
3865 1
            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3866
            "\tz=$from\r\n" .
3867
            "\t|$to\r\n" .
3868
            "\t|$date\r\n" .
3869
            "\t|$subject;\r\n" .
3870
            "\tbh=" . $DKIMb64 . ";\r\n" .
3871
            "\tb=";
3872
        $toSign = $this->DKIM_HeaderC(
3873
            $from_header . "\r\n" .
3874 2
            $to_header . "\r\n" .
3875
            $date_header . "\r\n" .
3876 2
            $subject_header . "\r\n" .
3877
            $dkimhdrs
3878
        );
3879
        $signed = $this->DKIM_Sign($toSign);
3880
        return $dkimhdrs . $signed . "\r\n";
3881
    }
3882
3883
    /**
3884
     * Detect if a string contains a line longer than the maximum line length allowed.
3885
     * @param string $str
3886
     * @return boolean
3887
     * @static
3888
     */
3889
    public static function hasLineLongerThanMax($str)
3890
    {
3891
        //+2 to include CRLF line break for a 1000 total
3892
        return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
3893
    }
3894
3895
    /**
3896
     * Allows for public read access to 'to' property.
3897
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3898
     * @access public
3899
     * @return array
3900 36
     */
3901
    public function getToAddresses()
3902 36
    {
3903
        return $this->to;
3904
    }
3905
3906 36
    /**
3907
     * Allows for public read access to 'cc' property.
3908
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3909
     * @access public
3910
     * @return array
3911
     */
3912
    public function getCcAddresses()
3913
    {
3914
        return $this->cc;
3915
    }
3916
3917
    /**
3918
     * Allows for public read access to 'bcc' property.
3919
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3920
     * @access public
3921
     * @return array
3922
     */
3923
    public function getBccAddresses()
3924
    {
3925
        return $this->bcc;
3926
    }
3927
3928
    /**
3929
     * Allows for public read access to 'ReplyTo' property.
3930
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3931
     * @access public
3932
     * @return array
3933
     */
3934
    public function getReplyToAddresses()
3935
    {
3936
        return $this->ReplyTo;
3937
    }
3938
3939
    /**
3940
     * Allows for public read access to 'all_recipients' property.
3941
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3942
     * @access public
3943
     * @return array
3944
     */
3945
    public function getAllRecipientAddresses()
3946
    {
3947
        return $this->all_recipients;
3948
    }
3949
3950
    /**
3951
     * Perform a callback.
3952
     * @param boolean $isSent
3953
     * @param array $to
3954
     * @param array $cc
3955
     * @param array $bcc
3956
     * @param string $subject
3957
     * @param string $body
3958
     * @param string $from
3959
     */
3960
    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3961
    {
3962
        if (!empty($this->action_function) && is_callable($this->action_function)) {
3963
            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3964
            call_user_func_array($this->action_function, $params);
3965
        }
3966
    }
3967
}
3968
3969
/**
3970
 * PHPMailer exception handler
3971
 * @package PHPMailer
3972
 */
3973
class phpmailerException extends Exception
3974
{
3975
    /**
3976
     * Prettify error message output
3977
     * @return string
3978
     */
3979
    public function errorMessage()
3980
    {
3981
        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3982
        return $errorMsg;
3983
    }
3984
}
3985