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