<?php

/**
 * Atmail DAV server
 *
 * Sets up a WebDAV server with caldav and ACL support, and it creates
 * the root 'principals' and 'calendars' collections.
 *
 * @package Atmail 
 * @subpackage Dav
 * @author Evert Pot (http://www.rooftopsolutions.nl/) (modified by Atmail)
 */
 
class Atmail_Dav_Server 
{

    /**     
     * The authentication realm
     *
     * Note that if this changes, the hashes in the auth backend must also 
     * be recalculated. 
     * 
     * @var string
     */
    public $authRealm = 'SabreDAV';

    /**
     * Server object  
     * 
     * @var Sabre_DAV_Server 
     */
    protected $server = null;

    /**
     * Database connection 
     * 
     * @var PDO 
     */
    protected $pdo = null;


    /**
     * Turns on all debugging features.
     *
     * This will allow you to list every node, and this will also provide 
     * stacktraces when exceptions occur. 
     * 
     * @var bool 
     */
    public $debug = false;


    /**
     * Sets up the object. A PDO object must be passed to setup all the backends.
     * 
     * @param PDO $pdo 
     */
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    
        /* Backends */
        $authBackend = new Atmail_Dav_Backend_Auth($pdo);
        $calendarBackend = new Atmail_CalDav_Backend_PDO($pdo);
        $addressBookBackend = new Atmail_Dav_Backend_Abook();
        $loggingBackend = new Atmail_Dav_Logging_Backend_PDO($pdo, 'Log_Login', '6');
       
        /* We need the auth plugin a bit earlier, as we need to pass it to the 
         * Directory node
         */ 
        $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend, $this->authRealm);
		$principalBackend = new Atmail_Dav_Backend_Principals($pdo, 'principals', 'groupMembers', $authPlugin);

        /* Directory structure */
        $tree = array();

        /* /principals & /principals/users */
        $node = new Sabre_DAVACL_PrincipalCollection($principalBackend, 'principals/users');
        $node->disableListing = !$this->debug;
        $tree[] = new Sabre_DAV_SimpleDirectory('principals', array($node));

        /* /calendars */
        $node = new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend, 'principals/users');
        $node->disableListing = !$this->debug;
        $tree[] = $node;

        /* /addressbooks */
        $node = new Sabre_CardDAV_AddressBookRoot($principalBackend, $addressBookBackend, 'principals/users');
        $node->disableListing = !$this->debug;
        $tree[] = $node;

        /* /directory */
        $tree[] = new Atmail_Dav_Directory($authPlugin);

		/* /files */
		$node = new Atmail_Dav_FS_Root($authPlugin, "/usr/local/atmail/users/", null); 
		$tree[] = $node;
		
        /* /calendar-delegates */
        $node = new Atmail_Dav_Delegates_Root($pdo, $principalBackend, $calendarBackend);
        $node->disableListing = !$this->debug;
        $tree[] = $node;

        $this->server = new Sabre_DAV_Server($tree);
        $this->server->debugExceptions = $this->debug;

        /* Server Plugins */
        $loggingPlugin = new Atmail_Dav_Logging_Plugin($loggingBackend);
        $this->server->addPlugin($loggingPlugin);

        $this->server->addPlugin($authPlugin);

        $aclPlugin = new Sabre_DAVACL_Plugin();
        $aclPlugin->defaultUsernamePath = 'principals/users';
        $aclPlugin->hideNodesFromListings = !$this->debug; 
        $this->server->addPlugin($aclPlugin);

        $caldavPlugin = new Sabre_CalDAV_Plugin();
        $this->server->addPlugin($caldavPlugin);

        $carddavPlugin = new Sabre_CardDAV_Plugin();
        $this->server->addPlugin($carddavPlugin);

        $carddavPlugin->directories = array(
            'directory',
        );

        if($this->debug)
        {
	        // Support for html frontend
			$browser = new Sabre_DAV_Browser_Plugin();
			$this->server->addPlugin($browser);
        }

        // Intercepting properties
        $this->server->subscribeEvent('beforeGetProperties', array($this,'beforeGetProperties'), 90);

        // A priority of '30' ensures this event is triggered after auth and 
        // acl, but before anything else.
        $this->server->subscribeEvent('beforeMethod', array($this,'beforeMethod'),30);

    }

    public function setBaseUri($baseUri) {

        $this->server->setBaseUri($baseUri);

    }

    /**
     * Starts the server 
     * 
     * @return void 
     */
    public function exec() {

        $this->server->exec();

    }

    /**
     * Returns an instance of the DAV server
     *
     * @return Sabre_DAV_Server 
     */
    public function getServer() {

        return $this->server;

    } 

    /**
     * beforeGetProperties
     *
     * This event handler is invoked before any after properties for a resource
     * are fetched.
     *
     * We need to do this to intercept and overwrite the calendar-home-root for 
     * the calendar-delegation system.
     *
     * @param string $path
     * @param Sabre_DAV_INode $node
     * @param array $requestedProperties
     * @param array $returnedProperties
     * @return void
     */
    public function beforeGetProperties($path, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {

        if ($node instanceof Atmail_Dav_Delegates_Principal) {

            // calendar-home-set property
            $calHome = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-home-set';
            if (($index = array_search($calHome,$requestedProperties)) !== false) {

                // The new calendar-home-root is simply the parent of the 
                // actual principal, so this is simple enough.
                $returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href(dirname($path) . '/');
                unset($requestedProperties[$index]);
            }

        } // instanceof IPrincipal

    }

    /**
     * This event is triggered before any WebDAV things are handled.
     *
     * This allows us to intercept requests to certain urls. We're specifically 
     * using this to only give people access to CalDAV and CardDAV services if 
     * it's turned on in the settings
     * 
     * @param string $method 
     * @param string $path 
     * @return void
     */
    public function beforeMethod($method, $path) {

        // Permission map
        $perms = array(
            'addressbooks'        => 'CardDAVServer',
            'calendars'           => 'CalDAVServer',
            'calendar-delegation' => 'CalDAVServer',
            'directory'           => 'CardDAVServer',
        );

        list($path) = explode('/', strtolower($path), 2);

        // Unrestricted path
        if (!isset($perms[$path])) {
            return;
        }

        $exception = new Sabre_DAV_Exception_Forbidden('Access to this path has been disabled through Atmail\'s admin interface');

        $userName = $this->server->getPlugin('auth')->getCurrentUser();
        if (!$userName)
            throw $exception;
            
         
        $stmt = $this->pdo->prepare("SELECT * FROM Groups WHERE GroupName = (SELECT Ugroup FROM Users WHERE Account = ? LIMIT 1) LIMIT 1");
        $stmt->execute(array($userName));
        $result = $stmt->fetchAll();
        
        $allowed = $result[0][$perms[$path]];
        if ($allowed)
            return;
        
        throw $exception; 

    }

}
