Passed
Push — master ( 310f46...0fdd8f )
by Malte
05:34
created

Message::parseHeader()   F

Complexity

Conditions 12
Paths 480

Size

Total Lines 44
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 20
Bugs 2 Features 7
Metric Value
cc 12
eloc 28
c 20
b 2
f 7
nc 480
nop 0
dl 0
loc 44
rs 3.5222

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/*
3
* 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 Illuminate\Support\Str;
17
use Webklex\IMAP\Events\MessageDeletedEvent;
18
use Webklex\IMAP\Events\MessageMovedEvent;
19
use Webklex\IMAP\Events\MessageRestoredEvent;
20
use Webklex\IMAP\Exceptions\InvalidMessageDateException;
21
use Webklex\IMAP\Exceptions\MaskNotFoundException;
22
use Webklex\IMAP\Exceptions\MethodNotFoundException;
23
use Webklex\IMAP\Support\AttachmentCollection;
24
use Webklex\IMAP\Support\FlagCollection;
25
use Webklex\IMAP\Support\Masks\MessageMask;
26
27
/**
28
 * Class Message
29
 *
30
 * @package Webklex\IMAP
31
 *
32
 * @property integer msglist
33
 * @property integer uid
34
 * @property integer msgn
35
 * @property integer priority
36
 * @property string subject
37
 * @property string message_id
38
 * @property string message_no
39
 * @property string references
40
 * @property carbon date
41
 * @property array from
42
 * @property array to
43
 * @property array cc
44
 * @property array bcc
45
 * @property array reply_to
46
 * @property array in_reply_to
47
 * @property array sender
48
 * @property string fallback_encoding
49
 *
50
 * @method integer getMsglist()
51
 * @method integer setMsglist(integer $msglist)
52
 * @method integer getUid()
53
 * @method integer setUid(integer $uid)
54
 * @method integer getMsgn()
55
 * @method integer setMsgn(integer $msgn)
56
 * @method integer getPriority()
57
 * @method integer setPriority(integer $priority)
58
 * @method string getSubject()
59
 * @method string setSubject(string $subject)
60
 * @method string getMessageId()
61
 * @method string setMessageId(string $message_id)
62
 * @method string getMessageNo()
63
 * @method string setMessageNo(string $message_no)
64
 * @method string getReferences()
65
 * @method string setReferences(string $references)
66
 * @method carbon getDate()
67
 * @method carbon setDate(carbon $date)
68
 * @method array getFrom()
69
 * @method array setFrom(array $from)
70
 * @method array getTo()
71
 * @method array setTo(array $to)
72
 * @method array getCc()
73
 * @method array setCc(array $cc)
74
 * @method array getBcc()
75
 * @method array setBcc(array $bcc)
76
 * @method array getReplyTo()
77
 * @method array setReplyTo(array $reply_to)
78
 * @method array getInReplyTo()
79
 * @method array setInReplyTo(array $in_reply_to)
80
 * @method array getSender()
81
 * @method array setSender(array $sender)
82
 */
83
class Message {
84
85
    /**
86
     * Client instance
87
     *
88
     * @var Client
89
     */
90
    private $client = Client::class;
91
92
    /**
93
     * Default mask
94
     * @var string $mask
95
     */
96
    protected $mask = MessageMask::class;
97
98
    /** @var array $config */
99
    protected $config = [];
100
101
    /** @var array $attributes */
102
    protected $attributes = [
103
        'message_id' => '',
104
        'message_no' => null,
105
        'subject' => '',
106
        'references' => null,
107
        'date' => null,
108
        'from' => [],
109
        'to' => [],
110
        'cc' => [],
111
        'bcc' => [],
112
        'reply_to' => [],
113
        'in_reply_to' => '',
114
        'sender' => [],
115
        'priority' => 0,
116
    ];
117
118
    /**
119
     * The message folder path
120
     *
121
     * @var string $folder_path
122
     */
123
    protected $folder_path;
124
125
    /**
126
     * Fetch body options
127
     *
128
     * @var integer
129
     */
130
    public $fetch_options = null;
131
132
    /**
133
     * Fetch body options
134
     *
135
     * @var bool
136
     */
137
    public $fetch_body = null;
138
139
    /**
140
     * Fetch attachments options
141
     *
142
     * @var bool
143
     */
144
    public $fetch_attachment = null;
145
146
    /**
147
     * Fetch flags options
148
     *
149
     * @var bool
150
     */
151
    public $fetch_flags = null;
152
153
    /**
154
     * @var string $header
155
     */
156
    public $header = null;
157
158
    /**
159
     * @var null|object $header_info
160
     */
161
    public $header_info = null;
162
163
    /** @var null|string $raw_body */
164
    public $raw_body = null;
165
166
    /** @var null $structure */
167
    protected $structure = null;
168
169
    /**
170
     * Message body components
171
     *
172
     * @var array   $bodies
173
     * @var AttachmentCollection|array $attachments
174
     * @var FlagCollection|array       $flags
175
     */
176
    public $bodies = [];
177
    public $attachments = [];
178
    public $flags = [];
179
180
    /**
181
     * Fallback Encoding
182
     * @var string
183
     */
184
    public $fallback_encoding = 'UTF-8';
185
186
    /**
187
     * A list of all available and supported flags
188
     *
189
     * @var array $available_flags
190
     */
191
    private $available_flags = ['recent', 'flagged', 'answered', 'deleted', 'seen', 'draft'];
192
193
    /**
194
     * Message constructor.
195
     *
196
     * @param integer       $uid
197
     * @param integer|null  $msglist
198
     * @param Client        $client
199
     * @param integer|null  $fetch_options
200
     * @param boolean       $fetch_body
201
     * @param boolean       $fetch_attachment
202
     * @param boolean       $fetch_flags
203
     *
204
     * @throws Exceptions\ConnectionFailedException
205
     * @throws InvalidMessageDateException
206
     */
207
    public function __construct($uid, $msglist, Client $client, $fetch_options = null, $fetch_body = null, $fetch_attachment = null, $fetch_flags = null) {
208
209
        $default_mask = $client->getDefaultMessageMask();
210
        if($default_mask != null) {
211
            $this->mask = $default_mask;
212
        }
213
214
        $this->folder_path = $client->getFolderPath();
0 ignored issues
show
Documentation Bug introduced by
It seems like $client->getFolderPath() of type Webklex\IMAP\Folder is incompatible with the declared type string of property $folder_path.

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

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

Loading history...
215
216
        $this->config = config('imap.options');
217
218
        $this->setFetchOption($fetch_options);
219
        $this->setFetchBodyOption($fetch_body);
220
        $this->setFetchAttachmentOption($fetch_attachment);
221
        $this->setFetchFlagsOption($fetch_flags);
222
223
        $this->attachments = AttachmentCollection::make([]);
224
        $this->flags = FlagCollection::make([]);
225
226
        $this->msglist = $msglist;
227
        $this->client = $client;
228
229
        $this->uid =  $uid;
230
        $this->msgn = ($this->fetch_options == IMAP::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

230
        $this->msgn = ($this->fetch_options == IMAP::FT_UID) ? \imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $uid) : $uid;
Loading history...
231
232
        $this->parseHeader();
233
234
        if ($this->getFetchFlagsOption() === true) {
235
            $this->parseFlags();
236
        }
237
238
        if ($this->getFetchBodyOption() === true) {
239
            $this->parseBody();
240
        }
241
    }
242
243
    /**
244
     * Call dynamic attribute setter and getter methods
245
     * @param string $method
246
     * @param array $arguments
247
     *
248
     * @return mixed
249
     * @throws MethodNotFoundException
250
     */
251
    public function __call($method, $arguments) {
252
        if(strtolower(substr($method, 0, 3)) === 'get') {
253
            $name = Str::snake(substr($method, 3));
254
255
            if(in_array($name, array_keys($this->attributes))) {
256
                return $this->attributes[$name];
257
            }
258
259
        }elseif (strtolower(substr($method, 0, 3)) === 'set') {
260
            $name = Str::snake(substr($method, 3));
261
262
            if(in_array($name, array_keys($this->attributes))) {
263
                $this->attributes[$name] = array_pop($arguments);
264
265
                return $this->attributes[$name];
266
            }
267
268
        }
269
270
        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
271
    }
272
273
    /**
274
     * @param $name
275
     * @param $value
276
     *
277
     * @return mixed
278
     */
279
    public function __set($name, $value) {
280
        $this->attributes[$name] = $value;
281
282
        return $this->attributes[$name];
283
    }
284
285
    /**
286
     * @param $name
287
     *
288
     * @return mixed|null
289
     */
290
    public function __get($name) {
291
        if(isset($this->attributes[$name])) {
292
            return $this->attributes[$name];
293
        }
294
295
        return null;
296
    }
297
298
    /**
299
     * Copy the current Messages to a mailbox
300
     *
301
     * @param $mailbox
302
     * @param int $options
303
     *
304
     * @return bool
305
     * @throws Exceptions\ConnectionFailedException
306
     */
307
    public function copy($mailbox, $options = 0) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

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

307
    public function copy($mailbox, /** @scrutinizer ignore-unused */ $options = 0) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
308
        $this->client->openFolder($this->folder_path);
309
        return \imap_mail_copy($this->client->getConnection(), $this->uid, $mailbox, IMAP::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_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

309
        return \imap_mail_copy(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $mailbox, IMAP::CP_UID);
Loading history...
310
    }
311
312
    /**
313
     * Move the current Messages to a mailbox
314
     *
315
     * @param $mailbox
316
     * @param int $options
317
     *
318
     * @return bool
319
     * @throws Exceptions\ConnectionFailedException
320
     */
321
    public function move($mailbox, $options = 0) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

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

321
    public function move($mailbox, /** @scrutinizer ignore-unused */ $options = 0) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
322
        $this->client->openFolder($this->folder_path);
323
        return \imap_mail_move($this->client->getConnection(), $this->uid, $mailbox, IMAP::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

323
        return \imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $mailbox, IMAP::CP_UID);
Loading history...
324
    }
325
326
    /**
327
     * Check if the Message has a text body
328
     *
329
     * @return bool
330
     */
331
    public function hasTextBody() {
332
        return isset($this->bodies['text']);
333
    }
334
335
    /**
336
     * Get the Message text body
337
     *
338
     * @return mixed
339
     */
340
    public function getTextBody() {
341
        if (!isset($this->bodies['text'])) {
342
            return false;
343
        }
344
345
        return $this->bodies['text']->content;
346
    }
347
348
    /**
349
     * Check if the Message has a html body
350
     *
351
     * @return bool
352
     */
353
    public function hasHTMLBody() {
354
        return isset($this->bodies['html']);
355
    }
356
357
    /**
358
     * Get the Message html body
359
     *
360
     * @return string|null
361
     */
362
    public function getHTMLBody() {
363
        if (!isset($this->bodies['html'])) {
364
            return null;
365
        }
366
        return $this->bodies['html']->content;
367
    }
368
369
    /**
370
     * Parse all defined headers
371
     *
372
     * @return void
373
     * @throws Exceptions\ConnectionFailedException
374
     * @throws InvalidMessageDateException
375
     */
376
    private function parseHeader() {
377
        $this->client->openFolder($this->folder_path);
378
        $this->header = $header = \imap_fetchheader($this->client->getConnection(), $this->uid, IMAP::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

378
        $this->header = $header = \imap_fetchheader(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
379
380
        $this->priority = $this->extractPriority($this->header);
381
382
        if ($this->header) {
383
            $header = \imap_rfc822_parse_headers($this->header);
384
        }
385
386
        if (property_exists($header, 'subject')) {
387
            if($this->config['decoder']['message']['subject'] === 'utf-8') {
388
                $this->subject = \imap_utf8($header->subject);
389
                if (Str::startsWith(mb_strtolower($this->subject), '=?utf-8?')) {
390
                    $this->subject = mb_decode_mimeheader($header->subject);
391
                }
392
            }elseif($this->config['decoder']['message']['subject'] === 'iconv') {
393
                $this->subject = iconv_mime_decode($header->subject);
394
            }else{
395
                $this->subject = mb_decode_mimeheader($header->subject);
396
            }
397
        }
398
399
        foreach(['from', 'to', 'cc', 'bcc', 'reply_to', 'sender'] as $part){
400
            $this->extractHeaderAddressPart($header, $part);
0 ignored issues
show
Bug introduced by
It seems like $header can also be of type string; however, parameter $header of Webklex\IMAP\Message::extractHeaderAddressPart() does only seem to accept object, 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

400
            $this->extractHeaderAddressPart(/** @scrutinizer ignore-type */ $header, $part);
Loading history...
401
        }
402
403
        if (property_exists($header, 'references')) {
404
            $this->references = $header->references;
405
        }
406
        if (property_exists($header, 'in_reply_to')) {
407
            $this->in_reply_to = str_replace(['<', '>'], '', $header->in_reply_to);
408
        }
409
        if (property_exists($header, 'message_id')) {
410
            $this->message_id = str_replace(['<', '>'], '', $header->message_id);
411
        }
412
        if (property_exists($header, 'Msgno')) {
413
            $messageNo = (int) trim($header->Msgno);
414
            $this->message_no = ($this->fetch_options == IMAP::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

414
            $this->message_no = ($this->fetch_options == IMAP::FT_UID) ? $messageNo : \imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $messageNo);
Loading history...
415
        } else {
416
            $this->message_no = \imap_msgno($this->client->getConnection(), $this->getUid());
417
        }
418
419
        $this->date = $this->parseDate($header);
0 ignored issues
show
Bug introduced by
It seems like $header can also be of type string; however, parameter $header of Webklex\IMAP\Message::parseDate() does only seem to accept object, 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

419
        $this->date = $this->parseDate(/** @scrutinizer ignore-type */ $header);
Loading history...
420
    }
421
422
    /**
423
     * Try to extract the priority from a given raw header string
424
     * @param string $header
425
     *
426
     * @return int|null
427
     */
428
    private function extractPriority($header) {
429
        if(preg_match('/x\-priority\:.*([0-9]{1,2})/i', $header, $priority)){
430
            $priority = isset($priority[1]) ? (int) $priority[1] : 0;
431
            switch($priority){
432
                case IMAP::MESSAGE_PRIORITY_HIGHEST;
433
                    $priority = IMAP::MESSAGE_PRIORITY_HIGHEST;
434
                    break;
435
                case IMAP::MESSAGE_PRIORITY_HIGH;
436
                    $priority = IMAP::MESSAGE_PRIORITY_HIGH;
437
                    break;
438
                case IMAP::MESSAGE_PRIORITY_NORMAL;
439
                    $priority = IMAP::MESSAGE_PRIORITY_NORMAL;
440
                    break;
441
                case IMAP::MESSAGE_PRIORITY_LOW;
442
                    $priority = IMAP::MESSAGE_PRIORITY_LOW;
443
                    break;
444
                case IMAP::MESSAGE_PRIORITY_LOWEST;
445
                    $priority = IMAP::MESSAGE_PRIORITY_LOWEST;
446
                    break;
447
                default:
448
                    $priority = IMAP::MESSAGE_PRIORITY_UNKNOWN;
449
                    break;
450
            }
451
        }
452
453
        return $priority;
454
    }
455
456
    /**
457
     * Exception handling for invalid dates
458
     *
459
     * Currently known invalid formats:
460
     * ^ Datetime                                   ^ Problem                           ^ Cause
461
     * | Mon, 20 Nov 2017 20:31:31 +0800 (GMT+8:00) | Double timezone specification     | A Windows feature
462
     * | Thu, 8 Nov 2018 08:54:58 -0200 (-02)       |
463
     * |                                            | and invalid timezone (max 6 char) |
464
     * | 04 Jan 2018 10:12:47 UT                    | Missing letter "C"                | Unknown
465
     * | Thu, 31 May 2018 18:15:00 +0800 (added by) | Non-standard details added by the | Unknown
466
     * |                                            | mail server                       |
467
     * | Sat, 31 Aug 2013 20:08:23 +0580            | Invalid timezone                  | PHPMailer bug https://sourceforge.net/p/phpmailer/mailman/message/6132703/
468
     *
469
     * Please report any new invalid timestamps to [#45](https://github.com/Webklex/laravel-imap/issues/45)
470
     *
471
     * @param object $header
472
     *
473
     * @return Carbon|null
474
     * @throws InvalidMessageDateException
475
     */
476
    private function parseDate($header) {
477
        $parsed_date = null;
478
479
        if (property_exists($header, 'date')) {
480
            $date = $header->date;
481
482
            if(preg_match('/\+0580/', $date)) {
483
                $date = str_replace('+0580', '+0530', $date);
484
            }
485
486
            $date = trim(rtrim($date));
487
            try {
488
                $parsed_date = Carbon::parse($date);
489
            } catch (\Exception $e) {
490
                switch (true) {
491
                    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:
492
                    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}\ UT)+$/i', $date) > 0:
493
                        $date .= 'C';
494
                        break;
495
                    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]{2,4}\ \(\+[0-9]{1,2}\))+$/i', $date) > 0:
496
                    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}.*)+$/i', $date) > 0:
497
                    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:
498
                    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:
499
                    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:
500
                        $array = explode('(', $date);
501
                        $array = array_reverse($array);
502
                        $date = trim(array_pop($array));
503
                        break;
504
                }
505
                try{
506
                    $parsed_date = Carbon::parse($date);
507
                } catch (\Exception $_e) {
508
                    throw new InvalidMessageDateException("Invalid message date. ID:".$this->getMessageId(), 1100, $e);
509
                }
510
            }
511
        }
512
513
        return $parsed_date;
514
    }
515
516
    /**
517
     * Parse additional flags
518
     *
519
     * @return void
520
     * @throws Exceptions\ConnectionFailedException
521
     */
522
    private function parseFlags() {
523
        $this->flags = FlagCollection::make([]);
524
525
        $this->client->openFolder($this->folder_path);
526
        $flags = \imap_fetch_overview($this->client->getConnection(), $this->uid, IMAP::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

526
        $flags = \imap_fetch_overview(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
527
        if (is_array($flags) && isset($flags[0])) {
528
            foreach($this->available_flags as $flag) {
529
                $this->parseFlag($flags, $flag);
530
            }
531
        }
532
    }
533
534
    /**
535
     * Extract a possible flag information from a given array
536
     * @param array $flags
537
     * @param string $flag
538
     */
539
    private function parseFlag($flags, $flag) {
540
        $flag = strtolower($flag);
541
542
        if (property_exists($flags[0], strtoupper($flag))) {
543
            $this->flags->put($flag, $flags[0]->{strtoupper($flag)});
544
        } elseif (property_exists($flags[0], ucfirst($flag))) {
545
            $this->flags->put($flag, $flags[0]->{ucfirst($flag)});
546
        } elseif (property_exists($flags[0], $flag)) {
547
            $this->flags->put($flag, $flags[0]->$flag);
548
        }
549
    }
550
551
    /**
552
     * Get the current Message header info
553
     *
554
     * @return object
555
     * @throws Exceptions\ConnectionFailedException
556
     */
557
    public function getHeaderInfo() {
558
        if ($this->header_info == null) {
559
            $this->client->openFolder($this->folder_path);
560
            $this->header_info = \imap_headerinfo($this->client->getConnection(), $this->getMessageNo());
0 ignored issues
show
Bug introduced by
$this->getMessageNo() of type string is incompatible with the type integer expected by parameter $msg_no of imap_headerinfo(). ( Ignorable by Annotation )

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

560
            $this->header_info = \imap_headerinfo($this->client->getConnection(), /** @scrutinizer ignore-type */ $this->getMessageNo());
Loading history...
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

560
            $this->header_info = \imap_headerinfo(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getMessageNo());
Loading history...
561
        }
562
563
        return $this->header_info;
564
    }
565
566
    /**
567
     * Extract a given part as address array from a given header
568
     * @param object $header
569
     * @param string $part
570
     */
571
    private function extractHeaderAddressPart($header, $part) {
572
        if (property_exists($header, $part)) {
573
            $this->$part = $this->parseAddresses($header->$part);
574
        }
575
    }
576
577
    /**
578
     * Parse Addresses
579
     * @param $list
580
     *
581
     * @return array
582
     */
583
    private function parseAddresses($list) {
584
        $addresses = [];
585
586
        foreach ($list as $item) {
587
            $address = (object) $item;
588
589
            if (!property_exists($address, 'mailbox')) {
590
                $address->mailbox = false;
591
            }
592
            if (!property_exists($address, 'host')) {
593
                $address->host = false;
594
            }
595
            if (!property_exists($address, 'personal')) {
596
                $address->personal = false;
597
            } else {
598
                $personalParts = \imap_mime_header_decode($address->personal);
599
600
                if(is_array($personalParts)) {
601
                    $address->personal = '';
602
                    foreach ($personalParts as $p) {
603
                        $address->personal .= $this->convertEncoding($p->text, $this->getEncoding($p));
604
                    }
605
                }
606
            }
607
608
            $address->mail = ($address->mailbox && $address->host) ? $address->mailbox.'@'.$address->host : false;
609
            $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

609
            $address->full = ($address->personal) ? $address->personal.' <'./** @scrutinizer ignore-type */ $address->mail.'>' : $address->mail;
Loading history...
610
611
            $addresses[] = $address;
612
        }
613
614
        return $addresses;
615
    }
616
617
    /**
618
     * Parse the Message body
619
     *
620
     * @return $this
621
     * @throws Exceptions\ConnectionFailedException
622
     */
623
    public function parseBody() {
624
        $this->client->openFolder($this->folder_path);
625
626
        $this->structure = \imap_fetchstructure($this->client->getConnection(), $this->uid, IMAP::FT_UID);
0 ignored issues
show
Documentation Bug introduced by
It seems like imap_fetchstructure($thi...klex\IMAP\IMAP::FT_UID) of type object is incompatible with the declared type null of property $structure.

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

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

Loading history...
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

626
        $this->structure = \imap_fetchstructure(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
627
        $this->fetchStructure($this->structure);
628
629
        return $this;
630
    }
631
632
    /**
633
     * Fetch the Message structure
634
     *
635
     * @param $structure
636
     * @param mixed $partNumber
637
     *
638
     * @throws Exceptions\ConnectionFailedException
639
     */
640
    private function fetchStructure($structure, $partNumber = null) {
641
        $this->client->openFolder($this->folder_path);
642
643
        if ($structure->type == IMAP::MESSAGE_TYPE_TEXT &&
644
            (empty($structure->disposition) || strtolower($structure->disposition) != 'attachment')
645
        ) {
646
            if (strtolower($structure->subtype) == "plain" || strtolower($structure->subtype) == "csv") {
647
                $this->bodies['text'] = $this->createBody("text", $structure, $partNumber);
648
                $this->fetchAttachment($structure, $partNumber);
649
            } elseif (strtolower($structure->subtype) == "html") {
650
                $this->bodies['html'] = $this->createBody("html", $structure, $partNumber);
651
            } elseif ($structure->ifdisposition == 1 && strtolower($structure->disposition) == 'attachment') {
652
                if ($this->getFetchAttachmentOption() === true) {
653
                    $this->fetchAttachment($structure, $partNumber);
654
                }
655
            }
656
        } elseif ($structure->type == IMAP::MESSAGE_TYPE_MULTIPART) {
657
            foreach ($structure->parts as $index => $subStruct) {
658
                $prefix = "";
659
                if ($partNumber) {
660
                    $prefix = $partNumber.".";
661
                }
662
                $this->fetchStructure($subStruct, $prefix.($index + 1));
663
            }
664
        } else if ($this->getFetchAttachmentOption() === true) {
665
            $this->fetchAttachment($structure, $partNumber);
666
        }
667
    }
668
669
    /**
670
     * Create a new body object of a given type
671
     * @param string $type
672
     * @param object $structure
673
     * @param mixed $partNumber
674
     *
675
     * @return object
676
     * @throws Exceptions\ConnectionFailedException
677
     */
678
    private function createBody($type, $structure, $partNumber){
679
        return (object) [
680
            "type" => $type,
681
            "content" => $this->fetchPart($structure, $partNumber),
682
        ];
683
    }
684
685
    /**
686
     * Fetch the content of a given part and message structure
687
     * @param object $structure
688
     * @param mixed $partNumber
689
     *
690
     * @return mixed|string
691
     * @throws Exceptions\ConnectionFailedException
692
     */
693
    private function fetchPart($structure, $partNumber){
694
        $encoding = $this->getEncoding($structure);
695
696
        if (!$partNumber) {
697
            $partNumber = 1;
698
        }
699
700
        $content = \imap_fetchbody($this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options | IMAP::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

700
        $content = \imap_fetchbody(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $partNumber, $this->fetch_options | IMAP::FT_UID);
Loading history...
701
        $content = $this->decodeString($content, $structure->encoding);
702
703
        // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
704
        //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
705
        //     https://stackoverflow.com/a/11303410
706
        //
707
        // us-ascii is the same as ASCII:
708
        //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
709
        //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
710
        //     based on the typographical symbols predominantly in use there.
711
        //     https://en.wikipedia.org/wiki/ASCII
712
        //
713
        // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
714
        if ($encoding != 'us-ascii') {
715
            $content = $this->convertEncoding($content, $encoding);
716
        }
717
718
        return $content;
719
    }
720
721
    /**
722
     * Fetch the Message attachment
723
     *
724
     * @param object $structure
725
     * @param mixed  $partNumber
726
     *
727
     * @throws Exceptions\ConnectionFailedException
728
     */
729
    protected function fetchAttachment($structure, $partNumber) {
730
731
        $oAttachment = new Attachment($this, $structure, $partNumber);
732
733
        if ($oAttachment->getName() !== null) {
0 ignored issues
show
introduced by
The condition $oAttachment->getName() !== null is always true.
Loading history...
734
            if ($oAttachment->getId() !== null) {
0 ignored issues
show
introduced by
The condition $oAttachment->getId() !== null is always true.
Loading history...
735
                $this->attachments->put($oAttachment->getId(), $oAttachment);
736
            } else {
737
                $this->attachments->push($oAttachment);
738
            }
739
        }
740
    }
741
742
    /**
743
     * Fail proof setter for $fetch_option
744
     *
745
     * @param $option
746
     *
747
     * @return $this
748
     */
749
    public function setFetchOption($option) {
750
        if (is_long($option) === true) {
751
            $this->fetch_options = $option;
752
        } elseif (is_null($option) === true) {
753
            $config = config('imap.options.fetch', IMAP::FT_UID);
754
            $this->fetch_options = is_long($config) ? $config : 1;
755
        }
756
757
        return $this;
758
    }
759
760
    /**
761
     * Fail proof setter for $fetch_body
762
     *
763
     * @param $option
764
     *
765
     * @return $this
766
     */
767
    public function setFetchBodyOption($option) {
768
        if (is_bool($option)) {
769
            $this->fetch_body = $option;
770
        } elseif (is_null($option)) {
771
            $config = config('imap.options.fetch_body', true);
772
            $this->fetch_body = is_bool($config) ? $config : true;
773
        }
774
775
        return $this;
776
    }
777
778
    /**
779
     * Fail proof setter for $fetch_attachment
780
     *
781
     * @param $option
782
     *
783
     * @return $this
784
     */
785
    public function setFetchAttachmentOption($option) {
786
        if (is_bool($option)) {
787
            $this->fetch_attachment = $option;
788
        } elseif (is_null($option)) {
789
            $config = config('imap.options.fetch_attachment', true);
790
            $this->fetch_attachment = is_bool($config) ? $config : true;
791
        }
792
793
        return $this;
794
    }
795
796
    /**
797
     * Fail proof setter for $fetch_flags
798
     *
799
     * @param $option
800
     *
801
     * @return $this
802
     */
803
    public function setFetchFlagsOption($option) {
804
        if (is_bool($option)) {
805
            $this->fetch_flags = $option;
806
        } elseif (is_null($option)) {
807
            $config = config('imap.options.fetch_flags', true);
808
            $this->fetch_flags = is_bool($config) ? $config : true;
809
        }
810
811
        return $this;
812
    }
813
814
    /**
815
     * Decode a given string
816
     *
817
     * @param $string
818
     * @param $encoding
819
     *
820
     * @return string
821
     */
822
    public function decodeString($string, $encoding) {
823
        switch ($encoding) {
824
            case IMAP::MESSAGE_ENC_8BIT:
825
                return quoted_printable_decode(\imap_8bit($string));
826
            case IMAP::MESSAGE_ENC_BINARY:
827
                return \imap_binary($string);
828
            case IMAP::MESSAGE_ENC_BASE64:
829
                return \imap_base64($string);
830
            case IMAP::MESSAGE_ENC_QUOTED_PRINTABLE:
831
                return quoted_printable_decode($string);
832
            case IMAP::MESSAGE_ENC_7BIT:
833
            case IMAP::MESSAGE_ENC_OTHER:
834
            default:
835
                return $string;
836
        }
837
    }
838
839
    /**
840
     * Convert the encoding
841
     *
842
     * @param $str
843
     * @param string $from
844
     * @param string $to
845
     *
846
     * @return mixed|string
847
     */
848
    public function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") {
849
850
        $from = EncodingAliases::get($from, $this->fallback_encoding);
851
        $to = EncodingAliases::get($to, $this->fallback_encoding);
852
853
        if ($from === $to) {
854
            return $str;
855
        }
856
857
        // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
858
        //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
859
        //     https://stackoverflow.com/a/11303410
860
        //
861
        // us-ascii is the same as ASCII:
862
        //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
863
        //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
864
        //     based on the typographical symbols predominantly in use there.
865
        //     https://en.wikipedia.org/wiki/ASCII
866
        //
867
        // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
868
        if (strtolower($from) == 'us-ascii' && $to == 'UTF-8') {
869
            return $str;
870
        }
871
872
        try {
873
            if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
874
                return iconv($from, $to, $str);
875
            } else {
876
                if (!$from) {
877
                    return mb_convert_encoding($str, $to);
878
                }
879
                return mb_convert_encoding($str, $to, $from);
880
            }
881
        } catch (\Exception $e) {
882
            if (strstr($from, '-')) {
883
                $from = str_replace('-', '', $from);
884
                return $this->convertEncoding($str, $from, $to);
885
            } else {
886
                return $str;
887
            }
888
        }
889
    }
890
891
    /**
892
     * Get the encoding of a given abject
893
     *
894
     * @param object|string $structure
895
     *
896
     * @return string
897
     */
898
    public function getEncoding($structure) {
899
        if (property_exists($structure, 'parameters')) {
900
            foreach ($structure->parameters as $parameter) {
901
                if (strtolower($parameter->attribute) == "charset") {
902
                    return EncodingAliases::get($parameter->value, $this->fallback_encoding);
903
                }
904
            }
905
        }elseif (property_exists($structure, 'charset')) {
906
            return EncodingAliases::get($structure->charset, $this->fallback_encoding);
907
        }elseif (is_string($structure) === true){
908
            return mb_detect_encoding($structure);
909
        }
910
911
        return $this->fallback_encoding;
912
    }
913
914
    /**
915
     * Find the folder containing this message.
916
     * @param null|Folder $folder where to start searching from (top-level inbox by default)
917
     *
918
     * @return mixed|null|Folder
919
     * @throws Exceptions\ConnectionFailedException
920
     * @throws Exceptions\MailboxFetchingException
921
     * @throws InvalidMessageDateException
922
     * @throws MaskNotFoundException
923
     */
924
    public function getContainingFolder(Folder $folder = null) {
925
        $folder = $folder ?: $this->client->getFolders()->first();
926
        $this->client->checkConnection();
927
928
        // Try finding the message by uid in the current folder
929
        $client = new Client;
930
        $client->openFolder($folder->path);
931
        $uidMatches = \imap_fetch_overview($client->getConnection(), $this->uid, IMAP::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

931
        $uidMatches = \imap_fetch_overview(/** @scrutinizer ignore-type */ $client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
932
        $uidMatch = count($uidMatches)
933
            ? new Message($uidMatches[0]->uid, $uidMatches[0]->msgno, $client)
934
            : null;
935
        $client->disconnect();
936
937
        // \imap_fetch_overview() on a parent folder will return the matching message
938
        // even when the message is in a child folder so we need to recursively
939
        // search the children
940
        foreach ($folder->children as $child) {
941
            $childFolder = $this->getContainingFolder($child);
942
943
            if ($childFolder) {
944
                return $childFolder;
945
            }
946
        }
947
948
        // before returning the parent
949
        if ($this->is($uidMatch)) {
950
            return $folder;
951
        }
952
953
        // or signalling that the message was not found in any folder
954
        return null;
955
    }
956
957
    public function getFolder(){
958
        return $this->client->getFolder($this->folder_path);
959
    }
960
961
    /**
962
     * Move the Message into an other Folder
963
     * @param string $mailbox
964
     * @param bool $expunge
965
     * @param bool $create_folder
966
     *
967
     * @return null|Message
968
     * @throws Exceptions\ConnectionFailedException
969
     * @throws InvalidMessageDateException
970
     */
971
    public function moveToFolder($mailbox = 'INBOX', $expunge = false, $create_folder = true) {
972
973
        if($create_folder) $this->client->createFolder($mailbox, true);
974
975
        $target_folder = $this->client->getFolder($mailbox);
976
        $target_status = $target_folder->getStatus(IMAP::SA_ALL);
977
978
        $this->client->openFolder($this->folder_path);
979
        $status = \imap_mail_move($this->client->getConnection(), $this->uid, $mailbox, IMAP::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

979
        $status = \imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $mailbox, IMAP::CP_UID);
Loading history...
980
981
        if($status === true){
982
            if($expunge) $this->client->expunge();
983
            $this->client->openFolder($target_folder->path);
984
985
            $message = $target_folder->getMessage($target_status->uidnext, null, $this->fetch_options, $this->fetch_body, $this->fetch_attachment, $this->fetch_flags);
986
            MessageMovedEvent::dispatch($this, $message);
987
            return $message;
988
        }
989
990
        return null;
991
    }
992
993
    /**
994
     * Delete the current Message
995
     * @param bool $expunge
996
     *
997
     * @return bool
998
     * @throws Exceptions\ConnectionFailedException
999
     */
1000
    public function delete($expunge = true) {
1001
        $this->client->openFolder($this->folder_path);
1002
1003
        $status = \imap_delete($this->client->getConnection(), $this->uid, IMAP::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

1003
        $status = \imap_delete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
1004
        if($expunge) $this->client->expunge();
1005
        MessageDeletedEvent::dispatch($this);
1006
1007
        return $status;
1008
    }
1009
1010
    /**
1011
     * Restore a deleted Message
1012
     * @param boolean $expunge
1013
     *
1014
     * @return bool
1015
     * @throws Exceptions\ConnectionFailedException
1016
     */
1017
    public function restore($expunge = true) {
1018
        $this->client->openFolder($this->folder_path);
1019
1020
        $status = \imap_undelete($this->client->getConnection(), $this->uid, IMAP::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

1020
        $status = \imap_undelete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
1021
        if($expunge) $this->client->expunge();
1022
        MessageRestoredEvent::dispatch($this);
1023
1024
        return $status;
1025
    }
1026
1027
    /**
1028
     * Get all message attachments.
1029
     *
1030
     * @return AttachmentCollection
1031
     */
1032
    public function getAttachments() {
1033
        return $this->attachments;
1034
    }
1035
1036
    /**
1037
     * Checks if there are any attachments present
1038
     *
1039
     * @return boolean
1040
     */
1041
    public function hasAttachments() {
1042
        return $this->attachments->isEmpty() === false;
1043
    }
1044
1045
    /**
1046
     * Set a given flag
1047
     * @param string|array $flag
1048
     *
1049
     * @return bool
1050
     * @throws Exceptions\ConnectionFailedException
1051
     */
1052
    public function setFlag($flag) {
1053
        $this->client->openFolder($this->folder_path);
1054
1055
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
1056
        $status = \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

1056
        $status = \imap_setflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
1057
        $this->parseFlags();
1058
1059
        return $status;
1060
    }
1061
1062
    /**
1063
     * Unset a given flag
1064
     * @param string|array $flag
1065
     *
1066
     * @return bool
1067
     * @throws Exceptions\ConnectionFailedException
1068
     */
1069
    public function unsetFlag($flag) {
1070
        $this->client->openFolder($this->folder_path);
1071
1072
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
1073
        $status = \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

1073
        $status = \imap_clearflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
1074
        $this->parseFlags();
1075
1076
        return $status;
1077
    }
1078
1079
    /**
1080
     * @return null|object|string
1081
     * @throws Exceptions\ConnectionFailedException
1082
     */
1083
    public function getRawBody() {
1084
        if ($this->raw_body === null) {
1085
            $this->client->openFolder($this->folder_path);
1086
1087
            $this->raw_body = \imap_fetchbody($this->client->getConnection(), $this->getUid(), '', $this->fetch_options | IMAP::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

1087
            $this->raw_body = \imap_fetchbody(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), '', $this->fetch_options | IMAP::FT_UID);
Loading history...
1088
        }
1089
1090
        return $this->raw_body;
1091
    }
1092
1093
    /**
1094
     * Get an almost unique message token
1095
     * @return string
1096
     * @throws Exceptions\ConnectionFailedException
1097
     */
1098
    public function getToken(){
1099
        return base64_encode(implode('-', [$this->message_id, $this->subject, strlen($this->getRawBody())]));
1100
    }
1101
1102
    /**
1103
     * @return string
1104
     */
1105
    public function getHeader() {
1106
        return $this->header;
1107
    }
1108
1109
    /**
1110
     * @return Client
1111
     */
1112
    public function getClient() {
1113
        return $this->client;
1114
    }
1115
1116
    /**
1117
     * @return integer
1118
     */
1119
    public function getFetchOptions() {
1120
        return $this->fetch_options;
1121
    }
1122
1123
    /**
1124
     * @return boolean
1125
     */
1126
    public function getFetchBodyOption() {
1127
        return $this->fetch_body;
1128
    }
1129
1130
    /**
1131
     * @return boolean
1132
     */
1133
    public function getFetchAttachmentOption() {
1134
        return $this->fetch_attachment;
1135
    }
1136
1137
    /**
1138
     * @return boolean
1139
     */
1140
    public function getFetchFlagsOption() {
1141
        return $this->fetch_flags;
1142
    }
1143
1144
    /**
1145
     * @return mixed
1146
     */
1147
    public function getBodies() {
1148
        return $this->bodies;
1149
    }
1150
1151
    /**
1152
     * @return FlagCollection
1153
     */
1154
    public function getFlags() {
1155
        return $this->flags;
1156
    }
1157
1158
    /**
1159
     * @return object|null
1160
     */
1161
    public function getStructure(){
1162
        return $this->structure;
1163
    }
1164
1165
    /**
1166
     * Does this message match another one?
1167
     *
1168
     * A match means same uid, message id, subject, body length and date/time.
1169
     *
1170
     * @param  null|Message $message
1171
     * @return bool
1172
     * @throws Exceptions\ConnectionFailedException
1173
     */
1174
    public function is(Message $message = null) {
1175
        if (is_null($message)) {
1176
            return false;
1177
        }
1178
1179
        return $this->getToken() == $message->getToken() && $this->date->eq($message->date);
1180
    }
1181
1182
    /**
1183
     * @return array
1184
     */
1185
    public function getAttributes(){
1186
        return $this->attributes;
1187
    }
1188
1189
    /**
1190
     * @param $mask
1191
     * @return $this
1192
     */
1193
    public function setMask($mask){
1194
        if(class_exists($mask)){
1195
            $this->mask = $mask;
1196
        }
1197
1198
        return $this;
1199
    }
1200
1201
    /**
1202
     * @return string
1203
     */
1204
    public function getMask(){
1205
        return $this->mask;
1206
    }
1207
1208
    /**
1209
     * Get a masked instance by providing a mask name
1210
     * @param string|null $mask
1211
     *
1212
     * @return mixed
1213
     * @throws MaskNotFoundException
1214
     */
1215
    public function mask($mask = null){
1216
        $mask = $mask !== null ? $mask : $this->mask;
1217
        if(class_exists($mask)){
1218
            return new $mask($this);
1219
        }
1220
1221
        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
1222
    }
1223
}
1224