get_lock.c revision 299742
1/* 2 * get_lock.c : obtain single lock information functions for ra_serf 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26#include <apr_uri.h> 27#include <serf.h> 28 29#include "svn_dav.h" 30#include "svn_pools.h" 31#include "svn_ra.h" 32 33#include "../libsvn_ra/ra_loader.h" 34#include "svn_config.h" 35#include "svn_path.h" 36#include "svn_time.h" 37#include "svn_private_config.h" 38 39#include "ra_serf.h" 40 41 42/* 43 * This enum represents the current state of our XML parsing for a REPORT. 44 */ 45enum { 46 INITIAL = 0, 47 MULTISTATUS, 48 RESPONSE, 49 PROPSTAT, 50 PROP, 51 LOCK_DISCOVERY, 52 ACTIVE_LOCK, 53 LOCK_TYPE, 54 LOCK_SCOPE, 55 DEPTH, 56 TIMEOUT, 57 LOCK_TOKEN, 58 OWNER, 59 HREF 60}; 61 62typedef struct lock_info_t { 63 apr_pool_t *pool; 64 65 const char *path; 66 67 svn_lock_t *lock; 68 69 svn_boolean_t read_headers; 70 71 svn_ra_serf__handler_t *handler; 72 73 /* The expat handler. We wrap this to do a bit more work. */ 74 svn_ra_serf__response_handler_t inner_handler; 75 void *inner_baton; 76 77} lock_info_t; 78 79#define D_ "DAV:" 80#define S_ SVN_XML_NAMESPACE 81static const svn_ra_serf__xml_transition_t locks_ttable[] = { 82 /* The INITIAL state can transition into D:prop (LOCK) or 83 to D:multistatus (PROPFIND) */ 84 { INITIAL, D_, "multistatus", MULTISTATUS, 85 FALSE, { NULL }, FALSE }, 86 87 { MULTISTATUS, D_, "response", RESPONSE, 88 FALSE, { NULL }, FALSE }, 89 90 { RESPONSE, D_, "propstat", PROPSTAT, 91 FALSE, { NULL }, FALSE }, 92 93 { PROPSTAT, D_, "prop", PROP, 94 FALSE, { NULL }, FALSE }, 95 96 { PROP, D_, "lockdiscovery", LOCK_DISCOVERY, 97 FALSE, { NULL }, FALSE }, 98 99 { LOCK_DISCOVERY, D_, "activelock", ACTIVE_LOCK, 100 FALSE, { NULL }, FALSE }, 101 102#if 0 103 /* ### we don't really need to parse locktype/lockscope. we know what 104 ### the values are going to be. we *could* validate that the only 105 ### possible children are D:write and D:exclusive. we'd need to 106 ### modify the state transition to tell us about all children 107 ### (ie. maybe support "*" for the name) and then validate. but it 108 ### just isn't important to validate, so disable this for now... */ 109 110 { ACTIVE_LOCK, D_, "locktype", LOCK_TYPE, 111 FALSE, { NULL }, FALSE }, 112 113 { LOCK_TYPE, D_, "write", WRITE, 114 FALSE, { NULL }, TRUE }, 115 116 { ACTIVE_LOCK, D_, "lockscope", LOCK_SCOPE, 117 FALSE, { NULL }, FALSE }, 118 119 { LOCK_SCOPE, D_, "exclusive", EXCLUSIVE, 120 FALSE, { NULL }, TRUE }, 121#endif /* 0 */ 122 123 { ACTIVE_LOCK, D_, "timeout", TIMEOUT, 124 TRUE, { NULL }, TRUE }, 125 126 { ACTIVE_LOCK, D_, "locktoken", LOCK_TOKEN, 127 FALSE, { NULL }, FALSE }, 128 129 { LOCK_TOKEN, D_, "href", HREF, 130 TRUE, { NULL }, TRUE }, 131 132 { ACTIVE_LOCK, D_, "owner", OWNER, 133 TRUE, { NULL }, TRUE }, 134 135 /* ACTIVE_LOCK has a D:depth child, but we can ignore that. */ 136 137 { 0 } 138}; 139 140static const int locks_expected_status[] = { 141 207, 142 0 143}; 144 145/* Conforms to svn_ra_serf__xml_closed_t */ 146static svn_error_t * 147locks_closed(svn_ra_serf__xml_estate_t *xes, 148 void *baton, 149 int leaving_state, 150 const svn_string_t *cdata, 151 apr_hash_t *attrs, 152 apr_pool_t *scratch_pool) 153{ 154 lock_info_t *lock_ctx = baton; 155 156 if (leaving_state == TIMEOUT) 157 { 158 if (strcasecmp(cdata->data, "Infinite") == 0) 159 lock_ctx->lock->expiration_date = 0; 160 else if (strncasecmp(cdata->data, "Second-", 7) == 0) 161 { 162 unsigned n; 163 SVN_ERR(svn_cstring_atoui(&n, cdata->data+7)); 164 165 lock_ctx->lock->expiration_date = apr_time_now() + 166 apr_time_from_sec(n); 167 } 168 else 169 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 170 _("Invalid LOCK timeout value '%s'"), 171 cdata->data); 172 } 173 else if (leaving_state == HREF) 174 { 175 if (cdata->len) 176 { 177 char *buf = apr_pstrmemdup(lock_ctx->pool, cdata->data, cdata->len); 178 179 apr_collapse_spaces(buf, buf); 180 lock_ctx->lock->token = buf; 181 } 182 } 183 else if (leaving_state == OWNER) 184 { 185 if (cdata->len) 186 { 187 lock_ctx->lock->comment = apr_pstrmemdup(lock_ctx->pool, 188 cdata->data, cdata->len); 189 } 190 } 191 192 return SVN_NO_ERROR; 193} 194 195/* Implements svn_ra_serf__response_handler_t */ 196static svn_error_t * 197handle_lock(serf_request_t *request, 198 serf_bucket_t *response, 199 void *handler_baton, 200 apr_pool_t *pool) 201{ 202 lock_info_t *ctx = handler_baton; 203 204 if (!ctx->read_headers) 205 { 206 serf_bucket_t *headers; 207 const char *val; 208 209 headers = serf_bucket_response_get_headers(response); 210 211 val = serf_bucket_headers_get(headers, SVN_DAV_LOCK_OWNER_HEADER); 212 if (val) 213 { 214 ctx->lock->owner = apr_pstrdup(ctx->pool, val); 215 } 216 217 val = serf_bucket_headers_get(headers, SVN_DAV_CREATIONDATE_HEADER); 218 if (val) 219 { 220 SVN_ERR(svn_time_from_cstring(&ctx->lock->creation_date, val, 221 ctx->pool)); 222 } 223 224 ctx->read_headers = TRUE; 225 } 226 227 return ctx->inner_handler(request, response, ctx->inner_baton, pool); 228} 229 230/* Implements svn_ra_serf__request_body_delegate_t */ 231static svn_error_t * 232create_getlock_body(serf_bucket_t **body_bkt, 233 void *baton, 234 serf_bucket_alloc_t *alloc, 235 apr_pool_t *pool /* request pool */, 236 apr_pool_t *scratch_pool) 237{ 238 serf_bucket_t *buckets; 239 240 buckets = serf_bucket_aggregate_create(alloc); 241 242 svn_ra_serf__add_xml_header_buckets(buckets, alloc); 243 svn_ra_serf__add_open_tag_buckets(buckets, alloc, "propfind", 244 "xmlns", "DAV:", 245 SVN_VA_NULL); 246 svn_ra_serf__add_open_tag_buckets(buckets, alloc, "prop", SVN_VA_NULL); 247 svn_ra_serf__add_empty_tag_buckets(buckets, alloc, 248 "lockdiscovery", SVN_VA_NULL); 249 svn_ra_serf__add_close_tag_buckets(buckets, alloc, "prop"); 250 svn_ra_serf__add_close_tag_buckets(buckets, alloc, "propfind"); 251 252 *body_bkt = buckets; 253 return SVN_NO_ERROR; 254} 255 256static svn_error_t* 257setup_getlock_headers(serf_bucket_t *headers, 258 void *baton, 259 apr_pool_t *pool /* request pool */, 260 apr_pool_t *scratch_pool) 261{ 262 serf_bucket_headers_setn(headers, "Depth", "0"); 263 264 return SVN_NO_ERROR; 265} 266 267svn_error_t * 268svn_ra_serf__get_lock(svn_ra_session_t *ra_session, 269 svn_lock_t **lock, 270 const char *path, 271 apr_pool_t *result_pool) 272{ 273 svn_ra_serf__session_t *session = ra_session->priv; 274 svn_ra_serf__handler_t *handler; 275 svn_ra_serf__xml_context_t *xmlctx; 276 apr_pool_t *scratch_pool = svn_pool_create(result_pool); 277 lock_info_t *lock_ctx; 278 const char *req_url; 279 svn_error_t *err; 280 281 req_url = svn_path_url_add_component2(session->session_url.path, path, 282 scratch_pool); 283 284 lock_ctx = apr_pcalloc(scratch_pool, sizeof(*lock_ctx)); 285 lock_ctx->pool = result_pool; 286 lock_ctx->path = req_url; 287 lock_ctx->lock = svn_lock_create(result_pool); 288 lock_ctx->lock->path = apr_pstrdup(result_pool, path); 289 290 xmlctx = svn_ra_serf__xml_context_create(locks_ttable, 291 NULL, locks_closed, NULL, 292 lock_ctx, 293 scratch_pool); 294 handler = svn_ra_serf__create_expat_handler(session, xmlctx, 295 locks_expected_status, 296 scratch_pool); 297 298 handler->method = "PROPFIND"; 299 handler->path = req_url; 300 handler->body_type = "text/xml"; 301 302 handler->body_delegate = create_getlock_body; 303 handler->body_delegate_baton = lock_ctx; 304 305 handler->header_delegate = setup_getlock_headers; 306 handler->header_delegate_baton = lock_ctx; 307 308 handler->no_dav_headers = TRUE; 309 310 lock_ctx->inner_handler = handler->response_handler; 311 lock_ctx->inner_baton = handler->response_baton; 312 handler->response_handler = handle_lock; 313 handler->response_baton = lock_ctx; 314 315 lock_ctx->handler = handler; 316 317 err = svn_ra_serf__context_run_one(handler, scratch_pool); 318 319 if ((err && (handler->sline.code == 500 || handler->sline.code == 501)) 320 || svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE)) 321 return svn_error_trace( 322 svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, 323 _("Server does not support locking features"))); 324 else if (svn_error_find_cause(err, SVN_ERR_FS_NOT_FOUND)) 325 svn_error_clear(err); /* Behave like the other RA layers */ 326 else if (handler->sline.code != 207) 327 return svn_error_trace(svn_ra_serf__unexpected_status(handler)); 328 329 if (lock_ctx->lock && lock_ctx->lock->token) 330 *lock = lock_ctx->lock; 331 else 332 *lock = NULL; 333 334 svn_pool_destroy(scratch_pool); 335 336 return SVN_NO_ERROR; 337} 338