Completed
Push — master ( 6b8225...8354f2 )
by Malte
01:48
created

Message::fetchPart()   B

Complexity

Conditions 10
Paths 6

Size

Total Lines 30
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 30
rs 7.6666
cc 10
nc 6
nop 1

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

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

216
        $this->client->openFolder(/** @scrutinizer ignore-type */ $this->folder_path);
Loading history...
217
218
        $this->msgn = $msgn;
219
        $this->msglist = $msglist;
220
221
        $this->uid = $this->client->getConnection()->getUid($this->msgn);
0 ignored issues
show
Bug introduced by
The method getUid() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

221
        $this->uid = $this->client->getConnection()->/** @scrutinizer ignore-call */ getUid($this->msgn);
Loading history...
222
223
        $this->parseHeader();
224
225
        if ($this->getFetchBodyOption() === true) {
226
            $this->parseBody();
227
        }
228
229
        if ($this->getFetchFlagsOption() === true && $this->flags->count() == 0) {
230
            $this->parseFlags();
231
        }
232
    }
233
234
    /**
235
     * Call dynamic attribute setter and getter methods
236
     * @param string $method
237
     * @param array $arguments
238
     *
239
     * @return mixed
240
     * @throws MethodNotFoundException
241
     */
242
    public function __call($method, $arguments) {
243
        if(strtolower(substr($method, 0, 3)) === 'get') {
244
            $name = Str::snake(substr($method, 3));
245
            return $this->get($name);
246
        }elseif (strtolower(substr($method, 0, 3)) === 'set') {
247
            $name = Str::snake(substr($method, 3));
248
249
            if(in_array($name, array_keys($this->attributes))) {
250
                $this->attributes[$name] = array_pop($arguments);
251
252
                return $this->attributes[$name];
253
            }
254
255
        }
256
257
        throw new MethodNotFoundException("Method ".self::class.'::'.$method.'() is not supported');
258
    }
259
260
    /**
261
     * Magic setter
262
     * @param $name
263
     * @param $value
264
     *
265
     * @return mixed
266
     */
267
    public function __set($name, $value) {
268
        $this->attributes[$name] = $value;
269
270
        return $this->attributes[$name];
271
    }
272
273
    /**
274
     * Magic getter
275
     * @param $name
276
     *
277
     * @return mixed|null
278
     */
279
    public function __get($name) {
280
        return $this->get($name);
281
    }
282
283
    /**
284
     * Get an available message or message header attribute
285
     * @param $name
286
     *
287
     * @return mixed|null
288
     */
289
    public function get($name) {
290
        if(isset($this->attributes[$name])) {
291
            return $this->attributes[$name];
292
        }
293
294
        return $this->header->get($name);
295
    }
296
297
    /**
298
     * Check if the Message has a text body
299
     *
300
     * @return bool
301
     */
302
    public function hasTextBody() {
303
        return isset($this->bodies['text']);
304
    }
305
306
    /**
307
     * Get the Message text body
308
     *
309
     * @return mixed
310
     */
311
    public function getTextBody() {
312
        if (!isset($this->bodies['text'])) {
313
            return false;
314
        }
315
316
        return $this->bodies['text'];
317
    }
318
319
    /**
320
     * Check if the Message has a html body
321
     *
322
     * @return bool
323
     */
324
    public function hasHTMLBody() {
325
        return isset($this->bodies['html']);
326
    }
327
328
    /**
329
     * Get the Message html body
330
     *
331
     * @return string|null
332
     */
333
    public function getHTMLBody() {
334
        if (!isset($this->bodies['html'])) {
335
            return null;
336
        }
337
338
        return $this->bodies['html'];
339
    }
340
341
    /**
342
     * Parse all defined headers
343
     *
344
     * @throws Exceptions\ConnectionFailedException
345
     * @throws Exceptions\RuntimeException
346
     * @throws InvalidMessageDateException
347
     * @throws MessageHeaderFetchingException
348
     */
349
    private function parseHeader() {
350
        $headers = $this->client->getConnection()->headers([$this->msgn]);
0 ignored issues
show
Bug introduced by
The method headers() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

350
        $headers = $this->client->getConnection()->/** @scrutinizer ignore-call */ headers([$this->msgn]);
Loading history...
351
        if (!isset($headers[$this->msgn])) {
352
            throw new MessageHeaderFetchingException("no headers found", 0);
353
        }
354
355
        $this->header = new Header($headers[$this->msgn]);
356
    }
357
358
    /**
359
     * Parse additional flags
360
     *
361
     * @return void
362
     * @throws Exceptions\ConnectionFailedException
363
     * @throws Exceptions\RuntimeException
364
     */
365
    private function parseFlags() {
366
        $this->client->openFolder($this->folder_path);
367
        $this->flags = FlagCollection::make([]);
368
369
        $flags = $this->client->getConnection()->flags([$this->msgn]);
0 ignored issues
show
Bug introduced by
The method flags() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

369
        $flags = $this->client->getConnection()->/** @scrutinizer ignore-call */ flags([$this->msgn]);
Loading history...
370
371
        if (isset($flags[$this->msgn])) {
372
            foreach($flags[$this->msgn] as $flag) {
373
                if (strpos($flag, "\\") === 0){
374
                    $flag = substr($flag, 1);
375
                }
376
                $flag_key = strtolower($flag);
377
                if (in_array($flag_key, $this->available_flags)) {
378
                    $this->flags->put($flag_key, $flag);
379
                }
380
            }
381
        }
382
    }
383
384
    /**
385
     * Parse the Message body
386
     *
387
     * @return $this
388
     * @throws Exceptions\ConnectionFailedException
389
     * @throws Exceptions\MessageContentFetchingException
390
     * @throws InvalidMessageDateException
391
     * @throws Exceptions\RuntimeException
392
     * @throws Exceptions\EventNotFoundException
393
     */
394
    public function parseBody() {
395
        $this->client->openFolder($this->folder_path);
396
397
        $contents = $this->client->getConnection()->content([$this->msgn]);
0 ignored issues
show
Bug introduced by
The method content() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

397
        $contents = $this->client->getConnection()->/** @scrutinizer ignore-call */ content([$this->msgn]);
Loading history...
398
        if (!isset($contents[$this->msgn])) {
399
            throw new MessageContentFetchingException("no content found", 0);
400
        }
401
        $content = $contents[$this->msgn];
402
403
        $this->structure = new Structure($content, $this->header);
404
405
        $this->fetchStructure($this->structure);
406
407
        if ($this->fetch_options == IMAP::FT_PEEK) {
408
            $this->parseFlags();
409
            if ($this->getFlags()->get("seen") !== null) {
410
                $this->unsetFlag("Seen");
411
            }
412
        }
413
414
        return $this;
415
    }
416
417
    /**
418
     * Fetch the Message structure
419
     * @param $structure
420
     *
421
     * @throws Exceptions\ConnectionFailedException
422
     */
423
    private function fetchStructure($structure) {
424
        $this->client->openFolder($this->folder_path);
425
426
        foreach ($structure->parts as $part) {
427
            $this->fetchPart($part);
428
        }
429
    }
430
431
    /**
432
     * Fetch a given part
433
     * @param Part $part
434
     */
435
    private function fetchPart(Part $part) {
436
437
        if ($part->type == IMAP::MESSAGE_TYPE_TEXT && ($part->ifdisposition == 0 || (empty($part->disposition) || !in_array(strtolower($part->disposition), ['attachment', 'inline'])) ) ) {
438
439
            if ( in_array(($subtype = strtolower($part->subtype)), ["plain", "csv", "html"]) && $part->filename == null && $part->name == null) {
440
                $encoding = $this->getEncoding($part);
441
442
                $content = $this->decodeString($part->content, $part->encoding);
443
444
                // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
445
                //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
446
                //     https://stackoverflow.com/a/11303410
447
                //
448
                // us-ascii is the same as ASCII:
449
                //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
450
                //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
451
                //     based on the typographical symbols predominantly in use there.
452
                //     https://en.wikipedia.org/wiki/ASCII
453
                //
454
                // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
455
                if ($encoding != 'us-ascii') {
456
                    $content = $this->convertEncoding($content, $encoding);
457
                }
458
459
                $this->bodies[$subtype == "plain" ? "text" : $subtype] = $content;
460
            } else {
461
                $this->fetchAttachment($part);
462
            }
463
        } else {
464
            $this->fetchAttachment($part);
465
        }
466
    }
467
468
    /**
469
     * Fetch the Message attachment
470
     * @param Part $part
471
     */
472
    protected function fetchAttachment($part) {
473
474
        $oAttachment = new Attachment($this, $part);
475
476
        if ($oAttachment->getName() !== null) {
0 ignored issues
show
introduced by
The condition $oAttachment->getName() !== null is always true.
Loading history...
477
            if ($oAttachment->getId() !== null) {
0 ignored issues
show
introduced by
The condition $oAttachment->getId() !== null is always true.
Loading history...
478
                $this->attachments->put($oAttachment->getId(), $oAttachment);
479
            } else {
480
                $this->attachments->push($oAttachment);
481
            }
482
        }
483
    }
484
485
    /**
486
     * Fail proof setter for $fetch_option
487
     * @param $option
488
     *
489
     * @return $this
490
     */
491
    public function setFetchOption($option) {
492
        if (is_long($option) === true) {
493
            $this->fetch_options = $option;
494
        } elseif (is_null($option) === true) {
495
            $config = ClientManager::get('options.fetch', IMAP::FT_UID);
496
            $this->fetch_options = is_long($config) ? $config : 1;
497
        }
498
499
        return $this;
500
    }
501
502
    /**
503
     * Fail proof setter for $fetch_body
504
     * @param $option
505
     *
506
     * @return $this
507
     */
508
    public function setFetchBodyOption($option) {
509
        if (is_bool($option)) {
510
            $this->fetch_body = $option;
511
        } elseif (is_null($option)) {
512
            $config = ClientManager::get('options.fetch_body', true);
513
            $this->fetch_body = is_bool($config) ? $config : true;
514
        }
515
516
        return $this;
517
    }
518
519
    /**
520
     * Fail proof setter for $fetch_flags
521
     * @param $option
522
     *
523
     * @return $this
524
     */
525
    public function setFetchFlagsOption($option) {
526
        if (is_bool($option)) {
527
            $this->fetch_flags = $option;
528
        } elseif (is_null($option)) {
529
            $config = ClientManager::get('options.fetch_flags', true);
530
            $this->fetch_flags = is_bool($config) ? $config : true;
531
        }
532
533
        return $this;
534
    }
535
536
    /**
537
     * Decode a given string
538
     * @param $string
539
     * @param $encoding
540
     *
541
     * @return string
542
     */
543
    public function decodeString($string, $encoding) {
544
        switch ($encoding) {
545
            case IMAP::MESSAGE_ENC_BINARY:
546
                if (extension_loaded('imap')) {
547
                    return base64_decode(\imap_binary($string));
548
                }
549
                return base64_decode($string);
550
            case IMAP::MESSAGE_ENC_BASE64:
551
                return base64_decode($string);
552
            case IMAP::MESSAGE_ENC_8BIT:
553
            case IMAP::MESSAGE_ENC_QUOTED_PRINTABLE:
554
                return quoted_printable_decode($string);
555
            case IMAP::MESSAGE_ENC_7BIT:
556
            case IMAP::MESSAGE_ENC_OTHER:
557
            default:
558
                return $string;
559
        }
560
    }
561
562
    /**
563
     * Convert the encoding
564
     * @param $str
565
     * @param string $from
566
     * @param string $to
567
     *
568
     * @return mixed|string
569
     */
570
    public function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") {
571
572
        $from = EncodingAliases::get($from);
573
        $to = EncodingAliases::get($to);
574
575
        if ($from === $to) {
576
            return $str;
577
        }
578
579
        // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
580
        //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
581
        //     https://stackoverflow.com/a/11303410
582
        //
583
        // us-ascii is the same as ASCII:
584
        //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
585
        //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
586
        //     based on the typographical symbols predominantly in use there.
587
        //     https://en.wikipedia.org/wiki/ASCII
588
        //
589
        // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
590
        if (strtolower($from) == 'us-ascii' && $to == 'UTF-8') {
591
            return $str;
592
        }
593
594
        if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
595
            return @iconv($from, $to.'//IGNORE', $str);
596
        } else {
597
            if (!$from) {
598
                return mb_convert_encoding($str, $to);
599
            }
600
            return mb_convert_encoding($str, $to, $from);
601
        }
602
    }
603
604
    /**
605
     * Get the encoding of a given abject
606
     * @param object|string $structure
607
     *
608
     * @return string
609
     */
610
    public function getEncoding($structure) {
611
        if (property_exists($structure, 'parameters')) {
612
            foreach ($structure->parameters as $parameter) {
613
                if (strtolower($parameter->attribute) == "charset") {
614
                    return EncodingAliases::get($parameter->value);
615
                }
616
            }
617
        }elseif (property_exists($structure, 'charset')){
618
            return EncodingAliases::get($structure->charset);
619
        }elseif (is_string($structure) === true){
620
            return mb_detect_encoding($structure);
621
        }
622
623
        return 'UTF-8';
624
    }
625
626
    /**
627
     * Get the messages folder
628
     *
629
     * @return mixed
630
     * @throws Exceptions\ConnectionFailedException
631
     * @throws Exceptions\FolderFetchingException
632
     */
633
    public function getFolder(){
634
        return $this->client->getFolder($this->folder_path);
635
    }
636
637
    /**
638
     * Copy the current Messages to a mailbox
639
     * @param string $folder
640
     *
641
     * @return null|Message
642
     * @throws Exceptions\ConnectionFailedException
643
     * @throws Exceptions\FolderFetchingException
644
     * @throws Exceptions\RuntimeException
645
     * @throws InvalidMessageDateException
646
     * @throws MessageContentFetchingException
647
     * @throws MessageHeaderFetchingException
648
     * @throws Exceptions\EventNotFoundException
649
     */
650
    public function copy($folder) {
651
        $this->client->openFolder($this->folder_path);
652
        $status = $this->client->getConnection()->examineFolder($folder);
0 ignored issues
show
Bug introduced by
The method examineFolder() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

652
        $status = $this->client->getConnection()->/** @scrutinizer ignore-call */ examineFolder($folder);
Loading history...
653
        /** @var Folder $folder */
654
        $folder = $this->client->getFolder($folder);
0 ignored issues
show
Bug introduced by
$folder of type Webklex\PHPIMAP\Folder is incompatible with the type string expected by parameter $folder_name of Webklex\PHPIMAP\Client::getFolder(). ( Ignorable by Annotation )

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

654
        $folder = $this->client->getFolder(/** @scrutinizer ignore-type */ $folder);
Loading history...
655
        if (isset($status["uidnext"]) && $folder !== null) {
656
            $next_uid = $status["uidnext"];
657
            if ($this->client->getConnection()->copyMessage($folder->path, $this->msgn) == true) {
0 ignored issues
show
Bug introduced by
The method copyMessage() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

657
            if ($this->client->getConnection()->/** @scrutinizer ignore-call */ copyMessage($folder->path, $this->msgn) == true) {
Loading history...
658
                $this->client->openFolder($folder->path);
659
                $message_num = $this->client->getConnection()->getMessageNumber($next_uid);
0 ignored issues
show
Bug introduced by
The method getMessageNumber() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

659
                $message_num = $this->client->getConnection()->/** @scrutinizer ignore-call */ getMessageNumber($next_uid);
Loading history...
660
661
                $message = $folder->query()->getMessage($message_num);
662
                $event = $this->getEvent("message", "copied");
663
                $event::dispatch($this, $message);
664
665
                return $message;
666
            }
667
        }
668
669
        return null;
670
    }
671
672
    /**
673
     * Move the current Messages to a mailbox
674
     * @param $folder
675
     * @param boolean $expunge
676
     *
677
     * @return Message|null
678
     * @throws Exceptions\ConnectionFailedException
679
     * @throws Exceptions\FolderFetchingException
680
     * @throws Exceptions\RuntimeException
681
     * @throws InvalidMessageDateException
682
     * @throws MessageContentFetchingException
683
     * @throws MessageHeaderFetchingException
684
     * @throws Exceptions\EventNotFoundException
685
     */
686
    public function move($folder, $expunge = false) {
687
        $message = $this->copy($folder);
688
        if ($message !== null) {
689
            $this->delete($expunge);
690
691
            $event = $this->getEvent("message", "moved");
692
            $event::dispatch($this, $message);
693
        }
694
695
696
        return $message;
697
    }
698
699
    /**
700
     * Delete the current Message
701
     * @param bool $expunge
702
     *
703
     * @return bool
704
     * @throws Exceptions\ConnectionFailedException
705
     * @throws Exceptions\RuntimeException
706
     * @throws Exceptions\EventNotFoundException
707
     */
708
    public function delete($expunge = true) {
709
        $status = $this->setFlag("Deleted");
710
        if($expunge) $this->client->expunge();
711
712
        $event = $this->getEvent("message", "deleted");
713
        $event::dispatch($this);
714
715
        return $status;
716
    }
717
718
    /**
719
     * Restore a deleted Message
720
     * @param boolean $expunge
721
     *
722
     * @return bool
723
     * @throws Exceptions\ConnectionFailedException
724
     * @throws Exceptions\RuntimeException
725
     * @throws Exceptions\EventNotFoundException
726
     */
727
    public function restore($expunge = true) {
728
        $status = $this->unsetFlag("Deleted");
729
        if($expunge) $this->client->expunge();
730
731
        $event = $this->getEvent("message", "restored");
732
        $event::dispatch($this);
733
734
        return $status;
735
    }
736
737
    /**
738
     * Set a given flag
739
     * @param string|array $flag
740
     *
741
     * @return bool
742
     * @throws Exceptions\ConnectionFailedException
743
     * @throws Exceptions\RuntimeException
744
     * @throws Exceptions\EventNotFoundException
745
     */
746
    public function setFlag($flag) {
747
        $this->client->openFolder($this->folder_path);
748
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
749
        $status = $this->client->getConnection()->store([$flag], $this->msgn, $this->msgn, "+");
0 ignored issues
show
Bug introduced by
The method store() does not exist on Webklex\PHPIMAP\Connection\Protocols\Protocol. Since it exists in all sub-types, consider adding an abstract or default implementation to Webklex\PHPIMAP\Connection\Protocols\Protocol. ( Ignorable by Annotation )

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

749
        $status = $this->client->getConnection()->/** @scrutinizer ignore-call */ store([$flag], $this->msgn, $this->msgn, "+");
Loading history...
750
        $this->parseFlags();
751
752
        $event = $this->getEvent("flag", "new");
753
        $event::dispatch($this, $flag);
754
755
        return $status;
756
    }
757
758
    /**
759
     * Unset a given flag
760
     * @param string|array $flag
761
     *
762
     * @return bool
763
     * @throws Exceptions\ConnectionFailedException
764
     * @throws Exceptions\RuntimeException
765
     * @throws Exceptions\EventNotFoundException
766
     */
767
    public function unsetFlag($flag) {
768
        $this->client->openFolder($this->folder_path);
769
770
        $flag = "\\".trim(is_array($flag) ? implode(" \\", $flag) : $flag);
771
        $status = $this->client->getConnection()->store([$flag], $this->msgn, $this->msgn, "-");
772
        $this->parseFlags();
773
774
        $event = $this->getEvent("flag", "deleted");
775
        $event::dispatch($this, $flag);
776
777
        return $status;
778
    }
779
780
    /**
781
     * Get all message attachments.
782
     *
783
     * @return AttachmentCollection
784
     */
785
    public function getAttachments() {
786
        return $this->attachments;
787
    }
788
789
    /**
790
     * Checks if there are any attachments present
791
     *
792
     * @return boolean
793
     */
794
    public function hasAttachments() {
795
        return $this->attachments->isEmpty() === false;
796
    }
797
798
    /**
799
     * Get the raw body
800
     *
801
     * @return string
802
     * @throws Exceptions\ConnectionFailedException
803
     */
804
    public function getRawBody() {
805
        if ($this->raw_body === null) {
806
            $this->client->openFolder($this->folder_path);
807
808
            $this->raw_body = $this->structure->raw;
809
        }
810
811
        return $this->raw_body;
812
    }
813
814
    /**
815
     * Get the message header
816
     *
817
     * @return Header
818
     */
819
    public function getHeader() {
820
        return $this->header;
821
    }
822
823
    /**
824
     * Get the current client
825
     *
826
     * @return Client
827
     */
828
    public function getClient() {
829
        return $this->client;
830
    }
831
832
    /**
833
     * Get the used fetch option
834
     *
835
     * @return integer
836
     */
837
    public function getFetchOptions() {
838
        return $this->fetch_options;
839
    }
840
841
    /**
842
     * Get the used fetch body option
843
     *
844
     * @return boolean
845
     */
846
    public function getFetchBodyOption() {
847
        return $this->fetch_body;
848
    }
849
850
    /**
851
     * Get the used fetch flags option
852
     *
853
     * @return boolean
854
     */
855
    public function getFetchFlagsOption() {
856
        return $this->fetch_flags;
857
    }
858
859
    /**
860
     * Get all available bodies
861
     *
862
     * @return array
863
     */
864
    public function getBodies() {
865
        return $this->bodies;
866
    }
867
868
    /**
869
     * Get all set flags
870
     *
871
     * @return FlagCollection
872
     */
873
    public function getFlags() {
874
        return $this->flags;
875
    }
876
877
    /**
878
     * Get the fetched structure
879
     *
880
     * @return object|null
881
     */
882
    public function getStructure(){
883
        return $this->structure;
884
    }
885
886
    /**
887
     * Check if a message matches an other by comparing basic attributes
888
     *
889
     * @param  null|Message $message
890
     * @return boolean
891
     */
892
    public function is(Message $message = null) {
893
        if (is_null($message)) {
894
            return false;
895
        }
896
897
        return $this->uid == $message->uid
898
            && $this->message_id == $message->message_id
899
            && $this->subject == $message->subject
900
            && $this->date->eq($message->date);
901
    }
902
903
    /**
904
     * Get all message attributes
905
     *
906
     * @return array
907
     */
908
    public function getAttributes(){
909
        return array_merge($this->attributes, $this->header->getAttributes());
910
    }
911
912
    /**
913
     * Set the message mask
914
     * @param $mask
915
     *
916
     * @return $this
917
     */
918
    public function setMask($mask){
919
        if(class_exists($mask)){
920
            $this->mask = $mask;
921
        }
922
923
        return $this;
924
    }
925
926
    /**
927
     * Get the used message mask
928
     *
929
     * @return string
930
     */
931
    public function getMask(){
932
        return $this->mask;
933
    }
934
935
    /**
936
     * Get a masked instance by providing a mask name
937
     * @param string|null $mask
938
     *
939
     * @return mixed
940
     * @throws MaskNotFoundException
941
     */
942
    public function mask($mask = null){
943
        $mask = $mask !== null ? $mask : $this->mask;
944
        if(class_exists($mask)){
945
            return new $mask($this);
946
        }
947
948
        throw new MaskNotFoundException("Unknown mask provided: ".$mask);
949
    }
950
}
951