Integration of SabreDAV
This commit is contained in:
parent
6230001a3c
commit
bf1ca75710
File diff suppressed because it is too large
Load Diff
|
@ -1,251 +0,0 @@
|
|||
<?php // $Id$
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2002-2007 Christian Stocker, Hartmut Holzgraefe |
|
||||
| All rights reserved |
|
||||
| |
|
||||
| Redistribution and use in source and binary forms, with or without |
|
||||
| modification, are permitted provided that the following conditions |
|
||||
| are met: |
|
||||
| |
|
||||
| 1. Redistributions of source code must retain the above copyright |
|
||||
| notice, this list of conditions and the following disclaimer. |
|
||||
| 2. Redistributions in binary form must reproduce the above copyright |
|
||||
| notice, this list of conditions and the following disclaimer in |
|
||||
| the documentation and/or other materials provided with the |
|
||||
| distribution. |
|
||||
| 3. The names of the authors may not be used to endorse or promote |
|
||||
| products derived from this software without specific prior |
|
||||
| written permission. |
|
||||
| |
|
||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
||||
| FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
||||
| COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
||||
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
||||
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
||||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
||||
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
||||
| ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
||||
| POSSIBILITY OF SUCH DAMAGE. |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* helper class for parsing LOCK request bodies
|
||||
*
|
||||
* @package HTTP_WebDAV_Server
|
||||
* @author Hartmut Holzgraefe <hholzgra@php.net>
|
||||
* @version @package-version@
|
||||
*/
|
||||
class _parse_lockinfo
|
||||
{
|
||||
/**
|
||||
* success state flag
|
||||
*
|
||||
* @var bool
|
||||
* @access public
|
||||
*/
|
||||
var $success = false;
|
||||
|
||||
/**
|
||||
* lock type, currently only "write"
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
var $locktype = "";
|
||||
|
||||
/**
|
||||
* lock scope, "shared" or "exclusive"
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
var $lockscope = "";
|
||||
|
||||
/**
|
||||
* lock owner information
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
var $owner = "";
|
||||
|
||||
/**
|
||||
* flag that is set during lock owner read
|
||||
*
|
||||
* @var bool
|
||||
* @access private
|
||||
*/
|
||||
var $collect_owner = false;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
* @param string path of stream to read
|
||||
* @access public
|
||||
*/
|
||||
function _parse_lockinfo($path)
|
||||
{
|
||||
// we assume success unless problems occur
|
||||
$this->success = true;
|
||||
|
||||
// remember if any input was parsed
|
||||
$had_input = false;
|
||||
|
||||
// open stream
|
||||
$f_in = fopen($path, "r");
|
||||
if (!$f_in) {
|
||||
$this->success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// create namespace aware parser
|
||||
$xml_parser = xml_parser_create_ns("UTF-8", " ");
|
||||
|
||||
// set tag and data handlers
|
||||
xml_set_element_handler($xml_parser,
|
||||
array(&$this, "_startElement"),
|
||||
array(&$this, "_endElement"));
|
||||
xml_set_character_data_handler($xml_parser,
|
||||
array(&$this, "_data"));
|
||||
|
||||
// we want a case sensitive parser
|
||||
xml_parser_set_option($xml_parser,
|
||||
XML_OPTION_CASE_FOLDING, false);
|
||||
|
||||
// parse input
|
||||
while ($this->success && !feof($f_in)) {
|
||||
$line = fgets($f_in);
|
||||
if (is_string($line)) {
|
||||
$had_input = true;
|
||||
$this->success &= xml_parse($xml_parser, $line, false);
|
||||
}
|
||||
}
|
||||
|
||||
// finish parsing
|
||||
if ($had_input) {
|
||||
$this->success &= xml_parse($xml_parser, "", true);
|
||||
}
|
||||
|
||||
// check if required tags where found
|
||||
$this->success &= !empty($this->locktype);
|
||||
$this->success &= !empty($this->lockscope);
|
||||
|
||||
// free parser resource
|
||||
xml_parser_free($xml_parser);
|
||||
|
||||
// close input stream
|
||||
fclose($f_in);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tag start handler
|
||||
*
|
||||
* @param resource parser
|
||||
* @param string tag name
|
||||
* @param array tag attributes
|
||||
* @return void
|
||||
* @access private
|
||||
*/
|
||||
function _startElement($parser, $name, $attrs)
|
||||
{
|
||||
// namespace handling
|
||||
if (strstr($name, " ")) {
|
||||
list($ns, $tag) = explode(" ", $name);
|
||||
} else {
|
||||
$ns = "";
|
||||
$tag = $name;
|
||||
}
|
||||
|
||||
|
||||
if ($this->collect_owner) {
|
||||
// everything within the <owner> tag needs to be collected
|
||||
$ns_short = "";
|
||||
$ns_attr = "";
|
||||
if ($ns) {
|
||||
if ($ns == "DAV:") {
|
||||
$ns_short = "D:";
|
||||
} else {
|
||||
$ns_attr = " xmlns='$ns'";
|
||||
}
|
||||
}
|
||||
$this->owner .= "<$ns_short$tag$ns_attr>";
|
||||
} else if ($ns == "DAV:") {
|
||||
// parse only the essential tags
|
||||
switch ($tag) {
|
||||
case "write":
|
||||
$this->locktype = $tag;
|
||||
break;
|
||||
case "exclusive":
|
||||
case "shared":
|
||||
$this->lockscope = $tag;
|
||||
break;
|
||||
case "owner":
|
||||
$this->collect_owner = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* data handler
|
||||
*
|
||||
* @param resource parser
|
||||
* @param string data
|
||||
* @return void
|
||||
* @access private
|
||||
*/
|
||||
function _data($parser, $data)
|
||||
{
|
||||
// only the <owner> tag has data content
|
||||
if ($this->collect_owner) {
|
||||
$this->owner .= $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tag end handler
|
||||
*
|
||||
* @param resource parser
|
||||
* @param string tag name
|
||||
* @return void
|
||||
* @access private
|
||||
*/
|
||||
function _endElement($parser, $name)
|
||||
{
|
||||
// namespace handling
|
||||
if (strstr($name, " ")) {
|
||||
list($ns, $tag) = explode(" ", $name);
|
||||
} else {
|
||||
$ns = "";
|
||||
$tag = $name;
|
||||
}
|
||||
|
||||
// <owner> finished?
|
||||
if (($ns == "DAV:") && ($tag == "owner")) {
|
||||
$this->collect_owner = false;
|
||||
}
|
||||
|
||||
// within <owner> we have to collect everything
|
||||
if ($this->collect_owner) {
|
||||
$ns_short = "";
|
||||
$ns_attr = "";
|
||||
if ($ns) {
|
||||
if ($ns == "DAV:") {
|
||||
$ns_short = "D:";
|
||||
} else {
|
||||
$ns_attr = " xmlns='$ns'";
|
||||
}
|
||||
}
|
||||
$this->owner .= "</$ns_short$tag$ns_attr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,191 +0,0 @@
|
|||
<?php // $Id$
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2002-2007 Christian Stocker, Hartmut Holzgraefe |
|
||||
| All rights reserved |
|
||||
| |
|
||||
| Redistribution and use in source and binary forms, with or without |
|
||||
| modification, are permitted provided that the following conditions |
|
||||
| are met: |
|
||||
| |
|
||||
| 1. Redistributions of source code must retain the above copyright |
|
||||
| notice, this list of conditions and the following disclaimer. |
|
||||
| 2. Redistributions in binary form must reproduce the above copyright |
|
||||
| notice, this list of conditions and the following disclaimer in |
|
||||
| the documentation and/or other materials provided with the |
|
||||
| distribution. |
|
||||
| 3. The names of the authors may not be used to endorse or promote |
|
||||
| products derived from this software without specific prior |
|
||||
| written permission. |
|
||||
| |
|
||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
||||
| FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
||||
| COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
||||
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
||||
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
||||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
||||
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
||||
| ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
||||
| POSSIBILITY OF SUCH DAMAGE. |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* helper class for parsing PROPFIND request bodies
|
||||
*
|
||||
* @package HTTP_WebDAV_Server
|
||||
* @author Hartmut Holzgraefe <hholzgra@php.net>
|
||||
* @version @package-version@
|
||||
*/
|
||||
class _parse_propfind
|
||||
{
|
||||
/**
|
||||
* success state flag
|
||||
*
|
||||
* @var bool
|
||||
* @access public
|
||||
*/
|
||||
var $success = false;
|
||||
|
||||
/**
|
||||
* found properties are collected here
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
var $props = false;
|
||||
|
||||
/**
|
||||
* internal tag nesting depth counter
|
||||
*
|
||||
* @var int
|
||||
* @access private
|
||||
*/
|
||||
var $depth = 0;
|
||||
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function _parse_propfind($path)
|
||||
{
|
||||
// success state flag
|
||||
$this->success = true;
|
||||
|
||||
// property storage array
|
||||
$this->props = array();
|
||||
|
||||
// internal tag depth counter
|
||||
$this->depth = 0;
|
||||
|
||||
// remember if any input was parsed
|
||||
$had_input = false;
|
||||
|
||||
// open input stream
|
||||
$f_in = fopen($path, "r");
|
||||
if (!$f_in) {
|
||||
$this->success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// create XML parser
|
||||
$xml_parser = xml_parser_create_ns("UTF-8", " ");
|
||||
|
||||
// set tag and data handlers
|
||||
xml_set_element_handler($xml_parser,
|
||||
array(&$this, "_startElement"),
|
||||
array(&$this, "_endElement"));
|
||||
|
||||
// we want a case sensitive parser
|
||||
xml_parser_set_option($xml_parser,
|
||||
XML_OPTION_CASE_FOLDING, false);
|
||||
|
||||
|
||||
// parse input
|
||||
while ($this->success && !feof($f_in)) {
|
||||
$line = fgets($f_in);
|
||||
if (is_string($line)) {
|
||||
$had_input = true;
|
||||
$this->success &= xml_parse($xml_parser, $line, false);
|
||||
}
|
||||
}
|
||||
|
||||
// finish parsing
|
||||
if ($had_input) {
|
||||
$this->success &= xml_parse($xml_parser, "", true);
|
||||
}
|
||||
|
||||
// free parser
|
||||
xml_parser_free($xml_parser);
|
||||
|
||||
// close input stream
|
||||
fclose($f_in);
|
||||
|
||||
// if no input was parsed it was a request
|
||||
if(!count($this->props)) $this->props = "all"; // default
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* start tag handler
|
||||
*
|
||||
* @access private
|
||||
* @param resource parser
|
||||
* @param string tag name
|
||||
* @param array tag attributes
|
||||
*/
|
||||
function _startElement($parser, $name, $attrs)
|
||||
{
|
||||
// name space handling
|
||||
if (strstr($name, " ")) {
|
||||
list($ns, $tag) = explode(" ", $name);
|
||||
if ($ns == "")
|
||||
$this->success = false;
|
||||
} else {
|
||||
$ns = "";
|
||||
$tag = $name;
|
||||
}
|
||||
|
||||
// special tags at level 1: <allprop> and <propname>
|
||||
if ($this->depth == 1) {
|
||||
if ($tag == "allprop")
|
||||
$this->props = "all";
|
||||
|
||||
if ($tag == "propname")
|
||||
$this->props = "names";
|
||||
}
|
||||
|
||||
// requested properties are found at level 2
|
||||
if ($this->depth == 2) {
|
||||
$prop = array("name" => $tag);
|
||||
if ($ns)
|
||||
$prop["xmlns"] = $ns;
|
||||
$this->props[] = $prop;
|
||||
}
|
||||
|
||||
// increment depth count
|
||||
$this->depth++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* end tag handler
|
||||
*
|
||||
* @access private
|
||||
* @param resource parser
|
||||
* @param string tag name
|
||||
*/
|
||||
function _endElement($parser, $name)
|
||||
{
|
||||
// here we only need to decrement the depth count
|
||||
$this->depth--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -1,237 +0,0 @@
|
|||
<?php // $Id$
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2002-2007 Christian Stocker, Hartmut Holzgraefe |
|
||||
| All rights reserved |
|
||||
| |
|
||||
| Redistribution and use in source and binary forms, with or without |
|
||||
| modification, are permitted provided that the following conditions |
|
||||
| are met: |
|
||||
| |
|
||||
| 1. Redistributions of source code must retain the above copyright |
|
||||
| notice, this list of conditions and the following disclaimer. |
|
||||
| 2. Redistributions in binary form must reproduce the above copyright |
|
||||
| notice, this list of conditions and the following disclaimer in |
|
||||
| the documentation and/or other materials provided with the |
|
||||
| distribution. |
|
||||
| 3. The names of the authors may not be used to endorse or promote |
|
||||
| products derived from this software without specific prior |
|
||||
| written permission. |
|
||||
| |
|
||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
||||
| FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
||||
| COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
||||
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
||||
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
||||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
||||
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
||||
| ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
||||
| POSSIBILITY OF SUCH DAMAGE. |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* helper class for parsing PROPPATCH request bodies
|
||||
*
|
||||
* @package HTTP_WebDAV_Server
|
||||
* @author Hartmut Holzgraefe <hholzgra@php.net>
|
||||
* @version @package-version@
|
||||
*/
|
||||
class _parse_proppatch
|
||||
{
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @var
|
||||
* @access
|
||||
*/
|
||||
var $success;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @var
|
||||
* @access
|
||||
*/
|
||||
var $props;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @var
|
||||
* @access
|
||||
*/
|
||||
var $depth;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @var
|
||||
* @access
|
||||
*/
|
||||
var $mode;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @var
|
||||
* @access
|
||||
*/
|
||||
var $current;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
* @param string path of input stream
|
||||
* @access public
|
||||
*/
|
||||
function _parse_proppatch($path)
|
||||
{
|
||||
$this->success = true;
|
||||
|
||||
$this->depth = 0;
|
||||
$this->props = array();
|
||||
$had_input = false;
|
||||
|
||||
$f_in = fopen($path, "r");
|
||||
if (!$f_in) {
|
||||
$this->success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$xml_parser = xml_parser_create_ns("UTF-8", " ");
|
||||
|
||||
xml_set_element_handler($xml_parser,
|
||||
array(&$this, "_startElement"),
|
||||
array(&$this, "_endElement"));
|
||||
|
||||
xml_set_character_data_handler($xml_parser,
|
||||
array(&$this, "_data"));
|
||||
|
||||
xml_parser_set_option($xml_parser,
|
||||
XML_OPTION_CASE_FOLDING, false);
|
||||
|
||||
while($this->success && !feof($f_in)) {
|
||||
$line = fgets($f_in);
|
||||
if (is_string($line)) {
|
||||
$had_input = true;
|
||||
$this->success &= xml_parse($xml_parser, $line, false);
|
||||
}
|
||||
}
|
||||
|
||||
if($had_input) {
|
||||
$this->success &= xml_parse($xml_parser, "", true);
|
||||
}
|
||||
|
||||
xml_parser_free($xml_parser);
|
||||
|
||||
fclose($f_in);
|
||||
}
|
||||
|
||||
/**
|
||||
* tag start handler
|
||||
*
|
||||
* @param resource parser
|
||||
* @param string tag name
|
||||
* @param array tag attributes
|
||||
* @return void
|
||||
* @access private
|
||||
*/
|
||||
function _startElement($parser, $name, $attrs)
|
||||
{
|
||||
if (strstr($name, " ")) {
|
||||
list($ns, $tag) = explode(" ", $name);
|
||||
if ($ns == "")
|
||||
$this->success = false;
|
||||
} else {
|
||||
$ns = "";
|
||||
$tag = $name;
|
||||
}
|
||||
|
||||
if ($this->depth == 1) {
|
||||
$this->mode = $tag;
|
||||
}
|
||||
|
||||
if ($this->depth == 3) {
|
||||
$prop = array("name" => $tag);
|
||||
$this->current = array("name" => $tag, "ns" => $ns, "status"=> 200);
|
||||
if ($this->mode == "set") {
|
||||
$this->current["val"] = ""; // default set val
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->depth >= 4) {
|
||||
$this->current["val"] .= "<$tag";
|
||||
if (isset($attr)) {
|
||||
foreach ($attr as $key => $val) {
|
||||
$this->current["val"] .= ' '.$key.'="'.str_replace('"','"', $val).'"';
|
||||
}
|
||||
}
|
||||
$this->current["val"] .= ">";
|
||||
}
|
||||
|
||||
|
||||
|
||||
$this->depth++;
|
||||
}
|
||||
|
||||
/**
|
||||
* tag end handler
|
||||
*
|
||||
* @param resource parser
|
||||
* @param string tag name
|
||||
* @return void
|
||||
* @access private
|
||||
*/
|
||||
function _endElement($parser, $name)
|
||||
{
|
||||
if (strstr($name, " ")) {
|
||||
list($ns, $tag) = explode(" ", $name);
|
||||
if ($ns == "")
|
||||
$this->success = false;
|
||||
} else {
|
||||
$ns = "";
|
||||
$tag = $name;
|
||||
}
|
||||
|
||||
$this->depth--;
|
||||
|
||||
if ($this->depth >= 4) {
|
||||
$this->current["val"] .= "</$tag>";
|
||||
}
|
||||
|
||||
if ($this->depth == 3) {
|
||||
if (isset($this->current)) {
|
||||
$this->props[] = $this->current;
|
||||
unset($this->current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* input data handler
|
||||
*
|
||||
* @param resource parser
|
||||
* @param string data
|
||||
* @return void
|
||||
* @access private
|
||||
*/
|
||||
function _data($parser, $data)
|
||||
{
|
||||
if (isset($this->current)) {
|
||||
$this->current["val"] .= $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode:nil
|
||||
* End:
|
||||
*/
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SabreDAV's PHP autoloader
|
||||
*
|
||||
* Warning: this file is deprecated. Please use Sabre/autoload.php instead.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @deprecated
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
include dirname(__FILE__) . '/Sabre/autoload.php';
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Library include file
|
||||
*
|
||||
* This file contains all includes to the rest of the SabreDAV library
|
||||
* Make sure the lib/ directory is in PHP's include_path
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
/* Utilities */
|
||||
include 'Sabre/HTTP/Util.php';
|
||||
include 'Sabre/HTTP/Response.php';
|
||||
include 'Sabre/HTTP/Request.php';
|
||||
include 'Sabre/HTTP/AbstractAuth.php';
|
||||
include 'Sabre/HTTP/BasicAuth.php';
|
||||
include 'Sabre/HTTP/DigestAuth.php';
|
||||
include 'Sabre/HTTP/AWSAuth.php';
|
||||
|
||||
/* Version */
|
||||
include 'Sabre/DAV/Version.php';
|
||||
include 'Sabre/HTTP/Version.php';
|
||||
|
||||
/* Exceptions */
|
||||
include 'Sabre/DAV/Exception.php';
|
||||
include 'Sabre/DAV/Exception/BadRequest.php';
|
||||
include 'Sabre/DAV/Exception/Conflict.php';
|
||||
include 'Sabre/DAV/Exception/FileNotFound.php';
|
||||
include 'Sabre/DAV/Exception/InsufficientStorage.php';
|
||||
include 'Sabre/DAV/Exception/Locked.php';
|
||||
include 'Sabre/DAV/Exception/LockTokenMatchesRequestUri.php';
|
||||
include 'Sabre/DAV/Exception/MethodNotAllowed.php';
|
||||
include 'Sabre/DAV/Exception/NotImplemented.php';
|
||||
include 'Sabre/DAV/Exception/Forbidden.php';
|
||||
include 'Sabre/DAV/Exception/PreconditionFailed.php';
|
||||
include 'Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php';
|
||||
include 'Sabre/DAV/Exception/UnsupportedMediaType.php';
|
||||
include 'Sabre/DAV/Exception/NotAuthenticated.php';
|
||||
|
||||
include 'Sabre/DAV/Exception/ConflictingLock.php';
|
||||
include 'Sabre/DAV/Exception/ReportNotImplemented.php';
|
||||
include 'Sabre/DAV/Exception/InvalidResourceType.php';
|
||||
|
||||
/* Properties */
|
||||
include 'Sabre/DAV/Property.php';
|
||||
include 'Sabre/DAV/Property/GetLastModified.php';
|
||||
include 'Sabre/DAV/Property/ResourceType.php';
|
||||
include 'Sabre/DAV/Property/SupportedLock.php';
|
||||
include 'Sabre/DAV/Property/LockDiscovery.php';
|
||||
include 'Sabre/DAV/Property/IHref.php';
|
||||
include 'Sabre/DAV/Property/Href.php';
|
||||
include 'Sabre/DAV/Property/HrefList.php';
|
||||
include 'Sabre/DAV/Property/SupportedReportSet.php';
|
||||
include 'Sabre/DAV/Property/Response.php';
|
||||
include 'Sabre/DAV/Property/ResponseList.php';
|
||||
|
||||
/* Node interfaces */
|
||||
include 'Sabre/DAV/INode.php';
|
||||
include 'Sabre/DAV/IFile.php';
|
||||
include 'Sabre/DAV/ICollection.php';
|
||||
include 'Sabre/DAV/IProperties.php';
|
||||
include 'Sabre/DAV/ILockable.php';
|
||||
include 'Sabre/DAV/IQuota.php';
|
||||
include 'Sabre/DAV/IExtendedCollection.php';
|
||||
|
||||
/* Node abstract implementations */
|
||||
include 'Sabre/DAV/Node.php';
|
||||
include 'Sabre/DAV/File.php';
|
||||
include 'Sabre/DAV/Directory.php';
|
||||
|
||||
/* Utilities */
|
||||
include 'Sabre/DAV/SimpleDirectory.php';
|
||||
include 'Sabre/DAV/XMLUtil.php';
|
||||
include 'Sabre/DAV/URLUtil.php';
|
||||
|
||||
/* Filesystem implementation */
|
||||
include 'Sabre/DAV/FS/Node.php';
|
||||
include 'Sabre/DAV/FS/File.php';
|
||||
include 'Sabre/DAV/FS/Directory.php';
|
||||
|
||||
/* Advanced filesystem implementation */
|
||||
include 'Sabre/DAV/FSExt/Node.php';
|
||||
include 'Sabre/DAV/FSExt/File.php';
|
||||
include 'Sabre/DAV/FSExt/Directory.php';
|
||||
|
||||
/* Trees */
|
||||
include 'Sabre/DAV/Tree.php';
|
||||
include 'Sabre/DAV/ObjectTree.php';
|
||||
include 'Sabre/DAV/Tree/Filesystem.php';
|
||||
|
||||
/* Server */
|
||||
include 'Sabre/DAV/Server.php';
|
||||
include 'Sabre/DAV/ServerPlugin.php';
|
||||
|
||||
/* Browser */
|
||||
include 'Sabre/DAV/Browser/Plugin.php';
|
||||
include 'Sabre/DAV/Browser/MapGetToPropFind.php';
|
||||
include 'Sabre/DAV/Browser/GuessContentType.php';
|
||||
|
||||
/* Locks */
|
||||
include 'Sabre/DAV/Locks/LockInfo.php';
|
||||
include 'Sabre/DAV/Locks/Plugin.php';
|
||||
include 'Sabre/DAV/Locks/Backend/Abstract.php';
|
||||
include 'Sabre/DAV/Locks/Backend/FS.php';
|
||||
include 'Sabre/DAV/Locks/Backend/PDO.php';
|
||||
|
||||
/* Temporary File Filter plugin */
|
||||
include 'Sabre/DAV/TemporaryFileFilterPlugin.php';
|
||||
|
||||
/* Authentication plugin */
|
||||
include 'Sabre/DAV/Auth/Plugin.php';
|
||||
include 'Sabre/DAV/Auth/IBackend.php';
|
||||
include 'Sabre/DAV/Auth/Backend/AbstractDigest.php';
|
||||
include 'Sabre/DAV/Auth/Backend/AbstractBasic.php';
|
||||
include 'Sabre/DAV/Auth/Backend/File.php';
|
||||
include 'Sabre/DAV/Auth/Backend/PDO.php';
|
||||
|
||||
/* DavMount plugin */
|
||||
include 'Sabre/DAV/Mount/Plugin.php';
|
||||
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Abstract Calendaring backend. Extend this class to create your own backends.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_CalDAV_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* Returns a list of calendars for a principal.
|
||||
*
|
||||
* Every project is an array with the following keys:
|
||||
* * id, a unique id that will be used by other functions to modify the
|
||||
* calendar. This can be the same as the uri or a database key.
|
||||
* * uri, which the basename of the uri with which the calendar is
|
||||
* accessed.
|
||||
* * principalUri. The owner of the calendar. Almost always the same as
|
||||
* principalUri passed to this method.
|
||||
*
|
||||
* Furthermore it can contain webdav properties in clark notation. A very
|
||||
* common one is '{DAV:}displayname'.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
abstract function getCalendarsForUser($principalUri);
|
||||
|
||||
/**
|
||||
* Creates a new calendar for a principal.
|
||||
*
|
||||
* If the creation was a success, an id must be returned that can be used to reference
|
||||
* this calendar in other methods, such as updateCalendar.
|
||||
*
|
||||
* This function must return a server-wide unique id that can be used
|
||||
* later to reference the calendar.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $calendarUri
|
||||
* @param array $properties
|
||||
* @return string|int
|
||||
*/
|
||||
abstract function createCalendar($principalUri,$calendarUri,array $properties);
|
||||
|
||||
/**
|
||||
* Updates properties on this node,
|
||||
*
|
||||
* The properties array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existant property is always succesful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param array $properties
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateCalendar($calendarId, array $properties) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar and all it's objects
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return void
|
||||
*/
|
||||
abstract function deleteCalendar($calendarId);
|
||||
|
||||
/**
|
||||
* Returns all calendar objects within a calendar object.
|
||||
*
|
||||
* Every item contains an array with the following keys:
|
||||
* * id - unique identifier which will be used for subsequent updates
|
||||
* * calendardata - The iCalendar-compatible calnedar data
|
||||
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
|
||||
* * lastmodified - a timestamp of the last modification time
|
||||
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||
* ' "abcdef"')
|
||||
* * calendarid - The calendarid as it was passed to this function.
|
||||
*
|
||||
* Note that the etag is optional, but it's highly encouraged to return for
|
||||
* speed reasons.
|
||||
*
|
||||
* The calendardata is also optional. If it's not returned
|
||||
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||
* calendardata.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return array
|
||||
*/
|
||||
abstract function getCalendarObjects($calendarId);
|
||||
|
||||
/**
|
||||
* Returns information from a single calendar object, based on it's object
|
||||
* uri.
|
||||
*
|
||||
* The returned array must have the same keys as getCalendarObjects. The
|
||||
* 'calendardata' object is required here though, while it's not required
|
||||
* for getCalendarObjects.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return array
|
||||
*/
|
||||
abstract function getCalendarObject($calendarId,$objectUri);
|
||||
|
||||
/**
|
||||
* Creates a new calendar object.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return void
|
||||
*/
|
||||
abstract function createCalendarObject($calendarId,$objectUri,$calendarData);
|
||||
|
||||
/**
|
||||
* Updates an existing calendarobject, based on it's uri.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return void
|
||||
*/
|
||||
abstract function updateCalendarObject($calendarId,$objectUri,$calendarData);
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
abstract function deleteCalendarObject($calendarId,$objectUri);
|
||||
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PDO CalDAV backend
|
||||
*
|
||||
* This backend is used to store calendar-data in a PDO database, such as
|
||||
* sqlite or MySQL
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* pdo
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* The table name that will be used for calendars
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $calendarTableName;
|
||||
|
||||
/**
|
||||
* The table name that will be used for calendar objects
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $calendarObjectTableName;
|
||||
|
||||
/**
|
||||
* List of CalDAV properties, and how they map to database fieldnames
|
||||
*
|
||||
* Add your own properties by simply adding on to this array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $propertyMap = array(
|
||||
'{DAV:}displayname' => 'displayname',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
|
||||
'{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
|
||||
'{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates the backend
|
||||
*
|
||||
* @param PDO $pdo
|
||||
*/
|
||||
public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') {
|
||||
|
||||
$this->pdo = $pdo;
|
||||
$this->calendarTableName = $calendarTableName;
|
||||
$this->calendarObjectTableName = $calendarObjectTableName;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of calendars for a principal.
|
||||
*
|
||||
* Every project is an array with the following keys:
|
||||
* * id, a unique id that will be used by other functions to modify the
|
||||
* calendar. This can be the same as the uri or a database key.
|
||||
* * uri, which the basename of the uri with which the calendar is
|
||||
* accessed.
|
||||
* * principalUri. The owner of the calendar. Almost always the same as
|
||||
* principalUri passed to this method.
|
||||
*
|
||||
* Furthermore it can contain webdav properties in clark notation. A very
|
||||
* common one is '{DAV:}displayname'.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarsForUser($principalUri) {
|
||||
|
||||
$fields = array_values($this->propertyMap);
|
||||
$fields[] = 'id';
|
||||
$fields[] = 'uri';
|
||||
$fields[] = 'ctag';
|
||||
$fields[] = 'components';
|
||||
$fields[] = 'principaluri';
|
||||
|
||||
// Making fields a comma-delimited list
|
||||
$fields = implode(', ', $fields);
|
||||
$stmt = $this->pdo->prepare("SELECT " . $fields . " FROM `".$this->calendarTableName."` WHERE principaluri = ?");
|
||||
$stmt->execute(array($principalUri));
|
||||
|
||||
$calendars = array();
|
||||
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
|
||||
$components = explode(',',$row['components']);
|
||||
|
||||
$calendar = array(
|
||||
'id' => $row['id'],
|
||||
'uri' => $row['uri'],
|
||||
'principaluri' => $row['principaluri'],
|
||||
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
|
||||
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
|
||||
);
|
||||
|
||||
|
||||
foreach($this->propertyMap as $xmlName=>$dbName) {
|
||||
$calendar[$xmlName] = $row[$dbName];
|
||||
}
|
||||
|
||||
$calendars[] = $calendar;
|
||||
|
||||
}
|
||||
|
||||
return $calendars;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar for a principal.
|
||||
*
|
||||
* If the creation was a success, an id must be returned that can be used to reference
|
||||
* this calendar in other methods, such as updateCalendar
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $calendarUri
|
||||
* @param array $properties
|
||||
* @return mixed
|
||||
*/
|
||||
public function createCalendar($principalUri,$calendarUri, array $properties) {
|
||||
|
||||
$fieldNames = array(
|
||||
'principaluri',
|
||||
'uri',
|
||||
'ctag',
|
||||
);
|
||||
$values = array(
|
||||
':principaluri' => $principalUri,
|
||||
':uri' => $calendarUri,
|
||||
':ctag' => 1,
|
||||
);
|
||||
|
||||
// Default value
|
||||
$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
|
||||
$fieldNames[] = 'components';
|
||||
if (!isset($properties[$sccs])) {
|
||||
$values[':components'] = 'VEVENT,VTODO';
|
||||
} else {
|
||||
if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
|
||||
throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
|
||||
}
|
||||
$values[':components'] = implode(',',$properties[$sccs]->getValue());
|
||||
}
|
||||
|
||||
foreach($this->propertyMap as $xmlName=>$dbName) {
|
||||
if (isset($properties[$xmlName])) {
|
||||
|
||||
$myValue = $properties[$xmlName];
|
||||
$values[':' . $dbName] = $properties[$xmlName];
|
||||
$fieldNames[] = $dbName;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare("INSERT INTO `".$this->calendarTableName."` (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
|
||||
$stmt->execute($values);
|
||||
|
||||
return $this->pdo->lastInsertId();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a calendars properties
|
||||
*
|
||||
* The properties array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existant property is always succesful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param array $properties
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateCalendar($calendarId, array $properties) {
|
||||
|
||||
$newValues = array();
|
||||
$result = array(
|
||||
200 => array(), // Ok
|
||||
403 => array(), // Forbidden
|
||||
424 => array(), // Failed Dependency
|
||||
);
|
||||
|
||||
$hasError = false;
|
||||
|
||||
foreach($properties as $propertyName=>$propertyValue) {
|
||||
|
||||
// We don't know about this property.
|
||||
if (!isset($this->propertyMap[$propertyName])) {
|
||||
$hasError = true;
|
||||
$result[403][$propertyName] = null;
|
||||
unset($properties[$propertyName]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldName = $this->propertyMap[$propertyName];
|
||||
$newValues[$fieldName] = $propertyValue;
|
||||
|
||||
}
|
||||
|
||||
// If there were any errors we need to fail the request
|
||||
if ($hasError) {
|
||||
// Properties has the remaining properties
|
||||
foreach($properties as $propertyName=>$propertyValue) {
|
||||
$result[424][$propertyName] = null;
|
||||
}
|
||||
|
||||
// Removing unused statuscodes for cleanliness
|
||||
foreach($result as $status=>$properties) {
|
||||
if (is_array($properties) && count($properties)===0) unset($result[$status]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
// Success
|
||||
|
||||
// Now we're generating the sql query.
|
||||
$valuesSql = array();
|
||||
foreach($newValues as $fieldName=>$value) {
|
||||
$valuesSql[] = $fieldName . ' = ?';
|
||||
}
|
||||
$valuesSql[] = 'ctag = ctag + 1';
|
||||
|
||||
$stmt = $this->pdo->prepare("UPDATE `" . $this->calendarTableName . "` SET " . implode(', ',$valuesSql) . " WHERE id = ?");
|
||||
$newValues['id'] = $calendarId;
|
||||
$stmt->execute(array_values($newValues));
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar and all it's objects
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendar($calendarId) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarTableName.'` WHERE id = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all calendar objects within a calendar object.
|
||||
*
|
||||
* Every item contains an array with the following keys:
|
||||
* * id - unique identifier which will be used for subsequent updates
|
||||
* * calendardata - The iCalendar-compatible calnedar data
|
||||
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
|
||||
* * lastmodified - a timestamp of the last modification time
|
||||
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||
* ' "abcdef"')
|
||||
* * calendarid - The calendarid as it was passed to this function.
|
||||
*
|
||||
* Note that the etag is optional, but it's highly encouraged to return for
|
||||
* speed reasons.
|
||||
*
|
||||
* The calendardata is also optional. If it's not returned
|
||||
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||
* calendardata.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarObjects($calendarId) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
return $stmt->fetchAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information from a single calendar object, based on it's object
|
||||
* uri.
|
||||
*
|
||||
* The returned array must have the same keys as getCalendarObjects. The
|
||||
* 'calendardata' object is required here though, while it's not required
|
||||
* for getCalendarObjects.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarObject($calendarId,$objectUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ? AND uri = ?');
|
||||
$stmt->execute(array($calendarId, $objectUri));
|
||||
return $stmt->fetch();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar object.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return void
|
||||
*/
|
||||
public function createCalendarObject($calendarId,$objectUri,$calendarData) {
|
||||
|
||||
$stmt = $this->pdo->prepare('INSERT INTO `'.$this->calendarObjectTableName.'` (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)');
|
||||
$stmt->execute(array($calendarId,$objectUri,$calendarData,time()));
|
||||
$stmt = $this->pdo->prepare('UPDATE `'.$this->calendarTableName.'` SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing calendarobject, based on it's uri.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return void
|
||||
*/
|
||||
public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
|
||||
|
||||
$stmt = $this->pdo->prepare('UPDATE `'.$this->calendarObjectTableName.'` SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?');
|
||||
$stmt->execute(array($calendarData,time(),$calendarId,$objectUri));
|
||||
$stmt = $this->pdo->prepare('UPDATE `'.$this->calendarTableName.'` SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendarObject($calendarId,$objectUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ? AND uri = ?');
|
||||
$stmt->execute(array($calendarId,$objectUri));
|
||||
$stmt = $this->pdo->prepare('UPDATE `'. $this->calendarTableName .'` SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This object represents a CalDAV calendar.
|
||||
*
|
||||
* A calendar can contain multiple TODO and or Events. These are represented
|
||||
* as Sabre_CalDAV_CalendarObject objects.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
|
||||
|
||||
/**
|
||||
* This is an array with calendar information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $calendarInfo;
|
||||
|
||||
/**
|
||||
* CalDAV backend
|
||||
*
|
||||
* @var Sabre_CalDAV_Backend_Abstract
|
||||
*/
|
||||
protected $caldavBackend;
|
||||
|
||||
/**
|
||||
* Principal backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
||||
* @param array $calendarInfo
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) {
|
||||
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
$this->principalBackend = $principalBackend;
|
||||
$this->calendarInfo = $calendarInfo;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the calendar
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return $this->calendarInfo['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties such as the display name and description
|
||||
*
|
||||
* @param array $mutations
|
||||
* @return array
|
||||
*/
|
||||
public function updateProperties($mutations) {
|
||||
|
||||
return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of properties
|
||||
*
|
||||
* @param array $properties
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties($requestedProperties) {
|
||||
|
||||
$response = array();
|
||||
|
||||
foreach($requestedProperties as $prop) switch($prop) {
|
||||
|
||||
case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' :
|
||||
$response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData();
|
||||
break;
|
||||
case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' :
|
||||
$response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet();
|
||||
break;
|
||||
case '{DAV:}owner' :
|
||||
$response[$prop] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,$this->calendarInfo['principaluri']);
|
||||
break;
|
||||
default :
|
||||
if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop];
|
||||
break;
|
||||
|
||||
}
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a calendar object
|
||||
*
|
||||
* The contained calendar objects are for example Events or Todo's.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Sabre_DAV_ICalendarObject
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
|
||||
if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Calendar object not found');
|
||||
return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full list of calendar objects
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
|
||||
$children = array();
|
||||
foreach($objs as $obj) {
|
||||
$children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
|
||||
}
|
||||
return $children;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a child-node exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
|
||||
if (!$obj)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory
|
||||
*
|
||||
* We actually block this, as subdirectories are not allowed in calendars.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file
|
||||
*
|
||||
* The contents of the new file must be a valid ICalendar string.
|
||||
*
|
||||
* @param string $name
|
||||
* @param resource $calendarData
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($name,$calendarData = null) {
|
||||
|
||||
$calendarData = stream_get_contents($calendarData);
|
||||
|
||||
$supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']->getValue();
|
||||
Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents);
|
||||
|
||||
$this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the calendar.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the calendar. Note that most calendars use the
|
||||
* {DAV:}displayname to display a name to display a name.
|
||||
*
|
||||
* @param string $newName
|
||||
* @return void
|
||||
*/
|
||||
public function setName($newName) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date as a unix timestamp.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->calendarInfo['principaluri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->calendarInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The CalendarObject represents a single VEVENT or VTODO within a Calendar.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_DAVACL_IACL {
|
||||
|
||||
/**
|
||||
* Sabre_CalDAV_Backend_Abstract
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $caldavBackend;
|
||||
|
||||
/**
|
||||
* Array with information about this CalendarObject
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $objectData;
|
||||
|
||||
/**
|
||||
* Array with information about the containing calendar
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $calendarInfo;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
||||
* @param array $calendarInfo
|
||||
* @param array $objectData
|
||||
*/
|
||||
public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) {
|
||||
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
|
||||
if (!isset($objectData['calendarid'])) {
|
||||
throw new InvalidArgumentException('The objectData argument must contain a \'calendarid\' property');
|
||||
}
|
||||
if (!isset($objectData['uri'])) {
|
||||
throw new InvalidArgumentException('The objectData argument must contain an \'uri\' property');
|
||||
}
|
||||
|
||||
$this->calendarInfo = $calendarInfo;
|
||||
$this->objectData = $objectData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uri for this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return $this->objectData['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ICalendar-formatted object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get() {
|
||||
|
||||
// Pre-populating the 'calendardata' is optional, if we don't have it
|
||||
// already we fetch it from the backend.
|
||||
if (!isset($this->objectData['calendardata'])) {
|
||||
$this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']);
|
||||
}
|
||||
return $this->objectData['calendardata'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ICalendar-formatted object
|
||||
*
|
||||
* @param string $calendarData
|
||||
* @return void
|
||||
*/
|
||||
public function put($calendarData) {
|
||||
|
||||
if (is_resource($calendarData))
|
||||
$calendarData = stream_get_contents($calendarData);
|
||||
|
||||
$supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']->getValue();
|
||||
Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents);
|
||||
|
||||
$this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData);
|
||||
$this->objectData['calendardata'] = $calendarData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the calendar object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime content-type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType() {
|
||||
|
||||
return 'text/calendar';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ETag for this object.
|
||||
*
|
||||
* The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getETag() {
|
||||
|
||||
if (isset($this->objectData['etag'])) {
|
||||
return $this->objectData['etag'];
|
||||
} else {
|
||||
return '"' . md5($this->get()). '"';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date as a unix timestamp
|
||||
*
|
||||
* @return time
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return $this->objectData['lastmodified'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of this object in bytes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize() {
|
||||
|
||||
return strlen($this->objectData['calendardata']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->calendarInfo['principaluri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->calendarInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Users collection
|
||||
*
|
||||
* This object is responsible for generating a collection of users.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollection {
|
||||
|
||||
/**
|
||||
* CalDAV backend
|
||||
*
|
||||
* @var Sabre_CalDAV_Backend_Abstract
|
||||
*/
|
||||
protected $caldavBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* This constructor needs both an authentication and a caldav backend.
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') {
|
||||
|
||||
parent::__construct($principalBackend, $principalPrefix);
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nodename
|
||||
*
|
||||
* We're overriding this, because the default will be the 'principalPrefix',
|
||||
* and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return Sabre_CalDAV_Plugin::CALENDAR_ROOT;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a node for a principal.
|
||||
*
|
||||
* The passed array contains principal information, and is guaranteed to
|
||||
* at least contain a uri item. Other properties may or may not be
|
||||
* supplied by the authentication backend.
|
||||
*
|
||||
* @param array $principal
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChildForPrincipal(array $principal) {
|
||||
|
||||
return new Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* InvalidICalendarObject
|
||||
*
|
||||
* This exception is thrown when an attempt is made to create or update
|
||||
* an invalid ICalendar object
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Exception_InvalidICalendarObject extends Sabre_DAV_Exception_PreconditionFailed {
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ICS Exporter
|
||||
*
|
||||
* This plugin adds the ability to export entire calendars as .ics files.
|
||||
* This is useful for clients that don't support CalDAV yet. They often do
|
||||
* support ics files.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* Reference to Server class
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* Initializes the plugin and registers event handlers
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
$this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 'beforeMethod' event handles. This event handles intercepts GET requests ending
|
||||
* with ?export
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $uri
|
||||
* @return void
|
||||
*/
|
||||
public function beforeMethod($method, $uri) {
|
||||
|
||||
if ($method!='GET') return;
|
||||
if ($this->server->httpRequest->getQueryString()!='export') return;
|
||||
|
||||
// splitting uri
|
||||
list($uri) = explode('?',$uri,2);
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
|
||||
if (!($node instanceof Sabre_CalDAV_Calendar)) return;
|
||||
|
||||
$this->server->httpResponse->setHeader('Content-Type','text/calendar');
|
||||
$this->server->httpResponse->sendStatus(200);
|
||||
$this->server->httpResponse->sendBody($this->generateICS($this->server->tree->getChildren($uri)));
|
||||
|
||||
// Returning false to break the event chain
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all calendar objects, and builds one big ics export
|
||||
*
|
||||
* @param array $nodes
|
||||
* @return void
|
||||
*/
|
||||
public function generateICS(array $nodes) {
|
||||
|
||||
$calendar = new Sabre_VObject_Component('vcalendar');
|
||||
$calendar->version = '2.0';
|
||||
$calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
|
||||
$calendar->calscale = 'GREGORIAN';
|
||||
|
||||
$collectedTimezones = array();
|
||||
|
||||
$timezones = array();
|
||||
$objects = array();
|
||||
|
||||
foreach($nodes as $node) {
|
||||
|
||||
$nodeData = $node->get();
|
||||
$nodeComp = Sabre_VObject_Reader::read($nodeData);
|
||||
|
||||
foreach($nodeComp->children() as $child) {
|
||||
|
||||
switch($child->name) {
|
||||
case 'VEVENT' :
|
||||
case 'VTODO' :
|
||||
case 'VJOURNAL' :
|
||||
$objects[] = $child;
|
||||
break;
|
||||
|
||||
// VTIMEZONE is special, because we need to filter out the duplicates
|
||||
case 'VTIMEZONE' :
|
||||
// Naively just checking tzid.
|
||||
if (in_array((string)$child->TZID, $collectedTimezones)) continue;
|
||||
|
||||
$timezones[] = $child;
|
||||
$collectedTimezones[] = $child->TZID;
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
foreach($timezones as $tz) $calendar->add($tz);
|
||||
foreach($objects as $obj) $calendar->add($obj);
|
||||
|
||||
return $calendar->serialize();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This class contains several utilities related to the ICalendar (rfc2445) format
|
||||
*
|
||||
* This class is now deprecated, and won't be further maintained. Please use
|
||||
* the Sabre_VObject package for your ics parsing needs.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
* @deprecated Use Sabre_VObject instead.
|
||||
*/
|
||||
class Sabre_CalDAV_ICalendarUtil {
|
||||
|
||||
/**
|
||||
* Validates an ICalendar object
|
||||
*
|
||||
* This method makes sure this ICalendar object is properly formatted.
|
||||
* If we can't parse it, we'll throw exceptions.
|
||||
*
|
||||
* @param string $icalData
|
||||
* @param array $allowedComponents
|
||||
* @return bool
|
||||
*/
|
||||
static function validateICalendarObject($icalData, array $allowedComponents) {
|
||||
|
||||
$xcal = simplexml_load_string(self::toXCal($icalData));
|
||||
if (!$xcal) throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Invalid calendarobject format');
|
||||
|
||||
$xcal->registerXPathNameSpace('cal','urn:ietf:params:xml:ns:xcal');
|
||||
|
||||
// Check if there's only 1 component
|
||||
$components = array('vevent','vtodo','vjournal','vfreebusy');
|
||||
$componentsFound = array();
|
||||
|
||||
foreach($components as $component) {
|
||||
$test = $xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:' . $component);
|
||||
if (is_array($test)) $componentsFound = array_merge($componentsFound, $test);
|
||||
}
|
||||
if (count($componentsFound)<1) {
|
||||
throw new Sabre_CalDAV_Exception_InvalidICalendarObject('One VEVENT, VTODO, VJOURNAL or VFREEBUSY must be specified. 0 found.');
|
||||
}
|
||||
$component = $componentsFound[0];
|
||||
|
||||
// Check if the component is allowed
|
||||
$name = $component->getName();
|
||||
if (!in_array(strtoupper($name),$allowedComponents)) {
|
||||
throw new Sabre_CalDAV_Exception_InvalidICalendarObject(strtoupper($name) . ' is not allowed in this calendar.');
|
||||
}
|
||||
|
||||
if (count($xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:method'))>0) {
|
||||
throw new Sabre_CalDAV_Exception_InvalidICalendarObject('The METHOD property is not allowed in calendar objects');
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts ICalendar data to XML.
|
||||
*
|
||||
* Properties are converted to lowercase xml elements. Parameters are;
|
||||
* converted to attributes. BEGIN:VEVENT is converted to <vevent> and
|
||||
* END:VEVENT </vevent> as well as other components.
|
||||
*
|
||||
* It's a very loose parser. If any line does not conform to the spec, it
|
||||
* will simply be ignored. It will try to detect if \r\n or \n line endings
|
||||
* are used.
|
||||
*
|
||||
* @todo Currently quoted attributes are not parsed correctly.
|
||||
* @see http://tools.ietf.org/html/draft-royer-calsch-xcal-03
|
||||
* @param string $icalData
|
||||
* @return string.
|
||||
*/
|
||||
static function toXCAL($icalData) {
|
||||
|
||||
// Detecting line endings
|
||||
$lb="\r\n";
|
||||
if (strpos($icalData,"\r\n")!==false) $lb = "\r\n";
|
||||
elseif (strpos($icalData,"\n")!==false) $lb = "\n";
|
||||
|
||||
// Splitting up items per line
|
||||
$lines = explode($lb,$icalData);
|
||||
|
||||
// Properties can be folded over 2 lines. In this case the second
|
||||
// line will be preceeded by a space or tab.
|
||||
$lines2 = array();
|
||||
foreach($lines as $line) {
|
||||
|
||||
if (!$line) continue;
|
||||
if ($line[0]===" " || $line[0]==="\t") {
|
||||
$lines2[count($lines2)-1].=substr($line,1);
|
||||
continue;
|
||||
}
|
||||
|
||||
$lines2[]=$line;
|
||||
|
||||
}
|
||||
|
||||
$xml = '<?xml version="1.0"?>' . "\n";
|
||||
$xml.= "<iCalendar xmlns=\"urn:ietf:params:xml:ns:xcal\">\n";
|
||||
|
||||
$spaces = 2;
|
||||
foreach($lines2 as $line) {
|
||||
|
||||
$matches = array();
|
||||
// This matches PROPERTYNAME;ATTRIBUTES:VALUE
|
||||
if (!preg_match('/^([^:^;]*)(?:;([^:]*))?:(.*)$/',$line,$matches))
|
||||
continue;
|
||||
|
||||
$propertyName = strtolower($matches[1]);
|
||||
$attributes = $matches[2];
|
||||
$value = $matches[3];
|
||||
|
||||
// If the line was in the format BEGIN:COMPONENT or END:COMPONENT, we need to special case it.
|
||||
if ($propertyName === 'begin') {
|
||||
$xml.=str_repeat(" ",$spaces);
|
||||
$xml.='<' . strtolower($value) . ">\n";
|
||||
$spaces+=2;
|
||||
continue;
|
||||
} elseif ($propertyName === 'end') {
|
||||
$spaces-=2;
|
||||
$xml.=str_repeat(" ",$spaces);
|
||||
$xml.='</' . strtolower($value) . ">\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$xml.=str_repeat(" ",$spaces);
|
||||
$xml.='<' . $propertyName;
|
||||
if ($attributes) {
|
||||
// There can be multiple attributes
|
||||
$attributes = explode(';',$attributes);
|
||||
foreach($attributes as $att) {
|
||||
|
||||
list($attName,$attValue) = explode('=',$att,2);
|
||||
$attName = strtolower($attName);
|
||||
if ($attName === 'language') $attName='xml:lang';
|
||||
$xml.=' ' . $attName . '="' . htmlspecialchars($attValue) . '"';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$xml.='>'. htmlspecialchars(trim($value)) . '</' . $propertyName . ">\n";
|
||||
|
||||
}
|
||||
$xml.="</iCalendar>";
|
||||
return $xml;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,788 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CalDAV plugin
|
||||
*
|
||||
* This plugin provides functionality added by CalDAV (RFC 4791)
|
||||
* It implements new reports, and the MKCALENDAR method.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* This is the official CalDAV namespace
|
||||
*/
|
||||
const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav';
|
||||
|
||||
/**
|
||||
* This is the namespace for the proprietary calendarserver extensions
|
||||
*/
|
||||
const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
|
||||
|
||||
/**
|
||||
* The following constants are used to differentiate
|
||||
* the various filters for the calendar-query report
|
||||
*/
|
||||
const FILTER_COMPFILTER = 1;
|
||||
const FILTER_TIMERANGE = 3;
|
||||
const FILTER_PROPFILTER = 4;
|
||||
const FILTER_PARAMFILTER = 5;
|
||||
const FILTER_TEXTMATCH = 6;
|
||||
|
||||
/**
|
||||
* The hardcoded root for calendar objects. It is unfortunate
|
||||
* that we're stuck with it, but it will have to do for now
|
||||
*/
|
||||
const CALENDAR_ROOT = 'calendars';
|
||||
|
||||
/**
|
||||
* Reference to server object
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* Use this method to tell the server this plugin defines additional
|
||||
* HTTP methods.
|
||||
*
|
||||
* This method is passed a uri. It should only return HTTP methods that are
|
||||
* available for the specified uri.
|
||||
*
|
||||
* @param string $uri
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPMethods($uri) {
|
||||
|
||||
// The MKCALENDAR is only available on unmapped uri's, whose
|
||||
// parents extend IExtendedCollection
|
||||
list($parent, $name) = Sabre_DAV_URLUtil::splitPath($uri);
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($parent);
|
||||
|
||||
if ($node instanceof Sabre_DAV_IExtendedCollection) {
|
||||
try {
|
||||
$node->getChild($name);
|
||||
} catch (Sabre_DAV_Exception_FileNotFound $e) {
|
||||
return array('MKCALENDAR');
|
||||
}
|
||||
}
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of features for the DAV: HTTP header.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeatures() {
|
||||
|
||||
return array('calendar-access', 'calendar-proxy');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using Sabre_DAV_Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName() {
|
||||
|
||||
return 'caldav';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of reports this plugin supports.
|
||||
*
|
||||
* This will be used in the {DAV:}supported-report-set property.
|
||||
* Note that you still need to subscribe to the 'report' event to actually
|
||||
* implement them
|
||||
*
|
||||
* @param string $uri
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedReportSet($uri) {
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
if ($node instanceof Sabre_CalDAV_Calendar || $node instanceof Sabre_CalDAV_CalendarObject) {
|
||||
return array(
|
||||
'{' . self::NS_CALDAV . '}calendar-multiget',
|
||||
'{' . self::NS_CALDAV . '}calendar-query',
|
||||
);
|
||||
}
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
$server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
|
||||
//$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000);
|
||||
$server->subscribeEvent('report',array($this,'report'));
|
||||
$server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
|
||||
|
||||
$server->xmlNamespaces[self::NS_CALDAV] = 'cal';
|
||||
$server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
|
||||
|
||||
$server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet';
|
||||
|
||||
$server->resourceTypeMapping['Sabre_CalDAV_Calendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
|
||||
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
|
||||
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
|
||||
|
||||
array_push($server->protectedProperties,
|
||||
|
||||
'{' . self::NS_CALDAV . '}supported-calendar-component-set',
|
||||
'{' . self::NS_CALDAV . '}supported-calendar-data',
|
||||
'{' . self::NS_CALDAV . '}max-resource-size',
|
||||
'{' . self::NS_CALDAV . '}min-date-time',
|
||||
'{' . self::NS_CALDAV . '}max-date-time',
|
||||
'{' . self::NS_CALDAV . '}max-instances',
|
||||
'{' . self::NS_CALDAV . '}max-attendees-per-instance',
|
||||
'{' . self::NS_CALDAV . '}calendar-home-set',
|
||||
'{' . self::NS_CALDAV . '}supported-collation-set',
|
||||
'{' . self::NS_CALDAV . '}calendar-data',
|
||||
|
||||
// scheduling extension
|
||||
'{' . self::NS_CALDAV . '}calendar-user-address-set',
|
||||
|
||||
// CalendarServer extensions
|
||||
'{' . self::NS_CALENDARSERVER . '}getctag',
|
||||
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
|
||||
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles support for the MKCALENDAR method
|
||||
*
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function unknownMethod($method, $uri) {
|
||||
|
||||
if ($method!=='MKCALENDAR') return;
|
||||
|
||||
$this->httpMkCalendar($uri);
|
||||
// false is returned to stop the unknownMethod event
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions handles REPORT requests specific to CalDAV
|
||||
*
|
||||
* @param string $reportName
|
||||
* @param DOMNode $dom
|
||||
* @return bool
|
||||
*/
|
||||
public function report($reportName,$dom) {
|
||||
|
||||
switch($reportName) {
|
||||
case '{'.self::NS_CALDAV.'}calendar-multiget' :
|
||||
$this->calendarMultiGetReport($dom);
|
||||
return false;
|
||||
case '{'.self::NS_CALDAV.'}calendar-query' :
|
||||
$this->calendarQueryReport($dom);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the MKCALENDAR HTTP method, which creates
|
||||
* a new calendar.
|
||||
*
|
||||
* @param string $uri
|
||||
* @return void
|
||||
*/
|
||||
public function httpMkCalendar($uri) {
|
||||
|
||||
// Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support
|
||||
// for clients matching iCal in the user agent
|
||||
//$ua = $this->server->httpRequest->getHeader('User-Agent');
|
||||
//if (strpos($ua,'iCal/')!==false) {
|
||||
// throw new Sabre_DAV_Exception_Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.');
|
||||
//}
|
||||
|
||||
$body = $this->server->httpRequest->getBody(true);
|
||||
$properties = array();
|
||||
|
||||
if ($body) {
|
||||
|
||||
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
|
||||
|
||||
foreach($dom->firstChild->childNodes as $child) {
|
||||
|
||||
if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue;
|
||||
foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) {
|
||||
$properties[$k] = $prop;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
|
||||
|
||||
$this->server->createCollection($uri,$resourceType,$properties);
|
||||
|
||||
$this->server->httpResponse->sendStatus(201);
|
||||
$this->server->httpResponse->setHeader('Content-Length',0);
|
||||
}
|
||||
|
||||
/**
|
||||
* beforeGetProperties
|
||||
*
|
||||
* This method handler is invoked before any after properties for a
|
||||
* resource are fetched. This allows us to add in any CalDAV specific
|
||||
* properties.
|
||||
*
|
||||
* @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 Sabre_DAVACL_IPrincipal) {
|
||||
|
||||
// calendar-home-set property
|
||||
$calHome = '{' . self::NS_CALDAV . '}calendar-home-set';
|
||||
if (in_array($calHome,$requestedProperties)) {
|
||||
$principalId = $node->getName();
|
||||
$calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/';
|
||||
unset($requestedProperties[$calHome]);
|
||||
$returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath);
|
||||
}
|
||||
|
||||
// calendar-user-address-set property
|
||||
$calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set';
|
||||
if (in_array($calProp,$requestedProperties)) {
|
||||
|
||||
$addresses = $node->getAlternateUriSet();
|
||||
$addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl();
|
||||
unset($requestedProperties[$calProp]);
|
||||
$returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false);
|
||||
|
||||
}
|
||||
|
||||
// These two properties are shortcuts for ical to easily find
|
||||
// other principals this principal has access to.
|
||||
$propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for';
|
||||
$propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for';
|
||||
if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) {
|
||||
|
||||
$membership = $node->getGroupMembership();
|
||||
$readList = array();
|
||||
$writeList = array();
|
||||
|
||||
foreach($membership as $group) {
|
||||
|
||||
$groupNode = $this->server->tree->getNodeForPath($group);
|
||||
|
||||
// If the node is either ap proxy-read or proxy-write
|
||||
// group, we grab the parent principal and add it to the
|
||||
// list.
|
||||
if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) {
|
||||
list($readList[]) = Sabre_DAV_URLUtil::splitPath($group);
|
||||
}
|
||||
if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyWrite) {
|
||||
list($writeList[]) = Sabre_DAV_URLUtil::splitPath($group);
|
||||
}
|
||||
|
||||
}
|
||||
if (in_array($propRead,$requestedProperties)) {
|
||||
unset($requestedProperties[$propRead]);
|
||||
$returnedProperties[200][$propRead] = new Sabre_DAV_Property_HrefList($readList);
|
||||
}
|
||||
if (in_array($propWrite,$requestedProperties)) {
|
||||
unset($requestedProperties[$propWrite]);
|
||||
$returnedProperties[200][$propWrite] = new Sabre_DAV_Property_HrefList($writeList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // instanceof IPrincipal
|
||||
|
||||
|
||||
if ($node instanceof Sabre_CalDAV_CalendarObject) {
|
||||
// The calendar-data property is not supposed to be a 'real'
|
||||
// property, but in large chunks of the spec it does act as such.
|
||||
// Therefore we simply expose it as a property.
|
||||
$calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data';
|
||||
if (in_array($calDataProp, $requestedProperties)) {
|
||||
unset($requestedProperties[$calDataProp]);
|
||||
$val = $node->get();
|
||||
if (is_resource($val))
|
||||
$val = stream_get_contents($val);
|
||||
|
||||
// Taking out \r to not screw up the xml output
|
||||
$returnedProperties[200][$calDataProp] = str_replace("\r","", $val);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the calendar-multiget REPORT.
|
||||
*
|
||||
* This report is used by the client to fetch the content of a series
|
||||
* of urls. Effectively avoiding a lot of redundant requests.
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
public function calendarMultiGetReport($dom) {
|
||||
|
||||
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
|
||||
|
||||
$hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
|
||||
foreach($hrefElems as $elem) {
|
||||
$uri = $this->server->calculateUri($elem->nodeValue);
|
||||
list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
|
||||
$propertyList[]=$objProps;
|
||||
|
||||
}
|
||||
|
||||
$this->server->httpResponse->sendStatus(207);
|
||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the calendar-query REPORT
|
||||
*
|
||||
* This report is used by clients to request calendar objects based on
|
||||
* complex conditions.
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
public function calendarQueryReport($dom) {
|
||||
|
||||
$requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
|
||||
|
||||
$filterNode = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav','filter');
|
||||
if ($filterNode->length!==1) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The calendar-query report must have a filter element');
|
||||
}
|
||||
$filters = Sabre_CalDAV_XMLUtil::parseCalendarQueryFilters($filterNode->item(0));
|
||||
|
||||
$requestedCalendarData = true;
|
||||
|
||||
if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) {
|
||||
// We always retrieve calendar-data, as we need it for filtering.
|
||||
$requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data';
|
||||
|
||||
// If calendar-data wasn't explicitly requested, we need to remove
|
||||
// it after processing.
|
||||
$requestedCalendarData = false;
|
||||
}
|
||||
|
||||
// These are the list of nodes that potentially match the requirement
|
||||
$candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(),$requestedProperties,$this->server->getHTTPDepth(0));
|
||||
|
||||
$verifiedNodes = array();
|
||||
|
||||
foreach($candidateNodes as $node) {
|
||||
|
||||
// If the node didn't have a calendar-data property, it must not be a calendar object
|
||||
if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) continue;
|
||||
|
||||
if ($this->validateFilters($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'],$filters)) {
|
||||
|
||||
if (!$requestedCalendarData) {
|
||||
unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
||||
}
|
||||
$verifiedNodes[] = $node;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->server->httpResponse->sendStatus(207);
|
||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify if a list of filters applies to the calendar data object
|
||||
*
|
||||
* The calendarData object must be a valid iCalendar blob. The list of
|
||||
* filters must be formatted as parsed by Sabre_CalDAV_Plugin::parseCalendarQueryFilters
|
||||
*
|
||||
* @param string $calendarData
|
||||
* @param array $filters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateFilters($calendarData,$filters) {
|
||||
|
||||
// We are converting the calendar object to an XML structure
|
||||
// This makes it far easier to parse
|
||||
$xCalendarData = Sabre_CalDAV_ICalendarUtil::toXCal($calendarData);
|
||||
$xml = simplexml_load_string($xCalendarData);
|
||||
$xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:xcal');
|
||||
|
||||
foreach($filters as $xpath=>$filter) {
|
||||
|
||||
// if-not-defined comes first
|
||||
if (isset($filter['is-not-defined'])) {
|
||||
if (!$xml->xpath($xpath))
|
||||
continue;
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
$elem = $xml->xpath($xpath);
|
||||
|
||||
if (!$elem) return false;
|
||||
$elem = $elem[0];
|
||||
|
||||
if (isset($filter['time-range'])) {
|
||||
|
||||
switch($elem->getName()) {
|
||||
case 'vevent' :
|
||||
$result = $this->validateTimeRangeFilterForEvent($xml,$xpath,$filter);
|
||||
if ($result===false) return false;
|
||||
break;
|
||||
case 'vtodo' :
|
||||
$result = $this->validateTimeRangeFilterForTodo($xml,$xpath,$filter);
|
||||
if ($result===false) return false;
|
||||
break;
|
||||
case 'vjournal' :
|
||||
case 'vfreebusy' :
|
||||
case 'valarm' :
|
||||
// TODO: not implemented
|
||||
break;
|
||||
|
||||
/*
|
||||
|
||||
case 'vjournal' :
|
||||
$result = $this->validateTimeRangeFilterForJournal($xml,$xpath,$filter);
|
||||
if ($result===false) return false;
|
||||
break;
|
||||
case 'vfreebusy' :
|
||||
$result = $this->validateTimeRangeFilterForFreeBusy($xml,$xpath,$filter);
|
||||
if ($result===false) return false;
|
||||
break;
|
||||
case 'valarm' :
|
||||
$result = $this->validateTimeRangeFilterForAlarm($xml,$xpath,$filter);
|
||||
if ($result===false) return false;
|
||||
break;
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isset($filter['text-match'])) {
|
||||
$currentString = (string)$elem;
|
||||
|
||||
$isMatching = Sabre_DAV_StringUtil::textMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']);
|
||||
if ($filter['text-match']['negate-condition'] && $isMatching) return false;
|
||||
if (!$filter['text-match']['negate-condition'] && !$isMatching) return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a time-range filter matches an event.
|
||||
*
|
||||
* @param SimpleXMLElement $xml Event as xml object
|
||||
* @param string $currentXPath XPath to check
|
||||
* @param array $currentFilter Filter information
|
||||
* @return void
|
||||
*/
|
||||
private function validateTimeRangeFilterForEvent(SimpleXMLElement $xml,$currentXPath,array $currentFilter) {
|
||||
|
||||
// Grabbing the DTSTART property
|
||||
$xdtstart = $xml->xpath($currentXPath.'/c:dtstart');
|
||||
if (!count($xdtstart)) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('DTSTART property missing from calendar object');
|
||||
}
|
||||
|
||||
// The dtstart can be both a date, or datetime property
|
||||
if ((string)$xdtstart[0]['value']==='DATE' || strlen((string)$xdtstart[0])===8) {
|
||||
$isDateTime = false;
|
||||
} else {
|
||||
$isDateTime = true;
|
||||
}
|
||||
|
||||
// Determining the timezone
|
||||
if ($tzid = (string)$xdtstart[0]['tzid']) {
|
||||
$tz = new DateTimeZone($tzid);
|
||||
} else {
|
||||
$tz = null;
|
||||
}
|
||||
if ($isDateTime) {
|
||||
$dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtstart[0],$tz);
|
||||
} else {
|
||||
$dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtstart[0]);
|
||||
}
|
||||
|
||||
|
||||
// Grabbing the DTEND property
|
||||
$xdtend = $xml->xpath($currentXPath.'/c:dtend');
|
||||
$dtend = null;
|
||||
|
||||
if (count($xdtend)) {
|
||||
// Determining the timezone
|
||||
if ($tzid = (string)$xdtend[0]['tzid']) {
|
||||
$tz = new DateTimeZone($tzid);
|
||||
} else {
|
||||
$tz = null;
|
||||
}
|
||||
|
||||
// Since the VALUE prameter of both DTSTART and DTEND must be the same
|
||||
// we can assume we don't need to check the VALUE paramter of DTEND.
|
||||
if ($isDateTime) {
|
||||
$dtend = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtend[0],$tz);
|
||||
} else {
|
||||
$dtend = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtend[0],$tz);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (is_null($dtend)) {
|
||||
// The DTEND property was not found. We will first see if the event has a duration
|
||||
// property
|
||||
|
||||
$xduration = $xml->xpath($currentXPath.'/c:duration');
|
||||
if (count($xduration)) {
|
||||
$duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]);
|
||||
|
||||
// Making sure that the duration is bigger than 0 seconds.
|
||||
$tempDT = clone $dtstart;
|
||||
$tempDT->modify($duration);
|
||||
if ($tempDT > $dtstart) {
|
||||
|
||||
// use DTEND = DTSTART + DURATION
|
||||
$dtend = $tempDT;
|
||||
} else {
|
||||
// use DTEND = DTSTART
|
||||
$dtend = $dtstart;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($dtend)) {
|
||||
if ($isDateTime) {
|
||||
// DTEND = DTSTART
|
||||
$dtend = $dtstart;
|
||||
} else {
|
||||
// DTEND = DTSTART + 1 DAY
|
||||
$dtend = clone $dtstart;
|
||||
$dtend->modify('+1 day');
|
||||
}
|
||||
}
|
||||
// TODO: we need to properly parse RRULE's, but it's very difficult.
|
||||
// For now, we're always returning events if they have an RRULE at all.
|
||||
$rrule = $xml->xpath($currentXPath.'/c:rrule');
|
||||
$hasRrule = (count($rrule))>0;
|
||||
|
||||
if (!is_null($currentFilter['time-range']['start']) && $currentFilter['time-range']['start'] >= $dtend) return false;
|
||||
if (!is_null($currentFilter['time-range']['end']) && $currentFilter['time-range']['end'] <= $dtstart && !$hasRrule) return false;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private function validateTimeRangeFilterForTodo(SimpleXMLElement $xml,$currentXPath,array $filter) {
|
||||
|
||||
// Gathering all relevant elements
|
||||
|
||||
$dtStart = null;
|
||||
$duration = null;
|
||||
$due = null;
|
||||
$completed = null;
|
||||
$created = null;
|
||||
|
||||
$xdt = $xml->xpath($currentXPath.'/c:dtstart');
|
||||
if (count($xdt)) {
|
||||
// The dtstart can be both a date, or datetime property
|
||||
if ((string)$xdt[0]['value']==='DATE') {
|
||||
$isDateTime = false;
|
||||
} else {
|
||||
$isDateTime = true;
|
||||
}
|
||||
|
||||
// Determining the timezone
|
||||
if ($tzid = (string)$xdt[0]['tzid']) {
|
||||
$tz = new DateTimeZone($tzid);
|
||||
} else {
|
||||
$tz = null;
|
||||
}
|
||||
if ($isDateTime) {
|
||||
$dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz);
|
||||
} else {
|
||||
$dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Only need to grab duration if dtStart is set
|
||||
if (!is_null($dtStart)) {
|
||||
|
||||
$xduration = $xml->xpath($currentXPath.'/c:duration');
|
||||
if (count($xduration)) {
|
||||
$duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!is_null($dtStart) && !is_null($duration)) {
|
||||
|
||||
// Comparision from RFC 4791:
|
||||
// (start <= DTSTART+DURATION) AND ((end > DTSTART) OR (end >= DTSTART+DURATION))
|
||||
|
||||
$end = clone $dtStart;
|
||||
$end->modify($duration);
|
||||
|
||||
if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $end) &&
|
||||
(is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart || $filter['time-range']['end'] >= $end) ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Need to grab the DUE property
|
||||
$xdt = $xml->xpath($currentXPath.'/c:due');
|
||||
if (count($xdt)) {
|
||||
// The due property can be both a date, or datetime property
|
||||
if ((string)$xdt[0]['value']==='DATE') {
|
||||
$isDateTime = false;
|
||||
} else {
|
||||
$isDateTime = true;
|
||||
}
|
||||
// Determining the timezone
|
||||
if ($tzid = (string)$xdt[0]['tzid']) {
|
||||
$tz = new DateTimeZone($tzid);
|
||||
} else {
|
||||
$tz = null;
|
||||
}
|
||||
if ($isDateTime) {
|
||||
$due = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz);
|
||||
} else {
|
||||
$due = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($dtStart) && !is_null($due)) {
|
||||
|
||||
// Comparision from RFC 4791:
|
||||
// ((start < DUE) OR (start <= DTSTART)) AND ((end > DTSTART) OR (end >= DUE))
|
||||
|
||||
if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due || $filter['time-range']['start'] < $dtstart) &&
|
||||
(is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!is_null($dtStart)) {
|
||||
|
||||
// Comparision from RFC 4791
|
||||
// (start <= DTSTART) AND (end > DTSTART)
|
||||
if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $dtStart) &&
|
||||
(is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart) ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!is_null($due)) {
|
||||
|
||||
// Comparison from RFC 4791
|
||||
// (start < DUE) AND (end >= DUE)
|
||||
if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due) &&
|
||||
(is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
// Need to grab the COMPLETED property
|
||||
$xdt = $xml->xpath($currentXPath.'/c:completed');
|
||||
if (count($xdt)) {
|
||||
$completed = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]);
|
||||
}
|
||||
// Need to grab the CREATED property
|
||||
$xdt = $xml->xpath($currentXPath.'/c:created');
|
||||
if (count($xdt)) {
|
||||
$created = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]);
|
||||
}
|
||||
|
||||
if (!is_null($completed) && !is_null($created)) {
|
||||
// Comparison from RFC 4791
|
||||
// ((start <= CREATED) OR (start <= COMPLETED)) AND ((end >= CREATED) OR (end >= COMPLETED))
|
||||
if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $created || $filter['time-range']['start'] <= $completed) &&
|
||||
(is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $created || $filter['time-range']['end'] >= $completed)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($completed)) {
|
||||
// Comparison from RFC 4791
|
||||
// (start <= COMPLETED) AND (end >= COMPLETED)
|
||||
if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $completed) &&
|
||||
(is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $completed)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($created)) {
|
||||
// Comparison from RFC 4791
|
||||
// (end > CREATED)
|
||||
if( (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $created) ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything else is TRUE
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Principal collection
|
||||
*
|
||||
* This is an alternative collection to the standard ACL principal collection.
|
||||
* This collection adds support for the calendar-proxy-read and
|
||||
* calendar-proxy-write sub-principals, as defined by the caldav-proxy
|
||||
* specification.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Principal_Collection extends Sabre_DAVACL_AbstractPrincipalCollection {
|
||||
|
||||
/**
|
||||
* Returns a child object based on principal information
|
||||
*
|
||||
* @param array $principalInfo
|
||||
* @return Sabre_CalDAV_Principal_User
|
||||
*/
|
||||
public function getChildForPrincipal(array $principalInfo) {
|
||||
|
||||
return new Sabre_CalDAV_Principal_User($this->principalBackend, $principalInfo);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ProxyRead principal
|
||||
*
|
||||
* This class represents a principal group, hosted under the main principal.
|
||||
* This is needed to implement 'Calendar delegation' support. This class is
|
||||
* instantiated by Sabre_CalDAV_Principal_User.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal {
|
||||
|
||||
/**
|
||||
* Principal information from the parent principal.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $principalInfo;
|
||||
|
||||
/**
|
||||
* Principal backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* Creates the object.
|
||||
*
|
||||
* Note that you MUST supply the parent principal information.
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param array $principalInfo
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
|
||||
|
||||
$this->principalInfo = $principalInfo;
|
||||
$this->principalBackend = $principalBackend;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this principals name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return 'calendar-proxy-read';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification time
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the current node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @param string $name The new name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of altenative urls for a principal
|
||||
*
|
||||
* This can for example be an email address, or ldap url.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAlternateUriSet() {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full principal url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrincipalUrl() {
|
||||
|
||||
return $this->principalInfo['uri'] . '/' . $this->getName();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of group members
|
||||
*
|
||||
* If this principal is a group, this function should return
|
||||
* all member principal uri's for the group.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMemberSet() {
|
||||
|
||||
return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of groups this principal is member of
|
||||
*
|
||||
* If this principal is a member of a (list of) groups, this function
|
||||
* should return a list of principal uri's for it's members.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMembership() {
|
||||
|
||||
return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of group members
|
||||
*
|
||||
* If this principal is a group, this method sets all the group members.
|
||||
* The list of members is always overwritten, never appended to.
|
||||
*
|
||||
* This method should throw an exception if the members could not be set.
|
||||
*
|
||||
* @param array $principals
|
||||
* @return void
|
||||
*/
|
||||
public function setGroupMemberSet(array $principals) {
|
||||
|
||||
$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the displayname
|
||||
*
|
||||
* This should be a human readable name for the principal.
|
||||
* If none is available, return the nodename.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName() {
|
||||
|
||||
return $this->getName();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ProxyWrite principal
|
||||
*
|
||||
* This class represents a principal group, hosted under the main principal.
|
||||
* This is needed to implement 'Calendar delegation' support. This class is
|
||||
* instantiated by Sabre_CalDAV_Principal_User.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal {
|
||||
|
||||
/**
|
||||
* Parent principal information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $principalInfo;
|
||||
|
||||
/**
|
||||
* Principal Backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* Creates the object
|
||||
*
|
||||
* Note that you MUST supply the parent principal information.
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param array $principalInfo
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
|
||||
|
||||
$this->principalInfo = $principalInfo;
|
||||
$this->principalBackend = $principalBackend;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this principals name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return 'calendar-proxy-write';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification time
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the current node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @param string $name The new name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of altenative urls for a principal
|
||||
*
|
||||
* This can for example be an email address, or ldap url.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAlternateUriSet() {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full principal url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrincipalUrl() {
|
||||
|
||||
return $this->principalInfo['uri'] . '/' . $this->getName();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of group members
|
||||
*
|
||||
* If this principal is a group, this function should return
|
||||
* all member principal uri's for the group.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMemberSet() {
|
||||
|
||||
return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of groups this principal is member of
|
||||
*
|
||||
* If this principal is a member of a (list of) groups, this function
|
||||
* should return a list of principal uri's for it's members.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMembership() {
|
||||
|
||||
return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of group members
|
||||
*
|
||||
* If this principal is a group, this method sets all the group members.
|
||||
* The list of members is always overwritten, never appended to.
|
||||
*
|
||||
* This method should throw an exception if the members could not be set.
|
||||
*
|
||||
* @param array $principals
|
||||
* @return void
|
||||
*/
|
||||
public function setGroupMemberSet(array $principals) {
|
||||
|
||||
$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the displayname
|
||||
*
|
||||
* This should be a human readable name for the principal.
|
||||
* If none is available, return the nodename.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName() {
|
||||
|
||||
return $this->getName();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CalDAV principal
|
||||
*
|
||||
* This is a standard user-principal for CalDAV. This principal is also a
|
||||
* collection and returns the caldav-proxy-read and caldav-proxy-write child
|
||||
* principals.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Principal_User extends Sabre_DAVACL_Principal implements Sabre_DAV_ICollection {
|
||||
|
||||
/**
|
||||
* Creates a new file in the directory
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource $data Initial payload, passed as a readable stream resource.
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($name, $data = null) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory
|
||||
*
|
||||
* @param string $name
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its name
|
||||
*
|
||||
* @param string $name
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
if ($name === 'calendar-proxy-read')
|
||||
return new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties);
|
||||
|
||||
if ($name === 'calendar-proxy-write')
|
||||
return new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties);
|
||||
|
||||
throw new Sabre_DAV_Exception_FileNotFound('Node with name ' . $name . ' was not found');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes
|
||||
*
|
||||
* @return Sabre_DAV_INode[]
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
return array(
|
||||
new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties),
|
||||
new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a child-node with the specified name exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
return $name === 'calendar-proxy-read' || $name === 'calendar-proxy-write';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalProperties['uri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Supported component set property
|
||||
*
|
||||
* This property is a representation of the supported-calendar_component-set
|
||||
* property in the CalDAV namespace. It simply requires an array of components,
|
||||
* such as VEVENT, VTODO
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Property_SupportedCalendarComponentSet extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* List of supported components, such as "VEVENT, VTODO"
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $components;
|
||||
|
||||
/**
|
||||
* Creates the property
|
||||
*
|
||||
* @param array $components
|
||||
*/
|
||||
public function __construct(array $components) {
|
||||
|
||||
$this->components = $components;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of supported components
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValue() {
|
||||
|
||||
return $this->components;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the property in a DOMDocument
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $node
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||
|
||||
$doc = $node->ownerDocument;
|
||||
foreach($this->components as $component) {
|
||||
|
||||
$xcomp = $doc->createElement('cal:comp');
|
||||
$xcomp->setAttribute('name',$component);
|
||||
$node->appendChild($xcomp);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserializes the DOMElement back into a Property class.
|
||||
*
|
||||
* @param DOMElement $node
|
||||
* @return void
|
||||
*/
|
||||
static function unserialize(DOMElement $node) {
|
||||
|
||||
$components = array();
|
||||
foreach($node->childNodes as $childNode) {
|
||||
if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)==='{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}comp') {
|
||||
$components[] = $childNode->getAttribute('name');
|
||||
}
|
||||
}
|
||||
return new self($components);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Supported-calendar-data property
|
||||
*
|
||||
* This property is a representation of the supported-calendar-data property
|
||||
* in the CalDAV namespace. SabreDAV only has support for text/calendar;2.0
|
||||
* so the value is currently hardcoded.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Property_SupportedCalendarData extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* Serializes the property in a DOMDocument
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $node
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||
|
||||
$doc = $node->ownerDocument;
|
||||
|
||||
$prefix = isset($server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV])?$server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV]:'cal';
|
||||
|
||||
$caldata = $doc->createElement($prefix . ':calendar-data');
|
||||
$caldata->setAttribute('content-type','text/calendar');
|
||||
$caldata->setAttribute('version','2.0');
|
||||
|
||||
$node->appendChild($caldata);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* supported-collation-set property
|
||||
*
|
||||
* This property is a representation of the supported-collation-set property
|
||||
* in the CalDAV namespace.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Property_SupportedCollationSet extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* Serializes the property in a DOM document
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $node
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||
|
||||
$doc = $node->ownerDocument;
|
||||
|
||||
$prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav');
|
||||
if (!$prefix) $prefix = 'cal';
|
||||
|
||||
$node->appendChild(
|
||||
$doc->createElement($prefix . ':supported-collation','i;ascii-casemap')
|
||||
);
|
||||
$node->appendChild(
|
||||
$doc->createElement($prefix . ':supported-collation','i;octet')
|
||||
);
|
||||
$node->appendChild(
|
||||
$doc->createElement($prefix . ':supported-collation','i;unicode-casemap')
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CalDAV server
|
||||
*
|
||||
* This script is a convenience script. It quickly sets up a WebDAV server
|
||||
* with caldav and ACL support, and it creates the root 'principals' and
|
||||
* 'calendars' collections.
|
||||
*
|
||||
* Note that if you plan to do anything moderately complex, you are advised to
|
||||
* not subclass this server, but use Sabre_DAV_Server directly instead. This
|
||||
* class is nothing more than an 'easy setup'.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Server extends Sabre_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';
|
||||
|
||||
/**
|
||||
* Sets up the object. A PDO object must be passed to setup all the backends.
|
||||
*
|
||||
* @param PDO $pdo
|
||||
*/
|
||||
public function __construct(PDO $pdo) {
|
||||
|
||||
/* Backends */
|
||||
$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
|
||||
$calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo);
|
||||
$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
|
||||
|
||||
/* Directory structure */
|
||||
$tree = array(
|
||||
new Sabre_CalDAV_Principal_Collection($principalBackend),
|
||||
new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend),
|
||||
);
|
||||
|
||||
/* Initializing server */
|
||||
parent::__construct($tree);
|
||||
|
||||
/* Server Plugins */
|
||||
$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,$this->authRealm);
|
||||
$this->addPlugin($authPlugin);
|
||||
|
||||
$aclPlugin = new Sabre_DAVACL_Plugin();
|
||||
$this->addPlugin($aclPlugin);
|
||||
|
||||
$caldavPlugin = new Sabre_CalDAV_Plugin();
|
||||
$this->addPlugin($caldavPlugin);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The UserCalenders class contains all calendars associated to one user
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
|
||||
|
||||
/**
|
||||
* Principal backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* CalDAV backend
|
||||
*
|
||||
* @var Sabre_CalDAV_Backend_Abstract
|
||||
*/
|
||||
protected $caldavBackend;
|
||||
|
||||
/**
|
||||
* Principal information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $principalInfo;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
||||
* @param mixed $userUri
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) {
|
||||
|
||||
$this->principalBackend = $principalBackend;
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
$this->principalInfo = $principalBackend->getPrincipalByPath($userUri);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']);
|
||||
return $name;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the name of this object
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file under this object.
|
||||
*
|
||||
* This is currently not allowed
|
||||
*
|
||||
* @param string $filename
|
||||
* @param resource $data
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($filename, $data=null) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory under this object.
|
||||
*
|
||||
* This is currently not allowed.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($filename) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single calendar, by name
|
||||
*
|
||||
* @param string $name
|
||||
* @todo needs optimizing
|
||||
* @return Sabre_CalDAV_Calendar
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
foreach($this->getChildren() as $child) {
|
||||
if ($name==$child->getName())
|
||||
return $child;
|
||||
|
||||
}
|
||||
throw new Sabre_DAV_Exception_FileNotFound('Calendar with name \'' . $name . '\' could not be found');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a calendar exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @todo needs optimizing
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
foreach($this->getChildren() as $child) {
|
||||
if ($name==$child->getName())
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of calendars
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
|
||||
$objs = array();
|
||||
foreach($calendars as $calendar) {
|
||||
$objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar);
|
||||
}
|
||||
return $objs;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $properties
|
||||
* @return void
|
||||
*/
|
||||
public function createExtendedCollection($name, array $resourceType, array $properties) {
|
||||
|
||||
if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) {
|
||||
throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
|
||||
}
|
||||
$this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->principalInfo['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalInfo['uri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->principalInfo['uri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This class contains the Sabre_CalDAV version constants.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Version {
|
||||
|
||||
/**
|
||||
* Full version number
|
||||
*/
|
||||
const VERSION = '1.5.0';
|
||||
|
||||
/**
|
||||
* Stability : alpha, beta, stable
|
||||
*/
|
||||
const STABILITY = 'stable';
|
||||
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* XML utilities for CalDAV
|
||||
*
|
||||
* This class contains a few static methods used for parsing certain CalDAV
|
||||
* requests.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_XMLUtil {
|
||||
|
||||
/**
|
||||
* This function parses the calendar-query report request body
|
||||
*
|
||||
* The body is quite complicated, so we're turning it into a PHP
|
||||
* array.
|
||||
*
|
||||
* The resulting associative array has xpath expressions as keys.
|
||||
* By default the xpath expressions should simply be checked for existance
|
||||
* The xpath expressions can point to elements or attributes.
|
||||
*
|
||||
* The array values can contain a number of items, which alters the query
|
||||
* filter.
|
||||
*
|
||||
* * time-range. Must also check if the todo or event falls within the
|
||||
* specified timerange. How this is interpreted depends on
|
||||
* the type of object (VTODO, VEVENT, VJOURNAL, etc)
|
||||
* * is-not-defined
|
||||
* Instead of checking if the attribute or element exist,
|
||||
* we must check if it doesn't.
|
||||
* * text-match
|
||||
* Checks if the value of the attribute or element matches
|
||||
* the specified value. This is actually another array with
|
||||
* the 'collation', 'value' and 'negate-condition' items.
|
||||
*
|
||||
* Refer to the CalDAV spec for more information.
|
||||
*
|
||||
* @param DOMNode $domNode
|
||||
* @param string $basePath used for recursive calls.
|
||||
* @param array $filters used for recursive calls.
|
||||
* @return array
|
||||
*/
|
||||
static public function parseCalendarQueryFilters($domNode,$basePath = '/c:iCalendar', &$filters = array()) {
|
||||
|
||||
foreach($domNode->childNodes as $child) {
|
||||
|
||||
switch(Sabre_DAV_XMLUtil::toClarkNotation($child)) {
|
||||
|
||||
case '{urn:ietf:params:xml:ns:caldav}comp-filter' :
|
||||
case '{urn:ietf:params:xml:ns:caldav}prop-filter' :
|
||||
|
||||
$filterName = $basePath . '/' . 'c:' . strtolower($child->getAttribute('name'));
|
||||
$filters[$filterName] = array();
|
||||
|
||||
self::parseCalendarQueryFilters($child, $filterName,$filters);
|
||||
break;
|
||||
|
||||
case '{urn:ietf:params:xml:ns:caldav}time-range' :
|
||||
|
||||
if ($start = $child->getAttribute('start')) {
|
||||
$start = self::parseICalendarDateTime($start);
|
||||
} else {
|
||||
$start = null;
|
||||
}
|
||||
if ($end = $child->getAttribute('end')) {
|
||||
$end = self::parseICalendarDateTime($end);
|
||||
} else {
|
||||
$end = null;
|
||||
}
|
||||
|
||||
if (!is_null($start) && !is_null($end) && $end <= $start) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter');
|
||||
}
|
||||
|
||||
$filters[$basePath]['time-range'] = array(
|
||||
'start' => $start,
|
||||
'end' => $end
|
||||
);
|
||||
break;
|
||||
|
||||
case '{urn:ietf:params:xml:ns:caldav}is-not-defined' :
|
||||
$filters[$basePath]['is-not-defined'] = true;
|
||||
break;
|
||||
|
||||
case '{urn:ietf:params:xml:ns:caldav}param-filter' :
|
||||
|
||||
$filterName = $basePath . '/@' . strtolower($child->getAttribute('name'));
|
||||
$filters[$filterName] = array();
|
||||
self::parseCalendarQueryFilters($child, $filterName, $filters);
|
||||
break;
|
||||
|
||||
case '{urn:ietf:params:xml:ns:caldav}text-match' :
|
||||
|
||||
$collation = $child->getAttribute('collation');
|
||||
if (!$collation) $collation = 'i;ascii-casemap';
|
||||
|
||||
$filters[$basePath]['text-match'] = array(
|
||||
'collation' => ($collation == 'default'?'i;ascii-casemap':$collation),
|
||||
'negate-condition' => $child->getAttribute('negate-condition')==='yes',
|
||||
'value' => $child->nodeValue,
|
||||
);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $filters;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
|
||||
*
|
||||
* Specifying a reference timezone is optional. It will only be used
|
||||
* if the non-UTC format is used. The argument is used as a reference, the
|
||||
* returned DateTime object will still be in the UTC timezone.
|
||||
*
|
||||
* @param string $dt
|
||||
* @param DateTimeZone $tz
|
||||
* @return DateTime
|
||||
*/
|
||||
static public function parseICalendarDateTime($dt,DateTimeZone $tz = null) {
|
||||
|
||||
// Format is YYYYMMDD + "T" + hhmmss
|
||||
$result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
|
||||
|
||||
if (!$result) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt);
|
||||
}
|
||||
|
||||
if ($matches[7]==='Z' || is_null($tz)) {
|
||||
$tz = new DateTimeZone('UTC');
|
||||
}
|
||||
$date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
|
||||
|
||||
// Still resetting the timezone, to normalize everything to UTC
|
||||
$date->setTimeZone(new DateTimeZone('UTC'));
|
||||
return $date;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
|
||||
*
|
||||
* @param string $date
|
||||
* @param DateTimeZone $tz
|
||||
* @return DateTime
|
||||
*/
|
||||
static public function parseICalendarDate($date) {
|
||||
|
||||
// Format is YYYYMMDD
|
||||
$result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
|
||||
|
||||
if (!$result) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date);
|
||||
}
|
||||
|
||||
$date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC'));
|
||||
return $date;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (RFC5545) formatted duration and returns a string suitable
|
||||
* for strtotime or DateTime::modify.
|
||||
*
|
||||
* NOTE: When we require PHP 5.3 this can be replaced by the DateTimeInterval object, which
|
||||
* supports ISO 8601 Intervals, which is a superset of ICalendar durations.
|
||||
*
|
||||
* For now though, we're just gonna live with this messy system
|
||||
*
|
||||
* @param string $duration
|
||||
* @return string
|
||||
*/
|
||||
static public function parseICalendarDuration($duration) {
|
||||
|
||||
$result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches);
|
||||
if (!$result) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration);
|
||||
}
|
||||
|
||||
$parts = array(
|
||||
'week',
|
||||
'day',
|
||||
'hour',
|
||||
'minute',
|
||||
'second',
|
||||
);
|
||||
|
||||
$newDur = '';
|
||||
foreach($parts as $part) {
|
||||
if (isset($matches[$part]) && $matches[$part]) {
|
||||
$newDur.=' '.$matches[$part] . ' ' . $part . 's';
|
||||
}
|
||||
}
|
||||
|
||||
$newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
|
||||
return $newDur;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* UserAddressBook class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
/**
|
||||
* The AddressBook class represents a CardDAV addressbook, owned by a specific user
|
||||
*
|
||||
* The AddressBook can contain multiple vcards
|
||||
*/
|
||||
class Sabre_CardDAV_AddressBook extends Sabre_DAV_Directory implements Sabre_CardDAV_IAddressBook, Sabre_DAV_IProperties {
|
||||
|
||||
/**
|
||||
* This is an array with addressbook information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $addressBookInfo;
|
||||
|
||||
/**
|
||||
* CardDAV backend
|
||||
*
|
||||
* @var Sabre_CardDAV_Backend_Abstract
|
||||
*/
|
||||
private $carddavBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_CardDAV_Backend_Abstract $carddavBackend
|
||||
* @param array $addressBookInfo
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo) {
|
||||
|
||||
$this->carddavBackend = $carddavBackend;
|
||||
$this->addressBookInfo = $addressBookInfo;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the addressbook
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return $this->addressBookInfo['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a card
|
||||
*
|
||||
* @param string $name
|
||||
* @return Sabre_DAV_Card
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
$obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name);
|
||||
if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Card not found');
|
||||
return new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full list of cards
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
|
||||
$children = array();
|
||||
foreach($objs as $obj) {
|
||||
$children[] = new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
|
||||
}
|
||||
return $children;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory
|
||||
*
|
||||
* We actually block this, as subdirectories are not allowed in addressbooks.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in addressbooks is not allowed');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file
|
||||
*
|
||||
* The contents of the new file must be a valid VCARD
|
||||
*
|
||||
* @param string $name
|
||||
* @param resource $vcardData
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($name,$vcardData = null) {
|
||||
|
||||
$vcardData = stream_get_contents($vcardData);
|
||||
|
||||
$this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the entire addressbook.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the addressbook. Note that most calendars use the
|
||||
* {DAV:}displayname to display a name to display a name.
|
||||
*
|
||||
* @param string $newName
|
||||
* @return void
|
||||
*/
|
||||
public function setName($newName) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming addressbooks is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date as a unix timestamp.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties on this node,
|
||||
*
|
||||
* The properties array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existant property is always succesful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param array $mutations
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateProperties($mutations) {
|
||||
|
||||
return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of properties for this nodes.
|
||||
*
|
||||
* The properties list is a list of propertynames the client requested,
|
||||
* encoded in clark-notation {xmlnamespace}tagname
|
||||
*
|
||||
* If the array is empty, it means 'all properties' were requested.
|
||||
*
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function getProperties($properties) {
|
||||
|
||||
$response = array();
|
||||
foreach($properties as $propertyName) {
|
||||
|
||||
if (isset($this->addressBookInfo[$propertyName])) {
|
||||
|
||||
$response[$propertyName] = $this->addressBookInfo[$propertyName];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Parses the addressbook-query report request body.
|
||||
*
|
||||
* Whoever designed this format, and the CalDAV equavalent even more so,
|
||||
* has no feel for design.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CardDAV_AddressBookQueryParser {
|
||||
|
||||
const TEST_ANYOF = 'anyof';
|
||||
const TEST_ALLOF = 'allof';
|
||||
|
||||
/**
|
||||
* List of requested properties the client wanted
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $requestedProperties;
|
||||
|
||||
/**
|
||||
* The number of results the client wants
|
||||
*
|
||||
* null means it wasn't specified, which in most cases means 'all results'.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $limit;
|
||||
|
||||
/**
|
||||
* List of property filters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $filters;
|
||||
|
||||
/**
|
||||
* Either TEST_ANYOF or TEST_ALLOF
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $test;
|
||||
|
||||
/**
|
||||
* DOM Document
|
||||
*
|
||||
* @var DOMDocument
|
||||
*/
|
||||
protected $dom;
|
||||
|
||||
/**
|
||||
* DOM XPath object
|
||||
*
|
||||
* @var DOMXPath
|
||||
*/
|
||||
protected $xpath;
|
||||
|
||||
/**
|
||||
* Creates the parser
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(DOMDocument $dom) {
|
||||
|
||||
$this->dom = $dom;
|
||||
|
||||
$this->xpath = new DOMXPath($dom);
|
||||
$this->xpath->registerNameSpace('card',Sabre_CardDAV_Plugin::NS_CARDDAV);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the request.
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
public function parse() {
|
||||
|
||||
$filterNode = null;
|
||||
|
||||
$limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)');
|
||||
if (is_nan($limit)) $limit = null;
|
||||
|
||||
$filter = $this->xpath->query('/card:addressbook-query/card:filter');
|
||||
if ($filter->length !== 1) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
|
||||
}
|
||||
|
||||
$filter = $filter->item(0);
|
||||
$test = $this->xpath->evaluate('string(@test)', $filter);
|
||||
if (!$test) $test = self::TEST_ANYOF;
|
||||
if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
|
||||
}
|
||||
|
||||
$propFilters = array();
|
||||
|
||||
$propFilterNodes = $this->xpath->query('card:prop-filter', $filter);
|
||||
for($ii=0; $ii < $propFilterNodes->length; $ii++) {
|
||||
|
||||
$propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii));
|
||||
|
||||
|
||||
}
|
||||
|
||||
$this->filters = $propFilters;
|
||||
$this->limit = $limit;
|
||||
$this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
|
||||
$this->test = $test;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the prop-filter xml element
|
||||
*
|
||||
* @param DOMElement $propFilterNode
|
||||
* @return array
|
||||
*/
|
||||
protected function parsePropFilterNode(DOMElement $propFilterNode) {
|
||||
|
||||
$propFilter = array();
|
||||
$propFilter['name'] = $propFilterNode->getAttribute('name');
|
||||
$propFilter['test'] = $propFilterNode->getAttribute('test');
|
||||
if (!$propFilter['test']) $propFilter['test'] = 'anyof';
|
||||
|
||||
$propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0;
|
||||
|
||||
$paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode);
|
||||
|
||||
$propFilter['param-filters'] = array();
|
||||
|
||||
|
||||
for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
|
||||
|
||||
$propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii));
|
||||
|
||||
}
|
||||
$propFilter['text-matches'] = array();
|
||||
$textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode);
|
||||
|
||||
for($ii=0;$ii<$textMatchNodes->length;$ii++) {
|
||||
|
||||
$propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii));
|
||||
|
||||
}
|
||||
|
||||
return $propFilter;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the param-filter element
|
||||
*
|
||||
* @param DOMElement $paramFilterNode
|
||||
* @return array
|
||||
*/
|
||||
public function parseParamFilterNode(DOMElement $paramFilterNode) {
|
||||
|
||||
$paramFilter = array();
|
||||
$paramFilter['name'] = $paramFilterNode->getAttribute('name');
|
||||
$paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0;
|
||||
$paramFilter['text-match'] = null;
|
||||
|
||||
$textMatch = $this->xpath->query('card:text-match', $paramFilterNode);
|
||||
if ($textMatch->length>0) {
|
||||
$paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0));
|
||||
}
|
||||
|
||||
return $paramFilter;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Text match
|
||||
*
|
||||
* @param DOMElement $textMatchNode
|
||||
* @return void
|
||||
*/
|
||||
public function parseTextMatchNode(DOMElement $textMatchNode) {
|
||||
|
||||
$matchType = $textMatchNode->getAttribute('match-type');
|
||||
if (!$matchType) $matchType = 'contains';
|
||||
|
||||
if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Unknown match-type: ' . $matchType);
|
||||
}
|
||||
|
||||
$negateCondition = $textMatchNode->getAttribute('negate-condition');
|
||||
$negateCondition = $negateCondition==='yes';
|
||||
$collation = $textMatchNode->getAttribute('collation');
|
||||
if (!$collation) $collation = 'i;unicode-casemap';
|
||||
|
||||
return array(
|
||||
'negate-condition' => $negateCondition,
|
||||
'collation' => $collation,
|
||||
'match-type' => $matchType,
|
||||
'value' => $textMatchNode->nodeValue
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* AddressBook rootnode
|
||||
*
|
||||
* This object lists a collection of users, which can contain addressbooks.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CardDAV_AddressBookRoot extends Sabre_DAVACL_AbstractPrincipalCollection {
|
||||
|
||||
/**
|
||||
* Principal Backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* CardDAV backend
|
||||
*
|
||||
* @var Sabre_CardDAV_Backend_Abstract
|
||||
*/
|
||||
protected $carddavBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* This constructor needs both a principal and a carddav backend.
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param Sabre_CardDAV_Backend_Abstract $carddavBackend
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CardDAV_Backend_Abstract $carddavBackend) {
|
||||
|
||||
$this->carddavBackend = $carddavBackend;
|
||||
parent::__construct($principalBackend);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the node
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a node for a principal.
|
||||
*
|
||||
* The passed array contains principal information, and is guaranteed to
|
||||
* at least contain a uri item. Other properties may or may not be
|
||||
* supplied by the authentication backend.
|
||||
*
|
||||
* @param array $principal
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChildForPrincipal(array $principal) {
|
||||
|
||||
return new Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Abstract Backend class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class serves as a base-class for addressbook backends
|
||||
*
|
||||
* Note that there are references to 'addressBookId' scattered throughout the
|
||||
* class. The value of the addressBookId is completely up to you, it can be any
|
||||
* arbitrary value you can use as an unique identifier.
|
||||
*/
|
||||
abstract class Sabre_CardDAV_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* Returns the list of addressbooks for a specific user.
|
||||
*
|
||||
* Every addressbook should have the following properties:
|
||||
* id - an arbitrary unique id
|
||||
* uri - the 'basename' part of the url
|
||||
* principaluri - Same as the passed parameter
|
||||
*
|
||||
* Any additional clark-notation property may be passed besides this. Some
|
||||
* common ones are :
|
||||
* {DAV:}displayname
|
||||
* {urn:ietf:params:xml:ns:carddav}addressbook-description
|
||||
* {http://calendarserver.org/ns/}getctag
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public abstract function getAddressBooksForUser($principalUri);
|
||||
|
||||
/**
|
||||
* Updates an addressbook's properties
|
||||
*
|
||||
* See Sabre_DAV_IProperties for a description of the mutations array, as
|
||||
* well as the return value.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param array $mutations
|
||||
* @see Sabre_DAV_IProperties::updateProperties
|
||||
* @return bool|array
|
||||
*/
|
||||
public abstract function updateAddressBook($addressBookId, array $mutations);
|
||||
|
||||
/**
|
||||
* Creates a new address book
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $url Just the 'basename' of the url.
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
abstract public function createAddressBook($principalUri, $url, array $properties);
|
||||
|
||||
/**
|
||||
* Deletes an entire addressbook and all its contents
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @return void
|
||||
*/
|
||||
abstract public function deleteAddressBook($addressBookId);
|
||||
|
||||
/**
|
||||
* Returns all cards for a specific addressbook id.
|
||||
*
|
||||
* This method should return the following properties for each card:
|
||||
* * carddata - raw vcard data
|
||||
* * uri - Some unique url
|
||||
* * lastmodified - A unix timestamp
|
||||
|
||||
* @param mixed $addressbookId
|
||||
* @return array
|
||||
*/
|
||||
public abstract function getCards($addressbookId);
|
||||
|
||||
/**
|
||||
* Returns a specfic card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @return void
|
||||
*/
|
||||
public abstract function getCard($addressBookId, $cardUri);
|
||||
|
||||
/**
|
||||
* Creates a new card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @param string $cardData
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function createCard($addressBookId, $cardUri, $cardData);
|
||||
|
||||
/**
|
||||
* Updates a card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @param string $cardData
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function updateCard($addressBookId, $cardUri, $cardData);
|
||||
|
||||
/**
|
||||
* Deletes a card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function deleteCard($addressBookId, $cardUri);
|
||||
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PDO CardDAV backend
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
/**
|
||||
* This CardDAV backend uses PDO to store addressbooks
|
||||
*/
|
||||
class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* PDO connection
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* Sets up the object
|
||||
*
|
||||
* @param PDO $pdo
|
||||
*/
|
||||
public function __construct(PDO $pdo) {
|
||||
|
||||
$this->pdo = $pdo;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of addressbooks for a specific user.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public function getAddressBooksForUser($principalUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM addressbooks WHERE principaluri = ?');
|
||||
$result = $stmt->execute(array($principalUri));
|
||||
|
||||
$addressBooks = array();
|
||||
|
||||
foreach($stmt->fetchAll() as $row) {
|
||||
|
||||
$addressBooks[] = array(
|
||||
'id' => $row['id'],
|
||||
'uri' => $row['uri'],
|
||||
'principaluri' => $row['principaluri'],
|
||||
'{DAV:}displayname' => $row['displayname'],
|
||||
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
|
||||
'{http://calendarserver.org/ns/}getctag' => $row['ctag'],
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return $addressBooks;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates an addressbook's properties
|
||||
*
|
||||
* See Sabre_DAV_IProperties for a description of the mutations array, as
|
||||
* well as the return value.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param array $mutations
|
||||
* @see Sabre_DAV_IProperties::updateProperties
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateAddressBook($addressBookId, array $mutations) {
|
||||
|
||||
$updates = array();
|
||||
|
||||
foreach($mutations as $property=>$newValue) {
|
||||
|
||||
switch($property) {
|
||||
case '{DAV:}displayname' :
|
||||
$updates['displayname'] = $newValue;
|
||||
break;
|
||||
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
|
||||
$updates['description'] = $newValue;
|
||||
break;
|
||||
default :
|
||||
// If any unsupported values were being updated, we must
|
||||
// let the entire request fail.
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// No values are being updated?
|
||||
if (!$updates) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = 'UPDATE addressbooks SET ctag = ctag + 1 ';
|
||||
foreach($updates as $key=>$value) {
|
||||
$query.=', `' . $key . '` = :' . $key . ' ';
|
||||
}
|
||||
$query.=' WHERE id = :addressbookid';
|
||||
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$updates['addressbookid'] = $addressBookId;
|
||||
|
||||
$stmt->execute($updates);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new address book
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $url Just the 'basename' of the url.
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function createAddressBook($principalUri, $url, array $properties) {
|
||||
|
||||
$values = array(
|
||||
'displayname' => null,
|
||||
'description' => null,
|
||||
'principaluri' => $principalUri,
|
||||
'uri' => $url,
|
||||
);
|
||||
|
||||
foreach($properties as $property=>$newValue) {
|
||||
|
||||
switch($property) {
|
||||
case '{DAV:}displayname' :
|
||||
$values['displayname'] = $newValue;
|
||||
break;
|
||||
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
|
||||
$values['description'] = $newValue;
|
||||
break;
|
||||
default :
|
||||
throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$query = 'INSERT INTO addressbooks (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)';
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$stmt->execute($values);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entire addressbook and all its contents
|
||||
*
|
||||
* @param int $addressBookId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAddressBook($addressBookId) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM cards WHERE addressbookid = ?');
|
||||
$stmt->execute(array($addressBookId));
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM addressbooks WHERE id = ?');
|
||||
$stmt->execute(array($addressBookId));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all cards for a specific addressbook id.
|
||||
*
|
||||
* @param mixed $addressbookId
|
||||
* @return array
|
||||
*/
|
||||
public function getCards($addressbookId) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM cards WHERE addressbookid = ?');
|
||||
$stmt->execute(array($addressbookId));
|
||||
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
}
|
||||
/**
|
||||
* Returns a specfic card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCard($addressBookId, $cardUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM cards WHERE addressbookid = ? AND uri = ? LIMIT 1');
|
||||
$stmt->execute(array($addressBookId, $cardUri));
|
||||
|
||||
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return (count($result)>0?$result[0]:false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @param string $cardData
|
||||
* @return bool
|
||||
*/
|
||||
public function createCard($addressBookId, $cardUri, $cardData) {
|
||||
|
||||
$stmt = $this->pdo->prepare('INSERT INTO cards (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)');
|
||||
|
||||
$result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId));
|
||||
|
||||
$stmt2 = $this->pdo->prepare('UPDATE addressbooks SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt2->execute(array($addressBookId));
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @param string $cardData
|
||||
* @return bool
|
||||
*/
|
||||
public function updateCard($addressBookId, $cardUri, $cardData) {
|
||||
|
||||
$stmt = $this->pdo->prepare('UPDATE cards SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?');
|
||||
$result = $stmt->execute(array($cardData, time(), $cardUri, $addressBookId));
|
||||
|
||||
$stmt2 = $this->pdo->prepare('UPDATE addressbooks SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt2->execute(array($addressBookId));
|
||||
|
||||
return $stmt->rowCount()===1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteCard($addressBookId, $cardUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM cards WHERE addressbookid = ? AND uri = ?');
|
||||
$stmt->execute(array($addressBookId, $cardUri));
|
||||
|
||||
$stmt2 = $this->pdo->prepare('UPDATE addressbooks SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt2->execute(array($addressBookId));
|
||||
|
||||
return $stmt->rowCount()===1;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Card class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*
|
||||
/
|
||||
/**
|
||||
* The Card object represents a single Card from an addressbook
|
||||
*/
|
||||
class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard {
|
||||
|
||||
/**
|
||||
* CardDAV backend
|
||||
*
|
||||
* @var Sabre_CardDAV_Backend_Abstract
|
||||
*/
|
||||
private $carddavBackend;
|
||||
|
||||
/**
|
||||
* Array with information about this Card
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $cardData;
|
||||
|
||||
/**
|
||||
* Array with information about the containing addressbook
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $addressBookInfo;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_CardDAV_Backend_Abstract $carddavBackend
|
||||
* @param array $addressBookInfo
|
||||
* @param array $cardData
|
||||
*/
|
||||
public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo,array $cardData) {
|
||||
|
||||
$this->carddavBackend = $carddavBackend;
|
||||
$this->addressBookInfo = $addressBookInfo;
|
||||
$this->cardData = $cardData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uri for this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return $this->cardData['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the VCard-formatted object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get() {
|
||||
|
||||
$cardData = $this->cardData['carddata'];
|
||||
$s = fopen('php://temp','r+');
|
||||
fwrite($s, $cardData);
|
||||
rewind($s);
|
||||
return $s;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the VCard-formatted object
|
||||
*
|
||||
* @param string $cardData
|
||||
* @return void
|
||||
*/
|
||||
public function put($cardData) {
|
||||
|
||||
if (is_resource($cardData))
|
||||
$cardData = stream_get_contents($cardData);
|
||||
|
||||
$this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData);
|
||||
$this->cardData['carddata'] = $cardData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the card
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime content-type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType() {
|
||||
|
||||
return 'text/x-vcard';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ETag for this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getETag() {
|
||||
|
||||
return md5($this->cardData['carddata']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date as a unix timestamp
|
||||
*
|
||||
* @return time
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of this object in bytes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize() {
|
||||
|
||||
return strlen($this->cardData['carddata']);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* AddressBook interface
|
||||
*
|
||||
* Implement this interface to allow a node to be recognized as an addressbook.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_CardDAV_IAddressBook extends Sabre_DAV_ICollection {
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Card interface
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*
|
||||
/
|
||||
/**
|
||||
* Extend the ICard interface to allow your custom nodes to be picked up as
|
||||
* 'Cards'.
|
||||
*/
|
||||
interface Sabre_CardDAV_ICard extends Sabre_DAV_IFile {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* IDirectory interface
|
||||
*
|
||||
* Implement this interface to have an addressbook marked as a 'directory'. A
|
||||
* directory is an (often) global addressbook.
|
||||
*
|
||||
* A full description can be found in the IETF draft:
|
||||
* - draft-daboo-carddav-directory-gateway
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_CardDAV_IDirectory extends Sabre_CardDAV_IAddressBook {
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,465 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CardDAV plugin
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The CardDAV plugin adds CardDAV functionality to the WebDAV server
|
||||
*/
|
||||
class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* Url to the addressbooks
|
||||
*/
|
||||
const ADDRESSBOOK_ROOT = 'addressbooks';
|
||||
|
||||
/**
|
||||
* xml namespace for CardDAV elements
|
||||
*/
|
||||
const NS_CARDDAV = 'urn:ietf:params:xml:ns:carddav';
|
||||
|
||||
/**
|
||||
* Add urls to this property to have them automatically exposed as
|
||||
* 'directories' to the user.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $directories = array();
|
||||
|
||||
/**
|
||||
* Server class
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* Initializes the plugin
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
/* Events */
|
||||
$server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
|
||||
$server->subscribeEvent('report', array($this,'report'));
|
||||
|
||||
/* Namespaces */
|
||||
$server->xmlNamespaces[self::NS_CARDDAV] = 'card';
|
||||
|
||||
/* Mapping Interfaces to {DAV:}resourcetype values */
|
||||
$server->resourceTypeMapping['Sabre_CardDAV_IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook';
|
||||
$server->resourceTypeMapping['Sabre_CardDAV_IDirectory'] = '{' . self::NS_CARDDAV . '}directory';
|
||||
|
||||
/* Adding properties that may never be changed */
|
||||
$server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data';
|
||||
$server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size';
|
||||
|
||||
|
||||
$this->server = $server;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of supported features.
|
||||
*
|
||||
* This is used in the DAV: header in the OPTIONS and PROPFIND requests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeatures() {
|
||||
|
||||
return array('addressbook');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of reports this plugin supports.
|
||||
*
|
||||
* This will be used in the {DAV:}supported-report-set property.
|
||||
* Note that you still need to subscribe to the 'report' event to actually
|
||||
* implement them
|
||||
*
|
||||
* @param string $uri
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedReportSet($uri) {
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
if ($node instanceof Sabre_CardDAV_AddressBook || $node instanceof Sabre_CardDAV_ICard) {
|
||||
return array(
|
||||
'{' . self::NS_CARDDAV . '}addressbook-multiget',
|
||||
);
|
||||
}
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds all CardDAV-specific properties
|
||||
*
|
||||
* @param string $path
|
||||
* @param Sabre_DAV_INode $node
|
||||
* @param array $requestedProperties
|
||||
* @param array $returnedProperties
|
||||
* @return void
|
||||
*/
|
||||
public function beforeGetProperties($path, Sabre_DAV_INode $node, array &$requestedProperties, array &$returnedProperties) {
|
||||
|
||||
if ($node instanceof Sabre_DAVACL_IPrincipal) {
|
||||
|
||||
// calendar-home-set property
|
||||
$addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set';
|
||||
if (in_array($addHome,$requestedProperties)) {
|
||||
$principalId = $node->getName();
|
||||
$addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/';
|
||||
unset($requestedProperties[array_search($addHome, $requestedProperties)]);
|
||||
$returnedProperties[200][$addHome] = new Sabre_DAV_Property_Href($addressbookHomePath);
|
||||
}
|
||||
|
||||
$directories = '{' . self::NS_CARDDAV . '}directory-gateway';
|
||||
if ($this->directories && in_array($directories, $requestedProperties)) {
|
||||
unset($requestedProperties[array_search($directories, $requestedProperties)]);
|
||||
$returnedProperties[200][$directories] = new Sabre_DAV_Property_HrefList($this->directories);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($node instanceof Sabre_CardDAV_ICard) {
|
||||
|
||||
// The address-data property is not supposed to be a 'real'
|
||||
// property, but in large chunks of the spec it does act as such.
|
||||
// Therefore we simply expose it as a property.
|
||||
$addressDataProp = '{' . self::NS_CARDDAV . '}address-data';
|
||||
if (in_array($addressDataProp, $requestedProperties)) {
|
||||
unset($requestedProperties[$addressDataProp]);
|
||||
$val = $node->get();
|
||||
if (is_resource($val))
|
||||
$val = stream_get_contents($val);
|
||||
|
||||
// Taking out \r to not screw up the xml output
|
||||
$returnedProperties[200][$addressDataProp] = str_replace("\r","", $val);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions handles REPORT requests specific to CardDAV
|
||||
*
|
||||
* @param string $reportName
|
||||
* @param DOMNode $dom
|
||||
* @return bool
|
||||
*/
|
||||
public function report($reportName,$dom) {
|
||||
|
||||
switch($reportName) {
|
||||
case '{'.self::NS_CARDDAV.'}addressbook-multiget' :
|
||||
$this->addressbookMultiGetReport($dom);
|
||||
return false;
|
||||
case '{'.self::NS_CARDDAV.'}addressbook-query' :
|
||||
$this->addressBookQueryReport($dom);
|
||||
return false;
|
||||
default :
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the addressbook-multiget REPORT.
|
||||
*
|
||||
* This report is used by the client to fetch the content of a series
|
||||
* of urls. Effectively avoiding a lot of redundant requests.
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
public function addressbookMultiGetReport($dom) {
|
||||
|
||||
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
|
||||
|
||||
$hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
|
||||
$propertyList = array();
|
||||
|
||||
foreach($hrefElems as $elem) {
|
||||
|
||||
$uri = $this->server->calculateUri($elem->nodeValue);
|
||||
list($propertyList[]) = $this->server->getPropertiesForPath($uri,$properties);
|
||||
|
||||
}
|
||||
|
||||
$this->server->httpResponse->sendStatus(207);
|
||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the addressbook-query REPORT
|
||||
*
|
||||
* This report is used by the client to filter an addressbook based on a
|
||||
* complex query.
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
protected function addressbookQueryReport($dom) {
|
||||
|
||||
$query = new Sabre_CardDAV_AddressBookQueryParser($dom);
|
||||
$query->parse();
|
||||
|
||||
$depth = $this->server->getHTTPDepth(0);
|
||||
|
||||
if ($depth==0) {
|
||||
$candidateNodes = array(
|
||||
$this->server->tree->getNodeForPath($this->server->getRequestUri())
|
||||
);
|
||||
} else {
|
||||
$candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri());
|
||||
}
|
||||
|
||||
$validNodes = array();
|
||||
foreach($candidateNodes as $node) {
|
||||
|
||||
if (!$node instanceof Sabre_CardDAV_ICard)
|
||||
continue;
|
||||
|
||||
$blob = $node->get();
|
||||
if (is_resource($blob)) {
|
||||
$blob = stream_get_contents($blob);
|
||||
}
|
||||
|
||||
if (!$this->validateFilters($blob, $query->filters, $query->test)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$validNodes[] = $node;
|
||||
|
||||
if ($query->limit && $query->limit <= count($validNodes)) {
|
||||
// We hit the maximum number of items, we can stop now.
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach($validNodes as $validNode) {
|
||||
if ($depth==0) {
|
||||
$href = $this->server->getRequestUri();
|
||||
} else {
|
||||
$href = $this->server->getRequestUri() . '/' . $validNode->getName();
|
||||
}
|
||||
|
||||
list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0);
|
||||
|
||||
}
|
||||
|
||||
$this->server->httpResponse->sendStatus(207);
|
||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($result));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a vcard makes it throught a list of filters.
|
||||
*
|
||||
* @param string $vcardData
|
||||
* @param array $filters
|
||||
* @param string $test anyof or allof (which means OR or AND)
|
||||
* @return bool
|
||||
*/
|
||||
public function validateFilters($vcardData, array $filters, $test) {
|
||||
|
||||
$vcard = Sabre_VObject_Reader::read($vcardData);
|
||||
|
||||
$success = true;
|
||||
|
||||
foreach($filters as $filter) {
|
||||
|
||||
$isDefined = isset($vcard->{$filter['name']});
|
||||
if ($filter['is-not-defined']) {
|
||||
if ($isDefined) {
|
||||
$success = false;
|
||||
} else {
|
||||
$success = true;
|
||||
}
|
||||
} elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) {
|
||||
|
||||
// We only need to check for existence
|
||||
$success = $isDefined;
|
||||
|
||||
} else {
|
||||
|
||||
$vProperties = $vcard->select($filter['name']);
|
||||
|
||||
$results = array();
|
||||
if ($filter['param-filters']) {
|
||||
$results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']);
|
||||
}
|
||||
if ($filter['text-matches']) {
|
||||
$texts = array();
|
||||
foreach($vProperties as $vProperty)
|
||||
$texts[] = $vProperty->value;
|
||||
|
||||
$results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']);
|
||||
}
|
||||
|
||||
if (count($results)===1) {
|
||||
$success = $results[0];
|
||||
} else {
|
||||
if ($filter['test'] === 'anyof') {
|
||||
$success = $results[0] || $results[1];
|
||||
} else {
|
||||
$success = $results[0] && $results[1];
|
||||
}
|
||||
}
|
||||
|
||||
} // else
|
||||
|
||||
// There are two conditions where we can already determine wether
|
||||
// or not this filter succeeds.
|
||||
if ($test==='anyof' && $success) {
|
||||
return true;
|
||||
}
|
||||
if ($test==='allof' && !$success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // foreach
|
||||
|
||||
// If we got all the way here, it means we haven't been able to
|
||||
// determine early if the test failed or not.
|
||||
//
|
||||
// This implies for 'anyof' that the test failed, and for 'allof' that
|
||||
// we succeeded. Sounds weird, but makes sense.
|
||||
return $test==='allof';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a param-filter can be applied to a specific property.
|
||||
*
|
||||
* @todo currently we're only validating the first parameter of the passed
|
||||
* property. Any subsequence parameters with the same name are
|
||||
* ignored.
|
||||
* @param Sabre_VObject_Property $vProperty
|
||||
* @param array $filters
|
||||
* @param string $test
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateParamFilters(array $vProperties, array $filters, $test) {
|
||||
|
||||
$success = false;
|
||||
foreach($filters as $filter) {
|
||||
|
||||
$isDefined = false;
|
||||
foreach($vProperties as $vProperty) {
|
||||
$isDefined = isset($vProperty[$filter['name']]);
|
||||
if ($isDefined) break;
|
||||
}
|
||||
|
||||
if ($filter['is-not-defined']) {
|
||||
if ($isDefined) {
|
||||
$success = false;
|
||||
} else {
|
||||
$success = true;
|
||||
}
|
||||
|
||||
// If there's no text-match, we can just check for existence
|
||||
} elseif (!$filter['text-match'] || !$isDefined) {
|
||||
|
||||
$success = $isDefined;
|
||||
|
||||
} else {
|
||||
|
||||
$texts = array();
|
||||
$success = false;
|
||||
foreach($vProperties as $vProperty) {
|
||||
// If we got all the way here, we'll need to validate the
|
||||
// text-match filter.
|
||||
$success = Sabre_DAV_StringUtil::textMatch($vProperty[$filter['name']]->value, $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['matchType']);
|
||||
if ($success) break;
|
||||
}
|
||||
if ($filter['text-match']['negate-condition']) {
|
||||
$success = !$success;
|
||||
}
|
||||
|
||||
} // else
|
||||
|
||||
// There are two conditions where we can already determine wether
|
||||
// or not this filter succeeds.
|
||||
if ($test==='anyof' && $success) {
|
||||
return true;
|
||||
}
|
||||
if ($test==='allof' && !$success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If we got all the way here, it means we haven't been able to
|
||||
// determine early if the test failed or not.
|
||||
//
|
||||
// This implies for 'anyof' that the test failed, and for 'allof' that
|
||||
// we succeeded. Sounds weird, but makes sense.
|
||||
return $test==='allof';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a text-filter can be applied to a specific property.
|
||||
*
|
||||
* @param array $texts
|
||||
* @param array $filters
|
||||
* @param string $test
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateTextMatches(array $texts, array $filters, $test) {
|
||||
|
||||
foreach($filters as $filter) {
|
||||
|
||||
$success = false;
|
||||
foreach($texts as $haystack) {
|
||||
$success = Sabre_DAV_StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['matchType']);
|
||||
|
||||
// Breaking on the first match
|
||||
if ($success) break;
|
||||
}
|
||||
if ($filter['negate-condition']) {
|
||||
$success = !$success;
|
||||
}
|
||||
|
||||
if ($success && $test==='anyof')
|
||||
return true;
|
||||
|
||||
if (!$success && $test=='allof')
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// If we got all the way here, it means we haven't been able to
|
||||
// determine early if the test failed or not.
|
||||
//
|
||||
// This implies for 'anyof' that the test failed, and for 'allof' that
|
||||
// we succeeded. Sounds weird, but makes sense.
|
||||
return $test==='allof';
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* UserAddressBooks class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
/**
|
||||
* The UserAddressBooks collection contains a list of addressbooks associated with a user
|
||||
*/
|
||||
class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Directory implements Sabre_DAV_IExtendedCollection {
|
||||
|
||||
/**
|
||||
* Principal uri
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $principalUri;
|
||||
|
||||
/**
|
||||
* carddavBackend
|
||||
*
|
||||
* @var Sabre_CardDAV_Backend_Abstract
|
||||
*/
|
||||
protected $carddavBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_CardDAV_Backend_Abstract $carddavBackend
|
||||
* @param string $principalUri
|
||||
*/
|
||||
public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalUri) {
|
||||
|
||||
$this->carddavBackend = $carddavBackend;
|
||||
$this->principalUri = $principalUri;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalUri);
|
||||
return $name;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the name of this object
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file under this object.
|
||||
*
|
||||
* This is currently not allowed
|
||||
*
|
||||
* @param string $filename
|
||||
* @param resource $data
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($filename, $data=null) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory under this object.
|
||||
*
|
||||
* This is currently not allowed.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($filename) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single calendar, by name
|
||||
*
|
||||
* @param string $name
|
||||
* @todo needs optimizing
|
||||
* @return Sabre_CardDAV_AddressBook
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
foreach($this->getChildren() as $child) {
|
||||
if ($name==$child->getName())
|
||||
return $child;
|
||||
|
||||
}
|
||||
throw new Sabre_DAV_Exception_FileNotFound('Addressbook with name \'' . $name . '\' could not be found');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of addressbooks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri);
|
||||
$objs = array();
|
||||
foreach($addressbooks as $addressbook) {
|
||||
$objs[] = new Sabre_CardDAV_AddressBook($this->carddavBackend, $addressbook);
|
||||
}
|
||||
return $objs;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new addressbook
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $resourceType
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function createExtendedCollection($name, array $resourceType, array $properties) {
|
||||
|
||||
if (!in_array('{'.Sabre_CardDAV_Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) {
|
||||
throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
|
||||
}
|
||||
$this->carddavBackend->createAddressBook($this->principalUri, $name, $properties);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Version Class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class contains the Sabre_CardDAV version information
|
||||
*/
|
||||
class Sabre_CardDAV_Version {
|
||||
|
||||
/**
|
||||
* Full version number
|
||||
*/
|
||||
const VERSION = '0.2';
|
||||
|
||||
/**
|
||||
* Stability : alpha, beta, stable
|
||||
*/
|
||||
const STABILITY = 'alpha';
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/**
|
||||
* HTTP Basic authentication backend class
|
||||
*
|
||||
* This class can be used by authentication objects wishing to use HTTP Basic
|
||||
* Most of the digest logic is handled, implementors just need to worry about
|
||||
* the validateUserPass method.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author James David Low (http://jameslow.com/)
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_Auth_Backend_AbstractBasic implements Sabre_DAV_Auth_IBackend {
|
||||
|
||||
/**
|
||||
* This variable holds the currently logged in username.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Validates a username and password
|
||||
*
|
||||
* This method should return true or false depending on if login
|
||||
* succeeded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function validateUserPass($username, $password);
|
||||
|
||||
/**
|
||||
* Returns information about the currently logged in username.
|
||||
*
|
||||
* If nobody is currently logged in, this method should return null.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCurrentUser() {
|
||||
return $this->currentUser;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Authenticates the user based on the current request.
|
||||
*
|
||||
* If authentication is succesful, true must be returned.
|
||||
* If authentication fails, an exception must be thrown.
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_NotAuthenticated
|
||||
* @return bool
|
||||
*/
|
||||
public function authenticate(Sabre_DAV_Server $server,$realm) {
|
||||
|
||||
$auth = new Sabre_HTTP_BasicAuth();
|
||||
$auth->setHTTPRequest($server->httpRequest);
|
||||
$auth->setHTTPResponse($server->httpResponse);
|
||||
$auth->setRealm($realm);
|
||||
$userpass = $auth->getUserPass();
|
||||
if (!$userpass) {
|
||||
$auth->requireLogin();
|
||||
throw new Sabre_DAV_Exception_NotAuthenticated('No basic authentication headers were found');
|
||||
}
|
||||
|
||||
// Authenticates the user
|
||||
if (!$this->validateUserPass($userpass[0],$userpass[1])) {
|
||||
$auth->requireLogin();
|
||||
throw new Sabre_DAV_Exception_NotAuthenticated('Username or password does not match');
|
||||
}
|
||||
$this->currentUser = $userpass[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* HTTP Digest authentication backend class
|
||||
*
|
||||
* This class can be used by authentication objects wishing to use HTTP Digest
|
||||
* Most of the digest logic is handled, implementors just need to worry about
|
||||
* the getDigestHash method
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_Auth_Backend_AbstractDigest implements Sabre_DAV_Auth_IBackend {
|
||||
|
||||
/**
|
||||
* This variable holds the currently logged in username.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Returns a users digest hash based on the username and realm.
|
||||
*
|
||||
* If the user was not known, null must be returned.
|
||||
*
|
||||
* @param string $realm
|
||||
* @param string $username
|
||||
* @return string|null
|
||||
*/
|
||||
abstract public function getDigestHash($realm,$username);
|
||||
|
||||
/**
|
||||
* Authenticates the user based on the current request.
|
||||
*
|
||||
* If authentication is succesful, true must be returned.
|
||||
* If authentication fails, an exception must be thrown.
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_NotAuthenticated
|
||||
* @return bool
|
||||
*/
|
||||
public function authenticate(Sabre_DAV_Server $server,$realm) {
|
||||
|
||||
$digest = new Sabre_HTTP_DigestAuth();
|
||||
|
||||
// Hooking up request and response objects
|
||||
$digest->setHTTPRequest($server->httpRequest);
|
||||
$digest->setHTTPResponse($server->httpResponse);
|
||||
|
||||
$digest->setRealm($realm);
|
||||
$digest->init();
|
||||
|
||||
$username = $digest->getUsername();
|
||||
|
||||
// No username was given
|
||||
if (!$username) {
|
||||
$digest->requireLogin();
|
||||
throw new Sabre_DAV_Exception_NotAuthenticated('No digest authentication headers were found');
|
||||
}
|
||||
|
||||
$hash = $this->getDigestHash($realm, $username);
|
||||
// If this was false, the user account didn't exist
|
||||
if ($hash===false || is_null($hash)) {
|
||||
$digest->requireLogin();
|
||||
throw new Sabre_DAV_Exception_NotAuthenticated('The supplied username was not on file');
|
||||
}
|
||||
if (!is_string($hash)) {
|
||||
throw new Sabre_DAV_Exception('The returned value from getDigestHash must be a string or null');
|
||||
}
|
||||
|
||||
// If this was false, the password or part of the hash was incorrect.
|
||||
if (!$digest->validateA1($hash)) {
|
||||
$digest->requireLogin();
|
||||
throw new Sabre_DAV_Exception_NotAuthenticated('Incorrect username');
|
||||
}
|
||||
|
||||
$this->currentUser = $username;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently logged in username.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCurrentUser() {
|
||||
|
||||
return $this->currentUser;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Apache authenticator
|
||||
*
|
||||
* This authentication backend assumes that authentication has been
|
||||
* conifgured in apache, rather than within SabreDAV.
|
||||
*
|
||||
* Make sure apache is properly configured for this to work.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Auth_Backend_Apache implements Sabre_DAV_Auth_IBackend {
|
||||
|
||||
/**
|
||||
* Current apache user
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $remoteUser;
|
||||
|
||||
/**
|
||||
* Authenticates the user based on the current request.
|
||||
*
|
||||
* If authentication is succesful, true must be returned.
|
||||
* If authentication fails, an exception must be thrown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authenticate(Sabre_DAV_Server $server,$realm) {
|
||||
|
||||
$remoteUser = $server->httpRequest->getRawServerValue('REMOTE_USER');
|
||||
if (is_null($remoteUser)) {
|
||||
throw new Sabre_DAV_Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured');
|
||||
}
|
||||
|
||||
$this->remoteUser = $remoteUser;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the currently logged in user.
|
||||
*
|
||||
* If nobody is currently logged in, this method should return null.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getCurrentUser() {
|
||||
|
||||
return $this->remoteUser;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This is an authentication backend that uses a file to manage passwords.
|
||||
*
|
||||
* The backend file must conform to Apache's htdigest format
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Auth_Backend_File extends Sabre_DAV_Auth_Backend_AbstractDigest {
|
||||
|
||||
/**
|
||||
* List of users
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $users = array();
|
||||
|
||||
/**
|
||||
* Creates the backend object.
|
||||
*
|
||||
* If the filename argument is passed in, it will parse out the specified file fist.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($filename=null) {
|
||||
|
||||
if (!is_null($filename))
|
||||
$this->loadFile($filename);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an htdigest-formatted file. This method can be called multiple times if
|
||||
* more than 1 file is used.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return void
|
||||
*/
|
||||
public function loadFile($filename) {
|
||||
|
||||
foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) {
|
||||
|
||||
if (substr_count($line, ":") !== 2)
|
||||
throw new Sabre_DAV_Exception('Malformed htdigest file. Every line should contain 2 colons');
|
||||
|
||||
list($username,$realm,$A1) = explode(':',$line);
|
||||
|
||||
if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1))
|
||||
throw new Sabre_DAV_Exception('Malformed htdigest file. Invalid md5 hash');
|
||||
|
||||
$this->users[$realm . ':' . $username] = $A1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a users' information
|
||||
*
|
||||
* @param string $realm
|
||||
* @param string $username
|
||||
* @return string
|
||||
*/
|
||||
public function getDigestHash($realm, $username) {
|
||||
|
||||
return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This is an authentication backend that uses a file to manage passwords.
|
||||
*
|
||||
* The backend file must conform to Apache's htdigest format
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Auth_Backend_PDO extends Sabre_DAV_Auth_Backend_AbstractDigest {
|
||||
|
||||
/**
|
||||
* Reference to PDO connection
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* PDO table name we'll be using
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableName;
|
||||
|
||||
|
||||
/**
|
||||
* Creates the backend object.
|
||||
*
|
||||
* If the filename argument is passed in, it will parse out the specified file fist.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $tableName The PDO table name to use
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(PDO $pdo, $tableName = 'users') {
|
||||
|
||||
$this->pdo = $pdo;
|
||||
$this->tableName = $tableName;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the digest hash for a user.
|
||||
*
|
||||
* @param string $realm
|
||||
* @param string $username
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDigestHash($realm,$username) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT username, digesta1 FROM `'.$this->tableName.'` WHERE username = ?');
|
||||
$stmt->execute(array($username));
|
||||
$result = $stmt->fetchAll();
|
||||
|
||||
if (!count($result)) return;
|
||||
|
||||
return $result[0]['digesta1'];
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This is the base class for any authentication object.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_Auth_IBackend {
|
||||
|
||||
/**
|
||||
* Authenticates the user based on the current request.
|
||||
*
|
||||
* If authentication is succesful, true must be returned.
|
||||
* If authentication fails, an exception must be thrown.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function authenticate(Sabre_DAV_Server $server,$realm);
|
||||
|
||||
/**
|
||||
* Returns information about the currently logged in username.
|
||||
*
|
||||
* If nobody is currently logged in, this method should return null.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
function getCurrentUser();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This plugin provides Authentication for a WebDAV server.
|
||||
*
|
||||
* It relies on a Backend object, which provides user information.
|
||||
*
|
||||
* Additionally, it provides support for:
|
||||
* * {DAV:}current-user-principal property from RFC5397
|
||||
* * {DAV:}principal-collection-set property from RFC3744
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* Reference to main server object
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* Authentication backend
|
||||
*
|
||||
* @var Sabre_DAV_Auth_Backend_Abstract
|
||||
*/
|
||||
private $authBackend;
|
||||
|
||||
/**
|
||||
* The authentication realm.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $realm;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param Sabre_DAV_Auth_Backend_Abstract $authBackend
|
||||
* @param string $realm
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Sabre_DAV_Auth_IBackend $authBackend, $realm) {
|
||||
|
||||
$this->authBackend = $authBackend;
|
||||
$this->realm = $realm;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin. This function is automatically called by the server
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
$this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using Sabre_DAV_Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName() {
|
||||
|
||||
return 'auth';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current users' principal uri.
|
||||
*
|
||||
* If nobody is logged in, this will return null.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCurrentUser() {
|
||||
|
||||
$userInfo = $this->authBackend->getCurrentUser();
|
||||
if (!$userInfo) return null;
|
||||
|
||||
return $userInfo;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called before any HTTP method and forces users to be authenticated
|
||||
*
|
||||
* @param string $method
|
||||
* @throws Sabre_DAV_Exception_NotAuthenticated
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeMethod($method, $uri) {
|
||||
|
||||
$this->authBackend->authenticate($this->server,$this->realm);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* GuessContentType plugin
|
||||
*
|
||||
* A lot of the built-in File objects just return application/octet-stream
|
||||
* as a content-type by default. This is a problem for some clients, because
|
||||
* they expect a correct contenttype.
|
||||
*
|
||||
* There's really no accurate, fast and portable way to determine the contenttype
|
||||
* so this extension does what the rest of the world does, and guesses it based
|
||||
* on the file extension.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Browser_GuessContentType extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* List of recognized file extensions
|
||||
*
|
||||
* Feel free to add more
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $extensionMap = array(
|
||||
|
||||
// images
|
||||
'jpg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
|
||||
// groupware
|
||||
'ics' => 'text/calendar',
|
||||
'vcf' => 'text/x-vcard',
|
||||
|
||||
// text
|
||||
'txt' => 'text/plain',
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* Initializes the plugin
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
// Using a relatively low priority (200) to allow other extensions
|
||||
// to set the content-type first.
|
||||
$server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for teh afterGetProperties event
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function afterGetProperties($path, &$properties) {
|
||||
|
||||
if (array_key_exists('{DAV:}getcontenttype', $properties[404])) {
|
||||
|
||||
list(, $fileName) = Sabre_DAV_URLUtil::splitPath($path);
|
||||
$contentType = $this->getContentType($fileName);
|
||||
|
||||
if ($contentType) {
|
||||
$properties[200]['{DAV:}getcontenttype'] = $contentType;
|
||||
unset($properties[404]['{DAV:}getcontenttype']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple method to return the contenttype
|
||||
*
|
||||
* @param string $fileName
|
||||
* @return string
|
||||
*/
|
||||
protected function getContentType($fileName) {
|
||||
|
||||
// Just grabbing the extension
|
||||
$extension = substr($fileName,strrpos($fileName,'.')+1);
|
||||
if (isset($this->extensionMap[$extension]))
|
||||
return $this->extensionMap[$extension];
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This is a simple plugin that will map any GET request for non-files to
|
||||
* PROPFIND allprops-requests.
|
||||
*
|
||||
* This should allow easy debugging of PROPFIND
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Browser_MapGetToPropFind extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* reference to server class
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* Initializes the plugin and subscribes to events
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
$this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request
|
||||
*
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function httpGetInterceptor($method, $uri) {
|
||||
|
||||
if ($method!='GET') return true;
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
if ($node instanceof Sabre_DAV_IFile) return;
|
||||
|
||||
$this->server->invokeMethod('PROPFIND',$uri);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Browser Plugin
|
||||
*
|
||||
* This plugin provides a html representation, so that a WebDAV server may be accessed
|
||||
* using a browser.
|
||||
*
|
||||
* The class intercepts GET requests to collection resources and generates a simple
|
||||
* html index.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* reference to server class
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* enableEditing
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enablePost = true;
|
||||
|
||||
/**
|
||||
* Creates the object.
|
||||
*
|
||||
* By default it will allow file creation and uploads.
|
||||
* Specify the first argument as false to disable this
|
||||
*
|
||||
* @param bool $enablePost
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($enablePost=true) {
|
||||
|
||||
$this->enablePost = $enablePost;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin and subscribes to events
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
$this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
|
||||
if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method intercepts GET requests to collections and returns the html
|
||||
*
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function httpGetInterceptor($method, $uri) {
|
||||
|
||||
if ($method!='GET') return true;
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
if ($node instanceof Sabre_DAV_IFile) return true;
|
||||
|
||||
$this->server->httpResponse->sendStatus(200);
|
||||
$this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8');
|
||||
|
||||
$this->server->httpResponse->sendBody(
|
||||
$this->generateDirectoryIndex($uri)
|
||||
);
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles POST requests for tree operations
|
||||
*
|
||||
* This method is not yet used.
|
||||
*
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function httpPOSTHandler($method, $uri) {
|
||||
|
||||
if ($method!='POST') return true;
|
||||
if (isset($_POST['action'])) switch($_POST['action']) {
|
||||
|
||||
case 'mkcol' :
|
||||
if (isset($_POST['name']) && trim($_POST['name'])) {
|
||||
// Using basename() because we won't allow slashes
|
||||
list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($_POST['name']));
|
||||
$this->server->createDirectory($uri . '/' . $folderName);
|
||||
}
|
||||
break;
|
||||
case 'put' :
|
||||
if ($_FILES) $file = current($_FILES);
|
||||
else break;
|
||||
$newName = trim($file['name']);
|
||||
list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name']));
|
||||
if (isset($_POST['name']) && trim($_POST['name']))
|
||||
$newName = trim($_POST['name']);
|
||||
|
||||
// Making sure we only have a 'basename' component
|
||||
list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName);
|
||||
|
||||
|
||||
if (is_uploaded_file($file['tmp_name'])) {
|
||||
$parent = $this->server->tree->getNodeForPath(trim($uri,'/'));
|
||||
$parent->createFile($newName,fopen($file['tmp_name'],'r'));
|
||||
}
|
||||
|
||||
}
|
||||
$this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri());
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string for html.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function escapeHTML($value) {
|
||||
|
||||
return htmlspecialchars($value,ENT_QUOTES,'UTF-8');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the html directory index for a given url
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function generateDirectoryIndex($path) {
|
||||
|
||||
$html = "<html>
|
||||
<head>
|
||||
<title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . Sabre_DAV_Version::VERSION . "</title>
|
||||
<style type=\"text/css\"> body { Font-family: arial}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Index for " . $this->escapeHTML($path) . "/</h1>
|
||||
<table>
|
||||
<tr><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>
|
||||
<tr><td colspan=\"4\"><hr /></td></tr>";
|
||||
|
||||
$files = $this->server->getPropertiesForPath($path,array(
|
||||
'{DAV:}displayname',
|
||||
'{DAV:}resourcetype',
|
||||
'{DAV:}getcontenttype',
|
||||
'{DAV:}getcontentlength',
|
||||
'{DAV:}getlastmodified',
|
||||
),1);
|
||||
|
||||
|
||||
if ($path) {
|
||||
|
||||
list($parentUri) = Sabre_DAV_URLUtil::splitPath($path);
|
||||
$fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
|
||||
|
||||
$html.= "<tr>
|
||||
<td><a href=\"{$fullPath}\">..</a></td>
|
||||
<td>[parent]</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>";
|
||||
|
||||
}
|
||||
|
||||
foreach($files as $k=>$file) {
|
||||
|
||||
// This is the current directory, we can skip it
|
||||
if (rtrim($file['href'],'/')==$path) continue;
|
||||
|
||||
list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']);
|
||||
|
||||
$type = null;
|
||||
|
||||
if (isset($file[200]['{DAV:}resourcetype'])) {
|
||||
$type = $file[200]['{DAV:}resourcetype']->getValue();
|
||||
|
||||
// resourcetype can have multiple values
|
||||
if (!is_array($type)) $type = array($type);
|
||||
|
||||
foreach($type as $k=>$v) {
|
||||
|
||||
// Some name mapping is preferred
|
||||
switch($v) {
|
||||
case '{DAV:}collection' :
|
||||
$type[$k] = 'Collection';
|
||||
break;
|
||||
case '{DAV:}principal' :
|
||||
$type[$k] = 'Principal';
|
||||
break;
|
||||
case '{urn:ietf:params:xml:ns:carddav}addressbook' :
|
||||
$type[$k] = 'Addressbook';
|
||||
break;
|
||||
case '{urn:ietf:params:xml:ns:caldav}calendar' :
|
||||
$type[$k] = 'Calendar';
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
$type = implode(', ', $type);
|
||||
}
|
||||
|
||||
// If no resourcetype was found, we attempt to use
|
||||
// the contenttype property
|
||||
if (!$type && isset($file[200]['{DAV:}getcontenttype'])) {
|
||||
$type = $file[200]['{DAV:}getcontenttype'];
|
||||
}
|
||||
if (!$type) $type = 'Unknown';
|
||||
|
||||
$size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:'';
|
||||
$lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM):'';
|
||||
|
||||
$fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/'));
|
||||
|
||||
$displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name;
|
||||
|
||||
$name = $this->escapeHTML($name);
|
||||
$displayName = $this->escapeHTML($displayName);
|
||||
$type = $this->escapeHTML($type);
|
||||
|
||||
$html.= "<tr>
|
||||
<td><a href=\"{$fullPath}\">{$displayName}</a></td>
|
||||
<td>{$type}</td>
|
||||
<td>{$size}</td>
|
||||
<td>{$lastmodified}</td>
|
||||
</tr>";
|
||||
|
||||
}
|
||||
|
||||
$html.= "<tr><td colspan=\"4\"><hr /></td></tr>";
|
||||
|
||||
if ($this->enablePost) {
|
||||
$html.= '<tr><td><form method="post" action="">
|
||||
<h3>Create new folder</h3>
|
||||
<input type="hidden" name="action" value="mkcol" />
|
||||
Name: <input type="text" name="name" /><br />
|
||||
<input type="submit" value="create" />
|
||||
</form>
|
||||
<form method="post" action="" enctype="multipart/form-data">
|
||||
<h3>Upload file</h3>
|
||||
<input type="hidden" name="action" value="put" />
|
||||
Name (optional): <input type="text" name="name" /><br />
|
||||
File: <input type="file" name="file" /><br />
|
||||
<input type="submit" value="upload" />
|
||||
</form>
|
||||
</td></tr>';
|
||||
}
|
||||
|
||||
$html.= "</table>
|
||||
<address>Generated by SabreDAV " . Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY . " (c)2007-2011 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>
|
||||
</body>
|
||||
</html>";
|
||||
|
||||
return $html;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,431 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SabreDAV DAV client
|
||||
*
|
||||
* This client wraps around Curl to provide a convenient API to a WebDAV
|
||||
* server.
|
||||
*
|
||||
* NOTE: This class is experimental, it's api will likely change in the future.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAVClient
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Client {
|
||||
|
||||
public $propertyMap = array();
|
||||
|
||||
protected $baseUri;
|
||||
protected $userName;
|
||||
protected $password;
|
||||
protected $proxy;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Settings are provided through the 'settings' argument. The following
|
||||
* settings are supported:
|
||||
*
|
||||
* * baseUri
|
||||
* * userName (optional)
|
||||
* * password (optional)
|
||||
* * proxy (optional)
|
||||
*
|
||||
* @param array $settings
|
||||
*/
|
||||
public function __construct(array $settings) {
|
||||
|
||||
if (!isset($settings['baseUri'])) {
|
||||
throw new InvalidArgumentException('A baseUri must be provided');
|
||||
}
|
||||
|
||||
$validSettings = array(
|
||||
'baseUri',
|
||||
'userName',
|
||||
'password',
|
||||
'proxy'
|
||||
);
|
||||
|
||||
foreach($validSettings as $validSetting) {
|
||||
if (isset($settings[$validSetting])) {
|
||||
$this->$validSetting = $settings[$validSetting];
|
||||
}
|
||||
}
|
||||
|
||||
$this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a PROPFIND request
|
||||
*
|
||||
* The list of requested properties must be specified as an array, in clark
|
||||
* notation.
|
||||
*
|
||||
* The returned array will contain a list of filenames as keys, and
|
||||
* properties as values.
|
||||
*
|
||||
* The properties array will contain the list of properties. Only properties
|
||||
* that are actually returned from the server (without error) will be
|
||||
* returned, anything else is discarded.
|
||||
*
|
||||
* Depth should be either 0 or 1. A depth of 1 will cause a request to be
|
||||
* made to the server to also return all child resources.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $properties
|
||||
* @param int $depth
|
||||
* @return array
|
||||
*/
|
||||
public function propFind($url, array $properties, $depth = 0) {
|
||||
|
||||
$body = '<?xml version="1.0"?>' . "\n";
|
||||
$body.= '<d:propfind xmlns:d="DAV:">' . "\n";
|
||||
$body.= ' <d:prop>' . "\n";
|
||||
|
||||
foreach($properties as $property) {
|
||||
|
||||
list(
|
||||
$namespace,
|
||||
$elementName
|
||||
) = Sabre_DAV_XMLUtil::parseClarkNotation($property);
|
||||
|
||||
if ($namespace === 'DAV:') {
|
||||
$body.=' <d:' . $elementName . ' />' . "\n";
|
||||
} else {
|
||||
$body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$body.= ' </d:prop>' . "\n";
|
||||
$body.= '</d:propfind>';
|
||||
|
||||
$response = $this->request('PROPFIND', $url, $body, array(
|
||||
'Depth' => $depth,
|
||||
'Content-Type' => 'application/xml'
|
||||
));
|
||||
|
||||
$result = $this->parseMultiStatus($response['body']);
|
||||
|
||||
// If depth was 0, we only return the top item
|
||||
if ($depth===0) {
|
||||
reset($result);
|
||||
$result = current($result);
|
||||
return $result[200];
|
||||
}
|
||||
|
||||
$newResult = array();
|
||||
foreach($result as $href => $statusList) {
|
||||
|
||||
$newResult[$href] = $statusList[200];
|
||||
|
||||
}
|
||||
|
||||
return $newResult;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a list of properties on the server
|
||||
*
|
||||
* The list of properties must have clark-notation properties for the keys,
|
||||
* and the actual (string) value for the value. If the value is null, an
|
||||
* attempt is made to delete the property.
|
||||
*
|
||||
* @todo Must be building the request using the DOM, and does not yet
|
||||
* support complex properties.
|
||||
* @param string $url
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function propPatch($url, array $properties) {
|
||||
|
||||
$body = '<?xml version="1.0"?>' . "\n";
|
||||
$body.= '<d:propertyupdate xmlns:d="DAV:">' . "\n";
|
||||
|
||||
foreach($properties as $propName => $propValue) {
|
||||
|
||||
list(
|
||||
$namespace,
|
||||
$elementName
|
||||
) = Sabre_DAV_XMLUtil::parseClarkNotation($propName);
|
||||
|
||||
if ($propValue === null) {
|
||||
|
||||
$body.="<d:remove><d:prop>\n";
|
||||
|
||||
if ($namespace === 'DAV:') {
|
||||
$body.=' <d:' . $elementName . ' />' . "\n";
|
||||
} else {
|
||||
$body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
|
||||
}
|
||||
|
||||
$body.="</d:prop></d:remove>\n";
|
||||
|
||||
} else {
|
||||
|
||||
$body.="<d:set><d:prop>\n";
|
||||
if ($namespace === 'DAV:') {
|
||||
$body.=' <d:' . $elementName . '>';
|
||||
} else {
|
||||
$body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\">";
|
||||
}
|
||||
// Shitty.. i know
|
||||
$body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8');
|
||||
if ($namespace === 'DAV:') {
|
||||
$body.='</d:' . $elementName . '>' . "\n";
|
||||
} else {
|
||||
$body.="</x:" . $elementName . ">\n";
|
||||
}
|
||||
$body.="</d:prop></d:set>\n";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$body.= '</d:propertyupdate>';
|
||||
|
||||
$response = $this->request('PROPPATCH', $url, $body, array(
|
||||
'Content-Type' => 'application/xml'
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an HTTP options request
|
||||
*
|
||||
* This method returns all the features from the 'DAV:' header as an array.
|
||||
* If there was no DAV header, or no contents this method will return an
|
||||
* empty array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function options() {
|
||||
|
||||
$result = $this->request('OPTIONS');
|
||||
if (!isset($result['headers']['dav'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$features = explode(',', $result['headers']['dav']);
|
||||
foreach($features as &$v) {
|
||||
$v = trim($v);
|
||||
}
|
||||
return $features;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an actual HTTP request, and returns the result.
|
||||
*
|
||||
* If the specified url is relative, it will be expanded based on the base
|
||||
* url.
|
||||
*
|
||||
* The returned array contains 3 keys:
|
||||
* * body - the response body
|
||||
* * httpCode - a HTTP code (200, 404, etc)
|
||||
* * headers - a list of response http headers. The header names have
|
||||
* been lowercased.
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param string $body
|
||||
* @param array $headers
|
||||
* @return array
|
||||
*/
|
||||
public function request($method, $url = '', $body = null, $headers = array()) {
|
||||
|
||||
$url = $this->getAbsoluteUrl($url);
|
||||
|
||||
$curlSettings = array(
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_POSTFIELDS => $body,
|
||||
// Return headers as part of the response
|
||||
CURLOPT_HEADER => true
|
||||
);
|
||||
|
||||
// Adding HTTP headers
|
||||
$nHeaders = array();
|
||||
foreach($headers as $key=>$value) {
|
||||
|
||||
$nHeaders[] = $key . ': ' . $value;
|
||||
|
||||
}
|
||||
$curlSettings[CURLOPT_HTTPHEADER] = $nHeaders;
|
||||
|
||||
if ($this->proxy) {
|
||||
$curlSettings[CURLOPT_PROXY] = $this->proxy;
|
||||
}
|
||||
|
||||
if ($this->userName) {
|
||||
$curlSettings[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC | CURLAUTH_DIGEST;
|
||||
$curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
|
||||
}
|
||||
|
||||
list(
|
||||
$response,
|
||||
$curlInfo,
|
||||
$curlErrNo,
|
||||
$curlError
|
||||
) = $this->curlRequest($url, $curlSettings);
|
||||
|
||||
$headerBlob = substr($response, 0, $curlInfo['header_size']);
|
||||
$response = substr($response, $curlInfo['header_size']);
|
||||
|
||||
// In the case of 100 Continue, or redirects we'll have multiple lists
|
||||
// of headers for each separate HTTP response. We can easily split this
|
||||
// because they are separated by \r\n\r\n
|
||||
$headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n"));
|
||||
|
||||
// We only care about the last set of headers
|
||||
$headerBlob = $headerBlob[count($headerBlob)-1];
|
||||
|
||||
// Splitting headers
|
||||
$headerBlob = explode("\r\n", $headerBlob);
|
||||
|
||||
$headers = array();
|
||||
foreach($headerBlob as $header) {
|
||||
$parts = explode(':', $header, 2);
|
||||
if (count($parts)==2) {
|
||||
$headers[strtolower(trim($parts[0]))] = trim($parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'body' => $response,
|
||||
'statusCode' => $curlInfo['http_code'],
|
||||
'headers' => $headers
|
||||
);
|
||||
|
||||
if ($curlErrNo) {
|
||||
throw new Sabre_DAV_Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')');
|
||||
}
|
||||
|
||||
if ($response['statusCode']>=400) {
|
||||
throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')');
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for all curl functions.
|
||||
*
|
||||
* The only reason this was split out in a separate method, is so it
|
||||
* becomes easier to unittest.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $settings
|
||||
* @return
|
||||
*/
|
||||
protected function curlRequest($url, $settings) {
|
||||
|
||||
$curl = curl_init($url);
|
||||
curl_setopt_array($curl, $settings);
|
||||
|
||||
return array(
|
||||
curl_exec($curl),
|
||||
curl_getinfo($curl),
|
||||
curl_errno($curl),
|
||||
curl_error($curl)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full url based on the given url (which may be relative). All
|
||||
* urls are expanded based on the base url as given by the server.
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
protected function getAbsoluteUrl($url) {
|
||||
|
||||
// If the url starts with http:// or https://, the url is already absolute.
|
||||
if (preg_match('/^http(s?):\/\//', $url)) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
// If the url starts with a slash, we must calculate the url based off
|
||||
// the root of the base url.
|
||||
if (strpos($url,'/') === 0) {
|
||||
$parts = parse_url($this->baseUri);
|
||||
return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url;
|
||||
}
|
||||
|
||||
// Otherwise...
|
||||
return $this->baseUri . $url;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a WebDAV multistatus response body
|
||||
*
|
||||
* This method returns an array with the following structure
|
||||
*
|
||||
* array(
|
||||
* 'url/to/resource' => array(
|
||||
* '200' => array(
|
||||
* '{DAV:}property1' => 'value1',
|
||||
* '{DAV:}property2' => 'value2',
|
||||
* ),
|
||||
* '404' => array(
|
||||
* '{DAV:}property1' => null,
|
||||
* '{DAV:}property2' => null,
|
||||
* ),
|
||||
* )
|
||||
* 'url/to/resource2' => array(
|
||||
* .. etc ..
|
||||
* )
|
||||
* )
|
||||
*
|
||||
*
|
||||
* @param string $body xml body
|
||||
* @return array
|
||||
*/
|
||||
public function parseMultiStatus($body) {
|
||||
|
||||
$body = Sabre_DAV_XMLUtil::convertDAVNamespace($body);
|
||||
|
||||
$responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA);
|
||||
if ($responseXML===false) {
|
||||
throw new InvalidArgumentException('The passed data is not valid XML');
|
||||
}
|
||||
|
||||
$responseXML->registerXPathNamespace('d','DAV:');
|
||||
|
||||
$propResult = array();
|
||||
|
||||
foreach($responseXML->xpath('d:response') as $response) {
|
||||
|
||||
$response->registerXPathNamespace('d','DAV:');
|
||||
$href = $response->xpath('d:href');
|
||||
$href = (string)$href[0];
|
||||
|
||||
$properties = array();
|
||||
|
||||
foreach($response->xpath('d:propstat') as $propStat) {
|
||||
|
||||
$propStat->registerXPathNamespace('d','DAV:');
|
||||
$status = $propStat->xpath('d:status');
|
||||
list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3);
|
||||
|
||||
$properties[$statusCode] = Sabre_DAV_XMLUtil::parseProperties(dom_import_simplexml($propStat), $this->propertyMap);
|
||||
|
||||
}
|
||||
|
||||
$propResult[$href] = $properties;
|
||||
|
||||
}
|
||||
|
||||
return $propResult;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Directory class
|
||||
*
|
||||
* This is a helper class, that should aid in getting directory classes setup.
|
||||
* Most of its methods are implemented, and throw permission denied exceptions
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_Directory extends Sabre_DAV_Node implements Sabre_DAV_ICollection {
|
||||
|
||||
/**
|
||||
* Returns a child object, by its name.
|
||||
*
|
||||
* This method makes use of the getChildren method to grab all the child nodes, and compares the name.
|
||||
* Generally its wise to override this, as this can usually be optimized
|
||||
*
|
||||
* @param string $name
|
||||
* @throws Sabre_DAV_Exception_FileNotFound
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
foreach($this->getChildren() as $child) {
|
||||
|
||||
if ($child->getName()==$name) return $child;
|
||||
|
||||
}
|
||||
throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks is a child-node exists.
|
||||
*
|
||||
* It is generally a good idea to try and override this. Usually it can be optimized.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
try {
|
||||
|
||||
$this->getChild($name);
|
||||
return true;
|
||||
|
||||
} catch(Sabre_DAV_Exception_FileNotFound $e) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file in the directory
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource $data Initial payload, passed as a readable stream resource.
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($name, $data = null) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory
|
||||
*
|
||||
* @param string $name
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory');
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SabreDAV base exception
|
||||
*
|
||||
* This is SabreDAV's base exception file, use this to implement your own exception.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Exception class.
|
||||
*
|
||||
* This class defines a getHTTPCode method, which should return the appropriate HTTP code for the Exception occured.
|
||||
* The default for this is 500.
|
||||
*
|
||||
* This class also allows you to generate custom xml data for your exceptions. This will be displayed
|
||||
* in the 'error' element in the failing response.
|
||||
*/
|
||||
class Sabre_DAV_Exception extends Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 500;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additonal information into the WebDAV error response
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $errorNode
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to return any extra HTTP response headers.
|
||||
*
|
||||
* The headers must be returned as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPHeaders(Sabre_DAV_Server $server) {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* BadRequest
|
||||
*
|
||||
* The BadRequest is thrown when the user submitted an invalid HTTP request
|
||||
* BadRequest
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_BadRequest extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 400;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Conflict
|
||||
*
|
||||
* A 409 Conflict is thrown when a user tried to make a directory over an existing
|
||||
* file or in a parent directory that doesn't exist.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_Conflict extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 409;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ConflictingLock
|
||||
*
|
||||
* Similar to Exception_Locked, this exception thrown when a LOCK request
|
||||
* was made, on a resource which was already locked
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_ConflictingLock extends Sabre_DAV_Exception_Locked {
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additonal information into the WebDAV error response
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $errorNode
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
|
||||
|
||||
if ($this->lock) {
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock');
|
||||
$errorNode->appendChild($error);
|
||||
if (!is_object($this->lock)) var_dump($this->lock);
|
||||
$error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* FileNotFound
|
||||
*
|
||||
* This Exception is thrown when a Node couldn't be found. It returns HTTP error code 404
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_FileNotFound extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 404;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Forbidden
|
||||
*
|
||||
* This exception is thrown whenever a user tries to do an operation he's not allowed to
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_Forbidden extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 403;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* InsufficientStorage
|
||||
*
|
||||
* This Exception can be thrown, when for example a harddisk is full or a quota is exceeded
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_InsufficientStorage extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 507;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* InvalidResourceType
|
||||
*
|
||||
* This exception is thrown when the user tried to create a new collection, with
|
||||
* a special resourcetype value that was not recognized by the server.
|
||||
*
|
||||
* See RFC5689 section 3.3
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_InvalidResourceType extends Sabre_DAV_Exception_Forbidden {
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additonal information into the WebDAV error response
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $errorNode
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
|
||||
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:','d:valid-resourcetype');
|
||||
$errorNode->appendChild($error);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* LockTokenMatchesRequestUri
|
||||
*
|
||||
* This exception is thrown by UNLOCK if a supplied lock-token is invalid
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_LockTokenMatchesRequestUri extends Sabre_DAV_Exception_Conflict {
|
||||
|
||||
/**
|
||||
* Creates the exception
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
$this->message = 'The locktoken supplied does not match any locks on this entity';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additonal information into the WebDAV error response
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $errorNode
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
|
||||
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri');
|
||||
$errorNode->appendChild($error);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Locked
|
||||
*
|
||||
* The 423 is thrown when a client tried to access a resource that was locked, without supplying a valid lock token
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_Locked extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Lock information
|
||||
*
|
||||
* @var Sabre_DAV_Locks_LockInfo
|
||||
*/
|
||||
protected $lock;
|
||||
|
||||
/**
|
||||
* Creates the exception
|
||||
*
|
||||
* A LockInfo object should be passed if the user should be informed
|
||||
* which lock actually has the file locked.
|
||||
*
|
||||
* @param Sabre_DAV_Locks_LockInfo $lock
|
||||
*/
|
||||
public function __construct(Sabre_DAV_Locks_LockInfo $lock = null) {
|
||||
|
||||
$this->lock = $lock;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 423;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additonal information into the WebDAV error response
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $errorNode
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
|
||||
|
||||
if ($this->lock) {
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted');
|
||||
$errorNode->appendChild($error);
|
||||
if (!is_object($this->lock)) var_dump($this->lock);
|
||||
$error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* MethodNotAllowed
|
||||
*
|
||||
* The 405 is thrown when a client tried to create a directory on an already existing directory
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_MethodNotAllowed extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 405;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to return any extra HTTP response headers.
|
||||
*
|
||||
* The headers must be returned as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPHeaders(Sabre_DAV_Server $server) {
|
||||
|
||||
$methods = $server->getAllowedMethods($server->getRequestUri());
|
||||
|
||||
return array(
|
||||
'Allow' => strtoupper(implode(', ',$methods)),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* NotAuthenticated
|
||||
*
|
||||
* This exception is thrown when the client did not provide valid
|
||||
* authentication credentials.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_NotAuthenticated extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 401;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* NotImplemented
|
||||
*
|
||||
* This exception is thrown when the client tried to call an unsupported HTTP method or other feature
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_NotImplemented extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 501;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PreconditionFailed
|
||||
*
|
||||
* This exception is normally thrown when a client submitted a conditional request,
|
||||
* like for example an If, If-None-Match or If-Match header, which caused the HTTP
|
||||
* request to not execute (the condition of the header failed)
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_PreconditionFailed extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* When this exception is thrown, the header-name might be set.
|
||||
*
|
||||
* This allows the exception-catching code to determine which HTTP header
|
||||
* caused the exception.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $header = null;
|
||||
|
||||
/**
|
||||
* Create the exception
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $header
|
||||
*/
|
||||
public function __construct($message, $header=null) {
|
||||
|
||||
parent::__construct($message);
|
||||
$this->header = $header;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 412;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additonal information into the WebDAV error response
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $errorNode
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
|
||||
|
||||
if ($this->header) {
|
||||
$prop = $errorNode->ownerDocument->createElement('s:header');
|
||||
$prop->nodeValue = $this->header;
|
||||
$errorNode->appendChild($prop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ReportNotImplemented
|
||||
*
|
||||
* This exception is thrown when the client requested an unknown report through the REPORT method
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_ReportNotImplemented extends Sabre_DAV_Exception_NotImplemented {
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additonal information into the WebDAV error response
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $errorNode
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
|
||||
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:','d:supported-report');
|
||||
$errorNode->appendChild($error);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* RequestedRangeNotSatisfiable
|
||||
*
|
||||
* This exception is normally thrown when the user
|
||||
* request a range that is out of the entity bounds.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_RequestedRangeNotSatisfiable extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* returns the http statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 416;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* UnSupportedMediaType
|
||||
*
|
||||
* The 415 Unsupported Media Type status code is generally sent back when the client
|
||||
* tried to call an HTTP method, with a body the server didn't understand
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Exception_UnsupportedMediaType extends Sabre_DAV_Exception {
|
||||
|
||||
/**
|
||||
* returns the http statuscode for this exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode() {
|
||||
|
||||
return 415;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Directory class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
|
||||
|
||||
/**
|
||||
* Creates a new file in the directory
|
||||
*
|
||||
* data is a readable stream resource
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource $data Initial payload
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($name, $data = null) {
|
||||
|
||||
$newPath = $this->path . '/' . $name;
|
||||
file_put_contents($newPath,$data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
$newPath = $this->path . '/' . $name;
|
||||
mkdir($newPath);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its name
|
||||
*
|
||||
* @param string $name
|
||||
* @throws Sabre_DAV_Exception_FileNotFound
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
$path = $this->path . '/' . $name;
|
||||
|
||||
if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File with name ' . $path . ' could not be located');
|
||||
|
||||
if (is_dir($path)) {
|
||||
|
||||
return new Sabre_DAV_FS_Directory($path);
|
||||
|
||||
} else {
|
||||
|
||||
return new Sabre_DAV_FS_File($path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes
|
||||
*
|
||||
* @return Sabre_DAV_INode[]
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$nodes = array();
|
||||
foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node);
|
||||
return $nodes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a child exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
$path = $this->path . '/' . $name;
|
||||
return file_exists($path);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all files in this directory, and then itself
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
foreach($this->getChildren() as $child) $child->delete();
|
||||
rmdir($this->path);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns available diskspace information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getQuotaInfo() {
|
||||
|
||||
return array(
|
||||
disk_total_space($this->path)-disk_free_space($this->path),
|
||||
disk_free_space($this->path)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* File class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_FS_File extends Sabre_DAV_FS_Node implements Sabre_DAV_IFile {
|
||||
|
||||
/**
|
||||
* Updates the data
|
||||
*
|
||||
* @param resource $data
|
||||
* @return void
|
||||
*/
|
||||
public function put($data) {
|
||||
|
||||
file_put_contents($this->path,$data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get() {
|
||||
|
||||
return fopen($this->path,'r');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the current file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
unlink($this->path);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the node, in bytes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize() {
|
||||
|
||||
return filesize($this->path);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
* The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getETag() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContentType() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Base node-class
|
||||
*
|
||||
* The node class implements the method used by both the File and the Directory classes
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_FS_Node implements Sabre_DAV_INode {
|
||||
|
||||
/**
|
||||
* The path to the current node
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Sets up the node, expects a full path name
|
||||
*
|
||||
* @param string $path
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($path) {
|
||||
|
||||
$this->path = $path;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the node
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path);
|
||||
return $name;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node
|
||||
*
|
||||
* @param string $name The new name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
|
||||
list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
|
||||
|
||||
$newPath = $parentPath . '/' . $newName;
|
||||
rename($this->path,$newPath);
|
||||
|
||||
$this->path = $newPath;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the last modification time, as a unix timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return filemtime($this->path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Directory class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
|
||||
|
||||
/**
|
||||
* Creates a new file in the directory
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource $data Initial payload
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($name, $data = null) {
|
||||
|
||||
// We're not allowing dots
|
||||
if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
|
||||
$newPath = $this->path . '/' . $name;
|
||||
file_put_contents($newPath,$data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
// We're not allowing dots
|
||||
if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
|
||||
$newPath = $this->path . '/' . $name;
|
||||
mkdir($newPath);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its name
|
||||
*
|
||||
* @param string $name
|
||||
* @throws Sabre_DAV_Exception_FileNotFound
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
$path = $this->path . '/' . $name;
|
||||
|
||||
if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File could not be located');
|
||||
if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
|
||||
|
||||
if (is_dir($path)) {
|
||||
|
||||
return new Sabre_DAV_FSExt_Directory($path);
|
||||
|
||||
} else {
|
||||
|
||||
return new Sabre_DAV_FSExt_File($path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a child exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
if ($name=='.' || $name=='..')
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
|
||||
|
||||
$path = $this->path . '/' . $name;
|
||||
return file_exists($path);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes
|
||||
*
|
||||
* @return Sabre_DAV_INode[]
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$nodes = array();
|
||||
foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node);
|
||||
return $nodes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all files in this directory, and then itself
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
// Deleting all children
|
||||
foreach($this->getChildren() as $child) $child->delete();
|
||||
|
||||
// Removing resource info, if its still around
|
||||
if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav');
|
||||
|
||||
// Removing the directory itself
|
||||
rmdir($this->path);
|
||||
|
||||
return parent::delete();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns available diskspace information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getQuotaInfo() {
|
||||
|
||||
return array(
|
||||
disk_total_space($this->path)-disk_free_space($this->path),
|
||||
disk_free_space($this->path)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* File class
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFile {
|
||||
|
||||
/**
|
||||
* Updates the data
|
||||
*
|
||||
* data is a readable stream resource.
|
||||
*
|
||||
* @param resource $data
|
||||
* @return void
|
||||
*/
|
||||
public function put($data) {
|
||||
|
||||
file_put_contents($this->path,$data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get() {
|
||||
|
||||
return fopen($this->path,'r');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the current file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
unlink($this->path);
|
||||
return parent::delete();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
* The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined
|
||||
*/
|
||||
public function getETag() {
|
||||
|
||||
return '"' . md5_file($this->path). '"';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*/
|
||||
public function getContentType() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the file, in bytes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize() {
|
||||
|
||||
return filesize($this->path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Base node-class
|
||||
*
|
||||
* The node class implements the method used by both the File and the Directory classes
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_DAV_ILockable, Sabre_DAV_IProperties {
|
||||
|
||||
/**
|
||||
* Returns all the locks on this node
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getLocks() {
|
||||
|
||||
$resourceData = $this->getResourceData();
|
||||
$locks = $resourceData['locks'];
|
||||
foreach($locks as $k=>$lock) {
|
||||
if (time() > $lock->timeout + $lock->created) unset($locks[$k]);
|
||||
}
|
||||
return $locks;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks this node
|
||||
*
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return void
|
||||
*/
|
||||
function lock(Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
// We're making the lock timeout 30 minutes
|
||||
$lockInfo->timeout = 1800;
|
||||
$lockInfo->created = time();
|
||||
|
||||
$resourceData = $this->getResourceData();
|
||||
if (!isset($resourceData['locks'])) $resourceData['locks'] = array();
|
||||
$current = null;
|
||||
foreach($resourceData['locks'] as $k=>$lock) {
|
||||
if ($lock->token === $lockInfo->token) $current = $k;
|
||||
}
|
||||
if (!is_null($current)) $resourceData['locks'][$current] = $lockInfo;
|
||||
else $resourceData['locks'][] = $lockInfo;
|
||||
|
||||
$this->putResourceData($resourceData);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a lock from this node
|
||||
*
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
function unlock(Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
//throw new Sabre_DAV_Exception('bla');
|
||||
$resourceData = $this->getResourceData();
|
||||
foreach($resourceData['locks'] as $k=>$lock) {
|
||||
|
||||
if ($lock->token === $lockInfo->token) {
|
||||
|
||||
unset($resourceData['locks'][$k]);
|
||||
$this->putResourceData($resourceData);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties on this node,
|
||||
*
|
||||
* @param array $mutations
|
||||
* @see Sabre_DAV_IProperties::updateProperties
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateProperties($properties) {
|
||||
|
||||
$resourceData = $this->getResourceData();
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach($properties as $propertyName=>$propertyValue) {
|
||||
|
||||
// If it was null, we need to delete the property
|
||||
if (is_null($propertyValue)) {
|
||||
if (isset($resourceData['properties'][$propertyName])) {
|
||||
unset($resourceData['properties'][$propertyName]);
|
||||
}
|
||||
} else {
|
||||
$resourceData['properties'][$propertyName] = $propertyValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->putResourceData($resourceData);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of properties for this nodes.;
|
||||
*
|
||||
* The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author
|
||||
* If the array is empty, all properties should be returned
|
||||
*
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
function getProperties($properties) {
|
||||
|
||||
$resourceData = $this->getResourceData();
|
||||
|
||||
// if the array was empty, we need to return everything
|
||||
if (!$properties) return $resourceData['properties'];
|
||||
|
||||
$props = array();
|
||||
foreach($properties as $property) {
|
||||
if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property];
|
||||
}
|
||||
|
||||
return $props;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the resource file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getResourceInfoPath() {
|
||||
|
||||
list($parentDir) = Sabre_DAV_URLUtil::splitPath($this->path);
|
||||
return $parentDir . '/.sabredav';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the stored resource information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getResourceData() {
|
||||
|
||||
$path = $this->getResourceInfoPath();
|
||||
if (!file_exists($path)) return array('locks'=>array(), 'properties' => array());
|
||||
|
||||
// opening up the file, and creating a shared lock
|
||||
$handle = fopen($path,'r');
|
||||
flock($handle,LOCK_SH);
|
||||
$data = '';
|
||||
|
||||
// Reading data until the eof
|
||||
while(!feof($handle)) {
|
||||
$data.=fread($handle,8192);
|
||||
}
|
||||
|
||||
// We're all good
|
||||
fclose($handle);
|
||||
|
||||
// Unserializing and checking if the resource file contains data for this file
|
||||
$data = unserialize($data);
|
||||
if (!isset($data[$this->getName()])) {
|
||||
return array('locks'=>array(), 'properties' => array());
|
||||
}
|
||||
|
||||
$data = $data[$this->getName()];
|
||||
if (!isset($data['locks'])) $data['locks'] = array();
|
||||
if (!isset($data['properties'])) $data['properties'] = array();
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the resource information
|
||||
*
|
||||
* @param array $newData
|
||||
* @return void
|
||||
*/
|
||||
protected function putResourceData(array $newData) {
|
||||
|
||||
$path = $this->getResourceInfoPath();
|
||||
|
||||
// opening up the file, and creating a shared lock
|
||||
$handle = fopen($path,'a+');
|
||||
flock($handle,LOCK_EX);
|
||||
$data = '';
|
||||
|
||||
rewind($handle);
|
||||
|
||||
// Reading data until the eof
|
||||
while(!feof($handle)) {
|
||||
$data.=fread($handle,8192);
|
||||
}
|
||||
|
||||
// Unserializing and checking if the resource file contains data for this file
|
||||
$data = unserialize($data);
|
||||
$data[$this->getName()] = $newData;
|
||||
ftruncate($handle,0);
|
||||
rewind($handle);
|
||||
|
||||
fwrite($handle,serialize($data));
|
||||
fclose($handle);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node
|
||||
*
|
||||
* @param string $name The new name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
|
||||
list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
|
||||
$newPath = $parentPath . '/' . $newName;
|
||||
|
||||
// We're deleting the existing resourcedata, and recreating it
|
||||
// for the new path.
|
||||
$resourceData = $this->getResourceData();
|
||||
$this->deleteResourceData();
|
||||
|
||||
rename($this->path,$newPath);
|
||||
$this->path = $newPath;
|
||||
$this->putResourceData($resourceData);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function deleteResourceData() {
|
||||
|
||||
// When we're deleting this node, we also need to delete any resource information
|
||||
$path = $this->getResourceInfoPath();
|
||||
if (!file_exists($path)) return true;
|
||||
|
||||
// opening up the file, and creating a shared lock
|
||||
$handle = fopen($path,'a+');
|
||||
flock($handle,LOCK_EX);
|
||||
$data = '';
|
||||
|
||||
rewind($handle);
|
||||
|
||||
// Reading data until the eof
|
||||
while(!feof($handle)) {
|
||||
$data.=fread($handle,8192);
|
||||
}
|
||||
|
||||
// Unserializing and checking if the resource file contains data for this file
|
||||
$data = unserialize($data);
|
||||
if (isset($data[$this->getName()])) unset($data[$this->getName()]);
|
||||
ftruncate($handle,0);
|
||||
rewind($handle);
|
||||
fwrite($handle,serialize($data));
|
||||
fclose($handle);
|
||||
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
|
||||
return $this->deleteResourceData();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* File class
|
||||
*
|
||||
* This is a helper class, that should aid in getting file classes setup.
|
||||
* Most of its methods are implemented, and throw permission denied exceptions
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_File extends Sabre_DAV_Node implements Sabre_DAV_IFile {
|
||||
|
||||
/**
|
||||
* Updates the data
|
||||
*
|
||||
* data is a readable stream resource.
|
||||
*
|
||||
* @param resource $data
|
||||
* @return void
|
||||
*/
|
||||
public function put($data) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to change data');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data
|
||||
*
|
||||
* This method may either return a string or a readable stream resource
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get() {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to read this file');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the file, in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize() {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
* The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined
|
||||
*/
|
||||
public function getETag() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*/
|
||||
public function getContentType() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The ICollection Interface
|
||||
*
|
||||
* This interface should be implemented by each class that represents a collection
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_ICollection extends Sabre_DAV_INode {
|
||||
|
||||
/**
|
||||
* Creates a new file in the directory
|
||||
*
|
||||
* data is a readable stream resource
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource $data Initial payload
|
||||
* @return void
|
||||
*/
|
||||
function createFile($name, $data = null);
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
function createDirectory($name);
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its name
|
||||
*
|
||||
* @param string $name
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
function getChild($name);
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes
|
||||
*
|
||||
* @return Sabre_DAV_INode[]
|
||||
*/
|
||||
function getChildren();
|
||||
|
||||
/**
|
||||
* Checks if a child-node with the specified name exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function childExists($name);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The IExtendedCollection interface.
|
||||
*
|
||||
* This interface can be used to create special-type of collection-resources
|
||||
* as defined by RFC 5689.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_IExtendedCollection extends Sabre_DAV_ICollection {
|
||||
|
||||
/**
|
||||
* Creates a new collection
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $resourceType
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
function createExtendedCollection($name, array $resourceType, array $properties);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This interface represents a file or leaf in the tree.
|
||||
*
|
||||
* The nature of a file is, as you might be aware of, that it doesn't contain sub-nodes and has contents
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_IFile extends Sabre_DAV_INode {
|
||||
|
||||
/**
|
||||
* Updates the data
|
||||
*
|
||||
* The data argument is a readable stream resource.
|
||||
*
|
||||
* @param resource $data
|
||||
* @return void
|
||||
*/
|
||||
function put($data);
|
||||
|
||||
/**
|
||||
* Returns the data
|
||||
*
|
||||
* This method may either return a string or a readable stream resource
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function get();
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function getContentType();
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function getETag();
|
||||
|
||||
/**
|
||||
* Returns the size of the node, in bytes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function getSize();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Implement this class to support locking
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_ILockable extends Sabre_DAV_INode {
|
||||
|
||||
/**
|
||||
* Returns an array with locks currently on the node
|
||||
*
|
||||
* @return Sabre_DAV_Locks_LockInfo[]
|
||||
*/
|
||||
function getLocks();
|
||||
|
||||
/**
|
||||
* Creates a new lock on the file.
|
||||
*
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo The lock information
|
||||
* @return void
|
||||
*/
|
||||
function lock(Sabre_DAV_Locks_LockInfo $lockInfo);
|
||||
|
||||
/**
|
||||
* Unlocks a file
|
||||
*
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo The lock information
|
||||
* @return void
|
||||
*/
|
||||
function unlock(Sabre_DAV_Locks_LockInfo $lockInfo);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The INode interface is the base interface, and the parent class of both ICollection and IFile
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_INode {
|
||||
|
||||
/**
|
||||
* Deleted the current node
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function delete();
|
||||
|
||||
/**
|
||||
* Returns the name of the node
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getName();
|
||||
|
||||
/**
|
||||
* Renames the node
|
||||
*
|
||||
* @param string $name The new name
|
||||
* @return void
|
||||
*/
|
||||
function setName($name);
|
||||
|
||||
/**
|
||||
* Returns the last modification time, as a unix timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function getLastModified();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* IProperties interface
|
||||
*
|
||||
* Implement this interface to support custom WebDAV properties requested and sent from clients.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_IProperties extends Sabre_DAV_INode {
|
||||
|
||||
/**
|
||||
* Updates properties on this node,
|
||||
*
|
||||
* The properties array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existant property is always succesful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param array $mutations
|
||||
* @return bool|array
|
||||
*/
|
||||
function updateProperties($mutations);
|
||||
|
||||
/**
|
||||
* Returns a list of properties for this nodes.
|
||||
*
|
||||
* The properties list is a list of propertynames the client requested,
|
||||
* encoded in clark-notation {xmlnamespace}tagname
|
||||
*
|
||||
* If the array is empty, it means 'all properties' were requested.
|
||||
*
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
function getProperties($properties);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* IQuota interface
|
||||
*
|
||||
* Implement this interface to add the ability to return quota information. The ObjectTree
|
||||
* will check for quota information on any given node. If the information is not available it will
|
||||
* attempt to fetch the information from the root node.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_IQuota extends Sabre_DAV_ICollection {
|
||||
|
||||
/**
|
||||
* Returns the quota information
|
||||
*
|
||||
* This method MUST return an array with 2 values, the first being the total used space,
|
||||
* the second the available space (in bytes)
|
||||
*/
|
||||
function getQuotaInfo();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The Lock manager allows you to handle all file-locks centrally.
|
||||
*
|
||||
* This is an alternative approach to doing this on a per-node basis
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_Locks_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* Returns a list of Sabre_DAV_Locks_LockInfo objects
|
||||
*
|
||||
* This method should return all the locks for a particular uri, including
|
||||
* locks that might be set on a parent uri.
|
||||
*
|
||||
* If returnChildLocks is set to true, this method should also look for
|
||||
* any locks in the subtree of the uri for locks.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
* @return array
|
||||
*/
|
||||
abstract function getLocks($uri, $returnChildLocks);
|
||||
|
||||
/**
|
||||
* Locks a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
abstract function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo);
|
||||
|
||||
/**
|
||||
* Removes a lock from a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
abstract function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The Lock manager allows you to handle all file-locks centrally.
|
||||
*
|
||||
* This Lock Manager is now deprecated. It has a bug that allows parent
|
||||
* collections to be deletes when children deeper in the tree are locked.
|
||||
*
|
||||
* You are recommended to use either the PDO or the File backend instead.
|
||||
*
|
||||
* This Lock Manager stores all its data in the filesystem.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @deprecated
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Locks_Backend_FS extends Sabre_DAV_Locks_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* The default data directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $dataDir;
|
||||
|
||||
public function __construct($dataDir) {
|
||||
|
||||
$this->dataDir = $dataDir;
|
||||
|
||||
}
|
||||
|
||||
protected function getFileNameForUri($uri) {
|
||||
|
||||
return $this->dataDir . '/sabredav_' . md5($uri) . '.locks';
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of Sabre_DAV_Locks_LockInfo objects
|
||||
*
|
||||
* This method should return all the locks for a particular uri, including
|
||||
* locks that might be set on a parent uri.
|
||||
*
|
||||
* If returnChildLocks is set to true, this method should also look for
|
||||
* any locks in the subtree of the uri for locks.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
* @return array
|
||||
*/
|
||||
public function getLocks($uri, $returnChildLocks) {
|
||||
|
||||
$lockList = array();
|
||||
$currentPath = '';
|
||||
|
||||
foreach(explode('/',$uri) as $uriPart) {
|
||||
|
||||
// weird algorithm that can probably be improved, but we're traversing the path top down
|
||||
if ($currentPath) $currentPath.='/';
|
||||
$currentPath.=$uriPart;
|
||||
|
||||
$uriLocks = $this->getData($currentPath);
|
||||
|
||||
foreach($uriLocks as $uriLock) {
|
||||
|
||||
// Unless we're on the leaf of the uri-tree we should ingore locks with depth 0
|
||||
if($uri==$currentPath || $uriLock->depth!=0) {
|
||||
$uriLock->uri = $currentPath;
|
||||
$lockList[] = $uriLock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Checking if we can remove any of these locks
|
||||
foreach($lockList as $k=>$lock) {
|
||||
if (time() > $lock->timeout + $lock->created) unset($lockList[$k]);
|
||||
}
|
||||
return $lockList;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
// We're making the lock timeout 30 minutes
|
||||
$lockInfo->timeout = 1800;
|
||||
$lockInfo->created = time();
|
||||
|
||||
$locks = $this->getLocks($uri,false);
|
||||
foreach($locks as $k=>$lock) {
|
||||
if ($lock->token == $lockInfo->token) unset($locks[$k]);
|
||||
}
|
||||
$locks[] = $lockInfo;
|
||||
$this->putData($uri,$locks);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a lock from a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
$locks = $this->getLocks($uri,false);
|
||||
foreach($locks as $k=>$lock) {
|
||||
|
||||
if ($lock->token == $lockInfo->token) {
|
||||
|
||||
unset($locks[$k]);
|
||||
$this->putData($uri,$locks);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored data for a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @return array
|
||||
*/
|
||||
protected function getData($uri) {
|
||||
|
||||
$path = $this->getFilenameForUri($uri);
|
||||
if (!file_exists($path)) return array();
|
||||
|
||||
// opening up the file, and creating a shared lock
|
||||
$handle = fopen($path,'r');
|
||||
flock($handle,LOCK_SH);
|
||||
$data = '';
|
||||
|
||||
// Reading data until the eof
|
||||
while(!feof($handle)) {
|
||||
$data.=fread($handle,8192);
|
||||
}
|
||||
|
||||
// We're all good
|
||||
fclose($handle);
|
||||
|
||||
// Unserializing and checking if the resource file contains data for this file
|
||||
$data = unserialize($data);
|
||||
if (!$data) return array();
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the lock information
|
||||
*
|
||||
* @param string $uri
|
||||
* @param array $newData
|
||||
* @return void
|
||||
*/
|
||||
protected function putData($uri,array $newData) {
|
||||
|
||||
$path = $this->getFileNameForUri($uri);
|
||||
|
||||
// opening up the file, and creating a shared lock
|
||||
$handle = fopen($path,'a+');
|
||||
flock($handle,LOCK_EX);
|
||||
ftruncate($handle,0);
|
||||
rewind($handle);
|
||||
|
||||
fwrite($handle,serialize($newData));
|
||||
fclose($handle);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The Lock manager allows you to handle all file-locks centrally.
|
||||
*
|
||||
* This Lock Manager stores all its data in a single file.
|
||||
*
|
||||
* Note that this is not nearly as robust as a database, you are encouraged
|
||||
* to use the PDO backend instead.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* The storage file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $locksFile;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $locksFile path to file
|
||||
*/
|
||||
public function __construct($locksFile) {
|
||||
|
||||
$this->locksFile = $locksFile;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of Sabre_DAV_Locks_LockInfo objects
|
||||
*
|
||||
* This method should return all the locks for a particular uri, including
|
||||
* locks that might be set on a parent uri.
|
||||
*
|
||||
* If returnChildLocks is set to true, this method should also look for
|
||||
* any locks in the subtree of the uri for locks.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
* @return array
|
||||
*/
|
||||
public function getLocks($uri, $returnChildLocks) {
|
||||
|
||||
$newLocks = array();
|
||||
$currentPath = '';
|
||||
|
||||
$locks = $this->getData();
|
||||
foreach($locks as $lock) {
|
||||
|
||||
if ($lock->uri === $uri ||
|
||||
//deep locks on parents
|
||||
($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) ||
|
||||
|
||||
// locks on children
|
||||
($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) {
|
||||
|
||||
$newLocks[] = $lock;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Checking if we can remove any of these locks
|
||||
foreach($newLocks as $k=>$lock) {
|
||||
if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]);
|
||||
}
|
||||
return $newLocks;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
// We're making the lock timeout 30 minutes
|
||||
$lockInfo->timeout = 1800;
|
||||
$lockInfo->created = time();
|
||||
$lockInfo->uri = $uri;
|
||||
|
||||
$locks = $this->getLocks($uri,false);
|
||||
foreach($locks as $k=>$lock) {
|
||||
if ($lock->token == $lockInfo->token) unset($locks[$k]);
|
||||
}
|
||||
$locks[] = $lockInfo;
|
||||
$this->putData($locks);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a lock from a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
$locks = $this->getLocks($uri,false);
|
||||
foreach($locks as $k=>$lock) {
|
||||
|
||||
if ($lock->token == $lockInfo->token) {
|
||||
|
||||
unset($locks[$k]);
|
||||
$this->putData($locks);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the lockdata from the filesystem.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getData() {
|
||||
|
||||
if (!file_exists($this->locksFile)) return array();
|
||||
|
||||
// opening up the file, and creating a shared lock
|
||||
$handle = fopen($this->locksFile,'r');
|
||||
flock($handle,LOCK_SH);
|
||||
|
||||
// Reading data until the eof
|
||||
$data = stream_get_contents($handle);
|
||||
|
||||
// We're all good
|
||||
fclose($handle);
|
||||
|
||||
// Unserializing and checking if the resource file contains data for this file
|
||||
$data = unserialize($data);
|
||||
if (!$data) return array();
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the lockdata
|
||||
*
|
||||
* @param array $newData
|
||||
* @return void
|
||||
*/
|
||||
protected function putData(array $newData) {
|
||||
|
||||
// opening up the file, and creating an exclusive lock
|
||||
$handle = fopen($this->locksFile,'a+');
|
||||
flock($handle,LOCK_EX);
|
||||
|
||||
// We can only truncate and rewind once the lock is acquired.
|
||||
ftruncate($handle,0);
|
||||
rewind($handle);
|
||||
|
||||
fwrite($handle,serialize($newData));
|
||||
fclose($handle);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The Lock manager allows you to handle all file-locks centrally.
|
||||
*
|
||||
* This Lock Manager stores all its data in a database. You must pass a PDO
|
||||
* connection object in the constructor.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Locks_Backend_PDO extends Sabre_DAV_Locks_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* The PDO connection object
|
||||
*
|
||||
* @var pdo
|
||||
*/
|
||||
private $pdo;
|
||||
|
||||
/**
|
||||
* The PDO tablename this backend uses.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableName;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param PDO $pdo
|
||||
* @param string $tableName
|
||||
*/
|
||||
public function __construct(PDO $pdo, $tableName = 'locks') {
|
||||
|
||||
$this->pdo = $pdo;
|
||||
$this->tableName = $tableName;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of Sabre_DAV_Locks_LockInfo objects
|
||||
*
|
||||
* This method should return all the locks for a particular uri, including
|
||||
* locks that might be set on a parent uri.
|
||||
*
|
||||
* If returnChildLocks is set to true, this method should also look for
|
||||
* any locks in the subtree of the uri for locks.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
* @return array
|
||||
*/
|
||||
public function getLocks($uri, $returnChildLocks) {
|
||||
|
||||
// NOTE: the following 10 lines or so could be easily replaced by
|
||||
// pure sql. MySQL's non-standard string concatination prevents us
|
||||
// from doing this though.
|
||||
$query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM `'.$this->tableName.'` WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)';
|
||||
$params = array(time(),$uri);
|
||||
|
||||
// We need to check locks for every part in the uri.
|
||||
$uriParts = explode('/',$uri);
|
||||
|
||||
// We already covered the last part of the uri
|
||||
array_pop($uriParts);
|
||||
|
||||
$currentPath='';
|
||||
|
||||
foreach($uriParts as $part) {
|
||||
|
||||
if ($currentPath) $currentPath.='/';
|
||||
$currentPath.=$part;
|
||||
|
||||
$query.=' OR (depth!=0 AND uri = ?)';
|
||||
$params[] = $currentPath;
|
||||
|
||||
}
|
||||
|
||||
if ($returnChildLocks) {
|
||||
|
||||
$query.=' OR (uri LIKE ?)';
|
||||
$params[] = $uri . '/%';
|
||||
|
||||
}
|
||||
$query.=')';
|
||||
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$result = $stmt->fetchAll();
|
||||
|
||||
$lockList = array();
|
||||
foreach($result as $row) {
|
||||
|
||||
$lockInfo = new Sabre_DAV_Locks_LockInfo();
|
||||
$lockInfo->owner = $row['owner'];
|
||||
$lockInfo->token = $row['token'];
|
||||
$lockInfo->timeout = $row['timeout'];
|
||||
$lockInfo->created = $row['created'];
|
||||
$lockInfo->scope = $row['scope'];
|
||||
$lockInfo->depth = $row['depth'];
|
||||
$lockInfo->uri = $row['uri'];
|
||||
$lockList[] = $lockInfo;
|
||||
|
||||
}
|
||||
|
||||
return $lockList;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
// We're making the lock timeout 30 minutes
|
||||
$lockInfo->timeout = 30*60;
|
||||
$lockInfo->created = time();
|
||||
$lockInfo->uri = $uri;
|
||||
|
||||
$locks = $this->getLocks($uri,false);
|
||||
$exists = false;
|
||||
foreach($locks as $k=>$lock) {
|
||||
if ($lock->token == $lockInfo->token) $exists = true;
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
$stmt = $this->pdo->prepare('UPDATE `'.$this->tableName.'` SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?');
|
||||
$stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
|
||||
} else {
|
||||
$stmt = $this->pdo->prepare('INSERT INTO `'.$this->tableName.'` (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)');
|
||||
$stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes a lock from a uri
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM `'.$this->tableName.'` WHERE uri = ? AND token = ?');
|
||||
$stmt->execute(array($uri,$lockInfo->token));
|
||||
|
||||
return $stmt->rowCount()===1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* LockInfo class
|
||||
*
|
||||
* An object of the LockInfo class holds all the information relevant to a
|
||||
* single lock.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Locks_LockInfo {
|
||||
|
||||
/**
|
||||
* A shared lock
|
||||
*/
|
||||
const SHARED = 1;
|
||||
|
||||
/**
|
||||
* An exclusive lock
|
||||
*/
|
||||
const EXCLUSIVE = 2;
|
||||
|
||||
/**
|
||||
* A never expiring timeout
|
||||
*/
|
||||
const TIMEOUT_INFINITE = -1;
|
||||
|
||||
/**
|
||||
* The owner of the lock
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $owner;
|
||||
|
||||
/**
|
||||
* The locktoken
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $token;
|
||||
|
||||
/**
|
||||
* How long till the lock is expiring
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $timeout;
|
||||
|
||||
/**
|
||||
* UNIX Timestamp of when this lock was created
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $created;
|
||||
|
||||
/**
|
||||
* Exclusive or shared lock
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $scope = self::EXCLUSIVE;
|
||||
|
||||
/**
|
||||
* Depth of lock, can be 0 or Sabre_DAV_Server::DEPTH_INFINITY
|
||||
*/
|
||||
public $depth = 0;
|
||||
|
||||
/**
|
||||
* The uri this lock locks
|
||||
*
|
||||
* TODO: This value is not always set
|
||||
* @var mixed
|
||||
*/
|
||||
public $uri;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,680 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Locking plugin
|
||||
*
|
||||
* This plugin provides locking support to a WebDAV server.
|
||||
* The easiest way to get started, is by hooking it up as such:
|
||||
*
|
||||
* $lockBackend = new Sabre_DAV_Locks_Backend_File('./mylockdb');
|
||||
* $lockPlugin = new Sabre_DAV_Locks_Plugin($lockBackend);
|
||||
* $server->addPlugin($lockPlugin);
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* locksBackend
|
||||
*
|
||||
* @var Sabre_DAV_Locks_Backend_Abstract
|
||||
*/
|
||||
private $locksBackend;
|
||||
|
||||
/**
|
||||
* server
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param Sabre_DAV_Locks_Backend_Abstract $locksBackend
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Sabre_DAV_Locks_Backend_Abstract $locksBackend = null) {
|
||||
|
||||
$this->locksBackend = $locksBackend;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin
|
||||
*
|
||||
* This method is automatically called by the Server class after addPlugin.
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
$server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
|
||||
$server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50);
|
||||
$server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using Sabre_DAV_Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName() {
|
||||
|
||||
return 'locks';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by the Server if the user used an HTTP method
|
||||
* the server didn't recognize.
|
||||
*
|
||||
* This plugin intercepts the LOCK and UNLOCK methods.
|
||||
*
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function unknownMethod($method, $uri) {
|
||||
|
||||
switch($method) {
|
||||
|
||||
case 'LOCK' : $this->httpLock($uri); return false;
|
||||
case 'UNLOCK' : $this->httpUnlock($uri); return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after most properties have been found
|
||||
* it allows us to add in any Lock-related properties
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $properties
|
||||
* @return bool
|
||||
*/
|
||||
public function afterGetProperties($path,&$newProperties) {
|
||||
|
||||
foreach($newProperties[404] as $propName=>$discard) {
|
||||
|
||||
$node = null;
|
||||
|
||||
switch($propName) {
|
||||
|
||||
case '{DAV:}supportedlock' :
|
||||
$val = false;
|
||||
if ($this->locksBackend) $val = true;
|
||||
else {
|
||||
if (!$node) $node = $this->server->tree->getNodeForPath($path);
|
||||
if ($node instanceof Sabre_DAV_ILockable) $val = true;
|
||||
}
|
||||
$newProperties[200][$propName] = new Sabre_DAV_Property_SupportedLock($val);
|
||||
unset($newProperties[404][$propName]);
|
||||
break;
|
||||
|
||||
case '{DAV:}lockdiscovery' :
|
||||
$newProperties[200][$propName] = new Sabre_DAV_Property_LockDiscovery($this->getLocks($path));
|
||||
unset($newProperties[404][$propName]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is called before the logic for any HTTP method is
|
||||
* handled.
|
||||
*
|
||||
* This plugin uses that feature to intercept access to locked resources.
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $uri
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeMethod($method, $uri) {
|
||||
|
||||
switch($method) {
|
||||
|
||||
case 'DELETE' :
|
||||
$lastLock = null;
|
||||
if (!$this->validateLock($uri,$lastLock, true))
|
||||
throw new Sabre_DAV_Exception_Locked($lastLock);
|
||||
break;
|
||||
case 'MKCOL' :
|
||||
case 'PROPPATCH' :
|
||||
case 'PUT' :
|
||||
$lastLock = null;
|
||||
if (!$this->validateLock($uri,$lastLock))
|
||||
throw new Sabre_DAV_Exception_Locked($lastLock);
|
||||
break;
|
||||
case 'MOVE' :
|
||||
$lastLock = null;
|
||||
if (!$this->validateLock(array(
|
||||
$uri,
|
||||
$this->server->calculateUri($this->server->httpRequest->getHeader('Destination')),
|
||||
),$lastLock, true))
|
||||
throw new Sabre_DAV_Exception_Locked($lastLock);
|
||||
break;
|
||||
case 'COPY' :
|
||||
$lastLock = null;
|
||||
if (!$this->validateLock(
|
||||
$this->server->calculateUri($this->server->httpRequest->getHeader('Destination')),
|
||||
$lastLock, true))
|
||||
throw new Sabre_DAV_Exception_Locked($lastLock);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to tell the server this plugin defines additional
|
||||
* HTTP methods.
|
||||
*
|
||||
* This method is passed a uri. It should only return HTTP methods that are
|
||||
* available for the specified uri.
|
||||
*
|
||||
* @param string $uri
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPMethods($uri) {
|
||||
|
||||
if ($this->locksBackend ||
|
||||
$this->server->tree->getNodeForPath($uri) instanceof Sabre_DAV_ILocks) {
|
||||
return array('LOCK','UNLOCK');
|
||||
}
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of features for the HTTP OPTIONS Dav: header.
|
||||
*
|
||||
* In this case this is only the number 2. The 2 in the Dav: header
|
||||
* indicates the server supports locks.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeatures() {
|
||||
|
||||
return array(2);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all lock information on a particular uri
|
||||
*
|
||||
* This function should return an array with Sabre_DAV_Locks_LockInfo objects. If there are no locks on a file, return an empty array.
|
||||
*
|
||||
* Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree
|
||||
* If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object
|
||||
* for any possible locks and return those as well.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
* @return array
|
||||
*/
|
||||
public function getLocks($uri, $returnChildLocks = false) {
|
||||
|
||||
$lockList = array();
|
||||
$currentPath = '';
|
||||
foreach(explode('/',$uri) as $uriPart) {
|
||||
|
||||
$uriLocks = array();
|
||||
if ($currentPath) $currentPath.='/';
|
||||
$currentPath.=$uriPart;
|
||||
|
||||
try {
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($currentPath);
|
||||
if ($node instanceof Sabre_DAV_ILockable) $uriLocks = $node->getLocks();
|
||||
|
||||
} catch (Sabre_DAV_Exception_FileNotFound $e){
|
||||
// In case the node didn't exist, this could be a lock-null request
|
||||
}
|
||||
|
||||
foreach($uriLocks as $uriLock) {
|
||||
|
||||
// Unless we're on the leaf of the uri-tree we should ignore locks with depth 0
|
||||
if($uri==$currentPath || $uriLock->depth!=0) {
|
||||
$uriLock->uri = $currentPath;
|
||||
$lockList[] = $uriLock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if ($this->locksBackend)
|
||||
$lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks));
|
||||
|
||||
return $lockList;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks an uri
|
||||
*
|
||||
* The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock
|
||||
* If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type
|
||||
* of lock (shared or exclusive) and the owner of the lock
|
||||
*
|
||||
* If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock
|
||||
*
|
||||
* Additionally, a lock can be requested for a non-existant file. In these case we're obligated to create an empty file as per RFC4918:S7.3
|
||||
*
|
||||
* @param string $uri
|
||||
* @return void
|
||||
*/
|
||||
protected function httpLock($uri) {
|
||||
|
||||
$lastLock = null;
|
||||
if (!$this->validateLock($uri,$lastLock)) {
|
||||
|
||||
// If the existing lock was an exclusive lock, we need to fail
|
||||
if (!$lastLock || $lastLock->scope == Sabre_DAV_Locks_LockInfo::EXCLUSIVE) {
|
||||
//var_dump($lastLock);
|
||||
throw new Sabre_DAV_Exception_ConflictingLock($lastLock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($body = $this->server->httpRequest->getBody(true)) {
|
||||
// This is a new lock request
|
||||
$lockInfo = $this->parseLockRequest($body);
|
||||
$lockInfo->depth = $this->server->getHTTPDepth();
|
||||
$lockInfo->uri = $uri;
|
||||
if($lastLock && $lockInfo->scope != Sabre_DAV_Locks_LockInfo::SHARED) throw new Sabre_DAV_Exception_ConflictingLock($lastLock);
|
||||
|
||||
} elseif ($lastLock) {
|
||||
|
||||
// This must have been a lock refresh
|
||||
$lockInfo = $lastLock;
|
||||
|
||||
// The resource could have been locked through another uri.
|
||||
if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri;
|
||||
|
||||
} else {
|
||||
|
||||
// There was neither a lock refresh nor a new lock request
|
||||
throw new Sabre_DAV_Exception_BadRequest('An xml body is required for lock requests');
|
||||
|
||||
}
|
||||
|
||||
if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout;
|
||||
|
||||
$newFile = false;
|
||||
|
||||
// If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first
|
||||
try {
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
|
||||
// We need to call the beforeWriteContent event for RFC3744
|
||||
$this->server->broadcastEvent('beforeWriteContent',array($uri));
|
||||
|
||||
} catch (Sabre_DAV_Exception_FileNotFound $e) {
|
||||
|
||||
// It didn't, lets create it
|
||||
$this->server->createFile($uri,fopen('php://memory','r'));
|
||||
$newFile = true;
|
||||
|
||||
}
|
||||
|
||||
$this->lockNode($uri,$lockInfo);
|
||||
|
||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||
$this->server->httpResponse->setHeader('Lock-Token','<opaquelocktoken:' . $lockInfo->token . '>');
|
||||
$this->server->httpResponse->sendStatus($newFile?201:200);
|
||||
$this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks a uri
|
||||
*
|
||||
* This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header
|
||||
* The server should return 204 (No content) on success
|
||||
*
|
||||
* @param string $uri
|
||||
* @return void
|
||||
*/
|
||||
protected function httpUnlock($uri) {
|
||||
|
||||
$lockToken = $this->server->httpRequest->getHeader('Lock-Token');
|
||||
|
||||
// If the locktoken header is not supplied, we need to throw a bad request exception
|
||||
if (!$lockToken) throw new Sabre_DAV_Exception_BadRequest('No lock token was supplied');
|
||||
|
||||
$locks = $this->getLocks($uri);
|
||||
|
||||
// Windows sometimes forgets to include < and > in the Lock-Token
|
||||
// header
|
||||
if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>';
|
||||
|
||||
foreach($locks as $lock) {
|
||||
|
||||
if ('<opaquelocktoken:' . $lock->token . '>' == $lockToken) {
|
||||
|
||||
$this->server->broadcastEvent('beforeUnlock',array($uri, $lock));
|
||||
$this->unlockNode($uri,$lock);
|
||||
$this->server->httpResponse->setHeader('Content-Length','0');
|
||||
$this->server->httpResponse->sendStatus(204);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If we got here, it means the locktoken was invalid
|
||||
throw new Sabre_DAV_Exception_LockTokenMatchesRequestUri();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a uri
|
||||
*
|
||||
* All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored
|
||||
* It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return void
|
||||
*/
|
||||
public function lockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return;
|
||||
|
||||
try {
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
if ($node instanceof Sabre_DAV_ILockable) return $node->lock($lockInfo);
|
||||
} catch (Sabre_DAV_Exception_FileNotFound $e) {
|
||||
// In case the node didn't exist, this could be a lock-null request
|
||||
}
|
||||
if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo);
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks a uri
|
||||
*
|
||||
* This method removes a lock from a uri. It is assumed all the supplied information is correct and verified
|
||||
*
|
||||
* @param string $uri
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return void
|
||||
*/
|
||||
public function unlockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return;
|
||||
try {
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
if ($node instanceof Sabre_DAV_ILockable) return $node->unlock($lockInfo);
|
||||
} catch (Sabre_DAV_Exception_FileNotFound $e) {
|
||||
// In case the node didn't exist, this could be a lock-null request
|
||||
}
|
||||
|
||||
if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the contents of the HTTP Timeout header.
|
||||
*
|
||||
* The method formats the header into an integer.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeoutHeader() {
|
||||
|
||||
$header = $this->server->httpRequest->getHeader('Timeout');
|
||||
|
||||
if ($header) {
|
||||
|
||||
if (stripos($header,'second-')===0) $header = (int)(substr($header,7));
|
||||
else if (strtolower($header)=='infinite') $header=Sabre_DAV_Locks_LockInfo::TIMEOUT_INFINITE;
|
||||
else throw new Sabre_DAV_Exception_BadRequest('Invalid HTTP timeout header');
|
||||
|
||||
} else {
|
||||
|
||||
$header = 0;
|
||||
|
||||
}
|
||||
|
||||
return $header;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the response for successfull LOCK requests
|
||||
*
|
||||
* @param Sabre_DAV_Locks_LockInfo $lockInfo
|
||||
* @return string
|
||||
*/
|
||||
protected function generateLockResponse(Sabre_DAV_Locks_LockInfo $lockInfo) {
|
||||
|
||||
$dom = new DOMDocument('1.0','utf-8');
|
||||
$dom->formatOutput = true;
|
||||
|
||||
$prop = $dom->createElementNS('DAV:','d:prop');
|
||||
$dom->appendChild($prop);
|
||||
|
||||
$lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery');
|
||||
$prop->appendChild($lockDiscovery);
|
||||
|
||||
$lockObj = new Sabre_DAV_Property_LockDiscovery(array($lockInfo),true);
|
||||
$lockObj->serialize($this->server,$lockDiscovery);
|
||||
|
||||
return $dom->saveXML();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* validateLock should be called when a write operation is about to happen
|
||||
* It will check if the requested url is locked, and see if the correct lock tokens are passed
|
||||
*
|
||||
* @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri
|
||||
* @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre_DAV_Locks_LockInfo)
|
||||
* @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees.
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateLock($urls = null,&$lastLock = null, $checkChildLocks = false) {
|
||||
|
||||
if (is_null($urls)) {
|
||||
$urls = array($this->server->getRequestUri());
|
||||
} elseif (is_string($urls)) {
|
||||
$urls = array($urls);
|
||||
} elseif (!is_array($urls)) {
|
||||
throw new Sabre_DAV_Exception('The urls parameter should either be null, a string or an array');
|
||||
}
|
||||
|
||||
$conditions = $this->getIfConditions();
|
||||
|
||||
// We're going to loop through the urls and make sure all lock conditions are satisfied
|
||||
foreach($urls as $url) {
|
||||
|
||||
$locks = $this->getLocks($url, $checkChildLocks);
|
||||
|
||||
// If there were no conditions, but there were locks, we fail
|
||||
if (!$conditions && $locks) {
|
||||
reset($locks);
|
||||
$lastLock = current($locks);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there were no locks or conditions, we go to the next url
|
||||
if (!$locks && !$conditions) continue;
|
||||
|
||||
foreach($conditions as $condition) {
|
||||
|
||||
if (!$condition['uri']) {
|
||||
$conditionUri = $this->server->getRequestUri();
|
||||
} else {
|
||||
$conditionUri = $this->server->calculateUri($condition['uri']);
|
||||
}
|
||||
|
||||
// If the condition has a url, and it isn't part of the affected url at all, check the next condition
|
||||
if ($conditionUri && strpos($url,$conditionUri)!==0) continue;
|
||||
|
||||
// The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken
|
||||
// At least 1 condition has to be satisfied
|
||||
foreach($condition['tokens'] as $conditionToken) {
|
||||
|
||||
$etagValid = true;
|
||||
$lockValid = true;
|
||||
|
||||
// key 2 can contain an etag
|
||||
if ($conditionToken[2]) {
|
||||
|
||||
$uri = $conditionUri?$conditionUri:$this->server->getRequestUri();
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
$etagValid = $node->getETag()==$conditionToken[2];
|
||||
|
||||
}
|
||||
|
||||
// key 1 can contain a lock token
|
||||
if ($conditionToken[1]) {
|
||||
|
||||
$lockValid = false;
|
||||
// Match all the locks
|
||||
foreach($locks as $lockIndex=>$lock) {
|
||||
|
||||
$lockToken = 'opaquelocktoken:' . $lock->token;
|
||||
|
||||
// Checking NOT
|
||||
if (!$conditionToken[0] && $lockToken != $conditionToken[1]) {
|
||||
|
||||
// Condition valid, onto the next
|
||||
$lockValid = true;
|
||||
break;
|
||||
}
|
||||
if ($conditionToken[0] && $lockToken == $conditionToken[1]) {
|
||||
|
||||
$lastLock = $lock;
|
||||
// Condition valid and lock matched
|
||||
unset($locks[$lockIndex]);
|
||||
$lockValid = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If, after checking both etags and locks they are stil valid,
|
||||
// we can continue with the next condition.
|
||||
if ($etagValid && $lockValid) continue 2;
|
||||
}
|
||||
// No conditions matched, so we fail
|
||||
throw new Sabre_DAV_Exception_PreconditionFailed('The tokens provided in the if header did not match','If');
|
||||
}
|
||||
|
||||
// Conditions were met, we'll also need to check if all the locks are gone
|
||||
if (count($locks)) {
|
||||
|
||||
reset($locks);
|
||||
|
||||
// There's still locks, we fail
|
||||
$lastLock = current($locks);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// We got here, this means every condition was satisfied
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is created to extract information from the WebDAV HTTP 'If:' header
|
||||
*
|
||||
* The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information
|
||||
* The function will return an array, containg structs with the following keys
|
||||
*
|
||||
* * uri - the uri the condition applies to. If this is returned as an
|
||||
* empty string, this implies it's referring to the request url.
|
||||
* * tokens - The lock token. another 2 dimensional array containg 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token)
|
||||
* * etag - an etag, if supplied
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getIfConditions() {
|
||||
|
||||
$header = $this->server->httpRequest->getHeader('If');
|
||||
if (!$header) return array();
|
||||
|
||||
$matches = array();
|
||||
|
||||
$regex = '/(?:\<(?P<uri>.*?)\>\s)?\((?P<not>Not\s)?(?:\<(?P<token>[^\>]*)\>)?(?:\s?)(?:\[(?P<etag>[^\]]*)\])?\)/im';
|
||||
preg_match_all($regex,$header,$matches,PREG_SET_ORDER);
|
||||
|
||||
$conditions = array();
|
||||
|
||||
foreach($matches as $match) {
|
||||
|
||||
$condition = array(
|
||||
'uri' => $match['uri'],
|
||||
'tokens' => array(
|
||||
array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'')
|
||||
),
|
||||
);
|
||||
|
||||
if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array(
|
||||
$match['not']?0:1,
|
||||
$match['token'],
|
||||
isset($match['etag'])?$match['etag']:''
|
||||
);
|
||||
else {
|
||||
$conditions[] = $condition;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a webdav lock xml body, and returns a new Sabre_DAV_Locks_LockInfo object
|
||||
*
|
||||
* @param string $body
|
||||
* @return Sabre_DAV_Locks_LockInfo
|
||||
*/
|
||||
protected function parseLockRequest($body) {
|
||||
|
||||
$xml = simplexml_load_string($body,null,LIBXML_NOWARNING);
|
||||
$xml->registerXPathNamespace('d','DAV:');
|
||||
$lockInfo = new Sabre_DAV_Locks_LockInfo();
|
||||
|
||||
$children = $xml->children("DAV:");
|
||||
$lockInfo->owner = (string)$children->owner;
|
||||
|
||||
$lockInfo->token = Sabre_DAV_UUIDUtil::getUUID();
|
||||
$lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0?Sabre_DAV_Locks_LockInfo::EXCLUSIVE:Sabre_DAV_Locks_LockInfo::SHARED;
|
||||
|
||||
return $lockInfo;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This plugin provides support for RFC4709: Mounting WebDAV servers
|
||||
*
|
||||
* Simply append ?mount to any collection to generate the davmount response.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
*/
|
||||
class Sabre_DAV_Mount_Plugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* Reference to Server class
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* Initializes the plugin and registers event handles
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
$this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 'beforeMethod' event handles. This event handles intercepts GET requests ending
|
||||
* with ?mount
|
||||
*
|
||||
* @param string $method
|
||||
* @return void
|
||||
*/
|
||||
public function beforeMethod($method, $uri) {
|
||||
|
||||
if ($method!='GET') return;
|
||||
if ($this->server->httpRequest->getQueryString()!='mount') return;
|
||||
|
||||
$currentUri = $this->server->httpRequest->getAbsoluteUri();
|
||||
|
||||
// Stripping off everything after the ?
|
||||
list($currentUri) = explode('?',$currentUri);
|
||||
|
||||
$this->davMount($currentUri);
|
||||
|
||||
// Returning false to break the event chain
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the davmount response
|
||||
*
|
||||
* @param string $uri absolute uri
|
||||
* @return void
|
||||
*/
|
||||
public function davMount($uri) {
|
||||
|
||||
$this->server->httpResponse->sendStatus(200);
|
||||
$this->server->httpResponse->setHeader('Content-Type','application/davmount+xml');
|
||||
ob_start();
|
||||
echo '<?xml version="1.0"?>', "\n";
|
||||
echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
|
||||
echo " <dm:url>", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "</dm:url>\n";
|
||||
echo "</dm:mount>";
|
||||
$this->server->httpResponse->sendBody(ob_get_clean());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Node class
|
||||
*
|
||||
* This is a helper class, that should aid in getting nodes setup.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_Node implements Sabre_DAV_INode {
|
||||
|
||||
/**
|
||||
* Returns the last modification time
|
||||
*
|
||||
* In this case, it will simply return the current time
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return time();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deleted the current node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @param string $name The new name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ObjectTree class
|
||||
*
|
||||
* This implementation of the Tree class makes use of the INode, IFile and ICollection API's
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree {
|
||||
|
||||
/**
|
||||
* The root node
|
||||
*
|
||||
* @var Sabre_DAV_ICollection
|
||||
*/
|
||||
protected $rootNode;
|
||||
|
||||
/**
|
||||
* This is the node cache. Accessed nodes are stored here
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = array();
|
||||
|
||||
/**
|
||||
* Creates the object
|
||||
*
|
||||
* This method expects the rootObject to be passed as a parameter
|
||||
*
|
||||
* @param Sabre_DAV_ICollection $rootNode
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Sabre_DAV_ICollection $rootNode) {
|
||||
|
||||
$this->rootNode = $rootNode;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the INode object for the requested path
|
||||
*
|
||||
* @param string $path
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getNodeForPath($path) {
|
||||
|
||||
$path = trim($path,'/');
|
||||
if (isset($this->cache[$path])) return $this->cache[$path];
|
||||
|
||||
//if (!$path || $path=='.') return $this->rootNode;
|
||||
$currentNode = $this->rootNode;
|
||||
$i=0;
|
||||
// We're splitting up the path variable into folder/subfolder components and traverse to the correct node..
|
||||
foreach(explode('/',$path) as $pathPart) {
|
||||
|
||||
// If this part of the path is just a dot, it actually means we can skip it
|
||||
if ($pathPart=='.' || $pathPart=='') continue;
|
||||
|
||||
if (!($currentNode instanceof Sabre_DAV_ICollection))
|
||||
throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path);
|
||||
|
||||
$currentNode = $currentNode->getChild($pathPart);
|
||||
|
||||
}
|
||||
|
||||
$this->cache[$path] = $currentNode;
|
||||
return $currentNode;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function allows you to check if a node exists.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public function nodeExists($path) {
|
||||
|
||||
try {
|
||||
|
||||
// The root always exists
|
||||
if ($path==='') return true;
|
||||
|
||||
list($parent, $base) = Sabre_DAV_URLUtil::splitPath($path);
|
||||
|
||||
$parentNode = $this->getNodeForPath($parent);
|
||||
return $parentNode->childExists($base);
|
||||
|
||||
} catch (Sabre_DAV_Exception_FileNotFound $e) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of childnodes for a given path.
|
||||
*
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren($path) {
|
||||
|
||||
$node = $this->getNodeForPath($path);
|
||||
$children = $node->getChildren();
|
||||
foreach($children as $child) {
|
||||
|
||||
$this->cache[trim($path,'/') . '/' . $child->getName()] = $child;
|
||||
|
||||
}
|
||||
return $children;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called with every tree update
|
||||
*
|
||||
* Examples of tree updates are:
|
||||
* * node deletions
|
||||
* * node creations
|
||||
* * copy
|
||||
* * move
|
||||
* * renaming nodes
|
||||
*
|
||||
* If Tree classes implement a form of caching, this will allow
|
||||
* them to make sure caches will be expired.
|
||||
*
|
||||
* If a path is passed, it is assumed that the entire subtree is dirty
|
||||
*
|
||||
* @param string $path
|
||||
* @return void
|
||||
*/
|
||||
public function markDirty($path) {
|
||||
|
||||
// We don't care enough about sub-paths
|
||||
// flushing the entire cache
|
||||
$path = trim($path,'/');
|
||||
foreach($this->cache as $nodePath=>$node) {
|
||||
if ($nodePath == $path || strpos($nodePath,$path.'/')===0)
|
||||
unset($this->cache[$nodePath]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Abstract property class
|
||||
*
|
||||
* Extend this class to create custom complex properties
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_DAV_Property {
|
||||
|
||||
abstract function serialize(Sabre_DAV_Server $server, DOMElement $prop);
|
||||
|
||||
static function unserialize(DOMElement $prop) {
|
||||
|
||||
throw new Sabre_DAV_Exception('Unserialize has not been implemented for this class');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This property represents the {DAV:}getlastmodified property.
|
||||
*
|
||||
* Although this is normally a simple property, windows requires us to add
|
||||
* some new attributes.
|
||||
*
|
||||
* This class uses unix timestamps internally, and converts them to RFC 1123 times for
|
||||
* serialization
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Property_GetLastModified extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* time
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $time;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param int|DateTime $time
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($time) {
|
||||
|
||||
if ($time instanceof DateTime) {
|
||||
$this->time = $time;
|
||||
} elseif (is_int($time) || ctype_digit($time)) {
|
||||
$this->time = new DateTime('@' . $time);
|
||||
} else {
|
||||
$this->time = new DateTime($time);
|
||||
}
|
||||
|
||||
// Setting timezone to UTC
|
||||
$this->time->setTimezone(new DateTimeZone('UTC'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* serialize
|
||||
*
|
||||
* @param DOMElement $prop
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server, DOMElement $prop) {
|
||||
|
||||
$doc = $prop->ownerDocument;
|
||||
$prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/');
|
||||
$prop->setAttribute('b:dt','dateTime.rfc1123');
|
||||
$prop->nodeValue = $this->time->format(DateTime::RFC1123);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* getTime
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getTime() {
|
||||
|
||||
return $this->time;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Href property
|
||||
*
|
||||
* The href property represpents a url within a {DAV:}href element.
|
||||
* This is used by many WebDAV extensions, but not really within the WebDAV core spec
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
|
||||
|
||||
/**
|
||||
* href
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $href;
|
||||
|
||||
/**
|
||||
* Automatically prefix the url with the server base directory
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $autoPrefix = true;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param string $href
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($href, $autoPrefix = true) {
|
||||
|
||||
$this->href = $href;
|
||||
$this->autoPrefix = $autoPrefix;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uri
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHref() {
|
||||
|
||||
return $this->href;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this property.
|
||||
*
|
||||
* It will additionally prepend the href property with the server's base uri.
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $dom
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
|
||||
|
||||
$prefix = $server->xmlNamespaces['DAV:'];
|
||||
|
||||
$elem = $dom->ownerDocument->createElement($prefix . ':href');
|
||||
$elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $this->href;
|
||||
$dom->appendChild($elem);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserializes this property from a DOM Element
|
||||
*
|
||||
* This method returns an instance of this class.
|
||||
* It will only decode {DAV:}href values. For non-compatible elements null will be returned.
|
||||
*
|
||||
* @param DOMElement $dom
|
||||
* @return Sabre_DAV_Property_Href
|
||||
*/
|
||||
static function unserialize(DOMElement $dom) {
|
||||
|
||||
if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') {
|
||||
return new self($dom->firstChild->textContent,false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* HrefList property
|
||||
*
|
||||
* This property contains multiple {DAV:}href elements, each containing a url.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* hrefs
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $hrefs;
|
||||
|
||||
/**
|
||||
* Automatically prefix the url with the server base directory
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $autoPrefix = true;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param array $hrefs
|
||||
* @param bool $autoPrefix
|
||||
*/
|
||||
public function __construct(array $hrefs, $autoPrefix = true) {
|
||||
|
||||
$this->hrefs = $hrefs;
|
||||
$this->autoPrefix = $autoPrefix;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uris
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHrefs() {
|
||||
|
||||
return $this->hrefs;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this property.
|
||||
*
|
||||
* It will additionally prepend the href property with the server's base uri.
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $dom
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
|
||||
|
||||
$prefix = $server->xmlNamespaces['DAV:'];
|
||||
|
||||
foreach($this->hrefs as $href) {
|
||||
$elem = $dom->ownerDocument->createElement($prefix . ':href');
|
||||
$elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $href;
|
||||
$dom->appendChild($elem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserializes this property from a DOM Element
|
||||
*
|
||||
* This method returns an instance of this class.
|
||||
* It will only decode {DAV:}href values.
|
||||
*
|
||||
* @param DOMElement $dom
|
||||
* @return Sabre_DAV_Property_Href
|
||||
*/
|
||||
static function unserialize(DOMElement $dom) {
|
||||
|
||||
$hrefs = array();
|
||||
foreach($dom->childNodes as $child) {
|
||||
if (Sabre_DAV_XMLUtil::toClarkNotation($child)==='{DAV:}href') {
|
||||
$hrefs[] = $child->textContent;
|
||||
}
|
||||
}
|
||||
return new self($hrefs, false);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* IHref interface
|
||||
*
|
||||
* Any property implementing this interface can expose a related url.
|
||||
* This is used by certain subsystems to aquire more information about for example
|
||||
* the owner of a file
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_DAV_Property_IHref {
|
||||
|
||||
/**
|
||||
* getHref
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getHref();
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Represents {DAV:}lockdiscovery property
|
||||
*
|
||||
* This property contains all the open locks on a given resource
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Property_LockDiscovery extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* locks
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $locks;
|
||||
|
||||
/**
|
||||
* Should we show the locktoken as well?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $revealLockToken;
|
||||
|
||||
/**
|
||||
* Hides the {DAV:}lockroot element from the response.
|
||||
*
|
||||
* It was reported that showing the lockroot in the response can break
|
||||
* Office 2000 compatibility.
|
||||
*/
|
||||
static public $hideLockRoot = false;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param array $locks
|
||||
* @param bool $revealLockToken
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($locks,$revealLockToken = false) {
|
||||
|
||||
$this->locks = $locks;
|
||||
$this->revealLockToken = $revealLockToken;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* serialize
|
||||
*
|
||||
* @param DOMElement $prop
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $prop) {
|
||||
|
||||
$doc = $prop->ownerDocument;
|
||||
|
||||
foreach($this->locks as $lock) {
|
||||
|
||||
$activeLock = $doc->createElementNS('DAV:','d:activelock');
|
||||
$prop->appendChild($activeLock);
|
||||
|
||||
$lockScope = $doc->createElementNS('DAV:','d:lockscope');
|
||||
$activeLock->appendChild($lockScope);
|
||||
|
||||
$lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==Sabre_DAV_Locks_LockInfo::EXCLUSIVE?'exclusive':'shared')));
|
||||
|
||||
$lockType = $doc->createElementNS('DAV:','d:locktype');
|
||||
$activeLock->appendChild($lockType);
|
||||
|
||||
$lockType->appendChild($doc->createElementNS('DAV:','d:write'));
|
||||
|
||||
/* {DAV:}lockroot */
|
||||
if (!self::$hideLockRoot) {
|
||||
$lockRoot = $doc->createElementNS('DAV:','d:lockroot');
|
||||
$activeLock->appendChild($lockRoot);
|
||||
$href = $doc->createElementNS('DAV:','d:href');
|
||||
$href->appendChild($doc->createTextNode($server->getBaseUri() . $lock->uri));
|
||||
$lockRoot->appendChild($href);
|
||||
}
|
||||
|
||||
$activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == Sabre_DAV_Server::DEPTH_INFINITY?'infinity':$lock->depth)));
|
||||
$activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout));
|
||||
|
||||
if ($this->revealLockToken) {
|
||||
$lockToken = $doc->createElementNS('DAV:','d:locktoken');
|
||||
$activeLock->appendChild($lockToken);
|
||||
$lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token));
|
||||
}
|
||||
|
||||
$activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This class represents the {DAV:}resourcetype property
|
||||
*
|
||||
* Normally for files this is empty, and for collection {DAV:}collection.
|
||||
* However, other specs define different values for this.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* resourceType
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $resourceType = array();
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param mixed $resourceType
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($resourceType = array()) {
|
||||
|
||||
if ($resourceType === Sabre_DAV_Server::NODE_FILE)
|
||||
$this->resourceType = array();
|
||||
elseif ($resourceType === Sabre_DAV_Server::NODE_DIRECTORY)
|
||||
$this->resourceType = array('{DAV:}collection');
|
||||
elseif (is_array($resourceType))
|
||||
$this->resourceType = $resourceType;
|
||||
else
|
||||
$this->resourceType = array($resourceType);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* serialize
|
||||
*
|
||||
* @param DOMElement $prop
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $prop) {
|
||||
|
||||
$propName = null;
|
||||
$rt = $this->resourceType;
|
||||
|
||||
foreach($rt as $resourceType) {
|
||||
if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) {
|
||||
|
||||
if (isset($server->xmlNamespaces[$propName[1]])) {
|
||||
$prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2]));
|
||||
} else {
|
||||
$prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2]));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values in clark-notation
|
||||
*
|
||||
* For example array('{DAV:}collection')
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValue() {
|
||||
|
||||
return $this->resourceType;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the principal contains a certain value
|
||||
*
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function is($type) {
|
||||
|
||||
return in_array($type, $this->resourceType);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a resourcetype value to this property
|
||||
*
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
public function add($type) {
|
||||
|
||||
$this->resourceType[] = $type;
|
||||
$this->resourceType = array_unique($this->resourceType);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserializes a DOM element into a ResourceType property.
|
||||
*
|
||||
* @param DOMElement $dom
|
||||
* @return void
|
||||
*/
|
||||
static public function unserialize(DOMElement $dom) {
|
||||
|
||||
$value = array();
|
||||
foreach($dom->childNodes as $child) {
|
||||
|
||||
$value[] = Sabre_DAV_XMLUtil::toClarkNotation($child);
|
||||
|
||||
}
|
||||
|
||||
return new self($value);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Response property
|
||||
*
|
||||
* This class represents the {DAV:}response XML element.
|
||||
* This is used by the Server class to encode individual items within a multistatus
|
||||
* response.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
|
||||
|
||||
/**
|
||||
* Url for the response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $href;
|
||||
|
||||
/**
|
||||
* Propertylist, ordered by HTTP status code
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $responseProperties;
|
||||
|
||||
/**
|
||||
* The responseProperties argument is a list of properties
|
||||
* within an array with keys representing HTTP status codes
|
||||
*
|
||||
* @param string $href
|
||||
* @param array $responseProperties
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($href,array $responseProperties) {
|
||||
|
||||
$this->href = $href;
|
||||
$this->responseProperties = $responseProperties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHref() {
|
||||
|
||||
return $this->href;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property list
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResponseProperties() {
|
||||
|
||||
return $this->responseProperties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* serialize
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $dom
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
|
||||
|
||||
$document = $dom->ownerDocument;
|
||||
$properties = $this->responseProperties;
|
||||
|
||||
$xresponse = $document->createElement('d:response');
|
||||
$dom->appendChild($xresponse);
|
||||
|
||||
$uri = Sabre_DAV_URLUtil::encodePath($this->href);
|
||||
|
||||
// Adding the baseurl to the beginning of the url
|
||||
$uri = $server->getBaseUri() . $uri;
|
||||
|
||||
$xresponse->appendChild($document->createElement('d:href',$uri));
|
||||
|
||||
// The properties variable is an array containing properties, grouped by
|
||||
// HTTP status
|
||||
foreach($properties as $httpStatus=>$propertyGroup) {
|
||||
|
||||
// The 'href' is also in this array, and it's special cased.
|
||||
// We will ignore it
|
||||
if ($httpStatus=='href') continue;
|
||||
|
||||
// If there are no properties in this group, we can also just carry on
|
||||
if (!count($propertyGroup)) continue;
|
||||
|
||||
$xpropstat = $document->createElement('d:propstat');
|
||||
$xresponse->appendChild($xpropstat);
|
||||
|
||||
$xprop = $document->createElement('d:prop');
|
||||
$xpropstat->appendChild($xprop);
|
||||
|
||||
$nsList = $server->xmlNamespaces;
|
||||
|
||||
foreach($propertyGroup as $propertyName=>$propertyValue) {
|
||||
|
||||
$propName = null;
|
||||
preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName);
|
||||
|
||||
// special case for empty namespaces
|
||||
if ($propName[1]=='') {
|
||||
|
||||
$currentProperty = $document->createElement($propName[2]);
|
||||
$xprop->appendChild($currentProperty);
|
||||
$currentProperty->setAttribute('xmlns','');
|
||||
|
||||
} else {
|
||||
|
||||
if (!isset($nsList[$propName[1]])) {
|
||||
$nsList[$propName[1]] = 'x' . count($nsList);
|
||||
}
|
||||
|
||||
// If the namespace was defined in the top-level xml namespaces, it means
|
||||
// there was already a namespace declaration, and we don't have to worry about it.
|
||||
if (isset($server->xmlNamespaces[$propName[1]])) {
|
||||
$currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]);
|
||||
} else {
|
||||
$currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]);
|
||||
}
|
||||
$xprop->appendChild($currentProperty);
|
||||
|
||||
}
|
||||
|
||||
if (is_scalar($propertyValue)) {
|
||||
$text = $document->createTextNode($propertyValue);
|
||||
$currentProperty->appendChild($text);
|
||||
} elseif ($propertyValue instanceof Sabre_DAV_Property) {
|
||||
$propertyValue->serialize($server,$currentProperty);
|
||||
} elseif (!is_null($propertyValue)) {
|
||||
throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ResponseList property
|
||||
*
|
||||
* This class represents multiple {DAV:}response XML elements.
|
||||
* This is used by the Server class to encode items within a multistatus
|
||||
* response.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Property_ResponseList extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* Response objects.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $responses;
|
||||
|
||||
/**
|
||||
* The only valid argument is a list of Sabre_DAV_Property_Response
|
||||
* objects.
|
||||
*
|
||||
* @param array $responses;
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($responses) {
|
||||
|
||||
foreach($responses as $response) {
|
||||
if (!($response instanceof Sabre_DAV_Property_Response)) {
|
||||
throw new InvalidArgumentException('You must pass an array of Sabre_DAV_Property_Response objects');
|
||||
}
|
||||
}
|
||||
$this->responses = $responses;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* serialize
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $dom
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
|
||||
|
||||
foreach($this->responses as $response) {
|
||||
$response->serialize($server, $dom);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This class represents the {DAV:}supportedlock property
|
||||
*
|
||||
* This property contains information about what kind of locks
|
||||
* this server supports.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_DAV_Property_SupportedLock extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* supportsLocks
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $supportsLocks = false;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param mixed $supportsLocks
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($supportsLocks) {
|
||||
|
||||
$this->supportsLocks = $supportsLocks;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* serialize
|
||||
*
|
||||
* @param DOMElement $prop
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $prop) {
|
||||
|
||||
$doc = $prop->ownerDocument;
|
||||
|
||||
if (!$this->supportsLocks) return null;
|
||||
|
||||
$lockEntry1 = $doc->createElementNS('DAV:','d:lockentry');
|
||||
$lockEntry2 = $doc->createElementNS('DAV:','d:lockentry');
|
||||
|
||||
$prop->appendChild($lockEntry1);
|
||||
$prop->appendChild($lockEntry2);
|
||||
|
||||
$lockScope1 = $doc->createElementNS('DAV:','d:lockscope');
|
||||
$lockScope2 = $doc->createElementNS('DAV:','d:lockscope');
|
||||
$lockType1 = $doc->createElementNS('DAV:','d:locktype');
|
||||
$lockType2 = $doc->createElementNS('DAV:','d:locktype');
|
||||
|
||||
$lockEntry1->appendChild($lockScope1);
|
||||
$lockEntry1->appendChild($lockType1);
|
||||
$lockEntry2->appendChild($lockScope2);
|
||||
$lockEntry2->appendChild($lockType2);
|
||||
|
||||
$lockScope1->appendChild($doc->createElementNS('DAV:','d:exclusive'));
|
||||
$lockScope2->appendChild($doc->createElementNS('DAV:','d:shared'));
|
||||
|
||||
$lockType1->appendChild($doc->createElementNS('DAV:','d:write'));
|
||||
$lockType2->appendChild($doc->createElementNS('DAV:','d:write'));
|
||||
|
||||
//$frag->appendXML('<d:lockentry><d:lockscope><d:exclusive /></d:lockscope><d:locktype><d:write /></d:locktype></d:lockentry>');
|
||||
//$frag->appendXML('<d:lockentry><d:lockscope><d:shared /></d:lockscope><d:locktype><d:write /></d:locktype></d:lockentry>');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue