Skip to content

Commit

Permalink
Add support for the RFC 4918 "DAV:lockroot" element in lock discovery:
Browse files Browse the repository at this point in the history
* src/ne_locks.c (end_element_common, can_accept): Support lockroot element.
  (common_cdata, ld_cdata, lk_cdata): Use common cdata collector.

* src/ne_locks.h (ne_lock_result): Clarify that the returned lock may
  have a different URI to the discovery URI.

* test/lock.c: Add checks for returned lock URI throughout.
  • Loading branch information
notroj committed Oct 3, 2023
1 parent b5448cb commit 5b58d7b
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 51 deletions.
67 changes: 44 additions & 23 deletions src/ne_locks.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,14 @@ struct lock_ctx {
#define ELM_shared (ELM_LOCK_FIRST + 11)
#define ELM_href (ELM_LOCK_FIRST + 12)
#define ELM_prop (NE_207_STATE_PROP)
#define ELM_lockroot (ELM_LOCK_FIRST + 13)

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)
ELM(shared), ELM(href), ELM(lockroot)
/* no "lockentry" */
#undef ELM
};
Expand Down Expand Up @@ -524,9 +525,16 @@ end_element_common(struct ne_lock *l, int state, const char *cdata)
case ELM_owner:
l->owner = strdup(cdata);
break;
case ELM_href:
case ELM_locktoken:
l->token = strdup(cdata);
break;
case ELM_lockroot:
ne_uri_free(&l->uri);
if (ne_uri_parse(cdata, &l->uri)) {
NE_DEBUG(NE_DBG_LOCKS, "lock: URI parse failed for %s\n", cdata);
return -1;
}
break;
}
return 0;
}
Expand All @@ -543,17 +551,20 @@ static int end_element_ldisc(void *userdata, int state,

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);
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
|| id == ELM_lockroot))
|| (parent == ELM_lockscope
&& (id == ELM_exclusive || id == ELM_shared))
|| (parent == ELM_locktype && id == ELM_write)
|| (id == ELM_href
&& (parent == ELM_locktoken || parent == ELM_lockroot));

}

static int ld_startelm(void *userdata, int parent,
Expand All @@ -574,13 +585,18 @@ static int ld_startelm(void *userdata, int parent,

#define MAX_CDATA (256)

static int lk_cdata(void *userdata, int state,
const char *cdata, size_t len)
static int common_cdata(ne_buffer *buf, 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);
switch (state) {
case ELM_depth:
case ELM_timeout:
case ELM_owner:
case ELM_href:
if (buf->used + len < MAX_CDATA)
ne_buffer_append(buf, cdata, len);
break;
}

return 0;
}
Expand All @@ -590,10 +606,15 @@ static int ld_cdata(void *userdata, int state,
{
struct discover_ctx *ctx = userdata;

if (ctx->cdata->used + len < MAX_CDATA)
ne_buffer_append(ctx->cdata, cdata, len);

return 0;
return common_cdata(ctx->cdata, state, cdata, len);
}

static int lk_cdata(void *userdata, int state,
const char *cdata, size_t len)
{
struct lock_ctx *ctx = userdata;

return common_cdata(ctx->cdata, state, cdata, len);
}

static int lk_startelm(void *userdata, int parent,
Expand Down
4 changes: 3 additions & 1 deletion src/ne_locks.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ int ne_lock_refresh(ne_session *sess, struct ne_lock *lock);

/* Callback for lock discovery. If 'lock' is NULL, something went
* wrong performing lockdiscovery for the resource, look at 'status'
* for the details.
* for the details. 'uri' is the URI against which lock discovery is
* being performed, which may be different from the URI of a
* discovered lock (lock->uri).
*
* If lock is non-NULL, at least lock->uri and lock->token will be
* filled in; and status will be NULL. */
Expand Down
86 changes: 59 additions & 27 deletions test/lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,33 @@

#define EOL "\r\n"

#define FOO_LOCKROOT "http://localhost/foo"

/* returns an activelock XML element. */
static char *activelock(enum ne_lock_scope scope,
int depth,
const char *owner,
unsigned long timeout,
const char *token_href)
int depth,
const char *owner,
const char *uri,
unsigned long timeout,
const char *token_href)
{
static char buf[BUFSIZ];
const char *lr_start = uri != NULL ? "<D:lockroot><D:href>" : "";
const char *lr_end = uri != NULL ? "</D:href></D:lockroot>\n" : "";

ne_snprintf(buf, BUFSIZ,
"<D:activelock>\n"
"<D:locktype><D:write/></D:locktype>\n"
"<D:lockscope><D:%s/></D:lockscope>\n"
"<D:depth>%d</D:depth>\n"
"<D:owner>%s</D:owner>\n"
"<D:timeout>Second-%lu</D:timeout>\n"
"<D:locktoken><D:href>%s</D:href></D:locktoken>\n"
"</D:activelock>",
scope==ne_lockscope_exclusive?"exclusive":"shared",
depth, owner, timeout, token_href);
ne_snprintf(buf, BUFSIZ, \
"<D:activelock>\n" \
"<D:locktype><D:write/></D:locktype>\n" \
"<D:lockscope><D:%s/></D:lockscope>\n" \
"<D:depth>%d</D:depth>\n" \
"<D:owner>%s</D:owner>\n" \
"<D:timeout>Second-%lu</D:timeout>\n" \
"<D:locktoken><D:href>%s</D:href></D:locktoken>\n" \
"%s%s%s" \
"</D:activelock>", \
scope==ne_lockscope_exclusive?"exclusive":"shared", \
depth, owner, timeout, token_href, \
lr_start, uri ? uri : "", lr_end);

return buf;
}
Expand All @@ -69,6 +76,7 @@ static char *activelock(enum ne_lock_scope scope,
static char *lock_response(enum ne_lock_scope scope,
int depth,
const char *owner,
const char *uri,
unsigned long timeout,
const char *token_href)
{
Expand All @@ -78,7 +86,7 @@ static char *lock_response(enum ne_lock_scope scope,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:prop xmlns:D=\"DAV:\">"
"<D:lockdiscovery>%s</D:lockdiscovery></D:prop>\n",
activelock(scope, depth, owner, timeout, token_href));
activelock(scope, depth, owner, uri, timeout, token_href));

return buf;
}
Expand All @@ -96,9 +104,9 @@ static char *multi_lock_response(struct ne_lock **locks)
"<D:lockdiscovery>");

for (n = 0; locks[n] != NULL; n++) {
char *lk = activelock(locks[n]->scope, locks[n]->depth,
locks[n]->owner, locks[n]->timeout,
locks[n]->token);
char *lk = activelock(locks[n]->scope, locks[n]->depth,
locks[n]->owner, NULL,
locks[n]->timeout, locks[n]->token);
ne_buffer_zappend(buf, lk);
}

Expand All @@ -108,17 +116,22 @@ static char *multi_lock_response(struct ne_lock **locks)

static char *discover_response(const char *href, const struct ne_lock *lk)
{
static char buf[BUFSIZ];
char buf[BUFSIZ];
char *uri = ne_uri_unparse(&lk->uri);

ne_snprintf(buf, BUFSIZ,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:multistatus xmlns:D='DAV:'>\n"
"<D:response><D:href>%s</D:href><D:propstat>\n"
"<D:prop><D:lockdiscovery>%s</D:lockdiscovery></D:prop>\n"
"<D:status>HTTP/1.1 200 OK</D:status></D:propstat>\n"
"</D:response></D:multistatus>\n",
href, activelock(lk->scope, lk->depth, lk->owner,
href, activelock(lk->scope, lk->depth, lk->owner, uri,
7200, lk->token));
return buf;

ne_free(uri);

return ne_strdup(buf);
}

static struct ne_lock *make_lock(const char *path, const char *token,
Expand Down Expand Up @@ -232,7 +245,7 @@ static int lock_timeout(void)
{
ne_session *sess;
char *resp, *rbody = lock_response(ne_lockscope_exclusive, 0, "me",
6500, "opaquelocktoken:foo");
NULL, 6500, "opaquelocktoken:foo");
struct ne_lock *lock = ne_lock_create();

resp = ne_concat("HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
Expand Down Expand Up @@ -267,7 +280,7 @@ static int lock_timeout(void)
static int lock_long_timeout(void)
{
ne_session *sess;
char *resp, *rbody = lock_response(ne_lockscope_exclusive, 0, "me",
char *resp, *rbody = lock_response(ne_lockscope_exclusive, 0, "me", FOO_LOCKROOT,
LONG_TIMEOUT, "opaquelocktoken:foo");
struct ne_lock *lock = ne_lock_create();

Expand Down Expand Up @@ -450,6 +463,7 @@ static int serve_discovery(ne_socket *sock, void *userdata)

struct result_args {
struct ne_lock *lock;
ne_uri uri;
int result;
};

Expand All @@ -474,7 +488,18 @@ static void discover_result(void *userdata, const struct ne_lock *lk,
const ne_uri *uri, const ne_status *st)
{
struct result_args *args = userdata;
args->result = lock_compare("discovered lock", lk, args->lock);

if (ne_uri_cmp(uri, &args->uri) != 0) {
NE_DEBUG(NE_DBG_HTTP, "discover: URI mismatch: %s not %s",
ne_uri_unparse(uri), ne_uri_unparse(&args->uri));
args->result = FAIL;
}
else {
NE_DEBUG(NE_DBG_HTTP, "test: Comparing discovered lock [%s] for %s "
"with expected [%s]...\n",
lk->token, uri->path, args->lock->token);
args->result = lock_compare("discovered lock", lk, args->lock);
}
}

static int discover(void)
Expand All @@ -483,6 +508,8 @@ static int discover(void)
char *response;
int ret;
struct result_args args;

memset(&args, 0, sizeof args);

args.lock = ne_lock_create();

Expand All @@ -491,20 +518,25 @@ static int discover(void)
args.lock->uri.host = ne_strdup("localhost");
args.lock->uri.port = 7777;
args.lock->uri.scheme = ne_strdup("http");
args.lock->uri.path = ne_strdup("/this/is/the/lock/path");

/* default */
args.result = FAIL;
t_context("results callback never invoked");

response = discover_response("/lockme", args.lock);
CALL(fake_session(&sess, serve_discovery, response));
args.lock->uri.path = ne_strdup("/lockme");
ne_free(response);

ne_fill_server_uri(sess, &args.uri);
args.uri.path = ne_strdup("/lockme");

ret = ne_lock_discover(sess, "/lockme", discover_result, &args);
CALL(await_server());
ONREQ(ret);

ne_lock_destroy(args.lock);
ne_uri_free(&args.uri);
ne_session_destroy(sess);

return args.result;
Expand Down Expand Up @@ -633,7 +665,7 @@ static int fail_noheader(void)
{
ne_session *sess;
char *resp, *rbody = lock_response(ne_lockscope_exclusive, 0, "me",
6500, "opaquelocktoken:foo");
FOO_LOCKROOT, 6500, "opaquelocktoken:foo");
struct ne_lock *lock = ne_lock_create();
int ret;

Expand Down

0 comments on commit 5b58d7b

Please sign in to comment.