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