DAViCal
caldav-REPORT.php
1 <?php
11 dbg_error_log("REPORT", "method handler");
12 
13 require_once("XMLDocument.php");
14 require_once('DAVResource.php');
15 
16 require_once('RRule.php');
17 
18 if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['report']) && $c->dbg['report'])) ) {
19  $fh = fopen('/var/log/davical/REPORT.debug','w');
20  if ( $fh ) {
21  fwrite($fh,$request->raw_post);
22  fclose($fh);
23  }
24 }
25 
26 if ( !isset($request->xml_tags) ) {
27  $request->DoResponse( 406, translate("REPORT body contains no XML data!") );
28 }
29 $xmltree = BuildXMLTree( $request->xml_tags );
30 if ( !is_object($xmltree) ) {
31  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
32 }
33 
34 $target = new DAVResource($request->path);
35 
36 if ( $xmltree->GetNSTag() != 'DAV::principal-property-search'
37  && $xmltree->GetNSTag() != 'DAV::principal-property-search-set' ) {
38  $target->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy'), true ); // They may have either
39 }
40 
41 require_once("vCalendar.php");
42 
43 $reportnum = -1;
44 $report = array();
45 $denied = array();
46 $unsupported = array();
47 if ( isset($prop_filter) ) unset($prop_filter);
48 
49 if ( $xmltree->GetNSTag() == 'urn:ietf:params:xml:ns:caldav:free-busy-query' ) {
50  include("caldav-REPORT-freebusy.php");
51  exit; // Not that the above include should return anyway
52 }
53 
54 $reply = new XMLDocument( array( "DAV:" => "" ) );
55 switch( $xmltree->GetNSTag() ) {
56  case 'DAV::principal-property-search':
57  include("caldav-REPORT-principal.php");
58  exit; // Not that it should return anyway.
59  case 'DAV::principal-search-property-set':
60  include("caldav-REPORT-pps-set.php");
61  exit; // Not that it should return anyway.
62  case 'DAV::sync-collection':
63  if ( $target->IsExternal() ) {
64  require_once("external-fetch.php");
65  update_external ( $target );
66  }
67  include("caldav-REPORT-sync-collection.php");
68  exit; // Not that it should return anyway.
69  case 'DAV::expand-property':
70  if ( $target->IsExternal() ) {
71  require_once("external-fetch.php");
72  update_external ( $target );
73  }
74  include("caldav-REPORT-expand-property.php");
75  exit; // Not that it should return anyway.
76  case 'DAV::principal-match':
77  include("caldav-REPORT-principal-match.php");
78  exit; // Not that it should return anyway.
79 }
80 
85 function check_for_expansion( $calendar_data_node ) {
86  global $need_expansion, $expand_range_start, $expand_range_end, $expand_as_floating;
87  $expand_as_floating = false;
88 
89  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
90  if ( isset($expansion[0]) ) {
91  $need_expansion = true;
92  $expand_range_start = $expansion[0]->GetAttribute('start');
93  $expand_range_end = $expansion[0]->GetAttribute('end');
94  $expand_as_floating = $expansion[0]->GetAttribute('floating');
95  if ( isset($expand_range_start) ) $expand_range_start = new RepeatRuleDateTime($expand_range_start);
96  if ( isset($expand_range_end) ) $expand_range_end = new RepeatRuleDateTime($expand_range_end);
97  if ( isset($expand_as_floating) && $expand_as_floating == "yes" )
98  $expand_as_floating = true;
99  else
100  $expand_as_floating = false;
101  }
102 }
103 
104 
113 function component_to_xml( $properties, $item ) {
114  global $session, $c, $request, $reply;
115 
116  dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
117 
118  $denied = array();
119  $unsupported = array();
120  $caldav_data = $item->caldav_data;
121  $displayname = preg_replace( '{^.*/}', '', $item->dav_name );
122  $type = 'unknown';
123  $contenttype = 'text/plain';
124  switch( strtoupper($item->caldav_type) ) {
125  case 'VJOURNAL':
126  case 'VEVENT':
127  case 'VTODO':
128  $displayname = $item->summary;
129  $type = 'calendar';
130  $contenttype = 'text/calendar';
131  if ( isset($properties['urn:ietf:params:xml:ns:caldav:calendar-data']) || isset($properties['DAV::displayname']) ) {
132  if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ) {
133  // the user is not admin / owner of this calendar looking at his calendar and can not admin the other cal
134  if ( $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
135  dbg_error_log("REPORT","Anonymising confidential event for: %s", $item->dav_name );
136  $vcal = new vCalendar( $caldav_data );
137  $caldav_data = $vcal->Confidential()->Render();
138  $displayname = translate('Busy');
139  }
140  }
141  }
142 
143  if ( isset($c->hide_alarm) && $c->hide_alarm ) {
144  $dav_resource = new DAVResource($item->dav_name);
145  if ( isset($properties['urn:ietf:params:xml:ns:caldav:calendar-data']) && !$dav_resource->HavePrivilegeTo('write') ) {
146  dbg_error_log("REPORT","Stripping event alarms for: %s", $item->dav_name );
147  $vcal = new vCalendar($caldav_data);
148  $vcal->ClearComponents('VALARM');
149  $caldav_data = $vcal->Render();
150  }
151  }
152  break;
153 
154  case 'VCARD':
155  $displayname = $item->fn;
156  $type = 'vcard';
157  $contenttype = 'text/vcard';
158  break;
159  }
160 
161  $url = ConstructURL($item->dav_name);
162 
163  $prop = new XMLElement("prop");
164  $need_resource = false;
165  foreach( $properties AS $full_tag => $v ) {
166  $base_tag = preg_replace('{^.*:}', '', $full_tag );
167  switch( $full_tag ) {
168  case 'DAV::getcontentlength':
169  $contentlength = strlen($caldav_data);
170  $prop->NewElement($base_tag, $contentlength );
171  break;
172  case 'DAV::getlastmodified':
173  $prop->NewElement($base_tag, ISODateToHTTPDate($item->modified) );
174  break;
175  case 'urn:ietf:params:xml:ns:caldav:calendar-data':
176  if ( $type == 'calendar' ) $reply->CalDAVElement($prop, $base_tag, $caldav_data );
177  else $unsupported[] = $base_tag;
178  break;
179  case 'urn:ietf:params:xml:ns:carddav:address-data':
180  if ( $type == 'vcard' ) $reply->CardDAVElement($prop, $base_tag, $caldav_data );
181  else $unsupported[] = $base_tag;
182  break;
183  case 'DAV::getcontenttype':
184  $prop->NewElement($base_tag, $contenttype );
185  break;
186  case 'DAV::current-user-principal':
187  $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
188  break;
189  case 'DAV::displayname':
190  $prop->NewElement($base_tag, $displayname );
191  break;
192  case 'DAV::resourcetype':
193  $prop->NewElement($base_tag); // Just an empty resourcetype for a non-collection.
194  break;
195  case 'DAV::getetag':
196  $prop->NewElement($base_tag, '"'.$item->dav_etag.'"' );
197  break;
198  case '"current-user-privilege-set"':
199  $prop->NewElement($base_tag, privileges($request->permissions) );
200  break;
201  default:
202  // It's harder. We need the DAVResource() to get this one.
203  $need_resource = true;
204  }
205  if ( $need_resource ) break;
206  }
207  $href = new XMLElement("href", $url );
208  if ( $need_resource ) {
209  if ( !isset($dav_resource) ) $dav_resource = new DAVResource($item->dav_name);
210  $elements = $dav_resource->GetPropStat(array_keys($properties), $reply);
211  array_unshift($elements, $href);
212  }
213  else {
214  $elements = array($href);
215  $status = new XMLElement("status", "HTTP/1.1 200 OK" );
216  $elements[] = new XMLElement( "propstat", array( $prop, $status) );
217  if ( count($denied) > 0 ) {
218  $status = new XMLElement("status", "HTTP/1.1 403 Forbidden" );
219  $noprop = new XMLElement("prop");
220  foreach( $denied AS $k => $v ) {
221  $reply->NSElement($noprop, $v);
222  }
223  $elements[] = new XMLElement( "propstat", array( $noprop, $status) );
224  }
225 
226  if ( ! $request->PreferMinimal() && count($unsupported) > 0 ) {
227  $status = new XMLElement("status", "HTTP/1.1 404 Not Found" );
228  $noprop = new XMLElement("prop");
229  foreach( $unsupported AS $k => $v ) {
230  $reply->NSElement($noprop, $v);
231  }
232  $elements[] = new XMLElement( "propstat", array( $noprop, $status) );
233  }
234  }
235 
236  $response = new XMLElement( "response", $elements );
237 
238  return $response;
239 }
240 
241 if ( $target->IsExternal() ) {
242  require_once("external-fetch.php");
243  update_external ( $target );
244 }
245 
246 // These reports are always allowed to see the resource_data because they are special
247 $c->sync_resource_data_ok = true;
248 
249 if ( $xmltree->GetNSTag() == "urn:ietf:params:xml:ns:caldav:calendar-query" ) {
250  $calquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:caldav:calendar-query/*");
251  include("caldav-REPORT-calquery.php");
252 }
253 elseif ( $xmltree->GetNSTag() == "urn:ietf:params:xml:ns:caldav:calendar-multiget" ) {
254  $mode = 'caldav';
255  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-multiget');
256  include("caldav-REPORT-multiget.php");
257 }
258 elseif ( $xmltree->GetNSTag() == "urn:ietf:params:xml:ns:carddav:addressbook-multiget" ) {
259  $mode = 'carddav';
260  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-multiget');
261  include("caldav-REPORT-multiget.php");
262 }
263 elseif ( $xmltree->GetNSTag() == "urn:ietf:params:xml:ns:carddav:addressbook-query" ) {
264  $cardquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:carddav:addressbook-query/*");
265  include("caldav-REPORT-cardquery.php");
266 }
267 else {
268  dbg_error_log( 'ERROR', "Request for unsupported report type '%s'.", $xmltree->GetNSTag() );
269  $request->PreconditionFailed( 403, 'DAV::supported-report', sprintf( '"%s" is not a supported report type', $xmltree->GetNSTag()) );
270 }
271