Fix compiler warning due to missing function prototype.
[svn.git] / subversion / libsvn_ra_neon / lock.c
blob9df9009af2a23f6d6bf648cd956f3790725524ed
1 /*
2 * lock.c : routines for managing lock states in the DAV server
4 * ====================================================================
5 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #define APR_WANT_STRFUNC
22 #include <apr_want.h>
24 #include "svn_error.h"
25 #include "svn_pools.h"
26 #include "svn_ra.h"
27 #include "../libsvn_ra/ra_loader.h"
28 #include "svn_path.h"
29 #include "svn_time.h"
30 #include "svn_private_config.h"
32 #include "ra_neon.h"
34 static const svn_ra_neon__xml_elm_t lock_elements[] =
36 /* lockdiscovery elements */
37 { "DAV:", "response", ELEM_response, 0 },
38 { "DAV:", "propstat", ELEM_propstat, 0 },
39 { "DAV:", "status", ELEM_status, SVN_RA_NEON__XML_CDATA },
40 /* extend lockdiscovery elements here;
41 ### Remember to update do_lock() when you change the number of
42 elements here: it contains a hard reference to the next element. */
44 /* lock and lockdiscovery elements */
45 { "DAV:", "prop", ELEM_prop, 0 },
46 { "DAV:", "lockdiscovery", ELEM_lock_discovery, 0 },
47 { "DAV:", "activelock", ELEM_lock_activelock, 0 },
48 { "DAV:", "locktype", ELEM_lock_type, SVN_RA_NEON__XML_CDATA },
49 { "DAV:", "lockscope", ELEM_lock_scope, SVN_RA_NEON__XML_CDATA },
50 { "DAV:", "depth", ELEM_lock_depth, SVN_RA_NEON__XML_CDATA },
51 { "DAV:", "owner", ELEM_lock_owner, SVN_RA_NEON__XML_COLLECT },
52 { "DAV:", "timeout", ELEM_lock_timeout, SVN_RA_NEON__XML_CDATA },
53 { "DAV:", "locktoken", ELEM_lock_token, 0 },
54 { "DAV:", "href", ELEM_href, SVN_RA_NEON__XML_CDATA },
55 { "", "", ELEM_unknown, SVN_RA_NEON__XML_COLLECT },
56 /* extend lock elements here */
58 { NULL }
61 typedef struct
63 svn_stringbuf_t *cdata;
64 apr_pool_t *pool;
65 const svn_ra_neon__xml_elm_t *xml_table;
67 /* lockdiscovery fields */
68 svn_stringbuf_t *href;
69 svn_stringbuf_t *status_line;
71 /* lock and lockdiscovery fields */
72 int parent;
73 svn_stringbuf_t *owner;
74 svn_stringbuf_t *timeout;
75 svn_stringbuf_t *depth;
76 svn_stringbuf_t *token;
77 } lock_baton_t;
79 static svn_error_t *
80 lock_start_element(int *elem, void *baton, int parent,
81 const char *nspace, const char *name, const char **atts)
83 lock_baton_t *b = baton;
84 const svn_ra_neon__xml_elm_t *elm =
85 svn_ra_neon__lookup_xml_elem(b->xml_table, nspace, name);
87 if (! elm)
89 *elem = NE_XML_DECLINE;
90 return SVN_NO_ERROR;
93 /* collect interesting element contents */
94 /* owner, href inside locktoken, depth, timeout */
95 switch (elm->id)
97 case ELEM_lock_owner:
98 case ELEM_lock_timeout:
99 case ELEM_lock_depth:
100 case ELEM_status:
101 b->cdata = svn_stringbuf_create("", b->pool);
102 break;
104 case ELEM_href:
105 if (parent == ELEM_lock_token
106 || parent == ELEM_response)
107 b->cdata = svn_stringbuf_create("", b->pool);
108 break;
110 default:
111 b->cdata = NULL;
114 b->parent = parent;
115 *elem = elm->id;
116 return SVN_NO_ERROR;
119 static svn_error_t *
120 lock_end_element(void *baton, int state, const char *nspace, const char *name)
122 lock_baton_t *b = baton;
124 if (b->cdata)
125 switch (state)
127 case ELEM_lock_owner:
128 b->owner = b->cdata;
129 break;
131 case ELEM_lock_timeout:
132 b->timeout = b->cdata;
133 break;
135 case ELEM_lock_depth:
136 b->depth = b->cdata;
137 break;
139 case ELEM_href:
140 if (b->parent == ELEM_lock_token)
141 b->token = b->cdata;
142 else
143 b->href = b->cdata;
144 break;
146 case ELEM_status:
147 b->status_line = b->cdata;
148 break;
151 b->cdata = NULL;
153 return SVN_NO_ERROR;
156 static svn_error_t *
157 lock_cdata(void *baton, int state, const char *cdata, size_t len)
159 lock_baton_t *b = baton;
161 if (b->cdata)
162 svn_stringbuf_appendbytes(b->cdata, cdata, len);
164 return SVN_NO_ERROR;
168 static svn_error_t *
169 lock_from_baton(svn_lock_t **lock,
170 svn_ra_neon__request_t *req,
171 const char *path,
172 lock_baton_t *lrb, apr_pool_t *pool)
174 const char *val;
175 svn_lock_t *lck = svn_lock_create(pool);
177 if (lrb->token)
178 lck->token = lrb->token->data;
179 else
181 /* No lock */
182 *lock = NULL;
183 return SVN_NO_ERROR;
186 val = ne_get_response_header(req->ne_req, SVN_DAV_CREATIONDATE_HEADER);
187 if (val)
188 SVN_ERR_W(svn_time_from_cstring(&(lck->creation_date), val, pool),
189 _("Invalid creation date header value in response."));
191 val = ne_get_response_header(req->ne_req, SVN_DAV_LOCK_OWNER_HEADER);
192 if (val)
193 lck->owner = apr_pstrdup(pool, val);
194 if (lrb->owner)
195 lck->comment = lrb->owner->data;
196 if (path)
197 lck->path = path;
198 if (lrb->timeout)
200 const char *timeout_str = lrb->timeout->data;
202 if (strcmp(timeout_str, "Infinite") != 0)
204 if (strncmp("Second-", timeout_str, strlen("Second-")) == 0)
206 int time_offset = atoi(&(timeout_str[7]));
208 lck->expiration_date = lck->creation_date
209 + apr_time_from_sec(time_offset);
211 else
212 return svn_error_create(SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS,
213 NULL, _("Invalid timeout value"));
215 else
216 lck->expiration_date = 0;
218 *lock = lck;
220 return SVN_NO_ERROR;
223 static svn_error_t *
224 do_lock(svn_lock_t **lock,
225 svn_ra_session_t *session,
226 const char *path,
227 const char *comment,
228 svn_boolean_t force,
229 svn_revnum_t current_rev,
230 apr_pool_t *pool)
232 svn_ra_neon__request_t *req;
233 svn_stringbuf_t *body;
234 ne_uri uri;
235 int code;
236 const char *url;
237 svn_string_t fs_path;
238 ne_xml_parser *lck_parser;
239 svn_ra_neon__session_t *ras = session->priv;
240 lock_baton_t *lrb = apr_pcalloc(pool, sizeof(*lrb));
241 apr_hash_t *extra_headers;
242 svn_error_t *err = SVN_NO_ERROR;
244 /* To begin, we convert the incoming path into an absolute fs-path. */
245 url = svn_path_url_add_component(ras->url->data, path, pool);
246 SVN_ERR(svn_ra_neon__get_baseline_info(NULL, NULL, &fs_path, NULL, ras,
247 url, SVN_INVALID_REVNUM, pool));
249 if (ne_uri_parse(url, &uri) != 0)
251 ne_uri_free(&uri);
252 return svn_error_createf(SVN_ERR_RA_DAV_CREATING_REQUEST, NULL,
253 _("Failed to parse URI '%s'"), url);
256 req = svn_ra_neon__request_create(ras, "LOCK", uri.path, pool);
257 ne_uri_free(&uri);
259 lrb->pool = pool;
260 lrb->xml_table = &(lock_elements[3]);
261 lck_parser = svn_ra_neon__xml_parser_create
262 (req, ne_accept_2xx,
263 lock_start_element, lock_cdata, lock_end_element, lrb);
265 body = svn_stringbuf_createf
266 (req->pool,
267 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" DEBUG_CR
268 "<D:lockinfo xmlns:D=\"DAV:\">" DEBUG_CR
269 " <D:lockscope><D:exclusive /></D:lockscope>" DEBUG_CR
270 " <D:locktype><D:write /></D:locktype>" DEBUG_CR
271 "%s" /* maybe owner */
272 "</D:lockinfo>",
273 (comment
274 ? apr_psprintf(req->pool, " <D:owner>%s</D:owner>" DEBUG_CR, comment)
275 : ""));
277 extra_headers = apr_hash_make(req->pool);
278 svn_ra_neon__set_header(extra_headers, "Depth", "0");
279 svn_ra_neon__set_header(extra_headers, "Timeout", "Infinite");
280 svn_ra_neon__set_header(extra_headers, "Content-Type",
281 "text/xml; charset=\"utf-8\"");
282 if (force)
283 svn_ra_neon__set_header(extra_headers, SVN_DAV_OPTIONS_HEADER,
284 SVN_DAV_OPTION_LOCK_STEAL);
285 if (SVN_IS_VALID_REVNUM(current_rev))
286 svn_ra_neon__set_header(extra_headers, SVN_DAV_VERSION_NAME_HEADER,
287 apr_psprintf(req->pool, "%ld", current_rev));
289 err = svn_ra_neon__request_dispatch(&code, req, extra_headers, body->data,
290 200, 0, pool);
291 if (err)
292 goto cleanup;
294 /*###FIXME: we never verified whether we have received back the type
295 of lock we requested: was it shared/exclusive? was it write/otherwise?
296 How many did we get back? Only one? */
297 err = lock_from_baton(lock, req, fs_path.data, lrb, pool);
299 cleanup:
300 svn_ra_neon__request_destroy(req);
301 return err;
304 svn_error_t *
305 svn_ra_neon__lock(svn_ra_session_t *session,
306 apr_hash_t *path_revs,
307 const char *comment,
308 svn_boolean_t force,
309 svn_ra_lock_callback_t lock_func,
310 void *lock_baton,
311 apr_pool_t *pool)
313 apr_hash_index_t *hi;
314 apr_pool_t *iterpool = svn_pool_create(pool);
315 svn_ra_neon__session_t *ras = session->priv;
316 svn_error_t *ret_err = NULL;
318 /* ### TODO for issue 2263: Send all the locks over the wire at once. This
319 loop is just a temporary shim. */
320 for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
322 svn_lock_t *lock;
323 const void *key;
324 const char *path;
325 void *val;
326 svn_revnum_t *revnum;
327 svn_error_t *err, *callback_err = NULL;
329 svn_pool_clear(iterpool);
331 apr_hash_this(hi, &key, NULL, &val);
332 path = key;
333 revnum = val;
335 err = do_lock(&lock, session, path, comment, force, *revnum, iterpool);
337 if (err && !SVN_ERR_IS_LOCK_ERROR(err))
339 ret_err = err;
340 goto departure;
343 if (lock_func)
344 callback_err = lock_func(lock_baton, path, TRUE, err ? NULL : lock,
345 err, iterpool);
347 svn_error_clear(err);
349 if (callback_err)
351 ret_err = callback_err;
352 goto departure;
357 svn_pool_destroy(iterpool);
359 departure:
360 return svn_ra_neon__maybe_store_auth_info_after_result(ret_err, ras, pool);
364 /* ###TODO for issue 2263: Send all lock tokens to the server at once. */
365 static svn_error_t *
366 do_unlock(svn_ra_session_t *session,
367 const char *path,
368 const char *token,
369 svn_boolean_t force,
370 apr_pool_t *pool)
372 svn_ra_neon__session_t *ras = session->priv;
373 const char *url;
374 const char *url_path;
375 ne_uri uri;
377 apr_hash_t *extra_headers = apr_hash_make(pool);
379 /* Make a neon lock structure containing token and full URL to unlock. */
380 url = svn_path_url_add_component(ras->url->data, path, pool);
381 if (ne_uri_parse(url, &uri) != 0)
383 ne_uri_free(&uri);
384 return svn_error_createf(SVN_ERR_RA_DAV_CREATING_REQUEST, NULL,
385 _("Failed to parse URI '%s'"), url);
388 url_path = apr_pstrdup(pool, uri.path);
389 ne_uri_free(&uri);
390 /* In the case of 'force', we might not have a token at all.
391 Unfortunately, mod_dav insists on having a valid token for
392 UNLOCK requests. That means we need to fetch the token. */
393 if (! token)
395 svn_lock_t *lock;
397 SVN_ERR(svn_ra_neon__get_lock(session, &lock, path, pool));
398 if (! lock)
399 return svn_error_createf(SVN_ERR_RA_NOT_LOCKED, NULL,
400 _("'%s' is not locked in the repository"),
401 path);
402 token = lock->token;
407 apr_hash_set(extra_headers, "Lock-Token", APR_HASH_KEY_STRING,
408 apr_psprintf(pool, "<%s>", token));
409 if (force)
410 apr_hash_set(extra_headers, SVN_DAV_OPTIONS_HEADER, APR_HASH_KEY_STRING,
411 SVN_DAV_OPTION_LOCK_BREAK);
413 return svn_ra_neon__simple_request(NULL, ras, "UNLOCK", url_path,
414 extra_headers, NULL, 204, 0, pool);
418 svn_error_t *
419 svn_ra_neon__unlock(svn_ra_session_t *session,
420 apr_hash_t *path_tokens,
421 svn_boolean_t force,
422 svn_ra_lock_callback_t lock_func,
423 void *lock_baton,
424 apr_pool_t *pool)
426 apr_hash_index_t *hi;
427 apr_pool_t *iterpool = svn_pool_create(pool);
428 svn_ra_neon__session_t *ras = session->priv;
429 svn_error_t *ret_err = NULL;
431 /* ### TODO for issue 2263: Send all the lock tokens over the wire at once.
432 This loop is just a temporary shim. */
433 for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
435 const void *key;
436 const char *path;
437 void *val;
438 const char *token;
439 svn_error_t *err, *callback_err = NULL;
441 svn_pool_clear(iterpool);
443 apr_hash_this(hi, &key, NULL, &val);
444 path = key;
445 /* Since we can't store NULL values in a hash, we turn "" to
446 NULL here. */
447 if (strcmp(val, "") != 0)
448 token = val;
449 else
450 token = NULL;
452 err = do_unlock(session, path, token, force, iterpool);
454 if (err && !SVN_ERR_IS_UNLOCK_ERROR(err))
456 ret_err = err;
457 goto departure;
460 if (lock_func)
461 callback_err = lock_func(lock_baton, path, FALSE, NULL, err, iterpool);
463 svn_error_clear(err);
465 if (callback_err)
467 ret_err = callback_err;
468 goto departure;
472 svn_pool_destroy(iterpool);
474 departure:
475 return svn_ra_neon__maybe_store_auth_info_after_result(ret_err, ras, pool);
479 svn_error_t *
480 svn_ra_neon__get_lock_internal(svn_ra_neon__session_t *ras,
481 svn_lock_t **lock,
482 const char *path,
483 apr_pool_t *pool)
485 const char *url;
486 svn_string_t fs_path;
487 svn_error_t *err;
488 ne_uri uri;
489 lock_baton_t *lrb = apr_pcalloc(pool, sizeof(*lrb));
490 svn_ra_neon__request_t *req;
491 ne_xml_parser *lck_parser;
492 apr_hash_t *extra_headers;
493 static const char *body =
494 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" DEBUG_CR
495 "<D:propfind xmlns:D=\"DAV:\">" DEBUG_CR
496 " <D:prop>" DEBUG_CR
497 " <D:lockdiscovery />" DEBUG_CR
498 " </D:prop>" DEBUG_CR
499 "</D:propfind>";
501 /* To begin, we convert the incoming path into an absolute fs-path. */
502 url = svn_path_url_add_component(ras->url->data, path, pool);
504 err = svn_ra_neon__get_baseline_info(NULL, NULL, &fs_path, NULL, ras,
505 url, SVN_INVALID_REVNUM, pool);
506 SVN_ERR(svn_ra_neon__maybe_store_auth_info_after_result(err, ras, pool));
508 ne_uri_parse(url, &uri);
509 url = apr_pstrdup(pool, uri.path);
510 ne_uri_free(&uri);
512 req = svn_ra_neon__request_create(ras, "PROPFIND", url, pool);
514 lrb->pool = pool;
515 lrb->xml_table = lock_elements;
516 lck_parser = svn_ra_neon__xml_parser_create
517 (req, ne_accept_207, lock_start_element, lock_cdata, lock_end_element, lrb);
519 extra_headers = apr_hash_make(req->pool);
520 svn_ra_neon__set_header(extra_headers, "Depth", "0");
521 svn_ra_neon__set_header(extra_headers, "Content-Type",
522 "text/xml; charset=\"utf-8\"");
524 err = svn_ra_neon__request_dispatch(NULL, req, extra_headers, body,
525 200, 207, pool);
526 if (err)
528 err = svn_error_quick_wrap(err, _("Failed to fetch lock information"));
529 goto cleanup;
532 /*###FIXME We assume here we only got one lock response. The WebDAV
533 spec makes no such guarantees. How to make sure we grab the one we need? */
534 err = lock_from_baton(lock, req, fs_path.data, lrb, pool);
536 cleanup:
537 svn_ra_neon__request_destroy(req);
538 return err;
542 svn_error_t *
543 svn_ra_neon__get_lock(svn_ra_session_t *session,
544 svn_lock_t **lock,
545 const char *path,
546 apr_pool_t *pool)
548 return svn_ra_neon__get_lock_internal(session->priv, lock, path, pool);