Completed
Push — master ( d3fb64...f0b287 )
by Marcus
16:03 queued 01:05
created

class.phpmailer.php (14 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
/**
3
 * PHPMailer - PHP email creation and transport class.
4
 * PHP Version 5
5
 * @package PHPMailer
6
 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7
 * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
8
 * @author Jim Jagielski (jimjag) <[email protected]>
9
 * @author Andy Prevost (codeworxtech) <[email protected]>
10
 * @author Brent R. Matzelle (original founder)
11
 * @copyright 2012 - 2014 Marcus Bointon
12
 * @copyright 2010 - 2012 Jim Jagielski
13
 * @copyright 2004 - 2009 Andy Prevost
14
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15
 * @note This program is distributed in the hope that it will be useful - WITHOUT
16
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
 * FITNESS FOR A PARTICULAR PURPOSE.
18
 */
19
20
/**
21
 * PHPMailer - PHP email creation and transport class.
22
 * @package PHPMailer
23
 * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
24
 * @author Jim Jagielski (jimjag) <[email protected]>
25
 * @author Andy Prevost (codeworxtech) <[email protected]>
26
 * @author Brent R. Matzelle (original founder)
27
 */
28
class PHPMailer
0 ignored issues
show
This class has 136 public methods and attributes which exceeds the configured maximum of 45.

The number of this metric differs depending on the chosen design (inheritance vs. composition). For inheritance, the number should generally be a bit lower.

A high number indicates a reusable class. It might also make the class harder to change without breaking other classes though.

Loading history...
This class has 75 fields which exceeds the configured maximum of 15.

Too many fields generally indicate a class which does too much and does not follow the single responsibility principle.

We suggest taking a look at the “Code” section for further suggestions on how to fix this.

Loading history...
This class has 3849 lines of code which exceeds the configured maximum of 1000.

Really long classes often contain too much logic and violate the single responsibility principle.

We suggest to take a look at the “Code” section for options on how to refactor this code.

Loading history...
This class has a complexity of 495 which exceeds the configured maximum of 50.

The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.

Some resources for further reading:

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

Loading history...
29
{
30
    /**
31
     * The PHPMailer Version number.
32
     * @var string
33
     */
34
    public $Version = '5.2.14';
35
36
    /**
37
     * Email priority.
38
     * Options: null (default), 1 = High, 3 = Normal, 5 = low.
39
     * When null, the header is not set at all.
40
     * @var integer
41
     */
42
    public $Priority = null;
43
44
    /**
45
     * The character set of the message.
46
     * @var string
47
     */
48
    public $CharSet = 'iso-8859-1';
49
50
    /**
51
     * The MIME Content-type of the message.
52
     * @var string
53
     */
54
    public $ContentType = 'text/plain';
55
56
    /**
57
     * The message encoding.
58
     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59
     * @var string
60
     */
61
    public $Encoding = '8bit';
62
63
    /**
64
     * Holds the most recent mailer error message.
65
     * @var string
66
     */
67
    public $ErrorInfo = '';
68
69
    /**
70
     * The From email address for the message.
71
     * @var string
72
     */
73
    public $From = 'root@localhost';
74
75
    /**
76
     * The From name of the message.
77
     * @var string
78
     */
79
    public $FromName = 'Root User';
80
81
    /**
82
     * The Sender email (Return-Path) of the message.
83
     * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
84
     * @var string
85
     */
86
    public $Sender = '';
87
88
    /**
89
     * The Return-Path of the message.
90
     * If empty, it will be set to either From or Sender.
91
     * @var string
92
     * @deprecated Email senders should never set a return-path header;
93
     * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
94
     * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
95
     */
96
    public $ReturnPath = '';
97
98
    /**
99
     * The Subject of the message.
100
     * @var string
101
     */
102
    public $Subject = '';
103
104
    /**
105
     * An HTML or plain text message body.
106
     * If HTML then call isHTML(true).
107
     * @var string
108
     */
109
    public $Body = '';
110
111
    /**
112
     * The plain-text message body.
113
     * This body can be read by mail clients that do not have HTML email
114
     * capability such as mutt & Eudora.
115
     * Clients that can read HTML will view the normal Body.
116
     * @var string
117
     */
118
    public $AltBody = '';
119
120
    /**
121
     * An iCal message part body.
122
     * Only supported in simple alt or alt_inline message types
123
     * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
124
     * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
125
     * @link http://kigkonsult.se/iCalcreator/
126
     * @var string
127
     */
128
    public $Ical = '';
129
130
    /**
131
     * The complete compiled MIME message body.
132
     * @access protected
133
     * @var string
134
     */
135
    protected $MIMEBody = '';
136
137
    /**
138
     * The complete compiled MIME message headers.
139
     * @var string
140
     * @access protected
141
     */
142
    protected $MIMEHeader = '';
143
144
    /**
145
     * Extra headers that createHeader() doesn't fold in.
146
     * @var string
147
     * @access protected
148
     */
149
    protected $mailHeader = '';
150
151
    /**
152
     * Word-wrap the message body to this number of chars.
153
     * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
154
     * @var integer
155
     */
156
    public $WordWrap = 0;
157
158
    /**
159
     * Which method to use to send mail.
160
     * Options: "mail", "sendmail", or "smtp".
161
     * @var string
162
     */
163
    public $Mailer = 'mail';
164
165
    /**
166
     * The path to the sendmail program.
167
     * @var string
168
     */
169
    public $Sendmail = '/usr/sbin/sendmail';
170
171
    /**
172
     * Whether mail() uses a fully sendmail-compatible MTA.
173
     * One which supports sendmail's "-oi -f" options.
174
     * @var boolean
175
     */
176
    public $UseSendmailOptions = true;
177
178
    /**
179
     * Path to PHPMailer plugins.
180
     * Useful if the SMTP class is not in the PHP include path.
181
     * @var string
182
     * @deprecated Should not be needed now there is an autoloader.
183
     */
184
    public $PluginDir = '';
185
186
    /**
187
     * The email address that a reading confirmation should be sent to, also known as read receipt.
188
     * @var string
189
     */
190
    public $ConfirmReadingTo = '';
191
192
    /**
193
     * The hostname to use in the Message-ID header and as default HELO string.
194
     * If empty, PHPMailer attempts to find one with, in order,
195
     * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
196
     * 'localhost.localdomain'.
197
     * @var string
198
     */
199
    public $Hostname = '';
200
201
    /**
202
     * An ID to be used in the Message-ID header.
203
     * If empty, a unique id will be generated.
204
     * @var string
205
     */
206
    public $MessageID = '';
207
208
    /**
209
     * The message Date to be used in the Date header.
210
     * If empty, the current date will be added.
211
     * @var string
212
     */
213
    public $MessageDate = '';
214
215
    /**
216
     * SMTP hosts.
217
     * Either a single hostname or multiple semicolon-delimited hostnames.
218
     * You can also specify a different port
219
     * for each host by using this format: [hostname:port]
220
     * (e.g. "smtp1.example.com:25;smtp2.example.com").
221
     * You can also specify encryption type, for example:
222
     * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
223
     * Hosts will be tried in order.
224
     * @var string
225
     */
226
    public $Host = 'localhost';
227
228
    /**
229
     * The default SMTP server port.
230
     * @var integer
231
     * @TODO Why is this needed when the SMTP class takes care of it?
232
     */
233
    public $Port = 25;
234
235
    /**
236
     * The SMTP HELO of the message.
237
     * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
238
     * one with the same method described above for $Hostname.
239
     * @var string
240
     * @see PHPMailer::$Hostname
241
     */
242
    public $Helo = '';
243
244
    /**
245
     * What kind of encryption to use on the SMTP connection.
246
     * Options: '', 'ssl' or 'tls'
247
     * @var string
248
     */
249
    public $SMTPSecure = '';
250
251
    /**
252
     * Whether to enable TLS encryption automatically if a server supports it,
253
     * even if `SMTPSecure` is not set to 'tls'.
254
     * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
255
     * @var boolean
256
     */
257
    public $SMTPAutoTLS = true;
258
259
    /**
260
     * Whether to use SMTP authentication.
261
     * Uses the Username and Password properties.
262
     * @var boolean
263
     * @see PHPMailer::$Username
264
     * @see PHPMailer::$Password
265
     */
266
    public $SMTPAuth = false;
267
268
    /**
269
     * Options array passed to stream_context_create when connecting via SMTP.
270
     * @var array
271
     */
272
    public $SMTPOptions = array();
273
274
    /**
275
     * SMTP username.
276
     * @var string
277
     */
278
    public $Username = '';
279
280
    /**
281
     * SMTP password.
282
     * @var string
283
     */
284
    public $Password = '';
285
286
    /**
287
     * SMTP auth type.
288
     * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
289
     * @var string
290
     */
291
    public $AuthType = '';
292
293
    /**
294
     * SMTP realm.
295
     * Used for NTLM auth
296
     * @var string
297
     */
298
    public $Realm = '';
299
300
    /**
301
     * SMTP workstation.
302
     * Used for NTLM auth
303
     * @var string
304
     */
305
    public $Workstation = '';
306
307
    /**
308
     * The SMTP server timeout in seconds.
309
     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
310
     * @var integer
311
     */
312
    public $Timeout = 300;
313
314
    /**
315
     * SMTP class debug output mode.
316
     * Debug output level.
317
     * Options:
318
     * * `0` No output
319
     * * `1` Commands
320
     * * `2` Data and commands
321
     * * `3` As 2 plus connection status
322
     * * `4` Low-level data output
323
     * @var integer
324
     * @see SMTP::$do_debug
325
     */
326
    public $SMTPDebug = 0;
327
328
    /**
329
     * How to handle debug output.
330
     * Options:
331
     * * `echo` Output plain-text as-is, appropriate for CLI
332
     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
333
     * * `error_log` Output to error log as configured in php.ini
334
     *
335
     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
336
     * <code>
337
     * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
338
     * </code>
339
     * @var string|callable
340
     * @see SMTP::$Debugoutput
341
     */
342
    public $Debugoutput = 'echo';
343
344
    /**
345
     * Whether to keep SMTP connection open after each message.
346
     * If this is set to true then to close the connection
347
     * requires an explicit call to smtpClose().
348
     * @var boolean
349
     */
350
    public $SMTPKeepAlive = false;
351
352
    /**
353
     * Whether to split multiple to addresses into multiple messages
354
     * or send them all in one message.
355
     * @var boolean
356
     */
357
    public $SingleTo = false;
358
359
    /**
360
     * Storage for addresses when SingleTo is enabled.
361
     * @var array
362
     * @TODO This should really not be public
363
     */
364
    public $SingleToArray = array();
365
366
    /**
367
     * Whether to generate VERP addresses on send.
368
     * Only applicable when sending via SMTP.
369
     * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
370
     * @link http://www.postfix.org/VERP_README.html Postfix VERP info
371
     * @var boolean
372
     */
373
    public $do_verp = false;
374
375
    /**
376
     * Whether to allow sending messages with an empty body.
377
     * @var boolean
378
     */
379
    public $AllowEmpty = false;
380
381
    /**
382
     * The default line ending.
383
     * @note The default remains "\n". We force CRLF where we know
384
     *        it must be used via self::CRLF.
385
     * @var string
386
     */
387
    public $LE = "\n";
1 ignored issue
show
Comprehensibility introduced by
Avoid variables with short names like $LE. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
388
389
    /**
390
     * DKIM selector.
391
     * @var string
392
     */
393
    public $DKIM_selector = '';
394
395
    /**
396
     * DKIM Identity.
397
     * Usually the email address used as the source of the email
398
     * @var string
399
     */
400
    public $DKIM_identity = '';
401
402
    /**
403
     * DKIM passphrase.
404
     * Used if your key is encrypted.
405
     * @var string
406
     */
407
    public $DKIM_passphrase = '';
408
409
    /**
410
     * DKIM signing domain name.
411
     * @example 'example.com'
412
     * @var string
413
     */
414
    public $DKIM_domain = '';
415
416
    /**
417
     * DKIM private key file path.
418
     * @var string
419
     */
420
    public $DKIM_private = '';
421
422
    /**
423
     * Callback Action function name.
424
     *
425
     * The function that handles the result of the send email action.
426
     * It is called out by send() for each email sent.
427
     *
428
     * Value can be any php callable: http://www.php.net/is_callable
429
     *
430
     * Parameters:
431
     *   boolean $result        result of the send action
432
     *   string  $to            email address of the recipient
433
     *   string  $cc            cc email addresses
434
     *   string  $bcc           bcc email addresses
435
     *   string  $subject       the subject
436
     *   string  $body          the email body
437
     *   string  $from          email address of sender
438
     * @var string
439
     */
440
    public $action_function = '';
441
442
    /**
443
     * What to put in the X-Mailer header.
444
     * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
445
     * @var string
446
     */
447
    public $XMailer = '';
448
449
    /**
450
     * An instance of the SMTP sender class.
451
     * @var SMTP
452
     * @access protected
453
     */
454
    protected $smtp = null;
455
456
    /**
457
     * The array of 'to' names and addresses.
458
     * @var array
459
     * @access protected
460
     */
461
    protected $to = array();
462
463
    /**
464
     * The array of 'cc' names and addresses.
465
     * @var array
466
     * @access protected
467
     */
468
    protected $cc = array();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $cc. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
469
470
    /**
471
     * The array of 'bcc' names and addresses.
472
     * @var array
473
     * @access protected
474
     */
475
    protected $bcc = array();
476
477
    /**
478
     * The array of reply-to names and addresses.
479
     * @var array
480
     * @access protected
481
     */
482
    protected $ReplyTo = array();
483
484
    /**
485
     * An array of all kinds of addresses.
486
     * Includes all of $to, $cc, $bcc
487
     * @var array
488
     * @access protected
489
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
490
     */
491
    protected $all_recipients = array();
492
493
    /**
494
     * An array of names and addresses queued for validation.
495
     * In send(), valid and non duplicate entries are moved to $all_recipients
496
     * and one of $to, $cc, or $bcc.
497
     * This array is used only for addresses with IDN.
498
     * @var array
499
     * @access protected
500
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
501
     * @see PHPMailer::$all_recipients
502
     */
503
    protected $RecipientsQueue = array();
504
505
    /**
506
     * An array of reply-to names and addresses queued for validation.
507
     * In send(), valid and non duplicate entries are moved to $ReplyTo.
508
     * This array is used only for addresses with IDN.
509
     * @var array
510
     * @access protected
511
     * @see PHPMailer::$ReplyTo
512
     */
513
    protected $ReplyToQueue = array();
514
515
    /**
516
     * The array of attachments.
517
     * @var array
518
     * @access protected
519
     */
520
    protected $attachment = array();
521
522
    /**
523
     * The array of custom headers.
524
     * @var array
525
     * @access protected
526
     */
527
    protected $CustomHeader = array();
528
529
    /**
530
     * The most recent Message-ID (including angular brackets).
531
     * @var string
532
     * @access protected
533
     */
534
    protected $lastMessageID = '';
535
536
    /**
537
     * The message's MIME type.
538
     * @var string
539
     * @access protected
540
     */
541
    protected $message_type = '';
542
543
    /**
544
     * The array of MIME boundary strings.
545
     * @var array
546
     * @access protected
547
     */
548
    protected $boundary = array();
549
550
    /**
551
     * The array of available languages.
552
     * @var array
553
     * @access protected
554
     */
555
    protected $language = array();
556
557
    /**
558
     * The number of errors encountered.
559
     * @var integer
560
     * @access protected
561
     */
562
    protected $error_count = 0;
563
564
    /**
565
     * The S/MIME certificate file path.
566
     * @var string
567
     * @access protected
568
     */
569
    protected $sign_cert_file = '';
570
571
    /**
572
     * The S/MIME key file path.
573
     * @var string
574
     * @access protected
575
     */
576
    protected $sign_key_file = '';
577
578
    /**
579
     * The optional S/MIME extra certificates ("CA Chain") file path.
580
     * @var string
581
     * @access protected
582
     */
583
    protected $sign_extracerts_file = '';
584
585
    /**
586
     * The S/MIME password for the key.
587
     * Used only if the key is encrypted.
588
     * @var string
589
     * @access protected
590
     */
591
    protected $sign_key_pass = '';
592
593
    /**
594
     * Whether to throw exceptions for errors.
595
     * @var boolean
596
     * @access protected
597
     */
598
    protected $exceptions = false;
599
600
    /**
601
     * Unique ID used for message ID and boundaries.
602
     * @var string
603
     * @access protected
604
     */
605
    protected $uniqueid = '';
606
607
    /**
608
     * Error severity: message only, continue processing.
609
     */
610
    const STOP_MESSAGE = 0;
611
612
    /**
613
     * Error severity: message, likely ok to continue processing.
614
     */
615
    const STOP_CONTINUE = 1;
616
617
    /**
618
     * Error severity: message, plus full stop, critical error reached.
619
     */
620
    const STOP_CRITICAL = 2;
621
622
    /**
623
     * SMTP RFC standard line ending.
624
     */
625
    const CRLF = "\r\n";
626
627
    /**
628
     * The maximum line length allowed by RFC 2822 section 2.1.1
629
     * @var integer
630
     */
631
    const MAX_LINE_LENGTH = 998;
632
633
    /**
634
     * Constructor.
635
     * @param boolean $exceptions Should we throw external exceptions?
636
     */
637 55
    public function __construct($exceptions = false)
638
    {
639 55
        $this->exceptions = (boolean)$exceptions;
640 55
    }
641
642
    /**
643
     * Destructor.
644
     */
645 55
    public function __destruct()
646
    {
647
        //Close any open SMTP connection nicely
648 55
        if ($this->Mailer == 'smtp') {
649 51
            $this->smtpClose();
650 51
        }
651 55
    }
652
653
    /**
654
     * Call mail() in a safe_mode-aware fashion.
655
     * Also, unless sendmail_path points to sendmail (or something that
656
     * claims to be sendmail), don't pass params (not a perfect fix,
657
     * but it will do)
658
     * @param string $to To
659
     * @param string $subject Subject
660
     * @param string $body Message Body
661
     * @param string $header Additional Header(s)
662
     * @param string $params Params
663
     * @access private
664
     * @return boolean
665
     */
666 2
    private function mailPassthru($to, $subject, $body, $header, $params)
667
    {
668
        //Check overloading of mail function to avoid double-encoding
669 2
        if (ini_get('mbstring.func_overload') & 1) {
670
            $subject = $this->secureHeader($subject);
671
        } else {
672 2
            $subject = $this->encodeHeader($this->secureHeader($subject));
673
        }
674 2
        if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
675
            $result = @mail($to, $subject, $body, $header);
676
        } else {
677 2
            $result = @mail($to, $subject, $body, $header, $params);
678
        }
679 2
        return $result;
680
    }
681
682
    /**
683
     * Output debugging info via user-defined method.
684
     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
685
     * @see PHPMailer::$Debugoutput
686
     * @see PHPMailer::$SMTPDebug
687
     * @param string $str
688
     */
689 3
    protected function edebug($str)
690
    {
691 3
        if ($this->SMTPDebug <= 0) {
692
            return;
693
        }
694
        //Avoid clash with built-in function names
695 3
        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
696
            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
697
            return;
698
        }
699 3
        switch ($this->Debugoutput) {
700 3
            case 'error_log':
701
                //Don't output, just log
702
                error_log($str);
703
                break;
704 3
            case 'html':
705
                //Cleans up output a bit for a better looking, HTML-safe output
706
                echo htmlentities(
707
                    preg_replace('/[\r\n]+/', '', $str),
708
                    ENT_QUOTES,
709
                    'UTF-8'
710
                )
711
                . "<br>\n";
712
                break;
713 3
            case 'echo':
714 3
            default:
715
                //Normalize line breaks
716 3
                $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
717 3
                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
718 3
                    "\n",
719 3
                    "\n                   \t                  ",
720 3
                    trim($str)
721 3
                ) . "\n";
722 3
        }
723 3
    }
724
725
    /**
726
     * Sets message type to HTML or plain.
727
     * @param boolean $isHtml True for HTML mode.
728
     * @return void
729
     */
730 15
    public function isHTML($isHtml = true)
731
    {
732 15
        if ($isHtml) {
733 12
            $this->ContentType = 'text/html';
734 12
        } else {
735 3
            $this->ContentType = 'text/plain';
736
        }
737 15
    }
738
739
    /**
740
     * Send messages using SMTP.
741
     * @return void
742
     */
743 1
    public function isSMTP()
744
    {
745 1
        $this->Mailer = 'smtp';
746 1
    }
747
748
    /**
749
     * Send messages using PHP's mail() function.
750
     * @return void
751
     */
752 3
    public function isMail()
753
    {
754 3
        $this->Mailer = 'mail';
755 3
    }
756
757
    /**
758
     * Send messages using $Sendmail.
759
     * @return void
760
     */
761 2
    public function isSendmail()
762
    {
763 2
        $ini_sendmail_path = ini_get('sendmail_path');
764
765 2
        if (!stristr($ini_sendmail_path, 'sendmail')) {
766
            $this->Sendmail = '/usr/sbin/sendmail';
767
        } else {
768 2
            $this->Sendmail = $ini_sendmail_path;
769
        }
770 2
        $this->Mailer = 'sendmail';
771 2
    }
772
773
    /**
774
     * Send messages using qmail.
775
     * @return void
776
     */
777 1
    public function isQmail()
778
    {
779 1
        $ini_sendmail_path = ini_get('sendmail_path');
780
781 1
        if (!stristr($ini_sendmail_path, 'qmail')) {
782 1
            $this->Sendmail = '/var/qmail/bin/qmail-inject';
783 1
        } else {
784
            $this->Sendmail = $ini_sendmail_path;
785
        }
786 1
        $this->Mailer = 'qmail';
787 1
    }
788
789
    /**
790
     * Add a "To" address.
791
     * @param string $address The email address to send to
792
     * @param string $name
793
     * @return boolean true on success, false if address already used or invalid in some way
794
     */
795 55
    public function addAddress($address, $name = '')
796
    {
797 55
        return $this->addOrEnqueueAnAddress('to', $address, $name);
798
    }
799
800
    /**
801
     * Add a "CC" address.
802
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
803
     * @param string $address The email address to send to
804
     * @param string $name
805
     * @return boolean true on success, false if address already used or invalid in some way
806
     */
807 55
    public function addCC($address, $name = '')
808
    {
809 55
        return $this->addOrEnqueueAnAddress('cc', $address, $name);
810
    }
811
812
    /**
813
     * Add a "BCC" address.
814
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
815
     * @param string $address The email address to send to
816
     * @param string $name
817
     * @return boolean true on success, false if address already used or invalid in some way
818
     */
819 3
    public function addBCC($address, $name = '')
820
    {
821 3
        return $this->addOrEnqueueAnAddress('bcc', $address, $name);
822
    }
823
824
    /**
825
     * Add a "Reply-To" address.
826
     * @param string $address The email address to reply to
827
     * @param string $name
828
     * @return boolean true on success, false if address already used or invalid in some way
829
     */
830 55
    public function addReplyTo($address, $name = '')
831
    {
832 55
        return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
833
    }
834
835
    /**
836
     * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
837
     * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
838
     * be modified after calling this function), addition of such addresses is delayed until send().
839
     * Addresses that have been added already return false, but do not throw exceptions.
840
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
841
     * @param string $address The email address to send, resp. to reply to
842
     * @param string $name
843
     * @throws phpmailerException
844
     * @return boolean true on success, false if address already used or invalid in some way
845
     * @access protected
846
     */
847 55
    protected function addOrEnqueueAnAddress($kind, $address, $name)
848
    {
849 55
        $address = trim($address);
850 55
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
851 55
        if (($pos = strrpos($address, '@')) === false) {
852
            // At-sign is misssing.
853 1
            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
854 1
            $this->setError($error_message);
855 1
            $this->edebug($error_message);
856 1
            if ($this->exceptions) {
857
                throw new phpmailerException($error_message);
858
            }
859 1
            return false;
860
        }
861 55
        $params = array($kind, $address, $name);
862
        // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
863 55
        if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
864 2
            if ($kind != 'Reply-To') {
865 2
                if (!array_key_exists($address, $this->RecipientsQueue)) {
866 2
                    $this->RecipientsQueue[$address] = $params;
867 2
                    return true;
868
                }
869 1
            } else {
870 2
                if (!array_key_exists($address, $this->ReplyToQueue)) {
871 2
                    $this->ReplyToQueue[$address] = $params;
872 2
                    return true;
873
                }
874
            }
875 1
            return false;
876
        }
877
        // Immediately add standard addresses without IDN.
878 55
        return call_user_func_array(array($this, 'addAnAddress'), $params);
879
    }
880
881
    /**
882
     * Add an address to one of the recipient arrays or to the ReplyTo array.
883
     * Addresses that have been added already return false, but do not throw exceptions.
884
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
885
     * @param string $address The email address to send, resp. to reply to
886
     * @param string $name
887
     * @throws phpmailerException
888
     * @return boolean true on success, false if address already used or invalid in some way
889
     * @access protected
890
     */
891 55
    protected function addAnAddress($kind, $address, $name = '')
892
    {
893 55
        if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
894
            $error_message = $this->lang('Invalid recipient kind: ') . $kind;
895
            $this->setError($error_message);
896
            $this->edebug($error_message);
897
            if ($this->exceptions) {
898
                throw new phpmailerException($error_message);
899
            }
900
            return false;
901
        }
902 55
        if (!$this->validateAddress($address)) {
903 1
            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
904 1
            $this->setError($error_message);
905 1
            $this->edebug($error_message);
906 1
            if ($this->exceptions) {
907
                throw new phpmailerException($error_message);
908
            }
909 1
            return false;
910
        }
911 55
        if ($kind != 'Reply-To') {
912 55
            if (!array_key_exists(strtolower($address), $this->all_recipients)) {
913 55
                array_push($this->$kind, array($address, $name));
914 55
                $this->all_recipients[strtolower($address)] = true;
915 55
                return true;
916
            }
917 2
        } else {
918 55
            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
919 55
                $this->ReplyTo[strtolower($address)] = array($address, $name);
920 55
                return true;
921
            }
922
        }
923 2
        return false;
924
    }
925
926
    /**
927
     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
928
     * of the form "display name <address>" into an array of name/address pairs.
929
     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
930
     * Note that quotes in the name part are removed.
931
     * @param string $addrstr The address list string
932
     * @param bool $useimap Whether to use the IMAP extension to parse the list
933
     * @return array
934
     * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
935
     */
936 1
    public function parseAddresses($addrstr, $useimap = true)
937
    {
938 1
        $addresses = array();
939 1
        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
940
            //Use this built-in parser if it's available
941 1
            $list = imap_rfc822_parse_adrlist($addrstr, '');
942 1
            foreach ($list as $address) {
943 1
                if ($address->host != '.SYNTAX-ERROR.') {
944 1
                    if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
945 1
                        $addresses[] = array(
946 1
                            'name' => (property_exists($address, 'personal') ? $address->personal : ''),
947 1
                            'address' => $address->mailbox . '@' . $address->host
948 1
                        );
949 1
                    }
950 1
                }
951 1
            }
952 1
        } else {
953
            //Use this simpler parser
954 1
            $list = explode(',', $addrstr);
955 1
            foreach ($list as $address) {
956 1
                $address = trim($address);
957
                //Is there a separate name part?
958 1
                if (strpos($address, '<') === false) {
959
                    //No separate name, just use the whole thing
960 1
                    if ($this->validateAddress($address)) {
961 1
                        $addresses[] = array(
962 1
                            'name' => '',
963
                            'address' => $address
964 1
                        );
965 1
                    }
966 1
                } else {
967 1
                    list($name, $email) = explode('<', $address);
968 1
                    $email = trim(str_replace('>', '', $email));
969 1
                    if ($this->validateAddress($email)) {
970 1
                        $addresses[] = array(
971 1
                            'name' => trim(str_replace(array('"', "'"), '', $name)),
972
                            'address' => $email
973 1
                        );
974 1
                    }
975
                }
976 1
            }
977
        }
978 1
        return $addresses;
979
    }
980
981
    /**
982
     * Set the From and FromName properties.
983
     * @param string $address
984
     * @param string $name
985
     * @param boolean $auto Whether to also set the Sender address, defaults to true
986
     * @throws phpmailerException
987
     * @return boolean
988
     */
989 1
    public function setFrom($address, $name = '', $auto = true)
990
    {
991 1
        $address = trim($address);
992 1
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
993
        // Don't validate now addresses with IDN. Will be done in send().
994 1
        if (($pos = strrpos($address, '@')) === false or
995 1
            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
996 1
            !$this->validateAddress($address)) {
997 1
            $error_message = $this->lang('invalid_address') . " (setFrom) $address";
998 1
            $this->setError($error_message);
999 1
            $this->edebug($error_message);
1000 1
            if ($this->exceptions) {
1001
                throw new phpmailerException($error_message);
1002
            }
1003 1
            return false;
1004
        }
1005 1
        $this->From = $address;
1006 1
        $this->FromName = $name;
1007 1
        if ($auto) {
1008 1
            if (empty($this->Sender)) {
1009 1
                $this->Sender = $address;
1010 1
            }
1011 1
        }
1012 1
        return true;
1013
    }
1014
1015
    /**
1016
     * Return the Message-ID header of the last email.
1017
     * Technically this is the value from the last time the headers were created,
1018
     * but it's also the message ID of the last sent message except in
1019
     * pathological cases.
1020
     * @return string
1021
     */
1022 1
    public function getLastMessageID()
1023
    {
1024 1
        return $this->lastMessageID;
1025
    }
1026
1027
    /**
1028
     * Check that a string looks like an email address.
1029
     * @param string $address The email address to check
1030
     * @param string $patternselect A selector for the validation pattern to use :
1031
     * * `auto` Pick best pattern automatically;
1032
     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
1033
     * * `pcre` Use old PCRE implementation;
1034
     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1035
     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1036
     * * `noregex` Don't use a regex: super fast, really dumb.
1037
     * @return boolean
1038
     * @static
1039
     * @access public
1040
     */
1041 55
    public static function validateAddress($address, $patternselect = 'auto')
1042
    {
1043
        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1044 55
        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1045 1
            return false;
1046
        }
1047 55
        if (!$patternselect or $patternselect == 'auto') {
1048
            //Check this constant first so it works when extension_loaded() is disabled by safe mode
1049
            //Constant was added in PHP 5.2.4
1050 55
            if (defined('PCRE_VERSION')) {
1051
                //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
1052 55
                if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
1053 55
                    $patternselect = 'pcre8';
1054 55
                } else {
1055
                    $patternselect = 'pcre';
1056
                }
1057 55
            } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
1058
                //Fall back to older PCRE
1059
                $patternselect = 'pcre';
1060
            } else {
1061
                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1062
                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1063
                    $patternselect = 'php';
1064
                } else {
1065
                    $patternselect = 'noregex';
1066
                }
1067
            }
1068 55
        }
1069
        switch ($patternselect) {
1070 55
            case 'pcre8':
1071
                /**
1072
                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1073
                 * @link http://squiloople.com/2009/12/20/email-address-validation/
1074
                 * @copyright 2009-2010 Michael Rushton
1075
                 * Feel free to use and redistribute this code. But please keep this copyright notice.
1076
                 */
1077 55
                return (boolean)preg_match(
1078
                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1079 55
                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1080 55
                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1081 55
                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1082 55
                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1083 55
                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1084 55
                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1085 55
                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1086 55
                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1087
                    $address
1088 55
                );
1089 1
            case 'pcre':
1090
                //An older regex that doesn't need a recent PCRE
1091 1
                return (boolean)preg_match(
1092
                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1093 1
                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1094 1
                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1095 1
                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1096 1
                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1097 1
                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1098 1
                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1099 1
                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1100 1
                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1101 1
                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1102
                    $address
1103 1
                );
1104 1
            case 'html5':
1105
                /**
1106
                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1107
                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1108
                 */
1109
                return (boolean)preg_match(
1110
                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1111
                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1112
                    $address
1113
                );
1114 1
            case 'noregex':
1115
                //No PCRE! Do something _very_ approximate!
1116
                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1117 1
                return (strlen($address) >= 3
1118 1
                    and strpos($address, '@') >= 1
1119 1
                    and strpos($address, '@') != strlen($address) - 1);
1120 1
            case 'php':
1121 1
            default:
1122 1
                return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
1123
        }
1124
    }
1125
1126
    /**
1127
     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1128
     * "intl" and "mbstring" PHP extensions.
1129
     * @return bool "true" if required functions for IDN support are present
1130
     */
1131 41
    public function idnSupported()
1132
    {
1133
        // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1134 41
        return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1135
    }
1136
1137
    /**
1138
     * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1139
     * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1140
     * This function silently returns unmodified address if:
1141
     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1142
     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1143
     *   or fails for any reason (e.g. domain has characters not allowed in an IDN)
1144
     * @see PHPMailer::$CharSet
1145
     * @param string $address The email address to convert
1146
     * @return string The encoded address in ASCII form
1147
     */
1148 41
    public function punyencodeAddress($address)
1149
    {
1150
        // Verify we have required functions, CharSet, and at-sign.
1151 41
        if ($this->idnSupported() and
1152 41
            !empty($this->CharSet) and
1153 41
            ($pos = strrpos($address, '@')) !== false) {
1154 41
            $domain = substr($address, ++$pos);
1155
            // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1156 41
            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1157 3
                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1158 3
                if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1159 3
                    idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
1160 3
                    idn_to_ascii($domain)) !== false) {
1161 3
                    return substr($address, 0, $pos) . $punycode;
1162
                }
1163
            }
1164 41
        }
1165 41
        return $address;
1166
    }
1167
1168
    /**
1169
     * Create a message and send it.
1170
     * Uses the sending method specified by $Mailer.
1171
     * @throws phpmailerException
1172
     * @return boolean false on error - See the ErrorInfo property for details of the error.
1173
     */
1174 36
    public function send()
1175
    {
1176
        try {
1177 36
            if (!$this->preSend()) {
1178 3
                return false;
1179
            }
1180 36
            return $this->postSend();
1181
        } catch (phpmailerException $exc) {
1182
            $this->mailHeader = '';
1183
            $this->setError($exc->getMessage());
1184
            if ($this->exceptions) {
1185
                throw $exc;
1186
            }
1187
            return false;
1188
        }
1189
    }
1190
1191
    /**
1192
     * Prepare a message for sending.
1193
     * @throws phpmailerException
1194
     * @return boolean
1195
     */
1196 41
    public function preSend()
0 ignored issues
show
This operation has 2522 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
1197
    {
1198
        try {
1199 41
            $this->error_count = 0; // Reset errors
1200 41
            $this->mailHeader = '';
1201
1202
            // Dequeue recipient and Reply-To addresses with IDN
1203 41
            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1204 2
                $params[1] = $this->punyencodeAddress($params[1]);
1205 2
                call_user_func_array(array($this, 'addAnAddress'), $params);
1206 41
            }
1207 41
            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1208 1
                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1209
            }
1210
1211
            // Validate From, Sender, and ConfirmReadingTo addresses
1212 41
            foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
1213 41
                $this->$address_kind = trim($this->$address_kind);
1214 41
                if (empty($this->$address_kind)) {
1215 40
                    continue;
1216
                }
1217 41
                $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1218 41
                if (!$this->validateAddress($this->$address_kind)) {
1219 1
                    $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
1220 1
                    $this->setError($error_message);
1221 1
                    $this->edebug($error_message);
1222 1
                    if ($this->exceptions) {
1223
                        throw new phpmailerException($error_message);
1224
                    }
1225 1
                    return false;
1226
                }
1227 41
            }
1228
1229
            // Set whether the message is multipart/alternative
1230 41
            if ($this->alternativeExists()) {
1231 6
                $this->ContentType = 'multipart/alternative';
1232 6
            }
1233
1234 41
            $this->setMessageType();
1235
            // Refuse to send an empty message unless we are specifically allowing it
1236 41
            if (!$this->AllowEmpty and empty($this->Body)) {
1237 1
                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1238
            }
1239
1240
            // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1241 41
            $this->MIMEHeader = '';
1242 41
            $this->MIMEBody = $this->createBody();
1243
            // createBody may have added some headers, so retain them
1244 41
            $tempheaders = $this->MIMEHeader;
1245 41
            $this->MIMEHeader = $this->createHeader();
1246 41
            $this->MIMEHeader .= $tempheaders;
1247
1248
            // To capture the complete message when using mail(), create
1249
            // an extra header list which createHeader() doesn't fold in
1250 41
            if ($this->Mailer == 'mail') {
1251 2
                if (count($this->to) > 0) {
1252 2
                    $this->mailHeader .= $this->addrAppend('To', $this->to);
1253 2
                } else {
1254
                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1255
                }
1256 2
                $this->mailHeader .= $this->headerLine(
1257 2
                    'Subject',
1258 2
                    $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1259 2
                );
1260 2
            }
1261
1262
            // Sign with DKIM if enabled
1263 41
            if (!empty($this->DKIM_domain)
1264 41
                && !empty($this->DKIM_private)
1265 41
                && !empty($this->DKIM_selector)
1266 41
                && file_exists($this->DKIM_private)) {
1267 1
                $header_dkim = $this->DKIM_Add(
1268 1
                    $this->MIMEHeader . $this->mailHeader,
1269 1
                    $this->encodeHeader($this->secureHeader($this->Subject)),
1270 1
                    $this->MIMEBody
1271 1
                );
1272 1
                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1273 1
                    str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1274 1
            }
1275 41
            return true;
1276 2
        } catch (phpmailerException $exc) {
1277 2
            $this->setError($exc->getMessage());
1278 2
            if ($this->exceptions) {
1279
                throw $exc;
1280
            }
1281 2
            return false;
1282
        }
1283
    }
1284
1285
    /**
1286
     * Actually send a message.
1287
     * Send the email via the selected mechanism
1288
     * @throws phpmailerException
1289
     * @return boolean
1290
     */
1291 36
    public function postSend()
1292
    {
1293
        try {
1294
            // Choose the mailer and send through it
1295 36
            switch ($this->Mailer) {
1296 36
                case 'sendmail':
1297 36
                case 'qmail':
1298 1
                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1299 35
                case 'smtp':
1300 33
                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1301 2
                case 'mail':
1302 2
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1303
                default:
1304
                    $sendMethod = $this->Mailer.'Send';
1305
                    if (method_exists($this, $sendMethod)) {
1306
                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1307
                    }
1308
1309
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1310
            }
1311
        } catch (phpmailerException $exc) {
1312
            $this->setError($exc->getMessage());
1313
            $this->edebug($exc->getMessage());
1314
            if ($this->exceptions) {
1315
                throw $exc;
1316
            }
1317
        }
1318
        return false;
1319
    }
1320
1321
    /**
1322
     * Send mail using the $Sendmail program.
1323
     * @param string $header The message headers
1324
     * @param string $body The message body
1325
     * @see PHPMailer::$Sendmail
1326
     * @throws phpmailerException
1327
     * @access protected
1328
     * @return boolean
1329
     */
1330 1
    protected function sendmailSend($header, $body)
1331
    {
1332 1
        if ($this->Sender != '') {
1333 1
            if ($this->Mailer == 'qmail') {
1334
                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1335
            } else {
1336 1
                $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1337
            }
1338 1
        } else {
1339
            if ($this->Mailer == 'qmail') {
1340
                $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1341
            } else {
1342
                $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1343
            }
1344
        }
1345 1
        if ($this->SingleTo) {
1346
            foreach ($this->SingleToArray as $toAddr) {
1347
                if (!@$mail = popen($sendmail, 'w')) {
1348
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1349
                }
1350
                fputs($mail, 'To: ' . $toAddr . "\n");
1351
                fputs($mail, $header);
1352
                fputs($mail, $body);
1353
                $result = pclose($mail);
1354
                $this->doCallback(
1355
                    ($result == 0),
1356
                    array($toAddr),
1357
                    $this->cc,
1358
                    $this->bcc,
1359
                    $this->Subject,
1360
                    $body,
1361
                    $this->From
1362
                );
1363
                if ($result != 0) {
1364
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1365
                }
1366
            }
1367
        } else {
1368 1
            if (!@$mail = popen($sendmail, 'w')) {
1369
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1370
            }
1371 1
            fputs($mail, $header);
1372 1
            fputs($mail, $body);
1373 1
            $result = pclose($mail);
1374 1
            $this->doCallback(
1375 1
                ($result == 0),
1376 1
                $this->to,
1377 1
                $this->cc,
1378 1
                $this->bcc,
1379 1
                $this->Subject,
1380 1
                $body,
1381 1
                $this->From
1382 1
            );
1383 1
            if ($result != 0) {
1384
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1385
            }
1386
        }
1387 1
        return true;
1388
    }
1389
1390
    /**
1391
     * Send mail using the PHP mail() function.
1392
     * @param string $header The message headers
1393
     * @param string $body The message body
1394
     * @link http://www.php.net/manual/en/book.mail.php
1395
     * @throws phpmailerException
1396
     * @access protected
1397
     * @return boolean
1398
     */
1399 2
    protected function mailSend($header, $body)
1400
    {
1401 2
        $toArr = array();
1402 2
        foreach ($this->to as $toaddr) {
1403 2
            $toArr[] = $this->addrFormat($toaddr);
1404 2
        }
1405 2
        $to = implode(', ', $toArr);
1406
1407 2
        if (empty($this->Sender)) {
1408
            $params = ' ';
1409
        } else {
1410 2
            $params = sprintf('-f%s', $this->Sender);
1411
        }
1412 2
        if ($this->Sender != '' and !ini_get('safe_mode')) {
1413 2
            $old_from = ini_get('sendmail_from');
1414 2
            ini_set('sendmail_from', $this->Sender);
1415 2
        }
1416 2
        $result = false;
1417 2
        if ($this->SingleTo && count($toArr) > 1) {
1418
            foreach ($toArr as $toAddr) {
1419
                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1420
                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1421
            }
1422
        } else {
1423 2
            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1424 2
            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1425
        }
1426 2
        if (isset($old_from)) {
1427 2
            ini_set('sendmail_from', $old_from);
1428 2
        }
1429 2
        if (!$result) {
1430
            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1431
        }
1432 2
        return true;
1433
    }
1434
1435
    /**
1436
     * Get an instance to use for SMTP operations.
1437
     * Override this function to load your own SMTP implementation
1438
     * @return SMTP
1439
     */
1440 35
    public function getSMTPInstance()
1441
    {
1442 35
        if (!is_object($this->smtp)) {
1443 35
            $this->smtp = new SMTP;
1444 35
        }
1445 35
        return $this->smtp;
1446
    }
1447
1448
    /**
1449
     * Send mail via SMTP.
1450
     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1451
     * Uses the PHPMailerSMTP class by default.
1452
     * @see PHPMailer::getSMTPInstance() to use a different class.
1453
     * @param string $header The message headers
1454
     * @param string $body The message body
1455
     * @throws phpmailerException
1456
     * @uses SMTP
1457
     * @access protected
1458
     * @return boolean
1459
     */
1460 33
    protected function smtpSend($header, $body)
0 ignored issues
show
This operation has 576 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
1461
    {
1462 33
        $bad_rcpt = array();
1463 33
        if (!$this->smtpConnect($this->SMTPOptions)) {
1464
            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1465
        }
1466 33
        if ('' == $this->Sender) {
1467
            $smtp_from = $this->From;
1468
        } else {
1469 33
            $smtp_from = $this->Sender;
1470
        }
1471 33
        if (!$this->smtp->mail($smtp_from)) {
1472
            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1473
            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1474
        }
1475
1476
        // Attempt to send to all recipients
1477 33
        foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1478 33
            foreach ($togroup as $to) {
1479 33
                if (!$this->smtp->recipient($to[0])) {
1480
                    $error = $this->smtp->getError();
1481
                    $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1482
                    $isSent = false;
1483
                } else {
1484 33
                    $isSent = true;
1485
                }
1486 33
                $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1487 33
            }
1488 33
        }
1489
1490
        // Only send the DATA command if we have viable recipients
1491 33
        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1492
            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1493
        }
1494 33
        if ($this->SMTPKeepAlive) {
1495 1
            $this->smtp->reset();
1496 1
        } else {
1497 32
            $this->smtp->quit();
1498 32
            $this->smtp->close();
1499
        }
1500
        //Create error message for any bad addresses
1501 33
        if (count($bad_rcpt) > 0) {
1502
            $errstr = '';
1503
            foreach ($bad_rcpt as $bad) {
1504
                $errstr .= $bad['to'] . ': ' . $bad['error'];
1505
            }
1506
            throw new phpmailerException(
1507
                $this->lang('recipients_failed') . $errstr,
1508
                self::STOP_CONTINUE
1509
            );
1510
        }
1511 33
        return true;
1512
    }
1513
1514
    /**
1515
     * Initiate a connection to an SMTP server.
1516
     * Returns false if the operation failed.
1517
     * @param array $options An array of options compatible with stream_context_create()
1518
     * @uses SMTP
1519
     * @access public
1520
     * @throws phpmailerException
1521
     * @return boolean
1522
     */
1523 35
    public function smtpConnect($options = array())
0 ignored issues
show
This operation has 132492 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
1524
    {
1525 35
        if (is_null($this->smtp)) {
1526 35
            $this->smtp = $this->getSMTPInstance();
1527 35
        }
1528
1529
        // Already connected?
1530 35
        if ($this->smtp->connected()) {
1531 1
            return true;
1532
        }
1533
1534 35
        $this->smtp->setTimeout($this->Timeout);
1535 35
        $this->smtp->setDebugLevel($this->SMTPDebug);
1536 35
        $this->smtp->setDebugOutput($this->Debugoutput);
1537 35
        $this->smtp->setVerp($this->do_verp);
1538 35
        $hosts = explode(';', $this->Host);
1539 35
        $lastexception = null;
1540
1541 35
        foreach ($hosts as $hostentry) {
1542 35
            $hostinfo = array();
1543 35
            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1544
                // Not a valid host entry
1545
                continue;
1546
            }
1547
            // $hostinfo[2]: optional ssl or tls prefix
1548
            // $hostinfo[3]: the hostname
1549
            // $hostinfo[4]: optional port number
1550
            // The host string prefix can temporarily override the current setting for SMTPSecure
1551
            // If it's not specified, the default value is used
1552 35
            $prefix = '';
1553 35
            $secure = $this->SMTPSecure;
1554 35
            $tls = ($this->SMTPSecure == 'tls');
1555 35
            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1556 1
                $prefix = 'ssl://';
1557 1
                $tls = false; // Can't have SSL and TLS at the same time
1558 1
                $secure = 'ssl';
1559 35
            } elseif ($hostinfo[2] == 'tls') {
1560 1
                $tls = true;
1561
                // tls doesn't use a prefix
1562 1
                $secure = 'tls';
1563 1
            }
1564
            //Do we need the OpenSSL extension?
1565 35
            $sslext = defined('OPENSSL_ALGO_SHA1');
1566 35
            if ('tls' === $secure or 'ssl' === $secure) {
1567
                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1568 1
                if (!$sslext) {
1569
                    throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1570
                }
1571 1
            }
1572 35
            $host = $hostinfo[3];
1573 35
            $port = $this->Port;
1574 35
            $tport = (integer)$hostinfo[4];
1575 35
            if ($tport > 0 and $tport < 65536) {
1576 1
                $port = $tport;
1577 1
            }
1578 35
            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1579
                try {
1580 35
                    if ($this->Helo) {
1581 35
                        $hello = $this->Helo;
1582 35
                    } else {
1583
                        $hello = $this->serverHostname();
1584
                    }
1585 35
                    $this->smtp->hello($hello);
1586
                    //Automatically enable TLS encryption if:
1587
                    // * it's not disabled
1588
                    // * we have openssl extension
1589
                    // * we are not already using SSL
1590
                    // * the server offers STARTTLS
1591 35
                    if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1592
                        $tls = true;
1593
                    }
1594 35
                    if ($tls) {
1595
                        if (!$this->smtp->startTLS()) {
1596
                            throw new phpmailerException($this->lang('connect_host'));
1597
                        }
1598
                        // We must resend HELO after tls negotiation
1599
                        $this->smtp->hello($hello);
1600
                    }
1601 35
                    if ($this->SMTPAuth) {
1602
                        if (!$this->smtp->authenticate(
1603
                            $this->Username,
1604
                            $this->Password,
1605
                            $this->AuthType,
1606
                            $this->Realm,
1607
                            $this->Workstation
1608
                        )
1609
                        ) {
1610
                            throw new phpmailerException($this->lang('authenticate'));
1611
                        }
1612
                    }
1613 35
                    return true;
1614
                } catch (phpmailerException $exc) {
1615
                    $lastexception = $exc;
1616
                    $this->edebug($exc->getMessage());
1617
                    // We must have connected, but then failed TLS or Auth, so close connection nicely
1618
                    $this->smtp->quit();
1619
                }
1620
            }
1621 1
        }
1622
        // If we get here, all connection attempts have failed, so close connection hard
1623 1
        $this->smtp->close();
1624
        // As we've caught all exceptions, just report whatever the last one was
1625 1
        if ($this->exceptions and !is_null($lastexception)) {
1626
            throw $lastexception;
1627
        }
1628 1
        return false;
1629
    }
1630
1631
    /**
1632
     * Close the active SMTP session if one exists.
1633
     * @return void
1634
     */
1635 51
    public function smtpClose()
1636
    {
1637 51
        if ($this->smtp !== null) {
1638 35
            if ($this->smtp->connected()) {
1639 3
                $this->smtp->quit();
1640 3
                $this->smtp->close();
1641 3
            }
1642 35
        }
1643 51
    }
1644
1645
    /**
1646
     * Set the language for error messages.
1647
     * Returns false if it cannot load the language file.
1648
     * The default language is English.
1649
     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1650
     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1651
     * @return boolean
1652
     * @access public
1653
     */
1654 7
    public function setLanguage($langcode = 'en', $lang_path = '')
1655
    {
1656
        // Define full set of translatable strings in English
1657
        $PHPMAILER_LANG = array(
1658 7
            'authenticate' => 'SMTP Error: Could not authenticate.',
1659 7
            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1660 7
            'data_not_accepted' => 'SMTP Error: data not accepted.',
1661 7
            'empty_message' => 'Message body empty',
1662 7
            'encoding' => 'Unknown encoding: ',
1663 7
            'execute' => 'Could not execute: ',
1664 7
            'file_access' => 'Could not access file: ',
1665 7
            'file_open' => 'File Error: Could not open file: ',
1666 7
            'from_failed' => 'The following From address failed: ',
1667 7
            'instantiate' => 'Could not instantiate mail function.',
1668 7
            'invalid_address' => 'Invalid address: ',
1669 7
            'mailer_not_supported' => ' mailer is not supported.',
1670 7
            'provide_address' => 'You must provide at least one recipient email address.',
1671 7
            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1672 7
            'signing' => 'Signing Error: ',
1673 7
            'smtp_connect_failed' => 'SMTP connect() failed.',
1674 7
            'smtp_error' => 'SMTP server error: ',
1675 7
            'variable_set' => 'Cannot set or reset variable: ',
1676
            'extension_missing' => 'Extension missing: '
1677 7
        );
1678 7
        if (empty($lang_path)) {
1679
            // Calculate an absolute path so it can work if CWD is not here
1680 7
            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1681 7
        }
1682 7
        $foundlang = true;
1683 7
        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1684
        // There is no English translation file
1685 7
        if ($langcode != 'en') {
1686
            // Make sure language file path is readable
1687 1
            if (!is_readable($lang_file)) {
1688
                $foundlang = false;
1689
            } else {
1690
                // Overwrite language-specific strings.
1691
                // This way we'll never have missing translation keys.
1692 1
                $foundlang = include $lang_file;
1693
            }
1694 1
        }
1695 7
        $this->language = $PHPMAILER_LANG;
1696 7
        return (boolean)$foundlang; // Returns false if language not found
1697
    }
1698
1699
    /**
1700
     * Get the array of strings for the current language.
1701
     * @return array
1702
     */
1703
    public function getTranslations()
1704
    {
1705
        return $this->language;
1706
    }
1707
1708
    /**
1709
     * Create recipient headers.
1710
     * @access public
1711
     * @param string $type
1712
     * @param array $addr An array of recipient,
1713
     * where each recipient is a 2-element indexed array with element 0 containing an address
1714
     * and element 1 containing a name, like:
1715
     * array(array('[email protected]', 'Joe User'), array('[email protected]', 'Zoe User'))
1716
     * @return string
1717
     */
1718 42
    public function addrAppend($type, $addr)
1719
    {
1720 42
        $addresses = array();
1721 42
        foreach ($addr as $address) {
1722 42
            $addresses[] = $this->addrFormat($address);
1723 42
        }
1724 42
        return $type . ': ' . implode(', ', $addresses) . $this->LE;
1725
    }
1726
1727
    /**
1728
     * Format an address for use in a message header.
1729
     * @access public
1730
     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1731
     *      like array('[email protected]', 'Joe User')
1732
     * @return string
1733
     */
1734 42
    public function addrFormat($addr)
1735
    {
1736 42
        if (empty($addr[1])) { // No name provided
1737 3
            return $this->secureHeader($addr[0]);
1738
        } else {
1739 42
            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1740 42
                $addr[0]
1741 42
            ) . '>';
1742
        }
1743
    }
1744
1745
    /**
1746
     * Word-wrap message.
1747
     * For use with mailers that do not automatically perform wrapping
1748
     * and for quoted-printable encoded messages.
1749
     * Original written by philippe.
1750
     * @param string $message The message to wrap
1751
     * @param integer $length The line length to wrap to
1752
     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1753
     * @access public
1754
     * @return string
1755
     */
1756 4
    public function wrapText($message, $length, $qp_mode = false)
0 ignored issues
show
This operation has 444 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
1757
    {
1758 4
        if ($qp_mode) {
1759
            $soft_break = sprintf(' =%s', $this->LE);
1760
        } else {
1761 4
            $soft_break = $this->LE;
1762
        }
1763
        // If utf-8 encoding is used, we will need to make sure we don't
1764
        // split multibyte characters when we wrap
1765 4
        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1766 4
        $lelen = strlen($this->LE);
1767 4
        $crlflen = strlen(self::CRLF);
1768
1769 4
        $message = $this->fixEOL($message);
1770
        //Remove a trailing line break
1771 4
        if (substr($message, -$lelen) == $this->LE) {
1772 2
            $message = substr($message, 0, -$lelen);
1773 2
        }
1774
1775
        //Split message into lines
1776 4
        $lines = explode($this->LE, $message);
1777
        //Message will be rebuilt in here
1778 4
        $message = '';
1779 4
        foreach ($lines as $line) {
1780 4
            $words = explode(' ', $line);
1781 4
            $buf = '';
1782 4
            $firstword = true;
1783 4
            foreach ($words as $word) {
1784 4
                if ($qp_mode and (strlen($word) > $length)) {
1785
                    $space_left = $length - strlen($buf) - $crlflen;
1786
                    if (!$firstword) {
1787
                        if ($space_left > 20) {
1788
                            $len = $space_left;
1789
                            if ($is_utf8) {
1790
                                $len = $this->utf8CharBoundary($word, $len);
1791
                            } elseif (substr($word, $len - 1, 1) == '=') {
1792
                                $len--;
1793
                            } elseif (substr($word, $len - 2, 1) == '=') {
1794
                                $len -= 2;
1795
                            }
1796
                            $part = substr($word, 0, $len);
1797
                            $word = substr($word, $len);
1798
                            $buf .= ' ' . $part;
1799
                            $message .= $buf . sprintf('=%s', self::CRLF);
1800
                        } else {
1801
                            $message .= $buf . $soft_break;
1802
                        }
1803
                        $buf = '';
1804
                    }
1805
                    while (strlen($word) > 0) {
1806
                        if ($length <= 0) {
1807
                            break;
1808
                        }
1809
                        $len = $length;
1810
                        if ($is_utf8) {
1811
                            $len = $this->utf8CharBoundary($word, $len);
1812
                        } elseif (substr($word, $len - 1, 1) == '=') {
1813
                            $len--;
1814
                        } elseif (substr($word, $len - 2, 1) == '=') {
1815
                            $len -= 2;
1816
                        }
1817
                        $part = substr($word, 0, $len);
1818
                        $word = substr($word, $len);
1819
1820
                        if (strlen($word) > 0) {
1821
                            $message .= $part . sprintf('=%s', self::CRLF);
1822
                        } else {
1823
                            $buf = $part;
1824
                        }
1825
                    }
1826
                } else {
1827 4
                    $buf_o = $buf;
1828 4
                    if (!$firstword) {
1829 4
                        $buf .= ' ';
1830 4
                    }
1831 4
                    $buf .= $word;
1832
1833 4
                    if (strlen($buf) > $length and $buf_o != '') {
1834 4
                        $message .= $buf_o . $soft_break;
1835 4
                        $buf = $word;
1836 4
                    }
1837
                }
1838 4
                $firstword = false;
1839 4
            }
1840 4
            $message .= $buf . self::CRLF;
1841 4
        }
1842
1843 4
        return $message;
1844
    }
1845
1846
    /**
1847
     * Find the last character boundary prior to $maxLength in a utf-8
1848
     * quoted-printable encoded string.
1849
     * Original written by Colin Brown.
1850
     * @access public
1851
     * @param string $encodedText utf-8 QP text
1852
     * @param integer $maxLength Find the last character boundary prior to this length
1853
     * @return integer
1854
     */
1855
    public function utf8CharBoundary($encodedText, $maxLength)
1856
    {
1857
        $foundSplitPos = false;
1858
        $lookBack = 3;
1859
        while (!$foundSplitPos) {
1860
            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1861
            $encodedCharPos = strpos($lastChunk, '=');
1862
            if (false !== $encodedCharPos) {
1863
                // Found start of encoded character byte within $lookBack block.
1864
                // Check the encoded byte value (the 2 chars after the '=')
1865
                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1866
                $dec = hexdec($hex);
1867
                if ($dec < 128) {
1868
                    // Single byte character.
1869
                    // If the encoded char was found at pos 0, it will fit
1870
                    // otherwise reduce maxLength to start of the encoded char
1871
                    if ($encodedCharPos > 0) {
1872
                        $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1873
                    }
1874
                    $foundSplitPos = true;
1875
                } elseif ($dec >= 192) {
1876
                    // First byte of a multi byte character
1877
                    // Reduce maxLength to split at start of character
1878
                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1879
                    $foundSplitPos = true;
1880
                } elseif ($dec < 192) {
1881
                    // Middle byte of a multi byte character, look further back
1882
                    $lookBack += 3;
1883
                }
1884
            } else {
1885
                // No encoded character found
1886
                $foundSplitPos = true;
1887
            }
1888
        }
1889
        return $maxLength;
1890
    }
1891
1892
    /**
1893
     * Apply word wrapping to the message body.
1894
     * Wraps the message body to the number of chars set in the WordWrap property.
1895
     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
1896
     * This is called automatically by createBody(), so you don't need to call it yourself.
1897
     * @access public
1898
     * @return void
1899
     */
1900 41
    public function setWordWrap()
1901
    {
1902 41
        if ($this->WordWrap < 1) {
1903 38
            return;
1904
        }
1905
1906 3
        switch ($this->message_type) {
1907 3
            case 'alt':
1908 3
            case 'alt_inline':
1909 3
            case 'alt_attach':
1910 3
            case 'alt_inline_attach':
1911 1
                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1912 1
                break;
1913 2
            default:
1914 2
                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1915 2
                break;
1916 3
        }
1917 3
    }
1918
1919
    /**
1920
     * Assemble message headers.
1921
     * @access public
1922
     * @return string The assembled headers
1923
     */
1924 42
    public function createHeader()
0 ignored issues
show
This operation has 80640 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
1925
    {
1926 42
        $result = '';
1927
1928 42
        if ($this->MessageDate == '') {
1929 42
            $this->MessageDate = self::rfcDate();
1930 42
        }
1931 42
        $result .= $this->headerLine('Date', $this->MessageDate);
1932
1933
        // To be created automatically by mail()
1934 42
        if ($this->SingleTo) {
1935
            if ($this->Mailer != 'mail') {
1936
                foreach ($this->to as $toaddr) {
1937
                    $this->SingleToArray[] = $this->addrFormat($toaddr);
1938
                }
1939
            }
1940
        } else {
1941 42
            if (count($this->to) > 0) {
1942 41
                if ($this->Mailer != 'mail') {
1943 39
                    $result .= $this->addrAppend('To', $this->to);
1944 39
                }
1945 42
            } elseif (count($this->cc) == 0) {
1946 1
                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1947 1
            }
1948
        }
1949
1950 42
        $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1951
1952
        // sendmail and mail() extract Cc from the header before sending
1953 42
        if (count($this->cc) > 0) {
1954 39
            $result .= $this->addrAppend('Cc', $this->cc);
1955 39
        }
1956
1957
        // sendmail and mail() extract Bcc from the header before sending
1958
        if ((
1959 42
                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1960 40
            )
1961 42
            and count($this->bcc) > 0
1962 42
        ) {
1963
            $result .= $this->addrAppend('Bcc', $this->bcc);
1964
        }
1965
1966 42
        if (count($this->ReplyTo) > 0) {
1967 42
            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1968 42
        }
1969
1970
        // mail() sets the subject itself
1971 42
        if ($this->Mailer != 'mail') {
1972 40
            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1973 40
        }
1974
1975 42
        if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
1976 1
            $this->lastMessageID = $this->MessageID;
1977 1
        } else {
1978 42
            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
1979
        }
1980 42
        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
1981 42
        if (!is_null($this->Priority)) {
1982 42
            $result .= $this->headerLine('X-Priority', $this->Priority);
1983 42
        }
1984 42
        if ($this->XMailer == '') {
1985 42
            $result .= $this->headerLine(
1986 42
                'X-Mailer',
1987 42
                'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
1988 42
            );
1989 42
        } else {
1990
            $myXmailer = trim($this->XMailer);
1991
            if ($myXmailer) {
1992
                $result .= $this->headerLine('X-Mailer', $myXmailer);
1993
            }
1994
        }
1995
1996 42
        if ($this->ConfirmReadingTo != '') {
1997 1
            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
1998 1
        }
1999
2000
        // Add custom headers
2001 42
        foreach ($this->CustomHeader as $header) {
2002
            $result .= $this->headerLine(
2003
                trim($header[0]),
2004
                $this->encodeHeader(trim($header[1]))
2005
            );
2006 42
        }
2007 42
        if (!$this->sign_key_file) {
2008 40
            $result .= $this->headerLine('MIME-Version', '1.0');
2009 40
            $result .= $this->getMailMIME();
2010 40
        }
2011
2012 42
        return $result;
2013
    }
2014
2015
    /**
2016
     * Get the message MIME type headers.
2017
     * @access public
2018
     * @return string
2019
     */
2020 42
    public function getMailMIME()
2021
    {
2022 42
        $result = '';
2023 42
        $ismultipart = true;
2024 42
        switch ($this->message_type) {
2025 42
            case 'inline':
2026 3
                $result .= $this->headerLine('Content-Type', 'multipart/related;');
2027 3
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2028 3
                break;
2029 39
            case 'attach':
2030 39
            case 'inline_attach':
2031 39
            case 'alt_attach':
2032 39
            case 'alt_inline_attach':
2033 6
                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2034 6
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2035 6
                break;
2036 33
            case 'alt':
2037 33
            case 'alt_inline':
2038 5
                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2039 5
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2040 5
                break;
2041 28
            default:
2042
                // Catches case 'plain': and case '':
2043 28
                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2044 28
                $ismultipart = false;
2045 28
                break;
2046 42
        }
2047
        // RFC1341 part 5 says 7bit is assumed if not specified
2048 42
        if ($this->Encoding != '7bit') {
2049
            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2050 42
            if ($ismultipart) {
2051 14
                if ($this->Encoding == '8bit') {
2052 14
                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2053 14
                }
2054
                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2055 14
            } else {
2056 28
                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2057
            }
2058 42
        }
2059
2060 42
        if ($this->Mailer != 'mail') {
2061 40
            $result .= $this->LE;
2062 40
        }
2063
2064 42
        return $result;
2065
    }
2066
2067
    /**
2068
     * Returns the whole MIME message.
2069
     * Includes complete headers and body.
2070
     * Only valid post preSend().
2071
     * @see PHPMailer::preSend()
2072
     * @access public
2073
     * @return string
2074
     */
2075 9
    public function getSentMIMEMessage()
2076
    {
2077 9
        return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
2078
    }
2079
2080
    /**
2081
     * Assemble the message body.
2082
     * Returns an empty string on failure.
2083
     * @access public
2084
     * @throws phpmailerException
2085
     * @return string The assembled message body
2086
     */
2087 41
    public function createBody()
0 ignored issues
show
This operation has 29160 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
2088
    {
2089 41
        $body = '';
2090
        //Create unique IDs and preset boundaries
2091 41
        $this->uniqueid = md5(uniqid(time()));
2092 41
        $this->boundary[1] = 'b1_' . $this->uniqueid;
2093 41
        $this->boundary[2] = 'b2_' . $this->uniqueid;
2094 41
        $this->boundary[3] = 'b3_' . $this->uniqueid;
2095
2096 41
        if ($this->sign_key_file) {
2097 2
            $body .= $this->getMailMIME() . $this->LE;
2098 2
        }
2099
2100 41
        $this->setWordWrap();
2101
2102 41
        $bodyEncoding = $this->Encoding;
2103 41
        $bodyCharSet = $this->CharSet;
2104
        //Can we do a 7-bit downgrade?
2105 41
        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2106 33
            $bodyEncoding = '7bit';
2107 33
            $bodyCharSet = 'us-ascii';
2108 33
        }
2109
        //If lines are too long, and we're not already using an encoding that will shorten them,
2110
        //change to quoted-printable transfer encoding
2111 41
        if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
2112 2
            $this->Encoding = 'quoted-printable';
2113 2
            $bodyEncoding = 'quoted-printable';
2114 2
        }
2115
2116 41
        $altBodyEncoding = $this->Encoding;
2117 41
        $altBodyCharSet = $this->CharSet;
2118
        //Can we do a 7-bit downgrade?
2119 41
        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2120 35
            $altBodyEncoding = '7bit';
2121 35
            $altBodyCharSet = 'us-ascii';
2122 35
        }
2123
        //If lines are too long, and we're not already using an encoding that will shorten them,
2124
        //change to quoted-printable transfer encoding
2125 41
        if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
2126
            $altBodyEncoding = 'quoted-printable';
2127
        }
2128
        //Use this as a preamble in all multipart message types
2129 41
        $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
2130 41
        switch ($this->message_type) {
2131 41
            case 'inline':
2132 3
                $body .= $mimepre;
2133 3
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2134 3
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2135 3
                $body .= $this->LE . $this->LE;
2136 3
                $body .= $this->attachAll('inline', $this->boundary[1]);
2137 3
                break;
2138 38
            case 'attach':
2139 4
                $body .= $mimepre;
2140 4
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2141 4
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2142 4
                $body .= $this->LE . $this->LE;
2143 4
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2144 4
                break;
2145 34
            case 'inline_attach':
2146 1
                $body .= $mimepre;
2147 1
                $body .= $this->textLine('--' . $this->boundary[1]);
2148 1
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2149 1
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2150 1
                $body .= $this->LE;
2151 1
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2152 1
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2153 1
                $body .= $this->LE . $this->LE;
2154 1
                $body .= $this->attachAll('inline', $this->boundary[2]);
2155 1
                $body .= $this->LE;
2156 1
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2157 1
                break;
2158 33
            case 'alt':
2159 3
                $body .= $mimepre;
2160 3
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2161 3
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2162 3
                $body .= $this->LE . $this->LE;
2163 3
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2164 3
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2165 3
                $body .= $this->LE . $this->LE;
2166 3
                if (!empty($this->Ical)) {
2167 1
                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2168 1
                    $body .= $this->encodeString($this->Ical, $this->Encoding);
2169 1
                    $body .= $this->LE . $this->LE;
2170 1
                }
2171 3
                $body .= $this->endBoundary($this->boundary[1]);
2172 3
                break;
2173 30
            case 'alt_inline':
2174 2
                $body .= $mimepre;
2175 2
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2176 2
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2177 2
                $body .= $this->LE . $this->LE;
2178 2
                $body .= $this->textLine('--' . $this->boundary[1]);
2179 2
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2180 2
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2181 2
                $body .= $this->LE;
2182 2
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2183 2
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2184 2
                $body .= $this->LE . $this->LE;
2185 2
                $body .= $this->attachAll('inline', $this->boundary[2]);
2186 2
                $body .= $this->LE;
2187 2
                $body .= $this->endBoundary($this->boundary[1]);
2188 2
                break;
2189 28
            case 'alt_attach':
2190 1
                $body .= $mimepre;
2191 1
                $body .= $this->textLine('--' . $this->boundary[1]);
2192 1
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2193 1
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2194 1
                $body .= $this->LE;
2195 1
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2196 1
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2197 1
                $body .= $this->LE . $this->LE;
2198 1
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2199 1
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2200 1
                $body .= $this->LE . $this->LE;
2201 1
                $body .= $this->endBoundary($this->boundary[2]);
2202 1
                $body .= $this->LE;
2203 1
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2204 1
                break;
2205 27
            case 'alt_inline_attach':
2206
                $body .= $mimepre;
2207
                $body .= $this->textLine('--' . $this->boundary[1]);
2208
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2209
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2210
                $body .= $this->LE;
2211
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2212
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2213
                $body .= $this->LE . $this->LE;
2214
                $body .= $this->textLine('--' . $this->boundary[2]);
2215
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2216
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2217
                $body .= $this->LE;
2218
                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2219
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2220
                $body .= $this->LE . $this->LE;
2221
                $body .= $this->attachAll('inline', $this->boundary[3]);
2222
                $body .= $this->LE;
2223
                $body .= $this->endBoundary($this->boundary[2]);
2224
                $body .= $this->LE;
2225
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2226
                break;
2227 27
            default:
2228
                // catch case 'plain' and case ''
2229 27
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2230 27
                break;
2231 41
        }
2232
2233 41
        if ($this->isError()) {
2234
            $body = '';
2235 41
        } elseif ($this->sign_key_file) {
2236
            try {
2237 2
                if (!defined('PKCS7_TEXT')) {
2238
                    throw new phpmailerException($this->lang('extension_missing') . 'openssl');
2239
                }
2240
                // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2241 2
                $file = tempnam(sys_get_temp_dir(), 'mail');
2242 2
                if (false === file_put_contents($file, $body)) {
2243
                    throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2244
                }
2245 2
                $signed = tempnam(sys_get_temp_dir(), 'signed');
2246
                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2247 2
                if (empty($this->sign_extracerts_file)) {
2248 1
                    $sign = @openssl_pkcs7_sign(
2249 1
                        $file,
2250 1
                        $signed,
2251 1
                        'file://' . realpath($this->sign_cert_file),
2252 1
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2253
                        null
2254 1
                    );
2255 1
                } else {
2256 1
                    $sign = @openssl_pkcs7_sign(
2257 1
                        $file,
2258 1
                        $signed,
2259 1
                        'file://' . realpath($this->sign_cert_file),
2260 1
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2261 1
                        null,
2262 1
                        PKCS7_DETACHED,
2263 1
                        $this->sign_extracerts_file
2264 1
                    );
2265
                }
2266 2
                if ($sign) {
2267 2
                    @unlink($file);
2268 2
                    $body = file_get_contents($signed);
2269 2
                    @unlink($signed);
2270
                    //The message returned by openssl contains both headers and body, so need to split them up
2271 2
                    $parts = explode("\n\n", $body, 2);
2272 2
                    $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2273 2
                    $body = $parts[1];
2274 2
                } else {
2275
                    @unlink($file);
2276
                    @unlink($signed);
2277
                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
2278
                }
2279 2
            } catch (phpmailerException $exc) {
2280
                $body = '';
2281
                if ($this->exceptions) {
2282
                    throw $exc;
2283
                }
2284
            }
2285 2
        }
2286 41
        return $body;
2287
    }
2288
2289
    /**
2290
     * Return the start of a message boundary.
2291
     * @access protected
2292
     * @param string $boundary
2293
     * @param string $charSet
2294
     * @param string $contentType
2295
     * @param string $encoding
2296
     * @return string
2297
     */
2298 14
    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2299
    {
2300 14
        $result = '';
2301 14
        if ($charSet == '') {
2302 1
            $charSet = $this->CharSet;
2303 1
        }
2304 14
        if ($contentType == '') {
2305 8
            $contentType = $this->ContentType;
2306 8
        }
2307 14
        if ($encoding == '') {
2308 1
            $encoding = $this->Encoding;
2309 1
        }
2310 14
        $result .= $this->textLine('--' . $boundary);
2311 14
        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2312 14
        $result .= $this->LE;
2313
        // RFC1341 part 5 says 7bit is assumed if not specified
2314 14
        if ($encoding != '7bit') {
2315 4
            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2316 4
        }
2317 14
        $result .= $this->LE;
2318
2319 14
        return $result;
2320
    }
2321
2322
    /**
2323
     * Return the end of a message boundary.
2324
     * @access protected
2325
     * @param string $boundary
2326
     * @return string
2327
     */
2328 6
    protected function endBoundary($boundary)
2329
    {
2330 6
        return $this->LE . '--' . $boundary . '--' . $this->LE;
2331
    }
2332
2333
    /**
2334
     * Set the message type.
2335
     * PHPMailer only supports some preset message types,
2336
     * not arbitrary MIME structures.
2337
     * @access protected
2338
     * @return void
2339
     */
2340 41
    protected function setMessageType()
2341
    {
2342 41
        $type = array();
2343 41
        if ($this->alternativeExists()) {
2344 6
            $type[] = 'alt';
2345 6
        }
2346 41
        if ($this->inlineImageExists()) {
2347 6
            $type[] = 'inline';
2348 6
        }
2349 41
        if ($this->attachmentExists()) {
2350 6
            $type[] = 'attach';
2351 6
        }
2352 41
        $this->message_type = implode('_', $type);
2353 41
        if ($this->message_type == '') {
2354 27
            $this->message_type = 'plain';
2355 27
        }
2356 41
    }
2357
2358
    /**
2359
     * Format a header line.
2360
     * @access public
2361
     * @param string $name
2362
     * @param string $value
2363
     * @return string
2364
     */
2365 42
    public function headerLine($name, $value)
2366
    {
2367 42
        return $name . ': ' . $value . $this->LE;
2368
    }
2369
2370
    /**
2371
     * Return a formatted mail line.
2372
     * @access public
2373
     * @param string $value
2374
     * @return string
2375
     */
2376 42
    public function textLine($value)
2377
    {
2378 42
        return $value . $this->LE;
2379
    }
2380
2381
    /**
2382
     * Add an attachment from a path on the filesystem.
2383
     * Returns false if the file could not be found or read.
2384
     * @param string $path Path to the attachment.
2385
     * @param string $name Overrides the attachment name.
2386
     * @param string $encoding File encoding (see $Encoding).
2387
     * @param string $type File extension (MIME) type.
2388
     * @param string $disposition Disposition to use
2389
     * @throws phpmailerException
2390
     * @return boolean
2391
     */
2392 5
    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2393
    {
2394
        try {
2395 5
            if (!@is_file($path)) {
2396 1
                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2397
            }
2398
2399
            // If a MIME type is not specified, try to work it out from the file name
2400 5
            if ($type == '') {
2401 5
                $type = self::filenameToType($path);
2402 5
            }
2403
2404 5
            $filename = basename($path);
2405 5
            if ($name == '') {
2406 1
                $name = $filename;
2407 1
            }
2408
2409 5
            $this->attachment[] = array(
2410 5
                0 => $path,
2411 5
                1 => $filename,
2412 5
                2 => $name,
2413 5
                3 => $encoding,
2414 5
                4 => $type,
2415 5
                5 => false, // isStringAttachment
2416 5
                6 => $disposition,
2417
                7 => 0
2418 5
            );
2419
2420 5
        } catch (phpmailerException $exc) {
2421 1
            $this->setError($exc->getMessage());
2422 1
            $this->edebug($exc->getMessage());
2423 1
            if ($this->exceptions) {
2424
                throw $exc;
2425
            }
2426 1
            return false;
2427
        }
2428 5
        return true;
2429
    }
2430
2431
    /**
2432
     * Return the array of attachments.
2433
     * @return array
2434
     */
2435 40
    public function getAttachments()
2436
    {
2437 40
        return $this->attachment;
2438
    }
2439
2440
    /**
2441
     * Attach all file, string, and binary attachments to the message.
2442
     * Returns an empty string on failure.
2443
     * @access protected
2444
     * @param string $disposition_type
2445
     * @param string $boundary
2446
     * @return string
2447
     */
2448 11
    protected function attachAll($disposition_type, $boundary)
0 ignored issues
show
This operation has 1538 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

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

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3870
    {
3871 36
        if (!empty($this->action_function) && is_callable($this->action_function)) {
3872
            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3873
            call_user_func_array($this->action_function, $params);
3874
        }
3875 36
    }
3876
}
3877
3878
/**
3879
 * PHPMailer exception handler
3880
 * @package PHPMailer
3881
 */
3882
class phpmailerException extends Exception
3883
{
3884
    /**
3885
     * Prettify error message output
3886
     * @return string
3887
     */
3888
    public function errorMessage()
3889
    {
3890
        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3891
        return $errorMsg;
3892
    }
3893
}
3894