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: class Horde_Kolab_Storage_Driver_Rcube
29: extends Horde_Kolab_Storage_Driver_Base
30: {
31: 32: 33: 34: 35:
36: private $_debug_log;
37:
38: 39: 40: 41: 42:
43: public function debugLog($driver, $message)
44: {
45: fwrite($this->_debug_log, $message . "\n");
46: }
47:
48: 49: 50:
51: public function __destruct()
52: {
53: if (is_resource($this->_debug_log)) {
54: fflush($this->_debug_log);
55: fclose($this->_debug_log);
56: $this->_debug_log = null;
57: }
58: }
59:
60: 61: 62: 63: 64:
65: public function createBackend()
66: {
67: $config = $this->getParams();
68: $client = new rcube_imap_generic();
69: if (isset($config['debug'])) {
70: if ($config['debug'] == 'STDOUT') {
71: $client->setDebug(true);
72: } else {
73: $this->_debug_log = fopen($config['debug'], 'a');
74: $client->setDebug(true, array($this, 'debugLog'));
75: }
76: }
77: $client->connect(
78: $config['host'], $config['username'], $config['password'],
79: array(
80: 'ssl_mode' => $config['secure'],
81: 'port' => $config['port'],
82: 'timeout' => 0,
83: 'force_caps' => false,
84: )
85: );
86: return $client;
87: }
88:
89: 90: 91: 92: 93:
94: public function listFolders()
95: {
96: return $this->decodeList($this->getBackend()->listMailboxes('', '*'));
97: }
98:
99: 100: 101: 102: 103: 104: 105:
106: public function create($folder)
107: {
108: $this->getBackend()->createFolder($this->encodePath($folder));
109: if ($this->getBackend()->errornum != 0) {
110: throw new Horde_Kolab_Storage_Exception(
111: sprintf(
112: Horde_Kolab_Storage_Translation::t(
113: "Creating folder %s failed. Error: %s"
114: ),
115: $folder,
116: $this->getBackend()->error
117: )
118: );
119: }
120: }
121:
122: 123: 124: 125: 126: 127: 128:
129: public function delete($folder)
130: {
131: $this->getBackend()->deleteFolder($this->encodePath($folder));
132: if ($this->getBackend()->errornum != 0) {
133: throw new Horde_Kolab_Storage_Exception(
134: sprintf(
135: Horde_Kolab_Storage_Translation::t(
136: "Deleting folder %s failed. Error: %s"
137: ),
138: $folder,
139: $this->getBackend()->error
140: )
141: );
142: }
143: }
144:
145: 146: 147: 148: 149: 150: 151: 152:
153: public function rename($old, $new)
154: {
155: $this->getBackend()->renameFolder(
156: $this->encodePath($old),
157: $this->encodePath($new)
158: );
159: if ($this->getBackend()->errornum != 0) {
160: throw new Horde_Kolab_Storage_Exception(
161: sprintf(
162: Horde_Kolab_Storage_Translation::t(
163: "Renaming folder %s to %s failed. Error: %s"
164: ),
165: $old,
166: $new,
167: $this->getBackend()->error
168: )
169: );
170: }
171: }
172:
173: 174: 175: 176: 177:
178: public function hasAclSupport()
179: {
180: return $this->getBackend()->getCapability('ACL');
181: }
182:
183: 184: 185: 186: 187: 188: 189:
190: public function getAcl($folder)
191: {
192: $acl = $this->getBackend()->getACL($this->encodePath($folder));
193: if ($this->getBackend()->errornum != 0) {
194: throw new Horde_Kolab_Storage_Exception(
195: sprintf(
196: Horde_Kolab_Storage_Translation::t(
197: "Failed reading ACL on folder %s. Error: %s"
198: ),
199: $folder,
200: $this->getBackend()->error
201: )
202: );
203: }
204: $result = array();
205: foreach ($acl as $user => $rights) {
206: $result[$user] = join('', $rights);
207: }
208: return $result;
209: }
210:
211: 212: 213: 214: 215: 216: 217:
218: public function getMyAcl($folder)
219: {
220: $result = $this->getBackend()->myRights($this->encodePath($folder));
221: if ($this->getBackend()->errornum != 0) {
222: throw new Horde_Kolab_Storage_Exception(
223: sprintf(
224: Horde_Kolab_Storage_Translation::t(
225: "Failed reading user rights on folder %s. Error: %s"
226: ),
227: $folder,
228: $this->getBackend()->error
229: )
230: );
231: }
232: return join('', $result);
233: }
234:
235: 236: 237: 238: 239: 240: 241: 242: 243:
244: public function setAcl($folder, $user, $acl)
245: {
246: $this->getBackend()->setACL($this->encodePath($folder), $user, $acl);
247: if ($this->getBackend()->errornum != 0) {
248: throw new Horde_Kolab_Storage_Exception(
249: sprintf(
250: Horde_Kolab_Storage_Translation::t(
251: "Failed setting ACL on folder %s for user %s to %s. Error: %s"
252: ),
253: $folder,
254: $user,
255: $acl,
256: $this->getBackend()->error
257: )
258: );
259: }
260: }
261:
262: 263: 264: 265: 266: 267: 268: 269:
270: public function deleteAcl($folder, $user)
271: {
272: $this->getBackend()->deleteACL($this->encodePath($folder), $user);
273: if ($this->getBackend()->errornum != 0) {
274: throw new Horde_Kolab_Storage_Exception(
275: sprintf(
276: Horde_Kolab_Storage_Translation::t(
277: "Failed deleting ACL on folder %s for user %s. Error: %s"
278: ),
279: $folder,
280: $user,
281: $this->getBackend()->error
282: )
283: );
284: }
285: }
286:
287: 288: 289: 290: 291: 292: 293: 294:
295: public function listAnnotation($annotation)
296: {
297: list($entry, $value) = $this->_getAnnotateMoreEntry($annotation);
298: $result = $this->getBackend()->getAnnotation('*', $entry, $value);
299: if (empty($result)) {
300: return array();
301: }
302: $data = array();
303: foreach ($result as $folder => $annotations) {
304: if (isset($annotations[$annotation])) {
305: $data[$folder] = $annotations[$annotation];
306: }
307: }
308: return $this->decodeListKeys($data);
309: }
310:
311: 312: 313: 314: 315: 316: 317: 318:
319: public function getAnnotation($folder, $annotation)
320: {
321: list($attr, $type) = $this->_getAnnotateMoreEntry($annotation);
322: $result = $this->getBackend()->getAnnotation(
323: $this->encodePath($folder), $attr, $type
324: );
325: if ($this->getBackend()->errornum != 0) {
326: throw new Horde_Kolab_Storage_Exception(
327: sprintf(
328: Horde_Kolab_Storage_Translation::t(
329: "Setting annotation %s[%s] on folder %s failed. Error: %s"
330: ),
331: $attr,
332: $type,
333: $folder,
334: $this->getBackend()->error
335: )
336: );
337: }
338: return $result[$folder][$annotation];
339: }
340:
341: 342: 343: 344: 345: 346: 347: 348: 349:
350: public function setAnnotation($folder, $annotation, $value)
351: {
352: list($attr, $type) = $this->_getAnnotateMoreEntry($annotation);
353: $this->getBackend()->setAnnotation(
354: $this->encodePath($folder), array(array($attr, $type, $value))
355: );
356: if ($this->getBackend()->errornum != 0) {
357: throw new Horde_Kolab_Storage_Exception(
358: sprintf(
359: Horde_Kolab_Storage_Translation::t(
360: "Setting annotation %s[%s] on folder %s to %s failed. Error: %s"
361: ),
362: $attr,
363: $type,
364: $folder,
365: $value,
366: $this->getBackend()->error
367: )
368: );
369: }
370: }
371:
372: 373: 374: 375: 376:
377: public function getNamespace()
378: {
379: if ($this->getBackend()->getCapability('NAMESPACE') === true) {
380: $namespaces = array();
381: foreach ($this->getBackend()->getNamespace() as $type => $elements) {
382: if (is_array($elements)) {
383: foreach ($elements as $namespace) {
384: $namespace['name'] = $namespace[0];
385: $namespace['delimiter'] = $namespace[1];
386: $namespace['type'] = $type;
387: $namespaces[] = $namespace;
388: }
389: }
390: }
391: $this->_namespace = $this->getFactory()->createNamespace('imap', $this->getAuth(), $namespaces);
392: }
393: return parent::getNamespace();
394: }
395:
396: 397: 398: 399: 400: 401: 402:
403: public function select($folder)
404: {
405: $this->getBackend()->select($this->encodePath($folder));
406: if ($this->getBackend()->errornum != 0) {
407: throw new Horde_Kolab_Storage_Exception(
408: sprintf(
409: Horde_Kolab_Storage_Translation::t(
410: "Selecting folder %s failed. Error: %s"
411: ),
412: $folder,
413: $this->getBackend()->error
414: )
415: );
416: }
417: }
418:
419: 420: 421: 422: 423: 424: 425:
426: public function status($folder)
427: {
428: $result = $this->getBackend()->status(
429: $this->encodePath($folder),
430: array('UIDVALIDITY', 'UIDNEXT')
431: );
432: if ($this->getBackend()->errornum != 0) {
433: throw new Horde_Kolab_Storage_Exception(
434: sprintf(
435: Horde_Kolab_Storage_Translation::t(
436: "Retrieving the status for folder %s failed. Error: %s"
437: ),
438: $folder,
439: $this->getBackend()->error
440: )
441: );
442: }
443: return array(
444: 'uidvalidity' => $result['UIDVALIDITY'],
445: 'uidnext' => $result['UIDNEXT']
446: );
447: }
448:
449: 450: 451: 452: 453: 454: 455:
456: public function getUids($folder)
457: {
458: $uids = $this->getBackend()->search(
459: $this->encodePath($folder),
460: 'UNDELETED',
461: true
462: );
463: if ($this->getBackend()->errornum != 0) {
464: throw new Horde_Kolab_Storage_Exception(
465: sprintf(
466: Horde_Kolab_Storage_Translation::t(
467: "Failed retrieving UIDs for folder %s. Error: %s"
468: ),
469: $folder,
470: $this->getBackend()->error
471: )
472: );
473: }
474: if (!is_array($uids)) {
475: $uids = array();
476: }
477: return $uids;
478: }
479:
480: 481: 482: 483: 484: 485: 486: 487: 488:
489: public function fetchComplete($folder, $uid)
490: {
491: $msg = $this->getBackend()->handlePartBody(
492: $this->encodePath($folder), $uid, true, '', null, false
493: );
494: if ($this->getBackend()->errornum != 0) {
495: throw new Horde_Kolab_Storage_Exception(
496: sprintf(
497: Horde_Kolab_Storage_Translation::t(
498: "Failed retrieving message %s in folder %s. Error: %s"
499: ),
500: $uid,
501: $folder,
502: $this->getBackend()->error
503: )
504: );
505: }
506:
507: return array(
508: Horde_Mime_Headers::parseHeaders($msg),
509: Horde_Mime_Part::parseMessage(
510: Horde_Mime_Part::getRawPartText($msg, 'body', 0)
511: )
512: );
513: }
514:
515: 516: 517: 518: 519: 520: 521: 522: 523:
524: public function fetchStructure($folder, $uids)
525: {
526: $result = array();
527: foreach ($uids as $uid) {
528: $structure = $this->getBackend()->tokenizeResponse(
529: $this->getBackend()->fetchStructureString(
530: $this->encodePath($folder), $uid, true
531: )
532: );
533: if ($this->getBackend()->errornum != 0) {
534: throw new Horde_Kolab_Storage_Exception(
535: sprintf(
536: Horde_Kolab_Storage_Translation::t(
537: "Failed retrieving structure of message %s in folder %s. Error: %s"
538: ),
539: $uid,
540: $folder,
541: $this->getBackend()->error
542: )
543: );
544: }
545: $ob = $this->_parseStructure($structure[0]);
546: $ob->buildMimeIds();
547: $result[$uid]['structure'] = $ob;
548: }
549: return $result;
550: }
551:
552: 553: 554: 555: 556: 557: 558: 559: 560:
561: public function fetchBodypart($folder, $uid, $id)
562: {
563: $resource = fopen('php://temp', 'r+');
564: $this->getBackend()->handlePartBody(
565: $this->encodePath($folder), $uid, true, $id, null, false, $resource
566: );
567: if ($this->getBackend()->errornum != 0) {
568: throw new Horde_Kolab_Storage_Exception(
569: sprintf(
570: Horde_Kolab_Storage_Translation::t(
571: "Failed retrieving mime part %s of message %s in folder %s. Error: %s"
572: ),
573: $id,
574: $uid,
575: $folder,
576: $this->getBackend()->error
577: )
578: );
579: }
580: return $resource;
581: }
582:
583: 584: 585: 586: 587: 588: 589: 590: 591:
592: public function appendMessage($folder, $msg)
593: {
594: rewind($msg);
595: $this->getBackend()->append(
596: $this->encodePath($folder), stream_get_contents($msg)
597: );
598: if ($this->getBackend()->errornum != 0) {
599: throw new Horde_Kolab_Storage_Exception(
600: sprintf(
601: Horde_Kolab_Storage_Translation::t(
602: "Failed appending new message to folder %s. Error: %s"
603: ),
604: $folder,
605: $this->getBackend()->error
606: )
607: );
608: }
609: return true;
610: }
611:
612: 613: 614: 615: 616: 617: 618: 619:
620: public function deleteMessages($folder, $uids)
621: {
622: $this->getBackend()->delete(
623: $this->encodePath($folder), $uids
624: );
625: if ($this->getBackend()->errornum != 0) {
626: throw new Horde_Kolab_Storage_Exception(
627: sprintf(
628: Horde_Kolab_Storage_Translation::t(
629: "Failed deleting messages from folder %s. Error: %s"
630: ),
631: $folder,
632: $this->getBackend()->error
633: )
634: );
635: }
636: }
637:
638: 639: 640: 641: 642: 643: 644: 645: 646:
647: public function moveMessage($uid, $old_folder, $new_folder)
648: {
649: $this->getBackend()->move(
650: $uid,
651: $this->encodePath($old_folder),
652: $this->encodePath($new_folder)
653: );
654: if ($this->getBackend()->errornum != 0) {
655: throw new Horde_Kolab_Storage_Exception(
656: sprintf(
657: Horde_Kolab_Storage_Translation::t(
658: "Failed moving message %s from folder %s to folder %s. Error: %s"
659: ),
660: $uid,
661: $old_folder,
662: $new_folder,
663: $this->getBackend()->error
664: )
665: );
666: }
667: $this->expunge($old_folder);
668: }
669:
670: 671: 672: 673: 674:
675: public function expunge($folder)
676: {
677: $this->getBackend()->expunge($this->encodePath($folder));
678: if ($this->getBackend()->errornum != 0) {
679: throw new Horde_Kolab_Storage_Exception(
680: sprintf(
681: Horde_Kolab_Storage_Translation::t(
682: "Failed expunging folder %s. Error: %s"
683: ),
684: $folder,
685: $this->getBackend()->error
686: )
687: );
688: }
689: }
690:
691: 692: 693: 694: 695: 696: 697:
698: 699: 700: 701: 702: 703: 704: 705:
706: protected function _parseStructure($data)
707: {
708: $ob = new Horde_Mime_Part();
709:
710:
711: if (is_array($data[0])) {
712:
713: for ($i = 0, $cnt = count($data); $i < $cnt; ++$i) {
714: if (!is_array($data[$i])) {
715: break;
716: }
717: $ob->addPart($this->_parseStructure($data[$i]));
718: }
719:
720:
721:
722: $ob->setType('multipart/' . $data[$i]);
723:
724:
725:
726:
727:
728: if (isset($data[++$i]) && is_array($data[$i])) {
729: foreach ($this->_parseStructureParams($data[$i], 'content-type') as $key => $val) {
730: $ob->setContentTypeParameter($key, $val);
731: }
732: }
733:
734:
735: if (isset($data[++$i]) && is_array($data[$i])) {
736: $ob->setDisposition($data[$i][0]);
737:
738: foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) {
739: $ob->setDispositionParameter($key, $val);
740: }
741: }
742:
743:
744:
745: if (isset($data[++$i])) {
746: $ob->setLanguage($data[$i]);
747: }
748:
749:
750:
751:
752: } else {
753: $ob->setType($data[0] . '/' . $data[1]);
754:
755: foreach ($this->_parseStructureParams($data[2], 'content-type') as $key => $val) {
756: $ob->setContentTypeParameter($key, $val);
757: }
758:
759: if ($data[3] !== null) {
760: $ob->setContentId($data[3]);
761: }
762:
763: if ($data[4] !== null) {
764: $ob->setDescription(Horde_Mime::decode($data[4], 'UTF-8'));
765: }
766:
767: if ($data[5] !== null) {
768: $ob->setTransferEncoding($data[5]);
769: }
770:
771: if ($data[6] !== null) {
772: $ob->setBytes($data[6]);
773: }
774:
775:
776:
777: switch ($ob->getPrimaryType()) {
778: case 'message':
779: if ($ob->getSubType() == 'rfc822') {
780:
781: $ob->addPart($this->_parseStructure($data[8]));
782:
783: $i = 10;
784: } else {
785: $i = 7;
786: }
787: break;
788:
789: case 'text':
790:
791: $i = 8;
792: break;
793:
794: default:
795: $i = 7;
796: break;
797: }
798:
799:
800:
801:
802:
803:
804:
805: if (isset($data[++$i]) && is_array($data[$i])) {
806: $ob->setDisposition($data[$i][0]);
807:
808: foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) {
809: $ob->setDispositionParameter($key, $val);
810: }
811: }
812:
813:
814:
815: if (isset($data[++$i])) {
816: $ob->setLanguage($data[$i]);
817: }
818:
819:
820: }
821:
822: return $ob;
823: }
824:
825: 826: 827: 828: 829: 830: 831: 832:
833: protected function _parseStructureParams($data, $type)
834: {
835: $params = array();
836:
837: if (is_array($data)) {
838: for ($i = 0, $cnt = count($data); $i < $cnt; ++$i) {
839: $params[strtolower($data[$i])] = $data[++$i];
840: }
841: }
842:
843: $ret = Horde_Mime::decodeParam($type, $params, 'UTF-8');
844:
845: return $ret['params'];
846: }
847: }
848: