Sat, 18 Jan 2020 16:31:52 +0100
add test for webdav_propfind() that checks if the response is valid xml
--- a/src/server/test/main.c Sat Jan 18 13:48:59 2020 +0100 +++ b/src/server/test/main.c Sat Jan 18 16:31:52 2020 +0100 @@ -94,6 +94,9 @@ ucx_test_register(suite, test_webdav_op_propfind_begin); ucx_test_register(suite, test_webdav_op_propfind_children); + // webdav methods + ucx_test_register(suite, test_webdav_propfind); + // run tests ucx_test_run(suite, stdout); fflush(stdout);
--- a/src/server/test/testutils.c Sat Jan 18 13:48:59 2020 +0100 +++ b/src/server/test/testutils.c Sat Jan 18 16:31:52 2020 +0100 @@ -41,6 +41,7 @@ Session* testutil_session(void) { pool_handle_t *pool = pool_create(); NSAPISession *sn = nsapisession_create(pool); + sn->connection = testutil_dummy_connection(pool); return &sn->sn; } @@ -75,6 +76,28 @@ return &rq->rq; } +static int dummyconn_read(Connection *conn, void *buf, int len) { + return len; +} + +static int dummyconn_write(Connection *conn, const void *buf, int len) { + return len; +} + +static void dummyconn_close(Connection *conn) { + +} + + +Connection* testutil_dummy_connection(pool_handle_t *pool) { + Connection *conn = pool_malloc(pool, sizeof(Connection)); + ZERO(conn, sizeof(Connection)); + conn->read = dummyconn_read; + conn->write = dummyconn_write; + conn->close = dummyconn_close; + return conn; +} + void testutil_request_body(Session *sn, Request *rq, const char *body, size_t len) { sstr_t cl = ucx_sprintf("%d", (int)len); pblock_nvreplace("content-length", cl.ptr, rq->headers);
--- a/src/server/test/testutils.h Sat Jan 18 13:48:59 2020 +0100 +++ b/src/server/test/testutils.h Sat Jan 18 16:31:52 2020 +0100 @@ -48,6 +48,8 @@ Request* testutil_request(pool_handle_t *pool, const char *method, const char *uri); +Connection* testutil_dummy_connection(pool_handle_t *pool); + void testutil_request_body(Session *sn, Request *rq, const char *body, size_t len); void testutil_destroy_session(Session *sn);
--- a/src/server/test/webdav.c Sat Jan 18 13:48:59 2020 +0100 +++ b/src/server/test/webdav.c Sat Jan 18 16:31:52 2020 +0100 @@ -975,3 +975,34 @@ UCX_TEST_END; testutil_destroy_session(sn); } + +UCX_TEST(test_webdav_propfind) { + Session *sn = testutil_session(); + Request *rq = testutil_request(sn->pool, "PROPFIND", "/"); + + pblock_nvinsert("path", "/", rq->vars); + pblock_nvinsert("uri", "/", rq->reqpb); + + + TestIOStream *st = testutil_iostream(2048, TRUE); + sn->csd = st; + + testutil_request_body(sn, rq, TEST_PROPFIND1, strlen(TEST_PROPFIND1)); + + UCX_TEST_BEGIN; + + pblock *pb = pblock_create_pool(sn->pool, 4); + + int ret = webdav_propfind(pb, sn, rq); + + UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (1) failed"); + + xmlDoc *doc = xmlReadMemory( + st->buf->space, st->buf->size, NULL, NULL, 0); + UCX_TEST_ASSERT(doc, "response is not valid xml"); + + //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); + + UCX_TEST_END; + testutil_destroy_session(sn); +}
--- a/src/server/test/webdav.h Sat Jan 18 13:48:59 2020 +0100 +++ b/src/server/test/webdav.h Sat Jan 18 16:31:52 2020 +0100 @@ -56,6 +56,8 @@ UCX_TEST(test_webdav_op_propfind_begin); UCX_TEST(test_webdav_op_propfind_children); +UCX_TEST(test_webdav_propfind); + /* --------------------------- PROPFIND --------------------------- */ #define TEST_PROPFIND1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
--- a/src/server/webdav/multistatus.c Sat Jan 18 13:48:59 2020 +0100 +++ b/src/server/webdav/multistatus.c Sat Jan 18 16:31:52 2020 +0100 @@ -30,13 +30,15 @@ #include <stdlib.h> #include "../daemon/session.h" +#include "../daemon/protocol.h" #include "../util/platform.h" #include <ucx/string.h> -#include "operation.h" +#include "multistatus.h" -#include "multistatus.h" +#include "operation.h" +#include "xml.h" #define MULTISTATUS_BUFFER_LENGTH 2048 @@ -141,7 +143,10 @@ switch(property->vtype) { case WS_VALUE_NO_TYPE: break; case WS_VALUE_XML_NODE: { - // TODO + wsxml_write_nodes_without_nsdef( + ms->sn->pool, + out, + property->value.node); break; } case WS_VALUE_XML_DATA: { @@ -163,13 +168,13 @@ } // end tag - writer_putc(out, '<'); + writer_puts(out, S("</")); writer_puts(out, sstr((char*)property->namespace->prefix)); writer_putc(out, ':'); writer_puts(out, sstr((char*)property->name)); writer_putc(out, '>'); } else { - writer_putc(out, '/>'); + writer_puts(out, S("/>")); } return out->error; @@ -179,42 +184,79 @@ writer_puts(out, S(" <D:response>\n" " <D:href>")); writer_puts(out, sstr(rp->resource.href)); - writer_puts(out, S("</href>\n")); + writer_puts(out, S("</D:href>\n")); if(rp->plist_begin) { - writer_puts(out, S(" <D:propstat>" + writer_puts(out, S(" <D:propstat>\n" " <D:prop>\n")); // send properties PropertyOkList *p = rp->plist_begin; while(p) { + writer_puts(out, S(" ")); if(send_property(ms, p->property, p->nsdef, TRUE, out)) { return out->error; } + writer_puts(out, S("\n ")); p = p->next; } writer_puts(out, S(" </D:prop>\n" - " <D:status>HTTP/1.1 200 OK</D:status>" + " <D:status>HTTP/1.1 200 OK</D:status>\n" " </D:propstat>\n")); } // send error properties PropertyErrorList *error = rp->errors; while(error) { + writer_puts(out, S(" <D:propstat>\n" + " <D:prop>\n")); + WebdavPList *errprop = error->begin; while(errprop) { + writer_puts(out, S(" ")); if(send_property(ms, errprop->property, NULL, FALSE, out)) { return out->error; } + writer_puts(out, S("\n ")); errprop = errprop->next; } + + char statuscode[8]; + int sclen = snprintf(statuscode, 8, "%d ", error->status); + if(sclen > 4) { + statuscode[0] = '5'; + statuscode[1] = '0'; + statuscode[2] = '0'; + statuscode[3] = ' '; + sclen = 4; + } + writer_puts(out, S(" </D:prop>\n" + " <D:status>HTTP/1.1 ")); + writer_put(out, statuscode, sclen); + const char *status_msg = protocol_status_message(error->status); + if(status_msg) { + writer_put(out, status_msg, strlen(status_msg)); + } else { + writer_puts(out, S("Server Error")); + } + writer_puts(out, S("</D:status>\n" + " </D:propstat>\n")); + + error = error->next; } + // end response tag + writer_puts(out, S(" </D:response>\n")); + return out->error; } int multistatus_send(Multistatus *ms, SYS_NETFD net) { + // start http response + protocol_status(ms->sn, ms->rq, 207, NULL); + protocol_start_response(ms->sn, ms->rq); + char buffer[MULTISTATUS_BUFFER_LENGTH]; // create a writer, that flushes the buffer when it is filled Writer writer; @@ -235,6 +277,11 @@ response = response->next; } + // end multistatus + writer_puts(out, S("</D:multistatus>\n")); + + writer_flush(out); + return 0; }
--- a/src/server/webdav/webdav.c Sat Jan 18 13:48:59 2020 +0100 +++ b/src/server/webdav/webdav.c Sat Jan 18 16:31:52 2020 +0100 @@ -54,7 +54,9 @@ static WebdavProperty dav_resourcetype_empty; static WebdavProperty dav_resourcetype_collection; -static WSXmlNode dav_resourcetype_collection_value; // TODO: change type to WSXmlData +static WSXmlData dav_resourcetype_collection_value; + +#define WEBDAV_RESOURCE_TYPE_COLLECTION "<D:collection/>" static void init_default_backend(void) { memset(&default_backend, 0, sizeof(WebdavBackend)); @@ -101,11 +103,10 @@ dav_resourcetype_collection.namespace = &dav_namespace; dav_resourcetype_collection.name = "resourcetype"; - dav_resourcetype_collection.value.node = &dav_resourcetype_collection_value; - dav_resourcetype_collection.vtype = WS_VALUE_XML_NODE; - dav_resourcetype_collection_value.content = (xmlChar*)"<D:collection/>"; - dav_resourcetype_collection_value.type = XML_TEXT_NODE; - + dav_resourcetype_collection.value.data = &dav_resourcetype_collection_value; + dav_resourcetype_collection.vtype = WS_VALUE_XML_DATA; + dav_resourcetype_collection_value.data = WEBDAV_RESOURCE_TYPE_COLLECTION; + dav_resourcetype_collection_value.length = sizeof(WEBDAV_RESOURCE_TYPE_COLLECTION)-1; return REQ_PROCEED; } @@ -262,20 +263,20 @@ } } - // finish the propfind request - // this function should cleanup all resources, therefore we execute it - // even if a previous function failed - if(webdav_op_propfind_finish(op)) { - ret = REQ_ABORTED; - } - // if propfind was successful, send the result to the client if(ret == REQ_PROCEED && multistatus_send(ms, sn->csd)) { ret = REQ_ABORTED; // TODO: log error } else { + // TODO: error response + } + + // finish the propfind request + // this function should cleanup all resources, therefore we execute it + // even if a previous function failed + if(webdav_op_propfind_finish(op)) { // TODO: log error - // TODO: error response + ret = REQ_ABORTED; } return ret;
--- a/src/server/webdav/xml.c Sat Jan 18 13:48:59 2020 +0100 +++ b/src/server/webdav/xml.c Sat Jan 18 16:31:52 2020 +0100 @@ -344,19 +344,30 @@ /* * Serialize an XML text node * This replaces some special characters with entity refs + * type: 0 = element text, 1 = attribute text */ -static void xml_ser_text(Writer *out, const char *text) { +static void xml_ser_text(Writer *out, int type, const char *text) { size_t start = 0; size_t i; sstr_t entityref = { NULL, 0 }; for(i=0;text[i]!='\0';i++) { - switch(text[i]) { - case '<': entityref = S("<"); break; - case '>': entityref = S(">"); break; - case '&': entityref = S("&"); break; - case '\"': entityref = S("""); break; - case '\'': entityref = S("'"); break; + char c = text[i]; + if(c == '&') { + entityref = S("&"); + } else if(type == 0) { + if(c == '<') { + entityref = S("<"); + } else if(c == '>') { + entityref = S(">"); + } + } else { + if(c == '\"') { + entityref = S("""); + } else if(c == '\'') { + entityref = S("'"); + } } + if(entityref.ptr) { size_t len = i-start; if(len > 0) { @@ -436,7 +447,7 @@ xmlNode *value = attr->children; while(value) { if(value->content) { - xml_ser_text(out, (const char*)value->content); + xml_ser_text(out, 1, (const char*)value->content); } value = value->next; } @@ -459,7 +470,7 @@ case XML_ELEMENT_NODE: xml_ser_element(xw, node); break; case XML_ATTRIBUTE_NODE: break; case XML_TEXT_NODE: { - xml_ser_text(xw->out, (const char*)node->content); + xml_ser_text(xw->out, 0, (const char*)node->content); break; } case XML_CDATA_SECTION_NODE: {