1: <?php
2: /**
3: * An IMAP based driver for accessing Kolab storage.
4: *
5: * PHP version 5
6: *
7: * @category Kolab
8: * @package Kolab_Storage
9: * @author Gunnar Wrobel <wrobel@pardus.de>
10: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
11: * @link http://pear.horde.org/index.php?package=Kolab_Storage
12: */
13:
14: /**
15: * The IMAP driver class for accessing Kolab storage.
16: *
17: * Copyright 2009-2012 Horde LLC (http://www.horde.org/)
18: *
19: * See the enclosed file COPYING for license information (LGPL). If you
20: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
21: *
22: * @category Kolab
23: * @package Kolab_Storage
24: * @author Gunnar Wrobel <wrobel@pardus.de>
25: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
26: * @link http://pear.horde.org/index.php?package=Kolab_Storage
27: */
28: class Horde_Kolab_Storage_Driver_Imap
29: extends Horde_Kolab_Storage_Driver_Base
30: {
31: /**
32: * Create the backend driver.
33: *
34: * @return mixed The backend driver.
35: */
36: public function createBackend()
37: {
38: $config = $this->getParams();
39: $config['hostspec'] = $config['host'];
40: unset($config['host']);
41: if (isset($config['debug']) && $config['debug'] == 'STDOUT') {
42: $config['debug'] = STDOUT;
43: }
44: if ($config['driver'] = 'horde') {
45: return new Horde_Imap_Client_Socket($config);
46: } else {
47: return new Horde_Imap_Client_Cclient($config);
48: }
49: }
50:
51: /**
52: * Retrieves a list of folders from the server.
53: *
54: * @return array The list of folders.
55: */
56: public function listFolders()
57: {
58: return $this->decodeList(
59: $this->getBackend()->listMailboxes(
60: '*', Horde_Imap_Client::MBOX_ALL, array('flat' => true)
61: )
62: );
63: }
64:
65: /**
66: * Create the specified folder.
67: *
68: * @param string $folder The folder to create.
69: *
70: * @return NULL
71: */
72: public function create($folder)
73: {
74: return $this->getBackend()->createMailbox($folder);
75: }
76:
77: /**
78: * Delete the specified folder.
79: *
80: * @param string $folder The folder to delete.
81: *
82: * @return NULL
83: */
84: public function delete($folder)
85: {
86: $this->getBackend()->deleteMailbox($folder);
87: }
88:
89: /**
90: * Rename the specified folder.
91: *
92: * @param string $old The folder to rename.
93: * @param string $new The new name of the folder.
94: *
95: * @return NULL
96: */
97: public function rename($old, $new)
98: {
99: $this->getBackend()->renameMailbox($old, $new);
100: }
101:
102: /**
103: * Does the backend support ACL?
104: *
105: * @return boolean True if the backend supports ACLs.
106: */
107: public function hasAclSupport()
108: {
109: $this->getBackend()->login();
110: return $this->getBackend()->queryCapability('ACL');
111: }
112:
113: /**
114: * Retrieve the access rights for a folder.
115: *
116: * @param Horde_Kolab_Storage_Folder $folder The folder to retrieve the ACL for.
117: *
118: * @return An array of rights.
119: */
120: public function getAcl($folder)
121: {
122: $acl = $this->getBackend()->getACL($folder);
123:
124: $result = array();
125: foreach ($acl as $user => $rights) {
126: $result[$user] = strval($rights);
127: }
128:
129: return $result;
130: }
131:
132: /**
133: * Retrieve the access rights the current user has on a folder.
134: *
135: * @param string $folder The folder to retrieve the user ACL for.
136: *
137: * @return string The user rights.
138: */
139: public function getMyAcl($folder)
140: {
141: return strval($this->getBackend()->getMyACLRights($folder));
142: }
143:
144: /**
145: * Set the access rights for a folder.
146: *
147: * @param string $folder The folder to act upon.
148: * @param string $user The user to set the ACL for.
149: * @param string $acl The ACL.
150: *
151: * @return NULL
152: */
153: public function setAcl($folder, $user, $acl)
154: {
155: $this->getBackend()->setACL($folder, $user, array('rights' => $acl));
156: }
157:
158: /**
159: * Delete the access rights for user on a folder.
160: *
161: * @param string $folder The folder to act upon.
162: * @param string $user The user to delete the ACL for
163: *
164: * @return NULL
165: */
166: public function deleteAcl($folder, $user)
167: {
168: $this->getBackend()->setACL($folder, $user, array('remove' => true));
169: }
170:
171: /**
172: * Retrieves the specified annotation for the complete list of folders.
173: *
174: * @param string $annotation The name of the annotation to retrieve.
175: *
176: * @return array An associative array combining the folder names as key with
177: * the corresponding annotation value.
178: */
179: public function listAnnotation($annotation)
180: {
181: $result = $this->getBackend()->getMetadata('*', $annotation);
182: $data = array();
183: foreach ($result as $folder => $annotations) {
184: if (isset($annotations[$annotation])) {
185: $data[$folder] = $annotations[$annotation];
186: }
187: }
188: return $this->decodeListKeys($data);
189: }
190:
191: /**
192: * Fetches the annotation from a folder.
193: *
194: * @param string $folder The name of the folder.
195: * @param string $annotation The annotation to get.
196: *
197: * @return string The annotation value.
198: */
199: public function getAnnotation($folder, $annotation)
200: {
201: try {
202: $this->getBackend()->login();
203: $result = $this->getBackend()->getMetadata($folder, $annotation);
204: } catch (Exception $e) {
205: return '';
206: }
207: return isset($result[$folder][$annotation]) ? $result[$folder][$annotation] : '';
208: }
209:
210: /**
211: * Sets the annotation on a folder.
212: *
213: * @param string $folder The name of the folder.
214: * @param string $annotation The annotation to set.
215: * @param array $value The values to set
216: *
217: * @return NULL
218: */
219: public function setAnnotation($folder, $annotation, $value)
220: {
221: $this->getBackend()->login();
222: try {
223: return $this->getBackend()->setMetadata(
224: $folder, array($annotation => $value)
225: );
226: } catch (Horde_Imap_Client_Exception $e) {
227: throw new Horde_Kolab_Storage_Exception($e);
228: }
229: }
230:
231: /**
232: * Retrieve the namespace information for this connection.
233: *
234: * @return Horde_Kolab_Storage_Driver_Namespace The initialized namespace handler.
235: */
236: public function getNamespace()
237: {
238: if ($this->_namespace === null) {
239: $this->getBackend()->login();
240: if ( $this->getBackend()->queryCapability('NAMESPACE') === true) {
241: $c = array();
242: $configuration = $this->getParam('namespaces', array());
243: foreach ($this->getBackend()->getNamespaces() as $namespace) {
244: if (in_array($namespace['name'], array_keys($configuration))) {
245: $namespace = array_merge($namespace, $configuration[$namespace['name']]);
246: }
247:
248: switch ($namespace['type']) {
249: case Horde_Imap_Client::NS_PERSONAL:
250: $namespace['type'] = Horde_Kolab_Storage_Folder_Namespace::PERSONAL;
251: break;
252:
253: case Horde_Imap_Client::NS_OTHER:
254: $namespace['type'] = Horde_Kolab_Storage_Folder_Namespace::OTHER;
255: break;
256:
257: case Horde_Imap_Client::NS_SHARED:
258: $namespace['type'] = Horde_Kolab_Storage_Folder_Namespace::SHARED;
259: break;
260: }
261:
262: $c[] = $namespace;
263: }
264: $this->_namespace = $this->getFactory()->createNamespace('imap', $this->getAuth(), $c);
265: }
266: }
267: return parent::getNamespace();
268: }
269:
270: /**
271: * Opens the given folder.
272: *
273: * @param string $folder The folder to open
274: *
275: * @return NULL
276: */
277: public function select($folder, $mode = Horde_Imap_Client::OPEN_AUTO)
278: {
279: $this->getBackend()->openMailbox($folder, $mode);
280: }
281:
282: /**
283: * Returns the status of the current folder.
284: *
285: * @param string $folder Check the status of this folder.
286: *
287: * @return array An array that contains 'uidvalidity' and 'uidnext'.
288: */
289: public function status($folder)
290: {
291: // @todo: Condstore
292: return $this->getBackend()->status(
293: $folder,
294: Horde_Imap_Client::STATUS_UIDNEXT
295: | Horde_Imap_Client::STATUS_UIDVALIDITY
296: );
297: }
298:
299: /**
300: * Returns the message ids of the messages in this folder.
301: *
302: * @param string $folder Check the status of this folder.
303: *
304: * @return array The message ids.
305: */
306: public function getUids($folder)
307: {
308: $search_query = new Horde_Imap_Client_Search_Query();
309: $search_query->flag('DELETED', false);
310: $uidsearch = $this->getBackend()->search($folder, $search_query);
311: $uids = $uidsearch['match'];
312: return $uids->ids;
313: }
314:
315: /**
316: * Retrieves a complete message.
317: *
318: * @param string $folder The folder to fetch the messages from.
319: * @param array $uid The message UID.
320: *
321: * @return array The message encapsuled as an array that contains a
322: * Horde_Mime_Headers and a Horde_Mime_Part object.
323: */
324: public function fetchComplete($folder, $uid)
325: {
326: $query = new Horde_Imap_Client_Fetch_Query();
327: $query->fullText();
328:
329: $ret = $this->getBackend()->fetch(
330: $folder,
331: $query,
332: array('ids' => new Horde_Imap_Client_Ids($uid))
333: );
334: $msg = $ret[$uid]->getFullMsg();
335: return array(
336: Horde_Mime_Headers::parseHeaders($msg),
337: Horde_Mime_Part::parseMessage($msg)
338: );
339: }
340:
341: /**
342: * Retrieves the messages for the given message ids.
343: *
344: * @param string $folder The folder to fetch the messages from.
345: * @param array $uids The message UIDs.
346: *
347: * @return array An array of message structures parsed into Horde_Mime_Part
348: * instances.
349: */
350: public function fetchStructure($folder, $uids)
351: {
352: if (empty($uids)) {
353: return array();
354: }
355:
356: $query = new Horde_Imap_Client_Fetch_Query();
357: $query->structure();
358:
359: $ret = $this->getBackend()->fetch(
360: $folder,
361: $query,
362: array('ids' => new Horde_Imap_Client_Ids($uids))
363: );
364:
365: $out = array();
366: foreach (array_keys($ret) as $key) {
367: $out[$key]['structure'] = $ret[$key]->getStructure();
368: }
369:
370: return $out;
371: }
372:
373: /**
374: * Retrieves a bodypart for the given message ID and mime part ID.
375: *
376: * @param string $folder The folder to fetch the messages from.
377: * @param array $uid The message UID.
378: * @param array $id The mime part ID.
379: *
380: * @return resource|string The body part, as a stream resource or string.
381: */
382: public function fetchBodypart($folder, $uid, $id)
383: {
384: $query = new Horde_Imap_Client_Fetch_Query();
385: $query->bodyPart($id);
386:
387: $ret = $this->getBackend()->fetch(
388: $folder,
389: $query,
390: array('ids' => new Horde_Imap_Client_Ids($uid))
391: );
392:
393: return $ret[$uid]->getBodyPart($id, true);
394: }
395:
396: /**
397: * Appends a message to the given folder.
398: *
399: * @param string $folder The folder to append the message(s) to.
400: * @param resource $msg The message to append.
401: *
402: * @return mixed True or the UID of the new message in case the backend
403: * supports UIDPLUS.
404: */
405: public function appendMessage($folder, $msg)
406: {
407: $result = $this->getBackend()->append($folder, array(array('data' => $msg)));
408: return $result->ids[0];
409: }
410:
411: /**
412: * Deletes messages from the specified folder.
413: *
414: * @param string $folder The folder to delete messages from.
415: * @param integer $uids IMAP message ids.
416: *
417: * @return NULL
418: */
419: public function deleteMessages($folder, $uids)
420: {
421: return $this->getBackend()->store($folder, array(
422: 'add' => array('\\deleted'),
423: 'ids' => new Horde_Imap_Client_Ids($uids)
424: ));
425: }
426:
427: /**
428: * Moves a message to a new folder.
429: *
430: * @param integer $uid IMAP message id.
431: * @param string $old_folder Source folder.
432: * @param string $new_folder Target folder.
433: *
434: * @return NULL
435: */
436: public function moveMessage($uid, $old_folder, $new_folder)
437: {
438: $options = array('ids' => new Horde_Imap_Client_Ids($uid), 'move' => true);
439: return $this->getBackend()->copy($old_folder, $new_folder, $options);
440: }
441:
442: /**
443: * Expunges messages in the current folder.
444: *
445: * @param string $folder The folder to expunge.
446: *
447: * @return NULL
448: */
449: public function expunge($folder)
450: {
451: return $this->getBackend()->expunge($folder);
452: }
453: }
454: