base
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
WebDAV 207 multi-status response handling
|
||||
Copyright (C) 1999-2004, 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
|
||||
|
||||
*/
|
||||
|
||||
/* Generic handling for WebDAV 207 Multi-Status responses. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "ne_alloc.h"
|
||||
#include "ne_utils.h"
|
||||
#include "ne_xml.h"
|
||||
#include "ne_207.h"
|
||||
#include "ne_uri.h"
|
||||
#include "ne_basic.h"
|
||||
|
||||
#include "ne_i18n.h"
|
||||
|
||||
struct ne_207_parser_s {
|
||||
ne_207_start_response *start_response;
|
||||
ne_207_end_response *end_response;
|
||||
ne_207_start_propstat *start_propstat;
|
||||
ne_207_end_propstat *end_propstat;
|
||||
ne_xml_parser *parser;
|
||||
void *userdata;
|
||||
|
||||
ne_buffer *cdata;
|
||||
|
||||
/* remember whether we are in a response: the validation
|
||||
* doesn't encapsulate this since we only count as being
|
||||
* "in a response" when we've seen the href element. */
|
||||
int in_response;
|
||||
|
||||
/* current position */
|
||||
void *response, *propstat;
|
||||
/* caching */
|
||||
ne_status status;
|
||||
char *description, *href;
|
||||
};
|
||||
|
||||
#define ELM_multistatus 1
|
||||
#define ELM_response 2
|
||||
#define ELM_responsedescription 3
|
||||
#define ELM_href 4
|
||||
#define ELM_prop (NE_207_STATE_PROP)
|
||||
#define ELM_status 6
|
||||
#define ELM_propstat 7
|
||||
|
||||
static const struct ne_xml_idmap map207[] = {
|
||||
{ "DAV:", "multistatus", ELM_multistatus },
|
||||
{ "DAV:", "response", ELM_response },
|
||||
{ "DAV:", "responsedescription", ELM_responsedescription },
|
||||
{ "DAV:", "href", ELM_href },
|
||||
{ "DAV:", "propstat", ELM_propstat },
|
||||
{ "DAV:", "prop", ELM_prop },
|
||||
{ "DAV:", "status", ELM_status }
|
||||
};
|
||||
|
||||
/* Set the callbacks for the parser */
|
||||
void ne_207_set_response_handlers(ne_207_parser *p,
|
||||
ne_207_start_response *start,
|
||||
ne_207_end_response *end)
|
||||
{
|
||||
p->start_response = start;
|
||||
p->end_response = end;
|
||||
}
|
||||
|
||||
void ne_207_set_propstat_handlers(ne_207_parser *p,
|
||||
ne_207_start_propstat *start,
|
||||
ne_207_end_propstat *end)
|
||||
{
|
||||
p->start_propstat = start;
|
||||
p->end_propstat = end;
|
||||
}
|
||||
|
||||
void *ne_207_get_current_response(ne_207_parser *p)
|
||||
{
|
||||
return p->response;
|
||||
}
|
||||
|
||||
void *ne_207_get_current_propstat(ne_207_parser *p)
|
||||
{
|
||||
return p->propstat;
|
||||
}
|
||||
|
||||
/* return non-zero if (child, parent) is an interesting element */
|
||||
static int can_handle(int parent, int child)
|
||||
{
|
||||
return (parent == 0 && child == ELM_multistatus) ||
|
||||
(parent == ELM_multistatus && child == ELM_response) ||
|
||||
(parent == ELM_response &&
|
||||
(child == ELM_href || child == ELM_status ||
|
||||
child == ELM_propstat || child == ELM_responsedescription)) ||
|
||||
(parent == ELM_propstat &&
|
||||
(child == ELM_prop || child == ELM_status ||
|
||||
child == ELM_responsedescription));
|
||||
}
|
||||
|
||||
static int cdata_207(void *userdata, int state, const char *buf, size_t len)
|
||||
{
|
||||
ne_207_parser *p = userdata;
|
||||
|
||||
if ((state == ELM_href || state == ELM_responsedescription ||
|
||||
state == ELM_status) && p->cdata->used + len < 2048)
|
||||
ne_buffer_append(p->cdata, buf, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_element(void *userdata, int parent,
|
||||
const char *nspace, const char *name,
|
||||
const char **atts)
|
||||
{
|
||||
ne_207_parser *p = userdata;
|
||||
int state = ne_xml_mapid(map207, NE_XML_MAPLEN(map207), nspace, name);
|
||||
|
||||
if (!can_handle(parent, state))
|
||||
return NE_XML_DECLINE;
|
||||
|
||||
/* if not in a response, ignore everything. */
|
||||
if (!p->in_response && state != ELM_response && state != ELM_multistatus &&
|
||||
state != ELM_href)
|
||||
return NE_XML_DECLINE;
|
||||
|
||||
if (state == ELM_propstat && p->start_propstat) {
|
||||
p->propstat = p->start_propstat(p->userdata, p->response);
|
||||
if (p->propstat == NULL) {
|
||||
return NE_XML_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
ne_buffer_clear(p->cdata);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
#define GIVE_STATUS(p) ((p)->status.reason_phrase?&(p)->status:NULL)
|
||||
|
||||
#define HAVE_CDATA(p) ((p)->cdata->used > 1)
|
||||
|
||||
static int
|
||||
end_element(void *userdata, int state, const char *nspace, const char *name)
|
||||
{
|
||||
ne_207_parser *p = userdata;
|
||||
const char *cdata = ne_shave(p->cdata->data, "\r\n\t ");
|
||||
|
||||
switch (state) {
|
||||
case ELM_responsedescription:
|
||||
if (HAVE_CDATA(p)) {
|
||||
NE_FREE(p->description);
|
||||
p->description = ne_strdup(cdata);
|
||||
}
|
||||
break;
|
||||
case ELM_href:
|
||||
/* Now we have the href, begin the response */
|
||||
if (p->start_response && HAVE_CDATA(p)) {
|
||||
p->response = p->start_response(p->userdata, cdata);
|
||||
p->in_response = 1;
|
||||
}
|
||||
break;
|
||||
case ELM_status:
|
||||
if (HAVE_CDATA(p)) {
|
||||
NE_FREE(p->status.reason_phrase);
|
||||
if (ne_parse_statusline(cdata, &p->status)) {
|
||||
char buf[500];
|
||||
NE_DEBUG(NE_DBG_HTTP, "Status line: %s\n", cdata);
|
||||
ne_snprintf(buf, 500,
|
||||
_("Invalid HTTP status line in status element "
|
||||
"at line %d of response:\nStatus line was: %s"),
|
||||
ne_xml_currentline(p->parser), cdata);
|
||||
ne_xml_set_error(p->parser, buf);
|
||||
return -1;
|
||||
} else {
|
||||
NE_DEBUG(NE_DBG_XML, "Decoded status line: %s\n", cdata);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ELM_propstat:
|
||||
if (p->end_propstat)
|
||||
p->end_propstat(p->userdata, p->propstat, GIVE_STATUS(p),
|
||||
p->description);
|
||||
p->propstat = NULL;
|
||||
NE_FREE(p->description);
|
||||
NE_FREE(p->status.reason_phrase);
|
||||
break;
|
||||
case ELM_response:
|
||||
if (!p->in_response) break;
|
||||
if (p->end_response)
|
||||
p->end_response(p->userdata, p->response, GIVE_STATUS(p),
|
||||
p->description);
|
||||
p->response = NULL;
|
||||
p->in_response = 0;
|
||||
NE_FREE(p->status.reason_phrase);
|
||||
NE_FREE(p->description);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ne_207_parser *ne_207_create(ne_xml_parser *parser, void *userdata)
|
||||
{
|
||||
ne_207_parser *p = ne_calloc(sizeof *p);
|
||||
|
||||
p->parser = parser;
|
||||
p->userdata = userdata;
|
||||
p->cdata = ne_buffer_create();
|
||||
|
||||
/* Add handler for the standard 207 elements */
|
||||
ne_xml_push_handler(parser, start_element, cdata_207, end_element, p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void ne_207_destroy(ne_207_parser *p)
|
||||
{
|
||||
if (p->status.reason_phrase) ne_free(p->status.reason_phrase);
|
||||
ne_buffer_destroy(p->cdata);
|
||||
ne_free(p);
|
||||
}
|
||||
|
||||
int ne_accept_207(void *userdata, ne_request *req, const ne_status *status)
|
||||
{
|
||||
return (status->code == 207);
|
||||
}
|
||||
|
||||
/* Handling of 207 errors: we keep a string buffer, and append
|
||||
* messages to it as they come down.
|
||||
*
|
||||
* Note, 424 means it would have worked but something else went wrong.
|
||||
* We will have had the error for "something else", so we display
|
||||
* that, and skip 424 errors. */
|
||||
|
||||
/* This is passed as userdata to the 207 code. */
|
||||
struct context {
|
||||
char *href;
|
||||
ne_buffer *buf;
|
||||
unsigned int is_error;
|
||||
};
|
||||
|
||||
static void *start_response(void *userdata, const char *href)
|
||||
{
|
||||
struct context *ctx = userdata;
|
||||
NE_FREE(ctx->href);
|
||||
ctx->href = ne_strdup(href);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void handle_error(struct context *ctx, const ne_status *status,
|
||||
const char *description)
|
||||
{
|
||||
if (status && status->klass != 2 && status->code != 424) {
|
||||
char buf[50];
|
||||
ctx->is_error = 1;
|
||||
sprintf(buf, "%d", status->code);
|
||||
ne_buffer_concat(ctx->buf, ctx->href, ": ",
|
||||
buf, " ", status->reason_phrase, "\n", NULL);
|
||||
if (description != NULL) {
|
||||
/* TODO: these can be multi-line. Would be good to
|
||||
* word-wrap this at col 80. */
|
||||
ne_buffer_concat(ctx->buf, " -> ", description, "\n", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void end_response(void *userdata, void *response,
|
||||
const ne_status *status, const char *description)
|
||||
{
|
||||
struct context *ctx = userdata;
|
||||
handle_error(ctx, status, description);
|
||||
}
|
||||
|
||||
static void
|
||||
end_propstat(void *userdata, void *propstat,
|
||||
const ne_status *status, const char *description)
|
||||
{
|
||||
struct context *ctx = userdata;
|
||||
handle_error(ctx, status, description);
|
||||
}
|
||||
|
||||
/* Dispatch a DAV request and handle a 207 error response appropriately */
|
||||
/* TODO: hook up Content-Type parsing; passing charset to XML parser */
|
||||
int ne_simple_request(ne_session *sess, ne_request *req)
|
||||
{
|
||||
int ret;
|
||||
struct context ctx = {0};
|
||||
ne_207_parser *p207;
|
||||
ne_xml_parser *p;
|
||||
|
||||
p = ne_xml_create();
|
||||
p207 = ne_207_create(p, &ctx);
|
||||
/* The error string is progressively written into the
|
||||
* ne_buffer by the element callbacks */
|
||||
ctx.buf = ne_buffer_create();
|
||||
|
||||
ne_207_set_response_handlers(p207, start_response, end_response);
|
||||
ne_207_set_propstat_handlers(p207, NULL, end_propstat);
|
||||
|
||||
ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, p);
|
||||
|
||||
ret = ne_request_dispatch(req);
|
||||
|
||||
if (ret == NE_OK) {
|
||||
if (ne_get_status(req)->code == 207) {
|
||||
if (ne_xml_failed(p)) {
|
||||
/* The parse was invalid */
|
||||
ne_set_error(sess, "%s", ne_xml_get_error(p));
|
||||
ret = NE_ERROR;
|
||||
} else if (ctx.is_error) {
|
||||
/* If we've actually got any error information
|
||||
* from the 207, then set that as the error */
|
||||
ne_set_error(sess, "%s", ctx.buf->data);
|
||||
ret = NE_ERROR;
|
||||
}
|
||||
} else if (ne_get_status(req)->klass != 2) {
|
||||
ret = NE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ne_207_destroy(p207);
|
||||
ne_xml_destroy(p);
|
||||
ne_buffer_destroy(ctx.buf);
|
||||
NE_FREE(ctx.href);
|
||||
|
||||
ne_request_destroy(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user