mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-03-13 08:00:15 +00:00
As a basic checkpoint we are now creating a lock for a resource.
This commit is contained in:
parent
0d66c9d9b7
commit
9eff8f92ae
@ -14,6 +14,8 @@
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
||||
*/
|
||||
|
||||
define('DEPTH_INFINITY', 9999);
|
||||
|
||||
/**
|
||||
* A class for collecting things to do with this request.
|
||||
*
|
||||
@ -21,7 +23,6 @@
|
||||
*/
|
||||
class CalDAVRequest
|
||||
{
|
||||
const DEPTH_INFINITY = 9999;
|
||||
|
||||
/**
|
||||
* Create a new CalDAVRequest object.
|
||||
@ -40,7 +41,7 @@ class CalDAVRequest
|
||||
* A variety of requests may set the "Depth" header to control recursion
|
||||
*/
|
||||
$this->depth = ( isset($_SERVER['HTTP_DEPTH']) ? $_SERVER['HTTP_DEPTH'] : 0 );
|
||||
if ( $this->depth == 'infinity' ) $this->depth = self::DEPTH_INFINITY;
|
||||
if ( $this->depth == 'infinity' ) $this->depth = DEPTH_INFINITY;
|
||||
$this->depth = intval($this->depth);
|
||||
|
||||
/**
|
||||
@ -55,6 +56,24 @@ class CalDAVRequest
|
||||
if ( isset($_SERVER['HTTP_IF']) ) $this->if_clause = $_SERVER['HTTP_IF'];
|
||||
if ( isset($_SERVER['HTTP_LOCK-TOKEN']) ) $this->lock_token = $_SERVER['HTTP_LOCK-TOKEN'];
|
||||
|
||||
/**
|
||||
* LOCK things use a "Timeout" header to set a series of reducing alternative values
|
||||
*/
|
||||
if ( isset($_SERVER['HTTP_TIMEOUT']) ) {
|
||||
$timeouts = split( ',', $_SERVER['HTTP_TIMEOUT'] );
|
||||
foreach( $timeouts AS $k => $v ) {
|
||||
if ( strtolower($v) == 'infinite' ) {
|
||||
$this->timeout = (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100);
|
||||
break;
|
||||
}
|
||||
elseif ( strtolower(substr($v,0,7)) == 'second-' ) {
|
||||
$this->timeout = max( intval(substr($v,7)), (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( ! isset($this->timeout) ) $this->timeout = (isset($c->default_lock_timeout) ? $c->default_lock_timeout : 900);
|
||||
}
|
||||
|
||||
/**
|
||||
* Our path is /<script name>/<user name>/<user controlled> if it ends in
|
||||
* a trailing '/' then it is referring to a DAV 'collection' but otherwise
|
||||
@ -157,11 +176,12 @@ class CalDAVRequest
|
||||
*/
|
||||
function IsLocked() {
|
||||
if ( !isset($this->_locks_found) ) {
|
||||
$this->_locks_found = array();
|
||||
/**
|
||||
* Find the locks that might apply and load them into an array
|
||||
*/
|
||||
$sql = "SELECT * FROM locks WHERE ? ~ '^'||dav_name||?";
|
||||
$qry = new PgQuery($sql, $this->path, ($this->IsInfiniteDepth() ? '', '$') );
|
||||
$sql = "SELECT * FROM locks WHERE ?::text ~ ('^'||dav_name||?)::text;";
|
||||
$qry = new PgQuery($sql, $this->path, ($this->IsInfiniteDepth() ? '' : '$') );
|
||||
if ( $qry->Exec("caldav",__LINE__,__FILE__) ) {
|
||||
while( $lock_row = $qry->Fetch() ) {
|
||||
$this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
|
||||
@ -174,7 +194,7 @@ class CalDAVRequest
|
||||
}
|
||||
|
||||
foreach( $this->_locks_found AS $lock_token => $lock_row ) {
|
||||
if ( $lock_row->depth == self::DEPTH_INFINITY || $lock_row->dav_name == $dav_name ) {
|
||||
if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->path ) {
|
||||
return $lock_token;
|
||||
}
|
||||
}
|
||||
@ -183,6 +203,39 @@ class CalDAVRequest
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name for this depth: 0, 1, infinity
|
||||
*/
|
||||
function GetDepthName( ) {
|
||||
if ( $this->IsInfiniteDepth() ) return 'infinity';
|
||||
return $this->depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locked row, either from the cache or from the database
|
||||
*
|
||||
* @param string $dav_name The resource which we want to know the lock status for
|
||||
*/
|
||||
function GetLockRow( $lock_token ) {
|
||||
if ( isset($this->_locks_found) && isset($this->_locks_found[$lock_token]) ) {
|
||||
return $this->_locks_found[$lock_token];
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM locks WHERE opaquelocktoken = ?;";
|
||||
$qry = new PgQuery($sql, $lock_token );
|
||||
if ( $qry->Exec("caldav",__LINE__,__FILE__) ) {
|
||||
$lock_row = $qry->Fetch();
|
||||
$this->_locks_found = array( $lock_token => $lock_row );
|
||||
return $this->_locks_found[$lock_token];
|
||||
}
|
||||
else {
|
||||
$request->DoResponse( 500, translate("Database Error") );
|
||||
}
|
||||
|
||||
return false; // Nothing matched
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks to see whether the lock token given matches one of the ones handed in
|
||||
* with the request.
|
||||
@ -230,7 +283,7 @@ class CalDAVRequest
|
||||
* Returns true if the request asked for infinite depth
|
||||
*/
|
||||
function IsInfiniteDepth( ) {
|
||||
return ($this->depth == self::DEPTH_INFINITY);
|
||||
return ($this->depth == DEPTH_INFINITY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -3,4 +3,158 @@
|
||||
* We support both LOCK and UNLOCK methods in this function
|
||||
*/
|
||||
|
||||
if ( ! $request->AllowedTo('write') ) {
|
||||
$request->DoResponse( 403, translate("You do not have sufficient access to lock that") );
|
||||
}
|
||||
|
||||
require_once("XMLElement.php");
|
||||
|
||||
$unsupported = array();
|
||||
$lockinfo = array();
|
||||
$inside = array();
|
||||
|
||||
foreach( $request->xml_tags AS $k => $v ) {
|
||||
|
||||
$tag = $v['tag'];
|
||||
dbg_error_log( "LOCK", " Handling Tag '%s' => '%s' ", $k, $v );
|
||||
switch ( $tag ) {
|
||||
case 'DAV::LOCKINFO':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
if ( $v['type'] == "open" ) {
|
||||
$lockscope = "";
|
||||
$locktype = "";
|
||||
$lockowner = "";
|
||||
$inside[$tag] = true;
|
||||
}
|
||||
else if ( $inside[$tag] && $v['type'] == "close" ) {
|
||||
$lockinfo['scope'] = $lockscope; unset($lockscope);
|
||||
$lockinfo['type'] = $locktype; unset($locktype);
|
||||
$lockinfo['owner'] = $lockowner; unset($lockowner);
|
||||
$inside[$tag] = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::OWNER':
|
||||
case 'DAV::LOCKTYPE':
|
||||
case 'DAV::LOCKSCOPE':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
if ( $inside['DAV::LOCKINFO'] ) {
|
||||
if ( $v['type'] == "open" ) {
|
||||
$inside[$tag] = true;
|
||||
}
|
||||
else if ( $inside[$tag] && $v['type'] == "close" ) {
|
||||
$inside[$tag] = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::EXCLUSIVE':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
if ( $inside['DAV::LOCKSCOPE'] && $v['type'] == "complete" ) {
|
||||
$lockscope = strtolower(substr($tag,5));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::WRITE':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
if ( $inside['DAV::LOCKTYPE'] && $v['type'] == "complete" ) {
|
||||
$locktype = strtolower(substr($tag,5));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::HREF':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
dbg_log_array( "LOCK", "DAV:HREF", $v, true );
|
||||
if ( $inside['DAV::OWNER'] && $v['type'] == "complete" ) {
|
||||
$lockowner = $v['value'];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( preg_match('/^(.*):([^:]+)$/', $tag, $matches) ) {
|
||||
$unsupported[$matches[2]] = $matches[1];
|
||||
}
|
||||
else {
|
||||
$unsupported[$tag] = "";
|
||||
}
|
||||
dbg_error_log( "LOCK", "Unhandled tag >>%s<<", $tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function lock_collection( $depth, $user_no, $path ) {
|
||||
dbg_error_log( "LOCK", "Attempting to lock collection '%s' to depth %d", $path, $depth);
|
||||
$lock = new XMLElement("lockinfo", '', array("xmlns" => "DAV:") );
|
||||
}
|
||||
|
||||
|
||||
function lock_resource( $user_no, $path ) {
|
||||
global $request, $lockinfo;
|
||||
|
||||
dbg_error_log( "LOCK", "Attempting to lock resource '%s'", $path);
|
||||
if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
|
||||
if ( $request->ValidateLockToken($lock_token) ) {
|
||||
$sql = "UPDATE locks SET start = current_timestamp WHERE opaquelocktoken = ?;";
|
||||
$qry = new PgQuery($sql, $lock_token );
|
||||
$qry->Exec("LOCK",__LINE__,__FILE__);
|
||||
}
|
||||
else {
|
||||
/** FIXME: Deny the lock */
|
||||
}
|
||||
}
|
||||
else {
|
||||
$lock_token = uuid();
|
||||
$sql = "INSERT INTO locks ( dav_name, opaquelocktoken, type, scope, depth, owner, timeout, start ) VALUES( ?, ?, ?, ?, ?, ?, ?::interval, current_timestamp );";
|
||||
$qry = new PgQuery($sql, $request->path, $lock_token, $lockinfo['type'], $lockinfo['scope'], $request->depth, $lockinfo['owner'], $request->timeout.' seconds' );
|
||||
$qry->Exec("LOCK",__LINE__,__FILE__);
|
||||
}
|
||||
|
||||
$lock_row = $request->GetLockRow($lock_token);
|
||||
$activelock = array(
|
||||
new XMLElement( 'locktype', new XMLElement( $lockinfo['type'] )),
|
||||
new XMLElement( 'lockscope', new XMLElement( $lockinfo['scope'] )),
|
||||
new XMLElement( 'depth', $request->GetDepthName() ),
|
||||
new XMLElement( 'owner', new XMLElement( 'href', $lockinfo['owner'] )),
|
||||
new XMLElement( 'timeout', 'Second-'.$request->timeout),
|
||||
new XMLElement( 'locktoken', new XMLElement( 'href', 'opaquelocktoken:'.$lock_token ))
|
||||
);
|
||||
$lock = new XMLElement("lockdiscovery", new XMLElement( "activelock", $activelock), array("xmlns" => "DAV:") );
|
||||
|
||||
return $lock;
|
||||
}
|
||||
|
||||
|
||||
if ( count($unsupported) > 0 ) {
|
||||
/**
|
||||
* That's a *BAD* request!
|
||||
*/
|
||||
$badprops = new XMLElement( "prop" );
|
||||
foreach( $unsupported AS $k => $v ) {
|
||||
// Not supported at this point...
|
||||
dbg_error_log("ERROR", " LOCK: Support for $v:$k properties is not implemented yet");
|
||||
$badprops->NewElement(strtolower($k),false,array("xmlns" => strtolower($v)));
|
||||
}
|
||||
$error = new XMLElement("error", new XMLElement( "LOCK",$badprops), array("xmlns" => "DAV:") );
|
||||
|
||||
$request->DoResponse( 403, $error->Render(0,'<?xml version="1.0" ?>'), 'text/xml; charset="utf-8"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Something that we can handle, at least roughly correctly.
|
||||
*/
|
||||
$url = $c->protocol_server_port_script . $request->path ;
|
||||
$url = preg_replace( '#/$#', '', $url);
|
||||
|
||||
if ( $request->IsCollection() ) {
|
||||
$response = lock_collection( $request->depth, (isset($request->user_no) ? $request->user_no : $session->user_no), $request->path );
|
||||
}
|
||||
else {
|
||||
$response = lock_resource( (isset($request->user_no) ? $request->user_no : $session->user_no), $request->path );
|
||||
}
|
||||
|
||||
$prop = new XMLElement( "prop", $response, array('xmlns'=>'DAV:') );
|
||||
dbg_log_array( "LOCK", "XML", $response, true );
|
||||
$xmldoc = $prop->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
|
||||
$request->DoResponse( 200, $xmldoc, 'text/xml; charset="utf-8"' );
|
||||
|
||||
?>
|
||||
@ -2,7 +2,7 @@
|
||||
# Test specification compliance for LOCK request
|
||||
#
|
||||
TYPE=LOCK
|
||||
URL=http://mycaldav/caldav.php/user1/home/
|
||||
URL=http://mycaldav/caldav.php/user1/home/i1278618276.ics
|
||||
HEADER=Depth: 0
|
||||
HEADER=Timeout: Infinite,Second-4100000000
|
||||
HEADER=Content-type: text/xml; charset="utf-8"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user