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:  * The Kolab implementation of the free/busy system.
  4:  *
  5:  * PHP version 5
  6:  *
  7:  * @category Kolab
  8:  * @package  Kolab_FreeBusy
  9:  * @author   Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
 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=Kolab_FreeBusy
 13:  */
 14: 
 15: /**
 16:  * The Horde_Kolab_FreeBusy class serves as Registry aka ServiceLocator for the
 17:  * Free/Busy application. It also provides the entry point into the the Horde
 18:  * MVC system and allows to dispatch a request.
 19:  *
 20:  * Copyright 2009-2012 Horde LLC (http://www.horde.org/)
 21:  *
 22:  * See the enclosed file COPYING for license information (LGPL). If you did not
 23:  * receive this file, see
 24:  * http://www.horde.org/licenses/lgpl21.
 25:  *
 26:  * @category Kolab
 27:  * @package  Kolab_FreeBusy
 28:  * @author   Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
 29:  * @author   Gunnar Wrobel <wrobel@pardus.de>
 30:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 31:  * @link     http://pear.horde.org/index.php?package=Kolab_FreeBusy
 32:  */
 33: class Horde_Kolab_FreeBusy_Driver_Freebusy_Base extends Horde_Kolab_FreeBusy_Driver_Base
 34: {
 35: 
 36:     /**
 37:      * Parameters for the driver.
 38:      *
 39:      * @var array
 40:      */
 41:     private $_params;
 42: 
 43:     /**
 44:      * Constructor.
 45:      *
 46:      * @param array $params The parameters for the driver.
 47:      */
 48:     public function __construct(array $params)
 49:     {
 50:         $this->_params = $params;
 51:     }
 52: 
 53:     /**
 54:      * Trigger regeneration of exported data.
 55:      *
 56:      * @param array $params The parameters required to regenerate the freebusy
 57:      *                      data.
 58:      *
 59:      * @return Horde_Kolab_FreeBusy_Driver_Result The freebusy data.
 60:      */
 61:     /* public function trigger(array $params) */
 62:     /* { */
 63: /*         $callee   = $this->getCallee(); */
 64: /*         $calendar = $this->getCalendar($callee); */
 65: 
 66: /*         $cache = $this->getCache(); */
 67: 
 68: /*         if (!$this->cacheRequested() || !$this->cacheValid($calendar, $callee)) { */
 69: /*             if (!$callee->isAuthenticated()) { */
 70: /*                 throw new Exception(); */
 71: /*             } */
 72: /*             $result = $calendar->fetch(); */
 73: /*             if ($result->isCacheable()) { */
 74: /*                 $cache->store($calendar, $result); */
 75: /*             } */
 76: /*             return $result; */
 77: /*         } */
 78: /*         return $cache->load($calendar); */
 79: /*     } */
 80: /*         $this->logger->debug(sprintf("Partial free/busy data of owner %s on server %s requested by user %s.", */
 81: /*                                      $this->callee, $this->freebusyserver, $this->user)); */
 82: 
 83: /*         if (!empty($this->remote)) { */
 84: /*             /\* Try to fetch the data if it is stored on a remote server *\/ */
 85: /*             //@todo: How to determine which hook/processor to run?  */
 86: /*             return $this->triggerRemote($params); */
 87: /*             // if (is_a($result, 'PEAR_Error')) { */
 88: /*             //    $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, 'error' => $result); */
 89: /*         } */
 90: 
 91: /*         if (!$req_cache) { */
 92: /*             /\* User wants to regenerate the cache *\/ */
 93: 
 94: /*             /\* Here we really need an authenticated IMAP user *\/ */
 95: /*             $result = $access->authenticated(); */
 96: /*             if (is_a($result, 'PEAR_Error')) { */
 97: /*                 $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, */
 98: /*                                'error' => $result); */
 99: /*                 $view = new Horde_Kolab_FreeBusy_View_error($error); */
100: /*                 return $view; */
101: /*             } */
102: 
103: /*             if (empty($access->owner)) { */
104: /*                 $message = sprintf(_("No such account %s!"), */
105: /*                                    htmlentities($access->req_owner)); */
106: /*                 $error = array('type' => FREEBUSY_ERROR_NOTFOUND, */
107: /*                                'error' => PEAR::raiseError($message)); */
108: /*                 $view = new Horde_Kolab_FreeBusy_View_error($error); */
109: /*                 return $view; */
110: /*             } */
111: 
112: /*             /\* Update the cache *\/ */
113: /*             $result = $this->_cache->store($access); */
114: /*             if (is_a($result, 'PEAR_Error')) { */
115: /*                 $error = array('type' => FREEBUSY_ERROR_NOTFOUND, */
116: /*                                'error' => $result); */
117: /*                 $view = new Horde_Kolab_FreeBusy_View_error($error); */
118: /*                 return $view; */
119: /*             } */
120: /*         } */
121: 
122: /*         /\* Load the cache data *\/ */
123: /*         $vfb = $this->_cache->loadPartial($access, $req_extended); */
124: /*         if (is_a($vfb, 'PEAR_Error')) { */
125: /*             $error = array('type' => FREEBUSY_ERROR_NOTFOUND, */
126: /*                            'error' => $vfb); */
127: /*             $view = new Horde_Kolab_FreeBusy_View_error($error); */
128: /*             return $view; */
129: /*         } */
130: 
131: /*         /\* Generate the renderer *\/ */
132: /*         //$data = array('fb' => $vfb, 'name' => $access->owner . '.ifb'); */
133: /*         //$view = new Horde_Kolab_FreeBusy_View_vfb($data); */
134: 
135: /*         /\* Finish up *\/ */
136: /*         return $view; */
137: /*     } */
138: 
139:     /**
140:      * Fetch the free/busy data.
141:      *
142:      * @params array   $params   Additional options.
143:      *
144:      * @return array The free/busy data.
145:      */
146:     public function fetch($params = array())
147:     {
148:         $this->logger->debug(sprintf("Free/busy data of owner %s requested by user %s (remote: %s).",
149:                                      $this->callee, $this->user, $this->remote));
150: 
151:         if (!empty($this->remote)) {
152:             /* Try to fetch the data if it is stored on a remote server */
153:             //@todo: How to determine which hook/processor to run?
154:             return $this->fetchRemote($params);
155:             // if (is_a($result, 'PEAR_Error')) {
156:             //    $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, 'error' => $result);
157:         }
158: 
159:         global $conf;
160: 
161:         /* Which files will we access? */
162:         if (!empty($conf['fb']['use_acls'])) {
163:             $aclcache = &Horde_Kolab_FreeBusy_Cache_DB_acl::singleton('acl', $this->_cache_dir);
164:             $files = $aclcache->get($access->owner);
165:             if (is_a($files, 'PEAR_Error')) {
166:                 return $files;
167:             }
168:         } else {
169:             $file_uid = str_replace("\0", '', str_replace(".", "^", $access->owner));
170:             $files = array();
171:             $this->findAll_readdir($file_uid, $conf['fb']['cache_dir'].'/'.$file_uid, $files);
172:         }
173: 
174:         $owner = $access->owner;
175:         if (ereg('(.*)@(.*)', $owner, $regs)) {
176:             $owner = $regs[2] . '/' . $regs[1];
177:         }
178:         $user = $access->user;
179:         if (ereg('(.*)@(.*)', $user, $regs)) {
180:             $user = $regs[2] . '/' . $regs[1];
181:         }
182:         $c_file = str_replace("\0", '', str_replace('.', '^', $user . '/' . $owner));
183: 
184:         $c_vcal = new Horde_Kolab_FreeBusy_Cache_File_vcal($this->_cache_dir,
185:                                 $c_file, $extended);
186: 
187:         /* If the current vCal cache did not expire, we can deliver it */
188:         if (!$this->cache->expired($files)) {
189:             return $this->cache->loadVcal();
190:         }
191: 
192:         // Create the new iCalendar.
193:         $vCal = new Horde_Icalendar();
194:         $vCal->setAttribute('PRODID', '-//kolab.org//NONSGML Kolab Server 2//EN');
195:         $vCal->setAttribute('METHOD', 'PUBLISH');
196: 
197:         // Create new vFreebusy.
198:         $vFb = Horde_Icalendar::newComponent('vfreebusy', $vCal);
199:         $params = array();
200: 
201:         $cn = $access->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_CN);
202:         if (!empty($cn) || is_a($cn, 'PEAR_Error')) {
203:             $params['cn'] = $access->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_CN);
204:         }
205:         $vFb->setAttribute('ORGANIZER', 'MAILTO:' . $access->owner, $params);
206: 
207:         $vFb->setAttribute('DTSTAMP', time());
208:         if (isset($_SERVER['SERVER_NAME'])) {
209:             $host = $_SERVER['SERVER_NAME'];
210:         } else {
211:             $host = 'localhost';
212:         }
213:         if (isset($_SERVER['REQUEST_URI'])) {
214:             $uri = $_SERVER['REQUEST_URI'];
215:         } else {
216:             $uri = '/';
217:         }
218:         $vFb->setAttribute('URL', 'http://' . $host . $uri);
219: 
220:         $mtimes = array();
221:         foreach ($files as $file) {
222:             if ($extended && !empty($conf['fb']['use_acls'])) {
223:                 $extended_pvc = $this->_allowExtended($file, $access);
224:             } else {
225:                 $extended_pvc = $extended;
226:             }
227:             $c_pvcal = new Horde_Kolab_FreeBusy_Cache_File_pvcal($this->_cache_dir, $file);
228:             $pvCal = $c_pvcal->loadPVcal($extended_pvc);
229:             if (is_a($pvCal, 'PEAR_Error')) {
230:                 Horde::logMessage(sprintf("Ignoring partial free/busy file %s: %s)",
231:                                           $file, $pvCal->getMessage()),
232:                                   __FILE__, __LINE__, PEAR_LOG_INFO);
233:                 continue;
234:             }
235:             $pvFb = &$pvCal->findComponent('vfreebusy');
236:             if( !$pvFb ) {
237:                 Horde::logMessage(sprintf("Could not find free/busy info in file %s.)",
238:                                           $file), __FILE__, __LINE__, PEAR_LOG_INFO);
239:                 continue;
240:             }
241:             if ($ets = $pvFb->getAttributeDefault('DTEND', false) !== false) {
242:                 // PENDING(steffen): Make value configurable
243:                 if ($ets < time()) {
244:                     Horde::logMessage(sprintf("Free/busy info in file %s is too old.)",
245:                                               $file), __FILE__, __LINE__, PEAR_LOG_INFO);
246:                     $c_pvcal->purge();
247:                     continue;
248:                 }
249:             }
250:             $vFb->merge($pvFb);
251: 
252:             /* Store last modification time */
253:             $mtimes[$file] = array($c_pvcal->getFile(), $c_pvcal->getMtime());
254:         }
255: 
256:         if (!empty($conf['fb']['remote_servers'])) {
257:             $remote_vfb = $this->_fetchRemote($conf['fb']['remote_servers'],
258:                                               $access);
259:             if (is_a($remote_vfb, 'PEAR_Error')) {
260:                 Horde::logMessage(sprintf("Ignoring remote free/busy files: %s)",
261:                                           $remote_vfb->getMessage()),
262:                                   __FILE__, __LINE__, PEAR_LOG_INFO);
263:             } else {
264:                 $vFb->merge($remote_vfb);
265:             }
266:         }
267: 
268:         if (!(boolean)$vFb->getBusyPeriods()) {
269:             /* No busy periods in fb list. We have to add a
270:              * dummy one to be standards compliant
271:              */
272:             $vFb->setAttribute('COMMENT', 'This is a dummy vfreebusy that indicates an empty calendar');
273:             $vFb->addBusyPeriod('BUSY', 0,0, null);
274:         }
275: 
276:         $vCal->addComponent($vFb);
277: 
278:         $c_vcal->storeVcal($vCal, $mtimes);
279: 
280:         return $vCal;
281: 
282:         $result = $this->app->getCache->load($access, $extended);
283:         // if (is_a($result, 'PEAR_Error')) {
284:         //    $error = array('type' => FREEBUSY_ERROR_NOTFOUND, 'error' => $result);
285: 
286:         //$data = array('fb' => $result, 'name' => $access->owner . '.vfb');
287:         //$view = &new Horde_Kolab_FreeBusy_View_vfb($data);
288:     }
289: 
290:     /**
291:      * Trigger regeneration of free/busy data in a calender.
292:      *
293:      * @return NULL
294:      */
295:     function &trigger($params = array())
296:     {
297:         $this->logger->debug(sprintf("Partial free/busy data of owner %s on server %s requested by user %s.",
298:                                      $this->callee, $this->freebusyserver, $this->user));
299: 
300:         if (!empty($this->remote)) {
301:             /* Try to fetch the data if it is stored on a remote server */
302:             //@todo: How to determine which hook/processor to run?
303:             return $this->triggerRemote($params);
304:             // if (is_a($result, 'PEAR_Error')) {
305:             //    $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, 'error' => $result);
306:         }
307: 
308:         if (!$req_cache) {
309:             /* User wants to regenerate the cache */
310: 
311:             /* Here we really need an authenticated IMAP user */
312:             $result = $access->authenticated();
313:             if (is_a($result, 'PEAR_Error')) {
314:                 $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED,
315:                                'error' => $result);
316:                 $view = new Horde_Kolab_FreeBusy_View_error($error);
317:                 return $view;
318:             }
319: 
320:             if (empty($access->owner)) {
321:                 $message = sprintf(Horde_Kolab_FreeBusy_Translation::t("No such account %s!"),
322:                                    htmlentities($access->req_owner));
323:                 $error = array('type' => FREEBUSY_ERROR_NOTFOUND,
324:                                'error' => PEAR::raiseError($message));
325:                 $view = new Horde_Kolab_FreeBusy_View_error($error);
326:                 return $view;
327:             }
328: 
329:             /* Update the cache */
330:             $result = $this->_cache->store($access);
331:             if (is_a($result, 'PEAR_Error')) {
332:                 $error = array('type' => FREEBUSY_ERROR_NOTFOUND,
333:                                'error' => $result);
334:                 $view = new Horde_Kolab_FreeBusy_View_error($error);
335:                 return $view;
336:             }
337:         }
338: 
339:         /* Load the cache data */
340:         $vfb = $this->_cache->loadPartial($access, $req_extended);
341:         if (is_a($vfb, 'PEAR_Error')) {
342:             $error = array('type' => FREEBUSY_ERROR_NOTFOUND,
343:                            'error' => $vfb);
344:             $view = new Horde_Kolab_FreeBusy_View_error($error);
345:             return $view;
346:         }
347: 
348:         /* Generate the renderer */
349:         //$data = array('fb' => $vfb, 'name' => $access->owner . '.ifb');
350:         //$view = new Horde_Kolab_FreeBusy_View_vfb($data);
351: 
352:         /* Finish up */
353:         return $view;
354:     }
355: 
356:     /**
357:      * Fetch remote free/busy user if the current user is not local or
358:      * redirect to the other server if configured this way.
359:      *
360:      * @param boolean $trigger Have we been called for triggering?
361:      * @param boolean $extended Should the extended information been delivered?
362:      */
363:     function fetchRemote($trigger = false, $extended = false)
364:     {
365:         global $conf;
366: 
367:         if (!empty($conf['kolab']['freebusy']['server'])) {
368:             $server = $conf['kolab']['freebusy']['server'];
369:         } else {
370:             $server = 'https://localhost/freebusy';
371:         }
372:         if (!empty($conf['fb']['redirect'])) {
373:             $do_redirect = $conf['fb']['redirect'];
374:         } else {
375:             $do_redirect = false;
376:         }
377: 
378:         if ($trigger) {
379:             $path = sprintf('/trigger/%s/%s.' . ($extended)?'pxfb':'pfb',
380:                             urlencode($this->owner), urlencode($this->imap_folder));
381:         } else {
382:             $path = sprintf('/%s.' . ($extended)?'xfb':'ifb', urlencode($this->owner));
383:         }
384: 
385:         /* Check if we are on the right server and redirect if appropriate */
386:         if ($this->freebusyserver && $this->freebusyserver != $server) {
387:             $redirect = $this->freebusyserver . $path;
388:             Horde::logMessage(sprintf("URL %s indicates remote free/busy server since we only offer %s. Redirecting.",
389:                                       $this->freebusyserver, $server), __FILE__,
390:                               __LINE__, PEAR_LOG_ERR);
391:             if ($do_redirect) {
392:                 header("Location: $redirect");
393:             } else {
394:                 header("X-Redirect-To: $redirect");
395:                 $redirect = 'https://' . urlencode($this->user) . ':' . urlencode($GLOBALS['registry']->getAuthCredential('password'))
396:                     . '@' . $this->freebusyserver . $path;
397:                 if (!@readfile($redirect)) {
398:                     $message = sprintf(Horde_Kolab_FreeBusy_Translation::t("Unable to read free/busy information from %s"),
399:                                        'https://' . urlencode($this->user) . ':XXX'
400:                                        . '@' . $this->freebusyserver . $_SERVER['REQUEST_URI']);
401:                     return PEAR::raiseError($message);
402:                 }
403:             }
404:             exit;
405:         }
406:     }
407: }
408: 
API documentation generated by ApiGen