1 /*
2    WebDAV Class 2 locking operations
3    Copyright (C) 1999-2006, Joe Orton <joe@manyfish.co.uk>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18    MA 02111-1307, USA
19 
20 */
21 
22 #include "config.h"
23 
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31 
32 #ifdef HAVE_LIMITS_H
33 #include <limits.h>
34 #endif
35 
36 #include <ctype.h> /* for isdigit() */
37 
38 #include "ne_alloc.h"
39 
40 #include "ne_request.h"
41 #include "ne_xml.h"
42 #include "ne_locks.h"
43 #include "ne_uri.h"
44 #include "ne_basic.h"
45 #include "ne_props.h"
46 #include "ne_207.h"
47 #include "ne_internal.h"
48 #include "ne_xmlreq.h"
49 
50 #define HOOK_ID "http://webdav.org/neon/hooks/webdav-locking"
51 
52 /* A list of lock objects. */
53 struct lock_list {
54     struct ne_lock *lock;
55     struct lock_list *next, *prev;
56 };
57 
58 struct ne_lock_store_s {
59     struct lock_list *locks;
60     struct lock_list *cursor; /* current position in 'locks' */
61 };
62 
63 struct lh_req_cookie {
64     const ne_lock_store *store;
65     struct lock_list *submit;
66 };
67 
68 /* Context for PROPFIND/lockdiscovery callbacks */
69 struct discover_ctx {
70     ne_propfind_handler *phandler;
71     ne_lock_result results;
72     void *userdata;
73     ne_buffer *cdata;
74 };
75 
76 /* Context for handling LOCK response */
77 struct lock_ctx {
78     struct ne_lock active; /* activelock */
79     ne_request *req; /* the request in question */
80     ne_xml_parser *parser;
81     char *token; /* the token we're after. */
82     int found;
83     ne_buffer *cdata;
84 };
85 
86 /* use the "application" state space. */
87 #define ELM_LOCK_FIRST (NE_PROPS_STATE_TOP + 66)
88 
89 #define ELM_lockdiscovery (ELM_LOCK_FIRST)
90 #define ELM_activelock (ELM_LOCK_FIRST + 1)
91 #define ELM_lockscope (ELM_LOCK_FIRST + 2)
92 #define ELM_locktype (ELM_LOCK_FIRST + 3)
93 #define ELM_depth (ELM_LOCK_FIRST + 4)
94 #define ELM_owner (ELM_LOCK_FIRST + 5)
95 #define ELM_timeout (ELM_LOCK_FIRST + 6)
96 #define ELM_locktoken (ELM_LOCK_FIRST + 7)
97 #define ELM_lockinfo (ELM_LOCK_FIRST + 8)
98 #define ELM_write (ELM_LOCK_FIRST + 9)
99 #define ELM_exclusive (ELM_LOCK_FIRST + 10)
100 #define ELM_shared (ELM_LOCK_FIRST + 11)
101 #define ELM_href (ELM_LOCK_FIRST + 12)
102 #define ELM_prop (NE_207_STATE_PROP)
103 
104 static const struct ne_xml_idmap element_map[] = {
105 #define ELM(x) { "DAV:", #x, ELM_ ## x }
106     ELM(lockdiscovery), ELM(activelock), ELM(prop), ELM(lockscope),
107     ELM(locktype), ELM(depth), ELM(owner), ELM(timeout), ELM(locktoken),
108     ELM(lockinfo), ELM(lockscope), ELM(locktype), ELM(write), ELM(exclusive),
109     ELM(shared), ELM(href)
110     /* no "lockentry" */
111 #undef ELM
112 };
113 
114 static const ne_propname lock_props[] = {
115     { "DAV:", "lockdiscovery" },
116     { NULL }
117 };
118 
119 /* this simply registers the accessor for the function. */
lk_create(ne_request * req,void * session,const char * method,const char * uri)120 static void lk_create(ne_request *req, void *session,
121 		       const char *method, const char *uri)
122 {
123     struct lh_req_cookie *lrc = ne_malloc(sizeof *lrc);
124     lrc->store = session;
125     lrc->submit = NULL;
126     ne_set_request_private(req, HOOK_ID, lrc);
127 }
128 
lk_pre_send(ne_request * r,void * userdata,ne_buffer * req)129 static void lk_pre_send(ne_request *r, void *userdata, ne_buffer *req)
130 {
131     struct lh_req_cookie *lrc = ne_get_request_private(r, HOOK_ID);
132 
133     if (lrc->submit != NULL) {
134 	struct lock_list *item;
135 
136 	/* Add in the If header */
137 	ne_buffer_czappend(req, "If:");
138 	for (item = lrc->submit; item != NULL; item = item->next) {
139 	    char *uri = ne_uri_unparse(&item->lock->uri);
140 	    ne_buffer_concat(req, " <", uri, "> (<",
141 			     item->lock->token, ">)", NULL);
142 	    ne_free(uri);
143 	}
144 	ne_buffer_czappend(req, "\r\n");
145     }
146 }
147 
148 /* Insert 'lock' into lock list *list. */
insert_lock(struct lock_list ** list,struct ne_lock * lock)149 static void insert_lock(struct lock_list **list, struct ne_lock *lock)
150 {
151     struct lock_list *item = ne_malloc(sizeof *item);
152     if (*list != NULL) {
153 	(*list)->prev = item;
154     }
155     item->prev = NULL;
156     item->next = *list;
157     item->lock = lock;
158     *list = item;
159 }
160 
free_list(struct lock_list * list,int destroy)161 static void free_list(struct lock_list *list, int destroy)
162 {
163     struct lock_list *next;
164 
165     while (list != NULL) {
166 	next = list->next;
167 	if (destroy)
168 	    ne_lock_destroy(list->lock);
169 	ne_free(list);
170 	list = next;
171     }
172 }
173 
lk_destroy(ne_request * req,void * userdata)174 static void lk_destroy(ne_request *req, void *userdata)
175 {
176     struct lh_req_cookie *lrc = ne_get_request_private(req, HOOK_ID);
177     free_list(lrc->submit, 0);
178     ne_free(lrc);
179 }
180 
ne_lockstore_destroy(ne_lock_store * store)181 void ne_lockstore_destroy(ne_lock_store *store)
182 {
183     free_list(store->locks, 1);
184     ne_free(store);
185 }
186 
ne_lockstore_create(void)187 ne_lock_store *ne_lockstore_create(void)
188 {
189     return ne_calloc(sizeof(ne_lock_store));
190 }
191 
192 #define CURSOR_RET(s) ((s)->cursor?(s)->cursor->lock:NULL)
193 
ne_lockstore_first(ne_lock_store * store)194 struct ne_lock *ne_lockstore_first(ne_lock_store *store)
195 {
196     store->cursor = store->locks;
197     return CURSOR_RET(store);
198 }
199 
ne_lockstore_next(ne_lock_store * store)200 struct ne_lock *ne_lockstore_next(ne_lock_store *store)
201 {
202     store->cursor = store->cursor->next;
203     return CURSOR_RET(store);
204 }
205 
ne_lockstore_register(ne_lock_store * store,ne_session * sess)206 void ne_lockstore_register(ne_lock_store *store, ne_session *sess)
207 {
208     /* Register the hooks */
209     ne_hook_create_request(sess, lk_create, store);
210     ne_hook_pre_send(sess, lk_pre_send, store);
211     ne_hook_destroy_request(sess, lk_destroy, store);
212 }
213 
214 /* Submit the given lock for the given URI */
submit_lock(struct lh_req_cookie * lrc,struct ne_lock * lock)215 static void submit_lock(struct lh_req_cookie *lrc, struct ne_lock *lock)
216 {
217     struct lock_list *item;
218 
219     /* Check for dups */
220     for (item = lrc->submit; item != NULL; item = item->next) {
221 	if (ne_strcasecmp(item->lock->token, lock->token) == 0)
222 	    return;
223     }
224 
225     insert_lock(&lrc->submit, lock);
226 }
227 
ne_lockstore_findbyuri(ne_lock_store * store,const ne_uri * uri)228 struct ne_lock *ne_lockstore_findbyuri(ne_lock_store *store,
229 				       const ne_uri *uri)
230 {
231     struct lock_list *cur;
232 
233     for (cur = store->locks; cur != NULL; cur = cur->next) {
234 	if (ne_uri_cmp(&cur->lock->uri, uri) == 0) {
235 	    return cur->lock;
236 	}
237     }
238 
239     return NULL;
240 }
241 
ne_lock_using_parent(ne_request * req,const char * path)242 void ne_lock_using_parent(ne_request *req, const char *path)
243 {
244     struct lh_req_cookie *lrc = ne_get_request_private(req, HOOK_ID);
245     ne_uri u = {0};
246     struct lock_list *item;
247     char *parent;
248 
249     if (lrc == NULL)
250 	return;
251 
252     parent = ne_path_parent(path);
253     if (parent == NULL)
254 	return;
255 
256     ne_fill_server_uri(ne_get_session(req), &u);
257 
258     for (item = lrc->store->locks; item != NULL; item = item->next) {
259 
260 	/* Only care about locks which are on this server. */
261 	u.path = item->lock->uri.path;
262 	if (ne_uri_cmp(&u, &item->lock->uri))
263 	    continue;
264 
265 	/* This lock is needed if it is an infinite depth lock which
266 	 * covers the parent, or a lock on the parent itself. */
267 	if ((item->lock->depth == NE_DEPTH_INFINITE &&
268 	     ne_path_childof(item->lock->uri.path, parent)) ||
269 	    ne_path_compare(item->lock->uri.path, parent) == 0) {
270 	    NE_DEBUG(NE_DBG_LOCKS, "Locked parent, %s on %s\n",
271 		     item->lock->token, item->lock->uri.path);
272 	    submit_lock(lrc, item->lock);
273 	}
274     }
275 
276     u.path = parent; /* handy: makes u.path valid and ne_free(parent). */
277     ne_uri_free(&u);
278 }
279 
ne_lock_using_resource(ne_request * req,const char * uri,int depth)280 void ne_lock_using_resource(ne_request *req, const char *uri, int depth)
281 {
282     struct lh_req_cookie *lrc = ne_get_request_private(req, HOOK_ID);
283     struct lock_list *item;
284     int match;
285 
286     if (lrc == NULL)
287 	return;
288 
289     /* Iterate over the list of stored locks to see if any of them
290      * apply to this resource */
291     for (item = lrc->store->locks; item != NULL; item = item->next) {
292 
293 	match = 0;
294 
295 	if (depth == NE_DEPTH_INFINITE &&
296 	    ne_path_childof(uri, item->lock->uri.path)) {
297 	    /* Case 1: this is a depth-infinity request which will
298 	     * modify a lock somewhere inside the collection. */
299 	    NE_DEBUG(NE_DBG_LOCKS, "Has child: %s\n", item->lock->token);
300 	    match = 1;
301 	}
302 	else if (ne_path_compare(uri, item->lock->uri.path) == 0) {
303 	    /* Case 2: this request is directly on a locked resource */
304 	    NE_DEBUG(NE_DBG_LOCKS, "Has direct lock: %s\n", item->lock->token);
305 	    match = 1;
306 	}
307 	else if (item->lock->depth == NE_DEPTH_INFINITE &&
308 		 ne_path_childof(item->lock->uri.path, uri)) {
309 	    /* Case 3: there is a higher-up infinite-depth lock which
310 	     * covers the resource that this request will modify. */
311 	    NE_DEBUG(NE_DBG_LOCKS, "Is child of: %s\n", item->lock->token);
312 	    match = 1;
313 	}
314 
315 	if (match) {
316 	    submit_lock(lrc, item->lock);
317 	}
318     }
319 
320 }
321 
ne_lockstore_add(ne_lock_store * store,struct ne_lock * lock)322 void ne_lockstore_add(ne_lock_store *store, struct ne_lock *lock)
323 {
324     insert_lock(&store->locks, lock);
325 }
326 
ne_lockstore_remove(ne_lock_store * store,struct ne_lock * lock)327 void ne_lockstore_remove(ne_lock_store *store, struct ne_lock *lock)
328 {
329     struct lock_list *item;
330 
331     /* Find the lock */
332     for (item = store->locks; item != NULL; item = item->next)
333 	if (item->lock == lock)
334 	    break;
335 
336     if (item->prev != NULL) {
337 	item->prev->next = item->next;
338     } else {
339 	store->locks = item->next;
340     }
341     if (item->next != NULL) {
342 	item->next->prev = item->prev;
343     }
344     ne_free(item);
345 }
346 
ne_lock_copy(const struct ne_lock * lock)347 struct ne_lock *ne_lock_copy(const struct ne_lock *lock)
348 {
349     struct ne_lock *ret = ne_calloc(sizeof *ret);
350 
351     ne_uri_copy(&ret->uri, &lock->uri);
352     ret->token = ne_strdup(lock->token);
353     ret->depth = lock->depth;
354     ret->type = lock->type;
355     ret->scope = lock->scope;
356     if (lock->owner) ret->owner = ne_strdup(lock->owner);
357     ret->timeout = lock->timeout;
358 
359     return ret;
360 }
361 
ne_lock_create(void)362 struct ne_lock *ne_lock_create(void)
363 {
364     struct ne_lock *lock = ne_calloc(sizeof *lock);
365     lock->depth = NE_DEPTH_ZERO;
366     lock->type = ne_locktype_write;
367     lock->scope = ne_lockscope_exclusive;
368     lock->timeout = NE_TIMEOUT_INVALID;
369     return lock;
370 }
371 
ne_lock_free(struct ne_lock * lock)372 void ne_lock_free(struct ne_lock *lock)
373 {
374     ne_uri_free(&lock->uri);
375     if (lock->owner) {
376         ne_free(lock->owner);
377         lock->owner = NULL;
378     }
379     if (lock->token) {
380         ne_free(lock->token);
381         lock->token = NULL;
382     }
383 }
384 
ne_lock_destroy(struct ne_lock * lock)385 void ne_lock_destroy(struct ne_lock *lock)
386 {
387     ne_lock_free(lock);
388     ne_free(lock);
389 }
390 
ne_unlock(ne_session * sess,const struct ne_lock * lock)391 int ne_unlock(ne_session *sess, const struct ne_lock *lock)
392 {
393     ne_request *req = ne_request_create(sess, "UNLOCK", lock->uri.path);
394     int ret;
395 
396     ne_print_request_header(req, "Lock-Token", "<%s>", lock->token);
397 
398     /* UNLOCK of a lock-null resource removes the resource from the
399      * parent collection; so an UNLOCK may modify the parent
400      * collection. (somewhat counter-intuitive, and not easily derived
401      * from 2518.) */
402     ne_lock_using_parent(req, lock->uri.path);
403 
404     ret = ne_request_dispatch(req);
405 
406     if (ret == NE_OK && ne_get_status(req)->klass != 2) {
407 	ret = NE_ERROR;
408     }
409 
410     ne_request_destroy(req);
411 
412     return ret;
413 }
414 
parse_depth(const char * depth)415 static int parse_depth(const char *depth)
416 {
417     if (ne_strcasecmp(depth, "infinity") == 0) {
418 	return NE_DEPTH_INFINITE;
419     } else if (isdigit(depth[0])) {
420 	return atoi(depth);
421     } else {
422 	return -1;
423     }
424 }
425 
parse_timeout(const char * timeout)426 static long parse_timeout(const char *timeout)
427 {
428     if (ne_strcasecmp(timeout, "infinite") == 0) {
429 	return NE_TIMEOUT_INFINITE;
430     } else if (strncasecmp(timeout, "Second-", 7) == 0) {
431 	long to = strtol(timeout+7, NULL, 10);
432 	if (to == LONG_MIN || to == LONG_MAX)
433 	    return NE_TIMEOUT_INVALID;
434 	return to;
435     } else {
436 	return NE_TIMEOUT_INVALID;
437     }
438 }
439 
discover_results(void * userdata,const ne_uri * uri,const ne_prop_result_set * set)440 static void discover_results(void *userdata, const ne_uri *uri,
441 			     const ne_prop_result_set *set)
442 {
443     struct discover_ctx *ctx = userdata;
444     struct ne_lock *lock = ne_propset_private(set);
445     const ne_status *status = ne_propset_status(set, &lock_props[0]);
446 
447     /* Require at least that the lock has a token. */
448     if (lock->token) {
449 	if (status && status->klass != 2) {
450 	    ctx->results(ctx->userdata, NULL, uri, status);
451 	} else {
452 	    ctx->results(ctx->userdata, lock, uri, NULL);
453 	}
454     }
455     else if (status) {
456 	ctx->results(ctx->userdata, NULL, uri, status);
457     }
458 
459     NE_DEBUG(NE_DBG_LOCKS, "End of response for %s\n", uri->path);
460 }
461 
462 static int
end_element_common(struct ne_lock * l,int state,const char * cdata)463 end_element_common(struct ne_lock *l, int state, const char *cdata)
464 {
465     switch (state) {
466     case ELM_write:
467 	l->type = ne_locktype_write;
468 	break;
469     case ELM_exclusive:
470 	l->scope = ne_lockscope_exclusive;
471 	break;
472     case ELM_shared:
473 	l->scope = ne_lockscope_shared;
474 	break;
475     case ELM_depth:
476 	NE_DEBUG(NE_DBG_LOCKS, "Got depth: %s\n", cdata);
477 	l->depth = parse_depth(cdata);
478 	if (l->depth == -1) {
479 	    return -1;
480 	}
481 	break;
482     case ELM_timeout:
483 	NE_DEBUG(NE_DBG_LOCKS, "Got timeout: %s\n", cdata);
484 	l->timeout = parse_timeout(cdata);
485 	if (l->timeout == NE_TIMEOUT_INVALID) {
486 	    return -1;
487 	}
488 	break;
489     case ELM_owner:
490 	l->owner = strdup(cdata);
491 	break;
492     case ELM_href:
493 	l->token = strdup(cdata);
494 	break;
495     }
496     return 0;
497 }
498 
499 /* End-element handler for lock discovery PROPFIND response */
end_element_ldisc(void * userdata,int state,const char * nspace,const char * name)500 static int end_element_ldisc(void *userdata, int state,
501                              const char *nspace, const char *name)
502 {
503     struct discover_ctx *ctx = userdata;
504     struct ne_lock *lock = ne_propfind_current_private(ctx->phandler);
505 
506     return end_element_common(lock, state, ctx->cdata->data);
507 }
508 
can_accept(int parent,int id)509 static inline int can_accept(int parent, int id)
510 {
511     return (parent == NE_XML_STATEROOT && id == ELM_prop) ||
512         (parent == ELM_prop && id == ELM_lockdiscovery) ||
513         (parent == ELM_lockdiscovery && id == ELM_activelock) ||
514         (parent == ELM_activelock &&
515          (id == ELM_lockscope || id == ELM_locktype ||
516           id == ELM_depth || id == ELM_owner ||
517           id == ELM_timeout || id == ELM_locktoken)) ||
518         (parent == ELM_lockscope &&
519          (id == ELM_exclusive || id == ELM_shared)) ||
520         (parent == ELM_locktype && id == ELM_write) ||
521         (parent == ELM_locktoken && id == ELM_href);
522 }
523 
ld_startelm(void * userdata,int parent,const char * nspace,const char * name,const char ** atts)524 static int ld_startelm(void *userdata, int parent,
525                        const char *nspace, const char *name,
526 		       const char **atts)
527 {
528     struct discover_ctx *ctx = userdata;
529     int id = ne_xml_mapid(element_map, NE_XML_MAPLEN(element_map),
530                           nspace, name);
531 
532     ne_buffer_clear(ctx->cdata);
533 
534     if (can_accept(parent, id))
535         return id;
536     else
537         return NE_XML_DECLINE;
538 }
539 
540 #define MAX_CDATA (256)
541 
lk_cdata(void * userdata,int state,const char * cdata,size_t len)542 static int lk_cdata(void *userdata, int state,
543                     const char *cdata, size_t len)
544 {
545     struct lock_ctx *ctx = userdata;
546 
547     if (ctx->cdata->used + len < MAX_CDATA)
548         ne_buffer_append(ctx->cdata, cdata, len);
549 
550     return 0;
551 }
552 
ld_cdata(void * userdata,int state,const char * cdata,size_t len)553 static int ld_cdata(void *userdata, int state,
554                     const char *cdata, size_t len)
555 {
556     struct discover_ctx *ctx = userdata;
557 
558     if (ctx->cdata->used + len < MAX_CDATA)
559         ne_buffer_append(ctx->cdata, cdata, len);
560 
561     return 0;
562 }
563 
lk_startelm(void * userdata,int parent,const char * nspace,const char * name,const char ** atts)564 static int lk_startelm(void *userdata, int parent,
565                        const char *nspace, const char *name,
566 		       const char **atts)
567 {
568     struct lock_ctx *ctx = userdata;
569     int id;
570 
571     id = ne_xml_mapid(element_map, NE_XML_MAPLEN(element_map), nspace, name);
572 
573     NE_DEBUG(NE_DBG_LOCKS, "lk_startelm: %s => %d\n", name, id);
574 
575     if (id == 0)
576         return NE_XML_DECLINE;
577 
578     if (parent == 0 && ctx->token == NULL) {
579         const char *token = ne_get_response_header(ctx->req, "Lock-Token");
580         /* at the root element; retrieve the Lock-Token header,
581          * and bail if it wasn't given. */
582         if (token == NULL) {
583             ne_xml_set_error(ctx->parser,
584                              _("LOCK response missing Lock-Token header"));
585             return NE_XML_ABORT;
586         }
587 
588         if (token[0] == '<') token++;
589         ctx->token = ne_strdup(token);
590         ne_shave(ctx->token, ">");
591         NE_DEBUG(NE_DBG_LOCKS, "lk_startelm: Finding token %s\n",
592                  ctx->token);
593     }
594 
595     /* TODO: only accept 'prop' as root for LOCK response */
596     if (!can_accept(parent, id))
597         return NE_XML_DECLINE;
598 
599     if (id == ELM_activelock && !ctx->found) {
600 	/* a new activelock */
601 	ne_lock_free(&ctx->active);
602 	memset(&ctx->active, 0, sizeof ctx->active);
603         ctx->active.timeout = NE_TIMEOUT_INVALID;
604     }
605 
606     ne_buffer_clear(ctx->cdata);
607 
608     return id;
609 }
610 
611 /* End-element handler for LOCK response */
lk_endelm(void * userdata,int state,const char * nspace,const char * name)612 static int lk_endelm(void *userdata, int state,
613                      const char *nspace, const char *name)
614 {
615     struct lock_ctx *ctx = userdata;
616 
617     if (ctx->found)
618 	return 0;
619 
620     if (end_element_common(&ctx->active, state, ctx->cdata->data))
621 	return -1;
622 
623     if (state == ELM_activelock) {
624 	if (ctx->active.token && strcmp(ctx->active.token, ctx->token) == 0) {
625 	    ctx->found = 1;
626 	}
627     }
628 
629     return 0;
630 }
631 
632 /* Creator callback for private structure. */
ld_create(void * userdata,const ne_uri * uri)633 static void *ld_create(void *userdata, const ne_uri *uri)
634 {
635     struct ne_lock *lk = ne_lock_create();
636 
637     ne_uri_copy(&lk->uri, uri);
638 
639     return lk;
640 }
641 
642 /* Destructor callback for private structure. */
ld_destroy(void * userdata,void * private)643 static void ld_destroy(void *userdata, void *private)
644 {
645     struct ne_lock *lk = private;
646 
647     ne_lock_destroy(lk);
648 }
649 
650 /* Discover all locks on URI */
ne_lock_discover(ne_session * sess,const char * uri,ne_lock_result callback,void * userdata)651 int ne_lock_discover(ne_session *sess, const char *uri,
652 		     ne_lock_result callback, void *userdata)
653 {
654     ne_propfind_handler *handler;
655     struct discover_ctx ctx = {0};
656     int ret;
657 
658     ctx.results = callback;
659     ctx.userdata = userdata;
660     ctx.cdata = ne_buffer_create();
661     ctx.phandler = handler = ne_propfind_create(sess, uri, NE_DEPTH_ZERO);
662 
663     ne_propfind_set_private(handler, ld_create, ld_destroy, &ctx);
664 
665     ne_xml_push_handler(ne_propfind_get_parser(handler),
666                         ld_startelm, ld_cdata, end_element_ldisc, &ctx);
667 
668     ret = ne_propfind_named(handler, lock_props, discover_results, &ctx);
669 
670     ne_buffer_destroy(ctx.cdata);
671     ne_propfind_destroy(handler);
672 
673     return ret;
674 }
675 
add_timeout_header(ne_request * req,long timeout)676 static void add_timeout_header(ne_request *req, long timeout)
677 {
678     if (timeout == NE_TIMEOUT_INFINITE) {
679 	ne_add_request_header(req, "Timeout", "Infinite");
680     }
681     else if (timeout != NE_TIMEOUT_INVALID && timeout > 0) {
682 	ne_print_request_header(req, "Timeout", "Second-%ld", timeout);
683     }
684     /* just ignore it if timeout == 0 or invalid. */
685 }
686 
ne_lock(ne_session * sess,struct ne_lock * lock)687 int ne_lock(ne_session *sess, struct ne_lock *lock)
688 {
689     ne_request *req = ne_request_create(sess, "LOCK", lock->uri.path);
690     ne_buffer *body = ne_buffer_create();
691     ne_xml_parser *parser = ne_xml_create();
692     int ret;
693     struct lock_ctx ctx;
694 
695     memset(&ctx, 0, sizeof ctx);
696     ctx.cdata = ne_buffer_create();
697     ctx.req = req;
698     ctx.parser = parser;
699 
700     /* LOCK is not idempotent. */
701     ne_set_request_flag(req, NE_REQFLAG_IDEMPOTENT, 0);
702 
703     ne_xml_push_handler(parser, lk_startelm, lk_cdata, lk_endelm, &ctx);
704 
705     /* Create the body */
706     ne_buffer_concat(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
707 		    "<lockinfo xmlns='DAV:'>\n" " <lockscope>",
708 		    lock->scope==ne_lockscope_exclusive?
709 		    "<exclusive/>":"<shared/>",
710 		    "</lockscope>\n"
711 		    "<locktype><write/></locktype>", NULL);
712 
713     if (lock->owner) {
714 	ne_buffer_concat(body, "<owner>", lock->owner, "</owner>\n", NULL);
715     }
716     ne_buffer_czappend(body, "</lockinfo>\n");
717 
718     ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
719     ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
720     ne_add_depth_header(req, lock->depth);
721     add_timeout_header(req, lock->timeout);
722 
723     /* TODO:
724      * By 2518, we need this only if we are creating a lock-null resource.
725      * Since we don't KNOW whether the lock we're given is a lock-null
726      * or not, we cover our bases.
727      */
728     ne_lock_using_parent(req, lock->uri.path);
729     /* This one is clearer from 2518 sec 8.10.4. */
730     ne_lock_using_resource(req, lock->uri.path, lock->depth);
731 
732     ret = ne_xml_dispatch_request(req, parser);
733 
734     ne_buffer_destroy(body);
735     ne_buffer_destroy(ctx.cdata);
736 
737     if (ret == NE_OK && ne_get_status(req)->klass == 2) {
738         if (ne_get_status(req)->code == 207) {
739             ret = NE_ERROR;
740             /* TODO: set the error string appropriately */
741         } else if (ctx.found) {
742 	    /* it worked: copy over real lock details if given. */
743             if (lock->token) ne_free(lock->token);
744 	    lock->token = ctx.token;
745             ctx.token = NULL;
746 	    if (ctx.active.timeout != NE_TIMEOUT_INVALID)
747 		lock->timeout = ctx.active.timeout;
748 	    lock->scope = ctx.active.scope;
749 	    lock->type = ctx.active.type;
750 	    if (ctx.active.depth >= 0)
751 		lock->depth = ctx.active.depth;
752 	    if (ctx.active.owner) {
753 		if (lock->owner) ne_free(lock->owner);
754 		lock->owner = ctx.active.owner;
755 		ctx.active.owner = NULL;
756 	    }
757 	} else {
758 	    ret = NE_ERROR;
759 	    ne_set_error(sess, _("Response missing activelock for %s"),
760 			 ctx.token);
761 	}
762     } else if (ret == NE_OK /* && status != 2xx */) {
763 	ret = NE_ERROR;
764     }
765 
766     ne_lock_free(&ctx.active);
767     if (ctx.token) ne_free(ctx.token);
768     ne_request_destroy(req);
769     ne_xml_destroy(parser);
770 
771     return ret;
772 }
773 
ne_lock_refresh(ne_session * sess,struct ne_lock * lock)774 int ne_lock_refresh(ne_session *sess, struct ne_lock *lock)
775 {
776     ne_request *req = ne_request_create(sess, "LOCK", lock->uri.path);
777     ne_xml_parser *parser = ne_xml_create();
778     int ret;
779     struct lock_ctx ctx;
780 
781     memset(&ctx, 0, sizeof ctx);
782     ctx.cdata = ne_buffer_create();
783     ctx.req = req;
784     ctx.token = lock->token;
785     ctx.parser = parser;
786 
787     /* Handle the response and update *lock appropriately. */
788     ne_xml_push_handler(parser, lk_startelm, lk_cdata, lk_endelm, &ctx);
789 
790     /* For a lock refresh, submitting only this lock token must be
791      * sufficient. */
792     ne_print_request_header(req, "If", "(<%s>)", lock->token);
793     add_timeout_header(req, lock->timeout);
794 
795     ret = ne_xml_dispatch_request(req, parser);
796 
797     if (ret == NE_OK) {
798         if (ne_get_status(req)->klass != 2) {
799             ret = NE_ERROR; /* and use default session error */
800 	} else if (!ctx.found) {
801             ne_set_error(sess, _("No activelock for <%s> returned in "
802                                  "LOCK refresh response"), lock->token);
803             ret = NE_ERROR;
804         } else /* success! */ {
805             /* update timeout for passed-in lock structure. */
806             lock->timeout = ctx.active.timeout;
807         }
808     }
809 
810     ne_lock_free(&ctx.active);
811     ne_buffer_destroy(ctx.cdata);
812     ne_request_destroy(req);
813     ne_xml_destroy(parser);
814 
815     return ret;
816 }
817