[Summary view]
1 <?php
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 abstract class Horde_Imap_Client_Base
34 implements Serializable, SplObserver
35 {
36
37 const VERSION = 3;
38
39
40 const CACHE_MODSEQ = '_m';
41 const CACHE_SEARCH = '_s';
42
43 const CACHE_SEARCHID = '_i';
44
45
46 const CACHE_DOWNGRADED = 'HICdg';
47
48
49
50
51
52
53 public $cacheFields = array(
54 Horde_Imap_Client::FETCH_ENVELOPE => 'HICenv',
55 Horde_Imap_Client::FETCH_FLAGS => 'HICflags',
56 Horde_Imap_Client::FETCH_HEADERS => 'HIChdrs',
57 Horde_Imap_Client::FETCH_IMAPDATE => 'HICdate',
58 Horde_Imap_Client::FETCH_SIZE => 'HICsize',
59 Horde_Imap_Client::FETCH_STRUCTURE => 'HICstruct'
60 );
61
62
63
64
65
66
67 public $changed = false;
68
69
70
71
72
73
74
75
76
77
78
79
80 public $statuscache = true;
81
82
83
84
85
86
87 protected $_alerts;
88
89
90
91
92
93
94 protected $_cache = null;
95
96
97
98
99
100
101 protected $_connection = null;
102
103
104
105
106
107
108 protected $_debug = null;
109
110
111
112
113
114
115
116 protected $_defaultPorts = array();
117
118
119
120
121
122
123 protected $_fetchDataClass = 'Horde_Imap_Client_Data_Fetch';
124
125
126
127
128
129
130 protected $_init;
131
132
133
134
135
136
137 protected $_isAuthenticated = false;
138
139
140
141
142
143
144 protected $_mode = 0;
145
146
147
148
149
150
151
152 protected $_params = array();
153
154
155
156
157
158
159 protected $_selected = null;
160
161
162
163
164
165
166 protected $_temp = array();
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241 public function __construct(array $params = array())
242 {
243 if (!isset($params['username'])) {
244 throw new InvalidArgumentException('Horde_Imap_Client requires a username.');
245 }
246
247 $this->_setInit();
248
249
250 $params = array_merge(array(
251 'context' => array(),
252 'hostspec' => 'localhost',
253 'secure' => false,
254 'timeout' => 30
255 ), array_filter($params));
256
257 if (!isset($params['port']) && strpos($params['hostspec'], 'unix://') !== 0) {
258 $params['port'] = (!empty($params['secure']) && in_array($params['secure'], array('ssl', 'sslv2', 'sslv3'), true))
259 ? $this->_defaultPorts[1]
260 : $this->_defaultPorts[0];
261 }
262
263 if (empty($params['cache'])) {
264 $params['cache'] = array('fields' => array());
265 } elseif (empty($params['cache']['fields'])) {
266 $params['cache']['fields'] = $this->cacheFields;
267 } else {
268 $params['cache']['fields'] = array_flip($params['cache']['fields']);
269 }
270
271 if (empty($params['cache']['fetch_ignore'])) {
272 $params['cache']['fetch_ignore'] = array();
273 }
274
275 $this->_params = $params;
276 if (isset($params['password'])) {
277 $this->setParam('password', $params['password']);
278 }
279
280 $this->changed = true;
281 $this->_initOb();
282 }
283
284
285
286
287
288
289
290
291 protected function _getEncryptKey()
292 {
293 if (is_callable($ekey = $this->getParam('encryptKey'))) {
294 return call_user_func($ekey);
295 }
296
297 throw new InvalidArgumentException('encryptKey parameter is not a valid callback.');
298 }
299
300
301
302
303 protected function _initOb()
304 {
305 register_shutdown_function(array($this, 'shutdown'));
306
307 $this->_alerts = new Horde_Imap_Client_Base_Alerts();
308
309 $this->_alerts->attach($this);
310
311 $this->_debug = ($debug = $this->getParam('debug'))
312 ? new Horde_Imap_Client_Base_Debug($debug)
313 : new Horde_Support_Stub();
314
315
316 if (isset($this->_init['capability']) &&
317 !is_object($this->_init['capability'])) {
318 $this->_setInit('capability');
319 }
320
321 foreach (array('capability', 'search_charset') as $val) {
322 if (isset($this->_init[$val])) {
323 $this->_init[$val]->attach($this);
324 }
325 }
326 }
327
328
329
330
331 public function shutdown()
332 {
333 try {
334 $this->logout();
335 } catch (Horde_Imap_Client_Exception $e) {
336 }
337 }
338
339
340
341
342 public function __clone()
343 {
344 throw new LogicException('Object cannot be cloned.');
345 }
346
347
348
349 public function update(SplSubject $subject)
350 {
351 if (($subject instanceof Horde_Imap_Client_Data_Capability) ||
352 ($subject instanceof Horde_Imap_Client_Data_SearchCharset)) {
353 $this->changed = true;
354 }
355
356
357 if ($subject instanceof Horde_Imap_Client_Base_Alerts) {
358 $this->_temp['alerts'][] = $subject->getLast()->alert;
359 }
360 }
361
362
363
364 public function serialize()
365 {
366 return serialize(array(
367 'i' => $this->_init,
368 'p' => $this->_params,
369 'v' => self::VERSION
370 ));
371 }
372
373
374
375 public function unserialize($data)
376 {
377 $data = @unserialize($data);
378 if (!is_array($data) ||
379 !isset($data['v']) ||
380 ($data['v'] != self::VERSION)) {
381 throw new Exception('Cache version change');
382 }
383
384 $this->_init = $data['i'];
385 $this->_params = $data['p'];
386
387 $this->_initOb();
388 }
389
390
391
392 public function __get($name)
393 {
394 switch ($name) {
395 case 'alerts_ob':
396 return $this->_alerts;
397
398 case 'capability':
399 return $this->_capability();
400
401 case 'search_charset':
402 if (!isset($this->_init['search_charset'])) {
403 $this->_init['search_charset'] = new Horde_Imap_Client_Data_SearchCharset();
404 $this->_init['search_charset']->attach($this);
405 }
406 $this->_init['search_charset']->setBaseOb($this);
407 return $this->_init['search_charset'];
408
409 case 'url':
410 $url = new Horde_Imap_Client_Url();
411 $url->hostspec = $this->getParam('hostspec');
412 $url->port = $this->getParam('port');
413 $url->protocol = 'imap';
414 return $url;
415 }
416 }
417
418
419
420
421
422
423
424 public function _setInit($key = null, $val = null)
425 {
426 if (is_null($key)) {
427 $this->_init = array();
428 } elseif (is_null($val)) {
429 unset($this->_init[$key]);
430 } else {
431 switch ($key) {
432 case 'capability':
433 if ($ci = $this->getParam('capability_ignore')) {
434 $ignored = array();
435
436 foreach ($ci as $val2) {
437 $c = explode('=', $val2);
438
439 if ($val->query($c[0], isset($c[1]) ? $c[1] : null)) {
440 $ignored[] = $val2;
441 $val->remove($c[0], isset($c[1]) ? $c[1] : null);
442 }
443 }
444
445 if ($this->_debug->debug && !empty($ignored)) {
446 $this->_debug->info(sprintf(
447 'CONFIG: IGNORING these IMAP capabilities: %s',
448 implode(', ', $ignored)
449 ));
450 }
451 }
452
453 $val->attach($this);
454 break;
455 }
456
457
458 if (isset($this->_init[$key]) && ($this->_init[$key] === $val)) {
459 return;
460 }
461
462 $this->_init[$key] = $val;
463 }
464
465 $this->changed = true;
466 }
467
468
469
470
471
472
473
474
475
476
477
478 protected function _initCache($current = false)
479 {
480 $c = $this->getParam('cache');
481
482 if (empty($c['fields'])) {
483 return false;
484 }
485
486 if (is_null($this->_cache)) {
487 if (isset($c['backend'])) {
488 $backend = $c['backend'];
489 } elseif (isset($c['cacheob'])) {
490
491 $backend = new Horde_Imap_Client_Cache_Backend_Cache($c);
492 } else {
493 return false;
494 }
495
496 $this->_cache = new Horde_Imap_Client_Cache(array(
497 'backend' => $backend,
498 'baseob' => $this,
499 'debug' => $this->_debug
500 ));
501 }
502
503 return $current
504
505
506 ? !($this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_UIDNOTSTICKY))
507 : true;
508 }
509
510
511
512
513
514
515
516
517 public function getParam($key)
518 {
519
520 switch ($key) {
521 case 'password':
522 if (isset($this->_params[$key]) &&
523 ($this->_params[$key] instanceof Horde_Imap_Client_Base_Password)) {
524 return $this->_params[$key]->getPassword();
525 }
526
527
528 if (!empty($this->_params['_passencrypt'])) {
529 try {
530 $secret = new Horde_Secret();
531 return $secret->read($this->_getEncryptKey(), $this->_params['password']);
532 } catch (Exception $e) {
533 return null;
534 }
535 }
536 break;
537 }
538
539 return isset($this->_params[$key])
540 ? $this->_params[$key]
541 : null;
542 }
543
544
545
546
547
548
549
550 public function setParam($key, $val)
551 {
552 switch ($key) {
553 case 'password':
554 if ($val instanceof Horde_Imap_Client_Base_Password) {
555 break;
556 }
557
558
559 try {
560 $encrypt_key = $this->_getEncryptKey();
561 if (strlen($encrypt_key)) {
562 $secret = new Horde_Secret();
563 $val = $secret->write($encrypt_key, $val);
564 $this->_params['_passencrypt'] = true;
565 }
566 } catch (Exception $e) {}
567 break;
568 }
569
570 $this->_params[$key] = $val;
571 $this->changed = true;
572 }
573
574
575
576
577
578
579 public function getCache()
580 {
581 $this->_initCache();
582 return $this->_cache;
583 }
584
585
586
587
588
589
590
591
592
593
594
595 public function getIdsOb($ids = null, $sequence = false)
596 {
597 return new Horde_Imap_Client_Ids($ids, $sequence);
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611
612 public function queryCapability($capability)
613 {
614 try {
615 $c = $this->_capability();
616 return ($out = $c->getParams($capability))
617 ? $out
618 : $c->query($capability);
619 } catch (Horde_Imap_Client_Exception $e) {
620 return false;
621 }
622 }
623
624
625
626
627
628
629
630
631
632
633 public function capability()
634 {
635 return $this->_capability()->toArray();
636 }
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651 protected function _capability()
652 {
653 if (!isset($this->_init['capability'])) {
654 $this->_initCapability();
655 }
656
657 return ($args = func_num_args())
658 ? $this->_init['capability']->query(func_get_arg(0), ($args > 1) ? func_get_arg(1) : null)
659 : $this->_init['capability'];
660 }
661
662
663
664
665
666
667 abstract protected function _initCapability();
668
669
670
671
672
673
674 public function noop()
675 {
676 if (!$this->_connection) {
677
678 $this->_connect();
679 }
680 $this->_noop();
681 }
682
683
684
685
686
687
688 abstract protected function _noop();
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722 public function getNamespaces(
723 array $additional = array(), array $opts = array()
724 )
725 {
726 $additional = array_map('strval', $additional);
727 $sig = hash(
728 'md5',
729 json_encode($additional) . intval(empty($opts['ob_return']))
730 );
731
732 if (isset($this->_init['namespace'][$sig])) {
733 $ns = $this->_init['namespace'][$sig];
734 } else {
735 $this->login();
736
737 $ns = $this->_getNamespaces();
738
739
740
741 $to_process = array_diff(array_filter($additional, 'strlen'), array_map('strlen', iterator_to_array($ns)));
742 if (!empty($to_process)) {
743 foreach ($this->listMailboxes($to_process, Horde_Imap_Client::MBOX_ALL, array('delimiter' => true)) as $key => $val) {
744 $ob = new Horde_Imap_Client_Data_Namespace();
745 $ob->delimiter = $val['delimiter'];
746 $ob->hidden = true;
747 $ob->name = $key;
748 $ob->type = $ob::NS_SHARED;
749 $ns[$val] = $ob;
750 }
751 }
752
753 if (!count($ns)) {
754
755
756
757 $mbox = $this->listMailboxes('', Horde_Imap_Client::MBOX_ALL, array('delimiter' => true));
758 $first = reset($mbox);
759
760 $ob = new Horde_Imap_Client_Data_Namespace();
761 $ob->delimiter = $first['delimiter'];
762 $ns[''] = $ob;
763 }
764
765 $this->_init['namespace'][$sig] = $ns;
766 $this->_setInit('namespace', $this->_init['namespace']);
767 }
768
769 if (!empty($opts['ob_return'])) {
770 return $ns;
771 }
772
773
774 $out = array();
775 foreach ($ns as $key => $val) {
776 $out[$key] = array(
777 'delimiter' => $val->delimiter,
778 'hidden' => $val->hidden,
779 'name' => $val->name,
780 'translation' => $val->translation,
781 'type' => $val->type
782 );
783 }
784
785 return $out;
786 }
787
788
789
790
791
792
793
794
795 abstract protected function _getNamespaces();
796
797
798
799
800
801
802 public function isSecureConnection()
803 {
804 return ($this->_connection && $this->_connection->secure);
805 }
806
807
808
809
810
811
812 abstract protected function _connect();
813
814
815
816
817
818
819
820
821
822 public function alerts()
823 {
824 $alerts = isset($this->_temp['alerts'])
825 ? $this->_temp['alerts']
826 : array();
827 unset($this->_temp['alerts']);
828 return $alerts;
829 }
830
831
832
833
834
835
836 public function login()
837 {
838 if (!$this->_isAuthenticated && $this->_login()) {
839 if ($this->getParam('id')) {
840 try {
841 $this->sendID();
842
843 $this->_sendCmd($this->_pipeline());
844 } catch (Horde_Imap_Client_Exception_NoSupportExtension $e) {
845
846 }
847 }
848
849 if ($this->getParam('comparator')) {
850 try {
851 $this->setComparator();
852 } catch (Horde_Imap_Client_Exception_NoSupportExtension $e) {
853
854 }
855 }
856 }
857
858 $this->_isAuthenticated = true;
859 }
860
861
862
863
864
865
866
867
868 abstract protected function _login();
869
870
871
872
873 public function logout()
874 {
875 if ($this->_isAuthenticated && $this->_connection->connected) {
876 $this->_logout();
877 $this->_connection->close();
878 }
879
880 $this->_connection = $this->_selected = null;
881 $this->_isAuthenticated = false;
882 $this->_mode = 0;
883 }
884
885
886
887
888 abstract protected function _logout();
889
890
891
892
893
894
895
896
897
898
899 public function sendID($info = null)
900 {
901 if (!$this->_capability('ID')) {
902 throw new Horde_Imap_Client_Exception_NoSupportExtension('ID');
903 }
904
905 $this->_sendID(is_null($info) ? ($this->getParam('id') ?: array()) : $info);
906 }
907
908
909
910
911
912
913
914
915 abstract protected function _sendID($info);
916
917
918
919
920
921
922
923
924
925
926 public function getID()
927 {
928 if (!$this->_capability('ID')) {
929 throw new Horde_Imap_Client_Exception_NoSupportExtension('ID');
930 }
931
932 return $this->_getID();
933 }
934
935
936
937
938
939
940
941
942
943 abstract protected function _getID();
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958 public function setLanguage($langs = null)
959 {
960 $lang = null;
961
962 if ($this->_capability('LANGUAGE')) {
963 $lang = is_null($langs)
964 ? $this->getParam('lang')
965 : $langs;
966 }
967
968 return is_null($lang)
969 ? null
970 : $this->_setLanguage($lang);
971 }
972
973
974
975
976
977
978
979
980
981
982
983 abstract protected function _setLanguage($langs);
984
985
986
987
988
989
990
991
992
993
994
995
996 public function getLanguage($list = false)
997 {
998 if (!$this->_capability('LANGUAGE')) {
999 return $list ? array() : null;
1000 }
1001
1002 return $this->_getLanguage($list);
1003 }
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016 abstract protected function _getLanguage($list);
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031 public function openMailbox($mailbox, $mode = Horde_Imap_Client::OPEN_AUTO)
1032 {
1033 $this->login();
1034
1035 $change = false;
1036 $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
1037
1038 if ($mode == Horde_Imap_Client::OPEN_AUTO) {
1039 if (is_null($this->_selected) ||
1040 !$mailbox->equals($this->_selected)) {
1041 $mode = Horde_Imap_Client::OPEN_READONLY;
1042 $change = true;
1043 }
1044 } else {
1045 $change = (is_null($this->_selected) ||
1046 !$mailbox->equals($this->_selected) ||
1047 ($mode != $this->_mode));
1048 }
1049
1050 if ($change) {
1051 $this->_openMailbox($mailbox, $mode);
1052 $this->_mailboxOb()->open = true;
1053 if ($this->_initCache(true)) {
1054 $this->_condstoreSync();
1055 }
1056 }
1057 }
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067 abstract protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox,
1068 $mode);
1069
1070
1071
1072
1073
1074
1075
1076 protected function _changeSelected($mailbox = null, $mode = null)
1077 {
1078 $this->_mode = $mode;
1079 if (is_null($mailbox)) {
1080 $this->_selected = null;
1081 } else {
1082 $this->_selected = clone $mailbox;
1083 $this->_mailboxOb()->reset();
1084 }
1085 }
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095 protected function _mailboxOb($mailbox = null)
1096 {
1097 $name = is_null($mailbox)
1098 ? strval($this->_selected)
1099 : strval($mailbox);
1100
1101 if (!isset($this->_temp['mailbox_ob'][$name])) {
1102 $this->_temp['mailbox_ob'][$name] = new Horde_Imap_Client_Base_Mailbox();
1103 }
1104
1105 return $this->_temp['mailbox_ob'][$name];
1106 }
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118 public function currentMailbox()
1119 {
1120 return is_null($this->_selected)
1121 ? null
1122 : array(
1123 'mailbox' => clone $this->_selected,
1124 'mode' => $this->_mode
1125 );
1126 }
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140 public function createMailbox($mailbox, array $opts = array())
1141 {
1142 $this->login();
1143
1144 if (!$this->_capability('CREATE-SPECIAL-USE')) {
1145 unset($opts['special_use']);
1146 }
1147
1148 $this->_createMailbox(Horde_Imap_Client_Mailbox::get($mailbox), $opts);
1149 }
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160 abstract protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox,
1161 $opts);
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172 public function deleteMailbox($mailbox)
1173 {
1174 $this->login();
1175
1176 $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
1177
1178 $this->_deleteMailbox($mailbox);
1179 $this->_deleteMailboxPost($mailbox);
1180 }
1181
1182
1183
1184
1185
1186
1187
1188
1189 abstract protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox);
1190
1191
1192
1193
1194
1195
1196 protected function _deleteMailboxPost(Horde_Imap_Client_Mailbox $mailbox)
1197 {
1198
1199 if ($this->_initCache()) {
1200 $this->_cache->deleteMailbox($mailbox);
1201 }
1202 unset($this->_temp['mailbox_ob'][strval($mailbox)]);
1203
1204
1205 try {
1206 $this->subscribeMailbox($mailbox, false);
1207 } catch (Horde_Imap_Client_Exception $e) {
1208
1209 }
1210 }
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222 public function renameMailbox($old, $new)
1223 {
1224
1225
1226 $old = Horde_Imap_Client_Mailbox::get($old);
1227 $new = Horde_Imap_Client_Mailbox::get($new);
1228
1229
1230 $base = $this->listMailboxes($old, Horde_Imap_Client::MBOX_SUBSCRIBED, array('delimiter' => true));
1231 if (empty($base)) {
1232 $base = $this->listMailboxes($old, Horde_Imap_Client::MBOX_ALL, array('delimiter' => true));
1233 $base = reset($base);
1234 $subscribed = array();
1235 } else {
1236 $base = reset($base);
1237 $subscribed = array($base['mailbox']);
1238 }
1239
1240 $all_mboxes = array($base['mailbox']);
1241 if (strlen($base['delimiter'])) {
1242 $search = $old->list_escape . $base['delimiter'] . '*';
1243 $all_mboxes = array_merge($all_mboxes, $this->listMailboxes($search, Horde_Imap_Client::MBOX_ALL, array('flat' => true)));
1244 $subscribed = array_merge($subscribed, $this->listMailboxes($search, Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true)));
1245 }
1246
1247 $this->_renameMailbox($old, $new);
1248
1249
1250 foreach ($all_mboxes as $val) {
1251 $this->_deleteMailboxPost($val);
1252 }
1253
1254 foreach ($subscribed as $val) {
1255 try {
1256 $this->subscribeMailbox(new Horde_Imap_Client_Mailbox(substr_replace($val, $new, 0, strlen($old))));
1257 } catch (Horde_Imap_Client_Exception $e) {
1258
1259 }
1260 }
1261 }
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271 abstract protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
1272 Horde_Imap_Client_Mailbox $new);
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284 public function subscribeMailbox($mailbox, $subscribe = true)
1285 {
1286 $this->login();
1287 $this->_subscribeMailbox(Horde_Imap_Client_Mailbox::get($mailbox), (bool)$subscribe);
1288 }
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300 abstract protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
1301 $subscribe);
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385 public function listMailboxes($pattern,
1386 $mode = Horde_Imap_Client::MBOX_ALL,
1387 array $options = array())
1388 {
1389 $this->login();
1390
1391 $pattern = is_array($pattern)
1392 ? array_unique($pattern)
1393 : array($pattern);
1394
1395
1396 $plist = array();
1397 foreach ($pattern as $val) {
1398 if ($val instanceof Horde_Imap_Client_Mailbox) {
1399 $val = $val->list_escape;
1400 }
1401 $plist[] = Horde_Imap_Client_Mailbox::get(preg_replace(
1402 array("/\*{2,}/", "/\%{2,}/"),
1403 array('*', '%'),
1404 Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($val)
1405 ), true);
1406 }
1407
1408 if (isset($options['special_use']) &&
1409 !$this->_capability('SPECIAL-USE')) {
1410 unset($options['special_use']);
1411 }
1412
1413 $ret = $this->_listMailboxes($plist, $mode, $options);
1414
1415 if (!empty($options['status']) &&
1416 !$this->_capability('LIST-STATUS')) {
1417 foreach ($this->status(array_keys($ret), $options['status']) as $key => $val) {
1418 $ret[$key]['status'] = $val;
1419 }
1420 }
1421
1422 if (empty($options['sort'])) {
1423 return $ret;
1424 }
1425
1426 $list_ob = new Horde_Imap_Client_Mailbox_List(empty($options['flat']) ? array_keys($ret) : $ret);
1427 $sorted = $list_ob->sort(array(
1428 'delimiter' => empty($options['sort_delimiter']) ? '.' : $options['sort_delimiter']
1429 ));
1430
1431 if (!empty($options['flat'])) {
1432 return $sorted;
1433 }
1434
1435 $out = array();
1436 foreach ($sorted as $val) {
1437 $out[$val] = $ret[$val];
1438 }
1439
1440 return $out;
1441 }
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455 abstract protected function _listMailboxes($pattern, $mode, $options);
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590 public function status($mailbox, $flags = Horde_Imap_Client::STATUS_ALL,
1591 array $opts = array())
1592 {
1593 $opts = array_merge(array(
1594 'sort' => false,
1595 'sort_delimiter' => '.'
1596 ), $opts);
1597
1598 $this->login();
1599
1600 if (is_array($mailbox)) {
1601 if (empty($mailbox)) {
1602 return array();
1603 }
1604 $ret_array = true;
1605 } else {
1606 $mailbox = array($mailbox);
1607 $ret_array = false;
1608 }
1609
1610 $mlist = array_map(array('Horde_Imap_Client_Mailbox', 'get'), $mailbox);
1611
1612 $unselected_flags = array(
1613 'messages' => Horde_Imap_Client::STATUS_MESSAGES,
1614 'recent' => Horde_Imap_Client::STATUS_RECENT,
1615 'uidnext' => Horde_Imap_Client::STATUS_UIDNEXT,
1616 'uidvalidity' => Horde_Imap_Client::STATUS_UIDVALIDITY,
1617 'unseen' => Horde_Imap_Client::STATUS_UNSEEN
1618 );
1619
1620 if (!$this->statuscache) {
1621 $flags |= Horde_Imap_Client::STATUS_FORCE_REFRESH;
1622 }
1623
1624 if ($flags & Horde_Imap_Client::STATUS_ALL) {
1625 foreach ($unselected_flags as $val) {
1626 $flags |= $val;
1627 }
1628 }
1629
1630 $master = $ret = array();
1631
1632
1633 if (($flags & Horde_Imap_Client::STATUS_HIGHESTMODSEQ) &&
1634 !$this->_capability()->isEnabled('CONDSTORE')) {
1635 $master['highestmodseq'] = 0;
1636 $flags &= ~Horde_Imap_Client::STATUS_HIGHESTMODSEQ;
1637 }
1638
1639 if (($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY) &&
1640 !$this->_capability('UIDPLUS')) {
1641 $master['uidnotsticky'] = false;
1642 $flags &= ~Horde_Imap_Client::STATUS_UIDNOTSTICKY;
1643 }
1644
1645
1646 if ($flags & Horde_Imap_Client::STATUS_UIDNEXT_FORCE) {
1647 $flags |= Horde_Imap_Client::STATUS_UIDNEXT;
1648 }
1649
1650 foreach ($mlist as $val) {
1651 $name = strval($val);
1652 $tmp_flags = $flags;
1653
1654 if ($val->equals($this->_selected)) {
1655
1656 $opened = true;
1657
1658 if ($flags & Horde_Imap_Client::STATUS_FORCE_REFRESH) {
1659 $this->noop();
1660 }
1661 } else {
1662
1663
1664 $opened = ($flags & Horde_Imap_Client::STATUS_FIRSTUNSEEN) ||
1665 ($flags & Horde_Imap_Client::STATUS_FLAGS) ||
1666 ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) ||
1667 ($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY) ||
1668
1669
1670
1671 (strpbrk($name, '*%') !== false);
1672 }
1673
1674 $ret[$name] = $master;
1675 $ptr = &$ret[$name];
1676
1677
1678 if ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) {
1679 $this->openMailbox($val, Horde_Imap_Client::OPEN_READWRITE);
1680 $opened = true;
1681 }
1682
1683
1684
1685 if ($flags & Horde_Imap_Client::STATUS_SYNCMODSEQ) {
1686 $this->openMailbox($val);
1687 $ptr['syncmodseq'] = $this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCMODSEQ);
1688 $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCMODSEQ;
1689 $opened = true;
1690 }
1691
1692 if ($flags & Horde_Imap_Client::STATUS_SYNCFLAGUIDS) {
1693 $this->openMailbox($val);
1694 $ptr['syncflaguids'] = $this->getIdsOb($this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCFLAGUIDS));
1695 $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCFLAGUIDS;
1696 $opened = true;
1697 }
1698
1699 if ($flags & Horde_Imap_Client::STATUS_SYNCVANISHED) {
1700 $this->openMailbox($val);
1701 $ptr['syncvanished'] = $this->getIdsOb($this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCVANISHED));
1702 $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCVANISHED;
1703 $opened = true;
1704 }
1705
1706
1707 if ($flags & Horde_Imap_Client::STATUS_RECENT_TOTAL) {
1708 $this->openMailbox($val);
1709 $ptr['recent_total'] = $this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_RECENT_TOTAL);
1710 $tmp_flags &= ~Horde_Imap_Client::STATUS_RECENT_TOTAL;
1711 $opened = true;
1712 }
1713
1714 if ($opened) {
1715 if ($tmp_flags) {
1716 $tmp = $this->_status(array($val), $tmp_flags);
1717 $ptr += reset($tmp);
1718 }
1719 } else {
1720 $to_process[] = $val;
1721 }
1722 }
1723
1724 if ($flags && !empty($to_process)) {
1725 if ((count($to_process) > 1) &&
1726 $this->_capability('LIST-STATUS')) {
1727 foreach ($this->listMailboxes($to_process, Horde_Imap_Client::MBOX_ALL, array('status' => $flags)) as $key => $val) {
1728 if (isset($val['status'])) {
1729 $ret[$key] += $val['status'];
1730 }
1731 }
1732 } else {
1733 foreach ($this->_status($to_process, $flags) as $key => $val) {
1734 $ret[$key] += $val;
1735 }
1736 }
1737 }
1738
1739 if (!$opts['sort'] || (count($ret) === 1)) {
1740 return $ret_array
1741 ? $ret
1742 : reset($ret);
1743 }
1744
1745 $list_ob = new Horde_Imap_Client_Mailbox_List(array_keys($ret));
1746 $sorted = $list_ob->sort(array(
1747 'delimiter' => $opts['sort_delimiter']
1748 ));
1749
1750 $out = array();
1751 foreach ($sorted as $val) {
1752 $out[$val] = $ret[$val];
1753 }
1754
1755 return $out;
1756 }
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769 abstract protected function _status($mboxes, $flags);
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794 public function statusMultiple($mailboxes,
1795 $flags = Horde_Imap_Client::STATUS_ALL,
1796 array $opts = array())
1797 {
1798 return $this->status($mailboxes, $flags, $opts);
1799 }
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840 public function append($mailbox, $data, array $options = array())
1841 {
1842 $this->login();
1843
1844 $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
1845
1846 $ret = $this->_append($mailbox, $data, $options);
1847
1848 if ($ret instanceof Horde_Imap_Client_Ids) {
1849 return $ret;
1850 }
1851
1852 $uids = $this->getIdsOb();
1853
1854 foreach ($data as $val) {
1855 if (is_resource($val['data'])) {
1856 rewind($val['data']);
1857 }
1858
1859 $uids->add($this->_getUidByMessageId(
1860 $mailbox,
1861 Horde_Mime_Headers::parseHeaders($val['data'])->getHeader('Message-ID')
1862 ));
1863 }
1864
1865 return $uids;
1866 }
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882 abstract protected function _append(Horde_Imap_Client_Mailbox $mailbox,
1883 $data, $options);
1884
1885
1886
1887
1888
1889
1890
1891 public function check()
1892 {
1893
1894 if ($this->_isAuthenticated) {
1895 $this->_check();
1896 }
1897 }
1898
1899
1900
1901
1902
1903
1904 abstract protected function _check();
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916 public function close(array $options = array())
1917 {
1918
1919 if (is_null($this->_selected)) {
1920 return;
1921 }
1922
1923
1924 if (!empty($options['expunge']) && $this->_initCache(true)) {
1925
1926 $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
1927 if ($this->_mode == Horde_Imap_Client::OPEN_READONLY) {
1928 throw new Horde_Imap_Client_Exception(
1929 Horde_Imap_Client_Translation::r("Cannot expunge read-only mailbox."),
1930 Horde_Imap_Client_Exception::MAILBOX_READONLY
1931 );
1932 }
1933
1934 $search_query = new Horde_Imap_Client_Search_Query();
1935 $search_query->flag(Horde_Imap_Client::FLAG_DELETED, true);
1936 $search_res = $this->search($this->_selected, $search_query);
1937 $mbox = $this->_selected;
1938 } else {
1939 $search_res = null;
1940 }
1941
1942 $this->_close($options);
1943 $this->_selected = null;
1944 $this->_mode = 0;
1945
1946 if (!is_null($search_res)) {
1947 $this->_deleteMsgs($mbox, $search_res['match']);
1948 }
1949 }
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959 abstract protected function _close($options);
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984 public function expunge($mailbox, array $options = array())
1985 {
1986
1987 $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_READWRITE);
1988
1989
1990 if ($this->_mode == Horde_Imap_Client::OPEN_READONLY) {
1991 throw new Horde_Imap_Client_Exception(
1992 Horde_Imap_Client_Translation::r("Cannot expunge read-only mailbox."),
1993 Horde_Imap_Client_Exception::MAILBOX_READONLY
1994 );
1995 }
1996
1997 if (empty($options['ids'])) {
1998 $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
1999 } elseif ($options['ids']->isEmpty()) {
2000 return $this->getIdsOb();
2001 }
2002
2003 return $this->_expunge($options);
2004 }
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016 abstract protected function _expunge($options);
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108 public function search($mailbox, $query = null, array $options = array())
2109 {
2110 $this->login();
2111
2112 if (empty($options['results'])) {
2113 $options['results'] = array(
2114 Horde_Imap_Client::SEARCH_RESULTS_MATCH,
2115 Horde_Imap_Client::SEARCH_RESULTS_COUNT
2116 );
2117 } elseif (!in_array(Horde_Imap_Client::SEARCH_RESULTS_COUNT, $options['results'])) {
2118 $options['results'][] = Horde_Imap_Client::SEARCH_RESULTS_COUNT;
2119 }
2120
2121
2122 if (is_null($query)) {
2123 $query = new Horde_Imap_Client_Search_Query();
2124 }
2125
2126
2127 if ((($pos = array_search(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) !== false) &&
2128 !$this->_capability('SEARCHRES')) {
2129 unset($options['results'][$pos]);
2130 }
2131
2132
2133 if (!empty($options['sort'])) {
2134 foreach ($options['sort'] as $key => $val) {
2135 switch ($val) {
2136 case Horde_Imap_Client::SORT_DISPLAYFROM_FALLBACK:
2137 $options['sort'][$key] = $this->_capability('SORT', 'DISPLAY')
2138 ? Horde_Imap_Client::SORT_DISPLAYFROM
2139 : Horde_Imap_Client::SORT_FROM;
2140 break;
2141
2142 case Horde_Imap_Client::SORT_DISPLAYTO_FALLBACK:
2143 $options['sort'][$key] = $this->_capability('SORT', 'DISPLAY')
2144 ? Horde_Imap_Client::SORT_DISPLAYTO
2145 : Horde_Imap_Client::SORT_TO;
2146 break;
2147 }
2148 }
2149 }
2150
2151
2152 $default_ret = array(
2153 'count' => 0,
2154 'match' => $this->getIdsOb(),
2155 'max' => null,
2156 'min' => null,
2157 'relevancy' => array()
2158 );
2159
2160
2161 $squery = $query->build($this);
2162
2163
2164
2165
2166 if (!count($squery['query'])) {
2167 return $default_ret;
2168 }
2169
2170
2171 if (!is_null($squery['charset']) &&
2172 ($this->search_charset->query($squery['charset'], true) === false)) {
2173 foreach ($this->search_charset->charsets as $val) {
2174 try {
2175 $new_query = clone $query;
2176 $new_query->charset($val);
2177 break;
2178 } catch (Horde_Imap_Client_Exception_SearchCharset $e) {
2179 unset($new_query);
2180 }
2181 }
2182
2183 if (!isset($new_query)) {
2184 throw $e;
2185 }
2186
2187 $query = $new_query;
2188 $squery = $query->build($this);
2189 }
2190
2191
2192 $options['_query'] = $squery;
2193
2194
2195
2196 if (in_array(Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY, $options['results']) &&
2197 !in_array('SEARCH=FUZZY', $squery['exts_used'])) {
2198 throw new InvalidArgumentException('Cannot specify RELEVANCY results if not doing a FUZZY search.');
2199 }
2200
2201
2202 if (!empty($options['partial'])) {
2203 $pids = $this->getIdsOb($options['partial'], true)->range_string;
2204 if (!strlen($pids)) {
2205 throw new InvalidArgumentException('Cannot specify empty sequence range for a PARTIAL search.');
2206 }
2207
2208 if (strpos($pids, ':') === false) {
2209 $pids .= ':' . $pids;
2210 }
2211
2212 $options['partial'] = $pids;
2213 }
2214
2215
2216
2217
2218
2219 if ((count($options['results']) === 1) &&
2220 (reset($options['results']) == Horde_Imap_Client::SEARCH_RESULTS_COUNT)) {
2221 switch ($squery['query']) {
2222 case 'ALL':
2223 $ret = $this->status($mailbox, Horde_Imap_Client::STATUS_MESSAGES);
2224 return array('count' => $ret['messages']);
2225
2226 case 'RECENT':
2227 $ret = $this->status($mailbox, Horde_Imap_Client::STATUS_RECENT);
2228 return array('count' => $ret['recent']);
2229 }
2230 }
2231
2232 $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
2233
2234
2235
2236
2237
2238
2239 $cache = null;
2240 if (empty($options['nocache']) &&
2241 $this->_initCache(true) &&
2242 ($this->_capability()->isEnabled('CONDSTORE') ||
2243 !$query->flagSearch())) {
2244 $cache = $this->_getSearchCache('search', $options);
2245 if (isset($cache['data'])) {
2246 if (isset($cache['data']['match'])) {
2247 $cache['data']['match'] = $this->getIdsOb($cache['data']['match']);
2248 }
2249 return $cache['data'];
2250 }
2251 }
2252
2253
2254 $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
2255 if ($status_res['messages'] ||
2256 in_array(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) {
2257
2258
2259 if (in_array('CONDSTORE', $squery['exts']) &&
2260 !$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
2261 throw new Horde_Imap_Client_Exception(
2262 Horde_Imap_Client_Translation::r("Mailbox does not support mod-sequences."),
2263 Horde_Imap_Client_Exception::MBOXNOMODSEQ
2264 );
2265 }
2266
2267 $ret = $this->_search($query, $options);
2268 } else {
2269 $ret = $default_ret;
2270 if (isset($status_res['highestmodseq'])) {
2271 $ret['modseq'] = $status_res['highestmodseq'];
2272 }
2273 }
2274
2275 if ($cache) {
2276 $save = $ret;
2277 if (isset($save['match'])) {
2278 $save['match'] = strval($ret['match']);
2279 }
2280 $this->_setSearchCache($save, $cache);
2281 }
2282
2283 return $ret;
2284 }
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297 abstract protected function _search($query, $options);
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310 public function setComparator($comparator = null)
2311 {
2312 $comp = is_null($comparator)
2313 ? $this->getParam('comparator')
2314 : $comparator;
2315 if (is_null($comp)) {
2316 return;
2317 }
2318
2319 $this->login();
2320
2321 if (!$this->_capability('I18NLEVEL', '2')) {
2322 throw new Horde_Imap_Client_Exception_NoSupportExtension(
2323 'I18NLEVEL',
2324 'The IMAP server does not support changing SEARCH/SORT comparators.'
2325 );
2326 }
2327
2328 $this->_setComparator($comp);
2329 }
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341 abstract protected function _setComparator($comparator);
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351 public function getComparator()
2352 {
2353 $this->login();
2354
2355 return $this->_capability('I18NLEVEL', '2')
2356 ? $this->_getComparator()
2357 : null;
2358 }
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368 abstract protected function _getComparator();
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396 public function thread($mailbox, array $options = array())
2397 {
2398
2399 $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
2400
2401
2402
2403
2404
2405 $cache = null;
2406 if ($this->_initCache(true) &&
2407 ($this->_capability()->isEnabled('CONDSTORE') ||
2408 empty($options['search']) ||
2409 !$options['search']->flagSearch())) {
2410 $cache = $this->_getSearchCache('thread', $options);
2411 if (isset($cache['data']) &&
2412 ($cache['data'] instanceof Horde_Imap_Client_Data_Thread)) {
2413 return $cache['data'];
2414 }
2415 }
2416
2417 $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
2418
2419 $ob = $status_res['messages']
2420 ? $this->_thread($options)
2421 : new Horde_Imap_Client_Data_Thread(array(), empty($options['sequence']) ? 'uid' : 'sequence');
2422
2423 if ($cache) {
2424 $this->_setSearchCache($ob, $cache);
2425 }
2426
2427 return $ob;
2428 }
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439 abstract protected function _thread($options);
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475 public function fetch($mailbox, $query, array $options = array())
2476 {
2477 try {
2478 $ret = $this->_fetchWrapper($mailbox, $query, $options);
2479 unset($this->_temp['fetch_nocache']);
2480 return $ret;
2481 } catch (Exception $e) {
2482 unset($this->_temp['fetch_nocache']);
2483 throw $e;
2484 }
2485 }
2486
2487
2488
2489
2490
2491
2492
2493 private function _fetchWrapper($mailbox, $query, $options)
2494 {
2495 $this->login();
2496
2497 $query = clone $query;
2498
2499 $cache_array = $header_cache = $new_query = array();
2500
2501 if (empty($options['ids'])) {
2502 $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
2503 } elseif ($options['ids']->isEmpty()) {
2504 return new Horde_Imap_Client_Fetch_Results($this->_fetchDataClass);
2505 } elseif ($options['ids']->search_res &&
2506 !$this->_capability('SEARCHRES')) {
2507
2508 throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
2509 }
2510
2511 $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
2512 $mbox_ob = $this->_mailboxOb();
2513
2514 if (!empty($options['nocache'])) {
2515 $this->_temp['fetch_nocache'] = true;
2516 }
2517
2518 $cf = $this->_initCache(true)
2519 ? $this->_cacheFields()
2520 : array();
2521
2522 if (!empty($cf)) {
2523
2524 $query->uid();
2525 }
2526
2527 $modseq_check = !empty($options['changedsince']);
2528 if ($query->contains(Horde_Imap_Client::FETCH_MODSEQ)) {
2529 if (!$this->_capability()->isEnabled('CONDSTORE')) {
2530 unset($query[Horde_Imap_Client::FETCH_MODSEQ]);
2531 } elseif (empty($options['changedsince'])) {
2532 $modseq_check = true;
2533 }
2534 }
2535
2536 if ($modseq_check &&
2537 !$mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
2538
2539
2540 throw new Horde_Imap_Client_Exception(
2541 Horde_Imap_Client_Translation::r("Mailbox does not support mod-sequences."),
2542 Horde_Imap_Client_Exception::MBOXNOMODSEQ
2543 );
2544 }
2545
2546
2547
2548 foreach ($cf as $k => $v) {
2549 if (isset($query[$k])) {
2550 switch ($k) {
2551 case Horde_Imap_Client::FETCH_ENVELOPE:
2552 case Horde_Imap_Client::FETCH_FLAGS:
2553 case Horde_Imap_Client::FETCH_IMAPDATE:
2554 case Horde_Imap_Client::FETCH_SIZE:
2555 case Horde_Imap_Client::FETCH_STRUCTURE:
2556 $cache_array[$k] = $v;
2557 break;
2558
2559 case Horde_Imap_Client::FETCH_HEADERS:
2560 $this->_temp['headers_caching'] = array();
2561
2562 foreach ($query[$k] as $key => $val) {
2563
2564
2565 if (!empty($val['cache']) && !empty($val['peek'])) {
2566 $cache_array[$k] = $v;
2567 ksort($val);
2568 $header_cache[$key] = hash('md5', serialize($val));
2569 }
2570 }
2571 break;
2572 }
2573 }
2574 }
2575
2576 $ret = new Horde_Imap_Client_Fetch_Results(
2577 $this->_fetchDataClass,
2578 $options['ids']->sequence ? Horde_Imap_Client_Fetch_Results::SEQUENCE : Horde_Imap_Client_Fetch_Results::UID
2579 );
2580
2581
2582 if (empty($cache_array)) {
2583 $options['_query'] = $query;
2584 $this->_fetch($ret, array($options));
2585 return $ret;
2586 }
2587
2588 $cs_ret = empty($options['changedsince'])
2589 ? null
2590 : clone $ret;
2591
2592
2593 $ids = $this->resolveIds(
2594 $this->_selected,
2595 $options['ids'],
2596 empty($options['exists']) ? 1 : 2
2597 );
2598
2599
2600 $cache_array[Horde_Imap_Client::FETCH_DOWNGRADED] = self::CACHE_DOWNGRADED;
2601
2602
2603 $data = $this->_cache->get(
2604 $this->_selected,
2605 $ids->ids,
2606 array_values($cache_array),
2607 $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY)
2608 );
2609
2610
2611 $map = array_flip($mbox_ob->map->map);
2612 $sequence = $options['ids']->sequence;
2613 foreach ($ids as $uid) {
2614 $crit = clone $query;
2615
2616 if ($sequence) {
2617 if (!isset($map[$uid])) {
2618 continue;
2619 }
2620 $entry_idx = $map[$uid];
2621 } else {
2622 $entry_idx = $uid;
2623 unset($crit[Horde_Imap_Client::FETCH_UID]);
2624 }
2625
2626 $entry = $ret->get($entry_idx);
2627
2628 if (isset($map[$uid])) {
2629 $entry->setSeq($map[$uid]);
2630 unset($crit[Horde_Imap_Client::FETCH_SEQ]);
2631 }
2632
2633 $entry->setUid($uid);
2634
2635 foreach ($cache_array as $key => $cid) {
2636 switch ($key) {
2637 case Horde_Imap_Client::FETCH_DOWNGRADED:
2638 if (!empty($data[$uid][$cid])) {
2639 $entry->setDowngraded(true);
2640 }
2641 break;
2642
2643 case Horde_Imap_Client::FETCH_ENVELOPE:
2644 if (isset($data[$uid][$cid]) &&
2645 ($data[$uid][$cid] instanceof Horde_Imap_Client_Data_Envelope)) {
2646 $entry->setEnvelope($data[$uid][$cid]);
2647 unset($crit[$key]);
2648 }
2649 break;
2650
2651 case Horde_Imap_Client::FETCH_FLAGS:
2652 if (isset($data[$uid][$cid]) &&
2653 is_array($data[$uid][$cid])) {
2654 $entry->setFlags($data[$uid][$cid]);
2655 unset($crit[$key]);
2656 }
2657 break;
2658
2659 case Horde_Imap_Client::FETCH_HEADERS:
2660 foreach ($header_cache as $hkey => $hval) {
2661 if (isset($data[$uid][$cid][$hval])) {
2662
2663
2664 $entry->setHeaders($hkey, $data[$uid][$cid][$hval]);
2665 $crit->remove($key, $hkey);
2666 } else {
2667 $this->_temp['headers_caching'][$hkey] = $hval;
2668 }
2669 }
2670 break;
2671
2672 case Horde_Imap_Client::FETCH_IMAPDATE:
2673 if (isset($data[$uid][$cid]) &&
2674 ($data[$uid][$cid] instanceof Horde_Imap_Client_DateTime)) {
2675 $entry->setImapDate($data[$uid][$cid]);
2676 unset($crit[$key]);
2677 }
2678 break;
2679
2680 case Horde_Imap_Client::FETCH_SIZE:
2681 if (isset($data[$uid][$cid])) {
2682 $entry->setSize($data[$uid][$cid]);
2683 unset($crit[$key]);
2684 }
2685 break;
2686
2687 case Horde_Imap_Client::FETCH_STRUCTURE:
2688 if (isset($data[$uid][$cid]) &&
2689 ($data[$uid][$cid] instanceof Horde_Mime_Part)) {
2690 $entry->setStructure($data[$uid][$cid]);
2691 unset($crit[$key]);
2692 }
2693 break;
2694 }
2695 }
2696
2697 if (count($crit)) {
2698 $sig = $crit->hash();
2699 if (isset($new_query[$sig])) {
2700 $new_query[$sig]['i'][] = $entry_idx;
2701 } else {
2702 $new_query[$sig] = array(
2703 'c' => $crit,
2704 'i' => array($entry_idx)
2705 );
2706 }
2707 }
2708 }
2709
2710 $to_fetch = array();
2711 foreach ($new_query as $val) {
2712 $ids_ob = $this->getIdsOb(null, $sequence);
2713 $ids_ob->duplicates = true;
2714 $ids_ob->add($val['i']);
2715 $to_fetch[] = array_merge($options, array(
2716 '_query' => $val['c'],
2717 'ids' => $ids_ob
2718 ));
2719 }
2720
2721 if (!empty($to_fetch)) {
2722 $this->_fetch(is_null($cs_ret) ? $ret : $cs_ret, $to_fetch);
2723 }
2724
2725 if (is_null($cs_ret)) {
2726 return $ret;
2727 }
2728
2729
2730
2731 if (empty($new_query)) {
2732 $squery = new Horde_Imap_Client_Search_Query();
2733 $squery->modseq($options['changedsince'] + 1);
2734 $squery->ids($options['ids']);
2735
2736 $cs = $this->search($this->_selected, $squery, array(
2737 'sequence' => $sequence
2738 ));
2739
2740 foreach ($cs['match'] as $val) {
2741 $entry = $ret->get($val);
2742 if ($sequence) {
2743 $entry->setSeq($val);
2744 } else {
2745 $entry->setUid($val);
2746 }
2747 $cs_ret[$val] = $entry;
2748 }
2749 } else {
2750 foreach ($cs_ret as $key => $val) {
2751 $val->merge($ret->get($key));
2752 }
2753 }
2754
2755 return $cs_ret;
2756 }
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771 abstract protected function _fetch(Horde_Imap_Client_Fetch_Results $results,
2772 $queries);
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793 public function vanished($mailbox, $modseq, array $opts = array())
2794 {
2795 $this->login();
2796
2797 if (empty($opts['ids'])) {
2798 if (!$this->_capability()->isEnabled('QRESYNC')) {
2799 return $this->getIdsOb();
2800 }
2801 $opts['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
2802 } elseif ($opts['ids']->isEmpty()) {
2803 return $this->getIdsOb();
2804 } elseif ($opts['ids']->sequence) {
2805 throw new InvalidArgumentException('Vanished requires UIDs.');
2806 }
2807
2808 $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
2809
2810 if ($this->_capability()->isEnabled('QRESYNC')) {
2811 if (!$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
2812 throw new Horde_Imap_Client_Exception(
2813 Horde_Imap_Client_Translation::r("Mailbox does not support mod-sequences."),
2814 Horde_Imap_Client_Exception::MBOXNOMODSEQ
2815 );
2816 }
2817
2818 return $this->_vanished(max(1, $modseq), $opts['ids']);
2819 }
2820
2821 $ids = $this->resolveIds($mailbox, $opts['ids']);
2822
2823 $squery = new Horde_Imap_Client_Search_Query();
2824 $squery->ids($ids);
2825 $search = $this->search($mailbox, $squery, array(
2826 'nocache' => true
2827 ));
2828
2829 return $this->getIdsOb(array_diff($ids->ids, $search['match']->ids));
2830 }
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840 abstract protected function _vanished($modseq, Horde_Imap_Client_Ids $ids);
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874 public function store($mailbox, array $options = array())
2875 {
2876
2877 $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_READWRITE);
2878
2879
2880 if (empty($options['ids'])) {
2881 $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
2882 } elseif ($options['ids']->isEmpty()) {
2883 return $this->getIdsOb();
2884 } elseif ($options['ids']->search_res &&
2885 !$this->_capability('SEARCHRES')) {
2886 throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
2887 }
2888
2889 if (!empty($options['unchangedsince'])) {
2890 if (!$this->_capability()->isEnabled('CONDSTORE')) {
2891 throw new Horde_Imap_Client_Exception_NoSupportExtension('CONDSTORE');
2892 }
2893
2894
2895
2896 if (!$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
2897 throw new Horde_Imap_Client_Exception(
2898 Horde_Imap_Client_Translation::r("Mailbox does not support mod-sequences."),
2899 Horde_Imap_Client_Exception::MBOXNOMODSEQ
2900 );
2901 }
2902 }
2903
2904 return $this->_store($options);
2905 }
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918 abstract protected function _store($options);
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946 public function copy($source, $dest, array $options = array())
2947 {
2948
2949 $this->openMailbox($source, empty($options['move']) ? Horde_Imap_Client::OPEN_AUTO : Horde_Imap_Client::OPEN_READWRITE);
2950
2951
2952 if (empty($options['ids'])) {
2953 $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
2954 } elseif ($options['ids']->isEmpty()) {
2955 return array();
2956 } elseif ($options['ids']->search_res &&
2957 !$this->_capability('SEARCHRES')) {
2958 throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
2959 }
2960
2961 $dest = Horde_Imap_Client_Mailbox::get($dest);
2962 $res = $this->_copy($dest, $options);
2963
2964 if (($res === true) && !empty($options['force_map'])) {
2965
2966 $query = new Horde_Imap_Client_Fetch_Query();
2967 $query->envelope();
2968 $fetch = $this->fetch($source, $query, array(
2969 'ids' => $options['ids']
2970 ));
2971
2972 $res = array();
2973 foreach ($fetch as $val) {
2974 if ($uid = $this->_getUidByMessageId($dest, $val->getEnvelope()->message_id)) {
2975 $res[$val->getUid()] = $uid;
2976 }
2977 }
2978 }
2979
2980 return $res;
2981 }
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995 abstract protected function _copy(Horde_Imap_Client_Mailbox $dest,
2996 $options);
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012 public function setQuota($root, array $resources = array())
3013 {
3014 $this->login();
3015
3016 if (!$this->_capability('QUOTA')) {
3017 throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
3018 }
3019
3020 if (!empty($resources)) {
3021 $this->_setQuota(Horde_Imap_Client_Mailbox::get($root), $resources);
3022 }
3023 }
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035 abstract protected function _setQuota(Horde_Imap_Client_Mailbox $root,
3036 $resources);
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051 public function getQuota($root)
3052 {
3053 $this->login();
3054
3055 if (!$this->_capability('QUOTA')) {
3056 throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
3057 }
3058
3059 return $this->_getQuota(Horde_Imap_Client_Mailbox::get($root));
3060 }
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072 abstract protected function _getQuota(Horde_Imap_Client_Mailbox $root);
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088 public function getQuotaRoot($mailbox)
3089 {
3090 $this->login();
3091
3092 if (!$this->_capability('QUOTA')) {
3093 throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
3094 }
3095
3096 return $this->_getQuotaRoot(Horde_Imap_Client_Mailbox::get($mailbox));
3097 }
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110 abstract protected function _getQuotaRoot(Horde_Imap_Client_Mailbox $mailbox);
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124 public function getACL($mailbox)
3125 {
3126 $this->login();
3127 return $this->_getACL(Horde_Imap_Client_Mailbox::get($mailbox));
3128 }
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140 abstract protected function _getACL(Horde_Imap_Client_Mailbox $mailbox);
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156 public function setACL($mailbox, $identifier, $options)
3157 {
3158 $this->login();
3159
3160 if (!$this->_capability('ACL')) {
3161 throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
3162 }
3163
3164 if (empty($options['rights'])) {
3165 if (!isset($options['action']) ||
3166 (($options['action'] != 'add') &&
3167 $options['action'] != 'remove')) {
3168 $this->_deleteACL(
3169 Horde_Imap_Client_Mailbox::get($mailbox),
3170 Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
3171 );
3172 }
3173 return;
3174 }
3175
3176 $acl = ($options['rights'] instanceof Horde_Imap_Client_Data_Acl)
3177 ? $options['rights']
3178 : new Horde_Imap_Client_Data_Acl(strval($options['rights']));
3179
3180 $options['rights'] = $acl->getString(
3181 $this->_capability('RIGHTS')
3182 ? Horde_Imap_Client_Data_AclCommon::RFC_4314
3183 : Horde_Imap_Client_Data_AclCommon::RFC_2086
3184 );
3185 if (isset($options['action'])) {
3186 switch ($options['action']) {
3187 case 'add':
3188 $options['rights'] = '+' . $options['rights'];
3189 break;
3190 case 'remove':
3191 $options['rights'] = '-' . $options['rights'];
3192 break;
3193 }
3194 }
3195
3196 $this->_setACL(
3197 Horde_Imap_Client_Mailbox::get($mailbox),
3198 Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier),
3199 $options
3200 );
3201 }
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215 abstract protected function _setACL(Horde_Imap_Client_Mailbox $mailbox,
3216 $identifier, $options);
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228 public function deleteACL($mailbox, $identifier)
3229 {
3230 $this->login();
3231
3232 if (!$this->_capability('ACL')) {
3233 throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
3234 }
3235
3236 $this->_deleteACL(
3237 Horde_Imap_Client_Mailbox::get($mailbox),
3238 Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
3239 );
3240 }
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251 abstract protected function _deleteACL(Horde_Imap_Client_Mailbox $mailbox,
3252 $identifier);
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267 public function listACLRights($mailbox, $identifier)
3268 {
3269 $this->login();
3270
3271 if (!$this->_capability('ACL')) {
3272 throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
3273 }
3274
3275 return $this->_listACLRights(
3276 Horde_Imap_Client_Mailbox::get($mailbox),
3277 Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
3278 );
3279 }
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292 abstract protected function _listACLRights(Horde_Imap_Client_Mailbox $mailbox,
3293 $identifier);
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307 public function getMyACLRights($mailbox)
3308 {
3309 $this->login();
3310
3311 if (!$this->_capability('ACL')) {
3312 throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
3313 }
3314
3315 return $this->_getMyACLRights(Horde_Imap_Client_Mailbox::get($mailbox));
3316 }
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327 abstract protected function _getMyACLRights(Horde_Imap_Client_Mailbox $mailbox);
3328
3329
3330
3331
3332
3333
3334 public function allAclRights()
3335 {
3336 $this->login();
3337
3338 $rights = array(
3339 Horde_Imap_Client::ACL_LOOKUP,
3340 Horde_Imap_Client::ACL_READ,
3341 Horde_Imap_Client::ACL_SEEN,
3342 Horde_Imap_Client::ACL_WRITE,
3343 Horde_Imap_Client::ACL_INSERT,
3344 Horde_Imap_Client::ACL_POST,
3345 Horde_Imap_Client::ACL_ADMINISTER
3346 );
3347
3348 if ($capability = $this->_capability()->getParams('RIGHTS')) {
3349
3350 return array_merge($rights, str_split(reset($capability)));
3351 }
3352
3353
3354
3355 return array_merge($rights, array(
3356 Horde_Imap_Client::ACL_CREATE,
3357 Horde_Imap_Client::ACL_DELETE
3358 ));
3359 }
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384 public function getMetadata($mailbox, $entries, array $options = array())
3385 {
3386 $this->login();
3387
3388 if (!is_array($entries)) {
3389 $entries = array($entries);
3390 }
3391
3392 return $this->_getMetadata(Horde_Imap_Client_Mailbox::get($mailbox), array_map(array('Horde_Imap_Client_Utf7imap', 'Utf8ToUtf7Imap'), $entries), $options);
3393 }
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408 abstract protected function _getMetadata(Horde_Imap_Client_Mailbox $mailbox,
3409 $entries, $options);
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423 public function setMetadata($mailbox, $data)
3424 {
3425 $this->login();
3426 $this->_setMetadata(Horde_Imap_Client_Mailbox::get($mailbox), $data);
3427 }
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438 abstract protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox,
3439 $data);
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460 public function getCacheId($mailbox, array $addl = array())
3461 {
3462 return Horde_Imap_Client_Base_Deprecated::getCacheId($this, $mailbox, $this->_capability()->isEnabled('CONDSTORE'), $addl);
3463 }
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478 public function parseCacheId($id)
3479 {
3480 return Horde_Imap_Client_Base_Deprecated::parseCacheId($id);
3481 }
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495 public function resolveIds(Horde_Imap_Client_Mailbox $mailbox,
3496 Horde_Imap_Client_Ids $ids, $convert = 0)
3497 {
3498 $map = $this->_mailboxOb($mailbox)->map;
3499
3500 if ($ids->special) {
3501
3502 if (!$convert && $ids->all && $ids->sequence) {
3503 $res = $this->status($mailbox, Horde_Imap_Client::STATUS_MESSAGES);
3504 return $this->getIdsOb($res['messages'] ? ('1:' . $res['messages']) : array(), true);
3505 }
3506
3507 $convert = 2;
3508 } elseif (!$convert ||
3509 (!$ids->sequence && ($convert == 1)) ||
3510 $ids->isEmpty()) {
3511 return clone $ids;
3512 } else {
3513
3514
3515
3516
3517
3518 $lookup = $map->lookup($ids);
3519 if (count($lookup) === count($ids)) {
3520 return $this->getIdsOb(array_values($lookup));
3521 }
3522 }
3523
3524 $query = new Horde_Imap_Client_Search_Query();
3525 $query->ids($ids);
3526
3527 $res = $this->search($mailbox, $query, array(
3528 'results' => array(
3529 Horde_Imap_Client::SEARCH_RESULTS_MATCH,
3530 Horde_Imap_Client::SEARCH_RESULTS_SAVE
3531 ),
3532 'sequence' => (!$convert && $ids->sequence),
3533 'sort' => array(Horde_Imap_Client::SORT_SEQUENCE)
3534 ));
3535
3536
3537 if ($convert) {
3538 if ($ids->all) {
3539 $ids = $this->getIdsOb('1:' . count($res['match']));
3540 } elseif ($ids->special) {
3541 return $res['match'];
3542 }
3543
3544
3545 $list1 = array_slice($ids->ids, 0, count($res['match']));
3546 $list2 = $res['match']->ids;
3547 if (!empty($list1) &&
3548 !empty($list2) &&
3549 (count($list1) === count($list2))) {
3550 $map->update(array_combine($list1, $list2));
3551 }
3552 }
3553
3554 return $res['match'];
3555 }
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567 public function validSearchCharset($charset)
3568 {
3569 return $this->search_charset->query($charset);
3570 }
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586 public function getSyncToken($mailbox)
3587 {
3588 $out = array();
3589
3590 foreach ($this->_syncStatus($mailbox) as $key => $val) {
3591 $out[] = $key . $val;
3592 }
3593
3594 return base64_encode(implode(',', $out));
3595 }
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617 public function sync($mailbox, $token, array $opts = array())
3618 {
3619 if (($token = base64_decode($token, true)) === false) {
3620 throw new Horde_Imap_Client_Exception_Sync('Bad token.', Horde_Imap_Client_Exception_Sync::BAD_TOKEN);
3621 }
3622
3623 $sync = array();
3624 foreach (explode(',', $token) as $val) {
3625 $sync[substr($val, 0, 1)] = substr($val, 1);
3626 }
3627
3628 return new Horde_Imap_Client_Data_Sync(
3629 $this,
3630 $mailbox,
3631 $sync,
3632 $this->_syncStatus($mailbox),
3633 (isset($opts['criteria']) ? $opts['criteria'] : Horde_Imap_Client::SYNC_ALL),
3634 (isset($opts['ids']) ? $opts['ids'] : null)
3635 );
3636 }
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647 protected function _updateCache(Horde_Imap_Client_Fetch_Results $data)
3648 {
3649 if (!empty($this->_temp['fetch_nocache']) ||
3650 empty($this->_selected) ||
3651 !count($data) ||
3652 !$this->_initCache(true)) {
3653 return;
3654 }
3655
3656 $c = $this->getParam('cache');
3657 if (in_array(strval($this->_selected), $c['fetch_ignore'])) {
3658 $this->_debug->info(sprintf(
3659 'CACHE: Ignoring FETCH data [%s]',
3660 $this->_selected
3661 ));
3662 return;
3663 }
3664
3665
3666
3667 $mbox_ob = $this->_mailboxOb();
3668 $highestmodseq = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
3669 $uidvalidity = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY);
3670
3671 $mapping = $modseq = $tocache = array();
3672 if (count($data)) {
3673 $cf = $this->_cacheFields();
3674 }
3675
3676 foreach ($data as $v) {
3677
3678
3679 if (!($uid = $v->getUid())) {
3680 return;
3681 }
3682
3683 $tmp = array();
3684
3685 if ($v->isDowngraded()) {
3686 $tmp[self::CACHE_DOWNGRADED] = true;
3687 }
3688
3689 foreach ($cf as $key => $val) {
3690 if ($v->exists($key)) {
3691 switch ($key) {
3692 case Horde_Imap_Client::FETCH_ENVELOPE:
3693 $tmp[$val] = $v->getEnvelope();
3694 break;
3695
3696 case Horde_Imap_Client::FETCH_FLAGS:
3697 if ($highestmodseq) {
3698 $modseq[$uid] = $v->getModSeq();
3699 $tmp[$val] = $v->getFlags();
3700 }
3701 break;
3702
3703 case Horde_Imap_Client::FETCH_HEADERS:
3704 foreach ($this->_temp['headers_caching'] as $label => $hash) {
3705 if ($hdr = $v->getHeaders($label)) {
3706 $tmp[$val][$hash] = $hdr;
3707 }
3708 }
3709 break;
3710
3711 case Horde_Imap_Client::FETCH_IMAPDATE:
3712 $tmp[$val] = $v->getImapDate();
3713 break;
3714
3715 case Horde_Imap_Client::FETCH_SIZE:
3716 $tmp[$val] = $v->getSize();
3717 break;
3718
3719 case Horde_Imap_Client::FETCH_STRUCTURE:
3720 $tmp[$val] = clone $v->getStructure();
3721 break;
3722 }
3723 }
3724 }
3725
3726 if (!empty($tmp)) {
3727 $tocache[$uid] = $tmp;
3728 }
3729
3730 $mapping[$v->getSeq()] = $uid;
3731 }
3732
3733 if (!empty($mapping)) {
3734 if (!empty($tocache)) {
3735 $this->_cache->set($this->_selected, $tocache, $uidvalidity);
3736 }
3737
3738 $this->_mailboxOb()->map->update($mapping);
3739 }
3740
3741 if (!empty($modseq)) {
3742 $this->_updateModSeq(max(array_merge($modseq, array($highestmodseq))));
3743 $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCFLAGUIDS, array_keys($modseq));
3744 }
3745 }
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758 protected function _moveCache(Horde_Imap_Client_Mailbox $to, $map,
3759 $uidvalid)
3760 {
3761 if (!$this->_initCache()) {
3762 return;
3763 }
3764
3765 $c = $this->getParam('cache');
3766 if (in_array(strval($to), $c['fetch_ignore'])) {
3767 $this->_debug->info(sprintf(
3768 'CACHE: Ignoring moving FETCH data (%s => %s)',
3769 $this->_selected,
3770 $to
3771 ));
3772 return;
3773 }
3774
3775 $old = $this->_cache->get($this->_selected, array_keys($map), null);
3776 $new = array();
3777
3778 foreach ($map as $key => $val) {
3779 if (!empty($old[$key])) {
3780 $new[$val] = $old[$key];
3781 }
3782 }
3783
3784 if (!empty($new)) {
3785 $this->_cache->set($to, $new, $uidvalid);
3786 }
3787 }
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801 protected function _deleteMsgs(Horde_Imap_Client_Mailbox $mailbox,
3802 Horde_Imap_Client_Ids $ids,
3803 array $opts = array())
3804 {
3805 if (!$this->_initCache()) {
3806 return $ids;
3807 }
3808
3809 $mbox_ob = $this->_mailboxOb();
3810 $ids_ob = $ids->sequence
3811 ? $this->getIdsOb($mbox_ob->map->lookup($ids))
3812 : $ids;
3813
3814 $this->_cache->deleteMsgs($mailbox, $ids_ob->ids);
3815 $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCVANISHED, $ids_ob->ids);
3816 $mbox_ob->map->remove($ids);
3817
3818 return $ids_ob;
3819 }
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831 protected function _getSearchCache($type, $options)
3832 {
3833 $status = $this->status($this->_selected, Horde_Imap_Client::STATUS_HIGHESTMODSEQ | Horde_Imap_Client::STATUS_UIDVALIDITY);
3834
3835
3836
3837 if (empty($status['highestmodseq'])) {
3838 return null;
3839 }
3840
3841 ksort($options);
3842 $cache = hash('md5', $type . serialize($options));
3843 $cacheid = $this->getSyncToken($this->_selected);
3844 $ret = array();
3845
3846 $md = $this->_cache->getMetaData(
3847 $this->_selected,
3848 $status['uidvalidity'],
3849 array(self::CACHE_SEARCH, self::CACHE_SEARCHID)
3850 );
3851
3852 if (!isset($md[self::CACHE_SEARCHID]) ||
3853 ($md[self::CACHE_SEARCHID] != $cacheid)) {
3854 $md[self::CACHE_SEARCH] = array();
3855 $md[self::CACHE_SEARCHID] = $cacheid;
3856 if ($this->_debug->debug &&
3857 !isset($this->_temp['searchcacheexpire'][strval($this->_selected)])) {
3858 $this->_debug->info(sprintf(
3859 'SEARCH: Expired from cache [%s]',
3860 $this->_selected
3861 ));
3862 $this->_temp['searchcacheexpire'][strval($this->_selected)] = true;
3863 }
3864 } elseif (isset($md[self::CACHE_SEARCH][$cache])) {
3865 $this->_debug->info(sprintf(
3866 'SEARCH: Retrieved %s from cache (%s [%s])',
3867 $type,
3868 $cache,
3869 $this->_selected
3870 ));
3871 $ret['data'] = $md[self::CACHE_SEARCH][$cache];
3872 unset($md[self::CACHE_SEARCHID]);
3873 }
3874
3875 return array_merge($ret, array(
3876 'id' => $cache,
3877 'metadata' => $md,
3878 'type' => $type
3879 ));
3880 }
3881
3882
3883
3884
3885
3886
3887
3888 protected function _setSearchCache($data, $sdata)
3889 {
3890 $sdata['metadata'][self::CACHE_SEARCH][$sdata['id']] = $data;
3891
3892 $this->_cache->setMetaData($this->_selected, null, $sdata['metadata']);
3893
3894 if ($this->_debug->debug) {
3895 $this->_debug->info(sprintf(
3896 'SEARCH: Saved %s to cache (%s [%s])',
3897 $sdata['type'],
3898 $sdata['id'],
3899 $this->_selected
3900 ));
3901 unset($this->_temp['searchcacheexpire'][strval($this->_selected)]);
3902 }
3903 }
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913 protected function _updateModSeq($modseq)
3914 {
3915 if (!$this->_initCache(true)) {
3916 return false;
3917 }
3918
3919 $mbox_ob = $this->_mailboxOb();
3920 $uidvalid = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY);
3921 $md = $this->_cache->getMetaData($this->_selected, $uidvalid, array(self::CACHE_MODSEQ));
3922
3923 if (isset($md[self::CACHE_MODSEQ])) {
3924 if ($md[self::CACHE_MODSEQ] < $modseq) {
3925 $set = true;
3926 $sync = $md[self::CACHE_MODSEQ];
3927 } else {
3928 $set = false;
3929 $sync = 0;
3930 }
3931 $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCMODSEQ, $md[self::CACHE_MODSEQ]);
3932 } else {
3933 $set = true;
3934 $sync = 0;
3935 }
3936
3937
3938 if ($set && $modseq) {
3939 $this->_cache->setMetaData($this->_selected, $uidvalid, array(
3940 self::CACHE_MODSEQ => $modseq
3941 ));
3942 }
3943
3944 return $sync;
3945 }
3946
3947
3948
3949
3950
3951 protected function _condstoreSync()
3952 {
3953 $mbox_ob = $this->_mailboxOb();
3954
3955
3956 if (!($highestmodseq = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) ||
3957 !($modseq = $this->_updateModSeq($highestmodseq))) {
3958 $mbox_ob->sync = true;
3959 }
3960
3961 if ($mbox_ob->sync) {
3962 return;
3963 }
3964
3965 $uids_ob = $this->getIdsOb($this->_cache->get(
3966 $this->_selected,
3967 array(),
3968 array(),
3969 $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY)
3970 ));
3971
3972 if (!count($uids_ob)) {
3973 $mbox_ob->sync = true;
3974 return;
3975 }
3976
3977
3978 if (array_key_exists(Horde_Imap_Client::FETCH_FLAGS, $this->_cacheFields())) {
3979 $fquery = new Horde_Imap_Client_Fetch_Query();
3980 $fquery->flags();
3981
3982
3983 $this->_fetch(new Horde_Imap_Client_Fetch_Results(), array(
3984 array(
3985 '_query' => $fquery,
3986 'changedsince' => $modseq,
3987 'ids' => $uids_ob
3988 )
3989 ));
3990 }
3991
3992
3993 $vanished = $this->vanished($this->_selected, $modseq, array(
3994 'ids' => $uids_ob
3995 ));
3996 $disappear = array_diff($uids_ob->ids, $vanished->ids);
3997 if (!empty($disappear)) {
3998 $this->_deleteMsgs($this->_selected, $this->getIdsOb($disappear));
3999 }
4000
4001 $mbox_ob->sync = true;
4002 }
4003
4004
4005
4006
4007
4008
4009
4010 protected function _cacheFields()
4011 {
4012 $c = $this->getParam('cache');
4013 $out = $c['fields'];
4014
4015 if (!$this->_capability()->isEnabled('CONDSTORE')) {
4016 unset($out[Horde_Imap_Client::FETCH_FLAGS]);
4017 }
4018
4019 return $out;
4020 }
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031 protected function _syncStatus($mailbox)
4032 {
4033 $status = $this->status(
4034 $mailbox,
4035 Horde_Imap_Client::STATUS_HIGHESTMODSEQ |
4036 Horde_Imap_Client::STATUS_MESSAGES |
4037 Horde_Imap_Client::STATUS_UIDNEXT_FORCE |
4038 Horde_Imap_Client::STATUS_UIDVALIDITY
4039 );
4040
4041 $fields = array('uidnext', 'uidvalidity');
4042 if (empty($status['highestmodseq'])) {
4043 $fields[] = 'messages';
4044 } else {
4045 $fields[] = 'highestmodseq';
4046 }
4047
4048 $out = array();
4049 $sync_map = array_flip(Horde_Imap_Client_Data_Sync::$map);
4050
4051 foreach ($fields as $val) {
4052 $out[$sync_map[$val]] = $status[$val];
4053 }
4054
4055 return array_filter($out);
4056 }
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067 protected function _getUidByMessageId($mailbox, $msgid)
4068 {
4069 if (!$msgid) {
4070 return null;
4071 }
4072
4073 $query = new Horde_Imap_Client_Search_Query();
4074 $query->headerText('Message-ID', $msgid);
4075 $res = $this->search($mailbox, $query, array(
4076 'results' => array(Horde_Imap_Client::SEARCH_RESULTS_MAX)
4077 ));
4078
4079 return $res['max'];
4080 }
4081
4082 }