PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.2.2
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.2.2
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 / Roundcube / ResultIndex.php

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

441 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * +-----------------------------------------------------------------------+
5 * | This file is part of the Roundcube Webmail client |
6 * | |
7 * | Copyright (C) The Roundcube Dev Team |
8 * | Copyright (C) Kolab Systems AG |
9 * | |
10 * | Licensed under the GNU General Public License version 3 or |
11 * | any later version with exceptions for skins & plugins. |
12 * | See the README file for a full license statement. |
13 * | |
14 * | PURPOSE: |
15 * | SORT/SEARCH/ESEARCH response handler |
16 * +-----------------------------------------------------------------------+
17 * | Author: Thomas Bruederli <[email protected]> |
18 * | Author: Aleksander Machniak <[email protected]> |
19 * +-----------------------------------------------------------------------+
20 */
21
22 namespace AcyMailing\Libraries\Imap2\Roundcube;
23
24 use AcyMailing\Libraries\Imap2\Offset;
25 use AcyMailing\Libraries\Imap2\rcube_imap_generic;
26
27 /**
28 * Class for accessing IMAP's SORT/SEARCH/ESEARCH result
29 *
30 * @package Framework
31 * @subpackage Storage
32 */
33 class ResultIndex
34 {
35 public $incomplete = false;
36
37 protected $raw_data;
38 protected $mailbox;
39 protected $meta = [];
40 protected $params = [];
41 protected $order = 'ASC';
42
43 const SEPARATOR_ELEMENT = ' ';
44
45
46 /**
47 * Object constructor.
48 */
49 public function __construct($mailbox = null, $data = null, $order = null)
50 {
51 $this->mailbox = $mailbox;
52 $this->order = $order == 'DESC' ? 'DESC' : 'ASC';
53 $this->init($data);
54 }
55
56 /**
57 * Initializes object with SORT command response
58 *
59 * @param string $data IMAP response string
60 */
61 public function init($data = null)
62 {
63 $this->meta = [];
64
65 $data = explode('*', (string)$data);
66
67 // ...skip unilateral untagged server responses
68 for ($i = 0, $len = count($data) ; $i < $len ; $i++) {
69 $data_item = &$data[$i];
70 if (preg_match('/^ SORT/i', $data_item)) {
71 // valid response, initialize raw_data for is_error()
72 $this->raw_data = '';
73 $data_item = substr($data_item, 5);
74 break;
75 } elseif (preg_match('/^ (E?SEARCH)/i', $data_item, $m)) {
76 // valid response, initialize raw_data for is_error()
77 $this->raw_data = '';
78 $data_item = substr($data_item, strlen($m[0]));
79
80 if (strtoupper($m[1]) == 'ESEARCH') {
81 $data_item = trim($data_item);
82 // remove MODSEQ response
83 if (preg_match('/\(MODSEQ ([0-9]+)\)$/i', $data_item, $m)) {
84 $data_item = substr($data_item, 0, -strlen($m[0]));
85 $this->params['MODSEQ'] = $m[1];
86 }
87 // remove TAG response part
88 if (preg_match('/^\(TAG ["a-z0-9]+\)\s*/i', $data_item, $m)) {
89 $data_item = substr($data_item, strlen($m[0]));
90 }
91 // remove UID
92 $data_item = preg_replace('/^UID\s*/i', '', $data_item);
93
94 // ESEARCH parameters
95 while (preg_match('/^([a-z]+) ([0-9:,]+)\s*/i', $data_item, $m)) {
96 $param = strtoupper($m[1]);
97 $value = $m[2];
98
99 $this->params[$param] = $value;
100 $data_item = substr($data_item, strlen($m[0]));
101
102 if (in_array($param, ['COUNT', 'MIN', 'MAX'])) {
103 $this->meta[strtolower($param)] = (int)$value;
104 }
105 }
106
107 // @TODO: Implement compression using compressMessageSet() in __sleep() and __wakeup() ?
108 // @TODO: work with compressed result?!
109 if (isset($this->params['ALL'])) {
110 $data_item = implode(
111 self::SEPARATOR_ELEMENT,
112 ImapClient::uncompressMessageSet($this->params['ALL'])
113 );
114 }
115 }
116
117 break;
118 }
119
120 unset($data[$i]);
121 }
122
123 $data = array_filter($data);
124
125 if (empty($data)) {
126 return;
127 }
128
129 $data = array_shift($data);
130 $data = trim($data);
131 $data = preg_replace('/[\r\n]/', '', $data);
132 $data = preg_replace('/\s+/', ' ', $data);
133
134 $this->raw_data = $data;
135 }
136
137 /**
138 * Checks the result from IMAP command
139 *
140 * @return bool True if the result is an error, False otherwise
141 */
142 public function is_error()
143 {
144 return $this->raw_data === null;
145 }
146
147 /**
148 * Checks if the result is empty
149 *
150 * @return bool True if the result is empty, False otherwise
151 */
152 public function is_empty()
153 {
154 return empty($this->raw_data);
155 }
156
157 /**
158 * Returns number of elements in the result
159 *
160 * @return int Number of elements
161 */
162 public function count()
163 {
164 if (isset($this->meta['count']) && $this->meta['count'] !== null) {
165 return $this->meta['count'];
166 }
167
168 if (empty($this->raw_data)) {
169 $this->meta['count'] = 0;
170 $this->meta['length'] = 0;
171 } else {
172 $this->meta['count'] = 1 + substr_count($this->raw_data, self::SEPARATOR_ELEMENT);
173 }
174
175 return $this->meta['count'];
176 }
177
178 /**
179 * Returns number of elements in the result.
180 * Alias for count() for compatibility with rcube_result_thread
181 *
182 * @return int Number of elements
183 */
184 public function count_messages()
185 {
186 return $this->count();
187 }
188
189 /**
190 * Returns maximal message identifier in the result
191 *
192 * @return int Maximal message identifier
193 */
194 public function max()
195 {
196 if (!isset($this->meta['max'])) {
197 $this->meta['max'] = (int)@max($this->get());
198 }
199
200 return $this->meta['max'];
201 }
202
203 /**
204 * Returns minimal message identifier in the result
205 *
206 * @return int Minimal message identifier
207 */
208 public function min()
209 {
210 if (!isset($this->meta['min'])) {
211 $this->meta['min'] = (int)@min($this->get());
212 }
213
214 return $this->meta['min'];
215 }
216
217 /**
218 * Slices data set.
219 *
220 * @param $offset Offset (as for PHP's array_slice())
221 * @param $length Number of elements (as for PHP's array_slice())
222 */
223 public function slice($offset, $length)
224 {
225 $data = $this->get();
226 $data = array_slice($data, $offset, $length);
227
228 $this->meta = [];
229 $this->meta['count'] = count($data);
230 $this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
231 }
232
233 /**
234 * Filters data set. Removes elements not listed in $ids list.
235 *
236 * @param array $ids List of IDs to remove.
237 */
238 public function filter($ids = [])
239 {
240 $data = $this->get();
241 $data = array_intersect($data, $ids);
242
243 $this->meta = [];
244 $this->meta['count'] = count($data);
245 $this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
246 }
247
248 /**
249 * Reverts order of elements in the result
250 */
251 public function revert()
252 {
253 $this->order = $this->order == 'ASC' ? 'DESC' : 'ASC';
254
255 if (empty($this->raw_data)) {
256 return;
257 }
258
259 $data = $this->get();
260 $data = array_reverse($data);
261 $this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
262
263 $this->meta['pos'] = [];
264 }
265
266 /**
267 * Check if the given message ID exists in the object
268 *
269 * @param int $msgid Message ID
270 * @param bool $get_index When enabled element's index will be returned.
271 * Elements are indexed starting with 0
272 *
273 * @return mixed False if message ID doesn't exist, True if exists or
274 * index of the element if $get_index=true
275 */
276 public function exists($msgid, $get_index = false)
277 {
278 if (empty($this->raw_data)) {
279 return false;
280 }
281
282 $msgid = (int)$msgid;
283 $begin = implode('|', ['^', preg_quote(self::SEPARATOR_ELEMENT, '/')]);
284 $end = implode('|', ['$', preg_quote(self::SEPARATOR_ELEMENT, '/')]);
285
286 if (preg_match(
287 "/($begin)$msgid($end)/",
288 $this->raw_data,
289 $m,
290 $get_index ? PREG_OFFSET_CAPTURE : null
291 )
292 ) {
293 if ($get_index) {
294 $idx = 0;
295 if ($m[0][1]) {
296 $idx = 1 + substr_count($this->raw_data, self::SEPARATOR_ELEMENT, 0, $m[0][1]);
297 }
298 // cache position of this element, so we can use it in get_element()
299 $this->meta['pos'][$idx] = (int)$m[0][1];
300
301 return $idx;
302 }
303
304 return true;
305 }
306
307 return false;
308 }
309
310 /**
311 * Return all messages in the result.
312 *
313 * @return array List of message IDs
314 */
315 public function get()
316 {
317 if (empty($this->raw_data)) {
318 return [];
319 }
320
321 return explode(self::SEPARATOR_ELEMENT, $this->raw_data);
322 }
323
324 /**
325 * Return all messages in the result.
326 *
327 * @return array List of message IDs
328 */
329 public function get_compressed()
330 {
331 if (empty($this->raw_data)) {
332 return '';
333 }
334
335 return rcube_imap_generic::compressMessageSet($this->get());
336 }
337
338 /**
339 * Return result element at specified index
340 *
341 * @param int|string $index Element's index or "FIRST" or "LAST"
342 *
343 * @return int Element value
344 */
345 public function get_element($index)
346 {
347 $count = $this->count();
348
349 if (!$count) {
350 return null;
351 }
352
353 // first element
354 if ($index === 0 || $index === '0' || $index === 'FIRST') {
355 $pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT);
356 if ($pos === false) {
357 $result = (int)$this->raw_data;
358 } else {
359 $result = (int)substr($this->raw_data, 0, $pos);
360 }
361
362 return $result;
363 }
364
365 // last element
366 if ($index === 'LAST' || $index == $count - 1) {
367 $pos = strrpos($this->raw_data, self::SEPARATOR_ELEMENT);
368 if ($pos === false) {
369 $result = (int)$this->raw_data;
370 } else {
371 $result = (int)substr($this->raw_data, $pos);
372 }
373
374 return $result;
375 }
376
377 // do we know the position of the element or the neighbour of it?
378 if (!empty($this->meta['pos'])) {
379 if (isset($this->meta['pos'][$index])) {
380 $pos = $this->meta['pos'][$index];
381 } elseif (isset($this->meta['pos'][$index - 1])) {
382 $pos = strpos(
383 $this->raw_data,
384 self::SEPARATOR_ELEMENT,
385 $this->meta['pos'][$index - 1] + 1
386 );
387 } elseif (isset($this->meta['pos'][$index + 1])) {
388 $pos = strrpos(
389 $this->raw_data,
390 self::SEPARATOR_ELEMENT,
391 $this->meta['pos'][$index + 1] - $this->length() - 1
392 );
393 }
394
395 if (isset($pos) && preg_match('/([0-9]+)/', $this->raw_data, $m, null, $pos)) {
396 return (int)$m[1];
397 }
398 }
399
400 // Finally use less effective method
401 $data = explode(self::SEPARATOR_ELEMENT, $this->raw_data);
402
403 return $data[$index];
404 }
405
406 /**
407 * Returns response parameters, e.g. ESEARCH's MIN/MAX/COUNT/ALL/MODSEQ
408 * or internal data e.g. MAILBOX, ORDER
409 *
410 * @param string $param Parameter name
411 *
412 * @return array|string Response parameters or parameter value
413 */
414 public function get_parameters($param = null)
415 {
416 $params = $this->params;
417 $params['MAILBOX'] = $this->mailbox;
418 $params['ORDER'] = $this->order;
419
420 if ($param !== null) {
421 return $params[$param];
422 }
423
424 return $params;
425 }
426
427 /**
428 * Returns length of internal data representation
429 *
430 * @return int Data length
431 */
432 protected function length()
433 {
434 if (!isset($this->meta['length'])) {
435 $this->meta['length'] = strlen($this->raw_data);
436 }
437
438 return $this->meta['length'];
439 }
440 }
441