Completed
Push — master ( c700f9...e0fefd )
by Marcus
02:14
created

PHPMailer::mailSend()   C

Complexity

Conditions 12
Paths 64

Size

Total Lines 35
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 15.358

Importance

Changes 0
Metric Value
cc 12
eloc 24
nc 64
nop 2
dl 0
loc 35
ccs 15
cts 21
cp 0.7143
crap 15.358
rs 5.1612
c 0
b 0
f 0

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * PHPMailer - PHP email creation and transport class.
4
 * PHP Version 5
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 138 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 3943 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 513 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.18';
35
36
    /**
37
     * Email priority.
38
     * Options: null (default), 1 = High, 3 = Normal, 5 = low.
39
     * When null, the header is not set at all.
40
     * @var integer
41
     */
42
    public $Priority = null;
43
44
    /**
45
     * The character set of the message.
46
     * @var string
47
     */
48
    public $CharSet = 'iso-8859-1';
49
50
    /**
51
     * The MIME Content-type of the message.
52
     * @var string
53
     */
54
    public $ContentType = 'text/plain';
55
56
    /**
57
     * The message encoding.
58
     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59
     * @var string
60
     */
61
    public $Encoding = '8bit';
62
63
    /**
64
     * Holds the most recent mailer error message.
65
     * @var string
66
     */
67
    public $ErrorInfo = '';
68
69
    /**
70
     * The From email address for the message.
71
     * @var string
72
     */
73
    public $From = 'root@localhost';
74
75
    /**
76
     * The From name of the message.
77
     * @var string
78
     */
79
    public $FromName = 'Root User';
80
81
    /**
82
     * The Sender email (Return-Path) of the message.
83
     * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
84
     * @var string
85
     */
86
    public $Sender = '';
87
88
    /**
89
     * The Return-Path of the message.
90
     * If empty, it will be set to either From or Sender.
91
     * @var string
92
     * @deprecated Email senders should never set a return-path header;
93
     * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
94
     * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
95
     */
96
    public $ReturnPath = '';
97
98
    /**
99
     * The Subject of the message.
100
     * @var string
101
     */
102
    public $Subject = '';
103
104
    /**
105
     * An HTML or plain text message body.
106
     * If HTML then call isHTML(true).
107
     * @var string
108
     */
109
    public $Body = '';
110
111
    /**
112
     * The plain-text message body.
113
     * This body can be read by mail clients that do not have HTML email
114
     * capability such as mutt & Eudora.
115
     * Clients that can read HTML will view the normal Body.
116
     * @var string
117
     */
118
    public $AltBody = '';
119
120
    /**
121
     * An iCal message part body.
122
     * Only supported in simple alt or alt_inline message types
123
     * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
124
     * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
125
     * @link http://kigkonsult.se/iCalcreator/
126
     * @var string
127
     */
128
    public $Ical = '';
129
130
    /**
131
     * The complete compiled MIME message body.
132
     * @access protected
133
     * @var string
134
     */
135
    protected $MIMEBody = '';
136
137
    /**
138
     * The complete compiled MIME message headers.
139
     * @var string
140
     * @access protected
141
     */
142
    protected $MIMEHeader = '';
143
144
    /**
145
     * Extra headers that createHeader() doesn't fold in.
146
     * @var string
147
     * @access protected
148
     */
149
    protected $mailHeader = '';
150
151
    /**
152
     * Word-wrap the message body to this number of chars.
153
     * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
154
     * @var integer
155
     */
156
    public $WordWrap = 0;
157
158
    /**
159
     * Which method to use to send mail.
160
     * Options: "mail", "sendmail", or "smtp".
161
     * @var string
162
     */
163
    public $Mailer = 'mail';
164
165
    /**
166
     * The path to the sendmail program.
167
     * Must contain only a path to an executable, with no parameters or switches
168
     * @var string
169
     */
170
    public $Sendmail = '/usr/sbin/sendmail';
171
172
    /**
173
     * Whether mail() uses a fully sendmail-compatible MTA.
174
     * One which supports sendmail's "-oi -f" options.
175
     * @var boolean
176
     */
177
    public $UseSendmailOptions = true;
178
179
    /**
180
     * Path to PHPMailer plugins.
181
     * Useful if the SMTP class is not in the PHP include path.
182
     * @var string
183
     * @deprecated Should not be needed now there is an autoloader.
184
     */
185
    public $PluginDir = '';
186
187
    /**
188
     * The email address that a reading confirmation should be sent to, also known as read receipt.
189
     * @var string
190
     */
191
    public $ConfirmReadingTo = '';
192
193
    /**
194
     * The hostname to use in the Message-ID header and as default HELO string.
195
     * If empty, PHPMailer attempts to find one with, in order,
196
     * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
197
     * 'localhost.localdomain'.
198
     * @var string
199
     */
200
    public $Hostname = '';
201
202
    /**
203
     * An ID to be used in the Message-ID header.
204
     * If empty, a unique id will be generated.
205
     * You can set your own, but it must be in the format "<id@domain>",
206
     * as defined in RFC5322 section 3.6.4 or it will be ignored.
207
     * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
208
     * @var string
209
     */
210
    public $MessageID = '';
211
212
    /**
213
     * The message Date to be used in the Date header.
214
     * If empty, the current date will be added.
215
     * @var string
216
     */
217
    public $MessageDate = '';
218
219
    /**
220
     * SMTP hosts.
221
     * Either a single hostname or multiple semicolon-delimited hostnames.
222
     * You can also specify a different port
223
     * for each host by using this format: [hostname:port]
224
     * (e.g. "smtp1.example.com:25;smtp2.example.com").
225
     * You can also specify encryption type, for example:
226
     * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
227
     * Hosts will be tried in order.
228
     * @var string
229
     */
230
    public $Host = 'localhost';
231
232
    /**
233
     * The default SMTP server port.
234
     * @var integer
235
     * @TODO Why is this needed when the SMTP class takes care of it?
236
     */
237
    public $Port = 25;
238
239
    /**
240
     * The SMTP HELO of the message.
241
     * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
242
     * one with the same method described above for $Hostname.
243
     * @var string
244
     * @see PHPMailer::$Hostname
245
     */
246
    public $Helo = '';
247
248
    /**
249
     * What kind of encryption to use on the SMTP connection.
250
     * Options: '', 'ssl' or 'tls'
251
     * @var string
252
     */
253
    public $SMTPSecure = '';
254
255
    /**
256
     * Whether to enable TLS encryption automatically if a server supports it,
257
     * even if `SMTPSecure` is not set to 'tls'.
258
     * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
259
     * @var boolean
260
     */
261
    public $SMTPAutoTLS = true;
262
263
    /**
264
     * Whether to use SMTP authentication.
265
     * Uses the Username and Password properties.
266
     * @var boolean
267
     * @see PHPMailer::$Username
268
     * @see PHPMailer::$Password
269
     */
270
    public $SMTPAuth = false;
271
272
    /**
273
     * Options array passed to stream_context_create when connecting via SMTP.
274
     * @var array
275
     */
276
    public $SMTPOptions = array();
277
278
    /**
279
     * SMTP username.
280
     * @var string
281
     */
282
    public $Username = '';
283
284
    /**
285
     * SMTP password.
286
     * @var string
287
     */
288
    public $Password = '';
289
290
    /**
291
     * SMTP auth type.
292
     * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified
293
     * @var string
294
     */
295
    public $AuthType = '';
296
297
    /**
298
     * SMTP realm.
299
     * Used for NTLM auth
300
     * @var string
301
     */
302
    public $Realm = '';
303
304
    /**
305
     * SMTP workstation.
306
     * Used for NTLM auth
307
     * @var string
308
     */
309
    public $Workstation = '';
310
311
    /**
312
     * The SMTP server timeout in seconds.
313
     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
314
     * @var integer
315
     */
316
    public $Timeout = 300;
317
318
    /**
319
     * SMTP class debug output mode.
320
     * Debug output level.
321
     * Options:
322
     * * `0` No output
323
     * * `1` Commands
324
     * * `2` Data and commands
325
     * * `3` As 2 plus connection status
326
     * * `4` Low-level data output
327
     * @var integer
328
     * @see SMTP::$do_debug
329
     */
330
    public $SMTPDebug = 0;
331
332
    /**
333
     * How to handle debug output.
334
     * Options:
335
     * * `echo` Output plain-text as-is, appropriate for CLI
336
     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
337
     * * `error_log` Output to error log as configured in php.ini
338
     *
339
     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
340
     * <code>
341
     * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
342
     * </code>
343
     * @var string|callable
344
     * @see SMTP::$Debugoutput
345
     */
346
    public $Debugoutput = 'echo';
347
348
    /**
349
     * Whether to keep SMTP connection open after each message.
350
     * If this is set to true then to close the connection
351
     * requires an explicit call to smtpClose().
352
     * @var boolean
353
     */
354
    public $SMTPKeepAlive = false;
355
356
    /**
357
     * Whether to split multiple to addresses into multiple messages
358
     * or send them all in one message.
359
     * Only supported in `mail` and `sendmail` transports, not in SMTP.
360
     * @var boolean
361
     */
362
    public $SingleTo = false;
363
364
    /**
365
     * Storage for addresses when SingleTo is enabled.
366
     * @var array
367
     * @TODO This should really not be public
368
     */
369
    public $SingleToArray = array();
370
371
    /**
372
     * Whether to generate VERP addresses on send.
373
     * Only applicable when sending via SMTP.
374
     * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
375
     * @link http://www.postfix.org/VERP_README.html Postfix VERP info
376
     * @var boolean
377
     */
378
    public $do_verp = false;
379
380
    /**
381
     * Whether to allow sending messages with an empty body.
382
     * @var boolean
383
     */
384
    public $AllowEmpty = false;
385
386
    /**
387
     * The default line ending.
388
     * @note The default remains "\n". We force CRLF where we know
389
     *        it must be used via self::CRLF.
390
     * @var string
391
     */
392
    public $LE = "\n";
393
394
    /**
395
     * DKIM selector.
396
     * @var string
397
     */
398
    public $DKIM_selector = '';
399
400
    /**
401
     * DKIM Identity.
402
     * Usually the email address used as the source of the email.
403
     * @var string
404
     */
405
    public $DKIM_identity = '';
406
407
    /**
408
     * DKIM passphrase.
409
     * Used if your key is encrypted.
410
     * @var string
411
     */
412
    public $DKIM_passphrase = '';
413
414
    /**
415
     * DKIM signing domain name.
416
     * @example 'example.com'
417
     * @var string
418
     */
419
    public $DKIM_domain = '';
420
421
    /**
422
     * DKIM private key file path.
423
     * @var string
424
     */
425
    public $DKIM_private = '';
426
427
    /**
428
     * DKIM private key string.
429
     * If set, takes precedence over `$DKIM_private`.
430
     * @var string
431
     */
432
    public $DKIM_private_string = '';
433
434
    /**
435
     * Callback Action function name.
436
     *
437
     * The function that handles the result of the send email action.
438
     * It is called out by send() for each email sent.
439
     *
440
     * Value can be any php callable: http://www.php.net/is_callable
441
     *
442
     * Parameters:
443
     *   boolean $result        result of the send action
444
     *   string  $to            email address of the recipient
445
     *   string  $cc            cc email addresses
446
     *   string  $bcc           bcc email addresses
447
     *   string  $subject       the subject
448
     *   string  $body          the email body
449
     *   string  $from          email address of sender
450
     * @var string
451
     */
452
    public $action_function = '';
453
454
    /**
455
     * What to put in the X-Mailer header.
456
     * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
457
     * @var string
458
     */
459
    public $XMailer = '';
460
461
    /**
462
     * Which validator to use by default when validating email addresses.
463
     * May be a callable to inject your own validator, but there are several built-in validators.
464
     * @see PHPMailer::validateAddress()
465
     * @var string|callable
466
     * @static
467
     */
468
    public static $validator = 'auto';
469
470
    /**
471
     * An instance of the SMTP sender class.
472
     * @var SMTP
473
     * @access protected
474
     */
475
    protected $smtp = null;
476
477
    /**
478
     * The array of 'to' names and addresses.
479
     * @var array
480
     * @access protected
481
     */
482
    protected $to = array();
483
484
    /**
485
     * The array of 'cc' names and addresses.
486
     * @var array
487
     * @access protected
488
     */
489
    protected $cc = array();
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...
490
491
    /**
492
     * The array of 'bcc' names and addresses.
493
     * @var array
494
     * @access protected
495
     */
496
    protected $bcc = array();
497
498
    /**
499
     * The array of reply-to names and addresses.
500
     * @var array
501
     * @access protected
502
     */
503
    protected $ReplyTo = array();
504
505
    /**
506
     * An array of all kinds of addresses.
507
     * Includes all of $to, $cc, $bcc
508
     * @var array
509
     * @access protected
510
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
511
     */
512
    protected $all_recipients = array();
513
514
    /**
515
     * An array of names and addresses queued for validation.
516
     * In send(), valid and non duplicate entries are moved to $all_recipients
517
     * and one of $to, $cc, or $bcc.
518
     * This array is used only for addresses with IDN.
519
     * @var array
520
     * @access protected
521
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
522
     * @see PHPMailer::$all_recipients
523
     */
524
    protected $RecipientsQueue = array();
525
526
    /**
527
     * An array of reply-to names and addresses queued for validation.
528
     * In send(), valid and non duplicate entries are moved to $ReplyTo.
529
     * This array is used only for addresses with IDN.
530
     * @var array
531
     * @access protected
532
     * @see PHPMailer::$ReplyTo
533
     */
534
    protected $ReplyToQueue = array();
535
536
    /**
537
     * The array of attachments.
538
     * @var array
539
     * @access protected
540
     */
541
    protected $attachment = array();
542
543
    /**
544
     * The array of custom headers.
545
     * @var array
546
     * @access protected
547
     */
548
    protected $CustomHeader = array();
549
550
    /**
551
     * The most recent Message-ID (including angular brackets).
552
     * @var string
553
     * @access protected
554
     */
555
    protected $lastMessageID = '';
556
557
    /**
558
     * The message's MIME type.
559
     * @var string
560
     * @access protected
561
     */
562
    protected $message_type = '';
563
564
    /**
565
     * The array of MIME boundary strings.
566
     * @var array
567
     * @access protected
568
     */
569
    protected $boundary = array();
570
571
    /**
572
     * The array of available languages.
573
     * @var array
574
     * @access protected
575
     */
576
    protected $language = array();
577
578
    /**
579
     * The number of errors encountered.
580
     * @var integer
581
     * @access protected
582
     */
583
    protected $error_count = 0;
584
585
    /**
586
     * The S/MIME certificate file path.
587
     * @var string
588
     * @access protected
589
     */
590
    protected $sign_cert_file = '';
591
592
    /**
593
     * The S/MIME key file path.
594
     * @var string
595
     * @access protected
596
     */
597
    protected $sign_key_file = '';
598
599
    /**
600
     * The optional S/MIME extra certificates ("CA Chain") file path.
601
     * @var string
602
     * @access protected
603
     */
604
    protected $sign_extracerts_file = '';
605
606
    /**
607
     * The S/MIME password for the key.
608
     * Used only if the key is encrypted.
609
     * @var string
610
     * @access protected
611
     */
612
    protected $sign_key_pass = '';
613
614
    /**
615
     * Whether to throw exceptions for errors.
616
     * @var boolean
617
     * @access protected
618
     */
619
    protected $exceptions = false;
620
621
    /**
622
     * Unique ID used for message ID and boundaries.
623
     * @var string
624
     * @access protected
625
     */
626
    protected $uniqueid = '';
627
628
    /**
629
     * Error severity: message only, continue processing.
630
     */
631
    const STOP_MESSAGE = 0;
632
633
    /**
634
     * Error severity: message, likely ok to continue processing.
635
     */
636
    const STOP_CONTINUE = 1;
637
638
    /**
639
     * Error severity: message, plus full stop, critical error reached.
640
     */
641
    const STOP_CRITICAL = 2;
642
643
    /**
644
     * SMTP RFC standard line ending.
645
     */
646
    const CRLF = "\r\n";
647 56
648
    /**
649 56
     * The maximum line length allowed by RFC 2822 section 2.1.1
650
     * @var integer
651
     */
652 56
    const MAX_LINE_LENGTH = 998;
653
654
    /**
655
     * Constructor.
656
     * @param boolean $exceptions Should we throw external exceptions?
657 56
     */
658
    public function __construct($exceptions = null)
659
    {
660 56
        if ($exceptions !== null) {
661 56
            $this->exceptions = (boolean)$exceptions;
662
        }
663
    }
664
665
    /**
666
     * Destructor.
667
     */
668
    public function __destruct()
669
    {
670
        //Close any open SMTP connection nicely
671
        $this->smtpClose();
672
    }
673
674
    /**
675
     * Call mail() in a safe_mode-aware fashion.
676 2
     * Also, unless sendmail_path points to sendmail (or something that
677
     * claims to be sendmail), don't pass params (not a perfect fix,
678
     * but it will do)
679 2
     * @param string $to To
680
     * @param string $subject Subject
681
     * @param string $body Message Body
682 2
     * @param string $header Additional Header(s)
683
     * @param string $params Params
684
     * @access private
685
     * @return boolean
686 2
     */
687
    private function mailPassthru($to, $subject, $body, $header, $params)
688
    {
689 2
        //Check overloading of mail function to avoid double-encoding
690
        if (ini_get('mbstring.func_overload') & 1) {
691 2
            $subject = $this->secureHeader($subject);
692
        } else {
693
            $subject = $this->encodeHeader($this->secureHeader($subject));
694
        }
695
696
        //Can't use additional_parameters in safe_mode, calling mail() with null params breaks
697
        //@link http://php.net/manual/en/function.mail.php
698
        if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
699
            $result = @mail($to, $subject, $body, $header);
700
        } else {
701 4
            $result = @mail($to, $subject, $body, $header, $params);
702
        }
703 4
        return $result;
704
    }
705
    /**
706
     * Output debugging info via user-defined method.
707 4
     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
708
     * @see PHPMailer::$Debugoutput
709
     * @see PHPMailer::$SMTPDebug
710
     * @param string $str
711 4
     */
712 4
    protected function edebug($str)
713
    {
714
        if ($this->SMTPDebug <= 0) {
715
            return;
716 4
        }
717
        //Avoid clash with built-in function names
718
        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
719
            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
720
            return;
721
        }
722
        switch ($this->Debugoutput) {
723
            case 'error_log':
724
                //Don't output, just log
725 4
                error_log($str);
726 4
                break;
727
            case 'html':
728 4
                //Cleans up output a bit for a better looking, HTML-safe output
729 4
                echo htmlentities(
730 4
                    preg_replace('/[\r\n]+/', '', $str),
731 4
                    ENT_QUOTES,
732 4
                    'UTF-8'
733 4
                )
734 4
                . "<br>\n";
735 4
                break;
736
            case 'echo':
737
            default:
738
                //Normalize line breaks
739
                $str = preg_replace('/\r\n?/ms', "\n", $str);
740
                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
741
                    "\n",
742 16
                    "\n                   \t                  ",
743
                    trim($str)
744 16
                ) . "\n";
745 13
        }
746 13
    }
747 3
748
    /**
749 16
     * Sets message type to HTML or plain.
750
     * @param boolean $isHtml True for HTML mode.
751
     * @return void
752
     */
753
    public function isHTML($isHtml = true)
754
    {
755 1
        if ($isHtml) {
756
            $this->ContentType = 'text/html';
757 1
        } else {
758 1
            $this->ContentType = 'text/plain';
759
        }
760
    }
761
762
    /**
763
     * Send messages using SMTP.
764 3
     * @return void
765
     */
766 3
    public function isSMTP()
767 3
    {
768
        $this->Mailer = 'smtp';
769
    }
770
771
    /**
772
     * Send messages using PHP's mail() function.
773 2
     * @return void
774
     */
775 2
    public function isMail()
776
    {
777 2
        $this->Mailer = 'mail';
778
    }
779
780 2
    /**
781
     * Send messages using $Sendmail.
782 2
     * @return void
783 2
     */
784
    public function isSendmail()
785
    {
786
        $ini_sendmail_path = ini_get('sendmail_path');
787
788
        if (!stristr($ini_sendmail_path, 'sendmail')) {
789 1
            $this->Sendmail = '/usr/sbin/sendmail';
790
        } else {
791 1
            $this->Sendmail = $ini_sendmail_path;
792
        }
793 1
        $this->Mailer = 'sendmail';
794 1
    }
795 1
796
    /**
797
     * Send messages using qmail.
798 1
     * @return void
799 1
     */
800
    public function isQmail()
801
    {
802
        $ini_sendmail_path = ini_get('sendmail_path');
803
804
        if (!stristr($ini_sendmail_path, 'qmail')) {
805
            $this->Sendmail = '/var/qmail/bin/qmail-inject';
806
        } else {
807 56
            $this->Sendmail = $ini_sendmail_path;
808
        }
809 56
        $this->Mailer = 'qmail';
810
    }
811
812
    /**
813
     * Add a "To" address.
814
     * @param string $address The email address to send to
815
     * @param string $name
816
     * @return boolean true on success, false if address already used or invalid in some way
817
     */
818
    public function addAddress($address, $name = '')
819 56
    {
820
        return $this->addOrEnqueueAnAddress('to', $address, $name);
821 56
    }
822
823
    /**
824
     * Add a "CC" address.
825
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
826
     * @param string $address The email address to send to
827
     * @param string $name
828
     * @return boolean true on success, false if address already used or invalid in some way
829
     */
830
    public function addCC($address, $name = '')
831 3
    {
832
        return $this->addOrEnqueueAnAddress('cc', $address, $name);
833 3
    }
834
835
    /**
836
     * Add a "BCC" address.
837
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
838
     * @param string $address The email address to send to
839
     * @param string $name
840
     * @return boolean true on success, false if address already used or invalid in some way
841
     */
842 56
    public function addBCC($address, $name = '')
843
    {
844 56
        return $this->addOrEnqueueAnAddress('bcc', $address, $name);
845
    }
846
847
    /**
848
     * Add a "Reply-To" address.
849
     * @param string $address The email address to reply to
850
     * @param string $name
851
     * @return boolean true on success, false if address already used or invalid in some way
852
     */
853
    public function addReplyTo($address, $name = '')
854
    {
855
        return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
856
    }
857
858
    /**
859 56
     * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
860
     * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
861 56
     * be modified after calling this function), addition of such addresses is delayed until send().
862 56
     * Addresses that have been added already return false, but do not throw exceptions.
863 56
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
864
     * @param string $address The email address to send, resp. to reply to
865 1
     * @param string $name
866 1
     * @throws phpmailerException
867 1
     * @return boolean true on success, false if address already used or invalid in some way
868 1
     * @access protected
869
     */
870
    protected function addOrEnqueueAnAddress($kind, $address, $name)
871 1
    {
872
        $address = trim($address);
873 56
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
874
        if (($pos = strrpos($address, '@')) === false) {
875 56
            // At-sign is misssing.
876 2
            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
877 2
            $this->setError($error_message);
878 2
            $this->edebug($error_message);
879 2
            if ($this->exceptions) {
880
                throw new phpmailerException($error_message);
881 1
            }
882 2
            return false;
883 2
        }
884 2
        $params = array($kind, $address, $name);
885
        // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
886
        if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
887 1
            if ($kind != 'Reply-To') {
888
                if (!array_key_exists($address, $this->RecipientsQueue)) {
889
                    $this->RecipientsQueue[$address] = $params;
890 56
                    return true;
891
                }
892
            } else {
893
                if (!array_key_exists($address, $this->ReplyToQueue)) {
894
                    $this->ReplyToQueue[$address] = $params;
895
                    return true;
896
                }
897
            }
898
            return false;
899
        }
900
        // Immediately add standard addresses without IDN.
901
        return call_user_func_array(array($this, 'addAnAddress'), $params);
902
    }
903 56
904
    /**
905 56
     * Add an address to one of the recipient arrays or to the ReplyTo array.
906
     * Addresses that have been added already return false, but do not throw exceptions.
907
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
908
     * @param string $address The email address to send, resp. to reply to
909
     * @param string $name
910
     * @throws phpmailerException
911
     * @return boolean true on success, false if address already used or invalid in some way
912
     * @access protected
913
     */
914 56
    protected function addAnAddress($kind, $address, $name = '')
915 2
    {
916 2
        if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
917 2
            $error_message = $this->lang('Invalid recipient kind: ') . $kind;
918 2
            $this->setError($error_message);
919
            $this->edebug($error_message);
920
            if ($this->exceptions) {
921 2
                throw new phpmailerException($error_message);
922
            }
923 56
            return false;
924 56
        }
925 56
        if (!$this->validateAddress($address)) {
926 56
            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
927 56
            $this->setError($error_message);
928
            $this->edebug($error_message);
929 2
            if ($this->exceptions) {
930 56
                throw new phpmailerException($error_message);
931 56
            }
932 56
            return false;
933
        }
934
        if ($kind != 'Reply-To') {
935 2
            if (!array_key_exists(strtolower($address), $this->all_recipients)) {
936
                array_push($this->$kind, array($address, $name));
937
                $this->all_recipients[strtolower($address)] = true;
938
                return true;
939
            }
940
        } else {
941
            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
942
                $this->ReplyTo[strtolower($address)] = array($address, $name);
943
                return true;
944
            }
945
        }
946
        return false;
947
    }
948 1
949
    /**
950 1
     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
951 1
     * of the form "display name <address>" into an array of name/address pairs.
952
     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
953 1
     * Note that quotes in the name part are removed.
954 1
     * @param string $addrstr The address list string
955 1
     * @param bool $useimap Whether to use the IMAP extension to parse the list
956 1
     * @return array
957 1
     * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
958 1
     */
959 1
    public function parseAddresses($addrstr, $useimap = true)
960 1
    {
961 1
        $addresses = array();
962 1
        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
963 1
            //Use this built-in parser if it's available
964 1
            $list = imap_rfc822_parse_adrlist($addrstr, '');
965
            foreach ($list as $address) {
966 1
                if ($address->host != '.SYNTAX-ERROR.') {
967 1
                    if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
968 1
                        $addresses[] = array(
969
                            'name' => (property_exists($address, 'personal') ? $address->personal : ''),
970 1
                            'address' => $address->mailbox . '@' . $address->host
971
                        );
972 1
                    }
973 1
                }
974 1
            }
975
        } else {
976 1
            //Use this simpler parser
977 1
            $list = explode(',', $addrstr);
978 1
            foreach ($list as $address) {
979 1
                $address = trim($address);
980 1
                //Is there a separate name part?
981 1
                if (strpos($address, '<') === false) {
982 1
                    //No separate name, just use the whole thing
983 1
                    if ($this->validateAddress($address)) {
984
                        $addresses[] = array(
985 1
                            'name' => '',
986 1
                            'address' => $address
987
                        );
988 1
                    }
989
                } else {
990 1
                    list($name, $email) = explode('<', $address);
991
                    $email = trim(str_replace('>', '', $email));
992
                    if ($this->validateAddress($email)) {
993
                        $addresses[] = array(
994
                            'name' => trim(str_replace(array('"', "'"), '', $name)),
995
                            'address' => $email
996
                        );
997
                    }
998
                }
999
            }
1000
        }
1001 1
        return $addresses;
1002
    }
1003 1
1004 1
    /**
1005
     * Set the From and FromName properties.
1006 1
     * @param string $address
1007 1
     * @param string $name
1008 1
     * @param boolean $auto Whether to also set the Sender address, defaults to true
1009 1
     * @throws phpmailerException
1010 1
     * @return boolean
1011 1
     */
1012 1
    public function setFrom($address, $name = '', $auto = true)
1013
    {
1014
        $address = trim($address);
1015 1
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
1016
        // Don't validate now addresses with IDN. Will be done in send().
1017 1
        if (($pos = strrpos($address, '@')) === false or
1018 1
            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
1019 1
            !$this->validateAddress($address)) {
1020 1
            $error_message = $this->lang('invalid_address') . " (setFrom) $address";
1021 1
            $this->setError($error_message);
1022 1
            $this->edebug($error_message);
1023 1
            if ($this->exceptions) {
1024 1
                throw new phpmailerException($error_message);
1025
            }
1026
            return false;
1027
        }
1028
        $this->From = $address;
1029
        $this->FromName = $name;
1030
        if ($auto) {
1031
            if (empty($this->Sender)) {
1032
                $this->Sender = $address;
1033
            }
1034 1
        }
1035
        return true;
1036 1
    }
1037
1038
    /**
1039
     * Return the Message-ID header of the last email.
1040
     * Technically this is the value from the last time the headers were created,
1041
     * but it's also the message ID of the last sent message except in
1042
     * pathological cases.
1043
     * @return string
1044
     */
1045
    public function getLastMessageID()
1046
    {
1047
        return $this->lastMessageID;
1048
    }
1049
1050
    /**
1051
     * Check that a string looks like an email address.
1052
     * @param string $address The email address to check
1053
     * @param string|callable $patternselect A selector for the validation pattern to use :
1054
     * * `auto` Pick best pattern automatically;
1055
     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
1056
     * * `pcre` Use old PCRE implementation;
1057
     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1058 56
     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1059
     * * `noregex` Don't use a regex: super fast, really dumb.
1060 56
     * Alternatively you may pass in a callable to inject your own validator, for example:
1061 56
     * PHPMailer::validateAddress('[email protected]', function($address) {
1062 56
     *     return (strpos($address, '@') !== false);
1063 56
     * });
1064 1
     * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
1065
     * @return boolean
1066
     * @static
1067 56
     * @access public
1068 1
     */
1069
    public static function validateAddress($address, $patternselect = null)
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...
1070 56
    {
1071
        if (is_null($patternselect)) {
1072
            $patternselect = self::$validator;
1073 4
        }
1074
        if (is_callable($patternselect)) {
1075 4
            return call_user_func($patternselect, $address);
1076 4
        }
1077 4
        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1078
        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1079
            return false;
1080 4
        }
1081
        if (!$patternselect or $patternselect == 'auto') {
1082
            //Check this constant first so it works when extension_loaded() is disabled by safe mode
1083
            //Constant was added in PHP 5.2.4
1084
            if (defined('PCRE_VERSION')) {
1085
                //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
1086
                if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
1087
                    $patternselect = 'pcre8';
1088
                } else {
1089
                    $patternselect = 'pcre';
1090
                }
1091 4
            } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
1092
                //Fall back to older PCRE
1093 56
                $patternselect = 'pcre';
1094
            } else {
1095
                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1096
                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1097
                    $patternselect = 'php';
1098
                } else {
1099
                    $patternselect = 'noregex';
1100 4
                }
1101
            }
1102 4
        }
1103 4
        switch ($patternselect) {
1104 4
            case 'pcre8':
1105 4
                /**
1106 4
                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1107 4
                 * @link http://squiloople.com/2009/12/20/email-address-validation/
1108 4
                 * @copyright 2009-2010 Michael Rushton
1109 4
                 * Feel free to use and redistribute this code. But please keep this copyright notice.
1110
                 */
1111 4
                return (boolean)preg_match(
1112 54
                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1113
                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1114 1
                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1115
                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1116 1
                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1117 1
                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1118 1
                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1119 1
                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1120 1
                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1121 1
                    $address
1122 1
                );
1123 1
            case 'pcre':
1124 1
                //An older regex that doesn't need a recent PCRE
1125
                return (boolean)preg_match(
1126 1
                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1127 54
                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1128
                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1129
                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1130
                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1131
                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1132
                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1133
                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1134
                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1135
                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1136
                    $address
1137 54
                );
1138
            case 'html5':
1139
                /**
1140 1
                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1141 1
                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1142 1
                 */
1143 54
                return (boolean)preg_match(
1144 54
                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1145 54
                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1146
                    $address
1147
                );
1148
            case 'noregex':
1149
                //No PCRE! Do something _very_ approximate!
1150
                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1151
                return (strlen($address) >= 3
1152
                    and strpos($address, '@') >= 1
1153
                    and strpos($address, '@') != strlen($address) - 1);
1154 41
            case 'php':
1155
            default:
1156
                return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
1157 41
        }
1158
    }
1159
1160
    /**
1161
     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1162
     * "intl" and "mbstring" PHP extensions.
1163
     * @return bool "true" if required functions for IDN support are present
1164
     */
1165
    public function idnSupported()
1166
    {
1167
        // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1168
        return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1169
    }
1170
1171 41
    /**
1172
     * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1173
     * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1174 41
     * This function silently returns unmodified address if:
1175 41
     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1176 41
     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1177 41
     *   or fails for any reason (e.g. domain has characters not allowed in an IDN)
1178
     * @see PHPMailer::$CharSet
1179 41
     * @param string $address The email address to convert
1180 3
     * @return string The encoded address in ASCII form
1181 3
     */
1182 3
    public function punyencodeAddress($address)
1183 3
    {
1184 3
        // Verify we have required functions, CharSet, and at-sign.
1185
        if ($this->idnSupported() and
1186
            !empty($this->CharSet) and
1187 41
            ($pos = strrpos($address, '@')) !== false) {
1188 41
            $domain = substr($address, ++$pos);
1189
            // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1190
            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1191
                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1192
                if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1193
                    idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
1194
                    idn_to_ascii($domain)) !== false) {
1195
                    return substr($address, 0, $pos) . $punycode;
1196
                }
1197 36
            }
1198
        }
1199
        return $address;
1200 36
    }
1201 3
1202
    /**
1203 36
     * Create a message and send it.
1204
     * Uses the sending method specified by $Mailer.
1205
     * @throws phpmailerException
1206
     * @return boolean false on error - See the ErrorInfo property for details of the error.
1207
     */
1208
    public function send()
1209
    {
1210
        try {
1211
            if (!$this->preSend()) {
1212
                return false;
1213
            }
1214
            return $this->postSend();
1215
        } catch (phpmailerException $exc) {
1216
            $this->mailHeader = '';
1217
            $this->setError($exc->getMessage());
1218
            if ($this->exceptions) {
1219 41
                throw $exc;
1220
            }
1221
            return false;
1222 41
        }
1223 41
    }
1224
1225
    /**
1226 41
     * Prepare a message for sending.
1227 2
     * @throws phpmailerException
1228 2
     * @return boolean
1229 41
     */
1230 41
    public function preSend()
0 ignored issues
show
Complexity introduced by
This operation has 3026 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...
1231 1
    {
1232
        try {
1233
            $this->error_count = 0; // Reset errors
1234
            $this->mailHeader = '';
1235 41
1236 41
            // Dequeue recipient and Reply-To addresses with IDN
1237 41
            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1238 40
                $params[1] = $this->punyencodeAddress($params[1]);
1239
                call_user_func_array(array($this, 'addAnAddress'), $params);
1240 41
            }
1241 41
            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1242 1
                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1243 1
            }
1244 1
1245 1
            // Validate From, Sender, and ConfirmReadingTo addresses
1246
            foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
1247
                $this->$address_kind = trim($this->$address_kind);
1248 1
                if (empty($this->$address_kind)) {
1249
                    continue;
1250 41
                }
1251
                $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1252
                if (!$this->validateAddress($this->$address_kind)) {
1253 41
                    $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
1254 7
                    $this->setError($error_message);
1255 7
                    $this->edebug($error_message);
1256
                    if ($this->exceptions) {
1257 41
                        throw new phpmailerException($error_message);
1258
                    }
1259 41
                    return false;
1260 1
                }
1261
            }
1262
1263
            // Set whether the message is multipart/alternative
1264 41
            if ($this->alternativeExists()) {
1265 41
                $this->ContentType = 'multipart/alternative';
1266
            }
1267 41
1268 41
            $this->setMessageType();
1269 41
            // Refuse to send an empty message unless we are specifically allowing it
1270
            if (!$this->AllowEmpty and empty($this->Body)) {
1271
                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1272
            }
1273 41
1274 2
            // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1275 2
            $this->MIMEHeader = '';
1276 2
            $this->MIMEBody = $this->createBody();
1277
            // createBody may have added some headers, so retain them
1278
            $tempheaders = $this->MIMEHeader;
1279 2
            $this->MIMEHeader = $this->createHeader();
1280 2
            $this->MIMEHeader .= $tempheaders;
1281 2
1282 2
            // To capture the complete message when using mail(), create
1283 2
            // an extra header list which createHeader() doesn't fold in
1284
            if ($this->Mailer == 'mail') {
1285
                if (count($this->to) > 0) {
1286 41
                    $this->mailHeader .= $this->addrAppend('To', $this->to);
1287 41
                } else {
1288 41
                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1289 41
                }
1290 1
                $this->mailHeader .= $this->headerLine(
1291 1
                    'Subject',
1292 1
                    $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1293 1
                );
1294 1
            }
1295 1
1296 1
            // Sign with DKIM if enabled
1297 1
            if (!empty($this->DKIM_domain)
1298 41
                && !empty($this->DKIM_selector)
1299 2
                && (!empty($this->DKIM_private_string)
1300 2
                   || (!empty($this->DKIM_private) && file_exists($this->DKIM_private))
1301 2
                )
1302
            ) {
1303
                $header_dkim = $this->DKIM_Add(
1304 2
                    $this->MIMEHeader . $this->mailHeader,
1305
                    $this->encodeHeader($this->secureHeader($this->Subject)),
1306
                    $this->MIMEBody
1307
                );
1308
                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1309
                    str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1310
            }
1311
            return true;
1312
        } catch (phpmailerException $exc) {
1313
            $this->setError($exc->getMessage());
1314 36
            if ($this->exceptions) {
1315
                throw $exc;
1316
            }
1317
            return false;
1318 36
        }
1319 36
    }
1320 36
1321 1
    /**
1322 35
     * Actually send a message.
1323 33
     * Send the email via the selected mechanism
1324 2
     * @throws phpmailerException
1325 2
     * @return boolean
1326
     */
1327
    public function postSend()
1328
    {
1329
        try {
1330
            // Choose the mailer and send through it
1331
            switch ($this->Mailer) {
1332
                case 'sendmail':
1333
                case 'qmail':
1334
                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1335
                case 'smtp':
1336
                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1337
                case 'mail':
1338
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1339
                default:
1340
                    $sendMethod = $this->Mailer.'Send';
1341
                    if (method_exists($this, $sendMethod)) {
1342
                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1343
                    }
1344
1345
                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1346
            }
1347
        } catch (phpmailerException $exc) {
1348
            $this->setError($exc->getMessage());
1349
            $this->edebug($exc->getMessage());
1350
            if ($this->exceptions) {
1351
                throw $exc;
1352
            }
1353 1
        }
1354
        return false;
1355 1
    }
1356 1
1357
    /**
1358
     * Send mail using the $Sendmail program.
1359 1
     * @param string $header The message headers
1360
     * @param string $body The message body
1361 1
     * @see PHPMailer::$Sendmail
1362
     * @throws phpmailerException
1363
     * @access protected
1364
     * @return boolean
1365
     */
1366
    protected function sendmailSend($header, $body)
1367
    {
1368 1
        if (!(is_file($this->Sendmail) and is_executable($this->Sendmail))) {
1369
            throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1370
        }
1371
        if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1372
            if ($this->Mailer == 'qmail') {
1373
                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1374
            } else {
1375
                $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1376
            }
1377
        } else {
1378
            if ($this->Mailer == 'qmail') {
1379
                $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1380
            } else {
1381
                $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1382
            }
1383
        }
1384
        if ($this->SingleTo) {
1385
            foreach ($this->SingleToArray as $toAddr) {
1386
                if (!@$mail = popen($sendmail, 'w')) {
1387
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1388
                }
1389
                fputs($mail, 'To: ' . $toAddr . "\n");
1390
                fputs($mail, $header);
1391 1
                fputs($mail, $body);
1392
                $result = pclose($mail);
1393
                $this->doCallback(
1394 1
                    ($result == 0),
1395 1
                    array($toAddr),
1396 1
                    $this->cc,
1397 1
                    $this->bcc,
1398 1
                    $this->Subject,
1399 1
                    $body,
1400 1
                    $this->From
1401 1
                );
1402 1
                if ($result != 0) {
1403 1
                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1404 1
                }
1405 1
            }
1406 1
        } else {
1407
            if (!@$mail = popen($sendmail, 'w')) {
1408
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1409
            }
1410 1
            fputs($mail, $header);
1411
            fputs($mail, $body);
1412
            $result = pclose($mail);
1413
            $this->doCallback(
1414
                ($result == 0),
1415
                $this->to,
1416
                $this->cc,
1417
                $this->bcc,
1418
                $this->Subject,
1419
                $body,
1420
                $this->From
1421
            );
1422 2
            if ($result != 0) {
1423
                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1424 2
            }
1425 2
        }
1426 2
        return true;
1427 2
    }
1428 2
1429
    /**
1430 2
     * Send mail using the PHP mail() function.
1431
     * @param string $header The message headers
1432 2
     * @param string $body The message body
1433 2
     * @link http://www.php.net/manual/en/book.mail.php
1434 2
     * @throws phpmailerException
1435 2
     * @access protected
1436 2
     * @return boolean
1437 2
     */
1438 2
    protected function mailSend($header, $body)
1439 2
    {
1440 2
        $toArr = array();
1441
        foreach ($this->to as $toaddr) {
1442
            $toArr[] = $this->addrFormat($toaddr);
1443
        }
1444
        $to = implode(', ', $toArr);
1445
1446 2
        $params = null;
1447 2
        //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
1448
        if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1449 2
            $params = sprintf('-f%s', escapeshellarg($this->Sender));
1450 2
        }
1451 2
        if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
1452 2
            $old_from = ini_get('sendmail_from');
1453
            ini_set('sendmail_from', $this->Sender);
1454
        }
1455 2
        $result = false;
1456
        if ($this->SingleTo and count($toArr) > 1) {
1457
            foreach ($toArr as $toAddr) {
1458
                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1459
                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1460
            }
1461
        } else {
1462
            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1463 35
            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1464
        }
1465 35
        if (isset($old_from)) {
1466 35
            ini_set('sendmail_from', $old_from);
1467 35
        }
1468 35
        if (!$result) {
1469
            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1470
        }
1471
        return true;
1472
    }
1473
1474
    /**
1475
     * Get an instance to use for SMTP operations.
1476
     * Override this function to load your own SMTP implementation
1477
     * @return SMTP
1478
     */
1479
    public function getSMTPInstance()
1480
    {
1481
        if (!is_object($this->smtp)) {
1482
            $this->smtp = new SMTP;
1483 33
        }
1484
        return $this->smtp;
1485 33
    }
1486 33
1487
    /**
1488
     * Send mail via SMTP.
1489 33
     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1490
     * Uses the PHPMailerSMTP class by default.
1491
     * @see PHPMailer::getSMTPInstance() to use a different class.
1492 33
     * @param string $header The message headers
1493
     * @param string $body The message body
1494 33
     * @throws phpmailerException
1495
     * @uses SMTP
1496
     * @access protected
1497
     * @return boolean
1498
     */
1499
    protected function smtpSend($header, $body)
0 ignored issues
show
Complexity introduced by
This operation has 864 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...
1500 33
    {
1501 33
        $bad_rcpt = array();
1502 33
        if (!$this->smtpConnect($this->SMTPOptions)) {
1503
            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1504
        }
1505
        if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1506
            $smtp_from = $this->Sender;
1507 33
        } else {
1508
            $smtp_from = $this->From;
1509 33
        }
1510 33
        if (!$this->smtp->mail($smtp_from)) {
1511 33
            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1512
            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1513
        }
1514 33
1515
        // Attempt to send to all recipients
1516
        foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1517 33
            foreach ($togroup as $to) {
1518 1
                if (!$this->smtp->recipient($to[0])) {
1519 1
                    $error = $this->smtp->getError();
1520 32
                    $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1521 32
                    $isSent = false;
1522
                } else {
1523
                    $isSent = true;
1524 33
                }
1525
                $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1526
            }
1527
        }
1528
1529
        // Only send the DATA command if we have viable recipients
1530
        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1531
            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1532
        }
1533
        if ($this->SMTPKeepAlive) {
1534 33
            $this->smtp->reset();
1535
        } else {
1536
            $this->smtp->quit();
1537
            $this->smtp->close();
1538
        }
1539
        //Create error message for any bad addresses
1540
        if (count($bad_rcpt) > 0) {
1541
            $errstr = '';
1542
            foreach ($bad_rcpt as $bad) {
1543
                $errstr .= $bad['to'] . ': ' . $bad['error'];
1544
            }
1545
            throw new phpmailerException(
1546 35
                $this->lang('recipients_failed') . $errstr,
1547
                self::STOP_CONTINUE
1548 35
            );
1549 35
        }
1550 35
        return true;
1551
    }
1552
1553 35
    /**
1554 2
     * Initiate a connection to an SMTP server.
1555 2
     * Returns false if the operation failed.
1556
     * @param array $options An array of options compatible with stream_context_create()
1557
     * @uses SMTP
1558 35
     * @access public
1559 1
     * @throws phpmailerException
1560
     * @return boolean
1561
     */
1562 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...
1563 35
    {
1564 35
        if (is_null($this->smtp)) {
1565 35
            $this->smtp = $this->getSMTPInstance();
1566 35
        }
1567 35
1568
        //If no options are provided, use whatever is set in the instance
1569 35
        if (is_null($options)) {
1570 35
            $options = $this->SMTPOptions;
1571 35
        }
1572
1573
        // Already connected?
1574
        if ($this->smtp->connected()) {
1575
            return true;
1576
        }
1577
1578
        $this->smtp->setTimeout($this->Timeout);
1579
        $this->smtp->setDebugLevel($this->SMTPDebug);
1580 35
        $this->smtp->setDebugOutput($this->Debugoutput);
1581 35
        $this->smtp->setVerp($this->do_verp);
1582 35
        $hosts = explode(';', $this->Host);
1583 35
        $lastexception = null;
1584 1
1585 1
        foreach ($hosts as $hostentry) {
1586 1
            $hostinfo = array();
1587 35
            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1588 1
                // Not a valid host entry
1589
                continue;
1590 1
            }
1591 1
            // $hostinfo[2]: optional ssl or tls prefix
1592
            // $hostinfo[3]: the hostname
1593 35
            // $hostinfo[4]: optional port number
1594 35
            // The host string prefix can temporarily override the current setting for SMTPSecure
1595
            // If it's not specified, the default value is used
1596 1
            $prefix = '';
1597
            $secure = $this->SMTPSecure;
1598
            $tls = ($this->SMTPSecure == 'tls');
1599 1
            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1600 35
                $prefix = 'ssl://';
1601 35
                $tls = false; // Can't have SSL and TLS at the same time
1602 35
                $secure = 'ssl';
1603 35
            } elseif ($hostinfo[2] == 'tls') {
1604 1
                $tls = true;
1605 1
                // tls doesn't use a prefix
1606 35
                $secure = 'tls';
1607
            }
1608 35
            //Do we need the OpenSSL extension?
1609 35
            $sslext = defined('OPENSSL_ALGO_SHA1');
1610 35
            if ('tls' === $secure or 'ssl' === $secure) {
1611
                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1612
                if (!$sslext) {
1613 35
                    throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1614
                }
1615
            }
1616
            $host = $hostinfo[3];
1617
            $port = $this->Port;
1618
            $tport = (integer)$hostinfo[4];
1619 35
            if ($tport > 0 and $tport < 65536) {
1620
                $port = $tport;
1621
            }
1622 35
            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1623
                try {
1624
                    if ($this->Helo) {
1625
                        $hello = $this->Helo;
1626
                    } else {
1627
                        $hello = $this->serverHostname();
1628
                    }
1629 35
                    $this->smtp->hello($hello);
1630
                    //Automatically enable TLS encryption if:
1631
                    // * it's not disabled
1632
                    // * we have openssl extension
1633
                    // * we are not already using SSL
1634
                    // * the server offers STARTTLS
1635
                    if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1636
                        $tls = true;
1637
                    }
1638
                    if ($tls) {
1639
                        if (!$this->smtp->startTLS()) {
1640
                            throw new phpmailerException($this->lang('connect_host'));
1641 35
                        }
1642
                        // We must resend EHLO after TLS negotiation
1643
                        $this->smtp->hello($hello);
1644
                    }
1645
                    if ($this->SMTPAuth) {
1646
                        if (!$this->smtp->authenticate(
1647
                            $this->Username,
1648
                            $this->Password,
1649 1
                            $this->AuthType,
1650
                            $this->Realm,
1651 1
                            $this->Workstation
1652
                        )
1653 1
                        ) {
1654
                            throw new phpmailerException($this->lang('authenticate'));
1655
                        }
1656 1
                    }
1657
                    return true;
1658
                } catch (phpmailerException $exc) {
1659
                    $lastexception = $exc;
1660
                    $this->edebug($exc->getMessage());
1661
                    // We must have connected, but then failed TLS or Auth, so close connection nicely
1662
                    $this->smtp->quit();
1663 56
                }
1664
            }
1665 56
        }
1666 35
        // If we get here, all connection attempts have failed, so close connection hard
1667 3
        $this->smtp->close();
1668 3
        // As we've caught all exceptions, just report whatever the last one was
1669 3
        if ($this->exceptions and !is_null($lastexception)) {
1670 35
            throw $lastexception;
1671 56
        }
1672
        return false;
1673
    }
1674
1675
    /**
1676
     * Close the active SMTP session if one exists.
1677
     * @return void
1678
     */
1679
    public function smtpClose()
1680
    {
1681
        if (is_a($this->smtp, 'SMTP')) {
1682 8
            if ($this->smtp->connected()) {
1683
                $this->smtp->quit();
1684
                $this->smtp->close();
1685
            }
1686 8
        }
1687 8
    }
1688 8
1689 8
    /**
1690 8
     * Set the language for error messages.
1691 8
     * Returns false if it cannot load the language file.
1692 8
     * The default language is English.
1693 8
     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1694 8
     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1695 8
     * @return boolean
1696 8
     * @access public
1697 8
     */
1698 8
    public function setLanguage($langcode = 'en', $lang_path = '')
1699 8
    {
1700 8
        // Backwards compatibility for renamed language codes
1701 8
        $renamed_langcodes = array(
1702 8
            'br' => 'pt_br',
1703 8
            'cz' => 'cs',
1704
            'dk' => 'da',
1705 8
            'no' => 'nb',
1706 8
            'se' => 'sv',
1707
        );
1708 8
1709 8
        if (isset($renamed_langcodes[$langcode])) {
1710 8
            $langcode = $renamed_langcodes[$langcode];
1711 8
        }
1712
1713 8
        // Define full set of translatable strings in English
1714
        $PHPMAILER_LANG = array(
1715 1
            'authenticate' => 'SMTP Error: Could not authenticate.',
1716
            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1717
            'data_not_accepted' => 'SMTP Error: data not accepted.',
1718
            'empty_message' => 'Message body empty',
1719
            'encoding' => 'Unknown encoding: ',
1720 1
            'execute' => 'Could not execute: ',
1721
            'file_access' => 'Could not access file: ',
1722 1
            'file_open' => 'File Error: Could not open file: ',
1723 8
            'from_failed' => 'The following From address failed: ',
1724 8
            'instantiate' => 'Could not instantiate mail function.',
1725
            'invalid_address' => 'Invalid address: ',
1726
            'mailer_not_supported' => ' mailer is not supported.',
1727
            'provide_address' => 'You must provide at least one recipient email address.',
1728
            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1729
            'signing' => 'Signing Error: ',
1730
            'smtp_connect_failed' => 'SMTP connect() failed.',
1731
            'smtp_error' => 'SMTP server error: ',
1732
            'variable_set' => 'Cannot set or reset variable: ',
1733
            'extension_missing' => 'Extension missing: '
1734
        );
1735
        if (empty($lang_path)) {
1736
            // Calculate an absolute path so it can work if CWD is not here
1737
            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1738
        }
1739
        //Validate $langcode
1740
        if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
1741
            $langcode = 'en';
1742
        }
1743
        $foundlang = true;
1744
        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1745
        // There is no English translation file
1746 42
        if ($langcode != 'en') {
1747
            // Make sure language file path is readable
1748 42
            if (!is_readable($lang_file)) {
1749 42
                $foundlang = false;
1750 42
            } else {
1751 42
                // Overwrite language-specific strings.
1752 42
                // This way we'll never have missing translation keys.
1753
                $foundlang = include $lang_file;
1754
            }
1755
        }
1756
        $this->language = $PHPMAILER_LANG;
1757
        return (boolean)$foundlang; // Returns false if language not found
1758
    }
1759
1760
    /**
1761
     * Get the array of strings for the current language.
1762 42
     * @return array
1763
     */
1764 42
    public function getTranslations()
1765 3
    {
1766
        return $this->language;
1767 42
    }
1768 42
1769 42
    /**
1770
     * Create recipient headers.
1771
     * @access public
1772
     * @param string $type
1773
     * @param array $addr An array of recipient,
1774
     * where each recipient is a 2-element indexed array with element 0 containing an address
1775
     * and element 1 containing a name, like:
1776
     * array(array('[email protected]', 'Joe User'), array('[email protected]', 'Zoe User'))
1777
     * @return string
1778
     */
1779
    public function addrAppend($type, $addr)
1780
    {
1781
        $addresses = array();
1782
        foreach ($addr as $address) {
1783
            $addresses[] = $this->addrFormat($address);
1784 4
        }
1785
        return $type . ': ' . implode(', ', $addresses) . $this->LE;
1786 4
    }
1787
1788
    /**
1789 4
     * Format an address for use in a message header.
1790
     * @access public
1791
     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1792
     *      like array('[email protected]', 'Joe User')
1793 4
     * @return string
1794 4
     */
1795 4
    public function addrFormat($addr)
1796
    {
1797 4
        if (empty($addr[1])) { // No name provided
1798
            return $this->secureHeader($addr[0]);
1799 4
        } else {
1800 2
            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1801 2
                $addr[0]
1802
            ) . '>';
1803
        }
1804 4
    }
1805
1806 4
    /**
1807 4
     * Word-wrap message.
1808 4
     * For use with mailers that do not automatically perform wrapping
1809 4
     * and for quoted-printable encoded messages.
1810 4
     * Original written by philippe.
1811 4
     * @param string $message The message to wrap
1812 4
     * @param integer $length The line length to wrap to
1813
     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1814
     * @access public
1815
     * @return string
1816
     */
1817
    public function wrapText($message, $length, $qp_mode = false)
1818
    {
1819
        if ($qp_mode) {
1820
            $soft_break = sprintf(' =%s', $this->LE);
1821
        } else {
1822
            $soft_break = $this->LE;
1823
        }
1824
        // If utf-8 encoding is used, we will need to make sure we don't
1825
        // split multibyte characters when we wrap
1826
        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1827
        $lelen = strlen($this->LE);
1828
        $crlflen = strlen(self::CRLF);
1829
1830
        $message = $this->fixEOL($message);
1831
        //Remove a trailing line break
1832
        if (substr($message, -$lelen) == $this->LE) {
1833
            $message = substr($message, 0, -$lelen);
1834
        }
1835
1836
        //Split message into lines
1837
        $lines = explode($this->LE, $message);
1838
        //Message will be rebuilt in here
1839
        $message = '';
1840
        foreach ($lines as $line) {
1841
            $words = explode(' ', $line);
1842
            $buf = '';
1843
            $firstword = true;
1844
            foreach ($words as $word) {
1845
                if ($qp_mode and (strlen($word) > $length)) {
1846
                    $space_left = $length - strlen($buf) - $crlflen;
1847
                    if (!$firstword) {
1848
                        if ($space_left > 20) {
1849
                            $len = $space_left;
1850
                            if ($is_utf8) {
1851
                                $len = $this->utf8CharBoundary($word, $len);
1852
                            } elseif (substr($word, $len - 1, 1) == '=') {
1853
                                $len--;
1854
                            } elseif (substr($word, $len - 2, 1) == '=') {
1855 4
                                $len -= 2;
1856 4
                            }
1857 4
                            $part = substr($word, 0, $len);
1858 4
                            $word = substr($word, $len);
1859 4
                            $buf .= ' ' . $part;
1860
                            $message .= $buf . sprintf('=%s', self::CRLF);
1861 4
                        } else {
1862 4
                            $message .= $buf . $soft_break;
1863 4
                        }
1864 4
                        $buf = '';
1865
                    }
1866 4
                    while (strlen($word) > 0) {
1867 4
                        if ($length <= 0) {
1868 4
                            break;
1869 4
                        }
1870
                        $len = $length;
1871 4
                        if ($is_utf8) {
1872
                            $len = $this->utf8CharBoundary($word, $len);
1873
                        } elseif (substr($word, $len - 1, 1) == '=') {
1874
                            $len--;
1875
                        } elseif (substr($word, $len - 2, 1) == '=') {
1876
                            $len -= 2;
1877
                        }
1878
                        $part = substr($word, 0, $len);
1879
                        $word = substr($word, $len);
1880
1881
                        if (strlen($word) > 0) {
1882
                            $message .= $part . sprintf('=%s', self::CRLF);
1883
                        } else {
1884
                            $buf = $part;
1885
                        }
1886
                    }
1887
                } else {
1888
                    $buf_o = $buf;
1889
                    if (!$firstword) {
1890
                        $buf .= ' ';
1891
                    }
1892
                    $buf .= $word;
1893
1894
                    if (strlen($buf) > $length and $buf_o != '') {
1895
                        $message .= $buf_o . $soft_break;
1896
                        $buf = $word;
1897
                    }
1898
                }
1899
                $firstword = false;
1900
            }
1901
            $message .= $buf . self::CRLF;
1902
        }
1903
1904
        return $message;
1905
    }
1906
1907
    /**
1908
     * Find the last character boundary prior to $maxLength in a utf-8
1909
     * quoted-printable encoded string.
1910
     * Original written by Colin Brown.
1911
     * @access public
1912
     * @param string $encodedText utf-8 QP text
1913
     * @param integer $maxLength Find the last character boundary prior to this length
1914
     * @return integer
1915
     */
1916
    public function utf8CharBoundary($encodedText, $maxLength)
1917
    {
1918
        $foundSplitPos = false;
1919
        $lookBack = 3;
1920
        while (!$foundSplitPos) {
1921
            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1922
            $encodedCharPos = strpos($lastChunk, '=');
1923
            if (false !== $encodedCharPos) {
1924
                // Found start of encoded character byte within $lookBack block.
1925
                // Check the encoded byte value (the 2 chars after the '=')
1926
                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1927
                $dec = hexdec($hex);
1928 41
                if ($dec < 128) {
1929
                    // Single byte character.
1930 41
                    // If the encoded char was found at pos 0, it will fit
1931 38
                    // otherwise reduce maxLength to start of the encoded char
1932
                    if ($encodedCharPos > 0) {
1933
                        $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1934 3
                    }
1935 3
                    $foundSplitPos = true;
1936 3
                } elseif ($dec >= 192) {
1937 3
                    // First byte of a multi byte character
1938 3
                    // Reduce maxLength to split at start of character
1939 1
                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1940 1
                    $foundSplitPos = true;
1941 2
                } elseif ($dec < 192) {
1942 2
                    // Middle byte of a multi byte character, look further back
1943 2
                    $lookBack += 3;
1944 3
                }
1945 3
            } else {
1946
                // No encoded character found
1947
                $foundSplitPos = true;
1948
            }
1949
        }
1950
        return $maxLength;
1951
    }
1952 42
1953
    /**
1954 42
     * Apply word wrapping to the message body.
1955
     * Wraps the message body to the number of chars set in the WordWrap property.
1956 42
     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
1957 42
     * This is called automatically by createBody(), so you don't need to call it yourself.
1958 42
     * @access public
1959 42
     * @return void
1960
     */
1961
    public function setWordWrap()
1962 42
    {
1963
        if ($this->WordWrap < 1) {
1964
            return;
1965
        }
1966
1967
        switch ($this->message_type) {
1968
            case 'alt':
1969 42
            case 'alt_inline':
1970 41
            case 'alt_attach':
1971 39
            case 'alt_inline_attach':
1972 39
                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1973 42
                break;
1974 1
            default:
1975 1
                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1976
                break;
1977
        }
1978 42
    }
1979
1980
    /**
1981 42
     * Assemble message headers.
1982 39
     * @access public
1983 39
     * @return string The assembled headers
1984
     */
1985
    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...
1986
    {
1987 42
        $result = '';
1988 40
1989 42
        if ($this->MessageDate == '') {
1990 42
            $this->MessageDate = self::rfcDate();
1991
        }
1992
        $result .= $this->headerLine('Date', $this->MessageDate);
1993
1994 42
        // To be created automatically by mail()
1995 42
        if ($this->SingleTo) {
1996 42
            if ($this->Mailer != 'mail') {
1997
                foreach ($this->to as $toaddr) {
1998
                    $this->SingleToArray[] = $this->addrFormat($toaddr);
1999 42
                }
2000 40
            }
2001 40
        } else {
2002
            if (count($this->to) > 0) {
2003 42
                if ($this->Mailer != 'mail') {
2004 1
                    $result .= $this->addrAppend('To', $this->to);
2005 1
                }
2006 42
            } elseif (count($this->cc) == 0) {
2007
                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
2008 42
            }
2009 42
        }
2010 42
2011 42
        $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
2012 42
2013 42
        // sendmail and mail() extract Cc from the header before sending
2014 42
        if (count($this->cc) > 0) {
2015 42
            $result .= $this->addrAppend('Cc', $this->cc);
2016 42
        }
2017 42
2018
        // sendmail and mail() extract Bcc from the header before sending
2019
        if ((
2020
                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
2021
            )
2022
            and count($this->bcc) > 0
2023
        ) {
2024 42
            $result .= $this->addrAppend('Bcc', $this->bcc);
2025 1
        }
2026 1
2027
        if (count($this->ReplyTo) > 0) {
2028
            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
2029 42
        }
2030
2031
        // mail() sets the subject itself
2032
        if ($this->Mailer != 'mail') {
2033
            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2034 42
        }
2035 42
2036 40
        // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
2037 40
        // https://tools.ietf.org/html/rfc5322#section-3.6.4
2038 40
        if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
2039
            $this->lastMessageID = $this->MessageID;
2040 42
        } else {
2041
            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
2042
        }
2043
        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
2044
        if (!is_null($this->Priority)) {
2045
            $result .= $this->headerLine('X-Priority', $this->Priority);
2046
        }
2047
        if ($this->XMailer == '') {
2048 42
            $result .= $this->headerLine(
2049
                'X-Mailer',
2050 42
                'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
2051 42
            );
2052 42
        } else {
2053 42
            $myXmailer = trim($this->XMailer);
2054 3
            if ($myXmailer) {
2055 3
                $result .= $this->headerLine('X-Mailer', $myXmailer);
2056 3
            }
2057 39
        }
2058 39
2059 39
        if ($this->ConfirmReadingTo != '') {
2060 39
            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
2061 6
        }
2062 6
2063 6
        // Add custom headers
2064 33
        foreach ($this->CustomHeader as $header) {
2065 33
            $result .= $this->headerLine(
2066 6
                trim($header[0]),
2067 6
                $this->encodeHeader(trim($header[1]))
2068 6
            );
2069 27
        }
2070
        if (!$this->sign_key_file) {
2071 27
            $result .= $this->headerLine('MIME-Version', '1.0');
2072 27
            $result .= $this->getMailMIME();
2073 27
        }
2074 42
2075
        return $result;
2076 42
    }
2077
2078 24
    /**
2079 15
     * Get the message MIME type headers.
2080 15
     * @access public
2081 15
     * @return string
2082
     */
2083 15
    public function getMailMIME()
2084 9
    {
2085
        $result = '';
2086 24
        $ismultipart = true;
2087
        switch ($this->message_type) {
2088 42
            case 'inline':
2089 40
                $result .= $this->headerLine('Content-Type', 'multipart/related;');
2090 40
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2091
                break;
2092 42
            case 'attach':
2093
            case 'inline_attach':
2094
            case 'alt_attach':
2095
            case 'alt_inline_attach':
2096
                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2097
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2098
                break;
2099
            case 'alt':
2100
            case 'alt_inline':
2101
                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2102
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2103 9
                break;
2104
            default:
2105 9
                // Catches case 'plain': and case '':
2106
                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2107
                $ismultipart = false;
2108
                break;
2109
        }
2110
        // RFC1341 part 5 says 7bit is assumed if not specified
2111
        if ($this->Encoding != '7bit') {
2112
            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2113
            if ($ismultipart) {
2114
                if ($this->Encoding == '8bit') {
2115 41
                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2116
                }
2117 41
                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2118
            } else {
2119 41
                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2120 41
            }
2121 41
        }
2122 41
2123
        if ($this->Mailer != 'mail') {
2124 41
            $result .= $this->LE;
2125 2
        }
2126 2
2127
        return $result;
2128 41
    }
2129
2130 41
    /**
2131 41
     * Returns the whole MIME message.
2132
     * Includes complete headers and body.
2133 41
     * Only valid post preSend().
2134 33
     * @see PHPMailer::preSend()
2135
     * @access public
2136 33
     * @return string
2137 33
     */
2138
    public function getSentMIMEMessage()
2139
    {
2140 41
        return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
2141 2
    }
2142 2
2143
    /**
2144 41
     * Create unique ID
2145 41
     * @return string
2146
     */
2147 41
    protected function generateId() {
2148 37
        return md5(uniqid(time()));
2149
    }
2150 37
2151 37
    /**
2152
     * Assemble the message body.
2153
     * Returns an empty string on failure.
2154 41
     * @access public
2155 1
     * @throws phpmailerException
2156 1
     * @return string The assembled message body
2157
     */
2158 41
    public function createBody()
2159 41
    {
2160 41
        $body = '';
2161 3
        //Create unique IDs and preset boundaries
2162 3
        $this->uniqueid = $this->generateId();
2163 3
        $this->boundary[1] = 'b1_' . $this->uniqueid;
2164 3
        $this->boundary[2] = 'b2_' . $this->uniqueid;
2165 3
        $this->boundary[3] = 'b3_' . $this->uniqueid;
2166 3
2167 38
        if ($this->sign_key_file) {
2168 4
            $body .= $this->getMailMIME() . $this->LE;
2169 4
        }
2170 4
2171 4
        $this->setWordWrap();
2172 4
2173 4
        $bodyEncoding = $this->Encoding;
2174 34
        $bodyCharSet = $this->CharSet;
2175 1
        //Can we do a 7-bit downgrade?
2176 1
        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2177 1
            $bodyEncoding = '7bit';
2178 1
            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2179 1
            $bodyCharSet = 'us-ascii';
2180 1
        }
2181 1
        //If lines are too long, and we're not already using an encoding that will shorten them,
2182 1
        //change to quoted-printable transfer encoding for the body part only
2183 1
        if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
2184 1
            $bodyEncoding = 'quoted-printable';
2185 1
        }
2186 1
2187 33
        $altBodyEncoding = $this->Encoding;
2188 4
        $altBodyCharSet = $this->CharSet;
2189 4
        //Can we do a 7-bit downgrade?
2190 4
        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2191 4
            $altBodyEncoding = '7bit';
2192 4
            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2193 4
            $altBodyCharSet = 'us-ascii';
2194 4
        }
2195 4
        //If lines are too long, and we're not already using an encoding that will shorten them,
2196 1
        //change to quoted-printable transfer encoding for the alt body part only
2197 1
        if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
2198 1
            $altBodyEncoding = 'quoted-printable';
2199 1
        }
2200 4
        //Use this as a preamble in all multipart message types
2201 4
        $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
2202 29
        switch ($this->message_type) {
2203 2
            case 'inline':
2204 2
                $body .= $mimepre;
2205 2
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2206 2
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2207 2
                $body .= $this->LE . $this->LE;
2208 2
                $body .= $this->attachAll('inline', $this->boundary[1]);
2209 2
                break;
2210 2
            case 'attach':
2211 2
                $body .= $mimepre;
2212 2
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2213 2
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2214 2
                $body .= $this->LE . $this->LE;
2215 2
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2216 2
                break;
2217 2
            case 'inline_attach':
2218 27
                $body .= $mimepre;
2219 1
                $body .= $this->textLine('--' . $this->boundary[1]);
2220 1
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2221 1
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2222 1
                $body .= $this->LE;
2223 1
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2224 1
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2225 1
                $body .= $this->LE . $this->LE;
2226 1
                $body .= $this->attachAll('inline', $this->boundary[2]);
2227 1
                $body .= $this->LE;
2228 1
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2229 1
                break;
2230 1
            case 'alt':
2231 1
                $body .= $mimepre;
2232 1
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2233 1
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2234 26
                $body .= $this->LE . $this->LE;
2235
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2236
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2237
                $body .= $this->LE . $this->LE;
2238
                if (!empty($this->Ical)) {
2239
                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2240
                    $body .= $this->encodeString($this->Ical, $this->Encoding);
2241
                    $body .= $this->LE . $this->LE;
2242
                }
2243
                $body .= $this->endBoundary($this->boundary[1]);
2244
                break;
2245
            case 'alt_inline':
2246
                $body .= $mimepre;
2247
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2248
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2249
                $body .= $this->LE . $this->LE;
2250
                $body .= $this->textLine('--' . $this->boundary[1]);
2251
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2252
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2253
                $body .= $this->LE;
2254
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2255
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2256 26
                $body .= $this->LE . $this->LE;
2257
                $body .= $this->attachAll('inline', $this->boundary[2]);
2258
                $body .= $this->LE;
2259 26
                $body .= $this->endBoundary($this->boundary[1]);
2260 26
                break;
2261 26
            case 'alt_attach':
2262 41
                $body .= $mimepre;
2263
                $body .= $this->textLine('--' . $this->boundary[1]);
2264 41
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2265
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2266 41
                $body .= $this->LE;
2267
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2268 2
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2269
                $body .= $this->LE . $this->LE;
2270
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2271
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2272 2
                $body .= $this->LE . $this->LE;
2273 2
                $body .= $this->endBoundary($this->boundary[2]);
2274
                $body .= $this->LE;
2275
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2276 2
                break;
2277
            case 'alt_inline_attach':
2278 2
                $body .= $mimepre;
2279 1
                $body .= $this->textLine('--' . $this->boundary[1]);
2280 1
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2281 1
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2282 1
                $body .= $this->LE;
2283 1
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2284
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2285 1
                $body .= $this->LE . $this->LE;
2286 1
                $body .= $this->textLine('--' . $this->boundary[2]);
2287 1
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2288 1
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2289 1
                $body .= $this->LE;
2290 1
                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2291 1
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2292 1
                $body .= $this->LE . $this->LE;
2293 1
                $body .= $this->attachAll('inline', $this->boundary[3]);
2294 1
                $body .= $this->LE;
2295 1
                $body .= $this->endBoundary($this->boundary[2]);
2296
                $body .= $this->LE;
2297 2
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2298 2
                break;
2299 2
            default:
2300 2
                // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
2301
                //Reset the `Encoding` property in case we changed it for line length reasons
2302 2
                $this->Encoding = $bodyEncoding;
2303 2
                $body .= $this->encodeString($this->Body, $this->Encoding);
2304 2
                break;
2305 2
        }
2306
2307
        if ($this->isError()) {
2308
            $body = '';
2309
        } elseif ($this->sign_key_file) {
2310 2
            try {
2311
                if (!defined('PKCS7_TEXT')) {
2312
                    throw new phpmailerException($this->lang('extension_missing') . 'openssl');
2313
                }
2314
                // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2315
                $file = tempnam(sys_get_temp_dir(), 'mail');
2316 2
                if (false === file_put_contents($file, $body)) {
2317 41
                    throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2318
                }
2319
                $signed = tempnam(sys_get_temp_dir(), 'signed');
2320
                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2321
                if (empty($this->sign_extracerts_file)) {
2322
                    $sign = @openssl_pkcs7_sign(
2323
                        $file,
2324
                        $signed,
2325
                        'file://' . realpath($this->sign_cert_file),
2326
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2327
                        null
2328
                    );
2329 15
                } else {
2330
                    $sign = @openssl_pkcs7_sign(
2331 15
                        $file,
2332 15
                        $signed,
2333 1
                        'file://' . realpath($this->sign_cert_file),
2334 1
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2335 15
                        null,
2336 8
                        PKCS7_DETACHED,
2337 8
                        $this->sign_extracerts_file
2338 15
                    );
2339 1
                }
2340 1
                if ($sign) {
2341 15
                    @unlink($file);
1 ignored issue
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...
2342 15
                    $body = file_get_contents($signed);
2343 15
                    @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...
2344
                    //The message returned by openssl contains both headers and body, so need to split them up
2345 15
                    $parts = explode("\n\n", $body, 2);
2346 5
                    $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2347 5
                    $body = $parts[1];
2348 15
                } else {
2349
                    @unlink($file);
1 ignored issue
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...
2350 15
                    @unlink($signed);
1 ignored issue
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...
2351
                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
2352
                }
2353
            } catch (phpmailerException $exc) {
2354
                $body = '';
2355
                if ($this->exceptions) {
2356
                    throw $exc;
2357
                }
2358
            }
2359 7
        }
2360
        return $body;
2361 7
    }
2362
2363
    /**
2364
     * Return the start of a message boundary.
2365
     * @access protected
2366
     * @param string $boundary
2367
     * @param string $charSet
2368
     * @param string $contentType
2369
     * @param string $encoding
2370 41
     * @return string
2371
     */
2372 41
    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2373 41
    {
2374 7
        $result = '';
2375 7
        if ($charSet == '') {
2376 41
            $charSet = $this->CharSet;
2377 6
        }
2378 6
        if ($contentType == '') {
2379 41
            $contentType = $this->ContentType;
2380 6
        }
2381 6
        if ($encoding == '') {
2382 41
            $encoding = $this->Encoding;
2383 41
        }
2384
        $result .= $this->textLine('--' . $boundary);
2385 26
        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2386 26
        $result .= $this->LE;
2387 41
        // RFC1341 part 5 says 7bit is assumed if not specified
2388
        if ($encoding != '7bit') {
2389
            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2390
        }
2391
        $result .= $this->LE;
2392
2393
        return $result;
2394
    }
2395
2396 42
    /**
2397
     * Return the end of a message boundary.
2398 42
     * @access protected
2399
     * @param string $boundary
2400
     * @return string
2401
     */
2402
    protected function endBoundary($boundary)
2403
    {
2404
        return $this->LE . '--' . $boundary . '--' . $this->LE;
2405
    }
2406
2407 42
    /**
2408
     * Set the message type.
2409 42
     * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2410
     * @access protected
2411
     * @return void
2412
     */
2413
    protected function setMessageType()
2414
    {
2415
        $type = array();
2416
        if ($this->alternativeExists()) {
2417
            $type[] = 'alt';
2418
        }
2419
        if ($this->inlineImageExists()) {
2420
            $type[] = 'inline';
2421
        }
2422
        if ($this->attachmentExists()) {
2423 5
            $type[] = 'attach';
2424
        }
2425
        $this->message_type = implode('_', $type);
2426 5
        if ($this->message_type == '') {
2427 1
            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2428
            $this->message_type = 'plain';
2429
        }
2430
    }
2431 5
2432 5
    /**
2433 5
     * Format a header line.
2434
     * @access public
2435 5
     * @param string $name
2436 5
     * @param string $value
2437 1
     * @return string
2438 1
     */
2439
    public function headerLine($name, $value)
2440 5
    {
2441 5
        return $name . ': ' . $value . $this->LE;
2442 5
    }
2443 5
2444 5
    /**
2445 5
     * Return a formatted mail line.
2446 5
     * @access public
2447 5
     * @param string $value
2448
     * @return string
2449 5
     */
2450
    public function textLine($value)
2451 5
    {
2452 1
        return $value . $this->LE;
2453 1
    }
2454 1
2455
    /**
2456
     * Add an attachment from a path on the filesystem.
2457 1
     * Returns false if the file could not be found or read.
2458
     * @param string $path Path to the attachment.
2459 5
     * @param string $name Overrides the attachment name.
2460
     * @param string $encoding File encoding (see $Encoding).
2461
     * @param string $type File extension (MIME) type.
2462
     * @param string $disposition Disposition to use
2463
     * @throws phpmailerException
2464
     * @return boolean
2465
     */
2466 40
    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2467
    {
2468 40
        try {
2469
            if (!@is_file($path)) {
2470
                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2471
            }
2472
2473
            // If a MIME type is not specified, try to work it out from the file name
2474
            if ($type == '') {
2475
                $type = self::filenameToType($path);
2476
            }
2477
2478
            $filename = basename($path);
2479 11
            if ($name == '') {
2480
                $name = $filename;
2481
            }
2482 11
2483 11
            $this->attachment[] = array(
2484 11
                0 => $path,
2485
                1 => $filename,
2486
                2 => $name,
2487 11
                3 => $encoding,
2488
                4 => $type,
2489 11
                5 => false, // isStringAttachment
2490
                6 => $disposition,
2491 11
                7 => 0
2492 11
            );
2493 11
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
2494 11
        } catch (phpmailerException $exc) {
2495 2
            $this->setError($exc->getMessage());
2496 2
            $this->edebug($exc->getMessage());
2497 9
            if ($this->exceptions) {
2498
                throw $exc;
2499
            }
2500 11
            return false;
2501 11
        }
2502 1
        return true;
2503
    }
2504 11
2505 11
    /**
2506 11
     * Return the array of attachments.
2507 11
     * @return array
2508 11
     */
2509 11
    public function getAttachments()
2510 11
    {
2511
        return $this->attachment;
2512
    }
2513 11
2514
    /**
2515 11
     * Attach all file, string, and binary attachments to the message.
2516
     * Returns an empty string on failure.
2517 11
     * @access protected
2518 10
     * @param string $disposition_type
2519 10
     * @param string $boundary
2520 10
     * @return string
2521 10
     */
2522 10
    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...
2523 10
    {
2524 10
        // Return text of body
2525 1
        $mime = array();
2526 1
        $cidUniq = array();
2527 1
        $incl = array();
2528 1
2529 1
        // Add all attachments
2530
        foreach ($this->attachment as $attachment) {
2531
            // Check if it is a valid disposition_filter
2532 11
            if ($attachment[6] == $disposition_type) {
2533 11
                // Check for string attachment
2534 11
                $string = '';
2535
                $path = '';
2536 11
                $bString = $attachment[5];
2537 6
                if ($bString) {
2538 6
                    $string = $attachment[0];
2539
                } else {
2540
                    $path = $attachment[0];
2541
                }
2542
2543
                $inclhash = md5(serialize($attachment));
2544 11
                if (in_array($inclhash, $incl)) {
2545 11
                    continue;
2546 11
                }
2547
                $incl[] = $inclhash;
2548
                $name = $attachment[2];
2549
                $encoding = $attachment[3];
2550
                $type = $attachment[4];
2551
                $disposition = $attachment[6];
2552
                $cid = $attachment[7];
2553
                if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
2554 11
                    continue;
2555 10
                }
2556 10
                $cidUniq[$cid] = true;
2557 10
2558 10
                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2559 10
                //Only include a filename property if we have one
2560 10
                if (!empty($name)) {
2561 10
                    $mime[] = sprintf(
2562 1
                        'Content-Type: %s; name="%s"%s',
2563 1
                        $type,
2564 1
                        $this->encodeHeader($this->secureHeader($name)),
2565 1
                        $this->LE
2566 1
                    );
2567
                } else {
2568
                    $mime[] = sprintf(
2569 11
                        'Content-Type: %s%s',
2570
                        $type,
2571
                        $this->LE
2572
                    );
2573
                }
2574 11
                // RFC1341 part 5 says 7bit is assumed if not specified
2575 2
                if ($encoding != '7bit') {
2576 2
                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2577
                }
2578
2579 2
                if ($disposition == 'inline') {
2580 2
                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2581 9
                }
2582 9
2583
                // If a filename contains any of these chars, it should be quoted,
2584
                // but not otherwise: RFC2183 & RFC2045 5.1
2585 9
                // Fixes a warning in IETF's msglint MIME checker
2586
                // Allow for bypassing the Content-Disposition header totally
2587 11
                if (!(empty($disposition))) {
2588 11
                    $encoded_name = $this->encodeHeader($this->secureHeader($name));
2589
                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2590 11
                        $mime[] = sprintf(
2591
                            'Content-Disposition: %s; filename="%s"%s',
2592 11
                            $disposition,
2593
                            $encoded_name,
2594
                            $this->LE . $this->LE
2595
                        );
2596
                    } else {
2597
                        if (!empty($encoded_name)) {
2598
                            $mime[] = sprintf(
2599
                                'Content-Disposition: %s; filename=%s%s',
2600
                                $disposition,
2601
                                $encoded_name,
2602
                                $this->LE . $this->LE
2603
                            );
2604 9
                        } else {
2605
                            $mime[] = sprintf(
2606
                                'Content-Disposition: %s%s',
2607 9
                                $disposition,
2608
                                $this->LE . $this->LE
2609
                            );
2610 9
                        }
2611 9
                    }
2612
                } else {
2613
                    $mime[] = $this->LE;
2614
                }
2615
2616
                // Encode as string attachment
2617
                if ($bString) {
2618
                    $mime[] = $this->encodeString($string, $encoding);
2619
                    if ($this->isError()) {
2620
                        return '';
2621 9
                    }
2622 9
                    $mime[] = $this->LE . $this->LE;
2623 9
                } else {
2624
                    $mime[] = $this->encodeFile($path, $encoding);
2625
                    if ($this->isError()) {
2626
                        return '';
2627
                    }
2628
                    $mime[] = $this->LE . $this->LE;
2629
                }
2630 9
            }
2631
        }
2632
2633
        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2634
2635
        return implode('', $mime);
2636
    }
2637
2638
    /**
2639
     * Encode a file attachment in requested format.
2640
     * Returns an empty string on failure.
2641
     * @param string $path The full path to the file
2642
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2643
     * @throws phpmailerException
2644
     * @access protected
2645 41
     * @return string
2646
     */
2647 41
    protected function encodeFile($path, $encoding = 'base64')
2648 41
    {
2649 41
        try {
2650 12
            if (!is_readable($path)) {
2651 12
                throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2652 40
            }
2653 40
            $magic_quotes = get_magic_quotes_runtime();
2654 37
            if ($magic_quotes) {
2655
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2656 37
                    set_magic_quotes_runtime(false);
2657 6
                } else {
2658 6
                    //Doesn't exist in PHP 5.4, but we don't need to check because
2659 37
                    //get_magic_quotes_runtime always returns false in 5.4+
2660 3
                    //so it will never get here
2661
                    ini_set('magic_quotes_runtime', false);
2662
                }
2663 3
            }
2664 3
            $file_buffer = file_get_contents($path);
2665 3
            $file_buffer = $this->encodeString($file_buffer, $encoding);
2666
            if ($magic_quotes) {
2667
                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2668
                    set_magic_quotes_runtime($magic_quotes);
2669 41
                } else {
2670 41
                    ini_set('magic_quotes_runtime', $magic_quotes);
2671
                }
2672
            }
2673
            return $file_buffer;
2674
        } catch (Exception $exc) {
2675
            $this->setError($exc->getMessage());
2676
            return '';
2677
        }
2678
    }
2679
2680
    /**
2681 42
     * Encode a string in requested format.
2682
     * Returns an empty string on failure.
2683 42
     * @param string $str The text to encode
2684 42
     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2685 42
     * @access public
2686 42
     * @return string
2687
     */
2688 42
    public function encodeString($str, $encoding = 'base64')
2689 42
    {
2690 42
        $encoded = '';
2691
        switch (strtolower($encoding)) {
2692 2
            case 'base64':
2693
                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2694
                break;
2695
            case '7bit':
2696
            case '8bit':
2697
                $encoded = $this->fixEOL($str);
2698 42
                // Make sure it ends with a line break
2699
                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2700
                    $encoded .= $this->LE;
2701 42
                }
2702 42
                break;
2703 42
            case 'binary':
2704 42
                $encoded = $str;
2705 42
                break;
2706
            case 'quoted-printable':
2707
                $encoded = $this->encodeQP($str);
2708 42
                break;
2709 42
            default:
2710
                $this->setError($this->lang('encoding') . $encoding);
2711
                break;
2712
        }
2713
        return $encoded;
2714
    }
2715
2716
    /**
2717
     * Encode a header string optimally.
2718
     * Picks shortest of Q, B, quoted-printable or none.
2719
     * @access public
2720
     * @param string $str
2721
     * @param string $position
2722
     * @return string
2723
     */
2724
    public function encodeHeader($str, $position = 'text')
2725
    {
2726
        $matchcount = 0;
2727
        switch (strtolower($position)) {
2728
            case 'phrase':
2729
                if (!preg_match('/[\200-\377]/', $str)) {
2730
                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
2731
                    $encoded = addcslashes($str, "\0..\37\177\\\"");
2732
                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2733
                        return ($encoded);
2734
                    } else {
2735
                        return ("\"$encoded\"");
2736
                    }
2737
                }
2738
                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2739
                break;
2740
            /** @noinspection PhpMissingBreakStatementInspection */
2741
            case 'comment':
2742
                $matchcount = preg_match_all('/[()"]/', $str, $matches);
2743
                // Intentional fall-through
2744
            case 'text':
2745
            default:
2746
                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2747
                break;
2748
        }
2749
2750
        //There are no chars that need encoding
2751
        if ($matchcount == 0) {
2752
            return ($str);
2753
        }
2754
2755
        $maxlen = 75 - 7 - strlen($this->CharSet);
2756
        // Try to select the encoding which should produce the shortest output
2757
        if ($matchcount > strlen($str) / 3) {
2758
            // More than a third of the content will need encoding, so B encoding will be most efficient
2759 56
            $encoding = 'B';
2760
            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2761 56
                // Use a custom function which correctly encodes and wraps long
2762
                // multibyte strings without breaking lines within a character
2763
                $encoded = $this->base64EncodeWrapMB($str, "\n");
2764
            } else {
2765
                $encoded = base64_encode($str);
2766
                $maxlen -= $maxlen % 4;
2767
                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2768
            }
2769
        } else {
2770
            $encoding = 'Q';
2771
            $encoded = $this->encodeQ($str, $position);
2772
            $encoded = $this->wrapText($encoded, $maxlen, true);
2773
            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2774
        }
2775
2776
        $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2777
        $encoded = trim(str_replace("\n", $this->LE, $encoded));
2778
2779
        return $encoded;
2780
    }
2781
2782
    /**
2783
     * Check if a string contains multi-byte characters.
2784
     * @access public
2785
     * @param string $str multi-byte text to wrap encode
2786
     * @return boolean
2787
     */
2788
    public function hasMultiBytes($str)
2789
    {
2790
        if (function_exists('mb_strlen')) {
2791
            return (strlen($str) > mb_strlen($str, $this->CharSet));
2792
        } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2793
            return false;
2794
        }
2795
    }
2796
2797
    /**
2798
     * Does a string contain any 8-bit chars (in any charset)?
2799
     * @param string $text
2800
     * @return boolean
2801
     */
2802
    public function has8bitChars($text)
2803
    {
2804
        return (boolean)preg_match('/[\x80-\xFF]/', $text);
2805
    }
2806
2807
    /**
2808
     * Encode and wrap long multibyte strings for mail headers
2809
     * without breaking lines within a character.
2810
     * Adapted from a function by paravoid
2811
     * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2812
     * @access public
2813
     * @param string $str multi-byte text to wrap encode
2814
     * @param string $linebreak string to use as linefeed/end-of-line
2815
     * @return string
2816 3
     */
2817
    public function base64EncodeWrapMB($str, $linebreak = null)
2818
    {
2819 3
        $start = '=?' . $this->CharSet . '?B?';
2820 3
        $end = '?=';
2821
        $encoded = '';
2822
        if ($linebreak === null) {
2823
            $linebreak = $this->LE;
2824
        }
2825
2826
        $mb_length = mb_strlen($str, $this->CharSet);
2827
        // Each line must have length <= 75, including $start and $end
2828
        $length = 75 - strlen($start) - strlen($end);
2829
        // Average multi-byte ratio
2830
        $ratio = $mb_length / strlen($str);
2831
        // Base64 has a 4:3 ratio
2832
        $avgLength = floor($length * $ratio * .75);
2833
2834
        for ($i = 0; $i < $mb_length; $i += $offset) {
2835
            $lookBack = 0;
2836
            do {
2837
                $offset = $avgLength - $lookBack;
2838
                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2839
                $chunk = base64_encode($chunk);
2840
                $lookBack++;
2841
            } while (strlen($chunk) > $length);
2842
            $encoded .= $chunk . $linebreak;
2843
        }
2844
2845
        // Chomp the last linefeed
2846
        $encoded = substr($encoded, 0, -strlen($linebreak));
2847
        return $encoded;
2848
    }
2849
2850
    /**
2851
     * Encode a string in quoted-printable format.
2852
     * According to RFC2045 section 6.7.
2853
     * @access public
2854
     * @param string $string The text to encode
2855
     * @param integer $line_max Number of chars allowed on a line before wrapping
2856
     * @return string
2857 1
     * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2858
     */
2859
    public function encodeQP($string, $line_max = 76)
2860 1
    {
2861 1
        // Use native function if it's available (>= PHP5.3)
2862 1
        if (function_exists('quoted_printable_encode')) {
2863 1
            return quoted_printable_encode($string);
2864
        }
2865 1
        // Fall back to a pure PHP implementation
2866 1
        $string = str_replace(
2867
            array('%20', '%0D%0A.', '%0D%0A', '%'),
2868 1
            array(' ', "\r\n=2E", "\r\n", '='),
2869
            rawurlencode($string)
2870 1
        );
2871
        return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2872
    }
2873 1
2874 1
    /**
2875
     * Backward compatibility wrapper for an old QP encoding function that was removed.
2876
     * @see PHPMailer::encodeQP()
2877 1
     * @access public
2878 1
     * @param string $string
2879 1
     * @param integer $line_max
2880 1
     * @param boolean $space_conv
2881 1
     * @return string
2882
     * @deprecated Use encodeQP instead.
2883
     */
2884 1
    public function encodeQPphp(
2885 1
        $string,
2886 1
        $line_max = 76,
2887 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...
2888 1
    ) {
2889 1
        return $this->encodeQP($string, $line_max);
2890 1
    }
2891 1
2892 1
    /**
2893
     * Encode a string using Q encoding.
2894 1
     * @link http://tools.ietf.org/html/rfc2047
2895
     * @param string $str the text to encode
2896
     * @param string $position Where the text is going to be used, see the RFC for what that means
2897
     * @access public
2898
     * @return string
2899
     */
2900
    public function encodeQ($str, $position = 'text')
2901
    {
2902
        // There should not be any EOL in the string
2903
        $pattern = '';
2904
        $encoded = str_replace(array("\r", "\n"), '', $str);
2905
        switch (strtolower($position)) {
2906
            case 'phrase':
2907
                // RFC 2047 section 5.3
2908 1
                $pattern = '^A-Za-z0-9!*+\/ -';
2909
                break;
2910
            /** @noinspection PhpMissingBreakStatementInspection */
2911
            case 'comment':
2912
                // RFC 2047 section 5.2
2913
                $pattern = '\(\)"';
2914
                // intentional fall-through
2915
                // for this reason we build the $pattern without including delimiters and []
2916 1
            case 'text':
2917 1
            default:
2918 1
                // RFC 2047 section 5.1
2919
                // Replace every high ascii, control, =, ? and _ characters
2920 1
                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2921 1
                break;
2922 1
        }
2923 1
        $matches = array();
2924 1
        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2925 1
            // If the string contains an '=', make sure it's the first thing we replace
2926 1
            // so as to avoid double-encoding
2927 1
            $eqkey = array_search('=', $matches[0]);
2928
            if (false !== $eqkey) {
2929 1
                unset($matches[0][$eqkey]);
2930 1
                array_unshift($matches[0], '=');
2931
            }
2932
            foreach (array_unique($matches[0]) as $char) {
2933
                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2934
            }
2935
        }
2936
        // Replace every spaces to _ (more readable than =20)
2937
        return str_replace(' ', '_', $encoded);
2938
    }
2939
2940
    /**
2941
     * Add a string or binary attachment (non-filesystem).
2942
     * This method can be used to attach ascii or binary data,
2943
     * such as a BLOB record from a database.
2944
     * @param string $string String attachment data.
2945
     * @param string $filename Name of the attachment.
2946
     * @param string $encoding File encoding (see $Encoding).
2947
     * @param string $type File extension (MIME) type.
2948 5
     * @param string $disposition Disposition to use
2949
     * @return void
2950 5
     */
2951 1
    public function addStringAttachment(
2952 1
        $string,
2953
        $filename,
2954
        $encoding = 'base64',
2955
        $type = '',
2956 5
        $disposition = 'attachment'
2957 1
    ) {
2958 1
        // If a MIME type is not specified, try to work it out from the file name
2959
        if ($type == '') {
2960 5
            $type = self::filenameToType($filename);
2961 5
        }
2962 1
        // Append to $attachment array
2963 1
        $this->attachment[] = array(
2964
            0 => $string,
2965
            1 => $filename,
2966 5
            2 => basename($filename),
2967 5
            3 => $encoding,
2968 5
            4 => $type,
2969 5
            5 => true, // isStringAttachment
2970 5
            6 => $disposition,
2971 5
            7 => 0
2972 5
        );
2973 5
    }
2974
2975 5
    /**
2976 5
     * Add an embedded (inline) attachment from a file.
2977
     * This can include images, sounds, and just about any other document type.
2978
     * These differ from 'regular' attachments in that they are intended to be
2979
     * displayed inline with the message, not just attached for download.
2980
     * This is used in HTML messages that embed the images
2981
     * the HTML refers to using the $cid value.
2982
     * @param string $path Path to the attachment.
2983
     * @param string $cid Content ID of the attachment; Use this to reference
2984
     *        the content when using an embedded image in HTML.
2985
     * @param string $name Overrides the attachment name.
2986
     * @param string $encoding File encoding (see $Encoding).
2987
     * @param string $type File MIME type.
2988
     * @param string $disposition Disposition to use
2989
     * @return boolean True on successfully adding an attachment
2990
     */
2991
    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2992
    {
2993 1
        if (!@is_file($path)) {
2994
            $this->setError($this->lang('file_access') . $path);
2995
            return false;
2996
        }
2997
2998
        // If a MIME type is not specified, try to work it out from the file name
2999
        if ($type == '') {
3000
            $type = self::filenameToType($path);
3001
        }
3002 1
3003
        $filename = basename($path);
3004
        if ($name == '') {
3005
            $name = $filename;
3006
        }
3007 1
3008 1
        // Append to $attachment array
3009 1
        $this->attachment[] = array(
3010 1
            0 => $path,
3011 1
            1 => $filename,
3012 1
            2 => $name,
3013 1
            3 => $encoding,
3014 1
            4 => $type,
3015
            5 => false, // isStringAttachment
3016 1
            6 => $disposition,
3017 1
            7 => $cid
3018
        );
3019
        return true;
3020
    }
3021
3022
    /**
3023
     * Add an embedded stringified attachment.
3024
     * This can include images, sounds, and just about any other document type.
3025 41
     * Be sure to set the $type to an image type for images:
3026
     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
3027 41
     * @param string $string The attachment binary data.
3028 11
     * @param string $cid Content ID of the attachment; Use this to reference
3029 6
     *        the content when using an embedded image in HTML.
3030
     * @param string $name
3031 35
     * @param string $encoding File encoding (see $Encoding).
3032 35
     * @param string $type MIME type.
3033
     * @param string $disposition Disposition to use
3034
     * @return boolean True on successfully adding an attachment
3035
     */
3036
    public function addStringEmbeddedImage(
3037
        $string,
3038
        $cid,
3039 41
        $name = '',
3040
        $encoding = 'base64',
3041 41
        $type = '',
3042 11
        $disposition = 'inline'
3043 6
    ) {
3044
        // If a MIME type is not specified, try to work it out from the name
3045 36
        if ($type == '' and !empty($name)) {
3046 35
            $type = self::filenameToType($name);
3047
        }
3048
3049
        // Append to $attachment array
3050
        $this->attachment[] = array(
3051
            0 => $string,
3052
            1 => $name,
3053 41
            2 => $name,
3054
            3 => $encoding,
3055 41
            4 => $type,
3056
            5 => true, // isStringAttachment
3057
            6 => $disposition,
3058
            7 => $cid
3059
        );
3060
        return true;
3061
    }
3062
3063
    /**
3064 3
     * Check if an inline attachment is present.
3065
     * @access public
3066 3
     * @return boolean
3067 3
     */
3068 1
    public function inlineImageExists()
3069 1
    {
3070 1
        foreach ($this->attachment as $attachment) {
3071 3
            if ($attachment[6] == 'inline') {
3072 3
                return true;
3073
            }
3074
        }
3075
        return false;
3076
    }
3077
3078 1
    /**
3079
     * Check if an attachment (non-inline) is present.
3080 1
     * @return boolean
3081 1
     */
3082 1
    public function attachmentExists()
3083 1
    {
3084 1
        foreach ($this->attachment as $attachment) {
3085 1
            if ($attachment[6] == 'attachment') {
3086
                return true;
3087
            }
3088
        }
3089
        return false;
3090
    }
3091 1
3092
    /**
3093 1
     * Check if this message has an alternative body set.
3094 1
     * @return boolean
3095 1
     */
3096 1
    public function alternativeExists()
3097 1
    {
3098 1
        return !empty($this->AltBody);
3099
    }
3100
3101
    /**
3102
     * Clear queued addresses of given kind.
3103
     * @access protected
3104 2
     * @param string $kind 'to', 'cc', or 'bcc'
3105
     * @return void
3106 2
     */
3107 1
    public function clearQueuedAddresses($kind)
3108 2
    {
3109 2
        $RecipientsQueue = $this->RecipientsQueue;
3110 2
        foreach ($RecipientsQueue as $address => $params) {
3111 2
            if ($params[0] == $kind) {
3112
                unset($this->RecipientsQueue[$address]);
3113
            }
3114
        }
3115
    }
3116
3117 3
    /**
3118
     * Clear all To recipients.
3119 3
     * @return void
3120 3
     */
3121 3
    public function clearAddresses()
3122
    {
3123
        foreach ($this->to as $to) {
3124
            unset($this->all_recipients[strtolower($to[0])]);
3125
        }
3126
        $this->to = array();
3127 5
        $this->clearQueuedAddresses('to');
3128
    }
3129 5
3130 5
    /**
3131 5
     * Clear all CC recipients.
3132 5
     * @return void
3133 5
     */
3134 5
    public function clearCCs()
3135
    {
3136
        foreach ($this->cc as $cc) {
3137
            unset($this->all_recipients[strtolower($cc[0])]);
3138
        }
3139
        $this->cc = array();
3140 1
        $this->clearQueuedAddresses('cc');
3141
    }
3142 1
3143 1
    /**
3144
     * Clear all BCC recipients.
3145
     * @return void
3146
     */
3147
    public function clearBCCs()
3148
    {
3149 2
        foreach ($this->bcc as $bcc) {
3150
            unset($this->all_recipients[strtolower($bcc[0])]);
3151 2
        }
3152 2
        $this->bcc = array();
3153
        $this->clearQueuedAddresses('bcc');
3154
    }
3155
3156
    /**
3157
     * Clear all ReplyTo recipients.
3158
     * @return void
3159
     */
3160 8
    public function clearReplyTos()
3161
    {
3162 8
        $this->ReplyTo = array();
3163 8
        $this->ReplyToQueue = array();
3164 1
    }
3165 1
3166
    /**
3167
     * Clear all recipient types.
3168
     * @return void
3169
     */
3170
    public function clearAllRecipients()
3171
    {
3172
        $this->to = array();
3173
        $this->cc = array();
3174
        $this->bcc = array();
3175
        $this->all_recipients = array();
3176
        $this->RecipientsQueue = array();
3177 1
    }
3178 8
3179 8
    /**
3180
     * Clear all filesystem, string, and binary attachments.
3181
     * @return void
3182
     */
3183
    public function clearAttachments()
3184
    {
3185
        $this->attachment = array();
3186
    }
3187 42
3188
    /**
3189
     * Clear all custom headers.
3190
     * @return void
3191 42
     */
3192 42
    public function clearCustomHeaders()
3193
    {
3194
        $this->CustomHeader = array();
3195
    }
3196
3197
    /**
3198
     * Add an error message to the error container.
3199
     * @access protected
3200
     * @param string $msg
3201 42
     * @return void
3202
     */
3203 42
    protected function setError($msg)
3204 42
    {
3205
        $this->error_count++;
3206 42
        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
3207
            $lasterror = $this->smtp->getError();
3208 42
            if (!empty($lasterror['error'])) {
3209 42
                $msg .= $this->lang('smtp_error') . $lasterror['error'];
3210 42
                if (!empty($lasterror['detail'])) {
3211
                    $msg .= ' Detail: '. $lasterror['detail'];
3212
                }
3213 42
                if (!empty($lasterror['smtp_code'])) {
3214
                    $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3215
                }
3216
                if (!empty($lasterror['smtp_code_ex'])) {
3217
                    $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3218
                }
3219
            }
3220
        }
3221
        $this->ErrorInfo = $msg;
3222 8
    }
3223
3224 8
    /**
3225 7
     * Return an RFC 822 formatted date.
3226 7
     * @access public
3227
     * @return string
3228 8
     * @static
3229 8
     */
3230
    public static function rfcDate()
3231
    {
3232
        // Set the time zone to whatever the default is to avoid 500 errors
3233
        // Will default to UTC if it's not set properly in php.ini
3234
        date_default_timezone_set(@date_default_timezone_get());
3235 8
        return date('D, j M Y H:i:s O');
3236
    }
3237
3238
    /**
3239
     * Get the server hostname.
3240
     * Returns 'localhost.localdomain' if unknown.
3241
     * @access protected
3242
     * @return string
3243
     */
3244
    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...
3245
    {
3246
        $result = 'localhost.localdomain';
3247 41
        if (!empty($this->Hostname)) {
3248
            $result = $this->Hostname;
3249 41
        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
3250
            $result = $_SERVER['SERVER_NAME'];
3251
        } elseif (function_exists('gethostname') && gethostname() !== false) {
3252
            $result = gethostname();
3253
        } elseif (php_uname('n') !== false) {
3254
            $result = php_uname('n');
3255
        }
3256
        return $result;
3257
    }
3258
3259 38
    /**
3260
     * Get an error message in the current language.
3261
     * @access protected
3262 38
     * @param string $key
3263
     * @return string
3264 38
     */
3265
    protected function lang($key)
3266
    {
3267 38
        if (count($this->language) < 1) {
3268
            $this->setLanguage('en'); // set the default language
3269
        }
3270
3271
        if (array_key_exists($key, $this->language)) {
3272
            if ($key == 'smtp_connect_failed') {
3273
                //Include a link to troubleshooting docs on SMTP connection failure
3274
                //this is by far the biggest cause of support questions
3275
                //but it's usually not PHPMailer's fault.
3276
                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3277
            }
3278
            return $this->language[$key];
3279 2
        } else {
3280
            //Return the key as a fallback
3281 2
            return $key;
3282
        }
3283 2
    }
3284 2
3285 1
    /**
3286
     * Check if an error occurred.
3287 2
     * @access public
3288
     * @return boolean True if an error did occur.
3289
     */
3290
    public function isError()
3291
    {
3292
        return ($this->error_count > 0);
3293 1
    }
3294
3295 1
    /**
3296
     * Ensure consistent line endings in a string.
3297
     * Changes every end of line from CRLF, CR or LF to $this->LE.
3298
     * @access public
3299
     * @param string $str String to fixEOL
3300
     * @return string
3301
     */
3302
    public function fixEOL($str)
3303
    {
3304
        // Normalise to \n
3305
        $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
3306
        // Now convert LE as needed
3307
        if ($this->LE !== "\n") {
3308
            $nstr = str_replace("\n", $this->LE, $nstr);
3309
        }
3310 2
        return $nstr;
3311
    }
3312 2
3313 2
    /**
3314 2
     * Add a custom header.
3315
     * $name value can be overloaded to contain
3316 2
     * both header name and value (name:value)
3317
     * @access public
3318
     * @param string $name Custom header name
3319
     * @param string $value Header value
3320
     * @return void
3321
     */
3322
    public function addCustomHeader($name, $value = null)
3323
    {
3324
        if ($value === null) {
3325
            // Value passed in as name:value
3326
            $this->CustomHeader[] = explode(':', $name, 2);
3327
        } else {
3328
            $this->CustomHeader[] = array($name, $value);
3329
        }
3330
    }
3331 2
3332
    /**
3333
     * Returns all custom headers.
3334 2
     * @return array
3335 2
     */
3336 2
    public function getCustomHeaders()
3337
    {
3338
        return $this->CustomHeader;
3339 2
    }
3340 2
3341 2
    /**
3342 2
     * Create a message body from an HTML string.
3343 2
     * Automatically inlines images and creates a plain-text version by converting the HTML,
3344 2
     * overwriting any existing values in Body and AltBody.
3345 2
     * $basedir is used when handling relative image paths, e.g. <img src="images/a.png">
3346 2
     * will look for an image file in $basedir/images/a.png and convert it to inline.
3347 2
     * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself.
3348 2
     * @access public
3349 2
     * @param string $message HTML message string
3350 2
     * @param string $basedir base directory for relative paths to images
3351 2
     * @param boolean|callable $advanced Whether to use the internal HTML to text converter
3352 2
     *    or your own custom converter @see PHPMailer::html2text()
3353 2
     * @return string $message The transformed message Body
3354 2
     */
3355 2
    public function msgHTML($message, $basedir = '', $advanced = false)
3356 2
    {
3357
        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3358 2
        if (array_key_exists(2, $images)) {
3359 2
            foreach ($images[2] as $imgindex => $url) {
3360 2
                // Convert data URIs into embedded images
3361 2
                if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3362 2
                    $data = substr($url, strpos($url, ','));
3363 2
                    if ($match[2]) {
3364
                        $data = base64_decode($data);
3365 2
                    } else {
3366 2
                        $data = rawurldecode($data);
3367 2
                    }
3368
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3369
                    if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
3370
                        $message = str_replace(
3371 2
                            $images[0][$imgindex],
3372
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3373
                            $message
3374
                        );
3375
                    }
3376
                } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) {
3377
                    // Do not change urls for absolute images (thanks to corvuscorax)
3378
                    // Do not change urls that are already inline images
3379
                    $filename = basename($url);
3380
                    $directory = dirname($url);
3381
                    if ($directory == '.') {
3382
                        $directory = '';
3383
                    }
3384
                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
3385
                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3386
                        $basedir .= '/';
3387
                    }
3388
                    if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3389
                        $directory .= '/';
3390
                    }
3391
                    if ($this->addEmbeddedImage(
3392
                        $basedir . $directory . $filename,
3393
                        $cid,
3394 2
                        $filename,
3395
                        'base64',
3396 2
                        self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
3397 1
                    )
3398
                    ) {
3399 2
                        $message = preg_replace(
3400 2
                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3401 2
                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3402 2
                            $message
3403 2
                        );
3404
                    }
3405
                }
3406
            }
3407
        }
3408
        $this->isHTML(true);
3409
        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3410
        $this->Body = $this->normalizeBreaks($message);
3411
        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3412
        if (!$this->alternativeExists()) {
3413 10
            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
3414
                self::CRLF . self::CRLF;
3415
        }
3416 10
        return $this->Body;
3417 10
    }
3418 10
3419 10
    /**
3420 10
     * Convert an HTML string into plain text.
3421 10
     * This is used by msgHTML().
3422 10
     * Note - older versions of this function used a bundled advanced converter
3423 10
     * which was been removed for license reasons in #232.
3424 10
     * Example usage:
3425 10
     * <code>
3426 10
     * // Use default conversion
3427 10
     * $plain = $mail->html2text($html);
3428 10
     * // Use your own custom converter
3429 10
     * $plain = $mail->html2text($html, function($html) {
3430 10
     *     $converter = new MyHtml2text($html);
3431 10
     *     return $converter->get_text();
3432 10
     * });
3433 10
     * </code>
3434 10
     * @param string $html The HTML text to convert
3435 10
     * @param boolean|callable $advanced Any boolean value to use the internal converter,
3436 10
     *   or provide your own callable for custom conversion.
3437 10
     * @return string
3438 10
     */
3439 10
    public function html2text($html, $advanced = false)
3440 10
    {
3441 10
        if (is_callable($advanced)) {
3442 10
            return call_user_func($advanced, $html);
3443 10
        }
3444 10
        return html_entity_decode(
3445 10
            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3446 10
            ENT_QUOTES,
3447 10
            $this->CharSet
3448 10
        );
3449 10
    }
3450 10
3451 10
    /**
3452 10
     * Get the MIME type for a file extension.
3453 10
     * @param string $ext File extension
3454 10
     * @access public
3455 10
     * @return string MIME type of file.
3456 10
     * @static
3457 10
     */
3458 10
    public static function _mime_types($ext = '')
3459 10
    {
3460 10
        $mimes = array(
3461 10
            'xl'    => 'application/excel',
3462 10
            'js'    => 'application/javascript',
3463 10
            'hqx'   => 'application/mac-binhex40',
3464 10
            'cpt'   => 'application/mac-compactpro',
3465 10
            'bin'   => 'application/macbinary',
3466 10
            'doc'   => 'application/msword',
3467 10
            'word'  => 'application/msword',
3468 10
            'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3469 10
            'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3470 10
            'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3471 10
            'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3472 10
            'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3473 10
            'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3474 10
            'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3475 10
            'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3476 10
            'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
3477 10
            'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3478 10
            'class' => 'application/octet-stream',
3479 10
            'dll'   => 'application/octet-stream',
3480 10
            'dms'   => 'application/octet-stream',
3481 10
            'exe'   => 'application/octet-stream',
3482 10
            'lha'   => 'application/octet-stream',
3483 10
            'lzh'   => 'application/octet-stream',
3484 10
            'psd'   => 'application/octet-stream',
3485 10
            'sea'   => 'application/octet-stream',
3486 10
            'so'    => 'application/octet-stream',
3487 10
            'oda'   => 'application/oda',
3488 10
            'pdf'   => 'application/pdf',
3489 10
            'ai'    => 'application/postscript',
3490 10
            'eps'   => 'application/postscript',
3491 10
            'ps'    => 'application/postscript',
3492 10
            'smi'   => 'application/smil',
3493 10
            'smil'  => 'application/smil',
3494 10
            'mif'   => 'application/vnd.mif',
3495 10
            'xls'   => 'application/vnd.ms-excel',
3496 10
            'ppt'   => 'application/vnd.ms-powerpoint',
3497 10
            'wbxml' => 'application/vnd.wap.wbxml',
3498 10
            'wmlc'  => 'application/vnd.wap.wmlc',
3499 10
            'dcr'   => 'application/x-director',
3500 10
            'dir'   => 'application/x-director',
3501 10
            'dxr'   => 'application/x-director',
3502 10
            'dvi'   => 'application/x-dvi',
3503 10
            'gtar'  => 'application/x-gtar',
3504 10
            'php3'  => 'application/x-httpd-php',
3505 10
            'php4'  => 'application/x-httpd-php',
3506 10
            'php'   => 'application/x-httpd-php',
3507 10
            'phtml' => 'application/x-httpd-php',
3508 10
            'phps'  => 'application/x-httpd-php-source',
3509 10
            'swf'   => 'application/x-shockwave-flash',
3510 10
            'sit'   => 'application/x-stuffit',
3511 10
            'tar'   => 'application/x-tar',
3512 10
            'tgz'   => 'application/x-tar',
3513
            'xht'   => 'application/xhtml+xml',
3514 10
            'xhtml' => 'application/xhtml+xml',
3515 10
            'zip'   => 'application/zip',
3516 10
            'mid'   => 'audio/midi',
3517
            'midi'  => 'audio/midi',
3518
            'mp2'   => 'audio/mpeg',
3519
            'mp3'   => 'audio/mpeg',
3520
            'mpga'  => 'audio/mpeg',
3521
            'aif'   => 'audio/x-aiff',
3522
            'aifc'  => 'audio/x-aiff',
3523
            'aiff'  => 'audio/x-aiff',
3524
            'ram'   => 'audio/x-pn-realaudio',
3525
            'rm'    => 'audio/x-pn-realaudio',
3526
            'rpm'   => 'audio/x-pn-realaudio-plugin',
3527
            'ra'    => 'audio/x-realaudio',
3528 7
            'wav'   => 'audio/x-wav',
3529
            'bmp'   => 'image/bmp',
3530
            'gif'   => 'image/gif',
3531 7
            'jpeg'  => 'image/jpeg',
3532 7
            'jpe'   => 'image/jpeg',
3533
            'jpg'   => 'image/jpeg',
3534
            'png'   => 'image/png',
3535 7
            'tiff'  => 'image/tiff',
3536 7
            'tif'   => 'image/tiff',
3537
            'eml'   => 'message/rfc822',
3538
            'css'   => 'text/css',
3539
            'html'  => 'text/html',
3540
            'htm'   => 'text/html',
3541
            'shtml' => 'text/html',
3542
            'log'   => 'text/plain',
3543
            'text'  => 'text/plain',
3544
            'txt'   => 'text/plain',
3545
            'rtx'   => 'text/richtext',
3546
            'rtf'   => 'text/rtf',
3547
            'vcf'   => 'text/vcard',
3548
            'vcard' => 'text/vcard',
3549
            'xml'   => 'text/xml',
3550 10
            'xsl'   => 'text/xml',
3551
            'mpeg'  => 'video/mpeg',
3552 10
            'mpe'   => 'video/mpeg',
3553 10
            'mpg'   => 'video/mpeg',
3554 10
            'mov'   => 'video/quicktime',
3555 10
            'qt'    => 'video/quicktime',
3556 10
            'rv'    => 'video/vnd.rn-realvideo',
3557 10
            'avi'   => 'video/x-msvideo',
3558 10
            'movie' => 'video/x-sgi-movie'
3559 10
        );
3560 10
        if (array_key_exists(strtolower($ext), $mimes)) {
3561 10
            return $mimes[strtolower($ext)];
3562 10
        }
3563 10
        return 'application/octet-stream';
3564 10
    }
3565 10
3566 10
    /**
3567 10
     * Map a file name to a MIME type.
3568
     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3569 10
     * @param string $filename A file name or full path, does not need to exist as a file
3570 10
     * @return string
3571 1
     * @static
3572 10
     */
3573 10
    public static function filenameToType($filename)
3574
    {
3575 10
        // In case the path is a URL, strip any query string before getting extension
3576 10
        $qpos = strpos($filename, '?');
3577 2
        if (false !== $qpos) {
3578 8
            $filename = substr($filename, 0, $qpos);
3579 8
        }
3580 1
        $pathinfo = self::mb_pathinfo($filename);
3581 8
        return self::_mime_types($pathinfo['extension']);
3582 8
    }
3583 8
3584
    /**
3585
     * Multi-byte-safe pathinfo replacement.
3586
     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3587
     * Works similarly to the one in PHP >= 5.2.0
3588
     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3589
     * @param string $path A filename or path, does not need to exist as a file
3590
     * @param integer|string $options Either a PATHINFO_* constant,
3591
     *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3592
     * @return string|array
3593
     * @static
3594
     */
3595
    public static function mb_pathinfo($path, $options = null)
3596
    {
3597
        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3598
        $pathinfo = array();
3599
        if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3600 1
            if (array_key_exists(1, $pathinfo)) {
3601
                $ret['dirname'] = $pathinfo[1];
3602 1
            }
3603 1
            if (array_key_exists(2, $pathinfo)) {
3604 1
                $ret['basename'] = $pathinfo[2];
3605
            }
3606 1
            if (array_key_exists(5, $pathinfo)) {
3607 1
                $ret['extension'] = $pathinfo[5];
3608
            }
3609
            if (array_key_exists(3, $pathinfo)) {
3610
                $ret['filename'] = $pathinfo[3];
3611
            }
3612
        }
3613
        switch ($options) {
3614
            case PATHINFO_DIRNAME:
3615
            case 'dirname':
3616
                return $ret['dirname'];
3617 42
            case PATHINFO_BASENAME:
3618
            case 'basename':
3619 42
                return $ret['basename'];
3620
            case PATHINFO_EXTENSION:
3621
            case 'extension':
3622
                return $ret['extension'];
3623
            case PATHINFO_FILENAME:
3624
            case 'filename':
3625
                return $ret['filename'];
3626
            default:
3627
                return $ret;
3628
        }
3629
    }
3630
3631
    /**
3632 3
     * Set or reset instance properties.
3633
     * You should avoid this function - it's more verbose, less efficient, more error-prone and
3634 3
     * harder to debug than setting properties directly.
3635
     * Usage Example:
3636
     * `$mail->set('SMTPSecure', 'tls');`
3637
     *   is the same as:
3638
     * `$mail->SMTPSecure = 'tls';`
3639
     * @access public
3640
     * @param string $name The property name to set
3641
     * @param mixed $value The value to set the property to
3642
     * @return boolean
3643
     * @TODO Should this not be using the __set() magic function?
3644
     */
3645 2
    public function set($name, $value = '')
3646
    {
3647 2
        if (property_exists($this, $name)) {
3648 2
            $this->$name = $value;
3649 2
            return true;
3650 2
        } else {
3651 2
            $this->setError($this->lang('variable_set') . $name);
3652
            return false;
3653
        }
3654
    }
3655
3656
    /**
3657
     * Strip newlines to prevent header injection.
3658
     * @access public
3659 1
     * @param string $str
3660
     * @return string
3661 1
     */
3662 1
    public function secureHeader($str)
3663 1
    {
3664 1
        return trim(str_replace(array("\r", "\n"), '', $str));
3665 1
    }
3666 1
3667 1
    /**
3668
     * Normalize line breaks in a string.
3669 1
     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3670 1
     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3671
     * @param string $text
3672
     * @param string $breaktype What kind of line break to use, defaults to CRLF
3673
     * @return string
3674
     * @access public
3675
     * @static
3676
     */
3677
    public static function normalizeBreaks($text, $breaktype = "\r\n")
3678
    {
3679
        return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3680 1
    }
3681
3682 1
    /**
3683
     * Set the public and private key files and password for S/MIME signing.
3684
     * @access public
3685
     * @param string $cert_filename
3686
     * @param string $key_filename
3687
     * @param string $key_pass Password for private key
3688 1
     * @param string $extracerts_filename Optional path to chain certificate
3689 1
     */
3690
    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3691
    {
3692 1
        $this->sign_cert_file = $cert_filename;
3693
        $this->sign_key_file = $key_filename;
3694 1
        $this->sign_key_pass = $key_pass;
3695 1
        $this->sign_extracerts_file = $extracerts_filename;
3696 1
    }
3697
3698
    /**
3699
     * Quoted-Printable-encode a DKIM header.
3700
     * @access public
3701
     * @param string $txt
3702
     * @return string
3703
     */
3704
    public function DKIM_QP($txt)
3705
    {
3706
        $line = '';
3707
        for ($i = 0; $i < strlen($txt); $i++) {
3708 1
            $ord = ord($txt[$i]);
3709
            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3710 1
                $line .= $txt[$i];
3711 1
            } else {
3712 1
                $line .= '=' . sprintf('%02X', $ord);
3713 1
            }
3714 1
        }
3715 1
        return $line;
3716 1
    }
3717 1
3718 1
    /**
3719 1
     * Generate a DKIM signature.
3720
     * @access public
3721
     * @param string $signHeader
3722
     * @throws phpmailerException
3723
     * @return string The DKIM signature value
3724
     */
3725
    public function DKIM_Sign($signHeader)
3726
    {
3727
        if (!defined('PKCS7_TEXT')) {
3728 1
            if ($this->exceptions) {
3729
                throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3730 1
            }
3731
            return '';
3732
        }
3733
        $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
3734 1
        if ('' != $this->DKIM_passphrase) {
3735 1
            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3736
        } else {
3737 1
            $privKey = openssl_pkey_get_private($privKeyStr);
3738 1
        }
3739 1
        //Workaround for missing digest algorithms in old PHP & OpenSSL versions
3740 1
        //@link http://stackoverflow.com/a/11117338/333340
3741
        if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
3742
            in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
3743
            if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
3744
                openssl_pkey_free($privKey);
3745
                return base64_encode($signature);
3746
            }
3747
        } else {
3748
            $pinfo = openssl_pkey_get_details($privKey);
3749
            $hash = hash('sha256', $signHeader);
3750
            //'Magic' constant for SHA256 from RFC3447
3751 1
            //@link https://tools.ietf.org/html/rfc3447#page-43
3752
            $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...
3753 1
            $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
3754 1
            $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...
3755 1
3756 1
            if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
3757 1
                openssl_pkey_free($privKey);
3758 1
                return base64_encode($signature);
3759 1
            }
3760 1
        }
3761 1
        openssl_pkey_free($privKey);
3762 1
        return '';
3763 1
    }
3764 1
3765 1
    /**
3766 1
     * Generate a DKIM canonicalization header.
3767 1
     * @access public
3768 1
     * @param string $signHeader Header
3769 1
     * @return string
3770 1
     */
3771 1
    public function DKIM_HeaderC($signHeader)
3772 1
    {
3773 1
        $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3774 1
        $lines = explode("\r\n", $signHeader);
3775
        foreach ($lines as $key => $line) {
3776
            list($heading, $value) = explode(':', $line, 2);
3777 1
            $heading = strtolower($heading);
3778
            $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
3779
            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3780 1
        }
3781 1
        $signHeader = implode("\r\n", $lines);
3782 1
        return $signHeader;
3783 1
    }
3784 1
3785 1
    /**
3786 1
     * Generate a DKIM canonicalization body.
3787 1
     * @access public
3788 1
     * @param string $body Message Body
3789 1
     * @return string
3790 1
     */
3791 1
    public function DKIM_BodyC($body)
3792 1
    {
3793 1
        if ($body == '') {
3794 1
            return "\r\n";
3795
        }
3796
        // stabilize line endings
3797
        $body = str_replace("\r\n", "\n", $body);
3798 1
        $body = str_replace("\n", "\r\n", $body);
3799 1
        // END stabilize line endings
3800 1
        while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3801 1
            $body = substr($body, 0, strlen($body) - 2);
3802 1
        }
3803 1
        return $body;
3804 1
    }
3805 1
3806 1
    /**
3807 1
     * Create the DKIM header and body in a new message header.
3808 1
     * @access public
3809 1
     * @param string $headers_line Header lines
3810 1
     * @param string $subject Subject
3811 1
     * @param string $body Body
3812 1
     * @return string
3813 1
     */
3814 1
    public function DKIM_Add($headers_line, $subject, $body)
3815 1
    {
3816 1
        $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
3817
        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3818 1
        $DKIMquery = 'dns/txt'; // Query method
3819 1
        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3820 1
        $subject_header = "Subject: $subject";
3821
        $headers = explode($this->LE, $headers_line);
3822
        $from_header = '';
3823
        $to_header = '';
3824
        $date_header = '';
3825
        $current = '';
3826
        foreach ($headers as $header) {
3827
            if (strpos($header, 'From:') === 0) {
3828
                $from_header = $header;
3829 40
                $current = 'from_header';
3830
            } elseif (strpos($header, 'To:') === 0) {
3831
                $to_header = $header;
3832 40
                $current = 'to_header';
3833
            } elseif (strpos($header, 'Date:') === 0) {
3834
                $date_header = $header;
3835
                $current = 'date_header';
3836
            } else {
3837
                if (!empty($$current) && strpos($header, ' =?') === 0) {
3838
                    $$current .= $header;
3839
                } else {
3840
                    $current = '';
3841 2
                }
3842
            }
3843 2
        }
3844
        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3845
        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3846
        $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
3847
        $subject = str_replace(
3848
            '|',
3849
            '=7C',
3850
            $this->DKIM_QP($subject_header)
3851
        ); // Copied header fields (dkim-quoted-printable)
3852 1
        $body = $this->DKIM_BodyC($body);
3853
        $DKIMlen = strlen($body); // Length of body
3854 1
        $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
3855
        if ('' == $this->DKIM_identity) {
3856
            $ident = '';
3857
        } else {
3858
            $ident = ' i=' . $this->DKIM_identity . ';';
3859
        }
3860
        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3861
            $DKIMsignatureType . '; q=' .
3862
            $DKIMquery . '; l=' .
3863 1
            $DKIMlen . '; s=' .
3864
            $this->DKIM_selector .
3865 1
            ";\r\n" .
3866
            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3867
            "\th=From:To:Date:Subject;\r\n" .
3868
            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3869
            "\tz=$from\r\n" .
3870
            "\t|$to\r\n" .
3871
            "\t|$date\r\n" .
3872
            "\t|$subject;\r\n" .
3873
            "\tbh=" . $DKIMb64 . ";\r\n" .
3874 2
            "\tb=";
3875
        $toSign = $this->DKIM_HeaderC(
3876 2
            $from_header . "\r\n" .
3877
            $to_header . "\r\n" .
3878
            $date_header . "\r\n" .
3879
            $subject_header . "\r\n" .
3880
            $dkimhdrs
3881
        );
3882
        $signed = $this->DKIM_Sign($toSign);
3883
        return $dkimhdrs . $signed . "\r\n";
3884
    }
3885
3886
    /**
3887
     * Detect if a string contains a line longer than the maximum line length allowed.
3888
     * @param string $str
3889
     * @return boolean
3890
     * @static
3891
     */
3892
    public static function hasLineLongerThanMax($str)
3893
    {
3894
        //+2 to include CRLF line break for a 1000 total
3895
        return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
3896
    }
3897
3898
    /**
3899
     * Allows for public read access to 'to' property.
3900 36
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3901
     * @access public
3902 36
     * @return array
3903
     */
3904
    public function getToAddresses()
3905
    {
3906 36
        return $this->to;
3907
    }
3908
3909
    /**
3910
     * Allows for public read access to 'cc' property.
3911
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3912
     * @access public
3913
     * @return array
3914
     */
3915
    public function getCcAddresses()
3916
    {
3917
        return $this->cc;
3918
    }
3919
3920
    /**
3921
     * Allows for public read access to 'bcc' property.
3922
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3923
     * @access public
3924
     * @return array
3925
     */
3926
    public function getBccAddresses()
3927
    {
3928
        return $this->bcc;
3929
    }
3930
3931
    /**
3932
     * Allows for public read access to 'ReplyTo' property.
3933
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3934
     * @access public
3935
     * @return array
3936
     */
3937
    public function getReplyToAddresses()
3938
    {
3939
        return $this->ReplyTo;
3940
    }
3941
3942
    /**
3943
     * Allows for public read access to 'all_recipients' property.
3944
     * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
3945
     * @access public
3946
     * @return array
3947
     */
3948
    public function getAllRecipientAddresses()
3949
    {
3950
        return $this->all_recipients;
3951
    }
3952
3953
    /**
3954
     * Perform a callback.
3955
     * @param boolean $isSent
3956
     * @param array $to
3957
     * @param array $cc
3958
     * @param array $bcc
3959
     * @param string $subject
3960
     * @param string $body
3961
     * @param string $from
3962
     */
3963
    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...
3964
    {
3965
        if (!empty($this->action_function) && is_callable($this->action_function)) {
3966
            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3967
            call_user_func_array($this->action_function, $params);
3968
        }
3969
    }
3970
}
3971
3972
/**
3973
 * PHPMailer exception handler
3974
 * @package PHPMailer
3975
 */
3976
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...
3977
{
3978
    /**
3979
     * Prettify error message output
3980
     * @return string
3981
     */
3982
    public function errorMessage()
3983
    {
3984
        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3985
        return $errorMsg;
3986
    }
3987
}
3988