/** * Return XML for a single component from the DB * * @param array $properties The properties for this component * @param string $item The DB row data for this component * * @return string An XML document which is the response for the component */ function component_to_xml($properties, $item) { global $session, $c, $request, $reply; dbg_error_log("REPORT", "Building XML Response for item '%s'", $item->dav_name); $denied = array(); $unsupported = array(); $caldav_data = $item->caldav_data; $displayname = preg_replace('{^.*/}', '', $item->dav_name); $type = 'unknown'; $contenttype = 'text/plain'; switch ($item->caldav_type) { case 'VJOURNAL': case 'VEVENT': case 'VTODO': $displayname = $item->summary; $type = 'calendar'; $contenttype = 'text/calendar'; break; case 'VCARD': $displayname = $item->fn; $type = 'vcard'; $contenttype = 'text/vcard'; break; } 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 /** @todo We should examine the ORGANIZER and ATTENDEE fields in the event. If this person is there then they should see this */ if ($type == 'calendar' && $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read')) { $ical = new iCalComponent($caldav_data); $resources = $ical->GetComponents('VTIMEZONE', false); $first = $resources[0]; // if the event is confidential we fake one that just says "Busy" $confidential = new iCalComponent(); $confidential->SetType($first->GetType()); $confidential->AddProperty('SUMMARY', translate('Busy')); $confidential->AddProperty('CLASS', 'CONFIDENTIAL'); $confidential->SetProperties($first->GetProperties('DTSTART'), 'DTSTART'); $confidential->SetProperties($first->GetProperties('RRULE'), 'RRULE'); $confidential->SetProperties($first->GetProperties('DURATION'), 'DURATION'); $confidential->SetProperties($first->GetProperties('DTEND'), 'DTEND'); $confidential->SetProperties($first->GetProperties('UID'), 'UID'); $ical->SetComponents(array($confidential), $confidential->GetType()); $caldav_data = $ical->Render(); $displayname = translate('Busy'); } } } $url = ConstructURL($item->dav_name); $prop = new XMLElement("prop"); foreach ($properties as $k => $v) { switch ($k) { case 'getcontentlength': $contentlength = strlen($caldav_data); $prop->NewElement($k, $contentlength); break; case 'getlastmodified': $prop->NewElement($k, ISODateToHTTPDate($item->modified)); break; case 'calendar-data': if ($type == 'calendar') { $reply->CalDAVElement($prop, $k, $caldav_data); } else { $unsupported[] = $k; } break; case 'address-data': if ($type == 'vcard') { $reply->CardDAVElement($prop, $k, $caldav_data); } else { $unsupported[] = $k; } break; case 'getcontenttype': $prop->NewElement($k, $contenttype); break; case 'current-user-principal': $prop->NewElement("current-user-principal", $request->current_user_principal_xml); break; case 'displayname': $prop->NewElement($k, $displayname); break; case 'resourcetype': $prop->NewElement($k); // Just an empty resourcetype for a non-collection. break; case 'getetag': $prop->NewElement($k, '"' . $item->dav_etag . '"'); break; case '"current-user-privilege-set"': $prop->NewElement($k, privileges($request->permissions)); break; case 'SOME-DENIED-PROPERTY': /** indicating the style for future expansion */ $denied[] = $k; break; default: dbg_error_log('REPORT', "Request for unsupported property '%s' of calendar item.", $v); $unsupported[] = $k; } } $status = new XMLElement("status", "HTTP/1.1 200 OK"); $propstat = new XMLElement("propstat", array($prop, $status)); $href = new XMLElement("href", $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($unsupported) > 0) { $status = new XMLElement("status", "HTTP/1.1 404 Not Found"); $noprop = new XMLElement("prop"); foreach ($unsupported as $k => $v) { $noprop->NewElement(strtolower($v)); } $elements[] = new XMLElement("propstat", array($noprop, $status)); } $response = new XMLElement("response", $elements); return $response; }
/** * 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 $request; dbg_error_log('principal', ': RenderAsXML: Principal "%s"', $this->username); $prop = new XMLElement('prop'); $denied = array(); $not_found = array(); foreach ($properties as $k => $tag) { if (!$this->PrincipalProperty($tag, $prop, $reply, $denied)) { dbg_error_log('principal', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username); $not_found[] = $reply->Tag($tag); } } if ($props_only) { return $prop; } $status = new XMLElement('status', 'HTTP/1.1 200 OK'); $propstat = new XMLElement('propstat', array($prop, $status)); $href = $reply->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($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($v); } $elements[] = new XMLElement('propstat', array($noprop, $status)); } $response = new XMLElement('response', $elements); return $response; }
/** * Sometimes it's a perfectly formed request, but we just don't do that :-( * @param array $unsupported An array of the properties we don't support. */ function UnsupportedRequest($unsupported) { if (isset($unsupported) && count($unsupported) > 0) { $badprops = new XMLElement("prop"); foreach ($unsupported as $k => $v) { // Not supported at this point... dbg_error_log("ERROR", " %s: Support for {$v}:{$k} properties is not implemented yet", $this->method); $badprops->NewElement(strtolower($k), false, array("xmlns" => strtolower($v))); } $error = new XMLElement("error", $badprops, array("xmlns" => "DAV:")); $this->XMLResponse(422, $error); } }
} /** * We also include _all_ caldav_data entries in there, since these * are events which failed to parse into timesheets. */ $qry = new PgQuery("SELECT * FROM caldav_data WHERE user_no = ?", $report_user_no); if ($qry->Exec() && $qry->rows > 0) { while ($dav = $qry->Fetch()) { $response = new XMLElement("response"); $prop = new XMLElement("prop"); $url = sprintf("http://%s:%d%s%s", $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $dav->dav_name); if (isset($report[$i]['include_href']) && $report[$i]['include_href'] > 0) { $response->NewElement("href", $url); } if (isset($report[$i]['include_data']) && $report[$i]['include_data'] > 0) { $prop->NewElement("calendar-data", $dav->caldav_data, array("xmlns" => "urn:ietf:params:xml:ns:caldav")); } if (isset($report[$i]['properties']['GETETAG'])) { $prop->NewElement("getetag", '"' . $dav->dav_etag . '"'); } $status = new XMLElement("status", "HTTP/1.1 200 OK"); $response->NewElement("propstat", array($prop, $status)); $responses[] = $response; dbg_error_log("REPORT", "DAV Response: ETag >>%s<< >>%s<<", $dav->dav_etag, $url); } } } $multistatus = new XMLElement("multistatus", $responses, array('xmlns' => 'DAV:')); // dbg_log_array( "REPORT", "XML", $multistatus, true ); $xmldoc = $multistatus->Render(); $etag = md5($xmldoc);
/** * Construct XML propstat fragment for this resource * * @param array of string $properties The requested properties for this resource * * @return string An XML fragment with the requested properties for this resource */ function GetPropStat($properties, &$reply, $props_only = false) { global $request; dbg_error_log('DAVResource', ':GetPropStat: propstat for href "%s"', $this->dav_name); $prop = new XMLElement('prop'); $denied = array(); $not_found = array(); foreach ($properties as $k => $tag) { if (is_object($tag)) { dbg_error_log('DAVResource', ':GetPropStat: "$properties" should be an array of text. Assuming this object is an XMLElement!.'); $tag = $tag->GetTag(); } $found = $this->ResourceProperty($tag, $prop, $reply, $denied); if (!$found) { if (!isset($this->principal)) { $this->FetchPrincipal(); } $found = $this->principal->PrincipalProperty($tag, $prop, $reply, $denied); } if (!$found) { // dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name ); $not_found[] = $reply->Tag($tag); } } if ($props_only) { return $prop; } $status = new XMLElement('status', 'HTTP/1.1 200 OK'); $elements = array(new XMLElement('propstat', array($prop, $status))); if (count($denied) > 0) { $status = new XMLElement('status', 'HTTP/1.1 403 Forbidden'); $noprop = new XMLElement('prop'); foreach ($denied as $k => $v) { $reply->NSElement($noprop, $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($v); } $elements[] = new XMLElement('propstat', array($noprop, $status)); } return $elements; }
/** * Sometimes it's a perfectly formed request, but we just don't do that :-( * @param array $unsupported An array of the properties we don't support. */ function UnsupportedRequest($unsupported) { if (isset($unsupported) && count($unsupported) > 0) { $badprops = new XMLElement("prop"); foreach ($unsupported as $k => $v) { // Not supported at this point... dbg_error_log("ERROR", " %s: Support for {$v}:{$k} properties is not implemented yet", $this->method); $badprops->NewElement(strtolower($k), false, array("xmlns" => strtolower($v))); } $error = new XMLElement("error", new XMLElement("LOCK", $badprops), array("xmlns" => "DAV:")); $this->DoResponse(422, $error->Render(0, '<?xml version="1.0" ?>'), 'text/xml; charset="utf-8"'); } }
/** * Return XML for a single data item from the DB */ function item_to_xml($item) { global $attribute_list, $session, $c, $request; dbg_error_log("PROPFIND", "Building XML Response for item '%s'", $item->dav_name); $item->properties = get_arbitrary_properties($item->dav_name); $url = $_SERVER['SCRIPT_NAME'] . $item->dav_name; $prop = 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'])) { $contentlength = strlen($item->caldav_data); $prop->NewElement("getcontentlength", $contentlength); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTTYPE'])) { $prop->NewElement("getcontenttype", "text/calendar"); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['CREATIONDATE'])) { $prop->NewElement("creationdate", $item->created); } /** * Non-collections should return an empty resource type, it appears from RFC2518 8.1.2 */ if (isset($attribute_list['ALLPROP']) || isset($attribute_list['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 . '"'); } 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))); } 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 = new XMLElement("response", array($href, $propstat)); return $response; }
/** * Return XML for a single component from the DB * * @param array $properties The properties for this component * @param string $item The DB row data for this component * * @return string An XML document which is the response for the component */ function component_to_xml($properties, $item) { global $session, $c, $request, $reply; dbg_error_log("REPORT", "Building XML Response for item '%s'", $item->dav_name); $denied = array(); $unsupported = array(); $caldav_data = $item->caldav_data; $displayname = preg_replace('{^.*/}', '', $item->dav_name); $type = 'unknown'; $contenttype = 'text/plain'; switch (strtoupper($item->caldav_type)) { case 'VJOURNAL': case 'VEVENT': case 'VTODO': $displayname = $item->summary; $type = 'calendar'; $contenttype = 'text/calendar'; if (isset($properties['urn:ietf:params:xml:ns:caldav:calendar-data']) || isset($properties['DAV::displayname'])) { if (!$request->AllowedTo('all') && $session->user_no != $item->user_no) { // the user is not admin / owner of this calendar looking at his calendar and can not admin the other cal if ($item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read')) { dbg_error_log("REPORT", "Anonymising confidential event for: %s", $item->dav_name); $vcal = new vCalendar($caldav_data); $caldav_data = $vcal->Confidential()->Render(); $displayname = translate('Busy'); } } } if (isset($c->hide_alarm) && $c->hide_alarm) { $dav_resource = new DAVResource($item->dav_name); if (isset($properties['urn:ietf:params:xml:ns:caldav:calendar-data']) && !$dav_resource->HavePrivilegeTo('write')) { dbg_error_log("REPORT", "Stripping event alarms for: %s", $item->dav_name); $vcal = new vCalendar($caldav_data); $vcal->ClearComponents('VALARM'); $caldav_data = $vcal->Render(); } } break; case 'VCARD': $displayname = $item->fn; $type = 'vcard'; $contenttype = 'text/vcard'; break; } $url = ConstructURL($item->dav_name); $prop = new XMLElement("prop"); $need_resource = false; foreach ($properties as $full_tag => $v) { $base_tag = preg_replace('{^.*:}', '', $full_tag); switch ($full_tag) { case 'DAV::getcontentlength': $contentlength = strlen($caldav_data); $prop->NewElement($base_tag, $contentlength); break; case 'DAV::getlastmodified': $prop->NewElement($base_tag, ISODateToHTTPDate($item->modified)); break; case 'urn:ietf:params:xml:ns:caldav:calendar-data': if ($type == 'calendar') { $reply->CalDAVElement($prop, $base_tag, $caldav_data); } else { $unsupported[] = $base_tag; } break; case 'urn:ietf:params:xml:ns:carddav:address-data': if ($type == 'vcard') { $reply->CardDAVElement($prop, $base_tag, $caldav_data); } else { $unsupported[] = $base_tag; } break; case 'DAV::getcontenttype': $prop->NewElement($base_tag, $contenttype); break; case 'DAV::current-user-principal': $prop->NewElement("current-user-principal", $request->current_user_principal_xml); break; case 'DAV::displayname': $prop->NewElement($base_tag, $displayname); break; case 'DAV::resourcetype': $prop->NewElement($base_tag); // Just an empty resourcetype for a non-collection. break; case 'DAV::getetag': $prop->NewElement($base_tag, '"' . $item->dav_etag . '"'); break; case '"current-user-privilege-set"': $prop->NewElement($base_tag, privileges($request->permissions)); break; default: // It's harder. We need the DAVResource() to get this one. $need_resource = true; } if ($need_resource) { break; } } $href = new XMLElement("href", $url); if ($need_resource) { if (!isset($dav_resource)) { $dav_resource = new DAVResource($item->dav_name); } $elements = $dav_resource->GetPropStat(array_keys($properties), $reply); array_unshift($elements, $href); } else { $elements = array($href); $status = new XMLElement("status", "HTTP/1.1 200 OK"); $elements[] = new XMLElement("propstat", array($prop, $status)); if (count($denied) > 0) { $status = new XMLElement("status", "HTTP/1.1 403 Forbidden"); $noprop = new XMLElement("prop"); foreach ($denied as $k => $v) { $reply->NSElement($noprop, $v); } $elements[] = new XMLElement("propstat", array($noprop, $status)); } if (!$request->PreferMinimal() && count($unsupported) > 0) { $status = new XMLElement("status", "HTTP/1.1 404 Not Found"); $noprop = new XMLElement("prop"); foreach ($unsupported as $k => $v) { $reply->NSElement($noprop, $v); } $elements[] = new XMLElement("propstat", array($noprop, $status)); } } $response = new XMLElement("response", $elements); return $response; }