1251881Speter/* 2251881Speter * getlocks.c : entry point for get_locks RA functions for ra_serf 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <apr_uri.h> 27251881Speter 28251881Speter#include <serf.h> 29251881Speter 30251881Speter#include "svn_hash.h" 31251881Speter#include "svn_path.h" 32251881Speter#include "svn_pools.h" 33251881Speter#include "svn_ra.h" 34251881Speter#include "svn_dav.h" 35251881Speter#include "svn_time.h" 36251881Speter#include "svn_xml.h" 37251881Speter 38251881Speter#include "private/svn_dav_protocol.h" 39251881Speter#include "private/svn_fspath.h" 40251881Speter#include "svn_private_config.h" 41251881Speter 42251881Speter#include "../libsvn_ra/ra_loader.h" 43251881Speter 44251881Speter#include "ra_serf.h" 45251881Speter 46251881Speter 47251881Speter/* 48251881Speter * This enum represents the current state of our XML parsing for a REPORT. 49251881Speter */ 50251881Speterenum { 51251881Speter INITIAL = 0, 52251881Speter REPORT, 53251881Speter LOCK, 54251881Speter PATH, 55251881Speter TOKEN, 56251881Speter OWNER, 57251881Speter COMMENT, 58251881Speter CREATION_DATE, 59251881Speter EXPIRATION_DATE 60251881Speter}; 61251881Speter 62251881Spetertypedef struct lock_context_t { 63251881Speter apr_pool_t *pool; 64251881Speter 65251881Speter /* target and requested depth of the operation. */ 66251881Speter const char *path; 67251881Speter svn_depth_t requested_depth; 68251881Speter 69251881Speter /* return hash */ 70251881Speter apr_hash_t *hash; 71251881Speter 72251881Speter} lock_context_t; 73251881Speter 74251881Speter#define D_ "DAV:" 75251881Speter#define S_ SVN_XML_NAMESPACE 76251881Speterstatic const svn_ra_serf__xml_transition_t getlocks_ttable[] = { 77251881Speter { INITIAL, S_, "get-locks-report", REPORT, 78251881Speter FALSE, { NULL }, FALSE }, 79251881Speter 80251881Speter { REPORT, S_, "lock", LOCK, 81251881Speter FALSE, { NULL }, TRUE }, 82251881Speter 83251881Speter { LOCK, S_, "path", PATH, 84251881Speter TRUE, { NULL }, TRUE }, 85251881Speter 86251881Speter { LOCK, S_, "token", TOKEN, 87251881Speter TRUE, { NULL }, TRUE }, 88251881Speter 89251881Speter { LOCK, S_, "owner", OWNER, 90251881Speter TRUE, { NULL }, TRUE }, 91251881Speter 92251881Speter { LOCK, S_, "comment", COMMENT, 93251881Speter TRUE, { NULL }, TRUE }, 94251881Speter 95251881Speter { LOCK, S_, SVN_DAV__CREATIONDATE, CREATION_DATE, 96251881Speter TRUE, { NULL }, TRUE }, 97251881Speter 98251881Speter { LOCK, S_, "expirationdate", EXPIRATION_DATE, 99251881Speter TRUE, { NULL }, TRUE }, 100251881Speter 101251881Speter { 0 } 102251881Speter}; 103251881Speter 104251881Speter 105251881Speter/* Conforms to svn_ra_serf__xml_closed_t */ 106251881Speterstatic svn_error_t * 107251881Spetergetlocks_closed(svn_ra_serf__xml_estate_t *xes, 108251881Speter void *baton, 109251881Speter int leaving_state, 110251881Speter const svn_string_t *cdata, 111251881Speter apr_hash_t *attrs, 112251881Speter apr_pool_t *scratch_pool) 113251881Speter{ 114251881Speter lock_context_t *lock_ctx = baton; 115251881Speter 116251881Speter if (leaving_state == LOCK) 117251881Speter { 118251881Speter const char *path = svn_hash_gets(attrs, "path"); 119269847Speter const char *token = svn_hash_gets(attrs, "token"); 120251881Speter svn_boolean_t save_lock = FALSE; 121251881Speter 122251881Speter /* Filter out unwanted paths. Since Subversion only allows 123251881Speter locks on files, we can treat depth=immediates the same as 124251881Speter depth=files for filtering purposes. Meaning, we'll keep 125251881Speter this lock if: 126251881Speter 127251881Speter a) its path is the very path we queried, or 128251881Speter b) we've asked for a fully recursive answer, or 129251881Speter c) we've asked for depth=files or depth=immediates, and this 130251881Speter lock is on an immediate child of our query path. 131251881Speter */ 132269847Speter if (! token) 133269847Speter { 134269847Speter /* A lock without a token is not a lock; just an answer that there 135269847Speter is no lock on the node. */ 136269847Speter save_lock = FALSE; 137269847Speter } 138251881Speter if (strcmp(lock_ctx->path, path) == 0 139251881Speter || lock_ctx->requested_depth == svn_depth_infinity) 140251881Speter { 141251881Speter save_lock = TRUE; 142251881Speter } 143251881Speter else if (lock_ctx->requested_depth == svn_depth_files 144251881Speter || lock_ctx->requested_depth == svn_depth_immediates) 145251881Speter { 146251881Speter const char *relpath = svn_fspath__skip_ancestor(lock_ctx->path, 147251881Speter path); 148251881Speter if (relpath && (svn_path_component_count(relpath) == 1)) 149251881Speter save_lock = TRUE; 150251881Speter } 151251881Speter 152251881Speter if (save_lock) 153251881Speter { 154251881Speter /* We get to put the structure on the stack rather than using 155251881Speter svn_lock_create(). Bwahahaha.... */ 156251881Speter svn_lock_t lock = { 0 }; 157251881Speter const char *date; 158251881Speter svn_lock_t *result_lock; 159251881Speter 160251881Speter /* Note: these "attributes" came from child elements. Some of 161251881Speter them may have not been sent, so the value will be NULL. */ 162251881Speter 163251881Speter lock.path = path; 164269847Speter lock.token = token; 165251881Speter lock.owner = svn_hash_gets(attrs, "owner"); 166251881Speter lock.comment = svn_hash_gets(attrs, "comment"); 167251881Speter 168251881Speter date = svn_hash_gets(attrs, SVN_DAV__CREATIONDATE); 169251881Speter if (date) 170251881Speter SVN_ERR(svn_time_from_cstring(&lock.creation_date, date, 171251881Speter scratch_pool)); 172251881Speter 173251881Speter date = svn_hash_gets(attrs, "expirationdate"); 174251881Speter if (date) 175251881Speter SVN_ERR(svn_time_from_cstring(&lock.expiration_date, date, 176251881Speter scratch_pool)); 177251881Speter 178251881Speter result_lock = svn_lock_dup(&lock, lock_ctx->pool); 179251881Speter svn_hash_sets(lock_ctx->hash, result_lock->path, result_lock); 180251881Speter } 181251881Speter } 182251881Speter else 183251881Speter { 184251881Speter const char *name; 185251881Speter 186251881Speter SVN_ERR_ASSERT(cdata != NULL); 187251881Speter 188251881Speter if (leaving_state == PATH) 189251881Speter name = "path"; 190251881Speter else if (leaving_state == TOKEN) 191251881Speter name = "token"; 192251881Speter else if (leaving_state == OWNER) 193251881Speter name = "owner"; 194251881Speter else if (leaving_state == COMMENT) 195251881Speter name = "comment"; 196251881Speter else if (leaving_state == CREATION_DATE) 197251881Speter name = SVN_DAV__CREATIONDATE; 198251881Speter else if (leaving_state == EXPIRATION_DATE) 199251881Speter name = "expirationdate"; 200251881Speter else 201251881Speter SVN_ERR_MALFUNCTION(); 202251881Speter 203251881Speter /* Store the lock information onto the LOCK elemstate. */ 204251881Speter svn_ra_serf__xml_note(xes, LOCK, name, cdata->data); 205251881Speter } 206251881Speter 207251881Speter return SVN_NO_ERROR; 208251881Speter} 209251881Speter 210251881Speter 211251881Speter/* Implements svn_ra_serf__request_body_delegate_t */ 212251881Speterstatic svn_error_t * 213251881Spetercreate_getlocks_body(serf_bucket_t **body_bkt, 214251881Speter void *baton, 215251881Speter serf_bucket_alloc_t *alloc, 216251881Speter apr_pool_t *pool) 217251881Speter{ 218251881Speter lock_context_t *lock_ctx = baton; 219251881Speter serf_bucket_t *buckets; 220251881Speter 221251881Speter buckets = serf_bucket_aggregate_create(alloc); 222251881Speter 223251881Speter svn_ra_serf__add_open_tag_buckets( 224251881Speter buckets, alloc, "S:get-locks-report", "xmlns:S", SVN_XML_NAMESPACE, 225251881Speter "depth", svn_depth_to_word(lock_ctx->requested_depth), NULL); 226251881Speter svn_ra_serf__add_close_tag_buckets(buckets, alloc, "S:get-locks-report"); 227251881Speter 228251881Speter *body_bkt = buckets; 229251881Speter return SVN_NO_ERROR; 230251881Speter} 231251881Speter 232251881Spetersvn_error_t * 233251881Spetersvn_ra_serf__get_locks(svn_ra_session_t *ra_session, 234251881Speter apr_hash_t **locks, 235251881Speter const char *path, 236251881Speter svn_depth_t depth, 237251881Speter apr_pool_t *pool) 238251881Speter{ 239251881Speter lock_context_t *lock_ctx; 240251881Speter svn_ra_serf__session_t *session = ra_session->priv; 241251881Speter svn_ra_serf__handler_t *handler; 242251881Speter svn_ra_serf__xml_context_t *xmlctx; 243251881Speter const char *req_url, *rel_path; 244269847Speter svn_error_t *err; 245251881Speter 246251881Speter req_url = svn_path_url_add_component2(session->session_url.path, path, pool); 247251881Speter SVN_ERR(svn_ra_serf__get_relative_path(&rel_path, req_url, session, 248251881Speter NULL, pool)); 249251881Speter 250251881Speter lock_ctx = apr_pcalloc(pool, sizeof(*lock_ctx)); 251251881Speter lock_ctx->pool = pool; 252251881Speter lock_ctx->path = apr_pstrcat(pool, "/", rel_path, (char *)NULL); 253251881Speter lock_ctx->requested_depth = depth; 254251881Speter lock_ctx->hash = apr_hash_make(pool); 255251881Speter 256251881Speter xmlctx = svn_ra_serf__xml_context_create(getlocks_ttable, 257251881Speter NULL, getlocks_closed, NULL, 258251881Speter lock_ctx, 259251881Speter pool); 260251881Speter handler = svn_ra_serf__create_expat_handler(xmlctx, pool); 261251881Speter 262251881Speter handler->method = "REPORT"; 263251881Speter handler->path = req_url; 264251881Speter handler->body_type = "text/xml"; 265251881Speter handler->conn = session->conns[0]; 266251881Speter handler->session = session; 267251881Speter 268251881Speter handler->body_delegate = create_getlocks_body; 269251881Speter handler->body_delegate_baton = lock_ctx; 270251881Speter 271269847Speter err = svn_ra_serf__context_run_one(handler, pool); 272269847Speter 273269847Speter /* Wrap the server generated error for an unsupported report with the 274269847Speter documented error for this ra function. */ 275269847Speter if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE)) 276269847Speter err = svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL); 277269847Speter 278269847Speter SVN_ERR(err); 279251881Speter 280251881Speter /* We get a 404 when a path doesn't exist in HEAD, but it might 281251881Speter have existed earlier (E.g. 'svn ls http://s/svn/trunk/file@1' */ 282251881Speter if (handler->sline.code != 404) 283251881Speter { 284253734Speter SVN_ERR(svn_ra_serf__error_on_status(handler->sline, 285251881Speter handler->path, 286251881Speter handler->location)); 287251881Speter } 288251881Speter 289251881Speter *locks = lock_ctx->hash; 290251881Speter 291251881Speter return SVN_NO_ERROR; 292251881Speter} 293