Completed
Push — master ( a7cd2a...eabd6c )
by Stephen
38:42
created

PHPMailer::addOrEnqueueAnAddress()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 33
rs 5.3846
cc 8
eloc 22
nc 7
nop 3
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.14';
35
36
    /**
37
     * Email priority.
38
     * Options: null (default), 1 = High, 3 = Normal, 5 = low.
39
     * When null, the header is not set at all.
40
     * @var integer
41
     */
42
    public $Priority = null;
43
44
    /**
45
     * The character set of the message.
46
     * @var string
47
     */
48
    public $CharSet = 'iso-8859-1';
49
50
    /**
51
     * The MIME Content-type of the message.
52
     * @var string
53
     */
54
    public $ContentType = 'text/plain';
55
56
    /**
57
     * The message encoding.
58
     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59
     * @var string
60
     */
61
    public $Encoding = '8bit';
62
63
    /**
64
     * Holds the most recent mailer error message.
65
     * @var string
66
     */
67
    public $ErrorInfo = '';
68
69
    /**
70
     * The From email address for the message.
71
     * @var string
72
     */
73
    public $From = 'root@localhost';
74
75
    /**
76
     * The From name of the message.
77
     * @var string
78
     */
79
    public $FromName = 'Root User';
80
81
    /**
82
     * The Sender email (Return-Path) of the message.
83
     * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
84
     * @var string
85
     */
86
    public $Sender = '';
87
88
    /**
89
     * The Return-Path of the message.
90
     * If empty, it will be set to either From or Sender.
91
     * @var string
92
     * @deprecated Email senders should never set a return-path header;
93
     * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
94
     * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
95
     */
96
    public $ReturnPath = '';
97
98
    /**
99
     * The Subject of the message.
100
     * @var string
101
     */
102
    public $Subject = '';
103
104
    /**
105
     * An HTML or plain text message body.
106
     * If HTML then call isHTML(true).
107
     * @var string
108
     */
109
    public $Body = '';
110
111
    /**
112
     * The plain-text message body.
113
     * This body can be read by mail clients that do not have HTML email
114
     * capability such as mutt & Eudora.
115
     * Clients that can read HTML will view the normal Body.
116
     * @var string
117
     */
118
    public $AltBody = '';
119
120
    /**
121
     * An iCal message part body.
122
     * Only supported in simple alt or alt_inline message types
123
     * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
124
     * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
125
     * @link http://kigkonsult.se/iCalcreator/
126
     * @var string
127
     */
128
    public $Ical = '';
129
130
    /**
131
     * The complete compiled MIME message body.
132
     * @access protected
133
     * @var string
134
     */
135
    protected $MIMEBody = '';
136
137
    /**
138
     * The complete compiled MIME message headers.
139
     * @var string
140
     * @access protected
141
     */
142
    protected $MIMEHeader = '';
143
144
    /**
145
     * Extra headers that createHeader() doesn't fold in.
146
     * @var string
147
     * @access protected
148
     */
149
    protected $mailHeader = '';
150
151
    /**
152
     * Word-wrap the message body to this number of chars.
153
     * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
154
     * @var integer
155
     */
156
    public $WordWrap = 0;
157
158
    /**
159
     * Which method to use to send mail.
160
     * Options: "mail", "sendmail", or "smtp".
161
     * @var string
162
     */
163
    public $Mailer = 'mail';
164
165
    /**
166
     * The path to the sendmail program.
167
     * @var string
168
     */
169
    public $Sendmail = '/usr/sbin/sendmail';
170
171
    /**
172
     * Whether mail() uses a fully sendmail-compatible MTA.
173
     * One which supports sendmail's "-oi -f" options.
174
     * @var boolean
175
     */
176
    public $UseSendmailOptions = true;
177
178
    /**
179
     * Path to PHPMailer plugins.
180
     * Useful if the SMTP class is not in the PHP include path.
181
     * @var string
182
     * @deprecated Should not be needed now there is an autoloader.
183
     */
184
    public $PluginDir = '';
185
186
    /**
187
     * The email address that a reading confirmation should be sent to, also known as read receipt.
188
     * @var string
189
     */
190
    public $ConfirmReadingTo = '';
191
192
    /**
193
     * The hostname to use in the Message-ID header and as default HELO string.
194
     * If empty, PHPMailer attempts to find one with, in order,
195
     * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
196
     * 'localhost.localdomain'.
197
     * @var string
198
     */
199
    public $Hostname = '';
200
201
    /**
202
     * An ID to be used in the Message-ID header.
203
     * If empty, a unique id will be generated.
204
     * @var string
205
     */
206
    public $MessageID = '';
207
208
    /**
209
     * The message Date to be used in the Date header.
210
     * If empty, the current date will be added.
211
     * @var string
212
     */
213
    public $MessageDate = '';
214
215
    /**
216
     * SMTP hosts.
217
     * Either a single hostname or multiple semicolon-delimited hostnames.
218
     * You can also specify a different port
219
     * for each host by using this format: [hostname:port]
220
     * (e.g. "smtp1.example.com:25;smtp2.example.com").
221
     * You can also specify encryption type, for example:
222
     * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
223
     * Hosts will be tried in order.
224
     * @var string
225
     */
226
    public $Host = 'localhost';
227
228
    /**
229
     * The default SMTP server port.
230
     * @var integer
231
     * @TODO Why is this needed when the SMTP class takes care of it?
232
     */
233
    public $Port = 25;
234
235
    /**
236
     * The SMTP HELO of the message.
237
     * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
238
     * one with the same method described above for $Hostname.
239
     * @var string
240
     * @see PHPMailer::$Hostname
241
     */
242
    public $Helo = '';
243
244
    /**
245
     * What kind of encryption to use on the SMTP connection.
246
     * Options: '', 'ssl' or 'tls'
247
     * @var string
248
     */
249
    public $SMTPSecure = '';
250
251
    /**
252
     * Whether to enable TLS encryption automatically if a server supports it,
253
     * even if `SMTPSecure` is not set to 'tls'.
254
     * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
255
     * @var boolean
256
     */
257
    public $SMTPAutoTLS = true;
258
259
    /**
260
     * Whether to use SMTP authentication.
261
     * Uses the Username and Password properties.
262
     * @var boolean
263
     * @see PHPMailer::$Username
264
     * @see PHPMailer::$Password
265
     */
266
    public $SMTPAuth = false;
267
268
    /**
269
     * Options array passed to stream_context_create when connecting via SMTP.
270
     * @var array
271
     */
272
    public $SMTPOptions = array();
273
274
    /**
275
     * SMTP username.
276
     * @var string
277
     */
278
    public $Username = '';
279
280
    /**
281
     * SMTP password.
282
     * @var string
283
     */
284
    public $Password = '';
285
286
    /**
287
     * SMTP auth type.
288
     * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
289
     * @var string
290
     */
291
    public $AuthType = '';
292
293
    /**
294
     * SMTP realm.
295
     * Used for NTLM auth
296
     * @var string
297
     */
298
    public $Realm = '';
299
300
    /**
301
     * SMTP workstation.
302
     * Used for NTLM auth
303
     * @var string
304
     */
305
    public $Workstation = '';
306
307
    /**
308
     * The SMTP server timeout in seconds.
309
     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
310
     * @var integer
311
     */
312
    public $Timeout = 300;
313
314
    /**
315
     * SMTP class debug output mode.
316
     * Debug output level.
317
     * Options:
318
     * * `0` No output
319
     * * `1` Commands
320
     * * `2` Data and commands
321
     * * `3` As 2 plus connection status
322
     * * `4` Low-level data output
323
     * @var integer
324
     * @see SMTP::$do_debug
325
     */
326
    public $SMTPDebug = 0;
327
328
    /**
329
     * How to handle debug output.
330
     * Options:
331
     * * `echo` Output plain-text as-is, appropriate for CLI
332
     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
333
     * * `error_log` Output to error log as configured in php.ini
334
     *
335
     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
336
     * <code>
337
     * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
338
     * </code>
339
     * @var string|callable
340
     * @see SMTP::$Debugoutput
341
     */
342
    public $Debugoutput = 'echo';
343
344
    /**
345
     * Whether to keep SMTP connection open after each message.
346
     * If this is set to true then to close the connection
347
     * requires an explicit call to smtpClose().
348
     * @var boolean
349
     */
350
    public $SMTPKeepAlive = false;
351
352
    /**
353
     * Whether to split multiple to addresses into multiple messages
354
     * or send them all in one message.
355
     * @var boolean
356
     */
357
    public $SingleTo = false;
358
359
    /**
360
     * Storage for addresses when SingleTo is enabled.
361
     * @var array
362
     * @TODO This should really not be public
363
     */
364
    public $SingleToArray = array();
365
366
    /**
367
     * Whether to generate VERP addresses on send.
368
     * Only applicable when sending via SMTP.
369
     * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
370
     * @link http://www.postfix.org/VERP_README.html Postfix VERP info
371
     * @var boolean
372
     */
373
    public $do_verp = false;
374
375
    /**
376
     * Whether to allow sending messages with an empty body.
377
     * @var boolean
378
     */
379
    public $AllowEmpty = false;
380
381
    /**
382
     * The default line ending.
383
     * @note The default remains "\n". We force CRLF where we know
384
     *        it must be used via self::CRLF.
385
     * @var string
386
     */
387
    public $LE = "\n";
388
389
    /**
390
     * DKIM selector.
391
     * @var string
392
     */
393
    public $DKIM_selector = '';
394
395
    /**
396
     * DKIM Identity.
397
     * Usually the email address used as the source of the email
398
     * @var string
399
     */
400
    public $DKIM_identity = '';
401
402
    /**
403
     * DKIM passphrase.
404
     * Used if your key is encrypted.
405
     * @var string
406
     */
407
    public $DKIM_passphrase = '';
408
409
    /**
410
     * DKIM signing domain name.
411
     * @example 'example.com'
412
     * @var string
413
     */
414
    public $DKIM_domain = '';
415
416
    /**
417
     * DKIM private key file path.
418
     * @var string
419
     */
420
    public $DKIM_private = '';
421
422
    /**
423
     * Callback Action function name.
424
     *
425
     * The function that handles the result of the send email action.
426
     * It is called out by send() for each email sent.
427
     *
428
     * Value can be any php callable: http://www.php.net/is_callable
429
     *
430
     * Parameters:
431
     *   boolean $result        result of the send action
432
     *   string  $to            email address of the recipient
433
     *   string  $cc            cc email addresses
434
     *   string  $bcc           bcc email addresses
435
     *   string  $subject       the subject
436
     *   string  $body          the email body
437
     *   string  $from          email address of sender
438
     * @var string
439
     */
440
    public $action_function = '';
441
442
    /**
443
     * What to put in the X-Mailer header.
444
     * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
445
     * @var string
446
     */
447
    public $XMailer = '';
448
449
    /**
450
     * An instance of the SMTP sender class.
451
     * @var SMTP
452
     * @access protected
453
     */
454
    protected $smtp = null;
455
456
    /**
457
     * The array of 'to' names and addresses.
458
     * @var array
459
     * @access protected
460
     */
461
    protected $to = array();
462
463
    /**
464
     * The array of 'cc' names and addresses.
465
     * @var array
466
     * @access protected
467
     */
468
    protected $cc = array();
469
470
    /**
471
     * The array of 'bcc' names and addresses.
472
     * @var array
473
     * @access protected
474
     */
475
    protected $bcc = array();
476
477
    /**
478
     * The array of reply-to names and addresses.
479
     * @var array
480
     * @access protected
481
     */
482
    protected $ReplyTo = array();
483
484
    /**
485
     * An array of all kinds of addresses.
486
     * Includes all of $to, $cc, $bcc
487
     * @var array
488
     * @access protected
489
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
490
     */
491
    protected $all_recipients = array();
492
493
    /**
494
     * An array of names and addresses queued for validation.
495
     * In send(), valid and non duplicate entries are moved to $all_recipients
496
     * and one of $to, $cc, or $bcc.
497
     * This array is used only for addresses with IDN.
498
     * @var array
499
     * @access protected
500
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
501
     * @see PHPMailer::$all_recipients
502
     */
503
    protected $RecipientsQueue = array();
504
505
    /**
506
     * An array of reply-to names and addresses queued for validation.
507
     * In send(), valid and non duplicate entries are moved to $ReplyTo.
508
     * This array is used only for addresses with IDN.
509
     * @var array
510
     * @access protected
511
     * @see PHPMailer::$ReplyTo
512
     */
513
    protected $ReplyToQueue = array();
514
515
    /**
516
     * The array of attachments.
517
     * @var array
518
     * @access protected
519
     */
520
    protected $attachment = array();
521
522
    /**
523
     * The array of custom headers.
524
     * @var array
525
     * @access protected
526
     */
527
    protected $CustomHeader = array();
528
529
    /**
530
     * The most recent Message-ID (including angular brackets).
531
     * @var string
532
     * @access protected
533
     */
534
    protected $lastMessageID = '';
535
536
    /**
537
     * The message's MIME type.
538
     * @var string
539
     * @access protected
540
     */
541
    protected $message_type = '';
542
543
    /**
544
     * The array of MIME boundary strings.
545
     * @var array
546
     * @access protected
547
     */
548
    protected $boundary = array();
549
550
    /**
551
     * The array of available languages.
552
     * @var array
553
     * @access protected
554
     */
555
    protected $language = array();
556
557
    /**
558
     * The number of errors encountered.
559
     * @var integer
560
     * @access protected
561
     */
562
    protected $error_count = 0;
563
564
    /**
565
     * The S/MIME certificate file path.
566
     * @var string
567
     * @access protected
568
     */
569
    protected $sign_cert_file = '';
570
571
    /**
572
     * The S/MIME key file path.
573
     * @var string
574
     * @access protected
575
     */
576
    protected $sign_key_file = '';
577
578
    /**
579
     * The optional S/MIME extra certificates ("CA Chain") file path.
580
     * @var string
581
     * @access protected
582
     */
583
    protected $sign_extracerts_file = '';
584
585
    /**
586
     * The S/MIME password for the key.
587
     * Used only if the key is encrypted.
588
     * @var string
589
     * @access protected
590
     */
591
    protected $sign_key_pass = '';
592
593
    /**
594
     * Whether to throw exceptions for errors.
595
     * @var boolean
596
     * @access protected
597
     */
598
    protected $exceptions = false;
599
600
    /**
601
     * Unique ID used for message ID and boundaries.
602
     * @var string
603
     * @access protected
604
     */
605
    protected $uniqueid = '';
606
607
    /**
608
     * Error severity: message only, continue processing.
609
     */
610
    const STOP_MESSAGE = 0;
611
612
    /**
613
     * Error severity: message, likely ok to continue processing.
614
     */
615
    const STOP_CONTINUE = 1;
616
617
    /**
618
     * Error severity: message, plus full stop, critical error reached.
619
     */
620
    const STOP_CRITICAL = 2;
621
622
    /**
623
     * SMTP RFC standard line ending.
624
     */
625
    const CRLF = "\r\n";
626
627
    /**
628
     * The maximum line length allowed by RFC 2822 section 2.1.1
629
     * @var integer
630
     */
631
    const MAX_LINE_LENGTH = 998;
632
633
    /**
634
     * Constructor.
635
     * @param boolean $exceptions Should we throw external exceptions?
636
     */
637
    public function __construct($exceptions = false)
638
    {
639
        $this->exceptions = (boolean)$exceptions;
640
    }
641
642
    /**
643
     * Destructor.
644
     */
645
    public function __destruct()
646
    {
647
        //Close any open SMTP connection nicely
648
        if ($this->Mailer == 'smtp') {
649
            $this->smtpClose();
650
        }
651
    }
652
653
    /**
654
     * Call mail() in a safe_mode-aware fashion.
655
     * Also, unless sendmail_path points to sendmail (or something that
656
     * claims to be sendmail), don't pass params (not a perfect fix,
657
     * but it will do)
658
     * @param string $to To
659
     * @param string $subject Subject
660
     * @param string $body Message Body
661
     * @param string $header Additional Header(s)
662
     * @param string $params Params
663
     * @access private
664
     * @return boolean
665
     */
666
    private function mailPassthru($to, $subject, $body, $header, $params)
667
    {
668
        //Check overloading of mail function to avoid double-encoding
669
        if (ini_get('mbstring.func_overload') & 1) {
670
            $subject = $this->secureHeader($subject);
671
        } else {
672
            $subject = $this->encodeHeader($this->secureHeader($subject));
673
        }
674
        if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
675
            $result = @mail($to, $subject, $body, $header);
676
        } else {
677
            $result = @mail($to, $subject, $body, $header, $params);
678
        }
679
        return $result;
680
    }
681
682
    /**
683
     * Output debugging info via user-defined method.
684
     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
685
     * @see PHPMailer::$Debugoutput
686
     * @see PHPMailer::$SMTPDebug
687
     * @param string $str
688
     */
689 View Code Duplication
    protected function edebug($str)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
690
    {
691
        if ($this->SMTPDebug <= 0) {
692
            return;
693
        }
694
        //Avoid clash with built-in function names
695
        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
696
            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
697
            return;
698
        }
699
        switch ($this->Debugoutput) {
700
            case 'error_log':
701
                //Don't output, just log
702
                error_log($str);
703
                break;
704
            case 'html':
705
                //Cleans up output a bit for a better looking, HTML-safe output
706
                echo htmlentities(
707
                    preg_replace('/[\r\n]+/', '', $str),
708
                    ENT_QUOTES,
709
                    'UTF-8'
710
                )
711
                . "<br>\n";
712
                break;
713
            case 'echo':
714
            default:
715
                //Normalize line breaks
716
                $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
717
                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
718
                    "\n",
719
                    "\n                   \t                  ",
720
                    trim($str)
721
                ) . "\n";
722
        }
723
    }
724
725
    /**
726
     * Sets message type to HTML or plain.
727
     * @param boolean $isHtml True for HTML mode.
728
     * @return void
729
     */
730
    public function isHTML($isHtml = true)
731
    {
732
        if ($isHtml) {
733
            $this->ContentType = 'text/html';
734
        } else {
735
            $this->ContentType = 'text/plain';
736
        }
737
    }
738
739
    /**
740
     * Send messages using SMTP.
741
     * @return void
742
     */
743
    public function isSMTP()
744
    {
745
        $this->Mailer = 'smtp';
746
    }
747
748
    /**
749
     * Send messages using PHP's mail() function.
750
     * @return void
751
     */
752
    public function isMail()
753
    {
754
        $this->Mailer = 'mail';
755
    }
756
757
    /**
758
     * Send messages using $Sendmail.
759
     * @return void
760
     */
761 View Code Duplication
    public function isSendmail()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
762
    {
763
        $ini_sendmail_path = ini_get('sendmail_path');
764
765
        if (!stristr($ini_sendmail_path, 'sendmail')) {
766
            $this->Sendmail = '/usr/sbin/sendmail';
767
        } else {
768
            $this->Sendmail = $ini_sendmail_path;
769
        }
770
        $this->Mailer = 'sendmail';
771
    }
772
773
    /**
774
     * Send messages using qmail.
775
     * @return void
776
     */
777 View Code Duplication
    public function isQmail()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
778
    {
779
        $ini_sendmail_path = ini_get('sendmail_path');
780
781
        if (!stristr($ini_sendmail_path, 'qmail')) {
782
            $this->Sendmail = '/var/qmail/bin/qmail-inject';
783
        } else {
784
            $this->Sendmail = $ini_sendmail_path;
785
        }
786
        $this->Mailer = 'qmail';
787
    }
788
789
    /**
790
     * Add a "To" address.
791
     * @param string $address The email address to send to
792
     * @param string $name
793
     * @return boolean true on success, false if address already used or invalid in some way
794
     */
795
    public function addAddress($address, $name = '')
796
    {
797
        return $this->addOrEnqueueAnAddress('to', $address, $name);
798
    }
799
800
    /**
801
     * Add a "CC" address.
802
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
803
     * @param string $address The email address to send to
804
     * @param string $name
805
     * @return boolean true on success, false if address already used or invalid in some way
806
     */
807
    public function addCC($address, $name = '')
808
    {
809
        return $this->addOrEnqueueAnAddress('cc', $address, $name);
810
    }
811
812
    /**
813
     * Add a "BCC" address.
814
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
815
     * @param string $address The email address to send to
816
     * @param string $name
817
     * @return boolean true on success, false if address already used or invalid in some way
818
     */
819
    public function addBCC($address, $name = '')
820
    {
821
        return $this->addOrEnqueueAnAddress('bcc', $address, $name);
822
    }
823
824
    /**
825
     * Add a "Reply-To" address.
826
     * @param string $address The email address to reply to
827
     * @param string $name
828
     * @return boolean true on success, false if address already used or invalid in some way
829
     */
830
    public function addReplyTo($address, $name = '')
831
    {
832
        return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
833
    }
834
835
    /**
836
     * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
837
     * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
838
     * be modified after calling this function), addition of such addresses is delayed until send().
839
     * Addresses that have been added already return false, but do not throw exceptions.
840
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
841
     * @param string $address The email address to send, resp. to reply to
842
     * @param string $name
843
     * @throws phpmailerException
844
     * @return boolean true on success, false if address already used or invalid in some way
845
     * @access protected
846
     */
847
    protected function addOrEnqueueAnAddress($kind, $address, $name)
848
    {
849
        $address = trim($address);
850
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
851
        if (($pos = strrpos($address, '@')) === false) {
852
            // At-sign is misssing.
853
            $error_message = $this->lang('invalid_address') . $address;
854
            $this->setError($error_message);
855
            $this->edebug($error_message);
856
            if ($this->exceptions) {
857
                throw new phpmailerException($error_message);
858
            }
859
            return false;
860
        }
861
        $params = array($kind, $address, $name);
862
        // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
863
        if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
864
            if ($kind != 'Reply-To') {
865
                if (!array_key_exists($address, $this->RecipientsQueue)) {
866
                    $this->RecipientsQueue[$address] = $params;
867
                    return true;
868
                }
869
            } else {
870
                if (!array_key_exists($address, $this->ReplyToQueue)) {
871
                    $this->ReplyToQueue[$address] = $params;
872
                    return true;
873
                }
874
            }
875
            return false;
876
        }
877
        // Immediately add standard addresses without IDN.
878
        return call_user_func_array(array($this, 'addAnAddress'), $params);
879
    }
880
881
    /**
882
     * Add an address to one of the recipient arrays or to the ReplyTo array.
883
     * Addresses that have been added already return false, but do not throw exceptions.
884
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
885
     * @param string $address The email address to send, resp. to reply to
886
     * @param string $name
887
     * @throws phpmailerException
888
     * @return boolean true on success, false if address already used or invalid in some way
889
     * @access protected
890
     */
891
    protected function addAnAddress($kind, $address, $name = '')
892
    {
893
        if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
894
            $error_message = $this->lang('Invalid recipient kind: ') . $kind;
895
            $this->setError($error_message);
896
            $this->edebug($error_message);
897
            if ($this->exceptions) {
898
                throw new phpmailerException($error_message);
899
            }
900
            return false;
901
        }
902 View Code Duplication
        if (!$this->validateAddress($address)) {
903
            $error_message = $this->lang('invalid_address') . $address;
904
            $this->setError($error_message);
905
            $this->edebug($error_message);
906
            if ($this->exceptions) {
907
                throw new phpmailerException($error_message);
908
            }
909
            return false;
910
        }
911
        if ($kind != 'Reply-To') {
912 View Code Duplication
            if (!array_key_exists(strtolower($address), $this->all_recipients)) {
913
                array_push($this->$kind, array($address, $name));
914
                $this->all_recipients[strtolower($address)] = true;
915
                return true;
916
            }
917
        } else {
918 View Code Duplication
            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
919
                $this->ReplyTo[strtolower($address)] = array($address, $name);
920
                return true;
921
            }
922
        }
923
        return false;
924
    }
925
926
    /**
927
     * Set the From and FromName properties.
928
     * @param string $address
929
     * @param string $name
930
     * @param boolean $auto Whether to also set the Sender address, defaults to true
931
     * @throws phpmailerException
932
     * @return boolean
933
     */
934
    public function setFrom($address, $name = '', $auto = true)
935
    {
936
        $address = trim($address);
937
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
938
        // Don't validate now addresses with IDN. Will be done in send().
939
        if (($pos = strrpos($address, '@')) === false or
940
            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
941
            !$this->validateAddress($address)) {
942
            $error_message = $this->lang('invalid_address') . $address;
943
            $this->setError($error_message);
944
            $this->edebug($error_message);
945
            if ($this->exceptions) {
946
                throw new phpmailerException($error_message);
947
            }
948
            return false;
949
        }
950
        $this->From = $address;
951
        $this->FromName = $name;
952
        if ($auto) {
953
            if (empty($this->Sender)) {
954
                $this->Sender = $address;
955
            }
956
        }
957
        return true;
958
    }
959
960
    /**
961
     * Return the Message-ID header of the last email.
962
     * Technically this is the value from the last time the headers were created,
963
     * but it's also the message ID of the last sent message except in
964
     * pathological cases.
965
     * @return string
966
     */
967
    public function getLastMessageID()
968
    {
969
        return $this->lastMessageID;
970
    }
971
972
    /**
973
     * Check that a string looks like an email address.
974
     * @param string $address The email address to check
975
     * @param string $patternselect A selector for the validation pattern to use :
976
     * * `auto` Pick best pattern automatically;
977
     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
978
     * * `pcre` Use old PCRE implementation;
979
     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
980
     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
981
     * * `noregex` Don't use a regex: super fast, really dumb.
982
     * @return boolean
983
     * @static
984
     * @access public
985
     */
986
    public static function validateAddress($address, $patternselect = 'auto')
987
    {
988
        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
989
        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
990
            return false;
991
        }
992
        if (!$patternselect or $patternselect == 'auto') {
993
            //Check this constant first so it works when extension_loaded() is disabled by safe mode
994
            //Constant was added in PHP 5.2.4
995
            if (defined('PCRE_VERSION')) {
996
                //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
997
                if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
998
                    $patternselect = 'pcre8';
999
                } else {
1000
                    $patternselect = 'pcre';
1001
                }
1002
            } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
1003
                //Fall back to older PCRE
1004
                $patternselect = 'pcre';
1005
            } else {
1006
                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1007
                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1008
                    $patternselect = 'php';
1009
                } else {
1010
                    $patternselect = 'noregex';
1011
                }
1012
            }
1013
        }
1014
        switch ($patternselect) {
1015 View Code Duplication
            case 'pcre8':
1016
                /**
1017
                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1018
                 * @link http://squiloople.com/2009/12/20/email-address-validation/
1019
                 * @copyright 2009-2010 Michael Rushton
1020
                 * Feel free to use and redistribute this code. But please keep this copyright notice.
1021
                 */
1022
                return (boolean)preg_match(
1023
                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1024
                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1025
                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1026
                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1027
                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1028
                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1029
                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1030
                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1031
                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1032
                    $address
1033
                );
1034 View Code Duplication
            case 'pcre':
1035
                //An older regex that doesn't need a recent PCRE
1036
                return (boolean)preg_match(
1037
                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1038
                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1039
                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1040
                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1041
                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1042
                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1043
                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1044
                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1045
                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1046
                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1047
                    $address
1048
                );
1049
            case 'html5':
1050
                /**
1051
                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1052
                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1053
                 */
1054
                return (boolean)preg_match(
1055
                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1056
                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1057
                    $address
1058
                );
1059
            case 'noregex':
1060
                //No PCRE! Do something _very_ approximate!
1061
                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1062
                return (strlen($address) >= 3
1063
                    and strpos($address, '@') >= 1
1064
                    and strpos($address, '@') != strlen($address) - 1);
1065
            case 'php':
1066
            default:
1067
                return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
1068
        }
1069
    }
1070
1071
    /**
1072
     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1073
     * "intl" and "mbstring" PHP extensions.
1074
     * @return bool "true" if required functions for IDN support are present
1075
     */
1076
    public function idnSupported()
1077
    {
1078
        // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1079
        return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1080
    }
1081
1082
    /**
1083
     * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1084
     * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1085
     * This function silently returns unmodified address if:
1086
     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1087
     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1088
     *   or fails for any reason (e.g. domain has characters not allowed in an IDN)
1089
     * @see PHPMailer::$CharSet
1090
     * @param string $address The email address to convert
1091
     * @return string The encoded address in ASCII form
1092
     */
1093
    public function punyencodeAddress($address)
1094
    {
1095
        // Verify we have required functions, CharSet, and at-sign.
1096
        if ($this->idnSupported() and
1097
            !empty($this->CharSet) and
1098
            ($pos = strrpos($address, '@')) !== false) {
1099
            $domain = substr($address, ++$pos);
1100
            // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1101
            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1102
                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1103
                if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1104
                    idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
1105
                    idn_to_ascii($domain)) !== false) {
1106
                    return substr($address, 0, $pos) . $punycode;
1107
                }
1108
            }
1109
        }
1110
        return $address;
1111
    }
1112
1113
    /**
1114
     * Create a message and send it.
1115
     * Uses the sending method specified by $Mailer.
1116
     * @throws phpmailerException
1117
     * @return boolean false on error - See the ErrorInfo property for details of the error.
1118
     */
1119
    public function send()
1120
    {
1121
        try {
1122
            if (!$this->preSend()) {
1123
                return false;
1124
            }
1125
            return $this->postSend();
1126
        } catch (phpmailerException $exc) {
1127
            $this->mailHeader = '';
1128
            $this->setError($exc->getMessage());
1129
            if ($this->exceptions) {
1130
                throw $exc;
1131
            }
1132
            return false;
1133
        }
1134
    }
1135
1136
    /**
1137
     * Prepare a message for sending.
1138
     * @throws phpmailerException
1139
     * @return boolean
1140
     */
1141
    public function preSend()
1142
    {
1143
        try {
1144
            $this->error_count = 0; // Reset errors
1145
            $this->mailHeader = '';
1146
1147
            // Dequeue recipient and Reply-To addresses with IDN
1148
            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1149
                $params[1] = $this->punyencodeAddress($params[1]);
1150
                call_user_func_array(array($this, 'addAnAddress'), $params);
1151
            }
1152
            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1153
                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1154
            }
1155
1156
            // Validate From, Sender, and ConfirmReadingTo addresses
1157
            foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
1158
                $this->$address_kind = trim($this->$address_kind);
1159
                if (empty($this->$address_kind)) {
1160
                    continue;
1161
                }
1162
                $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1163 View Code Duplication
                if (!$this->validateAddress($this->$address_kind)) {
1164
                    $error_message = $this->lang('invalid_address') . $this->$address_kind;
1165
                    $this->setError($error_message);
1166
                    $this->edebug($error_message);
1167
                    if ($this->exceptions) {
1168
                        throw new phpmailerException($error_message);
1169
                    }
1170
                    return false;
1171
                }
1172
            }
1173
1174
            // Set whether the message is multipart/alternative
1175
            if (!empty($this->AltBody)) {
1176
                $this->ContentType = 'multipart/alternative';
1177
            }
1178
1179
            $this->setMessageType();
1180
            // Refuse to send an empty message unless we are specifically allowing it
1181
            if (!$this->AllowEmpty and empty($this->Body)) {
1182
                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1183
            }
1184
1185
            // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1186
            $this->MIMEHeader = '';
1187
            $this->MIMEBody = $this->createBody();
1188
            // createBody may have added some headers, so retain them
1189
            $tempheaders = $this->MIMEHeader;
1190
            $this->MIMEHeader = $this->createHeader();
1191
            $this->MIMEHeader .= $tempheaders;
1192
1193
            // To capture the complete message when using mail(), create
1194
            // an extra header list which createHeader() doesn't fold in
1195
            if ($this->Mailer == 'mail') {
1196
                if (count($this->to) > 0) {
1197
                    $this->mailHeader .= $this->addrAppend('To', $this->to);
1198
                } else {
1199
                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1200
                }
1201
                $this->mailHeader .= $this->headerLine(
1202
                    'Subject',
1203
                    $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1204
                );
1205
            }
1206
1207
            // Sign with DKIM if enabled
1208
            if (!empty($this->DKIM_domain)
1209
                && !empty($this->DKIM_private)
1210
                && !empty($this->DKIM_selector)
1211
                && file_exists($this->DKIM_private)) {
1212
                $header_dkim = $this->DKIM_Add(
1213
                    $this->MIMEHeader . $this->mailHeader,
1214
                    $this->encodeHeader($this->secureHeader($this->Subject)),
1215
                    $this->MIMEBody
1216
                );
1217
                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1218
                    str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1219
            }
1220
            return true;
1221
        } catch (phpmailerException $exc) {
1222
            $this->setError($exc->getMessage());
1223
            if ($this->exceptions) {
1224
                throw $exc;
1225
            }
1226
            return false;
1227
        }
1228
    }
1229
1230
    /**
1231
     * Actually send a message.
1232
     * Send the email via the selected mechanism
1233
     * @throws phpmailerException
1234
     * @return boolean
1235
     */
1236
    public function postSend()
1237
    {
1238
        try {
1239
            // Choose the mailer and send through it
1240
            switch ($this->Mailer) {
1241
                case 'sendmail':
1242
                case 'qmail':
1243
                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1244
                case 'smtp':
1245
                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1246
                case 'mail':
1247
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1248
                default:
1249
                    $sendMethod = $this->Mailer.'Send';
1250
                    if (method_exists($this, $sendMethod)) {
1251
                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1252
                    }
1253
1254
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1255
            }
1256
        } catch (phpmailerException $exc) {
1257
            $this->setError($exc->getMessage());
1258
            $this->edebug($exc->getMessage());
1259
            if ($this->exceptions) {
1260
                throw $exc;
1261
            }
1262
        }
1263
        return false;
1264
    }
1265
1266
    /**
1267
     * Send mail using the $Sendmail program.
1268
     * @param string $header The message headers
1269
     * @param string $body The message body
1270
     * @see PHPMailer::$Sendmail
1271
     * @throws phpmailerException
1272
     * @access protected
1273
     * @return boolean
1274
     */
1275
    protected function sendmailSend($header, $body)
1276
    {
1277
        if ($this->Sender != '') {
1278
            if ($this->Mailer == 'qmail') {
1279
                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1280
            } else {
1281
                $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1282
            }
1283
        } else {
1284
            if ($this->Mailer == 'qmail') {
1285
                $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1286
            } else {
1287
                $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1288
            }
1289
        }
1290
        if ($this->SingleTo) {
1291
            foreach ($this->SingleToArray as $toAddr) {
1292 View Code Duplication
                if (!@$mail = popen($sendmail, 'w')) {
1293
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1294
                }
1295
                fputs($mail, 'To: ' . $toAddr . "\n");
1296
                fputs($mail, $header);
1297
                fputs($mail, $body);
1298
                $result = pclose($mail);
1299
                $this->doCallback(
1300
                    ($result == 0),
1301
                    array($toAddr),
1302
                    $this->cc,
1303
                    $this->bcc,
1304
                    $this->Subject,
1305
                    $body,
1306
                    $this->From
1307
                );
1308
                if ($result != 0) {
1309
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1310
                }
1311
            }
1312
        } else {
1313 View Code Duplication
            if (!@$mail = popen($sendmail, 'w')) {
1314
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1315
            }
1316
            fputs($mail, $header);
1317
            fputs($mail, $body);
1318
            $result = pclose($mail);
1319
            $this->doCallback(
1320
                ($result == 0),
1321
                $this->to,
1322
                $this->cc,
1323
                $this->bcc,
1324
                $this->Subject,
1325
                $body,
1326
                $this->From
1327
            );
1328
            if ($result != 0) {
1329
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1330
            }
1331
        }
1332
        return true;
1333
    }
1334
1335
    /**
1336
     * Send mail using the PHP mail() function.
1337
     * @param string $header The message headers
1338
     * @param string $body The message body
1339
     * @link http://www.php.net/manual/en/book.mail.php
1340
     * @throws phpmailerException
1341
     * @access protected
1342
     * @return boolean
1343
     */
1344
    protected function mailSend($header, $body)
1345
    {
1346
        $toArr = array();
1347
        foreach ($this->to as $toaddr) {
1348
            $toArr[] = $this->addrFormat($toaddr);
1349
        }
1350
        $to = implode(', ', $toArr);
1351
1352
        if (empty($this->Sender)) {
1353
            $params = ' ';
1354
        } else {
1355
            $params = sprintf('-f%s', $this->Sender);
1356
        }
1357
        if ($this->Sender != '' and !ini_get('safe_mode')) {
1358
            $old_from = ini_get('sendmail_from');
1359
            ini_set('sendmail_from', $this->Sender);
1360
        }
1361
        $result = false;
1362
        if ($this->SingleTo && count($toArr) > 1) {
1363
            foreach ($toArr as $toAddr) {
1364
                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1365
                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1366
            }
1367
        } else {
1368
            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1369
            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1370
        }
1371
        if (isset($old_from)) {
1372
            ini_set('sendmail_from', $old_from);
1373
        }
1374
        if (!$result) {
1375
            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1376
        }
1377
        return true;
1378
    }
1379
1380
    /**
1381
     * Get an instance to use for SMTP operations.
1382
     * Override this function to load your own SMTP implementation
1383
     * @return SMTP
1384
     */
1385
    public function getSMTPInstance()
1386
    {
1387
        if (!is_object($this->smtp)) {
1388
        	require_once( 'class-smtp.php' );
1389
            $this->smtp = new SMTP;
1390
        }
1391
        return $this->smtp;
1392
    }
1393
1394
    /**
1395
     * Send mail via SMTP.
1396
     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1397
     * Uses the PHPMailerSMTP class by default.
1398
     * @see PHPMailer::getSMTPInstance() to use a different class.
1399
     * @param string $header The message headers
1400
     * @param string $body The message body
1401
     * @throws phpmailerException
1402
     * @uses SMTP
1403
     * @access protected
1404
     * @return boolean
1405
     */
1406
    protected function smtpSend($header, $body)
1407
    {
1408
        $bad_rcpt = array();
1409
        if (!$this->smtpConnect($this->SMTPOptions)) {
1410
            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1411
        }
1412
        if ('' == $this->Sender) {
1413
            $smtp_from = $this->From;
1414
        } else {
1415
            $smtp_from = $this->Sender;
1416
        }
1417
        if (!$this->smtp->mail($smtp_from)) {
1418
            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1419
            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1420
        }
1421
1422
        // Attempt to send to all recipients
1423
        foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1424
            foreach ($togroup as $to) {
1425
                if (!$this->smtp->recipient($to[0])) {
1426
                    $error = $this->smtp->getError();
1427
                    $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1428
                    $isSent = false;
1429
                } else {
1430
                    $isSent = true;
1431
                }
1432
                $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1433
            }
1434
        }
1435
1436
        // Only send the DATA command if we have viable recipients
1437
        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1438
            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1439
        }
1440
        if ($this->SMTPKeepAlive) {
1441
            $this->smtp->reset();
1442
        } else {
1443
            $this->smtp->quit();
1444
            $this->smtp->close();
1445
        }
1446
        //Create error message for any bad addresses
1447
        if (count($bad_rcpt) > 0) {
1448
            $errstr = '';
1449
            foreach ($bad_rcpt as $bad) {
1450
                $errstr .= $bad['to'] . ': ' . $bad['error'];
1451
            }
1452
            throw new phpmailerException(
1453
                $this->lang('recipients_failed') . $errstr,
1454
                self::STOP_CONTINUE
1455
            );
1456
        }
1457
        return true;
1458
    }
1459
1460
    /**
1461
     * Initiate a connection to an SMTP server.
1462
     * Returns false if the operation failed.
1463
     * @param array $options An array of options compatible with stream_context_create()
1464
     * @uses SMTP
1465
     * @access public
1466
     * @throws phpmailerException
1467
     * @return boolean
1468
     */
1469
    public function smtpConnect($options = array())
1470
    {
1471
        if (is_null($this->smtp)) {
1472
            $this->smtp = $this->getSMTPInstance();
1473
        }
1474
1475
        // Already connected?
1476
        if ($this->smtp->connected()) {
1477
            return true;
1478
        }
1479
1480
        $this->smtp->setTimeout($this->Timeout);
1481
        $this->smtp->setDebugLevel($this->SMTPDebug);
1482
        $this->smtp->setDebugOutput($this->Debugoutput);
1483
        $this->smtp->setVerp($this->do_verp);
1484
        $hosts = explode(';', $this->Host);
1485
        $lastexception = null;
1486
1487
        foreach ($hosts as $hostentry) {
1488
            $hostinfo = array();
1489
            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1490
                // Not a valid host entry
1491
                continue;
1492
            }
1493
            // $hostinfo[2]: optional ssl or tls prefix
1494
            // $hostinfo[3]: the hostname
1495
            // $hostinfo[4]: optional port number
1496
            // The host string prefix can temporarily override the current setting for SMTPSecure
1497
            // If it's not specified, the default value is used
1498
            $prefix = '';
1499
            $secure = $this->SMTPSecure;
1500
            $tls = ($this->SMTPSecure == 'tls');
1501
            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1502
                $prefix = 'ssl://';
1503
                $tls = false; // Can't have SSL and TLS at the same time
1504
                $secure = 'ssl';
1505
            } elseif ($hostinfo[2] == 'tls') {
1506
                $tls = true;
1507
                // tls doesn't use a prefix
1508
                $secure = 'tls';
1509
            }
1510
            //Do we need the OpenSSL extension?
1511
            $sslext = defined('OPENSSL_ALGO_SHA1');
1512
            if ('tls' === $secure or 'ssl' === $secure) {
1513
                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1514
                if (!$sslext) {
1515
                    throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1516
                }
1517
            }
1518
            $host = $hostinfo[3];
1519
            $port = $this->Port;
1520
            $tport = (integer)$hostinfo[4];
1521
            if ($tport > 0 and $tport < 65536) {
1522
                $port = $tport;
1523
            }
1524
            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1525
                try {
1526
                    if ($this->Helo) {
1527
                        $hello = $this->Helo;
1528
                    } else {
1529
                        $hello = $this->serverHostname();
1530
                    }
1531
                    $this->smtp->hello($hello);
1532
                    //Automatically enable TLS encryption if:
1533
                    // * it's not disabled
1534
                    // * we have openssl extension
1535
                    // * we are not already using SSL
1536
                    // * the server offers STARTTLS
1537
                    if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1538
                        $tls = true;
1539
                    }
1540
                    if ($tls) {
1541
                        if (!$this->smtp->startTLS()) {
1542
                            throw new phpmailerException($this->lang('connect_host'));
1543
                        }
1544
                        // We must resend HELO after tls negotiation
1545
                        $this->smtp->hello($hello);
1546
                    }
1547
                    if ($this->SMTPAuth) {
1548
                        if (!$this->smtp->authenticate(
1549
                            $this->Username,
1550
                            $this->Password,
1551
                            $this->AuthType,
1552
                            $this->Realm,
1553
                            $this->Workstation
1554
                        )
1555
                        ) {
1556
                            throw new phpmailerException($this->lang('authenticate'));
1557
                        }
1558
                    }
1559
                    return true;
1560
                } catch (phpmailerException $exc) {
1561
                    $lastexception = $exc;
1562
                    $this->edebug($exc->getMessage());
1563
                    // We must have connected, but then failed TLS or Auth, so close connection nicely
1564
                    $this->smtp->quit();
1565
                }
1566
            }
1567
        }
1568
        // If we get here, all connection attempts have failed, so close connection hard
1569
        $this->smtp->close();
1570
        // As we've caught all exceptions, just report whatever the last one was
1571
        if ($this->exceptions and !is_null($lastexception)) {
1572
            throw $lastexception;
1573
        }
1574
        return false;
1575
    }
1576
1577
    /**
1578
     * Close the active SMTP session if one exists.
1579
     * @return void
1580
     */
1581
    public function smtpClose()
1582
    {
1583
        if ($this->smtp !== null) {
1584
            if ($this->smtp->connected()) {
1585
                $this->smtp->quit();
1586
                $this->smtp->close();
1587
            }
1588
        }
1589
    }
1590
1591
    /**
1592
     * Set the language for error messages.
1593
     * Returns false if it cannot load the language file.
1594
     * The default language is English.
1595
     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1596
     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1597
     * @return boolean
1598
     * @access public
1599
     */
1600
    public function setLanguage($langcode = 'en', $lang_path = '')
1601
    {
1602
        // Define full set of translatable strings in English
1603
        $PHPMAILER_LANG = array(
1604
            'authenticate' => 'SMTP Error: Could not authenticate.',
1605
            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1606
            'data_not_accepted' => 'SMTP Error: data not accepted.',
1607
            'empty_message' => 'Message body empty',
1608
            'encoding' => 'Unknown encoding: ',
1609
            'execute' => 'Could not execute: ',
1610
            'file_access' => 'Could not access file: ',
1611
            'file_open' => 'File Error: Could not open file: ',
1612
            'from_failed' => 'The following From address failed: ',
1613
            'instantiate' => 'Could not instantiate mail function.',
1614
            'invalid_address' => 'Invalid address: ',
1615
            'mailer_not_supported' => ' mailer is not supported.',
1616
            'provide_address' => 'You must provide at least one recipient email address.',
1617
            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1618
            'signing' => 'Signing Error: ',
1619
            'smtp_connect_failed' => 'SMTP connect() failed.',
1620
            'smtp_error' => 'SMTP server error: ',
1621
            'variable_set' => 'Cannot set or reset variable: ',
1622
            'extension_missing' => 'Extension missing: '
1623
        );
1624
        if (empty($lang_path)) {
1625
            // Calculate an absolute path so it can work if CWD is not here
1626
            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1627
        }
1628
        $foundlang = true;
1629
        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1630
        // There is no English translation file
1631
        if ($langcode != 'en') {
1632
            // Make sure language file path is readable
1633
            if (!is_readable($lang_file)) {
1634
                $foundlang = false;
1635
            } else {
1636
                // Overwrite language-specific strings.
1637
                // This way we'll never have missing translation keys.
1638
                $foundlang = include $lang_file;
1639
            }
1640
        }
1641
        $this->language = $PHPMAILER_LANG;
1642
        return (boolean)$foundlang; // Returns false if language not found
1643
    }
1644
1645
    /**
1646
     * Get the array of strings for the current language.
1647
     * @return array
1648
     */
1649
    public function getTranslations()
1650
    {
1651
        return $this->language;
1652
    }
1653
1654
    /**
1655
     * Create recipient headers.
1656
     * @access public
1657
     * @param string $type
1658
     * @param array $addr An array of recipient,
1659
     * where each recipient is a 2-element indexed array with element 0 containing an address
1660
     * and element 1 containing a name, like:
1661
     * array(array('[email protected]', 'Joe User'), array('[email protected]', 'Zoe User'))
1662
     * @return string
1663
     */
1664
    public function addrAppend($type, $addr)
1665
    {
1666
        $addresses = array();
1667
        foreach ($addr as $address) {
1668
            $addresses[] = $this->addrFormat($address);
1669
        }
1670
        return $type . ': ' . implode(', ', $addresses) . $this->LE;
1671
    }
1672
1673
    /**
1674
     * Format an address for use in a message header.
1675
     * @access public
1676
     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1677
     *      like array('[email protected]', 'Joe User')
1678
     * @return string
1679
     */
1680
    public function addrFormat($addr)
1681
    {
1682
        if (empty($addr[1])) { // No name provided
1683
            return $this->secureHeader($addr[0]);
1684
        } else {
1685
            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1686
                $addr[0]
1687
            ) . '>';
1688
        }
1689
    }
1690
1691
    /**
1692
     * Word-wrap message.
1693
     * For use with mailers that do not automatically perform wrapping
1694
     * and for quoted-printable encoded messages.
1695
     * Original written by philippe.
1696
     * @param string $message The message to wrap
1697
     * @param integer $length The line length to wrap to
1698
     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1699
     * @access public
1700
     * @return string
1701
     */
1702
    public function wrapText($message, $length, $qp_mode = false)
1703
    {
1704
        if ($qp_mode) {
1705
            $soft_break = sprintf(' =%s', $this->LE);
1706
        } else {
1707
            $soft_break = $this->LE;
1708
        }
1709
        // If utf-8 encoding is used, we will need to make sure we don't
1710
        // split multibyte characters when we wrap
1711
        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1712
        $lelen = strlen($this->LE);
1713
        $crlflen = strlen(self::CRLF);
1714
1715
        $message = $this->fixEOL($message);
1716
        //Remove a trailing line break
1717
        if (substr($message, -$lelen) == $this->LE) {
1718
            $message = substr($message, 0, -$lelen);
1719
        }
1720
1721
        //Split message into lines
1722
        $lines = explode($this->LE, $message);
1723
        //Message will be rebuilt in here
1724
        $message = '';
1725
        foreach ($lines as $line) {
1726
            $words = explode(' ', $line);
1727
            $buf = '';
1728
            $firstword = true;
1729
            foreach ($words as $word) {
1730
                if ($qp_mode and (strlen($word) > $length)) {
1731
                    $space_left = $length - strlen($buf) - $crlflen;
1732
                    if (!$firstword) {
1733
                        if ($space_left > 20) {
1734
                            $len = $space_left;
1735 View Code Duplication
                            if ($is_utf8) {
1736
                                $len = $this->utf8CharBoundary($word, $len);
1737
                            } elseif (substr($word, $len - 1, 1) == '=') {
1738
                                $len--;
1739
                            } elseif (substr($word, $len - 2, 1) == '=') {
1740
                                $len -= 2;
1741
                            }
1742
                            $part = substr($word, 0, $len);
1743
                            $word = substr($word, $len);
1744
                            $buf .= ' ' . $part;
1745
                            $message .= $buf . sprintf('=%s', self::CRLF);
1746
                        } else {
1747
                            $message .= $buf . $soft_break;
1748
                        }
1749
                        $buf = '';
1750
                    }
1751
                    while (strlen($word) > 0) {
1752
                        if ($length <= 0) {
1753
                            break;
1754
                        }
1755
                        $len = $length;
1756 View Code Duplication
                        if ($is_utf8) {
1757
                            $len = $this->utf8CharBoundary($word, $len);
1758
                        } elseif (substr($word, $len - 1, 1) == '=') {
1759
                            $len--;
1760
                        } elseif (substr($word, $len - 2, 1) == '=') {
1761
                            $len -= 2;
1762
                        }
1763
                        $part = substr($word, 0, $len);
1764
                        $word = substr($word, $len);
1765
1766
                        if (strlen($word) > 0) {
1767
                            $message .= $part . sprintf('=%s', self::CRLF);
1768
                        } else {
1769
                            $buf = $part;
1770
                        }
1771
                    }
1772
                } else {
1773
                    $buf_o = $buf;
1774
                    if (!$firstword) {
1775
                        $buf .= ' ';
1776
                    }
1777
                    $buf .= $word;
1778
1779
                    if (strlen($buf) > $length and $buf_o != '') {
1780
                        $message .= $buf_o . $soft_break;
1781
                        $buf = $word;
1782
                    }
1783
                }
1784
                $firstword = false;
1785
            }
1786
            $message .= $buf . self::CRLF;
1787
        }
1788
1789
        return $message;
1790
    }
1791
1792
    /**
1793
     * Find the last character boundary prior to $maxLength in a utf-8
1794
     * quoted-printable encoded string.
1795
     * Original written by Colin Brown.
1796
     * @access public
1797
     * @param string $encodedText utf-8 QP text
1798
     * @param integer $maxLength Find the last character boundary prior to this length
1799
     * @return integer
1800
     */
1801
    public function utf8CharBoundary($encodedText, $maxLength)
1802
    {
1803
        $foundSplitPos = false;
1804
        $lookBack = 3;
1805
        while (!$foundSplitPos) {
1806
            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1807
            $encodedCharPos = strpos($lastChunk, '=');
1808
            if (false !== $encodedCharPos) {
1809
                // Found start of encoded character byte within $lookBack block.
1810
                // Check the encoded byte value (the 2 chars after the '=')
1811
                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1812
                $dec = hexdec($hex);
1813
                if ($dec < 128) {
1814
                    // Single byte character.
1815
                    // If the encoded char was found at pos 0, it will fit
1816
                    // otherwise reduce maxLength to start of the encoded char
1817
                    if ($encodedCharPos > 0) {
1818
                        $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1819
                    }
1820
                    $foundSplitPos = true;
1821
                } elseif ($dec >= 192) {
1822
                    // First byte of a multi byte character
1823
                    // Reduce maxLength to split at start of character
1824
                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1825
                    $foundSplitPos = true;
1826
                } elseif ($dec < 192) {
1827
                    // Middle byte of a multi byte character, look further back
1828
                    $lookBack += 3;
1829
                }
1830
            } else {
1831
                // No encoded character found
1832
                $foundSplitPos = true;
1833
            }
1834
        }
1835
        return $maxLength;
1836
    }
1837
1838
    /**
1839
     * Apply word wrapping to the message body.
1840
     * Wraps the message body to the number of chars set in the WordWrap property.
1841
     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
1842
     * This is called automatically by createBody(), so you don't need to call it yourself.
1843
     * @access public
1844
     * @return void
1845
     */
1846
    public function setWordWrap()
1847
    {
1848
        if ($this->WordWrap < 1) {
1849
            return;
1850
        }
1851
1852
        switch ($this->message_type) {
1853
            case 'alt':
1854
            case 'alt_inline':
1855
            case 'alt_attach':
1856
            case 'alt_inline_attach':
1857
                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1858
                break;
1859
            default:
1860
                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1861
                break;
1862
        }
1863
    }
1864
1865
    /**
1866
     * Assemble message headers.
1867
     * @access public
1868
     * @return string The assembled headers
1869
     */
1870
    public function createHeader()
1871
    {
1872
        $result = '';
1873
1874
        if ($this->MessageDate == '') {
1875
            $this->MessageDate = self::rfcDate();
1876
        }
1877
        $result .= $this->headerLine('Date', $this->MessageDate);
1878
1879
        // To be created automatically by mail()
1880
        if ($this->SingleTo) {
1881
            if ($this->Mailer != 'mail') {
1882
                foreach ($this->to as $toaddr) {
1883
                    $this->SingleToArray[] = $this->addrFormat($toaddr);
1884
                }
1885
            }
1886
        } else {
1887
            if (count($this->to) > 0) {
1888
                if ($this->Mailer != 'mail') {
1889
                    $result .= $this->addrAppend('To', $this->to);
1890
                }
1891
            } elseif (count($this->cc) == 0) {
1892
                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1893
            }
1894
        }
1895
1896
        $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1897
1898
        // sendmail and mail() extract Cc from the header before sending
1899
        if (count($this->cc) > 0) {
1900
            $result .= $this->addrAppend('Cc', $this->cc);
1901
        }
1902
1903
        // sendmail and mail() extract Bcc from the header before sending
1904
        if ((
1905
                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1906
            )
1907
            and count($this->bcc) > 0
1908
        ) {
1909
            $result .= $this->addrAppend('Bcc', $this->bcc);
1910
        }
1911
1912
        if (count($this->ReplyTo) > 0) {
1913
            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1914
        }
1915
1916
        // mail() sets the subject itself
1917
        if ($this->Mailer != 'mail') {
1918
            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1919
        }
1920
1921
        if ($this->MessageID != '') {
1922
            $this->lastMessageID = $this->MessageID;
1923
        } else {
1924
            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
1925
        }
1926
        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
1927
        if (!is_null($this->Priority)) {
1928
            $result .= $this->headerLine('X-Priority', $this->Priority);
1929
        }
1930
        if ($this->XMailer == '') {
1931
            $result .= $this->headerLine(
1932
                'X-Mailer',
1933
                'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
1934
            );
1935
        } else {
1936
            $myXmailer = trim($this->XMailer);
1937
            if ($myXmailer) {
1938
                $result .= $this->headerLine('X-Mailer', $myXmailer);
1939
            }
1940
        }
1941
1942
        if ($this->ConfirmReadingTo != '') {
1943
            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
1944
        }
1945
1946
        // Add custom headers
1947
        foreach ($this->CustomHeader as $header) {
1948
            $result .= $this->headerLine(
1949
                trim($header[0]),
1950
                $this->encodeHeader(trim($header[1]))
1951
            );
1952
        }
1953
        if (!$this->sign_key_file) {
1954
            $result .= $this->headerLine('MIME-Version', '1.0');
1955
            $result .= $this->getMailMIME();
1956
        }
1957
1958
        return $result;
1959
    }
1960
1961
    /**
1962
     * Get the message MIME type headers.
1963
     * @access public
1964
     * @return string
1965
     */
1966
    public function getMailMIME()
1967
    {
1968
        $result = '';
1969
        $ismultipart = true;
1970
        switch ($this->message_type) {
1971 View Code Duplication
            case 'inline':
1972
                $result .= $this->headerLine('Content-Type', 'multipart/related;');
1973
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1974
                break;
1975
            case 'attach':
1976
            case 'inline_attach':
1977
            case 'alt_attach':
1978 View Code Duplication
            case 'alt_inline_attach':
1979
                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
1980
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1981
                break;
1982
            case 'alt':
1983 View Code Duplication
            case 'alt_inline':
1984
                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
1985
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1986
                break;
1987
            default:
1988
                // Catches case 'plain': and case '':
1989
                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
1990
                $ismultipart = false;
1991
                break;
1992
        }
1993
        // RFC1341 part 5 says 7bit is assumed if not specified
1994
        if ($this->Encoding != '7bit') {
1995
            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
1996
            if ($ismultipart) {
1997
                if ($this->Encoding == '8bit') {
1998
                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
1999
                }
2000
                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2001
            } else {
2002
                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2003
            }
2004
        }
2005
2006
        if ($this->Mailer != 'mail') {
2007
            $result .= $this->LE;
2008
        }
2009
2010
        return $result;
2011
    }
2012
2013
    /**
2014
     * Returns the whole MIME message.
2015
     * Includes complete headers and body.
2016
     * Only valid post preSend().
2017
     * @see PHPMailer::preSend()
2018
     * @access public
2019
     * @return string
2020
     */
2021
    public function getSentMIMEMessage()
2022
    {
2023
        return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
2024
    }
2025
2026
    /**
2027
     * Assemble the message body.
2028
     * Returns an empty string on failure.
2029
     * @access public
2030
     * @throws phpmailerException
2031
     * @return string The assembled message body
2032
     */
2033
    public function createBody()
2034
    {
2035
        $body = '';
2036
        //Create unique IDs and preset boundaries
2037
        $this->uniqueid = md5(uniqid(time()));
2038
        $this->boundary[1] = 'b1_' . $this->uniqueid;
2039
        $this->boundary[2] = 'b2_' . $this->uniqueid;
2040
        $this->boundary[3] = 'b3_' . $this->uniqueid;
2041
2042
        if ($this->sign_key_file) {
2043
            $body .= $this->getMailMIME() . $this->LE;
2044
        }
2045
2046
        $this->setWordWrap();
2047
2048
        $bodyEncoding = $this->Encoding;
2049
        $bodyCharSet = $this->CharSet;
2050
        //Can we do a 7-bit downgrade?
2051
        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2052
            $bodyEncoding = '7bit';
2053
            $bodyCharSet = 'us-ascii';
2054
        }
2055
        //If lines are too long, change to quoted-printable transfer encoding
2056
        if (self::hasLineLongerThanMax($this->Body)) {
2057
            $this->Encoding = 'quoted-printable';
2058
            $bodyEncoding = 'quoted-printable';
2059
        }
2060
2061
        $altBodyEncoding = $this->Encoding;
2062
        $altBodyCharSet = $this->CharSet;
2063
        //Can we do a 7-bit downgrade?
2064
        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2065
            $altBodyEncoding = '7bit';
2066
            $altBodyCharSet = 'us-ascii';
2067
        }
2068
        //If lines are too long, change to quoted-printable transfer encoding
2069
        if (self::hasLineLongerThanMax($this->AltBody)) {
2070
            $altBodyEncoding = 'quoted-printable';
2071
        }
2072
        //Use this as a preamble in all multipart message types
2073
        $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
2074
        switch ($this->message_type) {
2075 View Code Duplication
            case 'inline':
2076
                $body .= $mimepre;
2077
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2078
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2079
                $body .= $this->LE . $this->LE;
2080
                $body .= $this->attachAll('inline', $this->boundary[1]);
2081
                break;
2082 View Code Duplication
            case 'attach':
2083
                $body .= $mimepre;
2084
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2085
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2086
                $body .= $this->LE . $this->LE;
2087
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2088
                break;
2089
            case 'inline_attach':
2090
                $body .= $mimepre;
2091
                $body .= $this->textLine('--' . $this->boundary[1]);
2092
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2093
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2094
                $body .= $this->LE;
2095
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2096
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2097
                $body .= $this->LE . $this->LE;
2098
                $body .= $this->attachAll('inline', $this->boundary[2]);
2099
                $body .= $this->LE;
2100
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2101
                break;
2102
            case 'alt':
2103
                $body .= $mimepre;
2104
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2105
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2106
                $body .= $this->LE . $this->LE;
2107
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2108
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2109
                $body .= $this->LE . $this->LE;
2110
                if (!empty($this->Ical)) {
2111
                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2112
                    $body .= $this->encodeString($this->Ical, $this->Encoding);
2113
                    $body .= $this->LE . $this->LE;
2114
                }
2115
                $body .= $this->endBoundary($this->boundary[1]);
2116
                break;
2117 View Code Duplication
            case 'alt_inline':
2118
                $body .= $mimepre;
2119
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2120
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2121
                $body .= $this->LE . $this->LE;
2122
                $body .= $this->textLine('--' . $this->boundary[1]);
2123
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2124
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2125
                $body .= $this->LE;
2126
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2127
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2128
                $body .= $this->LE . $this->LE;
2129
                $body .= $this->attachAll('inline', $this->boundary[2]);
2130
                $body .= $this->LE;
2131
                $body .= $this->endBoundary($this->boundary[1]);
2132
                break;
2133 View Code Duplication
            case 'alt_attach':
2134
                $body .= $mimepre;
2135
                $body .= $this->textLine('--' . $this->boundary[1]);
2136
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2137
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2138
                $body .= $this->LE;
2139
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2140
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2141
                $body .= $this->LE . $this->LE;
2142
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2143
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2144
                $body .= $this->LE . $this->LE;
2145
                $body .= $this->endBoundary($this->boundary[2]);
2146
                $body .= $this->LE;
2147
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2148
                break;
2149
            case 'alt_inline_attach':
2150
                $body .= $mimepre;
2151
                $body .= $this->textLine('--' . $this->boundary[1]);
2152
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2153
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2154
                $body .= $this->LE;
2155
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2156
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2157
                $body .= $this->LE . $this->LE;
2158
                $body .= $this->textLine('--' . $this->boundary[2]);
2159
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2160
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2161
                $body .= $this->LE;
2162
                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2163
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2164
                $body .= $this->LE . $this->LE;
2165
                $body .= $this->attachAll('inline', $this->boundary[3]);
2166
                $body .= $this->LE;
2167
                $body .= $this->endBoundary($this->boundary[2]);
2168
                $body .= $this->LE;
2169
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2170
                break;
2171
            default:
2172
                // catch case 'plain' and case ''
2173
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2174
                break;
2175
        }
2176
2177
        if ($this->isError()) {
2178
            $body = '';
2179
        } elseif ($this->sign_key_file) {
2180
            try {
2181
                if (!defined('PKCS7_TEXT')) {
2182
                    throw new phpmailerException($this->lang('extension_missing') . 'openssl');
2183
                }
2184
                // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2185
                $file = tempnam(sys_get_temp_dir(), 'mail');
2186
                if (false === file_put_contents($file, $body)) {
2187
                    throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2188
                }
2189
                $signed = tempnam(sys_get_temp_dir(), 'signed');
2190
                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2191
                if (empty($this->sign_extracerts_file)) {
2192
                    $sign = @openssl_pkcs7_sign(
2193
                        $file,
2194
                        $signed,
2195
                        'file://' . realpath($this->sign_cert_file),
2196
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2197
                        null
2198
                    );
2199
                } else {
2200
                    $sign = @openssl_pkcs7_sign(
2201
                        $file,
2202
                        $signed,
2203
                        'file://' . realpath($this->sign_cert_file),
2204
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2205
                        null,
2206
                        PKCS7_DETACHED,
2207
                        $this->sign_extracerts_file
2208
                    );
2209
                }
2210
                if ($sign) {
2211
                    @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...
2212
                    $body = file_get_contents($signed);
2213
                    @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...
2214
                    //The message returned by openssl contains both headers and body, so need to split them up
2215
                    $parts = explode("\n\n", $body, 2);
2216
                    $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2217
                    $body = $parts[1];
2218
                } else {
2219
                    @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...
2220
                    @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...
2221
                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
2222
                }
2223
            } catch (phpmailerException $exc) {
2224
                $body = '';
2225
                if ($this->exceptions) {
2226
                    throw $exc;
2227
                }
2228
            }
2229
        }
2230
        return $body;
2231
    }
2232
2233
    /**
2234
     * Return the start of a message boundary.
2235
     * @access protected
2236
     * @param string $boundary
2237
     * @param string $charSet
2238
     * @param string $contentType
2239
     * @param string $encoding
2240
     * @return string
2241
     */
2242
    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2243
    {
2244
        $result = '';
2245
        if ($charSet == '') {
2246
            $charSet = $this->CharSet;
2247
        }
2248
        if ($contentType == '') {
2249
            $contentType = $this->ContentType;
2250
        }
2251
        if ($encoding == '') {
2252
            $encoding = $this->Encoding;
2253
        }
2254
        $result .= $this->textLine('--' . $boundary);
2255
        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2256
        $result .= $this->LE;
2257
        // RFC1341 part 5 says 7bit is assumed if not specified
2258
        if ($encoding != '7bit') {
2259
            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2260
        }
2261
        $result .= $this->LE;
2262
2263
        return $result;
2264
    }
2265
2266
    /**
2267
     * Return the end of a message boundary.
2268
     * @access protected
2269
     * @param string $boundary
2270
     * @return string
2271
     */
2272
    protected function endBoundary($boundary)
2273
    {
2274
        return $this->LE . '--' . $boundary . '--' . $this->LE;
2275
    }
2276
2277
    /**
2278
     * Set the message type.
2279
     * PHPMailer only supports some preset message types,
2280
     * not arbitrary MIME structures.
2281
     * @access protected
2282
     * @return void
2283
     */
2284
    protected function setMessageType()
2285
    {
2286
        $type = array();
2287
        if ($this->alternativeExists()) {
2288
            $type[] = 'alt';
2289
        }
2290
        if ($this->inlineImageExists()) {
2291
            $type[] = 'inline';
2292
        }
2293
        if ($this->attachmentExists()) {
2294
            $type[] = 'attach';
2295
        }
2296
        $this->message_type = implode('_', $type);
2297
        if ($this->message_type == '') {
2298
            $this->message_type = 'plain';
2299
        }
2300
    }
2301
2302
    /**
2303
     * Format a header line.
2304
     * @access public
2305
     * @param string $name
2306
     * @param string $value
2307
     * @return string
2308
     */
2309
    public function headerLine($name, $value)
2310
    {
2311
        return $name . ': ' . $value . $this->LE;
2312
    }
2313
2314
    /**
2315
     * Return a formatted mail line.
2316
     * @access public
2317
     * @param string $value
2318
     * @return string
2319
     */
2320
    public function textLine($value)
2321
    {
2322
        return $value . $this->LE;
2323
    }
2324
2325
    /**
2326
     * Add an attachment from a path on the filesystem.
2327
     * Returns false if the file could not be found or read.
2328
     * @param string $path Path to the attachment.
2329
     * @param string $name Overrides the attachment name.
2330
     * @param string $encoding File encoding (see $Encoding).
2331
     * @param string $type File extension (MIME) type.
2332
     * @param string $disposition Disposition to use
2333
     * @throws phpmailerException
2334
     * @return boolean
2335
     */
2336
    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2337
    {
2338
        try {
2339
            if (!@is_file($path)) {
2340
                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2341
            }
2342
2343
            // If a MIME type is not specified, try to work it out from the file name
2344
            if ($type == '') {
2345
                $type = self::filenameToType($path);
2346
            }
2347
2348
            $filename = basename($path);
2349
            if ($name == '') {
2350
                $name = $filename;
2351
            }
2352
2353
            $this->attachment[] = array(
2354
                0 => $path,
2355
                1 => $filename,
2356
                2 => $name,
2357
                3 => $encoding,
2358
                4 => $type,
2359
                5 => false, // isStringAttachment
2360
                6 => $disposition,
2361
                7 => 0
2362
            );
2363
2364
        } catch (phpmailerException $exc) {
2365
            $this->setError($exc->getMessage());
2366
            $this->edebug($exc->getMessage());
2367
            if ($this->exceptions) {
2368
                throw $exc;
2369
            }
2370
            return false;
2371
        }
2372
        return true;
2373
    }
2374
2375
    /**
2376
     * Return the array of attachments.
2377
     * @return array
2378
     */
2379
    public function getAttachments()
2380
    {
2381
        return $this->attachment;
2382
    }
2383
2384
    /**
2385
     * Attach all file, string, and binary attachments to the message.
2386
     * Returns an empty string on failure.
2387
     * @access protected
2388
     * @param string $disposition_type
2389
     * @param string $boundary
2390
     * @return string
2391
     */
2392
    protected function attachAll($disposition_type, $boundary)
2393
    {
2394
        // Return text of body
2395
        $mime = array();
2396
        $cidUniq = array();
2397
        $incl = array();
2398
2399
        // Add all attachments
2400
        foreach ($this->attachment as $attachment) {
2401
            // Check if it is a valid disposition_filter
2402
            if ($attachment[6] == $disposition_type) {
2403
                // Check for string attachment
2404
                $string = '';
2405
                $path = '';
2406
                $bString = $attachment[5];
2407
                if ($bString) {
2408
                    $string = $attachment[0];
2409
                } else {
2410
                    $path = $attachment[0];
2411
                }
2412
2413
                $inclhash = md5(serialize($attachment));
2414
                if (in_array($inclhash, $incl)) {
2415
                    continue;
2416
                }
2417
                $incl[] = $inclhash;
2418
                $name = $attachment[2];
2419
                $encoding = $attachment[3];
2420
                $type = $attachment[4];
2421
                $disposition = $attachment[6];
2422
                $cid = $attachment[7];
2423
                if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
2424
                    continue;
2425
                }
2426
                $cidUniq[$cid] = true;
2427
2428
                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2429
                //Only include a filename property if we have one
2430
                if (!empty($name)) {
2431
                    $mime[] = sprintf(
2432
                        'Content-Type: %s; name="%s"%s',
2433
                        $type,
2434
                        $this->encodeHeader($this->secureHeader($name)),
2435
                        $this->LE
2436
                    );
2437
                } else {
2438
                    $mime[] = sprintf(
2439
                        'Content-Type: %s%s',
2440
                        $type,
2441
                        $this->LE
2442
                    );
2443
                }
2444
                // RFC1341 part 5 says 7bit is assumed if not specified
2445
                if ($encoding != '7bit') {
2446
                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2447
                }
2448
2449
                if ($disposition == 'inline') {
2450
                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2451
                }
2452
2453
                // If a filename contains any of these chars, it should be quoted,
2454
                // but not otherwise: RFC2183 & RFC2045 5.1
2455
                // Fixes a warning in IETF's msglint MIME checker
2456
                // Allow for bypassing the Content-Disposition header totally
2457
                if (!(empty($disposition))) {
2458
                    $encoded_name = $this->encodeHeader($this->secureHeader($name));
2459
                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2460
                        $mime[] = sprintf(
2461
                            'Content-Disposition: %s; filename="%s"%s',
2462
                            $disposition,
2463
                            $encoded_name,
2464
                            $this->LE . $this->LE
2465
                        );
2466
                    } else {
2467
                        if (!empty($encoded_name)) {
2468
                            $mime[] = sprintf(
2469
                                'Content-Disposition: %s; filename=%s%s',
2470
                                $disposition,
2471
                                $encoded_name,
2472
                                $this->LE . $this->LE
2473
                            );
2474
                        } else {
2475
                            $mime[] = sprintf(
2476
                                'Content-Disposition: %s%s',
2477
                                $disposition,
2478
                                $this->LE . $this->LE
2479
                            );
2480
                        }
2481
                    }
2482
                } else {
2483
                    $mime[] = $this->LE;
2484
                }
2485
2486
                // Encode as string attachment
2487
                if ($bString) {
2488
                    $mime[] = $this->encodeString($string, $encoding);
2489
                    if ($this->isError()) {
2490
                        return '';
2491
                    }
2492
                    $mime[] = $this->LE . $this->LE;
2493
                } else {
2494
                    $mime[] = $this->encodeFile($path, $encoding);
2495
                    if ($this->isError()) {
2496
                        return '';
2497
                    }
2498
                    $mime[] = $this->LE . $this->LE;
2499
                }
2500
            }
2501
        }
2502
2503
        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2504
2505
        return implode('', $mime);
2506
    }
2507
2508
    /**
2509
     * Encode a file attachment in requested format.
2510
     * Returns an empty string on failure.
2511
     * @param string $path The full path to the file
2512
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2513
     * @throws phpmailerException
2514
     * @access protected
2515
     * @return string
2516
     */
2517
    protected function encodeFile($path, $encoding = 'base64')
2518
    {
2519
        try {
2520
            if (!is_readable($path)) {
2521
                throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2522
            }
2523
            $magic_quotes = get_magic_quotes_runtime();
2524
            if ($magic_quotes) {
2525
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2526
                    set_magic_quotes_runtime(false);
2527
                } else {
2528
                    //Doesn't exist in PHP 5.4, but we don't need to check because
2529
                    //get_magic_quotes_runtime always returns false in 5.4+
2530
                    //so it will never get here
2531
                    ini_set('magic_quotes_runtime', false);
2532
                }
2533
            }
2534
            $file_buffer = file_get_contents($path);
2535
            $file_buffer = $this->encodeString($file_buffer, $encoding);
2536
            if ($magic_quotes) {
2537
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2538
                    set_magic_quotes_runtime($magic_quotes);
2539
                } else {
2540
                    ini_set('magic_quotes_runtime', $magic_quotes);
2541
                }
2542
            }
2543
            return $file_buffer;
2544
        } catch (Exception $exc) {
2545
            $this->setError($exc->getMessage());
2546
            return '';
2547
        }
2548
    }
2549
2550
    /**
2551
     * Encode a string in requested format.
2552
     * Returns an empty string on failure.
2553
     * @param string $str The text to encode
2554
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2555
     * @access public
2556
     * @return string
2557
     */
2558
    public function encodeString($str, $encoding = 'base64')
2559
    {
2560
        $encoded = '';
2561
        switch (strtolower($encoding)) {
2562
            case 'base64':
2563
                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2564
                break;
2565
            case '7bit':
2566
            case '8bit':
2567
                $encoded = $this->fixEOL($str);
2568
                // Make sure it ends with a line break
2569
                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2570
                    $encoded .= $this->LE;
2571
                }
2572
                break;
2573
            case 'binary':
2574
                $encoded = $str;
2575
                break;
2576
            case 'quoted-printable':
2577
                $encoded = $this->encodeQP($str);
2578
                break;
2579
            default:
2580
                $this->setError($this->lang('encoding') . $encoding);
2581
                break;
2582
        }
2583
        return $encoded;
2584
    }
2585
2586
    /**
2587
     * Encode a header string optimally.
2588
     * Picks shortest of Q, B, quoted-printable or none.
2589
     * @access public
2590
     * @param string $str
2591
     * @param string $position
2592
     * @return string
2593
     */
2594
    public function encodeHeader($str, $position = 'text')
2595
    {
2596
        $matchcount = 0;
2597
        switch (strtolower($position)) {
2598
            case 'phrase':
2599
                if (!preg_match('/[\200-\377]/', $str)) {
2600
                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
2601
                    $encoded = addcslashes($str, "\0..\37\177\\\"");
2602
                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2603
                        return ($encoded);
2604
                    } else {
2605
                        return ("\"$encoded\"");
2606
                    }
2607
                }
2608
                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2609
                break;
2610
            /** @noinspection PhpMissingBreakStatementInspection */
2611
            case 'comment':
2612
                $matchcount = preg_match_all('/[()"]/', $str, $matches);
2613
                // Intentional fall-through
2614
            case 'text':
2615
            default:
2616
                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2617
                break;
2618
        }
2619
2620
        //There are no chars that need encoding
2621
        if ($matchcount == 0) {
2622
            return ($str);
2623
        }
2624
2625
        $maxlen = 75 - 7 - strlen($this->CharSet);
2626
        // Try to select the encoding which should produce the shortest output
2627
        if ($matchcount > strlen($str) / 3) {
2628
            // More than a third of the content will need encoding, so B encoding will be most efficient
2629
            $encoding = 'B';
2630
            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2631
                // Use a custom function which correctly encodes and wraps long
2632
                // multibyte strings without breaking lines within a character
2633
                $encoded = $this->base64EncodeWrapMB($str, "\n");
2634
            } else {
2635
                $encoded = base64_encode($str);
2636
                $maxlen -= $maxlen % 4;
2637
                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2638
            }
2639
        } else {
2640
            $encoding = 'Q';
2641
            $encoded = $this->encodeQ($str, $position);
2642
            $encoded = $this->wrapText($encoded, $maxlen, true);
2643
            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2644
        }
2645
2646
        $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2647
        $encoded = trim(str_replace("\n", $this->LE, $encoded));
2648
2649
        return $encoded;
2650
    }
2651
2652
    /**
2653
     * Check if a string contains multi-byte characters.
2654
     * @access public
2655
     * @param string $str multi-byte text to wrap encode
2656
     * @return boolean
2657
     */
2658
    public function hasMultiBytes($str)
2659
    {
2660
        if (function_exists('mb_strlen')) {
2661
            return (strlen($str) > mb_strlen($str, $this->CharSet));
2662
        } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2663
            return false;
2664
        }
2665
    }
2666
2667
    /**
2668
     * Does a string contain any 8-bit chars (in any charset)?
2669
     * @param string $text
2670
     * @return boolean
2671
     */
2672
    public function has8bitChars($text)
2673
    {
2674
        return (boolean)preg_match('/[\x80-\xFF]/', $text);
2675
    }
2676
2677
    /**
2678
     * Encode and wrap long multibyte strings for mail headers
2679
     * without breaking lines within a character.
2680
     * Adapted from a function by paravoid
2681
     * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2682
     * @access public
2683
     * @param string $str multi-byte text to wrap encode
2684
     * @param string $linebreak string to use as linefeed/end-of-line
2685
     * @return string
2686
     */
2687
    public function base64EncodeWrapMB($str, $linebreak = null)
2688
    {
2689
        $start = '=?' . $this->CharSet . '?B?';
2690
        $end = '?=';
2691
        $encoded = '';
2692
        if ($linebreak === null) {
2693
            $linebreak = $this->LE;
2694
        }
2695
2696
        $mb_length = mb_strlen($str, $this->CharSet);
2697
        // Each line must have length <= 75, including $start and $end
2698
        $length = 75 - strlen($start) - strlen($end);
2699
        // Average multi-byte ratio
2700
        $ratio = $mb_length / strlen($str);
2701
        // Base64 has a 4:3 ratio
2702
        $avgLength = floor($length * $ratio * .75);
2703
2704
        for ($i = 0; $i < $mb_length; $i += $offset) {
2705
            $lookBack = 0;
2706
            do {
2707
                $offset = $avgLength - $lookBack;
2708
                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2709
                $chunk = base64_encode($chunk);
2710
                $lookBack++;
2711
            } while (strlen($chunk) > $length);
2712
            $encoded .= $chunk . $linebreak;
2713
        }
2714
2715
        // Chomp the last linefeed
2716
        $encoded = substr($encoded, 0, -strlen($linebreak));
2717
        return $encoded;
2718
    }
2719
2720
    /**
2721
     * Encode a string in quoted-printable format.
2722
     * According to RFC2045 section 6.7.
2723
     * @access public
2724
     * @param string $string The text to encode
2725
     * @param integer $line_max Number of chars allowed on a line before wrapping
2726
     * @return string
2727
     * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2728
     */
2729
    public function encodeQP($string, $line_max = 76)
2730
    {
2731
        // Use native function if it's available (>= PHP5.3)
2732
        if (function_exists('quoted_printable_encode')) {
2733
            return quoted_printable_encode($string);
2734
        }
2735
        // Fall back to a pure PHP implementation
2736
        $string = str_replace(
2737
            array('%20', '%0D%0A.', '%0D%0A', '%'),
2738
            array(' ', "\r\n=2E", "\r\n", '='),
2739
            rawurlencode($string)
2740
        );
2741
        return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2742
    }
2743
2744
    /**
2745
     * Backward compatibility wrapper for an old QP encoding function that was removed.
2746
     * @see PHPMailer::encodeQP()
2747
     * @access public
2748
     * @param string $string
2749
     * @param integer $line_max
2750
     * @param boolean $space_conv
2751
     * @return string
2752
     * @deprecated Use encodeQP instead.
2753
     */
2754
    public function encodeQPphp(
2755
        $string,
2756
        $line_max = 76,
2757
        /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
0 ignored issues
show
Unused Code introduced by
The parameter $space_conv is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2758
    ) {
2759
        return $this->encodeQP($string, $line_max);
2760
    }
2761
2762
    /**
2763
     * Encode a string using Q encoding.
2764
     * @link http://tools.ietf.org/html/rfc2047
2765
     * @param string $str the text to encode
2766
     * @param string $position Where the text is going to be used, see the RFC for what that means
2767
     * @access public
2768
     * @return string
2769
     */
2770
    public function encodeQ($str, $position = 'text')
2771
    {
2772
        // There should not be any EOL in the string
2773
        $pattern = '';
2774
        $encoded = str_replace(array("\r", "\n"), '', $str);
2775
        switch (strtolower($position)) {
2776
            case 'phrase':
2777
                // RFC 2047 section 5.3
2778
                $pattern = '^A-Za-z0-9!*+\/ -';
2779
                break;
2780
            /** @noinspection PhpMissingBreakStatementInspection */
2781
            case 'comment':
2782
                // RFC 2047 section 5.2
2783
                $pattern = '\(\)"';
2784
                // intentional fall-through
2785
                // for this reason we build the $pattern without including delimiters and []
2786
            case 'text':
2787
            default:
2788
                // RFC 2047 section 5.1
2789
                // Replace every high ascii, control, =, ? and _ characters
2790
                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2791
                break;
2792
        }
2793
        $matches = array();
2794
        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2795
            // If the string contains an '=', make sure it's the first thing we replace
2796
            // so as to avoid double-encoding
2797
            $eqkey = array_search('=', $matches[0]);
2798
            if (false !== $eqkey) {
2799
                unset($matches[0][$eqkey]);
2800
                array_unshift($matches[0], '=');
2801
            }
2802
            foreach (array_unique($matches[0]) as $char) {
2803
                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2804
            }
2805
        }
2806
        // Replace every spaces to _ (more readable than =20)
2807
        return str_replace(' ', '_', $encoded);
2808
    }
2809
2810
    /**
2811
     * Add a string or binary attachment (non-filesystem).
2812
     * This method can be used to attach ascii or binary data,
2813
     * such as a BLOB record from a database.
2814
     * @param string $string String attachment data.
2815
     * @param string $filename Name of the attachment.
2816
     * @param string $encoding File encoding (see $Encoding).
2817
     * @param string $type File extension (MIME) type.
2818
     * @param string $disposition Disposition to use
2819
     * @return void
2820
     */
2821
    public function addStringAttachment(
2822
        $string,
2823
        $filename,
2824
        $encoding = 'base64',
2825
        $type = '',
2826
        $disposition = 'attachment'
2827
    ) {
2828
        // If a MIME type is not specified, try to work it out from the file name
2829
        if ($type == '') {
2830
            $type = self::filenameToType($filename);
2831
        }
2832
        // Append to $attachment array
2833
        $this->attachment[] = array(
2834
            0 => $string,
2835
            1 => $filename,
2836
            2 => basename($filename),
2837
            3 => $encoding,
2838
            4 => $type,
2839
            5 => true, // isStringAttachment
2840
            6 => $disposition,
2841
            7 => 0
2842
        );
2843
    }
2844
2845
    /**
2846
     * Add an embedded (inline) attachment from a file.
2847
     * This can include images, sounds, and just about any other document type.
2848
     * These differ from 'regular' attachments in that they are intended to be
2849
     * displayed inline with the message, not just attached for download.
2850
     * This is used in HTML messages that embed the images
2851
     * the HTML refers to using the $cid value.
2852
     * @param string $path Path to the attachment.
2853
     * @param string $cid Content ID of the attachment; Use this to reference
2854
     *        the content when using an embedded image in HTML.
2855
     * @param string $name Overrides the attachment name.
2856
     * @param string $encoding File encoding (see $Encoding).
2857
     * @param string $type File MIME type.
2858
     * @param string $disposition Disposition to use
2859
     * @return boolean True on successfully adding an attachment
2860
     */
2861
    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2862
    {
2863
        if (!@is_file($path)) {
2864
            $this->setError($this->lang('file_access') . $path);
2865
            return false;
2866
        }
2867
2868
        // If a MIME type is not specified, try to work it out from the file name
2869
        if ($type == '') {
2870
            $type = self::filenameToType($path);
2871
        }
2872
2873
        $filename = basename($path);
2874
        if ($name == '') {
2875
            $name = $filename;
2876
        }
2877
2878
        // Append to $attachment array
2879
        $this->attachment[] = array(
2880
            0 => $path,
2881
            1 => $filename,
2882
            2 => $name,
2883
            3 => $encoding,
2884
            4 => $type,
2885
            5 => false, // isStringAttachment
2886
            6 => $disposition,
2887
            7 => $cid
2888
        );
2889
        return true;
2890
    }
2891
2892
    /**
2893
     * Add an embedded stringified attachment.
2894
     * This can include images, sounds, and just about any other document type.
2895
     * Be sure to set the $type to an image type for images:
2896
     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2897
     * @param string $string The attachment binary data.
2898
     * @param string $cid Content ID of the attachment; Use this to reference
2899
     *        the content when using an embedded image in HTML.
2900
     * @param string $name
2901
     * @param string $encoding File encoding (see $Encoding).
2902
     * @param string $type MIME type.
2903
     * @param string $disposition Disposition to use
2904
     * @return boolean True on successfully adding an attachment
2905
     */
2906
    public function addStringEmbeddedImage(
2907
        $string,
2908
        $cid,
2909
        $name = '',
2910
        $encoding = 'base64',
2911
        $type = '',
2912
        $disposition = 'inline'
2913
    ) {
2914
        // If a MIME type is not specified, try to work it out from the name
2915
        if ($type == '' and !empty($name)) {
2916
            $type = self::filenameToType($name);
2917
        }
2918
2919
        // Append to $attachment array
2920
        $this->attachment[] = array(
2921
            0 => $string,
2922
            1 => $name,
2923
            2 => $name,
2924
            3 => $encoding,
2925
            4 => $type,
2926
            5 => true, // isStringAttachment
2927
            6 => $disposition,
2928
            7 => $cid
2929
        );
2930
        return true;
2931
    }
2932
2933
    /**
2934
     * Check if an inline attachment is present.
2935
     * @access public
2936
     * @return boolean
2937
     */
2938
    public function inlineImageExists()
2939
    {
2940
        foreach ($this->attachment as $attachment) {
2941
            if ($attachment[6] == 'inline') {
2942
                return true;
2943
            }
2944
        }
2945
        return false;
2946
    }
2947
2948
    /**
2949
     * Check if an attachment (non-inline) is present.
2950
     * @return boolean
2951
     */
2952
    public function attachmentExists()
2953
    {
2954
        foreach ($this->attachment as $attachment) {
2955
            if ($attachment[6] == 'attachment') {
2956
                return true;
2957
            }
2958
        }
2959
        return false;
2960
    }
2961
2962
    /**
2963
     * Check if this message has an alternative body set.
2964
     * @return boolean
2965
     */
2966
    public function alternativeExists()
2967
    {
2968
        return !empty($this->AltBody);
2969
    }
2970
2971
    /**
2972
     * Clear queued addresses of given kind.
2973
     * @access protected
2974
     * @param string $kind 'to', 'cc', or 'bcc'
2975
     * @return void
2976
     */
2977
    public function clearQueuedAddresses($kind)
2978
    {
2979
        $RecipientsQueue = $this->RecipientsQueue;
2980
        foreach ($RecipientsQueue as $address => $params) {
2981
            if ($params[0] == $kind) {
2982
                unset($this->RecipientsQueue[$address]);
2983
            }
2984
        }
2985
    }
2986
2987
    /**
2988
     * Clear all To recipients.
2989
     * @return void
2990
     */
2991
    public function clearAddresses()
2992
    {
2993
        foreach ($this->to as $to) {
2994
            unset($this->all_recipients[strtolower($to[0])]);
2995
        }
2996
        $this->to = array();
2997
        $this->clearQueuedAddresses('to');
2998
    }
2999
3000
    /**
3001
     * Clear all CC recipients.
3002
     * @return void
3003
     */
3004 View Code Duplication
    public function clearCCs()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3005
    {
3006
        foreach ($this->cc as $cc) {
3007
            unset($this->all_recipients[strtolower($cc[0])]);
3008
        }
3009
        $this->cc = array();
3010
        $this->clearQueuedAddresses('cc');
3011
    }
3012
3013
    /**
3014
     * Clear all BCC recipients.
3015
     * @return void
3016
     */
3017 View Code Duplication
    public function clearBCCs()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3018
    {
3019
        foreach ($this->bcc as $bcc) {
3020
            unset($this->all_recipients[strtolower($bcc[0])]);
3021
        }
3022
        $this->bcc = array();
3023
        $this->clearQueuedAddresses('bcc');
3024
    }
3025
3026
    /**
3027
     * Clear all ReplyTo recipients.
3028
     * @return void
3029
     */
3030
    public function clearReplyTos()
3031
    {
3032
        $this->ReplyTo = array();
3033
        $this->ReplyToQueue = array();
3034
    }
3035
3036
    /**
3037
     * Clear all recipient types.
3038
     * @return void
3039
     */
3040
    public function clearAllRecipients()
3041
    {
3042
        $this->to = array();
3043
        $this->cc = array();
3044
        $this->bcc = array();
3045
        $this->all_recipients = array();
3046
        $this->RecipientsQueue = array();
3047
    }
3048
3049
    /**
3050
     * Clear all filesystem, string, and binary attachments.
3051
     * @return void
3052
     */
3053
    public function clearAttachments()
3054
    {
3055
        $this->attachment = array();
3056
    }
3057
3058
    /**
3059
     * Clear all custom headers.
3060
     * @return void
3061
     */
3062
    public function clearCustomHeaders()
3063
    {
3064
        $this->CustomHeader = array();
3065
    }
3066
3067
    /**
3068
     * Add an error message to the error container.
3069
     * @access protected
3070
     * @param string $msg
3071
     * @return void
3072
     */
3073
    protected function setError($msg)
3074
    {
3075
        $this->error_count++;
3076
        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
3077
            $lasterror = $this->smtp->getError();
3078
            if (!empty($lasterror['error'])) {
3079
                $msg .= $this->lang('smtp_error') . $lasterror['error'];
3080
                if (!empty($lasterror['detail'])) {
3081
                    $msg .= ' Detail: '. $lasterror['detail'];
3082
                }
3083
                if (!empty($lasterror['smtp_code'])) {
3084
                    $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3085
                }
3086
                if (!empty($lasterror['smtp_code_ex'])) {
3087
                    $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3088
                }
3089
            }
3090
        }
3091
        $this->ErrorInfo = $msg;
3092
    }
3093
3094
    /**
3095
     * Return an RFC 822 formatted date.
3096
     * @access public
3097
     * @return string
3098
     * @static
3099
     */
3100
    public static function rfcDate()
3101
    {
3102
        // Set the time zone to whatever the default is to avoid 500 errors
3103
        // Will default to UTC if it's not set properly in php.ini
3104
        date_default_timezone_set(@date_default_timezone_get());
3105
        return date('D, j M Y H:i:s O');
3106
    }
3107
3108
    /**
3109
     * Get the server hostname.
3110
     * Returns 'localhost.localdomain' if unknown.
3111
     * @access protected
3112
     * @return string
3113
     */
3114
    protected function serverHostname()
3115
    {
3116
        $result = 'localhost.localdomain';
3117
        if (!empty($this->Hostname)) {
3118
            $result = $this->Hostname;
3119
        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
3120
            $result = $_SERVER['SERVER_NAME'];
3121
        } elseif (function_exists('gethostname') && gethostname() !== false) {
3122
            $result = gethostname();
3123
        } elseif (php_uname('n') !== false) {
3124
            $result = php_uname('n');
3125
        }
3126
        return $result;
3127
    }
3128
3129
    /**
3130
     * Get an error message in the current language.
3131
     * @access protected
3132
     * @param string $key
3133
     * @return string
3134
     */
3135
    protected function lang($key)
3136
    {
3137
        if (count($this->language) < 1) {
3138
            $this->setLanguage('en'); // set the default language
3139
        }
3140
3141
        if (array_key_exists($key, $this->language)) {
3142
            if ($key == 'smtp_connect_failed') {
3143
                //Include a link to troubleshooting docs on SMTP connection failure
3144
                //this is by far the biggest cause of support questions
3145
                //but it's usually not PHPMailer's fault.
3146
                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3147
            }
3148
            return $this->language[$key];
3149
        } else {
3150
            //Return the key as a fallback
3151
            return $key;
3152
        }
3153
    }
3154
3155
    /**
3156
     * Check if an error occurred.
3157
     * @access public
3158
     * @return boolean True if an error did occur.
3159
     */
3160
    public function isError()
3161
    {
3162
        return ($this->error_count > 0);
3163
    }
3164
3165
    /**
3166
     * Ensure consistent line endings in a string.
3167
     * Changes every end of line from CRLF, CR or LF to $this->LE.
3168
     * @access public
3169
     * @param string $str String to fixEOL
3170
     * @return string
3171
     */
3172
    public function fixEOL($str)
3173
    {
3174
        // Normalise to \n
3175
        $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
3176
        // Now convert LE as needed
3177
        if ($this->LE !== "\n") {
3178
            $nstr = str_replace("\n", $this->LE, $nstr);
3179
        }
3180
        return $nstr;
3181
    }
3182
3183
    /**
3184
     * Add a custom header.
3185
     * $name value can be overloaded to contain
3186
     * both header name and value (name:value)
3187
     * @access public
3188
     * @param string $name Custom header name
3189
     * @param string $value Header value
3190
     * @return void
3191
     */
3192
    public function addCustomHeader($name, $value = null)
3193
    {
3194
        if ($value === null) {
3195
            // Value passed in as name:value
3196
            $this->CustomHeader[] = explode(':', $name, 2);
3197
        } else {
3198
            $this->CustomHeader[] = array($name, $value);
3199
        }
3200
    }
3201
3202
    /**
3203
     * Returns all custom headers.
3204
     * @return array
3205
     */
3206
    public function getCustomHeaders()
3207
    {
3208
        return $this->CustomHeader;
3209
    }
3210
3211
    /**
3212
     * Create a message from an HTML string.
3213
     * Automatically makes modifications for inline images and backgrounds
3214
     * and creates a plain-text version by converting the HTML.
3215
     * Overwrites any existing values in $this->Body and $this->AltBody
3216
     * @access public
3217
     * @param string $message HTML message string
3218
     * @param string $basedir baseline directory for path
3219
     * @param boolean|callable $advanced Whether to use the internal HTML to text converter
3220
     *    or your own custom converter @see PHPMailer::html2text()
3221
     * @return string $message
3222
     */
3223
    public function msgHTML($message, $basedir = '', $advanced = false)
3224
    {
3225
        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3226
        if (array_key_exists(2, $images)) {
3227
            foreach ($images[2] as $imgindex => $url) {
3228
                // Convert data URIs into embedded images
3229
                if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3230
                    $data = substr($url, strpos($url, ','));
3231
                    if ($match[2]) {
3232
                        $data = base64_decode($data);
3233
                    } else {
3234
                        $data = rawurldecode($data);
3235
                    }
3236
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3237
                    if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
3238
                        $message = str_replace(
3239
                            $images[0][$imgindex],
3240
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3241
                            $message
3242
                        );
3243
                    }
3244
                } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) {
3245
                    // Do not change urls for absolute images (thanks to corvuscorax)
3246
                    // Do not change urls that are already inline images
3247
                    $filename = basename($url);
3248
                    $directory = dirname($url);
3249
                    if ($directory == '.') {
3250
                        $directory = '';
3251
                    }
3252
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3253
                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3254
                        $basedir .= '/';
3255
                    }
3256
                    if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3257
                        $directory .= '/';
3258
                    }
3259
                    if ($this->addEmbeddedImage(
3260
                        $basedir . $directory . $filename,
3261
                        $cid,
3262
                        $filename,
3263
                        'base64',
3264
                        self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
3265
                    )
3266
                    ) {
3267
                        $message = preg_replace(
3268
                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3269
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3270
                            $message
3271
                        );
3272
                    }
3273
                }
3274
            }
3275
        }
3276
        $this->isHTML(true);
3277
        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3278
        $this->Body = $this->normalizeBreaks($message);
3279
        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3280
        if (empty($this->AltBody)) {
3281
            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
3282
                self::CRLF . self::CRLF;
3283
        }
3284
        return $this->Body;
3285
    }
3286
3287
    /**
3288
     * Convert an HTML string into plain text.
3289
     * This is used by msgHTML().
3290
     * Note - older versions of this function used a bundled advanced converter
3291
     * which was been removed for license reasons in #232
3292
     * Example usage:
3293
     * <code>
3294
     * // Use default conversion
3295
     * $plain = $mail->html2text($html);
3296
     * // Use your own custom converter
3297
     * $plain = $mail->html2text($html, function($html) {
3298
     *     $converter = new MyHtml2text($html);
3299
     *     return $converter->get_text();
3300
     * });
3301
     * </code>
3302
     * @param string $html The HTML text to convert
3303
     * @param boolean|callable $advanced Any boolean value to use the internal converter,
3304
     *   or provide your own callable for custom conversion.
3305
     * @return string
3306
     */
3307
    public function html2text($html, $advanced = false)
3308
    {
3309
        if (is_callable($advanced)) {
3310
            return call_user_func($advanced, $html);
3311
        }
3312
        return html_entity_decode(
3313
            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3314
            ENT_QUOTES,
3315
            $this->CharSet
3316
        );
3317
    }
3318
3319
    /**
3320
     * Get the MIME type for a file extension.
3321
     * @param string $ext File extension
3322
     * @access public
3323
     * @return string MIME type of file.
3324
     * @static
3325
     */
3326
    public static function _mime_types($ext = '')
3327
    {
3328
        $mimes = array(
3329
            'xl'    => 'application/excel',
3330
            'js'    => 'application/javascript',
3331
            'hqx'   => 'application/mac-binhex40',
3332
            'cpt'   => 'application/mac-compactpro',
3333
            'bin'   => 'application/macbinary',
3334
            'doc'   => 'application/msword',
3335
            'word'  => 'application/msword',
3336
            'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3337
            'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3338
            'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3339
            'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3340
            'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3341
            'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3342
            'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3343
            'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3344
            'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
3345
            'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3346
            'class' => 'application/octet-stream',
3347
            'dll'   => 'application/octet-stream',
3348
            'dms'   => 'application/octet-stream',
3349
            'exe'   => 'application/octet-stream',
3350
            'lha'   => 'application/octet-stream',
3351
            'lzh'   => 'application/octet-stream',
3352
            'psd'   => 'application/octet-stream',
3353
            'sea'   => 'application/octet-stream',
3354
            'so'    => 'application/octet-stream',
3355
            'oda'   => 'application/oda',
3356
            'pdf'   => 'application/pdf',
3357
            'ai'    => 'application/postscript',
3358
            'eps'   => 'application/postscript',
3359
            'ps'    => 'application/postscript',
3360
            'smi'   => 'application/smil',
3361
            'smil'  => 'application/smil',
3362
            'mif'   => 'application/vnd.mif',
3363
            'xls'   => 'application/vnd.ms-excel',
3364
            'ppt'   => 'application/vnd.ms-powerpoint',
3365
            'wbxml' => 'application/vnd.wap.wbxml',
3366
            'wmlc'  => 'application/vnd.wap.wmlc',
3367
            'dcr'   => 'application/x-director',
3368
            'dir'   => 'application/x-director',
3369
            'dxr'   => 'application/x-director',
3370
            'dvi'   => 'application/x-dvi',
3371
            'gtar'  => 'application/x-gtar',
3372
            'php3'  => 'application/x-httpd-php',
3373
            'php4'  => 'application/x-httpd-php',
3374
            'php'   => 'application/x-httpd-php',
3375
            'phtml' => 'application/x-httpd-php',
3376
            'phps'  => 'application/x-httpd-php-source',
3377
            'swf'   => 'application/x-shockwave-flash',
3378
            'sit'   => 'application/x-stuffit',
3379
            'tar'   => 'application/x-tar',
3380
            'tgz'   => 'application/x-tar',
3381
            'xht'   => 'application/xhtml+xml',
3382
            'xhtml' => 'application/xhtml+xml',
3383
            'zip'   => 'application/zip',
3384
            'mid'   => 'audio/midi',
3385
            'midi'  => 'audio/midi',
3386
            'mp2'   => 'audio/mpeg',
3387
            'mp3'   => 'audio/mpeg',
3388
            'mpga'  => 'audio/mpeg',
3389
            'aif'   => 'audio/x-aiff',
3390
            'aifc'  => 'audio/x-aiff',
3391
            'aiff'  => 'audio/x-aiff',
3392
            'ram'   => 'audio/x-pn-realaudio',
3393
            'rm'    => 'audio/x-pn-realaudio',
3394
            'rpm'   => 'audio/x-pn-realaudio-plugin',
3395
            'ra'    => 'audio/x-realaudio',
3396
            'wav'   => 'audio/x-wav',
3397
            'bmp'   => 'image/bmp',
3398
            'gif'   => 'image/gif',
3399
            'jpeg'  => 'image/jpeg',
3400
            'jpe'   => 'image/jpeg',
3401
            'jpg'   => 'image/jpeg',
3402
            'png'   => 'image/png',
3403
            'tiff'  => 'image/tiff',
3404
            'tif'   => 'image/tiff',
3405
            'eml'   => 'message/rfc822',
3406
            'css'   => 'text/css',
3407
            'html'  => 'text/html',
3408
            'htm'   => 'text/html',
3409
            'shtml' => 'text/html',
3410
            'log'   => 'text/plain',
3411
            'text'  => 'text/plain',
3412
            'txt'   => 'text/plain',
3413
            'rtx'   => 'text/richtext',
3414
            'rtf'   => 'text/rtf',
3415
            'vcf'   => 'text/vcard',
3416
            'vcard' => 'text/vcard',
3417
            'xml'   => 'text/xml',
3418
            'xsl'   => 'text/xml',
3419
            'mpeg'  => 'video/mpeg',
3420
            'mpe'   => 'video/mpeg',
3421
            'mpg'   => 'video/mpeg',
3422
            'mov'   => 'video/quicktime',
3423
            'qt'    => 'video/quicktime',
3424
            'rv'    => 'video/vnd.rn-realvideo',
3425
            'avi'   => 'video/x-msvideo',
3426
            'movie' => 'video/x-sgi-movie'
3427
        );
3428
        if (array_key_exists(strtolower($ext), $mimes)) {
3429
            return $mimes[strtolower($ext)];
3430
        }
3431
        return 'application/octet-stream';
3432
    }
3433
3434
    /**
3435
     * Map a file name to a MIME type.
3436
     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3437
     * @param string $filename A file name or full path, does not need to exist as a file
3438
     * @return string
3439
     * @static
3440
     */
3441
    public static function filenameToType($filename)
3442
    {
3443
        // In case the path is a URL, strip any query string before getting extension
3444
        $qpos = strpos($filename, '?');
3445
        if (false !== $qpos) {
3446
            $filename = substr($filename, 0, $qpos);
3447
        }
3448
        $pathinfo = self::mb_pathinfo($filename);
3449
        return self::_mime_types($pathinfo['extension']);
3450
    }
3451
3452
    /**
3453
     * Multi-byte-safe pathinfo replacement.
3454
     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3455
     * Works similarly to the one in PHP >= 5.2.0
3456
     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3457
     * @param string $path A filename or path, does not need to exist as a file
3458
     * @param integer|string $options Either a PATHINFO_* constant,
3459
     *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3460
     * @return string|array
3461
     * @static
3462
     */
3463
    public static function mb_pathinfo($path, $options = null)
3464
    {
3465
        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3466
        $pathinfo = array();
3467
        if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3468
            if (array_key_exists(1, $pathinfo)) {
3469
                $ret['dirname'] = $pathinfo[1];
3470
            }
3471
            if (array_key_exists(2, $pathinfo)) {
3472
                $ret['basename'] = $pathinfo[2];
3473
            }
3474
            if (array_key_exists(5, $pathinfo)) {
3475
                $ret['extension'] = $pathinfo[5];
3476
            }
3477
            if (array_key_exists(3, $pathinfo)) {
3478
                $ret['filename'] = $pathinfo[3];
3479
            }
3480
        }
3481
        switch ($options) {
3482
            case PATHINFO_DIRNAME:
3483
            case 'dirname':
3484
                return $ret['dirname'];
3485
            case PATHINFO_BASENAME:
3486
            case 'basename':
3487
                return $ret['basename'];
3488
            case PATHINFO_EXTENSION:
3489
            case 'extension':
3490
                return $ret['extension'];
3491
            case PATHINFO_FILENAME:
3492
            case 'filename':
3493
                return $ret['filename'];
3494
            default:
3495
                return $ret;
3496
        }
3497
    }
3498
3499
    /**
3500
     * Set or reset instance properties.
3501
     * You should avoid this function - it's more verbose, less efficient, more error-prone and
3502
     * harder to debug than setting properties directly.
3503
     * Usage Example:
3504
     * `$mail->set('SMTPSecure', 'tls');`
3505
     *   is the same as:
3506
     * `$mail->SMTPSecure = 'tls';`
3507
     * @access public
3508
     * @param string $name The property name to set
3509
     * @param mixed $value The value to set the property to
3510
     * @return boolean
3511
     * @TODO Should this not be using the __set() magic function?
3512
     */
3513
    public function set($name, $value = '')
3514
    {
3515
        if (property_exists($this, $name)) {
3516
            $this->$name = $value;
3517
            return true;
3518
        } else {
3519
            $this->setError($this->lang('variable_set') . $name);
3520
            return false;
3521
        }
3522
    }
3523
3524
    /**
3525
     * Strip newlines to prevent header injection.
3526
     * @access public
3527
     * @param string $str
3528
     * @return string
3529
     */
3530
    public function secureHeader($str)
3531
    {
3532
        return trim(str_replace(array("\r", "\n"), '', $str));
3533
    }
3534
3535
    /**
3536
     * Normalize line breaks in a string.
3537
     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3538
     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3539
     * @param string $text
3540
     * @param string $breaktype What kind of line break to use, defaults to CRLF
3541
     * @return string
3542
     * @access public
3543
     * @static
3544
     */
3545
    public static function normalizeBreaks($text, $breaktype = "\r\n")
3546
    {
3547
        return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3548
    }
3549
3550
    /**
3551
     * Set the public and private key files and password for S/MIME signing.
3552
     * @access public
3553
     * @param string $cert_filename
3554
     * @param string $key_filename
3555
     * @param string $key_pass Password for private key
3556
     * @param string $extracerts_filename Optional path to chain certificate
3557
     */
3558
    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3559
    {
3560
        $this->sign_cert_file = $cert_filename;
3561
        $this->sign_key_file = $key_filename;
3562
        $this->sign_key_pass = $key_pass;
3563
        $this->sign_extracerts_file = $extracerts_filename;
3564
    }
3565
3566
    /**
3567
     * Quoted-Printable-encode a DKIM header.
3568
     * @access public
3569
     * @param string $txt
3570
     * @return string
3571
     */
3572
    public function DKIM_QP($txt)
3573
    {
3574
        $line = '';
3575
        for ($i = 0; $i < strlen($txt); $i++) {
3576
            $ord = ord($txt[$i]);
3577
            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3578
                $line .= $txt[$i];
3579
            } else {
3580
                $line .= '=' . sprintf('%02X', $ord);
3581
            }
3582
        }
3583
        return $line;
3584
    }
3585
3586
    /**
3587
     * Generate a DKIM signature.
3588
     * @access public
3589
     * @param string $signHeader
3590
     * @throws phpmailerException
3591
     * @return string
3592
     */
3593
    public function DKIM_Sign($signHeader)
3594
    {
3595
        if (!defined('PKCS7_TEXT')) {
3596
            if ($this->exceptions) {
3597
                throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3598
            }
3599
            return '';
3600
        }
3601
        $privKeyStr = file_get_contents($this->DKIM_private);
3602
        if ($this->DKIM_passphrase != '') {
3603
            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3604
        } else {
3605
            $privKey = $privKeyStr;
3606
        }
3607
        if (openssl_sign($signHeader, $signature, $privKey)) {
3608
            return base64_encode($signature);
3609
        }
3610
        return '';
3611
    }
3612
3613
    /**
3614
     * Generate a DKIM canonicalization header.
3615
     * @access public
3616
     * @param string $signHeader Header
3617
     * @return string
3618
     */
3619
    public function DKIM_HeaderC($signHeader)
3620
    {
3621
        $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3622
        $lines = explode("\r\n", $signHeader);
3623
        foreach ($lines as $key => $line) {
3624
            list($heading, $value) = explode(':', $line, 2);
3625
            $heading = strtolower($heading);
3626
            $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
3627
            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3628
        }
3629
        $signHeader = implode("\r\n", $lines);
3630
        return $signHeader;
3631
    }
3632
3633
    /**
3634
     * Generate a DKIM canonicalization body.
3635
     * @access public
3636
     * @param string $body Message Body
3637
     * @return string
3638
     */
3639
    public function DKIM_BodyC($body)
3640
    {
3641
        if ($body == '') {
3642
            return "\r\n";
3643
        }
3644
        // stabilize line endings
3645
        $body = str_replace("\r\n", "\n", $body);
3646
        $body = str_replace("\n", "\r\n", $body);
3647
        // END stabilize line endings
3648 View Code Duplication
        while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3649
            $body = substr($body, 0, strlen($body) - 2);
3650
        }
3651
        return $body;
3652
    }
3653
3654
    /**
3655
     * Create the DKIM header and body in a new message header.
3656
     * @access public
3657
     * @param string $headers_line Header lines
3658
     * @param string $subject Subject
3659
     * @param string $body Body
3660
     * @return string
3661
     */
3662
    public function DKIM_Add($headers_line, $subject, $body)
3663
    {
3664
        $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
3665
        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3666
        $DKIMquery = 'dns/txt'; // Query method
3667
        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3668
        $subject_header = "Subject: $subject";
3669
        $headers = explode($this->LE, $headers_line);
3670
        $from_header = '';
3671
        $to_header = '';
3672
        $current = '';
3673
        foreach ($headers as $header) {
3674
            if (strpos($header, 'From:') === 0) {
3675
                $from_header = $header;
3676
                $current = 'from_header';
3677
            } elseif (strpos($header, 'To:') === 0) {
3678
                $to_header = $header;
3679
                $current = 'to_header';
3680
            } else {
3681
                if (!empty($$current) && strpos($header, ' =?') === 0) {
3682
                    $$current .= $header;
3683
                } else {
3684
                    $current = '';
3685
                }
3686
            }
3687
        }
3688
        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3689
        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3690
        $subject = str_replace(
3691
            '|',
3692
            '=7C',
3693
            $this->DKIM_QP($subject_header)
3694
        ); // Copied header fields (dkim-quoted-printable)
3695
        $body = $this->DKIM_BodyC($body);
3696
        $DKIMlen = strlen($body); // Length of body
3697
        $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
3698
        if ('' == $this->DKIM_identity) {
3699
            $ident = '';
3700
        } else {
3701
            $ident = ' i=' . $this->DKIM_identity . ';';
3702
        }
3703
        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3704
            $DKIMsignatureType . '; q=' .
3705
            $DKIMquery . '; l=' .
3706
            $DKIMlen . '; s=' .
3707
            $this->DKIM_selector .
3708
            ";\r\n" .
3709
            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3710
            "\th=From:To:Subject;\r\n" .
3711
            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3712
            "\tz=$from\r\n" .
3713
            "\t|$to\r\n" .
3714
            "\t|$subject;\r\n" .
3715
            "\tbh=" . $DKIMb64 . ";\r\n" .
3716
            "\tb=";
3717
        $toSign = $this->DKIM_HeaderC(
3718
            $from_header . "\r\n" .
3719
            $to_header . "\r\n" .
3720
            $subject_header . "\r\n" .
3721
            $dkimhdrs
3722
        );
3723
        $signed = $this->DKIM_Sign($toSign);
3724
        return $dkimhdrs . $signed . "\r\n";
3725
    }
3726
3727
    /**
3728
     * Detect if a string contains a line longer than the maximum line length allowed.
3729
     * @param string $str
3730
     * @return boolean
3731
     * @static
3732
     */
3733
    public static function hasLineLongerThanMax($str)
3734
    {
3735
        //+2 to include CRLF line break for a 1000 total
3736
        return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
3737
    }
3738
3739
    /**
3740
     * Allows for public read access to 'to' property.
3741
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3742
     * @access public
3743
     * @return array
3744
     */
3745
    public function getToAddresses()
3746
    {
3747
        return $this->to;
3748
    }
3749
3750
    /**
3751
     * Allows for public read access to 'cc' property.
3752
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3753
     * @access public
3754
     * @return array
3755
     */
3756
    public function getCcAddresses()
3757
    {
3758
        return $this->cc;
3759
    }
3760
3761
    /**
3762
     * Allows for public read access to 'bcc' property.
3763
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3764
     * @access public
3765
     * @return array
3766
     */
3767
    public function getBccAddresses()
3768
    {
3769
        return $this->bcc;
3770
    }
3771
3772
    /**
3773
     * Allows for public read access to 'ReplyTo' property.
3774
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3775
     * @access public
3776
     * @return array
3777
     */
3778
    public function getReplyToAddresses()
3779
    {
3780
        return $this->ReplyTo;
3781
    }
3782
3783
    /**
3784
     * Allows for public read access to 'all_recipients' property.
3785
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3786
     * @access public
3787
     * @return array
3788
     */
3789
    public function getAllRecipientAddresses()
3790
    {
3791
        return $this->all_recipients;
3792
    }
3793
3794
    /**
3795
     * Perform a callback.
3796
     * @param boolean $isSent
3797
     * @param array $to
3798
     * @param array $cc
3799
     * @param array $bcc
3800
     * @param string $subject
3801
     * @param string $body
3802
     * @param string $from
3803
     */
3804
    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3805
    {
3806
        if (!empty($this->action_function) && is_callable($this->action_function)) {
3807
            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3808
            call_user_func_array($this->action_function, $params);
3809
        }
3810
    }
3811
}
3812
3813
/**
3814
 * PHPMailer exception handler
3815
 * @package PHPMailer
3816
 */
3817
class phpmailerException extends Exception
3818
{
3819
    /**
3820
     * Prettify error message output
3821
     * @return string
3822
     */
3823
    public function errorMessage()
3824
    {
3825
        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3826
        return $errorMsg;
3827
    }
3828
}
3829