Completed
Push — master ( cf8cd0...6eaec6 )
by Marcus
07:01
created

PHPMailer::mailPassthru()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 7.1941

Importance

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

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

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

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

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

Loading history...
482
483
    /**
484
     * The array of 'bcc' names and addresses.
485
     * @var array
486
     * @access protected
487
     */
488
    protected $bcc = array();
489
490
    /**
491
     * The array of reply-to names and addresses.
492
     * @var array
493
     * @access protected
494
     */
495
    protected $ReplyTo = array();
496
497
    /**
498
     * An array of all kinds of addresses.
499
     * Includes all of $to, $cc, $bcc
500
     * @var array
501
     * @access protected
502
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
503
     */
504
    protected $all_recipients = array();
505
506
    /**
507
     * An array of names and addresses queued for validation.
508
     * In send(), valid and non duplicate entries are moved to $all_recipients
509
     * and one of $to, $cc, or $bcc.
510
     * This array is used only for addresses with IDN.
511
     * @var array
512
     * @access protected
513
     * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
514
     * @see PHPMailer::$all_recipients
515
     */
516
    protected $RecipientsQueue = array();
517
518
    /**
519
     * An array of reply-to names and addresses queued for validation.
520
     * In send(), valid and non duplicate entries are moved to $ReplyTo.
521
     * This array is used only for addresses with IDN.
522
     * @var array
523
     * @access protected
524
     * @see PHPMailer::$ReplyTo
525
     */
526
    protected $ReplyToQueue = array();
527
528
    /**
529
     * The array of attachments.
530
     * @var array
531
     * @access protected
532
     */
533
    protected $attachment = array();
534
535
    /**
536
     * The array of custom headers.
537
     * @var array
538
     * @access protected
539
     */
540
    protected $CustomHeader = array();
541
542
    /**
543
     * The most recent Message-ID (including angular brackets).
544
     * @var string
545
     * @access protected
546
     */
547
    protected $lastMessageID = '';
548
549
    /**
550
     * The message's MIME type.
551
     * @var string
552
     * @access protected
553
     */
554
    protected $message_type = '';
555
556
    /**
557
     * The array of MIME boundary strings.
558
     * @var array
559
     * @access protected
560
     */
561
    protected $boundary = array();
562
563
    /**
564
     * The array of available languages.
565
     * @var array
566
     * @access protected
567
     */
568
    protected $language = array();
569
570
    /**
571
     * The number of errors encountered.
572
     * @var integer
573
     * @access protected
574
     */
575
    protected $error_count = 0;
576
577
    /**
578
     * The S/MIME certificate file path.
579
     * @var string
580
     * @access protected
581
     */
582
    protected $sign_cert_file = '';
583
584
    /**
585
     * The S/MIME key file path.
586
     * @var string
587
     * @access protected
588
     */
589
    protected $sign_key_file = '';
590
591
    /**
592
     * The optional S/MIME extra certificates ("CA Chain") file path.
593
     * @var string
594
     * @access protected
595
     */
596
    protected $sign_extracerts_file = '';
597
598
    /**
599
     * The S/MIME password for the key.
600
     * Used only if the key is encrypted.
601
     * @var string
602
     * @access protected
603
     */
604
    protected $sign_key_pass = '';
605
606
    /**
607
     * Whether to throw exceptions for errors.
608
     * @var boolean
609
     * @access protected
610
     */
611
    protected $exceptions = false;
612
613
    /**
614
     * Unique ID used for message ID and boundaries.
615
     * @var string
616
     * @access protected
617
     */
618
    protected $uniqueid = '';
619
620
    /**
621
     * Error severity: message only, continue processing.
622
     */
623
    const STOP_MESSAGE = 0;
624
625
    /**
626
     * Error severity: message, likely ok to continue processing.
627
     */
628
    const STOP_CONTINUE = 1;
629
630
    /**
631
     * Error severity: message, plus full stop, critical error reached.
632
     */
633
    const STOP_CRITICAL = 2;
634
635
    /**
636
     * SMTP RFC standard line ending.
637
     */
638
    const CRLF = "\r\n";
639
640
    /**
641
     * The maximum line length allowed by RFC 2822 section 2.1.1
642
     * @var integer
643
     */
644
    const MAX_LINE_LENGTH = 998;
645
646
    /**
647 56
     * Constructor.
648
     * @param boolean $exceptions Should we throw external exceptions?
649 56
     */
650
    public function __construct($exceptions = null)
651
    {
652 56
        if ($exceptions !== null) {
653
            $this->exceptions = (boolean)$exceptions;
654
        }
655
    }
656
657 56
    /**
658
     * Destructor.
659
     */
660 56
    public function __destruct()
661 56
    {
662
        //Close any open SMTP connection nicely
663
        $this->smtpClose();
664
    }
665
666
    /**
667
     * Call mail() in a safe_mode-aware fashion.
668
     * Also, unless sendmail_path points to sendmail (or something that
669
     * claims to be sendmail), don't pass params (not a perfect fix,
670
     * but it will do)
671
     * @param string $to To
672
     * @param string $subject Subject
673
     * @param string $body Message Body
674
     * @param string $header Additional Header(s)
675
     * @param string $params Params
676 2
     * @access private
677
     * @return boolean
678
     */
679 2
    private function mailPassthru($to, $subject, $body, $header, $params)
680
    {
681
        //Check overloading of mail function to avoid double-encoding
682 2
        if (ini_get('mbstring.func_overload') & 1) {
683
            $subject = $this->secureHeader($subject);
684
        } else {
685
            $subject = $this->encodeHeader($this->secureHeader($subject));
686 2
        }
687
688
        //Can't use additional_parameters in safe_mode
689 2
        //@link http://php.net/manual/en/function.mail.php
690
        if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
691 2
            $result = @mail($to, $subject, $body, $header);
692
        } else {
693
            $result = @mail($to, $subject, $body, $header, $params);
694
        }
695
        return $result;
696
    }
697
    /**
698
     * Output debugging info via user-defined method.
699
     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
700
     * @see PHPMailer::$Debugoutput
701 4
     * @see PHPMailer::$SMTPDebug
702
     * @param string $str
703 4
     */
704
    protected function edebug($str)
705
    {
706
        if ($this->SMTPDebug <= 0) {
707 4
            return;
708
        }
709
        //Avoid clash with built-in function names
710
        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
711 4
            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
712 4
            return;
713
        }
714
        switch ($this->Debugoutput) {
715
            case 'error_log':
716 4
                //Don't output, just log
717
                error_log($str);
718
                break;
719
            case 'html':
720
                //Cleans up output a bit for a better looking, HTML-safe output
721
                echo htmlentities(
722
                    preg_replace('/[\r\n]+/', '', $str),
723
                    ENT_QUOTES,
724
                    'UTF-8'
725 4
                )
726 4
                . "<br>\n";
727
                break;
728 4
            case 'echo':
729 4
            default:
730 4
                //Normalize line breaks
731 4
                $str = preg_replace('/\r\n?/ms', "\n", $str);
732 4
                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
733 4
                    "\n",
734 4
                    "\n                   \t                  ",
735 4
                    trim($str)
736
                ) . "\n";
737
        }
738
    }
739
740
    /**
741
     * Sets message type to HTML or plain.
742 16
     * @param boolean $isHtml True for HTML mode.
743
     * @return void
744 16
     */
745 13
    public function isHTML($isHtml = true)
746 13
    {
747 3
        if ($isHtml) {
748
            $this->ContentType = 'text/html';
749 16
        } else {
750
            $this->ContentType = 'text/plain';
751
        }
752
    }
753
754
    /**
755 1
     * Send messages using SMTP.
756
     * @return void
757 1
     */
758 1
    public function isSMTP()
759
    {
760
        $this->Mailer = 'smtp';
761
    }
762
763
    /**
764 3
     * Send messages using PHP's mail() function.
765
     * @return void
766 3
     */
767 3
    public function isMail()
768
    {
769
        $this->Mailer = 'mail';
770
    }
771
772
    /**
773 2
     * Send messages using $Sendmail.
774
     * @return void
775 2
     */
776
    public function isSendmail()
777 2
    {
778
        $ini_sendmail_path = ini_get('sendmail_path');
779
780 2
        if (!stristr($ini_sendmail_path, 'sendmail')) {
781
            $this->Sendmail = '/usr/sbin/sendmail';
782 2
        } else {
783 2
            $this->Sendmail = $ini_sendmail_path;
784
        }
785
        $this->Mailer = 'sendmail';
786
    }
787
788
    /**
789 1
     * Send messages using qmail.
790
     * @return void
791 1
     */
792
    public function isQmail()
793 1
    {
794 1
        $ini_sendmail_path = ini_get('sendmail_path');
795 1
796
        if (!stristr($ini_sendmail_path, 'qmail')) {
797
            $this->Sendmail = '/var/qmail/bin/qmail-inject';
798 1
        } else {
799 1
            $this->Sendmail = $ini_sendmail_path;
800
        }
801
        $this->Mailer = 'qmail';
802
    }
803
804
    /**
805
     * Add a "To" address.
806
     * @param string $address The email address to send to
807 56
     * @param string $name
808
     * @return boolean true on success, false if address already used or invalid in some way
809 56
     */
810
    public function addAddress($address, $name = '')
811
    {
812
        return $this->addOrEnqueueAnAddress('to', $address, $name);
813
    }
814
815
    /**
816
     * Add a "CC" address.
817
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
818
     * @param string $address The email address to send to
819 56
     * @param string $name
820
     * @return boolean true on success, false if address already used or invalid in some way
821 56
     */
822
    public function addCC($address, $name = '')
823
    {
824
        return $this->addOrEnqueueAnAddress('cc', $address, $name);
825
    }
826
827
    /**
828
     * Add a "BCC" address.
829
     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
830
     * @param string $address The email address to send to
831 3
     * @param string $name
832
     * @return boolean true on success, false if address already used or invalid in some way
833 3
     */
834
    public function addBCC($address, $name = '')
835
    {
836
        return $this->addOrEnqueueAnAddress('bcc', $address, $name);
837
    }
838
839
    /**
840
     * Add a "Reply-To" address.
841
     * @param string $address The email address to reply to
842 56
     * @param string $name
843
     * @return boolean true on success, false if address already used or invalid in some way
844 56
     */
845
    public function addReplyTo($address, $name = '')
846
    {
847
        return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
848
    }
849
850
    /**
851
     * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
852
     * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
853
     * be modified after calling this function), addition of such addresses is delayed until send().
854
     * Addresses that have been added already return false, but do not throw exceptions.
855
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
856
     * @param string $address The email address to send, resp. to reply to
857
     * @param string $name
858
     * @throws phpmailerException
859 56
     * @return boolean true on success, false if address already used or invalid in some way
860
     * @access protected
861 56
     */
862 56
    protected function addOrEnqueueAnAddress($kind, $address, $name)
863 56
    {
864
        $address = trim($address);
865 1
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
866 1
        if (($pos = strrpos($address, '@')) === false) {
867 1
            // At-sign is misssing.
868 1
            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
869
            $this->setError($error_message);
870
            $this->edebug($error_message);
871 1
            if ($this->exceptions) {
872
                throw new phpmailerException($error_message);
873 56
            }
874
            return false;
875 56
        }
876 2
        $params = array($kind, $address, $name);
877 2
        // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
878 2
        if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
879 2
            if ($kind != 'Reply-To') {
880
                if (!array_key_exists($address, $this->RecipientsQueue)) {
881 1
                    $this->RecipientsQueue[$address] = $params;
882 2
                    return true;
883 2
                }
884 2
            } else {
885
                if (!array_key_exists($address, $this->ReplyToQueue)) {
886
                    $this->ReplyToQueue[$address] = $params;
887 1
                    return true;
888
                }
889
            }
890 56
            return false;
891
        }
892
        // Immediately add standard addresses without IDN.
893
        return call_user_func_array(array($this, 'addAnAddress'), $params);
894
    }
895
896
    /**
897
     * Add an address to one of the recipient arrays or to the ReplyTo array.
898
     * Addresses that have been added already return false, but do not throw exceptions.
899
     * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
900
     * @param string $address The email address to send, resp. to reply to
901
     * @param string $name
902
     * @throws phpmailerException
903 56
     * @return boolean true on success, false if address already used or invalid in some way
904
     * @access protected
905 56
     */
906
    protected function addAnAddress($kind, $address, $name = '')
907
    {
908
        if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
909
            $error_message = $this->lang('Invalid recipient kind: ') . $kind;
910
            $this->setError($error_message);
911
            $this->edebug($error_message);
912
            if ($this->exceptions) {
913
                throw new phpmailerException($error_message);
914 56
            }
915 2
            return false;
916 2
        }
917 2
        if (!$this->validateAddress($address)) {
918 2
            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
919
            $this->setError($error_message);
920
            $this->edebug($error_message);
921 2
            if ($this->exceptions) {
922
                throw new phpmailerException($error_message);
923 56
            }
924 56
            return false;
925 56
        }
926 56
        if ($kind != 'Reply-To') {
927 56
            if (!array_key_exists(strtolower($address), $this->all_recipients)) {
928
                array_push($this->$kind, array($address, $name));
929 2
                $this->all_recipients[strtolower($address)] = true;
930 56
                return true;
931 56
            }
932 56
        } else {
933
            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
934
                $this->ReplyTo[strtolower($address)] = array($address, $name);
935 2
                return true;
936
            }
937
        }
938
        return false;
939
    }
940
941
    /**
942
     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
943
     * of the form "display name <address>" into an array of name/address pairs.
944
     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
945
     * Note that quotes in the name part are removed.
946
     * @param string $addrstr The address list string
947
     * @param bool $useimap Whether to use the IMAP extension to parse the list
948 1
     * @return array
949
     * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
950 1
     */
951 1
    public function parseAddresses($addrstr, $useimap = true)
952
    {
953 1
        $addresses = array();
954 1
        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
955 1
            //Use this built-in parser if it's available
956 1
            $list = imap_rfc822_parse_adrlist($addrstr, '');
957 1
            foreach ($list as $address) {
958 1
                if ($address->host != '.SYNTAX-ERROR.') {
959 1
                    if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
960 1
                        $addresses[] = array(
961 1
                            'name' => (property_exists($address, 'personal') ? $address->personal : ''),
962 1
                            'address' => $address->mailbox . '@' . $address->host
963 1
                        );
964 1
                    }
965
                }
966 1
            }
967 1
        } else {
968 1
            //Use this simpler parser
969
            $list = explode(',', $addrstr);
970 1
            foreach ($list as $address) {
971
                $address = trim($address);
972 1
                //Is there a separate name part?
973 1
                if (strpos($address, '<') === false) {
974 1
                    //No separate name, just use the whole thing
975
                    if ($this->validateAddress($address)) {
976 1
                        $addresses[] = array(
977 1
                            'name' => '',
978 1
                            'address' => $address
979 1
                        );
980 1
                    }
981 1
                } else {
982 1
                    list($name, $email) = explode('<', $address);
983 1
                    $email = trim(str_replace('>', '', $email));
984
                    if ($this->validateAddress($email)) {
985 1
                        $addresses[] = array(
986 1
                            'name' => trim(str_replace(array('"', "'"), '', $name)),
987
                            'address' => $email
988 1
                        );
989
                    }
990 1
                }
991
            }
992
        }
993
        return $addresses;
994
    }
995
996
    /**
997
     * Set the From and FromName properties.
998
     * @param string $address
999
     * @param string $name
1000
     * @param boolean $auto Whether to also set the Sender address, defaults to true
1001 1
     * @throws phpmailerException
1002
     * @return boolean
1003 1
     */
1004 1
    public function setFrom($address, $name = '', $auto = true)
1005
    {
1006 1
        $address = trim($address);
1007 1
        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
1008 1
        // Don't validate now addresses with IDN. Will be done in send().
1009 1
        if (($pos = strrpos($address, '@')) === false or
1010 1
            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
1011 1
            !$this->validateAddress($address)) {
1012 1
            $error_message = $this->lang('invalid_address') . " (setFrom) $address";
1013
            $this->setError($error_message);
1014
            $this->edebug($error_message);
1015 1
            if ($this->exceptions) {
1016
                throw new phpmailerException($error_message);
1017 1
            }
1018 1
            return false;
1019 1
        }
1020 1
        $this->From = $address;
1021 1
        $this->FromName = $name;
1022 1
        if ($auto) {
1023 1
            if (empty($this->Sender)) {
1024 1
                $this->Sender = $address;
1025
            }
1026
        }
1027
        return true;
1028
    }
1029
1030
    /**
1031
     * Return the Message-ID header of the last email.
1032
     * Technically this is the value from the last time the headers were created,
1033
     * but it's also the message ID of the last sent message except in
1034 1
     * pathological cases.
1035
     * @return string
1036 1
     */
1037
    public function getLastMessageID()
1038
    {
1039
        return $this->lastMessageID;
1040
    }
1041
1042
    /**
1043
     * Check that a string looks like an email address.
1044
     * @param string $address The email address to check
1045
     * @param string|callable $patternselect A selector for the validation pattern to use :
1046
     * * `auto` Pick best pattern automatically;
1047
     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
1048
     * * `pcre` Use old PCRE implementation;
1049
     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1050
     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1051
     * * `noregex` Don't use a regex: super fast, really dumb.
1052
     * Alternatively you may pass in a callable to inject your own validator, for example:
1053
     * PHPMailer::validateAddress('[email protected]', function($address) {
1054
     *     return (strpos($address, '@') !== false);
1055
     * });
1056
     * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
1057
     * @return boolean
1058 56
     * @static
1059
     * @access public
1060 56
     */
1061 56
    public static function validateAddress($address, $patternselect = null)
0 ignored issues
show
Complexity introduced by
This operation has 672 execution paths which exceeds the configured maximum of 200.

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

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

Loading history...
1062 56
    {
1063 56
        if (is_null($patternselect)) {
1064 1
            $patternselect = self::$validator;
1065
        }
1066
        if (is_callable($patternselect)) {
1067 56
            return call_user_func($patternselect, $address);
1068 1
        }
1069
        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1070 56
        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1071
            return false;
1072
        }
1073 4
        if (!$patternselect or $patternselect == 'auto') {
1074
            //Check this constant first so it works when extension_loaded() is disabled by safe mode
1075 4
            //Constant was added in PHP 5.2.4
1076 4
            if (defined('PCRE_VERSION')) {
1077 4
                //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
1078
                if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
1079
                    $patternselect = 'pcre8';
1080 4
                } else {
1081
                    $patternselect = 'pcre';
1082
                }
1083
            } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
1084
                //Fall back to older PCRE
1085
                $patternselect = 'pcre';
1086
            } else {
1087
                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1088
                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1089
                    $patternselect = 'php';
1090
                } else {
1091 4
                    $patternselect = 'noregex';
1092
                }
1093 56
            }
1094
        }
1095
        switch ($patternselect) {
1096
            case 'pcre8':
1097
                /**
1098
                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1099
                 * @link http://squiloople.com/2009/12/20/email-address-validation/
1100 4
                 * @copyright 2009-2010 Michael Rushton
1101
                 * Feel free to use and redistribute this code. But please keep this copyright notice.
1102 4
                 */
1103 4
                return (boolean)preg_match(
1104 4
                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1105 4
                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1106 4
                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1107 4
                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1108 4
                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1109 4
                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1110
                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1111 4
                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1112 54
                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1113
                    $address
1114 1
                );
1115
            case 'pcre':
1116 1
                //An older regex that doesn't need a recent PCRE
1117 1
                return (boolean)preg_match(
1118 1
                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1119 1
                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1120 1
                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1121 1
                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1122 1
                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1123 1
                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1124 1
                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1125
                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1126 1
                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1127 54
                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1128
                    $address
1129
                );
1130
            case 'html5':
1131
                /**
1132
                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1133
                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1134
                 */
1135
                return (boolean)preg_match(
1136
                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1137 54
                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1138
                    $address
1139
                );
1140 1
            case 'noregex':
1141 1
                //No PCRE! Do something _very_ approximate!
1142 1
                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1143 54
                return (strlen($address) >= 3
1144 54
                    and strpos($address, '@') >= 1
1145 54
                    and strpos($address, '@') != strlen($address) - 1);
1146
            case 'php':
1147
            default:
1148
                return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
1149
        }
1150
    }
1151
1152
    /**
1153
     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1154 41
     * "intl" and "mbstring" PHP extensions.
1155
     * @return bool "true" if required functions for IDN support are present
1156
     */
1157 41
    public function idnSupported()
1158
    {
1159
        // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1160
        return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1161
    }
1162
1163
    /**
1164
     * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1165
     * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1166
     * This function silently returns unmodified address if:
1167
     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1168
     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1169
     *   or fails for any reason (e.g. domain has characters not allowed in an IDN)
1170
     * @see PHPMailer::$CharSet
1171 41
     * @param string $address The email address to convert
1172
     * @return string The encoded address in ASCII form
1173
     */
1174 41
    public function punyencodeAddress($address)
1175 41
    {
1176 41
        // Verify we have required functions, CharSet, and at-sign.
1177 41
        if ($this->idnSupported() and
1178
            !empty($this->CharSet) and
1179 41
            ($pos = strrpos($address, '@')) !== false) {
1180 3
            $domain = substr($address, ++$pos);
1181 3
            // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1182 3
            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1183 3
                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1184 3
                if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1185
                    idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
1186
                    idn_to_ascii($domain)) !== false) {
1187 41
                    return substr($address, 0, $pos) . $punycode;
1188 41
                }
1189
            }
1190
        }
1191
        return $address;
1192
    }
1193
1194
    /**
1195
     * Create a message and send it.
1196
     * Uses the sending method specified by $Mailer.
1197 36
     * @throws phpmailerException
1198
     * @return boolean false on error - See the ErrorInfo property for details of the error.
1199
     */
1200 36
    public function send()
1201 3
    {
1202
        try {
1203 36
            if (!$this->preSend()) {
1204
                return false;
1205
            }
1206
            return $this->postSend();
1207
        } catch (phpmailerException $exc) {
1208
            $this->mailHeader = '';
1209
            $this->setError($exc->getMessage());
1210
            if ($this->exceptions) {
1211
                throw $exc;
1212
            }
1213
            return false;
1214
        }
1215
    }
1216
1217
    /**
1218
     * Prepare a message for sending.
1219 41
     * @throws phpmailerException
1220
     * @return boolean
1221
     */
1222 41
    public function preSend()
0 ignored issues
show
Complexity introduced by
This operation has 2522 execution paths which exceeds the configured maximum of 200.

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

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

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

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

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

Loading history...
1487
    {
1488
        $bad_rcpt = array();
1489 33
        if (!$this->smtpConnect($this->SMTPOptions)) {
1490
            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1491
        }
1492 33
        if ('' == $this->Sender) {
1493
            $smtp_from = $this->From;
1494 33
        } else {
1495
            $smtp_from = $this->Sender;
1496
        }
1497
        if (!$this->smtp->mail($smtp_from)) {
1498
            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1499
            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1500 33
        }
1501 33
1502 33
        // Attempt to send to all recipients
1503
        foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1504
            foreach ($togroup as $to) {
1505
                if (!$this->smtp->recipient($to[0])) {
1506
                    $error = $this->smtp->getError();
1507 33
                    $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1508
                    $isSent = false;
1509 33
                } else {
1510 33
                    $isSent = true;
1511 33
                }
1512
                $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1513
            }
1514 33
        }
1515
1516
        // Only send the DATA command if we have viable recipients
1517 33
        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1518 1
            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1519 1
        }
1520 32
        if ($this->SMTPKeepAlive) {
1521 32
            $this->smtp->reset();
1522
        } else {
1523
            $this->smtp->quit();
1524 33
            $this->smtp->close();
1525
        }
1526
        //Create error message for any bad addresses
1527
        if (count($bad_rcpt) > 0) {
1528
            $errstr = '';
1529
            foreach ($bad_rcpt as $bad) {
1530
                $errstr .= $bad['to'] . ': ' . $bad['error'];
1531
            }
1532
            throw new phpmailerException(
1533
                $this->lang('recipients_failed') . $errstr,
1534 33
                self::STOP_CONTINUE
1535
            );
1536
        }
1537
        return true;
1538
    }
1539
1540
    /**
1541
     * Initiate a connection to an SMTP server.
1542
     * Returns false if the operation failed.
1543
     * @param array $options An array of options compatible with stream_context_create()
1544
     * @uses SMTP
1545
     * @access public
1546 35
     * @throws phpmailerException
1547
     * @return boolean
1548 35
     */
1549 35
    public function smtpConnect($options = null)
0 ignored issues
show
Complexity introduced by
This operation has 264984 execution paths which exceeds the configured maximum of 200.

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

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

Loading history...
1550 35
    {
1551
        if (is_null($this->smtp)) {
1552
            $this->smtp = $this->getSMTPInstance();
1553 35
        }
1554 2
1555 2
        //If no options are provided, use whatever is set in the instance
1556
        if (is_null($options)) {
1557
            $options = $this->SMTPOptions;
1558 35
        }
1559 1
1560
        // Already connected?
1561
        if ($this->smtp->connected()) {
1562 35
            return true;
1563 35
        }
1564 35
1565 35
        $this->smtp->setTimeout($this->Timeout);
1566 35
        $this->smtp->setDebugLevel($this->SMTPDebug);
1567 35
        $this->smtp->setDebugOutput($this->Debugoutput);
1568
        $this->smtp->setVerp($this->do_verp);
1569 35
        $hosts = explode(';', $this->Host);
1570 35
        $lastexception = null;
1571 35
1572
        foreach ($hosts as $hostentry) {
1573
            $hostinfo = array();
1574
            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1575
                // Not a valid host entry
1576
                continue;
1577
            }
1578
            // $hostinfo[2]: optional ssl or tls prefix
1579
            // $hostinfo[3]: the hostname
1580 35
            // $hostinfo[4]: optional port number
1581 35
            // The host string prefix can temporarily override the current setting for SMTPSecure
1582 35
            // If it's not specified, the default value is used
1583 35
            $prefix = '';
1584 1
            $secure = $this->SMTPSecure;
1585 1
            $tls = ($this->SMTPSecure == 'tls');
1586 1
            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1587 35
                $prefix = 'ssl://';
1588 1
                $tls = false; // Can't have SSL and TLS at the same time
1589
                $secure = 'ssl';
1590 1
            } elseif ($hostinfo[2] == 'tls') {
1591 1
                $tls = true;
1592
                // tls doesn't use a prefix
1593 35
                $secure = 'tls';
1594 35
            }
1595
            //Do we need the OpenSSL extension?
1596 1
            $sslext = defined('OPENSSL_ALGO_SHA1');
1597
            if ('tls' === $secure or 'ssl' === $secure) {
1598
                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1599 1
                if (!$sslext) {
1600 35
                    throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1601 35
                }
1602 35
            }
1603 35
            $host = $hostinfo[3];
1604 1
            $port = $this->Port;
1605 1
            $tport = (integer)$hostinfo[4];
1606 35
            if ($tport > 0 and $tport < 65536) {
1607
                $port = $tport;
1608 35
            }
1609 35
            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1610 35
                try {
1611
                    if ($this->Helo) {
1612
                        $hello = $this->Helo;
1613 35
                    } else {
1614
                        $hello = $this->serverHostname();
1615
                    }
1616
                    $this->smtp->hello($hello);
1617
                    //Automatically enable TLS encryption if:
1618
                    // * it's not disabled
1619 35
                    // * we have openssl extension
1620
                    // * we are not already using SSL
1621
                    // * the server offers STARTTLS
1622 35
                    if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1623
                        $tls = true;
1624
                    }
1625
                    if ($tls) {
1626
                        if (!$this->smtp->startTLS()) {
1627
                            throw new phpmailerException($this->lang('connect_host'));
1628
                        }
1629 35
                        // We must resend EHLO after TLS negotiation
1630
                        $this->smtp->hello($hello);
1631
                    }
1632
                    if ($this->SMTPAuth) {
1633
                        if (!$this->smtp->authenticate(
1634
                            $this->Username,
1635
                            $this->Password,
1636
                            $this->AuthType,
1637
                            $this->Realm,
1638
                            $this->Workstation
1639
                        )
1640
                        ) {
1641 35
                            throw new phpmailerException($this->lang('authenticate'));
1642
                        }
1643
                    }
1644
                    return true;
1645
                } catch (phpmailerException $exc) {
1646
                    $lastexception = $exc;
1647
                    $this->edebug($exc->getMessage());
1648
                    // We must have connected, but then failed TLS or Auth, so close connection nicely
1649 1
                    $this->smtp->quit();
1650
                }
1651 1
            }
1652
        }
1653 1
        // If we get here, all connection attempts have failed, so close connection hard
1654
        $this->smtp->close();
1655
        // As we've caught all exceptions, just report whatever the last one was
1656 1
        if ($this->exceptions and !is_null($lastexception)) {
1657
            throw $lastexception;
1658
        }
1659
        return false;
1660
    }
1661
1662
    /**
1663 56
     * Close the active SMTP session if one exists.
1664
     * @return void
1665 56
     */
1666 35
    public function smtpClose()
1667 3
    {
1668 3
        if (is_a($this->smtp, 'SMTP')) {
1669 3
            if ($this->smtp->connected()) {
1670 35
                $this->smtp->quit();
1671 56
                $this->smtp->close();
1672
            }
1673
        }
1674
    }
1675
1676
    /**
1677
     * Set the language for error messages.
1678
     * Returns false if it cannot load the language file.
1679
     * The default language is English.
1680
     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1681
     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1682 8
     * @return boolean
1683
     * @access public
1684
     */
1685
    public function setLanguage($langcode = 'en', $lang_path = '')
1686 8
    {
1687 8
        // Backwards compatibility for renamed language codes
1688 8
        $renamed_langcodes = array(
1689 8
            'br' => 'pt_br',
1690 8
            'cz' => 'cs',
1691 8
            'dk' => 'da',
1692 8
            'no' => 'nb',
1693 8
            'se' => 'sv',
1694 8
        );
1695 8
1696 8
        if (isset($renamed_langcodes[$langcode])) {
1697 8
            $langcode = $renamed_langcodes[$langcode];
1698 8
        }
1699 8
1700 8
        // Define full set of translatable strings in English
1701 8
        $PHPMAILER_LANG = array(
1702 8
            'authenticate' => 'SMTP Error: Could not authenticate.',
1703 8
            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1704
            'data_not_accepted' => 'SMTP Error: data not accepted.',
1705 8
            'empty_message' => 'Message body empty',
1706 8
            'encoding' => 'Unknown encoding: ',
1707
            'execute' => 'Could not execute: ',
1708 8
            'file_access' => 'Could not access file: ',
1709 8
            'file_open' => 'File Error: Could not open file: ',
1710 8
            'from_failed' => 'The following From address failed: ',
1711 8
            'instantiate' => 'Could not instantiate mail function.',
1712
            'invalid_address' => 'Invalid address: ',
1713 8
            'mailer_not_supported' => ' mailer is not supported.',
1714
            'provide_address' => 'You must provide at least one recipient email address.',
1715 1
            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1716
            'signing' => 'Signing Error: ',
1717
            'smtp_connect_failed' => 'SMTP connect() failed.',
1718
            'smtp_error' => 'SMTP server error: ',
1719
            'variable_set' => 'Cannot set or reset variable: ',
1720 1
            'extension_missing' => 'Extension missing: '
1721
        );
1722 1
        if (empty($lang_path)) {
1723 8
            // Calculate an absolute path so it can work if CWD is not here
1724 8
            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1725
        }
1726
        //Validate $langcode
1727
        if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
1728
            $langcode = 'en';
1729
        }
1730
        $foundlang = true;
1731
        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1732
        // There is no English translation file
1733
        if ($langcode != 'en') {
1734
            // Make sure language file path is readable
1735
            if (!is_readable($lang_file)) {
1736
                $foundlang = false;
1737
            } else {
1738
                // Overwrite language-specific strings.
1739
                // This way we'll never have missing translation keys.
1740
                $foundlang = include $lang_file;
1741
            }
1742
        }
1743
        $this->language = $PHPMAILER_LANG;
1744
        return (boolean)$foundlang; // Returns false if language not found
1745
    }
1746 42
1747
    /**
1748 42
     * Get the array of strings for the current language.
1749 42
     * @return array
1750 42
     */
1751 42
    public function getTranslations()
1752 42
    {
1753
        return $this->language;
1754
    }
1755
1756
    /**
1757
     * Create recipient headers.
1758
     * @access public
1759
     * @param string $type
1760
     * @param array $addr An array of recipient,
1761
     * where each recipient is a 2-element indexed array with element 0 containing an address
1762 42
     * and element 1 containing a name, like:
1763
     * array(array('[email protected]', 'Joe User'), array('[email protected]', 'Zoe User'))
1764 42
     * @return string
1765 3
     */
1766
    public function addrAppend($type, $addr)
1767 42
    {
1768 42
        $addresses = array();
1769 42
        foreach ($addr as $address) {
1770
            $addresses[] = $this->addrFormat($address);
1771
        }
1772
        return $type . ': ' . implode(', ', $addresses) . $this->LE;
1773
    }
1774
1775
    /**
1776
     * Format an address for use in a message header.
1777
     * @access public
1778
     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1779
     *      like array('[email protected]', 'Joe User')
1780
     * @return string
1781
     */
1782
    public function addrFormat($addr)
1783
    {
1784 4
        if (empty($addr[1])) { // No name provided
1785
            return $this->secureHeader($addr[0]);
1786 4
        } else {
1787
            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1788
                $addr[0]
1789 4
            ) . '>';
1790
        }
1791
    }
1792
1793 4
    /**
1794 4
     * Word-wrap message.
1795 4
     * For use with mailers that do not automatically perform wrapping
1796
     * and for quoted-printable encoded messages.
1797 4
     * Original written by philippe.
1798
     * @param string $message The message to wrap
1799 4
     * @param integer $length The line length to wrap to
1800 2
     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1801 2
     * @access public
1802
     * @return string
1803
     */
1804 4
    public function wrapText($message, $length, $qp_mode = false)
1805
    {
1806 4
        if ($qp_mode) {
1807 4
            $soft_break = sprintf(' =%s', $this->LE);
1808 4
        } else {
1809 4
            $soft_break = $this->LE;
1810 4
        }
1811 4
        // If utf-8 encoding is used, we will need to make sure we don't
1812 4
        // split multibyte characters when we wrap
1813
        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1814
        $lelen = strlen($this->LE);
1815
        $crlflen = strlen(self::CRLF);
1816
1817
        $message = $this->fixEOL($message);
1818
        //Remove a trailing line break
1819
        if (substr($message, -$lelen) == $this->LE) {
1820
            $message = substr($message, 0, -$lelen);
1821
        }
1822
1823
        //Split message into lines
1824
        $lines = explode($this->LE, $message);
1825
        //Message will be rebuilt in here
1826
        $message = '';
1827
        foreach ($lines as $line) {
1828
            $words = explode(' ', $line);
1829
            $buf = '';
1830
            $firstword = true;
1831
            foreach ($words as $word) {
1832
                if ($qp_mode and (strlen($word) > $length)) {
1833
                    $space_left = $length - strlen($buf) - $crlflen;
1834
                    if (!$firstword) {
1835
                        if ($space_left > 20) {
1836
                            $len = $space_left;
1837
                            if ($is_utf8) {
1838
                                $len = $this->utf8CharBoundary($word, $len);
1839
                            } elseif (substr($word, $len - 1, 1) == '=') {
1840
                                $len--;
1841
                            } elseif (substr($word, $len - 2, 1) == '=') {
1842
                                $len -= 2;
1843
                            }
1844
                            $part = substr($word, 0, $len);
1845
                            $word = substr($word, $len);
1846
                            $buf .= ' ' . $part;
1847
                            $message .= $buf . sprintf('=%s', self::CRLF);
1848
                        } else {
1849
                            $message .= $buf . $soft_break;
1850
                        }
1851
                        $buf = '';
1852
                    }
1853
                    while (strlen($word) > 0) {
1854
                        if ($length <= 0) {
1855 4
                            break;
1856 4
                        }
1857 4
                        $len = $length;
1858 4
                        if ($is_utf8) {
1859 4
                            $len = $this->utf8CharBoundary($word, $len);
1860
                        } elseif (substr($word, $len - 1, 1) == '=') {
1861 4
                            $len--;
1862 4
                        } elseif (substr($word, $len - 2, 1) == '=') {
1863 4
                            $len -= 2;
1864 4
                        }
1865
                        $part = substr($word, 0, $len);
1866 4
                        $word = substr($word, $len);
1867 4
1868 4
                        if (strlen($word) > 0) {
1869 4
                            $message .= $part . sprintf('=%s', self::CRLF);
1870
                        } else {
1871 4
                            $buf = $part;
1872
                        }
1873
                    }
1874
                } else {
1875
                    $buf_o = $buf;
1876
                    if (!$firstword) {
1877
                        $buf .= ' ';
1878
                    }
1879
                    $buf .= $word;
1880
1881
                    if (strlen($buf) > $length and $buf_o != '') {
1882
                        $message .= $buf_o . $soft_break;
1883
                        $buf = $word;
1884
                    }
1885
                }
1886
                $firstword = false;
1887
            }
1888
            $message .= $buf . self::CRLF;
1889
        }
1890
1891
        return $message;
1892
    }
1893
1894
    /**
1895
     * Find the last character boundary prior to $maxLength in a utf-8
1896
     * quoted-printable encoded string.
1897
     * Original written by Colin Brown.
1898
     * @access public
1899
     * @param string $encodedText utf-8 QP text
1900
     * @param integer $maxLength Find the last character boundary prior to this length
1901
     * @return integer
1902
     */
1903
    public function utf8CharBoundary($encodedText, $maxLength)
1904
    {
1905
        $foundSplitPos = false;
1906
        $lookBack = 3;
1907
        while (!$foundSplitPos) {
1908
            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1909
            $encodedCharPos = strpos($lastChunk, '=');
1910
            if (false !== $encodedCharPos) {
1911
                // Found start of encoded character byte within $lookBack block.
1912
                // Check the encoded byte value (the 2 chars after the '=')
1913
                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1914
                $dec = hexdec($hex);
1915
                if ($dec < 128) {
1916
                    // Single byte character.
1917
                    // If the encoded char was found at pos 0, it will fit
1918
                    // otherwise reduce maxLength to start of the encoded char
1919
                    if ($encodedCharPos > 0) {
1920
                        $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1921
                    }
1922
                    $foundSplitPos = true;
1923
                } elseif ($dec >= 192) {
1924
                    // First byte of a multi byte character
1925
                    // Reduce maxLength to split at start of character
1926
                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1927
                    $foundSplitPos = true;
1928 41
                } elseif ($dec < 192) {
1929
                    // Middle byte of a multi byte character, look further back
1930 41
                    $lookBack += 3;
1931 38
                }
1932
            } else {
1933
                // No encoded character found
1934 3
                $foundSplitPos = true;
1935 3
            }
1936 3
        }
1937 3
        return $maxLength;
1938 3
    }
1939 1
1940 1
    /**
1941 2
     * Apply word wrapping to the message body.
1942 2
     * Wraps the message body to the number of chars set in the WordWrap property.
1943 2
     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
1944 3
     * This is called automatically by createBody(), so you don't need to call it yourself.
1945 3
     * @access public
1946
     * @return void
1947
     */
1948
    public function setWordWrap()
1949
    {
1950
        if ($this->WordWrap < 1) {
1951
            return;
1952 42
        }
1953
1954 42
        switch ($this->message_type) {
1955
            case 'alt':
1956 42
            case 'alt_inline':
1957 42
            case 'alt_attach':
1958 42
            case 'alt_inline_attach':
1959 42
                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1960
                break;
1961
            default:
1962 42
                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1963
                break;
1964
        }
1965
    }
1966
1967
    /**
1968
     * Assemble message headers.
1969 42
     * @access public
1970 41
     * @return string The assembled headers
1971 39
     */
1972 39
    public function createHeader()
0 ignored issues
show
Complexity introduced by
This operation has 80640 execution paths which exceeds the configured maximum of 200.

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

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

Loading history...
1973 42
    {
1974 1
        $result = '';
1975 1
1976
        if ($this->MessageDate == '') {
1977
            $this->MessageDate = self::rfcDate();
1978 42
        }
1979
        $result .= $this->headerLine('Date', $this->MessageDate);
1980
1981 42
        // To be created automatically by mail()
1982 39
        if ($this->SingleTo) {
1983 39
            if ($this->Mailer != 'mail') {
1984
                foreach ($this->to as $toaddr) {
1985
                    $this->SingleToArray[] = $this->addrFormat($toaddr);
1986
                }
1987 42
            }
1988 40
        } else {
1989 42
            if (count($this->to) > 0) {
1990 42
                if ($this->Mailer != 'mail') {
1991
                    $result .= $this->addrAppend('To', $this->to);
1992
                }
1993
            } elseif (count($this->cc) == 0) {
1994 42
                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1995 42
            }
1996 42
        }
1997
1998
        $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1999 42
2000 40
        // sendmail and mail() extract Cc from the header before sending
2001 40
        if (count($this->cc) > 0) {
2002
            $result .= $this->addrAppend('Cc', $this->cc);
2003 42
        }
2004 1
2005 1
        // sendmail and mail() extract Bcc from the header before sending
2006 42
        if ((
2007
                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
2008 42
            )
2009 42
            and count($this->bcc) > 0
2010 42
        ) {
2011 42
            $result .= $this->addrAppend('Bcc', $this->bcc);
2012 42
        }
2013 42
2014 42
        if (count($this->ReplyTo) > 0) {
2015 42
            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
2016 42
        }
2017 42
2018
        // mail() sets the subject itself
2019
        if ($this->Mailer != 'mail') {
2020
            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2021
        }
2022
2023
        // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
2024 42
        // https://tools.ietf.org/html/rfc5322#section-3.6.4
2025 1
        if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
2026 1
            $this->lastMessageID = $this->MessageID;
2027
        } else {
2028
            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
2029 42
        }
2030
        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
2031
        if (!is_null($this->Priority)) {
2032
            $result .= $this->headerLine('X-Priority', $this->Priority);
2033
        }
2034 42
        if ($this->XMailer == '') {
2035 42
            $result .= $this->headerLine(
2036 40
                'X-Mailer',
2037 40
                'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
2038 40
            );
2039
        } else {
2040 42
            $myXmailer = trim($this->XMailer);
2041
            if ($myXmailer) {
2042
                $result .= $this->headerLine('X-Mailer', $myXmailer);
2043
            }
2044
        }
2045
2046
        if ($this->ConfirmReadingTo != '') {
2047
            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
2048 42
        }
2049
2050 42
        // Add custom headers
2051 42
        foreach ($this->CustomHeader as $header) {
2052 42
            $result .= $this->headerLine(
2053 42
                trim($header[0]),
2054 3
                $this->encodeHeader(trim($header[1]))
2055 3
            );
2056 3
        }
2057 39
        if (!$this->sign_key_file) {
2058 39
            $result .= $this->headerLine('MIME-Version', '1.0');
2059 39
            $result .= $this->getMailMIME();
2060 39
        }
2061 6
2062 6
        return $result;
2063 6
    }
2064 33
2065 33
    /**
2066 6
     * Get the message MIME type headers.
2067 6
     * @access public
2068 6
     * @return string
2069 27
     */
2070
    public function getMailMIME()
2071 27
    {
2072 27
        $result = '';
2073 27
        $ismultipart = true;
2074 42
        switch ($this->message_type) {
2075
            case 'inline':
2076 42
                $result .= $this->headerLine('Content-Type', 'multipart/related;');
2077
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2078 24
                break;
2079 15
            case 'attach':
2080 15
            case 'inline_attach':
2081 15
            case 'alt_attach':
2082
            case 'alt_inline_attach':
2083 15
                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2084 9
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2085
                break;
2086 24
            case 'alt':
2087
            case 'alt_inline':
2088 42
                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2089 40
                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2090 40
                break;
2091
            default:
2092 42
                // Catches case 'plain': and case '':
2093
                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2094
                $ismultipart = false;
2095
                break;
2096
        }
2097
        // RFC1341 part 5 says 7bit is assumed if not specified
2098
        if ($this->Encoding != '7bit') {
2099
            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2100
            if ($ismultipart) {
2101
                if ($this->Encoding == '8bit') {
2102
                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2103 9
                }
2104
                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2105 9
            } else {
2106
                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2107
            }
2108
        }
2109
2110
        if ($this->Mailer != 'mail') {
2111
            $result .= $this->LE;
2112
        }
2113
2114
        return $result;
2115 41
    }
2116
2117 41
    /**
2118
     * Returns the whole MIME message.
2119 41
     * Includes complete headers and body.
2120 41
     * Only valid post preSend().
2121 41
     * @see PHPMailer::preSend()
2122 41
     * @access public
2123
     * @return string
2124 41
     */
2125 2
    public function getSentMIMEMessage()
2126 2
    {
2127
        return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
2128 41
    }
2129
2130 41
    /**
2131 41
     * Assemble the message body.
2132
     * Returns an empty string on failure.
2133 41
     * @access public
2134 33
     * @throws phpmailerException
2135
     * @return string The assembled message body
2136 33
     */
2137 33
    public function createBody()
2138
    {
2139
        $body = '';
2140 41
        //Create unique IDs and preset boundaries
2141 2
        $this->uniqueid = md5(uniqid(time()));
2142 2
        $this->boundary[1] = 'b1_' . $this->uniqueid;
2143
        $this->boundary[2] = 'b2_' . $this->uniqueid;
2144 41
        $this->boundary[3] = 'b3_' . $this->uniqueid;
2145 41
2146
        if ($this->sign_key_file) {
2147 41
            $body .= $this->getMailMIME() . $this->LE;
2148 37
        }
2149
2150 37
        $this->setWordWrap();
2151 37
2152
        $bodyEncoding = $this->Encoding;
2153
        $bodyCharSet = $this->CharSet;
2154 41
        //Can we do a 7-bit downgrade?
2155 1
        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2156 1
            $bodyEncoding = '7bit';
2157
            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2158 41
            $bodyCharSet = 'us-ascii';
2159 41
        }
2160 41
        //If lines are too long, and we're not already using an encoding that will shorten them,
2161 3
        //change to quoted-printable transfer encoding for the body part only
2162 3
        if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
2163 3
            $bodyEncoding = 'quoted-printable';
2164 3
        }
2165 3
2166 3
        $altBodyEncoding = $this->Encoding;
2167 38
        $altBodyCharSet = $this->CharSet;
2168 4
        //Can we do a 7-bit downgrade?
2169 4
        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2170 4
            $altBodyEncoding = '7bit';
2171 4
            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2172 4
            $altBodyCharSet = 'us-ascii';
2173 4
        }
2174 34
        //If lines are too long, and we're not already using an encoding that will shorten them,
2175 1
        //change to quoted-printable transfer encoding for the alt body part only
2176 1
        if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
2177 1
            $altBodyEncoding = 'quoted-printable';
2178 1
        }
2179 1
        //Use this as a preamble in all multipart message types
2180 1
        $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
2181 1
        switch ($this->message_type) {
2182 1
            case 'inline':
2183 1
                $body .= $mimepre;
2184 1
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2185 1
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2186 1
                $body .= $this->LE . $this->LE;
2187 33
                $body .= $this->attachAll('inline', $this->boundary[1]);
2188 4
                break;
2189 4
            case 'attach':
2190 4
                $body .= $mimepre;
2191 4
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2192 4
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2193 4
                $body .= $this->LE . $this->LE;
2194 4
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2195 4
                break;
2196 1
            case 'inline_attach':
2197 1
                $body .= $mimepre;
2198 1
                $body .= $this->textLine('--' . $this->boundary[1]);
2199 1
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2200 4
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2201 4
                $body .= $this->LE;
2202 29
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2203 2
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2204 2
                $body .= $this->LE . $this->LE;
2205 2
                $body .= $this->attachAll('inline', $this->boundary[2]);
2206 2
                $body .= $this->LE;
2207 2
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2208 2
                break;
2209 2
            case 'alt':
2210 2
                $body .= $mimepre;
2211 2
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2212 2
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2213 2
                $body .= $this->LE . $this->LE;
2214 2
                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2215 2
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2216 2
                $body .= $this->LE . $this->LE;
2217 2
                if (!empty($this->Ical)) {
2218 27
                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2219 1
                    $body .= $this->encodeString($this->Ical, $this->Encoding);
2220 1
                    $body .= $this->LE . $this->LE;
2221 1
                }
2222 1
                $body .= $this->endBoundary($this->boundary[1]);
2223 1
                break;
2224 1
            case 'alt_inline':
2225 1
                $body .= $mimepre;
2226 1
                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2227 1
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2228 1
                $body .= $this->LE . $this->LE;
2229 1
                $body .= $this->textLine('--' . $this->boundary[1]);
2230 1
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2231 1
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2232 1
                $body .= $this->LE;
2233 1
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2234 26
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2235
                $body .= $this->LE . $this->LE;
2236
                $body .= $this->attachAll('inline', $this->boundary[2]);
2237
                $body .= $this->LE;
2238
                $body .= $this->endBoundary($this->boundary[1]);
2239
                break;
2240
            case 'alt_attach':
2241
                $body .= $mimepre;
2242
                $body .= $this->textLine('--' . $this->boundary[1]);
2243
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2244
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2245
                $body .= $this->LE;
2246
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2247
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2248
                $body .= $this->LE . $this->LE;
2249
                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2250
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2251
                $body .= $this->LE . $this->LE;
2252
                $body .= $this->endBoundary($this->boundary[2]);
2253
                $body .= $this->LE;
2254
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2255
                break;
2256 26
            case 'alt_inline_attach':
2257
                $body .= $mimepre;
2258
                $body .= $this->textLine('--' . $this->boundary[1]);
2259 26
                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2260 26
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2261 26
                $body .= $this->LE;
2262 41
                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2263
                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2264 41
                $body .= $this->LE . $this->LE;
2265
                $body .= $this->textLine('--' . $this->boundary[2]);
2266 41
                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2267
                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2268 2
                $body .= $this->LE;
2269
                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2270
                $body .= $this->encodeString($this->Body, $bodyEncoding);
2271
                $body .= $this->LE . $this->LE;
2272 2
                $body .= $this->attachAll('inline', $this->boundary[3]);
2273 2
                $body .= $this->LE;
2274
                $body .= $this->endBoundary($this->boundary[2]);
2275
                $body .= $this->LE;
2276 2
                $body .= $this->attachAll('attachment', $this->boundary[1]);
2277
                break;
2278 2
            default:
2279 1
                // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
2280 1
                //Reset the `Encoding` property in case we changed it for line length reasons
2281 1
                $this->Encoding = $bodyEncoding;
2282 1
                $body .= $this->encodeString($this->Body, $this->Encoding);
2283 1
                break;
2284
        }
2285 1
2286 1
        if ($this->isError()) {
2287 1
            $body = '';
2288 1
        } elseif ($this->sign_key_file) {
2289 1
            try {
2290 1
                if (!defined('PKCS7_TEXT')) {
2291 1
                    throw new phpmailerException($this->lang('extension_missing') . 'openssl');
2292 1
                }
2293 1
                // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2294 1
                $file = tempnam(sys_get_temp_dir(), 'mail');
2295 1
                if (false === file_put_contents($file, $body)) {
2296
                    throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2297 2
                }
2298 2
                $signed = tempnam(sys_get_temp_dir(), 'signed');
2299 2
                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2300 2
                if (empty($this->sign_extracerts_file)) {
2301
                    $sign = @openssl_pkcs7_sign(
2302 2
                        $file,
2303 2
                        $signed,
2304 2
                        'file://' . realpath($this->sign_cert_file),
2305 2
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2306
                        null
2307
                    );
2308
                } else {
2309
                    $sign = @openssl_pkcs7_sign(
2310 2
                        $file,
2311
                        $signed,
2312
                        'file://' . realpath($this->sign_cert_file),
2313
                        array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2314
                        null,
2315
                        PKCS7_DETACHED,
2316 2
                        $this->sign_extracerts_file
2317 41
                    );
2318
                }
2319
                if ($sign) {
2320
                    @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...
2321
                    $body = file_get_contents($signed);
2322
                    @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...
2323
                    //The message returned by openssl contains both headers and body, so need to split them up
2324
                    $parts = explode("\n\n", $body, 2);
2325
                    $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2326
                    $body = $parts[1];
2327
                } else {
2328
                    @unlink($file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2329 15
                    @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...
2330
                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
2331 15
                }
2332 15
            } catch (phpmailerException $exc) {
2333 1
                $body = '';
2334 1
                if ($this->exceptions) {
2335 15
                    throw $exc;
2336 8
                }
2337 8
            }
2338 15
        }
2339 1
        return $body;
2340 1
    }
2341 15
2342 15
    /**
2343 15
     * Return the start of a message boundary.
2344
     * @access protected
2345 15
     * @param string $boundary
2346 5
     * @param string $charSet
2347 5
     * @param string $contentType
2348 15
     * @param string $encoding
2349
     * @return string
2350 15
     */
2351
    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2352
    {
2353
        $result = '';
2354
        if ($charSet == '') {
2355
            $charSet = $this->CharSet;
2356
        }
2357
        if ($contentType == '') {
2358
            $contentType = $this->ContentType;
2359 7
        }
2360
        if ($encoding == '') {
2361 7
            $encoding = $this->Encoding;
2362
        }
2363
        $result .= $this->textLine('--' . $boundary);
2364
        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2365
        $result .= $this->LE;
2366
        // RFC1341 part 5 says 7bit is assumed if not specified
2367
        if ($encoding != '7bit') {
2368
            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2369
        }
2370 41
        $result .= $this->LE;
2371
2372 41
        return $result;
2373 41
    }
2374 7
2375 7
    /**
2376 41
     * Return the end of a message boundary.
2377 6
     * @access protected
2378 6
     * @param string $boundary
2379 41
     * @return string
2380 6
     */
2381 6
    protected function endBoundary($boundary)
2382 41
    {
2383 41
        return $this->LE . '--' . $boundary . '--' . $this->LE;
2384
    }
2385 26
2386 26
    /**
2387 41
     * Set the message type.
2388
     * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2389
     * @access protected
2390
     * @return void
2391
     */
2392
    protected function setMessageType()
2393
    {
2394
        $type = array();
2395
        if ($this->alternativeExists()) {
2396 42
            $type[] = 'alt';
2397
        }
2398 42
        if ($this->inlineImageExists()) {
2399
            $type[] = 'inline';
2400
        }
2401
        if ($this->attachmentExists()) {
2402
            $type[] = 'attach';
2403
        }
2404
        $this->message_type = implode('_', $type);
2405
        if ($this->message_type == '') {
2406
            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2407 42
            $this->message_type = 'plain';
2408
        }
2409 42
    }
2410
2411
    /**
2412
     * Format a header line.
2413
     * @access public
2414
     * @param string $name
2415
     * @param string $value
2416
     * @return string
2417
     */
2418
    public function headerLine($name, $value)
2419
    {
2420
        return $name . ': ' . $value . $this->LE;
2421
    }
2422
2423 5
    /**
2424
     * Return a formatted mail line.
2425
     * @access public
2426 5
     * @param string $value
2427 1
     * @return string
2428
     */
2429
    public function textLine($value)
2430
    {
2431 5
        return $value . $this->LE;
2432 5
    }
2433 5
2434
    /**
2435 5
     * Add an attachment from a path on the filesystem.
2436 5
     * Returns false if the file could not be found or read.
2437 1
     * @param string $path Path to the attachment.
2438 1
     * @param string $name Overrides the attachment name.
2439
     * @param string $encoding File encoding (see $Encoding).
2440 5
     * @param string $type File extension (MIME) type.
2441 5
     * @param string $disposition Disposition to use
2442 5
     * @throws phpmailerException
2443 5
     * @return boolean
2444 5
     */
2445 5
    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2446 5
    {
2447 5
        try {
2448
            if (!@is_file($path)) {
2449 5
                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2450
            }
2451 5
2452 1
            // If a MIME type is not specified, try to work it out from the file name
2453 1
            if ($type == '') {
2454 1
                $type = self::filenameToType($path);
2455
            }
2456
2457 1
            $filename = basename($path);
2458
            if ($name == '') {
2459 5
                $name = $filename;
2460
            }
2461
2462
            $this->attachment[] = array(
2463
                0 => $path,
2464
                1 => $filename,
2465
                2 => $name,
2466 40
                3 => $encoding,
2467
                4 => $type,
2468 40
                5 => false, // isStringAttachment
2469
                6 => $disposition,
2470
                7 => 0
2471
            );
2472
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
2473
        } catch (phpmailerException $exc) {
2474
            $this->setError($exc->getMessage());
2475
            $this->edebug($exc->getMessage());
2476
            if ($this->exceptions) {
2477
                throw $exc;
2478
            }
2479 11
            return false;
2480
        }
2481
        return true;
2482 11
    }
2483 11
2484 11
    /**
2485
     * Return the array of attachments.
2486
     * @return array
2487 11
     */
2488
    public function getAttachments()
2489 11
    {
2490
        return $this->attachment;
2491 11
    }
2492 11
2493 11
    /**
2494 11
     * Attach all file, string, and binary attachments to the message.
2495 2
     * Returns an empty string on failure.
2496 2
     * @access protected
2497 9
     * @param string $disposition_type
2498
     * @param string $boundary
2499
     * @return string
2500 11
     */
2501 11
    protected function attachAll($disposition_type, $boundary)
0 ignored issues
show
Complexity introduced by
This operation has 1538 execution paths which exceeds the configured maximum of 200.

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

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

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