GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Mail::addCc()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace phpMyFAQ;
4
5
/**
6
 * MUA (Mail User Agent) implementation.
7
 *
8
 *
9
 *
10
 * This Source Code Form is subject to the terms of the Mozilla Public License,
11
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
12
 * obtain one at http://mozilla.org/MPL/2.0/.
13
 *
14
 * @package phpMyFAQ
15
 * @author Matteo Scaramuccia <[email protected]>
16
 * @author Thorsten Rinne <[email protected]>
17
 * @copyright 2009-2019 phpMyFAQ Team
18
 * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
19
 * @link https://www.phpmyfaq.de
20
 * @since 2009-09-11
21
 */
22
23
use phpMyFAQ\Mail\SwiftSMTP;
24
25
if (!defined('IS_VALID_PHPMYFAQ')) {
26
    exit();
27
}
28
29
/**
30
 * Mail.
31
 *
32
 * @package phpMyFAQ
33
 * @author Matteo Scaramuccia <[email protected]>
34
 * @author Thorsten Rinne <[email protected]>
35
 * @copyright 2009-2019 phpMyFAQ Team
36
 * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
37
 * @link https://www.phpmyfaq.de
38
 * @since 2009-09-11
39
 */
40
class Mail
41
{
42
    /**
43
     * Type of the used MUA. Possible values:
44
     * - built-in.     
45
     *
46
     * @var string
47
     */
48
    public $agent;
49
50
    /**
51
     * Attached filed.
52
     *
53
     * @var mixed
54
     */
55
    public $attachments;
56
57
    /**
58
     * Body of the e-mail.
59
     *
60
     * @var string
61
     */
62
    public $body = '';
63
64
    /**
65
     * Boundary.
66
     *
67
     * @var string
68
     */
69
    public $boundary = '----------';
70
71
    /**
72
     * Charset.
73
     *
74
     * @var string
75
     */
76
    public $charset = 'utf-8';
77
78
    /**
79
     * Content disposition.
80
     *
81
     * @var string
82
     */
83
    public $contentDisposition = 'inline';
84
85
    /**
86
     * Content type.
87
     *
88
     * @var string
89
     */
90
    public $contentType = 'text/plain';
91
92
    /**
93
     * Content transfer encoding.
94
     *
95
     * @var string
96
     */
97
    public $contentTransferEncoding = '8bit';
98
99
    /**
100
     * The one and only valid End Of Line sequence as per RFC 2822:
101
     * carriage-return followed by line-feed.     
102
     *
103
     * @var string
104
     */
105
    public $eol = "\r\n";
106
107
    /**
108
     * Array of headers of the e-mail
109
     *
110
     * @var array
111
     */
112
    public $headers;
113
114
    /**
115
     * Message of the e-mail: HTML text allowed.
116
     *
117
     * @var string
118
     */
119
    public $message;
120
121
    /**
122
     * Alternate message of the e-mail: only plain text allowed.
123
     *
124
     * @var string
125
     */
126
    public $messageAlt;
127
128
    /**
129
     * Message-ID of the e-mail.
130
     *
131
     * @var string
132
     */
133
    public $messageId;
134
135
    /**
136
     * Priorities: 1 (Highest), 2 (High), 3 (Normal), 4 (Low), 5 (Lowest).
137
     *
138
     * @var mixed
139
     */
140
    public $priorities = [
141
        1 => 'Highest',
142
        2 => 'High',
143
        3 => 'Normal',
144
        4 => 'Low',
145
        5 => 'Lowest'
146
    ];
147
148
    /**
149
     * Priority of the e-mail: 1 (Highest), 2 (High), 3 (Normal), 4 (Low), 5 (Lowest).
150
     *
151
     * @var int
152
     *
153
     * @see priorities     
154
     */
155
    public $priority;
156
157
    /**
158
     * Subject of the e-mail.
159
     *
160
     * @var string
161
     */
162
    public $subject;
163
164
    /**
165
     * Recipients of the e-mail as <BCC>.
166
     *
167
     * @var mixed
168
     */
169
    private $_bcc;
170
171
    /**
172
     * Recipients of the e-mail as <CC>.
173
     *
174
     * @var mixed
175
     */
176
    private $_cc;
177
178
    /**
179
     * Recipients of the e-mail as <From>.
180
     *
181
     * @var mixed
182
     */
183
    private $_from;
184
185
    /**
186
     * Mailer string.
187
     *
188
     * @var string
189
     */
190
    private $_mailer;
191
192
    /**
193
     * Recipient of the optional notification.
194
     *
195
     * @var mixed
196
     */
197
    private $_notifyTo;
198
199
    /**
200
     * Recipient of the e-mail as <Reply-To>.
201
     *
202
     * @var mixed
203
     */
204
    private $_replyTo;
205
206
    /**
207
     * Recipient of the e-mail as <Return-Path>.
208
     *
209
     * @var mixed
210
     */
211
    private $_returnPath;
212
213
    /**
214
     * Recipient of the e-mail as <Sender>.
215
     *
216
     * @var mixed
217
     */
218
    private $_sender;
219
220
    /**
221
     * Recipients of the e-mail as <TO:>.
222
     *
223
     * @var mixed
224
     */
225
    private $_to;
226
227
    /**
228
     * @var PMF_Configuration
229
     */
230
    private $_config;
231
232
    /*
233
     * Default constructor.
234
     * Note: any email will be sent from the PMF administrator, use unsetFrom
235
     *       before using setFrom.     
236
     *
237
     * @param Configuration $config
238
     */
239
    public function __construct(Configuration $config)
240
    {
241
        // Set default value for public properties
242
        $this->agent = $config->get('mail.remoteSMTP') ? 'SwiftSMTP' : 'built-in';
243
        $this->attachments = [];
244
        $this->boundary = self::createBoundary();
245
        $this->headers = [];
246
        $this->message = '';
247
        $this->messageAlt = '';
248
        $this->messageId = '<'.$_SERVER['REQUEST_TIME'].'.'.md5(microtime()).'@'.self::getServerName().'>';
249
        $this->priority = 3; // 3 -> Normal
250
        $this->subject = '';
251
252
        // Set default value for private properties
253
        $this->_config = $config;
0 ignored issues
show
Documentation Bug introduced by
It seems like $config of type object<phpMyFAQ\Configuration> is incompatible with the declared type object<phpMyFAQ\PMF_Configuration> of property $_config.

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

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

Loading history...
254
        $this->_bcc = [];
255
        $this->_cc = [];
256
        $this->_from = [];
257
        $this->_mailer = 'phpMyFAQ on PHP/'.PHP_VERSION;
258
        $this->_notifyTo = [];
259
        $this->_replyTo = [];
260
        $this->_returnPath = [];
261
        $this->_sender = [];
262
        $this->_to = [];
263
264
        // Set phpMyFAQ related data
265
        $this->_mailer = 'phpMyFAQ/'.$this->_config->get('main.currentVersion');
266
        $this->setFrom($this->_config->get('main.administrationMail'), $this->_config->get('main.titleFAQ'));
267
    }
268
269
    /**
270
     * Add an e-mail address to an array.
271
     *
272
     * @param array  $target      Target array.
273
     * @param string $targetAlias Alias Target alias.
274
     * @param string $address     User e-mail address.
275
     * @param string $name        User name (optional).
276
     *
277
     * @return bool True if successful, false otherwise.
278
     *
279
     * @todo   Enhance error handling using exceptions
280
     */
281
    private function _addEmailTo(&$target, $targetAlias, $address, $name = null)
282
    {
283
        // Sanity check
284
        if (!self::validateEmail($address)) {
285
            trigger_error(
286
                '<strong>Mail Class</strong>: '.$address.' is not a valid e-mail address!',
287
                E_USER_ERROR
288
            );
289
290
            return false;
291
        }
292
293
        // Don't allow duplicated addresses
294
        if (array_key_exists($address, $target)) {
295
            trigger_error(
296
                '<strong>Mail Class</strong>: '.$address." has been already added in '$targetAlias'!",
297
                E_USER_WARNING
298
            );
299
300
            return false;
301
        }
302
303
        if (!empty($name)) {
304
            // Remove CR and LF characters to prevent header injection
305
            $name = str_replace(array("\n", "\r"), '', $name);
306
307
            if (function_exists('mb_encode_mimeheader')) {
308
                // Encode any special characters in the displayed name
309
                $name = mb_encode_mimeheader($name);
310
            }
311
312
            // Wrap the displayed name in quotes (to fix problems with commas etc),
313
            // and escape any existing quotes
314
            $name = '"'.str_replace('"', '\"', $name).'"';
315
        }
316
317
        // Add the e-mail address into the target array
318
        $target[$address] = $name;
319
        // On Windows, when using PHP built-in mail drop any name, just use the e-mail address
320
        if (('WIN' === strtoupper(substr(PHP_OS, 0, 3))) && ('built-in' == $this->agent)) {
321
            $target[$address] = null;
322
        }
323
324
        return true;
325
    }
326
327
    /**
328
     * Create the body of the email.
329
     */
330
    private function _createBody()
331
    {
332
        $lines = [];
333
        $mainBoundary = $this->boundary;
334
335
        // Cleanup body
336
        $this->body = '';
337
338
        // Add lines
339
        if (strpos($this->contentType, 'multipart') !== false) {
340
            $lines[] = 'This is a multi-part message in MIME format.';
341
            $lines[] = '';
342
        }
343
344
        if (in_array($this->contentType,
345
                    array(
346
                        'multipart/mixed',
347
                        'multipart/related',
348
                    )
349
                )
350
            ) {
351
            $lines[] = '--'.$mainBoundary;
352
            $this->boundary = '--=alternative='.self::createBoundary();
353
            $lines[] = 'Content-Type: multipart/alternative; boundary="'.$this->boundary.'"';
354
            $lines[] = '';
355
        }
356
357
        if (strpos($this->contentType, 'multipart') !== false) {
358
            // At least we have messageAlt and message
359
            if (!empty($this->messageAlt)) {
360
                // 1/2. messageAlt, supposed as plain text
361
                $lines[] = '--'.$this->boundary;
362
                $lines[] = 'Content-Type: text/plain; charset="'.$this->charset.'"';
363
                $lines[] = 'Content-Transfer-Encoding: '.$this->contentTransferEncoding;
364
                $lines[] = '';
365
                $lines[] = self::wrapLines(Utils::resolveMarkers($this->messageAlt, $this->_config));
366
                $lines[] = '';
367
            }
368
            // 2/2. message, supposed as, potentially, HTML
369
            $lines[] = '--'.$this->boundary;
370
            $lines[] = 'Content-Type: text/html; charset="'.$this->charset.'"';
371
            $lines[] = 'Content-Transfer-Encoding: '.$this->contentTransferEncoding;
372
            $lines[] = '';
373
            $lines[] = self::wrapLines($this->message);
374
            // Close the boundary delimiter
375
            $lines[] = '--'.$this->boundary.'--';
376
        } else {
377
            $lines[] = self::wrapLines($this->message);
378
        }
379
380
        if (in_array($this->contentType,
381
                    array(
382
                        'multipart/mixed',
383
                        'multipart/related',
384
                    )
385
                )
386
            ) {
387
            // Back to the main boundary
388
            $this->boundary = $mainBoundary;
389
            // Add the attachments
390
            foreach ($this->attachments as $attachment) {
391
                $lines[] = '--'.$this->boundary;
392
                $lines[] = 'Content-Type: '.$attachment['mimetype'].'; name="'.$attachment['name'].'"';
393
                $lines[] = 'Content-Transfer-Encoding: base64';
394
                if ('inline' == $attachment['disposition']) {
395
                    $lines[] = 'Content-ID: <'.$attachment['cid'].'>';
396
                }
397
                $lines[] = 'Content-Disposition: '.$attachment['disposition'].'; filename="'.$attachment['name'].'"';
398
                $lines[] = '';
399
                $lines[] = chunk_split(base64_encode(file_get_contents($attachment['path'])));
400
            }
401
            // Close the boundary delimiter
402
            $lines[] = '--'.$this->boundary.'--';
403
        }
404
405
        // Create the final body
406
        $this->body = '';
407
        foreach ($lines as $line) {
408
            $this->body .= $line.$this->eol;
409
        }
410
    }
411
412
    /**
413
     * Create the headers of the email.
414
     */
415
    private function _createHeaders()
416
    {
417
        // Cleanup headers
418
        $this->headers = [];
419
420
        // Check if the message consists of just a "plain" single item
421
        if (false === strpos($this->contentType, 'multipart')) {
422
            // Content-Disposition: inline
423
            $this->headers['Content-Disposition'] = $this->contentDisposition;
424
            // Content-Type
425
            $this->headers['Content-Type'] = $this->contentType.'; format=flowed; charset="'.$this->charset.'"';
426
            // Content-Transfer-Encoding: 7bit
427
            $this->headers['Content-Transfer-Encoding'] = '7bit';
428
        } else {
429
            // Content-Type
430
            $this->headers['Content-Type'] = $this->contentType.'; boundary="'.$this->boundary.'"';
431
        }
432
433
        // Date
434
        $this->headers['Date'] = self::getDate(self::getTime());
435
436
        // Disposition-Notification-To, RFC 3798
437
        $notifyTos = [];
438
        foreach ($this->_notifyTo as $address => $name) {
439
            $notifyTos[] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
440
        }
441
        $notifyTo = implode(',', $notifyTos);
442
        if (!empty($notifyTo)) {
443
            $this->headers['Disposition-Notification-To'] = $notifyTo;
444
        }
445
446
        // From
447
        foreach ($this->_from as $address => $name) {
448
            $this->headers['From'] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
449
        }
450
451
        // CC
452
        foreach ($this->_cc as $address => $name) {
453
            $this->headers['CC'] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
454
        }
455
456
        // BCC
457 View Code Duplication
        foreach ($this->_bcc as $address => $name) {
458
            $this->headers['BCC'] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
459
        }
460
461
        // Message-Id
462
        $this->headers['Message-ID'] = $this->messageId;
463
464
        // MIME-Version: 1.0
465
        $this->headers['MIME-Version'] = '1.0';
466
467
        // Reply-To
468
        $this->headers['Reply-To'] = $this->headers['From'];
469
        foreach ($this->_replyTo as $address => $name) {
470
            $this->headers['Reply-To'] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
471
        }
472
473
        // Return-Path
474
        foreach ($this->_from as $address => $name) {
475
            $this->headers['Return-Path'] = '<'.$address.'>';
476
        }
477
        foreach ($this->_returnPath as $address => $name) {
478
            $this->headers['Return-Path'] = '<'.$address.'>';
479
        }
480
481
        // Sender
482
        $this->headers['Sender'] = $this->headers['From'];
483
        foreach ($this->_sender as $address => $name) {
484
            $this->headers['Sender'] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
485
        }
486
487
        // Subject. Note: it must be RFC 2047 compliant
488
        // TODO: wrap mb_encode_mimeheader() to add other content encodings
489
        $this->headers['Subject'] = Utils::resolveMarkers(
490
            html_entity_decode($this->subject, ENT_COMPAT, 'UTF-8'),
491
            $this->_config
492
        );
493
494
        // X-Mailer
495
        $this->headers['X-Mailer'] = $this->_mailer;
496
497
        // X-MSMail-Priority
498
        if (isset($this->priorities[(int)$this->priority])) {
499
            $this->headers['X-MSMail-Priority'] = $this->priorities[(int)$this->priority];
500
        }
501
502
        // X-Originating-IP
503
        if (isset($_SERVER['REMOTE_ADDR'])) {
504
            $this->headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
505
        }
506
507
        // X-Priority
508
        $this->headers['X-Priority'] = $this->priority;
509
    }
510
511
    /**
512
     * Set just one e-mail address into an array.
513
     *
514
     * @param array  $target      Target array.
515
     * @param string $targetAlias Alias Target alias.
516
     * @param string $address     User e-mail address.
517
     * @param string $name        User name (optional).
518
     *
519
     * @return bool True if successful, false otherwise.
520
     */
521
    private function _setEmailTo(&$target, $targetAlias, $address, $name = null)
522
    {
523
        // Check for the permitted number of items into the $target array
524
        if (count($target) > 2) {
525
            $keys = array_keys($target);
526
            trigger_error(
527
                "<strong>Mail Class</strong>: a valid e-mail address, $keys[0], has been already added as '$targetAlias'!",
528
                E_USER_ERROR
529
            );
530
531
            return false;
532
        }
533
534
        return $this->_addEmailTo($target, $targetAlias, $address, $name);
535
    }
536
537
    /**
538
     * Add an attachment.
539
     *
540
     * @param string $path        File path.
541
     * @param string $name        File name. Defaults to the basename.
542
     * @param string $mimetype    File MIME type. Defaults to 'application/octet-stream'.
543
     * @param string $disposition Attachment disposition. Defaults to 'attachment'.
544
     * @param string $cid         Content ID, required when disposition is 'inline'. Defaults to ''.
545
     *
546
     * @return bool True if successful, false otherwise.
547
     */
548
    public function addAttachment($path, $name = null, $mimetype = 'application/octet-stream', $disposition = 'attachment', $cid = '')
549
    {
550
        if (!file_exists($path)) {
551
            // File not found
552
            return false;
553
        } elseif (('inline' == $disposition) && empty($cid)) {
554
            // Content ID is required
555
            return false;
556
        } else {
557
            if (empty($name)) {
558
                $name = basename($path);
559
            }
560
561
            $this->attachments[] = array(
562
                'cid' => $cid,
563
                'disposition' => $disposition,
564
                'mimetype' => $mimetype,
565
                'name' => $name,
566
                'path' => $path,
567
            );
568
569
            return true;
570
        }
571
    }
572
573
    /**
574
     * Add a recipient as <BCC>.
575
     *
576
     * @param string $address User e-mail address.
577
     * @param string $name    User name (optional).
578
     *
579
     * @return bool True if successful, false otherwise.
580
     */
581
    public function addBcc($address, $name = null)
582
    {
583
        return $this->_addEmailTo($this->_bcc, 'Bcc', $address, $name);
584
    }
585
586
    /**
587
     * Add a recipient as <CC>.
588
     *
589
     * @param string $address User e-mail address.
590
     * @param string $name    User name (optional).
591
     *
592
     * @return bool True if successful, false otherwise.
593
     */
594
    public function addCc($address, $name = null)
595
    {
596
        return $this->_addEmailTo($this->_cc, 'Cc', $address, $name);
597
    }
598
599
    /**
600
     * Add an address to send a notification to.
601
     *
602
     * @param string $address User e-mail address.
603
     * @param string $name    User name (optional).
604
     *
605
     * @return bool True if successful, false otherwise.
606
     */
607
    public function addNotificationTo($address, $name = null)
608
    {
609
        return $this->_addEmailTo($this->_notifyTo, 'Disposition-Notification-To', $address, $name);
610
    }
611
612
    /**
613
     * Add a recipient as <TO>.
614
     *
615
     * @param string $address User e-mail address.
616
     * @param string $name    User name (optional).
617
     *
618
     * @return bool True if successful, false otherwise.
619
     */
620
    public function addTo($address, $name = null)
621
    {
622
        return $this->_addEmailTo($this->_to, 'To', $address, $name);
623
    }
624
625
    /**
626
     * Create a string to be used as a valid boundary value.
627
     *
628
     * @static     
629
     *
630
     * @return string The boundary value.
631
     */
632
    public static function createBoundary()
633
    {
634
        return '-----'.md5(microtime());
635
    }
636
637
    /**
638
     * Returns the given text being sure that any CR or LF has been fixed
639
     * according with RFC 2822 EOL setting.
640
     *
641
     * @param string $text Text with a mixed usage of CR, LF, CRLF.
642
     *
643
     * @return string The fixed text.
644
     *
645
     * @see eol
646
     */
647
    public function fixEOL($text)
648
    {
649
        // Assure that anything among CRLF, CR will be replaced with just LF
650
        $text = str_replace(
651
            array(
652
                "\r\n", // CRLF
653
                "\r", // CR
654
                "\n", // LF
655
            ),
656
            "\n", // LF
657
            $text
658
        );
659
        // Set any LF to the RFC 2822 EOL
660
        $text = str_replace("\n", $this->eol, $text);
661
662
        return $text;
663
    }
664
665
    /**
666
     * Returns the date according with RFC 2822.
667
     *
668
     * @static
669
     *
670
     * @param string $date Unix timestamp.
671
     *
672
     * @return string The RFC 2822 date if successful, false otherwise.
673
     */
674
    public static function getDate($date)
675
    {
676
        $rfc2822Date = date('r', $date);
677
678
        return $rfc2822Date;
679
    }
680
681
    /**
682
     * Returns the Unix timestamp with preference to the Page Request time.
683
     *
684
     * @static
685
     *
686
     * @return int Unix timestamp.
687
     */
688
    public static function getTime()
689
    {
690
        if (isset($_SERVER['REQUEST_TIME'])) {
691
            return $_SERVER['REQUEST_TIME'];
692
        }
693
694
        return time();
695
    }
696
697
    /**
698
     * Get the instance of the class implementing the MUA for the given type.
699
     *
700
     * @static
701
     *
702
     * @param string $mua Type of the MUA.
703
     *
704
     * @return mixed The class instance if successful, false otherwise.
705
     */
706
    public static function getMUA($mua)
707
    {
708
        $impl = ucfirst(
709
            str_replace(
710
                '-',
711
                '',
712
                $mua
713
            )
714
        );
715
        $class = 'phpMyFAQ\Mail\\'.$impl;
716
717
        return new $class();
718
    }
719
720
    /**
721
     * Returns the server name.
722
     *
723
     * @static
724
     *
725
     * @return string The server name.
726
     */
727
    public static function getServerName()
728
    {
729
        $hostname = 'localhost.localdomain';
730
        if (isset($_SERVER['HTTP_HOST'])) {
731
            $hostname = $_SERVER['HTTP_HOST'];
732
        } elseif (isset($_SERVER['SERVER_NAME'])) {
733
            $hostname = $_SERVER['SERVER_NAME'];
734
        }
735
736
        return $hostname;
737
    }
738
739
    /**
740
     * Send the e-mail according with the current settings.
741
     *
742
     * @return bool True if successful, false otherwise.
743
     *
744
     * @todo   Enhance error handling using exceptions
745
     */
746
    public function send()
747
    {
748
        // Sanity check
749
        if (count($this->_to) + count($this->_cc) + count($this->_bcc) < 1) {
750
            trigger_error(
751
                '<strong>Mail Class</strong>: you need at least to set one recipient among TO, CC and BCC!',
752
                E_USER_ERROR
753
            );
754
755
            return false;
756
        }
757
758
        // Has any alternative message been provided?
759
        if (!empty($this->messageAlt)) {
760
            $this->contentType = 'multipart/alternative';
761
        }
762
763
        // Has any attachment been provided?
764
        if (!empty($this->attachments)) {
765
            $this->contentType = 'multipart/mixed';
766
        }
767
768
        // Has any in-line attachment been provided?
769
        $hasInlineAttachments = false;
770
        $idx = 0;
771
        while (!$hasInlineAttachments && ($idx < count($this->attachments))) {
772
            $hasInlineAttachments = ('inline' == $this->attachments[$idx]['disposition']);
773
            ++$idx;
774
        }
775
776
        if ($hasInlineAttachments) {
777
            $this->contentType = 'multipart/related';
778
        }
779
780
        // A valid MUA needs to implement the PMF_Mail_IMUA interface
781
        // i.e. we must prepare recipients, headers, body for the send() method
782
783
        // Prepare the recipients
784
        $to = [];
785 View Code Duplication
        foreach ($this->_to as $address => $name) {
786
            $to[] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
787
        }
788
        $recipients = implode(',', $to);
789
        // Check for the need of undisclosed recipients outlook-like <TO:>
790
        if (empty($recipients) && (0 == count($this->_cc))) {
791
            $recipients = '<Undisclosed-Recipient:;>';
792
        }
793
794
        // Prepare the headers
795
        $this->_createHeaders();
796
797
        // Prepare the body
798
        $this->_createBody();
799
800
        // Send the email adopting to the given MUA
801
        $mua = self::getMUA($this->agent);
802
803
        if (is_object($mua) && method_exists($mua, 'setAuthConfig')) {
804
            $mua->setAuthConfig(
805
                $this->_config->get('mail.remoteSMTPServer'),
806
                $this->_config->get('mail.remoteSMTPUsername'),
807
                $this->_config->get('mail.remoteSMTPPassword')
808
            );
809
        }
810
811
        switch ($this->agent) {
812
            case 'SwiftSMTP':
813
                $sent = $mua->send($this->_to, $this->headers, $this->body);
814
                break;
815
            case 'built-in':
816
                $sent = $mua->send($recipients, $this->headers, $this->body);
817
                break;
818
            default:
819
                trigger_error(
820
                    "<strong>Mail Class</strong>: $this->agent has no implementation!",
821
                    E_USER_ERROR
822
                );
823
                $sent = false;
824
        }
825
826
        return $sent;
827
    }
828
829
    /**
830
     * Set the "From" address.
831
     *
832
     * @param string $address User e-mail address.
833
     * @param string $name    User name (optional).
834
     *
835
     * @return bool True if successful, false otherwise.
836
     */
837
    public function setFrom($address, $name = null)
838
    {
839
        return $this->_setEmailTo($this->_from, 'From', $address, $name);
840
    }
841
842
    /**
843
     * Set an HTML message providing also a plain text alternative message,
844
     * if not already set using the $messageAlt property.
845
     * Besides it is possible to put resources as inline attachments.
846
     *
847
     * @param string $message  HTML message.
848
     * @param bool   $sanitize Strip out potentially unsecured HTML tags. Defaults to false.
849
     * @param bool   $inline   Add images as inline attachments. Defaults to false.
850
     */
851
    public function setHTMLMessage($message, $sanitize = false, $inline = false)
852
    {
853
        // No Javascript at all
854
        // 1/2. <script blahblahblah>blahblahblah</tag>
855
        $message = Strings::preg_replace(
856
            '/(<script[^>]*>.*<\/script>)|<script[^\/]*\/>|<script[^\/]*>/is',
857
            '',
858
            $message
859
        );
860
861
        // Cleanup potentially dangerous HTML tags:
862
        if ($sanitize && !is_null($message)) {
863
            // 1/2. <tag blahblahblah>blahblahblah</tag>
864
            $message = Strings::preg_replace(
865
                '/<(applet|embed|head|meta|object|style|title)[^>]*>.*<\/\\1>/is',
866
                '',
867
                $message
868
            );
869
            // 2/2. <tag blahblahblah />
870
            $message = Strings::preg_replace(
871
                '/<(applet|embed|head|meta|object|style|title)[^\/]*\/>/is',
872
                '',
873
                $message
0 ignored issues
show
Bug introduced by
It seems like $message defined by \phpMyFAQ\Strings::preg_...\\/>/is', '', $message) on line 870 can also be of type null; however, phpMyFAQ\Strings::preg_replace() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
874
            );
875
        }
876
877
        if ($inline) {
878
            trigger_error(
879
                '<strong>Mail Class</strong>: inline option is not implemented yet.',
880
                E_USER_ERROR
881
            );
882
        }
883
884
        // Set the HTML text as the main message
885
        $this->message = trim($message);
886
887
        // If no alternative text has been provided yet, use just
888
        // the HTML message stripping any HTML tag
889
        if (empty($this->messageAlt)) {
890
            $this->messageAlt = trim(strip_tags($this->message));
891
        }
892
    }
893
894
    /**
895
     * Set the "Reply-to" address.
896
     *
897
     * @param string $address User e-mail address.
898
     * @param string $name    User name (optional).
899
     *
900
     * @return bool True if successful, false otherwise.
901
     */
902
    public function setReplyTo($address, $name = null)
903
    {
904
        return $this->_setEmailTo($this->_replyTo, 'Reply-To', $address, $name);
905
    }
906
907
    /**
908
     * Set the "Return-Path" address.
909
     *
910
     * @param string $address User e-mail address.
911
     *
912
     * @return bool True if successful, false otherwise.
913
     */
914
    public function setReturnPath($address)
915
    {
916
        return $this->_setEmailTo($this->_returnPath, 'Return-Path', $address);
917
    }
918
919
    /**
920
     * Set the "Sender" address.
921
     *
922
     * @param string $address User e-mail address.
923
     * @param string $name    User name (optional).
924
     *
925
     * @return bool True if successful, false otherwise.
926
     */
927
    public function setSender($address, $name = null)
928
    {
929
        return $this->_setEmailTo($this->_sender, 'Sender', $address, $name);
930
    }
931
932
    /**
933
     * Remove any previous "From" address.
934
     *
935
     * @return bool True if successful, false otherwise.
936
     */
937
    public function unsetFrom()
938
    {
939
        $this->_from = [];
940
941
        return true;
942
    }
943
944
    /**
945
     * Validate an address as an e-mail address.
946
     *
947
     * @param string $address E-Mail address
948
     *
949
     * @return bool True if the given address is a valid e-mail address, false otherwise.     
950
     */
951
    public static function validateEmail($address)
952
    {
953
        if (empty($address)) {
954
            return false;
955
        }
956
957
        if (Strings::strpos($address, '\0') !== false) {
958
            return false;
959
        }
960
961
        $unsafe = array("\r", "\n");
962
        if ($address !== str_replace($unsafe, '', $address)) {
963
            return false;
964
        }
965
966
        if (false === filter_var($address, FILTER_VALIDATE_EMAIL)) {
967
            return false;
968
        }
969
970
        return true;
971
    }
972
973
    /**
974
     * Wraps the lines contained into the given message.
975
     *
976
     * @param string $message Message.
977
     * @param int    $width   Column width. Defaults to 72.
978
     * @param bool   $cut     Cutting a word is allowed. Defaults to false.
979
     *
980
     * @return string The given message, wrapped as requested.
981
     */
982
    public function wrapLines($message, $width = 72, $cut = false)
983
    {
984
        $message = $this->fixEOL($message);
985
986
        if (Strings::strpos(strtolower($this->charset), 'utf') !== false) {
987
            // PHP wordwrap() is not safe with multibyte UTF chars
988
            return $message;
989
        } else {
990
            $lines = explode($this->eol, $message);
991
            $wrapped = '';
992
            foreach ($lines as $value) {
993
                $wrapped .= (empty($wrapped) ? '' : $this->eol);
994
                $wrapped .= wordwrap($value, $width, $this->eol, $cut);
995
            }
996
997
            return $wrapped;
998
        }
999
    }
1000
1001
    /**
1002
     * If the email spam protection has been activated from the general 
1003
     * phpMyFAQ configuration this method converts an email address e.g. 
1004
     * from "[email protected]" to "user_AT_example_DOT_org". Otherwise 
1005
     * it will return the plain email address.
1006
     *
1007
     * @param string $email E-mail address
1008
     * @static
1009
     *
1010
     * @return string
1011
     */
1012
    public function safeEmail($email)
1013
    {
1014
        if ($this->_config->get('spam.enableSafeEmail')) {
1015
            return str_replace(array('@', '.'), array('_AT_', '_DOT_'), $email);
1016
        } else {
1017
            return $email;
1018
        }
1019
    }
1020
}
1021