/** * Get XML response for items in the collection * If '/' is requested, a list of visible users is given, otherwise * a list of calendars for the user which are parented by this path. */ function get_collection_contents($depth, $collection, $parent_path = null) { global $c, $session, $request, $reply, $property_list; $bound_from = $collection->bound_from(); $bound_to = $collection->dav_name(); if (!isset($parent_path)) { $parent_path = $collection->dav_name(); } dbg_error_log('PROPFIND', 'Getting collection contents: Depth %d, Path: %s, Bound from: %s, Bound to: %s', $depth, $collection->dav_name(), $bound_from, $bound_to); $date_format = AwlDatabase::HttpDateFormat; $responses = array(); if (!$collection->IsCalendar() && !$collection->IsAddressbook()) { /** * Calendar/Addressbook collections may not contain collections, so we are only looking in the other ones */ $params = array(':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth); if ($bound_from == '/') { $sql = "SELECT usr.*, '/' || username || '/' AS dav_name, md5(username || updated::text) AS dav_etag, "; $sql .= "to_char(joined at time zone 'GMT',{$date_format}) AS created, "; $sql .= "to_char(updated at time zone 'GMT',{$date_format}) AS modified, "; $sql .= 'FALSE AS is_calendar, TRUE AS is_principal, FALSE AS is_addressbook, \'principal\' AS type, '; $sql .= 'principal_id AS collection_id, '; $sql .= 'principal.* '; $sql .= 'FROM usr JOIN principal USING (user_no) '; $sql .= "WHERE (pprivs(:session_principal::int8,principal.principal_id,:scan_depth::int) & 1::BIT(24))::INT4::BOOLEAN "; $sql .= 'ORDER BY usr.user_no'; } else { $qry = new AwlQuery('SELECT * FROM dav_binding WHERE dav_binding.parent_container = :this_dav_name ORDER BY bind_id', array(':this_dav_name' => $bound_from)); if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) { while ($binding = $qry->Fetch()) { $resource = new DAVResource($binding->dav_name); if ($resource->IsExternal()) { require_once "external-fetch.php"; update_external($resource); } if ($resource->HavePrivilegeTo('DAV::read', false)) { $resource->set_bind_location(str_replace($bound_from, $bound_to, $binding->dav_name)); $responses[] = $resource->RenderAsXML($property_list, $reply); if ($depth > 0) { $responses = array_merge($responses, get_collection_contents($depth - 1, $resource, $binding->dav_name)); } } } } $sql = 'SELECT principal.*, collection.*, \'collection\' AS type '; $sql .= 'FROM collection LEFT JOIN principal USING (user_no) '; $sql .= 'WHERE parent_container = :this_dav_name '; $sql .= ' ORDER BY collection_id'; $params[':this_dav_name'] = $bound_from; unset($params[':session_principal']); unset($params[':scan_depth']); } $qry = new AwlQuery($sql, $params); if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) { while ($subcollection = $qry->Fetch()) { $resource = new DAVResource($subcollection); if (!$resource->HavePrivilegeTo('DAV::read')) { continue; } $resource->set_bind_location(str_replace($bound_from, $bound_to, $subcollection->dav_name)); $responses[] = $resource->RenderAsXML($property_list, $reply); if ($depth > 0) { $responses = array_merge($responses, get_collection_contents($depth - 1, $resource, str_replace($resource->parent_path(), $parent_path, $resource->dav_name()))); } } } if ((!isset($c->disable_caldav_proxy) || $c->disable_caldav_proxy == false) && $collection->IsPrincipal()) { // Caldav Proxy: 5.1 par. 2: Add child resources calendar-proxy-(read|write) dbg_error_log('PROPFIND', 'Adding calendar-proxy-read and write. Path: %s', $bound_from); $response = add_proxy_response('read', $bound_from); if (isset($response)) { $responses[] = $response; } $response = add_proxy_response('write', $bound_from); if (isset($response)) { $responses[] = $response; } } } /** * freebusy permission is not allowed to see the items in a collection. Must have at least read permission. */ if ($collection->HavePrivilegeTo('DAV::read', false)) { dbg_error_log('PROPFIND', 'Getting collection items: Depth %d, Path: %s', $depth, $bound_from); $privacy_clause = ' '; $todo_clause = ' '; $time_limit_clause = ' '; if ($collection->IsCalendar()) { if (!$collection->HavePrivilegeTo('all', false)) { $privacy_clause = " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) "; } if (isset($c->hide_TODO) && ($c->hide_TODO === true || is_string($c->hide_TODO) && preg_match($c->hide_TODO, $_SERVER['HTTP_USER_AGENT'])) && !$collection->HavePrivilegeTo('all')) { $todo_clause = " AND caldav_data.caldav_type NOT IN ('VTODO') "; } if (isset($c->hide_older_than) && intval($c->hide_older_than > 0)) { $time_limit_clause = " AND (CASE WHEN caldav_data.caldav_type<>'VEVENT' OR calendar_item.dtstart IS NULL THEN true ELSE calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') END) "; } } $sql = 'SELECT collection.*, principal.*, calendar_item.*, caldav_data.*, '; $sql .= "to_char(coalesce(calendar_item.created, caldav_data.created) at time zone 'GMT',{$date_format}) AS created, "; $sql .= "to_char(coalesce(calendar_item.last_modified, caldav_data.modified) at time zone 'GMT',{$date_format}) AS modified, "; $sql .= 'summary AS dav_displayname '; $sql .= 'FROM caldav_data LEFT JOIN calendar_item USING( dav_id, user_no, dav_name, collection_id) '; $sql .= 'LEFT JOIN collection USING(collection_id,user_no) LEFT JOIN principal USING(user_no) '; $sql .= 'WHERE collection.dav_name = :collection_dav_name ' . $time_limit_clause . ' ' . $todo_clause . ' ' . $privacy_clause; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY caldav_data.dav_id"; } $qry = new AwlQuery($sql, array(':collection_dav_name' => $bound_from)); if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) { while ($item = $qry->Fetch()) { if ($bound_from != $bound_to) { $item->bound_from = $item->dav_name; $item->dav_name = str_replace($bound_from, $bound_to, $item->dav_name); } $resource = new DAVResource($item); $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path); } } } return $responses; }
/** * Get XML response for a single collection. If Depth is >0 then * subsidiary collections will also be got up to $depth */ function get_collection($depth, $user_no, $collection_path) { global $c; $responses = array(); dbg_error_log("PROPFIND", "Getting collection: Depth %d, User: %d, Path: %s", $depth, $user_no, $collection_path); if ($collection_path == '/') { $collection->dav_name = $collection_path; $collection->dav_etag = md5($c->system_name . $collection_path); $collection->is_calendar = 'f'; $collection->dav_displayname = $c->system_name; $collection->created = date('Ymd"T"His'); $responses[] = collection_to_xml($collection); } else { $user_no = intval($user_no); $sql = "SELECT user_no, '/' || username || '/' AS dav_name, md5( '/' || username || '/') AS dav_etag, "; $sql .= "to_char(joined at time zone 'GMT',?) AS created, "; $sql .= "to_char(last_update at time zone 'GMT',?) AS modified, "; $sql .= "fullname AS dav_displayname, TRUE AS is_calendar, TRUE AS is_principal FROM usr WHERE user_no = {$user_no} ; "; $qry = new PgQuery($sql, PgQuery::Plain(iCalendar::HttpDateFormat()), PgQuery::Plain(iCalendar::HttpDateFormat())); if ($qry->Exec("PROPFIND", __LINE__, __FILE__) && $qry->rows > 0 && ($collection = $qry->Fetch())) { $responses[] = collection_to_xml($collection); } elseif ($c->collections_always_exist) { $collection->dav_name = $collection_path; $collection->dav_etag = md5($collection_path); $collection->is_calendar = 't'; // Everything is a calendar, if it always exists! $collection->dav_displayname = $collection_path; $collection->created = date('Ymd"T"His'); $responses[] = collection_to_xml($collection); } } if ($depth > 0 && isset($collection)) { $responses = array_merge($responses, get_collection_contents($depth - 1, $user_no, $collection)); } return $responses; }