Overview

Packages

  • Kolab
    • FreeBusy

Classes

  • Horde_Kolab_FreeBusy
  • Horde_Kolab_FreeBusy_Cache
  • Horde_Kolab_FreeBusy_Cache_Base
  • Horde_Kolab_FreeBusy_Cache_DB
  • Horde_Kolab_FreeBusy_Cache_DB_acl
  • Horde_Kolab_FreeBusy_Cache_DB_xacl
  • Horde_Kolab_FreeBusy_Cache_File
  • Horde_Kolab_FreeBusy_Cache_File_acl
  • Horde_Kolab_FreeBusy_Cache_File_pvcal
  • Horde_Kolab_FreeBusy_Cache_File_vcal
  • Horde_Kolab_FreeBusy_Cache_File_xacl
  • Horde_Kolab_FreeBusy_Controller_Base
  • Horde_Kolab_FreeBusy_Controller_MatchDict
  • Horde_Kolab_FreeBusy_Controller_NotFound
  • Horde_Kolab_FreeBusy_Controller_RequestConfiguration
  • Horde_Kolab_FreeBusy_Driver_Base
  • Horde_Kolab_FreeBusy_Driver_Freebusy_Base
  • Horde_Kolab_FreeBusy_Driver_Freebusy_Kolab
  • Horde_Kolab_FreeBusy_Exception
  • Horde_Kolab_FreeBusy_Exception_NotFound
  • Horde_Kolab_FreeBusy_Exception_Unauthorized
  • Horde_Kolab_FreeBusy_Export_Freebusy_Backend_Kolab
  • Horde_Kolab_FreeBusy_Export_Freebusy_Base
  • Horde_Kolab_FreeBusy_Export_Freebusy_Decorator_Log
  • Horde_Kolab_FreeBusy_Factory_Base
  • Horde_Kolab_FreeBusy_Freebusy_Controller_Freebusy
  • Horde_Kolab_FreeBusy_Freebusy_Factory_Base
  • Horde_Kolab_FreeBusy_Freebusy_Factory_Kolab
  • Horde_Kolab_FreeBusy_Freebusy_Helper_Owa
  • Horde_Kolab_FreeBusy_Freebusy_Helper_StatusMap_Config
  • Horde_Kolab_FreeBusy_Freebusy_Helper_StatusMap_Default
  • Horde_Kolab_FreeBusy_Freebusy_Owner_Kolab
  • Horde_Kolab_FreeBusy_Freebusy_Params_Folder
  • Horde_Kolab_FreeBusy_Freebusy_UserDb_Kolab
  • Horde_Kolab_FreeBusy_Object_Event
  • Horde_Kolab_FreeBusy_Owner_Kolab
  • Horde_Kolab_FreeBusy_Params_Freebusy_Resource_Kolab
  • Horde_Kolab_FreeBusy_Params_User
  • Horde_Kolab_FreeBusy_Provider_Local
  • Horde_Kolab_FreeBusy_Provider_Remote
  • Horde_Kolab_FreeBusy_Provider_Remote_PassThrough
  • Horde_Kolab_FreeBusy_Provider_Remote_Redirect
  • Horde_Kolab_FreeBusy_Report
  • Horde_Kolab_FreeBusy_Resource_Decorator_Log
  • Horde_Kolab_FreeBusy_Resource_Decorator_Mcache
  • Horde_Kolab_FreeBusy_Resource_Event_Decorator_Log
  • Horde_Kolab_FreeBusy_Resource_Event_Decorator_Mcache
  • Horde_Kolab_FreeBusy_Resource_Event_Kolab
  • Horde_Kolab_FreeBusy_Resource_Event_Owa
  • Horde_Kolab_FreeBusy_Resource_Kolab
  • Horde_Kolab_FreeBusy_Translation
  • Horde_Kolab_FreeBusy_User_Anonymous
  • Horde_Kolab_FreeBusy_User_Decorator_Log
  • Horde_Kolab_FreeBusy_User_Kolab
  • Horde_Kolab_FreeBusy_UserDb_Kolab
  • Horde_Kolab_FreeBusy_UserDb_User_Kolab
  • Horde_Kolab_FreeBusy_View
  • Horde_Kolab_FreeBusy_View_error
  • Horde_Kolab_FreeBusy_View_vfb

Interfaces

  • Horde_Kolab_FreeBusy_Driver_Freebusy_Interface
  • Horde_Kolab_FreeBusy_Driver_Interface
  • Horde_Kolab_FreeBusy_Export_Freebusy
  • Horde_Kolab_FreeBusy_Export_Freebusy_Backend
  • Horde_Kolab_FreeBusy_Factory
  • Horde_Kolab_FreeBusy_Freebusy_Helper_StatusMap
  • Horde_Kolab_FreeBusy_Freebusy_Owner
  • Horde_Kolab_FreeBusy_Owner
  • Horde_Kolab_FreeBusy_Params_Owner
  • Horde_Kolab_FreeBusy_Params_Resource
  • Horde_Kolab_FreeBusy_Provider
  • Horde_Kolab_FreeBusy_Resource
  • Horde_Kolab_FreeBusy_Resource_Event
  • Horde_Kolab_FreeBusy_User
  • Horde_Kolab_FreeBusy_UserDb
  • Horde_Kolab_FreeBusy_UserDb_User
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Caching for the Kolab free/busy data.
   4:  *
   5:  * @package Kolab_FreeBusy
   6:  */
   7: 
   8: /** We require the iCalendar library to build the free/busy list */
   9: require_once 'Horde/Icalendar.php';
  10: require_once 'Horde/Icalendar/Vfreebusy.php';
  11: 
  12: /**
  13:  * The Horde_Kolab_FreeBusy_Cache:: class provides functionality to store
  14:  * prepared free/busy data for quick retrieval.
  15:  *
  16:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
  17:  *
  18:  * See the enclosed file COPYING for license information (LGPL). If you
  19:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  20:  *
  21:  * @author  Gunnar Wrobel <p@rdus.de>
  22:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
  23:  * @package Kolab_FreeBusy
  24:  */
  25: class Horde_Kolab_FreeBusy_Cache {
  26: 
  27:     /**
  28:      * The directory that should be used for caching.
  29:      *
  30:      * @var string
  31:      */
  32:     var $_cache_dir;
  33: 
  34:     /**
  35:      * Constructor.
  36:      *
  37:      * @param string  $cache_dir  The cache directory we should use.
  38:      */
  39:     function Horde_Kolab_FreeBusy_Cache($cache_dir)
  40:     {
  41:         $this->_cache_dir = $cache_dir;
  42:     }
  43: 
  44:     /**
  45:      * Update the cache information for a calendar.
  46:      *
  47:      * @param Horde_Kolab_FreeBusy_Access $access The object holding the
  48:      *                                      relevant access
  49:      *                                      parameters.
  50:      *
  51:      * @return boolean|PEAR_Error True if successful.
  52:      */
  53:     function store($access)
  54:     {
  55:         global $conf;
  56: 
  57:         /* Now we really need the free/busy library */
  58:         require_once 'Horde/Kolab/FreeBusy/Imap.php';
  59: 
  60:         $fb = new Horde_Kolab_FreeBusy_Imap();
  61: 
  62:         $result = $fb->connect($access->imap_folder);
  63:         if (is_a($result, 'PEAR_Error')) {
  64:             return $result;
  65:         }
  66: 
  67:         $fbpast = $fbfuture = null;
  68:         try {
  69:             if (!empty($access->server_object)) {
  70:                 $result = $access->server_object->get(Horde_Kolab_Server_Object_Kolab_Server::ATTRIBUTE_FBPAST);
  71:                 if (!is_a($result, 'PEAR_Error')) {
  72:                     $fbpast = $result;
  73:                 }
  74:             }
  75:         } catch (Horde_Kolab_Server_Exception $e) {
  76:             Horde::logMessage(sprintf("Failed fetching the k=kolab configuration object. Error was: %s", $e->getMessage()), 'ERR');
  77:             if (isset($conf['kolab']['freebusy']['past'])) {
  78:                 $fbpast = $conf['kolab']['freebusy']['past'];
  79:             } else {
  80:                 $fbpast = 10;
  81:             }
  82:         }
  83: 
  84:         if (!empty($access->owner_object)) {
  85:             $result = $access->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_FBFUTURE);
  86:             if (!is_a($result, 'PEAR_Error')) {
  87:                 $fbfuture = $result;
  88:             }
  89:         }
  90: 
  91:         $vCal = $fb->generate(null, null,
  92:                               !empty($fbpast)?$fbpast:0,
  93:                               !empty($fbfuture)?$fbfuture:60,
  94:                               $access->owner,
  95:                               $access->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_CN));
  96:         if (is_a($vCal, 'PEAR_Error')) {
  97:             $vCal;
  98:         }
  99: 
 100:         $fbfilename = $this->_getFilename($access->folder, $access->owner);
 101: 
 102:         $c_pvcal = new Horde_Kolab_FreeBusy_Cache_File_pvcal($this->_cache_dir, $fbfilename);
 103: 
 104:         if (!empty($conf['fb']['use_acls'])) {
 105:             $c_acl   = new Horde_Kolab_FreeBusy_Cache_File_acl($this->_cache_dir, $fbfilename);
 106:             $c_xacl  = new Horde_Kolab_FreeBusy_Cache_File_xacl($this->_cache_dir, $fbfilename);
 107:         }
 108: 
 109:         /* missing data means delete the cache files */
 110:         if (empty($vCal)) {
 111:             Horde::logMessage(sprintf("No events. Purging cache %s.", $fbfilename), 'DEBUG');
 112: 
 113:             $result = $c_pvcal->purge();
 114:             if (is_a($result, 'PEAR_Error')) {
 115:                 return $result;
 116:             }
 117: 
 118:             if (!empty($conf['fb']['use_acls'])) {
 119:             $result = $c_acl->purge();
 120:             if (is_a($result, 'PEAR_Error')) {
 121:                 return $result;
 122:             }
 123:             $result = $c_xacl->purge();
 124:             if (is_a($result, 'PEAR_Error')) {
 125:                 return $result;
 126:             }
 127:             }
 128:         } else {
 129:             $result = $c_pvcal->storePVcal($vCal);
 130:             if (is_a($result, 'PEAR_Error')) {
 131:                 return $result;
 132:             }
 133: 
 134:             $relevance = $fb->getRelevance();
 135:             if (is_a($relevance, 'PEAR_Error')) {
 136:                 return $relevance;
 137:             }
 138: 
 139:             if (!empty($conf['fb']['use_acls'])) {
 140:                 $acl = $fb->getACL();
 141:                 if (is_a($acl, 'PEAR_Error')) {
 142:                     return $acl;
 143:                 }
 144: 
 145:                 /**
 146:                  * Only store the acl information if the current user
 147:                  * has admin rights on the folder and can actually
 148:                  * retrieve the full ACL information.
 149:                  *
 150:                  * A folder that does not have admin rights for a user
 151:                  * will not be considered relvant for that user unless
 152:                  * it has been triggered by the folder owner before.
 153:                  */
 154:                 $append = false;
 155:                 if (isset($acl[$access->user])) {
 156:                     $myacl = $acl[$access->user];
 157:                     if (strpos($myacl, 'a') !== false) {
 158:                         $append = true;
 159:                     }
 160:                 }
 161: 
 162:                 $result = $c_acl->storeACL($acl, $relevance, $append);
 163:                 if (is_a($result, 'PEAR_Error')) {
 164:                     return $result;
 165:                 }
 166: 
 167:                 $xacl = $fb->getExtendedACL();
 168:                 if (is_a($xacl, 'PEAR_Error')) {
 169:                     return $xacl;
 170:                 }
 171: 
 172:                 $result = $c_xacl->storeXACL($xacl, $acl);
 173:                 if (is_a($result, 'PEAR_Error')) {
 174:                     return $result;
 175:                 }
 176:             } else {
 177:                 $acl = null;
 178:             }
 179: 
 180:             Horde::logMessage(sprintf("Horde_Kolab_FreeBusy_Cache::store(file=%s, relevance=%s, acl=%s, xacl=%s)", $fbfilename, $relevance, $acl, $xacl), 'DEBUG');
 181:         }
 182:         return true;
 183:     }
 184: 
 185:     /**
 186:      * Load partial free/busy data.
 187:      *
 188:      * @param Horde_Kolab_FreeBusy_Access $access   The object holding the
 189:      *                                        relevant access
 190:      *                                        parameters.
 191:      * @param boolean               $extended Should the data hold the extended
 192:      *                                        free/busy information?
 193:      *
 194:      * @return Horde_Icalendar|PEAR_Error The free/busy data of a
 195:      *                                    single calendar.
 196:      */
 197:     function &loadPartial(&$access, $extended)
 198:     {
 199:         global $conf;
 200: 
 201:         $file = $this->_getFilename($access->folder, $access->owner);
 202: 
 203:         if (!empty($conf['fb']['use_acls'])) {
 204:             $aclcache = &Horde_Kolab_FreeBusy_Cache_DB_acl::singleton('acl',
 205:                                                                       $this->_cache_dir);
 206:             if ($extended) {
 207:                 $extended = $this->_allowExtended($file, $access);
 208:             }
 209:         }
 210: 
 211:         $c_pvcal = new Horde_Kolab_FreeBusy_Cache_File_pvcal($this->_cache_dir, $file);
 212:         $pvCal = $c_pvcal->loadPVcal($extended);
 213:         if (is_a($pvCal, 'PEAR_Error')) {
 214:             return $pvCal;
 215:         }
 216:         return $pvCal;
 217:     }
 218: 
 219:     /**
 220:      * Is extended access to the given file allowed?
 221:      *
 222:      * @param string                $file     Name of the cache file.
 223:      * @param Horde_Kolab_FreeBusy_Access $access   The object holding the
 224:      *                                        relevant access
 225:      *                                        parameters.
 226:      *
 227:      * @return boolean|PEAR_Error True if extended access is allowed.
 228:      */
 229:     function _allowExtended($file, &$access)
 230:     {
 231:         if (!isset($access->user_object)) {
 232:             Horde::logMessage(sprintf("Extended attributes on folder %s disallowed for unknown user.", $access->folder, $access->user), 'DEBUG');
 233:             return false;
 234:         }
 235: 
 236:         $xaclcache = &Horde_Kolab_FreeBusy_Cache_DB_xacl::singleton('xacl', $this->_cache_dir);
 237: 
 238:         /* Check if the calling user has access to the extended information of
 239:          * the folder we are about to integrate into the free/busy data.
 240:          */
 241:         $groups = $access->user_object->getGroupAddresses();
 242:         if (is_a($groups, 'PEAR_Error')) {
 243:             return $groups;
 244:         }
 245: 
 246:         $groups[] = $access->user;
 247:         foreach ($groups as $id) {
 248:             if ($xaclcache->has($file, $id)) {
 249:                 return true;
 250:             }
 251:         }
 252:         Horde::logMessage(sprintf("Extended attributes on folder %s disallowed for user %s.", $access->folder, $access->user), 'DEBUG');
 253:         return false;
 254:     }
 255: 
 256:     /**
 257:      * Get a cache file name depending on the owner of the free/busy
 258:      * data.
 259:      *
 260:      * @param string  $folder  Name of the calendar folder.
 261:      * @param string  $owner   Owner of the calendar folder.
 262:      *
 263:      * @return string Name of the correspoding cache file.
 264:      */
 265:     function _getFilename($folder, $owner)
 266:     {
 267:         if (ereg('(.*)@(.*)', $owner, $regs)) {
 268:             $owner = $regs[2] . '/' . $regs[1];
 269:         }
 270: 
 271:         return str_replace("\0", '', str_replace('.', '^', $owner . '/' . $folder));
 272:     }
 273: 
 274:     /**
 275:      * Retrieve external free/busy data.
 276:      *
 277:      * @param array                 $servers  The remote servers to query
 278:      * @param Horde_Kolab_FreeBusy_Access $access   The object holding the
 279:      *                                        relevant access
 280:      *                                        parameters.
 281:      *
 282:      * @return Horde_Icalender The remote free/busy information.
 283:      */
 284:     function &_fetchRemote($servers, $access)
 285:     {
 286:         $vFb = null;
 287: 
 288:         foreach ($servers as $server) {
 289: 
 290:             $url = 'https://' . urlencode($access->user) . ':' . urlencode($access->pass)
 291:             . '@' . $server . $_SERVER['REQUEST_URI'];
 292:             $remote = @file_get_contents($url);
 293:             if (!$remote) {
 294:                 $message = sprintf("Unable to read free/busy information from %s",
 295:                                    'https://' . urlencode($access->user) . ':XXX'
 296:                                    . '@' . $server . $_SERVER['REQUEST_URI']);
 297:                 Horde::logMessage($message, 'INFO');
 298:             }
 299: 
 300:             $rvCal = new Horde_Icalendar();
 301:             $result = $rvCal->parsevCalendar($remote);
 302: 
 303:             if (is_a($result, 'PEAR_Error')) {
 304:                 $message = sprintf("Unable to parse free/busy information from %s: %s",
 305:                                    'https://' . urlencode($access->user) . ':XXX'
 306:                                    . '@' . $server . $_SERVER['REQUEST_URI'],
 307:                                    $result->getMessage());
 308:                 Horde::logMessage($message, 'INFO');
 309:             }
 310: 
 311:             $rvFb = &$rvCal->findComponent('vfreebusy');
 312:             if (!$pvFb) {
 313:                 $message = sprintf("Unable to find free/busy information in data from %s.",
 314:                                    'https://' . urlencode($access->user) . ':XXX'
 315:                                    . '@' . $server . $_SERVER['REQUEST_URI']);
 316:                 Horde::logMessage($message, 'INFO');
 317:             }
 318:             if ($ets = $rvFb->getAttributeDefault('DTEND', false) !== false) {
 319:                 // PENDING(steffen): Make value configurable
 320:                 if ($ets < time()) {
 321:                     $message = sprintf("free/busy information from %s is too old.",
 322:                                        'https://' . urlencode($access->user) . ':XXX'
 323:                                        . '@' . $server . $_SERVER['REQUEST_URI']);
 324:                     Horde::logMessage($message, 'INFO');
 325:                 }
 326:             }
 327:             if (!empty($vFb)) {
 328:                 $vFb->merge($rvFb);
 329:             } else {
 330:                 $vFb = $rvFb;
 331:             }
 332:         }
 333:         return $vFb;
 334:     }
 335: 
 336:     function findAll_readdir($uid, $dirname, &$lst) {
 337:         if ($dir = @opendir($dirname)) {
 338:             while (($file = readdir($dir)) !== false) {
 339:                 if ($file == "." || $file == "..")
 340:                     continue;
 341: 
 342:                 $full_path = $dirname."/".$file;
 343: 
 344:                 if (is_file($full_path) && preg_match("/(.*)\.x?pvc$/", $file, $matches))
 345:                     $lst[] = $uid."/".$matches[1];
 346:                 else if(is_dir($full_path))
 347:                     $this->findAll_readdir($uid."/".$file, $full_path, $lst);
 348:             }
 349:             closedir($dir);
 350:         }
 351:     }
 352: };
 353: 
 354: /**
 355:  * A berkeley db based cache for free/busy data.
 356:  *
 357:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
 358:  *
 359:  * See the enclosed file COPYING for license information (LGPL). If you
 360:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 361:  *
 362:  * @author  Gunnar Wrobel <p@rdus.de>
 363:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
 364:  * @package Kolab_FreeBusy
 365:  */
 366: class Horde_Kolab_FreeBusy_Cache_DB {
 367: 
 368:     /**
 369:      * The directory that should be used for caching.
 370:      *
 371:      * @var string
 372:      */
 373:     var $_cache_dir;
 374: 
 375:     /**
 376:      * The resource handle into the database.
 377:      *
 378:      * @var resource
 379:      */
 380:     var $_db = false;
 381: 
 382:     /**
 383:      * The format of the database.
 384:      *
 385:      * @var string
 386:      */
 387:     var $_dbformat;
 388: 
 389:     /**
 390:      * The type of this cache.
 391:      *
 392:      * @var string
 393:      */
 394:     var $_type = '';
 395: 
 396:     /**
 397:      * The directory that should be used for caching.
 398:      *
 399:      * @var string
 400:      */
 401:     function Horde_Kolab_FreeBusy_Cache_DB($cache_dir) {
 402:         global $conf;
 403: 
 404:         $this->_cache_dir = $cache_dir;
 405: 
 406:         if (!empty($conf['fb']['dbformat'])) {
 407:             $this->_dbformat = $conf['fb']['dbformat'];
 408:         } else {
 409:             $this->_dbformat = 'db4';
 410:         }
 411: 
 412:         /* make sure that a database really exists before accessing it */
 413:         if (!file_exists($this->_cache_dir . '/' . $this->_type . 'cache.db')) {
 414:             $result = $this->_open();
 415:             if (is_a($result, 'PEAR_Error')) {
 416:                 return $result;
 417:             }
 418:             $this->_close();
 419:         }
 420: 
 421:     }
 422: 
 423:     /**
 424:      * Open the database.
 425:      *
 426:      * @return boolean|PEAR_Error True if successful.
 427:      */
 428:     function _open()
 429:     {
 430:         if ($this->_db !== false) {
 431:             return true;
 432:         }
 433: 
 434:         $dbfile = $this->_cache_dir . '/' . $this->_type . 'cache.db';
 435:         $this->_db = dba_open($dbfile, 'cd', $this->_dbformat);
 436:         if ($this->_db === false) {
 437:             return PEAR::raiseError(sprintf("Unable to open freebusy cache db %s", $dbfile));
 438:         }
 439:         return true;
 440:     }
 441: 
 442:     /**
 443:      * Close the database.
 444:      */
 445:     function _close()
 446:     {
 447:         if ($this->_db !== false) {
 448:             dba_close($this->_db);
 449:         }
 450:         $this->_db = false;
 451:     }
 452: 
 453:     /**
 454:      * Set a cache file as irrelevant for a user.
 455:      *
 456:      * @param string $filename The cache file to remove.
 457:      * @param string $uid      The user ID.
 458:      *
 459:      * @return boolean|PEAR_Error True if successful.
 460:      */
 461:     function _remove($filename, $uid)
 462:     {
 463:         $result = $this->_open();
 464:         if (is_a($result, 'PEAR_Error')) {
 465:             return $result;
 466:         }
 467: 
 468:         if (dba_exists($uid, $this->_db)) {
 469:             $lst = dba_fetch($uid, $this->_db);
 470:             $lst = explode(',', $lst);
 471:             $lst = array_diff($lst, array($filename));
 472:             $result = dba_replace($uid, join(',', $lst), $this->_db);
 473:             if ($result === false) {
 474:                 $result = PEAR::raiseError(sprintf("Unable to set db value for uid %s", $uid));
 475:             }
 476:         }
 477:         $this->_close();
 478: 
 479:         return $result;
 480:     }
 481: 
 482:     /**
 483:      * Set a cache file as relevant for a user.
 484:      *
 485:      * @param string $filename The cache file to add.
 486:      * @param string $uid      The user ID.
 487:      *
 488:      * @return boolean|PEAR_Error True if successful.
 489:      */
 490:     function _add($filename, $uid)
 491:     {
 492:         if (empty($filename)) {
 493:             return true;
 494:         }
 495: 
 496:         $result = $this->_open();
 497:         if (is_a($result, 'PEAR_Error')) {
 498:             return $result;
 499:         }
 500: 
 501:         if (dba_exists($uid, $this->_db)) {
 502:             $lst = dba_fetch($uid, $this->_db);
 503:             $lst = explode(',', $lst);
 504:             $lst[] = $filename;
 505:             $result = dba_replace($uid, join(',', array_keys(array_flip($lst))), $this->_db);
 506:             if ($result === false) {
 507:                 $result = PEAR::raiseError(sprintf("Unable to set db value for uid %s", $uid));
 508:             }
 509:         } else {
 510:             $result = dba_insert($uid, $filename, $this->_db);
 511:             if ($result === false) {
 512:                 $result = PEAR::raiseError(sprintf("Unable to set db value for uid %s", $uid));
 513:             }
 514:         }
 515:         $this->_close();
 516: 
 517:         return $result;
 518:     }
 519: 
 520:     /**
 521:      * Is the cache file relevant for the user?
 522:      *
 523:      * @param string $filename The cache file.
 524:      * @param string $uid      The user ID.
 525:      *
 526:      * @return boolean|PEAR_Error True if the cache file is relevant.
 527:      */
 528:     function has($filename, $uid)
 529:     {
 530:         $result = $this->_open();
 531:         if (is_a($result, 'PEAR_Error')) {
 532:             return $result;
 533:         }
 534: 
 535:         $result = false;
 536:         if (dba_exists($uid, $this->_db)) {
 537:             $lst = dba_fetch($uid, $this->_db);
 538:             $lst = explode(',', $lst);
 539:             $result = in_array($filename, $lst);
 540:         }
 541:         $this->_close();
 542: 
 543:         return $result;
 544:     }
 545: 
 546:     /**
 547:      * Get the full list of relevant cache files for a uid.
 548:      *
 549:      * @param string $uid      The user ID.
 550:      *
 551:      * @return array|PEAR_Error The list of cache files.
 552:      */
 553:     function get($uid)
 554:     {
 555:         $result = $this->_open();
 556:         if (is_a($result, 'PEAR_Error')) {
 557:             return $result;
 558:         }
 559: 
 560:         $result = array();
 561:         if (dba_exists($uid, $this->_db)) {
 562:             $lst = dba_fetch($uid, $this->_db);
 563:             $lst = explode(',', $lst);
 564:             $result = array_filter($lst, array($this, '_notEmpty'));
 565:         }
 566:         $this->_close();
 567: 
 568:         return $result;
 569:     }
 570: 
 571:     /**
 572:      * Check if the value is set.
 573:      *
 574:      * @param mixed $value  The value to check.
 575:      *
 576:      * @return boolean True if the value is set.
 577:      */
 578:     function _notEmpty($value)
 579:     {
 580:         return !empty($value);
 581:     }
 582: 
 583:     /**
 584:      * Attempts to return a reference to a concrete FreeBusyACLCache
 585:      * instance. It will only create a new instance if no
 586:      * FreeBusyACLCache instance currently exists.
 587:      *
 588:      * This method must be invoked as:
 589:      *   <code>$var = &FreeBusyACLCache::singleton($cache_dir);</code>
 590:      *
 591:      * @static
 592:      *
 593:      * @param string $type       The type of the cache.
 594:      * @param string $cache_dir  The directory for storing the cache.
 595:      *
 596:      * @return FreeBusyACLCache The concrete FreeBusyACLCache
 597:      *                          reference, or false on an error.
 598:      */
 599:     function &singleton($type, $cache_dir)
 600:     {
 601:         static $cachedb = array();
 602: 
 603:         $signature = $type . $cache_dir;
 604: 
 605:         if (empty($cachedb[$signature])) {
 606:             $class = 'Horde_Kolab_FreeBusy_Cache_DB_' . $type;
 607:             $cachedb[$signature] = new $class($cache_dir);
 608:         }
 609: 
 610:         return $cachedb[$signature];
 611:     }
 612: }
 613: 
 614: /**
 615:  * A berkeley db based cache for free/busy data that holds relevant
 616:  * cache files based on folder ACLs.
 617:  *
 618:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
 619:  *
 620:  * See the enclosed file COPYING for license information (LGPL). If you
 621:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 622:  *
 623:  * @author  Gunnar Wrobel <p@rdus.de>
 624:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
 625:  * @package Kolab_FreeBusy
 626:  */
 627: class Horde_Kolab_FreeBusy_Cache_DB_acl extends Horde_Kolab_FreeBusy_Cache_DB {
 628: 
 629:     /**
 630:      * The type of this cache.
 631:      *
 632:      * @var string
 633:      */
 634:     var $_type = 'acl';
 635: 
 636:     /**
 637:      * Store permissions on a calender folder.
 638:      *
 639:      * @param string $filename The cache file representing the calendar folder.
 640:      * @param array  $acl      The new ACL.
 641:      * @param array  $oldacl   The old ACL.
 642:      * @param mixed  $perm     False if all permissions should be revoked, a
 643:      *                         single character specifying allowed access
 644:      *                         otherwise.
 645:      *
 646:      * @return boolean|PEAR_Error True if successful.
 647:      */
 648:     function store($filename, $acl, $oldacl, $perm)
 649:     {
 650:         /* We remove the filename from all users listed in the old ACL first */
 651:         foreach ($oldacl as $user => $ac) {
 652:             $result = $this->_remove($filename, $user);
 653:             if (is_a($result, 'PEAR_Error')) {
 654:                 return $result;
 655:             }
 656:         }
 657: 
 658:         /* Now add the filename for all users with the correct permissions */
 659:         if ($perm !== false ) {
 660:             foreach ($acl as $user => $ac) {
 661:                 if (strpos($ac, $perm) !== false) {
 662:                     if (!empty($user)) {
 663:                         $result = $this->_add($filename, $user);
 664:                         if (is_a($result, 'PEAR_Error')) {
 665:                             return $result;
 666:                         }
 667:                     }
 668:                 }
 669:             }
 670:         }
 671: 
 672:         return true;
 673:     }
 674: }
 675: 
 676: /**
 677:  * A berkeley db based cache for free/busy data that holds relevant
 678:  * cache files based on extended folder ACLs.
 679:  *
 680:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
 681:  *
 682:  * See the enclosed file COPYING for license information (LGPL). If you
 683:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 684:  *
 685:  * @author  Gunnar Wrobel <p@rdus.de>
 686:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
 687:  * @package Kolab_FreeBusy
 688:  */
 689: class Horde_Kolab_FreeBusy_Cache_DB_xacl extends Horde_Kolab_FreeBusy_Cache_DB {
 690: 
 691:     /**
 692:      * The type of this cache.
 693:      *
 694:      * @var string
 695:      */
 696:     var $_type = 'xacl';
 697: 
 698:     /**
 699:      * Store permissions on a calender folder.
 700:      *
 701:      * @param string $filename The cache file representing the calendar folder.
 702:      * @param array  $xacl     The new extended ACL.
 703:      * @param array  $oldxacl  The old extended ACL.
 704:      *
 705:      * @return boolean|PEAR_Error True if successful.
 706:      */
 707:     function store($filename, $xacl, $oldxacl)
 708:     {
 709:         $xacl = explode(' ', $xacl);
 710:         $oldxacl = explode(' ', $oldxacl);
 711:         $both = array_intersect($xacl, $oldxacl);
 712: 
 713:         /* Removed access rights */
 714:         foreach (array_diff($oldxacl, $both) as $uid) {
 715:             if (!empty($uid)) {
 716:                 $result = $this->_remove($filename, $uid);
 717:                 if (is_a($result, 'PEAR_Error')) {
 718:                     return $result;
 719:                 }
 720:             }
 721:         }
 722: 
 723:         /* Added access rights */
 724:         foreach (array_diff($xacl, $both) as $uid) {
 725:             if (!empty($uid)) {
 726:                 $result = $this->_add($filename, $uid);
 727:                 if (is_a($result, 'PEAR_Error')) {
 728:                     return $result;
 729:                 }
 730:             }
 731:         }
 732: 
 733:         return true;
 734:     }
 735: }
 736: 
 737: /**
 738:  * A representation of a cache file.
 739:  *
 740:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
 741:  *
 742:  * See the enclosed file COPYING for license information (LGPL). If you
 743:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 744:  *
 745:  * @author  Gunnar Wrobel <p@rdus.de>
 746:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
 747:  * @package Kolab_FreeBusy
 748:  */
 749: class Horde_Kolab_FreeBusy_Cache_File {
 750: 
 751:     /**
 752:      * The suffix of this cache file.
 753:      *
 754:      * @var string
 755:      */
 756:     var $_suffix = '';
 757: 
 758:     /**
 759:      * Name of the cache file.
 760:      *
 761:      * @var string
 762:      */
 763:     var $_filename;
 764: 
 765:     /**
 766:      * Full path to the cache file.
 767:      *
 768:      * @var string
 769:      */
 770:     var $_file;
 771: 
 772:     /**
 773:      * Cache file version.
 774:      *
 775:      * @var int
 776:      */
 777:     var $_version = 1;
 778: 
 779:     /**
 780:      * Construct the Horde_Kolab_FreeBusy_Cache_File instance.
 781:      *
 782:      * @param string $cache_dir The path to the cache direcory.
 783:      * @param string $filename  The file name of the cache file.
 784:      * @param string $suffix    The suffix of the cache file name.
 785:      */
 786:     function Horde_Kolab_FreeBusy_Cache_File($cache_dir, $filename, $suffix = null)
 787:     {
 788:         if (!empty($suffix)) {
 789:             $this->_suffix = $suffix;
 790:         }
 791: 
 792:         $this->_cache_dir = $cache_dir;
 793:         $this->_filename  = $filename;
 794:         $this->_file = $this->_cache_dir . '/' . $this->_filename . '.' . $this->_suffix;
 795:     }
 796: 
 797:     /**
 798:      * Get the full path to the cache file.
 799:      *
 800:      * @return string The full path to the file.
 801:      */
 802:     function getFile()
 803:     {
 804:         return $this->_file;
 805:     }
 806: 
 807:     /**
 808:      * Clean the cache file contents.
 809:      *
 810:      * @return boolean|PEAR_Error True if successful.
 811:      */
 812:     function purge()
 813:     {
 814:         if (file_exists($this->_file)) {
 815:             $result = @unlink($this->_file);
 816:             if (!$result) {
 817:                 return PEAR::raiseError(sprintf("Failed removing file %s",
 818:                                                 $this->_file));
 819:             }
 820:         }
 821:         return true;
 822:     }
 823: 
 824:     /**
 825:      * Store data in the cache file.
 826:      *
 827:      * @param mixed $data A reference to the data object.
 828:      *
 829:      * @return boolean|PEAR_Error True if successful.
 830:      */
 831:     function store(&$data)
 832:     {
 833:         /* Create directories if missing */
 834:         $fbdirname = dirname($this->_file);
 835:         if (!is_dir($fbdirname)) {
 836:             $result = $this->_makeTree($fbdirname);
 837:             if (is_a($result, 'PEAR_Error')) {
 838:                 return $result;
 839:             }
 840:         }
 841: 
 842:         /* Store the cache data */
 843:         $fh = fopen($this->_file, 'w');
 844:         if (!$fh) {
 845:             return PEAR::raiseError(sprintf("Failed creating cache file %s!",
 846:                                             $this->_file));
 847:         }
 848:         fwrite($fh, serialize(array('version' => $this->_version,
 849:                                     'data' => $data)));
 850:         fclose($fh);
 851:         return true;
 852:     }
 853: 
 854:     /**
 855:      * Load data from the cache file.
 856:      *
 857:      * @return mixed|PEAR_Error The data retrieved from the cache file.
 858:      */
 859:     function &load()
 860:     {
 861:         $file = @file_get_contents($this->_file);
 862:         if ($file === false) {
 863:             return PEAR::raiseError(sprintf("%s failed reading cache file %s!",
 864:                                             get_class($this), $this->_file));
 865:         }
 866:         $cache = @unserialize($file);
 867:         if ($cache === false) {
 868:             return PEAR::raiseError(sprintf("%s failed to unserialize cache data from file %s!",
 869:                                             get_class($this), $this->_file));
 870:         }
 871:         if (!isset($cache['version'])) {
 872:             return PEAR::raiseError(sprintf("Cache file %s lacks version data!",
 873:                                             $this->_file));
 874:         }
 875:         $this->_version = $cache['version'];
 876:         if (!isset($cache['data'])) {
 877:             return PEAR::raiseError(sprintf("Cache file %s lacks data!",
 878:                                             $this->_file));
 879:         }
 880:         if ($cache['version'] != $this->_version) {
 881:             return PEAR::raiseError(sprintf("Cache file %s has version %s while %s is required!",
 882:                                             $this->_file, $cache['version'], $this->_version));
 883:         }
 884:         return $cache['data'];
 885:     }
 886: 
 887:     /**
 888:      * Generate a tree of directories.
 889:      *
 890:      * @param string $dirname The path to a directory that should exist.
 891:      *
 892:      * @return boolean|PEAR_Error True if successful.
 893:      */
 894:     function _maketree($dirname)
 895:     {
 896:         $base = substr($dirname, 0, strrpos($dirname, '/'));
 897:         $base = str_replace(".", "^", $base);
 898:         if (!empty($base) && !is_dir($base)) {
 899:             $result = $this->_maketree($base);
 900:             if (is_a($result, 'PEAR_Error')) {
 901:                 return $result;
 902:             }
 903:         }
 904:         if (!file_exists($dirname)) {
 905:             $result = @mkdir($dirname, 0755);
 906:             if (!$result) {
 907:                 return PEAR::raiseError(sprintf("Error creating directory %s", $dirname));
 908:             }
 909:         }
 910:         return true;
 911:     }
 912: }
 913: 
 914: /**
 915:  * A cache file for partial free/busy information.
 916:  *
 917:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
 918:  *
 919:  * See the enclosed file COPYING for license information (LGPL). If you
 920:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 921:  *
 922:  * @author  Gunnar Wrobel <p@rdus.de>
 923:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
 924:  * @package Kolab_FreeBusy
 925:  */
 926: class Horde_Kolab_FreeBusy_Cache_File_pvcal extends Horde_Kolab_FreeBusy_Cache_File {
 927: 
 928:     /**
 929:      * The suffix of this cache file.
 930:      *
 931:      * @var string
 932:      */
 933:     var $_suffix = 'pvc';
 934: 
 935:     /**
 936:      * Store partial free/busy infomation in the cache file.
 937:      *
 938:      * @param Horde_Icalendar $pvcal A reference to the data object.
 939:      *
 940:      * @return boolean|PEAR_Error True if successful.
 941:      */
 942:     function storePVcal(&$pvcal)
 943:     {
 944:         return $this->store($pvcal);
 945:     }
 946: 
 947:     /**
 948:      * Load partial free/busy data from the cache file.
 949:      *
 950:      * @param boolean $extended Should the extended information be retrieved?
 951:      *
 952:      * @return Horde_Icalendar|PEAR_Error The data retrieved from the cache file.
 953:      */
 954:     function &loadPVcal($extended)
 955:     {
 956:         $pvcal = $this->load();
 957:         if (is_a($pvcal, 'PEAR_Error')) {
 958:             return $pvcal;
 959:         }
 960:         if (!$extended) {
 961:             $components = &$pvcal->getComponents();
 962:             foreach ($components as $component) {
 963:                 if ($component->getType() == 'vFreebusy') {
 964:                     $component->_extraParams = array();
 965:                 }
 966:             }
 967:         }
 968:         return $pvcal;
 969:     }
 970: 
 971:     /**
 972:      * Return the last modification date of the cache file.
 973:      *
 974:      * @return int The last modification date.
 975:      */
 976:     function getMtime()
 977:     {
 978:         return filemtime($this->_file);
 979:     }
 980: }
 981: 
 982: /**
 983:  * A cache file for complete free/busy information.
 984:  *
 985:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
 986:  *
 987:  * See the enclosed file COPYING for license information (LGPL). If you
 988:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 989:  *
 990:  * @author  Gunnar Wrobel <p@rdus.de>
 991:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
 992:  * @package Kolab_FreeBusy
 993:  */
 994: class Horde_Kolab_FreeBusy_Cache_File_vcal extends Horde_Kolab_FreeBusy_Cache_File {
 995: 
 996:     /**
 997:      * The suffix of this cache file.
 998:      *
 999:      * @var string
1000:      */
1001:     var $_suffix = 'vc';
1002: 
1003:     /**
1004:      * Cache file version.
1005:      *
1006:      * @var int
1007:      */
1008:     var $_version = 2;
1009: 
1010:     /**
1011:      * Cached data.
1012:      *
1013:      * @var array
1014:      */
1015:     var $_data;
1016: 
1017:     /**
1018:      * Construct the Horde_Kolab_FreeBusy_Cache_File_vcal instance.
1019:      *
1020:      * @param string  $cache_dir The path to the cache direcory.
1021:      * @param string  $filename  The file name of the cache file.
1022:      * @param boolean $extended  Does the cache hold extended data?
1023:      */
1024:     function Horde_Kolab_FreeBusy_Cache_File_vcal($cache_dir, $filename, $extended)
1025:     {
1026:         $extension = empty($extended) ? 'vc' : 'xvc';
1027:         parent::Horde_Kolab_FreeBusy_Cache_File($cache_dir, $filename, $extension);
1028:     }
1029: 
1030:     /**
1031:      * Store free/busy infomation in the cache file.
1032:      *
1033:      * @param Horde_Icalendar $vcal   A reference to the data object.
1034:      * @param array           $mtimes A list of modification times for the
1035:      *                                partial free/busy cache times.
1036:      *
1037:      * @return boolean|PEAR_Error True if successful.
1038:      */
1039:     function storeVcal(&$vcal, &$mtimes)
1040:     {
1041:         $data = array('vcal' => $vcal,
1042:                       'mtimes' => $mtimes);
1043:         return $this->store($data);
1044:     }
1045: 
1046:     /**
1047:      * Load the free/busy information from the cache.
1048:      *
1049:      * @return Horde_Icalendar|PEAR_Error The retrieved free/busy information.
1050:      */
1051:     function &loadVcal()
1052:     {
1053:         if ($this->_data) {
1054:             return $this->_data;
1055:         }
1056: 
1057:         $result = $this->load();
1058:         if (is_a($result, 'PEAR_Error')) {
1059:             return $result;
1060:         }
1061: 
1062:         $this->_data = $result['vcal'];
1063: 
1064:         return $this->_data;
1065:     }
1066: 
1067:     /**
1068:      * Check if the cached free/busy expired.
1069:      *
1070:      * @param array $files A list of partial free/busy cache files.
1071:      *
1072:      * @return boolean|PEAR_Error True if the cache expired.
1073:      */
1074:     function expired($files)
1075:     {
1076:         $result = $this->load();
1077:         if (is_a($result, 'PEAR_Error')) {
1078:             return $result;
1079:         }
1080: 
1081:         /* Check the cache version */
1082:         if ($this->_version < 2) {
1083:             return true;
1084:         }
1085: 
1086:         $this->_data = $result['vcal'];
1087: 
1088:         /* Files changed? */
1089:         $keys = array_keys($result['mtimes']);
1090:         $changes = array_diff($keys, $files);
1091:         if (count($keys) != count($files) || !empty($changes)) {
1092:             return true;
1093:         }
1094: 
1095:         /* Check the file mtimes */
1096:         foreach ($files as $file) {
1097:             if (filemtime($result['mtimes'][$file][0]) != $result['mtimes'][$file][1]) {
1098:                 return true;
1099:             }
1100:         }
1101: 
1102:         /* Older than three days? */
1103:         $components = $this->_data->getComponents();
1104:         foreach ($components as $component) {
1105:             if ($component->getType() == 'vFreebusy') {
1106:                 $attr = $component->getAttribute('DTSTAMP');
1107:                 if (!empty($attr) && !is_a($attr, 'PEAR_Error')) {
1108:                     //Should be configurable
1109:                     if (time() - (int)$attr > 259200) {
1110:                         return true;
1111:                     }
1112:                 }
1113:             }
1114:         }
1115: 
1116:         return false;
1117:     }
1118: }
1119: 
1120: /**
1121:  * A cache file for ACLs. This serves as a buffer between the DB based
1122:  * ACL storage and is required to hold the old ACL list for updates to
1123:  * the DB based cache.
1124:  *
1125:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
1126:  *
1127:  * See the enclosed file COPYING for license information (LGPL). If you
1128:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
1129:  *
1130:  * @author  Gunnar Wrobel <p@rdus.de>
1131:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
1132:  * @package Kolab_FreeBusy
1133:  */
1134: class Horde_Kolab_FreeBusy_Cache_File_acl extends Horde_Kolab_FreeBusy_Cache_File {
1135: 
1136:     /**
1137:      * The suffix of this cache file.
1138:      *
1139:      * @var string
1140:      */
1141:     var $_suffix = 'acl';
1142: 
1143:     /**
1144:      * Link to the ACL stored in a data base.
1145:      *
1146:      * @var Horde_Kolab_FreeBusy_Cache_DB
1147:      */
1148:     var $_acls;
1149: 
1150:     /**
1151:      * Construct the Horde_Kolab_FreeBusy_Cache_File_acl instance.
1152:      *
1153:      * @param string $cache_dir The path to the cache direcory.
1154:      * @param string $filename  The file name of the cache file.
1155:      */
1156:     function Horde_Kolab_FreeBusy_Cache_File_acl($cache_dir, $filename)
1157:     {
1158:         $this->_acls = &Horde_Kolab_FreeBusy_Cache_DB::singleton('acl', $cache_dir);
1159:         parent::Horde_Kolab_FreeBusy_Cache_File($cache_dir, $filename, 'acl');
1160:     }
1161: 
1162:     /**
1163:      * Clean the cache file contents.
1164:      *
1165:      * @return boolean|PEAR_Error True if successful.
1166:      */
1167:     function purge()
1168:     {
1169:         $oldacl = $this->load();
1170:         if (is_a($oldacl, 'PEAR_Error')) {
1171:             $oldacl = array();
1172:         }
1173: 
1174:         $result = $this->_acls->store($this->_filename, array(), $oldacl, false);
1175:         if (is_a($result, 'PEAR_Error')) {
1176:             return $result;
1177:         }
1178: 
1179:         return parent::purge();
1180:     }
1181: 
1182:     /**
1183:      * Store a new ACL.
1184:      *
1185:      * @param array  $acl       The new ACL.
1186:      * @param string $relevance Folder relevance.
1187:      * @param string $append    Should old entries be purged?
1188:      *
1189:      * @return boolean|PEAR_Error True if successful.
1190:      */
1191:     function storeACL(&$acl, $relevance, $append = false)
1192:     {
1193:         if (!$append) {
1194:             $oldacl = $this->load();
1195:             if (is_a($oldacl, 'PEAR_Error')) {
1196:                 $oldacl = array();
1197:             }
1198:             $acl = array_merge($oldacl, $acl);
1199:         } else {
1200:             $oldacl = array();
1201:         }
1202: 
1203:         /* Handle relevance */
1204:         switch ($relevance) {
1205:         case 'readers':
1206:             $perm = 'r';
1207:             break;
1208:         case 'nobody':
1209:             $perm = false;
1210:             break;
1211:         case 'admins':
1212:         default:
1213:             $perm = 'a';
1214:         }
1215: 
1216:         $result = $this->_acls->store($this->_filename, $acl, $oldacl, $perm);
1217:         if (is_a($result, 'PEAR_Error')) {
1218:             return $result;
1219:         }
1220: 
1221:         $result = $this->store($acl);
1222:         if (is_a($result, 'PEAR_Error')) {
1223:             return $result;
1224:         }
1225:         return true;
1226:     }
1227: }
1228: 
1229: /**
1230:  * A cache file for extended ACLs. This serves as a buffer between the
1231:  * DB based ACL storage and is required to hold the old ACL list for
1232:  * updates to the DB based cache.
1233:  *
1234:  * Copyright 2004-2008 Klarälvdalens Datakonsult AB
1235:  *
1236:  * See the enclosed file COPYING for license information (LGPL). If you
1237:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
1238:  *
1239:  * @author  Gunnar Wrobel <p@rdus.de>
1240:  * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
1241:  * @package Kolab_FreeBusy
1242:  */
1243: class Horde_Kolab_FreeBusy_Cache_File_xacl extends Horde_Kolab_FreeBusy_Cache_File {
1244: 
1245:     /**
1246:      * The suffix of this cache file.
1247:      *
1248:      * @var string
1249:      */
1250:     var $_suffix = 'xacl';
1251: 
1252:     /**
1253:      * Link to the ACL stored in a data base.
1254:      *
1255:      * @var Horde_Kolab_FreeBusy_Cache_DB
1256:      */
1257:     var $_xacls;
1258: 
1259:     /**
1260:      * Construct the Horde_Kolab_FreeBusy_Cache_File_xacl instance.
1261:      *
1262:      * @param string $cache_dir The path to the cache direcory.
1263:      * @param string $filename  The file name of the cache file.
1264:      */
1265:     function Horde_Kolab_FreeBusy_Cache_File_xacl($cache_dir, $filename)
1266:     {
1267:         $this->_xacls = &Horde_Kolab_FreeBusy_Cache_DB::singleton('xacl', $cache_dir);
1268:         parent::Horde_Kolab_FreeBusy_Cache_File($cache_dir, $filename, 'xacl');
1269:     }
1270: 
1271:     /**
1272:      * Clean the cache file contents.
1273:      *
1274:      * @return boolean|PEAR_Error True if successful.
1275:      */
1276:     function purge()
1277:     {
1278:         $oldxacl = $this->load();
1279:         if (is_a($oldxacl, 'PEAR_Error')) {
1280:             $oldxacl = '';
1281:         }
1282: 
1283:         $result = $this->_xacls->store($this->_filename, '', $oldxacl);
1284:         if (is_a($result, 'PEAR_Error')) {
1285:             return $result;
1286:         }
1287: 
1288:         return parent::purge();
1289:     }
1290: 
1291:     /**
1292:      * Store a new extended ACL.
1293:      *
1294:      * @param array $xacl  The new extended ACL.
1295:      * @param array $acl   General ACL for the folder.
1296:      *
1297:      * @return boolean|PEAR_Error True if successful.
1298:      */
1299:     function storeXACL(&$xacl, &$acl)
1300:     {
1301:         $oldxacl = $this->load();
1302:         if (is_a($oldxacl, 'PEAR_Error')) {
1303:             $oldxacl = '';
1304:         }
1305: 
1306:         /* Users with read access to the folder may also access the extended information */
1307:         foreach ($acl as $user => $ac) {
1308:             if (strpos($ac, 'r') !== false) {
1309:                 if (!empty($user)) {
1310:                     $xacl .= ' ' . $user;
1311:                 }
1312:             }
1313:         }
1314: 
1315:         $result = $this->_xacls->store($this->_filename, $xacl, $oldxacl);
1316:         if (is_a($result, 'PEAR_Error')) {
1317:             return $result;
1318:         }
1319: 
1320:         $result = $this->store($xacl);
1321:         if (is_a($result, 'PEAR_Error')) {
1322:             return $result;
1323:         }
1324:         return true;
1325:     }
1326: }
1327: 
API documentation generated by ApiGen