Getting 'MOVE' working has proven surprisingly complex.

This commit is contained in:
Andrew McMillan 2009-11-04 00:17:10 +13:00
parent a7d7bb63d5
commit 3df6ccc4ba
14 changed files with 466 additions and 134 deletions

View File

@ -35,6 +35,7 @@ GRANT SELECT,INSERT,UPDATE,DELETE
ON relationship_type
ON sync_tokens
ON sync_changes
ON grants
GRANT SELECT,UPDATE
ON relationship_type_rt_id_seq

View File

@ -417,7 +417,8 @@ BEGIN
IF TG_OP = 'UPDATE' THEN
IF NEW.dav_name != OLD.dav_name THEN
UPDATE caldav_data
SET dav_name = replace( dav_name, OLD.dav_name, NEW.dav_name)
SET dav_name = replace( dav_name, OLD.dav_name, NEW.dav_name),
user_no = NEW.user_no
WHERE substring(dav_name from 1 for char_length(OLD.dav_name)) = OLD.dav_name;
END IF;
END IF;
@ -475,6 +476,44 @@ CREATE TRIGGER caldav_data_modified AFTER INSERT OR UPDATE OR DELETE ON caldav_d
FOR EACH ROW EXECUTE PROCEDURE caldav_data_modified();
DROP TRIGGER caldav_data_sync_dav_id ON caldav_data CASCADE;
DROP TRIGGER calendar_item_sync_dav_id ON calendar_item CASCADE;
CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS $$
DECLARE
BEGIN
IF TG_OP = 'DELETE' THEN
-- Just let the ON DELETE CASCADE handle this case
RETURN OLD;
END IF;
IF NEW.dav_id IS NULL THEN
NEW.dav_id = nextval('dav_id_seq');
END IF;
IF TG_OP = 'UPDATE' THEN
IF OLD.dav_id != NEW.dav_id OR OLD.collection_id != NEW.collection_id
OR OLD.user_no != NEW.user_no OR OLD.dav_name != NEW.dav_name THEN
UPDATE calendar_item SET dav_id = NEW.dav_id, user_no = NEW.user_no,
collection_id = NEW.collection_id, dav_name = NEW.dav_name
WHERE dav_name = OLD.dav_name OR dav_id = OLD.dav_id;
END IF;
RETURN NEW;
END IF;
UPDATE calendar_item SET dav_id = NEW.dav_id, user_no = NEW.user_no,
collection_id = NEW.collection_id, dav_name = NEW.dav_name
WHERE dav_name = NEW.dav_name OR dav_id = NEW.dav_id;
RETURN NEW;
END
$$ LANGUAGE 'plpgsql';
CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
-- New in 1.2.6
CREATE or REPLACE FUNCTION legacy_privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
@ -927,14 +966,14 @@ BEGIN
-- We need to canonicalise the path, so:
-- If it matches '/' + some characters (+ optional '/') => a principal URL
IF in_path ~ '^/[^/]+/?$' THEN
alt1_path := replace(in_path, '/', '')
alt1_path := replace(in_path, '/', '');
SELECT principal_privileges(in_accessor,principal_id) INTO out_conferred FROM usr JOIN principal USING(user_no) WHERE username = alt1_path;
RETURN out_conferred;
END IF;
-- Otherwise look for the longest segment matching up to the last '/', or if we append one, or if we replace a final '.ics' with one.
alt1_path := in_path;
IF alt1_path ~ '\.ics$' THEN
IF alt1_path ~ E'\\.ics$' THEN
alt1_path := substr(alt1_path, 1, length(alt1_path) - 4) || '/';
END IF;
alt2_path := regexp_replace( in_path, '[^/]*$', '');

View File

@ -209,43 +209,41 @@ CREATE TABLE freebusy_ticket (
);
CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS '
CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS $$
DECLARE
BEGIN
IF TG_OP = ''DELETE'' THEN
IF TG_OP = 'DELETE' THEN
-- Just let the ON DELETE CASCADE handle this case
RETURN OLD;
END IF;
IF NEW.dav_id IS NULL THEN
NEW.dav_id = nextval(''dav_id_seq'');
NEW.dav_id = nextval('dav_id_seq');
END IF;
IF TG_OP = ''UPDATE'' THEN
IF OLD.dav_id = NEW.dav_id THEN
-- Nothing to do
RETURN NEW;
IF TG_OP = 'UPDATE' THEN
IF OLD.dav_id != NEW.dav_id OR OLD.collection_id != NEW.collection_id
OR OLD.user_no != NEW.user_no OR OLD.dav_name != NEW.dav_name THEN
UPDATE calendar_item SET dav_id = NEW.dav_id, user_no = NEW.user_no,
collection_id = NEW.collection_id, dav_name = NEW.dav_name
WHERE dav_name = OLD.dav_name OR dav_id = OLD.dav_id;
END IF;
RETURN NEW;
END IF;
IF TG_RELNAME = ''caldav_data'' THEN
UPDATE calendar_item SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
ELSE
UPDATE caldav_data SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
END IF;
UPDATE calendar_item SET dav_id = NEW.dav_id, user_no = NEW.user_no,
collection_id = NEW.collection_id, dav_name = NEW.dav_name
WHERE dav_name = NEW.dav_name OR dav_id = NEW.dav_id;
RETURN NEW;
END
' LANGUAGE 'plpgsql';
$$ LANGUAGE 'plpgsql';
CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
CREATE TRIGGER calendar_item_sync_dav_id AFTER INSERT OR UPDATE ON calendar_item
FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
-- Only needs SELECT access by website.
CREATE TABLE principal_type (

View File

@ -53,6 +53,7 @@ switch ( $request->method ) {
case 'MKCOL': include_once("caldav-MKCOL.php"); break;
case 'PUT': include_once("caldav-PUT.php"); break;
case 'POST': include_once("caldav-POST.php"); break;
case 'MOVE': include_once("caldav-MOVE.php"); break;
case 'GET': include_once("caldav-GET.php"); break;
case 'HEAD': include_once("caldav-GET.php"); break;
case 'DELETE': include_once("caldav-DELETE.php"); break;

View File

@ -350,6 +350,14 @@ class AwlQuery
}
/**
* Return the count of rows retrieved/affected
*/
function rows() {
return $this->rows;
}
/**
* Execute the query, logging any debugging.
*

View File

@ -163,6 +163,8 @@ class CalDAVPrincipal
if ( !isset($this->modified) ) $this->modified = ISODateToHTTPDate($this->updated);
if ( !isset($this->created) ) $this->created = ISODateToHTTPDate($this->joined);
$this->dav_etag = md5($this->username . $this->updated);
$this->by_email = false;
$this->principal_url = ConstructURL( '/'.$this->username.'/', true );
$this->url = $this->principal_url;
@ -180,9 +182,9 @@ class CalDAVPrincipal
$this->dropbox_url = sprintf( '%s.drop/', $this->url);
$this->notifications_url = sprintf( '%s.notify/', $this->url);
if ( isset ( $c->notifications_server ) ) {
if ( isset ( $c->notifications_server ) ) {
$this->xmpp_uri = 'xmpp:pubsub.'.$c->notifications_server['host'].'?pubsub;node=/home/'.$c->notifications_server['host'];
$this->xmpp_uri .= '/'.preg_replace ( '/@.*$/', '', $c->notifications_server['jid'] ).'/DAViCal'.$this->url;
$this->xmpp_uri .= '/'.preg_replace ( '/@.*$/', '', $c->notifications_server['jid'] ).'/DAViCal'.$this->url;
$this->xmpp_server = $c->notifications_server['host'];
}
@ -284,7 +286,7 @@ class CalDAVPrincipal
$username = $user->username;
}
}
elseif( $user = getUserByName( $username, 'caldav') ) {
elseif( $user = getUserByName( $username, 'principal') ) {
$user_no = $user->user_no;
}
return $username;
@ -315,6 +317,14 @@ class CalDAVPrincipal
}
/**
* Return the privileges bits for the current session user to this resource
*/
function Privileges() {
return $this->privileges;
}
/**
* Returns a representation of the principal as a collection
*/

View File

@ -77,6 +77,12 @@ class CalDAVRequest
*/
var $collection_type;
/**
* The type of collection being requested:
* calendar, schedule-inbox, schedule-outbox
*/
protected $exists;
/**
* A static structure of supported privileges.
*/

View File

@ -26,25 +26,25 @@ function privilege_to_bits( $raw_privs ) {
foreach( $raw_privs AS $priv ) {
$trim_priv = trim(strtolower(preg_replace( '/^.*:/', '', $priv)));
switch( $trim_priv ) {
case 'read' : $out_priv &= 4609; break; // 1 + 512 + 4096
case 'write' : $out_priv &= 198; break; // 2 + 4 + 64 + 128
case 'write-properties' : $out_priv &= 2; break;
case 'write-content' : $out_priv &= 4; break;
case 'unlock' : $out_priv &= 8; break;
case 'read-acl' : $out_priv &= 16; break;
case 'read-current-user-privilege-set' : $out_priv &= 32; break;
case 'bind' : $out_priv &= 64; break;
case 'unbind' : $out_priv &= 128; break;
case 'write-acl' : $out_priv &= 256; break;
case 'read-free-busy' : $out_priv &= 4608; break; // 512 + 4096
case 'schedule-deliver' : $out_priv &= 7168; break; // 1024 + 2048 + 4096
case 'schedule-deliver-invite' : $out_priv &= 1024; break;
case 'schedule-deliver-reply' : $out_priv &= 2048; break;
case 'schedule-query-freebusy' : $out_priv &= 4096; break;
case 'schedule-send' : $out_priv &= 57344; break; // 8192 + 16384 + 32768
case 'schedule-send-invite' : $out_priv &= 8192; break;
case 'schedule-send-reply' : $out_priv &= 16384; break;
case 'schedule-send-freebusy' : $out_priv &= 32768; break;
case 'read' : $out_priv |= 4609; break; // 1 + 512 + 4096
case 'write' : $out_priv |= 198; break; // 2 + 4 + 64 + 128
case 'write-properties' : $out_priv |= 2; break;
case 'write-content' : $out_priv |= 4; break;
case 'unlock' : $out_priv |= 8; break;
case 'read-acl' : $out_priv |= 16; break;
case 'read-current-user-privilege-set' : $out_priv |= 32; break;
case 'bind' : $out_priv |= 64; break;
case 'unbind' : $out_priv |= 128; break;
case 'write-acl' : $out_priv |= 256; break;
case 'read-free-busy' : $out_priv |= 4608; break; // 512 + 4096
case 'schedule-deliver' : $out_priv |= 7168; break; // 1024 + 2048 + 4096
case 'schedule-deliver-invite' : $out_priv |= 1024; break;
case 'schedule-deliver-reply' : $out_priv |= 2048; break;
case 'schedule-query-freebusy' : $out_priv |= 4096; break;
case 'schedule-send' : $out_priv |= 57344; break; // 8192 + 16384 + 32768
case 'schedule-send-invite' : $out_priv |= 8192; break;
case 'schedule-send-reply' : $out_priv |= 16384; break;
case 'schedule-send-freebusy' : $out_priv |= 32768; break;
default:
dbg_error_log( 'ERROR', 'Cannot convert privilege of "%s" into bits.', $priv );
@ -123,7 +123,7 @@ class DAVResource
/**
* @var The actual resource content, if it exists and is not a collection
*/
protected $content;
protected $resource;
/**
* @var The type of the resource, possibly multiple
@ -195,7 +195,7 @@ class DAVResource
$this->exists = null;
$this->dav_name = null;
$this->unique_tag = null;
$this->content = null;
$this->resource = null;
$this->collection = null;
$this->principal = null;
$this->resourcetype = null;
@ -231,7 +231,7 @@ class DAVResource
$this->exists = true;
foreach( $row AS $k => $v ) {
dbg_error_log( 'resource', 'Processing resource property "%s" has "%s".', $row->dav_name, $k );
dbg_error_log( 'DAVResource', 'Processing resource property "%s" has "%s".', $row->dav_name, $k );
switch ( $k ) {
case 'dav_etag':
$this->unique_tag = '"'.$v.'"';
@ -258,16 +258,20 @@ class DAVResource
$ourpath = $matches[1]. '/';
}
/** remove any leading protocol/server/port/prefix... */
$base_path = ConstructURL('/');
if ( preg_match( '%^(.*?)'.str_replace('%', '\\%',$base_path).'(.*)$%', $ourpath, $matches ) ) {
if ( $matches[1] == '' || $matches[1] == $c->protocol_server_port ) {
$ourpath = $matches[2];
}
}
/** strip doubled slashes */
if ( strstr($ourpath,'//') ) $ourpath = preg_replace( '#//+#', '/', $ourpath);
/** remove any leading protocol/server/port/prefix... */
$base_path = ConstructURL('/');
$this->dav_name = str_replace( $base_path, '/', $ourpath );
if ( substr($ourpath,0,1) != '/' ) $ourpath = '/'.$ourpath;
if ( substr($this->dav_name,0,1) != '/' ) {
$this->dav_name = '/'.$this->dav_name;
}
$this->dav_name = $ourpath;
}
@ -300,13 +304,13 @@ class DAVResource
$sql = $base_sql .'dav_name = :raw_path ';
$params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id );
if ( !preg_match( '#/$#', $this->dav_name ) ) {
$sql .= ' OR dav_name = :up_to_slash OR dav_name = :plus_slash';
$sql .= ' OR dav_name = :up_to_slash OR dav_name = :plus_slash ';
$params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
$params[':plus_slash'] = $this->dav_name.'/';
}
$sql .= 'ORDER BY LENGTH(dav_name) DESC LIMIT 1';
$qry = new AwlQuery( $sql, $params );
if ( $qry->Exec('DAVResource') && $qry->rows == 1 && ($row = $qry->Fetch()) ) {
if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
$this->collection = $row;
if ( $row->is_calendar == 't' )
$this->collection->type = 'calendar';
@ -331,8 +335,9 @@ EOSQL;
$qry->Exec('DAVResource');
dbg_error_log( 'DAVResource', 'Created new collection as "$displayname".' );
$qry = new AwlQuery( $base_sql . 'user_no = :user_no AND dav_name = :dav_name', $params );
if ( $qry->Exec('DAVResource') && $qry->rows == 1 && ($row = $qry->Fetch()) ) {
$params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id );
$qry = new AwlQuery( $base_sql . ' dav_name = :raw_path', $params );
if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
$this->collection = $row;
}
}
@ -342,12 +347,6 @@ EOSQL;
$this->proxy_type = $matches[3];
$this->collection->dav_name = $matches[1].'/';
}
else if ( $this->options['allow_by_email'] && preg_match( '#^/(\S+@\S+[.]\S+)/?$#', $this->dav_name, $matches) ) {
/** @TODO: perhaps we should deprecate this in favour of scheduling extensions */
$this->collection->type = 'principal_email';
$this->collection->dav_name = $matches[1].'/';
$this->_is_principal = true;
}
else if ( preg_match( '#^(/[^/]+)/?$#', $this->dav_name, $matches) ) {
$this->collection->dav_name = $matches[1].'/';
$this->collection->type = 'principal';
@ -362,12 +361,31 @@ EOSQL;
$this->collection->dav_name = '/';
$this->collection->type = 'root';
}
else {
dbg_error_log( 'DAVResource', 'No collection for path "%s".', $this->dav_name );
$this->collection->exists = false;
$this->collection->dav_name = preg_replace('{/[^/]*$}', '/', $this->dav_name);
}
$this->_is_collection = ( $this->collection->dav_name == $this->dav_name || $this->collection->dav_name == $this->dav_name.'/' );
if ( $this->_is_collection ) {
$this->_is_calendar = $this->collection->is_calendar;
$this->_is_addressbook = $this->collection->is_addressbook;
$this->dav_name = $this->collection->dav_name;
$this->_is_calendar = ($this->collection->type == 'calendar');
$this->_is_addressbook = ($this->collection->type == 'addressbook');
$this->contenttype = 'httpd/unix-directory';
if ( isset($this->collection->dav_etag) ) $this->unique_tag = $this->collection->dav_etag;
if ( isset($this->collection->created) ) $this->created = $this->collection->created;
if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
if ( isset($this->collection->resourcetype) )
$this->resourcetype = $this->collection->resourcetype;
else {
$this->resourcetype = '<DAV::collection/>';
if ( $this->_is_principal )
$this->resourcetype .= '<DAV::principal/>';
else {
$this->exists = (!isset($this->collection->exists) || $this->collection->exists);
}
}
}
}
@ -378,6 +396,13 @@ EOSQL;
function FetchPrincipal() {
global $c, $session;
$this->principal = new CalDAVPrincipal( array( "path" => $this->dav_name ) );
if ( $this->IsPrincipal() ) {
$this->contenttype = 'httpd/unix-directory';
$this->unique_tag = $this->principal->dav_etag;
$this->created = $this->principal->created;
$this->modified = $this->principal->modified;
$this->resourcetype = '<DAV::principal/>';
}
}
@ -398,9 +423,14 @@ EOQRY;
$params = array( ':dav_name' => $this->dav_name );
$qry = new AwlQuery( $sql, $params );
if ( $qry->Exec('DAVResource') && $qry->rows > 0 ) {
if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
$this->exists = true;
$this->resource = $qry->Fetch();
$this->unique_tag = $this->resource->dav_etag;
$this->created = $this->resource->created;
$this->modified = $this->resource->modified;
$this->contenttype = 'text/calendar';
$this->resourcetype = '';
}
else {
$this->exists = false;
@ -416,29 +446,54 @@ EOQRY;
if ( $this->dav_name == '/' || $this->dav_name == '' ) {
$this->privileges = 1; // read
dbg_error_log( 'DAVResource', 'Read permissions for user accessing /' );
// dbg_error_log( 'DAVResource', 'Read permissions for user accessing /' );
return;
}
if ( $session->AllowedTo('Admin') || $session->user_no == $this->user_no ) {
if ( $session->AllowedTo('Admin') ) {
$this->privileges = privilege_to_bits('all');
dbg_error_log( 'DAVResource', 'Full permissions for %s', ( $session->user_no == $this->user_no ? 'user accessing their own hierarchy' : 'an administrator') );
// dbg_error_log( 'DAVResource', 'Full permissions for an administrator.' );
return;
}
if ( $this->IsPrincipal() ) {
if ( !isset($this->principal) ) $this->FetchPrincipal();
$this->privileges = $this->principal->Privileges();
// dbg_error_log( 'DAVResource', 'Privileges of "%s" for user accessing principal "%s"', $this->privileges, $this->principal->username );
return;
}
$this->privileges = 0;
if ( !isset($this->collection) ) $this->FetchCollection();
if ( !isset($this->collection->path_privileges) ) {
$parent_path = preg_replace('{/[^/]*/$}', '/', $this->collection->dav_name );
// dbg_error_log( 'DAVResource', 'Checking privileges of "%s" - parent of "%s"', $parent_path, $this->collection->dav_name );
$parent = new DAVResource( $parent_path );
$this->collection->path_privileges = $parent->Privileges();
}
$this->privileges = $this->collection->path_privileges;
}
/**
* Return the privileges bits for the current session user to this resource
*/
function Privileges() {
if ( !isset($this->privileges) ) $this->FetchPrivileges();
return $this->privileges;
}
/**
* Is the user has the privileges to do what is requested.
*/
function HavePrivilegeTo( $do_what ) {
if ( !isset($this->privileges) ) $this->FetchPrivileges();
$test_bits = privilege_to_bits( $do_what );
// dbg_error_log( 'DAVResource', 'Testing privileges of "%s"(%d) against allowed "%s" => "%s"', $do_what, $test_bits, $this->privileges, ($this->privileges & $test_bits) );
return ($this->privileges & $test_bits) > 0;
}
@ -454,7 +509,7 @@ EOQRY;
if ( !isset($xmldoc) && isset($GLOBALS['reply']) ) $xmldoc = $GLOBALS['reply'];
$privileges = array();
foreach( $privilege_names AS $k ) {
dbg_error_log( 'DAVResource', 'Adding privilege "%s".', $k );
// dbg_error_log( 'DAVResource', 'Adding privilege "%s".', $k );
$privilege = new XMLElement('privilege');
if ( isset($xmldoc) )
$xmldoc->NSElement($privilege,$k);
@ -572,15 +627,56 @@ EOQRY;
}
/**
* Checks whether this resource is a collection
*/
function IsCollection() {
return $this->_is_collection;
}
/**
* Checks whether this resource is a principal
*/
function IsPrincipal() {
return $this->_is_collection;
}
/**
* Checks whether this resource is a calendar
*/
function IsCalendar() {
return $this->_is_calendar;
}
/**
* Checks whether this resource is an addressbook
*/
function IsAddressbook() {
return $this->_is_addressbook;
}
/**
* Checks whether this resource actually exists, in the virtual sense, within the hierarchy
*/
function Exists() {
if ( isset($this->exists) ) return $this->exists;
if ( isset($this->collection) && isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' ) {
return true;
if ( ! isset($this->exists) ) {
if ( $this->IsPrincipal() ) {
if ( !isset($this->principal) ) $this->FetchPrincipal();
$this->exists = $this->principal->Exists();
}
else if ( $this->IsCollection() ) {
if ( !isset($this->collection) ) $this->FetchCollection();
}
else {
if ( !isset($this->resource) ) $this->FetchResource();
}
}
return false;
dbg_error_log('DAVResource',' Checking whether "%s" exists. It would appear %s.', $this->dav_name, ($this->exists ? 'so' : 'not') );
return $this->exists;
}
@ -605,6 +701,23 @@ EOQRY;
}
/**
* Returns the principal-URL for this resource
*/
function unique_tag() {
if ( isset($this->unique_tag) ) return $this->unique_tag;
if ( $this->IsCollection() && !isset($this->collection) ) {
$this->FetchCollection();
if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
}
else if ( !isset($this->resource) ) $this->FetchResource();
if ( $this->exists !== true || !isset($this->unique_tag) ) $this->unique_tag = '';
return $this->unique_tag;
}
/**
* Checks whether the target collection is publicly_readable
*/
@ -632,7 +745,7 @@ EOQRY;
else {
$qry = new AwlQuery('SELECT * FROM collection WHERE dav_name = :parent_name',
array( ':parent_name' => $this->collection->parent_container ) );
if ( $qry->Exec('DAVResource') && $qry->rows > 0 && $parent = $qry->Fetch() ) {
if ( $qry->Exec('DAVResource') && $qry->rows() > 0 && $parent = $qry->Fetch() ) {
if ( $parent->is_calendar == 't' )
$this->parent_container_type = 'calendar';
else if ( $parent->is_addressbook == 't' )
@ -649,15 +762,56 @@ EOQRY;
}
/**
* Return general server-related properties, in plain form
*/
function GetProperty( $name ) {
global $c, $session;
// dbg_error_log( 'DAVResource', 'Processing "%s".', $name );
$value = null;
switch( $name ) {
case 'collection_id':
if ( !isset($this->collection) ) $this->FetchCollection();
return $this->collection->collection_id;
break;
default:
if ( $this->_is_principal ) {
if ( !isset($this->principal) ) $this->FetchPrincipal();
if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
}
else if ( $this->_is_collection ) {
if ( !isset($this->collection) ) $this->FetchCollection();
if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
}
else {
if ( !isset($this->resource) ) $this->FetchResource();
if ( isset($this->resource->{$name}) ) return $this->resource->{$name};
if ( !isset($this->principal) ) $this->FetchPrincipal();
if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
if ( !isset($this->collection) ) $this->FetchCollection();
if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
}
dbg_error_log( 'ERROR', 'Request for property "%s" which is not understood.', $name );
}
return $value;
}
/**
* Return general server-related properties for this URL
*/
function ResourceProperty( $tag, $prop, $reply = null ) {
function ResourceProperty( $tag, $prop, $reply = null, &$denied ) {
global $c, $session;
if ( $reply === null ) $reply = $GLOBALS['reply'];
dbg_error_log( 'resource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
switch( $tag ) {
case 'DAV::href':
@ -677,7 +831,7 @@ EOQRY;
break;
case 'DAV::getlastmodified':
$prop->NewElement('getlastmodified', $this->last_modified );
$prop->NewElement('getlastmodified', $this->modified );
break;
case 'DAV::creationdate':
@ -703,7 +857,7 @@ EOQRY;
case 'DAV::getetag':
if ( $this->_is_collection ) {
$not_found[] = $reply->Tag($tag);
return false;
}
else {
$prop->NewElement('getetag', $this->unique_tag );
@ -719,17 +873,22 @@ EOQRY;
$prop->NewElement('http://calendarserver.org/ns/:getctag', $this->unique_tag );
}
else {
$not_found[] = $reply->Tag($tag);
return false;
}
break;
case 'urn:ietf:params:xml:ns:caldav:calendar-data':
if ( isset($this->caldav_data) ) {
if ( $this->_is_collection ) {
if ( !isset($this->resource) ) $this->FetchResource();
$reply->CalDAVElement($prop, $k, $this->resource->caldav_data );
}
else {
return false;
}
break;
default:
dbg_error_log( 'resource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
return false;
}
return true;
@ -746,15 +905,15 @@ EOQRY;
function GetPropStat( $properties ) {
global $session, $c, $request, $reply;
dbg_error_log('resource',': GetPropStat: href "%s"', $this->dav_name );
dbg_error_log('DAVResource',': GetPropStat: href "%s"', $this->dav_name );
$prop = new XMLElement('prop');
$denied = array();
$not_found = array();
foreach( $properties AS $k => $tag ) {
dbg_error_log( 'resource', 'Looking at resource "%s" for property [%s]"%s".', $this->dav_name, $k, $tag );
if ( ! $this->ResourceProperty($tag, $prop, $reply) ) {
dbg_error_log( 'resource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name );
// dbg_error_log( 'DAVResource', 'Looking at resource "%s" for property [%s]"%s".', $this->dav_name, $k, $tag );
if ( ! $this->ResourceProperty($tag, $prop, $reply, $denied ) ) {
dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name );
$not_found[] = $reply->Tag($tag);
}
}
@ -795,7 +954,7 @@ EOQRY;
function RenderAsXML( $properties, &$reply, $props_only = false ) {
global $session, $c, $request;
dbg_error_log('principal',': RenderAsXML: Principal "%s"', $this->username );
dbg_error_log('DAVResource',': RenderAsXML: Principal "%s"', $this->username );
$prop = new XMLElement('prop');
$denied = array();
@ -812,7 +971,7 @@ EOQRY;
$status = new XMLElement('status', 'HTTP/1.1 200 OK' );
$propstat = new XMLElement( 'propstat', array( $prop, $status) );
$href = $reply->href( ConstructURL($this->dav_name) ); /** TODO: make ::href() into an accessor */
$href = $reply->href( ConstructURL($this->dav_name) ); /** @TODO: make ::href() into an accessor */
$elements = array($href,$propstat);

View File

@ -23,25 +23,25 @@ class HTTPAuthSession {
* User ID number
* @var user_no int
*/
var $user_no;
public $user_no;
/**
* User e-mail
* @var email string
*/
var $email;
public $email;
/**
* User full name
* @var fullname string
*/
var $fullname;
public $fullname;
/**
* Group rights
* @var groups array
*/
var $groups;
public $groups;
/**#@-*/
/**
@ -228,7 +228,7 @@ class HTTPAuthSession {
if (isset($c->authenticate_hook['optional']) && $c->authenticate_hook['optional']) {
if ($hook_response !== false) { return $hook_response; }
} else {
return $hook_response;
return $hook_response;
}
}

View File

@ -68,7 +68,7 @@ if ( !isset($_SERVER['SERVER_NAME']) ) {
/**
* Calculate the simplest form of reference to this page, excluding the PATH_INFO following the script name.
*/
$c->protocol_server_port_script = sprintf( '%s://%s%s%s',
$c->protocol_server_port = sprintf( '%s://%s%s',
(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'? 'https' : 'http'),
$_SERVER['SERVER_NAME'],
(
@ -76,8 +76,8 @@ $c->protocol_server_port_script = sprintf( '%s://%s%s%s',
|| (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' && $_SERVER['SERVER_PORT'] == 443 )
? ''
: ':'.$_SERVER['SERVER_PORT']
),
($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']) );
) );
$c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);
init_gettext( 'davical', '../locale' );
@ -167,7 +167,11 @@ function getUserByName( $username, $use_cache = true ) {
// Provide some basic caching in case this ends up being overused.
if ( $use_cache && isset( $_known_users_name[$username] ) ) return $_known_users_name[$username];
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified FROM usr WHERE lower(username) = lower(?) ", $username );
global $session;
if ( isset($session->user_no) )
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified, principal.*, user_privileges(?,usr.user_no) AS privileges FROM usr LEFT JOIN principal USING(user_no) WHERE lower(username) = lower(?) ", $session->user_no, $username );
else
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified, principal.*, 0::BIT(24) AS privileges FROM usr LEFT JOIN principal USING(user_no) WHERE lower(username) = lower(?) ", $username );
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
$_known_users_name[$username] = $qry->Fetch();
$id = $_known_users_name[$username]->user_no;
@ -188,7 +192,8 @@ function getUserByID( $user_no, $use_cache = true ) {
// Provide some basic caching in case this ends up being overused.
if ( $use_cache && isset( $_known_users_id[$user_no] ) ) return $_known_users_id[$user_no];
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified FROM usr WHERE user_no = ? ", intval($user_no) );
global $session;
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified, principal.*, user_privileges(?,usr.user_no) AS privileges FROM usr LEFT JOIN principal USING(user_no) WHERE user_no = ? ", $session->user_no, intval($user_no) );
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
$_known_users_id[$user_no] = $qry->Fetch();
$name = $_known_users_id[$user_no]->username;

View File

@ -68,7 +68,7 @@ if ( !isset($_SERVER['SERVER_NAME']) ) {
/**
* Calculate the simplest form of reference to this page, excluding the PATH_INFO following the script name.
*/
$c->protocol_server_port_script = sprintf( '%s://%s%s%s',
$c->protocol_server_port = sprintf( '%s://%s%s',
(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'? 'https' : 'http'),
$_SERVER['SERVER_NAME'],
(
@ -76,8 +76,8 @@ $c->protocol_server_port_script = sprintf( '%s://%s%s%s',
|| (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' && $_SERVER['SERVER_PORT'] == 443 )
? ''
: ':'.$_SERVER['SERVER_PORT']
),
($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']) );
) );
$c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);
init_gettext( 'davical', '../locale' );
@ -167,7 +167,11 @@ function getUserByName( $username, $use_cache = true ) {
// Provide some basic caching in case this ends up being overused.
if ( $use_cache && isset( $_known_users_name[$username] ) ) return $_known_users_name[$username];
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified FROM usr WHERE lower(username) = lower(?) ", $username );
global $session;
if ( isset($session->user_no) )
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified, principal.*, user_privileges(?,usr.user_no) AS privileges FROM usr LEFT JOIN principal USING(user_no) WHERE lower(username) = lower(?) ", $session->user_no, $username );
else
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified, principal.*, 0::BIT(24) AS privileges FROM usr LEFT JOIN principal USING(user_no) WHERE lower(username) = lower(?) ", $username );
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
$_known_users_name[$username] = $qry->Fetch();
$id = $_known_users_name[$username]->user_no;
@ -188,7 +192,8 @@ function getUserByID( $user_no, $use_cache = true ) {
// Provide some basic caching in case this ends up being overused.
if ( $use_cache && isset( $_known_users_id[$user_no] ) ) return $_known_users_id[$user_no];
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified FROM usr WHERE user_no = ? ", intval($user_no) );
global $session;
$qry = new PgQuery( "SELECT *, to_char(updated at time zone 'GMT','Dy, DD Mon IYYY HH24:MI:SS \"GMT\"') AS modified, principal.*, user_privileges(?,usr.user_no) AS privileges FROM usr LEFT JOIN principal USING(user_no) WHERE user_no = ? ", $session->user_no, intval($user_no) );
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
$_known_users_id[$user_no] = $qry->Fetch();
$name = $_known_users_id[$user_no]->username;

View File

@ -10,6 +10,8 @@
*/
dbg_error_log("MOVE", "method handler");
require_once('DAVResource.php');
if ( ! $request->AllowedTo("read") ) {
$request->DoResponse(403);
}
@ -28,18 +30,26 @@ if ( $request->path == '/' || $request->IsPrincipal() || $request->destination =
$request->DoResponse( 403 );
}
if ( !class_exists('DAVResource') ) require('DAVResource.php');
$dest = new DAVResource($request->destination);
if ( $dest->path == '/' || $dest->IsPrincipal() ) {
if ( $dest->dav_name() == '/' || $dest->IsPrincipal() ) {
$request->DoResponse( 403 );
}
if ( ! $request->overwrite && $dest->Exists() ) {
$request->DoResponse( 412 );
$request->DoResponse( 412, translate('Not overwriting existing destination resource') );
}
if ( $request->IsCollection() ) {
if ( isset($request->etag_none_match) && $request->etag_none_match != '*' ) {
$request->DoResponse( 412 ); /** request to move, but only if there is no source? WTF! */
}
$src = new DAVResource($request->path);
if ( ! $src->Exists() ) {
$request->DoResponse( 412, translate('Source resource does not exist.') );
}
if ( $src->IsCollection() ) {
switch( $dest->ContainerType() ) {
case 'calendar':
case 'addressbook':
@ -48,14 +58,41 @@ if ( $request->IsCollection() ) {
$request->DoResponse( 412, translate('Special collections may not contain a calendar or other special collection.') );
};
}
else {
if ( (isset($request->etag_if_match) && $request->etag_if_match != '' )
|| ( isset($request->etag_none_match) && $request->etag_none_match != '') ) {
if ( ! $request->AllowedTo('delete') ) $request->DoResponse( 403 );
if ( ! $dest->HaveRightsTo('DAV::write') ) $request->DoResponse( 403 );
if ( ! $dest->Exists() && !$dest->HaveRightsTo('DAV::bind') ) $request->DoResponse( 403 );
// if ( ! $request->HaveRightsTo('DAV::unbind') ) $request->DoResponse( 403 );
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*
* RFC2068, 14.26:
* If any of the entity tags match the entity tag of the entity that
* would have been returned in the response to a similar GET request
* (without the If-None-Match header) on that resource, or if "*" is
* given and any current entity exists for that resource, then the
* server MUST NOT perform the requested method.
*/
$error = '';
if ( isset($request->etag_if_match) && $request->etag_if_match != $src->unique_tag() ) {
$error = translate( 'Existing resource does not match "If-Match" header - not accepted.');
}
else if ( isset($request->etag_none_match) && $request->etag_none_match != '' && $request->etag_none_match == $src->unique_tag() ) {
$error = translate( 'Existing resource matches "If-None-Match" header - not accepted.');
}
if ( $error != '' ) $request->DoResponse( 412, $error );
}
}
if ( ! $src->HavePrivilegeTo('DAV::unbind') ) $request->DoResponse( 403 );
if ( ! $dest->HavePrivilegeTo('DAV::write') ) $request->DoResponse( 403 );
if ( ! $dest->Exists() && !$dest->HavePrivilegeTo('DAV::bind') ) $request->DoResponse( 403 );
function rollback( $response_code = 412 ) {
global $request;
$qry = new AwlQuery('ROLLBACK');
$qry->Exec('move'); // Just in case
$request->DoResponse( $response_code );
@ -66,21 +103,73 @@ function rollback( $response_code = 412 ) {
$qry = new AwlQuery('BEGIN');
if ( !$qry->Exec('move') ) rollback(500);
if ( $request->IsCollection() ) {
$src_name = $src->dav_name();
$dst_name = $dest->dav_name();
$src_collection = $src->GetProperty('collection_id');
$dst_collection = $dest->GetProperty('collection_id');
$src_user_no = $src->GetProperty('user_no');
$dst_user_no = $dest->GetProperty('user_no');
if ( $src->IsCollection() ) {
if ( $dest->Exists() ) {
$qry = new AwlQuery( 'DELETE FROM collection WHERE dav_name = :dst_name', array( ':dst_name' => $dst_name ) );
if ( !$qry->Exec('move') ) rollback(500);
}
/** @TODO: Need to confirm this will work correctly if we move this into another user's hierarchy. */
$qry = new AwlQuery( 'UPDATE collection SET dav_name = :new_dav_name WHERE collection_id = :collection_id', array(
':new_dav_name' => $dest->dav_name(),
':collection_id' => $request->collection
);
$sql = 'UPDATE collection SET dav_name = :dst_name ';
$params = array(':dst_name' => $dst_name);
if ( $src_user_no != $dst_user_no ) {
$sql .= ', user_no = :dst_user_no';
$params[':dst_user_no'] = $dst_user_no;
}
$sql .= 'WHERE collection_id = :src_collection';
$params[':src_collection'] = $src_collection;
$qry = new AwlQuery( $sql, $params );
if ( !$qry->Exec('move') ) rollback(500);
}
else {
$qry = new AwlQuery( 'UPDATE caldav_data SET dav_name = :new_dav_name WHERE dav_name = :old_dav_name', array(
':old_dav_name' => $request->dav_name(),
':new_dav_name' => $dest->dav_name()
);
if ( $dest->Exists() ) {
$qry = new AwlQuery( 'DELETE FROM caldav_data WHERE dav_name = :dst_name', array( ':dst_name' => $dst_name) );
if ( !$qry->Exec('move') ) rollback(500);
}
$sql = 'UPDATE caldav_data SET dav_name = :dst_name';
$params = array( ':dst_name' => $dst_name );
if ( $src_user_no != $dst_user_no ) {
$sql .= ', user_no = :dst_user_no';
$params[':dst_user_no'] = $dst_user_no;
}
if ( $src_collection != $dst_collection ) {
$sql .= ', collection_id = :dst_collection';
$params[':dst_collection'] = $dst_collection;
}
$sql .=' WHERE dav_name = :src_name';
$params[':src_name'] = $src_name;
$qry = new AwlQuery( $sql, $params );
if ( !$qry->Exec('move') ) rollback(500);
$qry = new AwlQuery( 'SELECT write_sync_change( :src_collection, 404, :src_name );', array(
':src_name' => $src_name,
':src_collection' => $src_collection
) );
if ( !$qry->Exec('move') ) rollback(500);
if ( function_exists('log_caldav_action') ) {
log_caldav_action( 'DELETE', $src->GetProperty('uid'), $src_user_no, $src_collection, $src_name );
}
$qry = new AwlQuery( 'SELECT write_sync_change( :dst_collection, :sync_type, :dst_name );', array(
':dst_name' => $dst_name,
':dst_collection' => $dst_collection,
':sync_type' => ( $dest->Exists() ? 200 : 201 )
) );
if ( !$qry->Exec('move') ) rollback(500);
if ( function_exists('log_caldav_action') ) {
log_caldav_action( ( $dest->Exists() ? 'UPDATE' : 'INSERT' ), $src->GetProperty('uid'), $dst_user_no, $dst_collection, $dst_name );
}
}
$qry = new PgQuery('COMMIT');
if ( !$qry->Exec('move') ) rollback(500);
$request->DoResponse( ($put_action_type == 'INSERT' ? 201 : 204) );
$request->DoResponse( 200 );

View File

@ -4,9 +4,9 @@
*
* @package davical
* @subpackage caldav
* @author Andrew McMillan <andrew@catalyst.net.nz>
* @copyright Catalyst .Net Ltd
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
* @author Andrew McMillan <andrew@mcmillan.net.nz>
* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
*/
dbg_error_log("OPTIONS", "method handler");
@ -53,8 +53,11 @@ if ( !$exists ) {
*/
if ( isset($c->override_allowed_methods) )
$allowed = $c->override_allowed_methods;
else if ( isset($request->supported_methods) ) {
$allowed = implode( ', ', array_keys($request->supported_methods) );
}
else {
$allowed = "OPTIONS, GET, HEAD, PUT, DELETE, PROPFIND, MKCOL, MKCALENDAR, LOCK, UNLOCK, REPORT, PROPPATCH, POST";
$allowed = "OPTIONS, GET, HEAD, PUT, DELETE, PROPFIND, MKCOL, MKCALENDAR, LOCK, UNLOCK, REPORT, PROPPATCH, POST, MOVE";
if ( $request->path == '/' ) {
$exists = true;
$allowed = "OPTIONS, GET, HEAD, PROPFIND, REPORT";
@ -65,4 +68,3 @@ header( "Allow: $allowed");
$request->DoResponse( 200, "" );
?>

View File

@ -365,9 +365,9 @@ function import_collection( $ics_content, $user_no, $path, $caldav_context ) {
}
$sql .= <<<EOSQL
INSERT INTO calendar_item (user_no, dav_name, dav_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp,
INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp,
description, rrule, tz_id, last_modified, url, priority, created, due, percent_complete, status, collection_id )
VALUES ( ?, ?, ?, ?, ?, ?, $dtend, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
VALUES ( ?, ?, currval('dav_id_seq'), ?, ?, ?, ?, $dtend, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
EOSQL;
$qry = new PgQuery( $sql, $user_no, $resource_path, $etag, $first->GetPValue('UID'), $dtstamp,
@ -417,13 +417,13 @@ function putCalendarResource( &$request, $author, $caldav_context ) {
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*/
rollback_on_error( $caldav_context, $request->user_no, $request->path, 412, translate('Resource changed on server - not changed.') );
rollback_on_error( $caldav_context, $request->user_no, $request->path, translate('Resource changed on server - not changed.'), 412 );
}
$put_action_type = 'INSERT';
if ( ! $request->AllowedTo('create') ) {
rollback_on_error( $caldav_context, $request->user_no, $request->path, 403, translate('You may not add entries to this calendar.') );
rollback_on_error( $caldav_context, $request->user_no, $request->path, translate('You may not add entries to this calendar.'), 403 );
}
}
elseif ( $qry->rows == 1 ) {
@ -447,7 +447,7 @@ function putCalendarResource( &$request, $author, $caldav_context ) {
if ( isset($request->etag_if_match) && $request->etag_if_match != $icalendar->dav_etag ) {
$error = translate( 'Existing resource does not match "If-Match" header - not accepted.');
}
if ( isset($etag_none_match) && $etag_none_match != '' && ($etag_none_match == $icalendar->dav_etag || $etag_none_match == '*') ) {
if ( isset($request->etag_none_match) && $request->etag_none_match != '' && ($request->etag_none_match == $icalendar->dav_etag || $request->etag_none_match == '*') ) {
$error = translate( 'Existing resource matches "If-None-Match" header - not accepted.');
}
$request->DoResponse( 412, $error );
@ -490,12 +490,21 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
global $tz_regex;
$resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
$first = $resources[0];
if ( !isset($resources[0]) ) {
$resource_type = 'Unknown';
/** @TODO: Handle writing non-calendar resources, like address book entries or random file data */
rollback_on_error( $caldav_context, $user_no, $path, translate('No calendar content'), 412 );
return false;
}
else {
$first = $resources[0];
$resource_type = $first->GetType();
}
if ( $put_action_type == 'INSERT' ) {
create_scheduling_requests($vcal);
$qry = new PgQuery( 'BEGIN; INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id ) VALUES( ?, ?, ?, ?, ?, ?, current_timestamp, current_timestamp, ? )',
$user_no, $path, $etag, $caldav_data, $first->GetType(), $author, $collection_id );
$user_no, $path, $etag, $caldav_data, $resource_type, $author, $collection_id );
if ( !$qry->Exec('PUT') ) {
rollback_on_error( $caldav_context, $user_no, $path);
return false;
@ -504,7 +513,7 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
else {
update_scheduling_requests($vcal);
$qry = new PgQuery( 'BEGIN;UPDATE caldav_data SET caldav_data=?, dav_etag=?, caldav_type=?, logged_user=?, modified=current_timestamp WHERE user_no=? AND dav_name=?',
$caldav_data, $etag, $first->GetType(), $author, $user_no, $path );
$caldav_data, $etag, $resource_type, $author, $user_no, $path );
if ( !$qry->Exec('PUT') ) {
rollback_on_error( $caldav_context, $user_no, $path);
return false;
@ -639,11 +648,11 @@ EOSQL;
}
else {
$sql .= <<<EOSQL
INSERT INTO calendar_item (user_no, dav_name, dav_etag, uid, dtstamp,
INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
dtstart, dtend, summary, location, class, transp,
description, rrule, tz_id, last_modified, url, priority,
created, due, percent_complete, status, collection_id )
VALUES ( $user_no, $escaped_path, ?, ?, ?,
VALUES ( $user_no, $escaped_path, currval('dav_id_seq'), ?, ?, ?,
?, $dtend, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?,
?, ?, ?, ?, $collection_id );