PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.1.4
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.1.4
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 / vendor / javanile / php-imap2 / src / Roundcube / ResultThread.php

ResultThread.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 10.1.4, at vendor/javanile/php-imap2/src/Roundcube/ResultThread.php

488 lines 13.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 namespace Javanile\Imap2\Roundcube;
5
6 use Javanile\Imap2\Offset;
7 use Javanile\Imap2\rcube_imap_generic;
8 use Javanile\Imap2\rcube_result_index;
9
10 class ResultThread
11 {
12 public $incomplete = false;
13
14 protected $raw_data;
15 protected $mailbox;
16 protected $meta = array();
17 protected $order = 'ASC';
18
19 const SEPARATOR_ELEMENT = ' ';
20 const SEPARATOR_ITEM = '~';
21 const SEPARATOR_LEVEL = ':';
22
23
24 public function __construct($mailbox = null, $data = null)
25 {
26 $this->mailbox = $mailbox;
27 $this->init($data);
28 }
29
30 public function init($data = null)
31 {
32 $this->meta = array();
33
34 $data = explode('*', (string)$data);
35
36 for ($i=0, $len=count($data); $i<$len; $i++) {
37 if (preg_match('/^ THREAD/i', $data[$i])) {
38 $this->raw_data = '';
39 $data[$i] = substr($data[$i], 7);
40 break;
41 }
42
43 unset($data[$i]);
44 }
45
46 if (empty($data)) {
47 return;
48 }
49
50 $data = array_shift($data);
51 $data = trim($data);
52 $data = preg_replace('/[\r\n]/', '', $data);
53 $data = preg_replace('/\s+/', ' ', $data);
54
55 $this->raw_data = $this->parse_thread($data);
56 }
57
58 public function is_error()
59 {
60 return $this->raw_data === null;
61 }
62
63 public function is_empty()
64 {
65 return empty($this->raw_data);
66 }
67
68 public function count()
69 {
70 if (isset($this->meta['count']) && $this->meta['count'] !== null)
71 return $this->meta['count'];
72
73 if (empty($this->raw_data)) {
74 $this->meta['count'] = 0;
75 }
76 else {
77 $this->meta['count'] = 1 + substr_count($this->raw_data, self::SEPARATOR_ELEMENT);
78 }
79
80 if (!$this->meta['count'])
81 $this->meta['messages'] = 0;
82
83 return $this->meta['count'];
84 }
85
86 public function count_messages()
87 {
88 if ($this->meta['messages'] !== null)
89 return $this->meta['messages'];
90
91 if (empty($this->raw_data)) {
92 $this->meta['messages'] = 0;
93 }
94 else {
95 $this->meta['messages'] = 1
96 + substr_count($this->raw_data, self::SEPARATOR_ELEMENT)
97 + substr_count($this->raw_data, self::SEPARATOR_ITEM);
98 }
99
100 if ($this->meta['messages'] == 0 || $this->meta['messages'] == 1)
101 $this->meta['count'] = $this->meta['messages'];
102
103 return $this->meta['messages'];
104 }
105
106 public function max()
107 {
108 if (!isset($this->meta['max'])) {
109 $this->meta['max'] = (int) @max($this->get());
110 }
111 return $this->meta['max'];
112 }
113
114 public function min()
115 {
116 if (!isset($this->meta['min'])) {
117 $this->meta['min'] = (int) @min($this->get());
118 }
119 return $this->meta['min'];
120 }
121
122 public function slice($offset, $length)
123 {
124 $data = explode(self::SEPARATOR_ELEMENT, $this->raw_data);
125 $data = array_slice($data, $offset, $length);
126
127 $this->meta = array();
128 $this->meta['count'] = count($data);
129 $this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
130 }
131
132 public function filter($roots)
133 {
134 $datalen = strlen($this->raw_data);
135 $roots = array_flip($roots);
136 $result = '';
137 $start = 0;
138
139 $this->meta = array();
140 $this->meta['count'] = 0;
141
142 while (($pos = @strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start))
143 || ($start < $datalen && ($pos = $datalen))
144 ) {
145 $len = $pos - $start;
146 $elem = substr($this->raw_data, $start, $len);
147 $start = $pos + 1;
148
149 if ($npos = strpos($elem, self::SEPARATOR_ITEM)) {
150 $root = (int) substr($elem, 0, $npos);
151 }
152 else {
153 $root = $elem;
154 }
155
156 if (isset($roots[$root])) {
157 $this->meta['count']++;
158 $result .= self::SEPARATOR_ELEMENT . $elem;
159 }
160 }
161
162 $this->raw_data = ltrim($result, self::SEPARATOR_ELEMENT);
163 }
164
165 public function revert()
166 {
167 $this->order = $this->order == 'ASC' ? 'DESC' : 'ASC';
168
169 if (empty($this->raw_data)) {
170 return;
171 }
172
173 $data = explode(self::SEPARATOR_ELEMENT, $this->raw_data);
174 $data = array_reverse($data);
175 $this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
176
177 $this->meta['pos'] = array();
178 }
179
180 public function exists($msgid, $get_index = false)
181 {
182 $msgid = (int) $msgid;
183 $begin = implode('|', array(
184 '^',
185 preg_quote(self::SEPARATOR_ELEMENT, '/'),
186 preg_quote(self::SEPARATOR_LEVEL, '/'),
187 ));
188 $end = implode('|', array(
189 '$',
190 preg_quote(self::SEPARATOR_ELEMENT, '/'),
191 preg_quote(self::SEPARATOR_ITEM, '/'),
192 ));
193
194 if (preg_match("/($begin)$msgid($end)/", $this->raw_data, $m,
195 $get_index ? PREG_OFFSET_CAPTURE : null)
196 ) {
197 if ($get_index) {
198 $idx = 0;
199 if ($m[0][1]) {
200 $idx = substr_count($this->raw_data, self::SEPARATOR_ELEMENT, 0, $m[0][1]+1)
201 + substr_count($this->raw_data, self::SEPARATOR_ITEM, 0, $m[0][1]+1);
202 }
203 $this->meta['pos'][$idx] = (int)$m[0][1];
204
205 return $idx;
206 }
207 return true;
208 }
209
210 return false;
211 }
212
213 public function get()
214 {
215 if (empty($this->raw_data)) {
216 return array();
217 }
218
219 $regexp = '/(' . preg_quote(self::SEPARATOR_ELEMENT, '/')
220 . '|' . preg_quote(self::SEPARATOR_ITEM, '/') . '[0-9]+' . preg_quote(self::SEPARATOR_LEVEL, '/')
221 .')/';
222
223 return preg_split($regexp, $this->raw_data);
224 }
225
226 public function get_compressed()
227 {
228 if (empty($this->raw_data)) {
229 return '';
230 }
231
232 return rcube_imap_generic::compressMessageSet($this->get());
233 }
234
235 public function get_element($index)
236 {
237 $count = $this->count();
238
239 if (!$count) {
240 return null;
241 }
242
243 if ($index === 0 || $index === '0' || $index === 'FIRST') {
244 preg_match('/^([0-9]+)/', $this->raw_data, $m);
245 $result = (int) $m[1];
246 return $result;
247 }
248
249 if ($index === 'LAST' || $index == $count-1) {
250 preg_match('/([0-9]+)$/', $this->raw_data, $m);
251 $result = (int) $m[1];
252 return $result;
253 }
254
255 if (!empty($this->meta['pos'])) {
256 $element = preg_quote(self::SEPARATOR_ELEMENT, '/');
257 $item = preg_quote(self::SEPARATOR_ITEM, '/') . '[0-9]+' . preg_quote(self::SEPARATOR_LEVEL, '/') .'?';
258 $regexp = '(' . $element . '|' . $item . ')';
259
260 if (isset($this->meta['pos'][$index])) {
261 if (preg_match('/([0-9]+)/', $this->raw_data, $m, null, $this->meta['pos'][$index]))
262 $result = $m[1];
263 }
264 else if (isset($this->meta['pos'][$index-1])) {
265 $data = substr($this->raw_data, $this->meta['pos'][$index-1]+1, 50);
266 $data = preg_replace('/^[0-9]+/', '', $data); // remove UID at $index position
267 $data = preg_replace("/^$regexp/", '', $data); // remove separator
268 if (preg_match('/^([0-9]+)/', $data, $m))
269 $result = $m[1];
270 }
271 else if (isset($this->meta['pos'][$index+1])) {
272 $pos = max(0, $this->meta['pos'][$index+1] - 50);
273 $len = min(50, $this->meta['pos'][$index+1]);
274 $data = substr($this->raw_data, $pos, $len);
275 $data = preg_replace("/$regexp\$/", '', $data); // remove separator
276
277 if (preg_match('/([0-9]+)$/', $data, $m))
278 $result = $m[1];
279 }
280
281 if (isset($result)) {
282 return (int) $result;
283 }
284 }
285
286 $data = $this->get();
287
288 return $data[$index];
289 }
290
291 public function get_parameters($param=null)
292 {
293 $params = array();
294 $params['MAILBOX'] = $this->mailbox;
295 $params['ORDER'] = $this->order;
296
297 if ($param !== null) {
298 return $params[$param];
299 }
300
301 return $params;
302 }
303
304 public function sort($index)
305 {
306 $this->sort_order = $index->get_parameters('ORDER');
307
308 if (empty($this->raw_data)) {
309 return;
310 }
311
312 if ($index->count() != $this->count_messages()) {
313 $index->filter($this->get());
314 }
315
316 $result = array_fill_keys($index->get(), null);
317 $datalen = strlen($this->raw_data);
318 $start = 0;
319
320
321 while (($pos = @strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start))
322 || ($start < $datalen && ($pos = $datalen))
323 ) {
324 $len = $pos - $start;
325 $elem = substr($this->raw_data, $start, $len);
326 $start = $pos + 1;
327
328 $items = explode(self::SEPARATOR_ITEM, $elem);
329 $root = (int) array_shift($items);
330
331 if ($root) {
332 $result[$root] = $root;
333 foreach ($items as $item) {
334 list($lv, $id) = explode(self::SEPARATOR_LEVEL, $item);
335 $result[$id] = $root;
336 }
337 }
338 }
339
340 $result = array_filter($result); // make sure there are no nulls
341 $result = array_unique($result);
342
343 $result = array_fill_keys($result, null);
344 $start = 0;
345
346 while (($pos = @strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start))
347 || ($start < $datalen && ($pos = $datalen))
348 ) {
349 $len = $pos - $start;
350 $elem = substr($this->raw_data, $start, $len);
351 $start = $pos + 1;
352
353 $npos = strpos($elem, self::SEPARATOR_ITEM);
354 $root = (int) ($npos ? substr($elem, 0, $npos) : $elem);
355
356 $result[$root] = $elem;
357 }
358
359 $this->raw_data = implode(self::SEPARATOR_ELEMENT, $result);
360 }
361
362 public function get_tree()
363 {
364 $datalen = strlen($this->raw_data);
365 $result = array();
366 $start = 0;
367
368 while (($pos = @strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start))
369 || ($start < $datalen && ($pos = $datalen))
370 ) {
371 $len = $pos - $start;
372 $elem = substr($this->raw_data, $start, $len);
373 $items = explode(self::SEPARATOR_ITEM, $elem);
374 $result[array_shift($items)] = $this->build_thread($items);
375 $start = $pos + 1;
376 }
377
378 return $result;
379 }
380
381 public function get_thread_data()
382 {
383 $data = $this->get_tree();
384 $depth = array();
385 $children = array();
386
387 $this->build_thread_data($data, $depth, $children);
388
389 return array($depth, $children);
390 }
391
392 protected function build_thread_data($data, &$depth, &$children, $level = 0)
393 {
394 foreach ((array)$data as $key => $val) {
395 $empty = empty($val) || !is_array($val);
396 $children[$key] = !$empty;
397 $depth[$key] = $level;
398 if (!$empty) {
399 $this->build_thread_data($val, $depth, $children, $level + 1);
400 }
401 }
402 }
403
404 protected function build_thread($items, $level = 1, &$pos = 0)
405 {
406 $result = array();
407
408 for ($len=count($items); $pos < $len; $pos++) {
409 list($lv, $id) = explode(self::SEPARATOR_LEVEL, $items[$pos]);
410 if ($level == $lv) {
411 $pos++;
412 $result[$id] = $this->build_thread($items, $level+1, $pos);
413 }
414 else {
415 $pos--;
416 break;
417 }
418 }
419
420 return $result;
421 }
422
423 protected function parse_thread($str, $begin = 0, $end = 0, $depth = 0)
424 {
425 $node = '';
426 if (!$end) {
427 $end = strlen($str);
428 }
429
430
431 if ($str[$begin] != '(') {
432 $stop = $begin + strcspn($str, '()', $begin, $end - $begin);
433 $messages = explode(' ', trim(substr($str, $begin, $stop - $begin)));
434
435 if (empty($messages)) {
436 return $node;
437 }
438
439 foreach ($messages as $msg) {
440 if ($msg) {
441 $node .= ($depth ? self::SEPARATOR_ITEM.$depth.self::SEPARATOR_LEVEL : '').$msg;
442 $this->meta['messages']++;
443 $depth++;
444 }
445 }
446
447 if ($stop < $end) {
448 $node .= $this->parse_thread($str, $stop, $end, $depth);
449 }
450 }
451 else {
452 $off = $begin;
453 while ($off < $end) {
454 $start = $off;
455 $off++;
456 $n = 1;
457 while ($n > 0) {
458 $p = strpos($str, ')', $off);
459 if ($p === false) {
460 return $node;
461 }
462 $p1 = strpos($str, '(', $off);
463 if ($p1 !== false && $p1 < $p) {
464 $off = $p1 + 1;
465 $n++;
466 }
467 else {
468 $off = $p + 1;
469 $n--;
470 }
471 }
472
473 $thread = $this->parse_thread($str, $start + 1, $off - 1, $depth);
474 if ($thread) {
475 if (!$depth) {
476 if ($node) {
477 $node .= self::SEPARATOR_ELEMENT;
478 }
479 }
480 $node .= $thread;
481 }
482 }
483 }
484
485 return $node;
486 }
487 }
488