Passed
Push — master ( a61a57...835875 )
by Malte
03:03
created

Message::__construct()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 15
nc 16
nop 7
dl 0
loc 23
rs 9.4555
c 0
b 0
f 0
1
<?php
2
/*
3
* File:     Message.php
4
* Category: -
5
* Author:   M. Goldenbaum
6
* Created:  19.01.17 22:21
7
* Updated:  -
8
*
9
* Description:
10
*  -
11
*/
12
13
namespace Webklex\IMAP;
14
15
use Carbon\Carbon;
16
use Webklex\IMAP\Support\AttachmentCollection;
17
use Webklex\IMAP\Support\FlagCollection;
18
19
/**
20
 * Class Message
21
 *
22
 * @package Webklex\IMAP
23
 */
24
class Message {
25
26
    /**
27
     * Client instance
28
     *
29
     * @var Client
30
     */
31
    private $client = Client::class;
32
33
    /**
34
     * U ID
35
     *
36
     * @var integer
37
     */
38
    public $uid = '';
39
40
    /**
41
     * Fetch body options
42
     *
43
     * @var integer
44
     */
45
    public $fetch_options = null;
46
47
    /**
48
     * Fetch body options
49
     *
50
     * @var bool
51
     */
52
    public $fetch_body = null;
53
54
    /**
55
     * Fetch attachments options
56
     *
57
     * @var bool
58
     */
59
    public $fetch_attachment = null;
60
61
    /**
62
     * Fetch flags options
63
     *
64
     * @var bool
65
     */
66
    public $fetch_flags = null;
67
    
68
    /**
69
     * @var int $msglist
70
     */
71
    public $msglist = 1;
72
73
    /**
74
     * @var int $msgn
75
     */
76
    public $msgn = null;
77
78
    /**
79
     * @var string $header
80
     */
81
    public $header = null;
82
83
    /**
84
     * @var null|object $header_info
85
     */
86
    public $header_info = null;
87
88
    /** @var null|string $raw_body */
89
    public $raw_body = null;
90
91
    /**
92
     * Message header components
93
     *
94
     * @var string  $message_id
95
     * @var mixed   $message_no
96
     * @var string  $subject
97
     * @var mixed   $references
98
     * @var mixed   $date
99
     * @var array   $from
100
     * @var array   $to
101
     * @var array   $cc
102
     * @var array   $bcc
103
     * @var array   $reply_to
104
     * @var string  $in_reply_to
105
     * @var array   $sender
106
     * @var array   $flags
107
     * @var array   $priority
108
     */
109
    public $message_id = '';
110
    public $message_no = null;
111
    public $subject = '';
112
    public $references = null;
113
    public $date = null;
114
    public $from = [];
115
    public $to = [];
116
    public $cc = [];
117
    public $bcc = [];
118
    public $reply_to = [];
119
    public $in_reply_to = '';
120
    public $sender = [];
121
    public $priority = 0;
122
123
    /**
124
     * Message body components
125
     *
126
     * @var array   $bodies
127
     * @var AttachmentCollection|array $attachments
128
     * @var FlagCollection|array       $flags
129
     */
130
    public $bodies = [];
131
    public $attachments = [];
132
    public $flags = [];
133
134
    /**
135
     * Message const
136
     *
137
     * @const integer   TYPE_TEXT
138
     * @const integer   TYPE_MULTIPART
139
     *
140
     * @const integer   ENC_7BIT
141
     * @const integer   ENC_8BIT
142
     * @const integer   ENC_BINARY
143
     * @const integer   ENC_BASE64
144
     * @const integer   ENC_QUOTED_PRINTABLE
145
     * @const integer   ENC_OTHER
146
     */
147
    const TYPE_TEXT = 0;
148
    const TYPE_MULTIPART = 1;
149
150
    const ENC_7BIT = 0;
151
    const ENC_8BIT = 1;
152
    const ENC_BINARY = 2;
153
    const ENC_BASE64 = 3;
154
    const ENC_QUOTED_PRINTABLE = 4;
155
    const ENC_OTHER = 5;
156
157
    const PRIORITY_UNKNOWN = 0;
158
    const PRIORITY_HIGHEST = 1;
159
    const PRIORITY_HIGH = 2;
160
    const PRIORITY_NORMAL = 3;
161
    const PRIORITY_LOW = 4;
162
    const PRIORITY_LOWEST = 5;
163
164
    /**
165
     * Message constructor.
166
     *
167
     * @param integer       $uid
168
     * @param integer|null  $msglist
169
     * @param Client        $client
170
     * @param integer|null  $fetch_options
171
     * @param boolean       $fetch_body
172
     * @param boolean       $fetch_attachment
173
     * @param boolean       $fetch_flags
174
     */
175
    public function __construct($uid, $msglist, Client $client, $fetch_options = null, $fetch_body = false, $fetch_attachment = false, $fetch_flags = false) {
176
        $this->setFetchOption($fetch_options);
177
        $this->setFetchBodyOption($fetch_body);
178
        $this->setFetchAttachmentOption($fetch_attachment);
179
        $this->setFetchFlagsOption($fetch_flags);
180
181
        $this->attachments = AttachmentCollection::make([]);
182
        $this->flags = FlagCollection::make([]);
183
184
        $this->msglist = $msglist;
185
        $this->client = $client;
186
187
        $this->uid =  ($this->fetch_options == FT_UID) ? $uid : $uid;
188
        $this->msgn = ($this->fetch_options == FT_UID) ? imap_msgno($this->client->getConnection(), $uid) : $uid;
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_msgno() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

188
        $this->msgn = ($this->fetch_options == FT_UID) ? imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $uid) : $uid;
Loading history...
189
190
        $this->parseHeader();
191
        
192
        if ($this->getFetchFlagsOption() === true) {
193
            $this->parseFlags();
194
        }
195
196
        if ($this->getFetchBodyOption() === true) {
197
            $this->parseBody();
198
        }
199
    }
200
201
    /**
202
     * Copy the current Messages to a mailbox
203
     *
204
     * @param $mailbox
205
     * @param int $options
206
     *
207
     * @return bool
208
     */
209
    public function copy($mailbox, $options = 0) {
210
        return imap_mail_copy($this->client->getConnection(), $this->msglist, $mailbox, $options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_mail_copy() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

210
        return imap_mail_copy(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->msglist, $mailbox, $options);
Loading history...
211
    }
212
213
    /**
214
     * Move the current Messages to a mailbox
215
     *
216
     * @param $mailbox
217
     * @param int $options
218
     *
219
     * @return bool
220
     */
221
    public function move($mailbox, $options = 0) {
222
        return imap_mail_move($this->client->getConnection(), $this->msglist, $mailbox, $options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_mail_move() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

222
        return imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->msglist, $mailbox, $options);
Loading history...
223
    }
224
225
    /**
226
     * Check if the Message has a text body
227
     *
228
     * @return bool
229
     */
230
    public function hasTextBody() {
231
        return isset($this->bodies['text']);
232
    }
233
234
    /**
235
     * Get the Message text body
236
     *
237
     * @return mixed
238
     */
239
    public function getTextBody() {
240
        if (!isset($this->bodies['text'])) {
241
            return false;
242
        }
243
244
        return $this->bodies['text']->content;
245
    }
246
247
    /**
248
     * Check if the Message has a html body
249
     *
250
     * @return bool
251
     */
252
    public function hasHTMLBody() {
253
        return isset($this->bodies['html']);
254
    }
255
256
    /**
257
     * Get the Message html body
258
     *
259
     * @var bool $replaceImages
260
     *
261
     * @return mixed
262
     */
263
    public function getHTMLBody($replaceImages = false) {
264
        if (!isset($this->bodies['html'])) {
265
            return false;
266
        }
267
268
        $body = $this->bodies['html']->content;
269
        if ($replaceImages) {
270
            $this->attachments->each(function($oAttachment) use(&$body) {
271
                if ($oAttachment->id && isset($oAttachment->img_src)) {
272
                    $body = str_replace('cid:'.$oAttachment->id, $oAttachment->img_src, $body);
273
                }
274
            });
275
        }
276
277
        return $body;
278
    }
279
280
    /**
281
     * Parse all defined headers
282
     *
283
     * @return void
284
     */
285
    private function parseHeader() {
286
        $this->header = $header = imap_fetchheader($this->client->getConnection(), $this->uid, FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchheader() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

286
        $this->header = $header = imap_fetchheader(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, FT_UID);
Loading history...
287
        if ($this->header) {
288
            $header = imap_rfc822_parse_headers($this->header);
289
        }
290
291
        if(preg_match('/x\-priority\:.*([0-9]{1,2})/i', $this->header, $priority)){
292
            $priority = isset($priority[1]) ? (int) $priority[1] : 0;
293
            switch($priority){
294
                case self::PRIORITY_HIGHEST;
295
                    $this->priority = self::PRIORITY_HIGHEST;
296
                    break;
297
                case self::PRIORITY_HIGH;
298
                    $this->priority = self::PRIORITY_HIGH;
299
                    break;
300
                case self::PRIORITY_NORMAL;
301
                    $this->priority = self::PRIORITY_NORMAL;
302
                    break;
303
                case self::PRIORITY_LOW;
304
                    $this->priority = self::PRIORITY_LOW;
305
                    break;
306
                case self::PRIORITY_LOWEST;
307
                    $this->priority = self::PRIORITY_LOWEST;
308
                    break;
309
                default:
310
                    $this->priority = self::PRIORITY_UNKNOWN;
311
                    break;
312
            }
313
        }
314
315
        if (property_exists($header, 'subject')) {
316
            $this->subject = imap_utf8($header->subject);
317
        }
318
        if (property_exists($header, 'subject')) {
319
            $this->subject = imap_utf8($header->subject);
320
        }
321
        if (property_exists($header, 'date')) {
322
            $date = $header->date;
323
324
            /**
325
             * Exception handling for invalid dates
326
             * Will be extended in the future
327
             *
328
             * Currently known invalid formats:
329
             * ^ Datetime                                   ^ Problem                           ^ Cause                 
330
             * | Mon, 20 Nov 2017 20:31:31 +0800 (GMT+8:00) | Double timezone specification     | A Windows feature
331
             * |                                            | and invalid timezone (max 6 char) |
332
             * | 04 Jan 2018 10:12:47 UT                    | Missing letter "C"                | Unknown
333
             * | Thu, 31 May 2018 18:15:00 +0800 (added by) | Non-standard details added by the | Unknown
334
             * |                                            | mail server                       |
335
             *
336
             * Please report any new invalid timestamps to [#45](https://github.com/Webklex/laravel-imap/issues/45)
337
             */
338
            try {
339
                $this->date = Carbon::parse($date);
340
            } catch (\Exception $e) {
341
                switch (true) {
342
                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
343
                    case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{2,4}\ [0-9]{2}\:[0-9]{2}\:[0-9]{2}\ [A-Z]{2}\ \-[0-9]{2}\:[0-9]{2}\ \([A-Z]{2,3}\ \-[0-9]{2}:[0-9]{2}\))+$/i', $date) > 0:
344
                    $array = explode('(', $date);
345
                        $array = array_reverse($array);
346
                        $date = trim(array_pop($array));
347
                        break;
348
                    case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
349
                        $date .= 'C';
350
                        break;
351
                }
352
                $this->date = Carbon::parse($date);
353
            }
354
        }
355
356
        if (property_exists($header, 'from')) {
357
            $this->from = $this->parseAddresses($header->from);
358
        }
359
        if (property_exists($header, 'to')) {
360
            $this->to = $this->parseAddresses($header->to);
361
        }
362
        if (property_exists($header, 'cc')) {
363
            $this->cc = $this->parseAddresses($header->cc);
364
        }
365
        if (property_exists($header, 'bcc')) {
366
            $this->bcc = $this->parseAddresses($header->bcc);
367
        }
368
        if (property_exists($header, 'references')) {
369
            $this->references = $header->references;
370
        }
371
372
        if (property_exists($header, 'reply_to')) {
373
            $this->reply_to = $this->parseAddresses($header->reply_to);
374
        }
375
        if (property_exists($header, 'in_reply_to')) {
376
            $this->in_reply_to = str_replace(['<', '>'], '', $header->in_reply_to);
377
        }
378
        if (property_exists($header, 'sender')) {
379
            $this->sender = $this->parseAddresses($header->sender);
380
        }
381
382
        if (property_exists($header, 'message_id')) {
383
            $this->message_id = str_replace(['<', '>'], '', $header->message_id);
384
        }
385
        if (property_exists($header, 'Msgno')) {
386
            $messageNo = (int) trim($header->Msgno);
387
            $this->message_no = ($this->fetch_options == FT_UID) ? $messageNo : imap_msgno($this->client->getConnection(), $messageNo);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_msgno() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

387
            $this->message_no = ($this->fetch_options == FT_UID) ? $messageNo : imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $messageNo);
Loading history...
388
        } else {
389
            $this->message_no = imap_msgno($this->client->getConnection(), $this->getUid());
390
        }
391
    }
392
393
    /**
394
     * Parse additional flags
395
     *
396
     * @return void
397
     */
398
    private function parseFlags() {
399
        $flags = imap_fetch_overview($this->client->getConnection(), $this->uid, FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetch_overview() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

399
        $flags = imap_fetch_overview(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, FT_UID);
Loading history...
400
        if (is_array($flags) && isset($flags[0])) {
401
            if (property_exists($flags[0], 'recent')) {
402
                $this->flags->put('recent', $flags[0]->recent);
403
            }
404
            if (property_exists($flags[0], 'flagged')) {
405
                $this->flags->put('flagged', $flags[0]->flagged);
406
            }
407
            if (property_exists($flags[0], 'answered')) {
408
                $this->flags->put('answered', $flags[0]->answered);
409
            }
410
            if (property_exists($flags[0], 'deleted')) {
411
                $this->flags->put('deleted', $flags[0]->deleted);
412
            }
413
            if (property_exists($flags[0], 'seen')) {
414
                $this->flags->put('seen', $flags[0]->seen);
415
            }
416
            if (property_exists($flags[0], 'draft')) {
417
                $this->flags->put('draft', $flags[0]->draft);
418
            }  
419
        }
420
    }
421
    
422
    /**
423
     * Get the current Message header info
424
     *
425
     * @return object
426
     */
427
    public function getHeaderInfo() {
428
        if ($this->header_info == null) {
429
            $this->header_info =
430
            $this->header_info = imap_headerinfo($this->client->getConnection(), $this->getMessageNo());
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $stream_id of imap_headerinfo() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

430
            $this->header_info = imap_headerinfo(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getMessageNo());
Loading history...
431
        }
432
433
        return $this->header_info;
434
    }
435
436
    /**
437
     * Parse Addresses
438
     *
439
     * @param $list
440
     *
441
     * @return array
442
     */
443
    private function parseAddresses($list) {
444
        $addresses = [];
445
446
        foreach ($list as $item) {
447
            $address = (object) $item;
448
449
            if (!property_exists($address, 'mailbox')) {
450
                $address->mailbox = false;
451
            }
452
            if (!property_exists($address, 'host')) {
453
                $address->host = false;
454
            }
455
            if (!property_exists($address, 'personal')) {
456
                $address->personal = false;
457
            }
458
459
            $address->personal = imap_utf8($address->personal);
460
461
            $address->mail = ($address->mailbox && $address->host) ? $address->mailbox.'@'.$address->host : false;
462
            $address->full = ($address->personal) ? $address->personal.' <'.$address->mail.'>' : $address->mail;
0 ignored issues
show
Bug introduced by
Are you sure $address->mail of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

462
            $address->full = ($address->personal) ? $address->personal.' <'./** @scrutinizer ignore-type */ $address->mail.'>' : $address->mail;
Loading history...
463
464
            $addresses[] = $address;
465
        }
466
467
        return $addresses;
468
    }
469
470
    /**
471
     * Parse the Message body
472
     *
473
     * @return $this
474
     */
475
    public function parseBody() {
476
        $structure = imap_fetchstructure($this->client->getConnection(), $this->uid, FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchstructure() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

476
        $structure = imap_fetchstructure(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, FT_UID);
Loading history...
477
478
        if(property_exists($structure, 'parts')){
479
            $parts = $structure->parts;
480
481
            foreach ($parts as $part)  {
482
                foreach ($part->parameters as $parameter)  {
483
                    if($parameter->attribute == "charset")  {
484
                        $encoding = $parameter->value;
485
                        $parameter->value = preg_replace('/Content-Transfer-Encoding/', '', $encoding);
486
                    }
487
                }
488
            }
489
        }
490
491
        $this->fetchStructure($structure);
492
493
        return $this;
494
    }
495
496
    /**
497
     * Fetch the Message structure
498
     *
499
     * @param $structure
500
     * @param mixed $partNumber
501
     */
502
    private function fetchStructure($structure, $partNumber = null) {
503
        if ($structure->type == self::TYPE_TEXT &&
504
            ($structure->ifdisposition == 0 ||
505
                ($structure->ifdisposition == 1 && !isset($structure->parts) && $partNumber == null)
506
            )
507
        ) {
508
            if ($structure->subtype == "PLAIN") {
509
                if (!$partNumber) {
510
                    $partNumber = 1;
511
                }
512
513
                $encoding = $this->getEncoding($structure);
514
515
                $content = imap_fetchbody($this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options | FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchbody() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

515
                $content = imap_fetchbody(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options | FT_UID);
Loading history...
516
                $content = $this->decodeString($content, $structure->encoding);
517
                $content = $this->convertEncoding($content, $encoding);
518
519
                $body = new \stdClass;
520
                $body->type = "text";
521
                $body->content = $content;
522
523
                $this->bodies['text'] = $body;
524
525
                $this->fetchAttachment($structure, $partNumber);
526
527
            } elseif ($structure->subtype == "HTML") {
528
                if (!$partNumber) {
529
                    $partNumber = 1;
530
                }
531
532
                $encoding = $this->getEncoding($structure);
533
534
                $content = imap_fetchbody($this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options | FT_UID);
535
                $content = $this->decodeString($content, $structure->encoding);
536
                $content = $this->convertEncoding($content, $encoding);
537
538
                $body = new \stdClass;
539
                $body->type = "html";
540
                $body->content = $content;
541
542
                $this->bodies['html'] = $body;
543
            }
544
        } elseif ($structure->type == self::TYPE_MULTIPART) {
545
            foreach ($structure->parts as $index => $subStruct) {
546
                $prefix = "";
547
                if ($partNumber) {
548
                    $prefix = $partNumber.".";
549
                }
550
                $this->fetchStructure($subStruct, $prefix.($index + 1));
551
            }
552
        } else {
553
            if ($this->getFetchAttachmentOption() === true) {
554
                $this->fetchAttachment($structure, $partNumber);
555
            }
556
        }
557
    }
558
559
    /**
560
     * Fetch the Message attachment
561
     *
562
     * @param object $structure
563
     * @param mixed  $partNumber
564
     */
565
    protected function fetchAttachment($structure, $partNumber) {
566
567
        $oAttachment = new Attachment($this, $structure, $partNumber);
568
569
        if ($oAttachment->getName() !== null) {
570
            if ($oAttachment->getId() !== null) {
571
                $this->attachments->put($oAttachment->getId(), $oAttachment);
572
            } else {
573
                $this->attachments->push($oAttachment);
574
            }
575
        }
576
    }
577
578
    /**
579
     * Fail proof setter for $fetch_option
580
     *
581
     * @param $option
582
     *
583
     * @return $this
584
     */
585
    public function setFetchOption($option) {
586
        if (is_long($option) === true) {
587
            $this->fetch_options = $option;
588
        } elseif (is_null($option) === true) {
589
            $config = config('imap.options.fetch', FT_UID);
590
            $this->fetch_options = is_long($config) ? $config : 1;
591
        }
592
593
        return $this;
594
    }
595
596
    /**
597
     * Fail proof setter for $fetch_body
598
     *
599
     * @param $option
600
     *
601
     * @return $this
602
     */
603
    public function setFetchBodyOption($option) {
604
        if (is_bool($option)) {
605
            $this->fetch_body = $option;
606
        } elseif (is_null($option)) {
607
            $config = config('imap.options.fetch_body', true);
608
            $this->fetch_body = is_bool($config) ? $config : true;
609
        }
610
611
        return $this;
612
    }
613
614
    /**
615
     * Fail proof setter for $fetch_attachment
616
     *
617
     * @param $option
618
     *
619
     * @return $this
620
     */
621
    public function setFetchAttachmentOption($option) {
622
        if (is_bool($option)) {
623
            $this->fetch_attachment = $option;
624
        } elseif (is_null($option)) {
625
            $config = config('imap.options.fetch_attachment', true);
626
            $this->fetch_attachment = is_bool($config) ? $config : true;
627
        }
628
629
        return $this;
630
    }
631
    
632
    /**
633
     * Fail proof setter for $fetch_flags
634
     *
635
     * @param $option
636
     *
637
     * @return $this
638
     */
639
    public function setFetchFlagsOption($option) {
640
        if (is_bool($option)) {
641
            $this->fetch_flags = $option;
642
        } elseif (is_null($option)) {
643
            $config = config('imap.options.fetch_flags', true);
644
            $this->fetch_flags = is_bool($config) ? $config : true;
645
        }
646
647
        return $this;
648
    }
649
650
    /**
651
     * Decode a given string
652
     *
653
     * @param $string
654
     * @param $encoding
655
     *
656
     * @return string
657
     */
658
    public function decodeString($string, $encoding) {
659
        switch ($encoding) {
660
            case self::ENC_7BIT:
661
                return $string;
662
            case self::ENC_8BIT:
663
                return quoted_printable_decode(imap_8bit($string));
664
            case self::ENC_BINARY:
665
                return imap_binary($string);
666
            case self::ENC_BASE64:
667
                return imap_base64($string);
668
            case self::ENC_QUOTED_PRINTABLE:
669
                return quoted_printable_decode($string);
670
            case self::ENC_OTHER:
671
                return $string;
672
            default:
673
                return $string;
674
        }
675
    }
676
    
677
    /**
678
     * Convert the encoding
679
     *
680
     * @param $str
681
     * @param string $from
682
     * @param string $to
683
     *
684
     * @return mixed|string
685
     */
686
    private function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") {
687
        if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
688
            return iconv(EncodingAliases::get($from), $to.'//IGNORE', $str);
689
        } else {
690
            if (!$from) {
691
                return mb_convert_encoding($str, $to);
692
            }
693
            return mb_convert_encoding($str, $to, $from);
694
        }
695
    }
696
697
    /**
698
     * Get the encoding of a given abject
699
     *
700
     * @param object $structure
701
     *
702
     * @return null|string
703
     */
704
    private function getEncoding($structure) {
705
        if (property_exists($structure, 'parameters')) {
706
            foreach ($structure->parameters as $parameter) {
707
                if (strtolower($parameter->attribute) == "charset") {
708
                    return strtoupper($parameter->value);
709
                }
710
            }
711
        }
712
        return null;
713
    }
714
715
    /**
716
     * Find the folder containing this message.
717
     *
718
     * @param null|Folder $folder where to start searching from (top-level inbox by default)
719
     * @return null|Folder
720
     */
721
    public function getContainingFolder(Folder $folder = null) {
722
        $folder = $folder ?: $this->client->getFolders()->first();
723
        $this->client->checkConnection();
724
725
        // Try finding the message by uid in the current folder
726
        $client = new Client;
727
        $client->openFolder($folder);
728
        $uidMatches = imap_fetch_overview($client->getConnection(), $this->uid, FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetch_overview() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

728
        $uidMatches = imap_fetch_overview(/** @scrutinizer ignore-type */ $client->getConnection(), $this->uid, FT_UID);
Loading history...
729
        $uidMatch = count($uidMatches)
730
            ? new Message($uidMatches[0]->uid, $uidMatches[0]->msgno, $client)
731
            : null;
732
        $client->disconnect();
733
734
        // imap_fetch_overview() on a parent folder will return the matching message
735
        // even when the message is in a child folder so we need to recursively
736
        // search the children
737
        foreach ($folder->children as $child) {
738
            $childFolder = $this->getContainingFolder($child);
739
740
            if ($childFolder) {
741
                return $childFolder;
742
            }
743
        }
744
745
        // before returning the parent
746
        if ($this->is($uidMatch)) {
747
            return $folder;
748
        }
749
750
        // or signalling that the message was not found in any folder
751
        return null;
752
    }
753
754
    /**
755
     * Move the Message into an other Folder
756
     *
757
     * @param string  $mailbox
758
     *
759
     * @return bool
760
     */
761
    public function moveToFolder($mailbox = 'INBOX') {
762
        $this->client->createFolder($mailbox);
763
764
        return imap_mail_move($this->client->getConnection(), $this->uid, $mailbox, CP_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_mail_move() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

764
        return imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $mailbox, CP_UID);
Loading history...
765
    }
766
767
    /**
768
     * Delete the current Message
769
     *
770
     * @return bool
771
     */
772
    public function delete() {
773
        $status = imap_delete($this->client->getConnection(), $this->uid, FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_delete() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

773
        $status = imap_delete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, FT_UID);
Loading history...
774
        $this->client->expunge();
775
776
        return $status;
777
    }
778
779
    /**
780
     * Restore a deleted Message
781
     *
782
     * @return bool
783
     */
784
    public function restore() {
785
        return imap_undelete($this->client->getConnection(), $this->uid, FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_undelete() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

785
        return imap_undelete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, FT_UID);
Loading history...
786
    }
787
788
    /**
789
     * Get all message attachments.
790
     *
791
     * @return AttachmentCollection
792
     */
793
    public function getAttachments() {
794
        return $this->attachments;
795
    }
796
797
    /**
798
     * Checks if there are any attachments present
799
     *
800
     * @return boolean
801
     */
802
    public function hasAttachments() {
803
        return $this->attachments->isEmpty() === false;
804
    }
805
806
    /**
807
     * Set a given flag
808
     * @param string|array $flag
809
     *
810
     * @return bool
811
     */
812
    public function setFlag($flag) {
813
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
814
        return imap_setflag_full($this->client->getConnection(), $this->getUid(), $flag, SE_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_setflag_full() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

814
        return imap_setflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
815
    }
816
817
    /**
818
     * Unset a given flag
819
     * @param string|array $flag
820
     *
821
     * @return bool
822
     */
823
    public function unsetFlag($flag) {
824
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
825
        return imap_clearflag_full($this->client->getConnection(), $this->getUid(), $flag, SE_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_clearflag_full() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

825
        return imap_clearflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
826
    }
827
828
    /**
829
     * @return null|object|string
830
     */
831
    public function getRawBody() {
832
        if ($this->raw_body === null) {
833
            $this->raw_body = imap_fetchbody($this->client->getConnection(), $this->getUid(), '', $this->fetch_options | FT_UID);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_fetchbody() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

833
            $this->raw_body = imap_fetchbody(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), '', $this->fetch_options | FT_UID);
Loading history...
834
        }
835
836
        return $this->raw_body;
837
    }
838
839
    /**
840
     * @return string
841
     */
842
    public function getHeader() {
843
        return $this->header;
844
    }
845
846
    /**
847
     * @return Client
848
     */
849
    public function getClient() {
850
        return $this->client;
851
    }
852
853
    /**
854
     * @return integer
855
     */
856
    public function getUid() {
857
        return $this->uid;
858
    }
859
860
    /**
861
     * @return integer
862
     */
863
    public function getFetchOptions() {
864
        return $this->fetch_options;
865
    }
866
867
    /**
868
     * @return boolean
869
     */
870
    public function getFetchBodyOption() {
871
        return $this->fetch_body;
872
    }
873
874
    /**
875
     * @return integer
876
     */
877
    public function getPriority() {
878
        return $this->priority;
879
    }
880
881
    /**
882
     * @return boolean
883
     */
884
    public function getFetchAttachmentOption() {
885
        return $this->fetch_attachment;
886
    }
887
    
888
    /**
889
     * @return boolean
890
     */
891
    public function getFetchFlagsOption() {
892
        return $this->fetch_flags;
893
    }
894
895
    /**
896
     * @return int
897
     */
898
    public function getMsglist() {
899
        return $this->msglist;
900
    }
901
902
    /**
903
     * @return mixed
904
     */
905
    public function getMessageId() {
906
        return $this->message_id;
907
    }
908
909
    /**
910
     * @return int
911
     */
912
    public function getMessageNo() {
913
        return $this->message_no;
914
    }
915
916
    /**
917
     * @return string
918
     */
919
    public function getSubject() {
920
        return $this->subject;
921
    }
922
923
    /**
924
     * @return mixed
925
     */
926
    public function getReferences() {
927
        return $this->references;
928
    }
929
930
    /**
931
     * @return Carbon|null
932
     */
933
    public function getDate() {
934
        return $this->date;
935
    }
936
937
    /**
938
     * @return array
939
     */
940
    public function getFrom() {
941
        return $this->from;
942
    }
943
944
    /**
945
     * @return array
946
     */
947
    public function getTo() {
948
        return $this->to;
949
    }
950
951
    /**
952
     * @return array
953
     */
954
    public function getCc() {
955
        return $this->cc;
956
    }
957
958
    /**
959
     * @return array
960
     */
961
    public function getBcc() {
962
        return $this->bcc;
963
    }
964
965
    /**
966
     * @return array
967
     */
968
    public function getReplyTo() {
969
        return $this->reply_to;
970
    }
971
    
972
    /**
973
     * @return string
974
     */
975
    public function getInReplyTo() {
976
        return $this->in_reply_to;
977
    }
978
979
    /**
980
     * @return array
981
     */
982
    public function getSender() {
983
        return $this->sender;
984
    }
985
986
    /**
987
     * @return mixed
988
     */
989
    public function getBodies() {
990
        return $this->bodies;
991
    }
992
    
993
    /**
994
     * @return FlagCollection
995
     */
996
    public function getFlags() {
997
        return $this->flags;
998
    }
999
1000
    /**
1001
     * Does this message match another one?
1002
     *
1003
     * A match means same uid, message id, subject and date/time.
1004
     *
1005
     * @param  null|static $message
1006
     * @return boolean
1007
     */
1008
    public function is(Message $message = null) {
1009
        if (is_null($message)) {
1010
            return false;
1011
        }
1012
1013
        return $this->uid == $message->uid
1014
            && $this->message_id == $message->message_id
1015
            && $this->subject == $message->subject
1016
            && $this->date->eq($message->date);
1017
    }
1018
}
1019