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: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111:
112: class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base
113: {
114: 115: 116: 117: 118:
119: protected $_tag = 0;
120:
121: 122: 123: 124: 125:
126: protected $_stream = null;
127:
128: 129: 130: 131: 132: 133: 134:
135: public function __construct(array $params = array())
136: {
137: $params = array_merge(array(
138: 'debug_literal' => false
139: ), $params);
140:
141: parent::__construct($params);
142: }
143:
144: 145:
146: protected function _capability()
147: {
148:
149:
150: $this->_connect();
151:
152:
153:
154: if (!isset($this->_init['capability'])) {
155: $this->_sendLine('CAPABILITY');
156: }
157:
158: return isset($this->_init['capability'])
159: ? $this->_init['capability']
160: : array();
161: }
162:
163: 164: 165: 166: 167:
168: protected function _parseCapability($data)
169: {
170: if (!empty($this->_temp['no_cap'])) {
171: unset($this->_temp['no_cap']);
172: return;
173: }
174:
175: if (empty($this->_temp['in_login'])) {
176: $c = array();
177: } else {
178: $c = $this->_init['capability'];
179: $this->_temp['logincapset'] = true;
180: }
181:
182: foreach ($data as $val) {
183: $cap_list = explode('=', $val);
184: $cap_list[0] = strtoupper($cap_list[0]);
185: if (isset($cap_list[1])) {
186: if (!isset($c[$cap_list[0]]) || !is_array($c[$cap_list[0]])) {
187: $c[$cap_list[0]] = array();
188: }
189: $c[$cap_list[0]][] = $cap_list[1];
190: } elseif (!isset($c[$cap_list[0]])) {
191: $c[$cap_list[0]] = true;
192: }
193: }
194:
195: 196:
197: if (isset($c['QRESYNC'])) {
198: $c['CONDSTORE'] = true;
199: $c['ENABLE'] = true;
200: }
201:
202: $this->_setInit('capability', $c);
203: }
204:
205: 206:
207: protected function _noop()
208: {
209:
210: $this->_sendLine('NOOP');
211: }
212:
213: 214:
215: protected function _getNamespaces()
216: {
217: if (!$this->queryCapability('NAMESPACE')) {
218: return array();
219: }
220:
221: $this->_sendLine('NAMESPACE');
222: return $this->_temp['namespace'];
223: }
224:
225: 226: 227: 228: 229:
230: protected function _parseNamespace($data)
231: {
232: $namespace_array = array(
233: Horde_Imap_Client::NS_PERSONAL,
234: Horde_Imap_Client::NS_OTHER,
235: Horde_Imap_Client::NS_SHARED
236: );
237:
238: $c = &$this->_temp['namespace'];
239: $c = array();
240:
241:
242:
243: foreach ($namespace_array as $i => $val) {
244: if (($entry = $this->_getString($data[$i], true)) === null) {
245: continue;
246: }
247: reset($data[$i]);
248: while (list(,$v) = each($data[$i])) {
249: $ob = Horde_Imap_Client_Mailbox::get($this->_getString($v[0]), true);
250:
251: $c[$ob->utf7imap] = array(
252: 'delimiter' => $v[1],
253: 'hidden' => false,
254: 'name' => $ob->utf7imap,
255: 'translation' => '',
256: 'type' => $val
257: );
258:
259:
260: for ($j = 2; isset($v[$j]); $j += 2) {
261: switch (strtoupper($v[$j])) {
262: case 'TRANSLATION':
263:
264: $c[$ob->utf7imap]['translation'] = reset($v[$j + 1]);
265: break;
266: }
267: }
268: }
269: }
270: }
271:
272: 273:
274: public function alerts()
275: {
276: $alerts = empty($this->_temp['alerts'])
277: ? array()
278: : $this->_temp['alerts'];
279: $this->_temp['alerts'] = array();
280: return $alerts;
281: }
282:
283: 284:
285: protected function _login()
286: {
287: if (!empty($this->_temp['preauth'])) {
288: unset($this->_temp['preauth']);
289: return $this->_loginTasks();
290: }
291:
292: $this->_connect();
293:
294: $first_login = empty($this->_init['authmethod']);
295: $t = &$this->_temp;
296:
297:
298: if (!$this->_isSecure &&
299: ($this->_params['secure'] == 'tls')) {
300: if ($first_login && !$this->queryCapability('STARTTLS')) {
301:
302:
303: $this->_exception(Horde_Imap_Client_Translation::t("Server does not support TLS connections."), 'LOGIN_TLSFAILURE');
304: }
305:
306:
307:
308: $this->_sendLine('STARTTLS');
309:
310: if (@stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT) !== true) {
311: $this->logout();
312: $this->_exception(Horde_Imap_Client_Translation::t("Could not open secure TLS connection to the IMAP server."), 'LOGIN_TLSFAILURE');
313: }
314:
315: if ($first_login) {
316:
317: $this->_setInit('capability');
318:
319:
320: $this->_setInit('lang');
321: }
322:
323:
324: if (!empty($this->_init['imapproxy'])) {
325: $this->setLanguage();
326: }
327:
328: $this->_isSecure = true;
329: }
330:
331: if ($first_login) {
332: $imap_auth_mech = array();
333:
334: $auth_methods = $this->queryCapability('AUTH');
335: if (!empty($auth_methods)) {
336:
337:
338: $imap_auth_mech = array_intersect(array('CRAM-MD5', 'DIGEST-MD5'), $auth_methods);
339:
340:
341: if (in_array('PLAIN', $auth_methods)) {
342: $imap_auth_mech[] = 'PLAIN';
343: }
344: }
345:
346:
347: if (!$this->queryCapability('LOGINDISABLED')) {
348: $imap_auth_mech[] = 'LOGIN';
349: }
350:
351: if (empty($imap_auth_mech)) {
352: $this->_exception(Horde_Imap_Client_Translation::t("No supported IMAP authentication method could be found."), 'LOGIN_NOAUTHMETHOD');
353: }
354:
355: 356: 357:
358: if ($this->_isSecure) {
359: $imap_auth_mech = array_reverse($imap_auth_mech);
360: }
361: } else {
362: $imap_auth_mech = array($this->_init['authmethod']);
363: }
364:
365:
366: $t['loginerr'] = 'LOGIN_AUTHENTICATIONFAILED';
367: $t['loginerrmsg'] = Horde_Imap_Client_Translation::t("Mail server denied authentication.");
368:
369: foreach ($imap_auth_mech as $method) {
370: $t['referral'] = null;
371:
372: 373: 374: 375: 376: 377:
378: $this->_temp['in_login'] = true;
379:
380: try {
381: $this->_tryLogin($method);
382: $success = true;
383: $this->_setInit('authmethod', $method);
384: unset($t['referralcount']);
385: } catch (Horde_Imap_Client_Exception $e) {
386: $success = false;
387: }
388:
389: unset($this->_temp['in_login']);
390:
391:
392:
393: if (!is_null($t['referral'])) {
394: foreach (array('hostspec', 'port', 'username') as $val) {
395: if (isset($t['referral'][$val])) {
396: $this->_params[$val] = $t['referral'][$val];
397: }
398: }
399:
400: if (isset($t['referral']['auth'])) {
401: $this->_setInit('authmethod', $t['referral']['auth']);
402: }
403:
404: if (!isset($t['referralcount'])) {
405: $t['referralcount'] = 0;
406: }
407:
408:
409:
410: if (++$t['referralcount'] < 10) {
411: $this->logout();
412: $this->_setInit('capability');
413: $this->_setInit('namespace', array());
414: return $this->login();
415: }
416:
417: unset($t['referralcount']);
418: }
419:
420: if ($success) {
421: return $this->_loginTasks($first_login);
422: }
423: }
424:
425: $err_msg = $t['loginerrmsg'];
426: $err_code = $t['loginerr'];
427:
428: 429:
430: if (!empty($this->_init['authmethod'])) {
431: $this->_setInit();
432: try {
433: return $this->login();
434: } catch (Horde_Imap_Client_Exception $e) {}
435: }
436:
437: $this->_exception($err_msg, $err_code);
438: }
439:
440: 441: 442: 443: 444:
445: protected function _connect()
446: {
447: if (!is_null($this->_stream)) {
448: return;
449: }
450:
451: if (!empty($this->_params['secure']) && !extension_loaded('openssl')) {
452: throw new InvalidArgumentException('Secure connections require the PHP openssl extension.');
453: }
454:
455: switch ($this->_params['secure']) {
456: case 'ssl':
457: case 'sslv2':
458: case 'sslv3':
459: $conn = $this->_params['secure'] . '://';
460: $this->_isSecure = true;
461: break;
462:
463: case 'tls':
464: default:
465: $conn = 'tcp://';
466: break;
467: }
468:
469: $this->_stream = @stream_socket_client($conn . $this->_params['hostspec'] . ':' . $this->_params['port'], $error_number, $error_string, $this->_params['timeout']);
470:
471: if ($this->_stream === false) {
472: $this->_stream = null;
473: $this->_isSecure = false;
474: $this->_exception(array(
475: Horde_Imap_Client_Translation::t("Error connecting to mail server."),
476: sprintf("[%u] %s", $error_number, $error_string)
477: ), 'SERVER_CONNECT');
478: }
479:
480: stream_set_timeout($this->_stream, $this->_params['timeout']);
481:
482:
483:
484: if (isset($this->_init['capability'])) {
485: $this->_temp['no_cap'] = true;
486: }
487:
488:
489:
490:
491: $ob = $this->_getLine();
492: switch ($ob['response']) {
493: case 'BAD':
494:
495: $this->_exception(array(
496: Horde_Imap_Client_Translation::t("Server rejected connection."),
497: $ob['line']
498: ), 'SERVER_CONNECT');
499:
500: case 'PREAUTH':
501:
502: $this->_temp['preauth'] = true;
503: break;
504: }
505: $this->_parseServerResponse($ob);
506:
507:
508: if (!$this->queryCapability('IMAP4REV1')) {
509: $this->_exception(Horde_Imap_Client_Translation::t("The mail server does not support IMAP4rev1 (RFC 3501)."), 'SERVER_CONNECT');
510: }
511:
512:
513: if (empty($this->_init['imapproxy'])) {
514: if ($this->queryCapability('XIMAPPROXY')) {
515: $this->_setInit('imapproxy', true);
516: } else {
517: $this->setLanguage();
518: }
519: }
520:
521:
522: if (!empty($this->_temp['preauth'])) {
523: $this->login();
524: }
525: }
526:
527: 528: 529: 530: 531: 532: 533:
534: protected function _tryLogin($method)
535: {
536: switch ($method) {
537: case 'CRAM-MD5':
538: case 'CRAM-SHA1':
539: case 'CRAM-SHA256':
540:
541:
542: $ob = $this->_sendLine(array(
543: 'AUTHENTICATE',
544: array('t' => Horde_Imap_Client::DATA_ATOM, 'v' => $method)
545: ), array(
546: 'noparse' => true
547: ));
548:
549: $response = base64_encode($this->_params['username'] . ' ' . hash_hmac(strtolower(substr($method, 5)), base64_decode($ob['line']), $this->getParam('password'), false));
550: $this->_sendLine($response, array(
551: 'debug' => '[' . $method . ' Response]',
552: 'notag' => true
553: ));
554: break;
555:
556: case 'DIGEST-MD5':
557:
558: $ob = $this->_sendLine(array(
559: 'AUTHENTICATE',
560: array('t' => Horde_Imap_Client::DATA_ATOM, 'v' => $method)
561: ), array(
562: 'noparse' => true
563: ));
564:
565: $response = base64_encode(new Horde_Imap_Client_Auth_DigestMD5(
566: $this->_params['username'],
567: $this->getParam('password'),
568: base64_decode($ob['line']),
569: $this->_params['hostspec'],
570: 'imap'
571: ));
572: $ob = $this->_sendLine($response, array(
573: 'debug' => '[DIGEST-MD5 Response]',
574: 'noparse' => true,
575: 'notag' => true
576: ));
577: $response = base64_decode($ob['line']);
578: if (strpos($response, 'rspauth=') === false) {
579: $this->_exception(Horde_Imap_Client_Translation::t("Unexpected response from server when authenticating."), 'SERVER_CONNECT');
580: }
581: $this->_sendLine('', array(
582: 'notag' => true
583: ));
584: break;
585:
586: case 'LOGIN':
587: $this->_sendLine(array(
588: 'LOGIN',
589: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $this->_params['username']),
590: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $this->getParam('password'))
591: ), array(
592: 'debug' => sprintf('[LOGIN Command - username: %s]', $this->_params['username'])
593: ));
594: break;
595:
596: case 'PLAIN':
597:
598: $auth = base64_encode(implode("\0", array($this->_params['username'], $this->_params['username'], $this->getParam('password'))));
599: if ($this->queryCapability('SASL-IR')) {
600:
601: $this->_sendLine(array(
602: 'AUTHENTICATE',
603: 'PLAIN',
604: $auth
605: ), array(
606: 'debug' => sprintf('[SASL-IR AUTHENTICATE Command - username: %s]', $this->_params['username'])
607: ));
608: } else {
609: $this->_sendLine('AUTHENTICATE PLAIN', array(
610: 'noparse' => true
611: ));
612: $this->_sendLine($auth, array(
613: 'debug' => sprintf('[AUTHENTICATE Command - username: %s]', $this->_params['username']),
614: 'notag' => true
615: ));
616: }
617: break;
618:
619: default:
620: $this->_exception(sprintf(Horde_Imap_Client_Translation::t("Unknown authentication method: %s"), $method), 'SERVER_CONNECT');
621: }
622: }
623:
624: 625: 626: 627: 628: 629: 630:
631: protected function _loginTasks($firstlogin = true)
632: {
633: 634:
635: if (!$firstlogin && !empty($this->_temp['proxyreuse'])) {
636:
637: if (!isset($this->_init['lang'])) {
638: $this->setLanguage();
639: }
640: return false;
641: }
642:
643: $this->_setInit('enabled', array());
644:
645: 646:
647: if ($firstlogin && empty($this->_temp['logincapset'])) {
648: $this->_setInit('capability');
649: }
650: $this->setLanguage();
651:
652:
653: if ($this->_initCache()) {
654: if ($this->queryCapability('QRESYNC')) {
655: $this->_enable(array('QRESYNC'));
656: } elseif ($this->queryCapability('CONDSTORE')) {
657: $this->_enable(array('CONDSTORE'));
658: }
659: }
660:
661: return true;
662: }
663:
664: 665:
666: protected function _logout()
667: {
668: if (!is_null($this->_stream)) {
669: if (empty($this->_temp['logout'])) {
670: $this->_temp['logout'] = true;
671: $this->_sendLine('LOGOUT', array('errignore' => true));
672: }
673: unset($this->_temp['logout']);
674: @fclose($this->_stream);
675: $this->_stream = null;
676: }
677:
678: unset($this->_temp['proxyreuse']);
679: }
680:
681: 682:
683: protected function _sendID($info)
684: {
685: $cmd = array('ID');
686:
687: if (empty($info)) {
688: $cmd[] = array('t' => Horde_Imap_Client::DATA_NSTRING, 'v' => null);
689: } else {
690: $tmp = array();
691: foreach ($info as $key => $val) {
692: $tmp[] = array('t' => Horde_Imap_Client::DATA_STRING, 'v' => strtolower($key));
693: $tmp[] = array('t' => Horde_Imap_Client::DATA_NSTRING, 'v' => $val);
694: }
695: $cmd[] = $tmp;
696: }
697:
698: $this->_sendLine($cmd);
699: }
700:
701: 702: 703: 704: 705:
706: protected function _parseID($data)
707: {
708: $this->_temp['id'] = array();
709: $d = reset($data);
710: if (is_array($d)) {
711: for ($i = 0; isset($d[$i]); $i += 2) {
712: if (($id = $this->_getString($d[$i + 1])) !== null) {
713: $this->_temp['id'][$this->_getString($d[$i])] = $id;
714: }
715: }
716: }
717: }
718:
719: 720:
721: protected function _getID()
722: {
723: if (!isset($this->_temp['id'])) {
724: $this->sendID();
725: }
726: return $this->_temp['id'];
727: }
728:
729: 730:
731: protected function _setLanguage($langs)
732: {
733: $cmd = array('LANGUAGE');
734: foreach ($langs as $lang) {
735: $cmd[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $lang);
736: }
737:
738: try {
739: $this->_sendLine($cmd);
740: } catch (Horde_Imap_Client_Exception $e) {
741: $this->_setInit('lang', false);
742: return null;
743: }
744:
745: return $this->_init['lang'];
746: }
747:
748: 749:
750: protected function _getLanguage($list)
751: {
752: if (!$list) {
753: return empty($this->_init['lang'])
754: ? null
755: : $this->_init['lang'];
756: }
757:
758: if (!isset($this->_init['langavail'])) {
759: try {
760: $this->_sendLine('LANGUAGE');
761: } catch (Horde_Imap_Client_Exception $e) {
762: $this->_setInit('langavail', array());
763: }
764: }
765:
766: return $this->_init['langavail'];
767: }
768:
769: 770: 771: 772: 773:
774: protected function _parseLanguage($data)
775: {
776: if (count($data[0]) == 1) {
777:
778: $this->_setInit('lang', reset($data[0]));
779: } else {
780:
781: $this->_setInit('langavail', $data[0]);
782: }
783: }
784:
785: 786: 787: 788: 789: 790: 791:
792: protected function _enable($exts)
793: {
794: if ($this->queryCapability('ENABLE')) {
795:
796: $exts = array_diff($exts, array_keys($this->_init['enabled']));
797: if (!empty($exts)) {
798: $this->_sendLine(array_merge(array('ENABLE'), $exts));
799: }
800: }
801: }
802:
803: 804: 805: 806: 807:
808: protected function _parseEnabled($data)
809: {
810: $enabled = array_flip($data);
811:
812: if (in_array('QRESYNC', $data)) {
813: $enabled['CONDSTORE'] = true;
814: }
815:
816: $this->_setInit('enabled', array_merge($this->_init['enabled'], $enabled));
817: }
818:
819: 820:
821: protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox, $mode)
822: {
823: $condstore = false;
824: $qresync = isset($this->_init['enabled']['QRESYNC']);
825:
826: 827:
828: $reopen = $mailbox->equals($this->_selected);
829:
830: 831:
832: if (empty($this->_temp['mailbox']['name']) ||
833: (!$qresync && ($mailbox != $this->_temp['mailbox']['name']))) {
834: $this->_temp['mailbox'] = array('name' => clone($mailbox));
835: $this->_selected = clone($mailbox);
836: } elseif ($qresync) {
837: $this->_temp['qresyncmbox'] = clone($mailbox);
838: }
839:
840: $cmd = array(
841: (($mode == Horde_Imap_Client::OPEN_READONLY) ? 'EXAMINE' : 'SELECT'),
842: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap)
843: );
844:
845:
846: if (!$reopen && $qresync) {
847: $this->_initCache();
848: $metadata = $this->cache->getMetaData($mailbox, null, array(self::CACHE_MODSEQ, 'uidvalid'));
849:
850: if (isset($metadata[self::CACHE_MODSEQ])) {
851: $uids = $this->cache->get($mailbox);
852: if (!empty($uids)) {
853: 854: 855: 856: 857: 858: 859: 860: 861: 862: 863:
864: $cmd[] = array(
865: 'QRESYNC',
866: array(
867: $metadata['uidvalid'],
868: $metadata[self::CACHE_MODSEQ],
869: $this->utils->toSequenceString($uids)
870: )
871: );
872: }
873: }
874: } elseif (!$reopen &&
875: !isset($this->_init['enabled']['CONDSTORE']) &&
876: $this->_initCache() &&
877: $this->queryCapability('CONDSTORE')) {
878:
879: $cmd[] = array('CONDSTORE');
880: $condstore = true;
881: }
882:
883: try {
884: $this->_sendLine($cmd);
885: } catch (Horde_Imap_Client_Exception $e) {
886:
887:
888: if (isset($this->_temp['parseresperr']['response']) &&
889: ($this->_temp['parseresperr']['response'] == 'NO')) {
890: $this->_selected = null;
891: $this->_mode = 0;
892: if (!$e->getCode()) {
893: $this->_exception(sprintf(Horde_Imap_Client_Translation::t("Could not open mailbox \"%s\"."), $mailbox), 'MAILBOX_NOOPEN');
894: }
895: }
896: throw $e;
897: }
898:
899: if ($condstore) {
900: $this->_parseEnabled(array('CONDSTORE'));
901: }
902: }
903:
904: 905:
906: protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox, $opts)
907: {
908: $cmd = array(
909: 'CREATE',
910: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap)
911: );
912:
913: if (!empty($opts['special_use'])) {
914: $cmd[] = 'USE';
915:
916: $flags = array();
917: foreach ($opts['special_use'] as $val) {
918: $flags[] = array('t' => Horde_Imap_Client::DATA_ATOM, 'v' => $val);
919: }
920: $cmd[] = $flags;
921: }
922:
923:
924: $this->_sendLine($cmd);
925: }
926:
927: 928:
929: protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox)
930: {
931:
932:
933: if ($mailbox->equals($this->_selected)) {
934: $this->close();
935: }
936:
937: try {
938:
939: $this->_sendLine(array(
940: 'DELETE',
941: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap)
942: ));
943: } catch (Horde_Imap_Client_Exception $e) {
944:
945:
946: if (!empty($this->_temp['deleteretry'])) {
947: unset($this->_temp['deleteretry']);
948: throw $e;
949: }
950:
951: $this->store($mailbox, array('add' => array(Horde_Imap_Client::FLAG_DELETED)));
952: $this->expunge($mailbox);
953:
954: $this->_temp['deleteretry'] = true;
955: $this->deleteMailbox($mailbox);
956: }
957:
958: unset($this->_temp['deleteretry']);
959: }
960:
961: 962:
963: protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
964: Horde_Imap_Client_Mailbox $new)
965: {
966:
967: $this->_sendLine(array(
968: 'RENAME',
969: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $old->utf7imap),
970: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $new->utf7imap)
971: ));
972: }
973:
974: 975:
976: protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
977: $subscribe)
978: {
979:
980:
981: $this->_sendLine(array(
982: ($subscribe ? 'SUBSCRIBE' : 'UNSUBSCRIBE'),
983: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap)
984: ));
985: }
986:
987: 988:
989: protected function _listMailboxes($pattern, $mode, $options)
990: {
991:
992:
993: if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) &&
994: empty($options['attributes']) &&
995: empty($options['children']) &&
996: empty($options['recursivematch']) &&
997: empty($options['remote']) &&
998: empty($options['special_use']) &&
999: empty($options['status'])) {
1000: return $this->_getMailboxList(
1001: $pattern,
1002: Horde_Imap_Client::MBOX_SUBSCRIBED,
1003: array(
1004: 'delimiter' => !empty($options['delimiter']),
1005: 'flat' => !empty($options['flat']),
1006: 'no_listext' => true,
1007: 'utf8' => !empty($options['utf8'])
1008: )
1009: );
1010: }
1011:
1012:
1013:
1014:
1015: if (($mode != Horde_Imap_Client::MBOX_ALL) &&
1016: !$this->queryCapability('LIST-EXTENDED')) {
1017: $subscribed = $this->_getMailboxList($pattern, Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true));
1018:
1019:
1020:
1021: if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) &&
1022: !empty($options['flat'])) {
1023: return $subscribed;
1024: }
1025: } else {
1026: $subscribed = null;
1027: }
1028:
1029: return $this->_getMailboxList($pattern, $mode, $options, $subscribed);
1030: }
1031:
1032: 1033: 1034: 1035: 1036: 1037: 1038: 1039: 1040: 1041: 1042: 1043: 1044:
1045: protected function _getMailboxList($pattern, $mode, $options,
1046: $subscribed = null)
1047: {
1048: $check = (($mode != Horde_Imap_Client::MBOX_ALL) && !is_null($subscribed));
1049:
1050:
1051: $t = &$this->_temp;
1052: $t['mailboxlist'] = array(
1053: 'check' => $check,
1054: 'ext' => false,
1055: 'options' => $options,
1056: 'subexist' => ($mode == Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS),
1057: 'subscribed' => ($check ? array_flip($subscribed) : null)
1058: );
1059: $t['listresponse'] = array();
1060: $return_opts = array();
1061:
1062: if ($this->queryCapability('LIST-EXTENDED') &&
1063: empty($options['no_listext'])) {
1064: $cmd = array('LIST');
1065: $t['mailboxlist']['ext'] = true;
1066:
1067: $select_opts = array();
1068:
1069: if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) ||
1070: ($mode == Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS)) {
1071: $select_opts[] = 'SUBSCRIBED';
1072: $return_opts[] = 'SUBSCRIBED';
1073: }
1074:
1075: if (!empty($options['remote'])) {
1076: $select_opts[] = 'REMOTE';
1077: }
1078:
1079: if (!empty($options['recursivematch'])) {
1080: $select_opts[] = 'RECURSIVEMATCH';
1081: }
1082:
1083: if (!empty($select_opts)) {
1084: $cmd[] = $select_opts;
1085: }
1086:
1087: $cmd[] = '""';
1088:
1089: if (!is_array($pattern)) {
1090: $pattern = array($pattern);
1091: }
1092: $tmp = array();
1093: foreach ($pattern as $val) {
1094: $tmp[] = array('t' => Horde_Imap_Client::DATA_LISTMAILBOX, 'v' => $val);
1095: }
1096: $cmd[] = $tmp;
1097:
1098: if (!empty($options['children'])) {
1099: $return_opts[] = 'CHILDREN';
1100: }
1101:
1102: if (!empty($options['special_use'])) {
1103: $return_opts[] = 'SPECIAL-USE';
1104: }
1105: } else {
1106: if (is_array($pattern)) {
1107: $return_array = array();
1108: foreach ($pattern as $val) {
1109: $return_array = array_merge($return_array, $this->_getMailboxList($val, $mode, $options, $subscribed));
1110: }
1111: return $return_array;
1112: }
1113:
1114: $cmd = array(
1115: (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) ? 'LSUB' : 'LIST'),
1116: '""',
1117: array('t' => Horde_Imap_Client::DATA_LISTMAILBOX, 'v' => $pattern)
1118: );
1119: }
1120:
1121:
1122: if (!empty($options['status']) &&
1123: $this->queryCapability('LIST-STATUS')) {
1124: $status_mask = array(
1125: Horde_Imap_Client::STATUS_MESSAGES => 'MESSAGES',
1126: Horde_Imap_Client::STATUS_RECENT => 'RECENT',
1127: Horde_Imap_Client::STATUS_UIDNEXT => 'UIDNEXT',
1128: Horde_Imap_Client::STATUS_UIDVALIDITY => 'UIDVALIDITY',
1129: Horde_Imap_Client::STATUS_UNSEEN => 'UNSEEN',
1130: Horde_Imap_Client::STATUS_HIGHESTMODSEQ => 'HIGHESTMODSEQ'
1131: );
1132:
1133: $status_opts = array();
1134: foreach ($status_mask as $key => $val) {
1135: if ($options['status'] & $key) {
1136: $status_opts[] = $val;
1137: }
1138: }
1139:
1140: if (!empty($status_opts)) {
1141: $return_opts[] = 'STATUS';
1142: $return_opts[] = $status_opts;
1143: }
1144: }
1145:
1146: if (!empty($return_opts)) {
1147: $cmd[] = 'RETURN';
1148: $cmd[] = $return_opts;
1149: }
1150:
1151: $this->_sendLine($cmd);
1152:
1153: if (!empty($options['flat'])) {
1154: return array_values($t['listresponse']);
1155: }
1156:
1157:
1158: if (!empty($options['status'])) {
1159: if (!is_array($pattern)) {
1160: $pattern = array($pattern);
1161: }
1162:
1163: foreach ($pattern as $val) {
1164: $val_utf8 = Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($val);
1165: if (isset($t['listresponse'][$val_utf8]) &&
1166: isset($t['status'][$val_utf8])) {
1167: $t['listresponse'][$val_utf8]['status'] = $t['status'][$val_utf8];
1168: }
1169: }
1170: }
1171:
1172: return $t['listresponse'];
1173: }
1174:
1175: 1176: 1177: 1178: 1179: 1180: 1181: 1182:
1183: protected function _parseList($data)
1184: {
1185: $ml = $this->_temp['mailboxlist'];
1186: $mlo = $ml['options'];
1187: $lr = &$this->_temp['listresponse'];
1188:
1189: $mode = strtoupper($data[0]);
1190: $mbox = $data[3];
1191:
1192: if ($ml['check'] &&
1193: $ml['subexist'] &&
1194: !isset($ml['subscribed'][$mbox])) {
1195: return;
1196: } elseif ((!$ml['check'] && $ml['subexist']) ||
1197: (empty($mlo['flat']) && !empty($mlo['attributes']))) {
1198: $attr = array_flip(array_map('strtolower', $data[1]));
1199: if ($ml['subexist'] &&
1200: !$ml['check'] &&
1201: isset($attr['\\nonexistent'])) {
1202: return;
1203: }
1204: }
1205:
1206: if (!empty($mlo['utf8'])) {
1207: $mbox = Horde_Imap_Client_Mailbox::get($mbox, true);
1208: }
1209:
1210: if (empty($mlo['flat'])) {
1211: $tmp = array(
1212: 'mailbox' => $mbox
1213: );
1214:
1215: if (!empty($mlo['attributes'])) {
1216:
1217: if ($ml['ext']) {
1218: if (isset($attr['\\noinferiors'])) {
1219: $attr['\\hasnochildren'] = 1;
1220: }
1221: if (isset($attr['\\nonexistent'])) {
1222: $attr['\\noselect'] = 1;
1223: }
1224: }
1225: $tmp['attributes'] = array_keys($attr);
1226: }
1227: if (!empty($mlo['delimiter'])) {
1228: $tmp['delimiter'] = $data[2];
1229: }
1230: if (isset($data[4])) {
1231: $tmp['extended'] = $data[4];
1232: }
1233: $lr[strval($mbox)] = $tmp;
1234: } else {
1235: $lr[] = $mbox;
1236: }
1237: }
1238:
1239: 1240:
1241: protected function _status(Horde_Imap_Client_Mailbox $mailbox, $flags)
1242: {
1243: $data = $query = array();
1244: $search = null;
1245:
1246: $items = array(
1247: Horde_Imap_Client::STATUS_MESSAGES => 'messages',
1248: Horde_Imap_Client::STATUS_RECENT => 'recent',
1249: Horde_Imap_Client::STATUS_UIDNEXT => 'uidnext',
1250: Horde_Imap_Client::STATUS_UIDVALIDITY => 'uidvalidity',
1251: Horde_Imap_Client::STATUS_UNSEEN => 'unseen',
1252: Horde_Imap_Client::STATUS_FIRSTUNSEEN => 'firstunseen',
1253: Horde_Imap_Client::STATUS_FLAGS => 'flags',
1254: Horde_Imap_Client::STATUS_PERMFLAGS => 'permflags',
1255: Horde_Imap_Client::STATUS_UIDNOTSTICKY => 'uidnotsticky',
1256: );
1257:
1258: 1259: 1260:
1261: if ($this->queryCapability('CONDSTORE')) {
1262: $items[Horde_Imap_Client::STATUS_HIGHESTMODSEQ] = 'highestmodseq';
1263: }
1264:
1265: 1266: 1267:
1268: if (($flags & Horde_Imap_Client::STATUS_FIRSTUNSEEN) ||
1269: ($flags & Horde_Imap_Client::STATUS_FLAGS) ||
1270: ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) ||
1271: ($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY)) {
1272: $this->openMailbox($mailbox);
1273: }
1274:
1275: foreach ($items as $key => $val) {
1276: if ($key & $flags) {
1277: if ($mailbox->equals($this->_selected)) {
1278: if (isset($this->_temp['mailbox'][$val])) {
1279: $data[$val] = $this->_temp['mailbox'][$val];
1280: } elseif ($key == Horde_Imap_Client::STATUS_UIDNEXT) {
1281: 1282:
1283: $data[$val] = 0;
1284: } elseif ($key == Horde_Imap_Client::STATUS_UIDNOTSTICKY) {
1285: 1286: 1287:
1288: $data[$val] = false;
1289: } elseif ($key == Horde_Imap_Client::STATUS_PERMFLAGS) {
1290: 1291: 1292:
1293: $data[$val] = isset($this->_temp['mailbox'][$items[Horde_Imap_Client::STATUS_FLAGS]])
1294: ? $this->_temp['mailbox'][$items[Horde_Imap_Client::STATUS_FLAGS]]
1295: : array();
1296: $data[$val][] = "\\*";
1297: } elseif (in_array($key, array(Horde_Imap_Client::STATUS_FIRSTUNSEEN, Horde_Imap_Client::STATUS_UNSEEN))) {
1298: 1299: 1300:
1301: if (empty($this->_temp['mailbox']['messages'])) {
1302: $data[$val] = ($key == Horde_Imap_Client::STATUS_FIRSTUNSEEN) ? null : 0;
1303: } else {
1304: 1305: 1306: 1307:
1308: if (is_null($search)) {
1309: $search_query = new Horde_Imap_Client_Search_Query();
1310: $search_query->flag(Horde_Imap_Client::FLAG_SEEN, false);
1311: $search = $this->search($mailbox, $search_query, array('results' => array(($key == Horde_Imap_Client::STATUS_FIRSTUNSEEN) ? Horde_Imap_Client::SEARCH_RESULTS_MIN : Horde_Imap_Client::SEARCH_RESULTS_COUNT), 'sequence' => true));
1312: }
1313:
1314: $data[$val] = $search[($key == Horde_Imap_Client::STATUS_FIRSTUNSEEN) ? 'min' : 'count'];
1315: }
1316: }
1317: } else {
1318: $query[] = $val;
1319: }
1320: }
1321: }
1322:
1323: if (empty($query)) {
1324: return $data;
1325: }
1326:
1327: $this->_sendLine(array(
1328: 'STATUS',
1329: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap),
1330: array_map('strtoupper', $query)
1331: ));
1332:
1333: return $this->_temp['status'][strval($mailbox)];
1334: }
1335:
1336: 1337: 1338: 1339: 1340: 1341:
1342: protected function _parseStatus($mailbox, $data)
1343: {
1344: $mailbox = Horde_Imap_Client_Mailbox::get($mailbox, true);
1345:
1346: $this->_temp['status'][strval($mailbox)] = array();
1347:
1348: for ($i = 0; isset($data[$i]); $i += 2) {
1349: $item = strtolower($data[$i]);
1350: $this->_temp['status'][strval($mailbox)][$item] = $data[$i + 1];
1351: }
1352: }
1353:
1354: 1355:
1356: protected function _append(Horde_Imap_Client_Mailbox $mailbox, $data,
1357: $options)
1358: {
1359:
1360: if ((count($data) > 1) && !$this->queryCapability('MULTIAPPEND')) {
1361: $result = $this->getIdsOb();
1362: foreach (array_keys($data) as $key) {
1363: $res = $this->_append($mailbox, array($data[$key]), $options);
1364: if (($res === true) || ($result === true)) {
1365: $result = true;
1366: } else {
1367: $result->add($res);
1368: }
1369: }
1370: return $result;
1371: }
1372:
1373:
1374:
1375: $this->close();
1376:
1377:
1378: $catenate = $this->queryCapability('CATENATE');
1379:
1380: $t = &$this->_temp;
1381: $t['appenduid'] = array();
1382: $t['trycreate'] = null;
1383: $t['uidplusmbox'] = $mailbox;
1384:
1385: $cmd = array(
1386: 'APPEND',
1387: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap)
1388: );
1389:
1390: foreach (array_keys($data) as $key) {
1391: if (!empty($data[$key]['flags'])) {
1392: $tmp = array();
1393: foreach ($data[$key]['flags'] as $val) {
1394:
1395: if (strcasecmp($val, Horde_Imap_Client::FLAG_RECENT) !== 0) {
1396: $tmp[] = array('t' => Horde_Imap_Client::DATA_ATOM, 'v' => $val);
1397: }
1398: }
1399: $cmd[] = $tmp;
1400: }
1401:
1402: if (!empty($data[$key]['internaldate'])) {
1403: $cmd[] = array(
1404: 't' => Horde_Imap_Client::DATA_DATETIME,
1405: 'v' => $data[$key]['internaldate']->format('j-M-Y H:i:s O')
1406: );
1407: }
1408:
1409: if (is_array($data[$key]['data'])) {
1410: if ($catenate) {
1411: $cmd[] = 'CATENATE';
1412:
1413: $tmp = array();
1414: foreach (array_keys($data[$key]['data']) as $key2) {
1415: switch ($data[$key]['data'][$key2]['t']) {
1416: case 'text':
1417: $tmp[] = 'TEXT';
1418: $tmp[] = $this->_prepareAppendData($data[$key]['data'][$key2]['v']);
1419: break;
1420:
1421: case 'url':
1422: $tmp[] = 'URL';
1423: $tmp[] = $data[$key]['data'][$key2]['v'];
1424: break;
1425: }
1426: }
1427: $cmd[] = $tmp;
1428: } else {
1429: $cmd[] = $this->_buildCatenateData($data[$key]['data']);
1430: }
1431: } else {
1432: $cmd[] = $this->_prepareAppendData($data[$key]['data']);
1433: }
1434: }
1435:
1436: try {
1437: $this->_sendLine($cmd);
1438: } catch (Horde_Imap_Client_Exception $e) {
1439: switch ($e->getCode()) {
1440: case Horde_Imap_Client_Exception::CATENATE_BADURL:
1441: case Horde_Imap_Client_Exception::CATENATE_TOOBIG:
1442: 1443: 1444:
1445: $cap = $this->capability();
1446: unset($cap['CATENATE']);
1447: $this->_setInit('capability', $cap);
1448:
1449: return $this->_append($mailbox, $data, $options);
1450: }
1451:
1452: if (!empty($options['create']) && $this->_temp['trycreate']) {
1453: $this->createMailbox($mailbox);
1454: unset($options['create']);
1455: return $this->_append($mailbox, $data, $options);
1456: }
1457:
1458: throw $e;
1459: }
1460:
1461: 1462:
1463: return empty($t['appenduid'])
1464: ? true
1465: : $this->getIdsOb($t['appenduid']);
1466: }
1467:
1468: 1469:
1470: protected function _check()
1471: {
1472:
1473: $this->_sendLine('CHECK');
1474: }
1475:
1476: 1477:
1478: protected function _close($options)
1479: {
1480: if (empty($options['expunge'])) {
1481: if ($this->queryCapability('UNSELECT')) {
1482:
1483: $this->_sendLine('UNSELECT');
1484: } else {
1485:
1486:
1487:
1488: $this->_sendLine('SELECT ""', array('errignore' => true));
1489: }
1490: } else {
1491:
1492:
1493: if ($this->_initCache(true)) {
1494: $this->expunge($this->_selected);
1495: }
1496:
1497:
1498: $this->_sendLine('CLOSE');
1499:
1500: 1501:
1502: }
1503:
1504:
1505: $this->_temp['mailbox'] = array();
1506: }
1507:
1508: 1509:
1510: protected function _expunge($options)
1511: {
1512: $unflag = array();
1513: $mailbox = clone($this->_selected);
1514: $s_res = null;
1515: $uidplus = $this->queryCapability('UIDPLUS');
1516: $use_cache = $this->_initCache(true);
1517:
1518: if ($options['ids']->all) {
1519: $uid_string = '1:*';
1520: } elseif ($uidplus) {
1521:
1522: if ($options['ids']->search_res) {
1523: $uid_string = '$';
1524: } elseif ($options['ids']->sequence) {
1525: $results = array(Horde_Imap_Client::SEARCH_RESULTS_MATCH);
1526: if ($this->queryCapability('SEARCHRES')) {
1527: $results[] = Horde_Imap_Client::SEARCH_RESULTS_SAVE;
1528: }
1529: $s_res = $this->search($mailbox, null, array(
1530: 'results' => $results
1531: ));
1532: $uid_string = (in_array(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $results) && !empty($s_res['save']))
1533: ? '$'
1534: : strval($s_res['match']);
1535: } else {
1536: $uid_string = strval($options['ids']);
1537: }
1538: } else {
1539: 1540: 1541:
1542: $search_query = new Horde_Imap_Client_Search_Query();
1543: $search_query->flag(Horde_Imap_Client::FLAG_DELETED, true);
1544: if ($options['ids']->search_res) {
1545: $search_query->previousSearch(true);
1546: } else {
1547: $search_query->ids($options['ids'], true);
1548: }
1549:
1550: $res = $this->search($mailbox, $search_query);
1551:
1552: $this->store($mailbox, array(
1553: 'ids' => $res['match'],
1554: 'remove' => array(Horde_Imap_Client::FLAG_DELETED)
1555: ));
1556:
1557: $unflag = $res['match'];
1558: }
1559:
1560: $list_msgs = !empty($options['list']);
1561: $tmp = &$this->_temp;
1562: $tmp['expunge'] = $tmp['vanished'] = array();
1563:
1564: 1565: 1566:
1567: if (is_null($s_res) && ($list_msgs || $use_cache)) {
1568: $s_res = $uidplus
1569: ? $this->_getSeqUidLookup($options['ids'], true)
1570: : $this->_getSeqUidLookup($this->getIdsOb(Horde_Imap_Client_Ids::ALL, true));
1571: }
1572:
1573:
1574: if ($uidplus) {
1575: $this->_sendLine(array(
1576: 'UID',
1577: 'EXPUNGE',
1578: $uid_string
1579: ));
1580: } elseif ($use_cache || $list_msgs) {
1581: $this->_sendLine('EXPUNGE');
1582: } else {
1583: 1584: 1585:
1586: $this->close(array('expunge' => true));
1587: }
1588:
1589: if (!empty($unflag)) {
1590: $this->store($mailbox, array(
1591: 'add' => array(Horde_Imap_Client::FLAG_DELETED),
1592: 'ids' => $unflag
1593: ));
1594: }
1595:
1596: if (!$use_cache && !$list_msgs) {
1597: return null;
1598: }
1599:
1600: $expunged = array();
1601:
1602: if (!empty($tmp['vanished'])) {
1603: $expunged = $tmp['vanished'];
1604: } elseif (!empty($tmp['expunge'])) {
1605: $lookup = $s_res['lookup'];
1606:
1607: 1608: 1609: 1610:
1611: foreach ($tmp['expunge'] as &$val) {
1612: $found = false;
1613: $tmp2 = array();
1614:
1615: foreach (array_keys($lookup) as $i => $seq) {
1616: if ($found) {
1617: $tmp2[$seq - 1] = $lookup[$seq];
1618: } elseif ($seq == $val) {
1619: $expunged[] = $lookup[$seq];
1620: $tmp2 = array_slice($lookup, 0, $i, true);
1621: $found = true;
1622: }
1623: }
1624:
1625: $lookup = $tmp2;
1626: }
1627: }
1628:
1629: if (empty($expunged)) {
1630: return null;
1631: }
1632:
1633: if ($use_cache) {
1634: $this->_deleteMsgs($mailbox, $expunged);
1635: }
1636:
1637:
1638: if (!empty($this->_temp['mailbox']['highestmodseq'])) {
1639: if (isset($this->_init['enabled']['QRESYNC'])) {
1640: $this->_updateMetaData($mailbox, array(
1641: self::CACHE_MODSEQ => $this->_temp['mailbox']['highestmodseq']
1642: ), isset($this->_temp['mailbox']['uidvalidity']) ? $this->_temp['mailbox']['uidvalidity'] : null);
1643: } else {
1644: 1645: 1646: 1647: 1648:
1649: $this->close();
1650: }
1651: }
1652:
1653: return $list_msgs
1654: ? $this->getIdsOb($expunged, $options['ids']->sequence)
1655: : null;
1656: }
1657:
1658: 1659: 1660: 1661: 1662:
1663: protected function _parseExpunge($seq)
1664: {
1665: $this->_temp['expunge'][] = $seq;
1666:
1667: 1668: 1669:
1670: --$this->_temp['mailbox']['messages'];
1671: $this->_temp['mailbox']['lookup'] = array();
1672: }
1673:
1674: 1675: 1676: 1677: 1678:
1679: protected function _parseVanished($data)
1680: {
1681: $vanished = array();
1682:
1683: 1684: 1685: 1686: 1687: 1688:
1689: if (is_array($data[0])) {
1690: if (strtoupper(reset($data[0])) == 'EARLIER') {
1691: 1692:
1693: $vanished = $this->utils->fromSequenceString($data[1]);
1694: $this->_deleteMsgs($this->_temp['mailbox']['name'], $vanished);
1695:
1696: if (!empty($this->_temp['fetch_vanished'])) {
1697: $this->_temp['fetch_vanished_res'] = $this->_newFetchResult();
1698: foreach ($vanished as $val) {
1699: $ob = new $this->_fetchDataClass();
1700: $ob->setUid($val);
1701: $this->_temp['fetch_vanished_res']->uid[$val] = $this->_temp['fetchresp']->uid[$val] = $ob;
1702: }
1703: }
1704: }
1705: } else {
1706: 1707:
1708: $vanished = $this->utils->fromSequenceString($data[0]);
1709: $this->_temp['mailbox']['messages'] -= count($vanished);
1710: $this->_temp['mailbox']['lookup'] = array();
1711: }
1712:
1713: $this->_temp['vanished'] = $vanished;
1714: }
1715:
1716: 1717: 1718: 1719:
1720: protected function _search($query, $options)
1721: {
1722: 1723: 1724:
1725: if (in_array('CONDSTORE', $options['_query']['exts']) &&
1726: empty($this->_temp['mailbox']['highestmodseq'])) {
1727: $this->_exception(Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."), 'MBOXNOMODSEQ');
1728: }
1729:
1730: $cmd = array();
1731: if (empty($options['sequence'])) {
1732: $cmd[] = 'UID';
1733: }
1734:
1735: $sort_criteria = array(
1736: Horde_Imap_Client::SORT_ARRIVAL => 'ARRIVAL',
1737: Horde_Imap_Client::SORT_CC => 'CC',
1738: Horde_Imap_Client::SORT_DATE => 'DATE',
1739: Horde_Imap_Client::SORT_DISPLAYFROM => 'DISPLAYFROM',
1740: Horde_Imap_Client::SORT_DISPLAYTO => 'DISPLAYTO',
1741: Horde_Imap_Client::SORT_FROM => 'FROM',
1742: Horde_Imap_Client::SORT_REVERSE => 'REVERSE',
1743: Horde_Imap_Client::SORT_RELEVANCY => 'RELEVANCY',
1744:
1745:
1746: Horde_Imap_Client::SORT_SEQUENCE => 'SEQUENCE',
1747: Horde_Imap_Client::SORT_SIZE => 'SIZE',
1748: Horde_Imap_Client::SORT_SUBJECT => 'SUBJECT',
1749: Horde_Imap_Client::SORT_TO => 'TO'
1750: );
1751:
1752: $results_criteria = array(
1753: Horde_Imap_Client::SEARCH_RESULTS_COUNT => 'COUNT',
1754: Horde_Imap_Client::SEARCH_RESULTS_MATCH => 'ALL',
1755: Horde_Imap_Client::SEARCH_RESULTS_MAX => 'MAX',
1756: Horde_Imap_Client::SEARCH_RESULTS_MIN => 'MIN',
1757: Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY => 'RELEVANCY',
1758: Horde_Imap_Client::SEARCH_RESULTS_SAVE => 'SAVE'
1759: );
1760:
1761:
1762: $esearch = $return_sort = $server_seq_sort = $server_sort = false;
1763: if (!empty($options['sort'])) {
1764: 1765:
1766: if (count(array_intersect($options['sort'], array_keys($sort_criteria))) === 0) {
1767: unset($options['sort']);
1768: } else {
1769: $return_sort = true;
1770:
1771: if ($server_sort = $this->queryCapability('SORT')) {
1772:
1773: $server_sort =
1774: !array_intersect($options['sort'], array(Horde_Imap_Client::SORT_DISPLAYFROM, Horde_Imap_Client::SORT_DISPLAYTO)) ||
1775: (is_array($server_sort) &&
1776: in_array('DISPLAY', $server_sort));
1777: }
1778:
1779: 1780:
1781: if ($server_sort &&
1782: in_array(Horde_Imap_Client::SORT_SEQUENCE, $options['sort'])) {
1783: $server_sort = false;
1784:
1785: 1786:
1787: switch (count($options['sort'])) {
1788: case 1:
1789: $server_seq_sort = true;
1790: break;
1791:
1792: case 2:
1793: $server_seq_sort = (reset($options['sort']) == Horde_Imap_Client::SORT_REVERSE);
1794: break;
1795: }
1796: }
1797: }
1798: }
1799:
1800: $charset = is_null($options['_query']['charset'])
1801: ? 'US-ASCII'
1802: : $options['_query']['charset'];
1803:
1804: if ($server_sort) {
1805: $cmd[] = 'SORT';
1806: $results = array();
1807:
1808:
1809: $esearch = false;
1810:
1811:
1812: if ($this->queryCapability('ESORT')) {
1813: foreach ($options['results'] as $val) {
1814: if (isset($results_criteria[$val]) &&
1815: ($val != Horde_Imap_Client::SEARCH_RESULTS_SAVE)) {
1816: $results[] = $results_criteria[$val];
1817: }
1818: }
1819: $esearch = true;
1820: }
1821:
1822:
1823: if ((!$esearch || !empty($options['partial'])) &&
1824: ($cap = $this->queryCapability('CONTEXT')) &&
1825: in_array('SORT', $cap)) {
1826: 1827:
1828: $esearch = true;
1829:
1830: if (!empty($options['partial'])) {
1831:
1832: $results = array_diff($results, array('ALL'));
1833:
1834: $results[] = 'PARTIAL';
1835: $results[] = strval($this->getIdsOb($options['partial']));
1836: }
1837: }
1838:
1839: if ($esearch && empty($this->_init['noesearch'])) {
1840: $cmd[] = 'RETURN';
1841: $cmd[] = $results;
1842: }
1843:
1844: $tmp = array();
1845: foreach ($options['sort'] as $val) {
1846: if (isset($sort_criteria[$val])) {
1847: $tmp[] = $sort_criteria[$val];
1848: }
1849: }
1850: $cmd[] = $tmp;
1851:
1852:
1853: $cmd[] = $charset;
1854: } else {
1855: $esearch = false;
1856: $results = array();
1857:
1858: $cmd[] = 'SEARCH';
1859:
1860:
1861: if ($this->queryCapability('ESEARCH')) {
1862: foreach ($options['results'] as $val) {
1863: if (isset($results_criteria[$val])) {
1864: $results[] = $results_criteria[$val];
1865: }
1866: }
1867: $esearch = true;
1868: }
1869:
1870:
1871: if ((!$esearch || !empty($options['partial'])) &&
1872: ($cap = $this->queryCapability('CONTEXT')) &&
1873: in_array('SEARCH', $cap)) {
1874: 1875:
1876: $esearch = true;
1877:
1878: if (!empty($options['partial'])) {
1879:
1880: $results = array_diff($results, array('ALL'));
1881:
1882: $results[] = 'PARTIAL';
1883: $results[] = strval($this->getIdsOb($options['partial']));
1884: }
1885: }
1886:
1887: if ($esearch && empty($this->_init['noesearch'])) {
1888:
1889:
1890: $cmd[] = 'RETURN';
1891: $cmd[] = $results;
1892: }
1893:
1894:
1895: if ($charset != 'US-ASCII') {
1896: $cmd[] = 'CHARSET';
1897: $cmd[] = $options['_query']['charset'];
1898: }
1899:
1900:
1901: unset($this->_temp['searchnotsaved']);
1902: }
1903:
1904: $er = &$this->_temp['esearchresp'];
1905: $sr = &$this->_temp['searchresp'];
1906: $er = $sr = array();
1907:
1908: $cmd = array_merge($cmd, $options['_query']['query']);
1909:
1910: try {
1911: $this->_sendLine($cmd);
1912: } catch (Horde_Imap_Client_Exception $e) {
1913: if (isset($this->_temp['parseresperr']['response']) &&
1914: ($this->_temp['parseresperr']['response'] == 'NO')) {
1915: 1916: 1917: 1918: 1919:
1920: $e->setCode(Horde_Imap_Client_Exception::BADCHARSET);
1921: } elseif (empty($this->_temp['search_retry'])) {
1922: $this->_temp['search_retry'] = true;
1923:
1924: 1925:
1926: if ($esearch && ($charset != 'US-ASCII')) {
1927: $cap = $this->capability();
1928: unset($cap['ESEARCH']);
1929: $this->_setInit('capability', $cap);
1930: $this->_setInit('noesearch', true);
1931:
1932: try {
1933: return $this->_search($query, $options);
1934: } catch (Horde_Imap_Client_Exception $e) {}
1935: }
1936:
1937:
1938: foreach ($this->_init['s_charset'] as $key => $val) {
1939: $this->_temp['search_retry'] = 1;
1940: if ($val && ($key != $charset)) {
1941: $new_query = clone($query);
1942: $options['_query'] = $new_query->build($this->capability());
1943: try {
1944: return $this->_search($new_query, $options);
1945: } catch (Horde_Imap_Client_Exception $e) {}
1946: }
1947: }
1948:
1949: unset($this->_temp['search_retry']);
1950: }
1951:
1952: throw $e;
1953: }
1954:
1955: if ($return_sort && !$server_sort) {
1956: if ($server_seq_sort) {
1957: sort($sr, SORT_NUMERIC);
1958: if (reset($options['sort']) == Horde_Imap_Client::SORT_REVERSE) {
1959: $sr = array_reverse($sr);
1960: }
1961: } else {
1962: $sr = array_values($this->_clientSort($sr, $options));
1963: }
1964: }
1965:
1966: $ret = array();
1967: foreach ($options['results'] as $val) {
1968: switch ($val) {
1969: case Horde_Imap_Client::SEARCH_RESULTS_COUNT:
1970: $ret['count'] = $esearch ? $er['count'] : count($sr);
1971: break;
1972:
1973: case Horde_Imap_Client::SEARCH_RESULTS_MATCH:
1974: $ret['match'] = $this->getIdsOb($sr, !empty($options['sequence']));
1975: break;
1976:
1977: case Horde_Imap_Client::SEARCH_RESULTS_MAX:
1978: $ret['max'] = $esearch ? (isset($er['max']) ? $er['max'] : null) : (empty($sr) ? null : max($sr));
1979: break;
1980:
1981: case Horde_Imap_Client::SEARCH_RESULTS_MIN:
1982: $ret['min'] = $esearch ? (isset($er['min']) ? $er['min'] : null) : (empty($sr) ? null : min($sr));
1983: break;
1984:
1985: case Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY:
1986: $ret['relevancy'] = ($esearch && isset($er['relevancy'])) ? $er['relevancy'] : array();
1987: break;
1988:
1989: case Horde_Imap_Client::SEARCH_RESULTS_SAVE:
1990: $ret['save'] = $esearch ? empty($this->_temp['searchnotsaved']) : false;
1991: break;
1992: }
1993: }
1994:
1995:
1996: if (!empty($er['modseq'])) {
1997: $ret['modseq'] = $er['modseq'];
1998: }
1999:
2000: unset($this->_temp['search_retry']);
2001:
2002:
2003: if (!empty($this->_temp['expungeissued'])) {
2004: unset($this->_temp['expungeissued']);
2005: $this->noop();
2006: }
2007:
2008: return $ret;
2009: }
2010:
2011: 2012: 2013: 2014: 2015: 2016:
2017: protected function _parseSearch($data)
2018: {
2019:
2020: $this->_temp['searchresp'] = array_merge($this->_temp['searchresp'], $data);
2021: }
2022:
2023: 2024: 2025: 2026: 2027: 2028:
2029: protected function _parseEsearch($data)
2030: {
2031: $i = 0;
2032: $len = count($data);
2033:
2034:
2035: if (is_array($data[$i])) {
2036: ++$i;
2037: }
2038:
2039:
2040: if (($i != $len) && (strtoupper($data[$i]) == 'UID')) {
2041: ++$i;
2042: }
2043:
2044:
2045: if ($i == $len) {
2046: return;
2047: }
2048:
2049: for (; $i < $len; $i += 2) {
2050: $val = $data[$i + 1];
2051: $tag = strtoupper($data[$i]);
2052: switch ($tag) {
2053: case 'ALL':
2054: $this->_parseSearch($this->utils->fromSequenceString($val));
2055: break;
2056:
2057: case 'COUNT':
2058: case 'MAX':
2059: case 'MIN':
2060: case 'MODSEQ':
2061: case 'RELEVANCY':
2062: $this->_temp['esearchresp'][strtolower($tag)] = $val;
2063: break;
2064:
2065: case 'PARTIAL':
2066: $this->_parseSearch($this->utils->fromSequenceString(end($val)));
2067: break;
2068: }
2069: }
2070: }
2071:
2072: 2073: 2074: 2075: 2076: 2077: 2078: 2079: 2080: 2081: 2082: 2083:
2084: protected function _clientSort($res, $opts)
2085: {
2086: if (empty($res)) {
2087: return $res;
2088: }
2089:
2090:
2091: if (empty($opts['fetch_res'])) {
2092: $query = new Horde_Imap_Client_Fetch_Query();
2093:
2094: foreach ($opts['sort'] as $val) {
2095: switch ($val) {
2096: case Horde_Imap_Client::SORT_ARRIVAL:
2097: $query->imapDate();
2098: break;
2099:
2100: case Horde_Imap_Client::SORT_DATE:
2101: $query->imapDate();
2102: $query->envelope();
2103: break;
2104:
2105: case Horde_Imap_Client::SORT_CC:
2106: case Horde_Imap_Client::SORT_DISPLAYFROM:
2107: case Horde_Imap_Client::SORT_DISPLAYTO:
2108: case Horde_Imap_Client::SORT_FROM:
2109: case Horde_Imap_Client::SORT_SUBJECT:
2110: case Horde_Imap_Client::SORT_TO:
2111: $query->envelope();
2112: break;
2113:
2114: case Horde_Imap_Client::SORT_SIZE:
2115: $query->size();
2116: break;
2117: }
2118: }
2119:
2120:
2121: if (count($query)) {
2122: $fetch_res = $this->fetch($this->_selected, $query, array(
2123: 'ids' => $this->getIdsOb($res, !empty($opts['sequence']))
2124: ));
2125: }
2126: } else {
2127: $fetch_res = $opts['fetch_res'];
2128: }
2129:
2130:
2131: $slices = array(0 => $res);
2132:
2133: $reverse = false;
2134: foreach ($opts['sort'] as $val) {
2135: if ($val == Horde_Imap_Client::SORT_REVERSE) {
2136: $reverse = true;
2137: continue;
2138: }
2139:
2140: $slices_list = $slices;
2141: $slices = array();
2142:
2143: foreach ($slices_list as $slice_start => $slice) {
2144: $sorted = array();
2145:
2146: if ($reverse) {
2147: $slice = array_reverse($slice);
2148: }
2149:
2150: switch ($val) {
2151: case Horde_Imap_Client::SORT_SEQUENCE:
2152: 2153: 2154:
2155: $sorted = array_flip($slice);
2156: ksort($sorted, SORT_NUMERIC);
2157: break;
2158:
2159: case Horde_Imap_Client::SORT_SIZE:
2160: foreach ($slice as $num) {
2161: $sorted[$num] = $fetch_res[$num]->getSize();
2162: }
2163: asort($sorted, SORT_NUMERIC);
2164: break;
2165:
2166: case Horde_Imap_Client::SORT_DISPLAYFROM:
2167: case Horde_Imap_Client::SORT_DISPLAYTO:
2168: $field = ($val == Horde_Imap_Client::SORT_DISPLAYFROM)
2169: ? 'from'
2170: : 'to';
2171:
2172: foreach ($slice as $num) {
2173: $env = $fetch_res[$num]->getEnvelope();
2174:
2175: if (empty($env->$field)) {
2176: $sorted[$num] = null;
2177: } else {
2178: $addr_ob = reset($env->$field);
2179: $sorted[$num] = empty($addr_ob['personal'])
2180: ? $addr_ob['mailbox']
2181: : $addr_ob['personal'];
2182: }
2183: }
2184:
2185: asort($sorted, SORT_LOCALE_STRING);
2186: break;
2187:
2188: case Horde_Imap_Client::SORT_CC:
2189: case Horde_Imap_Client::SORT_FROM:
2190: case Horde_Imap_Client::SORT_TO:
2191: if ($val == Horde_Imap_Client::SORT_CC) {
2192: $field = 'cc';
2193: } elseif ($val == Horde_Imap_Client::SORT_FROM) {
2194: $field = 'from';
2195: } else {
2196: $field = 'to';
2197: }
2198:
2199: foreach ($slice as $num) {
2200: $tmp = $fetch_res[$num]->getEnvelope()->$field;
2201: $sorted[$num] = empty($tmp)
2202: ? null
2203: : $tmp[0]['mailbox'];
2204: }
2205: asort($sorted, SORT_LOCALE_STRING);
2206: break;
2207:
2208: case Horde_Imap_Client::SORT_ARRIVAL:
2209: $sorted = $this->_getSentDates($fetch_res, $slice, true);
2210: asort($sorted, SORT_NUMERIC);
2211: break;
2212:
2213: case Horde_Imap_Client::SORT_DATE:
2214:
2215: $sorted = $this->_getSentDates($fetch_res, $slice);
2216: asort($sorted, SORT_NUMERIC);
2217: break;
2218:
2219: case Horde_Imap_Client::SORT_SUBJECT:
2220:
2221: foreach ($slice as $num) {
2222: $sorted[$num] = $this->utils->getBaseSubject($fetch_res[$num]->getEnvelope()->subject);
2223: }
2224: asort($sorted, SORT_LOCALE_STRING);
2225: break;
2226: }
2227:
2228:
2229:
2230: if (!empty($sorted)) {
2231: if (count($sorted) == count($res)) {
2232: $res = array_keys($sorted);
2233: } else {
2234: array_splice($res, $slice_start, count($slice), array_keys($sorted));
2235: }
2236:
2237:
2238: $last = $start = null;
2239: $i = 0;
2240: reset($sorted);
2241: while (list($k, $v) = each($sorted)) {
2242: if (is_null($last) || ($last != $v)) {
2243: if ($i) {
2244: $slices[array_search($start, $res)] = array_slice($sorted, array_search($start, $sorted), $i + 1);
2245: $i = 0;
2246: }
2247: $last = $v;
2248: $start = $k;
2249: } else {
2250: ++$i;
2251: }
2252: }
2253: if ($i) {
2254: $slices[array_search($start, $res)] = array_slice($sorted, array_search($start, $sorted), $i + 1);
2255: }
2256: }
2257: }
2258:
2259: $reverse = false;
2260: }
2261:
2262: return $res;
2263: }
2264:
2265: 2266: 2267: 2268: 2269: 2270: 2271: 2272: 2273: 2274: 2275:
2276: protected function _getSentDates($data, $ids, $internal = false)
2277: {
2278: $dates = array();
2279:
2280: foreach ($ids as $num) {
2281: $dt = ($internal || !isset($data[$num]->getEnvelope()->date))
2282:
2283:
2284: ? $data[$num]->getImapDate()
2285: : $data[$num]->getEnvelope()->date;
2286: $dates[$num] = $dt->format('U');
2287: }
2288:
2289: return $dates;
2290: }
2291:
2292: 2293:
2294: protected function _setComparator($comparator)
2295: {
2296: $cmd = array('COMPARATOR');
2297: foreach ($comparator as $val) {
2298: $cmd[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $val);
2299: }
2300: $this->_sendLine($cmd);
2301: }
2302:
2303: 2304:
2305: protected function _getComparator()
2306: {
2307: $this->_sendLine('COMPARATOR');
2308:
2309: return isset($this->_temp['comparator'])
2310: ? $this->_temp['comparator']
2311: : null;
2312: }
2313:
2314: 2315: 2316: 2317: 2318:
2319: protected function _parseComparator($data)
2320: {
2321: $this->_temp['comparator'] = $data;
2322: }
2323:
2324: 2325:
2326: protected function _thread($options)
2327: {
2328: $thread_criteria = array(
2329: Horde_Imap_Client::THREAD_ORDEREDSUBJECT => 'ORDEREDSUBJECT',
2330: Horde_Imap_Client::THREAD_REFERENCES => 'REFERENCES',
2331: Horde_Imap_Client::THREAD_REFS => 'REFS'
2332: );
2333:
2334: $tsort = (isset($options['criteria']))
2335: ? (is_string($options['criteria']) ? strtoupper($options['criteria']) : $thread_criteria[$options['criteria']])
2336: : 'ORDEREDSUBJECT';
2337:
2338: $cap = $this->queryCapability('THREAD');
2339: if (!$cap || !in_array($tsort, $cap)) {
2340: switch ($tsort) {
2341: case 'ORDEREDSUBJECT':
2342: if (empty($options['search'])) {
2343: $ids = $this->getIdsOb(Horde_Imap_Client_Ids::ALL, !empty($options['sequence']));
2344: } else {
2345: $search_res = $this->search($this->_selected, $options['search'], array('sequence' => !empty($options['sequence'])));
2346: $ids = $search_res['match'];
2347: }
2348:
2349:
2350: $query = new Horde_Imap_Client_Fetch_Query();
2351: $query->envelope();
2352: $query->imapDate();
2353:
2354: $fetch_res = $this->fetch($this->_selected, $query, array(
2355: 'ids' => $ids
2356: ));
2357: return $this->_clientThreadOrderedsubject($fetch_res);
2358:
2359: case 'REFERENCES':
2360: case 'REFS':
2361: $this->_exception(sprintf('Server does not support "%s" thread sort.', $tsort), 'NO_SUPPORT');
2362: }
2363: }
2364:
2365: $charset = 'US-ASCII';
2366: if (empty($options['search'])) {
2367: $search = array('ALL');
2368: } else {
2369: $search_query = $options['search']->build();
2370: if (!is_null($search_query['charset'])) {
2371: $charset = $search_query['charset'];
2372: }
2373: $search = $search_query['query'];
2374: }
2375:
2376: $this->_temp['threadparse'] = array('base' => null, 'resp' => array());
2377:
2378: $this->_sendLine(array_merge(array(
2379: (empty($options['sequence']) ? 'UID' : null),
2380: 'THREAD',
2381: $tsort,
2382: $charset
2383: ), $search));
2384:
2385: return $this->_temp['threadparse']['resp'];
2386: }
2387:
2388: 2389: 2390: 2391: 2392: 2393: 2394:
2395: protected function _parseThread($data, $level = 0, $islast = true)
2396: {
2397: $tp = &$this->_temp['threadparse'];
2398:
2399: if (!$level) {
2400: $tp['base'] = null;
2401: }
2402: $cnt = count($data) - 1;
2403:
2404: reset($data);
2405: while (list($key, $val) = each($data)) {
2406: if (is_array($val)) {
2407: $this->_parseThread($val, $level ? $level : 1, ($key == $cnt));
2408: } else {
2409: if (is_null($tp['base']) && ($level || $cnt)) {
2410: $tp['base'] = $val;
2411: }
2412:
2413: $tp['resp'][$val] = array();
2414: $ptr = &$tp['resp'][$val];
2415:
2416: if (!is_null($tp['base'])) {
2417: $ptr['b'] = $tp['base'];
2418: }
2419:
2420: if (!$islast) {
2421: $ptr['s'] = true;
2422: }
2423:
2424: if ($level++) {
2425: $ptr['l'] = $level - 1;
2426: }
2427: }
2428: $islast = true;
2429: }
2430: }
2431:
2432: 2433: 2434: 2435: 2436: 2437: 2438: 2439: 2440:
2441: protected function _clientThreadOrderedsubject($data)
2442: {
2443: $dates = $this->_getSentDates($data, array_keys($data));
2444: $level = $sorted = $tsort = array();
2445: $this->_temp['threadparse'] = array('base' => null, 'resp' => array());
2446:
2447: reset($data);
2448: while (list($k, $v) = each($data)) {
2449: $subject = $this->utils->getBaseSubject($v->getEnvelope()->subject);
2450: if (!isset($sorted[$subject])) {
2451: $sorted[$subject] = array();
2452: }
2453: $sorted[$subject][$k] = $dates[$k];
2454: }
2455:
2456: 2457:
2458: foreach (array_keys($sorted) as $key) {
2459: asort($sorted[$key], SORT_NUMERIC);
2460: $tsort[$key] = reset($sorted[$key]);
2461: }
2462:
2463: 2464:
2465: asort($tsort, SORT_NUMERIC);
2466:
2467: 2468:
2469: foreach (array_keys($tsort) as $key) {
2470: $keys = array_keys($sorted[$key]);
2471: $tmp = array($keys[0]);
2472: if (count($keys) > 1) {
2473: $tmp[] = array_slice($keys, 1);
2474: }
2475: $this->_parseThread($tmp);
2476: }
2477:
2478: return $this->_temp['threadparse']['resp'];
2479: }
2480:
2481: 2482:
2483: protected function _fetch($query, $results, $options)
2484: {
2485: $t = &$this->_temp;
2486: $t['fetchcmd'] = $t['vanished'] = array();
2487: $fetch = array();
2488:
2489: 2490: 2491: 2492: 2493: 2494: 2495: 2496: 2497: 2498: 2499: 2500: 2501: 2502: 2503: 2504: 2505: 2506: 2507: 2508: 2509: 2510: 2511: 2512: 2513: 2514: 2515:
2516:
2517: foreach ($query as $type => $c_val) {
2518: switch ($type) {
2519: case Horde_Imap_Client::FETCH_STRUCTURE:
2520: $fetch[] = 'BODYSTRUCTURE';
2521: break;
2522:
2523: case Horde_Imap_Client::FETCH_FULLMSG:
2524: if (empty($c_val['peek'])) {
2525: $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
2526: }
2527: $fetch[] = 'BODY' .
2528: (!empty($c_val['peek']) ? '.PEEK' : '') .
2529: '[]' .
2530: $this->_partialAtom($c_val);
2531: break;
2532:
2533: case Horde_Imap_Client::FETCH_HEADERTEXT:
2534: case Horde_Imap_Client::FETCH_BODYTEXT:
2535: case Horde_Imap_Client::FETCH_MIMEHEADER:
2536: case Horde_Imap_Client::FETCH_BODYPART:
2537: case Horde_Imap_Client::FETCH_HEADERS:
2538: foreach ($c_val as $key => $val) {
2539: $base_id = $cmd = ($key == 0)
2540: ? ''
2541: : $key . '.';
2542: $main_cmd = 'BODY';
2543:
2544: switch ($type) {
2545: case Horde_Imap_Client::FETCH_HEADERTEXT:
2546: $cmd .= 'HEADER';
2547: break;
2548:
2549: case Horde_Imap_Client::FETCH_BODYTEXT:
2550: $cmd .= 'TEXT';
2551: break;
2552:
2553: case Horde_Imap_Client::FETCH_MIMEHEADER:
2554: $cmd .= 'MIME';
2555: break;
2556:
2557: case Horde_Imap_Client::FETCH_BODYPART:
2558:
2559: $cmd = substr($cmd, 0, -1);
2560:
2561: if (!empty($val['decode']) &&
2562: $this->queryCapability('BINARY')) {
2563: $main_cmd = 'BINARY';
2564: }
2565: break;
2566:
2567: case Horde_Imap_Client::FETCH_HEADERS:
2568: $cmd .= 'HEADER.FIELDS';
2569: if (!empty($val['notsearch'])) {
2570: $cmd .= '.NOT';
2571: }
2572: $cmd .= ' (' . implode(' ', array_map('strtoupper', $val['headers'])) . ')';
2573:
2574:
2575:
2576: $t['fetchcmd'][$cmd] = $key;
2577: }
2578:
2579: if (empty($val['peek'])) {
2580: $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
2581: }
2582:
2583: $fetch[] = $main_cmd .
2584: (!empty($val['peek']) ? '.PEEK' : '') .
2585: '[' . $cmd . ']' .
2586: $this->_partialAtom($val);
2587: }
2588: break;
2589:
2590: case Horde_Imap_Client::FETCH_BODYPARTSIZE:
2591: if ($this->queryCapability('BINARY')) {
2592: foreach ($c_val as $val) {
2593: $fetch[] = 'BINARY.SIZE[' . $key . ']';
2594: }
2595: }
2596: break;
2597:
2598: case Horde_Imap_Client::FETCH_ENVELOPE:
2599: $fetch[] = 'ENVELOPE';
2600: break;
2601:
2602: case Horde_Imap_Client::FETCH_FLAGS:
2603: $fetch[] = 'FLAGS';
2604: break;
2605:
2606: case Horde_Imap_Client::FETCH_IMAPDATE:
2607: $fetch[] = 'INTERNALDATE';
2608: break;
2609:
2610: case Horde_Imap_Client::FETCH_SIZE:
2611: $fetch[] = 'RFC822.SIZE';
2612: break;
2613:
2614: case Horde_Imap_Client::FETCH_UID:
2615: 2616: 2617:
2618: if ($options['ids']->sequence) {
2619: $fetch[] = 'UID';
2620: }
2621: break;
2622:
2623: case Horde_Imap_Client::FETCH_SEQ:
2624:
2625:
2626: if (count($query) == 1) {
2627: $fetch[] = 'UID';
2628: }
2629: break;
2630:
2631: case Horde_Imap_Client::FETCH_MODSEQ:
2632: 2633: 2634:
2635: if (empty($options['changedsince'])) {
2636: 2637: 2638:
2639: if (empty($this->_temp['mailbox']['highestmodseq'])) {
2640: $this->_exception(Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."), 'MBOXNOMODSEQ');
2641: }
2642: $fetch[] = 'MODSEQ';
2643: }
2644: break;
2645: }
2646: }
2647:
2648: $seq = $options['ids']->all
2649: ? '1:*'
2650: : ($options['ids']->search_res ? '$' : strval($options['ids']));
2651:
2652: $cmd = array(
2653: ($options['ids']->sequence ? null : 'UID'),
2654: 'FETCH',
2655: $seq
2656: );
2657:
2658: if (empty($options['changedsince'])) {
2659: $cmd[] = $fetch;
2660: } else {
2661: if (empty($this->_temp['mailbox']['highestmodseq'])) {
2662: $this->_exception(Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."), 'MBOXNOMODSEQ');
2663: }
2664:
2665: $fetch_opts = array(
2666: 'CHANGEDSINCE',
2667: array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $options['changedsince'])
2668: );
2669:
2670: if (!empty($options['vanished'])) {
2671: $fetch_opts[] = 'VANISHED';
2672: $t['fetch_vanished'] = true;
2673: }
2674:
2675: 2676: 2677: 2678:
2679: $cmd[] = empty($fetch)
2680: ? 'UID'
2681: : $fetch;
2682: $cmd[] = $fetch_opts;
2683: }
2684:
2685: $fr = $this->_newFetchResult();
2686: if ($options['ids']->sequence) {
2687: $fr->seq = $results;
2688: } else {
2689: $fr->uid = $results;
2690: }
2691:
2692: try {
2693: $this->_sendLine($cmd, array(
2694: 'fetch' => $fr
2695: ));
2696: } catch (Horde_Imap_Client_Exception $e) {
2697:
2698:
2699: if ($options['ids']->sequence &&
2700: isset($this->_temp['parseresperr']['response']) &&
2701: ($this->_temp['parseresperr']['response'] == 'NO')) {
2702: $this->_temp['expungeissued'] = true;
2703: }
2704: }
2705:
2706: 2707: 2708: 2709:
2710: if (!empty($this->_temp['fetch_vanished'])) {
2711: $fr = $t['fetch_vanished_res'];
2712: unset($t['fetch_vanished'], $t['fetch_vanished_res']);
2713: }
2714:
2715: unset($t['fetchcmd']);
2716:
2717: $ret = $options['ids']->sequence
2718: ? $fr->seq
2719: : $fr->uid;
2720:
2721:
2722: if (!empty($this->_temp['expungeissued'])) {
2723: unset($this->_temp['expungeissued']);
2724: $this->noop();
2725: }
2726:
2727: return $ret;
2728: }
2729:
2730: 2731: 2732: 2733: 2734:
2735: protected function _newFetchResult()
2736: {
2737: if (!isset($this->_temp['fr_ob'])) {
2738: $fr = new stdClass;
2739: $fr->seq = array();
2740: $fr->uid = array();
2741: $this->_temp['fr_ob'] = $fr;
2742: }
2743:
2744: return clone $this->_temp['fr_ob'];
2745: }
2746:
2747: 2748: 2749: 2750: 2751: 2752: 2753:
2754: protected function _partialAtom($opts)
2755: {
2756: if (!empty($opts['length'])) {
2757: return '<' . (empty($opts['start']) ? 0 : intval($opts['start'])) . '.' . intval($opts['length']) . '>';
2758: }
2759:
2760: return empty($opts['start'])
2761: ? ''
2762: : ('<' . intval($opts['start']) . '>');
2763: }
2764:
2765: 2766: 2767: 2768: 2769: 2770: 2771: 2772:
2773: protected function _parseFetch($id, $data)
2774: {
2775: $cnt = count($data);
2776: $i = 0;
2777: $uid = null;
2778:
2779: 2780:
2781: $ob = new $this->_fetchDataClass();
2782: $ob->setSeq($id);
2783:
2784: while ($i < $cnt) {
2785: $tag = strtoupper($data[$i]);
2786: switch ($tag) {
2787: case 'BODYSTRUCTURE':
2788: $structure = $this->_parseBodystructure($data[++$i]);
2789: $structure->buildMimeIds();
2790: $ob->setStructure($structure);
2791: break;
2792:
2793: case 'ENVELOPE':
2794: $ob->setEnvelope($this->_parseEnvelope($data[++$i]));
2795: break;
2796:
2797: case 'FLAGS':
2798: $ob->setFlags($data[++$i]);
2799: break;
2800:
2801: case 'INTERNALDATE':
2802: $ob->setImapDate($data[++$i]);
2803: break;
2804:
2805: case 'RFC822.SIZE':
2806: $ob->setSize($data[++$i]);
2807: break;
2808:
2809: case 'UID':
2810: $uid = $data[++$i];
2811: $ob->setUid($uid);
2812: $this->_temp['mailbox']['lookup'][$id] = $uid;
2813: break;
2814:
2815: case 'MODSEQ':
2816: $modseq = reset($data[++$i]);
2817:
2818: $ob->setModSeq($modseq);
2819:
2820:
2821: if (!empty($this->_temp['mailbox']['highestmodseq']) &&
2822: ($modseq > $this->_temp['mailbox']['highestmodseq'])) {
2823: $this->_temp['mailbox']['highestmodseq'] = $modseq;
2824: }
2825: break;
2826:
2827: default:
2828:
2829: if (strpos($tag, 'BODY[') === 0) {
2830:
2831: $tag = substr($tag, 5);
2832:
2833:
2834: if (!empty($this->_temp['fetchcmd']) &&
2835: (strpos($tag, 'HEADER.FIELDS') !== false)) {
2836:
2837:
2838:
2839:
2840:
2841:
2842:
2843:
2844: $sig = $tag . ' (' . implode(' ', array_map('strtoupper', $data[++$i])) . ')';
2845:
2846:
2847: ++$i;
2848:
2849: $ob->setHeaders($this->_temp['fetchcmd'][$sig], $data[++$i]);
2850: } else {
2851:
2852: $tag = substr($tag, 0, strrpos($tag, ']'));
2853:
2854: if (!strlen($tag)) {
2855:
2856: if (($tmp = $this->_getString($data[++$i], true)) !== null) {
2857: $ob->setFullMsg($tmp);
2858: }
2859: } elseif (is_numeric(substr($tag, -1))) {
2860:
2861: if (($tmp = $this->_getString($data[++$i], true)) !== null) {
2862: $ob->setBodyPart($tag, $tmp);
2863: }
2864: } else {
2865:
2866: if (($last_dot = strrpos($tag, '.')) === false) {
2867: $mime_id = 0;
2868: } else {
2869: $mime_id = substr($tag, 0, $last_dot);
2870: $tag = substr($tag, $last_dot + 1);
2871: }
2872:
2873: if (($tmp = $this->_getString($data[++$i], true)) !== null) {
2874: switch ($tag) {
2875: case 'HEADER':
2876: $ob->setHeaderText($mime_id, $data[$i]);
2877: break;
2878:
2879: case 'TEXT':
2880: $ob->setBodyText($mime_id, $data[$i]);
2881: break;
2882:
2883: case 'MIME':
2884: $ob->setMimeHeader($mime_id, $data[$i]);
2885: break;
2886: }
2887: }
2888: }
2889: }
2890: } elseif (strpos($tag, 'BINARY[') === 0) {
2891:
2892:
2893:
2894: $tag = substr($tag, 7, strrpos($tag, ']') - 7);
2895: $ob->setBodyPart($tag, $data[++$i], empty($this->_temp['literal8']) ? '8bit' : 'binary');
2896: } elseif (strpos($tag, 'BINARY.SIZE[') === 0) {
2897:
2898:
2899:
2900: $tag = substr($tag, 12, strrpos($tag, ']') - 12);
2901: $ob->setBodyPartSize($tag, $data[++$i]);
2902: }
2903: break;
2904: }
2905:
2906: ++$i;
2907: }
2908:
2909: $fr = $this->_temp['fetchresp'];
2910:
2911: if (!is_null($uid) && isset($fr->uid[$uid])) {
2912: $fr->uid[$uid]->merge($ob);
2913: } else {
2914: if (isset($fr->seq[$id])) {
2915: $fr->seq[$id]->merge($ob);
2916: } else {
2917: $fr->seq[$id] = $ob;
2918: }
2919: if (!is_null($uid)) {
2920: $fr->uid[$uid] = $ob;
2921: }
2922: }
2923: }
2924:
2925: 2926: 2927: 2928: 2929: 2930: 2931: 2932:
2933: protected function _parseBodystructure($data)
2934: {
2935: $ob = new Horde_Mime_Part();
2936:
2937:
2938: if (is_array($data[0])) {
2939:
2940: for ($i = 0; isset($data[$i]) && is_array($data[$i]); ++$i) {
2941: $ob->addPart($this->_parseBodystructure($data[$i]));
2942: }
2943:
2944:
2945:
2946: $ob->setType('multipart/' . $this->_getString($data[$i]));
2947:
2948:
2949:
2950:
2951:
2952: if (isset($data[++$i]) && is_array($data[$i])) {
2953: foreach ($this->_parseStructureParams($data[$i], 'content-type') as $key => $val) {
2954: $ob->setContentTypeParameter($key, $val);
2955: }
2956: }
2957:
2958:
2959: if (isset($data[++$i]) && is_array($data[$i])) {
2960: $ob->setDisposition($this->_getString($data[$i][0]));
2961:
2962: foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) {
2963: $ob->setDispositionParameter($key, $val);
2964: }
2965: }
2966:
2967:
2968:
2969: if (isset($data[++$i])) {
2970: $ob->setLanguage($this->_getString($data[$i]));
2971: }
2972:
2973:
2974:
2975:
2976: } else {
2977: $ob->setType($this->_getString($data[0]) . '/' . $this->_getString($data[1]));
2978:
2979: foreach ($this->_parseStructureParams($data[2], 'content-type') as $key => $val) {
2980: $ob->setContentTypeParameter($key, $val);
2981: }
2982:
2983: if (($tmp = $this->_getString($data[3], true)) !== null) {
2984: $ob->setContentId($tmp);
2985: }
2986:
2987: if (($tmp = $this->_getString($data[4], true)) !== null) {
2988: $ob->setDescription(Horde_Mime::decode($tmp, 'UTF-8'));
2989: }
2990:
2991: if (($tmp = $this->_getString($data[5], true)) !== null) {
2992: $ob->setTransferEncoding($tmp);
2993: }
2994:
2995: $ob->setBytes($data[6]);
2996:
2997:
2998:
2999: $i = 7;
3000: switch ($ob->getPrimaryType()) {
3001: case 'message':
3002: if ($ob->getSubType() == 'rfc822') {
3003:
3004: $ob->addPart($this->_parseBodystructure($data[8]));
3005:
3006: $i = 10;
3007: }
3008: break;
3009:
3010: case 'text':
3011:
3012: $i = 8;
3013: break;
3014: }
3015:
3016:
3017:
3018:
3019:
3020:
3021:
3022: if (isset($data[++$i]) && is_array($data[$i])) {
3023: $ob->setDisposition($data[$i][0]);
3024:
3025: foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) {
3026: $ob->setDispositionParameter($key, $val);
3027: }
3028: }
3029:
3030:
3031:
3032: if (isset($data[++$i])) {
3033: $ob->setLanguage($this->_getString($data[$i]));
3034: }
3035:
3036:
3037: }
3038:
3039: return $ob;
3040: }
3041:
3042: 3043: 3044: 3045: 3046: 3047: 3048: 3049:
3050: protected function _parseStructureParams($data, $type)
3051: {
3052: $params = array();
3053:
3054: if (is_array($data)) {
3055: for ($i = 0; isset($data[$i]); $i += 2) {
3056: $params[strtolower($data[$i])] = $this->_getString($data[$i + 1]);
3057: }
3058: }
3059:
3060: $ret = Horde_Mime::decodeParam($type, $params, 'UTF-8');
3061:
3062: return $ret['params'];
3063: }
3064:
3065: 3066: 3067: 3068: 3069: 3070: 3071: 3072: 3073:
3074: protected function _getString($data, $nstring = false)
3075: {
3076: if (is_array($data)) {
3077: return array_map(array($this, '_getString'), $data, array_fill(0, count($data), $nstring));
3078: }
3079:
3080: if (is_resource($data)) {
3081: rewind($data);
3082: return stream_get_contents($data);
3083: }
3084:
3085: return ($nstring && (strcasecmp($data, 'NIL') === 0))
3086: ? null
3087: : $data;
3088: }
3089:
3090: 3091: 3092: 3093: 3094: 3095: 3096:
3097: protected function _parseEnvelope($data)
3098: {
3099: $addr_structure = array(
3100: 'personal', 'route', 'mailbox', 'host'
3101: );
3102: $env_data = array(
3103: 0 => 'date',
3104: 1 => 'subject',
3105: 2 => 'from',
3106: 3 => 'sender',
3107: 4 => 'reply_to',
3108: 5 => 'to',
3109: 6 => 'cc',
3110: 7 => 'bcc',
3111: 8 => 'in_reply_to',
3112: 9 => 'message_id'
3113: );
3114:
3115: $ret = new Horde_Imap_Client_Data_Envelope();
3116:
3117: foreach ($data as $key => $val) {
3118: if (!isset($env_data[$key]) ||
3119: ($val = $this->_getString($val, true)) === null) {
3120: continue;
3121: }
3122:
3123: if (is_string($val)) {
3124:
3125: $ret->$env_data[$key] = $val;
3126: } else {
3127:
3128: $group = null;
3129: $tmp = array();
3130:
3131: foreach ($val as $a_val) {
3132:
3133:
3134:
3135: if (is_null($a_val[3])) {
3136: $group = new Horde_Mail_Rfc822_Group();
3137:
3138: if (is_null($a_val[2])) {
3139: $group = null;
3140: } else {
3141: $group->groupname = $a_val[2];
3142: $tmp[] = $group;
3143: }
3144: } else {
3145: $addr = new Horde_Mail_Rfc822_Address();
3146:
3147: foreach ($addr_structure as $add_key => $add_val) {
3148: if (!is_null($a_val[$add_key])) {
3149: $addr->$add_val = ($add_val == 'route')
3150: ? array($a_val[$add_key])
3151: : $a_val[$add_key];
3152: }
3153: }
3154:
3155: if ($group) {
3156: $group->addresses[] = $addr;
3157: } else {
3158: $tmp[] = $addr;
3159: }
3160: }
3161:
3162: $ret->$env_data[$key] = $tmp;
3163: }
3164: }
3165: }
3166:
3167: return $ret;
3168: }
3169:
3170: 3171:
3172: protected function _store($options)
3173: {
3174: $seq = $options['ids']->all
3175: ? '1:*'
3176: : ($options['ids']->search_res ? '$' : strval($options['ids']));
3177:
3178: $cmd = array(
3179: (empty($options['sequence']) ? 'UID' : null),
3180: 'STORE',
3181: $seq
3182: );
3183:
3184: if (!empty($this->_temp['mailbox']['highestmodseq'])) {
3185: $ucsince = empty($options['unchangedsince'])
3186: 3187:
3188: ? $this->_temp['mailbox']['highestmodseq']
3189: : intval($options['unchangedsince']);
3190:
3191: if ($ucsince) {
3192: $cmd[] = array(
3193: 'UNCHANGEDSINCE',
3194: array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $ucsince)
3195: );
3196: }
3197: } elseif (!empty($options['unchangedsince'])) {
3198: 3199: 3200:
3201: $this->_exception(Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."), 'MBOXNOMODSEQ');
3202: }
3203:
3204: $this->_temp['modified'] = $this->getIdsOb();
3205:
3206: if (!empty($options['replace'])) {
3207: $cmd[] = 'FLAGS' . ($this->_debug ? '' : '.SILENT');
3208: foreach ($options['replace'] as $val) {
3209: $cmd[] = array('t' => Horde_Imap_Client::DATA_ATOM, 'v' => $val);
3210: }
3211:
3212: try {
3213: $this->_sendLine($cmd);
3214: } catch (Horde_Imap_Client_Exception $e) {
3215:
3216:
3217:
3218: if (!empty($options['sequence']) &&
3219: !$this->_debug &&
3220: isset($this->_temp['parseresperr']['response']) &&
3221: ($this->_temp['parseresperr']['response'] == 'NO')) {
3222: $this->_temp['expungeissued'] = true;
3223: } else {
3224: throw $e;
3225: }
3226: }
3227:
3228: $this->_storeUpdateCache('replace', $options['replace']);
3229: } else {
3230: foreach (array('add' => '+', 'remove' => '-') as $k => $v) {
3231: if (!empty($options[$k])) {
3232: $cmdtmp = $cmd;
3233: $cmdtmp[] = $v . 'FLAGS' . ($this->_debug ? '' : '.SILENT');
3234: foreach ($options[$k] as $val) {
3235: $cmdtmp[] = array('t' => Horde_Imap_Client::DATA_ATOM, 'v' => $val);
3236: }
3237:
3238: try {
3239: $this->_sendLine($cmdtmp);
3240: } catch (Horde_Imap_Client_Exception $e) {
3241:
3242:
3243:
3244: if (!empty($options['sequence']) &&
3245: !$this->_debug &&
3246: isset($this->_temp['parseresperr']['response']) &&
3247: ($this->_temp['parseresperr']['response'] == 'NO')) {
3248: $this->_temp['expungeissued'] = true;
3249: } else {
3250: throw $e;
3251: }
3252: }
3253:
3254: $this->_storeUpdateCache($k, $options[$k]);
3255: }
3256: }
3257: }
3258:
3259: $ret = $this->_temp['modified'];
3260:
3261:
3262: if (!empty($this->_temp['expungeissued'])) {
3263: unset($this->_temp['expungeissued']);
3264: $this->noop();
3265: }
3266:
3267: return $ret;
3268: }
3269:
3270: 3271: 3272: 3273:
3274: protected function _storeUpdateCache($type, $update_flags)
3275: {
3276: if (!isset($this->_init['enabled']['CONDSTORE']) ||
3277: empty($this->_temp['mailbox']['highestmodseq']) ||
3278: empty($this->_temp['fetchresp']->seq)) {
3279: return;
3280: }
3281:
3282: $fr = $this->_temp['fetchresp'];
3283: $tocache = $uids = array();
3284:
3285: if (empty($fr->uid)) {
3286: $res = $fr->seq;
3287: $seq_res = $this->_getSeqUidLookup($this->getIdsOb(array_keys($res), true));
3288: } else {
3289: $res = $fr->uid;
3290: $seq_res = null;
3291: }
3292:
3293: foreach (array_keys($res) as $key) {
3294: if (!$res[$key]->exists(Horde_Imap_Client::FETCH_FLAGS)) {
3295: $uids[$key] = is_null($seq_res)
3296: ? $key
3297: : $seq_res['lookup'][$key];
3298: }
3299: }
3300:
3301:
3302: switch ($type) {
3303: case 'add':
3304: case 'remove':
3305:
3306: $data = $this->cache->get($this->_selected, array_values($uids), array('HICflags'), $this->_temp['mailbox']['uidvalidity']);
3307:
3308: foreach ($uids as $key => $uid) {
3309: $flags = isset($data[$uid]['HICflags'])
3310: ? $data[$uid]['HICflags']
3311: : array();
3312: if ($type == 'add') {
3313: $flags = array_merge($flags, $update_flags);
3314: } else {
3315: $flags = array_diff($flags, $update_flags);
3316: }
3317:
3318: $tocache[$uid] = $res[$key];
3319: $tocache[$uid]->setFlags(array_keys(array_flip($flags)));
3320: }
3321: break;
3322:
3323: case 'update':
3324: foreach ($uids as $uid) {
3325: $tocache[$uid] = $res[$key];
3326: $tocache[$uid]->setFlags($update_flags);
3327: }
3328: break;
3329: }
3330:
3331: if (!empty($tocache)) {
3332: $this->_updateCache($tocache, array(
3333: 'fields' => array(
3334: Horde_Imap_Client::FETCH_FLAGS
3335: )
3336: ));
3337: }
3338: }
3339:
3340: 3341:
3342: protected function _copy(Horde_Imap_Client_Mailbox $dest, $options)
3343: {
3344: $this->_temp['copyuid'] = $this->_temp['copyuidvalid'] = $this->_temp['trycreate'] = null;
3345: $this->_temp['uidplusmbox'] = $dest;
3346:
3347: $seq = $options['ids']->all
3348: ? '1:*'
3349: : ($options['ids']->search_res ? '$' : strval($options['ids']));
3350:
3351:
3352: try {
3353: $this->_sendLine(array(
3354: ($options['ids']->sequence ? null : 'UID'),
3355: 'COPY',
3356: $seq,
3357: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $dest->utf7imap)
3358: ));
3359: } catch (Horde_Imap_Client_Exception $e) {
3360: if (!empty($options['create']) && $this->_temp['trycreate']) {
3361: $this->createMailbox($dest);
3362: unset($options['create']);
3363: return $this->_copy($dest, $options);
3364: }
3365: throw $e;
3366: }
3367:
3368: 3369: 3370: 3371: 3372:
3373: if (!is_null($this->_temp['copyuid'])) {
3374: $this->_moveCache($this->_selected, $dest, $this->_temp['copyuid'], $this->_temp['copyuidvalid']);
3375: }
3376:
3377:
3378: if (!empty($options['move'])) {
3379: $opts = array('ids' => $options['ids']);
3380: $this->store($this->_selected, array_merge(array(
3381: 'add' => array(Horde_Imap_Client::FLAG_DELETED)
3382: ), $opts));
3383: $this->expunge($this->_selected, $opts);
3384: }
3385:
3386: return is_null($this->_temp['copyuid'])
3387: ? true
3388: : $this->_temp['copyuid'];
3389: }
3390:
3391: 3392:
3393: protected function _setQuota(Horde_Imap_Client_Mailbox $root, $resources)
3394: {
3395: $limits = array();
3396:
3397: foreach ($resources as $key => $val) {
3398: $limits[] = strtoupper($key);
3399: $limits[] = array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => intval($val));
3400: }
3401:
3402: $this->_sendLine(array(
3403: 'SETQUOTA',
3404: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $root->utf7imap),
3405: $limits
3406: ));
3407: }
3408:
3409: 3410:
3411: protected function _getQuota(Horde_Imap_Client_Mailbox $root)
3412: {
3413: $this->_temp['quotaresp'] = array();
3414: $this->_sendLine(array(
3415: 'GETQUOTA',
3416: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $root->utf7imap)
3417: ));
3418: return reset($this->_temp['quotaresp']);
3419: }
3420:
3421: 3422: 3423: 3424: 3425:
3426: protected function _parseQuota($data)
3427: {
3428: $c = &$this->_temp['quotaresp'];
3429:
3430: $root = $data[0];
3431: $c[$root] = array();
3432:
3433: for ($i = 0; isset($data[1][$i]); $i += 3) {
3434: if (count($data[1][$i])) {
3435: $c[$root][strtolower($data[1][$i])] = array(
3436: 'limit' => $data[1][$i + 2],
3437: 'usage' => $data[1][$i + 1]
3438: );
3439: }
3440: }
3441: }
3442:
3443: 3444:
3445: protected function _getQuotaRoot(Horde_Imap_Client_Mailbox $mailbox)
3446: {
3447: $this->_temp['quotaresp'] = array();
3448: $this->_sendLine(array(
3449: 'GETQUOTAROOT',
3450: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $mailbox->utf7imap)
3451: ));
3452: return $this->_temp['quotaresp'];
3453: }
3454:
3455: 3456:
3457: protected function _setACL(Horde_Imap_Client_Mailbox $mailbox, $identifier,
3458: $options)
3459: {
3460:
3461:
3462: if (empty($options['rights']) && !empty($options['remove'])) {
3463: $this->_sendLine(array(
3464: 'DELETEACL',
3465: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap),
3466: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $identifier)
3467: ));
3468: } else {
3469: $this->_sendLine(array(
3470: 'SETACL',
3471: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap),
3472: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $identifier),
3473: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $options['rights'])
3474: ));
3475: }
3476: }
3477:
3478: 3479:
3480: protected function _getACL(Horde_Imap_Client_Mailbox $mailbox)
3481: {
3482: $this->_temp['getacl'] = array();
3483: $this->_sendLine(array(
3484: 'GETACL',
3485: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap)
3486: ));
3487:
3488: return $this->_temp['getacl'];
3489: }
3490:
3491: 3492: 3493: 3494: 3495:
3496: protected function _parseACL($data)
3497: {
3498: $acl = &$this->_temp['getacl'];
3499:
3500:
3501: for ($i = 1; isset($data[$i]); $i += 2) {
3502: $acl[$data[$i]] = ($data[$i][0] == '-')
3503: ? new Horde_Imap_Client_Data_AclNegative($data[$i + 1])
3504: : new Horde_Imap_Client_Data_Acl($data[$i + 1]);
3505: }
3506: }
3507:
3508: 3509:
3510: protected function _listACLRights(Horde_Imap_Client_Mailbox $mailbox,
3511: $identifier)
3512: {
3513: unset($this->_temp['listaclrights']);
3514: $this->_sendLine(array(
3515: 'LISTRIGHTS',
3516: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap),
3517: array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $identifier)
3518: ));
3519:
3520: return isset($this->_temp['listaclrights'])
3521: ? $this->_temp['listaclrights']
3522: : new Horde_Imap_Client_Data_AclRights();
3523: }
3524:
3525: 3526: 3527: 3528: 3529:
3530: protected function _parseListRights($data)
3531: {
3532:
3533: $this->_temp['listaclrights'] = new Horde_Imap_Client_Data_AclRights(
3534: str_split($data[2]),
3535: array_slice($data, 3)
3536: );
3537: }
3538:
3539: 3540:
3541: protected function _getMyACLRights(Horde_Imap_Client_Mailbox $mailbox)
3542: {
3543: unset($this->_temp['myrights']);
3544: $this->_sendLine(array(
3545: 'MYRIGHTS',
3546: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap)
3547: ));
3548:
3549: return isset($this->_temp['myrights'])
3550: ? $this->_temp['myrights']
3551: : new Horde_Imap_Client_Data_Acl();
3552: }
3553:
3554: 3555: 3556: 3557: 3558:
3559: protected function _parseMyRights($data)
3560: {
3561: $this->_temp['myrights'] = new Horde_Imap_Client_Data_Acl($data[1]);
3562: }
3563:
3564: 3565:
3566: protected function _getMetadata(Horde_Imap_Client_Mailbox $mailbox,
3567: $entries, $options)
3568: {
3569: $this->_temp['metadata'] = array();
3570: $queries = array();
3571:
3572: if ($this->queryCapability('METADATA') ||
3573: ((strlen($mailbox) == 0) &&
3574: $this->queryCapability('METADATA-SERVER'))) {
3575: $cmd_options = array();
3576:
3577: if (!empty($options['maxsize'])) {
3578: $cmd_options[] = array(
3579: 'MAXSIZE',
3580: array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $options['maxsize'])
3581: );
3582: }
3583: if (!empty($options['depth'])) {
3584: $cmd_options[] = array(
3585: 'DEPTH',
3586: array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $options['depth'])
3587: );
3588: }
3589:
3590: foreach ($entries as $md_entry) {
3591: $queries[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $md_entry);
3592: }
3593:
3594: $this->_sendLine(array(
3595: 'GETMETADATA',
3596: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap),
3597: (empty($cmd_options) ? null : $cmd_options),
3598: $queries
3599: ));
3600:
3601: return $this->_temp['metadata'];
3602: }
3603:
3604: if (!$this->queryCapability('ANNOTATEMORE') &&
3605: !$this->queryCapability('ANNOTATEMORE2')) {
3606: $this->_exception('Server does not support the METADATA extension.', 'NO_SUPPORT');
3607: }
3608:
3609: $queries = array();
3610: foreach ($entries as $md_entry) {
3611: list($entry, $type) = $this->_getAnnotateMoreEntry($md_entry);
3612:
3613: if (!isset($queries[$type])) {
3614: $queries[$type] = array();
3615: }
3616: $queries[$type][] = array('t' => Horde_Imap_Client::DATA_STRING, 'v' => $entry);
3617: }
3618:
3619: $result = array();
3620: foreach ($queries as $key => $val) {
3621:
3622: $this->_sendLine(array(
3623: 'GETANNOTATION',
3624: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap),
3625: $val,
3626: array('t' => Horde_Imap_Client::DATA_STRING, 'v' => $key)
3627: ));
3628:
3629: $result = array_merge($result, $this->_temp['metadata']);
3630: }
3631:
3632: return $result;
3633: }
3634:
3635: 3636: 3637: 3638: 3639: 3640: 3641: 3642: 3643: 3644: 3645:
3646: protected function _getAnnotateMoreEntry($name)
3647: {
3648: if (substr($name, 0, 7) == '/shared') {
3649: return array(substr($name, 7), 'value.shared');
3650: } else if (substr($name, 0, 8) == '/private') {
3651: return array(substr($name, 8), 'value.priv');
3652: }
3653:
3654: $this->_exception(sprintf(Horde_Imap_Client_Translation::t("Invalid METADATA entry: \"%s\"."), $name), 'METADATA_INVALID');
3655: }
3656:
3657: 3658:
3659: protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox, $data)
3660: {
3661: if ($this->queryCapability('METADATA') ||
3662: ((strlen($mailbox) == 0) &&
3663: $this->queryCapability('METADATA-SERVER'))) {
3664: $data_elts = array();
3665:
3666: foreach ($data as $key => $value) {
3667: $data_elts[] = array(
3668: 't' => Horde_Imap_Client::DATA_ASTRING,
3669: 'v' => $key
3670: );
3671: $data_elts[] = array(
3672: 't' => Horde_Imap_Client::DATA_NSTRING,
3673: 'v' => $value
3674: );
3675: }
3676:
3677: $this->_sendLine(array(
3678: 'SETMETADATA',
3679: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap),
3680: $data_elts
3681: ));
3682:
3683: return;
3684: }
3685:
3686: if (!$this->queryCapability('ANNOTATEMORE') &&
3687: !$this->queryCapability('ANNOTATEMORE2')) {
3688: $this->_exception('Server does not support the METADATA extension.', 'NO_SUPPORT');
3689: }
3690:
3691: foreach ($data as $md_entry => $value) {
3692: list($entry, $type) = $this->_getAnnotateMoreEntry($md_entry);
3693:
3694: $this->_sendLine(array(
3695: 'SETANNOTATION',
3696: array('t' => Horde_Imap_Client::DATA_MAILBOX, 'v' => $mailbox->utf7imap),
3697: array('t' => Horde_Imap_Client::DATA_STRING, 'v' => $entry),
3698: array(
3699: array('t' => Horde_Imap_Client::DATA_STRING, 'v' => $type),
3700: array('t' => Horde_Imap_Client::DATA_NSTRING, 'v' => $value)
3701: )
3702: ));
3703: }
3704: }
3705:
3706: 3707: 3708: 3709: 3710: 3711: 3712:
3713: protected function _parseMetadata($data)
3714: {
3715: switch ($data[0]) {
3716: case 'ANNOTATION':
3717: $values = $data[3];
3718: while (!empty($values)) {
3719: $type = array_shift($values);
3720: switch ($type) {
3721: case 'value.priv':
3722: $this->_temp['metadata'][$data[1]]['/private' . $data[2]] = array_shift($values);
3723: break;
3724:
3725: case 'value.shared':
3726: $this->_temp['metadata'][$data[1]]['/shared' . $data[2]] = array_shift($values);
3727: break;
3728:
3729: default:
3730: $this->_exception(sprintf(Horde_Imap_Client_Translation::t("Invalid METADATA value type \"%s\"."), $type));
3731: }
3732: }
3733: break;
3734:
3735: case 'METADATA':
3736: $values = $data[2];
3737: while (!empty($values)) {
3738: $entry = array_shift($values);
3739: $this->_temp['metadata'][$data[1]][$entry] = array_shift($values);
3740: }
3741: break;
3742: }
3743: }
3744:
3745:
3746:
3747: 3748:
3749: protected function _getSeqUidLookup(Horde_Imap_Client_Ids $ids,
3750: $reverse = false)
3751: {
3752: $ob = array(
3753: 'lookup' => array(),
3754: 'uids' => $this->getIdsOb()
3755: );
3756:
3757: if (!empty($this->_temp['mailbox']['lookup']) &&
3758: count($ids) &&
3759: ($ids->sequence || $reverse)) {
3760: $need = $this->getIdsOb(null, $ids->sequence);
3761: $t = $this->_temp['mailbox']['lookup'];
3762:
3763: foreach ($ids as $val) {
3764: if ($ids->sequence) {
3765: if (isset($t[$val])) {
3766: $ob['lookup'][$val] = $t[$val];
3767: $ob['uids']->add($t[$val]);
3768: } else {
3769: $need->add($val);
3770: }
3771: } else {
3772: if (($key = array_search($val, $t)) !== false) {
3773: $ob['lookup'][$key] = $val;
3774: $ob['uids']->add($val);
3775: } else {
3776: $need->add($val);
3777: }
3778: }
3779: }
3780:
3781: if (!count($need)) {
3782: return $ob;
3783: }
3784:
3785: $ids = $need;
3786: }
3787:
3788: $res = parent::_getSeqUidLookup($ids, $reverse);
3789:
3790: if (!empty($res['lookup'])) {
3791: $ob['lookup'] = $ob['lookup'] + $res['lookup'];
3792: }
3793: if (isset($res['uids'])) {
3794: $ob['uids']->add($res['uids']);
3795: }
3796:
3797: return $ob;
3798: }
3799:
3800: 3801:
3802: protected function _getSearchCache($type, $mailbox, $options)
3803: {
3804: 3805:
3806: return empty($this->_temp['mailbox']['highestmodseq'])
3807: ? null
3808: : parent::_getSearchCache($type, $mailbox, $options);
3809: }
3810:
3811:
3812:
3813: 3814: 3815: 3816: 3817: 3818: 3819: 3820: 3821: 3822: 3823: 3824: 3825: 3826: 3827: 3828: 3829: 3830: 3831: 3832: 3833: 3834: 3835: 3836: 3837: 3838: 3839: 3840: 3841: 3842: 3843: 3844: 3845: 3846: 3847: 3848: 3849: 3850: 3851: 3852: 3853: 3854: 3855: 3856: 3857: 3858:
3859: protected function _sendLine($data, $options = array())
3860: {
3861: $out = '';
3862:
3863: if (empty($options['notag'])) {
3864: $out = ++$this->_tag . ' ';
3865: $this->_temp['fetchresp'] = empty($options['fetch'])
3866: ? $this->_newFetchResult()
3867: : $options['fetch'];
3868: }
3869:
3870: if (is_array($data)) {
3871: if (!empty($options['debug'])) {
3872: $this->_temp['sendnodebug'] = true;
3873: }
3874: $out = rtrim($this->utils->parseCommandArray($data, array($this, 'parseCommandArrayCallback'), $out));
3875: unset($this->_temp['sendnodebug']);
3876: } elseif (is_string($data)) {
3877: $out .= $data;
3878: }
3879:
3880: $continuation = $literalplus = false;
3881:
3882: if (!empty($options['literal'])) {
3883: $out .= ' ';
3884:
3885: 3886:
3887: $literalplus = $this->queryCapability('LITERAL+');
3888:
3889:
3890: if (!empty($options['binary'])) {
3891: if (!$this->queryCapability('BINARY')) {
3892: $this->_exception('Can not send binary data to server that does not support it.', 'NO_SUPPORT');
3893: }
3894: $out .= '~';
3895: }
3896:
3897: $out .= '{' . $options['literal'] . ($literalplus ? '+' : '') . '}';
3898: }
3899:
3900: if ($this->_debug && empty($this->_temp['sendnodebug'])) {
3901: if (is_resource($data)) {
3902: if (empty($this->_params['debug_literal'])) {
3903: fseek($data, 0, SEEK_END);
3904: $this->writeDebug('[LITERAL DATA - ' . ftell($data) . ' bytes]' . "\n", Horde_Imap_Client::DEBUG_CLIENT);
3905: } else {
3906: rewind($data);
3907: $this->writeDebug('', Horde_Imap_Client::DEBUG_CLIENT);
3908: while (!feof($data)) {
3909: $this->writeDebug(fread($data, 8192));
3910: }
3911: }
3912: } else {
3913: $this->writeDebug((empty($options['debug']) ? $out : $options['debug']) . "\n", Horde_Imap_Client::DEBUG_CLIENT);
3914: }
3915: }
3916:
3917: if (is_resource($data)) {
3918: rewind($data);
3919: stream_copy_to_stream($data, $this->_stream);
3920: } else {
3921: fwrite($this->_stream, $out);
3922: if (empty($options['literaldata'])) {
3923: fwrite($this->_stream, "\r\n");
3924: }
3925: }
3926:
3927: if ($literalplus || !empty($options['literaldata'])) {
3928: return;
3929: }
3930:
3931: if (!empty($options['literal'])) {
3932: $ob = $this->_getLine();
3933: if ($ob['type'] != 'continuation') {
3934: $this->writeDebug("ERROR: Unexpected response from server while waiting for a continuation request.\n", Horde_Imap_Client::DEBUG_INFO);
3935: $this->_exception(array(
3936: Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
3937: $ob['line']
3938: ), 'SERVER_READERROR');
3939: }
3940: } elseif (empty($options['noparse'])) {
3941: $this->_parseResponse($this->_tag, !empty($options['errignore']));
3942: } else {
3943: return $this->_getLine();
3944: }
3945: }
3946:
3947: 3948: 3949: 3950: 3951: 3952: 3953: 3954:
3955: public function parseCommandArrayCallback($cmd, $data)
3956: {
3957: 3958: 3959: 3960:
3961: if (is_string($data)) {
3962: $binary = (strpos($data, "\0") !== false);
3963: $len = strlen($data);
3964: } else {
3965: $binary = false;
3966: rewind($data);
3967: while (!feof($data)) {
3968: if (strpos(fread($data, 4096), "\0") !== false) {
3969: $binary = true;
3970: break;
3971: }
3972: }
3973: fseek($data, 0, SEEK_END);
3974: $len = ftell($data);
3975: }
3976:
3977: $this->_sendLine(rtrim($cmd), array(
3978: 'binary' => $binary,
3979: 'literal' => $len,
3980: 'notag' => true
3981: ));
3982:
3983: $this->_sendLine($data, array(
3984: 'literaldata' => true,
3985: 'notag' => true
3986: ));
3987:
3988: return '';
3989: }
3990:
3991: 3992: 3993: 3994: 3995: 3996: 3997: 3998: 3999: 4000: 4001: 4002: 4003: 4004:
4005: protected function _getLine()
4006: {
4007: $ob = array(
4008: 'line' => '',
4009: 'response' => '',
4010: 'tag' => '',
4011: 'token' => ''
4012: );
4013:
4014: $read = explode(' ', $this->_readData(), 3);
4015:
4016: switch ($read[0]) {
4017:
4018: case '+':
4019: $ob['line'] = implode(' ', array_slice($read, 1));
4020: $ob['type'] = 'continuation';
4021: break;
4022:
4023:
4024: case '*':
4025: $ob['type'] = 'untagged';
4026:
4027: $read[1] = strtoupper($read[1]);
4028: if ($read[1] == 'BYE') {
4029: if (!empty($this->_temp['logout'])) {
4030: 4031: 4032: 4033:
4034: $ob['response'] = $read[1];
4035: $ob['line'] = implode(' ', array_slice($read, 2));
4036: } else {
4037: $this->_temp['logout'] = true;
4038: $this->logout();
4039: $this->_exception(array(
4040: Horde_Imap_Client_Translation::t("IMAP Server closed the connection."),
4041: implode(' ', array_slice($read, 1))
4042: ), 'DISCONNECT');
4043: }
4044: }
4045:
4046: if (in_array($read[1], array('OK', 'NO', 'BAD', 'PREAUTH'))) {
4047: $ob['response'] = $read[1];
4048: $ob['line'] = implode(' ', array_slice($read, 2));
4049: } else {
4050:
4051: $line = implode(' ', array_slice($read, 1));
4052: $binary = $literal = false;
4053: $this->_temp['literal8'] = array();
4054:
4055: do {
4056: $literal_len = null;
4057:
4058: if ($literal) {
4059: $this->_temp['token']->ptr[$this->_temp['token']->paren][] = $line;
4060: } else {
4061: if (substr($line, -1) == '}') {
4062: $pos = strrpos($line, '{');
4063: $literal_len = substr($line, $pos + 1, -1);
4064: if (is_numeric($literal_len)) {
4065:
4066: if ($line[$pos - 1] == '~') {
4067: $binary = true;
4068: $line = substr($line, 0, $pos - 1);
4069: $this->_temp['literal8'][substr($line, strrpos($line, ' '))] = true;
4070: } else {
4071: $line = substr($line, 0, $pos);
4072: }
4073: } else {
4074: $literal_len = null;
4075: }
4076: }
4077:
4078: $this->_tokenizeData($line);
4079: }
4080:
4081: if (is_null($literal_len)) {
4082: if (!$literal) {
4083: break;
4084: }
4085: $binary = $literal = false;
4086: $line = $this->_readData();
4087: } else {
4088: $literal = true;
4089: $line = $this->_readData($literal_len, $binary);
4090: }
4091: } while (true);
4092:
4093: $ob['token'] = $this->_temp['token']->out;
4094: unset($this->_temp['token']);
4095: }
4096: break;
4097:
4098:
4099: default:
4100: $ob['type'] = 'tagged';
4101: $ob['line'] = implode(' ', array_slice($read, 2));
4102: $ob['tag'] = $read[0];
4103: $ob['response'] = $read[1];
4104: break;
4105: }
4106:
4107: return $ob;
4108: }
4109:
4110: 4111: 4112: 4113: 4114: 4115: 4116: 4117: 4118: 4119: 4120:
4121: protected function _readData($len = null, $binary = false)
4122: {
4123: if (feof($this->_stream)) {
4124: $this->_temp['logout'] = true;
4125: $this->logout();
4126: $this->writeDebug("ERROR: Server closed the connection.\n", Horde_Imap_Client::DEBUG_INFO);
4127: $this->_exception(Horde_Imap_Client_Translation::t("Mail server closed the connection unexpectedly."), 'DISCONNECT');
4128: }
4129:
4130: $data = '';
4131: $got_data = $stream = false;
4132:
4133: if (is_null($len)) {
4134: do {
4135: 4136:
4137: if ($in = fgets($this->_stream, 8192)) {
4138: $data .= $in;
4139: $got_data = true;
4140: if (!isset($in[8190]) || ($in[8190] == "\n")) {
4141: break;
4142: }
4143: }
4144: } while ($in !== false);
4145: } else {
4146:
4147: if (!$len) {
4148: return $data;
4149: }
4150:
4151: $old_len = $len;
4152:
4153:
4154: if (isset($this->_temp['fetchcmd'])) {
4155: $data = fopen('php://temp', 'r+');
4156: $stream = true;
4157: }
4158:
4159: while ($len && !feof($this->_stream)) {
4160: $in = fread($this->_stream, min($len, 8192));
4161: if ($stream) {
4162: fwrite($data, $in);
4163: } else {
4164: $data .= $in;
4165: }
4166:
4167: $got_data = true;
4168:
4169: $in_len = strlen($in);
4170: if ($in_len > $len) {
4171: break;
4172: }
4173: $len -= $in_len;
4174: }
4175: }
4176:
4177: if (!$got_data) {
4178: $this->writeDebug("ERROR: IMAP read/timeout error.\n", Horde_Imap_Client::DEBUG_INFO);
4179: $this->logout();
4180: $this->_exception(Horde_Imap_Client_Translation::t("Error when communicating with the mail server."), 'SERVER_READERROR');
4181: }
4182:
4183: if ($this->_debug) {
4184: if ($binary) {
4185: $this->writeDebug('[BINARY DATA - ' . $old_len . ' bytes]' . "\n", Horde_Imap_Client::DEBUG_SERVER);
4186: } elseif (!is_null($len) &&
4187: empty($this->_params['debug_literal'])) {
4188: $this->writeDebug('[LITERAL DATA - ' . $old_len . ' bytes]' . "\n", Horde_Imap_Client::DEBUG_SERVER);
4189: } elseif ($stream) {
4190: $this->writeDebug(rtrim($this->_getString($data)) . "\n", Horde_Imap_Client::DEBUG_SERVER);
4191: } else {
4192: $this->writeDebug(rtrim($data) . "\n", Horde_Imap_Client::DEBUG_SERVER);
4193: }
4194: }
4195:
4196: return is_null($len) ? rtrim($data) : $data;
4197: }
4198:
4199: 4200: 4201: 4202: 4203:
4204: protected function _tokenizeData($line)
4205: {
4206: if (empty($this->_temp['token'])) {
4207: $c = $this->_temp['token'] = new stdClass;
4208: $c->in_quote = false;
4209: $c->out = array();
4210: $c->paren = 0;
4211: $c->ptr = array(&$c->out);
4212: } else {
4213: $c = $this->_temp['token'];
4214: }
4215:
4216: $tmp = '';
4217:
4218: for ($i = 0, $len = strlen($line); $i < $len; ++$i) {
4219: $char = $line[$i];
4220: switch ($char) {
4221: case '"':
4222: if ($c->in_quote) {
4223: if ($i && ($line[$i - 1] != '\\')) {
4224: $c->in_quote = false;
4225: $c->ptr[$c->paren][] = stripcslashes($tmp);
4226: $tmp = '';
4227: } else {
4228: $tmp .= $char;
4229: }
4230: } else {
4231: $c->in_quote = true;
4232: }
4233: break;
4234:
4235: default:
4236: if ($c->in_quote) {
4237: $tmp .= $char;
4238: break;
4239: }
4240:
4241: switch ($char) {
4242: case '(':
4243: $c->ptr[$c->paren][] = array();
4244: $c->ptr[$c->paren + 1] = &$c->ptr[$c->paren][count($c->ptr[$c->paren]) - 1];
4245: ++$c->paren;
4246: break;
4247:
4248: case ')':
4249: if (strlen($tmp)) {
4250: $c->ptr[$c->paren][] = $tmp;
4251: $tmp = '';
4252: }
4253: --$c->paren;
4254: break;
4255:
4256: case ' ':
4257: if (strlen($tmp)) {
4258: $c->ptr[$c->paren][] = $tmp;
4259: $tmp = '';
4260: }
4261: break;
4262:
4263: default:
4264: $tmp .= $char;
4265: break;
4266: }
4267: break;
4268: }
4269: }
4270:
4271: if (strlen($tmp)) {
4272: $c->ptr[$c->paren][] = $tmp;
4273: }
4274: }
4275:
4276: 4277: 4278: 4279: 4280: 4281: 4282: 4283:
4284: protected function _parseResponse($tag, $ignore)
4285: {
4286: while ($ob = $this->_getLine()) {
4287: if (($ob['type'] == 'tagged') && ($ob['tag'] == $tag)) {
4288:
4289:
4290: $this->_parseStatusResponse($ob);
4291:
4292:
4293:
4294: switch ($ob['response']) {
4295: case 'BAD':
4296: case 'NO':
4297: if ($ignore) {
4298: return;
4299: }
4300:
4301: $this->_temp['parseresperr'] = $ob;
4302:
4303: if (empty($this->_temp['parsestatuserr'])) {
4304: $errcode = 0;
4305: $err = Horde_Imap_Client_Translation::t("IMAP error reported by server.");
4306: if (!empty($ob['line'])) {
4307: $err = array($err, trim($ob['line']));
4308: }
4309: } else {
4310: list($errcode, $err) = $this->_temp['parsestatuserr'];
4311: }
4312:
4313: $this->_exception($err, $errcode);
4314: }
4315:
4316:
4317: $tmp = $this->_temp['fetchresp'];
4318: if (!empty($tmp->uid)) {
4319: $this->_updateCache($tmp->uid);
4320: } elseif (!empty($tmp->seq)) {
4321: $this->_updateCache($tmp->seq, array(
4322: 'seq' => true
4323: ));
4324: }
4325: break;
4326: }
4327:
4328: $this->_parseServerResponse($ob);
4329: }
4330: }
4331:
4332: 4333: 4334: 4335: 4336: 4337:
4338: protected function _parseServerResponse($ob)
4339: {
4340: if (!empty($ob['response'])) {
4341: $this->_parseStatusResponse($ob);
4342: } elseif ($ob['token']) {
4343:
4344:
4345: switch (strtoupper($ob['token'][0])) {
4346: case 'CAPABILITY':
4347: $this->_parseCapability(array_slice($ob['token'], 1));
4348: break;
4349:
4350: case 'LIST':
4351: case 'LSUB':
4352: $this->_parseList($ob['token'], 1);
4353: break;
4354:
4355: case 'STATUS':
4356:
4357: $this->_parseStatus($ob['token'][1], $ob['token'][2]);
4358: break;
4359:
4360: case 'SEARCH':
4361: case 'SORT':
4362:
4363:
4364: $this->_parseSearch(array_slice($ob['token'], 1));
4365: break;
4366:
4367: case 'ESEARCH':
4368:
4369: $this->_parseEsearch(array_slice($ob['token'], 1));
4370: break;
4371:
4372: case 'FLAGS':
4373: $this->_temp['mailbox']['flags'] = array_map('strtolower', $ob['token'][1]);
4374: break;
4375:
4376: case 'QUOTA':
4377: $this->_parseQuota(array_slice($ob['token'], 1));
4378: break;
4379:
4380: case 'QUOTAROOT':
4381:
4382:
4383: break;
4384:
4385: case 'NAMESPACE':
4386: $this->_parseNamespace(array_slice($ob['token'], 1));
4387: break;
4388:
4389: case 'THREAD':
4390: foreach (array_slice($ob['token'], 1) as $val) {
4391: $this->_parseThread($val);
4392: }
4393: break;
4394:
4395: case 'ACL':
4396: $this->_parseACL(array_slice($ob['token'], 1));
4397: break;
4398:
4399: case 'LISTRIGHTS':
4400: $this->_parseListRights(array_slice($ob['token'], 1));
4401: break;
4402:
4403: case 'MYRIGHTS':
4404: $this->_parseMyRights(array_slice($ob['token'], 1));
4405: break;
4406:
4407: case 'ID':
4408:
4409: $this->_parseID(array_slice($ob['token'], 1));
4410: break;
4411:
4412: case 'ENABLED':
4413:
4414: $this->_parseEnabled(array_slice($ob['token'], 1));
4415: break;
4416:
4417: case 'LANGUAGE':
4418:
4419: $this->_parseLanguage(array_slice($ob['token'], 1));
4420: break;
4421:
4422: case 'COMPARATOR':
4423:
4424: $this->_parseComparator(array_slice($ob['token'], 1));
4425: break;
4426:
4427: case 'VANISHED':
4428:
4429: $this->_parseVanished(array_slice($ob['token'], 1));
4430: break;
4431:
4432: case 'ANNOTATION':
4433: case 'METADATA':
4434:
4435: $this->_parseMetadata($ob['token']);
4436: break;
4437:
4438: default:
4439:
4440: $type = strtoupper($ob['token'][1]);
4441: switch ($type) {
4442: case 'EXISTS':
4443: case 'RECENT':
4444:
4445:
4446: $this->_temp['mailbox'][$type == 'RECENT' ? 'recent' : 'messages'] = $ob['token'][0];
4447: break;
4448:
4449: case 'EXPUNGE':
4450:
4451: $this->_parseExpunge($ob['token'][0]);
4452: break;
4453:
4454: case 'FETCH':
4455:
4456: $rest = array_slice($ob['token'], 2);
4457: $this->_parseFetch($ob['token'][0], reset($rest));
4458: break;
4459: }
4460: break;
4461: }
4462: }
4463: }
4464:
4465: 4466: 4467: 4468: 4469:
4470: protected function _parseStatusResponse($ob)
4471: {
4472: $response = $this->_parseResponseText($ob['line']);
4473: if (!isset($response->code)) {
4474: return;
4475: }
4476:
4477: $this->_temp['parsestatuserr'] = null;
4478:
4479: switch ($response->code) {
4480: case 'ALERT':
4481:
4482: case 'CONTACTADMIN':
4483: if (!isset($this->_temp['alerts'])) {
4484: $this->_temp['alerts'] = array();
4485: }
4486: $this->_temp['alerts'][] = $response->text;
4487: break;
4488:
4489: case 'BADCHARSET':
4490: $this->_tokenizeData($response->data);
4491:
4492:
4493: if (!empty($this->_temp['token']->out)) {
4494: $s_charset = $this->_init['s_charset'];
4495: foreach ($this->_temp['token']->out as $val) {
4496: $s_charset[$val] = true;
4497: }
4498: $this->_setInit('s_charset', $s_charset);
4499: }
4500:
4501: $this->_temp['parsestatuserr'] = array(
4502: 'BADCHARSET',
4503: array(
4504: Horde_Imap_Client_Translation::t("Charset used in search query is not supported on the mail server."),
4505: $response->text
4506: )
4507: );
4508: break;
4509:
4510: case 'CAPABILITY':
4511: $this->_tokenizeData($response->data);
4512: $this->_parseCapability($this->_temp['token']->out);
4513: unset($this->_temp['token']);
4514: break;
4515:
4516: case 'PARSE':
4517: $this->_temp['parsestatuserr'] = array(
4518: 'PARSEERROR',
4519: array(
4520: Horde_Imap_Client_Translation::t("The mail server was unable to parse the contents of the mail message."),
4521: $response->text
4522: )
4523: );
4524: break;
4525:
4526: case 'READ-ONLY':
4527: $this->_mode = Horde_Imap_Client::OPEN_READONLY;
4528: break;
4529:
4530: case 'READ-WRITE':
4531: $this->_mode = Horde_Imap_Client::OPEN_READWRITE;
4532: break;
4533:
4534: case 'TRYCREATE':
4535:
4536: $this->_temp['trycreate'] = true;
4537: break;
4538:
4539: case 'PERMANENTFLAGS':
4540: $this->_tokenizeData($response->data);
4541: $this->_temp['mailbox']['permflags'] = array_map('strtolower', reset($this->_temp['token']->out));
4542: unset($this->_temp['token']);
4543: break;
4544:
4545: case 'UIDNEXT':
4546: case 'UIDVALIDITY':
4547: $this->_temp['mailbox'][strtolower($response->code)] = $response->data;
4548: break;
4549:
4550: case 'UNSEEN':
4551: 4552:
4553: $this->_temp['mailbox']['firstunseen'] = $response->data;
4554: break;
4555:
4556: case 'REFERRAL':
4557:
4558: $this->_temp['referral'] = $this->utils->parseUrl($response->data);
4559: break;
4560:
4561: case 'UNKNOWN-CTE':
4562:
4563: $this->_temp['parsestatuserr'] = array(
4564: 'UNKNOWNCTE',
4565: array(
4566: Horde_Imap_Client_Translation::t("The mail server was unable to parse the contents of the mail message."),
4567: $response->text
4568: )
4569: );
4570: break;
4571:
4572: case 'APPENDUID':
4573: case 'COPYUID':
4574:
4575:
4576:
4577: $parts = explode(' ', $response->data);
4578:
4579: if ($this->_temp['uidplusmbox']->equals($this->_selected) &&
4580: ($this->_temp['mailbox']['uidvalidity'] != $parts[0])) {
4581: $this->_temp['mailbox'] = array('uidvalidity' => $parts[0]);
4582: $this->_temp['searchnotsaved'] = true;
4583: }
4584:
4585:
4586: $this->_updateCache(array(), array(
4587: 'mailbox' => $this->_temp['uidplusmbox'],
4588: 'uidvalid' => $parts[0]
4589: ));
4590:
4591: if ($response->code == 'APPENDUID') {
4592: $this->_temp['appenduid'] = array_merge($this->_temp['appenduid'], $this->utils->fromSequenceString($parts[1]));
4593: } else {
4594: $this->_temp['copyuid'] = array_combine($this->utils->fromSequenceString($parts[1]), $this->utils->fromSequenceString($parts[2]));
4595: $this->_temp['copyuidvalid'] = $parts[0];
4596: }
4597: break;
4598:
4599: case 'UIDNOTSTICKY':
4600:
4601: $this->_temp['mailbox']['uidnotsticky'] = true;
4602: break;
4603:
4604: case 'BADURL':
4605:
4606: $this->_temp['parsestatuserr'] = array(
4607: 'CATENATE_BADURL',
4608: array(
4609: Horde_Imap_Client_Translation::t("Could not save message on server."),
4610: $response->text
4611: )
4612: );
4613: break;
4614:
4615: case 'TOOBIG':
4616:
4617: $this->_temp['parsestatuserr'] = array(
4618: 'CATENATE_TOOBIG',
4619: array(
4620: Horde_Imap_Client_Translation::t("Could not save message data because it is too large."),
4621: $response->text
4622: )
4623: );
4624: break;
4625:
4626: case 'HIGHESTMODSEQ':
4627:
4628: $this->_temp['mailbox']['highestmodseq'] = $response->data;
4629: break;
4630:
4631: case 'NOMODSEQ':
4632:
4633: $this->_temp['mailbox']['highestmodseq'] = 0;
4634: break;
4635:
4636: case 'MODIFIED':
4637:
4638: $this->_temp['modified']->add($response->data);
4639: break;
4640:
4641: case 'CLOSED':
4642:
4643: if (isset($this->_temp['qresyncmbox'])) {
4644: $this->_temp['mailbox'] = array(
4645: 'name' => $this->_temp['qresyncmbox']
4646: );
4647: $this->_selected = $this->_temp['qresyncmbox'];
4648: }
4649: break;
4650:
4651: case 'NOTSAVED':
4652:
4653: $this->_temp['searchnotsaved'] = true;
4654: break;
4655:
4656: case 'BADCOMPARATOR':
4657:
4658: $this->_temp['parsestatuserr'] = array(
4659: 'BADCOMPARATOR',
4660: array(
4661: Horde_Imap_Client_Translation::t("The comparison algorithm was not recognized by the server."),
4662: $response->text
4663: )
4664: );
4665: break;
4666:
4667: case 'METADATA':
4668: $this->_tokenizeData($response->data);
4669:
4670: switch (reset($this->_temp['token']->out)) {
4671: case 'LONGENTRIES':
4672:
4673: $this->_temp['metadata']['*longentries'] = intval(end($this->_temp['token']->out));
4674: break;
4675:
4676: case 'MAXSIZE':
4677:
4678: $this->_temp['parsestatuserr'] = array(
4679: 'METADATA_MAXSIZE',
4680: array(
4681: Horde_Imap_Client_Translation::t("The metadata item could not be saved because it is too large."),
4682: intval(end($this->_temp['token']->out))
4683: )
4684: );
4685: break;
4686:
4687: case 'NOPRIVATE':
4688:
4689: $this->_temp['parsestatuserr'] = array(
4690: 'METADATA_NOPRIVATE',
4691: array(
4692: Horde_Imap_Client_Translation::t("The metadata item could not be saved because the server does not support private annotations."),
4693: $response->text
4694: )
4695: );
4696: break;
4697:
4698: case 'TOOMANY':
4699:
4700: $this->_temp['parsestatuserr'] = array(
4701: 'METADATA_TOOMANY',
4702: array(
4703: Horde_Imap_Client_Translation::t("The metadata item could not be saved because the maximum number of annotations has been exceeded."),
4704: $response->text
4705: )
4706: );
4707: break;
4708: }
4709: break;
4710:
4711: case 'UNAVAILABLE':
4712:
4713: $this->_temp['loginerr'] = 'LOGIN_UNAVAILABLE';
4714: $this->_temp['loginerrmsg'] = Horde_Imap_Client_Translation::t("Remote server is temporarily unavailable.");
4715: break;
4716:
4717: case 'AUTHENTICATIONFAILED':
4718:
4719: $this->_temp['loginerr'] = 'LOGIN_AUTHENTICATIONFAILED';
4720: $this->_temp['loginerrmsg'] = Horde_Imap_Client_Translation::t("Authentication failed.");
4721: break;
4722:
4723: case 'AUTHORIZATIONFAILED':
4724:
4725: $this->_temp['loginerr'] = 'LOGIN_AUTHORIZATIONFAILED';
4726: $this->_temp['loginerrmsg'] = Horde_Imap_Client_Translation::t("Authentication was successful, but authorization failed.");
4727: break;
4728:
4729: case 'EXPIRED':
4730:
4731: $this->_temp['loginerr'] = 'LOGIN_EXPIRED';
4732: $this->_temp['loginerrmsg'] = Horde_Imap_Client_Translation::t("Authentication credentials have expired.");
4733: break;
4734:
4735: case 'PRIVACYREQUIRED':
4736:
4737: $this->_temp['loginerr'] = 'LOGIN_PRIVACYREQUIRED';
4738: $this->_temp['loginerrmsg'] = Horde_Imap_Client_Translation::t("Operation failed due to a lack of a secure connection.");
4739: break;
4740:
4741: case 'NOPERM':
4742:
4743: $this->_temp['parsestatuserr'] = array(
4744: 'NOPERM',
4745: array(
4746: Horde_Imap_Client_Translation::t("You do not have adequate permissions to carry out this operation."),
4747: $response->text
4748: )
4749: );
4750: break;
4751:
4752: case 'INUSE':
4753:
4754: $this->_temp['parsestatuserr'] = array(
4755: 'INUSE',
4756: array(
4757: Horde_Imap_Client_Translation::t("There was a temporary issue when attempting this operation. Please try again later."),
4758: $response->text
4759: )
4760: );
4761: break;
4762:
4763: case 'EXPUNGEISSUED':
4764:
4765: $this->_temp['expungeissued'] = true;
4766: break;
4767:
4768: case 'CORRUPTION':
4769:
4770: $this->_temp['parsestatuserr'] = array(
4771: 'CORRUPTION',
4772: array(
4773: Horde_Imap_Client_Translation::t("The mail server is reporting corrupt data in your mailbox."),
4774: $response->text
4775: )
4776: );
4777: break;
4778:
4779: case 'SERVERBUG':
4780: case 'CLIENTBUG':
4781: case 'CANNOT':
4782:
4783: $this->writeDebug("ERROR: mail server explicitly reporting an error.\n", Horde_Imap_Client::DEBUG_INFO);
4784: break;
4785:
4786: case 'LIMIT':
4787:
4788: $this->_temp['parsestatuserr'] = array(
4789: 'LIMIT',
4790: array(
4791: Horde_Imap_Client_Translation::t("The mail server has denied the request."),
4792: $response->text
4793: )
4794: );
4795: break;
4796:
4797: case 'OVERQUOTA':
4798:
4799: $this->_temp['parsestatuserr'] = array(
4800: 'OVERQUOTA',
4801: array(
4802: Horde_Imap_Client_Translation::t("The operation failed because the quota has been exceeded on the mail server."),
4803: $response->text
4804: )
4805: );
4806: break;
4807:
4808: case 'ALREADYEXISTS':
4809:
4810: $this->_temp['parsestatuserr'] = array(
4811: 'ALREADYEXISTS',
4812: array(
4813: Horde_Imap_Client_Translation::t("The object could not be created because it already exists."),
4814: $response->text
4815: )
4816: );
4817: break;
4818:
4819: case 'NONEXISTENT':
4820:
4821: $this->_temp['parsestatuserr'] = array(
4822: 'NONEXISTENT',
4823: array(
4824: Horde_Imap_Client_Translation::t("The object could not be deleted because it does not exist."),
4825: $response->text
4826: )
4827: );
4828: break;
4829:
4830: case 'USEATTR':
4831:
4832: $this->_temp['parsestatuserr'] = array(
4833: 'USEATTR',
4834: array(
4835: Horde_Imap_Client_Translation::t("The special-use attribute requested for the mailbox is not supported."),
4836: $response->text
4837: )
4838: );
4839: break;
4840:
4841: case 'XPROXYREUSE':
4842:
4843: $this->_temp['proxyreuse'] = true;
4844: break;
4845:
4846: default:
4847:
4848: break;
4849: }
4850: }
4851:
4852: }
4853: