Passed
Pull Request — master (#260)
by
unknown
04:21
created

Message::parseAddresses()   C

Complexity

Conditions 10
Paths 193

Size

Total Lines 36
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
cc 10
eloc 20
c 5
b 0
f 1
nc 193
nop 1
dl 0
loc 36
rs 6.8916

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

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

299
        return imap_mail_copy(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $mailbox, IMAP::CP_UID);
Loading history...
300
    }
301
302
    /**
303
     * Move the current Messages to a mailbox
304
     *
305
     * @param $mailbox
306
     * @param int $options
307
     *
308
     * @return bool
309
     * @throws Exceptions\ConnectionFailedException
310
     */
311
    public function move($mailbox, $options = 0) {
312
        $this->client->openFolder($this->folder_path);
313
        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

313
        return imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $mailbox, IMAP::CP_UID);
Loading history...
314
    }
315
316
    /**
317
     * Check if the Message has a text body
318
     *
319
     * @return bool
320
     */
321
    public function hasTextBody() {
322
        return isset($this->bodies['text']);
323
    }
324
325
    /**
326
     * Get the Message text body
327
     *
328
     * @return mixed
329
     */
330
    public function getTextBody() {
331
        if (!isset($this->bodies['text'])) {
332
            return false;
333
        }
334
335
        return $this->bodies['text']->content;
336
    }
337
338
    /**
339
     * Check if the Message has a html body
340
     *
341
     * @return bool
342
     */
343
    public function hasHTMLBody() {
344
        return isset($this->bodies['html']);
345
    }
346
347
    /**
348
     * Get the Message html body
349
     * If $replaceImages is callable it should expect string $body as first parameter, $oAttachment as second and return
350
     * the resulting $body.
351
     *
352
     * @var bool|callable $replaceImages
353
     *
354
     * @return string|null
355
     *
356
     * @deprecated 1.4.0:2.0.0 No longer needed. Use AttachmentMask::getImageSrc() instead
357
     */
358
    public function getHTMLBody($replaceImages = false) {
359
        if (!isset($this->bodies['html'])) {
360
            return null;
361
        }
362
363
        $body = $this->bodies['html']->content;
364
        if ($replaceImages !== false) {
365
            $this->attachments->each(function($oAttachment) use(&$body, $replaceImages) {
366
                /** @var Attachment $oAttachment */
367
                if(is_callable($replaceImages)) {
368
                    $body = $replaceImages($body, $oAttachment);
369
                }elseif(is_string($replaceImages)) {
370
                    call_user_func($replaceImages, [$body, $oAttachment]);
371
                }else{
372
                    if ($oAttachment->id && $oAttachment->getImgSrc() != null) {
0 ignored issues
show
Deprecated Code introduced by
The function Webklex\IMAP\Attachment::getImgSrc() has been deprecated: 1.4.0:2.0.0 No longer needed. Use AttachmentMask::getImageSrc() instead ( Ignorable by Annotation )

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

372
                    if ($oAttachment->id && /** @scrutinizer ignore-deprecated */ $oAttachment->getImgSrc() != null) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
373
                        $body = str_replace('cid:'.$oAttachment->id, $oAttachment->getImgSrc(), $body);
0 ignored issues
show
Deprecated Code introduced by
The function Webklex\IMAP\Attachment::getImgSrc() has been deprecated: 1.4.0:2.0.0 No longer needed. Use AttachmentMask::getImageSrc() instead ( Ignorable by Annotation )

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

373
                        $body = str_replace('cid:'.$oAttachment->id, /** @scrutinizer ignore-deprecated */ $oAttachment->getImgSrc(), $body);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
374
                    }
375
                }
376
            });
377
        }
378
379
        return $body;
380
    }
381
382
    /**
383
     * Parse all defined headers
384
     *
385
     * @return void
386
     * @throws Exceptions\ConnectionFailedException
387
     * @throws InvalidMessageDateException
388
     */
389
    private function parseHeader() {
390
        $this->client->openFolder($this->folder_path);
391
        $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

391
        $this->header = $header = imap_fetchheader(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
392
393
        $this->priority = $this->extractPriority($this->header);
394
395
        if ($this->header) {
396
            $header = imap_rfc822_parse_headers($this->header);
397
        }
398
399
        if (property_exists($header, 'subject')) {
400
            if($this->config['decoder']['message']['subject'] === 'utf-8') {
401
                $this->subject = imap_utf8($header->subject);
402
            }else{
403
                $this->subject = mb_decode_mimeheader($header->subject);
404
            }
405
        }
406
407
        foreach(['from', 'to', 'cc', 'bcc', 'reply_to', 'sender'] as $part){
408
            $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

408
            $this->extractHeaderAddressPart(/** @scrutinizer ignore-type */ $header, $part);
Loading history...
409
        }
410
411
        if (property_exists($header, 'references')) {
412
            $this->references = $header->references;
413
        }
414
        if (property_exists($header, 'in_reply_to')) {
415
            $this->in_reply_to = str_replace(['<', '>'], '', $header->in_reply_to);
416
        }
417
        if (property_exists($header, 'message_id')) {
418
            $this->message_id = str_replace(['<', '>'], '', $header->message_id);
419
        }
420
        if (property_exists($header, 'Msgno')) {
421
            $messageNo = (int) trim($header->Msgno);
422
            $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

422
            $this->message_no = ($this->fetch_options == IMAP::FT_UID) ? $messageNo : imap_msgno(/** @scrutinizer ignore-type */ $this->client->getConnection(), $messageNo);
Loading history...
423
        } else {
424
            $this->message_no = imap_msgno($this->client->getConnection(), $this->getUid());
425
        }
426
427
        $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

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

533
        $flags = imap_fetch_overview(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
534
        if (is_array($flags) && isset($flags[0])) {
535
            foreach($this->available_flags as $flag) {
536
                $this->parseFlag($flags, $flag);
537
            }
538
        }
539
    }
540
541
    /**
542
     * Extract a possible flag information from a given array
543
     * @param array $flags
544
     * @param string $flag
545
     */
546
    private function parseFlag($flags, $flag) {
547
        $flag = strtolower($flag);
548
549
        if (property_exists($flags[0], strtoupper($flag))) {
550
            $this->flags->put($flag, $flags[0]->{strtoupper($flag)});
551
        } elseif (property_exists($flags[0], ucfirst($flag))) {
552
            $this->flags->put($flag, $flags[0]->{ucfirst($flag)});
553
        } elseif (property_exists($flags[0], $flag)) {
554
            $this->flags->put($flag, $flags[0]->$flag);
555
        }
556
    }
557
558
    /**
559
     * Get the current Message header info
560
     *
561
     * @return object
562
     * @throws Exceptions\ConnectionFailedException
563
     */
564
    public function getHeaderInfo() {
565
        if ($this->header_info == null) {
566
            $this->client->openFolder($this->folder_path);
567
            $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

567
            $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

567
            $this->header_info = imap_headerinfo(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getMessageNo());
Loading history...
568
        }
569
570
        return $this->header_info;
571
    }
572
573
    /**
574
     * Extract a given part as address array from a given header
575
     * @param object $header
576
     * @param string $part
577
     */
578
    private function extractHeaderAddressPart($header, $part) {
579
        if (property_exists($header, $part)) {
580
            $this->$part = $this->parseAddresses($header->$part);
581
        }
582
    }
583
584
    /**
585
     * Parse Addresses
586
     * @param $list
587
     *
588
     * @return array
589
     */
590
    private function parseAddresses($list) {
591
        $addresses = [];
592
593
        foreach ($list as $item) {
594
            $address = (object) $item;
595
596
            if (!property_exists($address, 'mailbox')) {
597
                $address->mailbox = false;
598
            }
599
            if (!property_exists($address, 'host')) {
600
                $address->host = false;
601
            }
602
            if (!property_exists($address, 'personal')) {
603
                $address->personal = false;
604
            }
605
606
            $personalParts = imap_mime_header_decode($address->personal);
607
608
            $address->personal = '';
609
            foreach ($personalParts as $p) {
610
                $text = $p->text;
611
                
612
                if ($p->charset != 'default') {
613
                    $text = mb_convert_encoding($text, "UTF-8", $p->charset);
614
                }
615
616
                $address->personal .= $text;
617
            }
618
619
            $address->mail = ($address->mailbox && $address->host) ? $address->mailbox.'@'.$address->host : false;
620
            $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

620
            $address->full = ($address->personal) ? $address->personal.' <'./** @scrutinizer ignore-type */ $address->mail.'>' : $address->mail;
Loading history...
621
622
            $addresses[] = $address;
623
        }
624
625
        return $addresses;
626
    }
627
628
    /**
629
     * Parse the Message body
630
     *
631
     * @return $this
632
     * @throws Exceptions\ConnectionFailedException
633
     */
634
    public function parseBody() {
635
        $this->client->openFolder($this->folder_path);
636
        $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

636
        $this->structure = imap_fetchstructure(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
637
638
        if(property_exists($this->structure, 'parts')){
639
            $parts = $this->structure->parts;
640
641
            foreach ($parts as $part)  {
642
                foreach ($part->parameters as $parameter)  {
643
                    if($parameter->attribute == "charset")  {
644
                        $encoding = $parameter->value;
645
646
                        $encoding = preg_replace('/Content-Transfer-Encoding/', '', $encoding);
647
                        $encoding = preg_replace('/iso-8859-8-i/', 'iso-8859-8', $encoding);
648
649
                        $parameter->value = $encoding;
650
                    }
651
                }
652
            }
653
        }
654
655
        $this->fetchStructure($this->structure);
656
657
        return $this;
658
    }
659
660
    /**
661
     * Fetch the Message structure
662
     *
663
     * @param $structure
664
     * @param mixed $partNumber
665
     *
666
     * @throws Exceptions\ConnectionFailedException
667
     */
668
    private function fetchStructure($structure, $partNumber = null) {
669
        $this->client->openFolder($this->folder_path);
670
671
        if ($structure->type == IMAP::MESSAGE_TYPE_TEXT &&
672
            ($structure->ifdisposition == 0 ||
673
                ($structure->ifdisposition == 1 && !isset($structure->parts) && $partNumber != null)
674
            )
675
        ) {
676
            if (strtolower($structure->subtype) == "plain" || strtolower($structure->subtype) == "csv") {
677
                if (!$partNumber) {
678
                    $partNumber = 1;
679
                }
680
681
                $encoding = $this->getEncoding($structure);
682
683
                $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

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

948
        $uidMatches = imap_fetch_overview(/** @scrutinizer ignore-type */ $client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
949
        $uidMatch = count($uidMatches)
950
            ? new Message($uidMatches[0]->uid, $uidMatches[0]->msgno, $client)
951
            : null;
952
        $client->disconnect();
953
954
        // imap_fetch_overview() on a parent folder will return the matching message
955
        // even when the message is in a child folder so we need to recursively
956
        // search the children
957
        foreach ($folder->children as $child) {
958
            $childFolder = $this->getContainingFolder($child);
959
960
            if ($childFolder) {
961
                return $childFolder;
962
            }
963
        }
964
965
        // before returning the parent
966
        if ($this->is($uidMatch)) {
967
            return $folder;
968
        }
969
970
        // or signalling that the message was not found in any folder
971
        return null;
972
    }
973
974
    public function getFolder(){
975
        return $this->client->getFolder($this->folder_path);
976
    }
977
978
    /**
979
     * Move the Message into an other Folder
980
     * @param string $mailbox
981
     * @param bool $expunge
982
     * @param bool $create_folder
983
     *
984
     * @return null|Message
985
     * @throws Exceptions\ConnectionFailedException
986
     * @throws InvalidMessageDateException
987
     */
988
    public function moveToFolder($mailbox = 'INBOX', $expunge = false, $create_folder = true) {
989
990
        if($create_folder) $this->client->createFolder($mailbox, true);
991
992
        $target_folder = $this->client->getFolder($mailbox);
993
        $target_status = $target_folder->getStatus(IMAP::SA_ALL);
994
995
        $this->client->openFolder($this->folder_path);
996
        $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

996
        $status = imap_mail_move(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, $mailbox, IMAP::CP_UID);
Loading history...
997
998
        if($status === true){
999
            if($expunge) $this->client->expunge();
1000
            $this->client->openFolder($target_folder->path);
1001
1002
            return $target_folder->getMessage($target_status->uidnext, null, $this->fetch_options, $this->fetch_body, $this->fetch_attachment, $this->fetch_flags);
1003
        }
1004
1005
        return null;
1006
    }
1007
1008
    /**
1009
     * Delete the current Message
1010
     * @param bool $expunge
1011
     *
1012
     * @return bool
1013
     * @throws Exceptions\ConnectionFailedException
1014
     */
1015
    public function delete($expunge = true) {
1016
        $this->client->openFolder($this->folder_path);
1017
1018
        $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

1018
        $status = imap_delete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
1019
        if($expunge) $this->client->expunge();
1020
1021
        return $status;
1022
    }
1023
1024
    /**
1025
     * Restore a deleted Message
1026
     * @param boolean $expunge
1027
     *
1028
     * @return bool
1029
     * @throws Exceptions\ConnectionFailedException
1030
     */
1031
    public function restore($expunge = true) {
1032
        $this->client->openFolder($this->folder_path);
1033
1034
        $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

1034
        $status = imap_undelete(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->uid, IMAP::FT_UID);
Loading history...
1035
        if($expunge) $this->client->expunge();
1036
1037
        return $status;
1038
    }
1039
1040
    /**
1041
     * Get all message attachments.
1042
     *
1043
     * @return AttachmentCollection
1044
     */
1045
    public function getAttachments() {
1046
        return $this->attachments;
1047
    }
1048
1049
    /**
1050
     * Checks if there are any attachments present
1051
     *
1052
     * @return boolean
1053
     */
1054
    public function hasAttachments() {
1055
        return $this->attachments->isEmpty() === false;
1056
    }
1057
1058
    /**
1059
     * Set a given flag
1060
     * @param string|array $flag
1061
     *
1062
     * @return bool
1063
     * @throws Exceptions\ConnectionFailedException
1064
     */
1065
    public function setFlag($flag) {
1066
        $this->client->openFolder($this->folder_path);
1067
1068
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
1069
        $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

1069
        $status = imap_setflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
1070
        $this->parseFlags();
1071
1072
        return $status;
1073
    }
1074
1075
    /**
1076
     * Unset a given flag
1077
     * @param string|array $flag
1078
     *
1079
     * @return bool
1080
     * @throws Exceptions\ConnectionFailedException
1081
     */
1082
    public function unsetFlag($flag) {
1083
        $this->client->openFolder($this->folder_path);
1084
1085
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
1086
        $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

1086
        $status = imap_clearflag_full(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->getUid(), $flag, SE_UID);
Loading history...
1087
        $this->parseFlags();
1088
1089
        return $status;
1090
    }
1091
1092
    /**
1093
     * @return null|object|string
1094
     * @throws Exceptions\ConnectionFailedException
1095
     */
1096
    public function getRawBody() {
1097
        if ($this->raw_body === null) {
1098
            $this->client->openFolder($this->folder_path);
1099
1100
            $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

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