Completed
Push — master ( af5976...f92b36 )
by Marcus
06:07
created

PHPMailer::generateId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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

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

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

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

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

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

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2338 15
                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
2339 1
                }
2340 1
            } catch (phpmailerException $exc) {
2341 15
                $body = '';
2342 15
                if ($this->exceptions) {
2343 15
                    throw $exc;
2344
                }
2345 15
            }
2346 5
        }
2347 5
        return $body;
2348 15
    }
2349
2350 15
    /**
2351
     * Return the start of a message boundary.
2352
     * @access protected
2353
     * @param string $boundary
2354
     * @param string $charSet
2355
     * @param string $contentType
2356
     * @param string $encoding
2357
     * @return string
2358
     */
2359 7
    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2360
    {
2361 7
        $result = '';
2362
        if ($charSet == '') {
2363
            $charSet = $this->CharSet;
2364
        }
2365
        if ($contentType == '') {
2366
            $contentType = $this->ContentType;
2367
        }
2368
        if ($encoding == '') {
2369
            $encoding = $this->Encoding;
2370 41
        }
2371
        $result .= $this->textLine('--' . $boundary);
2372 41
        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2373 41
        $result .= $this->LE;
2374 7
        // RFC1341 part 5 says 7bit is assumed if not specified
2375 7
        if ($encoding != '7bit') {
2376 41
            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2377 6
        }
2378 6
        $result .= $this->LE;
2379 41
2380 6
        return $result;
2381 6
    }
2382 41
2383 41
    /**
2384
     * Return the end of a message boundary.
2385 26
     * @access protected
2386 26
     * @param string $boundary
2387 41
     * @return string
2388
     */
2389
    protected function endBoundary($boundary)
2390
    {
2391
        return $this->LE . '--' . $boundary . '--' . $this->LE;
2392
    }
2393
2394
    /**
2395
     * Set the message type.
2396 42
     * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2397
     * @access protected
2398 42
     * @return void
2399
     */
2400
    protected function setMessageType()
2401
    {
2402
        $type = array();
2403
        if ($this->alternativeExists()) {
2404
            $type[] = 'alt';
2405
        }
2406
        if ($this->inlineImageExists()) {
2407 42
            $type[] = 'inline';
2408
        }
2409 42
        if ($this->attachmentExists()) {
2410
            $type[] = 'attach';
2411
        }
2412
        $this->message_type = implode('_', $type);
2413
        if ($this->message_type == '') {
2414
            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2415
            $this->message_type = 'plain';
2416
        }
2417
    }
2418
2419
    /**
2420
     * Format a header line.
2421
     * @access public
2422
     * @param string $name
2423 5
     * @param string $value
2424
     * @return string
2425
     */
2426 5
    public function headerLine($name, $value)
2427 1
    {
2428
        return $name . ': ' . $value . $this->LE;
2429
    }
2430
2431 5
    /**
2432 5
     * Return a formatted mail line.
2433 5
     * @access public
2434
     * @param string $value
2435 5
     * @return string
2436 5
     */
2437 1
    public function textLine($value)
2438 1
    {
2439
        return $value . $this->LE;
2440 5
    }
2441 5
2442 5
    /**
2443 5
     * Add an attachment from a path on the filesystem.
2444 5
     * Returns false if the file could not be found or read.
2445 5
     * @param string $path Path to the attachment.
2446 5
     * @param string $name Overrides the attachment name.
2447 5
     * @param string $encoding File encoding (see $Encoding).
2448
     * @param string $type File extension (MIME) type.
2449 5
     * @param string $disposition Disposition to use
2450
     * @throws phpmailerException
2451 5
     * @return boolean
2452 1
     */
2453 1
    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2454 1
    {
2455
        try {
2456
            if (!@is_file($path)) {
2457 1
                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2458
            }
2459 5
2460
            // If a MIME type is not specified, try to work it out from the file name
2461
            if ($type == '') {
2462
                $type = self::filenameToType($path);
2463
            }
2464
2465
            $filename = basename($path);
2466 40
            if ($name == '') {
2467
                $name = $filename;
2468 40
            }
2469
2470
            $this->attachment[] = array(
2471
                0 => $path,
2472
                1 => $filename,
2473
                2 => $name,
2474
                3 => $encoding,
2475
                4 => $type,
2476
                5 => false, // isStringAttachment
2477
                6 => $disposition,
2478
                7 => 0
2479 11
            );
2480
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
2481
        } catch (phpmailerException $exc) {
2482 11
            $this->setError($exc->getMessage());
2483 11
            $this->edebug($exc->getMessage());
2484 11
            if ($this->exceptions) {
2485
                throw $exc;
2486
            }
2487 11
            return false;
2488
        }
2489 11
        return true;
2490
    }
2491 11
2492 11
    /**
2493 11
     * Return the array of attachments.
2494 11
     * @return array
2495 2
     */
2496 2
    public function getAttachments()
2497 9
    {
2498
        return $this->attachment;
2499
    }
2500 11
2501 11
    /**
2502 1
     * Attach all file, string, and binary attachments to the message.
2503
     * Returns an empty string on failure.
2504 11
     * @access protected
2505 11
     * @param string $disposition_type
2506 11
     * @param string $boundary
2507 11
     * @return string
2508 11
     */
2509 11
    protected function attachAll($disposition_type, $boundary)
0 ignored issues
show
Complexity introduced by
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...
2510 11
    {
2511
        // Return text of body
2512
        $mime = array();
2513 11
        $cidUniq = array();
2514
        $incl = array();
2515 11
2516
        // Add all attachments
2517 11
        foreach ($this->attachment as $attachment) {
2518 10
            // Check if it is a valid disposition_filter
2519 10
            if ($attachment[6] == $disposition_type) {
2520 10
                // Check for string attachment
2521 10
                $string = '';
2522 10
                $path = '';
2523 10
                $bString = $attachment[5];
2524 10
                if ($bString) {
2525 1
                    $string = $attachment[0];
2526 1
                } else {
2527 1
                    $path = $attachment[0];
2528 1
                }
2529 1
2530
                $inclhash = md5(serialize($attachment));
2531
                if (in_array($inclhash, $incl)) {
2532 11
                    continue;
2533 11
                }
2534 11
                $incl[] = $inclhash;
2535
                $name = $attachment[2];
2536 11
                $encoding = $attachment[3];
2537 6
                $type = $attachment[4];
2538 6
                $disposition = $attachment[6];
2539
                $cid = $attachment[7];
2540
                if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
2541
                    continue;
2542
                }
2543
                $cidUniq[$cid] = true;
2544 11
2545 11
                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2546 11
                //Only include a filename property if we have one
2547
                if (!empty($name)) {
2548
                    $mime[] = sprintf(
2549
                        'Content-Type: %s; name="%s"%s',
2550
                        $type,
2551
                        $this->encodeHeader($this->secureHeader($name)),
2552
                        $this->LE
2553
                    );
2554 11
                } else {
2555 10
                    $mime[] = sprintf(
2556 10
                        'Content-Type: %s%s',
2557 10
                        $type,
2558 10
                        $this->LE
2559 10
                    );
2560 10
                }
2561 10
                // RFC1341 part 5 says 7bit is assumed if not specified
2562 1
                if ($encoding != '7bit') {
2563 1
                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2564 1
                }
2565 1
2566 1
                if ($disposition == 'inline') {
2567
                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2568
                }
2569 11
2570
                // If a filename contains any of these chars, it should be quoted,
2571
                // but not otherwise: RFC2183 & RFC2045 5.1
2572
                // Fixes a warning in IETF's msglint MIME checker
2573
                // Allow for bypassing the Content-Disposition header totally
2574 11
                if (!(empty($disposition))) {
2575 2
                    $encoded_name = $this->encodeHeader($this->secureHeader($name));
2576 2
                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2577
                        $mime[] = sprintf(
2578
                            'Content-Disposition: %s; filename="%s"%s',
2579 2
                            $disposition,
2580 2
                            $encoded_name,
2581 9
                            $this->LE . $this->LE
2582 9
                        );
2583
                    } else {
2584
                        if (!empty($encoded_name)) {
2585 9
                            $mime[] = sprintf(
2586
                                'Content-Disposition: %s; filename=%s%s',
2587 11
                                $disposition,
2588 11
                                $encoded_name,
2589
                                $this->LE . $this->LE
2590 11
                            );
2591
                        } else {
2592 11
                            $mime[] = sprintf(
2593
                                'Content-Disposition: %s%s',
2594
                                $disposition,
2595
                                $this->LE . $this->LE
2596
                            );
2597
                        }
2598
                    }
2599
                } else {
2600
                    $mime[] = $this->LE;
2601
                }
2602
2603
                // Encode as string attachment
2604 9
                if ($bString) {
2605
                    $mime[] = $this->encodeString($string, $encoding);
2606
                    if ($this->isError()) {
2607 9
                        return '';
2608
                    }
2609
                    $mime[] = $this->LE . $this->LE;
2610 9
                } else {
2611 9
                    $mime[] = $this->encodeFile($path, $encoding);
2612
                    if ($this->isError()) {
2613
                        return '';
2614
                    }
2615
                    $mime[] = $this->LE . $this->LE;
2616
                }
2617
            }
2618
        }
2619
2620
        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2621 9
2622 9
        return implode('', $mime);
2623 9
    }
2624
2625
    /**
2626
     * Encode a file attachment in requested format.
2627
     * Returns an empty string on failure.
2628
     * @param string $path The full path to the file
2629
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2630 9
     * @throws phpmailerException
2631
     * @access protected
2632
     * @return string
2633
     */
2634
    protected function encodeFile($path, $encoding = 'base64')
2635
    {
2636
        try {
2637
            if (!is_readable($path)) {
2638
                throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2639
            }
2640
            $magic_quotes = get_magic_quotes_runtime();
2641
            if ($magic_quotes) {
2642
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2643
                    set_magic_quotes_runtime(false);
2644
                } else {
2645 41
                    //Doesn't exist in PHP 5.4, but we don't need to check because
2646
                    //get_magic_quotes_runtime always returns false in 5.4+
2647 41
                    //so it will never get here
2648 41
                    ini_set('magic_quotes_runtime', false);
2649 41
                }
2650 12
            }
2651 12
            $file_buffer = file_get_contents($path);
2652 40
            $file_buffer = $this->encodeString($file_buffer, $encoding);
2653 40
            if ($magic_quotes) {
2654 37
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2655
                    set_magic_quotes_runtime($magic_quotes);
2656 37
                } else {
2657 6
                    ini_set('magic_quotes_runtime', $magic_quotes);
2658 6
                }
2659 37
            }
2660 3
            return $file_buffer;
2661
        } catch (Exception $exc) {
2662
            $this->setError($exc->getMessage());
2663 3
            return '';
2664 3
        }
2665 3
    }
2666
2667
    /**
2668
     * Encode a string in requested format.
2669 41
     * Returns an empty string on failure.
2670 41
     * @param string $str The text to encode
2671
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2672
     * @access public
2673
     * @return string
2674
     */
2675
    public function encodeString($str, $encoding = 'base64')
2676
    {
2677
        $encoded = '';
2678
        switch (strtolower($encoding)) {
2679
            case 'base64':
2680
                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2681 42
                break;
2682
            case '7bit':
2683 42
            case '8bit':
2684 42
                $encoded = $this->fixEOL($str);
2685 42
                // Make sure it ends with a line break
2686 42
                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2687
                    $encoded .= $this->LE;
2688 42
                }
2689 42
                break;
2690 42
            case 'binary':
2691
                $encoded = $str;
2692 2
                break;
2693
            case 'quoted-printable':
2694
                $encoded = $this->encodeQP($str);
2695
                break;
2696
            default:
2697
                $this->setError($this->lang('encoding') . $encoding);
2698 42
                break;
2699
        }
2700
        return $encoded;
2701 42
    }
2702 42
2703 42
    /**
2704 42
     * Encode a header string optimally.
2705 42
     * Picks shortest of Q, B, quoted-printable or none.
2706
     * @access public
2707
     * @param string $str
2708 42
     * @param string $position
2709 42
     * @return string
2710
     */
2711
    public function encodeHeader($str, $position = 'text')
2712
    {
2713
        $matchcount = 0;
2714
        switch (strtolower($position)) {
2715
            case 'phrase':
2716
                if (!preg_match('/[\200-\377]/', $str)) {
2717
                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
2718
                    $encoded = addcslashes($str, "\0..\37\177\\\"");
2719
                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2720
                        return ($encoded);
2721
                    } else {
2722
                        return ("\"$encoded\"");
2723
                    }
2724
                }
2725
                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2726
                break;
2727
            /** @noinspection PhpMissingBreakStatementInspection */
2728
            case 'comment':
2729
                $matchcount = preg_match_all('/[()"]/', $str, $matches);
2730
                // Intentional fall-through
2731
            case 'text':
2732
            default:
2733
                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2734
                break;
2735
        }
2736
2737
        //There are no chars that need encoding
2738
        if ($matchcount == 0) {
2739
            return ($str);
2740
        }
2741
2742
        $maxlen = 75 - 7 - strlen($this->CharSet);
2743
        // Try to select the encoding which should produce the shortest output
2744
        if ($matchcount > strlen($str) / 3) {
2745
            // More than a third of the content will need encoding, so B encoding will be most efficient
2746
            $encoding = 'B';
2747
            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2748
                // Use a custom function which correctly encodes and wraps long
2749
                // multibyte strings without breaking lines within a character
2750
                $encoded = $this->base64EncodeWrapMB($str, "\n");
2751
            } else {
2752
                $encoded = base64_encode($str);
2753
                $maxlen -= $maxlen % 4;
2754
                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2755
            }
2756
        } else {
2757
            $encoding = 'Q';
2758
            $encoded = $this->encodeQ($str, $position);
2759 56
            $encoded = $this->wrapText($encoded, $maxlen, true);
2760
            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2761 56
        }
2762
2763
        $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2764
        $encoded = trim(str_replace("\n", $this->LE, $encoded));
2765
2766
        return $encoded;
2767
    }
2768
2769
    /**
2770
     * Check if a string contains multi-byte characters.
2771
     * @access public
2772
     * @param string $str multi-byte text to wrap encode
2773
     * @return boolean
2774
     */
2775
    public function hasMultiBytes($str)
2776
    {
2777
        if (function_exists('mb_strlen')) {
2778
            return (strlen($str) > mb_strlen($str, $this->CharSet));
2779
        } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2780
            return false;
2781
        }
2782
    }
2783
2784
    /**
2785
     * Does a string contain any 8-bit chars (in any charset)?
2786
     * @param string $text
2787
     * @return boolean
2788
     */
2789
    public function has8bitChars($text)
2790
    {
2791
        return (boolean)preg_match('/[\x80-\xFF]/', $text);
2792
    }
2793
2794
    /**
2795
     * Encode and wrap long multibyte strings for mail headers
2796
     * without breaking lines within a character.
2797
     * Adapted from a function by paravoid
2798
     * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2799
     * @access public
2800
     * @param string $str multi-byte text to wrap encode
2801
     * @param string $linebreak string to use as linefeed/end-of-line
2802
     * @return string
2803
     */
2804
    public function base64EncodeWrapMB($str, $linebreak = null)
2805
    {
2806
        $start = '=?' . $this->CharSet . '?B?';
2807
        $end = '?=';
2808
        $encoded = '';
2809
        if ($linebreak === null) {
2810
            $linebreak = $this->LE;
2811
        }
2812
2813
        $mb_length = mb_strlen($str, $this->CharSet);
2814
        // Each line must have length <= 75, including $start and $end
2815
        $length = 75 - strlen($start) - strlen($end);
2816 3
        // Average multi-byte ratio
2817
        $ratio = $mb_length / strlen($str);
2818
        // Base64 has a 4:3 ratio
2819 3
        $avgLength = floor($length * $ratio * .75);
2820 3
2821
        for ($i = 0; $i < $mb_length; $i += $offset) {
2822
            $lookBack = 0;
2823
            do {
2824
                $offset = $avgLength - $lookBack;
2825
                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2826
                $chunk = base64_encode($chunk);
2827
                $lookBack++;
2828
            } while (strlen($chunk) > $length);
2829
            $encoded .= $chunk . $linebreak;
2830
        }
2831
2832
        // Chomp the last linefeed
2833
        $encoded = substr($encoded, 0, -strlen($linebreak));
2834
        return $encoded;
2835
    }
2836
2837
    /**
2838
     * Encode a string in quoted-printable format.
2839
     * According to RFC2045 section 6.7.
2840
     * @access public
2841
     * @param string $string The text to encode
2842
     * @param integer $line_max Number of chars allowed on a line before wrapping
2843
     * @return string
2844
     * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2845
     */
2846
    public function encodeQP($string, $line_max = 76)
2847
    {
2848
        // Use native function if it's available (>= PHP5.3)
2849
        if (function_exists('quoted_printable_encode')) {
2850
            return quoted_printable_encode($string);
2851
        }
2852
        // Fall back to a pure PHP implementation
2853
        $string = str_replace(
2854
            array('%20', '%0D%0A.', '%0D%0A', '%'),
2855
            array(' ', "\r\n=2E", "\r\n", '='),
2856
            rawurlencode($string)
2857 1
        );
2858
        return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2859
    }
2860 1
2861 1
    /**
2862 1
     * Backward compatibility wrapper for an old QP encoding function that was removed.
2863 1
     * @see PHPMailer::encodeQP()
2864
     * @access public
2865 1
     * @param string $string
2866 1
     * @param integer $line_max
2867
     * @param boolean $space_conv
2868 1
     * @return string
2869
     * @deprecated Use encodeQP instead.
2870 1
     */
2871
    public function encodeQPphp(
2872
        $string,
2873 1
        $line_max = 76,
2874 1
        /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
0 ignored issues
show
Unused Code introduced by
The parameter $space_conv is not used and could be removed.

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

Loading history...
2875
    ) {
2876
        return $this->encodeQP($string, $line_max);
2877 1
    }
2878 1
2879 1
    /**
2880 1
     * Encode a string using Q encoding.
2881 1
     * @link http://tools.ietf.org/html/rfc2047
2882
     * @param string $str the text to encode
2883
     * @param string $position Where the text is going to be used, see the RFC for what that means
2884 1
     * @access public
2885 1
     * @return string
2886 1
     */
2887 1
    public function encodeQ($str, $position = 'text')
2888 1
    {
2889 1
        // There should not be any EOL in the string
2890 1
        $pattern = '';
2891 1
        $encoded = str_replace(array("\r", "\n"), '', $str);
2892 1
        switch (strtolower($position)) {
2893
            case 'phrase':
2894 1
                // RFC 2047 section 5.3
2895
                $pattern = '^A-Za-z0-9!*+\/ -';
2896
                break;
2897
            /** @noinspection PhpMissingBreakStatementInspection */
2898
            case 'comment':
2899
                // RFC 2047 section 5.2
2900
                $pattern = '\(\)"';
2901
                // intentional fall-through
2902
                // for this reason we build the $pattern without including delimiters and []
2903
            case 'text':
2904
            default:
2905
                // RFC 2047 section 5.1
2906
                // Replace every high ascii, control, =, ? and _ characters
2907
                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2908 1
                break;
2909
        }
2910
        $matches = array();
2911
        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2912
            // If the string contains an '=', make sure it's the first thing we replace
2913
            // so as to avoid double-encoding
2914
            $eqkey = array_search('=', $matches[0]);
2915
            if (false !== $eqkey) {
2916 1
                unset($matches[0][$eqkey]);
2917 1
                array_unshift($matches[0], '=');
2918 1
            }
2919
            foreach (array_unique($matches[0]) as $char) {
2920 1
                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2921 1
            }
2922 1
        }
2923 1
        // Replace every spaces to _ (more readable than =20)
2924 1
        return str_replace(' ', '_', $encoded);
2925 1
    }
2926 1
2927 1
    /**
2928
     * Add a string or binary attachment (non-filesystem).
2929 1
     * This method can be used to attach ascii or binary data,
2930 1
     * such as a BLOB record from a database.
2931
     * @param string $string String attachment data.
2932
     * @param string $filename Name of the attachment.
2933
     * @param string $encoding File encoding (see $Encoding).
2934
     * @param string $type File extension (MIME) type.
2935
     * @param string $disposition Disposition to use
2936
     * @return void
2937
     */
2938
    public function addStringAttachment(
2939
        $string,
2940
        $filename,
2941
        $encoding = 'base64',
2942
        $type = '',
2943
        $disposition = 'attachment'
2944
    ) {
2945
        // If a MIME type is not specified, try to work it out from the file name
2946
        if ($type == '') {
2947
            $type = self::filenameToType($filename);
2948 5
        }
2949
        // Append to $attachment array
2950 5
        $this->attachment[] = array(
2951 1
            0 => $string,
2952 1
            1 => $filename,
2953
            2 => basename($filename),
2954
            3 => $encoding,
2955
            4 => $type,
2956 5
            5 => true, // isStringAttachment
2957 1
            6 => $disposition,
2958 1
            7 => 0
2959
        );
2960 5
    }
2961 5
2962 1
    /**
2963 1
     * Add an embedded (inline) attachment from a file.
2964
     * This can include images, sounds, and just about any other document type.
2965
     * These differ from 'regular' attachments in that they are intended to be
2966 5
     * displayed inline with the message, not just attached for download.
2967 5
     * This is used in HTML messages that embed the images
2968 5
     * the HTML refers to using the $cid value.
2969 5
     * @param string $path Path to the attachment.
2970 5
     * @param string $cid Content ID of the attachment; Use this to reference
2971 5
     *        the content when using an embedded image in HTML.
2972 5
     * @param string $name Overrides the attachment name.
2973 5
     * @param string $encoding File encoding (see $Encoding).
2974
     * @param string $type File MIME type.
2975 5
     * @param string $disposition Disposition to use
2976 5
     * @return boolean True on successfully adding an attachment
2977
     */
2978
    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2979
    {
2980
        if (!@is_file($path)) {
2981
            $this->setError($this->lang('file_access') . $path);
2982
            return false;
2983
        }
2984
2985
        // If a MIME type is not specified, try to work it out from the file name
2986
        if ($type == '') {
2987
            $type = self::filenameToType($path);
2988
        }
2989
2990
        $filename = basename($path);
2991
        if ($name == '') {
2992
            $name = $filename;
2993 1
        }
2994
2995
        // Append to $attachment array
2996
        $this->attachment[] = array(
2997
            0 => $path,
2998
            1 => $filename,
2999
            2 => $name,
3000
            3 => $encoding,
3001
            4 => $type,
3002 1
            5 => false, // isStringAttachment
3003
            6 => $disposition,
3004
            7 => $cid
3005
        );
3006
        return true;
3007 1
    }
3008 1
3009 1
    /**
3010 1
     * Add an embedded stringified attachment.
3011 1
     * This can include images, sounds, and just about any other document type.
3012 1
     * Be sure to set the $type to an image type for images:
3013 1
     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
3014 1
     * @param string $string The attachment binary data.
3015
     * @param string $cid Content ID of the attachment; Use this to reference
3016 1
     *        the content when using an embedded image in HTML.
3017 1
     * @param string $name
3018
     * @param string $encoding File encoding (see $Encoding).
3019
     * @param string $type MIME type.
3020
     * @param string $disposition Disposition to use
3021
     * @return boolean True on successfully adding an attachment
3022
     */
3023
    public function addStringEmbeddedImage(
3024
        $string,
3025 41
        $cid,
3026
        $name = '',
3027 41
        $encoding = 'base64',
3028 11
        $type = '',
3029 6
        $disposition = 'inline'
3030
    ) {
3031 35
        // If a MIME type is not specified, try to work it out from the name
3032 35
        if ($type == '' and !empty($name)) {
3033
            $type = self::filenameToType($name);
3034
        }
3035
3036
        // Append to $attachment array
3037
        $this->attachment[] = array(
3038
            0 => $string,
3039 41
            1 => $name,
3040
            2 => $name,
3041 41
            3 => $encoding,
3042 11
            4 => $type,
3043 6
            5 => true, // isStringAttachment
3044
            6 => $disposition,
3045 36
            7 => $cid
3046 35
        );
3047
        return true;
3048
    }
3049
3050
    /**
3051
     * Check if an inline attachment is present.
3052
     * @access public
3053 41
     * @return boolean
3054
     */
3055 41
    public function inlineImageExists()
3056
    {
3057
        foreach ($this->attachment as $attachment) {
3058
            if ($attachment[6] == 'inline') {
3059
                return true;
3060
            }
3061
        }
3062
        return false;
3063
    }
3064 3
3065
    /**
3066 3
     * Check if an attachment (non-inline) is present.
3067 3
     * @return boolean
3068 1
     */
3069 1
    public function attachmentExists()
3070 1
    {
3071 3
        foreach ($this->attachment as $attachment) {
3072 3
            if ($attachment[6] == 'attachment') {
3073
                return true;
3074
            }
3075
        }
3076
        return false;
3077
    }
3078 1
3079
    /**
3080 1
     * Check if this message has an alternative body set.
3081 1
     * @return boolean
3082 1
     */
3083 1
    public function alternativeExists()
3084 1
    {
3085 1
        return !empty($this->AltBody);
3086
    }
3087
3088
    /**
3089
     * Clear queued addresses of given kind.
3090
     * @access protected
3091 1
     * @param string $kind 'to', 'cc', or 'bcc'
3092
     * @return void
3093 1
     */
3094 1
    public function clearQueuedAddresses($kind)
3095 1
    {
3096 1
        $RecipientsQueue = $this->RecipientsQueue;
3097 1
        foreach ($RecipientsQueue as $address => $params) {
3098 1
            if ($params[0] == $kind) {
3099
                unset($this->RecipientsQueue[$address]);
3100
            }
3101
        }
3102
    }
3103
3104 2
    /**
3105
     * Clear all To recipients.
3106 2
     * @return void
3107 1
     */
3108 2
    public function clearAddresses()
3109 2
    {
3110 2
        foreach ($this->to as $to) {
3111 2
            unset($this->all_recipients[strtolower($to[0])]);
3112
        }
3113
        $this->to = array();
3114
        $this->clearQueuedAddresses('to');
3115
    }
3116
3117 3
    /**
3118
     * Clear all CC recipients.
3119 3
     * @return void
3120 3
     */
3121 3
    public function clearCCs()
3122
    {
3123
        foreach ($this->cc as $cc) {
3124
            unset($this->all_recipients[strtolower($cc[0])]);
3125
        }
3126
        $this->cc = array();
3127 5
        $this->clearQueuedAddresses('cc');
3128
    }
3129 5
3130 5
    /**
3131 5
     * Clear all BCC recipients.
3132 5
     * @return void
3133 5
     */
3134 5
    public function clearBCCs()
3135
    {
3136
        foreach ($this->bcc as $bcc) {
3137
            unset($this->all_recipients[strtolower($bcc[0])]);
3138
        }
3139
        $this->bcc = array();
3140 1
        $this->clearQueuedAddresses('bcc');
3141
    }
3142 1
3143 1
    /**
3144
     * Clear all ReplyTo recipients.
3145
     * @return void
3146
     */
3147
    public function clearReplyTos()
3148
    {
3149 2
        $this->ReplyTo = array();
3150
        $this->ReplyToQueue = array();
3151 2
    }
3152 2
3153
    /**
3154
     * Clear all recipient types.
3155
     * @return void
3156
     */
3157
    public function clearAllRecipients()
3158
    {
3159
        $this->to = array();
3160 8
        $this->cc = array();
3161
        $this->bcc = array();
3162 8
        $this->all_recipients = array();
3163 8
        $this->RecipientsQueue = array();
3164 1
    }
3165 1
3166
    /**
3167
     * Clear all filesystem, string, and binary attachments.
3168
     * @return void
3169
     */
3170
    public function clearAttachments()
3171
    {
3172
        $this->attachment = array();
3173
    }
3174
3175
    /**
3176
     * Clear all custom headers.
3177 1
     * @return void
3178 8
     */
3179 8
    public function clearCustomHeaders()
3180
    {
3181
        $this->CustomHeader = array();
3182
    }
3183
3184
    /**
3185
     * Add an error message to the error container.
3186
     * @access protected
3187 42
     * @param string $msg
3188
     * @return void
3189
     */
3190
    protected function setError($msg)
3191 42
    {
3192 42
        $this->error_count++;
3193
        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
3194
            $lasterror = $this->smtp->getError();
3195
            if (!empty($lasterror['error'])) {
3196
                $msg .= $this->lang('smtp_error') . $lasterror['error'];
3197
                if (!empty($lasterror['detail'])) {
3198
                    $msg .= ' Detail: '. $lasterror['detail'];
3199
                }
3200
                if (!empty($lasterror['smtp_code'])) {
3201 42
                    $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3202
                }
3203 42
                if (!empty($lasterror['smtp_code_ex'])) {
3204 42
                    $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3205
                }
3206 42
            }
3207
        }
3208 42
        $this->ErrorInfo = $msg;
3209 42
    }
3210 42
3211
    /**
3212
     * Return an RFC 822 formatted date.
3213 42
     * @access public
3214
     * @return string
3215
     * @static
3216
     */
3217
    public static function rfcDate()
3218
    {
3219
        // Set the time zone to whatever the default is to avoid 500 errors
3220
        // Will default to UTC if it's not set properly in php.ini
3221
        date_default_timezone_set(@date_default_timezone_get());
3222 8
        return date('D, j M Y H:i:s O');
3223
    }
3224 8
3225 7
    /**
3226 7
     * Get the server hostname.
3227
     * Returns 'localhost.localdomain' if unknown.
3228 8
     * @access protected
3229 8
     * @return string
3230
     */
3231
    protected function serverHostname()
0 ignored issues
show
Coding Style introduced by
serverHostname uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3232
    {
3233
        $result = 'localhost.localdomain';
3234
        if (!empty($this->Hostname)) {
3235 8
            $result = $this->Hostname;
3236
        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
3237
            $result = $_SERVER['SERVER_NAME'];
3238
        } elseif (function_exists('gethostname') && gethostname() !== false) {
3239
            $result = gethostname();
3240
        } elseif (php_uname('n') !== false) {
3241
            $result = php_uname('n');
3242
        }
3243
        return $result;
3244
    }
3245
3246
    /**
3247 41
     * Get an error message in the current language.
3248
     * @access protected
3249 41
     * @param string $key
3250
     * @return string
3251
     */
3252
    protected function lang($key)
3253
    {
3254
        if (count($this->language) < 1) {
3255
            $this->setLanguage('en'); // set the default language
3256
        }
3257
3258
        if (array_key_exists($key, $this->language)) {
3259 38
            if ($key == 'smtp_connect_failed') {
3260
                //Include a link to troubleshooting docs on SMTP connection failure
3261
                //this is by far the biggest cause of support questions
3262 38
                //but it's usually not PHPMailer's fault.
3263
                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3264 38
            }
3265
            return $this->language[$key];
3266
        } else {
3267 38
            //Return the key as a fallback
3268
            return $key;
3269
        }
3270
    }
3271
3272
    /**
3273
     * Check if an error occurred.
3274
     * @access public
3275
     * @return boolean True if an error did occur.
3276
     */
3277
    public function isError()
3278
    {
3279 2
        return ($this->error_count > 0);
3280
    }
3281 2
3282
    /**
3283 2
     * Ensure consistent line endings in a string.
3284 2
     * Changes every end of line from CRLF, CR or LF to $this->LE.
3285 1
     * @access public
3286
     * @param string $str String to fixEOL
3287 2
     * @return string
3288
     */
3289
    public function fixEOL($str)
3290
    {
3291
        // Normalise to \n
3292
        $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
3293 1
        // Now convert LE as needed
3294
        if ($this->LE !== "\n") {
3295 1
            $nstr = str_replace("\n", $this->LE, $nstr);
3296
        }
3297
        return $nstr;
3298
    }
3299
3300
    /**
3301
     * Add a custom header.
3302
     * $name value can be overloaded to contain
3303
     * both header name and value (name:value)
3304
     * @access public
3305
     * @param string $name Custom header name
3306
     * @param string $value Header value
3307
     * @return void
3308
     */
3309
    public function addCustomHeader($name, $value = null)
3310 2
    {
3311
        if ($value === null) {
3312 2
            // Value passed in as name:value
3313 2
            $this->CustomHeader[] = explode(':', $name, 2);
3314 2
        } else {
3315
            $this->CustomHeader[] = array($name, $value);
3316 2
        }
3317
    }
3318
3319
    /**
3320
     * Returns all custom headers.
3321
     * @return array
3322
     */
3323
    public function getCustomHeaders()
3324
    {
3325
        return $this->CustomHeader;
3326
    }
3327
3328
    /**
3329
     * Create a message body from an HTML string.
3330
     * Automatically inlines images and creates a plain-text version by converting the HTML,
3331 2
     * overwriting any existing values in Body and AltBody.
3332
     * $basedir is used when handling relative image paths, e.g. <img src="images/a.png">
3333
     * will look for an image file in $basedir/images/a.png and convert it to inline.
3334 2
     * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself.
3335 2
     * @access public
3336 2
     * @param string $message HTML message string
3337
     * @param string $basedir base directory for relative paths to images
3338
     * @param boolean|callable $advanced Whether to use the internal HTML to text converter
3339 2
     *    or your own custom converter @see PHPMailer::html2text()
3340 2
     * @return string $message The transformed message Body
3341 2
     */
3342 2
    public function msgHTML($message, $basedir = '', $advanced = false)
3343 2
    {
3344 2
        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3345 2
        if (array_key_exists(2, $images)) {
3346 2
            foreach ($images[2] as $imgindex => $url) {
3347 2
                // Convert data URIs into embedded images
3348 2
                if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3349 2
                    $data = substr($url, strpos($url, ','));
3350 2
                    if ($match[2]) {
3351 2
                        $data = base64_decode($data);
3352 2
                    } else {
3353 2
                        $data = rawurldecode($data);
3354 2
                    }
3355 2
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3356 2
                    if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
3357
                        $message = str_replace(
3358 2
                            $images[0][$imgindex],
3359 2
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3360 2
                            $message
3361 2
                        );
3362 2
                    }
3363 2
                } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) {
3364
                    // Do not change urls for absolute images (thanks to corvuscorax)
3365 2
                    // Do not change urls that are already inline images
3366 2
                    $filename = basename($url);
3367 2
                    $directory = dirname($url);
3368
                    if ($directory == '.') {
3369
                        $directory = '';
3370
                    }
3371 2
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3372
                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3373
                        $basedir .= '/';
3374
                    }
3375
                    if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3376
                        $directory .= '/';
3377
                    }
3378
                    if ($this->addEmbeddedImage(
3379
                        $basedir . $directory . $filename,
3380
                        $cid,
3381
                        $filename,
3382
                        'base64',
3383
                        self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
3384
                    )
3385
                    ) {
3386
                        $message = preg_replace(
3387
                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3388
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3389
                            $message
3390
                        );
3391
                    }
3392
                }
3393
            }
3394 2
        }
3395
        $this->isHTML(true);
3396 2
        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3397 1
        $this->Body = $this->normalizeBreaks($message);
3398
        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3399 2
        if (!$this->alternativeExists()) {
3400 2
            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
3401 2
                self::CRLF . self::CRLF;
3402 2
        }
3403 2
        return $this->Body;
3404
    }
3405
3406
    /**
3407
     * Convert an HTML string into plain text.
3408
     * This is used by msgHTML().
3409
     * Note - older versions of this function used a bundled advanced converter
3410
     * which was been removed for license reasons in #232.
3411
     * Example usage:
3412
     * <code>
3413 10
     * // Use default conversion
3414
     * $plain = $mail->html2text($html);
3415
     * // Use your own custom converter
3416 10
     * $plain = $mail->html2text($html, function($html) {
3417 10
     *     $converter = new MyHtml2text($html);
3418 10
     *     return $converter->get_text();
3419 10
     * });
3420 10
     * </code>
3421 10
     * @param string $html The HTML text to convert
3422 10
     * @param boolean|callable $advanced Any boolean value to use the internal converter,
3423 10
     *   or provide your own callable for custom conversion.
3424 10
     * @return string
3425 10
     */
3426 10
    public function html2text($html, $advanced = false)
3427 10
    {
3428 10
        if (is_callable($advanced)) {
3429 10
            return call_user_func($advanced, $html);
3430 10
        }
3431 10
        return html_entity_decode(
3432 10
            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3433 10
            ENT_QUOTES,
3434 10
            $this->CharSet
3435 10
        );
3436 10
    }
3437 10
3438 10
    /**
3439 10
     * Get the MIME type for a file extension.
3440 10
     * @param string $ext File extension
3441 10
     * @access public
3442 10
     * @return string MIME type of file.
3443 10
     * @static
3444 10
     */
3445 10
    public static function _mime_types($ext = '')
3446 10
    {
3447 10
        $mimes = array(
3448 10
            'xl'    => 'application/excel',
3449 10
            'js'    => 'application/javascript',
3450 10
            'hqx'   => 'application/mac-binhex40',
3451 10
            'cpt'   => 'application/mac-compactpro',
3452 10
            'bin'   => 'application/macbinary',
3453 10
            'doc'   => 'application/msword',
3454 10
            'word'  => 'application/msword',
3455 10
            'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3456 10
            'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3457 10
            'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3458 10
            'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3459 10
            'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3460 10
            'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3461 10
            'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3462 10
            'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3463 10
            'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
3464 10
            'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3465 10
            'class' => 'application/octet-stream',
3466 10
            'dll'   => 'application/octet-stream',
3467 10
            'dms'   => 'application/octet-stream',
3468 10
            'exe'   => 'application/octet-stream',
3469 10
            'lha'   => 'application/octet-stream',
3470 10
            'lzh'   => 'application/octet-stream',
3471 10
            'psd'   => 'application/octet-stream',
3472 10
            'sea'   => 'application/octet-stream',
3473 10
            'so'    => 'application/octet-stream',
3474 10
            'oda'   => 'application/oda',
3475 10
            'pdf'   => 'application/pdf',
3476 10
            'ai'    => 'application/postscript',
3477 10
            'eps'   => 'application/postscript',
3478 10
            'ps'    => 'application/postscript',
3479 10
            'smi'   => 'application/smil',
3480 10
            'smil'  => 'application/smil',
3481 10
            'mif'   => 'application/vnd.mif',
3482 10
            'xls'   => 'application/vnd.ms-excel',
3483 10
            'ppt'   => 'application/vnd.ms-powerpoint',
3484 10
            'wbxml' => 'application/vnd.wap.wbxml',
3485 10
            'wmlc'  => 'application/vnd.wap.wmlc',
3486 10
            'dcr'   => 'application/x-director',
3487 10
            'dir'   => 'application/x-director',
3488 10
            'dxr'   => 'application/x-director',
3489 10
            'dvi'   => 'application/x-dvi',
3490 10
            'gtar'  => 'application/x-gtar',
3491 10
            'php3'  => 'application/x-httpd-php',
3492 10
            'php4'  => 'application/x-httpd-php',
3493 10
            'php'   => 'application/x-httpd-php',
3494 10
            'phtml' => 'application/x-httpd-php',
3495 10
            'phps'  => 'application/x-httpd-php-source',
3496 10
            'swf'   => 'application/x-shockwave-flash',
3497 10
            'sit'   => 'application/x-stuffit',
3498 10
            'tar'   => 'application/x-tar',
3499 10
            'tgz'   => 'application/x-tar',
3500 10
            'xht'   => 'application/xhtml+xml',
3501 10
            'xhtml' => 'application/xhtml+xml',
3502 10
            'zip'   => 'application/zip',
3503 10
            'mid'   => 'audio/midi',
3504 10
            'midi'  => 'audio/midi',
3505 10
            'mp2'   => 'audio/mpeg',
3506 10
            'mp3'   => 'audio/mpeg',
3507 10
            'mpga'  => 'audio/mpeg',
3508 10
            'aif'   => 'audio/x-aiff',
3509 10
            'aifc'  => 'audio/x-aiff',
3510 10
            'aiff'  => 'audio/x-aiff',
3511 10
            'ram'   => 'audio/x-pn-realaudio',
3512 10
            'rm'    => 'audio/x-pn-realaudio',
3513
            'rpm'   => 'audio/x-pn-realaudio-plugin',
3514 10
            'ra'    => 'audio/x-realaudio',
3515 10
            'wav'   => 'audio/x-wav',
3516 10
            'bmp'   => 'image/bmp',
3517
            'gif'   => 'image/gif',
3518
            'jpeg'  => 'image/jpeg',
3519
            'jpe'   => 'image/jpeg',
3520
            'jpg'   => 'image/jpeg',
3521
            'png'   => 'image/png',
3522
            'tiff'  => 'image/tiff',
3523
            'tif'   => 'image/tiff',
3524
            'eml'   => 'message/rfc822',
3525
            'css'   => 'text/css',
3526
            'html'  => 'text/html',
3527
            'htm'   => 'text/html',
3528 7
            'shtml' => 'text/html',
3529
            'log'   => 'text/plain',
3530
            'text'  => 'text/plain',
3531 7
            'txt'   => 'text/plain',
3532 7
            'rtx'   => 'text/richtext',
3533
            'rtf'   => 'text/rtf',
3534
            'vcf'   => 'text/vcard',
3535 7
            'vcard' => 'text/vcard',
3536 7
            'xml'   => 'text/xml',
3537
            'xsl'   => 'text/xml',
3538
            'mpeg'  => 'video/mpeg',
3539
            'mpe'   => 'video/mpeg',
3540
            'mpg'   => 'video/mpeg',
3541
            'mov'   => 'video/quicktime',
3542
            'qt'    => 'video/quicktime',
3543
            'rv'    => 'video/vnd.rn-realvideo',
3544
            'avi'   => 'video/x-msvideo',
3545
            'movie' => 'video/x-sgi-movie'
3546
        );
3547
        if (array_key_exists(strtolower($ext), $mimes)) {
3548
            return $mimes[strtolower($ext)];
3549
        }
3550 10
        return 'application/octet-stream';
3551
    }
3552 10
3553 10
    /**
3554 10
     * Map a file name to a MIME type.
3555 10
     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3556 10
     * @param string $filename A file name or full path, does not need to exist as a file
3557 10
     * @return string
3558 10
     * @static
3559 10
     */
3560 10
    public static function filenameToType($filename)
3561 10
    {
3562 10
        // In case the path is a URL, strip any query string before getting extension
3563 10
        $qpos = strpos($filename, '?');
3564 10
        if (false !== $qpos) {
3565 10
            $filename = substr($filename, 0, $qpos);
3566 10
        }
3567 10
        $pathinfo = self::mb_pathinfo($filename);
3568
        return self::_mime_types($pathinfo['extension']);
3569 10
    }
3570 10
3571 1
    /**
3572 10
     * Multi-byte-safe pathinfo replacement.
3573 10
     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3574
     * Works similarly to the one in PHP >= 5.2.0
3575 10
     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3576 10
     * @param string $path A filename or path, does not need to exist as a file
3577 2
     * @param integer|string $options Either a PATHINFO_* constant,
3578 8
     *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3579 8
     * @return string|array
3580 1
     * @static
3581 8
     */
3582 8
    public static function mb_pathinfo($path, $options = null)
3583 8
    {
3584
        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3585
        $pathinfo = array();
3586
        if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3587
            if (array_key_exists(1, $pathinfo)) {
3588
                $ret['dirname'] = $pathinfo[1];
3589
            }
3590
            if (array_key_exists(2, $pathinfo)) {
3591
                $ret['basename'] = $pathinfo[2];
3592
            }
3593
            if (array_key_exists(5, $pathinfo)) {
3594
                $ret['extension'] = $pathinfo[5];
3595
            }
3596
            if (array_key_exists(3, $pathinfo)) {
3597
                $ret['filename'] = $pathinfo[3];
3598
            }
3599
        }
3600 1
        switch ($options) {
3601
            case PATHINFO_DIRNAME:
3602 1
            case 'dirname':
3603 1
                return $ret['dirname'];
3604 1
            case PATHINFO_BASENAME:
3605
            case 'basename':
3606 1
                return $ret['basename'];
3607 1
            case PATHINFO_EXTENSION:
3608
            case 'extension':
3609
                return $ret['extension'];
3610
            case PATHINFO_FILENAME:
3611
            case 'filename':
3612
                return $ret['filename'];
3613
            default:
3614
                return $ret;
3615
        }
3616
    }
3617 42
3618
    /**
3619 42
     * Set or reset instance properties.
3620
     * You should avoid this function - it's more verbose, less efficient, more error-prone and
3621
     * harder to debug than setting properties directly.
3622
     * Usage Example:
3623
     * `$mail->set('SMTPSecure', 'tls');`
3624
     *   is the same as:
3625
     * `$mail->SMTPSecure = 'tls';`
3626
     * @access public
3627
     * @param string $name The property name to set
3628
     * @param mixed $value The value to set the property to
3629
     * @return boolean
3630
     * @TODO Should this not be using the __set() magic function?
3631
     */
3632 3
    public function set($name, $value = '')
3633
    {
3634 3
        if (property_exists($this, $name)) {
3635
            $this->$name = $value;
3636
            return true;
3637
        } else {
3638
            $this->setError($this->lang('variable_set') . $name);
3639
            return false;
3640
        }
3641
    }
3642
3643
    /**
3644
     * Strip newlines to prevent header injection.
3645 2
     * @access public
3646
     * @param string $str
3647 2
     * @return string
3648 2
     */
3649 2
    public function secureHeader($str)
3650 2
    {
3651 2
        return trim(str_replace(array("\r", "\n"), '', $str));
3652
    }
3653
3654
    /**
3655
     * Normalize line breaks in a string.
3656
     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3657
     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3658
     * @param string $text
3659 1
     * @param string $breaktype What kind of line break to use, defaults to CRLF
3660
     * @return string
3661 1
     * @access public
3662 1
     * @static
3663 1
     */
3664 1
    public static function normalizeBreaks($text, $breaktype = "\r\n")
3665 1
    {
3666 1
        return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3667 1
    }
3668
3669 1
    /**
3670 1
     * Set the public and private key files and password for S/MIME signing.
3671
     * @access public
3672
     * @param string $cert_filename
3673
     * @param string $key_filename
3674
     * @param string $key_pass Password for private key
3675
     * @param string $extracerts_filename Optional path to chain certificate
3676
     */
3677
    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3678
    {
3679
        $this->sign_cert_file = $cert_filename;
3680 1
        $this->sign_key_file = $key_filename;
3681
        $this->sign_key_pass = $key_pass;
3682 1
        $this->sign_extracerts_file = $extracerts_filename;
3683
    }
3684
3685
    /**
3686
     * Quoted-Printable-encode a DKIM header.
3687
     * @access public
3688 1
     * @param string $txt
3689 1
     * @return string
3690
     */
3691
    public function DKIM_QP($txt)
3692 1
    {
3693
        $line = '';
3694 1
        for ($i = 0; $i < strlen($txt); $i++) {
3695 1
            $ord = ord($txt[$i]);
3696 1
            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3697
                $line .= $txt[$i];
3698
            } else {
3699
                $line .= '=' . sprintf('%02X', $ord);
3700
            }
3701
        }
3702
        return $line;
3703
    }
3704
3705
    /**
3706
     * Generate a DKIM signature.
3707
     * @access public
3708 1
     * @param string $signHeader
3709
     * @throws phpmailerException
3710 1
     * @return string The DKIM signature value
3711 1
     */
3712 1
    public function DKIM_Sign($signHeader)
3713 1
    {
3714 1
        if (!defined('PKCS7_TEXT')) {
3715 1
            if ($this->exceptions) {
3716 1
                throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3717 1
            }
3718 1
            return '';
3719 1
        }
3720
        $privKeyStr = file_get_contents($this->DKIM_private);
3721
        if ('' != $this->DKIM_passphrase) {
3722
            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3723
        } else {
3724
            $privKey = openssl_pkey_get_private($privKeyStr);
3725
        }
3726
        //Workaround for missing digest algorithms in old PHP & OpenSSL versions
3727
        //@link http://stackoverflow.com/a/11117338/333340
3728 1
        if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
3729
            in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
3730 1
            if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
3731
                openssl_pkey_free($privKey);
3732
                return base64_encode($signature);
3733
            }
3734 1
        } else {
3735 1
            $pinfo = openssl_pkey_get_details($privKey);
3736
            $hash = hash('sha256', $signHeader);
3737 1
            //'Magic' constant for SHA256 from RFC3447
3738 1
            //@link https://tools.ietf.org/html/rfc3447#page-43
3739 1
            $t = '3031300d060960864801650304020105000420' . $hash;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $t. 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...
3740 1
            $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
3741
            $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $eb. 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...
3742
3743
            if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
3744
                openssl_pkey_free($privKey);
3745
                return base64_encode($signature);
3746
            }
3747
        }
3748
        openssl_pkey_free($privKey);
3749
        return '';
3750
    }
3751 1
3752
    /**
3753 1
     * Generate a DKIM canonicalization header.
3754 1
     * @access public
3755 1
     * @param string $signHeader Header
3756 1
     * @return string
3757 1
     */
3758 1
    public function DKIM_HeaderC($signHeader)
3759 1
    {
3760 1
        $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3761 1
        $lines = explode("\r\n", $signHeader);
3762 1
        foreach ($lines as $key => $line) {
3763 1
            list($heading, $value) = explode(':', $line, 2);
3764 1
            $heading = strtolower($heading);
3765 1
            $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
3766 1
            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3767 1
        }
3768 1
        $signHeader = implode("\r\n", $lines);
3769 1
        return $signHeader;
3770 1
    }
3771 1
3772 1
    /**
3773 1
     * Generate a DKIM canonicalization body.
3774 1
     * @access public
3775
     * @param string $body Message Body
3776
     * @return string
3777 1
     */
3778
    public function DKIM_BodyC($body)
3779
    {
3780 1
        if ($body == '') {
3781 1
            return "\r\n";
3782 1
        }
3783 1
        // stabilize line endings
3784 1
        $body = str_replace("\r\n", "\n", $body);
3785 1
        $body = str_replace("\n", "\r\n", $body);
3786 1
        // END stabilize line endings
3787 1
        while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3788 1
            $body = substr($body, 0, strlen($body) - 2);
3789 1
        }
3790 1
        return $body;
3791 1
    }
3792 1
3793 1
    /**
3794 1
     * Create the DKIM header and body in a new message header.
3795
     * @access public
3796
     * @param string $headers_line Header lines
3797
     * @param string $subject Subject
3798 1
     * @param string $body Body
3799 1
     * @return string
3800 1
     */
3801 1
    public function DKIM_Add($headers_line, $subject, $body)
3802 1
    {
3803 1
        $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
3804 1
        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3805 1
        $DKIMquery = 'dns/txt'; // Query method
3806 1
        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3807 1
        $subject_header = "Subject: $subject";
3808 1
        $headers = explode($this->LE, $headers_line);
3809 1
        $from_header = '';
3810 1
        $to_header = '';
3811 1
        $date_header = '';
3812 1
        $current = '';
3813 1
        foreach ($headers as $header) {
3814 1
            if (strpos($header, 'From:') === 0) {
3815 1
                $from_header = $header;
3816 1
                $current = 'from_header';
3817
            } elseif (strpos($header, 'To:') === 0) {
3818 1
                $to_header = $header;
3819 1
                $current = 'to_header';
3820 1
            } elseif (strpos($header, 'Date:') === 0) {
3821
                $date_header = $header;
3822
                $current = 'date_header';
3823
            } else {
3824
                if (!empty($$current) && strpos($header, ' =?') === 0) {
3825
                    $$current .= $header;
3826
                } else {
3827
                    $current = '';
3828
                }
3829 40
            }
3830
        }
3831
        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3832 40
        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3833
        $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
3834
        $subject = str_replace(
3835
            '|',
3836
            '=7C',
3837
            $this->DKIM_QP($subject_header)
3838
        ); // Copied header fields (dkim-quoted-printable)
3839
        $body = $this->DKIM_BodyC($body);
3840
        $DKIMlen = strlen($body); // Length of body
3841 2
        $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
3842
        if ('' == $this->DKIM_identity) {
3843 2
            $ident = '';
3844
        } else {
3845
            $ident = ' i=' . $this->DKIM_identity . ';';
3846
        }
3847
        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3848
            $DKIMsignatureType . '; q=' .
3849
            $DKIMquery . '; l=' .
3850
            $DKIMlen . '; s=' .
3851
            $this->DKIM_selector .
3852 1
            ";\r\n" .
3853
            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3854 1
            "\th=From:To:Date:Subject;\r\n" .
3855
            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3856
            "\tz=$from\r\n" .
3857
            "\t|$to\r\n" .
3858
            "\t|$date\r\n" .
3859
            "\t|$subject;\r\n" .
3860
            "\tbh=" . $DKIMb64 . ";\r\n" .
3861
            "\tb=";
3862
        $toSign = $this->DKIM_HeaderC(
3863 1
            $from_header . "\r\n" .
3864
            $to_header . "\r\n" .
3865 1
            $date_header . "\r\n" .
3866
            $subject_header . "\r\n" .
3867
            $dkimhdrs
3868
        );
3869
        $signed = $this->DKIM_Sign($toSign);
3870
        return $dkimhdrs . $signed . "\r\n";
3871
    }
3872
3873
    /**
3874 2
     * Detect if a string contains a line longer than the maximum line length allowed.
3875
     * @param string $str
3876 2
     * @return boolean
3877
     * @static
3878
     */
3879
    public static function hasLineLongerThanMax($str)
3880
    {
3881
        //+2 to include CRLF line break for a 1000 total
3882
        return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
3883
    }
3884
3885
    /**
3886
     * Allows for public read access to 'to' property.
3887
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3888
     * @access public
3889
     * @return array
3890
     */
3891
    public function getToAddresses()
3892
    {
3893
        return $this->to;
3894
    }
3895
3896
    /**
3897
     * Allows for public read access to 'cc' property.
3898
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3899
     * @access public
3900 36
     * @return array
3901
     */
3902 36
    public function getCcAddresses()
3903
    {
3904
        return $this->cc;
3905
    }
3906 36
3907
    /**
3908
     * Allows for public read access to 'bcc' property.
3909
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3910
     * @access public
3911
     * @return array
3912
     */
3913
    public function getBccAddresses()
3914
    {
3915
        return $this->bcc;
3916
    }
3917
3918
    /**
3919
     * Allows for public read access to 'ReplyTo' property.
3920
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3921
     * @access public
3922
     * @return array
3923
     */
3924
    public function getReplyToAddresses()
3925
    {
3926
        return $this->ReplyTo;
3927
    }
3928
3929
    /**
3930
     * Allows for public read access to 'all_recipients' property.
3931
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3932
     * @access public
3933
     * @return array
3934
     */
3935
    public function getAllRecipientAddresses()
3936
    {
3937
        return $this->all_recipients;
3938
    }
3939
3940
    /**
3941
     * Perform a callback.
3942
     * @param boolean $isSent
3943
     * @param array $to
3944
     * @param array $cc
3945
     * @param array $bcc
3946
     * @param string $subject
3947
     * @param string $body
3948
     * @param string $from
3949
     */
3950
    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...
3951
    {
3952
        if (!empty($this->action_function) && is_callable($this->action_function)) {
3953
            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3954
            call_user_func_array($this->action_function, $params);
3955
        }
3956
    }
3957
}
3958
3959
/**
3960
 * PHPMailer exception handler
3961
 * @package PHPMailer
3962
 */
3963
class phpmailerException extends Exception
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
3964
{
3965
    /**
3966
     * Prettify error message output
3967
     * @return string
3968
     */
3969
    public function errorMessage()
3970
    {
3971
        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3972
        return $errorMsg;
3973
    }
3974
}
3975