PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / trunk
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress vtrunk
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Libraries / Imap2 / Message.php

Message.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress trunk, at back/Libraries/Imap2/Message.php

547 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the PHP IMAP2 package.
5 *
6 * (c) Francesco Bianco <[email protected]>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace AcyMailing\Libraries\Imap2;
13
14 class Message
15 {
16 /**
17 * Returns an array of messages matching the given search criteria.
18 *
19 * @param $imap
20 * @param $criteria
21 * @param $flags
22 * @param $charset
23 *
24 * @return array|false|mixed
25 */
26 public static function search($imap, $criteria, $flags = SE_FREE, $charset = "")
27 {
28 if (!is_a($imap, Connection::class)) {
29 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
30 }
31
32 $client = $imap->getClient();
33 #$client->setDebug(true);
34
35 $result = $client->search($imap->getMailboxName(), $criteria, $flags & SE_UID);
36
37 if (empty($result->count())) {
38 return false;
39 }
40
41 $messages = $result->get();
42 foreach ($messages as &$message) {
43 $message = is_numeric($message) ? intval($message) : $message;
44 }
45
46 return $messages;
47 }
48
49 public static function sort($imap, $criteria, $reverse, $flags = 0, $searchCriteria = null, $charset = null)
50 {
51 if (is_a($imap, Connection::class)) {
52 $client = $imap->getClient();
53 #$client->setDebug(true);
54
55 $result = $client->search($imap->getMailboxName(), $criteria, $flags & SE_UID);
56
57 if (empty($result->count())) {
58 return false;
59 }
60
61 $messages = $result->get();
62 foreach ($messages as &$message) {
63 $message = is_numeric($message) ? intval($message) : $message;
64 }
65
66 return $messages;
67 }
68
69 return imap_sort($imap, $criteria, $reverse, $flags, $searchCriteria, $charset);
70 }
71
72 public static function headerInfo($imap, $messageNum, $fromLength = 0, $subjectLength = 0, $defaultHost = null)
73 {
74 if (!is_a($imap, Connection::class)) {
75 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
76 }
77
78 $client = $imap->getClient();
79 #$client->setDebug(true);
80
81 $messages = $client->fetch($imap->getMailboxName(), $messageNum, false, [
82 'BODY.PEEK[HEADER.FIELDS (SUBJECT FROM TO CC REPLY-TO DATE SIZE REFERENCES)]',
83 'ENVELOPE',
84 'INTERNALDATE',
85 'UID',
86 'FLAGS',
87 'RFC822.SIZE',
88 'RFC822.HEADER',
89 ]);
90
91 if (empty($messages)) {
92 return false;
93 }
94
95 foreach ($messages as $message) {
96 return HeaderInfo::fromMessage($message, $defaultHost);
97 }
98 }
99
100 public static function headers($imap)
101 {
102 if (!is_a($imap, Connection::class)) {
103 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
104 }
105
106 $client = $imap->getClient();
107 #$client->setDebug(true);
108
109 $status = $client->status($imap->getMailboxName(), ['MESSAGES']);
110 if (empty($status['MESSAGES'])) {
111 return [];
112 }
113
114 $sequence = '1:'.intval($status['MESSAGES']);
115 $messages = $client->fetch($imap->getMailboxName(), $sequence, false, [
116 'BODY.PEEK[HEADER.FIELDS (SUBJECT FROM TO CC REPLYTO MESSAGEID DATE SIZE REFERENCES)]',
117 #'UID',
118 'FLAGS',
119 'INTERNALDATE',
120 'RFC822.SIZE',
121 #'ENVELOPE',
122 'RFC822.HEADER',
123 ]);
124
125 if (empty($messages)) {
126 return [];
127 }
128
129 $headers = [];
130 foreach ($messages as $message) {
131 $from = ' ';
132 if ($message->from != 'no_host') {
133 $from = imap_rfc822_parse_adrlist($message->from, 'no_host');
134 $from = isset($from[0]->personal) ? $from[0]->personal : $message->from;
135 }
136
137 $date = explode(' ', $message->internaldate);
138 $subject = empty($message->subject) ? ' ' : $message->subject;
139 $unseen = empty($message->flags['SEEN']) ? 'U' : ' ';
140 $flagged = empty($message->flags['FLAGGED']) ? ' ' : 'F';
141 $answered = empty($message->flags['ANSWERED']) ? ' ' : 'A';
142 $draft = empty($message->flags['DRAFT']) ? ' ' : 'D';
143 $deleted = empty($message->flags['DELETED']) ? ' ' : 'X';
144
145 $header = ' '.$unseen.$flagged.$answered.$draft.$deleted.' '
146 .str_pad($message->id, 3, ' ', STR_PAD_LEFT).')'.$date[0].' '.str_pad($from, 20, ' ').' '
147 .substr($subject, 0, 25).' ('.$message->size.' chars)';
148
149 $headers[] = $header;
150 }
151
152 return $headers;
153 }
154
155 /**
156 * Picks the requested message in a fetch result.
157 * The result is keyed by sequence number, and it may also contain unsolicited FETCH responses
158 * the server sent for other messages (flag updates for instance), so we can't simply take the first one.
159 *
160 * @return object|null
161 */
162 private static function findMessage($messages, $messageNum, bool $isUid)
163 {
164 if (empty($messages) || !is_array($messages)) {
165 return null;
166 }
167
168 if (!$isUid) {
169 return $messages[$messageNum] ?? null;
170 }
171
172 // On a UID FETCH the server has to include the UID in its response, that's the only reliable way to identify the message
173 foreach ($messages as $message) {
174 if (isset($message->uid) && intval($message->uid) === intval($messageNum)) {
175 return $message;
176 }
177 }
178
179 return null;
180 }
181
182 public static function body($imap, $messageNum, $flags = 0)
183 {
184 if (!is_a($imap, Connection::class)) {
185 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
186 }
187
188 $client = $imap->getClient();
189 #$client->setDebug(true);
190
191 $isUid = boolval($flags & FT_UID);
192
193 $messages = $client->fetch($imap->getMailboxName(), $messageNum, $isUid, ['BODY[TEXT]']);
194
195 $message = self::findMessage($messages, $messageNum, $isUid);
196
197 if ($message === null) {
198 return false;
199 }
200
201 return $message->bodypart['TEXT'] ?? false;
202 }
203
204 public static function fetchBody($imap, $messageNum, $section, $flags = 0)
205 {
206 if (!is_a($imap, Connection::class)) {
207 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
208 }
209
210 $client = $imap->getClient();
211 #$client->setDebug(true);
212
213 if ($messageNum <= 0 || !is_int($messageNum)) {
214 trigger_error(Errors::badMessageNumber(debug_backtrace(), 1), E_USER_WARNING);
215
216 return false;
217 }
218
219 $isUid = boolval($flags & FT_UID);
220 $messages = $client->fetch($imap->getMailboxName(), $messageNum, $isUid, ['BODY['.$section.']']);
221
222 $message = self::findMessage($messages, $messageNum, $isUid);
223
224 // The message may have been expunged, the section may not exist, or the connection may have dropped
225 if ($message === null) {
226 return false;
227 }
228
229 if ($section) {
230 return $message->bodypart[$section] ?? false;
231 }
232
233 return $message->body ?? false;
234 }
235
236 public static function fetchMime($imap, $messageNum, $section, $flags = 0)
237 {
238 if (!is_a($imap, Connection::class)) {
239 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
240 }
241
242 if ($messageNum <= 0 || !is_int($messageNum)) {
243 trigger_error(Errors::badMessageNumber(debug_backtrace(), 1), E_USER_WARNING);
244
245 return false;
246 }
247
248 $client = $imap->getClient();
249 #$client->setDebug(true);
250
251 $isUid = boolval($flags & FT_UID);
252
253 $sectionKey = $section.'.MIME';
254 $messages = $client->fetch($imap->getMailboxName(), $messageNum, $isUid, ['BODY['.$sectionKey.']']);
255
256 $message = self::findMessage($messages, $messageNum, $isUid);
257
258 if ($message === null) {
259 return '';
260 }
261
262 if ($section && isset($message->bodypart[$sectionKey])) {
263 return $message->bodypart[$sectionKey];
264 }
265
266 return $message->body ?? '';
267 }
268
269 public static function saveBody($imap, $file, $messageNum, $section = "", $flags = 0)
270 {
271 if (is_a($imap, Connection::class)) {
272 $client = $imap->getClient();
273 #$client->setDebug(true);
274
275 $messages = $client->fetch($imap->getMailboxName(), $messageNum, false, ['BODY['.$section.']']);
276
277 $message = self::findMessage($messages, $messageNum, false);
278
279 if ($message === null) {
280 return false;
281 }
282
283 $body = $section ? ($message->bodypart[$section] ?? '') : ($message->body ?? '');
284
285 return file_put_contents($file, $body);
286 }
287
288 return imap_savebody($imap, $file, $messageNum, $section, $flags);
289 }
290
291 public static function fetchStructure($imap, $messageNum, $flags = 0)
292 {
293 if (!is_a($imap, Connection::class)) {
294 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
295 }
296
297 $client = $imap->getClient();
298 #$client->setDebug(true);
299
300 $isUid = boolval($flags & FT_UID);
301
302 $messages = $client->fetch($imap->getMailboxName(), $messageNum, $isUid, ['BODYSTRUCTURE']);
303
304 if (empty($messages)) {
305 return false;
306 }
307
308 foreach ($messages as $message) {
309 return BodyStructure::fromMessage($message);
310 }
311 }
312
313 public static function fetchHeader($imap, $messageNum, $flags = 0)
314 {
315 if (!is_a($imap, Connection::class)) {
316 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
317 }
318
319 /*
320 * FT_UID - The message_num argument is a UID
321 FT_INTERNAL - The return string is in "internal" format, without any attempt to canonicalize to CRLF newlines
322 FT_PREFETCHTEXT
323 * */
324
325 $client = $imap->getClient();
326 #$client->setDebug(true);
327
328 $isUid = boolval($flags & FT_UID);
329
330 $messages = $client->fetch($imap->getMailboxName(), $messageNum, $isUid, ['BODY[HEADER]']);
331
332 if (empty($messages)) {
333 return false;
334 }
335
336 foreach ($messages as $message) {
337 return $message->bodypart['HEADER'] ?? false;
338 }
339 }
340
341 public static function fetchOverview($imap, $sequence, $flags = 0)
342 {
343 if (!is_a($imap, Connection::class)) {
344 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
345 }
346
347 $client = $imap->getClient();
348 #$client->setDebug(true);
349
350 $messages = $client->fetch($imap->getMailboxName(), $sequence, false, [
351 'BODY[HEADER.FIELDS (SUBJECT FROM TO CC REPLYTO MESSAGEID DATE SIZE REFERENCES)]',
352 'UID',
353 'FLAGS',
354 'INTERNALDATE',
355 'RFC822.SIZE',
356 'ENVELOPE',
357 'RFC822.HEADER',
358 ]);
359
360 if ($sequence != '*' && count($messages) < Functions::expectedNumberOfMessages($sequence)) {
361 return [];
362 }
363
364 $overview = [];
365 foreach ($messages as $message) {
366 #var_dump($message);
367 #die();
368 $messageEntry = (object)[
369 'subject' => $message->envelope[1],
370 'from' => Functions::writeAddressFromEnvelope($message->envelope[2]),
371 'to' => $message->get('to'),
372 'date' => $message->envelope[0],
373 'message_id' => $message->envelope[9],
374 'references' => $message->references,
375 'in_reply_to' => $message->envelope[8],
376 'size' => $message->size,
377 'uid' => $message->uid,
378 'msgno' => $message->id,
379 'recent' => intval($message->flags['RECENT'] ?? 0),
380 'flagged' => intval($message->flags['FLAGGED'] ?? 0),
381 'answered' => intval($message->flags['ANSWERED'] ?? 0),
382 'deleted' => intval($message->flags['DELETED'] ?? 0),
383 'seen' => intval($message->flags['SEEN'] ?? 0),
384 'draft' => intval($message->flags['DRAFT'] ?? 0),
385 'udate' => strtotime($message->internaldate),
386 ];
387
388 if (empty($messageEntry->subject)) {
389 unset($messageEntry->subject);
390 }
391
392 if (empty($messageEntry->references)) {
393 unset($messageEntry->references);
394 }
395
396 if (empty($messageEntry->in_reply_to)) {
397 unset($messageEntry->in_reply_to);
398 }
399
400 if (empty($messageEntry->to)) {
401 unset($messageEntry->to);
402 }
403
404 $overview[] = $messageEntry;
405 }
406
407 return $overview;
408 }
409
410 public static function delete($imap, $messageNums, $flags = 0)
411 {
412 if (!is_a($imap, Connection::class)) {
413 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
414 }
415
416 $mailboxName = $imap->getMailboxName();
417
418 if ($mailboxName === Connection::DEFAULT_MAILBOX_GMAIL) {
419 Mail::move($imap, $messageNums, Connection::GMAIL_TRASH_MAILBOX, $flags);
420 } else {
421 $client = $imap->getClient();
422 $uids = ImapHelpers::idToUid($imap, $messageNums);
423 $client->flag($mailboxName, $uids, $client->flags['DELETED']);
424 }
425
426 return true;
427 }
428
429 public static function undelete($imap, $messageNums, $flags = 0)
430 {
431 if (is_a($imap, Connection::class)) {
432 $client = $imap->getClient();
433 #$client->setDebug(true);
434
435 $messages = $client->fetch($imap->getMailboxName(), $messageNums, false, ['UID']);
436 foreach ($messages as $message) {
437 $client->unflag($imap->getMailboxName(), $message->uid, $client->flags['DELETED']);
438 }
439
440 return true;
441 }
442
443 return imap_undelete($imap, $messageNums, $flags);
444 }
445
446 public static function expunge($imap)
447 {
448 if (is_a($imap, Connection::class)) {
449 $client = $imap->getClient();
450
451 return $client->expunge($imap->getMailboxName());
452 }
453
454 return imap_expunge($imap);
455 }
456
457 /**
458 * Sets flags on messages.
459 *
460 * @param $imap
461 * @param $sequence
462 * @param $flag
463 * @param $options
464 *
465 * @return bool
466 */
467 public static function setFlagFull($imap, $sequence, $flag, $options = 0)
468 {
469 if (!is_a($imap, Connection::class)) {
470 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
471 }
472
473 $client = $imap->getClient();
474
475 if (!($options & ST_UID)) {
476 $messages = $client->fetch($imap->getMailboxName(), $sequence, false, ['UID']);
477
478 $uid = [];
479 foreach ($messages as $message) {
480 $uid[] = $message->uid;
481 }
482
483 $sequence = implode(',', $uid);
484 }
485
486 return $client->flag($imap->getMailboxName(), $sequence, strtoupper(substr($flag, 1)));
487 }
488
489 /**
490 * Clears flags on messages.
491 *
492 * @param $imap
493 * @param $sequence
494 * @param $flag
495 * @param $options
496 *
497 * @return false|string
498 */
499 public static function clearFlagFull($imap, $sequence, $flag, $options = 0)
500 {
501 if (!is_a($imap, Connection::class)) {
502 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
503 }
504
505 $client = $imap->getClient();
506
507 if (!($options & ST_UID)) {
508 $messages = $client->fetch($imap->getMailboxName(), $sequence, false, ['UID']);
509
510 $uid = [];
511 foreach ($messages as $message) {
512 $uid[] = $message->uid;
513 }
514
515 $sequence = implode(',', $uid);
516 }
517
518 $client->unflag($imap->getMailboxName(), $sequence, strtoupper(substr($flag, 1)));
519
520 return false;
521 }
522
523 public static function msgno($imap, $messageUid)
524 {
525 if (!is_a($imap, Connection::class)) {
526 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
527 }
528
529 $client = $imap->getClient();
530
531 $msgNo = ImapHelpers::uidToId($imap, $messageUid);
532
533 return is_numeric($msgNo) ? intval($msgNo) : $msgNo;
534 }
535
536 public static function uid($imap, $messageNum)
537 {
538 if (!is_a($imap, Connection::class)) {
539 return Errors::invalidImapConnection(debug_backtrace(), 1, false);
540 }
541
542 $uid = ImapHelpers::idToUid($imap, $messageNum);
543
544 return is_numeric($uid) ? intval($uid) : $uid;
545 }
546 }
547