From 033ffd5cc0bb1e3ffbf22728e2186a2976774ce6 Mon Sep 17 00:00:00 2001 From: Andrew McMillan Date: Wed, 6 Jun 2007 19:18:29 +1200 Subject: [PATCH] Refactoring and rewriting the support for calendar-multiget reports with a view to complete compliance with the CalDAV specification. --- inc/caldav-REPORT-multiget.php | 65 +++++++ inc/caldav-REPORT.php | 329 +++++++++++++++------------------ 2 files changed, 212 insertions(+), 182 deletions(-) create mode 100644 inc/caldav-REPORT-multiget.php diff --git a/inc/caldav-REPORT-multiget.php b/inc/caldav-REPORT-multiget.php new file mode 100644 index 00000000..edd4fffa --- /dev/null +++ b/inc/caldav-REPORT-multiget.php @@ -0,0 +1,65 @@ +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/*'); + foreach( $mg_props AS $k => $v ) { + $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() ); + $properties[$propertyname] = 1; + } + break; + + case 'DAV::ALLPROP': + $properties['ALLPROP'] = 1; + break; + + default: + $propertyname = preg_replace( '/^.*:/', '', $proptype ); + $properties[$propertyname] = 1; +} + +/** + * 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'); +$href_in = ''; +foreach( $mg_hrefs AS $k => $v ) { + /** + * We need this to work if they specified a relative *or* a full path, so we strip off + * anything up to the matching request->path (which will include any http...) and then + * put the $request->path back on. + */ + $href = $request->path . preg_replace( "#^.*$request->path#", '', $v->GetContent() ); + dbg_error_log("REPORT", "Reporting on href '%s'", $href ); + $href_in .= ($href_in == '' ? '' : ', '); + $href_in .= qpg($href); +} + +$where = " WHERE caldav_data.dav_name ~ ".qpg("^".$request->path)." "; +if ( $href_in != "" ) { + $where .= " AND caldav_data.dav_name IN ( $href_in ) "; +} + +$where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL OR get_permissions($session->user_no,caldav_data.user_no) ~ 'A') "; // Must have 'all' permissions to see confidential items +if ( isset($c->hide_TODO) && $c->hide_TODO ) { + $where .= "AND (caldav_data.caldav_type NOT IN ('VTODO') OR get_permissions($session->user_no,caldav_data.user_no) ~ 'A') "; +} +$qry = new PgQuery( "SELECT * , get_permissions($session->user_no,caldav_data.user_no) as permissions FROM caldav_data INNER JOIN calendar_item USING(user_no, dav_name)". $where ); +if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows > 0 ) { + while( $calendar_object = $qry->Fetch() ) { + $responses[] = calendar_to_xml( $properties, $calendar_object ); + } +} + +$multistatus = new XMLElement( "multistatus", $responses, array('xmlns'=>'DAV:') ); + +$request->XMLResponse( 207, $multistatus ); +?> \ No newline at end of file diff --git a/inc/caldav-REPORT.php b/inc/caldav-REPORT.php index 1022fd22..37f07d46 100644 --- a/inc/caldav-REPORT.php +++ b/inc/caldav-REPORT.php @@ -47,175 +47,6 @@ if ( $xmltree->GetTag() == "URN:IETF:PARAMS:XML:NS:CALDAV:FREE-BUSY-QUERY" ) { // Must have read privilege for all other reports if ( ! ($request->AllowedTo('read') ) ) $request->DoResponse( 404, translate("You may not access that calendar") ); -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-calendar.php"); -} -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" ); -} - -foreach( $request->xml_tags AS $k => $v ) { - - $fulltag = $v['tag']; - if ( preg_match('/^(.*):([^:]+)$/', $fulltag, $matches) ) { - $xmlns = $matches[1]; - $xmltag = $matches[2]; - } - else { - $xmlns = 'DAV:'; - $xmltag = $tag; - } - - switch ( $fulltag ) { - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY': - dbg_error_log( "REPORT", ":Request: %s -> %s", $v['type'], $xmltag ); - if ( $v['type'] == "open" ) { - $reportnum++; - $report[$reportnum]['type'] = $xmltag; - $report[$reportnum]['include_href'] = 1; - $report[$reportnum]['include_data'] = 1; - } - else { - unset($report_type); - } - break; - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-MULTIGET': - dbg_error_log( "REPORT", ":Request: %s -> %s", $v['type'], $xmltag ); - $report[$reportnum]['multiget'] = 1; - if ( $v['type'] == "open" ) { - $reportnum++; - $report[$reportnum]['type'] = $xmltag; - $multiget_names = array(); - } - else if ( $v['type'] == "close" ) { - $report[$reportnum]['get_names'] = $multiget_names; - unset($multiget_names); - } - break; - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:FILTER': - dbg_error_log( "REPORT", ":Request: %s -> %s", $v['type'], $xmltag ); - if ( $v['type'] == "open" ) { - $filters = array(); - } - else if ( $v['type'] == "close" ) { - $report[$reportnum]['filters'] = $filters; - unset($filters); - } - break; - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:IS-DEFINED': - case 'URN:IETF:PARAMS:XML:NS:CALDAV:COMP-FILTER': - dbg_error_log( "REPORT", ":Request: %s -> %s", $v['type'], $xmltag ); - if ( $v['type'] == "close" ) { - break; - } - $filter_name = $v['attributes']['NAME']; - dbg_log_array( "REPORT", "COMP-FILTER", $v, true ); - if ( isset($filters) ) { - dbg_error_log( "REPORT", "Adding filter '%s'", $filter_name ); - $filters[$filter_name] = 1; - } - else { - dbg_error_log( "ERROR", "Not using COMP-FILTER '%s' outside of defined FILTER!", $filter_name ); - } - break; - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:TIME-RANGE': - dbg_log_array( "REPORT", "TIME-RANGE", $v, true ); - if ( isset($v['attributes']['START']) ) { - $report[$reportnum]['start'] = $v['attributes']['START']; - } - if ( isset($v['attributes']['END']) ) { - $report[$reportnum]['end'] = $v['attributes']['END']; - } - break; - - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:FILTER': - dbg_error_log( "REPORT", "Not using %s information which follows...", $v['tag'] ); - dbg_log_array( "REPORT", "FILTER", $v, true ); - break; - - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:PROP-FILTER': - dbg_log_array( "REPORT", "PROP-FILTER", $v, true ); - if ( $v['type'] == "open" ) { - $prop_filter = array( "name" => $v['attributes']['NAME'] ); - } - elseif ( $v['type'] == "close" ) { - $report[$reportnum]['propfilter'] = $prop_filter; - unset($prop_filter); - } - break; - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:TEXT-MATCH': - dbg_log_array( "REPORT", "TEXT-MATCH", $v, true ); - $prop_filter["text-match"] = $v['value']; - break; - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:IS-NOT-DEFINED': - dbg_log_array( "REPORT", "TEXT-MATCH", $v, true ); - $prop_filter["is-not-defined"] = 1; - break; - - - case 'DAV::PROP': - dbg_log_array( "REPORT", "DAV::PROP", $v, true ); - if ( isset($report[$reportnum]['type']) ) { - if ( $v['type'] == "open" ) { - $report_properties = array(); - } - else if ( $v['type'] == "close" ) { - $report[$reportnum]['properties'] = $report_properties; - unset($report_properties); - } - else { - dbg_error_log( "REPORT", "Unexpected DAV::PROP type of ".$v['type'] ); - } - } - else { - dbg_error_log( "REPORT", "Unexpected DAV::PROP type of ".$v['type']." when no active report type."); - } - break; - - case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA': - case 'DAV::GETETAG': - case 'DAV::GETCONTENTLENGTH': - case 'DAV::GETCONTENTTYPE': - case 'DAV::RESOURCETYPE': - if ( isset($report_properties) ) { - dbg_error_log( "REPORT", "Adding property '%s'", $xmltag ); - $report_properties[$xmltag] = 1; - } - else { - dbg_error_log( "ERROR", "Not using property '%s' outside of defined report!", $xmltag ); - } - break; - - case 'DAV::HREF': - if ( $report[$reportnum]['type'] == 'CALENDAR-MULTIGET' ) { - $value = preg_replace( "#^.*".$_SERVER['SCRIPT_NAME']."/#", "/", $v['value'] ); - $multiget_names[] = $value; - } - else { - dbg_error_log( "ERROR", "Not using DAV::HREF '%s' for report type '%s'!", $v['value'], $report[$reportnum]['type'] ); - } - break; - - default: - $unsupported[$xmltag] = $xmlns; - dbg_error_log( "REPORT", "Unhandled tag >>%s<<", $fulltag ); - } -} - /** * Return XML for a single calendar (or todo) entry from the DB @@ -308,6 +139,153 @@ 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/*"); +// include("caldav-REPORT-calendar.php"); +} +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" ); +} + +foreach( $request->xml_tags AS $k => $v ) { + + $fulltag = $v['tag']; + if ( preg_match('/^(.*):([^:]+)$/', $fulltag, $matches) ) { + $xmlns = $matches[1]; + $xmltag = $matches[2]; + } + else { + $xmlns = 'DAV:'; + $xmltag = $tag; + } + + switch ( $fulltag ) { + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY': + dbg_error_log( "REPORT", ":Request: %s -> %s", $v['type'], $xmltag ); + if ( $v['type'] == "open" ) { + $reportnum++; + $report[$reportnum]['type'] = $xmltag; + $report[$reportnum]['include_href'] = 1; + $report[$reportnum]['include_data'] = 1; + } + else { + unset($report_type); + } + break; + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:FILTER': + dbg_error_log( "REPORT", ":Request: %s -> %s", $v['type'], $xmltag ); + if ( $v['type'] == "open" ) { + $filters = array(); + } + else if ( $v['type'] == "close" ) { + $report[$reportnum]['filters'] = $filters; + unset($filters); + } + break; + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:IS-DEFINED': + case 'URN:IETF:PARAMS:XML:NS:CALDAV:COMP-FILTER': + dbg_error_log( "REPORT", ":Request: %s -> %s", $v['type'], $xmltag ); + if ( $v['type'] == "close" ) { + break; + } + $filter_name = $v['attributes']['NAME']; + dbg_log_array( "REPORT", "COMP-FILTER", $v, true ); + if ( isset($filters) ) { + dbg_error_log( "REPORT", "Adding filter '%s'", $filter_name ); + $filters[$filter_name] = 1; + } + else { + dbg_error_log( "ERROR", "Not using COMP-FILTER '%s' outside of defined FILTER!", $filter_name ); + } + break; + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:TIME-RANGE': + dbg_log_array( "REPORT", "TIME-RANGE", $v, true ); + if ( isset($v['attributes']['START']) ) { + $report[$reportnum]['start'] = $v['attributes']['START']; + } + if ( isset($v['attributes']['END']) ) { + $report[$reportnum]['end'] = $v['attributes']['END']; + } + break; + + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:FILTER': + dbg_error_log( "REPORT", "Not using %s information which follows...", $v['tag'] ); + dbg_log_array( "REPORT", "FILTER", $v, true ); + break; + + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:PROP-FILTER': + dbg_log_array( "REPORT", "PROP-FILTER", $v, true ); + if ( $v['type'] == "open" ) { + $prop_filter = array( "name" => $v['attributes']['NAME'] ); + } + elseif ( $v['type'] == "close" ) { + $report[$reportnum]['propfilter'] = $prop_filter; + unset($prop_filter); + } + break; + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:TEXT-MATCH': + dbg_log_array( "REPORT", "TEXT-MATCH", $v, true ); + $prop_filter["text-match"] = $v['value']; + break; + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:IS-NOT-DEFINED': + dbg_log_array( "REPORT", "TEXT-MATCH", $v, true ); + $prop_filter["is-not-defined"] = 1; + break; + + + case 'DAV::PROP': + dbg_log_array( "REPORT", "DAV::PROP", $v, true ); + if ( isset($report[$reportnum]['type']) ) { + if ( $v['type'] == "open" ) { + $report_properties = array(); + } + else if ( $v['type'] == "close" ) { + $report[$reportnum]['properties'] = $report_properties; + unset($report_properties); + } + else { + dbg_error_log( "REPORT", "Unexpected DAV::PROP type of ".$v['type'] ); + } + } + else { + dbg_error_log( "REPORT", "Unexpected DAV::PROP type of ".$v['type']." when no active report type."); + } + break; + + case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA': + case 'DAV::HREF': + case 'DAV::GETETAG': + case 'DAV::GETCONTENTLENGTH': + case 'DAV::GETCONTENTTYPE': + case 'DAV::RESOURCETYPE': + if ( isset($report_properties) ) { + dbg_error_log( "REPORT", "Adding property '%s'", $xmltag ); + $report_properties[$xmltag] = 1; + } + else { + dbg_error_log( "ERROR", "Not using property '%s' outside of defined report!", $xmltag ); + } + break; + + default: + $unsupported[$xmltag] = $xmlns; + dbg_error_log( "REPORT", "Unhandled tag >>%s<<", $fulltag ); + } +} + + $request->UnsupportedRequest($unsupported); // Won't return if there was unsupported stuff. @@ -332,19 +310,6 @@ for ( $i=0; $i <= $reportnum; $i++ ) { } break; - case 'CALENDAR-MULTIGET': - if ( ! ($request->AllowedTo('read') ) ) $request->DoResponse( 403, translate("You may not access that calendar") ); - $href_in = ''; - foreach( $report[$reportnum]['get_names'] AS $k => $v ) { - dbg_error_log("REPORT", "Reporting on href '%s'", $v ); - $href_in .= ($href_in == '' ? '' : ', '); - $href_in .= qpg($v); - } - if ( $href_in != "" ) { - $where .= " AND caldav_data.dav_name IN ( $href_in ) "; - } - break; - default: dbg_error_log("REPORT", "Unhandled report type of '%s'", $report[$i]['type'] ); }