From 762eb101bc5a266c454e45f4dcb45ec6443f0ad6 Mon Sep 17 00:00:00 2001 From: Gunnar Wrobel Date: Wed, 16 Sep 2009 15:12:18 +0200 Subject: [PATCH] Switch to Horde_Provider and start restructuring the driver part. --- .../Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php | 234 ++--------- .../lib/Horde/Kolab/FreeBusy/Access.php | 442 --------------------- .../lib/Horde/Kolab/FreeBusy/Cache.php | 137 ------- .../lib/Horde/Kolab/FreeBusy/Cache/Base.php | 56 +++ .../FreeBusy/Controller/FreebusyController.php | 203 +++------- .../lib/Horde/Kolab/FreeBusy/Driver/Base.php | 197 +++++++++ .../Horde/Kolab/FreeBusy/Driver/Freebusy/Base.php | 305 ++++++++++++++ .../Horde/Kolab/FreeBusy/Driver/Freebusy/Kolab.php | 251 ++++++++++++ .../lib/Horde/Kolab/FreeBusy/Factory.php | 217 ++++++++++ framework/Kolab_FreeBusy/package.xml | 12 + .../test/Horde/Kolab/FreeBusy/DispatchTest.php | 8 + .../test/Horde/Kolab/FreeBusy/FreeBusyTest.php | 2 + .../Mock/Controller/FreebusyController.php | 4 +- .../test/Horde/Kolab/FreeBusy/TriggerTest.php | 106 +++++ 14 files changed, 1250 insertions(+), 924 deletions(-) delete mode 100644 framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Access.php create mode 100644 framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache/Base.php create mode 100644 framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Base.php create mode 100644 framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Base.php create mode 100644 framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Kolab.php create mode 100644 framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Factory.php create mode 100644 framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/TriggerTest.php diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php index 09a7f1894..d616936bf 100644 --- a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php @@ -12,7 +12,7 @@ */ /** - * The Horde_Kolab_FreeBusy class serves as Registry aka ServiceLocator for the + * The Horde_Kolab_FreeBusy class holds the Registry aka ServiceLocator for the * Free/Busy application. It also provides the entry point into the the Horde * MVC system and allows to dispatch a request. * @@ -39,35 +39,52 @@ class Horde_Kolab_FreeBusy static protected $instance; /** - * The object representing the request. + * The provider for dependency injection * - * @var Horde_Controller_Request_Base + * @var Horde_Provider_Base */ - private $_request; + protected $provider; /** - * The object representing the request<->controller mapping. + * Constructor. * - * @var Horde_Routes_Mapper + * @param array $params The parameters required to initialize the + * application. */ - private $_mapper; + public function __construct($params = array()) + { + $this->provider = new Horde_Provider_Base(); + $this->provider->params = $params; + $this->provider->request = new Horde_Provider_Injection_Factory(array('Horde_Kolab_FreeBusy_Factory', 'getRequest')); + $this->provider->mapper = new Horde_Provider_Injection_Factory(array('Horde_Kolab_FreeBusy_Factory', 'getMapper')); + $this->provider->dispatcher = new Horde_Provider_Injection_Factory(array('Horde_Kolab_FreeBusy_Factory', 'getDispatcher')); + $this->provider->logger = new Horde_Provider_Injection_Factory(array('Horde_Kolab_FreeBusy_Factory', 'getLogger')); + $this->provider->driver = new Horde_Provider_Injection_Factory(array('Horde_Kolab_FreeBusy_Driver_Base', 'factory')); + } /** - * The request dispatcher. + * Get an element. + * + * @param string $key The key of the element to retrieve. * - * @var Horde_Controller_Dispatcher + * @return mixed The element. */ - private $_dispatcher; + public function __get($key) + { + return $this->provider->{$key}; + } /** - * Constructor. + * Set an element to the given value. * - * @param array $params The parameters required to initialize the - * application. + * @param string $key The key of the element to set. + * @param mixed $value The value to set the element to. + * + * @return NULL */ - public function __construct($params = array()) + public function __set($key, $value) { - $this->_params = $params; + $this->provider->{$key} = $value; } /** @@ -114,6 +131,14 @@ class Horde_Kolab_FreeBusy * 'controllerDir' - (string) The directory holding controllers. * 'viewsDir' - (string) The directory holding views. * + * 'logger' - (array) The keys of the array are log handler class names + * (e.g. Horde_Log_Handler_Stream) while the + * corresponding values are arrays. Each such array + * may contain a key 'params' that holds parameters + * passed to the constructor of the log handler. It + * may also hold a second key 'options' with options + * passed to the instantiated log handler. [optional] + * * * * @return Horde_Kolab_FreeBusy The Horde_Registry instance. @@ -138,183 +163,6 @@ class Horde_Kolab_FreeBusy } /** - * Inject the request object into the application context. - * - * @param Horde_Controller_Request_Base $request The object that should - * represent the current - * request. - * - * @return NULL - */ - public function setRequest(Horde_Controller_Request_Base $request) - { - $this->_request = $request; - } - - /** - * Return the object representing the current request. - * - * @return Horde_Controller_Request_Base The current request. - * - * @throws Horde_Exception - */ - public function getRequest() - { - if (!isset($this->_request)) { - if (!empty($this->_params['request']['class'])) { - $request_class = $this->_params['request']['class']; - } else { - $request_class = 'Horde_Controller_Request_Http'; - } - if (!empty($this->_params['request']['params'])) { - $params = $this->_params['request']['params']; - } else { - $params = array(); - } - // Set up our request and routing objects - $this->_request = new $request_class($params); - /** - * The HTTP request object would hide errors. Display them. - */ - if (isset($this->request->_exception)) { - throw $this->request->_exception; - } - } - - return $this->_request; - } - - /** - * Inject the mapper object into the application context. - * - * @param Horde_Route_Mapper $mapper The object that handles mapping. - * - * @return NULL - */ - public function setMapper(Horde_Route_Mapper $mapper) - { - $this->_mapper = $mapper; - } - - /** - * Return the mapper. - * - * @return Horde_Route_Mapper The mapper. - * - * @throws Horde_Exception - */ - public function getMapper() - { - if (!isset($this->_mapper)) { - if (!empty($this->_params['mapper']['params'])) { - $params = $this->_params['mapper']['params']; - } else { - $params = array(); - } - $this->_mapper = new Horde_Routes_Mapper($params); - - /** - * Application routes are relative only to the application. Let the - * mapper know where they start. - */ - if (!empty($this->_params['script'])) { - $this->_mapper->prefix = dirname($this->_params['script']); - } else { - $this->_mapper->prefix = dirname($_SERVER['PHP_SELF']); - } - - // Check for route definitions. - if (!empty($this->_params['config']['dir'])) { - $routeFile = $this->_params['config']['dir'] . '/routes.php'; - } - if (empty($this->_params['config']['dir']) - || !file_exists($routeFile)) { - $this->_mapper->connect(':(mail).:(type)', - array('controller' => 'freebusy', - 'action' => 'fetch', - 'requirements' => array('type' => '(i|x|v)fb', - 'mail' => '[^/]+'), - )); - - $this->_mapper->connect('trigger/*(folder).pfb', - array('controller' => 'freebusy', - 'action' => 'trigger' - )); - - $this->_mapper->connect('*(folder).:(type)', - array('controller' => 'freebusy', - 'action' => 'trigger', - 'requirements' => array('type' => '(p|px)fb'), - )); - - $this->_mapper->connect('delete/:(mail)', - array('controller' => 'freebusy', - 'action' => 'delete', - 'requirements' => array('mail' => '[^/]+'), - )); - - $this->_mapper->connect('regenerate', - array('controller' => 'freebusy', - 'action' => 'regenerate', - )); - } else { - // Load application routes. - include $routeFile; - } - } - - return $this->_mapper; - } - - /** - * Inject the dispatcher object into the application context. - * - * @param Horde_Controller_Dispatcher $dispatcher The object that handles - * dispatching. - * - * @return NULL - */ - public function setDispatcher(Horde_Controller_Dispatcher $dispatcher) - { - $this->_dispatcher = $dispatcher; - } - - /** - * Return the dispatcher. - * - * @return Horde_Controller_Dispatcher The dispatcher. - * - * @throws Horde_Exception - */ - public function getDispatcher() - { - if (!isset($this->_dispatcher)) { - if (empty($this->_params['dispatch']['controllerDir'])) { - $controllerDir = dirname(__FILE__) . '/FreeBusy/Controller'; - } else { - $controllerDir = $this->_params['dispatch']['controllerDir']; - } - - if (empty($this->_params['dispatch']['viewsDir'])) { - $viewsDir = dirname(__FILE__) . '/FreeBusy/View'; - } else { - $viewsDir = $this->_params['dispatch']['viewsDir']; - } - - $context = array( - 'mapper' => $this->getMapper(), - 'controllerDir' => $controllerDir, - 'viewsDir' => $viewsDir, - // 'logger' => '', - ); - - $this->_dispatcher = Horde_Controller_Dispatcher::singleton($context); - } - - return $this->_dispatcher; - } - - /** * Handle the current request. * * @return NULL @@ -322,7 +170,7 @@ class Horde_Kolab_FreeBusy public function dispatch() { try { - $this->getDispatcher()->dispatch($this->getRequest()); + $this->provider->dispatcher->dispatch($this->provider->request); } catch (Exception $e) { //@todo: Error view throw $e; diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Access.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Access.php deleted file mode 100644 index 0bb50871d..000000000 --- a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Access.php +++ /dev/null @@ -1,442 +0,0 @@ - - * @author Steffen Hansen - * @package Kolab_FreeBusy - */ -class Horde_Kolab_FreeBusy_Access { - - /** - * The user calling the script. - * - * @var string - */ - var $user; - - /** - * Did the above combination authenticate? - * - * @var string - */ - var $_authenticated = false; - - /** - * The object representing the user calling the script. - * - * @var string - */ - var $user_object; - - /** - * The requested owner. - * - * @var string - */ - var $owner; - - /** - * The object representing the folder owner. - * - * @var string - */ - var $owner_object; - - /** - * The object representing the server configuration. - * - * @var string - */ - var $server_object; - - /** - * The folder we try to access. - * - * @var string - */ - var $folder; - - /** - * The IMAP path of folder we try to access. - * - * @var string - */ - var $imap_folder; - - /** - * The common name (CN) of the owner. - * - * @var string - */ - var $cn = ''; - - /** - * The free/busy server for the folder owner. - * - * @var string - */ - var $freebusyserver; - - /** - * Constructor. - * - * @param array $params Any additional options - */ - function Horde_Kolab_FreeBusy_Access() - { - $this->_parseUser(); - } - - /** - * Parse the requested folder for the owner of that folder. - * - * @param string $req_folder The folder requested. - * - * @return boolean|PEAR_Error True if successful. - */ - function parseFolder($req_folder = '') - { - /* Handle the owner/folder name and make sure the owner part is in lower case */ - $req_folder = Horde_String::convertCharset($req_folder, 'UTF-8', 'UTF7-IMAP'); - $folder = explode('/', $req_folder); - if (count($folder) < 2) { - return PEAR::raiseError(sprintf(_("No such folder %s"), $req_folder)); - } - - $folder[0] = strtolower($folder[0]); - $req_folder = implode('/', $folder); - $this->owner = $folder[0]; - unset($folder[0]); - $this->folder = join('/', $folder); - - $result = $this->_process(); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - return true; - } - - /** - * Parse the owner value. - * - * @param string $owner The owner that should be processed. - * - * @return boolean|PEAR_Error True if successful. - */ - function parseOwner($owner = '') - { - $this->owner = $owner; - - $result = $this->_process(); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - return true; - } - - /** - * Fetch remote free/busy user if the current user is not local or - * redirect to the other server if configured this way. - * - * @param boolean $trigger Have we been called for triggering? - * @param boolean $extended Should the extended information been delivered? - */ - function fetchRemote($trigger = false, $extended = false) - { - global $conf; - - if (!empty($conf['kolab']['freebusy']['server'])) { - $server = $conf['kolab']['freebusy']['server']; - } else { - $server = 'https://localhost/freebusy'; - } - if (!empty($conf['fb']['redirect'])) { - $do_redirect = $conf['fb']['redirect']; - } else { - $do_redirect = false; - } - - if ($trigger) { - $path = sprintf('/trigger/%s/%s.' . ($extended)?'pxfb':'pfb', - urlencode($this->owner), urlencode($this->imap_folder)); - } else { - $path = sprintf('/%s.' . ($extended)?'xfb':'ifb', urlencode($this->owner)); - } - - /* Check if we are on the right server and redirect if appropriate */ - if ($this->freebusyserver && $this->freebusyserver != $server) { - $redirect = $this->freebusyserver . $path; - Horde::logMessage(sprintf("URL %s indicates remote free/busy server since we only offer %s. Redirecting.", - $this->freebusyserver, $server), __FILE__, - __LINE__, PEAR_LOG_ERR); - if ($do_redirect) { - header("Location: $redirect"); - } else { - header("X-Redirect-To: $redirect"); - $redirect = 'https://' . urlencode($this->user) . ':' . urlencode(Horde_Auth::getCredential('password')) - . '@' . $this->freebusyserver . $path; - if (!@readfile($redirect)) { - $message = sprintf(_("Unable to read free/busy information from %s"), - 'https://' . urlencode($this->user) . ':XXX' - . '@' . $this->freebusyserver . $_SERVER['REQUEST_URI']); - return PEAR::raiseError($message); - } - } - exit; - } - } - - /** - * Check if we are in an authenticated situation. - * - * @return boolean|PEAR_Error True if successful. - */ - function authenticated() - { - global $conf; - - if (empty($this->user)) { - header('WWW-Authenticate: Basic realm="Kolab Freebusy"'); - return PEAR::raiseError(_("Please authenticate!")); - } - - if (!$this->_authenticated) { - return PEAR::raiseError(sprintf(_("Invalid authentication for user %s!"), - $this->user)); - } - return true; - } - - /** - * Parse the current user accessing the page and try to - * authenticate the user. - */ - function _parseUser() - { - global $conf; - - $this->user = Horde_Auth::getAuth(); - - if (empty($this->user)) { - $this->user = isset($_SERVER['PHP_AUTH_USER'])?$_SERVER['PHP_AUTH_USER']:false; - $pass = isset($_SERVER['PHP_AUTH_PW'])?$_SERVER['PHP_AUTH_PW']:false; - } else { - $this->_authenticated = true; - return; - } - - // This part allows you to use the PHP scripts with CGI rather than as - // an apache module. This will of course slow down things but on the - // other hand it allows you to reduce the memory footprint of the - // apache server. The default is to use PHP as a module and the CGI - // version requires specific Apache configuration. - // - // The line you need to add to your configuration of the /freebusy - // location of your server looks like this: - // - // RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}] - // - // The complete section will probably look like this then: - // - // - // RewriteEngine On - // # FreeBusy list handling - // RewriteBase /freebusy - // RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}] - // RewriteRule ^([^/]+)\.ifb freebusy.php?uid=$1 [L] - // RewriteRule ^([^/]+)\.vfb freebusy.php?uid=$1 [L] - // RewriteRule ^([^/]+)\.xfb freebusy.php?uid=$1&extended=1 [L] - // RewriteRule ^trigger/(.+)\.pfb pfb.php?folder=$1&cache=0 [L] - // RewriteRule ^(.+)\.pfb pfb.php?folder=$1&cache=1 [L] - // RewriteRule ^(.+)\.pxfb pfb.php?folder=$1&cache=1&extended=1 [L] - // - if (empty($this->user) && isset($_ENV['REDIRECT_REDIRECT_REMOTE_USER'])) { - $a = base64_decode(substr($_ENV['REDIRECT_REDIRECT_REMOTE_USER'], 6)) ; - if ((strlen($a) != 0) && (strcasecmp($a, ':') == 0)) { - list($this->user, $pass) = explode(':', $a, 2); - } - } - - if (!empty($this->user)) { - /* Load the authentication libraries */ - $auth = Horde_Auth::singleton(isset($conf['auth']['driver'])?$conf['auth']['driver']:'kolab'); - if (!$this->_authenticated) { - $this->_authenticated = $auth->authenticate($this->user, array('password' => $pass), false); - } - if ($this->_authenticated) { - @session_start(); - $_SESSION['__auth'] = array( - 'authenticated' => true, - 'userId' => $this->user, - 'timestamp' => time(), - 'remote_addr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null, - ); - Horde_Auth::setCredential('password', $pass); - } - } - } - - /** - * Process both the user accessing the page as well as the - * owner of the requested free/busy information. - * - * @return boolean|PEAR_Error True if successful. - */ - function _process() - { - global $conf; - - require_once 'Horde/Kolab/Server.php'; - - if (isset($conf['kolab']['ldap']['phpdn'])) { - $params = array( - 'uid' => $conf['kolab']['ldap']['phpdn'], - 'pass' => $conf['kolab']['ldap']['phppw'], - ); - } else { - $params = array( - 'user' => Horde_Auth::getAuth(), - 'pass' => Horde_Auth::getCredential('password'), - ); - } - - /* Connect to the Kolab user database */ - $db = &Horde_Kolab_Server::singleton($params); - // TODO: Remove once Kolab_Server has been fixed to always return the base dn - $db->fetch(); - - /* Retrieve the server configuration */ - try { - $server = $db->fetch(sprintf('k=kolab,%s', - $db->getBaseUid()), - 'Horde_Kolab_Server_Object_Kolab_Server'); - $this->server_object = $server; - } catch (Horde_Kolab_Server_Exception $e) { - Horde::logMessage(sprintf("Failed fetching the k=kolab configuration object. Error was: %s", - $e->getMessage()), - __FILE__, __LINE__, PEAR_LOG_ERR); - $this->server_object = null; - } - - /* Fetch the user calling us */ - $udn = $db->uidForIdOrMail($this->user); - if (is_a($udn, 'PEAR_Error')) { - return $udn; - } - if ($udn) { - $user = $db->fetch($udn, 'Horde_Kolab_Server_Object_Kolab_User'); - if (is_a($user, 'PEAR_Error')) { - return $user; - } - $this->user_object = $user; - } - - if ($this->user_object && $this->user_object->exists()) { - $mail = $this->user_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_MAIL); - if (is_a($mail, 'PEAR_Error')) { - return $mail; - } - if ($mail) { - $this->user = $mail; - } - } - - /* Fetch the owner of the free/busy data */ - $odn = $db->uidForIdOrMailOrAlias($this->owner); - if (is_a($odn, 'PEAR_Error')) { - return $odn; - } - if (!$odn) { - $idx = strpos($this->user, '@'); - if($idx !== false) { - $domain = substr($this->user, $idx+1); - Horde::logMessage(sprintf("Trying to append %s to %s", - $domain, $this->owner), - __FILE__, __LINE__, PEAR_LOG_DEBUG); - $odn = $odn = $db->uidForIdOrMail($this->owner . '@' . $domain); - } - } - - if ($odn) { - $owner = $db->fetch($odn, 'Horde_Kolab_Server_Object_Kolab_User'); - if (is_a($owner, 'PEAR_Error')) { - return $owner; - } - $this->owner_object = &$owner; - } - - if (!empty($this->owner_object)) { - if ($this->owner_object->exists()) { - $this->owner = $this->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_MAIL); - - $freebusyserver = $this->owner_object->getServer('freebusy'); - if (!is_a($freebusyserver, 'PEAR_Error')) { - $this->freebusyserver = $freebusyserver; - } - } - } else { - return PEAR::raiseError(_("Unable to determine owner of the free/busy data!")); - } - - /* Mangle the folder request into an IMAP folder name */ - $this->imap_folder = $this->_getImapFolder(); - - return true; - } - - /** - * Calculate the correct IMAP folder name to access based on the - * combination of user and owner. - * - * @return string The IMAP folder we should access. - */ - function _getImapFolder() - { - $userdom = false; - $ownerdom = false; - if (ereg( '(.*)@(.*)', $this->user, $regs)) { - // Regular user - $user = $regs[1]; - $userdom = $regs[2]; - } else { - $user = $this->user; - } - - if(ereg( '(.*)@(.*)', $this->owner, $regs)) { - // Regular owner - $owner = $regs[1]; - $ownerdom = $regs[2]; - } else { - $owner = $this->owner; - } - - $fldrcomp = array(); - if ($user == $owner) { - $fldrcomp[] = 'INBOX'; - } else { - $fldrcomp[] = 'user'; - $fldrcomp[] = $owner; - } - - if (!empty($this->folder)) { - $fldrcomp[] = $this->folder; - } - - $folder = join('/', $fldrcomp); - if ($ownerdom && !$userdom) { - $folder .= '@' . $ownerdom; - } - return $folder; - } - -} - diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache.php index d52d38f50..9091af980 100644 --- a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache.php +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache.php @@ -227,143 +227,6 @@ class Horde_Kolab_FreeBusy_Cache { } /** - * Load the complete free/busy data of a user. - * - * @param Horde_Kolab_FreeBusy_Access $access The object holding the - * relevant access - * parameters. - * @param boolean $extended Should the data hold the extended - * free/busy information? - * - * @return Horde_iCalendar|PEAR_Error The free/busy data for a user. - */ - function &load(&$access, $extended) - { - global $conf; - - /* Which files will we access? */ - if (!empty($conf['fb']['use_acls'])) { - $aclcache = &Horde_Kolab_FreeBusy_Cache_DB_acl::singleton('acl', $this->_cache_dir); - $files = $aclcache->get($access->owner); - if (is_a($files, 'PEAR_Error')) { - return $files; - } - } else { - $file_uid = str_replace("\0", '', str_replace(".", "^", $access->owner)); - $files = array(); - $this->findAll_readdir($file_uid, $conf['fb']['cache_dir'].'/'.$file_uid, $files); - } - - $owner = $access->owner; - if (ereg('(.*)@(.*)', $owner, $regs)) { - $owner = $regs[2] . '/' . $regs[1]; - } - $user = $access->user; - if (ereg('(.*)@(.*)', $user, $regs)) { - $user = $regs[2] . '/' . $regs[1]; - } - $c_file = str_replace("\0", '', str_replace('.', '^', $user . '/' . $owner)); - - $c_vcal = new Horde_Kolab_FreeBusy_Cache_File_vcal($this->_cache_dir, - $c_file, $extended); - - /* If the current vCal cache did not expire, we can deliver it */ - if (!$c_vcal->expired($files)) { - return $c_vcal->loadVcal(); - } - - // Create the new iCalendar. - $vCal = new Horde_iCalendar(); - $vCal->setAttribute('PRODID', '-//kolab.org//NONSGML Kolab Server 2//EN'); - $vCal->setAttribute('METHOD', 'PUBLISH'); - - // Create new vFreebusy. - $vFb = &Horde_iCalendar::newComponent('vfreebusy', $vCal); - $params = array(); - - $cn = $access->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_CN); - if (!empty($cn) || is_a($cn, 'PEAR_Error')) { - $params['cn'] = $access->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_CN); - } - $vFb->setAttribute('ORGANIZER', 'MAILTO:' . $access->owner, $params); - - $vFb->setAttribute('DTSTAMP', time()); - if (isset($_SERVER['SERVER_NAME'])) { - $host = $_SERVER['SERVER_NAME']; - } else { - $host = 'localhost'; - } - if (isset($_SERVER['REQUEST_URI'])) { - $uri = $_SERVER['REQUEST_URI']; - } else { - $uri = '/'; - } - $vFb->setAttribute('URL', 'http://' . $host . $uri); - - $mtimes = array(); - foreach ($files as $file) { - if ($extended && !empty($conf['fb']['use_acls'])) { - $extended_pvc = $this->_allowExtended($file, $access); - } else { - $extended_pvc = $extended; - } - $c_pvcal = new Horde_Kolab_FreeBusy_Cache_File_pvcal($this->_cache_dir, $file); - $pvCal = $c_pvcal->loadPVcal($extended_pvc); - if (is_a($pvCal, 'PEAR_Error')) { - Horde::logMessage(sprintf("Ignoring partial free/busy file %s: %s)", - $file, $pvCal->getMessage()), - __FILE__, __LINE__, PEAR_LOG_INFO); - continue; - } - $pvFb = &$pvCal->findComponent('vfreebusy'); - if( !$pvFb ) { - Horde::logMessage(sprintf("Could not find free/busy info in file %s.)", - $file), __FILE__, __LINE__, PEAR_LOG_INFO); - continue; - } - if ($ets = $pvFb->getAttributeDefault('DTEND', false) !== false) { - // PENDING(steffen): Make value configurable - if ($ets < time()) { - Horde::logMessage(sprintf("Free/busy info in file %s is too old.)", - $file), __FILE__, __LINE__, PEAR_LOG_INFO); - $c_pvcal->purge(); - continue; - } - } - $vFb->merge($pvFb); - - /* Store last modification time */ - $mtimes[$file] = array($c_pvcal->getFile(), $c_pvcal->getMtime()); - } - - if (!empty($conf['fb']['remote_servers'])) { - $remote_vfb = $this->_fetchRemote($conf['fb']['remote_servers'], - $access); - if (is_a($remote_vfb, 'PEAR_Error')) { - Horde::logMessage(sprintf("Ignoring remote free/busy files: %s)", - $remote_vfb->getMessage()), - __FILE__, __LINE__, PEAR_LOG_INFO); - } else { - $vFb->merge($remote_vfb); - } - } - - if (!(boolean)$vFb->getBusyPeriods()) { - /* No busy periods in fb list. We have to add a - * dummy one to be standards compliant - */ - $vFb->setAttribute('COMMENT', 'This is a dummy vfreebusy that indicates an empty calendar'); - $vFb->addBusyPeriod('BUSY', 0,0, null); - } - - $vCal->addComponent($vFb); - - $c_vcal->storeVcal($vCal, $mtimes); - - return $vCal; - } - - /** * Is extended access to the given file allowed? * * @param string $file Name of the cache file. diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache/Base.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache/Base.php new file mode 100644 index 000000000..3fbdd8122 --- /dev/null +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache/Base.php @@ -0,0 +1,56 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * The Horde_Kolab_FreeBusy class serves as Registry aka ServiceLocator for the + * Free/Busy application. It also provides the entry point into the the Horde + * MVC system and allows to dispatch a request. + * + * Copyright 2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Steffen Hansen + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + * @since Horde 3.2 + */ +class Horde_Kolab_FreeBusy_Cache_Base +{ + + public function getParts($callee) + { + } + + public function calleeExpired($callee, $parts) + { + } + + public function getCallee($callee, $params = array()) + { + } + + public function setCallee($callee, $parts, $data, $params = array()) + { + } + + public function getCalleePart($callee, $part, $params = array()) + { + } +} \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php index 6902ea1de..7c692537f 100644 --- a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php @@ -39,6 +39,20 @@ require_once 'Horde/Autoloader.php'; class FreeBusyController extends Horde_Controller_Base { /** + * A reference to the application class. + * + * @var Horde_Kolab_FreeBusy + */ + protected $app; + + /** + * A reference to the logger. + * + * @var Horde_Log_Logger + */ + protected $logger; + + /** * Parameters provided to this class. * * @var array @@ -53,184 +67,73 @@ class FreeBusyController extends Horde_Controller_Base var $_cache; /** - * Setup the cache. + * Constructor. */ - function _initCache() + public function __construct() { - global $conf; - - /* Load the cache class now */ - require_once 'Horde/Kolab/FreeBusy/Cache.php'; - - /* Where is the cache data stored? */ - if (!empty($conf['fb']['cache_dir'])) { - $cache_dir = $conf['fb']['cache_dir']; - } else { - if (class_exists('Horde')) { - $cache_dir = Horde::getTempDir(); - } else { - $cache_dir = '/tmp'; - } - } - - $this->_cache = new Horde_Kolab_FreeBusy_Cache($cache_dir); + /** + * The dispatcher does not know how to construct this class so we are + * left to fetching our dependencies ourselves. The application class is + * used as a service locator here. + */ + $this->app = Horde_Kolab_FreeBusy::singleton(); + $this->logger = $this->app->logger; } /** - * Trigger regeneration of free/busy data in a calender. + * Fetch the free/busy data for a user. * * @return NULL */ - function &trigger() + public function fetch() { - global $conf; - - /* Get the folder name */ - $req_folder = Horde_Util::getFormData('folder', ''); - - Horde::logMessage(sprintf("Starting generation of partial free/busy data for folder %s", - $req_folder), __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Validate folder access */ - $access = new Horde_Kolab_FreeBusy_Access(); - $result = $access->parseFolder($req_folder); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, - 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - Horde::logMessage(sprintf("Partial free/busy data of owner %s on server %s requested by user %s.", - $access->owner, $access->freebusyserver, $access->user), - __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Get the cache request variables */ - $req_cache = Horde_Util::getFormData('cache', false); - $req_extended = Horde_Util::getFormData('extended', false); - - /* Try to fetch the data if it is stored on a remote server */ - $result = $access->fetchRemote(true, $req_extended); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, - 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - $this->_initCache(); - - if (!$req_cache) { - /* User wants to regenerate the cache */ + $this->logger->debug(sprintf("Starting generation of free/busy data for user %s", + $this->params->callee)); - /* Here we really need an authenticated IMAP user */ - $result = $access->authenticated(); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, - 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - if (empty($access->owner)) { - $message = sprintf(_("No such account %s!"), - htmlentities($access->req_owner)); - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, - 'error' => PEAR::raiseError($message)); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - /* Update the cache */ - $result = $this->_cache->store($access); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, - 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - } - - /* Load the cache data */ - $vfb = $this->_cache->loadPartial($access, $req_extended); - if (is_a($vfb, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, - 'error' => $vfb); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } + $params = array('extended' => $this->params->type == 'xfb'); - Horde::logMessage("Delivering partial free/busy data.", __FILE__, __LINE__, PEAR_LOG_DEBUG); + // @todo: Reconsider this. We have been decoupled from the + // global context here but reinjecting this value seems + // extremely weird. Are there any other options? + $this->app->callee = $this->params->callee; + $this->data = $this->app->driver->fetch($this->params); - /* Generate the renderer */ - $data = array('fb' => $vfb, 'name' => $access->owner . '.ifb'); - $view = new Horde_Kolab_FreeBusy_View_vfb($data); + $this->logger->debug('Delivering complete free/busy data.'); - /* Finish up */ - Horde::logMessage("Free/busy generation complete.", __FILE__, __LINE__, PEAR_LOG_DEBUG); + /* Display the result to the user */ + $this->render(); - return $view; + $this->logger->debug('Free/busy generation complete.'); } /** - * Fetch the free/busy data for a user. + * Trigger regeneration of free/busy data in a calender. * * @return NULL */ - function &fetch() + function &trigger() { - global $conf; + $this->logger->debug(sprintf("Starting generation of partial free/busy data for folder %s", + $this->params->part)); - /* Get the user requsted */ - $req_owner = Horde_Util::getFormData('uid'); + $params = array('extended' => $this->params->type == 'pxfb', + 'cached' => $this->params->cache); - Horde::logMessage(sprintf("Starting generation of free/busy data for user %s", - $req_owner), __FILE__, __LINE__, PEAR_LOG_DEBUG); + // @todo: Reconsider this. We have been decoupled from the + // global context here but reinjecting this value seems + // extremely weird. Are there any other options? + $this->app->callee_part = $this->params->part; + $this->data = $this->app->driver->trigger($this->params); - /* Validate folder access */ - $access = new Horde_Kolab_FreeBusy_Access(); - $result = $access->parseOwner($req_owner); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - Horde::logMessage(sprintf("Free/busy data of owner %s on server %s requested by user %s.", - $access->owner, $access->freebusyserver, $access->user), - __FILE__, __LINE__, PEAR_LOG_DEBUG); - - $req_extended = Horde_Util::getFormData('extended', false); + $this->logger->debug("Delivering partial free/busy data."); - /* Try to fetch the data if it is stored on a remote server */ - $result = $access->fetchRemote(false, $req_extended); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - $this->_initCache(); + /* Display the result to the user */ + $this->render(); - $result = $this->_cache->load($access, $req_extended); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - Horde::logMessage("Delivering complete free/busy data.", __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Generate the renderer */ - $data = array('fb' => $result, 'name' => $access->owner . '.vfb'); - $view = new Horde_Kolab_FreeBusy_View_vfb($data); - - /* Finish up */ - Horde::logMessage("Free/busy generation complete.", __FILE__, __LINE__, PEAR_LOG_DEBUG); - - return $view; + $this->logger->debug("Free/busy generation complete."); } + /** * Regenerate the free/busy cache data. * diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Base.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Base.php new file mode 100644 index 000000000..d9a5d8225 --- /dev/null +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Base.php @@ -0,0 +1,197 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * The Horde_Kolab_FreeBusy class serves as Registry aka ServiceLocator for the + * Free/Busy application. It also provides the entry point into the the Horde + * MVC system and allows to dispatch a request. + * + * Copyright 2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Steffen Hansen + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + * @since Horde 3.2 + */ +class Horde_Kolab_FreeBusy_Driver_Base +{ + /** + * The user calling the script. + * + * @var string + */ + protected $user; + + /** + * The password of the user calling the script. + * + * @var string + */ + protected $pass; + + /** + * The logging handler. + * + * @var Horde_Log_Logger + */ + protected $logger; + + /** + * Constructor. + * + * @param array $params Any additional options + */ + public function __construct($callee = null, $callee_part = null, $logger = null) + { + list($this->user, $this->pass) = $this->getCredentials(); + + if (!empty($this->user)) { + $this->authenticate(); + } + + if (!empty($callee)) { + list($this->callee, $this->remote) = $this->handleCallee($callee); + } + if (!empty($callee_part)) { + list($this->callee, $this->remote, $this->part) = $this->handleCallee($callee_part); + } + + $this->logger = $logger; + } + + /** + * Create a new driver. + * + * @param Horde_Provider $provider The instance providing required + * dependencies. + * + * @return Horde_Kolab_FreeBusy_Driver_Base The new driver. + */ + static public function factory($provider) + { + $class = 'Horde_Kolab_FreeBusy_Driver_Freebusy_Kolab'; + $callee = isset($provider->callee) ? $provider->callee : null; + $callee_part = isset($provider->callee_part) ? $provider->callee_part : null; + $driver = new $class($callee, $callee_part, $provider->logger); + return $driver; + } + + /** + * Parse the current user accessing the page and try to + * authenticate the user. + */ + protected function getCredentials() + { + $user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : false; + $pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : false; + + //@todo: Fix! + // This part allows you to use the PHP scripts with CGI rather than as + // an apache module. This will of course slow down things but on the + // other hand it allows you to reduce the memory footprint of the + // apache server. The default is to use PHP as a module and the CGI + // version requires specific Apache configuration. + // + // The line you need to add to your configuration of the /freebusy + // location of your server looks like this: + // + // RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}] + // + // The complete section will probably look like this then: + // + // + // RewriteEngine On + // # FreeBusy list handling + // RewriteBase /freebusy + // RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}] + // RewriteRule ^([^/]+)\.ifb freebusy.php?uid=$1 [L] + // RewriteRule ^([^/]+)\.vfb freebusy.php?uid=$1 [L] + // RewriteRule ^([^/]+)\.xfb freebusy.php?uid=$1&extended=1 [L] + // RewriteRule ^trigger/(.+)\.pfb pfb.php?folder=$1&cache=0 [L] + // RewriteRule ^(.+)\.pfb pfb.php?folder=$1&cache=1 [L] + // RewriteRule ^(.+)\.pxfb pfb.php?folder=$1&cache=1&extended=1 [L] + // + if (empty($user) && isset($_ENV['REDIRECT_REDIRECT_REMOTE_USER'])) { + $a = base64_decode(substr($_ENV['REDIRECT_REDIRECT_REMOTE_USER'], 6)) ; + if ((strlen($a) != 0) && (strcasecmp($a, ':') == 0)) { + list($user, $pass) = explode(':', $a, 2); + } + } + return array($user, $pass); + } + + /** + * Authenticate + * + * @return boolean|PEAR_Error True if successful. + */ + public function authenticate() + { + /* Load the authentication libraries */ + require_once 'Horde/Auth.php'; + require_once 'Horde/Secret.php'; + + $auth = &Auth::singleton(isset($conf['auth']['driver'])?$conf['auth']['driver']:'kolab'); + if (!$this->_authenticated) { + $this->_authenticated = $auth->authenticate($this->user, array('password' => $pass), false); + } + if ($this->_authenticated) { + @session_start(); + $_SESSION['__auth'] = array( + 'authenticated' => true, + 'userId' => $this->user, + 'timestamp' => time(), + 'remote_addr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null, + ); + Auth::setCredential('password', $pass); + } + } + + /** + * Check if we are in an authenticated situation. + * + * @return boolean|PEAR_Error True if successful. + */ + public function authenticated() + { + global $conf; + + if (empty($this->user)) { + header('WWW-Authenticate: Basic realm="Kolab Freebusy"'); + return PEAR::raiseError(_("Please authenticate!")); + } + + if (!$this->_authenticated) { + return PEAR::raiseError(sprintf(_("Invalid authentication for user %s!"), + $this->user)); + } + return true; + } + + /** + * Fetch the data. + * + * @params array $params Additional options. + * + * @return array The fetched data. + */ + //abstract public function fetch($params = array()); +} \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Base.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Base.php new file mode 100644 index 000000000..e16c6234b --- /dev/null +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Base.php @@ -0,0 +1,305 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * The Horde_Kolab_FreeBusy class serves as Registry aka ServiceLocator for the + * Free/Busy application. It also provides the entry point into the the Horde + * MVC system and allows to dispatch a request. + * + * Copyright 2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Steffen Hansen + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + * @since Horde 3.2 + */ +class Horde_Kolab_FreeBusy_Driver_Freebusy_Base extends Horde_Kolab_FreeBusy_Driver_Base +{ + + /** + * Fetch the free/busy data. + * + * @params array $params Additional options. + * + * @return array The free/busy data. + */ + public function fetch($params = array()) + { + $this->logger->debug(sprintf("Free/busy data of owner %s requested by user %s (remote: %s).", + $this->callee, $this->user, $this->remote)); + + if (!empty($this->remote)) { + /* Try to fetch the data if it is stored on a remote server */ + //@todo: How to determine which hook/processor to run? + return $this->fetchRemote($params); + // if (is_a($result, 'PEAR_Error')) { + // $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, 'error' => $result); + } + + global $conf; + + /* Which files will we access? */ + if (!empty($conf['fb']['use_acls'])) { + $aclcache = &Horde_Kolab_FreeBusy_Cache_DB_acl::singleton('acl', $this->_cache_dir); + $files = $aclcache->get($access->owner); + if (is_a($files, 'PEAR_Error')) { + return $files; + } + } else { + $file_uid = str_replace("\0", '', str_replace(".", "^", $access->owner)); + $files = array(); + $this->findAll_readdir($file_uid, $conf['fb']['cache_dir'].'/'.$file_uid, $files); + } + + $owner = $access->owner; + if (ereg('(.*)@(.*)', $owner, $regs)) { + $owner = $regs[2] . '/' . $regs[1]; + } + $user = $access->user; + if (ereg('(.*)@(.*)', $user, $regs)) { + $user = $regs[2] . '/' . $regs[1]; + } + $c_file = str_replace("\0", '', str_replace('.', '^', $user . '/' . $owner)); + + $c_vcal = new Horde_Kolab_FreeBusy_Cache_File_vcal($this->_cache_dir, + $c_file, $extended); + + /* If the current vCal cache did not expire, we can deliver it */ + if (!$this->cache->expired($files)) { + return $this->cache->loadVcal(); + } + + // Create the new iCalendar. + $vCal = new Horde_iCalendar(); + $vCal->setAttribute('PRODID', '-//kolab.org//NONSGML Kolab Server 2//EN'); + $vCal->setAttribute('METHOD', 'PUBLISH'); + + // Create new vFreebusy. + $vFb = &Horde_iCalendar::newComponent('vfreebusy', $vCal); + $params = array(); + + $cn = $access->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_CN); + if (!empty($cn) || is_a($cn, 'PEAR_Error')) { + $params['cn'] = $access->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_CN); + } + $vFb->setAttribute('ORGANIZER', 'MAILTO:' . $access->owner, $params); + + $vFb->setAttribute('DTSTAMP', time()); + if (isset($_SERVER['SERVER_NAME'])) { + $host = $_SERVER['SERVER_NAME']; + } else { + $host = 'localhost'; + } + if (isset($_SERVER['REQUEST_URI'])) { + $uri = $_SERVER['REQUEST_URI']; + } else { + $uri = '/'; + } + $vFb->setAttribute('URL', 'http://' . $host . $uri); + + $mtimes = array(); + foreach ($files as $file) { + if ($extended && !empty($conf['fb']['use_acls'])) { + $extended_pvc = $this->_allowExtended($file, $access); + } else { + $extended_pvc = $extended; + } + $c_pvcal = new Horde_Kolab_FreeBusy_Cache_File_pvcal($this->_cache_dir, $file); + $pvCal = $c_pvcal->loadPVcal($extended_pvc); + if (is_a($pvCal, 'PEAR_Error')) { + Horde::logMessage(sprintf("Ignoring partial free/busy file %s: %s)", + $file, $pvCal->getMessage()), + __FILE__, __LINE__, PEAR_LOG_INFO); + continue; + } + $pvFb = &$pvCal->findComponent('vfreebusy'); + if( !$pvFb ) { + Horde::logMessage(sprintf("Could not find free/busy info in file %s.)", + $file), __FILE__, __LINE__, PEAR_LOG_INFO); + continue; + } + if ($ets = $pvFb->getAttributeDefault('DTEND', false) !== false) { + // PENDING(steffen): Make value configurable + if ($ets < time()) { + Horde::logMessage(sprintf("Free/busy info in file %s is too old.)", + $file), __FILE__, __LINE__, PEAR_LOG_INFO); + $c_pvcal->purge(); + continue; + } + } + $vFb->merge($pvFb); + + /* Store last modification time */ + $mtimes[$file] = array($c_pvcal->getFile(), $c_pvcal->getMtime()); + } + + if (!empty($conf['fb']['remote_servers'])) { + $remote_vfb = $this->_fetchRemote($conf['fb']['remote_servers'], + $access); + if (is_a($remote_vfb, 'PEAR_Error')) { + Horde::logMessage(sprintf("Ignoring remote free/busy files: %s)", + $remote_vfb->getMessage()), + __FILE__, __LINE__, PEAR_LOG_INFO); + } else { + $vFb->merge($remote_vfb); + } + } + + if (!(boolean)$vFb->getBusyPeriods()) { + /* No busy periods in fb list. We have to add a + * dummy one to be standards compliant + */ + $vFb->setAttribute('COMMENT', 'This is a dummy vfreebusy that indicates an empty calendar'); + $vFb->addBusyPeriod('BUSY', 0,0, null); + } + + $vCal->addComponent($vFb); + + $c_vcal->storeVcal($vCal, $mtimes); + + return $vCal; + + $result = $this->app->getCache->load($access, $extended); + // if (is_a($result, 'PEAR_Error')) { + // $error = array('type' => FREEBUSY_ERROR_NOTFOUND, 'error' => $result); + + //$data = array('fb' => $result, 'name' => $access->owner . '.vfb'); + //$view = &new Horde_Kolab_FreeBusy_View_vfb($data); + } + + /** + * Trigger regeneration of free/busy data in a calender. + * + * @return NULL + */ + function &trigger($params = array()) + { + $this->logger->debug(sprintf("Partial free/busy data of owner %s on server %s requested by user %s.", + $this->callee, $this->freebusyserver, $this->user)); + + if (!empty($this->remote)) { + /* Try to fetch the data if it is stored on a remote server */ + //@todo: How to determine which hook/processor to run? + return $this->triggerRemote($params); + // if (is_a($result, 'PEAR_Error')) { + // $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, 'error' => $result); + } + + if (!$req_cache) { + /* User wants to regenerate the cache */ + + /* Here we really need an authenticated IMAP user */ + $result = $access->authenticated(); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, + 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + if (empty($access->owner)) { + $message = sprintf(_("No such account %s!"), + htmlentities($access->req_owner)); + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, + 'error' => PEAR::raiseError($message)); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + /* Update the cache */ + $result = $this->_cache->store($access); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, + 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + } + + /* Load the cache data */ + $vfb = $this->_cache->loadPartial($access, $req_extended); + if (is_a($vfb, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, + 'error' => $vfb); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + /* Generate the renderer */ + //$data = array('fb' => $vfb, 'name' => $access->owner . '.ifb'); + //$view = new Horde_Kolab_FreeBusy_View_vfb($data); + + /* Finish up */ + return $view; + } + + /** + * Fetch remote free/busy user if the current user is not local or + * redirect to the other server if configured this way. + * + * @param boolean $trigger Have we been called for triggering? + * @param boolean $extended Should the extended information been delivered? + */ + function fetchRemote($trigger = false, $extended = false) + { + global $conf; + + if (!empty($conf['kolab']['freebusy']['server'])) { + $server = $conf['kolab']['freebusy']['server']; + } else { + $server = 'https://localhost/freebusy'; + } + if (!empty($conf['fb']['redirect'])) { + $do_redirect = $conf['fb']['redirect']; + } else { + $do_redirect = false; + } + + if ($trigger) { + $path = sprintf('/trigger/%s/%s.' . ($extended)?'pxfb':'pfb', + urlencode($this->owner), urlencode($this->imap_folder)); + } else { + $path = sprintf('/%s.' . ($extended)?'xfb':'ifb', urlencode($this->owner)); + } + + /* Check if we are on the right server and redirect if appropriate */ + if ($this->freebusyserver && $this->freebusyserver != $server) { + $redirect = $this->freebusyserver . $path; + Horde::logMessage(sprintf("URL %s indicates remote free/busy server since we only offer %s. Redirecting.", + $this->freebusyserver, $server), __FILE__, + __LINE__, PEAR_LOG_ERR); + if ($do_redirect) { + header("Location: $redirect"); + } else { + header("X-Redirect-To: $redirect"); + $redirect = 'https://' . urlencode($this->user) . ':' . urlencode(Horde_Auth::getCredential('password')) + . '@' . $this->freebusyserver . $path; + if (!@readfile($redirect)) { + $message = sprintf(_("Unable to read free/busy information from %s"), + 'https://' . urlencode($this->user) . ':XXX' + . '@' . $this->freebusyserver . $_SERVER['REQUEST_URI']); + return PEAR::raiseError($message); + } + } + exit; + } + } +} \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Kolab.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Kolab.php new file mode 100644 index 000000000..9a5328fbb --- /dev/null +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Kolab.php @@ -0,0 +1,251 @@ + + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * The Horde_Kolab_FreeBusy class serves as Registry aka ServiceLocator for the + * Free/Busy application. It also provides the entry point into the the Horde + * MVC system and allows to dispatch a request. + * + * Copyright 2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Steffen Hansen + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + * @since Horde 3.2 + */ +class Horde_Kolab_FreeBusy_Driver_Freebusy_Kolab extends Horde_Kolab_FreeBusy_Driver_Freebusy_Base +{ + /** + * Fetch the free/busy data for a user. + * + * @params array $params Additional options. + *
+     * 'extended' - Whether to fetch extended free/busy information or not.
+     * 
+ * + * @return array The free/busy data. + */ + public function fetch($params = array()) + { + $extended = !empty($params['extended']); + + } + + /** + * Parse the owner value. + * + * @param string $owner The owner that should be processed. + * + * @return boolean|PEAR_Error True if successful. + */ + protected function handleCallee($callee) + { + $this->owner = $owner; + + $result = $this->_process(); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + return true; + } + + /** + * Parse the requested folder for the owner of that folder. + * + * @param string $req_folder The folder requested. + * + * @return boolean|PEAR_Error True if successful. + */ + protected function handleCalleePart($callee_part) + { + /* Handle the owner/folder name and make sure the owner part is in lower case */ + $req_folder = Horde_String::convertCharset($req_folder, 'UTF-8', 'UTF7-IMAP'); + $folder = explode('/', $req_folder); + if (count($folder) < 2) { + return PEAR::raiseError(sprintf(_("No such folder %s"), $req_folder)); + } + + $folder[0] = strtolower($folder[0]); + $req_folder = implode('/', $folder); + $this->owner = $folder[0]; + unset($folder[0]); + $this->folder = join('/', $folder); + + $result = $this->_process(); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + return true; + } + + /** + * Process both the user accessing the page as well as the + * owner of the requested free/busy information. + * + * @return boolean|PEAR_Error True if successful. + */ + private function _process() + { + global $conf; + + require_once 'Horde/Kolab/Server.php'; + + if (isset($conf['kolab']['ldap']['phpdn'])) { + $params = array( + 'uid' => $conf['kolab']['ldap']['phpdn'], + 'pass' => $conf['kolab']['ldap']['phppw'], + ); + } else { + $params = array( + 'user' => Horde_Auth::getAuth(), + 'pass' => Horde_Auth::getCredential('password'), + ); + } + + /* Connect to the Kolab user database */ + $db = &Horde_Kolab_Server::singleton($params); + // TODO: Remove once Kolab_Server has been fixed to always return the base dn + $db->fetch(); + + /* Retrieve the server configuration */ + try { + $server = $db->fetch(sprintf('k=kolab,%s', + $db->getBaseUid()), + 'Horde_Kolab_Server_Object_Kolab_Server'); + $this->server_object = $server; + } catch (Horde_Kolab_Server_Exception $e) { + Horde::logMessage(sprintf("Failed fetching the k=kolab configuration object. Error was: %s", + $e->getMessage()), + __FILE__, __LINE__, PEAR_LOG_ERR); + $this->server_object = null; + } + + /* Fetch the user calling us */ + $udn = $db->uidForIdOrMail($this->user); + if (is_a($udn, 'PEAR_Error')) { + return $udn; + } + if ($udn) { + $user = $db->fetch($udn, 'Horde_Kolab_Server_Object_Kolab_User'); + if (is_a($user, 'PEAR_Error')) { + return $user; + } + $this->user_object = $user; + } + + if ($this->user_object && $this->user_object->exists()) { + $mail = $this->user_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_MAIL); + if (is_a($mail, 'PEAR_Error')) { + return $mail; + } + if ($mail) { + $this->user = $mail; + } + } + + /* Fetch the owner of the free/busy data */ + $odn = $db->uidForIdOrMailOrAlias($this->owner); + if (is_a($odn, 'PEAR_Error')) { + return $odn; + } + if (!$odn) { + $idx = strpos($this->user, '@'); + if($idx !== false) { + $domain = substr($this->user, $idx+1); + Horde::logMessage(sprintf("Trying to append %s to %s", + $domain, $this->owner), + __FILE__, __LINE__, PEAR_LOG_DEBUG); + $odn = $odn = $db->uidForIdOrMail($this->owner . '@' . $domain); + } + } + + if ($odn) { + $owner = $db->fetch($odn, 'Horde_Kolab_Server_Object_Kolab_User'); + if (is_a($owner, 'PEAR_Error')) { + return $owner; + } + $this->owner_object = &$owner; + } + + if (!empty($this->owner_object)) { + if ($this->owner_object->exists()) { + $this->owner = $this->owner_object->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_MAIL); + + $freebusyserver = $this->owner_object->getServer('freebusy'); + if (!is_a($freebusyserver, 'PEAR_Error')) { + $this->freebusyserver = $freebusyserver; + } + } + } else { + return PEAR::raiseError(_("Unable to determine owner of the free/busy data!")); + } + + /* Mangle the folder request into an IMAP folder name */ + $this->imap_folder = $this->_getImapFolder(); + + return true; + } + + /** + * Calculate the correct IMAP folder name to access based on the + * combination of user and owner. + * + * @return string The IMAP folder we should access. + */ + function _getImapFolder() + { + $userdom = false; + $ownerdom = false; + if (ereg( '(.*)@(.*)', $this->user, $regs)) { + // Regular user + $user = $regs[1]; + $userdom = $regs[2]; + } else { + $user = $this->user; + } + + if(ereg( '(.*)@(.*)', $this->owner, $regs)) { + // Regular owner + $owner = $regs[1]; + $ownerdom = $regs[2]; + } else { + $owner = $this->owner; + } + + $fldrcomp = array(); + if ($user == $owner) { + $fldrcomp[] = 'INBOX'; + } else { + $fldrcomp[] = 'user'; + $fldrcomp[] = $owner; + } + + if (!empty($this->folder)) { + $fldrcomp[] = $this->folder; + } + + $folder = join('/', $fldrcomp); + if ($ownerdom && !$userdom) { + $folder .= '@' . $ownerdom; + } + return $folder; + } +} \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Factory.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Factory.php new file mode 100644 index 000000000..1e6f2cda0 --- /dev/null +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Factory.php @@ -0,0 +1,217 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * Factory methods for objects required by the free/busy system. + * + * Copyright 2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + * @since Horde 3.2 + */ +class Horde_Kolab_FreeBusy_Factory +{ + + /** + * Create the object representing the current request. + * + * @param Horde_Provider $provider The instance providing required + * dependencies. + * + * @return Horde_Controller_Request_Base The current request. + * + * @throws Horde_Exception + */ + static public function getRequest($provider) + { + $params = isset($provider->params['request']) ? $provider->params['request'] : array(); + if (!empty($params['class'])) { + $request_class = $params['class']; + } else { + $request_class = 'Horde_Controller_Request_Http'; + } + + if (!empty($params['params'])) { + $request_params = $params['params']; + } else { + $request_params = array(); + } + + /** Set up our request and routing objects */ + $request = new $request_class($request_params); + + /** The HTTP request object would hide errors. Display them. */ + if (isset($request->_exception)) { + throw $request->_exception; + } + + return $request; + } + + /** + * Create the mapper. + * + * @param Horde_Provider $provider The instance providing required + * dependencies. + * + * @return Horde_Route_Mapper The mapper. + * + * @throws Horde_Exception + */ + static public function getMapper($provider) + { + $params = isset($provider->params['mapper']) ? $provider->params['mapper'] : array(); + if (!empty($params['params'])) { + $mapper_params = $params['params']; + } else { + $mapper_params = array(); + } + $mapper = new Horde_Routes_Mapper($mapper_params); + + /** + * Application routes are relative only to the application. Let the + * mapper know where they start. + */ + if (!empty($provider->params['script'])) { + $mapper->prefix = dirname($provider->params['script']); + } else { + $mapper->prefix = dirname($_SERVER['PHP_SELF']); + } + + // Check for route definitions. + if (!empty($provider->params['config']['dir'])) { + $routeFile = $provider->params['config']['dir'] . '/routes.php'; + } + if (empty($params['config']['dir']) + || !file_exists($routeFile)) { + $mapper->connect(':(callee).:(type)', + array('controller' => 'freebusy', + 'action' => 'fetch', + 'requirements' => array('type' => '(i|x|v)fb', + 'callee' => '[^/]+'), + )); + + $mapper->connect('trigger/*(folder).pfb', + array('controller' => 'freebusy', + 'action' => 'trigger' + )); + + $mapper->connect('*(folder).:(type)', + array('controller' => 'freebusy', + 'action' => 'trigger', + 'requirements' => array('type' => '(p|px)fb'), + )); + + $mapper->connect('delete/:(callee)', + array('controller' => 'freebusy', + 'action' => 'delete', + 'requirements' => array('callee' => '[^/]+'), + )); + + $mapper->connect('regenerate', + array('controller' => 'freebusy', + 'action' => 'regenerate', + )); + } else { + // Load application routes. + include $routeFile; + } + return $mapper; + } + + /** + * Create the dispatcher. + * + * @param Horde_Provider $provider The instance providing required + * dependencies. + * + * @return Horde_Controller_Dispatcher The dispatcher. + */ + static public function getDispatcher($provider) + { + $params = isset($provider->params['dispatch']) ? $provider->params['dispatch'] : array(); + if (empty($params['controllerDir'])) { + $controllerDir = dirname(__FILE__) . '/Controller'; + } else { + $controllerDir = $params['controllerDir']; + } + + if (empty($params['viewsDir'])) { + $viewsDir = dirname(__FILE__) . '/View'; + } else { + $viewsDir = $params['viewsDir']; + } + + $context = array( + 'mapper' => $provider->mapper, + 'controllerDir' => $controllerDir, + 'viewsDir' => $viewsDir, + 'logger' => $provider->logger, + ); + + $dispatcher = Horde_Controller_Dispatcher::singleton($context); + + return $dispatcher; + } + + /** + * Return the logger. + * + * @param Horde_Provider $provider The instance providing required + * dependencies. + * + * @return Horde_Log_Logger The logger. + */ + static public function getLogger($provider) + { + $logger = new Horde_Log_Logger(); + + $logger_params = isset($provider->params['logger']) ? $provider->params['logger'] : array(); + + if (empty($params)) { + $handlers = array('Horde_Log_Handler_Syslog' => array()); + } else { + $handlers = $logger_params['logger']; + } + + foreach ($handlers as $name => $params) { + if (!empty($params['params'])) { + /** + * We need to pass parameters to the constructor so use + * reflection. + */ + $reflectionObj = new ReflectionClass($name); + $handler = $reflectionObj->newInstanceArgs($params['params']); + } else { + $handler = new $name(); + } + + if (!empty($params['options'])) { + foreach ($params['options'] as $key => $value) { + $handler->setOption($key, $value); + } + } + + $logger->addHandler($handler); + } + return $logger; + } +} \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/package.xml b/framework/Kolab_FreeBusy/package.xml index 212f160ee..19661b431 100644 --- a/framework/Kolab_FreeBusy/package.xml +++ b/framework/Kolab_FreeBusy/package.xml @@ -67,6 +67,14 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + + + + + @@ -146,6 +154,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + diff --git a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/DispatchTest.php b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/DispatchTest.php index 8cbf510a5..fe3f7b57c 100644 --- a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/DispatchTest.php +++ b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/DispatchTest.php @@ -35,6 +35,8 @@ class Horde_Kolab_FreeBusy_DispatchTest extends Horde_Kolab_Test_FreeBusy /** * Test setup. + * + * @return NULL */ public function setUp() { @@ -49,6 +51,8 @@ class Horde_Kolab_FreeBusy_DispatchTest extends Horde_Kolab_Test_FreeBusy /** * Test destruction. + * + * @return NULL */ public function tearDown() { @@ -96,8 +100,12 @@ class Horde_Kolab_FreeBusy_DispatchTest extends Horde_Kolab_Test_FreeBusy ), 'dispatch' => array( 'controllerDir' => dirname(__FILE__) . '/Mock/Controller', + ), + 'logger' => array( + 'Horde_Log_Handler_Null' => array(), ) ); + $application = Horde_Kolab_FreeBusy::singleton($params); $output = ''; diff --git a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/FreeBusyTest.php b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/FreeBusyTest.php index 0aa7124a7..9fef9ec27 100644 --- a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/FreeBusyTest.php +++ b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/FreeBusyTest.php @@ -35,6 +35,8 @@ class Horde_Kolab_FreeBusy_FreeBusyTest extends Horde_Kolab_Test_FreeBusy /** * Test setup. + * + * @return NULL */ public function setUp() { diff --git a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/Mock/Controller/FreebusyController.php b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/Mock/Controller/FreebusyController.php index bdd80fe97..226b6daed 100644 --- a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/Mock/Controller/FreebusyController.php +++ b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/Mock/Controller/FreebusyController.php @@ -52,7 +52,7 @@ class FreeBusyController extends Horde_Controller_Base */ public function fetch() { - $this->renderText('fetched "' . $this->params->type . '" data for user "' . $this->params->mail . '"'); + $this->renderText('fetched "' . $this->params->type . '" data for user "' . $this->params->callee . '"'); } /** @@ -72,6 +72,6 @@ class FreeBusyController extends Horde_Controller_Base */ public function delete() { - $this->renderText('deleted data for user "' . $this->params->mail . '"'); + $this->renderText('deleted data for user "' . $this->params->callee . '"'); } } \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/TriggerTest.php b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/TriggerTest.php new file mode 100644 index 000000000..dece032c4 --- /dev/null +++ b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/TriggerTest.php @@ -0,0 +1,106 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * The Autoloader allows us to omit "require/include" statements. + */ +require_once 'Horde/Autoloader.php'; + +/** + * Test triggering the generation/caching of free/busy data. + * + * Copyright 2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ +class Horde_Kolab_FreeBusy_TriggerTest extends Horde_Kolab_Test_FreeBusy +{ + + /** + * Test setup. + * + * @return NULL + */ + public function setUp() + { + /** + * The controller automatically starts a session. But we don't want to + * send out cookie headers since we are running in PHPUnit. + */ + ini_set('session.use_cookies', 0); + ini_set('session.use_only_cookies', 0); + session_cache_limiter(null); + } + + /** + * Test destruction. + * + * @return NULL + */ + public function tearDown() + { + Horde_Kolab_FreeBusy::destroy(); + } + + /** + * Test triggering a folder. + * + * @return NULL + */ + public function testTriggering() + { + $params = array( + 'script' => '/freebusy/freebusy.php', + 'request' => array( + 'params' => array( + 'server' => array( + 'REQUEST_URI' => '/freebusy/test@example.com/Kalender.pxfb' + ) + ) + ), + 'logger' => array( + 'Horde_Log_Handler_Null' => array(), + ) + ); + + $application = Horde_Kolab_FreeBusy::singleton($params); + + $output = ''; + + if (empty($result)) { + try { + ob_start(); + $application->dispatch(); + $output = ob_get_contents(); + ob_end_clean(); + } catch (Horde_Controller_Exception $e) { + $this->assertEquals('No routes match the path: "' . trim($key, '/') . '"', $e->getMessage()); + } + $this->assertEquals('', $output); + } else { + ob_start(); + $application->dispatch(); + $output = ob_get_contents(); + ob_end_clean(); + $this->assertEquals($result, $output); + } + Horde_Kolab_FreeBusy::destroy(); + } +} \ No newline at end of file -- 2.11.0