mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-03-14 08:10:13 +00:00
initial support for remote url BINDing
This commit is contained in:
parent
40b4bdfd7f
commit
6331c50003
@ -188,6 +188,16 @@ $c->collections_always_exist = false;
|
||||
$c->schedule_private_key = 'PRIVATE-KEY-BASE-64-DATA';
|
||||
*/
|
||||
|
||||
/*
|
||||
* External subscription (BIND) minimum refresh interval
|
||||
* Required if you want to enable remote binding ( webcal subscriptions )
|
||||
* Default: none
|
||||
*/
|
||||
/*
|
||||
$c->external_refresh = 60;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
@ -97,6 +97,11 @@ class DAVResource
|
||||
*/
|
||||
private $_is_binding;
|
||||
|
||||
/**
|
||||
* @var True if this resource is a binding to an external resource
|
||||
*/
|
||||
private $_is_external;
|
||||
|
||||
/**
|
||||
* @var True if this resource is an addressbook collection
|
||||
*/
|
||||
@ -158,6 +163,7 @@ class DAVResource
|
||||
$this->_is_principal = false;
|
||||
$this->_is_calendar = false;
|
||||
$this->_is_binding = false;
|
||||
$this->_is_external = false;
|
||||
$this->_is_addressbook = false;
|
||||
$this->_is_proxy_request = false;
|
||||
if ( isset($parameters) && is_object($parameters) ) {
|
||||
@ -400,7 +406,8 @@ EOSQL;
|
||||
SELECT collection.*, path_privs(:session_principal::int8, collection.dav_name,:scan_depth::int), p.principal_id,
|
||||
p.type_id AS principal_type_id, p.displayname AS principal_displayname, p.default_privileges AS principal_default_privileges,
|
||||
time_zone.tz_spec, dav_binding.access_ticket_id, dav_binding.parent_container AS bind_parent_container,
|
||||
dav_binding.dav_displayname, owner.dav_name AS bind_owner_url, dav_binding.dav_name AS bound_to
|
||||
dav_binding.dav_displayname, owner.dav_name AS bind_owner_url, dav_binding.dav_name AS bound_to,
|
||||
dav_binding.external_url AS external_url, dav_binding.type AS external_type, dav_binding.bind_id AS bind_id
|
||||
FROM dav_binding
|
||||
LEFT JOIN collection ON (collection.collection_id=bound_source_id)
|
||||
LEFT JOIN principal p USING (user_no)
|
||||
@ -431,7 +438,16 @@ EOSQL;
|
||||
$this->collection->type = 'schedule-'. $matches[3]. 'box';
|
||||
else
|
||||
$this->collection->type = 'collection';
|
||||
|
||||
if ( strlen($row->external_url) > 8 ) {
|
||||
$this->collection->bound_from = $row->external_url;
|
||||
$this->_is_external = true;
|
||||
if ( $row->external_type == 'calendar' )
|
||||
$this->collection->type = 'calendar';
|
||||
else if ( $row->external_type == 'addressbook' )
|
||||
$this->collection->type = 'addressbook';
|
||||
else
|
||||
$this->collection->type = 'collection';
|
||||
}
|
||||
$this->_is_binding = true;
|
||||
$this->bound_from = str_replace( $row->bound_to, $row->dav_name, $this->dav_name);
|
||||
if ( isset($row->access_ticket_id) ) {
|
||||
@ -1047,6 +1063,14 @@ EOQRY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether this resource is a bind to an external resource
|
||||
*/
|
||||
function IsExternal() {
|
||||
return $this->_is_external;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether this resource actually exists, in the virtual sense, within the hierarchy
|
||||
*/
|
||||
|
||||
@ -47,45 +47,88 @@ if ( $destination->Exists() ) {
|
||||
$request->PreconditionFailed(403,'DAV::can-overwrite',translate('A resource already exists at the destination.'));
|
||||
}
|
||||
|
||||
$source = new DAVResource( $href );
|
||||
if ( !$source->Exists() ) {
|
||||
$request->PreconditionFailed(403,'DAV::bind-source-exists',translate('The BIND Request MUST identify an existing resource.'));
|
||||
}
|
||||
if ( preg_match ( '{^https?://[A-Za-z][^/]*/.+$}', $href ) ) {
|
||||
require_once('external-fetch.php');
|
||||
$qry = new AwlQuery( );
|
||||
$qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array( ':dav_name' => '/.external/'. md5($href) ));
|
||||
if ( $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
|
||||
$dav_id = $row->dav_id;
|
||||
}
|
||||
else {
|
||||
create_external ( '/.external/'. md5($href) ,true,false );
|
||||
$qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array( ':dav_name' => '/.external/'. md5($href) ));
|
||||
if ( $qry->rows() != 1 || !($row = $qry->Fetch()) )
|
||||
$request->DoResponse(500,translate('Database Error1'));
|
||||
$dav_id = $row->collection_id;
|
||||
}
|
||||
|
||||
if ( $source->IsPrincipal() || !$source->IsCollection() ) {
|
||||
$request->PreconditionFailed(403,'DAV::binding-allowed',translate('DAViCal only allows BIND requests for collections at present.'));
|
||||
}
|
||||
|
||||
/*
|
||||
bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
|
||||
bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
parent_container TEXT NOT NULL,
|
||||
dav_name TEXT UNIQUE NOT NULL,
|
||||
dav_displayname TEXT
|
||||
*/
|
||||
|
||||
$sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname )
|
||||
VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname )';
|
||||
$params = array(
|
||||
':target_id' => $source->GetProperty('collection_id'),
|
||||
':ticket_id' => (isset($request->ticket) ? $request->ticket->id() : null),
|
||||
':parent_container' => $parent->dav_name(),
|
||||
':session_principal' => $session->principal_id,
|
||||
':dav_name' => $destination_path,
|
||||
':displayname' => $source->GetProperty('displayname')
|
||||
);
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
if ( $qry->Exec('BIND',__LINE__,__FILE__) ) {
|
||||
header('Location: '. ConstructURL($destination_path) );
|
||||
|
||||
// Uncache anything to do with the target
|
||||
$cache = getCacheInstance();
|
||||
$cache_ns = 'collection-'.$destination_path;
|
||||
$cache->delete( $cache_ns, null );
|
||||
|
||||
$request->DoResponse(201);
|
||||
}
|
||||
$sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname, external_url, type )
|
||||
VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname, :external_url, :external_type )';
|
||||
$params = array(
|
||||
':target_id' => $dav_id,
|
||||
':ticket_id' => null,
|
||||
':parent_container' => $parent->dav_name(),
|
||||
':session_principal' => $session->principal_id,
|
||||
':dav_name' => $destination_path,
|
||||
':displayname' => $segment,
|
||||
':external_url' => $href,
|
||||
':external_type' => 'calendar'
|
||||
);
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
if ( $qry->Exec('BIND',__LINE__,__FILE__) ) {
|
||||
$qry = new AwlQuery( 'SELECT bind_id from dav_binding where dav_name = :dav_name', array( ':dav_name' => $destination_path ) );
|
||||
if ( ! $qry->Exec('BIND',__LINE__,__FILE__) || $qry->rows() != 1 || !($row = $qry->Fetch()) )
|
||||
$request->DoResponse(500,translate('Database Error1'));
|
||||
fetch_external ( $row->bind_id, '' );
|
||||
$request->DoResponse(201);
|
||||
}
|
||||
else {
|
||||
$request->DoResponse(500,translate('Database Error2'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$request->DoResponse(500,translate('Database Error'));
|
||||
}
|
||||
$source = new DAVResource( $href );
|
||||
if ( !$source->Exists() ) {
|
||||
$request->PreconditionFailed(403,'DAV::bind-source-exists',translate('The BIND Request MUST identify an existing resource.'));
|
||||
}
|
||||
|
||||
if ( $source->IsPrincipal() || !$source->IsCollection() ) {
|
||||
$request->PreconditionFailed(403,'DAV::binding-allowed',translate('DAViCal only allows BIND requests for collections at present.'));
|
||||
}
|
||||
|
||||
/*
|
||||
bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
|
||||
bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
parent_container TEXT NOT NULL,
|
||||
dav_name TEXT UNIQUE NOT NULL,
|
||||
dav_displayname TEXT,
|
||||
external_url TEXT,
|
||||
type TEXT
|
||||
*/
|
||||
|
||||
$sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname )
|
||||
VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname )';
|
||||
$params = array(
|
||||
':target_id' => $source->GetProperty('collection_id'),
|
||||
':ticket_id' => (isset($request->ticket) ? $request->ticket->id() : null),
|
||||
':parent_container' => $parent->dav_name(),
|
||||
':session_principal' => $session->principal_id,
|
||||
':dav_name' => $destination_path,
|
||||
':displayname' => $source->GetProperty('displayname')
|
||||
);
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
if ( $qry->Exec('BIND',__LINE__,__FILE__) ) {
|
||||
header('Location: '. ConstructURL($destination_path) );
|
||||
|
||||
// Uncache anything to do with the target
|
||||
$cache = getCacheInstance();
|
||||
$cache_ns = 'collection-'.$destination_path;
|
||||
$cache->delete( $cache_ns, null );
|
||||
|
||||
$request->DoResponse(201);
|
||||
}
|
||||
else {
|
||||
$request->DoResponse(500,translate('Database Error'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,9 @@ require_once("DAVResource.php");
|
||||
|
||||
$dav_resource = new DAVResource($request->path);
|
||||
$dav_resource->NeedPrivilege( array('urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read') );
|
||||
if ( $dav_resource->IsExternal() ) {
|
||||
update_external ( $dav_resource );
|
||||
}
|
||||
|
||||
if ( ! $dav_resource->Exists() ) {
|
||||
$request->DoResponse( 404, translate("Resource Not Found.") );
|
||||
|
||||
@ -61,9 +61,15 @@ switch( $xmltree->GetTag() ) {
|
||||
include("caldav-REPORT-pps-set.php");
|
||||
exit; // Not that it should return anyway.
|
||||
case 'DAV::sync-collection':
|
||||
require_once("external-fetch.php");
|
||||
if ( $target->IsExternal() )
|
||||
update_external ( $target );
|
||||
include("caldav-REPORT-sync-collection.php");
|
||||
exit; // Not that it should return anyway.
|
||||
case 'DAV::expand-property':
|
||||
require_once("external-fetch.php");
|
||||
if ( $target->IsExternal() )
|
||||
update_external ( $target );
|
||||
include("caldav-REPORT-expand-property.php");
|
||||
exit; // Not that it should return anyway.
|
||||
case 'DAV::principal-match':
|
||||
@ -313,6 +319,10 @@ function component_to_xml( $properties, $item ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
require_once("external-fetch.php");
|
||||
if ( $target->IsExternal() )
|
||||
update_external ( $target );
|
||||
|
||||
if ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-query" ) {
|
||||
$calquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:caldav:calendar-query/*");
|
||||
include("caldav-REPORT-calquery.php");
|
||||
|
||||
73
inc/external-fetch.php
Normal file
73
inc/external-fetch.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?PHP
|
||||
/**
|
||||
* Functions for managing external BIND resources
|
||||
*
|
||||
*
|
||||
* @package davical
|
||||
* @subpackage external-bind
|
||||
* @author Rob Ostensen <rob@boxacle.net>
|
||||
* @copyright Rob Ostensen
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
function create_external ( $path,$is_calendar,$is_addressbook )
|
||||
{
|
||||
global $request;
|
||||
$resourcetypes = '<DAV::collection/>';
|
||||
if ($is_calendar) $resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
|
||||
$qry = new AwlQuery();
|
||||
if ( ! $qry->QDo( 'INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
|
||||
is_calendar, is_addressbook, resourcetypes, created, modified )
|
||||
VALUES( :user_no, :parent_container, :dav_name, :dav_etag, :dav_displayname,
|
||||
:is_calendar, :is_addressbook, :resourcetypes, current_timestamp, current_timestamp )',
|
||||
array(
|
||||
':user_no' => $request->user_no,
|
||||
':parent_container' => '/.external/',
|
||||
':dav_name' => $path,
|
||||
':dav_etag' => md5($request->user_no. $path),
|
||||
':dav_displayname' => $path,
|
||||
':is_calendar' => ($is_calendar ? 't' : 'f'),
|
||||
':is_addressbook' => ($is_addressbook ? 't' : 'f'),
|
||||
':resourcetypes' => $resourcetypes
|
||||
) ) ) {
|
||||
$request->DoResponse( 500, translate('Error writing calendar details to database.') );
|
||||
}
|
||||
}
|
||||
|
||||
function fetch_external ( $bind_id, $min_age )
|
||||
{
|
||||
$sql = 'SELECT collection.*, collection.dav_name as path, dav_binding.external_url as external_url FROM dav_binding LEFT JOIN collection ON (collection.collection_id=bound_source_id) WHERE bind_id = :bind_id';
|
||||
$params = array( ':bind_id' => $bind_id );
|
||||
if ( strlen ( $min_age ) > 2 ) {
|
||||
$sql .= ' and collection.modified + interval :interval > NOW()';
|
||||
$params[':interval'] = $min_age;
|
||||
}
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
if ( $qry->Exec('DAVResource') && $qry->rows() > 0 && $row = $qry->Fetch() ) {
|
||||
$curl = curl_init ( $row->external_url );
|
||||
curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, true );
|
||||
$ics = curl_exec ( $curl );
|
||||
curl_close ( $curl );
|
||||
if ( is_string ( $ics ) && strlen ( $ics ) > 20 ) {
|
||||
$qry = new AwlQuery( 'UPDATE collection SET modified=NOW() where collection_id = :cid', array ( ':cid' => $row->collection_id ) );
|
||||
$qry->Exec('DAVResource');
|
||||
require_once ( 'caldav-PUT-functions.php');
|
||||
import_collection ( $ics , $row->user_no, $row->path, 'External Fetch' , false ) ;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function update_external ( $request )
|
||||
{
|
||||
global $c;
|
||||
if ( $c->external_refresh < 1 )
|
||||
return ;
|
||||
$sql = 'SELECT bind_id from dav_binding LEFT JOIN collection ON (collection.collection_id=bound_source_id) collection where dav_name = :dav_name and collection.modified + interval :interval < NOW()';
|
||||
$qry = new AwlQuery( $sql, array ( ':dav_name' => $request->dav_name, ':interval' => $c->external_refresh + ' minutes' ) );
|
||||
if ( $qry->Exec('DAVResource') && $qry->rows() > 0 && $row = $qry->Fetch() ) {
|
||||
if ( $row->bind_id != 0 )
|
||||
fetch_external ( $row->bind_id );
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user