2008-05-08 22:31:32 +02:00
|
|
|
<?php
|
|
|
|
/**
|
2016-04-02 10:40:34 +02:00
|
|
|
* EGroupware: CalDAV/CardDAV/GroupDAV access: abstract baseclass for application handlers
|
2008-05-08 22:31:32 +02:00
|
|
|
*
|
|
|
|
* @link http://www.egroupware.org
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @package api
|
2016-04-02 12:44:17 +02:00
|
|
|
* @subpackage caldav
|
2008-05-08 22:31:32 +02:00
|
|
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
2016-04-02 10:40:34 +02:00
|
|
|
* @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
2008-05-08 22:31:32 +02:00
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
2016-04-02 12:44:17 +02:00
|
|
|
namespace EGroupware\Api\CalDAV;
|
|
|
|
|
|
|
|
use EGroupware\Api;
|
|
|
|
|
2008-05-08 22:31:32 +02:00
|
|
|
/**
|
2009-10-17 11:13:36 +02:00
|
|
|
* EGroupware: GroupDAV access: abstract baseclass for groupdav/caldav/carddav handlers
|
2012-02-21 21:04:45 +01:00
|
|
|
*
|
2016-04-02 12:44:17 +02:00
|
|
|
* Permanent error_log() calls should use $this->caldav->log($str) instead, to be send to PHP error_log()
|
2012-02-21 21:04:45 +01:00
|
|
|
* and our request-log (prefixed with "### " after request and response, like exceptions).
|
2012-09-26 16:30:47 +02:00
|
|
|
*
|
|
|
|
* @ToDo: If precondition for PUT, see https://tools.ietf.org/html/rfc6578#section-5
|
2008-05-08 22:31:32 +02:00
|
|
|
*/
|
2016-04-02 12:44:17 +02:00
|
|
|
abstract class Handler
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Debug level: 0 = nothing, 1 = function calls, 2 = more info, eg. complete $_SERVER array
|
|
|
|
*
|
|
|
|
* The debug messages are send to the apache error_log
|
|
|
|
*
|
|
|
|
* @var integer
|
|
|
|
*/
|
2011-04-10 17:05:47 +02:00
|
|
|
var $debug = 0;
|
2008-05-08 22:31:32 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* eGW's charset
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
var $egw_charset;
|
2010-03-07 00:06:43 +01:00
|
|
|
/**
|
|
|
|
* Reference to the accounts class
|
|
|
|
*
|
2016-04-02 12:44:17 +02:00
|
|
|
* @var Api\Accounts
|
2010-03-07 00:06:43 +01:00
|
|
|
*/
|
|
|
|
var $accounts;
|
2011-09-21 22:08:21 +02:00
|
|
|
/**
|
|
|
|
* Reference to the ACL class
|
|
|
|
*
|
2016-04-02 12:44:17 +02:00
|
|
|
* @var Api\Acl
|
2011-09-21 22:08:21 +02:00
|
|
|
*/
|
|
|
|
var $acl;
|
2008-05-08 22:31:32 +02:00
|
|
|
/**
|
|
|
|
* Translates method names into ACL bits
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
var $method2acl = array(
|
2016-05-11 21:23:14 +02:00
|
|
|
'GET' => Api\Acl::READ,
|
|
|
|
'PUT' => Api\Acl::EDIT,
|
|
|
|
'DELETE' => Api\Acl::DELETE,
|
2008-05-08 22:31:32 +02:00
|
|
|
);
|
|
|
|
/**
|
|
|
|
* eGW application responsible for the handler
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
var $app;
|
2008-05-17 14:54:26 +02:00
|
|
|
/**
|
2016-04-02 12:44:17 +02:00
|
|
|
* Calling CalDAV object
|
2008-05-17 14:54:26 +02:00
|
|
|
*
|
2016-04-02 12:44:17 +02:00
|
|
|
* @var Api\CalDAV
|
2008-05-17 14:54:26 +02:00
|
|
|
*/
|
2016-04-02 12:44:17 +02:00
|
|
|
var $caldav;
|
2010-03-07 00:06:43 +01:00
|
|
|
/**
|
2016-04-02 12:44:17 +02:00
|
|
|
* Base url of handler, need to prefix all pathes not automatic handled by Api\CalDAV
|
2010-03-07 00:06:43 +01:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2011-09-18 12:56:56 +02:00
|
|
|
var $base_uri;
|
2008-05-08 22:31:32 +02:00
|
|
|
/**
|
|
|
|
* HTTP_IF_MATCH / etag of current request / last call to _common_get_put_delete() method
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
var $http_if_match;
|
2008-11-03 10:36:20 +01:00
|
|
|
/**
|
|
|
|
* Identified user agent
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
var $agent;
|
2008-05-08 22:31:32 +02:00
|
|
|
|
2011-04-05 22:39:13 +02:00
|
|
|
/**
|
|
|
|
* Extension to append to url/path
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
static $path_extension = '.ics';
|
|
|
|
|
2011-09-21 22:08:21 +02:00
|
|
|
/**
|
|
|
|
* Which attribute to use to contruct name part of url/path
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
static $path_attr = 'id';
|
|
|
|
|
2013-09-23 12:21:31 +02:00
|
|
|
/**
|
|
|
|
* New id of put/post stored here by put_response_headers for check_return_representation
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
var $new_id;
|
|
|
|
|
2008-05-10 22:15:02 +02:00
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
2008-05-17 14:54:26 +02:00
|
|
|
* @param string $app 'calendar', 'addressbook' or 'infolog'
|
2016-04-02 12:44:17 +02:00
|
|
|
* @param Api\CalDAV $caldav calling class
|
2008-05-10 22:15:02 +02:00
|
|
|
*/
|
2016-04-02 12:44:17 +02:00
|
|
|
function __construct($app, Api\CalDAV $caldav)
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
|
|
|
$this->app = $app;
|
2016-04-02 12:44:17 +02:00
|
|
|
if (!is_null($caldav->debug)) $this->debug = $caldav->debug;
|
|
|
|
$this->base_uri = $caldav->base_uri;
|
|
|
|
$this->caldav = $caldav;
|
2010-03-07 00:06:43 +01:00
|
|
|
|
2008-11-03 10:36:20 +01:00
|
|
|
$this->agent = self::get_agent();
|
2009-10-17 11:13:36 +02:00
|
|
|
|
2016-04-02 12:44:17 +02:00
|
|
|
$this->egw_charset = Api\Translation::charset();
|
2011-09-21 22:08:21 +02:00
|
|
|
|
|
|
|
$this->accounts = $GLOBALS['egw']->accounts;
|
|
|
|
$this->acl = $GLOBALS['egw']->acl;
|
2008-05-08 22:31:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle propfind request for an application folder
|
|
|
|
*
|
|
|
|
* @param string $path
|
2012-06-27 22:08:56 +02:00
|
|
|
* @param array &$options
|
2008-05-08 22:31:32 +02:00
|
|
|
* @param array &$files
|
|
|
|
* @param int $user account_id
|
|
|
|
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
|
|
|
|
*/
|
2012-06-27 22:08:56 +02:00
|
|
|
abstract function propfind($path,&$options,&$files,$user);
|
2008-05-08 22:31:32 +02:00
|
|
|
|
2009-10-17 11:13:36 +02:00
|
|
|
/**
|
|
|
|
* Propfind callback, if interator is used
|
|
|
|
*
|
2010-03-07 00:06:43 +01:00
|
|
|
* @param string $path
|
2009-10-17 11:13:36 +02:00
|
|
|
* @param array $filter
|
|
|
|
* @param array|boolean $start false=return all or array(start,num)
|
|
|
|
* @return array with "files" array with values for keys path and props
|
|
|
|
*/
|
2016-06-06 13:54:09 +02:00
|
|
|
function &propfind_callback($path, array $filter, $start)
|
2016-04-02 10:40:34 +02:00
|
|
|
{
|
2016-06-06 13:54:09 +02:00
|
|
|
unset($path, $filter, $start); // not used, but required by function signature
|
2016-04-02 10:40:34 +02:00
|
|
|
}
|
2009-10-17 11:13:36 +02:00
|
|
|
|
2008-05-08 22:31:32 +02:00
|
|
|
/**
|
|
|
|
* Handle get request for an applications entry
|
|
|
|
*
|
|
|
|
* @param array &$options
|
|
|
|
* @param int $id
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param int $user =null account_id
|
2008-05-08 22:31:32 +02:00
|
|
|
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
|
|
|
|
*/
|
2011-03-05 11:21:32 +01:00
|
|
|
abstract function get(&$options,$id,$user=null);
|
2008-05-08 22:31:32 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle get request for an applications entry
|
|
|
|
*
|
|
|
|
* @param array &$options
|
|
|
|
* @param int $id
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param int $user =null account_id of owner, default null
|
2008-05-08 22:31:32 +02:00
|
|
|
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
|
|
|
|
*/
|
|
|
|
abstract function put(&$options,$id,$user=null);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle get request for an applications entry
|
|
|
|
*
|
|
|
|
* @param array &$options
|
|
|
|
* @param int $id
|
2018-10-09 13:14:36 +02:00
|
|
|
* @param int $user account_id of collection owner
|
2008-05-08 22:31:32 +02:00
|
|
|
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
|
|
|
|
*/
|
2018-10-09 13:14:36 +02:00
|
|
|
abstract function delete(&$options,$id,$user);
|
2008-05-08 22:31:32 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Read an entry
|
|
|
|
*
|
2012-02-09 21:09:49 +01:00
|
|
|
* @param string|int $id
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param string $path =null implementation can use it, used in call from _common_get_put_delete
|
2012-02-09 21:09:49 +01:00
|
|
|
* @return array|boolean array with entry, false if no read rights, null if $id does not exist
|
2008-05-08 22:31:32 +02:00
|
|
|
*/
|
2012-02-09 21:09:49 +01:00
|
|
|
abstract function read($id /*,$path=null*/);
|
2008-05-08 22:31:32 +02:00
|
|
|
|
2013-09-23 12:21:31 +02:00
|
|
|
/**
|
|
|
|
* Get id from entry-array returned by read()
|
|
|
|
*
|
|
|
|
* @param int|string|array $entry
|
|
|
|
* @return int|string
|
|
|
|
*/
|
|
|
|
function get_id($entry)
|
|
|
|
{
|
|
|
|
return is_array($entry) ? $entry['id'] : $entry;
|
|
|
|
}
|
|
|
|
|
2008-05-08 22:31:32 +02:00
|
|
|
/**
|
|
|
|
* Check if user has the neccessary rights on an entry
|
|
|
|
*
|
2016-05-11 21:23:14 +02:00
|
|
|
* @param int $acl Api\Acl::READ, Api\Acl::EDIT or Api\Acl::DELETE
|
2012-02-09 21:09:49 +01:00
|
|
|
* @param array|int $entry entry-array or id
|
2008-05-08 22:31:32 +02:00
|
|
|
* @return boolean null if entry does not exist, false if no access, true if access permitted
|
|
|
|
*/
|
|
|
|
abstract function check_access($acl,$entry);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add extra properties for collections
|
|
|
|
*
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param array $props =array() regular props by the groupdav handler
|
2010-03-07 00:06:43 +01:00
|
|
|
* @param string $displayname
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param string $base_uri =null base url of handler
|
|
|
|
* @param int $user =null account_id of owner of collection
|
2008-05-08 22:31:32 +02:00
|
|
|
* @return array
|
|
|
|
*/
|
2016-04-02 10:40:34 +02:00
|
|
|
public function extra_properties(array $props, $displayname, $base_uri=null, $user=null)
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
2016-04-02 10:40:34 +02:00
|
|
|
unset($displayname, $base_uri, $user); // not used, but required by function signature
|
|
|
|
|
2008-05-08 22:31:32 +02:00
|
|
|
return $props;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the etag for an entry, can be reimplemented for other algorithm or field names
|
|
|
|
*
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param array|int $entry array with event or cal_id
|
2012-02-09 21:09:49 +01:00
|
|
|
* @return string|boolean string with etag or false
|
2008-05-08 22:31:32 +02:00
|
|
|
*/
|
|
|
|
function get_etag($entry)
|
|
|
|
{
|
|
|
|
if (!is_array($entry))
|
|
|
|
{
|
|
|
|
$entry = $this->read($entry);
|
|
|
|
}
|
|
|
|
if (!is_array($entry) || !isset($entry['id']) || !(isset($entry['modified']) || isset($entry['etag'])))
|
|
|
|
{
|
2009-04-02 14:39:52 +02:00
|
|
|
// error_log(__METHOD__."(".array2string($entry).") Cant create etag!");
|
2008-05-08 22:31:32 +02:00
|
|
|
return false;
|
|
|
|
}
|
2011-10-08 13:34:55 +02:00
|
|
|
return $entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']);
|
2008-05-08 22:31:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert etag to the raw etag column value (without quotes, double colon and id)
|
|
|
|
*
|
|
|
|
* @param string $etag
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
static function etag2value($etag)
|
|
|
|
{
|
2011-11-08 22:02:47 +01:00
|
|
|
list(,$val) = explode(':',$etag,2);
|
2008-05-08 22:31:32 +02:00
|
|
|
|
|
|
|
return $val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle common stuff for get, put and delete requests:
|
|
|
|
* - application rights
|
|
|
|
* - entry level acl, incl. edit and delete rights
|
|
|
|
* - etag handling for precondition failed and not modified
|
|
|
|
*
|
|
|
|
* @param string $method GET, PUT, DELETE
|
|
|
|
* @param array &$options
|
2011-04-05 22:39:13 +02:00
|
|
|
* @param int|string &$id on return self::$path_extension got removed
|
2008-05-17 14:54:26 +02:00
|
|
|
* @param boolean &$return_no_access=false if set to true on call, instead of '403 Forbidden' the entry is returned and $return_no_access===false
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param boolean $ignore_if_match =false if true, ignore If-Match precondition
|
2010-03-07 00:06:43 +01:00
|
|
|
* @return array|string entry on success, string with http-error-code on failure, null for PUT on an unknown id
|
2008-05-08 22:31:32 +02:00
|
|
|
*/
|
2011-10-20 22:10:04 +02:00
|
|
|
function _common_get_put_delete($method,&$options,&$id,&$return_no_access=false,$ignore_if_match=false)
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
2011-04-05 22:39:13 +02:00
|
|
|
if (self::$path_extension) $id = basename($id,self::$path_extension);
|
|
|
|
|
2010-10-20 01:30:16 +02:00
|
|
|
if ($this->app != 'principals' && !$GLOBALS['egw_info']['user']['apps'][$this->app])
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
2008-05-10 22:15:02 +02:00
|
|
|
if ($this->debug) error_log(__METHOD__."($method,,$id) 403 Forbidden: no app rights for '$this->app'");
|
|
|
|
return '403 Forbidden'; // no app rights
|
2008-05-08 22:31:32 +02:00
|
|
|
}
|
|
|
|
$extra_acl = $this->method2acl[$method];
|
2014-12-04 16:01:16 +01:00
|
|
|
if ($id && !($entry = $this->read($id, $options['path'])) && ($method != 'PUT' || $entry === false) ||
|
2016-05-11 21:23:14 +02:00
|
|
|
($extra_acl != Api\Acl::READ && $this->check_access($extra_acl,$entry) === false))
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
2008-05-17 14:54:26 +02:00
|
|
|
if ($return_no_access && !is_null($entry))
|
|
|
|
{
|
2010-10-31 08:56:29 +01:00
|
|
|
if ($this->debug) error_log(__METHOD__."($method,,$id,$return_no_access) \$entry=".array2string($entry).", \$return_no_access set to false");
|
2008-05-17 14:54:26 +02:00
|
|
|
$return_no_access = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ($this->debug) error_log(__METHOD__."($method,,$id) 403 Forbidden/404 Not Found: read($id)==".($entry===false?'false':'null'));
|
|
|
|
return !is_null($entry) ? '403 Forbidden' : '404 Not Found';
|
|
|
|
}
|
2008-05-08 22:31:32 +02:00
|
|
|
}
|
|
|
|
if ($entry)
|
|
|
|
{
|
|
|
|
$etag = $this->get_etag($entry);
|
|
|
|
// If the clients sends an "If-Match" header ($_SERVER['HTTP_IF_MATCH']) we check with the current etag
|
|
|
|
// of the calendar --> on failure we return 412 Precondition failed, to not overwrite the modifications
|
2011-10-20 22:10:04 +02:00
|
|
|
if (isset($_SERVER['HTTP_IF_MATCH']) && !$ignore_if_match)
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
2011-10-05 10:15:24 +02:00
|
|
|
$this->http_if_match = $_SERVER['HTTP_IF_MATCH'];
|
|
|
|
// strip of quotes around etag, if they exist, that way we allow etag with and without quotes
|
|
|
|
if ($this->http_if_match[0] == '"') $this->http_if_match = substr($this->http_if_match, 1, -1);
|
|
|
|
|
|
|
|
if ($this->http_if_match !== $etag)
|
2010-06-14 09:38:41 +02:00
|
|
|
{
|
2012-02-09 21:09:49 +01:00
|
|
|
if ($this->debug) error_log(__METHOD__."($method,path=$options[path],$id) HTTP_IF_MATCH='$_SERVER[HTTP_IF_MATCH]', etag='$etag': 412 Precondition failed".array2string($entry));
|
2013-09-25 09:09:44 +02:00
|
|
|
// honor Prefer: return=representation for 412 too (no need for client to explicitly reload)
|
|
|
|
$this->check_return_representation($options, $id);
|
2010-06-14 09:38:41 +02:00
|
|
|
return '412 Precondition Failed';
|
|
|
|
}
|
2008-05-08 22:31:32 +02:00
|
|
|
}
|
2010-06-14 09:38:41 +02:00
|
|
|
if (isset($_SERVER['HTTP_IF_NONE_MATCH']))
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
2011-10-08 20:27:02 +02:00
|
|
|
$if_none_match = $_SERVER['HTTP_IF_NONE_MATCH'];
|
|
|
|
// strip of quotes around etag, if they exist, that way we allow etag with and without quotes
|
|
|
|
if ($if_none_match[0] == '"') $if_none_match = substr($if_none_match, 1, -1);
|
|
|
|
|
|
|
|
// if an IF_NONE_MATCH is given, check if we need to send a new export, or the current one is still up-to-date
|
|
|
|
if (in_array($method, array('GET','HEAD')) && $etag === $if_none_match)
|
|
|
|
{
|
|
|
|
if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_NONE_MATCH='$_SERVER[HTTP_IF_NONE_MATCH]', etag='$etag': 304 Not Modified");
|
|
|
|
return '304 Not Modified';
|
|
|
|
}
|
|
|
|
if ($method == 'PUT' && ($if_none_match == '*' || $if_none_match == $etag))
|
|
|
|
{
|
|
|
|
if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_NONE_MATCH='$_SERVER[HTTP_IF_NONE_MATCH]', etag='$etag': 412 Precondition failed");
|
2013-09-25 09:09:44 +02:00
|
|
|
// honor Prefer: return=representation for 412 too (no need for client to explicitly reload)
|
|
|
|
$this->check_return_representation($options, $id);
|
2011-10-08 20:27:02 +02:00
|
|
|
return '412 Precondition Failed';
|
|
|
|
}
|
2008-05-08 22:31:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return $entry;
|
|
|
|
}
|
|
|
|
|
2013-09-23 12:21:31 +02:00
|
|
|
/**
|
|
|
|
* Return representation, if requested by HTTP Prefer header
|
|
|
|
*
|
|
|
|
* @param array $options
|
|
|
|
* @param int $id
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param int $user =null account_id
|
2013-09-23 12:21:31 +02:00
|
|
|
* @return string|boolean http status of get or null if no representation was requested
|
|
|
|
*/
|
|
|
|
public function check_return_representation($options, $id, $user=null)
|
|
|
|
{
|
2013-09-25 09:09:44 +02:00
|
|
|
//error_log(__METHOD__."(, $id, $user) start ".function_backtrace());
|
|
|
|
if (isset($_SERVER['HTTP_PREFER']) && in_array('return=representation', preg_split('/, ?/', $_SERVER['HTTP_PREFER'])))
|
2013-09-23 12:21:31 +02:00
|
|
|
{
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] == 'POST')
|
|
|
|
{
|
2016-04-02 12:44:17 +02:00
|
|
|
$location = $this->caldav->base_uri.$options['path'];
|
2013-09-23 12:21:31 +02:00
|
|
|
if ($location[0] == '/')
|
|
|
|
{
|
|
|
|
$location = (@$_SERVER['HTTPS'] === 'on' ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].$location;
|
|
|
|
}
|
|
|
|
header('Content-Location: '.$location);
|
|
|
|
}
|
2013-09-25 09:09:44 +02:00
|
|
|
|
|
|
|
// remove If-Match or If-None-Match headers, otherwise HTTP status 412 goes into endless loop!
|
|
|
|
unset($_SERVER['HTTP_IF_MATCH']);
|
|
|
|
unset($_SERVER['HTTP_IF_NONE_MATCH']);
|
|
|
|
|
2013-09-23 12:21:31 +02:00
|
|
|
if (($ret = $this->get($options, $id ? $id : $this->new_id, $user)) && !empty($options['data']))
|
|
|
|
{
|
2016-04-02 12:44:17 +02:00
|
|
|
if (!$this->caldav->use_compression()) header('Content-Length: '.$this->caldav->bytes($options['data']));
|
2013-09-23 12:21:31 +02:00
|
|
|
header('Content-Type: '.$options['mimetype']);
|
|
|
|
echo $options['data'];
|
|
|
|
}
|
|
|
|
}
|
2013-09-25 09:09:44 +02:00
|
|
|
//error_log(__METHOD__."(, $id, $user) returning ".array2string($ret));
|
2013-09-23 12:21:31 +02:00
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update etag, ctag and sync-token to reflect changed attachments
|
|
|
|
*
|
|
|
|
* Not abstract, as not need to implement for apps not supporting managed attachments
|
|
|
|
*
|
|
|
|
* @param array|string|int $entry array with entry data from read, or id
|
|
|
|
*/
|
|
|
|
public function update_tags($entry)
|
|
|
|
{
|
2016-04-02 10:40:34 +02:00
|
|
|
unset($entry); // not used, but required by function signature
|
2013-09-23 12:21:31 +02:00
|
|
|
}
|
|
|
|
|
2008-05-08 22:31:32 +02:00
|
|
|
/**
|
|
|
|
* Get the handler for the given app
|
|
|
|
*
|
|
|
|
* @static
|
|
|
|
* @param string $app 'calendar', 'addressbook' or 'infolog'
|
2016-04-02 12:44:17 +02:00
|
|
|
* @param Api\CalDAV $groupdav calling class
|
2008-05-08 22:31:32 +02:00
|
|
|
* @return groupdav_handler
|
|
|
|
*/
|
2016-04-02 12:44:17 +02:00
|
|
|
static function app_handler($app, Api\CalDAV $groupdav)
|
2008-05-08 22:31:32 +02:00
|
|
|
{
|
|
|
|
static $handler_cache = array();
|
|
|
|
|
|
|
|
if (!array_key_exists($app,$handler_cache))
|
|
|
|
{
|
|
|
|
$class = $app.'_groupdav';
|
2016-04-02 12:44:17 +02:00
|
|
|
if (!class_exists($class) && !class_exists($class = __NAMESPACE__.'\\'.ucfirst($app))) return null;
|
2008-05-08 22:31:32 +02:00
|
|
|
|
2011-09-18 12:56:56 +02:00
|
|
|
$handler_cache[$app] = new $class($app, $groupdav);
|
2008-05-08 22:31:32 +02:00
|
|
|
}
|
2010-03-07 00:06:43 +01:00
|
|
|
|
2008-05-08 22:31:32 +02:00
|
|
|
return $handler_cache[$app];
|
|
|
|
}
|
2008-11-03 10:36:20 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Identify know GroupDAV agents by HTTP_USER_AGENT header
|
|
|
|
*
|
|
|
|
* @return string|boolean agent name or false
|
|
|
|
*/
|
|
|
|
static function get_agent()
|
|
|
|
{
|
2016-04-02 10:40:34 +02:00
|
|
|
static $agent=null;
|
2008-11-03 10:36:20 +01:00
|
|
|
|
|
|
|
if (is_null($agent))
|
|
|
|
{
|
|
|
|
$agent = false;
|
|
|
|
// identify the agent (GroupDAV client) from the HTTP_USER_AGENT header
|
|
|
|
$user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
|
|
|
|
foreach(array(
|
2012-02-29 17:24:49 +01:00
|
|
|
'carddav-sync' => 'carddav-sync', // dmfs.org CardDAV client for Android: CardDAV-Sync (Android) (like iOS/5.0.1 (9A405) dataaccessd/1.0) gzip
|
2010-09-25 11:08:37 +02:00
|
|
|
'iphone' => 'iphone', // Apple iPhone iCal
|
2011-08-02 14:59:23 +02:00
|
|
|
'davkit' => 'davkit', // Apple iCal 10.6
|
|
|
|
'coredav' => 'coredav', // Apple iCal 10.7
|
2011-11-08 22:02:47 +01:00
|
|
|
'calendarstore' => 'calendarstore', // Apple iCal 5.0.1 under OS X 10.7.2
|
2012-09-26 16:30:47 +02:00
|
|
|
'calendaragent/' => 'calendaragent', // Apple iCal OS X 10.8*: Mac OS X/10.8.2 (12C54) CalendarAgent/55
|
2010-09-25 11:08:37 +02:00
|
|
|
'dataaccess' => 'dataaccess', // Apple addressbook iPhone
|
2011-08-02 14:59:23 +02:00
|
|
|
'cfnetwork' => 'cfnetwork', // Apple Addressbook 10.6/7
|
2012-09-26 16:30:47 +02:00
|
|
|
'addressbook/' => 'cfnetwork', // Apple Addressbook OS X 10.8*: Mac OS X/10.8.2 (12C54) AddressBook/1167
|
2008-11-03 10:36:20 +01:00
|
|
|
'bionicmessage.net' => 'funambol', // funambol GroupDAV connector from bionicmessage.net
|
|
|
|
'zideone' => 'zideone', // zideone outlook plugin
|
2012-02-07 18:10:52 +01:00
|
|
|
'lightning' => 'lightning', // Lighting (incl. SOGo connector for addressbook)
|
2010-10-31 08:56:29 +01:00
|
|
|
'webkit' => 'webkit', // Webkit Browser (also reports KHTML!)
|
2011-09-26 12:20:27 +02:00
|
|
|
'akonadi' => 'akonadi', // new KDE PIM framework (also reports KHTML!)
|
2008-11-03 10:36:20 +01:00
|
|
|
'khtml' => 'kde', // KDE clients
|
2010-10-31 08:56:29 +01:00
|
|
|
'neon' => 'neon',
|
|
|
|
'ical4ol' => 'ical4ol', // iCal4OL client
|
2011-03-07 15:00:37 +01:00
|
|
|
'evolution' => 'evolution', // Evolution
|
2012-02-07 18:10:52 +01:00
|
|
|
'thunderbird' => 'thunderbird', // SOGo connector for addressbook, no Lightning installed
|
2018-09-20 15:56:19 +02:00
|
|
|
'caldavsynchronizer'=> 'caldavsynchronizer', // Outlook CalDAV Synchroniser (https://caldavsynchronizer.org/)
|
2008-11-03 10:36:20 +01:00
|
|
|
) as $pattern => $name)
|
|
|
|
{
|
|
|
|
if (strpos($user_agent,$pattern) !== false)
|
|
|
|
{
|
|
|
|
$agent = $name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!$agent)
|
|
|
|
{
|
2010-07-08 11:41:06 +02:00
|
|
|
//error_log("Unrecogniced GroupDAV client: HTTP_USER_AGENT='$_SERVER[HTTP_USER_AGENT]'!");
|
2008-11-03 10:36:20 +01:00
|
|
|
}
|
2010-04-21 19:44:36 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
switch ($agent)
|
|
|
|
{
|
|
|
|
case 'cfnetwork':
|
2016-04-02 10:40:34 +02:00
|
|
|
$matches = null;
|
2010-04-21 19:44:36 +02:00
|
|
|
if (preg_match('/address%20book\/([0-9.]+)/', $user_agent, $matches))
|
|
|
|
{
|
|
|
|
if ((int)$matches[1] < 868) $agent .= '_old';
|
|
|
|
}
|
2010-10-31 08:56:29 +01:00
|
|
|
break;
|
2011-09-26 12:20:27 +02:00
|
|
|
case 'kde':
|
|
|
|
// Akonadi (new KDE Pim framework) unfortunately has same user-agent as old kde
|
|
|
|
// we can only assume KDE 4.7+ uses Akonadi native resource, while below this was not available
|
|
|
|
// Unfortunately the old pre-Akonadi GroupDAV resource can still be used, but we have no way of detecting it
|
|
|
|
if (preg_match('/KHTML\/([0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $matches) && (float)$matches[1] >= 4.7)
|
|
|
|
{
|
|
|
|
$agent = 'akonadi';
|
|
|
|
}
|
|
|
|
break;
|
2010-04-21 19:44:36 +02:00
|
|
|
}
|
|
|
|
}
|
2008-11-03 10:36:20 +01:00
|
|
|
}
|
2016-04-02 10:40:34 +02:00
|
|
|
//error_log(__METHOD__."GroupDAV client: $agent");
|
2008-11-03 10:36:20 +01:00
|
|
|
return $agent;
|
|
|
|
}
|
2011-09-21 22:08:21 +02:00
|
|
|
|
2013-01-22 09:37:58 +01:00
|
|
|
/**
|
|
|
|
* Get grants of current user and app
|
|
|
|
*
|
2016-05-11 21:23:14 +02:00
|
|
|
* @return array user-id => Api\Acl::ADD|Api\Acl::READ|Api\Acl::EDIT|Api\Acl::DELETE pairs
|
2013-01-22 09:37:58 +01:00
|
|
|
*/
|
|
|
|
public function get_grants()
|
|
|
|
{
|
|
|
|
return $this->acl->get_grants($this->app, $this->app != 'addressbook');
|
|
|
|
}
|
|
|
|
|
2011-09-21 22:08:21 +02:00
|
|
|
/**
|
|
|
|
* Return priviledges for current user, default is read and read-current-user-privilege-set
|
|
|
|
*
|
|
|
|
* Priviledges are for the collection, not the resources / entries!
|
|
|
|
*
|
2011-09-22 20:46:16 +02:00
|
|
|
* @param string $path path of collection
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param int $user =null owner of the collection, default current user
|
2011-09-21 22:08:21 +02:00
|
|
|
* @return array with privileges
|
|
|
|
*/
|
2011-09-22 20:46:16 +02:00
|
|
|
public function current_user_privileges($path, $user=null)
|
2011-09-21 22:08:21 +02:00
|
|
|
{
|
2016-04-02 10:40:34 +02:00
|
|
|
unset($path); // not used, but required by function signature
|
|
|
|
|
|
|
|
static $grants=null;
|
2011-09-21 22:08:21 +02:00
|
|
|
if (is_null($grants))
|
|
|
|
{
|
2013-01-22 09:37:58 +01:00
|
|
|
$grants = $this->get_grants();
|
2011-09-21 22:08:21 +02:00
|
|
|
}
|
2011-09-22 20:46:16 +02:00
|
|
|
$priviledes = array('read-current-user-privilege-set' => 'read-current-user-privilege-set');
|
2011-09-21 22:08:21 +02:00
|
|
|
|
2016-05-11 21:23:14 +02:00
|
|
|
if (is_null($user) || $grants[$user] & Api\Acl::READ)
|
2011-09-21 22:08:21 +02:00
|
|
|
{
|
2011-09-22 20:46:16 +02:00
|
|
|
$priviledes['read'] = 'read';
|
2012-01-31 01:20:58 +01:00
|
|
|
// allows on all calendars/addressbooks to write properties, as we store them on a per-user basis
|
|
|
|
// and only allow to modify explicit named properties in CalDAV, CardDAV or Calendarserver name-space
|
|
|
|
$priviledes['write-properties'] = 'write-properties';
|
2011-09-21 22:08:21 +02:00
|
|
|
}
|
2016-05-11 21:23:14 +02:00
|
|
|
if (is_null($user) || $grants[$user] & Api\Acl::ADD)
|
2011-09-21 22:08:21 +02:00
|
|
|
{
|
2011-09-22 20:46:16 +02:00
|
|
|
$priviledes['bind'] = 'bind'; // PUT for new resources
|
2011-09-21 22:08:21 +02:00
|
|
|
}
|
2016-05-11 21:23:14 +02:00
|
|
|
if (is_null($user) || $grants[$user] & Api\Acl::EDIT)
|
2011-09-22 17:22:52 +02:00
|
|
|
{
|
2011-09-22 20:46:16 +02:00
|
|
|
$priviledes['write-content'] = 'write-content'; // otherwise iOS calendar does not allow to add events
|
2011-09-22 17:22:52 +02:00
|
|
|
}
|
2016-05-11 21:23:14 +02:00
|
|
|
if (is_null($user) || $grants[$user] & Api\Acl::DELETE)
|
2011-09-21 22:08:21 +02:00
|
|
|
{
|
2011-09-22 20:46:16 +02:00
|
|
|
$priviledes['unbind'] = 'unbind'; // DELETE
|
2011-09-21 22:08:21 +02:00
|
|
|
}
|
|
|
|
// copy/move of existing resources might require write-properties, thought we do not support an explicit PROPATCH
|
2013-01-22 09:37:58 +01:00
|
|
|
//error_log(__METHOD__."('$path', ".array2string($user).') returning '.array2string($priviledes).' '.function_backtrace());
|
2011-09-21 22:08:21 +02:00
|
|
|
return $priviledes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create the path/name for an entry
|
|
|
|
*
|
2016-08-28 11:19:08 +02:00
|
|
|
* @param int|string|array $entry
|
2011-09-21 22:08:21 +02:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
function get_path($entry)
|
|
|
|
{
|
2016-08-28 11:19:08 +02:00
|
|
|
return (is_array($entry) ? $entry[self::$path_attr] : $entry).self::$path_extension;
|
2011-09-21 22:08:21 +02:00
|
|
|
}
|
|
|
|
|
2012-02-04 21:24:01 +01:00
|
|
|
/**
|
|
|
|
* Send response-headers for a PUT (or POST with add-member query parameter)
|
|
|
|
*
|
|
|
|
* @param int|array $entry id or array of new created entry
|
|
|
|
* @param string $path
|
|
|
|
* @param int|string $retval
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param boolean $path_attr_is_name =true true: path_attr is ca(l|rd)dav_name, false: id (GroupDAV needs Location header)
|
|
|
|
* @param string $etag =null etag, to not calculate it again (if != null)
|
2012-02-04 21:24:01 +01:00
|
|
|
*/
|
2012-10-29 13:23:17 +01:00
|
|
|
function put_response_headers($entry, $path, $retval, $path_attr_is_name=true, $etag=null)
|
2012-02-04 21:24:01 +01:00
|
|
|
{
|
2013-08-02 21:28:38 +02:00
|
|
|
//error_log(__METHOD__."(".array2string($entry).", '$path', ".array2string($retval).", path_attr_is_name=$path_attr_is_name, etag=".array2string($etag).")");
|
2012-02-04 21:24:01 +01:00
|
|
|
// we should not return an etag here, as EGroupware never stores ical/vcard byte-by-byte
|
2012-02-07 18:10:52 +01:00
|
|
|
// as SOGO Connector requires ETag header to recognice as successful PUT, we are sending them again for it
|
2012-02-20 12:33:43 +01:00
|
|
|
// --> as all clients dislike not getting an ETag for a PUT, we sending it again even not storing byte-by-byte
|
|
|
|
//if (get_class($this) == 'addressbook_groupdav' && in_array(self::get_agent(),array('thunderbird','lightning')))
|
2012-02-07 18:10:52 +01:00
|
|
|
{
|
2012-10-29 13:23:17 +01:00
|
|
|
if (is_null($etag)) $etag = $this->get_etag($entry);
|
|
|
|
header('ETag: "'.$etag.'"');
|
2012-02-07 18:10:52 +01:00
|
|
|
}
|
2013-09-23 12:21:31 +02:00
|
|
|
|
|
|
|
// store (new) id for check_return_representation
|
|
|
|
$this->new_id = $this->get_path($entry);
|
|
|
|
|
2012-10-29 13:23:17 +01:00
|
|
|
// send Location header only on success AND if we dont use caldav_name as path-attribute or
|
2013-08-02 21:28:38 +02:00
|
|
|
if ((is_bool($retval) ? $retval : $retval[0] === '2') && (!$path_attr_is_name ||
|
2012-02-04 21:24:01 +01:00
|
|
|
// POST with add-member query parameter
|
|
|
|
$_SERVER['REQUEST_METHOD'] == 'POST' && isset($_GET['add-member'])))
|
|
|
|
{
|
|
|
|
$path = preg_replace('|(.*)/[^/]*|', '\1/', $path);
|
|
|
|
header('Location: '.$this->base_uri.$path.$this->get_path($entry));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-04 02:03:56 +01:00
|
|
|
/**
|
|
|
|
* Return calendars/addressbooks shared from other users with the current one
|
|
|
|
*
|
|
|
|
* return array account_id => account_lid pairs
|
|
|
|
*/
|
|
|
|
function get_shared()
|
|
|
|
{
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2012-02-04 02:24:34 +01:00
|
|
|
/**
|
|
|
|
* Return appliction specific settings
|
|
|
|
*
|
2012-02-14 18:38:45 +01:00
|
|
|
* @param array $hook_data
|
|
|
|
* @return array of array with settings
|
2012-02-04 02:24:34 +01:00
|
|
|
*/
|
2012-02-14 18:38:45 +01:00
|
|
|
static function get_settings($hook_data)
|
2012-02-04 02:24:34 +01:00
|
|
|
{
|
2016-04-02 10:40:34 +02:00
|
|
|
unset($hook_data); // not used, but required by function signature
|
|
|
|
|
2012-02-04 02:24:34 +01:00
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2011-09-21 22:08:21 +02:00
|
|
|
/**
|
|
|
|
* Add a resource
|
|
|
|
*
|
|
|
|
* @param string $path path of collection, NOT entry!
|
|
|
|
* @param array $entry
|
|
|
|
* @param array $props
|
|
|
|
* @return array with values for keys 'path' and 'props'
|
|
|
|
*/
|
|
|
|
public function add_resource($path, array $entry, array $props)
|
|
|
|
{
|
2013-02-25 12:17:59 +01:00
|
|
|
// only run get_etag, if we really need it, as it might be expensive (eg. calendar)
|
|
|
|
if (!isset($props['getetag']))
|
|
|
|
{
|
|
|
|
$props['getetag'] = $this->get_etag($entry);
|
|
|
|
}
|
2011-09-21 22:08:21 +02:00
|
|
|
foreach(array(
|
|
|
|
'getcontenttype' => 'text/calendar',
|
|
|
|
'getlastmodified' => $entry['modified'],
|
|
|
|
'displayname' => $entry['title'],
|
|
|
|
) as $name => $value)
|
|
|
|
{
|
|
|
|
if (!isset($props[$name]))
|
|
|
|
{
|
|
|
|
$props[$name] = $value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if requested add privileges
|
|
|
|
$privileges = array('read', 'read-current-user-privilege-set');
|
2016-04-02 12:44:17 +02:00
|
|
|
if ($this->caldav->prop_requested('current-user-privilege-set') === true && !isset($props['current-user-privilege-set']))
|
2011-09-21 22:08:21 +02:00
|
|
|
{
|
2016-05-11 21:23:14 +02:00
|
|
|
if ($this->check_access(Api\Acl::EDIT, $entry))
|
2011-09-21 22:08:21 +02:00
|
|
|
{
|
|
|
|
$privileges[] = 'write-content';
|
|
|
|
}
|
|
|
|
}
|
2016-04-02 12:44:17 +02:00
|
|
|
if ($this->caldav->prop_requested('owner') === true && !isset($props['owner']) &&
|
2013-02-25 11:30:44 +01:00
|
|
|
($account_lid = $this->accounts->id2name($entry['owner'])))
|
2011-09-21 22:08:21 +02:00
|
|
|
{
|
|
|
|
$type = $this->accounts->get_type($entry['owner']) == 'u' ? 'users' : 'groups';
|
2016-04-02 12:44:17 +02:00
|
|
|
$props['owner'] = Api\CalDAV::mkprop('href', $this->base_uri.'/principals/'.$type.'/'.$account_lid.'/');
|
2011-09-21 22:08:21 +02:00
|
|
|
}
|
2016-04-02 12:44:17 +02:00
|
|
|
// we urldecode here, as Api\CalDAV uses a minimal (#?%) urlencoding for incomming pathes and urlencodes pathes in propfind
|
|
|
|
return $this->caldav->add_resource($path.urldecode($this->get_path($entry)), $props, $privileges);
|
2011-09-21 22:08:21 +02:00
|
|
|
}
|
2012-09-24 09:07:57 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return base uri, making sure it's either a full uri (incl. protocoll and host) or just a path
|
|
|
|
*
|
|
|
|
* base_uri of WebDAV class can be both, depending on EGroupware config
|
|
|
|
*
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param boolean $full_uri =true
|
2012-09-24 09:07:57 +02:00
|
|
|
* @return string eg. https://domain.com/egroupware/groupdav.php
|
|
|
|
*/
|
|
|
|
public function base_uri($full_uri=true)
|
|
|
|
{
|
2016-04-02 10:40:34 +02:00
|
|
|
static $uri=null;
|
|
|
|
static $path=null;
|
2012-09-24 09:07:57 +02:00
|
|
|
|
|
|
|
if (!isset($uri))
|
|
|
|
{
|
2016-04-02 12:44:17 +02:00
|
|
|
$uri = $path = $this->caldav->base_uri;
|
2012-09-24 09:07:57 +02:00
|
|
|
if ($uri[0] == '/')
|
|
|
|
{
|
|
|
|
$uri = ($_SERVER["HTTPS"] === "on" ? "https:" : "http:") .'//' . $_SERVER['HTTP_HOST'] . $uri;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$path = parse_url($uri, PHP_URL_PATH);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $full_uri ? $uri : $path;
|
|
|
|
}
|
2012-09-24 12:26:29 +02:00
|
|
|
|
2012-09-26 16:30:47 +02:00
|
|
|
/**
|
|
|
|
* sync-token to be filled by propfind_callback and returned by get_sync_token method
|
|
|
|
*/
|
|
|
|
protected $sync_collection_token;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Query sync-token from a just run sync-collection report
|
|
|
|
*
|
|
|
|
* Modified time is taken from value filled by propfind_callback in sync_collection_token.
|
|
|
|
*
|
|
|
|
* @param string $path
|
|
|
|
* @param int $user parameter necessary to call getctag, if no $token specified
|
|
|
|
* @return string
|
|
|
|
*/
|
2014-02-20 18:46:15 +01:00
|
|
|
public function get_sync_collection_token($path, $user=null, $more_results=null)
|
2012-09-26 16:30:47 +02:00
|
|
|
{
|
2014-02-20 18:46:15 +01:00
|
|
|
//error_log(__METHOD__."('$path', $user, more_results=$more_results) this->sync_collection_token=".$this->sync_collection_token);
|
|
|
|
if ($more_results)
|
|
|
|
{
|
|
|
|
$error =
|
|
|
|
' <D:response>
|
2016-04-02 12:44:17 +02:00
|
|
|
<D:href>'.htmlspecialchars($this->caldav->base_uri.$this->caldav->path).'</D:href>
|
2014-02-20 18:46:15 +01:00
|
|
|
<D:status>HTTP/1.1 507 Insufficient Storage</D:status>
|
|
|
|
<D:error><D:number-of-matches-within-limits/></D:error>
|
|
|
|
</D:response>
|
|
|
|
';
|
2016-04-02 12:44:17 +02:00
|
|
|
if ($this->caldav->crrnd)
|
2014-02-20 18:46:15 +01:00
|
|
|
{
|
|
|
|
$error = str_replace(array('<D:', '</D:'), array('<', '</'), $error);
|
|
|
|
}
|
|
|
|
echo $error;
|
|
|
|
}
|
2012-09-26 16:30:47 +02:00
|
|
|
return $this->get_sync_token($path, $user, $this->sync_collection_token);
|
|
|
|
}
|
|
|
|
|
2012-09-24 12:26:29 +02:00
|
|
|
/**
|
|
|
|
* Query sync-token
|
|
|
|
*
|
|
|
|
* We use ctag / max. modification time as sync-token. As garnularity is 1sec, we can never be sure,
|
|
|
|
* if there are more modifications to come in the current second.
|
|
|
|
*
|
|
|
|
* Therefor we are never returning current time, but 1sec less!
|
|
|
|
*
|
2012-09-26 16:30:47 +02:00
|
|
|
* Modified time is either taken from value filled by propfind_callback in $this->sync_token or
|
|
|
|
* by call to getctag();
|
|
|
|
*
|
2012-09-24 12:26:29 +02:00
|
|
|
* @param string $path
|
2012-09-26 16:30:47 +02:00
|
|
|
* @param int $user parameter necessary to call getctag, if no $token specified
|
2016-04-02 10:40:34 +02:00
|
|
|
* @param int $token =null modification time, default call getctag($path, $user) to fetch it
|
2012-09-24 12:26:29 +02:00
|
|
|
* @return string
|
|
|
|
*/
|
2012-09-26 16:30:47 +02:00
|
|
|
public function get_sync_token($path, $user, $token=null)
|
2012-09-24 12:26:29 +02:00
|
|
|
{
|
2012-09-26 16:30:47 +02:00
|
|
|
if (!isset($token)) $token = $this->getctag($path, $user);
|
2012-09-24 12:26:29 +02:00
|
|
|
|
2012-09-26 16:30:47 +02:00
|
|
|
// never return current time, as more modifications might happen due to second granularity --> return 1sec less
|
|
|
|
if ($token >= (int)$GLOBALS['egw_info']['flags']['page_start_time'])
|
|
|
|
{
|
|
|
|
$token = (int)$GLOBALS['egw_info']['flags']['page_start_time'] - 1;
|
|
|
|
}
|
|
|
|
return $this->base_uri().$path.$token;
|
2012-09-24 12:26:29 +02:00
|
|
|
}
|
2009-04-02 14:39:52 +02:00
|
|
|
}
|