Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
2.92% |
4 / 137 |
|
10.26% |
4 / 39 |
CRAP | |
0.00% |
0 / 1 |
Imap | |
2.92% |
4 / 137 |
|
10.26% |
4 / 39 |
6539.83 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
connect | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
42 | |||
open | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
72 | |||
isOpen | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
connection | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getConnectionString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
listMailboxes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getInfo | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNumberOfMessages | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNumberOfReadMessages | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNumberOfUnreadMessages | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getOverview | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getMessageIds | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getMessageIdsBy | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getMessageHeaders | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getMessageHeadersBy | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getMessageNumber | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMessageHeaderInfoById | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
getMessageRawHeadersById | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
182 | |||
getMessageHeadersById | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getMessageStructure | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMessageBoundary | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
30 | |||
getMessageBody | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMessageParts | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getMessageAttachments | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
hasMessageAttachments | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
copyMessage | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
moveMessage | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
markAsRead | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
markAsUnread | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setMessageFlags | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
clearMessageFlags | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
deleteMessage | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
createMailbox | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
renameMailbox | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
deleteMailbox | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
decodeText | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
close | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Mail\Client; |
15 | |
16 | use Pop\Mail\Message; |
17 | |
18 | /** |
19 | * Mail client IMAP class |
20 | * |
21 | * @category Pop |
22 | * @package Pop\Mail |
23 | * @author Nick Sagona, III <dev@nolainteractive.com> |
24 | * @copyright Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
25 | * @license http://www.popphp.org/license New BSD License |
26 | * @version 3.6.0 |
27 | */ |
28 | class Imap extends AbstractClient |
29 | { |
30 | |
31 | /** |
32 | * Mailbox connection string |
33 | * @var string |
34 | */ |
35 | protected $connectionString = null; |
36 | |
37 | /** |
38 | * Mailbox connection resource |
39 | * @var resource |
40 | */ |
41 | protected $connection = null; |
42 | |
43 | /** |
44 | * Constructor |
45 | * |
46 | * Instantiate the IMAP mail client object |
47 | * |
48 | * @param string $host |
49 | * @param int $port |
50 | * @param string $service |
51 | */ |
52 | public function __construct($host, $port, $service = 'imap') |
53 | { |
54 | parent::__construct($host, $port, $service); |
55 | } |
56 | |
57 | /** |
58 | * Connect to an IMAP mailbox |
59 | * |
60 | * @param array $creds |
61 | * @param string $flags |
62 | * @param int $options |
63 | * @param int $retries |
64 | * @param array $params |
65 | * @return Imap |
66 | */ |
67 | public static function connect(array $creds, $flags = null, $options = null, $retries = null, array $params = null) |
68 | { |
69 | if (!isset($creds['host']) || !isset($creds['port']) || !isset($creds['username']) || !isset($creds['password'])) { |
70 | throw new Exception( |
71 | "Error: The credentials were incomplete. They must contain 'host', 'port', 'username' and 'password'." |
72 | ); |
73 | } |
74 | |
75 | $imap = new static($creds['host'], $creds['port']); |
76 | $imap->setUsername($creds['username']) |
77 | ->setPassword($creds['password']); |
78 | |
79 | if (isset($creds['folder'])) { |
80 | $imap->setFolder($creds['folder']); |
81 | } |
82 | |
83 | return $imap->open($flags, $options, $retries, $params); |
84 | } |
85 | |
86 | /** |
87 | * Open mailbox connection |
88 | * |
89 | * @param string $flags |
90 | * @param int $options |
91 | * @param int $retries |
92 | * @param array $params |
93 | * @return Imap |
94 | */ |
95 | public function open($flags = null, $options = null, $retries = null, array $params = null) |
96 | { |
97 | $this->connectionString = '{' . $this->host . ':' . $this->port . '/' . $this->service; |
98 | |
99 | if (null !== $flags) { |
100 | $this->connectionString .= $flags; |
101 | } |
102 | |
103 | $this->connectionString .= '}'; |
104 | |
105 | if ((null !== $options) && (null !== $retries) && (null !== $params)) { |
106 | $this->connection = imap_open( |
107 | $this->connectionString . $this->folder, $this->username, $this->password, $options, $retries, $params |
108 | ); |
109 | } else if ((null !== $options) && (null !== $retries)) { |
110 | $this->connection = imap_open( |
111 | $this->connectionString . $this->folder, $this->username, $this->password, $options, $retries |
112 | ); |
113 | } else if (null !== $options) { |
114 | $this->connection = imap_open( |
115 | $this->connectionString . $this->folder, $this->username, $this->password, $options |
116 | ); |
117 | } else { |
118 | $this->connection = imap_open( |
119 | $this->connectionString . $this->folder, $this->username, $this->password |
120 | ); |
121 | } |
122 | |
123 | return $this; |
124 | } |
125 | |
126 | /** |
127 | * Determine if the mailbox connection has been opened |
128 | * |
129 | * @return boolean |
130 | */ |
131 | public function isOpen() |
132 | { |
133 | return is_resource($this->connection); |
134 | } |
135 | |
136 | /** |
137 | * Get mailbox connection |
138 | * |
139 | * @return resource |
140 | */ |
141 | public function connection() |
142 | { |
143 | return $this->connection; |
144 | } |
145 | |
146 | /** |
147 | * Get mailbox connection string |
148 | * |
149 | * @return string |
150 | */ |
151 | public function getConnectionString() |
152 | { |
153 | return $this->connectionString; |
154 | } |
155 | |
156 | /** |
157 | * List mailboxes |
158 | * |
159 | * @param string $pattern |
160 | * @return array |
161 | */ |
162 | public function listMailboxes($pattern = '*') |
163 | { |
164 | return imap_list($this->connection, $this->connectionString . $this->folder, $pattern); |
165 | } |
166 | |
167 | /** |
168 | * Get mailbox status |
169 | * |
170 | * @return \stdClass |
171 | */ |
172 | public function getStatus() |
173 | { |
174 | return imap_status($this->connection, $this->connectionString . $this->folder, SA_ALL); |
175 | |
176 | } |
177 | |
178 | /** |
179 | * Get mailbox info |
180 | * |
181 | * @return \stdClass |
182 | */ |
183 | public function getInfo() |
184 | { |
185 | return imap_mailboxmsginfo($this->connection); |
186 | |
187 | } |
188 | /** |
189 | * Get total number of messages |
190 | * |
191 | * @return int |
192 | */ |
193 | public function getNumberOfMessages() |
194 | { |
195 | return imap_num_msg($this->connection); |
196 | } |
197 | |
198 | /** |
199 | * Get total number of read messages |
200 | * |
201 | * @return int |
202 | */ |
203 | public function getNumberOfReadMessages() |
204 | { |
205 | return abs($this->getNumberOfMessages() - $this->getStatus()->unseen); |
206 | } |
207 | |
208 | /** |
209 | * Get total number of unread messages |
210 | * |
211 | * @return int |
212 | */ |
213 | public function getNumberOfUnreadMessages() |
214 | { |
215 | return $this->getStatus()->unseen; |
216 | } |
217 | |
218 | /** |
219 | * Get message overviews |
220 | * |
221 | * @param mixed $ids |
222 | * @param int $options |
223 | * @return array |
224 | */ |
225 | public function getOverview($ids, $options = FT_UID) |
226 | { |
227 | if (is_array($ids)) { |
228 | $ids = implode(',', $ids); |
229 | } |
230 | return imap_fetch_overview($this->connection, $ids, $options); |
231 | } |
232 | |
233 | /** |
234 | * Get message IDs from a mailbox |
235 | * |
236 | * @param string $criteria |
237 | * @param int $options |
238 | * @param string $charset |
239 | * @return array |
240 | */ |
241 | public function getMessageIds($criteria = 'ALL', $options = SE_UID, $charset = null) |
242 | { |
243 | return (null !== $charset) ? |
244 | imap_search($this->connection, $criteria, $options, $charset) : |
245 | imap_search($this->connection, $criteria, $options); |
246 | } |
247 | |
248 | /** |
249 | * Get message IDs from a mailbox by a sort criteria |
250 | * |
251 | * @param int $criteria |
252 | * @param boolean $reverse |
253 | * @param int $options |
254 | * @param string $search |
255 | * @param string $charset |
256 | * @return array |
257 | */ |
258 | public function getMessageIdsBy($criteria = SORTDATE, $reverse = true, $options = SE_UID, $search = 'ALL', $charset = null) |
259 | { |
260 | return (null !== $charset) ? |
261 | imap_sort($this->connection, $criteria, (int)$reverse, $options, $search, $charset) : |
262 | imap_sort($this->connection, $criteria, (int)$reverse, $options, $search); |
263 | } |
264 | |
265 | /** |
266 | * Get message headers from a mailbox |
267 | * |
268 | * @param string $criteria |
269 | * @param int $options |
270 | * @param string $charset |
271 | * @return array |
272 | */ |
273 | public function getMessageHeaders($criteria = 'ALL', $options = SE_UID, $charset = null) |
274 | { |
275 | $headers = []; |
276 | $ids = $this->getMessageIds($criteria, $options, $charset); |
277 | |
278 | foreach ($ids as $id) { |
279 | $headers[$id] = $this->getMessageHeadersById($id); |
280 | } |
281 | |
282 | return $headers; |
283 | } |
284 | |
285 | /** |
286 | * Get message headers from a mailbox |
287 | * |
288 | * @param int $criteria |
289 | * @param boolean $reverse |
290 | * @param int $options |
291 | * @param string $search |
292 | * @param string $charset |
293 | * @return array |
294 | */ |
295 | public function getMessageHeadersBy($criteria = SORTDATE, $reverse = true, $options = SE_UID, $search = 'ALL', $charset = null) |
296 | { |
297 | $headers = []; |
298 | $ids = $this->getMessageIdsBy($criteria, $reverse, $options, $search, $charset); |
299 | |
300 | foreach ($ids as $id) { |
301 | $headers[$id] = $this->getMessageHeadersById($id); |
302 | } |
303 | |
304 | return $headers; |
305 | } |
306 | |
307 | /** |
308 | * Get message number from UID |
309 | * |
310 | * @param int $id |
311 | * @return int |
312 | */ |
313 | public function getMessageNumber($id) |
314 | { |
315 | return imap_msgno($this->connection, $id); |
316 | } |
317 | |
318 | /** |
319 | * Get raw message headers by message ID |
320 | * |
321 | * @param int $id |
322 | * @return array |
323 | */ |
324 | public function getMessageHeaderInfoById($id) |
325 | { |
326 | $headers = imap_headerinfo($this->connection, imap_msgno($this->connection, $id)); |
327 | return ($headers !== false) ? json_decode(json_encode($headers), true) : []; |
328 | } |
329 | |
330 | /** |
331 | * Get raw message headers by message ID |
332 | * |
333 | * @param int $id |
334 | * @return array |
335 | */ |
336 | public function getMessageRawHeadersById($id) |
337 | { |
338 | $headers = explode("\r\n", imap_fetchheader($this->connection, $id, FT_UID)); |
339 | $parsedHeaders = []; |
340 | $name = null; |
341 | |
342 | foreach ($headers as $header) { |
343 | if (((substr($header, 0, 1) == ' ') || (substr($header, 0, 1) == "\t")) && |
344 | (null !== $name) && isset($parsedHeaders[$name])) { |
345 | if (is_array($parsedHeaders[$name])) { |
346 | $parsedHeaders[$name][key($parsedHeaders[$name])] .= $header; |
347 | } else { |
348 | $parsedHeaders[$name] .= $header; |
349 | } |
350 | } else if (!empty($header) && (strpos($header, ':') !== false)) { |
351 | $name = substr($header, 0, strpos($header, ':')); |
352 | $value = (strpos($header, ':') < (strlen($header) - 1)) ? substr($header, (strpos($header, ': ') + 2)) : ''; |
353 | |
354 | if (isset($parsedHeaders[$name])) { |
355 | if (!is_array($parsedHeaders[$name])) { |
356 | $parsedHeaders[$name] = [$parsedHeaders[$name]]; |
357 | } |
358 | $parsedHeaders[$name][] = $value; |
359 | end($parsedHeaders[$name]); |
360 | } else { |
361 | $parsedHeaders[$name] = $value; |
362 | } |
363 | } |
364 | } |
365 | |
366 | return array_map(function($value) { |
367 | if (is_array($value)) { |
368 | return array_map('trim', $value); |
369 | } else { |
370 | return trim($value); |
371 | } |
372 | }, $parsedHeaders); |
373 | } |
374 | |
375 | /** |
376 | * Get message headers by message ID |
377 | * |
378 | * @param int $id |
379 | * @return array |
380 | */ |
381 | public function getMessageHeadersById($id) |
382 | { |
383 | $headers = imap_rfc822_parse_headers(imap_fetchheader($this->connection, $id, FT_UID)); |
384 | return json_decode(json_encode($headers), true); |
385 | } |
386 | |
387 | /** |
388 | * Get message structure by message ID |
389 | * |
390 | * @param int $id |
391 | * @return \stdClass |
392 | */ |
393 | public function getMessageStructure($id) |
394 | { |
395 | return imap_fetchstructure($this->connection, $id, FT_UID); |
396 | } |
397 | |
398 | /** |
399 | * Get message boundary by message ID |
400 | * |
401 | * @param int $id |
402 | * @return string |
403 | */ |
404 | public function getMessageBoundary($id) |
405 | { |
406 | $boundary = null; |
407 | $structure = $this->getMessageStructure($id); |
408 | |
409 | if (isset($structure->parameters) && (count($structure->parameters) > 0)) { |
410 | foreach ($structure->parameters as $parameter) { |
411 | if (strtolower($parameter->attribute) == 'boundary') { |
412 | $boundary = $parameter->value; |
413 | break; |
414 | } |
415 | } |
416 | } |
417 | |
418 | return $boundary; |
419 | } |
420 | |
421 | /** |
422 | * Get message body by message ID |
423 | * |
424 | * @param int $id |
425 | * @return string |
426 | */ |
427 | public function getMessageBody($id) |
428 | { |
429 | return imap_body($this->connection, $id, FT_UID); |
430 | } |
431 | |
432 | /** |
433 | * Get message parts by message ID |
434 | * |
435 | * @param int $id |
436 | * @return array |
437 | */ |
438 | public function getMessageParts($id) |
439 | { |
440 | $boundary = $this->getMessageBoundary($id); |
441 | $body = $this->getMessageBody($id); |
442 | return Message\Part::parse($body, $boundary); |
443 | } |
444 | |
445 | /** |
446 | * Get message parts by message ID |
447 | * |
448 | * @param int $id |
449 | * @param string $encoding |
450 | * @return array |
451 | */ |
452 | public function getMessageAttachments($id, $encoding = null) |
453 | { |
454 | return array_filter($this->getMessageParts($id, $encoding), function($part){ |
455 | return $part->attachment; |
456 | }); |
457 | } |
458 | |
459 | /** |
460 | * Get message parts by message ID |
461 | * |
462 | * @param int $id |
463 | * @param string $encoding |
464 | * @return boolean |
465 | */ |
466 | public function hasMessageAttachments($id, $encoding = null) |
467 | { |
468 | return (count($this->getMessageAttachments($id, $encoding)) > 0); |
469 | } |
470 | |
471 | /** |
472 | * Copy messages to another mailbox |
473 | * |
474 | * @param mixed $ids |
475 | * @param string|array $to |
476 | * @param int $options |
477 | * @return Imap |
478 | */ |
479 | public function copyMessage($ids, $to, $options = CP_UID) |
480 | { |
481 | if (is_array($ids)) { |
482 | $ids = implode(',', $ids); |
483 | } |
484 | |
485 | imap_mail_copy($this->connection, $ids, $to, $options); |
486 | return $this; |
487 | } |
488 | |
489 | /** |
490 | * Move messages to another mailbox |
491 | * |
492 | * @param mixed $ids |
493 | * @param string|array $to |
494 | * @param int $options |
495 | * @return Imap |
496 | */ |
497 | public function moveMessage($ids, $to, $options = CP_UID) |
498 | { |
499 | if (is_array($ids)) { |
500 | $ids = implode(',', $ids); |
501 | } |
502 | |
503 | imap_mail_move($this->connection, $ids, $to, $options); |
504 | return $this; |
505 | } |
506 | |
507 | /** |
508 | * Mark a message or messages as read |
509 | * |
510 | * @param mixed $ids |
511 | * @param int $options |
512 | * @return Imap |
513 | */ |
514 | public function markAsRead($ids, $options = ST_UID) |
515 | { |
516 | return $this->setMessageFlags($ids, "\\Seen", $options); |
517 | } |
518 | |
519 | /** |
520 | * Mark a message or messages as unread |
521 | * |
522 | * @param mixed $ids |
523 | * @param int $options |
524 | * @return Imap |
525 | */ |
526 | public function markAsUnread($ids, $options = ST_UID) |
527 | { |
528 | return $this->clearMessageFlags($ids, "\\Seen", $options); |
529 | } |
530 | |
531 | /** |
532 | * Mark a message or messages as read |
533 | * |
534 | * @param mixed $ids |
535 | * @param string $flags |
536 | * @param int $options |
537 | * @return Imap |
538 | */ |
539 | public function setMessageFlags($ids, $flags, $options = ST_UID) |
540 | { |
541 | if (is_array($ids)) { |
542 | $ids = implode(',', $ids); |
543 | } |
544 | imap_setflag_full($this->connection, $ids, $flags, $options); |
545 | |
546 | return $this; |
547 | } |
548 | |
549 | /** |
550 | * Mark a message or messages as unread |
551 | * |
552 | * @param mixed $ids |
553 | * @param string $flags |
554 | * @param int $options |
555 | * @return Imap |
556 | */ |
557 | public function clearMessageFlags($ids, $flags, $options = ST_UID) |
558 | { |
559 | if (is_array($ids)) { |
560 | $ids = implode(',', $ids); |
561 | } |
562 | imap_clearflag_full($this->connection, $ids, $flags, $options); |
563 | |
564 | return $this; |
565 | } |
566 | |
567 | /** |
568 | * Delete message |
569 | * |
570 | * @param int $id |
571 | * @param int $options |
572 | * @return Imap |
573 | */ |
574 | public function deleteMessage($id, $options = FT_UID) |
575 | { |
576 | imap_delete($this->connection, $id, $options); |
577 | return $this; |
578 | } |
579 | |
580 | /** |
581 | * Create mailbox |
582 | * |
583 | * @param string $new |
584 | * @return Imap |
585 | */ |
586 | public function createMailbox($new) |
587 | { |
588 | if (strpos($new, $this->connectionString) === false) { |
589 | $new = $this->connectionString . $new; |
590 | } |
591 | imap_createmailbox($this->connection, $new); |
592 | return $this; |
593 | } |
594 | |
595 | /** |
596 | * Rename mailbox |
597 | * |
598 | * @param string $new |
599 | * @param string $old |
600 | * @return Imap |
601 | */ |
602 | public function renameMailbox($new, $old = null) |
603 | { |
604 | if (null === $old) { |
605 | $old = $this->connectionString . $this->folder; |
606 | } else if (strpos($old, $this->connectionString) === false) { |
607 | $old = $this->connectionString . $old; |
608 | } |
609 | |
610 | if (strpos($new, $this->connectionString) === false) { |
611 | $new = $this->connectionString . $new; |
612 | } |
613 | |
614 | imap_renamemailbox($this->connection, $old, $new); |
615 | return $this; |
616 | } |
617 | |
618 | /** |
619 | * Delete mailbox |
620 | * |
621 | * @param string $mailbox |
622 | * @throws Exception |
623 | * @return Imap |
624 | */ |
625 | public function deleteMailbox($mailbox = null) |
626 | { |
627 | if (null === $mailbox) { |
628 | $mailbox = $this->folder; |
629 | } |
630 | |
631 | if (empty($mailbox)) { |
632 | throw new Exception('Error: The mailbox is not set.'); |
633 | } |
634 | |
635 | imap_deletemailbox($this->connection, $this->connectionString . $mailbox); |
636 | return $this; |
637 | } |
638 | |
639 | /** |
640 | * Decode text |
641 | * |
642 | * @param string $text |
643 | * @return string |
644 | */ |
645 | public function decodeText($text) |
646 | { |
647 | return Message::decodeText($text); |
648 | } |
649 | |
650 | /** |
651 | * Close the mailbox connection resource |
652 | * |
653 | * @return void |
654 | */ |
655 | public function close() |
656 | { |
657 | if (is_resource($this->connection)) { |
658 | imap_close($this->connection); |
659 | } |
660 | } |
661 | |
662 | } |