"Fossies" - the Fresh Open Source Software Archive

Member "sitecopy-0.16.6/lib/neon/ne_locks.c" (5 Feb 2007, 22306 Bytes) of archive /linux/www/sitecopy-0.16.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ne_locks.c" see the Fossies "Dox" file reference documentation.

    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. */
  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 
  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. */
  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 
  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 
  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 
  181 void ne_lockstore_destroy(ne_lock_store *store)
  182 {
  183     free_list(store->locks, 1);
  184     ne_free(store);
  185 }
  186 
  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 
  194 struct ne_lock *ne_lockstore_first(ne_lock_store *store)
  195 {
  196     store->cursor = store->locks;
  197     return CURSOR_RET(store);
  198 }
  199 
  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 
  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 */
  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 
  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 
  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 
  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 
  322 void ne_lockstore_add(ne_lock_store *store, struct ne_lock *lock)
  323 {
  324     insert_lock(&store->locks, lock);
  325 }
  326 
  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 
  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 
  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 
  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 
  385 void ne_lock_destroy(struct ne_lock *lock)
  386 {
  387     ne_lock_free(lock);
  388     ne_free(lock);
  389 }
  390 
  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 
  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 
  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 
  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 
  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 */
  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 
  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 
  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 
  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 
  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 
  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 */
  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. */
  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. */
  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 */
  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 
  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 
  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 
  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 }