PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.0
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 11.1.0, at back/Libraries/Imap2/Roundcube/ResultIndex.php

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