Passed
Push — master ( b0bac1...41e857 )
by Malte
03:36
created

Folder::getEncodedName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 2
rs 10
1
<?php
2
/*
3
* File:     Folder.php
4
* Category: -
5
* Author:   M. Goldenbaum
6
* Created:  19.01.17 22:21
7
* Updated:  -
8
*
9
* Description:
10
*  -
11
*/
12
13
namespace Webklex\IMAP;
14
15
use Carbon\Carbon;
16
use Webklex\IMAP\Exceptions\GetMessagesFailedException;
17
use Webklex\IMAP\Exceptions\MessageSearchValidationException;
18
use Webklex\IMAP\Query\WhereQuery;
19
use Webklex\IMAP\Support\FolderCollection;
20
use Webklex\IMAP\Support\MessageCollection;
21
22
/**
23
 * Class Folder
24
 *
25
 * @package Webklex\IMAP
26
 */
27
class Folder {
28
29
    /**
30
     * Client instance
31
     *
32
     * @var \Webklex\IMAP\Client
33
     */
34
    protected $client;
35
36
    /**
37
     * Folder full path
38
     *
39
     * @var string
40
     */
41
    public $path;
42
43
    /**
44
     * Folder name
45
     *
46
     * @var string
47
     */
48
    public $name;
49
50
    /**
51
     * Folder fullname
52
     *
53
     * @var string
54
     */
55
    public $full_name;
56
57
    /**
58
     * Children folders
59
     *
60
     * @var FolderCollection|array
61
     */
62
    public $children = [];
63
64
    /**
65
     * Delimiter for folder
66
     *
67
     * @var string
68
     */
69
    public $delimiter;
70
71
    /**
72
     * Indicates if folder can't containg any "children".
73
     * CreateFolder won't work on this folder.
74
     *
75
     * @var boolean
76
     */
77
    public $no_inferiors;
78
79
    /**
80
     * Indicates if folder is only container, not a mailbox - you can't open it.
81
     *
82
     * @var boolean
83
     */
84
    public $no_select;
85
86
    /**
87
     * Indicates if folder is marked. This means that it may contain new messages since the last time it was checked.
88
     * Not provided by all IMAP servers.
89
     *
90
     * @var boolean
91
     */
92
    public $marked;
93
94
    /**
95
     * Indicates if folder containg any "children".
96
     * Not provided by all IMAP servers.
97
     *
98
     * @var boolean
99
     */
100
    public $has_children;
101
102
    /**
103
     * Indicates if folder refers to other.
104
     * Not provided by all IMAP servers.
105
     *
106
     * @var boolean
107
     */
108
    public $referal;
109
110
    /**
111
     * Folder constructor.
112
     *
113
     * @param \Webklex\IMAP\Client $client
114
     *
115
     * @param object $structure
116
     */
117
    public function __construct(Client $client, $structure) {
118
        $this->client = $client;
119
120
        $this->setDelimiter($structure->delimiter);
121
        $this->path      = $structure->name;
122
        $this->full_name  = $this->decodeName($structure->name);
123
        $this->name      = $this->getSimpleName($this->delimiter, $this->full_name);
124
125
        $this->parseAttributes($structure->attributes);
126
    }
127
128
    /**
129
     * Get a new search query instance
130
     * @param string $charset
131
     *
132
     * @return WhereQuery
133
     * @throws Exceptions\ConnectionFailedException
134
     */
135
    public function query($charset = 'UTF-8'){
136
        $this->getClient()->checkConnection();
137
        $this->getClient()->openFolder($this->path);
138
139
        return new WhereQuery($this->getClient(), $charset);
140
    }
141
142
    /**
143
     * @inheritdoc self::query($charset = 'UTF-8')
144
     * @throws Exceptions\ConnectionFailedException
145
     */
146
    public function search($charset = 'UTF-8'){
147
        return $this->query($charset);
148
    }
149
150
    /**
151
     * @inheritdoc self::query($charset = 'UTF-8')
152
     * @throws Exceptions\ConnectionFailedException
153
     */
154
    public function messages($charset = 'UTF-8'){
155
        return $this->query($charset);
156
    }
157
158
    /**
159
     * Determine if folder has children.
160
     *
161
     * @return bool
162
     */
163
    public function hasChildren() {
164
        return $this->has_children;
165
    }
166
167
    /**
168
     * Set children.
169
     *
170
     * @param FolderCollection|array $children
171
     *
172
     * @return self
173
     */
174
    public function setChildren($children = []) {
175
        $this->children = $children;
176
177
        return $this;
178
    }
179
180
    /**
181
     * Get a specific message by UID
182
     *
183
     * @param integer      $uid     Please note that the uid is not unique and can change
184
     * @param integer|null $msglist
185
     * @param integer|null $fetch_options
186
     * @param boolean      $fetch_body
187
     * @param boolean      $fetch_attachment
188
     * @param boolean      $fetch_flags
189
     *
190
     * @return Message|null
191
     * @throws Exceptions\ConnectionFailedException
192
     * @throws Exceptions\InvalidMessageDateException
193
     */
194
    public function getMessage($uid, $msglist = null, $fetch_options = null, $fetch_body = null, $fetch_attachment = null, $fetch_flags = null) {
195
        $this->client->openFolder($this->path);
196
        if (\imap_msgno($this->getClient()->getConnection(), $uid) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $this->getClient()->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

196
        if (\imap_msgno(/** @scrutinizer ignore-type */ $this->getClient()->getConnection(), $uid) > 0) {
Loading history...
197
            return new Message($uid, $msglist, $this->getClient(), $fetch_options, $fetch_body, $fetch_attachment, $fetch_flags);
198
        }
199
200
        return null;
201
    }
202
203
    /**
204
     * Get all messages
205
     *
206
     * @param string    $criteria
207
     * @param int|null  $fetch_options
208
     * @param boolean   $fetch_body
209
     * @param boolean   $fetch_attachment
210
     * @param boolean   $fetch_flags
211
     * @param int|null  $limit
212
     * @param int       $page
213
     * @param string    $charset
214
     *
215
     * @return MessageCollection
216
     * @throws Exceptions\ConnectionFailedException
217
     * @throws Exceptions\InvalidWhereQueryCriteriaException
218
     * @throws GetMessagesFailedException
219
     */
220
    public function getMessages($criteria = 'ALL', $fetch_options = null, $fetch_body = null, $fetch_attachment = null, $fetch_flags = null, $limit = null, $page = 1, $charset = "UTF-8") {
221
222
        return $this->query($charset)->where($criteria)->setFetchOptions($fetch_options)->setFetchBody($fetch_body)
0 ignored issues
show
Bug introduced by
It seems like $fetch_options can also be of type integer; however, parameter $fetch_options of Webklex\IMAP\Query\Query::setFetchOptions() does only seem to accept boolean, 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

222
        return $this->query($charset)->where($criteria)->setFetchOptions(/** @scrutinizer ignore-type */ $fetch_options)->setFetchBody($fetch_body)
Loading history...
223
            ->setFetchAttachment($fetch_attachment)->setFetchFlags($fetch_flags)
0 ignored issues
show
Bug introduced by
It seems like $fetch_flags can also be of type boolean; however, parameter $fetch_flags of Webklex\IMAP\Query\Query::setFetchFlags() does only seem to accept integer, 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

223
            ->setFetchAttachment($fetch_attachment)->setFetchFlags(/** @scrutinizer ignore-type */ $fetch_flags)
Loading history...
224
            ->limit($limit, $page)->get();
225
    }
226
227
    /**
228
     * Get all unseen messages
229
     *
230
     * @param string    $criteria
231
     * @param int|null  $fetch_options
232
     * @param boolean   $fetch_body
233
     * @param boolean   $fetch_attachment
234
     * @param boolean   $fetch_flags
235
     * @param int|null  $limit
236
     * @param int       $page
237
     * @param string    $charset
238
     *
239
     * @return MessageCollection
240
     * @throws Exceptions\ConnectionFailedException
241
     * @throws Exceptions\InvalidWhereQueryCriteriaException
242
     * @throws GetMessagesFailedException
243
     * @throws MessageSearchValidationException
244
     *
245
     * @deprecated 1.0.5:2.0.0 No longer needed. Use Folder::getMessages('UNSEEN') instead
246
     * @see Folder::getMessages()
247
     */
248
    public function getUnseenMessages($criteria = 'UNSEEN', $fetch_options = null, $fetch_body = true, $fetch_attachment = true, $fetch_flags = true, $limit = null, $page = 1, $charset = "UTF-8") {
249
        return $this->getMessages($criteria, $fetch_options, $fetch_body, $fetch_attachment, $fetch_flags, $limit, $page, $charset);
250
    }
251
252
    /**
253
     * Search messages by a given search criteria
254
     *
255
     * @param array   $where  Is a two dimensional array where each array represents a criteria set:
256
     *                        ---------------------------------------------------------------------------------------
257
     *                        The following sample would search for all messages received from [email protected] or
258
     *                        contain the text "Hello world!":
259
     *                        [['FROM' => '[email protected]'],[' TEXT' => 'Hello world!']]
260
     *                        ---------------------------------------------------------------------------------------
261
     *                        The following sample would search for all messages received since march 15 2018:
262
     *                        [['SINCE' => Carbon::parse('15.03.2018')]]
263
     *                        ---------------------------------------------------------------------------------------
264
     *                        The following sample would search for all flagged messages:
265
     *                        [['FLAGGED']]
266
     *                        ---------------------------------------------------------------------------------------
267
     * @param int|null  $fetch_options
268
     * @param boolean   $fetch_body
269
     * @param boolean   $fetch_attachment
270
     * @param boolean   $fetch_flags
271
     * @param int|null  $limit
272
     * @param int       $page
273
     * @param string    $charset
274
     *
275
     * @return MessageCollection
276
     *
277
     * @throws Exceptions\ConnectionFailedException
278
     * @throws Exceptions\InvalidWhereQueryCriteriaException
279
     * @throws GetMessagesFailedException
280
     *
281
     * @doc http://php.net/manual/en/function.imap-search.php
282
     *      \imap_search() only supports IMAP2 search criterias, because the function mail_criteria() (from c-client lib)
283
     *      is used in ext/imap/php_imap.c for parsing the search string.
284
     *      IMAP2 search criteria is defined in RFC 1176, section "tag SEARCH search_criteria".
285
     *
286
     *      https://tools.ietf.org/html/rfc1176 - INTERACTIVE MAIL ACCESS PROTOCOL - VERSION 2
287
     *      https://tools.ietf.org/html/rfc1064 - INTERACTIVE MAIL ACCESS PROTOCOL - VERSION 2
288
     *      https://tools.ietf.org/html/rfc822  - STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES
289
     *      Date and time example from RFC822:
290
     *      date-time   =  [ day "," ] date time        ; dd mm yy
291
     *                                                  ;  hh:mm:ss zzz
292
     *
293
     *      day         =  "Mon"  / "Tue" /  "Wed"  / "Thu" /  "Fri"  / "Sat" /  "Sun"
294
     *
295
     *      date        =  1*2DIGIT month 2DIGIT        ; day month year
296
     *                                                  ;  e.g. 20 Jun 82
297
     *
298
     *      month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr" /  "May"  /  "Jun" /  "Jul"  /  "Aug" /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
299
     *
300
     *      time        =  hour zone                    ; ANSI and Military
301
     *
302
     *      hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59
303
     *
304
     *      zone        =  "UT"  / "GMT"         ; Universal Time
305
     *                                           ; North American : UT
306
     *                  =  "EST" / "EDT"         ;  Eastern:  - 5/ - 4
307
     *                  =  "CST" / "CDT"         ;  Central:  - 6/ - 5
308
     *                  =  "MST" / "MDT"         ;  Mountain: - 7/ - 6
309
     *                  =  "PST" / "PDT"         ;  Pacific:  - 8/ - 7
310
     *                  =  1ALPHA                ; Military: Z = UT;
311
     *                                           ;  A:-1; (J not used)
312
     *                                           ;  M:-12; N:+1; Y:+12
313
     *                  / ( ("+" / "-") 4DIGIT ) ; Local differential
314
     *                                           ;  hours+min. (HHMM)
315
     *
316
     * @deprecated 1.2.1:2.0.0 No longer needed. Use Folder::query() instead
317
     * @see Folder::query()
318
     */
319
    public function searchMessages(array $where, $fetch_options = null, $fetch_body = true,  $fetch_attachment = true, $fetch_flags = true, $limit = null, $page = 1, $charset = "UTF-8") {
320
        $this->getClient()->checkConnection();
321
322
        return $this->query($charset)->where($where)->setFetchOptions($fetch_options)->setFetchBody($fetch_body)
0 ignored issues
show
Bug introduced by
It seems like $fetch_options can also be of type integer; however, parameter $fetch_options of Webklex\IMAP\Query\Query::setFetchOptions() does only seem to accept boolean, 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

322
        return $this->query($charset)->where($where)->setFetchOptions(/** @scrutinizer ignore-type */ $fetch_options)->setFetchBody($fetch_body)
Loading history...
323
            ->setFetchAttachment($fetch_attachment)->setFetchFlags($fetch_flags)
0 ignored issues
show
Bug introduced by
$fetch_flags of type boolean is incompatible with the type integer expected by parameter $fetch_flags of Webklex\IMAP\Query\Query::setFetchFlags(). ( Ignorable by Annotation )

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

323
            ->setFetchAttachment($fetch_attachment)->setFetchFlags(/** @scrutinizer ignore-type */ $fetch_flags)
Loading history...
324
            ->limit($limit, $page)->get();
325
326
    }
327
328
    /**
329
     * Decode name.
330
     * It converts UTF7-IMAP encoding to UTF-8.
331
     *
332
     * @param $name
333
     *
334
     * @return mixed|string
335
     */
336
    protected function decodeName($name) {
337
        preg_match('#\{(.*)\}(.*)#', $name, $preg);
338
        return mb_convert_encoding($preg[2], "UTF-8", "UTF7-IMAP");
339
    }
340
341
    /**
342
     * Get the encoded name.
343
     * It converts the UTF-8 encoded name into an UTF7-IMAP encoded string.
344
     *
345
     * @return mixed|string
346
     */
347
    public function getEncodedName() {
348
        return mb_convert_encoding($this->full_name, "UTF7-IMAP", "UTF-8");
349
    }
350
351
    /**
352
     * Get simple name (without parent folders).
353
     *
354
     * @param $delimiter
355
     * @param $full_name
356
     *
357
     * @return mixed
358
     */
359
    protected function getSimpleName($delimiter, $full_name) {
360
        $arr = explode($delimiter, $full_name);
361
362
        return end($arr);
363
    }
364
365
    /**
366
     * Parse attributes and set it to object properties.
367
     *
368
     * @param $attributes
369
     */
370
    protected function parseAttributes($attributes) {
371
        $this->no_inferiors = ($attributes & LATT_NOINFERIORS) ? true : false;
372
        $this->no_select    = ($attributes & LATT_NOSELECT) ? true : false;
373
        $this->marked       = ($attributes & LATT_MARKED) ? true : false;
374
        $this->referal      = ($attributes & LATT_REFERRAL) ? true : false;
375
        $this->has_children = ($attributes & LATT_HASCHILDREN) ? true : false;
376
    }
377
378
    /**
379
     * Delete the current Mailbox
380
     * @param boolean $expunge
381
     *
382
     * @return bool
383
     *
384
     * @throws Exceptions\ConnectionFailedException
385
     */
386
    public function delete($expunge = true) {
387
        $status = \imap_deletemailbox($this->client->getConnection(), $this->path);
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_deletemailbox() 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

387
        $status = \imap_deletemailbox(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->path);
Loading history...
388
        if($expunge) $this->client->expunge();
389
390
        return $status;
391
    }
392
393
    /**
394
     * Move or Rename the current Mailbox
395
     *
396
     * @param string $target_mailbox
397
     * @param boolean $expunge
398
     *
399
     * @return bool
400
     *
401
     * @throws Exceptions\ConnectionFailedException
402
     */
403
    public function move($target_mailbox, $expunge = true) {
404
        $status = \imap_renamemailbox($this->client->getConnection(), $this->path, $target_mailbox);
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_renamemailbox() 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

404
        $status = \imap_renamemailbox(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->path, $target_mailbox);
Loading history...
405
        if($expunge) $this->client->expunge();
406
407
        return $status;
408
    }
409
410
    /**
411
     * Returns status information on a mailbox
412
     *
413
     * @param integer   $options
414
     *                  SA_MESSAGES     - set $status->messages to the number of messages in the mailbox
415
     *                  SA_RECENT       - set $status->recent to the number of recent messages in the mailbox
416
     *                  SA_UNSEEN       - set $status->unseen to the number of unseen (new) messages in the mailbox
417
     *                  SA_UIDNEXT      - set $status->uidnext to the next uid to be used in the mailbox
418
     *                  SA_UIDVALIDITY  - set $status->uidvalidity to a constant that changes when uids for the mailbox may no longer be valid
419
     *                  SA_ALL          - set all of the above
420
     *
421
     * @return object
422
     * @throws Exceptions\ConnectionFailedException
423
     */
424
    public function getStatus($options) {
425
        return \imap_status($this->client->getConnection(), $this->path, $options);
0 ignored issues
show
Bug introduced by
It seems like $this->client->getConnection() can also be of type true; however, parameter $imap_stream of imap_status() 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

425
        return \imap_status(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->path, $options);
Loading history...
426
    }
427
428
    /**
429
     * Append a string message to the current mailbox
430
     *
431
     * @param string $message
432
     * @param string $options
433
     * @param string $internal_date
434
     *
435
     * @return bool
436
     * @throws Exceptions\ConnectionFailedException
437
     */
438
    public function appendMessage($message, $options = null, $internal_date = null) {
439
        /**
440
         * Check if $internal_date is parsed. If it is null it should not be set. Otherwise the message can't be stored.
441
         * If this parameter is set, it will set the INTERNALDATE on the appended message. The parameter should be a
442
         * date string that conforms to the rfc2060 specifications for a date_time value or be a Carbon object.
443
         */
444
445
        if ($internal_date != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $internal_date of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
446
            if ($internal_date instanceof \Carbon\Carbon){
0 ignored issues
show
introduced by
$internal_date is never a sub-type of Carbon\Carbon.
Loading history...
447
                $internal_date = $internal_date->format('d-M-Y H:i:s O');
448
            }
449
            return \imap_append($this->client->getConnection(), $this->path, $message, $options, $internal_date);
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_append() 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

449
            return \imap_append(/** @scrutinizer ignore-type */ $this->client->getConnection(), $this->path, $message, $options, $internal_date);
Loading history...
450
        }
451
452
        return \imap_append($this->client->getConnection(), $this->path, $message, $options);
453
    }
454
455
    /**
456
     * Get the current Client instance
457
     *
458
     * @return Client
459
     */
460
    public function getClient() {
461
        return $this->client;
462
    }
463
464
    /**
465
     * @param $delimiter
466
     */
467
    public function setDelimiter($delimiter){
468
        if(in_array($delimiter, [null, '', ' ', false]) === true) {
469
            $delimiter = config('imap.options.delimiter', '/');
470
        }
471
472
        $this->delimiter = $delimiter;
473
    }
474
}
475