1251881Speter/* 2251881Speter * locks.c : entry point for locking 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#include <serf.h> 28251881Speter 29251881Speter#include "svn_dav.h" 30251881Speter#include "svn_pools.h" 31251881Speter#include "svn_ra.h" 32251881Speter 33251881Speter#include "../libsvn_ra/ra_loader.h" 34251881Speter#include "svn_config.h" 35251881Speter#include "svn_path.h" 36251881Speter#include "svn_time.h" 37251881Speter#include "svn_private_config.h" 38251881Speter 39251881Speter#include "ra_serf.h" 40251881Speter 41251881Speter 42251881Speter/* 43251881Speter * This enum represents the current state of our XML parsing for a REPORT. 44251881Speter */ 45251881Speterenum { 46251881Speter INITIAL = 0, 47251881Speter MULTISTATUS, 48251881Speter RESPONSE, 49251881Speter PROPSTAT, 50251881Speter PROP, 51251881Speter LOCK_DISCOVERY, 52251881Speter ACTIVE_LOCK, 53251881Speter LOCK_TYPE, 54251881Speter LOCK_SCOPE, 55251881Speter DEPTH, 56251881Speter TIMEOUT, 57251881Speter LOCK_TOKEN, 58251881Speter OWNER, 59251881Speter HREF 60251881Speter}; 61251881Speter 62251881Spetertypedef struct lock_info_t { 63251881Speter apr_pool_t *pool; 64251881Speter 65251881Speter const char *path; 66251881Speter 67251881Speter svn_lock_t *lock; 68251881Speter 69251881Speter svn_boolean_t force; 70251881Speter svn_revnum_t revision; 71251881Speter 72251881Speter svn_boolean_t read_headers; 73251881Speter 74251881Speter svn_ra_serf__handler_t *handler; 75251881Speter 76251881Speter /* The expat handler. We wrap this to do a bit more work. */ 77251881Speter svn_ra_serf__response_handler_t inner_handler; 78251881Speter void *inner_baton; 79251881Speter 80251881Speter} lock_info_t; 81251881Speter 82251881Speter#define D_ "DAV:" 83251881Speter#define S_ SVN_XML_NAMESPACE 84251881Speterstatic const svn_ra_serf__xml_transition_t locks_ttable[] = { 85251881Speter /* The INITIAL state can transition into D:prop (LOCK) or 86251881Speter to D:multistatus (PROPFIND) */ 87251881Speter { INITIAL, D_, "prop", PROP, 88251881Speter FALSE, { NULL }, FALSE }, 89251881Speter { INITIAL, D_, "multistatus", MULTISTATUS, 90251881Speter FALSE, { NULL }, FALSE }, 91251881Speter 92251881Speter { MULTISTATUS, D_, "response", RESPONSE, 93251881Speter FALSE, { NULL }, FALSE }, 94251881Speter 95251881Speter { RESPONSE, D_, "propstat", PROPSTAT, 96251881Speter FALSE, { NULL }, FALSE }, 97251881Speter 98251881Speter { PROPSTAT, D_, "prop", PROP, 99251881Speter FALSE, { NULL }, FALSE }, 100251881Speter 101251881Speter { PROP, D_, "lockdiscovery", LOCK_DISCOVERY, 102251881Speter FALSE, { NULL }, FALSE }, 103251881Speter 104251881Speter { LOCK_DISCOVERY, D_, "activelock", ACTIVE_LOCK, 105251881Speter FALSE, { NULL }, FALSE }, 106251881Speter 107251881Speter#if 0 108251881Speter /* ### we don't really need to parse locktype/lockscope. we know what 109251881Speter ### the values are going to be. we *could* validate that the only 110251881Speter ### possible children are D:write and D:exclusive. we'd need to 111251881Speter ### modify the state transition to tell us about all children 112251881Speter ### (ie. maybe support "*" for the name) and then validate. but it 113251881Speter ### just isn't important to validate, so disable this for now... */ 114251881Speter 115251881Speter { ACTIVE_LOCK, D_, "locktype", LOCK_TYPE, 116251881Speter FALSE, { NULL }, FALSE }, 117251881Speter 118251881Speter { LOCK_TYPE, D_, "write", WRITE, 119251881Speter FALSE, { NULL }, TRUE }, 120251881Speter 121251881Speter { ACTIVE_LOCK, D_, "lockscope", LOCK_SCOPE, 122251881Speter FALSE, { NULL }, FALSE }, 123251881Speter 124251881Speter { LOCK_SCOPE, D_, "exclusive", EXCLUSIVE, 125251881Speter FALSE, { NULL }, TRUE }, 126251881Speter#endif /* 0 */ 127251881Speter 128251881Speter { ACTIVE_LOCK, D_, "timeout", TIMEOUT, 129251881Speter TRUE, { NULL }, TRUE }, 130251881Speter 131251881Speter { ACTIVE_LOCK, D_, "locktoken", LOCK_TOKEN, 132251881Speter FALSE, { NULL }, FALSE }, 133251881Speter 134251881Speter { LOCK_TOKEN, D_, "href", HREF, 135251881Speter TRUE, { NULL }, TRUE }, 136251881Speter 137251881Speter { ACTIVE_LOCK, D_, "owner", OWNER, 138251881Speter TRUE, { NULL }, TRUE }, 139251881Speter 140251881Speter /* ACTIVE_LOCK has a D:depth child, but we can ignore that. */ 141251881Speter 142251881Speter { 0 } 143251881Speter}; 144251881Speter 145251881Speter 146251881Speter/* Conforms to svn_ra_serf__xml_closed_t */ 147251881Speterstatic svn_error_t * 148251881Speterlocks_closed(svn_ra_serf__xml_estate_t *xes, 149251881Speter void *baton, 150251881Speter int leaving_state, 151251881Speter const svn_string_t *cdata, 152251881Speter apr_hash_t *attrs, 153251881Speter apr_pool_t *scratch_pool) 154251881Speter{ 155251881Speter lock_info_t *lock_ctx = baton; 156251881Speter 157251881Speter if (leaving_state == TIMEOUT) 158251881Speter { 159269847Speter if (strcasecmp(cdata->data, "Infinite") == 0) 160251881Speter lock_ctx->lock->expiration_date = 0; 161269847Speter else if (strncasecmp(cdata->data, "Second-", 7) == 0) 162269847Speter { 163269847Speter unsigned n; 164269847Speter SVN_ERR(svn_cstring_atoui(&n, cdata->data+7)); 165269847Speter 166269847Speter lock_ctx->lock->expiration_date = apr_time_now() + 167269847Speter apr_time_from_sec(n); 168269847Speter } 169251881Speter else 170269847Speter return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 171269847Speter _("Invalid LOCK timeout value '%s'"), 172269847Speter cdata->data); 173251881Speter } 174251881Speter else if (leaving_state == HREF) 175251881Speter { 176251881Speter if (cdata->len) 177251881Speter { 178251881Speter char *buf = apr_pstrmemdup(lock_ctx->pool, cdata->data, cdata->len); 179251881Speter 180251881Speter apr_collapse_spaces(buf, buf); 181251881Speter lock_ctx->lock->token = buf; 182251881Speter } 183251881Speter } 184251881Speter else if (leaving_state == OWNER) 185251881Speter { 186251881Speter if (cdata->len) 187251881Speter { 188251881Speter lock_ctx->lock->comment = apr_pstrmemdup(lock_ctx->pool, 189251881Speter cdata->data, cdata->len); 190251881Speter } 191251881Speter } 192251881Speter 193251881Speter return SVN_NO_ERROR; 194251881Speter} 195251881Speter 196251881Speter 197251881Speterstatic svn_error_t * 198251881Speterset_lock_headers(serf_bucket_t *headers, 199251881Speter void *baton, 200251881Speter apr_pool_t *pool) 201251881Speter{ 202251881Speter lock_info_t *lock_ctx = baton; 203251881Speter 204251881Speter if (lock_ctx->force) 205251881Speter { 206251881Speter serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER, 207251881Speter SVN_DAV_OPTION_LOCK_STEAL); 208251881Speter } 209251881Speter 210251881Speter if (SVN_IS_VALID_REVNUM(lock_ctx->revision)) 211251881Speter { 212251881Speter serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER, 213251881Speter apr_ltoa(pool, lock_ctx->revision)); 214251881Speter } 215251881Speter 216251881Speter return APR_SUCCESS; 217251881Speter} 218251881Speter 219251881Speter 220251881Speter/* Register an error within the session. If something is already there, 221251881Speter then it will take precedence. */ 222251881Speterstatic svn_error_t * 223251881Speterdetermine_error(svn_ra_serf__handler_t *handler, 224251881Speter svn_error_t *err) 225251881Speter{ 226251881Speter { 227251881Speter apr_status_t errcode; 228251881Speter 229251881Speter if (handler->sline.code == 423) 230251881Speter errcode = SVN_ERR_FS_PATH_ALREADY_LOCKED; 231251881Speter else if (handler->sline.code == 403) 232251881Speter errcode = SVN_ERR_RA_DAV_FORBIDDEN; 233251881Speter else 234251881Speter return err; 235251881Speter 236251881Speter /* Client-side or server-side error already. Return it. */ 237251881Speter if (err != NULL) 238251881Speter return err; 239251881Speter 240251881Speter /* The server did not send us a detailed human-readable error. 241251881Speter Provide a generic error. */ 242251881Speter err = svn_error_createf(errcode, NULL, 243251881Speter _("Lock request failed: %d %s"), 244251881Speter handler->sline.code, 245251881Speter handler->sline.reason); 246251881Speter } 247251881Speter 248251881Speter return err; 249251881Speter} 250251881Speter 251251881Speter 252251881Speter/* Implements svn_ra_serf__response_handler_t */ 253251881Speterstatic svn_error_t * 254251881Speterhandle_lock(serf_request_t *request, 255251881Speter serf_bucket_t *response, 256251881Speter void *handler_baton, 257251881Speter apr_pool_t *pool) 258251881Speter{ 259251881Speter lock_info_t *ctx = handler_baton; 260251881Speter 261251881Speter /* 403 (Forbidden) when a lock doesn't exist. 262251881Speter 423 (Locked) when a lock already exists. */ 263251881Speter if (ctx->handler->sline.code == 403 264251881Speter || ctx->handler->sline.code == 423) 265251881Speter { 266251881Speter /* Go look in the body for a server-provided error. This will 267251881Speter reset flags for the core handler to Do The Right Thing. We 268251881Speter won't be back to this handler again. */ 269251881Speter return svn_error_trace(svn_ra_serf__expect_empty_body( 270251881Speter request, response, ctx->handler, pool)); 271251881Speter } 272251881Speter 273251881Speter if (!ctx->read_headers) 274251881Speter { 275251881Speter serf_bucket_t *headers; 276251881Speter const char *val; 277251881Speter 278251881Speter headers = serf_bucket_response_get_headers(response); 279251881Speter 280251881Speter val = serf_bucket_headers_get(headers, SVN_DAV_LOCK_OWNER_HEADER); 281251881Speter if (val) 282251881Speter { 283251881Speter ctx->lock->owner = apr_pstrdup(ctx->pool, val); 284251881Speter } 285251881Speter 286251881Speter val = serf_bucket_headers_get(headers, SVN_DAV_CREATIONDATE_HEADER); 287251881Speter if (val) 288251881Speter { 289251881Speter SVN_ERR(svn_time_from_cstring(&ctx->lock->creation_date, val, 290251881Speter ctx->pool)); 291251881Speter } 292251881Speter 293251881Speter ctx->read_headers = TRUE; 294251881Speter } 295251881Speter 296251881Speter return ctx->inner_handler(request, response, ctx->inner_baton, pool); 297251881Speter} 298251881Speter 299251881Speter/* Implements svn_ra_serf__request_body_delegate_t */ 300251881Speterstatic svn_error_t * 301251881Spetercreate_getlock_body(serf_bucket_t **body_bkt, 302251881Speter void *baton, 303251881Speter serf_bucket_alloc_t *alloc, 304251881Speter apr_pool_t *pool) 305251881Speter{ 306251881Speter serf_bucket_t *buckets; 307251881Speter 308251881Speter buckets = serf_bucket_aggregate_create(alloc); 309251881Speter 310251881Speter svn_ra_serf__add_xml_header_buckets(buckets, alloc); 311251881Speter svn_ra_serf__add_open_tag_buckets(buckets, alloc, "propfind", 312251881Speter "xmlns", "DAV:", 313251881Speter NULL); 314251881Speter svn_ra_serf__add_open_tag_buckets(buckets, alloc, "prop", NULL); 315251881Speter svn_ra_serf__add_tag_buckets(buckets, "lockdiscovery", NULL, alloc); 316251881Speter svn_ra_serf__add_close_tag_buckets(buckets, alloc, "prop"); 317251881Speter svn_ra_serf__add_close_tag_buckets(buckets, alloc, "propfind"); 318251881Speter 319251881Speter *body_bkt = buckets; 320251881Speter return SVN_NO_ERROR; 321251881Speter} 322251881Speter 323251881Speterstatic svn_error_t* 324251881Spetersetup_getlock_headers(serf_bucket_t *headers, 325251881Speter void *baton, 326251881Speter apr_pool_t *pool) 327251881Speter{ 328251881Speter serf_bucket_headers_setn(headers, "Depth", "0"); 329251881Speter 330251881Speter return SVN_NO_ERROR; 331251881Speter} 332251881Speter 333251881Speter/* Implements svn_ra_serf__request_body_delegate_t */ 334251881Speterstatic svn_error_t * 335251881Spetercreate_lock_body(serf_bucket_t **body_bkt, 336251881Speter void *baton, 337251881Speter serf_bucket_alloc_t *alloc, 338251881Speter apr_pool_t *pool) 339251881Speter{ 340251881Speter lock_info_t *ctx = baton; 341251881Speter serf_bucket_t *buckets; 342251881Speter 343251881Speter buckets = serf_bucket_aggregate_create(alloc); 344251881Speter 345251881Speter svn_ra_serf__add_xml_header_buckets(buckets, alloc); 346251881Speter svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockinfo", 347251881Speter "xmlns", "DAV:", 348251881Speter NULL); 349251881Speter 350251881Speter svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockscope", NULL); 351251881Speter svn_ra_serf__add_tag_buckets(buckets, "exclusive", NULL, alloc); 352251881Speter svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockscope"); 353251881Speter 354251881Speter svn_ra_serf__add_open_tag_buckets(buckets, alloc, "locktype", NULL); 355251881Speter svn_ra_serf__add_tag_buckets(buckets, "write", NULL, alloc); 356251881Speter svn_ra_serf__add_close_tag_buckets(buckets, alloc, "locktype"); 357251881Speter 358251881Speter if (ctx->lock->comment) 359251881Speter { 360251881Speter svn_ra_serf__add_tag_buckets(buckets, "owner", ctx->lock->comment, 361251881Speter alloc); 362251881Speter } 363251881Speter 364251881Speter svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockinfo"); 365251881Speter 366251881Speter *body_bkt = buckets; 367251881Speter return SVN_NO_ERROR; 368251881Speter} 369251881Speter 370251881Spetersvn_error_t * 371251881Spetersvn_ra_serf__get_lock(svn_ra_session_t *ra_session, 372251881Speter svn_lock_t **lock, 373251881Speter const char *path, 374269847Speter apr_pool_t *result_pool) 375251881Speter{ 376251881Speter svn_ra_serf__session_t *session = ra_session->priv; 377251881Speter svn_ra_serf__handler_t *handler; 378251881Speter svn_ra_serf__xml_context_t *xmlctx; 379269847Speter apr_pool_t *scratch_pool = svn_pool_create(result_pool); 380251881Speter lock_info_t *lock_ctx; 381251881Speter const char *req_url; 382251881Speter svn_error_t *err; 383251881Speter 384269847Speter req_url = svn_path_url_add_component2(session->session_url.path, path, 385269847Speter scratch_pool); 386251881Speter 387269847Speter lock_ctx = apr_pcalloc(scratch_pool, sizeof(*lock_ctx)); 388269847Speter lock_ctx->pool = result_pool; 389251881Speter lock_ctx->path = req_url; 390269847Speter lock_ctx->lock = svn_lock_create(result_pool); 391269847Speter lock_ctx->lock->path = apr_pstrdup(result_pool, path); 392251881Speter 393251881Speter xmlctx = svn_ra_serf__xml_context_create(locks_ttable, 394251881Speter NULL, locks_closed, NULL, 395251881Speter lock_ctx, 396269847Speter scratch_pool); 397269847Speter handler = svn_ra_serf__create_expat_handler(xmlctx, scratch_pool); 398251881Speter 399251881Speter handler->method = "PROPFIND"; 400251881Speter handler->path = req_url; 401251881Speter handler->body_type = "text/xml"; 402251881Speter handler->conn = session->conns[0]; 403251881Speter handler->session = session; 404251881Speter 405251881Speter handler->body_delegate = create_getlock_body; 406251881Speter handler->body_delegate_baton = lock_ctx; 407251881Speter 408251881Speter handler->header_delegate = setup_getlock_headers; 409251881Speter handler->header_delegate_baton = lock_ctx; 410251881Speter 411251881Speter lock_ctx->inner_handler = handler->response_handler; 412251881Speter lock_ctx->inner_baton = handler->response_baton; 413251881Speter handler->response_handler = handle_lock; 414251881Speter handler->response_baton = lock_ctx; 415251881Speter 416251881Speter lock_ctx->handler = handler; 417251881Speter 418269847Speter err = svn_ra_serf__context_run_one(handler, scratch_pool); 419251881Speter err = determine_error(handler, err); 420251881Speter 421251881Speter if (handler->sline.code == 404) 422251881Speter { 423251881Speter return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, err, 424251881Speter _("Malformed URL for repository")); 425251881Speter } 426251881Speter if (err) 427251881Speter { 428251881Speter /* TODO Shh. We're telling a white lie for now. */ 429251881Speter return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, 430251881Speter _("Server does not support locking features")); 431251881Speter } 432251881Speter 433269847Speter if (lock_ctx->lock && lock_ctx->lock->token) 434269847Speter *lock = lock_ctx->lock; 435269847Speter else 436269847Speter *lock = NULL; 437251881Speter 438269847Speter svn_pool_destroy(scratch_pool); 439269847Speter 440251881Speter return SVN_NO_ERROR; 441251881Speter} 442251881Speter 443251881Spetersvn_error_t * 444251881Spetersvn_ra_serf__lock(svn_ra_session_t *ra_session, 445251881Speter apr_hash_t *path_revs, 446251881Speter const char *comment, 447251881Speter svn_boolean_t force, 448251881Speter svn_ra_lock_callback_t lock_func, 449251881Speter void *lock_baton, 450251881Speter apr_pool_t *scratch_pool) 451251881Speter{ 452251881Speter svn_ra_serf__session_t *session = ra_session->priv; 453251881Speter apr_hash_index_t *hi; 454251881Speter apr_pool_t *iterpool; 455251881Speter 456251881Speter iterpool = svn_pool_create(scratch_pool); 457251881Speter 458251881Speter /* ### TODO for issue 2263: Send all the locks over the wire at once. This 459251881Speter ### loop is just a temporary shim. 460251881Speter ### an alternative, which is backwards-compat with all servers is to 461251881Speter ### pipeline these requests. ie. stop using run_wait/run_one. */ 462251881Speter 463251881Speter for (hi = apr_hash_first(scratch_pool, path_revs); 464251881Speter hi; 465251881Speter hi = apr_hash_next(hi)) 466251881Speter { 467251881Speter svn_ra_serf__handler_t *handler; 468251881Speter svn_ra_serf__xml_context_t *xmlctx; 469251881Speter const char *req_url; 470251881Speter lock_info_t *lock_ctx; 471251881Speter svn_error_t *err; 472251881Speter svn_error_t *new_err = NULL; 473251881Speter 474251881Speter svn_pool_clear(iterpool); 475251881Speter 476251881Speter lock_ctx = apr_pcalloc(iterpool, sizeof(*lock_ctx)); 477251881Speter 478251881Speter lock_ctx->pool = iterpool; 479251881Speter lock_ctx->path = svn__apr_hash_index_key(hi); 480251881Speter lock_ctx->revision = *((svn_revnum_t*)svn__apr_hash_index_val(hi)); 481251881Speter lock_ctx->lock = svn_lock_create(iterpool); 482251881Speter lock_ctx->lock->path = lock_ctx->path; 483251881Speter lock_ctx->lock->comment = comment; 484251881Speter 485251881Speter lock_ctx->force = force; 486251881Speter req_url = svn_path_url_add_component2(session->session_url.path, 487251881Speter lock_ctx->path, iterpool); 488251881Speter 489251881Speter xmlctx = svn_ra_serf__xml_context_create(locks_ttable, 490251881Speter NULL, locks_closed, NULL, 491251881Speter lock_ctx, 492251881Speter iterpool); 493251881Speter handler = svn_ra_serf__create_expat_handler(xmlctx, iterpool); 494251881Speter 495251881Speter handler->method = "LOCK"; 496251881Speter handler->path = req_url; 497251881Speter handler->body_type = "text/xml"; 498251881Speter handler->conn = session->conns[0]; 499251881Speter handler->session = session; 500251881Speter 501251881Speter handler->header_delegate = set_lock_headers; 502251881Speter handler->header_delegate_baton = lock_ctx; 503251881Speter 504251881Speter handler->body_delegate = create_lock_body; 505251881Speter handler->body_delegate_baton = lock_ctx; 506251881Speter 507251881Speter lock_ctx->inner_handler = handler->response_handler; 508251881Speter lock_ctx->inner_baton = handler->response_baton; 509251881Speter handler->response_handler = handle_lock; 510251881Speter handler->response_baton = lock_ctx; 511251881Speter 512251881Speter lock_ctx->handler = handler; 513251881Speter 514251881Speter err = svn_ra_serf__context_run_one(handler, iterpool); 515251881Speter err = determine_error(handler, err); 516251881Speter 517251881Speter if (lock_func) 518251881Speter new_err = lock_func(lock_baton, lock_ctx->path, TRUE, lock_ctx->lock, 519251881Speter err, iterpool); 520251881Speter svn_error_clear(err); 521251881Speter 522251881Speter SVN_ERR(new_err); 523251881Speter } 524251881Speter 525251881Speter svn_pool_destroy(iterpool); 526251881Speter 527251881Speter return SVN_NO_ERROR; 528251881Speter} 529251881Speter 530251881Speterstruct unlock_context_t { 531251881Speter const char *token; 532251881Speter svn_boolean_t force; 533251881Speter}; 534251881Speter 535251881Speterstatic svn_error_t * 536251881Speterset_unlock_headers(serf_bucket_t *headers, 537251881Speter void *baton, 538251881Speter apr_pool_t *pool) 539251881Speter{ 540251881Speter struct unlock_context_t *ctx = baton; 541251881Speter 542251881Speter serf_bucket_headers_set(headers, "Lock-Token", ctx->token); 543251881Speter if (ctx->force) 544251881Speter { 545251881Speter serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER, 546251881Speter SVN_DAV_OPTION_LOCK_BREAK); 547251881Speter } 548251881Speter 549251881Speter return SVN_NO_ERROR; 550251881Speter} 551251881Speter 552251881Spetersvn_error_t * 553251881Spetersvn_ra_serf__unlock(svn_ra_session_t *ra_session, 554251881Speter apr_hash_t *path_tokens, 555251881Speter svn_boolean_t force, 556251881Speter svn_ra_lock_callback_t lock_func, 557251881Speter void *lock_baton, 558251881Speter apr_pool_t *scratch_pool) 559251881Speter{ 560251881Speter svn_ra_serf__session_t *session = ra_session->priv; 561251881Speter apr_hash_index_t *hi; 562251881Speter apr_pool_t *iterpool; 563251881Speter 564251881Speter iterpool = svn_pool_create(scratch_pool); 565251881Speter 566251881Speter /* ### TODO for issue 2263: Send all the locks over the wire at once. This 567251881Speter ### loop is just a temporary shim. 568251881Speter ### an alternative, which is backwards-compat with all servers is to 569251881Speter ### pipeline these requests. ie. stop using run_wait/run_one. */ 570251881Speter 571251881Speter for (hi = apr_hash_first(scratch_pool, path_tokens); 572251881Speter hi; 573251881Speter hi = apr_hash_next(hi)) 574251881Speter { 575251881Speter svn_ra_serf__handler_t *handler; 576251881Speter const char *req_url, *path, *token; 577251881Speter svn_lock_t *existing_lock = NULL; 578251881Speter struct unlock_context_t unlock_ctx; 579251881Speter svn_error_t *err = NULL; 580251881Speter svn_error_t *new_err = NULL; 581251881Speter 582251881Speter 583251881Speter svn_pool_clear(iterpool); 584251881Speter 585251881Speter path = svn__apr_hash_index_key(hi); 586251881Speter token = svn__apr_hash_index_val(hi); 587251881Speter 588251881Speter if (force && (!token || token[0] == '\0')) 589251881Speter { 590251881Speter SVN_ERR(svn_ra_serf__get_lock(ra_session, &existing_lock, path, 591251881Speter iterpool)); 592269847Speter token = existing_lock ? existing_lock->token : NULL; 593251881Speter if (!token) 594251881Speter { 595251881Speter err = svn_error_createf(SVN_ERR_RA_NOT_LOCKED, NULL, 596251881Speter _("'%s' is not locked in the repository"), 597251881Speter path); 598251881Speter 599251881Speter if (lock_func) 600251881Speter { 601251881Speter svn_error_t *err2; 602251881Speter err2 = lock_func(lock_baton, path, FALSE, NULL, err, 603251881Speter iterpool); 604251881Speter svn_error_clear(err); 605251881Speter err = NULL; 606251881Speter if (err2) 607251881Speter return svn_error_trace(err2); 608251881Speter } 609251881Speter else 610251881Speter { 611251881Speter svn_error_clear(err); 612251881Speter err = NULL; 613251881Speter } 614251881Speter continue; 615251881Speter } 616251881Speter } 617251881Speter 618251881Speter unlock_ctx.force = force; 619251881Speter unlock_ctx.token = apr_pstrcat(iterpool, "<", token, ">", (char *)NULL); 620251881Speter 621251881Speter req_url = svn_path_url_add_component2(session->session_url.path, path, 622251881Speter iterpool); 623251881Speter 624251881Speter handler = apr_pcalloc(iterpool, sizeof(*handler)); 625251881Speter 626251881Speter handler->handler_pool = iterpool; 627251881Speter handler->method = "UNLOCK"; 628251881Speter handler->path = req_url; 629251881Speter handler->conn = session->conns[0]; 630251881Speter handler->session = session; 631251881Speter 632251881Speter handler->header_delegate = set_unlock_headers; 633251881Speter handler->header_delegate_baton = &unlock_ctx; 634251881Speter 635251881Speter handler->response_handler = svn_ra_serf__expect_empty_body; 636251881Speter handler->response_baton = handler; 637251881Speter 638251881Speter SVN_ERR(svn_ra_serf__context_run_one(handler, iterpool)); 639251881Speter 640251881Speter switch (handler->sline.code) 641251881Speter { 642251881Speter case 204: 643251881Speter break; /* OK */ 644251881Speter case 403: 645251881Speter /* Api users expect this specific error code to detect failures */ 646251881Speter err = svn_error_createf(SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL, 647251881Speter _("Unlock request failed: %d %s"), 648251881Speter handler->sline.code, 649251881Speter handler->sline.reason); 650251881Speter break; 651251881Speter default: 652251881Speter err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 653251881Speter _("Unlock request failed: %d %s"), 654251881Speter handler->sline.code, 655251881Speter handler->sline.reason); 656251881Speter } 657251881Speter 658251881Speter if (lock_func) 659251881Speter new_err = lock_func(lock_baton, path, FALSE, existing_lock, err, 660251881Speter iterpool); 661251881Speter 662251881Speter svn_error_clear(err); 663251881Speter SVN_ERR(new_err); 664251881Speter } 665251881Speter 666251881Speter svn_pool_destroy(iterpool); 667251881Speter 668251881Speter return SVN_NO_ERROR; 669251881Speter} 670