PHPMailer::createBody()   F
last analyzed

Complexity

Conditions 16
Paths 486

Size

Total Lines 142
Code Lines 124

Duplication

Lines 42
Ratio 29.58 %

Importance

Changes 0
Metric Value
cc 16
eloc 124
nc 486
nop 0
dl 42
loc 142
rs 3.195
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * PHPMailer - PHP email creation and transport class.
4
 * PHP Version 5.0.0
5
 * Version 5.2.7.
6
 *
7
 * @link      https://github.com/PHPMailer/PHPMailer/
8
 *
9
 * @author    Marcus Bointon (coolbru) <[email protected]>
10
 * @author    Jim Jagielski (jimjag) <[email protected]>
11
 * @author    Andy Prevost (codeworxtech) <[email protected]>
12
 * @author    Brent R. Matzelle (original founder)
13
 * @copyright 2013 Marcus Bointon
14
 * @copyright 2010 - 2012 Jim Jagielski
15
 * @copyright 2004 - 2009 Andy Prevost
16
 * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
17
 * @note      This program is distributed in the hope that it will be useful - WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
 * FITNESS FOR A PARTICULAR PURPOSE.
20
 */
21
if (version_compare(PHP_VERSION, '5.0.0', '<')) {
22
    exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n");
23
}
24
25
/**
26
 * PHPMailer - PHP email creation and transport class.
27
 * PHP Version 5.0.0.
28
 *
29
 * @author    Marcus Bointon (coolbru) <[email protected]>
30
 * @author    Jim Jagielski (jimjag) <[email protected]>
31
 * @author    Andy Prevost (codeworxtech) <[email protected]>
32
 * @author    Brent R. Matzelle (original founder)
33
 * @copyright 2013 Marcus Bointon
34
 * @copyright 2010 - 2012 Jim Jagielski
35
 * @copyright 2004 - 2009 Andy Prevost
36
 */
37
class PHPMailer
38
{
39
    /**
40
     * The PHPMailer Version number.
41
     *
42
     * @var string
43
     */
44
    public $Version = '5.2.7';
45
46
    /**
47
     * Email priority.
48
     * Options: 1 = High, 3 = Normal, 5 = low.
49
     *
50
     * @var int
51
     */
52
    public $Priority = 3;
53
54
    /**
55
     * The character set of the message.
56
     *
57
     * @var string
58
     */
59
    public $CharSet = 'iso-8859-1';
60
61
    /**
62
     * The MIME Content-type of the message.
63
     *
64
     * @var string
65
     */
66
    public $ContentType = 'text/plain';
67
68
    /**
69
     * The message encoding.
70
     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
71
     *
72
     * @var string
73
     */
74
    public $Encoding = '8bit';
75
76
    /**
77
     * Holds the most recent mailer error message.
78
     *
79
     * @var string
80
     */
81
    public $ErrorInfo = '';
82
83
    /**
84
     * The From email address for the message.
85
     *
86
     * @var string
87
     */
88
    public $From = 'root@localhost';
89
90
    /**
91
     * The From name of the message.
92
     *
93
     * @var string
94
     */
95
    public $FromName = 'Root User';
96
97
    /**
98
     * The Sender email (Return-Path) of the message.
99
     * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
100
     *
101
     * @var string
102
     */
103
    public $Sender = '';
104
105
    /**
106
     * The Return-Path of the message.
107
     * If empty, it will be set to either From or Sender.
108
     *
109
     * @var string
110
     */
111
    public $ReturnPath = '';
112
113
    /**
114
     * The Subject of the message.
115
     *
116
     * @var string
117
     */
118
    public $Subject = '';
119
120
    /**
121
     * An HTML or plain text message body.
122
     * If HTML then call isHTML(true).
123
     *
124
     * @var string
125
     */
126
    public $Body = '';
127
128
    /**
129
     * The plain-text message body.
130
     * This body can be read by mail clients that do not have HTML email
131
     * capability such as mutt & Eudora.
132
     * Clients that can read HTML will view the normal Body.
133
     *
134
     * @var string
135
     */
136
    public $AltBody = '';
137
138
    /**
139
     * An iCal message part body.
140
     * Only supported in simple alt or alt_inline message types
141
     * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator.
142
     *
143
     * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
144
     * @link http://kigkonsult.se/iCalcreator/
145
     * @var string
146
     */
147
    public $Ical = '';
148
149
    /**
150
     * The complete compiled MIME message body.
151
     *
152
     * @var string
153
     */
154
    protected $MIMEBody = '';
155
156
    /**
157
     * The complete compiled MIME message headers.
158
     *
159
     * @var string
160
     */
161
    protected $MIMEHeader = '';
162
163
    /**
164
     * Extra headers that createHeader() doesn't fold in.
165
     *
166
     * @var string
167
     */
168
    protected $mailHeader = '';
169
170
    /**
171
     * Word-wrap the message body to this number of chars.
172
     *
173
     * @var int
174
     */
175
    public $WordWrap = 0;
176
177
    /**
178
     * Which method to use to send mail.
179
     * Options: "mail", "sendmail", or "smtp".
180
     *
181
     * @var string
182
     */
183
    public $Mailer = 'mail';
184
185
    /**
186
     * The path to the sendmail program.
187
     *
188
     * @var string
189
     */
190
    public $Sendmail = '/usr/sbin/sendmail';
191
192
    /**
193
     * Whether mail() uses a fully sendmail-compatible MTA.
194
     * One which supports sendmail's "-oi -f" options.
195
     *
196
     * @var bool
197
     */
198
    public $UseSendmailOptions = true;
199
200
    /**
201
     * Path to PHPMailer plugins.
202
     * Useful if the SMTP class is not in the PHP include path.
203
     *
204
     * @var string
205
     *
206
     * @deprecated Should not be needed now there is an autoloader.
207
     */
208
    public $PluginDir = '';
209
210
    /**
211
     * The email address that a reading confirmation should be sent to.
212
     *
213
     * @var string
214
     */
215
    public $ConfirmReadingTo = '';
216
217
    /**
218
     * The hostname to use in Message-Id and Received headers
219
     * and as default HELO string.
220
     * If empty, the value returned
221
     * by SERVER_NAME is used or 'localhost.localdomain'.
222
     *
223
     * @var string
224
     */
225
    public $Hostname = '';
226
227
    /**
228
     * An ID to be used in the Message-Id header.
229
     * If empty, a unique id will be generated.
230
     *
231
     * @var string
232
     */
233
    public $MessageID = '';
234
235
    /**
236
     * The message Date to be used in the Date header.
237
     * If empty, the current date will be added.
238
     *
239
     * @var string
240
     */
241
    public $MessageDate = '';
242
243
    /**
244
     * SMTP hosts.
245
     * Either a single hostname or multiple semicolon-delimited hostnames.
246
     * You can also specify a different port
247
     * for each host by using this format: [hostname:port]
248
     * (e.g. "smtp1.example.com:25;smtp2.example.com").
249
     * Hosts will be tried in order.
250
     *
251
     * @var string
252
     */
253
    public $Host = 'localhost';
254
255
    /**
256
     * The default SMTP server port.
257
     *
258
     * @var int
259
     * @Todo Why is this needed when the SMTP class takes care of it?
260
     */
261
    public $Port = 25;
262
263
    /**
264
     * The SMTP HELO of the message.
265
     * Default is $Hostname.
266
     *
267
     * @var string
268
     *
269
     * @see PHPMailer::$Hostname
270
     */
271
    public $Helo = '';
272
273
    /**
274
     * The secure connection prefix.
275
     * Options: "", "ssl" or "tls".
276
     *
277
     * @var string
278
     */
279
    public $SMTPSecure = '';
280
281
    /**
282
     * Whether to use SMTP authentication.
283
     * Uses the Username and Password properties.
284
     *
285
     * @var bool
286
     *
287
     * @see PHPMailer::$Username
288
     * @see PHPMailer::$Password
289
     */
290
    public $SMTPAuth = false;
291
292
    /**
293
     * SMTP username.
294
     *
295
     * @var string
296
     */
297
    public $Username = '';
298
299
    /**
300
     * SMTP password.
301
     *
302
     * @var string
303
     */
304
    public $Password = '';
305
306
    /**
307
     * SMTP auth type.
308
     * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5.
309
     *
310
     * @var string
311
     */
312
    public $AuthType = '';
313
314
    /**
315
     * SMTP realm.
316
     * Used for NTLM auth.
317
     *
318
     * @var string
319
     */
320
    public $Realm = '';
321
322
    /**
323
     * SMTP workstation.
324
     * Used for NTLM auth.
325
     *
326
     * @var string
327
     */
328
    public $Workstation = '';
329
330
    /**
331
     * The SMTP server timeout in seconds.
332
     *
333
     * @var int
334
     */
335
    public $Timeout = 10;
336
337
    /**
338
     * SMTP class debug output mode.
339
     * Options: 0 = off, 1 = commands, 2 = commands and data.
340
     *
341
     * @var int
342
     *
343
     * @see SMTP::$do_debug
344
     */
345
    public $SMTPDebug = 0;
346
347
    /**
348
     * The function/method to use for debugging output.
349
     * Options: "echo" or "error_log".
350
     *
351
     * @var string
352
     *
353
     * @see SMTP::$Debugoutput
354
     */
355
    public $Debugoutput = 'echo';
356
357
    /**
358
     * Whether to keep SMTP connection open after each message.
359
     * If this is set to true then to close the connection
360
     * requires an explicit call to smtpClose().
361
     *
362
     * @var bool
363
     */
364
    public $SMTPKeepAlive = false;
365
366
    /**
367
     * Whether to split multiple to addresses into multiple messages
368
     * or send them all in one message.
369
     *
370
     * @var bool
371
     */
372
    public $SingleTo = false;
373
374
    /**
375
     * Storage for addresses when SingleTo is enabled.
376
     *
377
     * @var array
378
     *
379
     * @todo This should really not be public
380
     */
381
    public $SingleToArray = [];
382
383
    /**
384
     * Whether to generate VERP addresses on send.
385
     * Only applicable when sending via SMTP.
386
     *
387
     * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
388
     * @var bool
389
     */
390
    public $do_verp = false;
391
392
    /**
393
     * Whether to allow sending messages with an empty body.
394
     *
395
     * @var bool
396
     */
397
    public $AllowEmpty = false;
398
399
    /**
400
     * The default line ending.
401
     *
402
     * @note The default remains "\n". We force CRLF where we know
403
     *        it must be used via self::CRLF.
404
     * @var string
405
     */
406
    public $LE = "\n";
407
408
    /**
409
     * DKIM selector.
410
     *
411
     * @var string
412
     */
413
    public $DKIM_selector = '';
414
415
    /**
416
     * DKIM Identity.
417
     * Usually the email address used as the source of the email.
418
     *
419
     * @var string
420
     */
421
    public $DKIM_identity = '';
422
423
    /**
424
     * DKIM passphrase.
425
     * Used if your key is encrypted.
426
     *
427
     * @var string
428
     */
429
    public $DKIM_passphrase = '';
430
431
    /**
432
     * DKIM signing domain name.
433
     *
434
     * @example 'example.com'
435
     * @var string
436
     */
437
    public $DKIM_domain = '';
438
439
    /**
440
     * DKIM private key file path.
441
     *
442
     * @var string
443
     */
444
    public $DKIM_private = '';
445
446
    /**
447
     * Callback Action function name.
448
     *
449
     * The function that handles the result of the send email action.
450
     * It is called out by send() for each email sent.
451
     *
452
     * Value can be:
453
     * - 'function_name' for function names
454
     * - 'Class::Method' for static method calls
455
     * - array($object, 'Method') for calling methods on $object
456
     * See http://php.net/is_callable manual page for more details.
457
     *
458
     * Parameters:
459
     *   bool    $result        result of the send action
460
     *   string  $to            email address of the recipient
461
     *   string  $cc            cc email addresses
462
     *   string  $bcc           bcc email addresses
463
     *   string  $subject       the subject
464
     *   string  $body          the email body
465
     *   string  $from          email address of sender
466
     *
467
     * @var string
468
     */
469
    public $action_function = '';
470
471
    /**
472
     * What to use in the X-Mailer header.
473
     * Options: null for default, whitespace for none, or a string to use.
474
     *
475
     * @var string
476
     */
477
    public $XMailer = '';
478
479
    /**
480
     * An instance of the SMTP sender class.
481
     *
482
     * @var SMTP
483
     */
484
    protected $smtp = null;
485
486
    /**
487
     * The array of 'to' addresses.
488
     *
489
     * @var array
490
     */
491
    protected $to = [];
492
493
    /**
494
     * The array of 'cc' addresses.
495
     *
496
     * @var array
497
     */
498
    protected $cc = [];
499
500
    /**
501
     * The array of 'bcc' addresses.
502
     *
503
     * @var array
504
     */
505
    protected $bcc = [];
506
507
    /**
508
     * The array of reply-to names and addresses.
509
     *
510
     * @var array
511
     */
512
    protected $ReplyTo = [];
513
514
    /**
515
     * An array of all kinds of addresses.
516
     * Includes all of $to, $cc, $bcc, $replyto.
517
     *
518
     * @var array
519
     */
520
    protected $all_recipients = [];
521
522
    /**
523
     * The array of attachments.
524
     *
525
     * @var array
526
     */
527
    protected $attachment = [];
528
529
    /**
530
     * The array of custom headers.
531
     *
532
     * @var array
533
     */
534
    protected $CustomHeader = [];
535
536
    /**
537
     * The most recent Message-ID (including angular brackets).
538
     *
539
     * @var string
540
     */
541
    protected $lastMessageID = '';
542
543
    /**
544
     * The message's MIME type.
545
     *
546
     * @var string
547
     */
548
    protected $message_type = '';
549
550
    /**
551
     * The array of MIME boundary strings.
552
     *
553
     * @var array
554
     */
555
    protected $boundary = [];
556
557
    /**
558
     * The array of available languages.
559
     *
560
     * @var array
561
     */
562
    protected $language = [];
563
564
    /**
565
     * The number of errors encountered.
566
     *
567
     * @var int
568
     */
569
    protected $error_count = 0;
570
571
    /**
572
     * The S/MIME certificate file path.
573
     *
574
     * @var string
575
     */
576
    protected $sign_cert_file = '';
577
578
    /**
579
     * The S/MIME key file path.
580
     *
581
     * @var string
582
     */
583
    protected $sign_key_file = '';
584
585
    /**
586
     * The S/MIME password for the key.
587
     * Used only if the key is encrypted.
588
     *
589
     * @var string
590
     */
591
    protected $sign_key_pass = '';
592
593
    /**
594
     * Whether to throw exceptions for errors.
595
     *
596
     * @var bool
597
     */
598
    protected $exceptions = false;
599
600
    /**
601
     * Error severity: message only, continue processing.
602
     */
603
    const STOP_MESSAGE = 0;
604
605
    /**
606
     * Error severity: message, likely ok to continue processing.
607
     */
608
    const STOP_CONTINUE = 1;
609
610
    /**
611
     * Error severity: message, plus full stop, critical error reached.
612
     */
613
    const STOP_CRITICAL = 2;
614
615
    /**
616
     * SMTP RFC standard line ending.
617
     */
618
    const CRLF = "\r\n";
619
620
    /**
621
     * Constructor.
622
     *
623
     * @param bool $exceptions Should we throw external exceptions?
624
     */
625
    public function __construct($exceptions = false)
626
    {
627
        $this->exceptions = ($exceptions == true);
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
628
        //Make sure our autoloader is loaded
629
        if (!spl_autoload_functions() || !in_array('PHPMailerAutoload', spl_autoload_functions())) {
630
            require 'PHPMailerAutoload.php';
631
        }
632
    }
633
634
    /**
635
     * Destructor.
636
     */
637
    public function __destruct()
638
    {
639
        if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
640
            $this->smtpClose();
641
        }
642
    }
643
644
    /**
645
     * Call mail() in a safe_mode-aware fashion.
646
     * Also, unless sendmail_path points to sendmail (or something that
647
     * claims to be sendmail), don't pass params (not a perfect fix,
648
     * but it will do).
649
     *
650
     * @param string $to      To
651
     * @param string $subject Subject
652
     * @param string $body    Message Body
653
     * @param string $header  Additional Header(s)
654
     * @param string $params  Params
655
     *
656
     * @return bool
657
     */
658
    private function mailPassthru($to, $subject, $body, $header, $params)
659
    {
660
        if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
661
            $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header);
662
        } else {
663
            $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header, $params);
664
        }
665
666
        return $rt;
667
    }
668
669
    /**
670
     * Output debugging info via user-defined method.
671
     * Only if debug output is enabled.
672
     *
673
     * @see PHPMailer::$Debugoutput
674
     * @see PHPMailer::$SMTPDebug
675
     *
676
     * @param string $str
677
     */
678
    protected function edebug($str)
679
    {
680
        if (!$this->SMTPDebug) {
681
            return;
682
        }
683
        switch ($this->Debugoutput) {
684
            case 'error_log':
685
                error_log($str);
686
                break;
687
            case 'html':
688
                //Cleans up output a bit for a better looking display that's HTML-safe
689
                echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet)."<br>\n";
690
                break;
691
            case 'echo':
692
            default:
693
                //Just echoes exactly what was received
694
                echo $str;
695
        }
696
    }
697
698
    /**
699
     * Sets message type to HTML or plain.
700
     *
701
     * @param bool $ishtml True for HTML mode.
702
     *
703
     * @return void
704
     */
705
    public function isHTML($ishtml = true)
706
    {
707
        if ($ishtml) {
708
            $this->ContentType = 'text/html';
709
        } else {
710
            $this->ContentType = 'text/plain';
711
        }
712
    }
713
714
    /**
715
     * Send messages using SMTP.
716
     *
717
     * @return void
718
     */
719
    public function isSMTP()
720
    {
721
        $this->Mailer = 'smtp';
722
    }
723
724
    /**
725
     * Send messages using PHP's mail() function.
726
     *
727
     * @return void
728
     */
729
    public function isMail()
730
    {
731
        $this->Mailer = 'mail';
732
    }
733
734
    /**
735
     * Send messages using $Sendmail.
736
     *
737
     * @return void
738
     */
739
    public function isSendmail()
740
    {
741
        if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
742
            $this->Sendmail = '/var/qmail/bin/sendmail';
743
        }
744
        $this->Mailer = 'sendmail';
745
    }
746
747
    /**
748
     * Send messages using qmail.
749
     *
750
     * @return void
751
     */
752
    public function isQmail()
753
    {
754
        if (stristr(ini_get('sendmail_path'), 'qmail')) {
755
            $this->Sendmail = '/var/qmail/bin/sendmail';
756
        }
757
        $this->Mailer = 'sendmail';
758
    }
759
760
    /**
761
     * Add a "To" address.
762
     *
763
     * @param string $address
764
     * @param string $name
765
     *
766
     * @return bool true on success, false if address already used
767
     */
768
    public function addAddress($address, $name = '')
769
    {
770
        return $this->addAnAddress('to', $address, $name);
771
    }
772
773
    /**
774
     * Add a "CC" address.
775
     *
776
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
777
     *
778
     * @param string $address
779
     * @param string $name
780
     *
781
     * @return bool true on success, false if address already used
782
     */
783
    public function addCC($address, $name = '')
784
    {
785
        return $this->addAnAddress('cc', $address, $name);
786
    }
787
788
    /**
789
     * Add a "BCC" address.
790
     *
791
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
792
     *
793
     * @param string $address
794
     * @param string $name
795
     *
796
     * @return bool true on success, false if address already used
797
     */
798
    public function addBCC($address, $name = '')
799
    {
800
        return $this->addAnAddress('bcc', $address, $name);
801
    }
802
803
    /**
804
     * Add a "Reply-to" address.
805
     *
806
     * @param string $address
807
     * @param string $name
808
     *
809
     * @return bool
810
     */
811
    public function addReplyTo($address, $name = '')
812
    {
813
        return $this->addAnAddress('Reply-To', $address, $name);
814
    }
815
816
    /**
817
     * Add an address to one of the recipient arrays.
818
     * Addresses that have been added already return false, but do not throw exceptions.
819
     *
820
     * @param string $kind    One of 'to', 'cc', 'bcc', 'ReplyTo'
821
     * @param string $address The email address to send to
822
     * @param string $name
823
     *
824
     * @throws phpmailerException
825
     *
826
     * @return bool true on success, false if address already used or invalid in some way
827
     */
828
    protected function addAnAddress($kind, $address, $name = '')
829
    {
830
        if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
831
            $this->setError($this->lang('Invalid recipient array').': '.$kind);
832
            if ($this->exceptions) {
833
                throw new phpmailerException('Invalid recipient array: '.$kind);
834
            }
835
            $this->edebug($this->lang('Invalid recipient array').': '.$kind);
836
837
            return false;
838
        }
839
        $address = trim($address);
840
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
841 View Code Duplication
        if (!$this->validateAddress($address)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
842
            $this->setError($this->lang('invalid_address').': '.$address);
843
            if ($this->exceptions) {
844
                throw new phpmailerException($this->lang('invalid_address').': '.$address);
845
            }
846
            $this->edebug($this->lang('invalid_address').': '.$address);
847
848
            return false;
849
        }
850
        if ($kind != 'Reply-To') {
851
            if (!isset($this->all_recipients[strtolower($address)])) {
852
                array_push($this->$kind, [$address, $name]);
853
                $this->all_recipients[strtolower($address)] = true;
854
855
                return true;
856
            }
857
        } else {
858
            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
859
                $this->ReplyTo[strtolower($address)] = [$address, $name];
860
861
                return true;
862
            }
863
        }
864
865
        return false;
866
    }
867
868
    /**
869
     * Set the From and FromName properties.
870
     *
871
     * @param string $address
872
     * @param string $name
873
     * @param bool   $auto    Whether to also set the Sender address, defaults to true
874
     *
875
     * @throws phpmailerException
876
     *
877
     * @return bool
878
     */
879
    public function setFrom($address, $name = '', $auto = true)
880
    {
881
        $address = trim($address);
882
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
883 View Code Duplication
        if (!$this->validateAddress($address)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
884
            $this->setError($this->lang('invalid_address').': '.$address);
885
            if ($this->exceptions) {
886
                throw new phpmailerException($this->lang('invalid_address').': '.$address);
887
            }
888
            $this->edebug($this->lang('invalid_address').': '.$address);
889
890
            return false;
891
        }
892
        $this->From = $address;
893
        $this->FromName = $name;
894
        if ($auto) {
895
            if (empty($this->Sender)) {
896
                $this->Sender = $address;
897
            }
898
        }
899
900
        return true;
901
    }
902
903
    /**
904
     * Return the Message-ID header of the last email.
905
     * Technically this is the value from the last time the headers were created,
906
     * but it's also the message ID of the last sent message except in
907
     * pathological cases.
908
     *
909
     * @return string
910
     */
911
    public function getLastMessageID()
912
    {
913
        return $this->lastMessageID;
914
    }
915
916
    /**
917
     * Check that a string looks like an email address.
918
     *
919
     * @param string $address       The email address to check
920
     * @param string $patternselect A selector for the validation pattern to use :
921
     *                              'auto' - pick best one automatically;
922
     *                              'pcre8' - use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
923
     *                              'pcre' - use old PCRE implementation;
924
     *                              'php' - use PHP built-in FILTER_VALIDATE_EMAIL; faster, less thorough;
925
     *                              'noregex' - super fast, really dumb.
926
     *
927
     * @return bool
928
     * @static
929
     */
930
    public static function validateAddress($address, $patternselect = 'auto')
931
    {
932
        if ($patternselect == 'auto') {
933
            if (defined(
934
                'PCRE_VERSION'
935
            )
936
            ) { //Check this instead of extension_loaded so it works when that function is disabled
937
                if (version_compare(PCRE_VERSION, '8.0') >= 0) {
938
                    $patternselect = 'pcre8';
939
                } else {
940
                    $patternselect = 'pcre';
941
                }
942
            } else {
943
                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
944
                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
945
                    $patternselect = 'php';
946
                } else {
947
                    $patternselect = 'noregex';
948
                }
949
            }
950
        }
951
        switch ($patternselect) {
952 View Code Duplication
            case 'pcre8':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
953
                /*
954
                 * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is
955
                 * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to
956
                 * not allow a@b type valid addresses :(
957
                 *
958
                 * @link      http://squiloople.com/2009/12/20/email-address-validation/
959
                 * @copyright 2009-2010 Michael Rushton
960
                 * Feel free to use and redistribute this code. But please keep this copyright notice.
961
                 */
962
                return (bool) preg_match(
963
                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)'.
964
                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)'.
965
                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)'.
966
                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*'.
967
                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)'.
968
                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}'.
969
                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:'.
970
                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}'.
971
                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
972
                    $address
973
                );
974
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
975 View Code Duplication
            case 'pcre':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
976
                //An older regex that doesn't need a recent PCRE
977
                return (bool) preg_match(
978
                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>'.
979
                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")'.
980
                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*'.
981
                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})'.
982
                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:'.
983
                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?'.
984
                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:'.
985
                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?'.
986
                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}'.
987
                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
988
                    $address
989
                );
990
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
991
            case 'php':
992
            default:
993
                return (bool) filter_var($address, FILTER_VALIDATE_EMAIL);
994
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
995
            case 'noregex':
0 ignored issues
show
Unused Code introduced by
case 'noregex': //No...ddress) - 1; break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
996
                //No PCRE! Do something _very_ approximate!
997
                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
998
                return strlen($address) >= 3
999
                    and strpos($address, '@') >= 1
1000
                    and strpos($address, '@') != strlen($address) - 1;
1001
                break;
1002
        }
1003
    }
1004
1005
    /**
1006
     * Create a message and send it.
1007
     * Uses the sending method specified by $Mailer.
1008
     * Returns false on error - Use the ErrorInfo variable to view description of the error.
1009
     *
1010
     * @throws phpmailerException
1011
     *
1012
     * @return bool
1013
     */
1014
    public function send()
1015
    {
1016
        try {
1017
            if (!$this->preSend()) {
1018
                return false;
1019
            }
1020
1021
            return $this->postSend();
1022
        } catch (phpmailerException $e) {
1023
            $this->mailHeader = '';
1024
            $this->setError($e->getMessage());
1025
            if ($this->exceptions) {
1026
                throw $e;
1027
            }
1028
1029
            return false;
1030
        }
1031
    }
1032
1033
    /**
1034
     * Prepare a message for sending.
1035
     *
1036
     * @throws phpmailerException
1037
     *
1038
     * @return bool
1039
     */
1040
    public function preSend()
1041
    {
1042
        try {
1043
            $this->mailHeader = '';
1044
            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1045
                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1046
            }
1047
1048
            // Set whether the message is multipart/alternative
1049
            if (!empty($this->AltBody)) {
1050
                $this->ContentType = 'multipart/alternative';
1051
            }
1052
1053
            $this->error_count = 0; // reset errors
1054
            $this->setMessageType();
1055
            // Refuse to send an empty message unless we are specifically allowing it
1056
            if (!$this->AllowEmpty and empty($this->Body)) {
1057
                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1058
            }
1059
1060
            $this->MIMEHeader = $this->createHeader();
1061
            $this->MIMEBody = $this->createBody();
1062
1063
            // To capture the complete message when using mail(), create
1064
            // an extra header list which createHeader() doesn't fold in
1065
            if ($this->Mailer == 'mail') {
1066
                if (count($this->to) > 0) {
1067
                    $this->mailHeader .= $this->addrAppend('To', $this->to);
1068
                } else {
1069
                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1070
                }
1071
                $this->mailHeader .= $this->headerLine(
1072
                    'Subject',
1073
                    $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1074
                );
1075
            }
1076
1077
            // Sign with DKIM if enabled
1078
            if (!empty($this->DKIM_domain)
1079
                && !empty($this->DKIM_private)
1080
                && !empty($this->DKIM_selector)
1081
                && !empty($this->DKIM_domain)
1082
                && file_exists($this->DKIM_private)
1083
            ) {
1084
                $header_dkim = $this->DKIM_Add(
1085
                    $this->MIMEHeader.$this->mailHeader,
1086
                    $this->encodeHeader($this->secureHeader($this->Subject)),
1087
                    $this->MIMEBody
1088
                );
1089
                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ").self::CRLF.
1090
                    str_replace("\r\n", "\n", $header_dkim).self::CRLF;
1091
            }
1092
1093
            return true;
1094
        } catch (phpmailerException $e) {
1095
            $this->setError($e->getMessage());
1096
            if ($this->exceptions) {
1097
                throw $e;
1098
            }
1099
1100
            return false;
1101
        }
1102
    }
1103
1104
    /**
1105
     * Actually send a message.
1106
     * Send the email via the selected mechanism.
1107
     *
1108
     * @throws phpmailerException
1109
     *
1110
     * @return bool
1111
     */
1112
    public function postSend()
1113
    {
1114
        try {
1115
            // Choose the mailer and send through it
1116
            switch ($this->Mailer) {
1117
                case 'sendmail':
1118
                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1119
                case 'smtp':
1120
                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1121
                case 'mail':
1122
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1123
                default:
1124
                    if (method_exists($this, $this->Mailer.'Send')) {
1125
                        $sendMethod = $this->Mailer.'Send';
1126
1127
                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1128
                    } else {
1129
                        return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1130
                    }
1131
            }
1132
        } catch (phpmailerException $e) {
1133
            $this->setError($e->getMessage());
1134
            if ($this->exceptions) {
1135
                throw $e;
1136
            }
1137
            $this->edebug($e->getMessage()."\n");
1138
        }
1139
1140
        return false;
1141
    }
1142
1143
    /**
1144
     * Send mail using the $Sendmail program.
1145
     *
1146
     * @param string $header The message headers
1147
     * @param string $body   The message body
1148
     *
1149
     * @see    PHPMailer::$Sendmail
1150
     *
1151
     * @throws phpmailerException
1152
     *
1153
     * @return bool
1154
     */
1155
    protected function sendmailSend($header, $body)
1156
    {
1157
        if ($this->Sender != '') {
1158
            $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1159
        } else {
1160
            $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1161
        }
1162
        if ($this->SingleTo === true) {
1163
            foreach ($this->SingleToArray as $val) {
1164 View Code Duplication
                if (!@$mail = popen($sendmail, 'w')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1165
                    throw new phpmailerException($this->lang('execute').$this->Sendmail, self::STOP_CRITICAL);
1166
                }
1167
                fwrite($mail, 'To: '.$val."\n");
1168
                fwrite($mail, $header);
1169
                fwrite($mail, $body);
1170
                $result = pclose($mail);
1171
                // implement call back function if it exists
1172
                $isSent = ($result == 0) ? 1 : 0;
1173
                $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
0 ignored issues
show
Documentation introduced by
$isSent is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->cc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->bcc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1174
                if ($result != 0) {
1175
                    throw new phpmailerException($this->lang('execute').$this->Sendmail, self::STOP_CRITICAL);
1176
                }
1177
            }
1178
        } else {
1179 View Code Duplication
            if (!@$mail = popen($sendmail, 'w')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1180
                throw new phpmailerException($this->lang('execute').$this->Sendmail, self::STOP_CRITICAL);
1181
            }
1182
            fwrite($mail, $header);
1183
            fwrite($mail, $body);
1184
            $result = pclose($mail);
1185
            // implement call back function if it exists
1186
            $isSent = ($result == 0) ? 1 : 0;
1187
            $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
0 ignored issues
show
Documentation introduced by
$isSent is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->to is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->cc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->bcc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1188
            if ($result != 0) {
1189
                throw new phpmailerException($this->lang('execute').$this->Sendmail, self::STOP_CRITICAL);
1190
            }
1191
        }
1192
1193
        return true;
1194
    }
1195
1196
    /**
1197
     * Send mail using the PHP mail() function.
1198
     *
1199
     * @param string $header The message headers
1200
     * @param string $body   The message body
1201
     *
1202
     * @link   http://www.php.net/manual/en/book.mail.php
1203
     *
1204
     * @throws phpmailerException
1205
     *
1206
     * @return bool
1207
     */
1208
    protected function mailSend($header, $body)
1209
    {
1210
        $toArr = [];
1211
        foreach ($this->to as $t) {
1212
            $toArr[] = $this->addrFormat($t);
1213
        }
1214
        $to = implode(', ', $toArr);
1215
1216
        if (empty($this->Sender)) {
1217
            $params = ' ';
1218
        } else {
1219
            $params = sprintf('-f%s', $this->Sender);
1220
        }
1221
        if ($this->Sender != '' and !ini_get('safe_mode')) {
1222
            $old_from = ini_get('sendmail_from');
1223
            ini_set('sendmail_from', $this->Sender);
1224
        }
1225
        $rt = false;
1226
        if ($this->SingleTo === true && count($toArr) > 1) {
1227
            foreach ($toArr as $val) {
1228
                $rt = $this->mailPassthru($val, $this->Subject, $body, $header, $params);
1229
                // implement call back function if it exists
1230
                $isSent = ($rt == 1) ? 1 : 0;
1231
                $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
0 ignored issues
show
Documentation introduced by
$isSent is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->cc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->bcc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1232
            }
1233
        } else {
1234
            $rt = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1235
            // implement call back function if it exists
1236
            $isSent = ($rt == 1) ? 1 : 0;
1237
            $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
0 ignored issues
show
Documentation introduced by
$isSent is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->cc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->bcc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1238
        }
1239
        if (isset($old_from)) {
1240
            ini_set('sendmail_from', $old_from);
1241
        }
1242
        if (!$rt) {
1243
            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1244
        }
1245
1246
        return true;
1247
    }
1248
1249
    /**
1250
     * Get an instance to use for SMTP operations.
1251
     * Override this function to load your own SMTP implementation.
1252
     *
1253
     * @return SMTP
1254
     */
1255
    public function getSMTPInstance()
1256
    {
1257
        if (!is_object($this->smtp)) {
1258
            $this->smtp = new SMTP();
1259
        }
1260
1261
        return $this->smtp;
1262
    }
1263
1264
    /**
1265
     * Send mail via SMTP.
1266
     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1267
     * Uses the PHPMailerSMTP class by default.
1268
     *
1269
     * @see    PHPMailer::getSMTPInstance() to use a different class.
1270
     *
1271
     * @param string $header The message headers
1272
     * @param string $body   The message body
1273
     *
1274
     * @throws phpmailerException
1275
     *
1276
     * @uses   SMTP
1277
     *
1278
     * @return bool
1279
     */
1280
    protected function smtpSend($header, $body)
1281
    {
1282
        $bad_rcpt = [];
1283
1284
        if (!$this->smtpConnect()) {
1285
            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1286
        }
1287
        $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
1288
        if (!$this->smtp->mail($smtp_from)) {
1289
            $this->setError($this->lang('from_failed').$smtp_from.' : '.implode(',', $this->smtp->getError()));
1290
            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1291
        }
1292
1293
        // Attempt to send attach all recipients
1294 View Code Duplication
        foreach ($this->to as $to) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1295
            if (!$this->smtp->recipient($to[0])) {
1296
                $bad_rcpt[] = $to[0];
1297
                $isSent = 0;
1298
            } else {
1299
                $isSent = 1;
1300
            }
1301
            $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From);
0 ignored issues
show
Documentation introduced by
$isSent is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1302
        }
1303 View Code Duplication
        foreach ($this->cc as $cc) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1304
            if (!$this->smtp->recipient($cc[0])) {
1305
                $bad_rcpt[] = $cc[0];
1306
                $isSent = 0;
1307
            } else {
1308
                $isSent = 1;
1309
            }
1310
            $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From);
0 ignored issues
show
Documentation introduced by
$isSent is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1311
        }
1312 View Code Duplication
        foreach ($this->bcc as $bcc) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1313
            if (!$this->smtp->recipient($bcc[0])) {
1314
                $bad_rcpt[] = $bcc[0];
1315
                $isSent = 0;
1316
            } else {
1317
                $isSent = 1;
1318
            }
1319
            $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From);
0 ignored issues
show
Documentation introduced by
$isSent is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1320
        }
1321
1322
        if (count($bad_rcpt) > 0) { //Create error message for any bad addresses
1323
            throw new phpmailerException($this->lang('recipients_failed').implode(', ', $bad_rcpt));
1324
        }
1325
        if (!$this->smtp->data($header.$body)) {
1326
            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1327
        }
1328
        if ($this->SMTPKeepAlive == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1329
            $this->smtp->reset();
1330
        } else {
1331
            $this->smtp->quit();
1332
            $this->smtp->close();
1333
        }
1334
1335
        return true;
1336
    }
1337
1338
    /**
1339
     * Initiate a connection to an SMTP server.
1340
     * Returns false if the operation failed.
1341
     *
1342
     * @param array $options An array of options compatible with stream_context_create()
1343
     *
1344
     * @uses   SMTP
1345
     *
1346
     * @throws phpmailerException
1347
     *
1348
     * @return bool
1349
     */
1350
    public function smtpConnect($options = [])
1351
    {
1352
        if (is_null($this->smtp)) {
1353
            $this->smtp = $this->getSMTPInstance();
1354
        }
1355
1356
        //Already connected?
1357
        if ($this->smtp->connected()) {
1358
            return true;
1359
        }
1360
1361
        $this->smtp->setTimeout($this->Timeout);
1362
        $this->smtp->setDebugLevel($this->SMTPDebug);
1363
        $this->smtp->setDebugOutput($this->Debugoutput);
1364
        $this->smtp->setVerp($this->do_verp);
1365
        $hosts = explode(';', $this->Host);
1366
        $lastexception = null;
1367
1368
        foreach ($hosts as $hostentry) {
1369
            $hostinfo = [];
1370
            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1371
                //Not a valid host entry
1372
                continue;
1373
            }
1374
            //$hostinfo[2]: optional ssl or tls prefix
1375
            //$hostinfo[3]: the hostname
1376
            //$hostinfo[4]: optional port number
1377
            //The host string prefix can temporarily override the current setting for SMTPSecure
1378
            //If it's not specified, the default value is used
1379
            $prefix = '';
1380
            $tls = ($this->SMTPSecure == 'tls');
1381
            if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) {
1382
                $prefix = 'ssl://';
1383
                $tls = false; //Can't have SSL and TLS at once
1384
            } elseif ($hostinfo[2] == 'tls') {
1385
                $tls = true;
1386
                //tls doesn't use a prefix
1387
            }
1388
            $host = $hostinfo[3];
1389
            $port = $this->Port;
1390
            $tport = (int) $hostinfo[4];
1391
            if ($tport > 0 and $tport < 65536) {
1392
                $port = $tport;
1393
            }
1394
            if ($this->smtp->connect($prefix.$host, $port, $this->Timeout, $options)) {
1395
                try {
1396
                    if ($this->Helo) {
1397
                        $hello = $this->Helo;
1398
                    } else {
1399
                        $hello = $this->serverHostname();
1400
                    }
1401
                    $this->smtp->hello($hello);
1402
1403
                    if ($tls) {
1404
                        if (!$this->smtp->startTLS()) {
1405
                            throw new phpmailerException($this->lang('connect_host'));
1406
                        }
1407
                        //We must resend HELO after tls negotiation
1408
                        $this->smtp->hello($hello);
1409
                    }
1410
                    if ($this->SMTPAuth) {
1411
                        if (!$this->smtp->authenticate(
1412
                            $this->Username,
1413
                            $this->Password,
1414
                            $this->AuthType,
1415
                            $this->Realm,
1416
                            $this->Workstation
1417
                        )
1418
                        ) {
1419
                            throw new phpmailerException($this->lang('authenticate'));
1420
                        }
1421
                    }
1422
1423
                    return true;
1424
                } catch (phpmailerException $e) {
1425
                    $lastexception = $e;
1426
                    //We must have connected, but then failed TLS or Auth, so close connection nicely
1427
                    $this->smtp->quit();
1428
                }
1429
            }
1430
        }
1431
        //If we get here, all connection attempts have failed, so close connection hard
1432
        $this->smtp->close();
1433
        //As we've caught all exceptions, just report whatever the last one was
1434
        if ($this->exceptions and !is_null($lastexception)) {
1435
            throw $lastexception;
1436
        }
1437
1438
        return false;
1439
    }
1440
1441
    /**
1442
     * Close the active SMTP session if one exists.
1443
     *
1444
     * @return void
1445
     */
1446
    public function smtpClose()
1447
    {
1448
        if ($this->smtp !== null) {
1449
            if ($this->smtp->connected()) {
1450
                $this->smtp->quit();
1451
                $this->smtp->close();
1452
            }
1453
        }
1454
    }
1455
1456
    /**
1457
     * Set the language for error messages.
1458
     * Returns false if it cannot load the language file.
1459
     * The default language is English.
1460
     *
1461
     * @param string $langcode  ISO 639-1 2-character language code (e.g. French is "fr")
1462
     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1463
     *
1464
     * @return bool
1465
     */
1466
    public function setLanguage($langcode = 'en', $lang_path = 'language/')
1467
    {
1468
        //Define full set of translatable strings
1469
        $PHPMAILER_LANG = [
1470
            'authenticate'         => 'SMTP Error: Could not authenticate.',
1471
            'connect_host'         => 'SMTP Error: Could not connect to SMTP host.',
1472
            'data_not_accepted'    => 'SMTP Error: data not accepted.',
1473
            'empty_message'        => 'Message body empty',
1474
            'encoding'             => 'Unknown encoding: ',
1475
            'execute'              => 'Could not execute: ',
1476
            'file_access'          => 'Could not access file: ',
1477
            'file_open'            => 'File Error: Could not open file: ',
1478
            'from_failed'          => 'The following From address failed: ',
1479
            'instantiate'          => 'Could not instantiate mail function.',
1480
            'invalid_address'      => 'Invalid address',
1481
            'mailer_not_supported' => ' mailer is not supported.',
1482
            'provide_address'      => 'You must provide at least one recipient email address.',
1483
            'recipients_failed'    => 'SMTP Error: The following recipients failed: ',
1484
            'signing'              => 'Signing Error: ',
1485
            'smtp_connect_failed'  => 'SMTP connect() failed.',
1486
            'smtp_error'           => 'SMTP server error: ',
1487
            'variable_set'         => 'Cannot set or reset variable: ',
1488
        ];
1489
        //Overwrite language-specific strings.
1490
        //This way we'll never have missing translations - no more "language string failed to load"!
1491
        $l = true;
1492
        if ($langcode != 'en') { //There is no English translation file
1493
            $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php';
1494
        }
1495
        $this->language = $PHPMAILER_LANG;
1496
1497
        return $l == true; //Returns false if language not found
1498
    }
1499
1500
    /**
1501
     * Get the array of strings for the current language.
1502
     *
1503
     * @return array
1504
     */
1505
    public function getTranslations()
1506
    {
1507
        return $this->language;
1508
    }
1509
1510
    /**
1511
     * Create recipient headers.
1512
     *
1513
     * @param string $type
1514
     * @param array  $addr An array of recipient,
1515
     *                     where each recipient is a 2-element indexed array with element 0 containing an address
1516
     *                     and element 1 containing a name, like:
1517
     *                     array(array('[email protected]', 'Joe User'), array('[email protected]', 'Zoe User'))
1518
     *
1519
     * @return string
1520
     */
1521
    public function addrAppend($type, $addr)
1522
    {
1523
        $addresses = [];
1524
        foreach ($addr as $a) {
1525
            $addresses[] = $this->addrFormat($a);
1526
        }
1527
1528
        return $type.': '.implode(', ', $addresses).$this->LE;
1529
    }
1530
1531
    /**
1532
     * Format an address for use in a message header.
1533
     *
1534
     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1535
     *                    like array('[email protected]', 'Joe User')
1536
     *
1537
     * @return string
1538
     */
1539
    public function addrFormat($addr)
1540
    {
1541
        if (empty($addr[1])) { // No name provided
1542
            return $this->secureHeader($addr[0]);
1543
        } else {
1544
            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase').' <'.$this->secureHeader(
1545
                $addr[0]
1546
            ).'>';
1547
        }
1548
    }
1549
1550
    /**
1551
     * Word-wrap message.
1552
     * For use with mailers that do not automatically perform wrapping
1553
     * and for quoted-printable encoded messages.
1554
     * Original written by philippe.
1555
     *
1556
     * @param string $message The message to wrap
1557
     * @param int    $length  The line length to wrap to
1558
     * @param bool   $qp_mode Whether to run in Quoted-Printable mode
1559
     *
1560
     * @return string
1561
     */
1562
    public function wrapText($message, $length, $qp_mode = false)
1563
    {
1564
        $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;
1565
        // If utf-8 encoding is used, we will need to make sure we don't
1566
        // split multibyte characters when we wrap
1567
        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1568
        $lelen = strlen($this->LE);
1569
        $crlflen = strlen(self::CRLF);
1570
1571
        $message = $this->fixEOL($message);
1572
        if (substr($message, -$lelen) == $this->LE) {
1573
            $message = substr($message, 0, -$lelen);
1574
        }
1575
1576
        $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
1577
        $message = '';
1578
        for ($i = 0; $i < count($line); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1579
            $line_part = explode(' ', $line[$i]);
1580
            $buf = '';
1581
            for ($e = 0; $e < count($line_part); $e++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1582
                $word = $line_part[$e];
1583
                if ($qp_mode and (strlen($word) > $length)) {
1584
                    $space_left = $length - strlen($buf) - $crlflen;
1585
                    if ($e != 0) {
1586
                        if ($space_left > 20) {
1587
                            $len = $space_left;
1588 View Code Duplication
                            if ($is_utf8) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1589
                                $len = $this->utf8CharBoundary($word, $len);
1590
                            } elseif (substr($word, $len - 1, 1) == '=') {
1591
                                $len--;
1592
                            } elseif (substr($word, $len - 2, 1) == '=') {
1593
                                $len -= 2;
1594
                            }
1595
                            $part = substr($word, 0, $len);
1596
                            $word = substr($word, $len);
1597
                            $buf .= ' '.$part;
1598
                            $message .= $buf.sprintf('=%s', self::CRLF);
1599
                        } else {
1600
                            $message .= $buf.$soft_break;
1601
                        }
1602
                        $buf = '';
1603
                    }
1604
                    while (strlen($word) > 0) {
1605
                        if ($length <= 0) {
1606
                            break;
1607
                        }
1608
                        $len = $length;
1609 View Code Duplication
                        if ($is_utf8) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1610
                            $len = $this->utf8CharBoundary($word, $len);
1611
                        } elseif (substr($word, $len - 1, 1) == '=') {
1612
                            $len--;
1613
                        } elseif (substr($word, $len - 2, 1) == '=') {
1614
                            $len -= 2;
1615
                        }
1616
                        $part = substr($word, 0, $len);
1617
                        $word = substr($word, $len);
1618
1619
                        if (strlen($word) > 0) {
1620
                            $message .= $part.sprintf('=%s', self::CRLF);
1621
                        } else {
1622
                            $buf = $part;
1623
                        }
1624
                    }
1625
                } else {
1626
                    $buf_o = $buf;
1627
                    $buf .= ($e == 0) ? $word : (' '.$word);
1628
1629
                    if (strlen($buf) > $length and $buf_o != '') {
1630
                        $message .= $buf_o.$soft_break;
1631
                        $buf = $word;
1632
                    }
1633
                }
1634
            }
1635
            $message .= $buf.self::CRLF;
1636
        }
1637
1638
        return $message;
1639
    }
1640
1641
    /**
1642
     * Find the last character boundary prior to $maxLength in a utf-8
1643
     * quoted (printable) encoded string.
1644
     * Original written by Colin Brown.
1645
     *
1646
     * @param string $encodedText utf-8 QP text
1647
     * @param int    $maxLength   find last character boundary prior to this length
1648
     *
1649
     * @return int
1650
     */
1651
    public function utf8CharBoundary($encodedText, $maxLength)
1652
    {
1653
        $foundSplitPos = false;
1654
        $lookBack = 3;
1655
        while (!$foundSplitPos) {
1656
            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1657
            $encodedCharPos = strpos($lastChunk, '=');
1658
            if ($encodedCharPos !== false) {
1659
                // Found start of encoded character byte within $lookBack block.
1660
                // Check the encoded byte value (the 2 chars after the '=')
1661
                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1662
                $dec = hexdec($hex);
1663
                if ($dec < 128) { // Single byte character.
1664
                    // If the encoded char was found at pos 0, it will fit
1665
                    // otherwise reduce maxLength to start of the encoded char
1666
                    $maxLength = ($encodedCharPos == 0) ? $maxLength :
1667
                        $maxLength - ($lookBack - $encodedCharPos);
1668
                    $foundSplitPos = true;
1669
                } elseif ($dec >= 192) { // First byte of a multi byte character
1670
                    // Reduce maxLength to split at start of character
1671
                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1672
                    $foundSplitPos = true;
1673
                } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
1674
                    $lookBack += 3;
1675
                }
1676
            } else {
1677
                // No encoded character found
1678
                $foundSplitPos = true;
1679
            }
1680
        }
1681
1682
        return $maxLength;
1683
    }
1684
1685
    /**
1686
     * Set the body wrapping.
1687
     *
1688
     * @return void
1689
     */
1690
    public function setWordWrap()
1691
    {
1692
        if ($this->WordWrap < 1) {
1693
            return;
1694
        }
1695
1696
        switch ($this->message_type) {
1697
            case 'alt':
1698
            case 'alt_inline':
1699
            case 'alt_attach':
1700
            case 'alt_inline_attach':
1701
                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1702
                break;
1703
            default:
1704
                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1705
                break;
1706
        }
1707
    }
1708
1709
    /**
1710
     * Assemble message headers.
1711
     *
1712
     * @return string The assembled headers
1713
     */
1714
    public function createHeader()
1715
    {
1716
        $result = '';
1717
1718
        // Set the boundaries
1719
        $uniq_id = md5(uniqid(time()));
1720
        $this->boundary[1] = 'b1_'.$uniq_id;
1721
        $this->boundary[2] = 'b2_'.$uniq_id;
1722
        $this->boundary[3] = 'b3_'.$uniq_id;
1723
1724
        if ($this->MessageDate == '') {
1725
            $result .= $this->headerLine('Date', self::rfcDate());
1726
        } else {
1727
            $result .= $this->headerLine('Date', $this->MessageDate);
1728
        }
1729
1730
        if ($this->ReturnPath) {
1731
            $result .= $this->headerLine('Return-Path', '<'.trim($this->ReturnPath).'>');
1732
        } elseif ($this->Sender == '') {
1733
            $result .= $this->headerLine('Return-Path', '<'.trim($this->From).'>');
1734
        } else {
1735
            $result .= $this->headerLine('Return-Path', '<'.trim($this->Sender).'>');
1736
        }
1737
1738
        // To be created automatically by mail()
1739
        if ($this->Mailer != 'mail') {
1740
            if ($this->SingleTo === true) {
1741
                foreach ($this->to as $t) {
1742
                    $this->SingleToArray[] = $this->addrFormat($t);
1743
                }
1744
            } else {
1745
                if (count($this->to) > 0) {
1746
                    $result .= $this->addrAppend('To', $this->to);
1747
                } elseif (count($this->cc) == 0) {
1748
                    $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1749
                }
1750
            }
1751
        }
1752
1753
        $result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]);
1754
1755
        // sendmail and mail() extract Cc from the header before sending
1756
        if (count($this->cc) > 0) {
1757
            $result .= $this->addrAppend('Cc', $this->cc);
1758
        }
1759
1760
        // sendmail and mail() extract Bcc from the header before sending
1761
        if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
1762
            $result .= $this->addrAppend('Bcc', $this->bcc);
1763
        }
1764
1765
        if (count($this->ReplyTo) > 0) {
1766
            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1767
        }
1768
1769
        // mail() sets the subject itself
1770
        if ($this->Mailer != 'mail') {
1771
            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1772
        }
1773
1774
        if ($this->MessageID != '') {
1775
            $this->lastMessageID = $this->MessageID;
1776
        } else {
1777
            $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());
1778
        }
1779
        $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
1780
        $result .= $this->headerLine('X-Priority', $this->Priority);
1781
        if ($this->XMailer == '') {
1782
            $result .= $this->headerLine(
1783
                'X-Mailer',
1784
                'PHPMailer '.$this->Version.' (https://github.com/PHPMailer/PHPMailer/)'
1785
            );
1786
        } else {
1787
            $myXmailer = trim($this->XMailer);
1788
            if ($myXmailer) {
1789
                $result .= $this->headerLine('X-Mailer', $myXmailer);
1790
            }
1791
        }
1792
1793
        if ($this->ConfirmReadingTo != '') {
1794
            $result .= $this->headerLine('Disposition-Notification-To', '<'.trim($this->ConfirmReadingTo).'>');
1795
        }
1796
1797
        // Add custom headers
1798
        for ($index = 0; $index < count($this->CustomHeader); $index++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1799
            $result .= $this->headerLine(
1800
                trim($this->CustomHeader[$index][0]),
1801
                $this->encodeHeader(trim($this->CustomHeader[$index][1]))
1802
            );
1803
        }
1804
        if (!$this->sign_key_file) {
1805
            $result .= $this->headerLine('MIME-Version', '1.0');
1806
            $result .= $this->getMailMIME();
1807
        }
1808
1809
        return $result;
1810
    }
1811
1812
    /**
1813
     * Get the message MIME type headers.
1814
     *
1815
     * @return string
1816
     */
1817
    public function getMailMIME()
1818
    {
1819
        $result = '';
1820
        switch ($this->message_type) {
1821 View Code Duplication
            case 'inline':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1822
                $result .= $this->headerLine('Content-Type', 'multipart/related;');
1823
                $result .= $this->textLine("\tboundary=\"".$this->boundary[1].'"');
1824
                break;
1825
            case 'attach':
1826
            case 'inline_attach':
1827
            case 'alt_attach':
1828 View Code Duplication
            case 'alt_inline_attach':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1829
                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
1830
                $result .= $this->textLine("\tboundary=\"".$this->boundary[1].'"');
1831
                break;
1832
            case 'alt':
1833 View Code Duplication
            case 'alt_inline':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1834
                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
1835
                $result .= $this->textLine("\tboundary=\"".$this->boundary[1].'"');
1836
                break;
1837
            default:
1838
                // Catches case 'plain': and case '':
1839
                $result .= $this->textLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet);
1840
                break;
1841
        }
1842
        //RFC1341 part 5 says 7bit is assumed if not specified
1843
        if ($this->Encoding != '7bit') {
1844
            $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
1845
        }
1846
1847
        if ($this->Mailer != 'mail') {
1848
            $result .= $this->LE;
1849
        }
1850
1851
        return $result;
1852
    }
1853
1854
    /**
1855
     * Returns the whole MIME message.
1856
     * Includes complete headers and body.
1857
     * Only valid post PreSend().
1858
     *
1859
     * @see    PHPMailer::PreSend()
1860
     *
1861
     * @return string
1862
     */
1863
    public function getSentMIMEMessage()
1864
    {
1865
        return $this->MIMEHeader.$this->mailHeader.self::CRLF.$this->MIMEBody;
1866
    }
1867
1868
    /**
1869
     * Assemble the message body.
1870
     * Returns an empty string on failure.
1871
     *
1872
     * @throws phpmailerException
1873
     *
1874
     * @return string The assembled message body
1875
     */
1876
    public function createBody()
1877
    {
1878
        $body = '';
1879
1880
        if ($this->sign_key_file) {
1881
            $body .= $this->getMailMIME().$this->LE;
1882
        }
1883
1884
        $this->setWordWrap();
1885
1886
        switch ($this->message_type) {
1887 View Code Duplication
            case 'inline':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1888
                $body .= $this->getBoundary($this->boundary[1], '', '', '');
1889
                $body .= $this->encodeString($this->Body, $this->Encoding);
1890
                $body .= $this->LE.$this->LE;
1891
                $body .= $this->attachAll('inline', $this->boundary[1]);
1892
                break;
1893 View Code Duplication
            case 'attach':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1894
                $body .= $this->getBoundary($this->boundary[1], '', '', '');
1895
                $body .= $this->encodeString($this->Body, $this->Encoding);
1896
                $body .= $this->LE.$this->LE;
1897
                $body .= $this->attachAll('attachment', $this->boundary[1]);
1898
                break;
1899
            case 'inline_attach':
1900
                $body .= $this->textLine('--'.$this->boundary[1]);
1901
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
1902
                $body .= $this->textLine("\tboundary=\"".$this->boundary[2].'"');
1903
                $body .= $this->LE;
1904
                $body .= $this->getBoundary($this->boundary[2], '', '', '');
1905
                $body .= $this->encodeString($this->Body, $this->Encoding);
1906
                $body .= $this->LE.$this->LE;
1907
                $body .= $this->attachAll('inline', $this->boundary[2]);
1908
                $body .= $this->LE;
1909
                $body .= $this->attachAll('attachment', $this->boundary[1]);
1910
                break;
1911
            case 'alt':
1912
                $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
1913
                $body .= $this->encodeString($this->AltBody, $this->Encoding);
1914
                $body .= $this->LE.$this->LE;
1915
                $body .= $this->getBoundary($this->boundary[1], '', 'text/html', '');
1916
                $body .= $this->encodeString($this->Body, $this->Encoding);
1917
                $body .= $this->LE.$this->LE;
1918
                if (!empty($this->Ical)) {
1919
                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
1920
                    $body .= $this->encodeString($this->Ical, $this->Encoding);
1921
                    $body .= $this->LE.$this->LE;
1922
                }
1923
                $body .= $this->endBoundary($this->boundary[1]);
1924
                break;
1925 View Code Duplication
            case 'alt_inline':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1926
                $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
1927
                $body .= $this->encodeString($this->AltBody, $this->Encoding);
1928
                $body .= $this->LE.$this->LE;
1929
                $body .= $this->textLine('--'.$this->boundary[1]);
1930
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
1931
                $body .= $this->textLine("\tboundary=\"".$this->boundary[2].'"');
1932
                $body .= $this->LE;
1933
                $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
1934
                $body .= $this->encodeString($this->Body, $this->Encoding);
1935
                $body .= $this->LE.$this->LE;
1936
                $body .= $this->attachAll('inline', $this->boundary[2]);
1937
                $body .= $this->LE;
1938
                $body .= $this->endBoundary($this->boundary[1]);
1939
                break;
1940 View Code Duplication
            case 'alt_attach':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1941
                $body .= $this->textLine('--'.$this->boundary[1]);
1942
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1943
                $body .= $this->textLine("\tboundary=\"".$this->boundary[2].'"');
1944
                $body .= $this->LE;
1945
                $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
1946
                $body .= $this->encodeString($this->AltBody, $this->Encoding);
1947
                $body .= $this->LE.$this->LE;
1948
                $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
1949
                $body .= $this->encodeString($this->Body, $this->Encoding);
1950
                $body .= $this->LE.$this->LE;
1951
                $body .= $this->endBoundary($this->boundary[2]);
1952
                $body .= $this->LE;
1953
                $body .= $this->attachAll('attachment', $this->boundary[1]);
1954
                break;
1955
            case 'alt_inline_attach':
1956
                $body .= $this->textLine('--'.$this->boundary[1]);
1957
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1958
                $body .= $this->textLine("\tboundary=\"".$this->boundary[2].'"');
1959
                $body .= $this->LE;
1960
                $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
1961
                $body .= $this->encodeString($this->AltBody, $this->Encoding);
1962
                $body .= $this->LE.$this->LE;
1963
                $body .= $this->textLine('--'.$this->boundary[2]);
1964
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
1965
                $body .= $this->textLine("\tboundary=\"".$this->boundary[3].'"');
1966
                $body .= $this->LE;
1967
                $body .= $this->getBoundary($this->boundary[3], '', 'text/html', '');
1968
                $body .= $this->encodeString($this->Body, $this->Encoding);
1969
                $body .= $this->LE.$this->LE;
1970
                $body .= $this->attachAll('inline', $this->boundary[3]);
1971
                $body .= $this->LE;
1972
                $body .= $this->endBoundary($this->boundary[2]);
1973
                $body .= $this->LE;
1974
                $body .= $this->attachAll('attachment', $this->boundary[1]);
1975
                break;
1976
            default:
1977
                // catch case 'plain' and case ''
1978
                $body .= $this->encodeString($this->Body, $this->Encoding);
1979
                break;
1980
        }
1981
1982
        if ($this->isError()) {
1983
            $body = '';
1984
        } elseif ($this->sign_key_file) {
1985
            try {
1986
                if (!defined('PKCS7_TEXT')) {
1987
                    throw new phpmailerException($this->lang('signing').' OpenSSL extension missing.');
1988
                }
1989
                $file = tempnam(sys_get_temp_dir(), 'mail');
1990
                file_put_contents($file, $body); //TODO check this worked
1991
                $signed = tempnam(sys_get_temp_dir(), 'signed');
1992
                if (@openssl_pkcs7_sign(
1993
                    $file,
1994
                    $signed,
1995
                    'file://'.realpath($this->sign_cert_file),
1996
                    ['file://'.realpath($this->sign_key_file), $this->sign_key_pass],
1997
                    null
1998
                )
1999
                ) {
2000
                    @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...
2001
                    $body = file_get_contents($signed);
2002
                    @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...
2003
                } else {
2004
                    @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...
2005
                    @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...
2006
                    throw new phpmailerException($this->lang('signing').openssl_error_string());
2007
                }
2008
            } catch (phpmailerException $e) {
2009
                $body = '';
2010
                if ($this->exceptions) {
2011
                    throw $e;
2012
                }
2013
            }
2014
        }
2015
2016
        return $body;
2017
    }
2018
2019
    /**
2020
     * Return the start of a message boundary.
2021
     *
2022
     * @param string $boundary
2023
     * @param string $charSet
2024
     * @param string $contentType
2025
     * @param string $encoding
2026
     *
2027
     * @return string
2028
     */
2029
    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2030
    {
2031
        $result = '';
2032
        if ($charSet == '') {
2033
            $charSet = $this->CharSet;
2034
        }
2035
        if ($contentType == '') {
2036
            $contentType = $this->ContentType;
2037
        }
2038
        if ($encoding == '') {
2039
            $encoding = $this->Encoding;
2040
        }
2041
        $result .= $this->textLine('--'.$boundary);
2042
        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2043
        $result .= $this->LE;
2044
        $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2045
        $result .= $this->LE;
2046
2047
        return $result;
2048
    }
2049
2050
    /**
2051
     * Return the end of a message boundary.
2052
     *
2053
     * @param string $boundary
2054
     *
2055
     * @return string
2056
     */
2057
    protected function endBoundary($boundary)
2058
    {
2059
        return $this->LE.'--'.$boundary.'--'.$this->LE;
2060
    }
2061
2062
    /**
2063
     * Set the message type.
2064
     * PHPMailer only supports some preset message types,
2065
     * not arbitrary MIME structures.
2066
     *
2067
     * @return void
2068
     */
2069
    protected function setMessageType()
2070
    {
2071
        $this->message_type = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $message_type.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2072
        if ($this->alternativeExists()) {
2073
            $this->message_type[] = 'alt';
2074
        }
2075
        if ($this->inlineImageExists()) {
2076
            $this->message_type[] = 'inline';
2077
        }
2078
        if ($this->attachmentExists()) {
2079
            $this->message_type[] = 'attach';
2080
        }
2081
        $this->message_type = implode('_', $this->message_type);
2082
        if ($this->message_type == '') {
2083
            $this->message_type = 'plain';
2084
        }
2085
    }
2086
2087
    /**
2088
     * Format a header line.
2089
     *
2090
     * @param string $name
2091
     * @param string $value
2092
     *
2093
     * @return string
2094
     */
2095
    public function headerLine($name, $value)
2096
    {
2097
        return $name.': '.$value.$this->LE;
2098
    }
2099
2100
    /**
2101
     * Return a formatted mail line.
2102
     *
2103
     * @param string $value
2104
     *
2105
     * @return string
2106
     */
2107
    public function textLine($value)
2108
    {
2109
        return $value.$this->LE;
2110
    }
2111
2112
    /**
2113
     * Add an attachment from a path on the filesystem.
2114
     * Returns false if the file could not be found or read.
2115
     *
2116
     * @param string $path        Path to the attachment.
2117
     * @param string $name        Overrides the attachment name.
2118
     * @param string $encoding    File encoding (see $Encoding).
2119
     * @param string $type        File extension (MIME) type.
2120
     * @param string $disposition Disposition to use
2121
     *
2122
     * @throws phpmailerException
2123
     *
2124
     * @return bool
2125
     */
2126
    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2127
    {
2128
        try {
2129
            if (!@is_file($path)) {
2130
                throw new phpmailerException($this->lang('file_access').$path, self::STOP_CONTINUE);
2131
            }
2132
2133
            //If a MIME type is not specified, try to work it out from the file name
2134
            if ($type == '') {
2135
                $type = self::filenameToType($path);
2136
            }
2137
2138
            $filename = basename($path);
2139
            if ($name == '') {
2140
                $name = $filename;
2141
            }
2142
2143
            $this->attachment[] = [
2144
                0 => $path,
2145
                1 => $filename,
2146
                2 => $name,
2147
                3 => $encoding,
2148
                4 => $type,
2149
                5 => false, // isStringAttachment
2150
                6 => $disposition,
2151
                7 => 0,
2152
            ];
2153
        } catch (phpmailerException $e) {
2154
            $this->setError($e->getMessage());
2155
            if ($this->exceptions) {
2156
                throw $e;
2157
            }
2158
            $this->edebug($e->getMessage()."\n");
2159
2160
            return false;
2161
        }
2162
2163
        return true;
2164
    }
2165
2166
    /**
2167
     * Return the array of attachments.
2168
     *
2169
     * @return array
2170
     */
2171
    public function getAttachments()
2172
    {
2173
        return $this->attachment;
2174
    }
2175
2176
    /**
2177
     * Attach all file, string, and binary attachments to the message.
2178
     * Returns an empty string on failure.
2179
     *
2180
     * @param string $disposition_type
2181
     * @param string $boundary
2182
     *
2183
     * @return string
2184
     */
2185
    protected function attachAll($disposition_type, $boundary)
2186
    {
2187
        // Return text of body
2188
        $mime = [];
2189
        $cidUniq = [];
2190
        $incl = [];
2191
2192
        // Add all attachments
2193
        foreach ($this->attachment as $attachment) {
2194
            // Check if it is a valid disposition_filter
2195
            if ($attachment[6] == $disposition_type) {
2196
                // Check for string attachment
2197
                $string = '';
2198
                $path = '';
2199
                $bString = $attachment[5];
2200
                if ($bString) {
2201
                    $string = $attachment[0];
2202
                } else {
2203
                    $path = $attachment[0];
2204
                }
2205
2206
                $inclhash = md5(serialize($attachment));
2207
                if (in_array($inclhash, $incl)) {
2208
                    continue;
2209
                }
2210
                $incl[] = $inclhash;
2211
                $name = $attachment[2];
2212
                $encoding = $attachment[3];
2213
                $type = $attachment[4];
2214
                $disposition = $attachment[6];
2215
                $cid = $attachment[7];
2216
                if ($disposition == 'inline' && isset($cidUniq[$cid])) {
2217
                    continue;
2218
                }
2219
                $cidUniq[$cid] = true;
2220
2221
                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2222
                $mime[] = sprintf(
2223
                    'Content-Type: %s; name="%s"%s',
2224
                    $type,
2225
                    $this->encodeHeader($this->secureHeader($name)),
2226
                    $this->LE
2227
                );
2228
                $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2229
2230
                if ($disposition == 'inline') {
2231
                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2232
                }
2233
2234
                // If a filename contains any of these chars, it should be quoted,
2235
                // but not otherwise: RFC2183 & RFC2045 5.1
2236
                // Fixes a warning in IETF's msglint MIME checker
2237
                // Allow for bypassing the Content-Disposition header totally
2238
                if (!(empty($disposition))) {
2239
                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
2240
                        $mime[] = sprintf(
2241
                            'Content-Disposition: %s; filename="%s"%s',
2242
                            $disposition,
2243
                            $this->encodeHeader($this->secureHeader($name)),
2244
                            $this->LE.$this->LE
2245
                        );
2246
                    } else {
2247
                        $mime[] = sprintf(
2248
                            'Content-Disposition: %s; filename=%s%s',
2249
                            $disposition,
2250
                            $this->encodeHeader($this->secureHeader($name)),
2251
                            $this->LE.$this->LE
2252
                        );
2253
                    }
2254
                } else {
2255
                    $mime[] = $this->LE;
2256
                }
2257
2258
                // Encode as string attachment
2259
                if ($bString) {
2260
                    $mime[] = $this->encodeString($string, $encoding);
2261
                    if ($this->isError()) {
2262
                        return '';
2263
                    }
2264
                    $mime[] = $this->LE.$this->LE;
2265
                } else {
2266
                    $mime[] = $this->encodeFile($path, $encoding);
2267
                    if ($this->isError()) {
2268
                        return '';
2269
                    }
2270
                    $mime[] = $this->LE.$this->LE;
2271
                }
2272
            }
2273
        }
2274
2275
        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2276
2277
        return implode('', $mime);
2278
    }
2279
2280
    /**
2281
     * Encode a file attachment in requested format.
2282
     * Returns an empty string on failure.
2283
     *
2284
     * @param string $path     The full path to the file
2285
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2286
     *
2287
     * @throws phpmailerException
2288
     *
2289
     * @see    EncodeFile(encodeFile
2290
     *
2291
     * @return string
2292
     */
2293
    protected function encodeFile($path, $encoding = 'base64')
2294
    {
2295
        try {
2296
            if (!is_readable($path)) {
2297
                throw new phpmailerException($this->lang('file_open').$path, self::STOP_CONTINUE);
2298
            }
2299
            $magic_quotes = get_magic_quotes_runtime();
2300 View Code Duplication
            if ($magic_quotes) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2301
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2302
                    set_magic_quotes_runtime(0);
2303
                } else {
2304
                    ini_set('magic_quotes_runtime', 0);
2305
                }
2306
            }
2307
            $file_buffer = file_get_contents($path);
2308
            $file_buffer = $this->encodeString($file_buffer, $encoding);
2309 View Code Duplication
            if ($magic_quotes) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2310
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2311
                    set_magic_quotes_runtime($magic_quotes);
2312
                } else {
2313
                    ini_set('magic_quotes_runtime', $magic_quotes);
2314
                }
2315
            }
2316
2317
            return $file_buffer;
2318
        } catch (Exception $e) {
2319
            $this->setError($e->getMessage());
2320
2321
            return '';
2322
        }
2323
    }
2324
2325
    /**
2326
     * Encode a string in requested format.
2327
     * Returns an empty string on failure.
2328
     *
2329
     * @param string $str      The text to encode
2330
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2331
     *
2332
     * @return string
2333
     */
2334
    public function encodeString($str, $encoding = 'base64')
2335
    {
2336
        $encoded = '';
2337
        switch (strtolower($encoding)) {
2338
            case 'base64':
2339
                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2340
                break;
2341
            case '7bit':
2342
            case '8bit':
2343
                $encoded = $this->fixEOL($str);
2344
                //Make sure it ends with a line break
2345
                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2346
                    $encoded .= $this->LE;
2347
                }
2348
                break;
2349
            case 'binary':
2350
                $encoded = $str;
2351
                break;
2352
            case 'quoted-printable':
2353
                $encoded = $this->encodeQP($str);
2354
                break;
2355
            default:
2356
                $this->setError($this->lang('encoding').$encoding);
2357
                break;
2358
        }
2359
2360
        return $encoded;
2361
    }
2362
2363
    /**
2364
     * Encode a header string optimally.
2365
     * Picks shortest of Q, B, quoted-printable or none.
2366
     *
2367
     * @param string $str
2368
     * @param string $position
2369
     *
2370
     * @return string
2371
     */
2372
    public function encodeHeader($str, $position = 'text')
2373
    {
2374
        $x = 0;
2375
        switch (strtolower($position)) {
2376
            case 'phrase':
2377
                if (!preg_match('/[\200-\377]/', $str)) {
2378
                    // Can't use addslashes as we don't know what value has magic_quotes_sybase
2379
                    $encoded = addcslashes($str, "\0..\37\177\\\"");
2380
                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2381
                        return $encoded;
2382
                    } else {
2383
                        return "\"$encoded\"";
2384
                    }
2385
                }
2386
                $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2387
                break;
2388
            /* @noinspection PhpMissingBreakStatementInspection */
2389
            case 'comment':
2390
                $x = preg_match_all('/[()"]/', $str, $matches);
2391
            // Intentional fall-through
2392
            case 'text':
2393
            default:
2394
                $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2395
                break;
2396
        }
2397
2398
        if ($x == 0) { //There are no chars that need encoding
2399
            return $str;
2400
        }
2401
2402
        $maxlen = 75 - 7 - strlen($this->CharSet);
2403
        // Try to select the encoding which should produce the shortest output
2404
        if ($x > strlen($str) / 3) {
2405
            //More than a third of the content will need encoding, so B encoding will be most efficient
2406
            $encoding = 'B';
2407
            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2408
                // Use a custom function which correctly encodes and wraps long
2409
                // multibyte strings without breaking lines within a character
2410
                $encoded = $this->base64EncodeWrapMB($str, "\n");
2411
            } else {
2412
                $encoded = base64_encode($str);
2413
                $maxlen -= $maxlen % 4;
2414
                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2415
            }
2416
        } else {
2417
            $encoding = 'Q';
2418
            $encoded = $this->encodeQ($str, $position);
2419
            $encoded = $this->wrapText($encoded, $maxlen, true);
2420
            $encoded = str_replace('='.self::CRLF, "\n", trim($encoded));
2421
        }
2422
2423
        $encoded = preg_replace('/^(.*)$/m', ' =?'.$this->CharSet."?$encoding?\\1?=", $encoded);
2424
        $encoded = trim(str_replace("\n", $this->LE, $encoded));
2425
2426
        return $encoded;
2427
    }
2428
2429
    /**
2430
     * Check if a string contains multi-byte characters.
2431
     *
2432
     * @param string $str multi-byte text to wrap encode
2433
     *
2434
     * @return bool
2435
     */
2436
    public function hasMultiBytes($str)
2437
    {
2438
        if (function_exists('mb_strlen')) {
2439
            return strlen($str) > mb_strlen($str, $this->CharSet);
2440
        } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2441
            return false;
2442
        }
2443
    }
2444
2445
    /**
2446
     * Encode and wrap long multibyte strings for mail headers
2447
     * without breaking lines within a character.
2448
     * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php.
2449
     *
2450
     * @param string $str multi-byte text to wrap encode
2451
     * @param string $lf  string to use as linefeed/end-of-line
2452
     *
2453
     * @return string
2454
     */
2455
    public function base64EncodeWrapMB($str, $lf = null)
2456
    {
2457
        $start = '=?'.$this->CharSet.'?B?';
2458
        $end = '?=';
2459
        $encoded = '';
2460
        if ($lf === null) {
2461
            $lf = $this->LE;
2462
        }
2463
2464
        $mb_length = mb_strlen($str, $this->CharSet);
2465
        // Each line must have length <= 75, including $start and $end
2466
        $length = 75 - strlen($start) - strlen($end);
2467
        // Average multi-byte ratio
2468
        $ratio = $mb_length / strlen($str);
2469
        // Base64 has a 4:3 ratio
2470
        $avgLength = floor($length * $ratio * .75);
2471
2472
        for ($i = 0; $i < $mb_length; $i += $offset) {
2473
            $lookBack = 0;
2474
            do {
2475
                $offset = $avgLength - $lookBack;
2476
                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2477
                $chunk = base64_encode($chunk);
2478
                $lookBack++;
2479
            } while (strlen($chunk) > $length);
2480
            $encoded .= $chunk.$lf;
2481
        }
2482
2483
        // Chomp the last linefeed
2484
        $encoded = substr($encoded, 0, -strlen($lf));
2485
2486
        return $encoded;
2487
    }
2488
2489
    /**
2490
     * Encode a string in quoted-printable format.
2491
     * According to RFC2045 section 6.7.
2492
     *
2493
     * @param string $string   The text to encode
2494
     * @param int    $line_max Number of chars allowed on a line before wrapping
2495
     *
2496
     * @return string
2497
     *
2498
     * @link   PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417
2499
     */
2500
    public function encodeQP($string, $line_max = 76)
2501
    {
2502
        if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
2503
            return quoted_printable_encode($string);
2504
        }
2505
        //Fall back to a pure PHP implementation
2506
        $string = str_replace(
2507
            ['%20', '%0D%0A.', '%0D%0A', '%'],
2508
            [' ', "\r\n=2E", "\r\n", '='],
2509
            rawurlencode($string)
2510
        );
2511
        $string = preg_replace('/[^\r\n]{'.($line_max - 3).'}[^=\r\n]{2}/', "$0=\r\n", $string);
2512
2513
        return $string;
2514
    }
2515
2516
    /**
2517
     * Backward compatibility wrapper for an old QP encoding function that was removed.
2518
     *
2519
     * @see        PHPMailer::encodeQP()
2520
     *
2521
     * @param string $string
2522
     * @param int    $line_max
2523
     * @param bool   $space_conv
2524
     *
2525
     * @return string
2526
     *
2527
     * @deprecated Use encodeQP instead.
2528
     */
2529
    public function encodeQPphp(
2530
        $string,
2531
        $line_max = 76,
2532
        /* @noinspection PhpUnusedParameterInspection */
2533
        $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...
2534
    ) {
2535
        return $this->encodeQP($string, $line_max);
2536
    }
2537
2538
    /**
2539
     * Encode a string using Q encoding.
2540
     *
2541
     * @link   http://tools.ietf.org/html/rfc2047
2542
     *
2543
     * @param string $str      the text to encode
2544
     * @param string $position Where the text is going to be used, see the RFC for what that means
2545
     *
2546
     * @return string
2547
     */
2548
    public function encodeQ($str, $position = 'text')
2549
    {
2550
        //There should not be any EOL in the string
2551
        $pattern = '';
2552
        $encoded = str_replace(["\r", "\n"], '', $str);
2553
        switch (strtolower($position)) {
2554
            case 'phrase':
2555
                //RFC 2047 section 5.3
2556
                $pattern = '^A-Za-z0-9!*+\/ -';
2557
                break;
2558
            /* @noinspection PhpMissingBreakStatementInspection */
2559
            case 'comment':
2560
                //RFC 2047 section 5.2
2561
                $pattern = '\(\)"';
2562
            //intentional fall-through
2563
            //for this reason we build the $pattern without including delimiters and []
2564
            case 'text':
2565
            default:
2566
                //RFC 2047 section 5.1
2567
                //Replace every high ascii, control, =, ? and _ characters
2568
                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377'.$pattern;
2569
                break;
2570
        }
2571
        $matches = [];
2572
        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2573
            //If the string contains an '=', make sure it's the first thing we replace
2574
            //so as to avoid double-encoding
2575
            $s = array_search('=', $matches[0]);
2576
            if ($s !== false) {
2577
                unset($matches[0][$s]);
2578
                array_unshift($matches[0], '=');
2579
            }
2580
            foreach (array_unique($matches[0]) as $char) {
2581
                $encoded = str_replace($char, '='.sprintf('%02X', ord($char)), $encoded);
2582
            }
2583
        }
2584
2585
        //Replace every spaces to _ (more readable than =20)
2586
        return str_replace(' ', '_', $encoded);
2587
    }
2588
2589
    /**
2590
     * Add a string or binary attachment (non-filesystem).
2591
     * This method can be used to attach ascii or binary data,
2592
     * such as a BLOB record from a database.
2593
     *
2594
     * @param string $string      String attachment data.
2595
     * @param string $filename    Name of the attachment.
2596
     * @param string $encoding    File encoding (see $Encoding).
2597
     * @param string $type        File extension (MIME) type.
2598
     * @param string $disposition Disposition to use
2599
     *
2600
     * @return void
2601
     */
2602 View Code Duplication
    public function addStringAttachment(
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...
2603
        $string,
2604
        $filename,
2605
        $encoding = 'base64',
2606
        $type = '',
2607
        $disposition = 'attachment'
2608
    ) {
2609
        //If a MIME type is not specified, try to work it out from the file name
2610
        if ($type == '') {
2611
            $type = self::filenameToType($filename);
2612
        }
2613
        // Append to $attachment array
2614
        $this->attachment[] = [
2615
            0 => $string,
2616
            1 => $filename,
2617
            2 => basename($filename),
2618
            3 => $encoding,
2619
            4 => $type,
2620
            5 => true, // isStringAttachment
2621
            6 => $disposition,
2622
            7 => 0,
2623
        ];
2624
    }
2625
2626
    /**
2627
     * Add an embedded (inline) attachment from a file.
2628
     * This can include images, sounds, and just about any other document type.
2629
     * These differ from 'regular' attachmants in that they are intended to be
2630
     * displayed inline with the message, not just attached for download.
2631
     * This is used in HTML messages that embed the images
2632
     * the HTML refers to using the $cid value.
2633
     *
2634
     * @param string $path        Path to the attachment.
2635
     * @param string $cid         Content ID of the attachment; Use this to reference
2636
     *                            the content when using an embedded image in HTML.
2637
     * @param string $name        Overrides the attachment name.
2638
     * @param string $encoding    File encoding (see $Encoding).
2639
     * @param string $type        File MIME type.
2640
     * @param string $disposition Disposition to use
2641
     *
2642
     * @return bool True on successfully adding an attachment
2643
     */
2644
    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2645
    {
2646
        if (!@is_file($path)) {
2647
            $this->setError($this->lang('file_access').$path);
2648
2649
            return false;
2650
        }
2651
2652
        //If a MIME type is not specified, try to work it out from the file name
2653
        if ($type == '') {
2654
            $type = self::filenameToType($path);
2655
        }
2656
2657
        $filename = basename($path);
2658
        if ($name == '') {
2659
            $name = $filename;
2660
        }
2661
2662
        // Append to $attachment array
2663
        $this->attachment[] = [
2664
            0 => $path,
2665
            1 => $filename,
2666
            2 => $name,
2667
            3 => $encoding,
2668
            4 => $type,
2669
            5 => false, // isStringAttachment
2670
            6 => $disposition,
2671
            7 => $cid,
2672
        ];
2673
2674
        return true;
2675
    }
2676
2677
    /**
2678
     * Add an embedded stringified attachment.
2679
     * This can include images, sounds, and just about any other document type.
2680
     * Be sure to set the $type to an image type for images:
2681
     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2682
     *
2683
     * @param string $string      The attachment binary data.
2684
     * @param string $cid         Content ID of the attachment; Use this to reference
2685
     *                            the content when using an embedded image in HTML.
2686
     * @param string $name
2687
     * @param string $encoding    File encoding (see $Encoding).
2688
     * @param string $type        MIME type.
2689
     * @param string $disposition Disposition to use
2690
     *
2691
     * @return bool True on successfully adding an attachment
2692
     */
2693 View Code Duplication
    public function addStringEmbeddedImage(
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...
2694
        $string,
2695
        $cid,
2696
        $name = '',
2697
        $encoding = 'base64',
2698
        $type = '',
2699
        $disposition = 'inline'
2700
    ) {
2701
        //If a MIME type is not specified, try to work it out from the name
2702
        if ($type == '') {
2703
            $type = self::filenameToType($name);
2704
        }
2705
2706
        // Append to $attachment array
2707
        $this->attachment[] = [
2708
            0 => $string,
2709
            1 => $name,
2710
            2 => $name,
2711
            3 => $encoding,
2712
            4 => $type,
2713
            5 => true, // isStringAttachment
2714
            6 => $disposition,
2715
            7 => $cid,
2716
        ];
2717
2718
        return true;
2719
    }
2720
2721
    /**
2722
     * Check if an inline attachment is present.
2723
     *
2724
     * @return bool
2725
     */
2726
    public function inlineImageExists()
2727
    {
2728
        foreach ($this->attachment as $attachment) {
2729
            if ($attachment[6] == 'inline') {
2730
                return true;
2731
            }
2732
        }
2733
2734
        return false;
2735
    }
2736
2737
    /**
2738
     * Check if an attachment (non-inline) is present.
2739
     *
2740
     * @return bool
2741
     */
2742
    public function attachmentExists()
2743
    {
2744
        foreach ($this->attachment as $attachment) {
2745
            if ($attachment[6] == 'attachment') {
2746
                return true;
2747
            }
2748
        }
2749
2750
        return false;
2751
    }
2752
2753
    /**
2754
     * Check if this message has an alternative body set.
2755
     *
2756
     * @return bool
2757
     */
2758
    public function alternativeExists()
2759
    {
2760
        return !empty($this->AltBody);
2761
    }
2762
2763
    /**
2764
     * Clear all To recipients.
2765
     *
2766
     * @return void
2767
     */
2768
    public function clearAddresses()
2769
    {
2770
        foreach ($this->to as $to) {
2771
            unset($this->all_recipients[strtolower($to[0])]);
2772
        }
2773
        $this->to = [];
2774
    }
2775
2776
    /**
2777
     * Clear all CC recipients.
2778
     *
2779
     * @return void
2780
     */
2781 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...
2782
    {
2783
        foreach ($this->cc as $cc) {
2784
            unset($this->all_recipients[strtolower($cc[0])]);
2785
        }
2786
        $this->cc = [];
2787
    }
2788
2789
    /**
2790
     * Clear all BCC recipients.
2791
     *
2792
     * @return void
2793
     */
2794 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...
2795
    {
2796
        foreach ($this->bcc as $bcc) {
2797
            unset($this->all_recipients[strtolower($bcc[0])]);
2798
        }
2799
        $this->bcc = [];
2800
    }
2801
2802
    /**
2803
     * Clear all ReplyTo recipients.
2804
     *
2805
     * @return void
2806
     */
2807
    public function clearReplyTos()
2808
    {
2809
        $this->ReplyTo = [];
2810
    }
2811
2812
    /**
2813
     * Clear all recipient types.
2814
     *
2815
     * @return void
2816
     */
2817
    public function clearAllRecipients()
2818
    {
2819
        $this->to = [];
2820
        $this->cc = [];
2821
        $this->bcc = [];
2822
        $this->all_recipients = [];
2823
    }
2824
2825
    /**
2826
     * Clear all filesystem, string, and binary attachments.
2827
     *
2828
     * @return void
2829
     */
2830
    public function clearAttachments()
2831
    {
2832
        $this->attachment = [];
2833
    }
2834
2835
    /**
2836
     * Clear all custom headers.
2837
     *
2838
     * @return void
2839
     */
2840
    public function clearCustomHeaders()
2841
    {
2842
        $this->CustomHeader = [];
2843
    }
2844
2845
    /**
2846
     * Add an error message to the error container.
2847
     *
2848
     * @param string $msg
2849
     *
2850
     * @return void
2851
     */
2852
    protected function setError($msg)
2853
    {
2854
        $this->error_count++;
2855
        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2856
            $lasterror = $this->smtp->getError();
2857
            if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
2858
                $msg .= '<p>'.$this->lang('smtp_error').$lasterror['smtp_msg']."</p>\n";
2859
            }
2860
        }
2861
        $this->ErrorInfo = $msg;
2862
    }
2863
2864
    /**
2865
     * Return an RFC 822 formatted date.
2866
     *
2867
     * @return string
2868
     * @static
2869
     */
2870
    public static function rfcDate()
2871
    {
2872
        //Set the time zone to whatever the default is to avoid 500 errors
2873
        //Will default to UTC if it's not set properly in php.ini
2874
        date_default_timezone_set(@date_default_timezone_get());
2875
2876
        return date('D, j M Y H:i:s O');
2877
    }
2878
2879
    /**
2880
     * Get the server hostname.
2881
     * Returns 'localhost.localdomain' if unknown.
2882
     *
2883
     * @return string
2884
     */
2885
    protected function serverHostname()
2886
    {
2887
        if (!empty($this->Hostname)) {
2888
            $result = $this->Hostname;
2889
        } elseif (isset($_SERVER['SERVER_NAME'])) {
2890
            $result = $_SERVER['SERVER_NAME'];
2891
        } else {
2892
            $result = 'localhost.localdomain';
2893
        }
2894
2895
        return $result;
2896
    }
2897
2898
    /**
2899
     * Get an error message in the current language.
2900
     *
2901
     * @param string $key
2902
     *
2903
     * @return string
2904
     */
2905
    protected function lang($key)
2906
    {
2907
        if (count($this->language) < 1) {
2908
            $this->setLanguage('en'); // set the default language
2909
        }
2910
2911
        if (isset($this->language[$key])) {
2912
            return $this->language[$key];
2913
        } else {
2914
            return 'Language string failed to load: '.$key;
2915
        }
2916
    }
2917
2918
    /**
2919
     * Check if an error occurred.
2920
     *
2921
     * @return bool True if an error did occur.
2922
     */
2923
    public function isError()
2924
    {
2925
        return $this->error_count > 0;
2926
    }
2927
2928
    /**
2929
     * Ensure consistent line endings in a string.
2930
     * Changes every end of line from CRLF, CR or LF to $this->LE.
2931
     *
2932
     * @param string $str String to fixEOL
2933
     *
2934
     * @return string
2935
     */
2936
    public function fixEOL($str)
2937
    {
2938
        // Normalise to \n
2939
        $nstr = str_replace(["\r\n", "\r"], "\n", $str);
2940
        // Now convert LE as needed
2941
        if ($this->LE !== "\n") {
2942
            $nstr = str_replace("\n", $this->LE, $nstr);
2943
        }
2944
2945
        return $nstr;
2946
    }
2947
2948
    /**
2949
     * Add a custom header.
2950
     * $name value can be overloaded to contain
2951
     * both header name and value (name:value).
2952
     *
2953
     * @param string $name  Custom header name
2954
     * @param string $value Header value
2955
     *
2956
     * @return void
2957
     */
2958
    public function addCustomHeader($name, $value = null)
2959
    {
2960
        if ($value === null) {
2961
            // Value passed in as name:value
2962
            $this->CustomHeader[] = explode(':', $name, 2);
2963
        } else {
2964
            $this->CustomHeader[] = [$name, $value];
2965
        }
2966
    }
2967
2968
    /**
2969
     * Create a message from an HTML string.
2970
     * Automatically makes modifications for inline images and backgrounds
2971
     * and creates a plain-text version by converting the HTML.
2972
     * Overwrites any existing values in $this->Body and $this->AltBody.
2973
     *
2974
     * @param string $message  HTML message string
2975
     * @param string $basedir  baseline directory for path
2976
     * @param bool   $advanced Whether to use the advanced HTML to text converter
2977
     *
2978
     * @return string $message
2979
     */
2980
    public function msgHTML($message, $basedir = '', $advanced = false)
2981
    {
2982
        preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);
2983
        if (isset($images[2])) {
2984
            foreach ($images[2] as $i => $url) {
2985
                // do not change urls for absolute images (thanks to corvuscorax)
2986
                if (!preg_match('#^[A-z]+://#', $url)) {
2987
                    $filename = basename($url);
2988
                    $directory = dirname($url);
2989
                    if ($directory == '.') {
2990
                        $directory = '';
2991
                    }
2992
                    $cid = md5($url).'@phpmailer.0'; //RFC2392 S 2
2993
                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
2994
                        $basedir .= '/';
2995
                    }
2996
                    if (strlen($directory) > 1 && substr($directory, -1) != '/') {
2997
                        $directory .= '/';
2998
                    }
2999
                    if ($this->addEmbeddedImage(
3000
                        $basedir.$directory.$filename,
3001
                        $cid,
3002
                        $filename,
3003
                        'base64',
3004
                        self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION))
0 ignored issues
show
Bug introduced by
It seems like self::mb_pathinfo($filename, PATHINFO_EXTENSION) targeting PHPMailer::mb_pathinfo() can also be of type array<string,string>; however, PHPMailer::_mime_types() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
3005
                    )
3006
                    ) {
3007
                        $message = preg_replace(
3008
                            '/'.$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui",
3009
                            $images[1][$i].'="cid:'.$cid.'"',
3010
                            $message
3011
                        );
3012
                    }
3013
                }
3014
            }
3015
        }
3016
        $this->isHTML(true);
3017
        if (empty($this->AltBody)) {
3018
            $this->AltBody = 'To view this email message, open it in a program that understands HTML!'."\n\n";
3019
        }
3020
        //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3021
        $this->Body = $this->normalizeBreaks($message);
3022
        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3023
3024
        return $this->Body;
3025
    }
3026
3027
    /**
3028
     * Convert an HTML string into plain text.
3029
     *
3030
     * @param string $html     The HTML text to convert
3031
     * @param bool   $advanced Should this use the more complex html2text converter or just a simple one?
3032
     *
3033
     * @return string
3034
     */
3035
    public function html2text($html, $advanced = false)
3036
    {
3037
        if ($advanced) {
3038
            require_once 'extras/class.html2text.php';
3039
            $h = new html2text($html);
3040
3041
            return $h->get_text();
3042
        }
3043
3044
        return html_entity_decode(
3045
            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3046
            ENT_QUOTES,
3047
            $this->CharSet
3048
        );
3049
    }
3050
3051
    /**
3052
     * Get the MIME type for a file extension.
3053
     *
3054
     * @param string $ext File extension
3055
     *
3056
     * @return string MIME type of file.
3057
     * @static
3058
     */
3059
    public static function _mime_types($ext = '')
3060
    {
3061
        $mimes = [
3062
            'xl'    => 'application/excel',
3063
            'hqx'   => 'application/mac-binhex40',
3064
            'cpt'   => 'application/mac-compactpro',
3065
            'bin'   => 'application/macbinary',
3066
            'doc'   => 'application/msword',
3067
            'word'  => 'application/msword',
3068
            'class' => 'application/octet-stream',
3069
            'dll'   => 'application/octet-stream',
3070
            'dms'   => 'application/octet-stream',
3071
            'exe'   => 'application/octet-stream',
3072
            'lha'   => 'application/octet-stream',
3073
            'lzh'   => 'application/octet-stream',
3074
            'psd'   => 'application/octet-stream',
3075
            'sea'   => 'application/octet-stream',
3076
            'so'    => 'application/octet-stream',
3077
            'oda'   => 'application/oda',
3078
            'pdf'   => 'application/pdf',
3079
            'ai'    => 'application/postscript',
3080
            'eps'   => 'application/postscript',
3081
            'ps'    => 'application/postscript',
3082
            'smi'   => 'application/smil',
3083
            'smil'  => 'application/smil',
3084
            'mif'   => 'application/vnd.mif',
3085
            'xls'   => 'application/vnd.ms-excel',
3086
            'ppt'   => 'application/vnd.ms-powerpoint',
3087
            'wbxml' => 'application/vnd.wap.wbxml',
3088
            'wmlc'  => 'application/vnd.wap.wmlc',
3089
            'dcr'   => 'application/x-director',
3090
            'dir'   => 'application/x-director',
3091
            'dxr'   => 'application/x-director',
3092
            'dvi'   => 'application/x-dvi',
3093
            'gtar'  => 'application/x-gtar',
3094
            'php3'  => 'application/x-httpd-php',
3095
            'php4'  => 'application/x-httpd-php',
3096
            'php'   => 'application/x-httpd-php',
3097
            'phtml' => 'application/x-httpd-php',
3098
            'phps'  => 'application/x-httpd-php-source',
3099
            'js'    => 'application/x-javascript',
3100
            'swf'   => 'application/x-shockwave-flash',
3101
            'sit'   => 'application/x-stuffit',
3102
            'tar'   => 'application/x-tar',
3103
            'tgz'   => 'application/x-tar',
3104
            'xht'   => 'application/xhtml+xml',
3105
            'xhtml' => 'application/xhtml+xml',
3106
            'zip'   => 'application/zip',
3107
            'mid'   => 'audio/midi',
3108
            'midi'  => 'audio/midi',
3109
            'mp2'   => 'audio/mpeg',
3110
            'mp3'   => 'audio/mpeg',
3111
            'mpga'  => 'audio/mpeg',
3112
            'aif'   => 'audio/x-aiff',
3113
            'aifc'  => 'audio/x-aiff',
3114
            'aiff'  => 'audio/x-aiff',
3115
            'ram'   => 'audio/x-pn-realaudio',
3116
            'rm'    => 'audio/x-pn-realaudio',
3117
            'rpm'   => 'audio/x-pn-realaudio-plugin',
3118
            'ra'    => 'audio/x-realaudio',
3119
            'wav'   => 'audio/x-wav',
3120
            'bmp'   => 'image/bmp',
3121
            'gif'   => 'image/gif',
3122
            'jpeg'  => 'image/jpeg',
3123
            'jpe'   => 'image/jpeg',
3124
            'jpg'   => 'image/jpeg',
3125
            'png'   => 'image/png',
3126
            'tiff'  => 'image/tiff',
3127
            'tif'   => 'image/tiff',
3128
            'eml'   => 'message/rfc822',
3129
            'css'   => 'text/css',
3130
            'html'  => 'text/html',
3131
            'htm'   => 'text/html',
3132
            'shtml' => 'text/html',
3133
            'log'   => 'text/plain',
3134
            'text'  => 'text/plain',
3135
            'txt'   => 'text/plain',
3136
            'rtx'   => 'text/richtext',
3137
            'rtf'   => 'text/rtf',
3138
            'xml'   => 'text/xml',
3139
            'xsl'   => 'text/xml',
3140
            'mpeg'  => 'video/mpeg',
3141
            'mpe'   => 'video/mpeg',
3142
            'mpg'   => 'video/mpeg',
3143
            'mov'   => 'video/quicktime',
3144
            'qt'    => 'video/quicktime',
3145
            'rv'    => 'video/vnd.rn-realvideo',
3146
            'avi'   => 'video/x-msvideo',
3147
            'movie' => 'video/x-sgi-movie',
3148
        ];
3149
3150
        return array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)] : 'application/octet-stream';
3151
    }
3152
3153
    /**
3154
     * Map a file name to a MIME type.
3155
     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3156
     *
3157
     * @param string $filename A file name or full path, does not need to exist as a file
3158
     *
3159
     * @return string
3160
     * @static
3161
     */
3162
    public static function filenameToType($filename)
3163
    {
3164
        //In case the path is a URL, strip any query string before getting extension
3165
        $qpos = strpos($filename, '?');
3166
        if ($qpos !== false) {
3167
            $filename = substr($filename, 0, $qpos);
3168
        }
3169
        $pathinfo = self::mb_pathinfo($filename);
3170
3171
        return self::_mime_types($pathinfo['extension']);
3172
    }
3173
3174
    /**
3175
     * Multi-byte-safe pathinfo replacement.
3176
     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3177
     * Works similarly to the one in PHP >= 5.2.0.
3178
     *
3179
     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3180
     *
3181
     * @param string     $path    A filename or path, does not need to exist as a file
3182
     * @param int|string $options Either a PATHINFO_* constant,
3183
     *                            or a string name to return only the specified piece, allows 'filename' to work on
3184
     *                            PHP < 5.2
3185
     *
3186
     * @return string|array
3187
     * @static
3188
     */
3189
    public static function mb_pathinfo($path, $options = null)
3190
    {
3191
        $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
3192
        $m = [];
3193
        preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
3194
        if (array_key_exists(1, $m)) {
3195
            $ret['dirname'] = $m[1];
3196
        }
3197
        if (array_key_exists(2, $m)) {
3198
            $ret['basename'] = $m[2];
3199
        }
3200
        if (array_key_exists(5, $m)) {
3201
            $ret['extension'] = $m[5];
3202
        }
3203
        if (array_key_exists(3, $m)) {
3204
            $ret['filename'] = $m[3];
3205
        }
3206
        switch ($options) {
3207
            case PATHINFO_DIRNAME:
3208
            case 'dirname':
3209
                return $ret['dirname'];
3210
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3211
            case PATHINFO_BASENAME:
3212
            case 'basename':
3213
                return $ret['basename'];
3214
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3215
            case PATHINFO_EXTENSION:
3216
            case 'extension':
3217
                return $ret['extension'];
3218
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3219
            case PATHINFO_FILENAME:
3220
            case 'filename':
3221
                return $ret['filename'];
3222
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3223
            default:
3224
                return $ret;
3225
        }
3226
    }
3227
3228
    /**
3229
     * Set or reset instance properties.
3230
     *
3231
     * Usage Example:
3232
     * $page->set('X-Priority', '3');
3233
     *
3234
     * @param string $name
3235
     * @param mixed  $value
3236
     *                      NOTE: will not work with arrays, there are no arrays to set/reset
3237
     *
3238
     * @throws phpmailerException
3239
     *
3240
     * @return bool
3241
     *
3242
     * @todo   Should this not be using __set() magic function?
3243
     */
3244
    public function set($name, $value = '')
3245
    {
3246
        try {
3247
            if (isset($this->$name)) {
3248
                $this->$name = $value;
3249
            } else {
3250
                throw new phpmailerException($this->lang('variable_set').$name, self::STOP_CRITICAL);
3251
            }
3252
        } catch (Exception $e) {
3253
            $this->setError($e->getMessage());
3254
            if ($e->getCode() == self::STOP_CRITICAL) {
3255
                return false;
3256
            }
3257
        }
3258
3259
        return true;
3260
    }
3261
3262
    /**
3263
     * Strip newlines to prevent header injection.
3264
     *
3265
     * @param string $str
3266
     *
3267
     * @return string
3268
     */
3269
    public function secureHeader($str)
3270
    {
3271
        return trim(str_replace(["\r", "\n"], '', $str));
3272
    }
3273
3274
    /**
3275
     * Normalize line breaks in a string.
3276
     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3277
     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3278
     *
3279
     * @param string $text
3280
     * @param string $breaktype What kind of line break to use, defaults to CRLF
3281
     *
3282
     * @return string
3283
     * @static
3284
     */
3285
    public static function normalizeBreaks($text, $breaktype = "\r\n")
3286
    {
3287
        return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3288
    }
3289
3290
    /**
3291
     * Set the private key file and password for S/MIME signing.
3292
     *
3293
     * @param string $cert_filename
3294
     * @param string $key_filename
3295
     * @param string $key_pass      Password for private key
3296
     */
3297
    public function sign($cert_filename, $key_filename, $key_pass)
3298
    {
3299
        $this->sign_cert_file = $cert_filename;
3300
        $this->sign_key_file = $key_filename;
3301
        $this->sign_key_pass = $key_pass;
3302
    }
3303
3304
    /**
3305
     * Quoted-Printable-encode a DKIM header.
3306
     *
3307
     * @param string $txt
3308
     *
3309
     * @return string
3310
     */
3311
    public function DKIM_QP($txt)
3312
    {
3313
        $line = '';
3314
        for ($i = 0; $i < strlen($txt); $i++) {
3315
            $ord = ord($txt[$i]);
3316
            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3317
                $line .= $txt[$i];
3318
            } else {
3319
                $line .= '='.sprintf('%02X', $ord);
3320
            }
3321
        }
3322
3323
        return $line;
3324
    }
3325
3326
    /**
3327
     * Generate a DKIM signature.
3328
     *
3329
     * @param string $s Header
3330
     *
3331
     * @throws phpmailerException
3332
     *
3333
     * @return string
3334
     */
3335
    public function DKIM_Sign($s)
3336
    {
3337
        if (!defined('PKCS7_TEXT')) {
3338
            if ($this->exceptions) {
3339
                throw new phpmailerException($this->lang('signing').' OpenSSL extension missing.');
3340
            }
3341
3342
            return '';
3343
        }
3344
        $privKeyStr = file_get_contents($this->DKIM_private);
3345
        if ($this->DKIM_passphrase != '') {
3346
            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3347
        } else {
3348
            $privKey = $privKeyStr;
3349
        }
3350
        if (openssl_sign($s, $signature, $privKey)) {
3351
            return base64_encode($signature);
3352
        }
3353
3354
        return '';
3355
    }
3356
3357
    /**
3358
     * Generate a DKIM canonicalization header.
3359
     *
3360
     * @param string $s Header
3361
     *
3362
     * @return string
3363
     */
3364
    public function DKIM_HeaderC($s)
3365
    {
3366
        $s = preg_replace("/\r\n\s+/", ' ', $s);
3367
        $lines = explode("\r\n", $s);
3368
        foreach ($lines as $key => $line) {
3369
            list($heading, $value) = explode(':', $line, 2);
3370
            $heading = strtolower($heading);
3371
            $value = preg_replace("/\s+/", ' ', $value); // Compress useless spaces
3372
            $lines[$key] = $heading.':'.trim($value); // Don't forget to remove WSP around the value
3373
        }
3374
        $s = implode("\r\n", $lines);
3375
3376
        return $s;
3377
    }
3378
3379
    /**
3380
     * Generate a DKIM canonicalization body.
3381
     *
3382
     * @param string $body Message Body
3383
     *
3384
     * @return string
3385
     */
3386
    public function DKIM_BodyC($body)
3387
    {
3388
        if ($body == '') {
3389
            return "\r\n";
3390
        }
3391
        // stabilize line endings
3392
        $body = str_replace("\r\n", "\n", $body);
3393
        $body = str_replace("\n", "\r\n", $body);
3394
        // END stabilize line endings
3395
        while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3396
            $body = substr($body, 0, strlen($body) - 2);
3397
        }
3398
3399
        return $body;
3400
    }
3401
3402
    /**
3403
     * Create the DKIM header and body in a new message header.
3404
     *
3405
     * @param string $headers_line Header lines
3406
     * @param string $subject      Subject
3407
     * @param string $body         Body
3408
     *
3409
     * @return string
3410
     */
3411
    public function DKIM_Add($headers_line, $subject, $body)
3412
    {
3413
        $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
3414
        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3415
        $DKIMquery = 'dns/txt'; // Query method
3416
        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3417
        $subject_header = "Subject: $subject";
3418
        $headers = explode($this->LE, $headers_line);
3419
        $from_header = '';
3420
        $to_header = '';
3421
        $current = '';
3422
        foreach ($headers as $header) {
3423
            if (strpos($header, 'From:') === 0) {
3424
                $from_header = $header;
3425
                $current = 'from_header';
3426
            } elseif (strpos($header, 'To:') === 0) {
3427
                $to_header = $header;
3428
                $current = 'to_header';
3429
            } else {
3430
                if ($current && strpos($header, ' =?') === 0) {
3431
                    $current .= $header;
3432
                } else {
3433
                    $current = '';
3434
                }
3435
            }
3436
        }
3437
        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3438
        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3439
        $subject = str_replace(
3440
            '|',
3441
            '=7C',
3442
            $this->DKIM_QP($subject_header)
3443
        ); // Copied header fields (dkim-quoted-printable)
3444
        $body = $this->DKIM_BodyC($body);
3445
        $DKIMlen = strlen($body); // Length of body
3446
        $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
3447
        $ident = ($this->DKIM_identity == '') ? '' : ' i='.$this->DKIM_identity.';';
3448
        $dkimhdrs = 'DKIM-Signature: v=1; a='.
3449
            $DKIMsignatureType.'; q='.
3450
            $DKIMquery.'; l='.
3451
            $DKIMlen.'; s='.
3452
            $this->DKIM_selector.
3453
            ";\r\n".
3454
            "\tt=".$DKIMtime.'; c='.$DKIMcanonicalization.";\r\n".
3455
            "\th=From:To:Subject;\r\n".
3456
            "\td=".$this->DKIM_domain.';'.$ident."\r\n".
3457
            "\tz=$from\r\n".
3458
            "\t|$to\r\n".
3459
            "\t|$subject;\r\n".
3460
            "\tbh=".$DKIMb64.";\r\n".
3461
            "\tb=";
3462
        $toSign = $this->DKIM_HeaderC(
3463
            $from_header."\r\n".$to_header."\r\n".$subject_header."\r\n".$dkimhdrs
3464
        );
3465
        $signed = $this->DKIM_Sign($toSign);
3466
3467
        return $dkimhdrs.$signed."\r\n";
3468
    }
3469
3470
    /**
3471
     * Perform a callback.
3472
     *
3473
     * @param bool   $isSent
3474
     * @param string $to
3475
     * @param string $cc
3476
     * @param string $bcc
3477
     * @param string $subject
3478
     * @param string $body
3479
     * @param string $from
3480
     */
3481
    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null)
3482
    {
3483
        if (!empty($this->action_function) && is_callable($this->action_function)) {
3484
            $params = [$isSent, $to, $cc, $bcc, $subject, $body, $from];
3485
            call_user_func_array($this->action_function, $params);
3486
        }
3487
    }
3488
}
3489
3490
/**
3491
 * PHPMailer exception handler.
3492
 */
3493
class phpmailerException extends Exception
3494
{
3495
    /**
3496
     * Prettify error message output.
3497
     *
3498
     * @return string
3499
     */
3500
    public function errorMessage()
3501
    {
3502
        $errorMsg = '<strong>'.$this->getMessage()."</strong><br />\n";
3503
3504
        return $errorMsg;
3505
    }
3506
}
3507