$props = $xmltree->GetPath('/DAV::sync-collection/DAV::prop/*'); if (!empty($props)) { foreach ($props as $k => $v) { $proplist[] = $v->GetNSTag(); } } function display_status($status_code) { return sprintf('HTTP/1.1 %03d %s', intval($status_code), getStatusMessage($status_code)); } $collection = new DAVResource($request->path); if (!$collection->Exists()) { $request->DoResponse(404); } $bound_from = $collection->bound_from(); $collection_path = $collection->dav_name(); $request_via_binding = $bound_from != $collection_path; $params = array(':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token); $sql = "SELECT new_sync_token( :sync_token, :collection_id)"; $qry = new AwlQuery($sql, $params); if (!$qry->Exec("REPORT", __LINE__, __FILE__) || $qry->rows() <= 0) { $request->DoResponse(500, translate("Database error")); } $row = $qry->Fetch(); if (!isset($row->new_sync_token)) { /** If we got a null back then they gave us a sync token we know not of, so provide a full sync */ $sync_token = 0; $params[':sync_token'] = $sync_token; if (!$qry->QDo($sql, $params) || $qry->rows() <= 0) { $request->DoResponse(500, translate("Database error")); }
$components = array(); $filter_fragment = SqlFilterCardDAV($qry_filters, $components); if ($filter_fragment !== false) { $where .= ' ' . $filter_fragment['sql']; $params = $filter_fragment['params']; } } else { dbg_error_log('cardquery', 'No query filters'); } $sql = 'SELECT * FROM caldav_data INNER JOIN addressbook_resource USING(dav_id)' . $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY dav_id"; } $qry = new AwlQuery($sql, $params); if ($qry->Exec("cardquery", __LINE__, __FILE__) && $qry->rows() > 0) { while ($address_object = $qry->Fetch()) { if (!$need_post_filter || apply_filter($qry_filters, $address_object)) { if ($bound_from != $target_collection->dav_name()) { $address_object->dav_name = str_replace($bound_from, $target_collection->dav_name(), $address_object->dav_name); } if (count($address_data_properties) > 0) { $vcard = new VCard($address_object->caldav_data); $vcard->MaskProperties($address_data_properties); $address_object->caldav_data = $vcard->Render(); } $responses[] = component_to_xml($properties, $address_object); } } } $multistatus = new XMLElement("multistatus", $responses, $reply->GetXmlNsArray()); $request->XMLResponse(207, $multistatus);
$dest->NeedPrivilege('DAV::bind'); } function rollback($response_code = 412) { global $request; $qry = new AwlQuery('ROLLBACK'); $qry->Exec('move'); // Just in case $request->DoResponse($response_code); // And we don't return from that. } $qry = new AwlQuery('BEGIN'); if (!$qry->Exec('move')) { rollback(500); } $src_name = $src->dav_name(); $dst_name = $dest->dav_name(); $src_collection = $src->GetProperty('collection_id'); $dst_collection = $dest->GetProperty('collection_id'); $src_user_no = $src->GetProperty('user_no'); $dst_user_no = $dest->GetProperty('user_no'); if ($src->IsCollection()) { if ($dest->Exists()) { $qry = new AwlQuery('DELETE FROM collection WHERE dav_name = :dst_name', array(':dst_name' => $dst_name)); if (!$qry->Exec('move')) { rollback(500); } } /** @TODO: Need to confirm this will work correctly if we move this into another user's hierarchy. */ $sql = 'UPDATE collection SET dav_name = :dst_name '; $params = array(':dst_name' => $dst_name);
$dest->NeedPrivilege('DAV::bind'); } function rollback($response_code = 412) { global $request; $qry = new AwlQuery('ROLLBACK'); $qry->Exec('move'); // Just in case $request->DoResponse($response_code); // And we don't return from that. } $qry = new AwlQuery('BEGIN'); if (!$qry->Exec('move')) { rollback(500); } $src_name = $src->dav_name(); $dst_name = $dest->IsBinding() ? $dest->bound_from() : $dest->dav_name(); $src_collection = $src->GetProperty('collection_id'); $dst_collection = $dest->GetProperty('collection_id'); $src_user_no = $src->GetProperty('user_no'); $dst_user_no = $dest->GetProperty('user_no'); $cache = getCacheInstance(); $cachekeys = array(); if ($src->IsCollection()) { $cachekeys[] = ($src->ContainerType() == 'principal' ? 'principal' : 'collection') . '-' . $src->parent_path(); $cachekeys[] = ($src->IsPrincipal() == 'principal' ? 'principal' : 'collection') . '-' . $src->dav_name(); $cachekeys[] = ($src->IsPrincipal() ? 'principal' : 'collection') . '-' . $dest->dav_name(); if ($dest->Exists()) { $qry = new AwlQuery('DELETE FROM collection WHERE dav_name = :dst_name', array(':dst_name' => $dst_name)); if (!$qry->Exec('move')) { rollback(500);
return $responses; } /** * Something that we can handle, at least roughly correctly. */ $responses = array(); if ($request->IsProxyRequest()) { $response = add_proxy_response($request->proxy_type, $request->principal->dav_name()); if (isset($response)) { $responses[] = $response; } } else { $resource = new DAVResource($request->path); if (!$resource->Exists()) { $request->PreconditionFailed(404, 'must-exist', translate('That resource is not present on this server.')); } $resource->NeedPrivilege('DAV::read'); if ($resource->IsCollection()) { dbg_error_log('PROPFIND', 'Getting collection contents: Depth %d, Path: %s', $request->depth, $resource->dav_name()); $responses[] = $resource->RenderAsXML($property_list, $reply); if ($request->depth > 0) { $responses = array_merge($responses, get_collection_contents($request->depth - 1, $resource)); } } elseif ($request->HavePrivilegeTo('DAV::read', false)) { $responses[] = $resource->RenderAsXML($property_list, $reply); } } $xmldoc = $reply->Render('multistatus', $responses); $etag = md5($xmldoc); header('ETag: "' . $etag . '"'); $request->DoResponse(207, $xmldoc, 'text/xml; charset="utf-8"');
LEFT JOIN calendar_item USING(dav_id, user_no, dav_name, collection_id) LEFT JOIN addressbook_resource USING(dav_id) LEFT JOIN collection USING(collection_id)'; /** * @todo: Add stanzas for missing rows, so we don't just return a blank multistatus but * actually return <response> stanzas with a 404 for each absent href. We could do * this relatively easily with an array_flip($params) and remove each matching dav_name * as we process it. */ if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $where .= " ORDER BY caldav_data.dav_id"; } $qry = new AwlQuery($sql . $where, $params); if ($qry->Exec('REPORT', __LINE__, __FILE__) && $qry->rows() > 0) { while ($dav_object = $qry->Fetch()) { if ($bound_from != $collection->dav_name()) { $dav_object->dav_name = str_replace($bound_from, $collection->dav_name(), $dav_object->dav_name); } //if ( $need_expansion ) { $vResource = new vComponent($dav_object->caldav_data); $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end); // $event = $expanded->GetComponents("VEVENT")[0]; // // $attendeeName = "ATTENDEE"; // // $event->ClearProperties($attendeeName); // // $attendeeQry = new AwlQuery("SELECT params, attendee FROM calendar_attendee WHERE dav_id = :dav_id", array(':dav_id' => $dav_object->dav_id)); // $attendeeQry->Execute(); // //
} if (isset($c->hide_TODO) && $c->hide_TODO && !$target_collection->HavePrivilegeTo('DAV::write-content')) { $where .= " AND caldav_data.caldav_type NOT IN ('VTODO') "; } if (isset($c->hide_older_than) && intval($c->hide_older_than > 0)) { $where .= " AND calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') "; } $sql = 'SELECT caldav_data.*,calendar_item.* FROM collection INNER JOIN caldav_data USING(collection_id) INNER JOIN calendar_item USING(dav_id) ' . $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY caldav_data.dav_id"; } $qry = new AwlQuery($sql, $params); if ($qry->Exec("calquery", __LINE__, __FILE__) && $qry->rows() > 0) { while ($calendar_object = $qry->Fetch()) { if (!$need_post_filter || apply_filter($qry_filters, $calendar_object)) { if ($bound_from != $target_collection->dav_name()) { $calendar_object->dav_name = str_replace($bound_from, $target_collection->dav_name(), $calendar_object->dav_name); } if ($need_expansion) { $vResource = new vComponent($calendar_object->caldav_data); $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end); if ($expanded->ComponentCount() == 0) { continue; } $calendar_object->caldav_data = $expanded->Render(); } $responses[] = calendar_to_xml($properties, $calendar_object); } } } $multistatus = new XMLElement("multistatus", $responses, $reply->GetXmlNsArray());
function export_iCalendar(DAVResource $dav_resource) { global $session, $c, $request; if (!$dav_resource->IsCalendar() && !(isset($c->get_includes_subcollections) && $c->get_includes_subcollections)) { /** RFC2616 says we must send an Allow header if we send a 405 */ header("Allow: PROPFIND,PROPPATCH,OPTIONS,MKCOL,REPORT,DELETE"); $request->DoResponse(405, translate("GET requests on collections are only supported for calendars.")); } /** * The CalDAV specification does not define GET on a collection, but typically this is * used as a .ics download for the whole collection, which is what we do also. */ if (isset($c->get_includes_subcollections) && $c->get_includes_subcollections) { $where = 'caldav_data.collection_id IN '; $where .= '(SELECT bound_source_id FROM dav_binding WHERE dav_binding.dav_name ~ :path_match '; $where .= 'UNION '; $where .= 'SELECT collection_id FROM collection WHERE collection.dav_name ~ :path_match) '; $params = array(':path_match' => '^' . $dav_resource->dav_name()); $distinct = 'DISTINCT ON (calendar_item.uid) '; } else { $where = 'caldav_data.collection_id = :collection_id '; $params = array(':collection_id' => $dav_resource->resource_id()); $distinct = ''; } $sql = 'SELECT ' . $distinct . ' caldav_data, class, caldav_type, calendar_item.user_no, logged_user '; $sql .= 'FROM collection INNER JOIN caldav_data USING(collection_id) '; $sql .= 'INNER JOIN calendar_item USING ( dav_id ) WHERE ' . $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= ' ORDER BY calendar_item.uid, calendar_item.dav_id'; } $qry = new AwlQuery($sql, $params); if (!$qry->Exec("GET", __LINE__, __FILE__)) { $request->DoResponse(500, translate("Database Error")); } /** * Here we are constructing a whole calendar response for this collection, including * the timezones that are referred to by the events we have selected. */ $vcal = new iCalComponent(); $vcal->VCalendar(); $displayname = $dav_resource->GetProperty('displayname'); if (isset($displayname)) { $vcal->AddProperty("X-WR-CALNAME", $displayname); } if (!empty($c->auto_refresh_duration)) { $vcal->AddProperty("X-APPLE-AUTO-REFRESH-INTERVAL", $c->auto_refresh_duration); $vcal->AddProperty("AUTO-REFRESH", $c->auto_refresh_duration); $vcal->AddProperty("X-PUBLISHED-TTL", $c->auto_refresh_duration); } $need_zones = array(); $timezones = array(); while ($event = $qry->Fetch()) { $ical = new iCalComponent($event->caldav_data); /** Save the timezone component(s) into a minimal set for inclusion later */ $event_zones = $ical->GetComponents('VTIMEZONE', true); foreach ($event_zones as $k => $tz) { $tzid = $tz->GetPValue('TZID'); if (!isset($tzid)) { continue; } if ($tzid != '' && !isset($timezones[$tzid])) { $timezones[$tzid] = $tz; } } /** Work out which ones are actually used here */ $comps = $ical->GetComponents('VTIMEZONE', false); foreach ($comps as $k => $comp) { $tzid = $comp->GetPParamValue('DTSTART', 'TZID'); if (isset($tzid) && !isset($need_zones[$tzid])) { $need_zones[$tzid] = 1; } $tzid = $comp->GetPParamValue('DUE', 'TZID'); if (isset($tzid) && !isset($need_zones[$tzid])) { $need_zones[$tzid] = 1; } $tzid = $comp->GetPParamValue('DTEND', 'TZID'); if (isset($tzid) && !isset($need_zones[$tzid])) { $need_zones[$tzid] = 1; } if ($dav_resource->HavePrivilegeTo('all', false) || $session->user_no == $event->user_no || $session->user_no == $event->logged_user || isset($session->email) && $c->allow_get_email_visibility && $comp->IsAttendee($session->email)) { /** * These people get to see all of the event, and they should always * get any alarms as well. */ $vcal->AddComponent($comp); continue; } /** No visibility even of the existence of these events if they aren't admin/owner/attendee */ if ($event->class == 'PRIVATE') { continue; } if (!$dav_resource->HavePrivilegeTo('DAV::read') || $event->class == 'CONFIDENTIAL') { $vcal->AddComponent(obfuscated_event($comp)); } elseif (isset($c->hide_alarm) && $c->hide_alarm) { // Otherwise we hide the alarms (if configured to) $comp->ClearComponents('VALARM'); $vcal->AddComponent($comp); } else { $vcal->AddComponent($comp); } } } /** Put the timezones on there that we need */ foreach ($need_zones as $tzid => $v) { if (isset($timezones[$tzid])) { $vcal->AddComponent($timezones[$tzid]); } } return $vcal->Render(); }
require_once 'external-fetch.php'; $qry = new AwlQuery(); $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array(':dav_name' => '/.external/' . md5($href))); if ($qry->rows() == 1 && ($row = $qry->Fetch())) { $dav_id = $row->collection_id; } else { create_external('/.external/' . md5($href), true, false); $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array(':dav_name' => '/.external/' . md5($href))); if ($qry->rows() != 1 || !($row = $qry->Fetch())) { $request->DoResponse(500, translate('Database Error')); } $dav_id = $row->collection_id; } $sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname, external_url, type ) VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname, :external_url, :external_type )'; $params = array(':target_id' => $dav_id, ':ticket_id' => null, ':parent_container' => $parent->dav_name(), ':session_principal' => $session->principal_id, ':dav_name' => $destination_path, ':displayname' => $segment, ':external_url' => $href, ':external_type' => 'calendar'); $qry = new AwlQuery($sql, $params); if ($qry->Exec('BIND', __LINE__, __FILE__)) { $qry = new AwlQuery('SELECT bind_id from dav_binding where dav_name = :dav_name', array(':dav_name' => $destination_path)); if (!$qry->Exec('BIND', __LINE__, __FILE__) || $qry->rows() != 1 || !($row = $qry->Fetch())) { $request->DoResponse(500, translate('Database Error')); } fetch_external($row->bind_id, ''); $request->DoResponse(201); } else { $request->DoResponse(500, translate('Database Error')); } } else { $source = new DAVResource($href); if (!$source->Exists()) { $request->PreconditionFailed(403, 'DAV::bind-source-exists', translate('The BIND Request MUST identify an existing resource.'));
* stuffs the property somewhere we will be able to retrieve it from later. */ $qry = new AwlQuery(); $qry->Begin(); $setcalendar = count($xmltree->GetPath('/DAV::propertyupdate/DAV::set/DAV::prop/DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar')); foreach ($setprops as $k => $setting) { $tag = $setting->GetTag(); $content = $setting->RenderContent(); switch ($tag) { case 'DAV::displayname': /** * Can't set displayname on resources - only collections or principals */ if ($dav_resource->IsCollection() || $dav_resource->IsPrincipal()) { if ($dav_resource->IsBinding()) { $qry->QDo('UPDATE dav_binding SET dav_displayname = :displayname WHERE dav_name = :dav_name', array(':displayname' => $content, ':dav_name' => $dav_resource->dav_name())); } else { if ($dav_resource->IsPrincipal()) { $qry->QDo('UPDATE dav_principal SET fullname = :displayname, displayname = :displayname, modified = current_timestamp WHERE user_no = :user_no', array(':displayname' => $content, ':user_no' => $request->user_no)); } else { $qry->QDo('UPDATE collection SET dav_displayname = :displayname, modified = current_timestamp WHERE dav_name = :dav_name', array(':displayname' => $content, ':dav_name' => $dav_resource->dav_name())); } } $success[$tag] = 1; } else { $failure['set-' . $tag] = new XMLElement('propstat', array(new XMLElement('prop', new XMLElement($tag)), new XMLElement('status', 'HTTP/1.1 403 Forbidden'), new XMLElement('responsedescription', array(new XMLElement('error', new XMLElement('cannot-modify-protected-property')), translate("The displayname may only be set on collections, principals or bindings."))))); } break; case 'DAV::resourcetype': /** * We only allow resourcetype setting on a normal collection, and not on a resource, a principal or a bind.
@dbg_error_log("DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path); return true; } return false; } if (!$dav_resource->Exists()) { $request->DoResponse(404); } if (!($dav_resource->resource_id() > 0)) { $request->DoResponse(403); } $qry = new AwlQuery(); $qry->Begin(); if ($dav_resource->IsCollection()) { if ($dav_resource->IsBinding()) { $params = array(':dav_name' => $dav_resource->dav_name()); if ($qry->QDo("DELETE FROM dav_binding WHERE dav_name = :dav_name", $params) && $qry->Commit()) { @dbg_error_log("DELETE", "DELETE: Binding: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path); $request->DoResponse(204); } } else { if (delete_collection($dav_resource->resource_id()) && $qry->Commit()) { // Uncache anything to do with the collection $cache = getCacheInstance(); $cache->delete('collection-' . $dav_resource->dav_name(), null); $request->DoResponse(204); } } } else { if (isset($request->etag_if_match) && $request->etag_if_match != $dav_resource->unique_tag()) { $request->DoResponse(412, translate("Resource has changed on server - not deleted"));
function process_ace($grantor, $by_principal, $by_collection, $ace) { global $cache_delete_list, $request; $elements = $ace->GetContent(); $principal_node = $elements[0]; $grant = $elements[1]; if ($principal_node->GetNSTag() != 'DAV::principal') { $request->MalformedRequest('ACL request must contain a principal, not ' . $principal->GetNSTag()); } $grant_tag = $grant->GetNSTag(); if ($grant_tag == 'DAV::deny') { $request->PreconditionFailed(403, 'grant-only'); } if ($grant_tag == 'DAV::invert') { $request->PreconditionFailed(403, 'no-invert'); } if ($grant->GetNSTag() != 'DAV::grant') { $request->MalformedRequest('ACL request must contain a principal for each ACE'); } $privilege_names = array(); $xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*"); foreach ($xml_privs as $k => $priv) { $privilege_names[] = $priv->GetNSTag(); } $privileges = privilege_to_bits($privilege_names); $principal_content = $principal_node->GetContent(); if (count($principal_content) != 1) { $request->MalformedRequest('ACL request must contain exactly one principal per ACE'); } $principal_content = $principal_content[0]; switch ($principal_content->GetNSTag()) { case 'DAV::property': $principal_property = $principal_content->GetContent(); if ($principal_property[0]->GetNSTag() != 'DAV::owner') { $request->PreconditionFailed(403, 'recognized-principal'); } if (privilege_to_bits('all') != $privileges) { $request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions'); } continue; // and then we ignore it, since it's protected break; case 'DAV::unauthenticated': $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users'); break; case 'DAV::href': $principal_type = 'href'; $grantee = new DAVResource(DeconstructURL($principal_content->GetContent())); $grantee_id = $grantee->getProperty('principal_id'); if (!$grantee->Exists() || !$grantee->IsPrincipal()) { $request->PreconditionFailed(403, 'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.'); } $sqlparms = array(':to_principal' => $grantee_id); $where = 'WHERE to_principal=:to_principal AND '; if (isset($by_principal)) { $sqlparms[':by_principal'] = $by_principal; $where .= 'by_principal = :by_principal'; } else { $sqlparms[':by_collection'] = $by_collection; $where .= 'by_collection = :by_collection'; } $qry = new AwlQuery('SELECT privileges FROM grants ' . $where, $sqlparms); if ($qry->Exec('ACL', __LINE__, __FILE__) && $qry->rows() == 1 && ($current = $qry->Fetch())) { $sql = 'UPDATE grants SET privileges=:privileges::INT::BIT(24) ' . $where; } else { $sqlparms[':by_principal'] = $by_principal; $sqlparms[':by_collection'] = $by_collection; $sql = 'INSERT INTO grants (by_principal, by_collection, to_principal, privileges) VALUES(:by_principal, :by_collection, :to_principal, :privileges::INT::BIT(24))'; } $sqlparms[':privileges'] = $privileges; $qry = new AwlQuery($sql, $sqlparms); if ($qry->Exec('ACL', __LINE__, __FILE__)) { Principal::cacheDelete('dav_name', $grantee->dav_name()); Principal::cacheFlush('principal_id IN (SELECT member_id FROM group_member WHERE group_id = ?)', array($grantee_id)); } break; case 'DAV::authenticated': $principal_type = 'authenticated'; if (bindec($grantor->GetProperty('default_privileges')) == $privileges) { continue; } // There is no change, so skip it $sqlparms = array(':privileges' => $privileges); if (isset($by_collection)) { $sql = 'UPDATE collection SET default_privileges=:privileges::INT::BIT(24) WHERE collection_id=:by_collection'; $sqlparms[':by_collection'] = $by_collection; } else { $sql = 'UPDATE principal SET default_privileges=:privileges::INT::BIT(24) WHERE principal_id=:by_principal'; $sqlparms[':by_principal'] = $by_principal; } $qry = new AwlQuery($sql, $sqlparms); if ($qry->Exec('ACL', __LINE__, __FILE__)) { /** * Basically this has changed everyone's permissions now, so... */ Principal::cacheFlush('TRUE'); } break; case 'DAV::all': // $principal_type = 'all'; $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users'); break; default: $request->PreconditionFailed(403, 'recognized-principal'); break; } }
if ($qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE collection_id = :collection_id", $params) && $qry->QDo("DELETE FROM property WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params) && $qry->QDo("DELETE FROM locks WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params) && $qry->QDo("DELETE FROM caldav_data WHERE collection_id = :collection_id", $params) && $qry->QDo("DELETE FROM collection WHERE collection_id = :collection_id", $params)) { @dbg_error_log("DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path); return true; } return false; } if (!$dav_resource->Exists()) { $request->DoResponse(404); } if (!($dav_resource->resource_id() > 0)) { $request->DoResponse(403); } $qry = new AwlQuery(); $qry->Begin(); if ($dav_resource->IsBinding()) { $params = array(':dav_name' => $dav_resource->dav_name()); if ($qry->QDo("DELETE FROM dav_binding WHERE dav_name = :dav_name", $params) && $qry->Commit()) { @dbg_error_log("DELETE", "DELETE: Binding: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path); $request->DoResponse(204); } } else { if ($dav_resource->IsCollection()) { if (delete_collection($dav_resource->resource_id()) && $qry->Commit()) { $request->DoResponse(204); } } else { if (isset($request->etag_if_match) && $request->etag_if_match != $dav_resource->unique_tag()) { $request->DoResponse(412, translate("Resource has changed on server - not deleted")); } $params = array(':dav_id' => $dav_resource->resource_id()); if ($qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE dav_id = :dav_id", $params) && $qry->QDo("DELETE FROM property WHERE dav_name = (SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id)", $params) && $qry->QDo("DELETE FROM locks WHERE dav_name = (SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id)", $params) && $qry->QDo("DELETE FROM caldav_data WHERE dav_id = :dav_id", $params) && $qry->Commit()) {