} if (!$dav_resource->Exists() && !$dav_resource->HavePrivilegeTo('DAV::bind')) { $request->DoResponse(403); } if (!ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put']) && $c->dbg['put'])) { $fh = fopen('/tmp/PUT.txt', 'w'); if ($fh) { fwrite($fh, $request->raw_post); fclose($fh); } } include_once 'caldav-PUT-functions.php'; controlRequestContainer($dav_resource->GetProperty('username'), $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), true); $lock_opener = $request->FailIfLocked(); if ($dav_resource->IsCollection()) { if ($dav_resource->IsPrincipal() || $dav_resource->IsBinding() || !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true) { $request->DoResponse(405); // Method not allowed return; } $appending = isset($_GET['mode']) && $_GET['mode'] == 'append'; /** * CalDAV does not define the result of a PUT on a collection. We treat that * as an import. The code is in caldav-PUT-functions.php */ import_collection($request->raw_post, $request->user_no, $request->path, true, $appending); $request->DoResponse(200); return; } $etag = md5($request->raw_post); $ic = new iCalComponent($request->raw_post);
} /** * Something that we can handle, at least roughly correctly. */ $responses = array(); $target_collection = new DAVResource($request->path); $bound_from = $target_collection->bound_from(); if (!$target_collection->Exists()) { $request->DoResponse(404); } $params = array(); if (!($target_collection->IsCalendar() || $target_collection->IsSchedulingCollection())) { if (!(isset($c->allow_recursive_report) && $c->allow_recursive_report)) { $request->DoResponse(403, translate('The calendar-query report must be run against a calendar or a scheduling collection')); } else { if ($request->path == '/' || $target_collection->IsPrincipal() || $target_collection->IsAddressbook()) { $request->DoResponse(403, translate('The calendar-query report may not be run against that URL.')); } } /** * We're here because they allow recursive reports, and this appears to be such a location. */ $where = '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) '; $distinct = 'DISTINCT ON (calendar_item.uid) '; $params[':path_match'] = '^' . $target_collection->bound_from(); } else { $where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id(); $distinct = '';
} $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); } } /** @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); if ($src_user_no != $dst_user_no) { $sql .= ', user_no = :dst_user_no '; $params[':dst_user_no'] = $dst_user_no; } if ($src->parent_path() != $dest->parent_path()) {
* @copyright Morphoss Ltd * @license http://gnu.org/copyleft/gpl.html GNU GPL v2 */ dbg_error_log("MOVE", "method handler"); require_once 'DAVResource.php'; $request->NeedPrivilege('DAV::unbind'); if (!ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['move']) && $c->dbg['move'])) { $fh = fopen('/tmp/MOVE.txt', 'w'); if ($fh) { fwrite($fh, $request->raw_post); fclose($fh); } } $lock_opener = $request->FailIfLocked(); $dest = new DAVResource($request->destination); if ($dest->dav_name() == '/' || $dest->IsPrincipal()) { $dest->NeedPrivilege('DAV::bind'); } if (!$dest->ContainerExists()) { $request->DoResponse(409, translate('Destination collection does not exist')); } if (!$request->overwrite && $dest->Exists()) { $request->DoResponse(412, translate('Not overwriting existing destination resource')); } if (isset($request->etag_none_match) && $request->etag_none_match != '*') { $request->DoResponse(412); /** request to move, but only if there is no source? WTF! */ } $src = new DAVResource($request->path); if (!$src->Exists()) { $request->DoResponse(412, translate('Source resource does not exist.'));
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.')); } if ($source->IsPrincipal() || !$source->IsCollection()) { $request->PreconditionFailed(403, 'DAV::binding-allowed', translate('DAViCal only allows BIND requests for collections at present.')); } if ($source->IsBinding()) { $source = new DAVResource($source->bound_from()); } /* bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY, bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE, access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL, parent_container TEXT NOT NULL, dav_name TEXT UNIQUE NOT NULL, dav_displayname TEXT, external_url TEXT, type TEXT */
* Not much for it but to process the incoming settings in a big loop, doing * the special-case stuff as needed and falling through to a default which * 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':
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 ($principal_property[0]->GetTag() != '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'; $principal = new DAVResource(DeconstructURL($principal_content->GetContent())); if (!$principal->Exists() || !$principal->IsPrincipal()) { $request->PreconditionFailed(403, 'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.'); } $sqlparms = array(':to_principal' => $principal->GetProperty('principal_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 {