Completed
Pull Request — master (#102)
by
unknown
02:54
created

Message::getHeaderInfo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
rs 9.4285
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
             *
287
             * Please report any new invalid timestamps to [#45](https://github.com/Webklex/laravel-imap/issues/45)
288
             */
289
            try {
290
                $this->date = Carbon::parse($date);
291
            } catch (\Exception $e) {
292
                switch (true) {
293
                    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}\ \([A-Z]{2,3}\+[0-9]{1,2}\:[0-9]{1,2})\)+$/i', $date) > 0:
294
                        $array = explode('(', $date);
295
                        $array = array_reverse($array);
296
                        $date = trim(array_pop($array));
297
                        break;
298
                    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:
299
                        $date .= 'C';
300
                        break;
301
                }
302
                $this->date = Carbon::parse($date);
303
            }
304
        }
305
306
        if (property_exists($header, 'from')) {
307
            $this->from = $this->parseAddresses($header->from);
308
        }
309
        if (property_exists($header, 'to')) {
310
            $this->to = $this->parseAddresses($header->to);
311
        }
312
        if (property_exists($header, 'cc')) {
313
            $this->cc = $this->parseAddresses($header->cc);
314
        }
315
        if (property_exists($header, 'bcc')) {
316
            $this->bcc = $this->parseAddresses($header->bcc);
317
        }
318
        if (property_exists($header, 'references')) {
319
            $this->references = $header->references;
320
        }
321
322
        if (property_exists($header, 'reply_to')) {
323
            $this->reply_to = $this->parseAddresses($header->reply_to);
324
        }
325
        if (property_exists($header, 'in_reply_to')) {
326
            $this->in_reply_to = str_replace(['<', '>'], '', $header->in_reply_to);
327
        }
328
        if (property_exists($header, 'sender')) {
329
            $this->sender = $this->parseAddresses($header->sender);
330
        }
331
332
        if (property_exists($header, 'message_id')) {
333
            $this->message_id = str_replace(['<', '>'], '', $header->message_id);
334
        }
335
        if (property_exists($header, 'Msgno')) {
336
            $messageNo = (int) trim($header->Msgno);
337
            $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

337
            $this->message_no = ($this->fetch_options == FT_UID) ? $messageNo : imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $messageNo);
Loading history...
338
        } else {
339
            $this->message_no = imap_msgno($this->client->getConnection(), $this->getUid());
340
        }
341
    }
342
343
    /**
344
     * Parse additional flags
345
     *
346
     * @return object
347
     */
348
    private function parseFlags() {
349
        $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

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

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

412
            $address->full = ($address->personal) ? $address->personal.' <'./** @scrutinizer ignore-type */ $address->mail.'>' : $address->mail;
Loading history...
413
414
            $addresses[] = $address;
415
        }
416
417
        return $addresses;
418
    }
419
420
    /**
421
     * Parse the Message body
422
     *
423
     * @return $this
424
     */
425
    public function parseBody() {
426
        $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

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

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

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

703
        return imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->msglist, $mailbox, $options);
Loading history...
704
    }
705
706
    /**
707
     * Delete the current Message
708
     *
709
     * @return bool
710
     */
711
    public function delete() {
712
        $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

712
        $status = imap_delete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $this->fetch_options);
Loading history...
713
        $this->client->expunge();
714
715
        return $status;
716
    }
717
718
    /**
719
     * Restore a deleted Message
720
     *
721
     * @return bool
722
     */
723
    public function restore() {
724
        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

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

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

764
        return imap_clearflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
765
    }
766
767
    /**
768
     * @return null|object|string
769
     */
770
    public function getRawBody() {
771
        if ($this->raw_body === null) {
772
            $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

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