Overview

Packages

  • None
  • Share

Classes

  • Horde_Share_Base
  • Horde_Share_Datatree
  • Horde_Share_Kolab
  • Horde_Share_Object
  • Horde_Share_Object_Datatree
  • Horde_Share_Object_DataTree_Share
  • Horde_Share_Object_Kolab
  • Horde_Share_Object_Sql
  • Horde_Share_Object_Sqlng
  • Horde_Share_Sql
  • Horde_Share_Sqlng
  • Horde_Share_Translation
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Horde_Share_Kolab:: provides the Kolab backend for the horde share driver.
  4:  *
  5:  * PHP version 5
  6:  *
  7:  * @category Horde
  8:  * @package  Share
  9:  * @author   Stuart Binge <omicron@mighty.co.za>
 10:  * @author   Gunnar Wrobel <wrobel@pardus.de>
 11:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 12:  * @link     http://pear.horde.org/index.php?package=Share
 13:  */
 14: 
 15: /**
 16:  * Horde_Share_Kolab:: provides the Kolab backend for the horde share driver.
 17:  *
 18:  * Copyright 2004-2012 Horde LLC (http://www.horde.org/)
 19:  *
 20:  * See the enclosed file COPYING for license information (LGPL). If you
 21:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 22:  *
 23:  * @category Horde
 24:  * @package  Share
 25:  * @author   Stuart Binge <omicron@mighty.co.za>
 26:  * @author   Gunnar Wrobel <wrobel@pardus.de>
 27:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 28:  * @link     http://pear.horde.org/index.php?package=Share
 29:  */
 30: class Horde_Share_Kolab extends Horde_Share_Base
 31: {
 32:     const VERSION = 1;
 33: 
 34:     /**
 35:      * The Kolab storage handler
 36:      *
 37:      * @var Horde_Kolab_Storage
 38:      */
 39:     private $_storage;
 40: 
 41:     /**
 42:      * The folder type in the storage backend.
 43:      *
 44:      * @var string
 45:      */
 46:     private $_type;
 47: 
 48:     /**
 49:      * A map of IDs to folder names.
 50:      *
 51:      * @var array
 52:      */
 53:     private $_id_map = array();
 54: 
 55:     /**
 56:      * Constructor.
 57:      *
 58:      * @param string $app               The application that the shares belong
 59:      *                                  to
 60:      * @param string $user              The current user
 61:      * @param Horde_Perms_Base $perms   The permissions object
 62:      * @param Horde_Group_Base $groups  The Horde_Group driver.
 63:      *
 64:      */
 65:     public function __construct($app, $user, Horde_Perms_Base $perms,
 66:                                 Horde_Group_Base $groups)
 67:     {
 68:         switch ($app) {
 69:         case 'mnemo':
 70:         case 'jonah':
 71:             $this->_type = 'note';
 72:             break;
 73:         case 'kronolith':
 74:             $this->_type = 'event';
 75:             break;
 76:         case 'turba':
 77:             $this->_type = 'contact';
 78:             break;
 79:         case 'nag':
 80:             $this->_type = 'task';
 81:             break;
 82:         default:
 83:             throw new Horde_Share_Exception(sprintf(Horde_Share_Translation::t("The Horde/Kolab integration engine does not support \"%s\""), $app));
 84:         }
 85:         parent::__construct($app, $user, $perms, $groups);
 86:     }
 87: 
 88:     /**
 89:      * Set the Kolab storage backend.
 90:      *
 91:      * @param Horde_Kolab_Storage $storage The Kolab storage handler.
 92:      *
 93:      * @return NULL
 94:      */
 95:     public function setStorage(Horde_Kolab_Storage $storage)
 96:     {
 97:         $this->_storage = $storage;
 98:     }
 99: 
100:     /**
101:      * Return the Kolab storage backend associated with this driver.
102:      *
103:      * @return Horde_Kolab_Storage The Kolab storage driver.
104:      */
105:     public function getStorage()
106:     {
107:         if ($this->_storage === null) {
108:             throw new Horde_Share_Exception('The storage backend has not yet been set!');
109:         }
110:         return $this->_storage;
111:     }
112: 
113:     /**
114:      * Return the Kolab storage folder list handler.
115:      *
116:      * @return Horde_Kolab_Storage_List The folder list handler.
117:      */
118:     public function getList()
119:     {
120:         return $this->getStorage()->getList();
121:     }
122: 
123:     /**
124:      * Return the type of folder this share driver will access in the Kolab
125:      * storage backend (depends on the application calling the share driver).
126:      *
127:      * @return string
128:      */
129:     public function getType()
130:     {
131:         return $this->_type;
132:     }
133:     
134:     /**
135:      * Encode a share ID.
136:      *
137:      * @todo: In Horde3 share IDs were not properly escaped everywhere and it
138:      * made sense to escape them here just in case they are placed in a
139:      * URL. Needs checking in Horde4.
140:      *
141:      * @param string $id The ID to be encoded.
142:      *
143:      * @return string The encoded ID.
144:      */
145:     private function _idEncode($id)
146:     {
147:         $folder = $this->getList()->getFolder($id);
148:         if (!method_exists($folder, 'getPrefix')) {
149:             //@todo BC (remove this option later)
150:             return $this->constructId($folder->getOwner(), $folder->getSubpath());
151:         } else {
152:             return $this->constructId($folder->getOwner(), $folder->getSubpath(), $folder->getPrefix());
153:         }
154:     }
155: 
156:     /**
157:      * Construct the ID from the owner name and the folder subpath.
158:      *
159:      * @param string $owner  The share owner.
160:      * @param string $name   The name of the folder without the namespace prefix.
161:      * @param string $prefix The namespace prefix.
162:      *
163:      * @return string The encoded ID.
164:      */
165:     public function constructId($owner, $name, $prefix = null)
166:     {
167:         return Horde_Url::uriB64Encode(serialize(array($owner, $name, $prefix)));
168:     }
169: 
170:     /**
171:      * Construct the Kolab storage folder name based on the share name and owner
172:      * attributes.
173:      *
174:      * @param string $owner   The owner of the share.
175:      * @param string $subpath The folder subpath.
176:      * @param string $prefix  The namespace prefix.
177:      *
178:      * @return string The folder name for the Kolab backend.
179:      */
180:     public function constructFolderName($owner, $subpath, $prefix = null)
181:     {
182:         return $this->getList()
183:             ->getNamespace()
184:             ->constructFolderName($owner, $subpath, $prefix);
185:     }
186: 
187:     /**
188:      * Retrieve namespace information for a folder name.
189:      *
190:      * @param string $folder The folder name.
191:      *
192:      * @since Horde_Share 1.2.0
193:      *
194:      * @return array A list of namespace prefix, the delimiter and the folder
195:      *               subpath.
196:      */
197:     public function getFolderNameElements($folder)
198:     {
199:         $ns = $this->getList()->getNamespace()->matchNamespace($folder);
200:         return array(
201:             $ns->getName(), $ns->getDelimiter(), $ns->getSubpath($folder)
202:         );
203:     }
204: 
205:     /**
206:      * Decode a share ID.
207:      *
208:      * @param string $id The ID to be decoded.
209:      *
210:      * @return string The decoded ID.
211:      */
212:     private function _idDecode($id)
213:     {
214:         if (!isset($this->_id_map[$id])) {
215:             $result = $this->_idDeconstruct($id);
216:             $this->_id_map[$id] = $this->constructFolderName(
217:                 $result[0],
218:                 $result[1],
219:                 isset($result[2]) ? $result[2] : null
220:             );
221:         }
222:         return $this->_id_map[$id];
223:     }
224: 
225:     /**
226:      * Deconstruct the ID elements from the ID string
227:      *
228:      * @param string $id The ID to be deconstructed.
229:      *
230:      * @return array A tuple of (owner, folder subpath).
231:      */
232:     private function _idDeconstruct($id)
233:     {
234:         if (!$decoded_id = Horde_Url::uriB64Decode($id)) {
235:             $msg = sprintf('Share id %s is invalid.', $id);
236:             $this->_logger->err($msg);
237:             throw new Horde_Exception_NotFound($msg);
238:         }
239:         if (!$sid = @unserialize($decoded_id)) {
240:             $msg = sprintf('Share id %s is invalid.', $decoded_id);
241:             $this->_logger->err($msg);
242:             throw new Horde_Exception_NotFound($msg);
243:         }
244:         return $sid;
245:     }
246: 
247: 
248:     /**
249:      * Returns a Horde_Share_Object_Kolab object corresponding to the given
250:      * share name, with the details retrieved appropriately.
251:      *
252:      * @param string $id    The id of the share to retrieve.
253:      * @param array  $data  The share data.
254:      *
255:      * @return Horde_Share_Object  The requested share.
256:      */
257:     private function _createObject($id, array $data = array())
258:     {
259:         $object = new Horde_Share_Object_Kolab($id, $this->_groups, $data);
260:         $this->initShareObject($object);
261:         return $object;
262:     }
263: 
264:     /**
265:      * Returns a Horde_Share_Object_Kolab object corresponding to the given
266:      * share name, with the details retrieved appropriately.
267:      *
268:      * @param string $name  The name of the share to retrieve.
269:      *
270:      * @return Horde_Share_Object  The requested share.
271:      * @throws Horde_Exception_NotFound
272:      * @throws Horde_Share_Exception
273:      */
274:     protected function _getShare($name)
275:     {
276:         $list = $this->getList()
277:             ->getQuery()
278:             ->dataByType($this->_type);
279: 
280:         $query = $this->getList()
281:             ->getQuery(Horde_Kolab_Storage_List::QUERY_SHARE);
282: 
283:         foreach ($list as $folder => $folder_data) {
284:             $data = $query->getParameters($folder);
285:             if (isset($data['share_name']) && $data['share_name'] == $name) {
286:                 return $this->getShareById(
287:                     $this->constructId(
288:                         $folder_data['owner'],
289:                         $folder_data['subpath'],
290:                         isset($folder_data['prefix']) ? $folder_data['prefix'] : null
291:                     )
292:                 );
293:             }
294:         }
295:         return $this->getShareById($name);
296:     }
297: 
298:     /**
299:      * Returns a Horde_Share_Object_sql object corresponding to the given
300:      * unique ID, with the details retrieved appropriately.
301:      *
302:      * @param integer $id  The id of the share to retrieve.
303:      *
304:      * @return Horde_Share_Object_sql  The requested share.
305:      * @throws Horde_Share_Exception, Horde_Exception_NotFound
306:      */
307:     protected function _getShareById($id)
308:     {
309:         $list = $this->getList()
310:             ->getQuery()
311:             ->dataByType($this->_type);
312: 
313:         if (!isset($list[$this->_idDecode($id)])) {
314:             $msg = sprintf('Share id %s not found', $id);
315:             $this->_logger->err($msg);
316:             throw new Horde_Exception_NotFound($msg);
317:         }
318: 
319:         $query = $this->getList()
320:             ->getQuery(Horde_Kolab_Storage_List::QUERY_SHARE);
321: 
322:         $data = array_merge(
323:             $query->getParameters($this->_idDecode($id)),
324:             $list[$this->_idDecode($id)]
325:         );
326:         $data['desc'] = $query->getDescription($this->_idDecode($id));
327:         if (isset($data['parent'])) {
328:             try {
329:                 $data['parent'] = $this->_idEncode($data['parent']);
330:             } catch (Horde_Kolab_Storage_Exception $e) {
331:                 unset($data['parent']);
332:             }
333:         }
334:         return $this->_createObject($id, $data);
335:     }
336: 
337:     /**
338:      * Returns an array of Horde_Share_Object_kolab objects corresponding to
339:      * the requested folders.
340:      *
341:      * @param string $ids  The ids of the shares to fetch.
342:      *
343:      * @return array  An array of Horde_Share_Object_kolab objects.
344:      */
345:     protected function _getShares(array $ids)
346:     {
347:         $objects = array();
348:         foreach ($ids as $id) {
349:             $share = $this->_getShareById($id);
350:             $objects[$share->getName()] = $share;
351:         }
352:         return $objects;
353:     }
354: 
355:     /**
356:      * Checks if a share exists in the system.
357:      *
358:      * @param string $share  The share to check.
359:      *
360:      * @return boolean  True if the share exists.
361:      * @throws Horde_Share_Exception
362:      */
363:     protected function _exists($share)
364:     {
365:         try {
366:             $this->getShare($share);
367:             return true;
368:         } catch (Horde_Exception_NotFound $e) {
369:             return false;
370:         }
371:     }
372: 
373:     /**
374:      * Check that a share id exists in the system.
375:      *
376:      * @param integer $id  The share id
377:      *
378:      * @return boolean True if the share exists.
379:      */
380:     protected function _idExists($id)
381:     {
382:         return in_array(
383:             $this->_idDecode($id),
384:             $this->getList()
385:             ->getQuery()
386:             ->listByType($this->_type)
387:         );
388:     }
389: 
390:     /**
391:      * Returns an array of all shares that $userid has access to.
392:      *
393:      * @param string $userid     The userid of the user to check access for.
394:      * @param array  $params     See listShares().
395:      *
396:      * @return array  The shares the user has access to.
397:      */
398:     protected function _listShares($userid, array $params = array())
399:     {
400:         $stamp = $this->getList()->getQuery()->getStamp();
401:         $key = md5(serialize(array($userid, $params, $stamp)));
402: 
403:         if (!isset($this->_listcache[$key])) {
404:             $shares = array_map(
405:                 array($this, '_idEncode'),
406:                 $this->getList()
407:                 ->getQuery()
408:                 ->listByType($this->_type)
409:             );
410:             $remove = array();
411:             if ($params['perm'] != Horde_Perms::SHOW || empty($userid)) {
412:                 foreach ($shares as $share) {
413:                     $object = $this->_getShareById($share);
414:                     if (!$object->hasPermission($userid, $params['perm'], $object->get('owner'))) {
415:                         $remove[] = $share;
416:                     }
417:                 }
418:             }
419:             if (isset($params['parent'])) {
420:                 foreach ($shares as $share) {
421:                     $object = $this->getShareById($share);
422:                     if ($params['parent'] instanceOf Horde_Share_Object) {
423:                         $parent = $params['parent'];
424:                     } else {
425:                         $parent = $this->getShare($params['parent']);
426:                     }
427:                     if (!$object->getParent() || $object->getParent()->getId() != $parent->getId()) {
428:                         $remove[] = $share;
429:                     }
430:                 }
431:             }
432:             if (isset($params['all_levels']) && empty($params['all_levels'])) {
433:                 foreach ($shares as $share) {
434:                     $object = $this->getShareById($share);
435:                     $parent = $object->get('parent');
436:                     if (!empty($parent) && in_array($parent, $shares)) {
437:                         $remove[] = $share;
438:                     }
439:                 }
440:             }
441:             if (isset($params['attributes'])) {
442:                 if (!is_array($params['attributes'])) {
443:                     $attributes = array('owner' => $params['attributes']);
444:                 } else {
445:                     $attributes = $params['attributes'];
446:                 }
447:                 foreach ($shares as $share) {
448:                     $object = $this->getShareById($share);
449:                     foreach ($attributes as $key => $value) {
450:                         if ($object->get($key) != $value) {
451:                             $remove[] = $share;
452:                         }
453:                     }
454:                 }
455:             }
456:             if (!empty($remove)) {
457:                 $shares = array_diff($shares, $remove);
458:             }
459:             if (isset($params['sort_by'])) {
460:                 if ($params['sort_by'] == 'id') {
461:                     sort($shares);
462:                 } else {
463:                     $sorted = array();
464:                     foreach ($shares as $share) {
465:                         $object = $this->getShareById($share);
466:                         $key = $object->get($params['sort_by']);
467:                         $sorted[$key] = $object->getId();
468:                     }
469:                     ksort($sorted);
470:                     $shares = array_values($sorted);
471:                 }
472:             }
473:             if (!empty($params['direction'])) {
474:                 $shares = array_reverse($shares);
475:             }
476:             if (isset($params['from']) && !empty($params['from'])) {
477:                 $shares = array_slice($shares, $params['from']);
478:             }
479:             if (isset($params['count']) && !empty($params['count'])) {
480:                 $shares = array_slice($shares, 0, $params['count']);
481:             }
482:             $this->_listcache[$key] = $shares;
483:         }
484:         return $this->_listcache[$key];
485:     }
486: 
487:     /**
488:      * Returns the count of all shares that $userid has access to.
489:      *
490:      * @param string  $userid      The userid of the user to check access for.
491:      * @param integer $perm        The level of permissions required.
492:      * @param mixed   $attributes  Restrict the shares counted to those
493:      *                             matching $attributes. An array of
494:      *                             attribute/values pairs or a share owner
495:      *                             username.
496:      * @param mixed  $parent      The share to start searching from
497:      *                            (Horde_Share_Object, share_id, or null)
498:      * @param boolean $allLevels  Return all levels, or just the direct
499:      *                            children of $parent?
500:      *
501:      * @return integer  Number of shares the user has access to.
502:      * @throws Horde_Share_Exception
503:      */
504:     public function countShares($userid, $perm = Horde_Perms::SHOW,
505:         $attributes = null, $parent = null, $allLevels = true)
506:     {
507:         return count(
508:             $this->listShares(
509:                 $userid,
510:                 array(
511:                     'perm' => $perm,
512:                     'attributes' => $attributes,
513:                     'parent' => $parent,
514:                     'all_levels' => $allLevels
515:                 )
516:             )
517:         );
518:     }
519: 
520:     /**
521:      * Returns an array of all system shares.
522:      *
523:      * @return array  All system shares.
524:      */
525:     public function listSystemShares()
526:     {
527:         $shares = array_map(
528:             array($this, '_idEncode'),
529:             $this->getList()
530:             ->getQuery()
531:             ->listByType($this->_type)
532:         );
533:         $result = array();
534:         foreach ($shares as $share) {
535:             $object = $this->_getShareById($share);
536:             //@todo: Remove "null" check as this is only required for BC
537:             if ($object->get('owner') === false ||
538:                 $object->get('owner') === null) {
539:                 $result[$object->getName()] = $object;
540:             }
541:         }
542:         return $result;
543:     }
544: 
545:     /**
546:      * Lists *all* shares for the current app/share, regardless of
547:      * permissions.
548:      *
549:      * For the Kolab backend this cannot work in the same way as for the SQL
550:      * based backend. Permissions are always handled by the backend automatically (IMAP ACLs) and cannot be disabled.
551:      *
552:      * listAllShares() is apparently used during command line scipts where it
553:      * represents administrator access. This is possible on Kolab by using the
554:      * "manager" user. In that case a standard listShares() authenticated as
555:      * "manager" should be sufficient.
556:      *
557:      * @return array  All shares for the current app/share.
558:      */
559:     protected function _listAllShares()
560:     {
561:         $shares = array_map(
562:             array($this, '_idEncode'),
563:             $this->getList()
564:             ->getQuery()
565:             ->listByType($this->_type)
566:         );
567:         $result = array();
568:         foreach ($shares as $share) {
569:             $object = $this->_getShareById($share);
570:             $result[$object->getName()] = $object;
571:         }
572:         return $result;
573:     }
574: 
575:     /**
576:      * Returns a new share object.
577:      *
578:      * @param string $name  The share's name.
579:      *
580:      * @return Horde_Share_Object_kolab  A new share object.
581:      */
582:     protected function _newShare($name)
583:     {
584:         return $this->_createObject(
585:             null,
586:             array(
587:                 'type' => $this->_type,
588:                 'share_name' => $name
589:             )
590:         );
591:     }
592: 
593:     /**
594:      * Adds a share to the shares system.
595:      *
596:      * The share must first be created with Horde_Share::newShare(),
597:      * and have any initial details added to it, before this function is
598:      * called.
599:      *
600:      * @param Horde_Share_Object $share  The new share object.
601:      */
602:     protected function _addShare(Horde_Share_Object $share)
603:     {
604:         $share->save();
605:     }
606: 
607:     /**
608:      * Removes a share from the shares system permanently.
609:      *
610:      * @param Horde_Share_Object $share  The share to remove.
611:      */
612:     protected function _removeShare(Horde_Share_Object $share)
613:     {
614:         $this->getList()->deleteFolder($this->_idDecode($share->getId()));
615:     }
616: 
617:     /**
618:      * Renames a share in the shares system.
619:      *
620:      * @param Horde_Share_Object $share  The share to rename.
621:      * @param string $name               The share's new name.
622:      *
623:      * @throws Horde_Share_Exception
624:      */
625:     protected function _renameShare(Horde_Share_Object $share, $name)
626:     {
627:         $share->set('share_name', $name);
628:     }
629: 
630:     /**
631:      * Retrieve the Kolab specific access rights for a share.
632:      *
633:      * @param string $id The share ID.
634:      *
635:      * @return An array of rights.
636:      */
637:     public function getAcl($id)
638:     {
639:         return $this->getList()
640:             ->getQuery(Horde_Kolab_Storage_List::QUERY_ACL)
641:             ->getAcl(
642:                 $this->_idDecode($id)
643:             );
644:     }
645: 
646:     /**
647:      * Set the Kolab specific access rights for a share.
648:      *
649:      * @param string $id   The share ID.
650:      * @param string $user The user to set the ACL for.
651:      * @param string $acl  The ACL.
652:      *
653:      * @return NULL
654:      */
655:     public function setAcl($id, $user, $acl)
656:     {
657:         $this->getList()
658:             ->getQuery(Horde_Kolab_Storage_List::QUERY_ACL)
659:             ->setAcl(
660:                 $this->_idDecode($id), $user, $acl
661:             );
662:     }
663: 
664:     /**
665:      * Delete Kolab specific access rights for a share.
666:      *
667:      * @param string $id   The share ID.
668:      * @param string $user The user to delete the ACL for
669:      *
670:      * @return NULL
671:      */
672:     public function deleteAcl($id, $user)
673:     {
674:         $this->getList()
675:             ->getQuery(Horde_Kolab_Storage_List::QUERY_ACL)
676:             ->deleteAcl(
677:                 $this->_idDecode($id), $user
678:             );
679:     }
680: 
681:     /**
682:      * Save share data to the storage backend.
683:      *
684:      * @param string $id          The share id.
685:      * @param string $old_id      The old share id.
686:      * @param array  $data        The share data.
687:      *
688:      * @return NULL
689:      */
690:     public function save($id, $old_id, $data)
691:     {
692:         if ($old_id === null) {
693:             $this->getList()->createFolder(
694:                 $this->_idDecode($id), $this->_type
695:             );
696:         } elseif ($id != $old_id) {
697:             $this->getList()->renameFolder(
698:                 $this->_idDecode($old_id), $this->_idDecode($id), $this->_type
699:             );
700:         }
701: 
702:         $query = $this->getList()
703:             ->getQuery(Horde_Kolab_Storage_List::QUERY_SHARE);
704:         if (isset($data['desc'])) {
705:             $query->setDescription($this->_idDecode($id), $data['desc']);
706:         }
707:         unset(
708:             $data['desc'],
709:             $data['owner'],
710:             $data['name'],
711:             $data['default'],
712:             $data['parent'],
713:             $data['type'],
714:             $data['delimiter'],
715:             $data['prefix'],
716:             $data['subpath'],
717:             $data['folder']
718:         );
719:         $query->setParameters($this->_idDecode($id), $data);
720:     }
721: }
722: 
API documentation generated by ApiGen