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.
Completed
Pull Request — master (#1192)
by
unknown
14:48
created

PMF_Mail::_createHeaders()   F

Complexity

Conditions 19
Paths > 20000

Size

Total Lines 95
Code Lines 42

Duplication

Lines 3
Ratio 3.16 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
dl 3
loc 95
rs 2
c 1
b 1
f 0
cc 19
eloc 42
nc 46656
nop 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * MUA (Mail User Agent) implementation.
4
 *
5
 * PHP Version 5.4
6
 *
7
 * This Source Code Form is subject to the terms of the Mozilla Public License,
8
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
9
 * obtain one at http://mozilla.org/MPL/2.0/.
10
 *
11
 * @category  phpMyFAQ
12
 * @package   Mail
13
 * @author    Matteo Scaramuccia <[email protected]>
14
 * @author    Thorsten Rinne <[email protected]>
15
 * @copyright 2009-2014 phpMyFAQ Team
16
 * @license   http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
17
 * @link      http://www.phpmyfaq.de
18
 * @since     2009-09-11
19
 */
20
21
if (!defined('IS_VALID_PHPMYFAQ')) {
22
    exit();
23
}
24
25
/**
26
 * Mail
27
 *
28
 * @category  phpMyFAQ
29
 * @package   Mail
30
 * @author    Matteo Scaramuccia <[email protected]>
31
 * @author    Thorsten Rinne <[email protected]>
32
 * @copyright 2009-2014 phpMyFAQ Team
33
 * @license   http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
34
 * @link      http://www.phpmyfaq.de
35
 * @since     2009-09-11
36
 */ 
37
class PMF_Mail
38
{
39
    /**
40
     * Type of the used MUA. Possible values:
41
     * - built-in.     
42
     *
43
     * @var string $agent
44
     */
45
    public $agent;
46
47
    /**
48
     * Attached filed.
49
     *
50
     * @var mixed $attachments
51
     */
52
    public $attachments;
53
54
    /**
55
     * Body of the e-mail.
56
     *
57
     * @var string $body
58
     */
59
    public $body = '';
60
61
    /**
62
     * Boundary.
63
     *
64
     * @var string $boundary
65
     */
66
    public $boundary = '----------';
67
68
    /**
69
     * Charset.
70
     *
71
     * @var string $charset
72
     */
73
    public $charset = 'utf-8';
74
75
    /**
76
     * Content disposition.
77
     *
78
     * @var string $contentDisposition
79
     */
80
    public $contentDisposition = 'inline';
81
82
    /**
83
     * Content type.
84
     *
85
     * @var string $contentType
86
     */
87
    public $contentType = 'text/plain';
88
89
    /**
90
     * Content transfer encoding.
91
     *
92
     * @var string $contentTransferEncoding
93
     */
94
    public $contentTransferEncoding = '8bit';
95
96
    /**
97
     * The one and only valid End Of Line sequence as per RFC 2822:
98
     * carriage-return followed by line-feed.     
99
     *
100
     * @var string $eol
101
     */
102
    public $eol = "\r\n";
103
104
    /**
105
     * Headers of the e-mail.
106
     *
107
     * @var string $headers
108
     */
109
    public $headers;
110
111
    /**
112
     * Message of the e-mail: HTML text allowed.
113
     *
114
     * @var string $message
115
     */
116
    public $message;
117
118
    /**
119
     * Alternate message of the e-mail: only plain text allowed.
120
     *
121
     * @var string $messageAlt
122
     */
123
    public $messageAlt;
124
125
    /**
126
     * Message-ID of the e-mail.
127
     *
128
     * @var string $messageId
129
     */
130
    public $messageId;
131
132
    /**
133
     * Priorities: 1 (Highest), 2 (High), 3 (Normal), 4 (Low), 5 (Lowest).
134
     *
135
     * @var mixed $priorities
136
     */
137
    public $priorities = array(
138
        1 => 'Highest',
139
        2 => 'High',
140
        3 => 'Normal',
141
        4 => 'Low',
142
        5 => 'Lowest');
143
144
    /**
145
     * Priority of the e-mail: 1 (Highest), 2 (High), 3 (Normal), 4 (Low), 5 (Lowest).
146
     *
147
     * @var int $priority
148
     * @see priorities     
149
     */
150
    public $priority;
151
152
    /**
153
     * Subject of the e-mail.
154
     *
155
     * @var string $subject
156
     */
157
    public $subject;
158
159
    /**
160
     * Recipients of the e-mail as <BCC>.
161
     *
162
     * @var mixed $_bcc
163
     */
164
    private $_bcc;
165
166
    /**
167
     * Recipients of the e-mail as <CC>.
168
     *
169
     * @var mixed $_cc
170
     */
171
    private $_cc;
172
173
    /**
174
     * Recipients of the e-mail as <From>.
175
     *
176
     * @var mixed $_from
177
     */
178
    private $_from;
179
180
    /**
181
     * Mailer string.
182
     *
183
     * @var string $_mailer
184
     */
185
    private $_mailer;
186
187
    /**
188
     * Recipient of the optional notification.
189
     *
190
     * @var mixed $_notifyTo
191
     */
192
    private $_notifyTo;
193
194
    /**
195
     * Recipient of the e-mail as <Reply-To>.
196
     *
197
     * @var mixed $_replyTo
198
     */
199
    private $_replyTo;
200
201
    /**
202
     * Recipient of the e-mail as <Return-Path>.
203
     *
204
     * @var mixed $_returnPath
205
     */
206
    private $_returnPath;
207
208
    /**
209
     * Recipient of the e-mail as <Sender>.
210
     *
211
     * @var mixed $_sender
212
     */
213
    private $_sender;
214
215
    /**
216
     * Recipients of the e-mail as <TO:>.
217
     *
218
     * @var mixed $_to
219
     */
220
    private $_to;
221
222
    /**
223
     * @var PMF_Configuration
224
     */
225
    private $_config;
226
227
    /*
228
     * Default constructor.
229
     * Note: any email will be sent from the PMF administrator, use unsetFrom
230
     *       before using setFrom.     
231
     *
232
     * @param Configuration $config
233
     */     
234
    function __construct(PMF_Configuration $config)
235
    {
236
        // Set default value for public properties
237
        $this->agent       = 'built-in';
238
        $this->attachments = [];
239
        $this->boundary    = self::createBoundary();
240
        $this->headers     = [];
241
        $this->message     = '';
242
        $this->messageAlt  = '';
243
        $this->messageId   = '<'.$_SERVER['REQUEST_TIME'] . '.'. md5(microtime()) . '@' . self::getServerName() . '>';
244
        $this->priority    = 3; // 3 -> Normal
245
        $this->subject     = '';
246
247
        // Set default value for private properties
248
        $this->_config     = $config;
249
        $this->_bcc        = [];
250
        $this->_cc         = [];
251
        $this->_from       = [];
252
        $this->_mailer     = 'phpMyFAQ on PHP/' . PHP_VERSION;
253
        $this->_notifyTo   = [];
254
        $this->_replyTo    = [];
255
        $this->_returnPath = [];
256
        $this->_sender     = [];
257
        $this->_to         = [];
258
259
        // Set phpMyFAQ related data
260
        $this->_mailer = 'phpMyFAQ/' . $this->_config->get('main.currentVersion');
261
        $this->setFrom($this->_config->get('main.administrationMail'));
262
    }
263
264
    /**
265
     * Add an e-mail address to an array.
266
     *
267
     * @param  array  $target      Target array.
268
     * @param  string $targetAlias Alias Target alias.
269
     * @param  string $address     User e-mail address.
270
     * @param  string $name        User name (optional).
271
     *
272
     * @return bool   True if successful, false otherwise.
273
     *
274
     * @todo   Enhance error handling using exceptions
275
     */          
276
    private function _addEmailTo(&$target, $targetAlias, $address, $name = null)
277
    {
278
        // Sanity check
279
        if (!self::validateEmail($address)) {
280
            trigger_error(
281
                "<strong>Mail Class</strong>: Value '$address' is not a valid e-mail address!",
282
                E_USER_ERROR
283
            );
284
            return false;
285
        }
286
287
        // Don't allow duplicated addresses
288
        if (array_key_exists($address, $target)) {
289
            trigger_error(
290
                "<strong>Mail Class</strong>: $address has been already added in '$targetAlias'!",
291
                E_USER_WARNING
292
            );
293
            return false;
294
        }
295
296
        if (!empty($name)) {
297
            // Remove CR and LF characters to prevent header injection
298
            $name = str_replace(array("\n", "\r"), '', $name);
299
            
300
            if (function_exists('mb_encode_mimeheader')) {
301
                // Encode any special characters in the displayed name
302
                $name = mb_encode_mimeheader($name);
303
            }
304
            
305
            // Wrap the displayed name in quotes (to fix problems with commas etc),
306
            // and escape any existing quotes
307
            $name = '"' . str_replace('"', '\"', $name) . '"';
308
        }
309
310
        // Add the e-mail address into the target array
311
        $target[$address] = $name;
312
        // On Windows, when using PHP built-in mail drop any name, just use the e-mail address
313
        if (('WIN' === strtoupper(substr(PHP_OS, 0, 3))) && ('built-in' == $this->agent)) {
314
            $target[$address] = null;
315
        }
316
317
        return true;
318
    }
319
320
    /**
321
     * Create the body of the email.
322
     *
323
     * @return void     
324
     */          
325
    private function _createBody()
326
    {
327
        $lines        = [];
328
        $mainBoundary = $this->boundary;
329
330
        // Cleanup body
331
        $this->body = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $body.

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...
332
333
        // Add lines
334
        if (strpos($this->contentType, 'multipart') !== false) {
335
            $lines[] = 'This is a multi-part message in MIME format.';
336
            $lines[] = '';
337
        }
338
339
        if (in_array($this->contentType,
340
                    array(
341
                        'multipart/mixed',
342
                        'multipart/related'
343
                    )
344
                )
345
            ) {
346
            $lines[] = '--'.$mainBoundary;
347
            $this->boundary = "--=alternative=".self::createBoundary();
348
            $lines[] = 'Content-Type: multipart/alternative; boundary="'.$this->boundary.'"';
349
            $lines[] = '';
350
        }
351
352
        if (strpos($this->contentType, 'multipart') !== false) {
353
            // At least we have messageAlt and message
354
            if (!empty($this->messageAlt)) {
355
                // 1/2. messageAlt, supposed as plain text
356
                $lines[] = '--'.$this->boundary;
357
                $lines[] = 'Content-Type: text/plain; charset="'.$this->charset.'"';
358
                $lines[] = 'Content-Transfer-Encoding: '.$this->contentTransferEncoding;
359
                $lines[] = '';
360
                $lines[] = self::wrapLines(PMF_Utils::resolveMarkers($this->messageAlt, $this->_config));
361
                $lines[] = '';
362
            }
363
            // 2/2. message, supposed as, potentially, HTML
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
364
            $lines[] = '--'.$this->boundary;
365
            $lines[] = 'Content-Type: text/html; charset="'.$this->charset.'"';
366
            $lines[] = 'Content-Transfer-Encoding: '.$this->contentTransferEncoding;
367
            $lines[] = '';
368
            $lines[] = self::wrapLines($this->message);
369
            // Close the boundary delimiter
370
            $lines[] = '--'.$this->boundary.'--';
371
        } else {
372
            $lines[] = self::wrapLines($this->message);
373
        }
374
375
        if (in_array($this->contentType,
376
                    array(
377
                        'multipart/mixed',
378
                        'multipart/related'
379
                    )
380
                )
381
            ) {
382
            // Back to the main boundary
383
            $this->boundary = $mainBoundary;
384
            // Add the attachments
385
            foreach ($this->attachments as $attachment) {
386
                $lines[] = '--'.$this->boundary;
387
                $lines[] = 'Content-Type: '.$attachment['mimetype'].'; name="'.$attachment['name'].'"'; 
388
                $lines[] = 'Content-Transfer-Encoding: base64';
389
                if ('inline' == $attachment['disposition']) {
390
                    $lines[] = 'Content-ID: <'.$attachment['cid'].'>';
391
                }
392
                $lines[] = 'Content-Disposition: '.$attachment['disposition'].'; filename="'.$attachment['name'].'"';
393
                $lines[] = '';
394
                $lines[] = chunk_split(base64_encode(file_get_contents($attachment['path'])));
395
            }
396
            // Close the boundary delimiter
397
            $lines[] = '--'.$this->boundary.'--';
398
        }
399
400
        // Create the final body
401
        $this->body = '';
402
        foreach ($lines as $line) {
403
            $this->body .= $line.$this->eol;
404
        }
405
    }
406
407
    /**
408
     * Create the headers of the email.
409
     *
410
     * @return void     
411
     */          
412
    private function _createHeaders()
413
    {
414
        // Cleanup headers
415
        $this->headers = [];
416
417
        // Check if the message consists of just a "plain" single item
418
        if (false === strpos($this->contentType, 'multipart')) {
419
            // Content-Disposition: inline
420
            $this->headers['Content-Disposition'] = $this->contentDisposition;
421
            // Content-Type
422
            $this->headers['Content-Type'] = $this->contentType.'; format=flowed; charset="'.$this->charset.'"';
423
            // Content-Transfer-Encoding: 7bit
424
            $this->headers['Content-Transfer-Encoding'] = '7bit';
425
        } else {
426
            // Content-Type
427
            $this->headers['Content-Type'] = $this->contentType.'; boundary="'.$this->boundary.'"';
428
        }
429
430
        // Date
431
        $this->headers['Date'] = self::getDate(self::getTime());
432
433
        // Disposition-Notification-To, RFC 3798
434
        $notifyTos = [];
435
        foreach($this->_notifyTo as $address => $name) {
436
            $notifyTos[] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
437
        }
438
        $notifyTo = implode(',', $notifyTos);
439
        if (!empty($notifyTo)) {
440
            $this->headers['Disposition-Notification-To'] = $notifyTo;
441
        }
442
443
        // From
444
        foreach ($this->_from as $address => $name) {
445
            $this->headers['From'] = (empty($name) ? '' : $name.' ') . '<' . $address . '>';
446
        }
447
448
        // CC
449
        foreach ($this->_cc as $address => $name) {
450
            $this->headers['CC'] = (empty($name) ? '' : $name.' ') . '<' . $address . '>';
451
        }
452
453
        // BCC
454 View Code Duplication
        foreach ($this->_bcc as $address => $name) {
455
            $this->headers['BCC'] = (empty($name) ? '' : $name.' ') . '<' . $address . '>';
456
        }
457
458
        // Message-Id
459
        $this->headers['Message-ID'] = $this->messageId;
460
461
        // MIME-Version: 1.0
462
        $this->headers['MIME-Version'] = '1.0';
463
464
        // Reply-To
465
        $this->headers['Reply-To'] = $this->headers['From'];
466
        foreach($this->_replyTo as $address => $name) {
467
            $this->headers['Reply-To'] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
468
        }
469
470
        // Return-Path
471
        foreach($this->_from as $address => $name) {
472
            $this->headers['Return-Path'] = '<'.$address.'>';
473
        }
474
        foreach($this->_returnPath as $address => $name) {
475
            $this->headers['Return-Path'] = '<'.$address.'>';
476
        }
477
478
        // Sender
479
        $this->headers['Sender'] = $this->headers['From'];
480
        foreach($this->_sender as $address => $name) {
481
            $this->headers['Sender'] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
482
        }
483
484
        // Subject. Note: it must be RFC 2047 compliant
485
        // TODO: wrap mb_encode_mimeheader() to add other content encodings
486
        $this->headers['Subject'] = PMF_Utils::resolveMarkers(
487
            html_entity_decode($this->subject, ENT_COMPAT, 'UTF-8'),
488
            $this->_config
489
        );
490
491
        // X-Mailer
492
        $this->headers['X-Mailer'] = $this->_mailer;
493
494
        // X-MSMail-Priority
495
        if (isset($this->priorities[(int)$this->priority])) {
496
            $this->headers['X-MSMail-Priority'] = $this->priorities[(int)$this->priority];
497
        }
498
499
        // X-Originating-IP
500
        if (isset($_SERVER['REMOTE_ADDR'])) {
501
            $this->headers['X-Originating-IP'] = $_SERVER['REMOTE_ADDR'];
502
        }
503
504
        // X-Priority
505
        $this->headers['X-Priority'] = $this->priority;
506
    }
507
508
    /**
509
     * Set just one e-mail address into an array.
510
     *
511
     * @param  array  $target      Target array.
512
     * @param  string $targetAlias Alias Target alias.
513
     * @param  string $address     User e-mail address.
514
     * @param  string $name        User name (optional).
515
     *
516
     * @return bool   True if successful, false otherwise.
517
     */          
518
    private function _setEmailTo(&$target, $targetAlias, $address, $name = null)
519
    {
520
        // Check for the permitted number of items into the $target array
521
        if (count($target) > 0) {
522
            $keys = array_keys($target);
523
            trigger_error(
524
                "<strong>Mail Class</strong>: a valid e-mail address, $keys[0], has been already added as '$targetAlias'!",
525
                E_USER_ERROR
526
            );
527
            return false;
528
        }
529
        
530
        return $this->_addEmailTo($target, $targetAlias, $address, $name);
531
    }
532
533
    /**
534
     * Add an attachment.
535
     *
536
     * @param  string $path        File path.
537
     * @param  string $name        File name. Defaults to the basename.
538
     * @param  string $mimetype    File MIME type. Defaults to 'application/octet-stream'.
539
     * @param  string $disposition Attachment disposition. Defaults to 'attachment'.
540
     * @param  string $cid         Content ID, required when disposition is 'inline'. Defaults to ''.
541
     * @return bool   True if successful, false otherwise.
542
     */          
543
    public function addAttachment($path, $name = null, $mimetype = 'application/octet-stream', $disposition = 'attachment', $cid = '')
544
    {
545
        if (!file_exists($path)) {
546
            // File not found
547
            return false;
548
        } else if (('inline' == $disposition) && empty($cid)) {
549
            // Content ID is required
550
            return false;
551
        } else {
552
            if (empty($name)) {
553
                $name = basename($path);
554
            }
555
556
            $this->attachments[] = array(
557
                "cid"           => $cid,
558
                "disposition"   => $disposition,
559
                "mimetype"      => $mimetype,
560
                "name"          => $name,
561
                "path"          => $path
562
            );
563
564
            return true;
565
        }
566
    }
567
568
    /**
569
     * Add a recipient as <BCC>.
570
     *
571
     * @param  string $address User e-mail address.
572
     * @param  string $name    User name (optional).
573
     * @return bool True if successful, false otherwise.
574
     */          
575
    public function addBcc($address, $name = null)
576
    {
577
        return $this->_addEmailTo($this->_bcc, 'Bcc', $address, $name);
578
    }
579
580
    /**
581
     * Add a recipient as <CC>.
582
     *
583
     * @param  string $address User e-mail address.
584
     * @param  string $name    User name (optional).
585
     * @return bool True if successful, false otherwise.
586
     */          
587
    public function addCc($address, $name = null)
588
    {
589
        return $this->_addEmailTo($this->_cc, 'Cc', $address, $name);
590
    }
591
592
    /**
593
     * Add an address to send a notification to.
594
     *
595
     * @param  string $address User e-mail address.
596
     * @param  string $name    User name (optional).
597
     * @return bool True if successful, false otherwise.
598
     */          
599
    public function addNotificationTo($address, $name = null)
600
    {
601
        return $this->_addEmailTo($this->_notifyTo, 'Disposition-Notification-To', $address, $name);
602
    }
603
604
    /**
605
     * Add a recipient as <TO>.
606
     *
607
     * @param  string $address User e-mail address.
608
     * @param  string $name    User name (optional).
609
     * @return bool True if successful, false otherwise.
610
     */          
611
    public function addTo($address, $name = null)
612
    {
613
        return $this->_addEmailTo($this->_to, 'To', $address, $name);
614
    }
615
616
    /**
617
     * Create a string to be used as a valid boundary value.
618
     *
619
     * @static     
620
     * @return string The boundary value.
621
     */          
622
    public static function createBoundary()
623
    {
624
        return '-----'  .md5(microtime());
625
    }
626
627
    /**
628
     * Returns the given text being sure that any CR or LF has been fixed
629
     * according with RFC 2822 EOL setting.
630
     *
631
     * @param  string $text Text with a mixed usage of CR, LF, CRLF.
632
     * @return string The fixed text.
633
     * @see eol
634
     */          
635
    public function fixEOL($text)
636
    {
637
        // Assure that anything among CRLF, CR will be replaced with just LF
638
        $text = str_replace(
639
            array(
640
                "\r\n",// CRLF
641
                "\r", // CR
642
                "\n",// LF
643
            ),
644
            "\n", // LF
645
            $text
646
        );
647
        // Set any LF to the RFC 2822 EOL
648
        $text = str_replace("\n", $this->eol, $text);
649
650
        return $text;
651
    }
652
653
    /**
654
     * Returns the date according with RFC 2822.
655
     *
656
     * @static
657
     * @param  string $date Unix timestamp.
658
     * @return string The RFC 2822 date if successful, false otherwise.
659
     */          
660
    public static function getDate($date)
661
    {
662
        $rfc2822Date = date('r', $date);
663
664
        return $rfc2822Date;
665
    }
666
667
    /**
668
     * Returns the Unix timestamp with preference to the Page Request time.
669
     *
670
     * @static
671
     * @return int Unix timestamp.
672
     */          
673
    public static function getTime()
674
    {
675
        if (isset($_SERVER['REQUEST_TIME'])) {
676
            return $_SERVER['REQUEST_TIME'];
677
        }
678
679
        return time();
680
    }
681
682
    /**
683
     * Get the instance of the class implementing the MUA for the given type.
684
     *
685
     * @static
686
     * @param string $mua Type of the MUA.
687
     *
688
     * @return mixed The class instance if successful, false otherwise.
689
     */          
690
    public static function getMUA($mua)
691
    {
692
        $impl = ucfirst(
693
            str_replace(
694
                '-',
695
                '',
696
                $mua
697
            )
698
        );
699
        $class = 'PMF_Mail_'.$impl;
700
701
        return new $class;
702
    }
703
704
    /**
705
     * Returns the server name.
706
     *
707
     * @static
708
     * @return string The server name.
709
     */          
710
    public static function getServerName()
711
    {
712
        $hostname = 'localhost.localdomain';
713 View Code Duplication
        if (isset($_SERVER['HTTP_HOST'])) {
714
            $hostname = $_SERVER['HTTP_HOST'];
715
        } else if (isset($_SERVER['SERVER_NAME'])) {
716
            $hostname = $_SERVER['SERVER_NAME'];
717
        }
718
719
        return $hostname;
720
    }
721
722
    /**
723
     * Send the e-mail according with the current settings.
724
     *
725
     * @return bool True if successful, false otherwise.
726
     *
727
     * @todo   Enhance error handling using exceptions
728
     */          
729
    public function send()
730
    {
731
        // Sanity check
732
        if (count($this->_to) + count($this->_cc) + count($this->_bcc) < 1) {
733
            trigger_error(
734
                "<strong>Mail Class</strong>: you need at least to set one recipient among TO, CC and BCC!",
735
                E_USER_ERROR
736
            );
737
            return false;
738
        }
739
740
        // Has any alternative message been provided?
741
        if (!empty($this->messageAlt)) {
742
            $this->contentType = 'multipart/alternative';
743
        }
744
745
        // Has any attachment been provided?
746
        if (!empty($this->attachments)) {
747
            $this->contentType = 'multipart/mixed';
748
        }
749
750
        // Has any in-line attachment been provided?
751
        $hasInlineAttachments = false;
752
        $idx = 0;
753
        while (!$hasInlineAttachments && ($idx < count($this->attachments))) {
754
            $hasInlineAttachments = ('inline' == $this->attachments[$idx]['disposition']);
755
            $idx++;
756
        }
757
758
        if ($hasInlineAttachments) {
759
            $this->contentType = 'multipart/related';
760
        }
761
762
        // A valid MUA needs to implement the PMF_Mail_IMUA interface
763
        // i.e. we must prepare recipients, headers, body for the send() method
764
765
        // Prepare the recipients
766
        $to = [];
767 View Code Duplication
        foreach($this->_to as $address => $name) {
768
            $to[] = (empty($name) ? '' : $name.' ').'<'.$address.'>';
769
        }
770
        $recipients = implode(',', $to);
771
        // Check for the need of undisclosed recipients outlook-like <TO:>
772
        if (empty($recipients) && (0 == count($this->_cc))) {
773
            $recipients = '<Undisclosed-Recipient:;>';
774
        }
775
776
        // Prepare the headers
777
        $this->_createHeaders();
778
779
        // Prepare the body
780
        $this->_createBody();
781
782
        // Send the email adopting to the given MUA
783
        $mua  = self::getMUA($this->agent);
784
        switch ($this->agent) {
785
            case 'built-in':
786
                $sent = $mua->send($recipients, $this->headers, $this->body);
787
                break;
788
            default:
789
                trigger_error(
790
                    "<strong>Mail Class</strong>: $this->agent has no implementation!",
791
                    E_USER_ERROR
792
                );
793
                $sent = false;
794
        }
795
796
        return $sent;
797
    }
798
799
    /**
800
     * Set the "From" address.
801
     *
802
     * @param  string $address User e-mail address.
803
     * @param  string $name User name (optional).
804
     * @return bool   True if successful, false otherwise.
805
     */          
806
    public function setFrom($address, $name = null)
807
    {
808
        return $this->_setEmailTo($this->_from, 'From', $address, $name);
809
    }
810
811
    /**
812
     * Set an HTML message providing also a plain text alternative message,
813
     * if not already set using the $messageAlt property.
814
     * Besides it is possible to put resources as inline attachments
815
     *
816
     * @param  string $message     HTML message.
817
     * @param  bool   $sanitize Strip out potentially unsecured HTML tags. Defaults to false.
818
     * @param  bool   $inline   Add images as inline attachments. Defaults to false.
819
     *
820
     * @return void
821
     */          
822
    public function setHTMLMessage($message, $sanitize = false, $inline = false)
823
    {
824
        // No Javascript at all
825
        // 1/2. <script blahblahblah>blahblahblah</tag>
826
        $message = PMF_String::preg_replace(
827
            '/(<script[^>]*>.*<\/script>)|<script[^\/]*\/>|<script[^\/]*>/is',
828
            '',
829
            $message
830
        );
831
832
        // Cleanup potentially dangerous HTML tags:
833
        if ($sanitize) {
834
            // 1/2. <tag blahblahblah>blahblahblah</tag>
835
            $message = PMF_String::preg_replace(
836
                '/<(applet|embed|head|meta|object|style|title)[^>]*>.*<\/\\1>/is',
837
                '',
838
                $message
0 ignored issues
show
Bug introduced by
It seems like $message defined by \PMF_String::preg_replac...\\1>/is', '', $message) on line 835 can also be of type null; however, PMF_String::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...
839
            );
840
            // 2/2. <tag blahblahblah />
841
            $message = PMF_String::preg_replace(
842
                '/<(applet|embed|head|meta|object|style|title)[^\/]*\/>/is',
843
                '',
844
                $message
0 ignored issues
show
Bug introduced by
It seems like $message defined by \PMF_String::preg_replac...\\/>/is', '', $message) on line 841 can also be of type null; however, PMF_String::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...
845
            );
846
        }
847
848
        if ($inline) {
849
            trigger_error(
850
                "<strong>Mail Class</strong>: inline option is not implemented yet.",
851
                E_USER_ERROR
852
            );
853
        }
854
855
        // Set the HTML text as the main message
856
        $this->message = trim($message);
857
858
        // If no alternative text has been provided yet, use just
859
        // the HTML message stripping any HTML tag
860
        if (empty($this->messageAlt)) {
861
            $this->messageAlt = trim(strip_tags($this->message));
862
        }
863
    }
864
865
    /**
866
     * Set the "Reply-to" address.
867
     *
868
     * @param string $address User e-mail address.
869
     * @param string $name User name (optional).
870
     * @return bool True if successful, false otherwise.
871
     */          
872
    public function setReplyTo($address, $name = null)
873
    {
874
        return $this->_setEmailTo($this->_replyTo, 'Reply-To', $address, $name);
875
    }
876
877
    /**
878
     * Set the "Return-Path" address.
879
     *
880
     * @param  string $address User e-mail address.
881
     * @return bool   True if successful, false otherwise.
882
     */          
883
    public function setReturnPath($address)
884
    {
885
        return $this->_setEmailTo($this->_returnPath, 'Return-Path', $address);
886
    }
887
888
    /**
889
     * Set the "Sender" address.
890
     *
891
     * @param  string $address User e-mail address.
892
     * @param  string $name    User name (optional).
893
     * @return bool True if successful, false otherwise.
894
     */          
895
    public function setSender($address, $name = null)
896
    {
897
        return $this->_setEmailTo($this->_sender, 'Sender', $address, $name);
898
    }
899
900
    /**
901
     * Remove any previous "From" address.
902
     *
903
     * @return bool True if successful, false otherwise.
904
     */          
905
    public function unsetFrom()
906
    {
907
        $this->_from = [];
908
909
        return true;
910
    }
911
912
    /**
913
     * Validate an address as an e-mail address.
914
     *
915
     * @param string $address E-Mail address
916
     *
917
     * @return bool True if the given address is a valid e-mail address, false otherwise.     
918
     */
919
    public static function validateEmail($address)
920
    {
921
        if (empty($address)) {
922
            return false;
923
        }
924
        
925
        if (PMF_String::strpos($address, '\0') !== false) {
926
            return false;
927
        }
928
        
929
        $unsafe = array ("\r", "\n");
930
        if ($address !== str_replace($unsafe, '', $address)) {
931
            return false;
932
        }
933
        
934
        if (false === filter_var($address, FILTER_VALIDATE_EMAIL)) {
935
          return false;
936
        }
937
        
938
        return true;
939
    }
940
941
    /**
942
     * Wraps the lines contained into the given message.
943
     *
944
     * @param  string  $message Message.
945
     * @param  integer $width   Column width. Defaults to 72.
946
     * @param  boolean $cut     Cutting a word is allowed. Defaults to false.
947
     *
948
     * @return string The given message, wrapped as requested.
949
     */          
950
    public function wrapLines($message, $width = 72, $cut = false)
951
    {
952
        $message = $this->fixEOL($message);
953
954
        if (PMF_String::strpos(strtolower($this->charset), 'utf') !== false) {
955
            // PHP wordwrap() is not safe with multibyte UTF chars
956
            return $message;
957
        } else {
958
            $lines = explode($this->eol, $message);
959
            $wrapped = '';
960
            foreach ($lines as $value) {
961
                $wrapped .= (empty($wrapped) ? '' : $this->eol);
962
                $wrapped .= wordwrap($value, $width, $this->eol, $cut);
963
            }
964
    
965
            return $wrapped;
966
        }
967
    }
968
    
969
    /**
970
     * If the email spam protection has been activated from the general 
971
     * phpMyFAQ configuration this method converts an email address e.g. 
972
     * from "[email protected]" to "user_AT_example_DOT_org". Otherwise 
973
     * it will return the plain email address.
974
     *
975
     * @param  string $email E-mail address
976
     * @static
977
     *
978
     * @return string
979
     */
980
    public function safeEmail($email)
981
    {
982
        if ($this->_config->get('spam.enableSafeEmail')) {
983
            return str_replace ( array ('@', '.' ), array ('_AT_', '_DOT_' ), $email );
984
        } else {
985
            return $email;
986
        }
987
    }
988
}
989