Completed
Push — master ( db7ac7...10b243 )
by Malte
04:40 queued 02:17
created

Message::getFetchFlagsOption()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
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
18
/**
19
 * Class Message
20
 *
21
 * @package Webklex\IMAP
22
 */
23
class Message {
24
25
    /**
26
     * Client instance
27
     *
28
     * @var Client
29
     */
30
    private $client = Client::class;
31
32
    /**
33
     * U ID
34
     *
35
     * @var integer
36
     */
37
    public $uid = '';
38
39
    /**
40
     * Fetch body options
41
     *
42
     * @var integer
43
     */
44
    public $fetch_options = null;
45
46
    /**
47
     * Fetch body options
48
     *
49
     * @var bool
50
     */
51
    public $fetch_body = null;
52
53
    /**
54
     * Fetch attachments options
55
     *
56
     * @var bool
57
     */
58
    public $fetch_attachment = null;
59
60
    /**
61
     * Fetch flags options
62
     *
63
     * @var bool
64
     */
65
    public $fetch_flags = null;
66
    
67
    /**
68
     * @var int $msglist
69
     */
70
    public $msglist = 1;
71
72
    /**
73
     * @var string $header
74
     */
75
    public $header = null;
76
77
    /**
78
     * @var null|object $header_info
79
     */
80
    public $header_info = null;
81
82
    /** @var null|string $raw_body */
83
    public $raw_body = null;
84
85
    /**
86
     * Message header components
87
     *
88
     * @var string  $message_id
89
     * @var mixed   $message_no
90
     * @var string  $subject
91
     * @var mixed   $references
92
     * @var mixed   $date
93
     * @var array   $from
94
     * @var array   $to
95
     * @var array   $cc
96
     * @var array   $bcc
97
     * @var array   $reply_to
98
     * @var string  $in_reply_to
99
     * @var array   $sender
100
     * @var array   $flags
101
     */
102
    public $message_id = '';
103
    public $message_no = null;
104
    public $subject = '';
105
    public $references = null;
106
    public $date = null;
107
    public $from = [];
108
    public $to = [];
109
    public $cc = [];
110
    public $bcc = [];
111
    public $reply_to = [];
112
    public $in_reply_to = '';
113
    public $sender = [];
114
    public $flags = [];
115
116
    /**
117
     * Message body components
118
     *
119
     * @var array   $bodies
120
     * @var AttachmentCollection|array  $attachments
121
     */
122
    public $bodies = [];
123
    public $attachments = [];
124
125
    /**
126
     * Message const
127
     *
128
     * @const integer   TYPE_TEXT
129
     * @const integer   TYPE_MULTIPART
130
     *
131
     * @const integer   ENC_7BIT
132
     * @const integer   ENC_8BIT
133
     * @const integer   ENC_BINARY
134
     * @const integer   ENC_BASE64
135
     * @const integer   ENC_QUOTED_PRINTABLE
136
     * @const integer   ENC_OTHER
137
     */
138
    const TYPE_TEXT = 0;
139
    const TYPE_MULTIPART = 1;
140
141
    const ENC_7BIT = 0;
142
    const ENC_8BIT = 1;
143
    const ENC_BINARY = 2;
144
    const ENC_BASE64 = 3;
145
    const ENC_QUOTED_PRINTABLE = 4;
146
    const ENC_OTHER = 5;
147
148
    /**
149
     * Message constructor.
150
     *
151
     * @param integer       $uid
152
     * @param integer|null  $msglist
153
     * @param Client        $client
154
     * @param integer|null  $fetch_options
155
     * @param boolean       $fetch_body
156
     * @param boolean       $fetch_attachment
157
     */
158
    public function __construct($uid, $msglist, Client $client, $fetch_options = null, $fetch_body = false, $fetch_attachment = false, $fetch_flags = false) {
159
        $this->setFetchOption($fetch_options);
160
        $this->setFetchBodyOption($fetch_body);
161
        $this->setFetchAttachmentOption($fetch_attachment);
162
        $this->setFetchFlagsOption($fetch_flags);
163
164
        $this->attachments = AttachmentCollection::make([]);
165
        
166
        $this->msglist = $msglist;
167
        $this->client = $client;
168
        $this->uid = ($this->fetch_options == FT_UID) ? $uid : imap_msgno($this->client->getConnection(), $uid);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type boolean; 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

168
        $this->uid = ($this->fetch_options == FT_UID) ? $uid : imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $uid);
Loading history...
169
        
170
        $this->parseHeader();
171
        
172
        if ($this->getFetchFlagsOption() === true) {
173
            $this->parseFlags();
174
        }
175
176
        if ($this->getFetchBodyOption() === true) {
177
            $this->parseBody();
178
        }
179
    }
180
181
    /**
182
     * Copy the current Messages to a mailbox
183
     *
184
     * @param $mailbox
185
     * @param int $options
186
     *
187
     * @return bool
188
     */
189
    public function copy($mailbox, $options = 0) {
190
        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 boolean; 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

190
        return imap_mail_copy(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->msglist, $mailbox, $options);
Loading history...
191
    }
192
193
    /**
194
     * Move the current Messages to a mailbox
195
     *
196
     * @param $mailbox
197
     * @param int $options
198
     *
199
     * @return bool
200
     */
201
    public function move($mailbox, $options = 0) {
202
        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 boolean; 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

202
        return imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->msglist, $mailbox, $options);
Loading history...
203
    }
204
205
    /**
206
     * Check if the Message has a text body
207
     *
208
     * @return bool
209
     */
210
    public function hasTextBody() {
211
        return isset($this->bodies['text']);
212
    }
213
214
    /**
215
     * Get the Message text body
216
     *
217
     * @return mixed
218
     */
219
    public function getTextBody() {
220
        if (!isset($this->bodies['text'])) {
221
            return false;
222
        }
223
224
        return $this->bodies['text']->content;
225
    }
226
227
    /**
228
     * Check if the Message has a html body
229
     *
230
     * @return bool
231
     */
232
    public function hasHTMLBody() {
233
        return isset($this->bodies['html']);
234
    }
235
236
    /**
237
     * Get the Message html body
238
     *
239
     * @var bool $replaceImages
240
     *
241
     * @return mixed
242
     */
243
    public function getHTMLBody($replaceImages = false) {
244
        if (!isset($this->bodies['html'])) {
245
            return false;
246
        }
247
248
        $body = $this->bodies['html']->content;
249
        if ($replaceImages) {
250
            $this->attachments->each(function($oAttachment) use(&$body){
251
                if ($oAttachment->id && isset($oAttachment->img_src)) {
252
                    $body = str_replace('cid:'.$oAttachment->id, $oAttachment->img_src, $body);
253
                }
254
            });
255
        }
256
257
        return $body;
258
    }
259
260
    /**
261
     * Parse all defined headers
262
     *
263
     * @return void
264
     */
265
    private function parseHeader() {
266
        $this->header = $header = imap_fetchheader($this->client->getConnection(), $this->uid, $this->fetch_options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type boolean; 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

266
        $this->header = $header = imap_fetchheader(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $this->fetch_options);
Loading history...
267
        if ($this->header) {
268
            $header = imap_rfc822_parse_headers($this->header);
269
        }
270
271
        if (property_exists($header, 'subject')) {
272
            $this->subject = imap_utf8($header->subject);
273
        }
274
        if (property_exists($header, 'date')) {
275
            $date = $header->date;
276
277
            /**
278
             * Exception handling for invalid dates
279
             * Will be extended in the future
280
             *
281
             * Currently known invalid formats:
282
             * ^ Datetime                                   ^ Problem                           ^ Cause                 
283
             * | Mon, 20 Nov 2017 20:31:31 +0800 (GMT+8:00) | Double timezone specification     | A Windows feature
284
             * |                                            | and invalid timezone (max 6 char) |
285
             * | 04 Jan 2018 10:12:47 UT                    | Missing letter "C"                | Unknown
286
             * | Thu, 31 May 2018 18:15:00 +0800 (added by) | Non-standard details added by the | Unknown
287
             * |                                            | mail server                       |
288
             *
289
             * Please report any new invalid timestamps to [#45](https://github.com/Webklex/laravel-imap/issues/45)
290
             */
291
            try {
292
                $this->date = Carbon::parse($date);
293
            } catch (\Exception $e) {
294
                switch (true) {
295
                    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:
296
                        $array = explode('(', $date);
297
                        $array = array_reverse($array);
298
                        $date = trim(array_pop($array));
299
                        break;
300
                    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:
301
                        $date .= 'C';
302
                        break;
303
                }
304
                $this->date = Carbon::parse($date);
305
            }
306
        }
307
308
        if (property_exists($header, 'from')) {
309
            $this->from = $this->parseAddresses($header->from);
310
        }
311
        if (property_exists($header, 'to')) {
312
            $this->to = $this->parseAddresses($header->to);
313
        }
314
        if (property_exists($header, 'cc')) {
315
            $this->cc = $this->parseAddresses($header->cc);
316
        }
317
        if (property_exists($header, 'bcc')) {
318
            $this->bcc = $this->parseAddresses($header->bcc);
319
        }
320
        if (property_exists($header, 'references')) {
321
            $this->references = $header->references;
322
        }
323
324
        if (property_exists($header, 'reply_to')) {
325
            $this->reply_to = $this->parseAddresses($header->reply_to);
326
        }
327
        if (property_exists($header, 'in_reply_to')) {
328
            $this->in_reply_to = str_replace(['<', '>'], '', $header->in_reply_to);
329
        }
330
        if (property_exists($header, 'sender')) {
331
            $this->sender = $this->parseAddresses($header->sender);
332
        }
333
334
        if (property_exists($header, 'message_id')) {
335
            $this->message_id = str_replace(['<', '>'], '', $header->message_id);
336
        }
337
        if (property_exists($header, 'Msgno')) {
338
            $messageNo = (int) trim($header->Msgno);
339
            $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 boolean; 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

339
            $this->message_no = ($this->fetch_options == FT_UID) ? $messageNo : imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $messageNo);
Loading history...
340
        } else {
341
            $this->message_no = imap_msgno($this->client->getConnection(), $this->getUid());
342
        }
343
    }
344
345
    /**
346
     * Parse additional flags
347
     *
348
     * @return void
349
     */
350
    private function parseFlags() {
351
        $flags = imap_fetch_overview($this->client->getConnection(), $this->uid, $this->fetch_options);        
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type boolean; 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

351
        $flags = imap_fetch_overview(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $this->fetch_options);        
Loading history...
352
        if (is_array($flags) && isset($flags[0])) {
353
            if (property_exists($flags[0], 'recent')) {
354
                $this->flags['recent'] = $flags[0]->recent;
355
            }
356
            if (property_exists($flags[0], 'flagged')) {
357
                $this->flags['flagged'] = $flags[0]->flagged;
358
            }
359
            if (property_exists($flags[0], 'answered')) {
360
                $this->flags['answered'] = $flags[0]->answered;
361
            }
362
            if (property_exists($flags[0], 'deleted')) {
363
                $this->flags['deleted'] = $flags[0]->deleted;
364
            }
365
            if (property_exists($flags[0], 'seen')) {
366
                $this->flags['seen'] = $flags[0]->seen;
367
            }
368
            if (property_exists($flags[0], 'draft')) {
369
                $this->flags['draft'] = $flags[0]->draft;
370
            }  
371
        }
372
    }
373
    
374
    /**
375
     * Get the current Message header info
376
     *
377
     * @return object
378
     */
379
    public function getHeaderInfo() {
380
        if ($this->header_info == null) {
381
            $this->header_info =
382
            $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 boolean; 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

382
            $this->header_info = imap_headerinfo(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getMessageNo()); ;
Loading history...
383
        }
384
385
        return $this->header_info;
386
    }
387
388
    /**
389
     * Parse Addresses
390
     *
391
     * @param $list
392
     *
393
     * @return array
394
     */
395
    private function parseAddresses($list) {
396
        $addresses = [];
397
398
        foreach ($list as $item) {
399
            $address = (object) $item;
400
401
            if (!property_exists($address, 'mailbox')) {
402
                $address->mailbox = false;
403
            }
404
            if (!property_exists($address, 'host')) {
405
                $address->host = false;
406
            }
407
            if (!property_exists($address, 'personal')) {
408
                $address->personal = false;
409
            }
410
411
            $address->personal = imap_utf8($address->personal);
412
413
            $address->mail = ($address->mailbox && $address->host) ? $address->mailbox.'@'.$address->host : false;
414
            $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

414
            $address->full = ($address->personal) ? $address->personal.' <'./** @scrutinizer ignore-type */ $address->mail.'>' : $address->mail;
Loading history...
415
416
            $addresses[] = $address;
417
        }
418
419
        return $addresses;
420
    }
421
422
    /**
423
     * Parse the Message body
424
     *
425
     * @return $this
426
     */
427
    public function parseBody() {
428
        $structure = imap_fetchstructure($this->client->getConnection(), $this->uid, $this->fetch_options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type boolean; 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

428
        $structure = imap_fetchstructure(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $this->fetch_options);
Loading history...
429
430
        $this->fetchStructure($structure);
431
432
        return $this;
433
    }
434
435
    /**
436
     * Fetch the Message structure
437
     *
438
     * @param $structure
439
     * @param mixed $partNumber
440
     */
441
    private function fetchStructure($structure, $partNumber = null) {
442
        if ($structure->type == self::TYPE_TEXT &&
443
            ($structure->ifdisposition == 0 ||
444
                ($structure->ifdisposition == 1 && !isset($structure->parts) && $partNumber == null)
445
            )
446
        ) {
447
            if ($structure->subtype == "PLAIN") {
448
                if (!$partNumber) {
449
                    $partNumber = 1;
450
                }
451
452
                $encoding = $this->getEncoding($structure);
453
454
                $content = imap_fetchbody($this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type boolean; 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

454
                $content = imap_fetchbody(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options);
Loading history...
455
                $content = $this->decodeString($content, $structure->encoding);
456
                $content = $this->convertEncoding($content, $encoding);
457
458
                $body = new \stdClass;
459
                $body->type = "text";
460
                $body->content = $content;
461
462
                $this->bodies['text'] = $body;
463
464
                $this->fetchAttachment($structure, $partNumber);
465
466
            } elseif ($structure->subtype == "HTML") {
467
                if (!$partNumber) {
468
                    $partNumber = 1;
469
                }
470
471
                $encoding = $this->getEncoding($structure);
472
473
                $content = imap_fetchbody($this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options);
474
                $content = $this->decodeString($content, $structure->encoding);
475
                $content = $this->convertEncoding($content, $encoding);
476
477
                $body = new \stdClass;
478
                $body->type = "html";
479
                $body->content = $content;
480
481
                $this->bodies['html'] = $body;
482
            }
483
        } elseif ($structure->type == self::TYPE_MULTIPART) {
484
            foreach ($structure->parts as $index => $subStruct) {
485
                $prefix = "";
486
                if ($partNumber) {
487
                    $prefix = $partNumber.".";
488
                }
489
                $this->fetchStructure($subStruct, $prefix.($index + 1));
490
            }
491
        } else {
492
            if ($this->getFetchAttachmentOption() === true) {
493
                $this->fetchAttachment($structure, $partNumber);
494
            }
495
        }
496
    }
497
498
    /**
499
     * Fetch the Message attachment
500
     *
501
     * @param object $structure
502
     * @param mixed  $partNumber
503
     */
504
    protected function fetchAttachment($structure, $partNumber) {
505
506
        $oAttachment = new Attachment($this, $structure, $partNumber);
507
508
        if ($oAttachment->getName() !== null) {
509
            if ($oAttachment->getId() !== null) {
510
                $this->attachments->put($oAttachment->getId(), $oAttachment);
511
            } else {
512
                $this->attachments->push($oAttachment);
513
            }
514
        }
515
    }
516
517
    /**
518
     * Fail proof setter for $fetch_option
519
     *
520
     * @param $option
521
     *
522
     * @return $this
523
     */
524
    public function setFetchOption($option) {
525
        if (is_long($option) === true) {
526
            $this->fetch_options = $option;
527
        } elseif (is_null($option) === true) {
528
            $config = config('imap.options.fetch', FT_UID);
529
            $this->fetch_options = is_long($config) ? $config : 1;
530
        }
531
532
        return $this;
533
    }
534
535
    /**
536
     * Fail proof setter for $fetch_body
537
     *
538
     * @param $option
539
     *
540
     * @return $this
541
     */
542
    public function setFetchBodyOption($option) {
543
        if (is_bool($option)) {
544
            $this->fetch_body = $option;
545
        } elseif (is_null($option)) {
546
            $config = config('imap.options.fetch_body', true);
547
            $this->fetch_body = is_bool($config) ? $config : true;
548
        }
549
550
        return $this;
551
    }
552
553
    /**
554
     * Fail proof setter for $fetch_attachment
555
     *
556
     * @param $option
557
     *
558
     * @return $this
559
     */
560
    public function setFetchAttachmentOption($option) {
561
        if (is_bool($option)) {
562
            $this->fetch_attachment = $option;
563
        } elseif (is_null($option)) {
564
            $config = config('imap.options.fetch_attachment', true);
565
            $this->fetch_attachment = is_bool($config) ? $config : true;
566
        }
567
568
        return $this;
569
    }
570
    
571
    /**
572
     * Fail proof setter for $fetch_flags
573
     *
574
     * @param $option
575
     *
576
     * @return $this
577
     */
578
    public function setFetchFlagsOption($option) {
579
        if (is_bool($option)) {
580
            $this->fetch_flags = $option;
581
        } elseif (is_null($option)) {
582
            $config = config('imap.options.fetch_flags', true);
583
            $this->fetch_flags = is_bool($config) ? $config : true;
584
        }
585
586
        return $this;
587
    }
588
589
    /**
590
     * Decode a given string
591
     *
592
     * @param $string
593
     * @param $encoding
594
     *
595
     * @return string
596
     */
597
    public function decodeString($string, $encoding) {
598
        switch ($encoding) {
599
            case self::ENC_7BIT:
600
                return $string;
601
            case self::ENC_8BIT:
602
                return quoted_printable_decode(imap_8bit($string));
603
            case self::ENC_BINARY:
604
                return imap_binary($string);
605
            case self::ENC_BASE64:
606
                return imap_base64($string);
607
            case self::ENC_QUOTED_PRINTABLE:
608
                return quoted_printable_decode($string);
609
            case self::ENC_OTHER:
610
                return $string;
611
            default:
612
                return $string;
613
        }
614
    }
615
    
616
    /**
617
     * Convert the encoding
618
     *
619
     * @param $str
620
     * @param string $from
621
     * @param string $to
622
     *
623
     * @return mixed|string
624
     */
625
    private function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") {
626
        if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
627
            return iconv(EncodingAliases::get($from), $to.'//IGNORE', $str);
628
        } else {
629
            if (!$from) {
630
                return mb_convert_encoding($str, $to);
631
            }
632
            return mb_convert_encoding($str, $to, $from);
633
        }
634
    }
635
636
    /**
637
     * Get the encoding of a given abject
638
     *
639
     * @param object $structure
640
     *
641
     * @return null|string
642
     */
643
    private function getEncoding($structure) {
644
        if (property_exists($structure, 'parameters')) {
645
            foreach ($structure->parameters as $parameter) {
646
                if (strtolower($parameter->attribute) == "charset") {
647
                    return strtoupper($parameter->value);
648
                }
649
            }
650
        }
651
        return null;
652
    }
653
654
    /**
655
     * Find the folder containing this message.
656
     *
657
     * @param null|Folder $folder where to start searching from (top-level inbox by default)
658
     * @return null|Folder
659
     */
660
    public function getContainingFolder(Folder $folder = null)
661
    {
662
        $folder = $folder ?: $this->client->getFolders()->first();
663
        $this->client->checkConnection();
664
665
        // Try finding the message by uid in the current folder
666
        $client = new Client;
667
        $client->openFolder($folder);
668
        $uidMatches = imap_fetch_overview($client->getConnection(), $this->uid, $this->fetch_options);
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

668
        $uidMatches = imap_fetch_overview(/** @scrutinizer ignore-type */ $client->getConnection(), $this->uid, $this->fetch_options);
Loading history...
669
        $uidMatch = count($uidMatches)
670
            ? new Message($uidMatches[0]->uid, $uidMatches[0]->msgno, $client)
671
            : null;
672
        $client->disconnect();
673
674
        // imap_fetch_overview() on a parent folder will return the matching message
675
        // even when the message is in a child folder so we need to recursively
676
        // search the children
677
        foreach ($folder->children as $child) {
678
            $childFolder = $this->getContainingFolder($child);
679
680
            if ($childFolder) {
681
                return $childFolder;
682
            }
683
        }
684
685
        // before returning the parent
686
        if ($this->is($uidMatch)) {
687
            return $folder;
688
        }
689
690
        // or signalling that the message was not found in any folder
691
        return null;
692
    }
693
694
    /**
695
     * Move the Message into an other Folder
696
     *
697
     * @param string  $mailbox
698
     * @param integer $options [optional]
699
     *
700
     * @return bool
701
     */
702
    public function moveToFolder($mailbox = 'INBOX', $options = 0) {
703
        $this->client->createFolder($mailbox);
704
705
        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

705
        return imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->msglist, $mailbox, $options);
Loading history...
706
    }
707
708
    /**
709
     * Delete the current Message
710
     *
711
     * @return bool
712
     */
713
    public function delete() {
714
        $status = imap_delete($this->client->getConnection(), $this->uid, $this->fetch_options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type boolean; 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

714
        $status = imap_delete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $this->fetch_options);
Loading history...
715
        $this->client->expunge();
716
717
        return $status;
718
    }
719
720
    /**
721
     * Restore a deleted Message
722
     *
723
     * @return bool
724
     */
725
    public function restore() {
726
        return imap_undelete($this->client->getConnection(), $this->message_no);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type boolean; 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

726
        return imap_undelete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->message_no);
Loading history...
727
    }
728
729
    /**
730
     * Get all message attachments.
731
     *
732
     * @return AttachmentCollection
733
     */
734
    public function getAttachments() {
735
        return $this->attachments;
736
    }
737
738
    /**
739
     * Checks if there are any attachments present
740
     *
741
     * @return boolean
742
     */
743
    public function hasAttachments() {
744
        return $this->attachments->isEmpty() === false;
745
    }
746
747
    /**
748
     * Set a given flag
749
     * @param string|array $flag
750
     *
751
     * @return bool
752
     */
753
    public function setFlag($flag) {
754
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
755
        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 boolean; 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

755
        return imap_setflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
756
    }
757
758
    /**
759
     * Unset a given flag
760
     * @param string|array $flag
761
     *
762
     * @return bool
763
     */
764
    public function unsetFlag($flag) {
765
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
766
        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 boolean; 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

766
        return imap_clearflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
767
    }
768
769
    /**
770
     * @return null|object|string
771
     */
772
    public function getRawBody() {
773
        if ($this->raw_body === null) {
774
            $this->raw_body = imap_fetchbody($this->client->getConnection(), $this->getMessageNo(), '');
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type boolean; 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

774
            $this->raw_body = imap_fetchbody(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getMessageNo(), '');
Loading history...
775
        }
776
777
        return $this->raw_body;
778
    }
779
780
    /**
781
     * @return string
782
     */
783
    public function getHeader() {
784
        return $this->header;
785
    }
786
787
    /**
788
     * @return Client
789
     */
790
    public function getClient() {
791
        return $this->client;
792
    }
793
794
    /**
795
     * @return integer
796
     */
797
    public function getUid() {
798
        return $this->uid;
799
    }
800
801
    /**
802
     * @return integer
803
     */
804
    public function getFetchOptions() {
805
        return $this->fetch_options;
806
    }
807
808
    /**
809
     * @return boolean
810
     */
811
    public function getFetchBodyOption() {
812
        return $this->fetch_body;
813
    }
814
815
    /**
816
     * @return boolean
817
     */
818
    public function getFetchAttachmentOption() {
819
        return $this->fetch_attachment;
820
    }
821
    
822
    /**
823
     * @return boolean
824
     */
825
    public function getFetchFlagsOption() {
826
        return $this->fetch_flags;
827
    }
828
829
    /**
830
     * @return int
831
     */
832
    public function getMsglist() {
833
        return $this->msglist;
834
    }
835
836
    /**
837
     * @return mixed
838
     */
839
    public function getMessageId() {
840
        return $this->message_id;
841
    }
842
843
    /**
844
     * @return int
845
     */
846
    public function getMessageNo() {
847
        return $this->message_no;
848
    }
849
850
    /**
851
     * @return string
852
     */
853
    public function getSubject() {
854
        return $this->subject;
855
    }
856
857
    /**
858
     * @return mixed
859
     */
860
    public function getReferences() {
861
        return $this->references;
862
    }
863
864
    /**
865
     * @return Carbon|null
866
     */
867
    public function getDate() {
868
        return $this->date;
869
    }
870
871
    /**
872
     * @return array
873
     */
874
    public function getFrom() {
875
        return $this->from;
876
    }
877
878
    /**
879
     * @return array
880
     */
881
    public function getTo() {
882
        return $this->to;
883
    }
884
885
    /**
886
     * @return array
887
     */
888
    public function getCc() {
889
        return $this->cc;
890
    }
891
892
    /**
893
     * @return array
894
     */
895
    public function getBcc() {
896
        return $this->bcc;
897
    }
898
899
    /**
900
     * @return array
901
     */
902
    public function getReplyTo() {
903
        return $this->reply_to;
904
    }
905
    
906
    /**
907
     * @return string
908
     */
909
    public function getInReplyTo() {
910
        return $this->in_reply_to;
911
    }
912
913
    /**
914
     * @return array
915
     */
916
    public function getSender() {
917
        return $this->sender;
918
    }
919
920
    /**
921
     * @return mixed
922
     */
923
    public function getBodies() {
924
        return $this->bodies;
925
    }
926
    
927
    /**
928
     * @return array
929
     */
930
    public function getFlags() {
931
        return $this->flags;
932
    }
933
934
    /**
935
     * Does this message match another one?
936
     *
937
     * A match means same uid, message id, subject and date/time.
938
     *
939
     * @param  null|static $message
940
     * @return boolean
941
     */
942
    public function is(Message $message = null)
943
    {
944
        if (is_null($message)) {
945
            return false;
946
        }
947
948
        return $this->uid == $message->uid
949
            && $this->message_id == $message->message_id
950
            && $this->subject == $message->subject
951
            && $this->date->eq($message->date);
952
    }
953
}
954