Switch to Horde_Provider and start restructuring the driver part.
authorGunnar Wrobel <p@rdus.de>
Wed, 16 Sep 2009 13:12:18 +0000 (15:12 +0200)
committerGunnar Wrobel <p@rdus.de>
Wed, 16 Sep 2009 13:12:18 +0000 (15:12 +0200)
14 files changed:
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Access.php [deleted file]
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache.php
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Cache/Base.php [new file with mode: 0644]
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Base.php [new file with mode: 0644]
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Base.php [new file with mode: 0644]
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Kolab.php [new file with mode: 0644]
framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Factory.php [new file with mode: 0644]
framework/Kolab_FreeBusy/package.xml
framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/DispatchTest.php
framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/FreeBusyTest.php
framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/Mock/Controller/FreebusyController.php
framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/TriggerTest.php [new file with mode: 0644]

index 09a7f18..d616936 100644 (file)
@@ -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]
+     *
      * </pre>
      *
      * @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 (file)
index 0bb5087..0000000
+++ /dev/null
@@ -1,442 +0,0 @@
-<?php
-/**
- * The Horde_Kolab_FreeBusy_Access:: class provides functionality to check
- * free/busy access rights for the specified folder.
- *
- * $Horde: framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Access.php,v 1.23 2009/07/08 18:39:07 slusarz Exp $
- *
- * Copyright 2004-2008 Klarälvdalens Datakonsult AB
- *
- * 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.
- *
- * @author  Gunnar Wrobel <p@rdus.de>
- * @author  Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
- * @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:
-        //
-        //  <IfModule mod_rewrite.c>
-        //    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]
-        //  </IfModule>
-        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;
-    }
-
-}
-
index d52d38f..9091af9 100644 (file)
@@ -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 (file)
index 0000000..3fbdd81
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+/**
+ * The Kolab implementation of the free/busy system.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_FreeBusy
+ * @author   Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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
index 6902ea1..7c69253 100644 (file)
@@ -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 (file)
index 0000000..d9a5d82
--- /dev/null
@@ -0,0 +1,197 @@
+<?php
+/**
+ * The Kolab implementation of the free/busy system.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_FreeBusy
+ * @author   Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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:
+        //
+        //  <IfModule mod_rewrite.c>
+        //    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]
+        //  </IfModule>
+        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 (file)
index 0000000..e16c623
--- /dev/null
@@ -0,0 +1,305 @@
+<?php
+/**
+ * The Kolab implementation of the free/busy system.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_FreeBusy
+ * @author   Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 (file)
index 0000000..9a5328f
--- /dev/null
@@ -0,0 +1,251 @@
+<?php
+/**
+ * The Kolab implementation of the free/busy system.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_FreeBusy
+ * @author   Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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.
+     * <pre>
+     * 'extended' - Whether to fetch extended free/busy information or not.
+     * </pre>
+     *
+     * @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 (file)
index 0000000..1e6f2cd
--- /dev/null
@@ -0,0 +1,217 @@
+<?php
+/**
+ * The Kolab implementation of the free/busy system.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_FreeBusy
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 <wrobel@pardus.de>
+ * @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
index 212f160..19661b4 100644 (file)
@@ -67,6 +67,14 @@ http://pear.php.net/dtd/package-2.0.xsd">
        <dir name="Controller">
         <file name="FreebusyController.php" role="php" />
        </dir> <!-- /lib/Horde/Kolab/FreeBusy/Controller -->
+       <dir name="Driver">
+        <file name="Base.php" role="php" />
+        <dir name="Freebusy">
+         <file name="Base.php" role="php" />
+         <file name="Kolab.php" role="php" />
+        </dir> <!-- /lib/Horde/Kolab/FreeBusy/Driver/Freebusy -->
+       </dir> <!-- /lib/Horde/Kolab/FreeBusy/Driver -->
+       <file name="Factory.php" role="php" />
        <file name="Imap.php" role="php" />
        <file name="Report.php" role="php" />
        <file name="View.php" role="php" />
@@ -146,6 +154,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <install name="lib/Horde/Kolab/FreeBusy/Access.php" as="Horde/Kolab/FreeBusy/Access.php" />
    <install name="lib/Horde/Kolab/FreeBusy/Cache.php" as="Horde/Kolab/FreeBusy/Cache.php" />
    <install name="lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php" as="Horde/Kolab/FreeBusy/Controller/FreebusyController.php" />
+   <install name="lib/Horde/Kolab/FreeBusy/Driver/Base.php" as="Horde/Kolab/FreeBusy/Driver/Base.php" />
+   <install name="lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Base.php" as="Horde/Kolab/FreeBusy/Driver/Freebusy/Base.php" />
+   <install name="lib/Horde/Kolab/FreeBusy/Driver/Freebusy/Kolab.php" as="Horde/Kolab/FreeBusy/Driver/Freebusy/Kolab.php" />
+   <install name="lib/Horde/Kolab/FreeBusy/Factory.php" as="Horde/Kolab/FreeBusy/Factory.php" />
    <install name="lib/Horde/Kolab/FreeBusy/Imap.php" as="Horde/Kolab/FreeBusy/Imap.php" />
    <install name="lib/Horde/Kolab/FreeBusy/Report.php" as="Horde/Kolab/FreeBusy/Report.php" />
    <install name="lib/Horde/Kolab/FreeBusy/View.php" as="Horde/Kolab/FreeBusy/View.php" />
index 8cbf510..fe3f7b5 100644 (file)
@@ -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 = '';
index 0aa7124..9fef9ec 100644 (file)
@@ -35,6 +35,8 @@ class Horde_Kolab_FreeBusy_FreeBusyTest extends Horde_Kolab_Test_FreeBusy
 
     /**
      * Test setup.
+     *
+     * @return NULL
      */
     public function setUp()
     {
index bdd80fe..226b6da 100644 (file)
@@ -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 (file)
index 0000000..dece032
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Test triggering free/busy generation.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_FreeBusy
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 <wrobel@pardus.de>
+ * @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