mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-03-14 08:10:13 +00:00
Remove case-folding of incoming XML.
This commit is contained in:
parent
a8ae3b1406
commit
5ec4c33670
@ -52,11 +52,23 @@ class CalDAVPrincipal
|
||||
*/
|
||||
var $by_email;
|
||||
|
||||
/**
|
||||
* @var RFC3744: The principals that are direct members of this group.
|
||||
*/
|
||||
var $group_member_set;
|
||||
|
||||
/**
|
||||
* @var RFC3744: The groups in which the principal is directly a member.
|
||||
*/
|
||||
var $group_membership;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param mixed $parameters If null, an empty Principal is created. If it
|
||||
* is an integer then that ID is read (if possible). If it is
|
||||
* an array then the Principal matching the supplied elements is read.
|
||||
* an array then the Principal matching the supplied elements
|
||||
* is read. If it is an object then it is expected to be a 'usr'
|
||||
* record that was read elsewhere.
|
||||
*
|
||||
* @return boolean Whether we actually read data from the DB to initialise the record.
|
||||
*/
|
||||
@ -65,7 +77,11 @@ class CalDAVPrincipal
|
||||
|
||||
if ( $parameters == null ) return false;
|
||||
$this->by_email = false;
|
||||
if ( is_int($parameters) ) {
|
||||
if ( is_object($parameters) ) {
|
||||
dbg_error_log( "principal", "Principal: record for %s", $parameters->username );
|
||||
$usr = $parameters;
|
||||
}
|
||||
else if ( is_int($parameters) ) {
|
||||
dbg_error_log( "principal", "Principal: %d", $parameters );
|
||||
$usr = getUserByID($parameters);
|
||||
}
|
||||
@ -110,17 +126,43 @@ class CalDAVPrincipal
|
||||
$this->url = ConstructURL( "/".$this->username."/" );
|
||||
// $this->url = ConstructURL( "/__uuids__/" . $this->username . "/" );
|
||||
|
||||
$this->calendar_home_set = ConstructURL( "/".$this->username."/" );
|
||||
// $qry = new PgQuery("SELECT dav_name FROM collection WHERE user_no = ?", $this->user_no);
|
||||
// // Should be only one record, but this might change in future.
|
||||
// $qry = new PgQuery("SELECT DISTINCT parent_container FROM collection WHERE user_no = ?", $this->user_no);
|
||||
// $this->calendar_home_set = array();
|
||||
// if( $qry->Exec("CalDAVPrincipal",__LINE__,__FILE__) && $qry->rows > 0 ) {
|
||||
// while( $calendar = $qry->Fetch() ) {
|
||||
// $this->calendar_home_set[] = ConstructURL($calendar->dav_name);
|
||||
// }
|
||||
// }
|
||||
$this->calendar_home_set = array( $this->url );
|
||||
|
||||
$this->user_address_set = array(
|
||||
"mailto:".$this->email,
|
||||
ConstructURL( "/".$this->username."/" ),
|
||||
// ConstructURL( "/~".$this->username."/" ),
|
||||
// ConstructURL( "/__uuids__/".$this->username."/" ),
|
||||
);
|
||||
$this->schedule_inbox_url = sprintf( "%s.in/", $this->calendar_home_set);
|
||||
$this->schedule_outbox_url = sprintf( "%s.out/", $this->calendar_home_set);
|
||||
$this->dropbox_url = sprintf( "%s.drop/", $this->calendar_home_set);
|
||||
$this->notifications_url = sprintf( "%s.notify/", $this->calendar_home_set);
|
||||
$this->schedule_inbox_url = sprintf( "%s.in/", $this->url);
|
||||
$this->schedule_outbox_url = sprintf( "%s.out/", $this->url);
|
||||
$this->dropbox_url = sprintf( "%s.drop/", $this->url);
|
||||
$this->notifications_url = sprintf( "%s.notify/", $this->url);
|
||||
|
||||
$this->group_member_set = array();
|
||||
$qry = new PgQuery("SELECT * FROM relationship LEFT JOIN usr ON (from_user = usr.user_no) LEFT JOIN role_member ON (to_user = role_member.user_no) LEFT JOIN roles USING (role_no) WHERE to_user = ? AND role_name = 'Group';", $this->user_no );
|
||||
if ( $qry->Exec("CalDAVPrincipal") && $qry->rows > 0 ) {
|
||||
while( $membership = $qry->Fetch() ) {
|
||||
$this->group_member_set[] = ConstructURL( "/". $membership->username . "/");
|
||||
}
|
||||
}
|
||||
|
||||
$this->group_membership = array();
|
||||
$qry = new PgQuery("SELECT * FROM relationship LEFT JOIN usr ON (to_user = user_no) LEFT JOIN role_member USING (user_no) LEFT JOIN roles USING (role_no) WHERE from_user = ? AND role_name = 'Group';", $this->user_no );
|
||||
if ( $qry->Exec("CalDAVPrincipal") && $qry->rows > 0 ) {
|
||||
while( $membership = $qry->Fetch() ) {
|
||||
$this->group_membership[] = ConstructURL( "/". $membership->username . "/");
|
||||
}
|
||||
}
|
||||
|
||||
dbg_error_log( "principal", "User: %s (%d) URL: %s, Home: %s, By Email: %d", $this->username, $this->user_no, $this->url, $this->calendar_home_set, $this->by_email );
|
||||
}
|
||||
@ -167,5 +209,189 @@ class CalDAVPrincipal
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the array of privilege names converted into XMLElements
|
||||
*/
|
||||
function RenderPrivileges($privilege_names, $container="privilege") {
|
||||
$privileges = array();
|
||||
foreach( $privilege_names AS $k => $v ) {
|
||||
$privileges[] = new XMLElement($container, new XMLElement($k));
|
||||
}
|
||||
return $privileges;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render XML for a single Principal (user) from the DB
|
||||
*
|
||||
* @param array $properties The requested properties for this principal
|
||||
* @param reference $reply A reference to the XMLDocument being used for the reply
|
||||
* @param boolean $props_only Default false. If true will only return the fragment with the properties, not a full response fragment.
|
||||
*
|
||||
* @return string An XML fragment with the requested properties for this principal
|
||||
*/
|
||||
function RenderAsXML( $properties, &$reply, $props_only = false ) {
|
||||
global $session, $c, $request;
|
||||
|
||||
dbg_error_log("CalDAVPrincipal",": RenderAsXML: Principal '%s'", $this->username );
|
||||
|
||||
$prop = new XMLElement("prop");
|
||||
$denied = array();
|
||||
$not_found = array();
|
||||
foreach( $properties AS $k => $tag ) {
|
||||
dbg_error_log("CalDAVPrincipal",": RenderAsXML: Principal Property '%s'", $tag );
|
||||
switch( $tag ) {
|
||||
case 'DAV::getcontenttype':
|
||||
$prop->NewElement("getcontenttype", "httpd/unix-directory" );
|
||||
break;
|
||||
|
||||
case 'DAV::resourcetype':
|
||||
$prop->NewElement("resourcetype", array( new XMLElement("principal"), new XMLElement("collection")) );
|
||||
break;
|
||||
|
||||
case 'DAV::displayname':
|
||||
$prop->NewElement("displayname", $this->fullname );
|
||||
break;
|
||||
|
||||
case 'DAV::principal-URL':
|
||||
$prop->NewElement("principal-URL", $this->url );
|
||||
break;
|
||||
|
||||
case 'DAV::getlastmodified':
|
||||
$prop->NewElement("getlastmodified", $this->modified );
|
||||
break;
|
||||
|
||||
case 'DAV::group-member-set':
|
||||
$set = array();
|
||||
foreach( $this->group_member_set AS $k => $url ) {
|
||||
$set[] = new XMLElement('href', $url );
|
||||
}
|
||||
$prop->NewElement("group-member-set", $set );
|
||||
break;
|
||||
|
||||
case 'DAV::group-membership':
|
||||
$set = array();
|
||||
foreach( $this->group_membership AS $k => $url ) {
|
||||
$set[] = new XMLElement('href', $url );
|
||||
}
|
||||
$prop->NewElement("group-membership", $set );
|
||||
break;
|
||||
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
|
||||
$prop->NewElement($reply->Caldav("schedule-inbox-URL"), new XMLElement('href', $this->schedule_inbox_url) );
|
||||
break;
|
||||
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
|
||||
$prop->NewElement($reply->Caldav("schedule-outbox-URL"), new XMLElement('href', $this->schedule_outbox_url) );
|
||||
break;
|
||||
|
||||
case 'http://calendarserver.org/ns/:dropbox-home-URL':
|
||||
$prop->NewElement($reply->Calendarserver("dropbox-home-URL"), new XMLElement('href', $this->dropbox_url) );
|
||||
break;
|
||||
|
||||
case 'http://calendarserver.org/ns/:notifications-URL':
|
||||
$prop->NewElement($reply->Calendarserver("notifications-URL"), new XMLElement('href', $this->notifications_url) );
|
||||
break;
|
||||
|
||||
case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
|
||||
$set = array();
|
||||
foreach( $this->calendar_home_set AS $k => $url ) {
|
||||
$set[] = new XMLElement('href', $url );
|
||||
}
|
||||
$prop->NewElement($reply->Caldav("calendar-home-set"), $set );
|
||||
break;
|
||||
|
||||
case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
|
||||
$set = array();
|
||||
foreach( $this->user_address_set AS $k => $v ) {
|
||||
$set[] = new XMLElement('href', $v );
|
||||
}
|
||||
$prop->NewElement($reply->Caldav("calendar-user-address-set"), $set );
|
||||
break;
|
||||
|
||||
case 'DAV::getcontentlanguage':
|
||||
$locale = $c->current_locale;
|
||||
if ( isset($this->locale) && $this->locale != "" ) $locale = $this->locale;
|
||||
$prop->NewElement("getcontentlanguage", $locale );
|
||||
break;
|
||||
|
||||
case 'DAV::supportedlock':
|
||||
$prop->NewElement("supportedlock",
|
||||
new XMLElement( "lockentry",
|
||||
array(
|
||||
new XMLElement("lockscope", new XMLElement("exclusive")),
|
||||
new XMLElement("locktype", new XMLElement("write")),
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
||||
case 'DAV::acl':
|
||||
/**
|
||||
* FIXME: This information is semantically valid but presents an incorrect picture.
|
||||
*/
|
||||
$principal = new XMLElement("principal");
|
||||
$principal->NewElement("authenticated");
|
||||
$grant = new XMLElement( "grant", array($this->RenderPrivileges($request->permissions)) );
|
||||
$prop->NewElement("acl", new XMLElement( "ace", array( $principal, $grant ) ) );
|
||||
break;
|
||||
|
||||
case 'DAV::current-user-privilege-set':
|
||||
$prop->NewElement("current-user-privilege-set", $this->RenderPrivileges($request->permissions) );
|
||||
break;
|
||||
|
||||
case 'DAV::supported-privilege-set':
|
||||
$prop->NewElement("supported-privilege-set", $this->RenderPrivileges( $request->SupportedPrivileges(), "supported-privilege") );
|
||||
break;
|
||||
|
||||
// Empty tag responses.
|
||||
case 'DAV::creationdate':
|
||||
case 'DAV::alternate-URI-set':
|
||||
case 'DAV::getcontentlength':
|
||||
$prop->NewElement( $reply->Tag($tag));
|
||||
break;
|
||||
|
||||
case 'SOME-DENIED-PROPERTY': /** TODO: indicating the style for future expansion */
|
||||
$denied[] = $reply->Tag($tag);
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_error_log( 'CalDAVPrincipal', "Request for unsupported property '%s' of principal.", $item->username );
|
||||
$not_found[] = $reply->Tag($tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $props_only ) return $prop;
|
||||
|
||||
$status = new XMLElement("status", "HTTP/1.1 200 OK" );
|
||||
|
||||
$propstat = new XMLElement( "propstat", array( $prop, $status) );
|
||||
$href = new XMLElement("href", $this->url );
|
||||
|
||||
$elements = array($href,$propstat);
|
||||
|
||||
if ( count($denied) > 0 ) {
|
||||
$status = new XMLElement("status", "HTTP/1.1 403 Forbidden" );
|
||||
$noprop = new XMLElement("prop");
|
||||
foreach( $denied AS $k => $v ) {
|
||||
$noprop->NewElement( strtolower($v) );
|
||||
}
|
||||
$elements[] = new XMLElement( "propstat", array( $noprop, $status) );
|
||||
}
|
||||
|
||||
if ( count($not_found) > 0 ) {
|
||||
$status = new XMLElement("status", "HTTP/1.1 404 Not Found" );
|
||||
$noprop = new XMLElement("prop");
|
||||
foreach( $not_found AS $k => $v ) {
|
||||
$noprop->NewElement( strtolower($v) );
|
||||
}
|
||||
$elements[] = new XMLElement( "propstat", array( $noprop, $status) );
|
||||
}
|
||||
|
||||
$response = new XMLElement( "response", $elements );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
@ -220,6 +220,7 @@ class CalDAVRequest
|
||||
$xml_parser = xml_parser_create_ns('UTF-8');
|
||||
$this->xml_tags = array();
|
||||
xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
|
||||
xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
|
||||
xml_parse_into_struct( $xml_parser, $this->raw_post, $this->xml_tags );
|
||||
xml_parser_free($xml_parser);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @package rscds
|
||||
* @package davical
|
||||
* @author Andrew McMillan <andrew@catalyst.net.nz>
|
||||
* @copyright Catalyst .Net Ltd
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
||||
@ -96,7 +96,7 @@ awl_set_locale($c->default_locale);
|
||||
*
|
||||
*/
|
||||
$c->code_version = 0;
|
||||
$c->version_string = '0.9.5.2'; // The actual version # is replaced into that during the build /release process
|
||||
$c->version_string = '0.9.5.4'; // The actual version # is replaced into that during the build /release process
|
||||
if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
|
||||
$c->code_major = $matches[1];
|
||||
$c->code_minor = $matches[2];
|
||||
|
||||
@ -134,7 +134,7 @@ 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 * FROM usr WHERE lower(username) = lower(?) ", $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 );
|
||||
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
|
||||
$_known_users_name[$username] = $qry->Fetch();
|
||||
$id = $_known_users_name[$username]->user_no;
|
||||
@ -155,7 +155,7 @@ 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 * FROM usr WHERE user_no = ? ", intval($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) );
|
||||
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
|
||||
$_known_users_id[$user_no] = $qry->Fetch();
|
||||
$name = $_known_users_id[$user_no]->username;
|
||||
|
||||
@ -18,7 +18,7 @@ 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':
|
||||
case 'DAV::lockinfo':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
if ( $v['type'] == "open" ) {
|
||||
$lockscope = "";
|
||||
@ -34,11 +34,11 @@ foreach( $request->xml_tags AS $k => $v ) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::OWNER':
|
||||
case 'DAV::LOCKTYPE':
|
||||
case 'DAV::LOCKSCOPE':
|
||||
case 'DAV::owner':
|
||||
case 'DAV::locktype':
|
||||
case 'DAV::lockscope':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
if ( $inside['DAV::LOCKINFO'] ) {
|
||||
if ( $inside['DAV::lockinfo'] ) {
|
||||
if ( $v['type'] == "open" ) {
|
||||
$inside[$tag] = true;
|
||||
}
|
||||
@ -49,25 +49,25 @@ foreach( $request->xml_tags AS $k => $v ) {
|
||||
break;
|
||||
|
||||
/*case 'DAV::SHARED': */ /** Shared lock is not supported yet */
|
||||
case 'DAV::EXCLUSIVE':
|
||||
case 'DAV::exclusive':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
if ( $inside['DAV::LOCKSCOPE'] && $v['type'] == "complete" ) {
|
||||
if ( $inside['DAV::lockscope'] && $v['type'] == "complete" ) {
|
||||
$lockscope = strtolower(substr($tag,5));
|
||||
}
|
||||
break;
|
||||
|
||||
/* case 'DAV::READ': */ /** RFC2518 is pretty vague about read locks */
|
||||
case 'DAV::WRITE':
|
||||
case 'DAV::write':
|
||||
dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
|
||||
if ( $inside['DAV::LOCKTYPE'] && $v['type'] == "complete" ) {
|
||||
if ( $inside['DAV::locktype'] && $v['type'] == "complete" ) {
|
||||
$locktype = strtolower(substr($tag,5));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::HREF':
|
||||
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" ) {
|
||||
dbg_log_array( "LOCK", "DAV:href", $v, true );
|
||||
if ( $inside['DAV::owner'] && $v['type'] == "complete" ) {
|
||||
$lockowner = $v['value'];
|
||||
}
|
||||
break;
|
||||
@ -136,4 +136,3 @@ $prop = new XMLElement( "prop", $response, array('xmlns'=>'DAV:') );
|
||||
$xmldoc = $prop->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
|
||||
$request->DoResponse( 200, $xmldoc, 'text/xml; charset="utf-8"' );
|
||||
|
||||
?>
|
||||
@ -4,8 +4,8 @@
|
||||
*
|
||||
* @package davical
|
||||
* @subpackage caldav
|
||||
* @author Andrew McMillan <andrew@catalyst.net.nz>
|
||||
* @copyright Catalyst .Net Ltd
|
||||
* @author Andrew McMillan <andrew@mcmillan.net.nz>
|
||||
* @copyright Morphoss Ltd - http://www.morphoss.com/
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
||||
*/
|
||||
dbg_error_log("MKCALENDAR", "method handler");
|
||||
@ -35,10 +35,10 @@ if ( isset($request->xml_tags) ) {
|
||||
$position = 0;
|
||||
$xmltree = BuildXMLTree( $request->xml_tags, $position);
|
||||
// echo $xmltree->Render();
|
||||
if ( $xmltree->GetTag() != "URN:IETF:PARAMS:XML:NS:CALDAV:MKCALENDAR" ) {
|
||||
$request->DoResponse( 403, "XML is not a URN:IETF:PARAMS:XML:NS:CALDAV:MKCALENDAR document" );
|
||||
if ( $xmltree->GetTag() != "urn:ietf:params:xml:ns:caldav:mkcalendar" ) {
|
||||
$request->DoResponse( 403, "The supplied XML is not a 'urn:ietf:params:xml:ns:caldav:mkcalendar' document" );
|
||||
}
|
||||
$setprops = $xmltree->GetPath("/URN:IETF:PARAMS:XML:NS:CALDAV:MKCALENDAR/DAV::SET/DAV::PROP/*");
|
||||
$setprops = $xmltree->GetPath("/urn:ietf:params:xml:ns:caldav:mkcalendar/DAV::set/DAV::prop/*");
|
||||
|
||||
$propertysql = "";
|
||||
foreach( $setprops AS $k => $setting ) {
|
||||
@ -47,7 +47,7 @@ if ( isset($request->xml_tags) ) {
|
||||
|
||||
switch( $tag ) {
|
||||
|
||||
case 'DAV::DISPLAYNAME':
|
||||
case 'DAV::displayname':
|
||||
$displayname = $content;
|
||||
/**
|
||||
* TODO: This is definitely a bug in SOHO Organizer and we probably should respond
|
||||
@ -61,27 +61,27 @@ if ( isset($request->xml_tags) ) {
|
||||
$success[$tag] = 1;
|
||||
break;
|
||||
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:SUPPORTED-CALENDAR-COMPONENT-SET': /** Ignored, since we will support all component types */
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:SUPPORTED-CALENDAR-DATA': /** Ignored, since we will support iCalendar 2.0 */
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA': /** Ignored, since we will support iCalendar 2.0 */
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-RESOURCE-SIZE': /** Ignored, since we will support arbitrary size */
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MIN-DATE-TIME': /** Ignored, since we will support arbitrary time */
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-DATE-TIME': /** Ignored, since we will support arbitrary time */
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-INSTANCES': /** Ignored, since we will support arbitrary instances */
|
||||
case 'DAV::RESOURCETYPE': /** Any value for resourcetype is ignored */
|
||||
case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set': /** Ignored, since we will support all component types */
|
||||
case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data': /** Ignored, since we will support iCalendar 2.0 */
|
||||
case 'urn:ietf:params:xml:ns:caldav:calendar-data': /** Ignored, since we will support iCalendar 2.0 */
|
||||
case 'urn:ietf:params:xml:ns:caldav:max-resource-size': /** Ignored, since we will support arbitrary size */
|
||||
case 'urn:ietf:params:xml:ns:caldav:min-date-time': /** Ignored, since we will support arbitrary time */
|
||||
case 'urn:ietf:params:xml:ns:caldav:max-date-time': /** Ignored, since we will support arbitrary time */
|
||||
case 'urn:ietf:params:xml:ns:caldav:max-instances': /** Ignored, since we will support arbitrary instances */
|
||||
case 'DAV::resourcetype': /** Any value for resourcetype is ignored */
|
||||
$success[$tag] = 1;
|
||||
break;
|
||||
|
||||
/**
|
||||
* The following properties are read-only, so they will cause the request to fail
|
||||
*/
|
||||
case 'DAV::GETETAG':
|
||||
case 'DAV::GETCONTENTLENGTH':
|
||||
case 'DAV::GETCONTENTTYPE':
|
||||
case 'DAV::GETLASTMODIFIED':
|
||||
case 'DAV::CREATIONDATE':
|
||||
case 'DAV::LOCKDISCOVERY':
|
||||
case 'DAV::SUPPORTEDLOCK':
|
||||
case 'DAV::getetag':
|
||||
case 'DAV::getcontentlength':
|
||||
case 'DAV::getcontenttype':
|
||||
case 'DAV::getlastmodified':
|
||||
case 'DAV::creationdate':
|
||||
case 'DAV::lockdiscovery':
|
||||
case 'DAV::supportedlock':
|
||||
$failure['set-'.$tag] = new XMLElement( 'propstat', array(
|
||||
new XMLElement( 'prop', new XMLElement($tag)),
|
||||
new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
|
||||
@ -182,4 +182,3 @@ else {
|
||||
*
|
||||
*/
|
||||
|
||||
?>
|
||||
|
||||
37
inc/caldav-POST.php
Normal file
37
inc/caldav-POST.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* CalDAV Server - handle PUT method
|
||||
*
|
||||
* @package davical
|
||||
* @subpackage caldav
|
||||
* @author Andrew McMillan <andrew@morphoss.com>
|
||||
* @copyright Morphoss Ltd - http://www.morphoss.com/
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
||||
*/
|
||||
dbg_error_log("POST", "method handler");
|
||||
|
||||
require_once("iCalendar.php");
|
||||
|
||||
if ( ! $request->AllowedTo("CALDAV:schedule-send-freebusy")
|
||||
&& ! $request->AllowedTo("CALDAV:schedule-send-invite")
|
||||
&& ! $request->AllowedTo("CALDAV:schedule-send-reply") ) {
|
||||
$request->DoResponse(403);
|
||||
}
|
||||
|
||||
if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || $c->dbg['post']) ) {
|
||||
$fh = fopen('/tmp/POST.txt','w');
|
||||
if ( $fh ) {
|
||||
fwrite($fh,$request->raw_post);
|
||||
fclose($fh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$ical = new iCalendar( array('icalendar' => $request->raw_post) );
|
||||
switch ( $ical->properties['METHOD'] ) {
|
||||
case 'REQUEST':
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_error_log("POST", ": Unhandled '%s' method in request.", $ical->properties['METHOD'] );
|
||||
}
|
||||
@ -14,141 +14,28 @@ if ( ! ($request->AllowedTo('read') || $request->AllowedTo('freebusy')) ) {
|
||||
$request->DoResponse( 403, translate("You may not access that calendar") );
|
||||
}
|
||||
|
||||
require_once("XMLElement.php");
|
||||
require_once("iCalendar.php");
|
||||
require_once("XMLDocument.php");
|
||||
|
||||
$href_list = array();
|
||||
$attribute_list = array();
|
||||
$prop_list = array();
|
||||
$unsupported = array();
|
||||
$arbitrary = array();
|
||||
|
||||
$namespaces = array( "DAV:" => "" );
|
||||
$prefixes = array();
|
||||
function add_namespace( $prefix, $namespace ) {
|
||||
global $namespaces;
|
||||
global $prefixes;
|
||||
|
||||
if ( !isset($namespaces[$namespace]) ) {
|
||||
if ( $prefix == "" || isset($prefixes[$prefix]) ) {
|
||||
dbg_error_log("ERROR", "Cannot assign the same prefix to two different namespaces");
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
$prefixes[$prefix] = $prefix;
|
||||
$namespaces[$namespace] = $prefix;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( $namespaces[$namespace] != $prefix ) {
|
||||
dbg_error_log("ERROR", "Cannot use the same namespace with two different prefixes");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function ns_tag( $in_tag, $namespace=null, $prefix=null ) {
|
||||
global $namespaces, $prefixes;
|
||||
|
||||
if ( $namespace == null ) {
|
||||
// Attempt to split out from namespace:tag
|
||||
if ( preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
|
||||
$namespace = $matches[1];
|
||||
$tag = $matches[2];
|
||||
}
|
||||
else {
|
||||
// There is nothing we can do here
|
||||
return $in_tag;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$tag = $in_tag;
|
||||
}
|
||||
$namespace = strtolower($namespace);
|
||||
if ( $namespace == 'dav:' ) $namespace = 'DAV:'; // Special case for conventional naming
|
||||
$tag = strtolower($tag);
|
||||
|
||||
if ( $prefix == null ) {
|
||||
// Attempt to assign one
|
||||
if ( isset($namespaces[$namespace]) ) {
|
||||
$prefix = $namespaces[$namespace];
|
||||
}
|
||||
else {
|
||||
// Try and build a prefix based on the first alphabetic character of the last element of the namespace
|
||||
if ( preg_match('/^(.*):([^:]+)$/', $namespace, $matches) ) {
|
||||
$alpha = preg_replace( '/[^a-z]/i', '', $matches[2] );
|
||||
$prefix = strtoupper(substr($alpha,0,1));
|
||||
}
|
||||
else {
|
||||
$prefix = 'x';
|
||||
}
|
||||
$i = "";
|
||||
if ( isset($prefixes[$prefix]) ) {
|
||||
for ( $i=1; $i<10 && isset($prefixes["$prefix$i"]); $i++ ) {
|
||||
}
|
||||
}
|
||||
if ( isset($prefixes["$prefix$i"]) ) {
|
||||
dbg_error_log("ERROR", "Cannot find a free prefix for this namespace");
|
||||
exit;
|
||||
}
|
||||
$prefix = "$prefix$i";
|
||||
$namespaces[$namespace] = $prefix;
|
||||
$prefixes[$prefix] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isset($namespaces[$namespace]) ) {
|
||||
add_namespace( $prefix, $namespace );
|
||||
}
|
||||
|
||||
return $prefix . ($prefix == "" ? "" : ":") . $tag;
|
||||
}
|
||||
|
||||
|
||||
function namespace_array() {
|
||||
global $namespaces;
|
||||
|
||||
$ns = array();
|
||||
foreach( $namespaces AS $n => $p ) {
|
||||
if ( $p == "" ) $ns["xmlns"] = $n; else $ns["xmlns:$p"] = $n;
|
||||
}
|
||||
|
||||
return $ns;
|
||||
}
|
||||
|
||||
|
||||
function calendar_server_tag( $tag ) {
|
||||
add_namespace("A", "http://calendarserver.org/ns/");
|
||||
return ns_tag( $tag, 'http://calendarserver.org/ns/' );
|
||||
}
|
||||
|
||||
|
||||
function caldav_tag( $tag ) {
|
||||
return ns_tag( $tag, 'urn:ietf:params:xml:ns:caldav' );
|
||||
}
|
||||
|
||||
$reply = new XMLDocument( array( "DAV:" => "" ) );
|
||||
|
||||
foreach( $request->xml_tags AS $k => $v ) {
|
||||
|
||||
$ns_tag = $v['tag'];
|
||||
if ( preg_match('/^(.*):([^:]+)$/', $ns_tag, $matches) ) {
|
||||
$namespace = $matches[1];
|
||||
$tag = $matches[2];
|
||||
}
|
||||
else {
|
||||
$namespace = "";
|
||||
$tag = $ns_tag;
|
||||
}
|
||||
dbg_error_log( "PROPFIND", " Handling Tag '%s' => '%s' ", $k, $v );
|
||||
dbg_error_log( "PROPFIND", " Handling Tag '%s' => '%s' ", $k, $ns_tag );
|
||||
|
||||
switch ( $tag ) {
|
||||
case 'PROPFIND':
|
||||
case 'PROP':
|
||||
dbg_error_log( "PROPFIND", ":Request: %s -> %s", $v['type'], $tag );
|
||||
switch ( $ns_tag ) {
|
||||
case 'DAV::propfind':
|
||||
case 'DAV::prop':
|
||||
dbg_error_log( "PROPFIND", ":Request: %s -> %s", $v['type'], $ns_tag );
|
||||
break;
|
||||
|
||||
case 'HREF':
|
||||
// dbg_log_array( "PROPFIND", "HREF", $v, true );
|
||||
case 'DAV::href':
|
||||
$href_list[] = $v['value'];
|
||||
dbg_error_log( "PROPFIND", "Adding href '%s'", $v['value'] );
|
||||
break;
|
||||
@ -157,83 +44,87 @@ foreach( $request->xml_tags AS $k => $v ) {
|
||||
/**
|
||||
* Handled DAV properties
|
||||
*/
|
||||
case 'ACL': /** acl - only vaguely supported */
|
||||
case 'CREATIONDATE': /** creationdate - should work fine */
|
||||
case 'GETLASTMODIFIED': /** getlastmodified - should work fine */
|
||||
case 'DISPLAYNAME': /** displayname - should work fine */
|
||||
case 'GETCONTENTLENGTH': /** getcontentlength- should work fine */
|
||||
case 'GETCONTENTTYPE': /** getcontenttype - should work fine */
|
||||
case 'GETETAG': /** getetag - should work fine */
|
||||
case 'GETCTAG': /** Calendar Server extension like etag - should work fine (we just return etag) */
|
||||
case 'SUPPORTEDLOCK': /** supportedlock - should work fine */
|
||||
case 'PRINCIPAL-URL': /** principal-url - should work fine */
|
||||
case 'RESOURCETYPE': /** resourcetype - should work fine */
|
||||
case 'GETCONTENTLANGUAGE': /** resourcetype - should return the user's chosen locale, or default locale */
|
||||
case 'SUPPORTED-PRIVILEGE-SET': /** supported-privilege-set - should work fine */
|
||||
case 'CURRENT-USER-PRIVILEGE-SET': /** current-user-privilege-set - only vaguely supported */
|
||||
case 'ALLPROP': /** allprop - limited support */
|
||||
case 'DAV::acl': /** Only vaguely supported as yet. Will need work.*/
|
||||
case 'DAV::creationdate': /** should work fine */
|
||||
case 'DAV::getlastmodified': /** should work fine */
|
||||
case 'DAV::displayname': /** should work fine */
|
||||
case 'DAV::getcontentlength': /** should work fine */
|
||||
case 'DAV::getcontenttype': /** should work fine */
|
||||
case 'DAV::getetag': /** should work fine */
|
||||
case 'DAV::supportedlock': /** should work fine */
|
||||
case 'DAV::principal-URL': /** should work fine */
|
||||
case 'DAV::owner': /** should work fine */
|
||||
case 'DAV::resourcetype': /** should work fine */
|
||||
case 'DAV::getcontentlanguage': /** should return the user's chosen locale, or default locale */
|
||||
case 'DAV::current-user-privilege-set': /** only vaguely supported */
|
||||
case 'DAV::allprop': /** limited support, needs to be checked for correctness at some point */
|
||||
|
||||
/**
|
||||
* Handled CalDAV properties
|
||||
*/
|
||||
case 'CALENDAR-HOME-SET': /** calendar-home-set is used by iCal in Leopard - should work fine */
|
||||
$attribute_list[$tag] = 1;
|
||||
dbg_error_log( "PROPFIND", "Adding %s attribute '%s'", $namespace, $tag );
|
||||
break;
|
||||
|
||||
|
||||
case 'SUPPORTED-COLLATION-SET': /** fixed server definition - should work fine */
|
||||
case 'SUPPORTED-CALENDAR-COMPONENT-SET': /** fixed server definition - should work fine */
|
||||
case 'urn:ietf:params:xml:ns:caldav:calendar-home-set': /** Should work fine */
|
||||
case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set': /** Should work fine */
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL': /** Support in development */
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL': /** Support in development */
|
||||
|
||||
/**
|
||||
* Handled calendar-schedule properties
|
||||
* Handled calendarserver properties
|
||||
*/
|
||||
case 'CALENDAR-USER-ADDRESS-SET': /** CalDAV+s: slightly supported */
|
||||
// case 'SCHEDULE-INBOX-URL': /** CalDAV+s: not supported */
|
||||
// case 'SCHEDULE-OUTBOX-URL': /** CalDAV+s: not supported */
|
||||
// case 'DROPBOX-HOME-URL': // HTTP://CALENDARSERVER.ORG/NS/
|
||||
// case 'NOTIFICATIONS-URL': // HTTP://CALENDARSERVER.ORG/NS/
|
||||
case 'http://calendarserver.org/ns/:getctag': /** Calendar Server extension like etag - should work fine (we just return etag) */
|
||||
|
||||
$prop_list[$ns_tag] = $ns_tag;
|
||||
dbg_error_log( "PROPFIND", "Adding attribute '%s'", $ns_tag );
|
||||
break;
|
||||
|
||||
/** fixed server definitions - should work fine */
|
||||
case 'DAV::supported-collation-set':
|
||||
case 'DAV::supported-calendar-component-set':
|
||||
case 'DAV::principal-collection-set':
|
||||
case 'DAV::supported-privilege-set':
|
||||
|
||||
// case 'dropbox-home-URL': // HTTP://CALENDARSERVER.ORG/NS/
|
||||
// case 'notifications-URL': // HTTP://CALENDARSERVER.ORG/NS/
|
||||
if ( $_SERVER['PATH_INFO'] == '/' || $_SERVER['PATH_INFO'] == '' ) {
|
||||
$arbitrary[$ns_tag] = $ns_tag;
|
||||
dbg_error_log( "PROPFIND", "Adding arbitrary DAV property '%s'", $ns_tag );
|
||||
}
|
||||
else {
|
||||
$attribute_list[$tag] = 1;
|
||||
dbg_error_log( "PROPFIND", "Adding %s attribute '%s'", $namespace, $tag );
|
||||
$prop_list[$ns_tag] = $ns_tag;
|
||||
dbg_error_log( "PROPFIND", "Adding attribute '%s'", $ns_tag );
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'CALENDAR-TIMEZONE': // CalDAV
|
||||
case 'SUPPORTED-CALENDAR-DATA': // CalDAV
|
||||
case 'MAX-RESOURCE-SIZE': // CalDAV
|
||||
case 'MIN-DATE-TIME': // CalDAV
|
||||
case 'MAX-DATE-TIME': // CalDAV
|
||||
case 'MAX-INSTANCES': // CalDAV
|
||||
case 'MAX-ATTENDEES-PER-INSTANCE': // CalDAV
|
||||
// case 'CHECKED-OUT': // DAV:
|
||||
// case 'CHECKED-IN': // DAV:
|
||||
// case 'SOURCE': // DAV:
|
||||
// case 'LOCKDISCOVERY': // DAV:
|
||||
// case 'EXECUTABLE': // HTTP://APACHE.ORG/DAV/PROPS/
|
||||
case 'urn:ietf:params:xml:ns:caldav:calendar-timezone': // Ignored
|
||||
case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data': // Ignored
|
||||
case 'urn:ietf:params:xml:ns:caldav:max-resource-size': // Ignored - should be a server setting
|
||||
case 'urn:ietf:params:xml:ns:caldav:min-date-time': // Ignored - should be a server setting
|
||||
case 'urn:ietf:params:xml:ns:caldav:max-date-time': // Ignored - should be a server setting
|
||||
case 'urn:ietf:params:xml:ns:caldav:max-instances': // Ignored - should be a server setting
|
||||
case 'urn:ietf:params:xml:ns:caldav:max-attendees-per-instance': // Ignored - should be a server setting
|
||||
/** These are ignored specifically */
|
||||
break;
|
||||
|
||||
/**
|
||||
* Add the ones that are specifically unsupported here.
|
||||
*/
|
||||
// case 'DAV::checked-out': // DAV:
|
||||
// case 'DAV::checked-in': // DAV:
|
||||
// case 'DAV::source': // DAV:
|
||||
// case 'DAV::lockdiscovery': // DAV:
|
||||
// case 'http://apache.org/dav/props/:executable': //
|
||||
case 'This is not a supported property': // an impossible example
|
||||
$unsupported[$tag] = "";
|
||||
dbg_error_log( "PROPFIND", "Unsupported tag >>%s<< in xmlns >>%s<<", $tag, $namespace);
|
||||
$unsupported[$ns_tag] = "";
|
||||
dbg_error_log( "PROPFIND", "Unsupported tag >>%s<< ", $ns_tag);
|
||||
break;
|
||||
|
||||
/**
|
||||
* Arbitrary DAV properties may also be reported
|
||||
*/
|
||||
case 'CALENDAR-DESCRIPTION': // CalDAV, informational
|
||||
case 'urn:ietf:params:xml:ns:caldav:calendar-description': // Supported purely as an arbitrary property
|
||||
default:
|
||||
$arbitrary[$ns_tag] = $ns_tag;
|
||||
dbg_error_log( "PROPFIND", "Adding arbitrary DAV property '%s'", $ns_tag );
|
||||
dbg_error_log( "PROPFIND", "Adding arbitrary property '%s'", $ns_tag );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -281,146 +172,76 @@ function get_arbitrary_properties($dav_name) {
|
||||
* Handles any properties related to the DAV::PRINCIPAL in the request
|
||||
*/
|
||||
function add_principal_properties( &$prop, &$not_found, &$denied ) {
|
||||
global $attribute_list, $session, $c, $request;
|
||||
global $prop_list, $session, $c, $request, $reply;
|
||||
|
||||
if ( isset($attribute_list['PRINCIPAL-URL'] ) ) {
|
||||
$prop->NewElement("principal-url", new XMLElement('href', $request->principal->url ) );
|
||||
$allprop = isset($prop_list['DAV::allprop']);
|
||||
|
||||
if ( isset($prop_list['DAV::principal-URL'] ) ) {
|
||||
$prop->NewElement("principal-URL", new XMLElement('href', $request->principal->url ) );
|
||||
}
|
||||
if ( isset($prop_list['DAV::alternate-URI-set'] ) ) {
|
||||
$prop->NewElement("alternate-URI-set" ); // Empty - there are no alternatives!
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['CALENDAR-HOME-SET'] ) ) {
|
||||
$prop->NewElement(caldav_tag("calendar-home-set"), new XMLElement('href', $request->principal->calendar_home_set ) );
|
||||
if ( isset($prop_list['urn:ietf:params:xml:ns:caldav:calendar-home-set'] ) ) {
|
||||
$home_set = array();
|
||||
$chs = $request->principal->calendar_home_set;
|
||||
foreach( $chs AS $k => $url ) {
|
||||
$home_set[] = new XMLElement('href', $url );
|
||||
}
|
||||
$prop->NewElement($reply->Caldav("calendar-home-set"), $home_set );
|
||||
}
|
||||
if ( isset($attribute_list['SCHEDULE-INBOX-URL'] ) ) {
|
||||
$prop->NewElement(caldav_tag("schedule-inbox-url"), new XMLElement('href', $request->principal->schedule_inbox_url) );
|
||||
if ( isset($prop_list['urn:ietf:params:xml:ns:caldav:schedule-inbox-URL'] ) ) {
|
||||
$prop->NewElement($reply->Caldav("schedule-inbox-URL"), new XMLElement('href', $request->principal->schedule_inbox_url) );
|
||||
}
|
||||
if ( isset($attribute_list['SCHEDULE-OUTBOX-URL'] ) ) {
|
||||
$prop->NewElement(caldav_tag("schedule-outbox-url"), new XMLElement('href', $request->principal->schedule_outbox_url) );
|
||||
if ( isset($prop_list['urn:ietf:params:xml:ns:caldav:schedule-outbox-URL'] ) ) {
|
||||
$prop->NewElement($reply->Caldav("schedule-outbox-URL"), new XMLElement('href', $request->principal->schedule_outbox_url) );
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['DROPBOX-HOME-URL'] ) ) {
|
||||
$prop->NewElement(calendar_server_tag("dropbox-home-url"), new XMLElement('href', $request->principal->dropbox_url) );
|
||||
if ( isset($prop_list['http://calendarserver.org/ns/:dropbox-home-URL'] ) ) {
|
||||
$prop->NewElement($reply->Calendarserver("dropbox-home-URL"), new XMLElement('href', $request->principal->dropbox_url) );
|
||||
}
|
||||
if ( isset($attribute_list['NOTIFICATIONS-URL'] ) ) {
|
||||
$prop->NewElement(calendar_server_tag("notifications-url"), new XMLElement('href', $request->principal->notifications_url) );
|
||||
if ( isset($prop_list['http://calendarserver.org/ns/:notifications-URL'] ) ) {
|
||||
$prop->NewElement($reply->Calendarserver("notifications-URL"), new XMLElement('href', $request->principal->notifications_url) );
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['CALENDAR-USER-ADDRESS-SET'] ) ) {
|
||||
if ( isset($prop_list['urn:ietf:params:xml:ns:caldav:calendar-user-address-set'] ) ) {
|
||||
$addr_set = array();
|
||||
foreach( $request->principal->user_address_set AS $k => $v ) {
|
||||
$uas = $request->principal->user_address_set;
|
||||
foreach( $uas AS $k => $v ) {
|
||||
$addr_set[] = new XMLElement('href', $v );
|
||||
}
|
||||
$prop->NewElement(caldav_tag("calendar-user-address-set"), $addr_set );
|
||||
$prop->NewElement($reply->Caldav("calendar-user-address-set"), $addr_set );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an XML sub-tree for a single collection record from the DB
|
||||
* Handles any properties related to the DAV::PRINCIPAL in the request
|
||||
*/
|
||||
function collection_to_xml( $collection ) {
|
||||
global $arbitrary, $attribute_list, $session, $c, $request;
|
||||
function add_general_properties( &$prop, &$not_found, &$denied, $record ) {
|
||||
global $prop_list, $session, $c, $request, $reply;
|
||||
|
||||
dbg_error_log("PROPFIND","Building XML Response for collection '%s'", $collection->dav_name );
|
||||
$allprop = isset($prop_list['DAV::allprop']);
|
||||
|
||||
$arbitrary_results = get_arbitrary_properties($collection->dav_name);
|
||||
$collection->properties = $arbitrary_results->found;
|
||||
|
||||
$url = ConstructURL($collection->dav_name);
|
||||
|
||||
$resourcetypes = array( new XMLElement("collection") );
|
||||
$contentlength = false;
|
||||
if ( $collection->is_calendar == 't' ) {
|
||||
$resourcetypes[] = new XMLElement(caldav_tag("calendar"), false);
|
||||
$lqry = new PgQuery("SELECT sum(length(caldav_data)) FROM caldav_data WHERE user_no = ? AND dav_name ~ ?;", $collection->user_no, $collection->dav_name.'[^/]+$' );
|
||||
if ( $lqry->Exec("PROPFIND",__LINE__,__FILE__) && $row = $lqry->Fetch() ) {
|
||||
$contentlength = $row->sum;
|
||||
}
|
||||
if ( $allprop || isset($prop_list['DAV::getlastmodified']) ) {
|
||||
$prop->NewElement("getlastmodified", ( isset($record->modified)? $record->modified : false ));
|
||||
}
|
||||
if ( $collection->is_principal == 't' ) {
|
||||
$resourcetypes[] = new XMLElement("principal");
|
||||
if ( $allprop || isset($prop_list['DAV::creationdate']) ) {
|
||||
$prop->NewElement("creationdate", $record->created );
|
||||
}
|
||||
$prop = new XMLElement("prop");
|
||||
$not_found = new XMLElement("prop");
|
||||
$denied = new XMLElement("prop");
|
||||
|
||||
/**
|
||||
* First process any static values we do support
|
||||
*/
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['SUPPORTED-COLLATION-SET']) ) {
|
||||
$collations = array();
|
||||
$collations[] = new XMLElement(caldav_tag("supported-collation"), 'i;ascii-casemap');
|
||||
$collations[] = new XMLElement(caldav_tag("supported-collation"), 'i;octet');
|
||||
$prop->NewElement(caldav_tag("supported-collation-set"), $collations );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['SUPPORTED-CALENDAR-COMPONENT-SET']) ) {
|
||||
$components = array();
|
||||
$components[] = new XMLElement(caldav_tag("comp"), '', array("name" => "VEVENT"));
|
||||
$components[] = new XMLElement(caldav_tag("comp"), '', array("name" => "VTODO"));
|
||||
$prop->NewElement(caldav_tag("supported-calendar-component-set"), $components );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTTYPE']) ) {
|
||||
$prop->NewElement("getcontenttype", "httpd/unix-directory" );
|
||||
if ( $allprop || isset($prop_list['DAV::getetag']) ) {
|
||||
$prop->NewElement("getetag", '"'.$record->dav_etag.'"' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Second process any dynamic values we do support
|
||||
*/
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETLASTMODIFIED']) ) {
|
||||
$prop->NewElement("getlastmodified", ( isset($collection->modified)? $collection->modified : false ));
|
||||
if ( isset($prop_list['DAV::owner']) ) {
|
||||
$prop->NewElement("owner", new XMLElement('href', 'mailto:'.$request->principal->email ) );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTLENGTH']) ) {
|
||||
$prop->NewElement("getcontentlength", $contentlength );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['CREATIONDATE']) ) {
|
||||
$prop->NewElement("creationdate", $collection->created );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['RESOURCETYPE']) ) {
|
||||
$prop->NewElement("resourcetype", $resourcetypes );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['DISPLAYNAME']) ) {
|
||||
$displayname = ( $collection->dav_displayname == "" ? ucfirst(trim(str_replace("/"," ", $collection->dav_name))) : $collection->dav_displayname );
|
||||
$prop->NewElement("displayname", $displayname );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETETAG']) ) {
|
||||
$prop->NewElement("getetag", '"'.$collection->dav_etag.'"' );
|
||||
}
|
||||
if ( isset($attribute_list['GETCTAG']) ) {
|
||||
// Calendar Server extension which only applies to collections. We return the etag, which does the needful.
|
||||
$prop->NewElement(calendar_server_tag('getctag'),$collection->dav_etag );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['CURRENT-USER-PRIVILEGE-SET']) ) {
|
||||
$prop->NewElement("current-user-privilege-set", privileges($request->permissions) );
|
||||
if ( isset($prop_list['DAV::principal-collection-set']) ) {
|
||||
$prop->NewElement("principal-collection-set", new XMLElement('href', ConstructURL('/') ) );
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['CALENDAR-FREE-BUSY-SET'] ) ) {
|
||||
if ( isset($collection->is_inbox) && $collection->is_inbox && $session->user_no == $collection->user_no ) {
|
||||
$fb_set = array();
|
||||
foreach( $collection->free_busy_set AS $k => $v ) {
|
||||
$fb_set[] = new XMLElement('href', $v );
|
||||
}
|
||||
$prop->NewElement(caldav_tag("calendar-free-busy-set"), $fb_set );
|
||||
}
|
||||
else if ( $session->user_no == $collection->user_no ) {
|
||||
$not_found->NewElement(caldav_tag("calendar-free-busy-set") );
|
||||
}
|
||||
else {
|
||||
$denied->NewElement(caldav_tag("calendar-free-busy-set") );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Then look at any properties related to the principal
|
||||
*/
|
||||
add_principal_properties( $prop, $not_found, $denied );
|
||||
|
||||
if ( count($collection->properties) > 0 ) {
|
||||
foreach( $collection->properties AS $k => $v ) {
|
||||
$prop->NewElement(ns_tag($k), $v);
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['ACL']) ) {
|
||||
if ( isset($prop_list['DAV::acl']) ) {
|
||||
/**
|
||||
* FIXME: This information is semantically valid but presents an incorrect picture.
|
||||
*/
|
||||
@ -430,12 +251,12 @@ function collection_to_xml( $collection ) {
|
||||
$prop->NewElement("acl", new XMLElement( "ace", array( $principal, $grant ) ) );
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTLANGUAGE']) ) {
|
||||
if ( $allprop || isset($prop_list['DAV::getcontentlanguage']) ) {
|
||||
$contentlength = strlen($item->caldav_data);
|
||||
$prop->NewElement("getcontentlanguage", $c->current_locale );
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['SUPPORTEDLOCK']) ) {
|
||||
if ( isset($prop_list['DAV::supportedlock']) ) {
|
||||
$prop->NewElement("supportedlock",
|
||||
new XMLElement( "lockentry",
|
||||
array(
|
||||
@ -446,21 +267,28 @@ function collection_to_xml( $collection ) {
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['SUPPORTED-PRIVILEGE-SET']) ) {
|
||||
if ( isset($prop_list['DAV::current-user-privilege-set']) ) {
|
||||
$prop->NewElement("current-user-privilege-set", privileges($request->permissions) );
|
||||
}
|
||||
|
||||
if ( isset($prop_list['DAV::supported-privilege-set']) ) {
|
||||
$prop->NewElement("supported-privilege-set", privileges( $request->SupportedPrivileges(), "supported-privilege") );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the <propstat><prop></prop><status></status></propstat> part of the response
|
||||
*/
|
||||
function build_propstat_response( $prop, $not_found, $denied, $url ) {
|
||||
|
||||
$status = new XMLElement("status", "HTTP/1.1 200 OK" );
|
||||
|
||||
$propstat = new XMLElement( "propstat", array( $prop, $status) );
|
||||
$href = new XMLElement("href", $url );
|
||||
$response = array($href,$propstat);
|
||||
|
||||
if ( count($arbitrary_results->missing) > 0 ) {
|
||||
foreach( $arbitrary_results->missing AS $k => $v ) {
|
||||
$not_found->NewElement(ns_tag($k), '');
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_array($not_found->content) && count($not_found->content) > 0 ) {
|
||||
$response[] = new XMLElement( "propstat", array( $not_found, new XMLElement("status", "HTTP/1.1 404 Not Found" )) );
|
||||
}
|
||||
@ -475,14 +303,134 @@ function collection_to_xml( $collection ) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an XML sub-tree for a single collection record from the DB
|
||||
*/
|
||||
function collection_to_xml( $collection ) {
|
||||
global $arbitrary, $prop_list, $session, $c, $request, $reply;
|
||||
|
||||
dbg_error_log("PROPFIND","Building XML Response for collection '%s'", $collection->dav_name );
|
||||
|
||||
$allprop = isset($prop_list['DAV::allprop']);
|
||||
|
||||
$arbitrary_results = get_arbitrary_properties($collection->dav_name);
|
||||
$collection->properties = $arbitrary_results->found;
|
||||
|
||||
$url = ConstructURL($collection->dav_name);
|
||||
|
||||
$prop = new XMLElement("prop");
|
||||
$not_found = new XMLElement("prop");
|
||||
$denied = new XMLElement("prop");
|
||||
|
||||
/**
|
||||
* First process any static values we do support
|
||||
*/
|
||||
if ( isset($prop_list['urn:ietf:params:xml:ns:caldav:supported-collation-set']) ) {
|
||||
$collations = array();
|
||||
$collations[] = new XMLElement($reply->Caldav("supported-collation"), 'i;ascii-casemap');
|
||||
$collations[] = new XMLElement($reply->Caldav("supported-collation"), 'i;octet');
|
||||
$prop->NewElement($reply->Caldav("supported-collation-set"), $collations );
|
||||
}
|
||||
if ( isset($prop_list['urn:ietf:params:xml:ns:caldav:supported-calendar-component-set']) ) {
|
||||
$components = array();
|
||||
$components[] = new XMLElement($reply->Caldav("comp"), '', array("name" => "VEVENT"));
|
||||
$components[] = new XMLElement($reply->Caldav("comp"), '', array("name" => "VTODO"));
|
||||
$prop->NewElement($reply->Caldav("supported-calendar-component-set"), $components );
|
||||
}
|
||||
if ( $allprop || isset($prop_list['DAV::getcontenttype']) ) {
|
||||
$prop->NewElement("getcontenttype", "httpd/unix-directory" ); // Strictly text/icalendar perhaps
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any dynamic values we do support
|
||||
*/
|
||||
if ( $allprop || isset($prop_list['DAV::getcontentlength'])
|
||||
|| isset($prop_list['DAV::resourcetype']) ) {
|
||||
$resourcetypes = array( new XMLElement("collection") );
|
||||
$contentlength = false;
|
||||
if ( $collection->is_calendar == 't' ) {
|
||||
$resourcetypes[] = new XMLElement($reply->Caldav("calendar"), false);
|
||||
$lqry = new PgQuery("SELECT sum(length(caldav_data)) FROM caldav_data WHERE user_no = ? AND dav_name ~ ?;", $collection->user_no, $collection->dav_name.'[^/]+$' );
|
||||
if ( $lqry->Exec("PROPFIND",__LINE__,__FILE__) && $row = $lqry->Fetch() ) {
|
||||
$contentlength = $row->sum;
|
||||
}
|
||||
}
|
||||
if ( $collection->is_principal == 't' ) {
|
||||
$resourcetypes[] = new XMLElement("principal");
|
||||
}
|
||||
if ( $allprop || isset($prop_list['DAV::getcontentlength']) ) {
|
||||
$prop->NewElement("getcontentlength", $contentlength ); // Not strictly correct as a GET on this URL would be longer
|
||||
}
|
||||
if ( $allprop || isset($prop_list['DAV::resourcetype']) ) {
|
||||
$prop->NewElement("resourcetype", $resourcetypes );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $allprop || isset($prop_list['DAV::displayname']) ) {
|
||||
$displayname = ( $collection->dav_displayname == "" ? ucfirst(trim(str_replace("/"," ", $collection->dav_name))) : $collection->dav_displayname );
|
||||
$prop->NewElement("displayname", $displayname );
|
||||
}
|
||||
if ( isset($prop_list['http://calendarserver.org/ns/:getctag']) ) {
|
||||
// Calendar Server extension which only applies to collections. We return the etag, which does the needful.
|
||||
$prop->NewElement($reply->Calendarserver('getctag'),$collection->dav_etag );
|
||||
}
|
||||
|
||||
if ( isset($prop_list['urn:ietf:params:xml:ns:caldav:calendar-free-busy-set'] ) ) {
|
||||
if ( isset($collection->is_inbox) && $collection->is_inbox && $session->user_no == $collection->user_no ) {
|
||||
$fb_set = array();
|
||||
foreach( $collection->free_busy_set AS $k => $v ) {
|
||||
$fb_set[] = new XMLElement('href', $v );
|
||||
}
|
||||
$prop->NewElement($reply->Caldav("calendar-free-busy-set"), $fb_set );
|
||||
}
|
||||
else if ( $session->user_no == $collection->user_no ) {
|
||||
$not_found->NewElement($reply->Caldav("calendar-free-busy-set") );
|
||||
}
|
||||
else {
|
||||
$denied->NewElement($reply->Caldav("calendar-free-busy-set") );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Then look at any properties related to the principal
|
||||
*/
|
||||
add_principal_properties( $prop, $not_found, $denied );
|
||||
|
||||
/**
|
||||
* And any properties that are server/request related, or standard fields
|
||||
* from our query.
|
||||
*/
|
||||
add_general_properties( $prop, $not_found, $denied, $collection );
|
||||
|
||||
/**
|
||||
* Arbitrary collection properties
|
||||
*/
|
||||
if ( count($collection->properties) > 0 ) {
|
||||
foreach( $collection->properties AS $k => $v ) {
|
||||
$prop->NewElement($reply->Tag($k), $v);
|
||||
}
|
||||
}
|
||||
|
||||
if ( count($arbitrary_results->missing) > 0 ) {
|
||||
foreach( $arbitrary_results->missing AS $k => $v ) {
|
||||
$not_found->NewElement($reply->Tag($k), '');
|
||||
}
|
||||
}
|
||||
|
||||
return build_propstat_response( $prop, $not_found, $denied, $url );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return XML for a single data item from the DB
|
||||
*/
|
||||
function item_to_xml( $item ) {
|
||||
global $attribute_list, $session, $c, $request;
|
||||
global $prop_list, $session, $c, $request, $reply;
|
||||
|
||||
dbg_error_log("PROPFIND","Building XML Response for item '%s'", $item->dav_name );
|
||||
|
||||
$allprop = isset($prop_list['DAV::allprop']);
|
||||
|
||||
$item->properties = get_arbitrary_properties($item->dav_name);
|
||||
|
||||
$url = ConstructURL($item->dav_name);
|
||||
@ -492,82 +440,35 @@ function item_to_xml( $item ) {
|
||||
$denied = new XMLElement("prop");
|
||||
|
||||
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETLASTMODIFIED']) ) {
|
||||
$prop->NewElement("getlastmodified", ( isset($item->modified)? $item->modified : false ));
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTLENGTH']) ) {
|
||||
if ( $allprop || isset($prop_list['DAV::getcontentlength']) ) {
|
||||
$contentlength = strlen($item->caldav_data);
|
||||
$prop->NewElement("getcontentlength", $contentlength );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTTYPE']) ) {
|
||||
if ( $allprop || isset($prop_list['DAV::getcontenttype']) ) {
|
||||
$prop->NewElement("getcontenttype", "text/calendar" );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['CREATIONDATE']) ) {
|
||||
$prop->NewElement("creationdate", $item->created );
|
||||
if ( $allprop || isset($prop_list['DAV::displayname']) ) {
|
||||
$prop->NewElement("displayname", $item->dav_displayname );
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-collections should return an empty resource type, it appears from RFC2518 8.1.2
|
||||
*/
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['RESOURCETYPE']) ) {
|
||||
if ( $allprop || isset($prop_list['DAV::resourcetype']) ) {
|
||||
$prop->NewElement("resourcetype");
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['DISPLAYNAME']) ) {
|
||||
$prop->NewElement("displayname", $item->dav_displayname );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETETAG']) ) {
|
||||
$prop->NewElement("getetag", '"'.$item->dav_etag.'"' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Then look at any properties related to the principal
|
||||
*/
|
||||
add_principal_properties( $prop, $not_found, $denied );
|
||||
|
||||
if ( isset($attribute_list['ACL']) ) {
|
||||
/**
|
||||
* FIXME: This information is semantically valid but presents an incorrect picture.
|
||||
*/
|
||||
$principal = new XMLElement("principal");
|
||||
$principal->NewElement("authenticated");
|
||||
$grant = new XMLElement( "grant", array(privileges($request->permissions)) );
|
||||
$prop->NewElement("acl", new XMLElement( "ace", array( $principal, $grant ) ) );
|
||||
}
|
||||
/**
|
||||
* And any properties that are server/request related.
|
||||
*/
|
||||
add_general_properties( $prop, $not_found, $denied, $item );
|
||||
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTLANGUAGE']) ) {
|
||||
$contentlength = strlen($item->caldav_data);
|
||||
$prop->NewElement("getcontentlanguage", $c->current_locale );
|
||||
}
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['CURRENT-USER-PRIVILEGE-SET']) ) {
|
||||
$prop->NewElement("current-user-privilege-set", privileges($request->permissions) );
|
||||
}
|
||||
|
||||
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['SUPPORTEDLOCK']) ) {
|
||||
$prop->NewElement("supportedlock",
|
||||
new XMLElement( "lockentry",
|
||||
array(
|
||||
new XMLElement("lockscope", new XMLElement("exclusive")),
|
||||
new XMLElement("locktype", new XMLElement("write")),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
$status = new XMLElement("status", "HTTP/1.1 200 OK" );
|
||||
|
||||
$propstat = new XMLElement( "propstat", array( $prop, $status) );
|
||||
$href = new XMLElement("href", $url );
|
||||
$response = array($href,$propstat);
|
||||
|
||||
if ( is_array($not_found->content) && count($not_found->content) > 0 ) {
|
||||
$response[] = new XMLElement( "propstat", array( $not_found, new XMLElement("status", "HTTP/1.1 404 Not Found" )) );
|
||||
}
|
||||
|
||||
if ( is_array($denied->content) && count($denied->content) > 0 ) {
|
||||
$response[] = new XMLElement( "propstat", array( $denied, new XMLElement("status", "HTTP/1.1 403 Forbidden" )) );
|
||||
}
|
||||
|
||||
$response = new XMLElement( "response", $response );
|
||||
|
||||
return $response;
|
||||
return build_propstat_response( $prop, $not_found, $denied, $url );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -576,7 +477,7 @@ function item_to_xml( $item ) {
|
||||
* a list of calendars for the user which are parented by this path.
|
||||
*/
|
||||
function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
global $session, $request;
|
||||
global $session, $request, $reply, $prop_list;
|
||||
|
||||
dbg_error_log("PROPFIND","Getting collection contents: Depth %d, User: %d, Path: %s", $depth, $user_no, $collection->dav_name );
|
||||
|
||||
@ -586,7 +487,7 @@ function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
* Calendar collections may not contain calendar collections.
|
||||
*/
|
||||
if ( $collection->dav_name == '/' ) {
|
||||
$sql = "SELECT user_no, user_no, '/' || username || '/' AS dav_name, md5( '/' || username || '/') AS dav_etag, ";
|
||||
$sql = "SELECT usr.*, '/' || username || '/' AS dav_name, md5( '/' || username || '/') AS dav_etag, ";
|
||||
$sql .= "to_char(updated at time zone 'GMT',?) AS created, ";
|
||||
$sql .= "to_char(updated at time zone 'GMT',?) AS modified, ";
|
||||
$sql .= "fullname AS dav_displayname, FALSE AS is_calendar, TRUE AS is_principal FROM usr ";
|
||||
@ -603,7 +504,13 @@ function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
|
||||
if( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows > 0 ) {
|
||||
while( $subcollection = $qry->Fetch() ) {
|
||||
$responses[] = collection_to_xml( $subcollection );
|
||||
if ( $subcollection->is_principal == "t" ) {
|
||||
$principal = new CalDAVPrincipal($subcollection);
|
||||
$responses[] = $principal->RenderAsXML($prop_list, &$reply);
|
||||
}
|
||||
else {
|
||||
$responses[] = collection_to_xml( $subcollection );
|
||||
}
|
||||
if ( $depth > 0 ) {
|
||||
$responses = array_merge( $responses, get_collection_contents( $depth - 1, $user_no, $subcollection ) );
|
||||
}
|
||||
@ -740,7 +647,8 @@ else {
|
||||
$request->DoResponse( 403, translate("You do not have appropriate rights to view that resource.") );
|
||||
}
|
||||
|
||||
$multistatus = new XMLElement( "multistatus", $responses, namespace_array() );
|
||||
|
||||
$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
|
||||
|
||||
// dbg_log_array( "PROPFIND", "XML", $multistatus, true );
|
||||
$xmldoc = $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
*
|
||||
* @package davical
|
||||
* @subpackage caldav
|
||||
* @author Andrew McMillan <andrew@catalyst.net.nz>
|
||||
* @copyright Catalyst .Net Ltd
|
||||
* @author Andrew McMillan <andrew@mcmillan.net.nz>
|
||||
* @copyright Morphoss Ltd - http://www.morphoss.com/
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
||||
*/
|
||||
dbg_error_log("PROPPATCH", "method handler");
|
||||
@ -19,15 +19,15 @@ $xmltree = BuildXMLTree( $request->xml_tags, $position);
|
||||
|
||||
// echo $xmltree->Render();
|
||||
|
||||
if ( $xmltree->GetTag() != "DAV::PROPERTYUPDATE" ) {
|
||||
if ( $xmltree->GetTag() != "DAV::propertyupdate" ) {
|
||||
$request->DoResponse( 403 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the properties being set, and the properties being removed
|
||||
*/
|
||||
$setprops = $xmltree->GetPath("/DAV::PROPERTYUPDATE/DAV::SET/DAV::PROP/*");
|
||||
$rmprops = $xmltree->GetPath("/DAV::PROPERTYUPDATE/DAV::REMOVE/DAV::PROP/*");
|
||||
$setprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::set/DAV::prop/*");
|
||||
$rmprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::remove/DAV::prop/*");
|
||||
|
||||
/**
|
||||
* We build full status responses for failures. For success we just record
|
||||
@ -50,7 +50,7 @@ foreach( $setprops AS $k => $setting ) {
|
||||
|
||||
switch( $tag ) {
|
||||
|
||||
case 'DAV::DISPLAYNAME':
|
||||
case 'DAV::displayname':
|
||||
/**
|
||||
* Can't set displayname on resources - only collections or principals
|
||||
*/
|
||||
@ -74,12 +74,12 @@ foreach( $setprops AS $k => $setting ) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::RESOURCETYPE':
|
||||
case 'DAV::resourcetype':
|
||||
/**
|
||||
* We don't allow a collection to change to/from a resource. Only collections may be CalDAV calendars.
|
||||
*/
|
||||
$setcollection = count($setting->GetPath('DAV::RESOURCETYPE/DAV::COLLECTION'));
|
||||
$setcalendar = count($setting->GetPath('DAV::RESOURCETYPE/urn:ietf:params:xml:ns:caldav:calendar'));
|
||||
$setcollection = count($setting->GetPath('DAV::resourcetype/DAV::collection'));
|
||||
$setcalendar = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
|
||||
if ( $request->IsCollection() && ($setcollection || $setcalendar) ) {
|
||||
if ( $setcalendar ) {
|
||||
$sql .= sprintf( "UPDATE collection SET is_calendar = TRUE WHERE dav_name = %s;", qpg($request->path) );
|
||||
@ -98,13 +98,19 @@ foreach( $setprops AS $k => $setting ) {
|
||||
/**
|
||||
* The following properties are read-only, so they will cause the request to fail
|
||||
*/
|
||||
case 'DAV::GETETAG':
|
||||
case 'DAV::GETCONTENTLENGTH':
|
||||
case 'DAV::GETCONTENTTYPE':
|
||||
case 'DAV::GETLASTMODIFIED':
|
||||
case 'DAV::CREATIONDATE':
|
||||
case 'DAV::LOCKDISCOVERY':
|
||||
case 'DAV::SUPPORTEDLOCK':
|
||||
case 'http://calendarserver.org/ns/:getctag':
|
||||
case 'DAV::owner':
|
||||
case 'DAV::principal-collection-set':
|
||||
case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
|
||||
case 'DAV::getetag':
|
||||
case 'DAV::getcontentlength':
|
||||
case 'DAV::getcontenttype':
|
||||
case 'DAV::getlastmodified':
|
||||
case 'DAV::creationdate':
|
||||
case 'DAV::lockdiscovery':
|
||||
case 'DAV::supportedlock':
|
||||
$failure['set-'.$tag] = new XMLElement( 'propstat', array(
|
||||
new XMLElement( 'prop', new XMLElement($tag)),
|
||||
new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
|
||||
@ -130,12 +136,12 @@ foreach( $rmprops AS $k => $setting ) {
|
||||
|
||||
switch( $tag ) {
|
||||
|
||||
case 'DAV::RESOURCETYPE':
|
||||
case 'DAV::resourcetype':
|
||||
/**
|
||||
* We don't allow a collection to change to/from a resource. Only collections may be CalDAV calendars.
|
||||
*/
|
||||
$rmcollection = (count($setting->GetPath('DAV::RESOURCETYPE/DAV::COLLECTION')) > 0);
|
||||
$rmcalendar = (count($setting->GetPath('DAV::RESOURCETYPE/urn:ietf:params:xml:ns:caldav:calendar')) > 0);
|
||||
$rmcollection = (count($setting->GetPath('DAV::resourcetype/DAV::collection')) > 0);
|
||||
$rmcalendar = (count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar')) > 0);
|
||||
if ( $request->IsCollection() && !$rmcollection ) {
|
||||
dbg_error_log( 'PROPPATCH', ' RMProperty %s : IsCollection=%d, rmcoll=%d, rmcal=%d', $tag, $request->IsCollection(), $rmcollection, $rmcalendar );
|
||||
if ( $rmcalendar ) {
|
||||
@ -156,14 +162,20 @@ foreach( $rmprops AS $k => $setting ) {
|
||||
/**
|
||||
* The following properties are read-only, so they will cause the request to fail
|
||||
*/
|
||||
case 'DAV::GETETAG':
|
||||
case 'DAV::GETCONTENTLENGTH':
|
||||
case 'DAV::GETCONTENTTYPE':
|
||||
case 'DAV::GETLASTMODIFIED':
|
||||
case 'DAV::CREATIONDATE':
|
||||
case 'DAV::DISPLAYNAME':
|
||||
case 'DAV::LOCKDISCOVERY':
|
||||
case 'DAV::SUPPORTEDLOCK':
|
||||
case 'http://calendarserver.org/ns/:getctag':
|
||||
case 'DAV::owner':
|
||||
case 'DAV::principal-collection-set':
|
||||
case 'urn:ietf:params:xml:ns:caldav:CALENDAR-USER-ADDRESS-SET':
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
|
||||
case 'DAV::getetag':
|
||||
case 'DAV::getcontentlength':
|
||||
case 'DAV::getcontenttype':
|
||||
case 'DAV::getlastmodified':
|
||||
case 'DAV::creationdate':
|
||||
case 'DAV::displayname':
|
||||
case 'DAV::lockdiscovery':
|
||||
case 'DAV::supportedlock':
|
||||
$failure['rm-'.$tag] = new XMLElement( 'propstat', array(
|
||||
new XMLElement( 'prop', new XMLElement($tag)),
|
||||
new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
|
||||
@ -225,4 +237,3 @@ $request->DoResponse( 500 );
|
||||
|
||||
exit(0);
|
||||
|
||||
?>
|
||||
|
||||
@ -3,20 +3,20 @@
|
||||
/**
|
||||
* Build the array of properties to include in the report output
|
||||
*/
|
||||
$qry_content = $xmltree->GetContent('URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY');
|
||||
$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-query');
|
||||
$proptype = $qry_content[0]->GetTag();
|
||||
$properties = array();
|
||||
switch( $proptype ) {
|
||||
case 'DAV::PROP':
|
||||
$qry_props = $xmltree->GetPath('/URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY/DAV::PROP/*');
|
||||
case 'DAV::prop':
|
||||
$qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/DAV::prop/*');
|
||||
foreach( $qry_props AS $k => $v ) {
|
||||
$propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
|
||||
$properties[$propertyname] = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::ALLPROP':
|
||||
$properties['ALLPROP'] = 1;
|
||||
case 'DAV::allprop':
|
||||
$properties['allprop'] = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -30,13 +30,13 @@ switch( $proptype ) {
|
||||
* VCALENDAR, but perhaps there are others. In our case we strip it if that is
|
||||
* the case and leave it alone otherwise.
|
||||
*/
|
||||
$qry_filters = $xmltree->GetPath('/URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY/URN:IETF:PARAMS:XML:NS:CALDAV:FILTER/*');
|
||||
$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/urn:ietf:params:xml:ns:caldav:filter/*');
|
||||
if ( count($qry_filters) == 1 ) {
|
||||
$qry_filters = $qry_filters[0]; // There can only be one FILTER element
|
||||
if ( $qry_filters->GetTag() == "URN:IETF:PARAMS:XML:NS:CALDAV:COMP-FILTER" && $qry_filters->GetAttribute("NAME") == "VCALENDAR" )
|
||||
if ( $qry_filters->GetTag() == "urn:ietf:params:xml:ns:caldav:comp-filter" && $qry_filters->GetAttribute("name") == "VCALENDAR" )
|
||||
$qry_filters = $qry_filters->GetContent(); // Everything is inside a VCALENDAR AFAICS
|
||||
else {
|
||||
dbg_error_log("calquery", "Got bizarre CALDAV:FILTER[%s=%s]] which does not contain COMP-FILTER = VCALENDAR!!", $qry_filters->GetTag(), $qry_filters->GetAttribute("NAME") );
|
||||
dbg_error_log("calquery", "Got bizarre CALDAV:FILTER[%s=%s]] which does not contain comp-filter = VCALENDAR!!", $qry_filters->GetTag(), $qry_filters->GetAttribute("name") );
|
||||
$qry_filters = false;
|
||||
}
|
||||
}
|
||||
@ -82,12 +82,12 @@ function SqlFilterFragment( $filter, $components, $property = null, $parameter =
|
||||
|
||||
$not_defined = "";
|
||||
switch( $tag ) {
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:IS-NOT-DEFINED':
|
||||
$not_defined = "NOT "; // then fall through to IS-DEFINED case
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:IS-DEFINED':
|
||||
case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
|
||||
$not_defined = "not-"; // then fall through to IS-DEFINED case
|
||||
case 'urn:ietf:params:xml:ns:caldav:is-defined':
|
||||
if ( isset( $parameter ) ) {
|
||||
$need_post_filter = true;
|
||||
dbg_error_log("calquery", "Could not handle IS-%sDEFINED on property %s, parameter %s in SQL", $not_defined, $property, $parameter );
|
||||
dbg_error_log("calquery", "Could not handle 'is-%sdefined' on property %s, parameter %s in SQL", $not_defined, $property, $parameter );
|
||||
return false; // Not handled in SQL
|
||||
}
|
||||
if ( isset( $property ) ) {
|
||||
@ -111,14 +111,14 @@ function SqlFilterFragment( $filter, $components, $property = null, $parameter =
|
||||
}
|
||||
break;
|
||||
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:TIME-RANGE':
|
||||
case 'urn:ietf:params:xml:ns:caldav:time-range':
|
||||
/**
|
||||
* TODO: We should probably allow time range queries against other properties, since eventually some client may want to do this.
|
||||
*/
|
||||
$start_column = ($components[sizeof($components)-1] == 'VTODO' ? "due" : 'dtend'); // The column we compare against the START attribute
|
||||
$finish_column = 'dtstart'; // The column we compare against the END attribute
|
||||
$start = $v->GetAttribute("START");
|
||||
$finish = $v->GetAttribute("END");
|
||||
$start = $v->GetAttribute("start");
|
||||
$finish = $v->GetAttribute("end");
|
||||
if ( isset($start) && isset($finish) ) {
|
||||
$sql .= sprintf( "AND ( (%s >= %s::timestamp with time zone AND %s <= %s::timestamp with time zone) ",
|
||||
$start_column, qpg($start), $finish_column, qpg($finish));
|
||||
@ -137,10 +137,10 @@ function SqlFilterFragment( $filter, $components, $property = null, $parameter =
|
||||
}
|
||||
break;
|
||||
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:TEXT-MATCH':
|
||||
case 'urn:ietf:params:xml:ns:caldav:text-match':
|
||||
$search = $v->GetContent();
|
||||
$negate = $v->GetAttribute("NEGATE-CONDITION");
|
||||
$collation = $v->GetAttribute("COLLATION");
|
||||
$negate = $v->GetAttribute("negate-condition");
|
||||
$collation = $v->GetAttribute("collation");
|
||||
switch( strtolower($collation) ) {
|
||||
case 'i;octet':
|
||||
$comparison = 'LIKE';
|
||||
@ -150,12 +150,14 @@ function SqlFilterFragment( $filter, $components, $property = null, $parameter =
|
||||
$comparison = 'ILIKE';
|
||||
break;
|
||||
}
|
||||
$sql .= sprintf( "AND %s%s %s %s ", (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
|
||||
dbg_error_log("calquery", " text-match: (%s IS NULL OR %s%s %s %s) ", $property, (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
|
||||
$property, $comparison, qpg("%".$search."%") );
|
||||
$sql .= sprintf( "AND (%s IS NULL OR %s%s %s %s) ", $property, (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
|
||||
$property, $comparison, qpg("%".$search."%") );
|
||||
break;
|
||||
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:COMP-FILTER':
|
||||
$comp_filter_name = $v->GetAttribute("NAME");
|
||||
case 'urn:ietf:params:xml:ns:caldav:comp-filter':
|
||||
$comp_filter_name = $v->GetAttribute("name");
|
||||
if ( count($components) == 0 ) {
|
||||
$sql .= "AND caldav_data.caldav_type = ".qpg($comp_filter_name)." ";
|
||||
}
|
||||
@ -167,8 +169,8 @@ function SqlFilterFragment( $filter, $components, $property = null, $parameter =
|
||||
}
|
||||
break;
|
||||
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:PROP-FILTER':
|
||||
$propertyname = $v->GetAttribute("NAME");
|
||||
case 'urn:ietf:params:xml:ns:caldav:prop-filter':
|
||||
$propertyname = $v->GetAttribute("name");
|
||||
switch( $propertyname ) {
|
||||
case 'PERCENT-COMPLETE':
|
||||
$property = 'percent_complete';
|
||||
@ -195,18 +197,18 @@ function SqlFilterFragment( $filter, $components, $property = null, $parameter =
|
||||
case 'COMPLETED': /** TODO: this should be moved into the properties supported in SQL. */
|
||||
default:
|
||||
$need_post_filter = true;
|
||||
dbg_error_log("calquery", "Could not handle PROP-FILTER on %s in SQL", $propertyname );
|
||||
return false; // Can't handle PROP-FILTER conditions in the SQL for this property
|
||||
dbg_error_log("calquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
|
||||
continue;
|
||||
}
|
||||
$subfilter = $v->GetContent();
|
||||
$success = SqlFilterFragment( $subfilter, $components, $property, $parameter );
|
||||
if ( $success === false ) continue; else $sql .= $success;
|
||||
break;
|
||||
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:PARAM-FILTER':
|
||||
case 'urn:ietf:params:xml:ns:caldav:param-filter':
|
||||
$need_post_filter = true;
|
||||
return false; // Can't handle PARAM-FILTER conditions in the SQL
|
||||
$parameter = $v->GetAttribute("NAME");
|
||||
$parameter = $v->GetAttribute("name");
|
||||
$subfilter = $v->GetContent();
|
||||
$success = SqlFilterFragment( $subfilter, $components, $property, $parameter );
|
||||
if ( $success === false ) continue; else $sql .= $success;
|
||||
@ -270,6 +272,6 @@ if ( $qry->Exec("calquery",__LINE__,__FILE__) && $qry->rows > 0 ) {
|
||||
}
|
||||
}
|
||||
}
|
||||
$multistatus = new XMLElement( "multistatus", $responses, array('xmlns'=>'DAV:') );
|
||||
$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
|
||||
|
||||
$request->XMLResponse( 207, $multistatus );
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
include_once("iCalendar.php");
|
||||
include_once("RRule.php");
|
||||
|
||||
$fbq_content = $xmltree->GetContent('URN:IETF:PARAMS:XML:NS:CALDAV:FREE-BUSY-QUERY');
|
||||
$fbq_start = $fbq_content[0]->GetAttribute('START');
|
||||
$fbq_end = $fbq_content[0]->GetAttribute('END');
|
||||
$fbq_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:free-busy-query');
|
||||
$fbq_start = $fbq_content[0]->GetAttribute('start');
|
||||
$fbq_end = $fbq_content[0]->GetAttribute('end');
|
||||
|
||||
if ( ! ( isset($fbq_start) || isset($fbq_end) ) ) {
|
||||
$request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
|
||||
|
||||
@ -9,20 +9,20 @@ $responses = array();
|
||||
/**
|
||||
* Build the array of properties to include in the report output
|
||||
*/
|
||||
$mg_content = $xmltree->GetContent('URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-MULTIGET');
|
||||
$mg_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-multiget');
|
||||
$proptype = $mg_content[0]->GetTag();
|
||||
$properties = array();
|
||||
switch( $proptype ) {
|
||||
case 'DAV::PROP':
|
||||
$mg_props = $xmltree->GetPath('/URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-MULTIGET/DAV::PROP/*');
|
||||
case 'DAV::prop':
|
||||
$mg_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-multiget/DAV::prop/*');
|
||||
foreach( $mg_props AS $k => $v ) {
|
||||
$propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
|
||||
$properties[$propertyname] = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::ALLPROP':
|
||||
$properties['ALLPROP'] = 1;
|
||||
case 'DAV::allprop':
|
||||
$properties['allprop'] = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -33,7 +33,7 @@ switch( $proptype ) {
|
||||
/**
|
||||
* Build the href list for the IN ( href, href, href, ... ) clause.
|
||||
*/
|
||||
$mg_hrefs = $xmltree->GetPath('/URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-MULTIGET/DAV::HREF');
|
||||
$mg_hrefs = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-multiget/DAV::href');
|
||||
$href_in = '';
|
||||
foreach( $mg_hrefs AS $k => $v ) {
|
||||
/**
|
||||
@ -65,6 +65,6 @@ if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows > 0 ) {
|
||||
}
|
||||
}
|
||||
|
||||
$multistatus = new XMLElement( "multistatus", $responses, array('xmlns'=>'DAV:') );
|
||||
$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
|
||||
|
||||
$request->XMLResponse( 207, $multistatus );
|
||||
|
||||
@ -2,108 +2,24 @@
|
||||
|
||||
$responses = array();
|
||||
|
||||
/**
|
||||
* Return XML for a single Principal (user) from the DB
|
||||
* TODO: Refactor this functionality into the CalDAVPrincipal object
|
||||
*
|
||||
* @param array $properties The requested properties for this principal
|
||||
* @param string $item The user data for this calendar
|
||||
*
|
||||
* @return string An XML document which is the response for the principal
|
||||
*/
|
||||
function principal_to_xml( $properties, $item ) {
|
||||
global $session, $c, $request;
|
||||
|
||||
dbg_error_log("REPORT","Building XML Response for principal '%s'", $item->username );
|
||||
|
||||
$this_url = ConstructURL( $request->dav_name );
|
||||
$principal_url = ConstructURL( "/".$item->username."/");
|
||||
$home_calendar = ConstructURL( "/".$item->username."/");
|
||||
$prop = new XMLElement("prop");
|
||||
$denied = array();
|
||||
foreach( $properties AS $k => $v ) {
|
||||
switch( $v ) {
|
||||
case 'DAV::RESOURCETYPE':
|
||||
$prop->NewElement("resourcetype", new XMLElement("principal") );
|
||||
break;
|
||||
case 'DAV::DISPLAYNAME':
|
||||
$prop->NewElement("displayname", $item->username );
|
||||
break;
|
||||
case 'DAV::PRINCIPAL-URL':
|
||||
$prop->NewElement("principal-url", $principal_url );
|
||||
break;
|
||||
case 'DAV::ALTERNATE-URI':
|
||||
$prop->NewElement("alternate-uri" );
|
||||
break;
|
||||
case 'DAV::GROUP-MEMBER-SET':
|
||||
$qry = new PgQuery("SELECT * FROM relationship LEFT JOIN usr ON (from_user = usr.user_no) LEFT JOIN role_member ON (to_user = role_member.user_no) LEFT JOIN roles USING (role_no) WHERE to_user = ? AND role_name = 'Group';", $item->user_no );
|
||||
$group = array();
|
||||
if ( $qry->Exec("REPORT-principal") && $qry->rows > 0 ) {
|
||||
while( $membership = $qry->Fetch() ) {
|
||||
$group[] = new XMLElement("href", ConstructURL( "/". $membership->username . "/") );
|
||||
}
|
||||
}
|
||||
$prop->NewElement("group-member-set", $group );
|
||||
break;
|
||||
case 'DAV::GROUP-MEMBERSHIP':
|
||||
$qry = new PgQuery("SELECT * FROM relationship LEFT JOIN usr ON (to_user = user_no) LEFT JOIN role_member USING (user_no) LEFT JOIN roles USING (role_no) WHERE from_user = ? AND role_name = 'Group';", $item->user_no );
|
||||
$group = array();
|
||||
if ( $qry->Exec("REPORT-principal") && $qry->rows > 0 ) {
|
||||
while( $membership = $qry->Fetch() ) {
|
||||
$group[] = new XMLElement("href", ConstructURL( "/". $membership->username . "/") );
|
||||
}
|
||||
}
|
||||
$prop->NewElement("group-membership", $group );
|
||||
break;
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-HOME-SET':
|
||||
$prop->NewElement("calendar-home-set", $home_calendar, array("xmlns" => "urn:ietf:params:xml:ns:caldav") );
|
||||
break;
|
||||
case 'SOME-DENIED-PROPERTY': /** TODO: indicating the style for future expansion */
|
||||
$denied[] = $v;
|
||||
break;
|
||||
default:
|
||||
dbg_error_log( 'REPORT', "Request for unsupported property '%s' of principal.", $item->username );
|
||||
break;
|
||||
}
|
||||
}
|
||||
$status = new XMLElement("status", "HTTP/1.1 200 OK" );
|
||||
|
||||
$propstat = new XMLElement( "propstat", array( $prop, $status) );
|
||||
$href = new XMLElement("href", $principal_url );
|
||||
|
||||
$elements = array($href,$propstat);
|
||||
|
||||
if ( count($denied) > 0 ) {
|
||||
$status = new XMLElement("status", "HTTP/1.1 403 Forbidden" );
|
||||
$noprop = new XMLElement("prop");
|
||||
foreach( $denied AS $k => $v ) {
|
||||
$noprop->NewElement( strtolower($v) );
|
||||
}
|
||||
$elements[] = new XMLElement( "propstat", array( $noprop, $status) );
|
||||
}
|
||||
|
||||
$response = new XMLElement( "response", $elements );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the array of properties to include in the report output
|
||||
*/
|
||||
$searches = $xmltree->GetPath('/DAV::PRINCIPAL-PROPERTY-SEARCH/DAV::PROPERTY-SEARCH');
|
||||
$searches = $xmltree->GetPath('/DAV::principal-property-search/DAV::property-search');
|
||||
dbg_log_array( "principal", "SEARCH", $searches, true );
|
||||
|
||||
$where = "";
|
||||
foreach( $searches AS $k => $search ) {
|
||||
$qry_props = $search->GetPath('/DAV::PROPERTY-SEARCH/DAV::PROP/*'); // There may be many
|
||||
$match = $search->GetPath('/DAV::PROPERTY-SEARCH/DAV::MATCH'); // There may only be one
|
||||
$qry_props = $search->GetPath('/DAV::property-search/DAV::prop/*'); // There may be many
|
||||
$match = $search->GetPath('/DAV::property-search/DAV::match'); // There may only be one
|
||||
dbg_log_array( "principal", "MATCH", $match, true );
|
||||
$match = qpg($match[0]->GetContent());
|
||||
$subwhere = "";
|
||||
foreach( $qry_props AS $k1 => $v1 ) {
|
||||
if ( $subwhere != "" ) $subwhere .= " OR ";
|
||||
switch( $v1->GetTag() ) {
|
||||
case 'DAV::DISPLAYNAME':
|
||||
case 'DAV::displayname':
|
||||
$subwhere .= "username = ".$match;
|
||||
break;
|
||||
default:
|
||||
@ -119,18 +35,19 @@ $sql = "SELECT * FROM usr $where";
|
||||
$qry = new PgQuery($sql);
|
||||
|
||||
|
||||
$get_props = $xmltree->GetPath('/DAV::PRINCIPAL-PROPERTY-SEARCH/DAV::PROP/*');
|
||||
$get_props = $xmltree->GetPath('/DAV::principal-property-search/DAV::prop/*');
|
||||
$properties = array();
|
||||
foreach( $get_props AS $k1 => $v1 ) {
|
||||
$properties[] = $v1->GetTag();
|
||||
}
|
||||
|
||||
if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows > 0 ) {
|
||||
while( $principal_object = $qry->Fetch() ) {
|
||||
$responses[] = principal_to_xml( $properties, $principal_object );
|
||||
while( $row = $qry->Fetch() ) {
|
||||
$principal = new CalDAVPrincipal($row);
|
||||
$responses[] = $principal->RenderAsXML( $properties, &$reply );
|
||||
}
|
||||
}
|
||||
|
||||
$multistatus = new XMLElement( "multistatus", $responses, array('xmlns'=>'DAV:') );
|
||||
$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
|
||||
|
||||
$request->XMLResponse( 207, $multistatus );
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
*/
|
||||
dbg_error_log("REPORT", "method handler");
|
||||
|
||||
require_once("XMLDocument.php");
|
||||
|
||||
if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || $c->dbg['report']) ) {
|
||||
$fh = fopen('/tmp/REPORT.txt','w');
|
||||
if ( $fh ) {
|
||||
@ -40,11 +42,13 @@ $denied = array();
|
||||
$unsupported = array();
|
||||
if ( isset($prop_filter) ) unset($prop_filter);
|
||||
|
||||
if ( $xmltree->GetTag() == "URN:IETF:PARAMS:XML:NS:CALDAV:FREE-BUSY-QUERY" ) {
|
||||
if ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:free-busy-query" ) {
|
||||
include("caldav-REPORT-freebusy.php");
|
||||
exit; // Not that the above include should return anyway
|
||||
}
|
||||
if ( $xmltree->GetTag() == "DAV::PRINCIPAL-PROPERTY-SEARCH" ) {
|
||||
|
||||
$reply = new XMLDocument( array( "DAV:" => "" ) );
|
||||
if ( $xmltree->GetTag() == "DAV::principal-property-search" ) {
|
||||
include("caldav-REPORT-principal.php");
|
||||
exit; // Not that the above include should return anyway
|
||||
}
|
||||
@ -66,14 +70,14 @@ if ( ! ($request->AllowedTo('read') ) ) {
|
||||
* @return string An XML document which is the response for the calendar
|
||||
*/
|
||||
function calendar_to_xml( $properties, $item ) {
|
||||
global $session, $c, $request;
|
||||
global $session, $c, $request, $reply;
|
||||
|
||||
dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
|
||||
|
||||
$denied = array();
|
||||
$caldav_data = $item->caldav_data;
|
||||
$displayname = $item->summary;
|
||||
if ( isset($properties['CALENDAR-DATA']) || isset($properties['DISPLAYNAME']) ) {
|
||||
if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
|
||||
if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
|
||||
// the user is not admin / owner of this calendarlooking at his calendar and can not admin the other cal
|
||||
if ( $item->class == 'CONFIDENTIAL' ) {
|
||||
@ -108,27 +112,27 @@ function calendar_to_xml( $properties, $item ) {
|
||||
$prop = new XMLElement("prop");
|
||||
foreach( $properties AS $k => $v ) {
|
||||
switch( $k ) {
|
||||
case 'GETCONTENTLENGTH':
|
||||
case 'getcontentlength':
|
||||
$contentlength = strlen($caldav_data);
|
||||
$prop->NewElement("getcontentlength", $contentlength );
|
||||
$prop->NewElement($k, $contentlength );
|
||||
break;
|
||||
case 'CALENDAR-DATA':
|
||||
$prop->NewElement("calendar-data","$caldav_data" , array("xmlns" => "urn:ietf:params:xml:ns:caldav") );
|
||||
case 'calendar-data':
|
||||
$prop->NewElement($reply->Caldav($k), $caldav_data );
|
||||
break;
|
||||
case 'GETCONTENTTYPE':
|
||||
$prop->NewElement("getcontenttype", "text/calendar" );
|
||||
case 'getcontenttype':
|
||||
$prop->NewElement($k, "text/calendar" );
|
||||
break;
|
||||
case 'RESOURCETYPE':
|
||||
$prop->NewElement("resourcetype", new XMLElement("calendar", false, array("xmlns" => "urn:ietf:params:xml:ns:caldav")) );
|
||||
case 'resourcetype':
|
||||
$prop->NewElement($k, new XMLElement($reply->Caldav("calendar"), false) );
|
||||
break;
|
||||
case 'DISPLAYNAME':
|
||||
$prop->NewElement("displayname", $displayname );
|
||||
case 'displayname':
|
||||
$prop->NewElement($k, $displayname );
|
||||
break;
|
||||
case 'GETETAG':
|
||||
$prop->NewElement("getetag", '"'.$item->dav_etag.'"' );
|
||||
case 'getetag':
|
||||
$prop->NewElement($k, '"'.$item->dav_etag.'"' );
|
||||
break;
|
||||
case 'CURRENT-USER-PRIVILEGE-SET':
|
||||
$prop->NewElement("current-user-privilege-set", privileges($request->permissions) );
|
||||
case '"current-user-privilege-set"':
|
||||
$prop->NewElement($k, privileges($request->permissions) );
|
||||
break;
|
||||
case 'SOME-DENIED-PROPERTY': /** TODO: indicating the style for future expansion */
|
||||
$denied[] = $v;
|
||||
@ -158,16 +162,15 @@ function calendar_to_xml( $properties, $item ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ( $xmltree->GetTag() == "URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY" ) {
|
||||
$calquery = $xmltree->GetPath("/URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY/*");
|
||||
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");
|
||||
}
|
||||
elseif ( $xmltree->GetTag() == "URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-MULTIGET" ) {
|
||||
$multiget = $xmltree->GetPath("/URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-MULTIGET/*");
|
||||
elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-multiget" ) {
|
||||
$multiget = $xmltree->GetPath("/urn:ietf:params:xml:ns:caldav:calendar-multiget/*");
|
||||
include("caldav-REPORT-multiget.php");
|
||||
}
|
||||
else {
|
||||
$request->DoResponse( 501, "XML is not a supported REPORT query document" );
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user