Sun, 26 Feb 2012 19:51:14 +0100
Minimal webdav support
--- a/src/server/Makefile Sat Feb 25 12:43:26 2012 +0100 +++ b/src/server/Makefile Sun Feb 26 19:51:14 2012 +0100 @@ -30,8 +30,7 @@ CFLAGS = -# LDFLAGS += pg -LDFLAGS = -L/usr/lib/mps -R/usr/lib/mps -lplds4 -lplc4 -lnspr4 -lpthread -ldl -lposix4 -lsocket -lnsl -lgen -lm -lsendfile -lxerces-c +LDFLAGS = -pg -L/usr/lib/mps -R/usr/lib/mps -lplds4 -lplc4 -lnspr4 -lpthread -ldl -lposix4 -lsocket -lnsl -lgen -lm -lsendfile -lxerces-c OBJ_DIR = $(BUILD_ROOT)build/
--- a/src/server/daemon/httprequest.c Sat Feb 25 12:43:26 2012 +0100 +++ b/src/server/daemon/httprequest.c Sun Feb 26 19:51:14 2012 +0100 @@ -150,7 +150,7 @@ break; } } - + /* Get abs_path part of request URI, and canonicalize the path */ absPath.ptr = util_canonicalize_uri( request->pool, @@ -198,12 +198,12 @@ ha->headers[i].value, rq->rq.headers); } - + /* check for request body and prepare input buffer */ char *ctlen_str = pblock_findkeyval(pb_key_content_length, rq->rq.headers); if(ctlen_str) { int ctlen = atoi(ctlen_str); - + printf("request body length: %d\n", ctlen); netbuf *nb = request->netbuf; @@ -212,15 +212,15 @@ NetIOStream *net_io = (NetIOStream*)net_stream_from_fd( request->connection->fd); net_io->max_read = ctlen; - + sn->sn.inbuf = malloc(sizeof(netbuf)); sn->sn.inbuf->sd = net_io; sn->sn.inbuf->pos = 0; /* prepare buffer */ int cur_input_len = nb->cursize - nb->pos; + if(cur_input_len >= ctlen) { - /* * all data is already in the primary input buffer * just link the new netbuf to the primary buffer @@ -230,7 +230,7 @@ sn->sn.inbuf->inbuf = nb->inbuf + nb->pos; } else { sn->sn.inbuf->maxsize = (ctlen > 2048) ? (2048) : (ctlen); - sn->sn.inbuf->inbuf = malloc(sizeof(sn->sn.inbuf->maxsize)); + sn->sn.inbuf->inbuf = malloc(sn->sn.inbuf->maxsize); if(cur_input_len > 0) { /* we have read a part of the request body -> copy to netbuf */ @@ -242,8 +242,8 @@ } else { sn->sn.inbuf = NULL; } - - + + // Send the request to the NSAPI system nsapi_handle_request(sn, rq);
--- a/src/server/webdav/davparser.cpp Sat Feb 25 12:43:26 2012 +0100 +++ b/src/server/webdav/davparser.cpp Sun Feb 26 19:51:14 2012 +0100 @@ -59,6 +59,7 @@ sizeof(PropfindRequest)); davrq->allprop = 0; davrq->propname = 0; + davrq->prop = 0; davrq->properties = NULL; // create xml parser SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();
--- a/src/server/webdav/webdav.c Sat Feb 25 12:43:26 2012 +0100 +++ b/src/server/webdav/webdav.c Sun Feb 26 19:51:14 2012 +0100 @@ -38,13 +38,10 @@ #include "davparser.h" int webdav_service(pblock *pb, Session *sn, Request *rq) { - /* TODO: - * Dies ist die Implementierung für PROPFIND. Es sollte für jede webdav- - * Methode eine eigene Service-Funktion geben. Solange die anderen - * Methoden nicht implementiert werden, behandelt webdav_service nur - * PROPFIND. - */ + return webdav_propfind(pb, sn, rq); +} +int webdav_propfind(pblock *pb, Session *sn, Request *rq) { /* TODO: clean up if errors occurs */ /* Get request body which contains the webdav XML request */ @@ -61,6 +58,9 @@ } xml_body = pool_malloc(sn->pool, xml_len + 1); + if(xml_body == NULL) { + return REQ_ABORTED; + } xml_body[xml_len] = 0; if(!xml_body) { /* server error */ @@ -77,182 +77,198 @@ xl -= xml_len; } + /* + * get requested properties and initialize some stuff + */ PropfindRequest *davrq = dav_parse_propfind(sn, rq, xml_body, xml_len); davrq->sn = sn; davrq->rq = rq; + davrq->out = sbuf_new(512); davrq->propertyBackend = create_property_backend(); - davrq->notFoundProps = NULL; - davrq->forbiddenProps = NULL; - davrq->out = sbuf_new(512); - - /* write header */ + + /* write xml response header */ sbuf_puts(davrq->out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\n"); - - /* get stat of file */ + + /* begin multistatus response */ + int is_dir = 0; + char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); - char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); - + struct stat st; if(stat(ppath, &st) != 0) { - perror("webdav_service: stat"); + perror("webdav_propfind: stat"); return REQ_ABORTED; - } - /* TODO: check for more modes */ + } + + /* + * if the requested webdav resource(file) is a directory, we create + * a response for every child + */ if(S_ISDIR(st.st_mode)) { DIR *dir = opendir(ppath); if(dir == NULL) { protocol_status(sn, rq, 500, NULL); - printf("webdav_service: DIR is null\n"); + printf("webdav_propfind: DIR is null\n"); return REQ_ABORTED; } - + struct dirent *f; while((f = readdir(dir)) != NULL) { if(strcmp(f->d_name, ".") == 0 || strcmp(f->d_name, "..") == 0) { continue; } - + sstr_t filename = sstr(f->d_name); - sstr_t _path = sstr(ppath); + sstr_t _path = sstr(ppath); sstr_t _uri = sstr(uri); - + sstr_t newuri; newuri.length = filename.length + _uri.length; newuri.ptr = alloca(newuri.length + 1); newuri = sstrncat(2, newuri, _uri, filename); - + sstr_t newpath; newpath.length = _path.length + filename.length; newpath.ptr = alloca(newpath.length + 1); newpath = sstrncat(2, newpath, _path, filename); - - davrq->path = newpath.ptr; - davrq->uri = newuri.ptr; - dav_create_response(davrq); + + /* child response */ + dav_resource_response(davrq, newpath, newuri); } } - davrq->path = ppath; - davrq->uri = uri; - dav_create_response(davrq); - + + /* create the response for the requested resource */ + dav_resource_response(davrq, sstr(ppath), sstr(uri)); + + /* end xml */ sbuf_puts(davrq->out, "</D:multistatus>\n"); - - /* send buffer to client */ + + /* send the xml response to the client */ + protocol_status(sn, rq, 207, "Multi Status"); pblock_removekey(pb_key_content_type, rq->srvhdrs); pblock_nvinsert("content-type", "text/xml", rq->srvhdrs); pblock_nninsert("content-length", davrq->out->length, rq->srvhdrs); - - protocol_status(sn, rq, 207, NULL); + + pblock_nvinsert("connection", "close", rq->srvhdrs); http_start_response(sn, rq); - + net_write(sn->csd, davrq->out->ptr, davrq->out->length); + + + return REQ_PROCEED; } -int dav_foreach_reqprop(UcxDlist *list, PropfindRequest *davrq) { - DavProperty *prop = list->data; - int error = 0; +void dav_resource_response(PropfindRequest *davrq, sstr_t path, sstr_t uri) { + printf("dav_resource_response %s %s\n", sstrdub(path).ptr, sstrdub(uri).ptr); + + sbuf_puts(davrq->out, "<D:response>\n"); + sbuf_puts(davrq->out, "<D:href>"); + sbuf_append(davrq->out, uri); + sbuf_puts(davrq->out, "</D:href>\n"); - char *str = davrq->propertyBackend->get_property( - davrq->propertyBackend, - davrq, - davrq->path, - prop->xmlns, - prop->name, - &error); - if(str == NULL) { - UcxDlist **dl = NULL; - if(error == 404) { - dl = &davrq->notFoundProps; - } else { - dl = &davrq->forbiddenProps; - } - *dl = ucx_dlist_append(*dl, prop); - } else { - //printf("dav property: {%s|%s::%s\n", prop->xmlns, prop->name, str); - sbuf_puts(davrq->out, "<D:"); - sbuf_puts(davrq->out, prop->name); - sbuf_puts(davrq->out, ">"); - sbuf_puts(davrq->out, str); - sbuf_puts(davrq->out, "</D:"); - sbuf_puts(davrq->out, prop->name); - sbuf_puts(davrq->out, ">\n"); + davrq->propertyBackend->propfind(davrq->propertyBackend, davrq, path.ptr); + + if(davrq->prop) { + /* + * there are some properties written, so we close the + * prop and propstat tag + */ + sbuf_puts(davrq->out, "</D:prop>\n"); + sbuf_puts(davrq->out, "<D:status>HTTP/1.1 200 OK</D:status>\n"); + sbuf_puts(davrq->out, "</D:propstat>\n"); } - return 0; -} - -void dav_create_response(PropfindRequest *davrq) { - sbuf_puts(davrq->out, "<D:response>\n"); - - sbuf_puts(davrq->out, "<D:href>"); - sbuf_puts(davrq->out, davrq->uri); - sbuf_puts(davrq->out, "</D:href>\n"); - - sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); - ucx_dlist_foreach( - davrq->properties, - (ucx_callback)dav_foreach_reqprop, - davrq); - - sbuf_puts(davrq->out, "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n"); - sbuf_puts(davrq->out, "</D:propstat>\n"); - - /* 404 props */ - sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); - UcxDlist *dl = davrq->notFoundProps; - while(dl != NULL) { - DavProperty *nfp = dl->data; - sbuf_puts(davrq->out, "<D:"); - sbuf_puts(davrq->out, nfp->name); - sbuf_puts(davrq->out, " />\n"); - dl = dl->next; + if(davrq->notFoundProps != NULL) { + sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); + DAV_FOREACH(elm, davrq->notFoundProps) { + DavProperty *prop = (DavProperty*)elm->data; + sbuf_puts(davrq->out, "<D:"); + sbuf_puts(davrq->out, prop->name); + sbuf_puts(davrq->out, " />\n"); + } + sbuf_puts(davrq->out, "</D:prop>\n"); + sbuf_puts(davrq->out, "<D:status>HTTP/1.1 404 Not Found</D:status>\n"); + sbuf_puts(davrq->out, "</D:propstat>\n"); } - sbuf_puts(davrq->out, "</D:prop>\n"); - sbuf_puts(davrq->out, "<D:status>HTTP/1.1 404 Not Found</D:status>\n"); - sbuf_puts(davrq->out, "</D:propstat>\n"); - - /* end */ + sbuf_puts(davrq->out, "</D:response>\n"); + /* reset */ + davrq->prop = 0; + davrq->notFoundProps = NULL; + davrq->forbiddenProps = NULL; + } -char* dav_get_property( - DAVPropertyBackend *b, +void dav_propfind_add_str_prop( PropfindRequest *davrq, - char *path, - char *xmlns, - char *name, - int *error) + DavProperty* prop, + char *str, + size_t len) { - DAVDefaultBackend *be = (DAVDefaultBackend*)b; - *error = 200; + if(!davrq->prop) { + sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); + davrq->prop = 1; + } + + sbuf_puts(davrq->out, "<D:"); + sbuf_puts(davrq->out, prop->name); + sbuf_puts(davrq->out, ">"); + + sbuf_append(davrq->out, sstrn(str, len)); + + sbuf_puts(davrq->out, "</D:"); + sbuf_puts(davrq->out, prop->name); + sbuf_puts(davrq->out, ">\n"); +} - if(strcmp(name, "getcontentlength") == 0) { - struct stat s; - if(stat(davrq->path, &s) != 0) { - *error = 403; /* really? */ - return NULL; - } - if(S_ISDIR(s.st_mode)) { - *error = 404; - return NULL; - } - char *buf = pool_malloc(davrq->sn->pool, 24); - sprintf(buf, "%d", s.st_size); - return buf; - } - - *error = 404; - return NULL; +void dav_propfind_add_prop_error( + PropfindRequest *davrq, + DavProperty *prop, + int error) +{ + davrq->notFoundProps = ucx_dlist_append(davrq->notFoundProps, prop); } + + + +/* WebDAV Default Backend */ DAVPropertyBackend* create_property_backend() { - DAVDefaultBackend *backend = malloc(sizeof(DAVDefaultBackend)); - backend->backend.get_property = dav_get_property; - backend->path = NULL; - backend->s = 0; - return (DAVPropertyBackend*)backend; + DAVPropertyBackend *pb = malloc(sizeof(DAVPropertyBackend)); + if(pb == NULL) { + // + } + pb->propfind = dav_rq_propfind; + return pb; } + +void dav_rq_propfind(DAVPropertyBackend *b, PropfindRequest *rq ,char *path) { + struct stat st; + if(stat(path, &st) != 0) { + perror("dav_be_propfind"); + fprintf(stderr, "Cannot get stat of file: %s\n", path); + } + + DAV_FOREACH(elm, rq->properties) { + DavProperty *prop = (DavProperty*)elm->data; + + char *s = prop->name; + if(!strcmp(s, "resourcetype")) { + if(S_ISDIR(st.st_mode)) { + dav_propfind_add_str_prop(rq, prop, "<D:collection/>", 15); + } else { + dav_propfind_add_str_prop(rq, prop, NULL, 0); + } + } else if(!strcmp(s, "getcontentlength") && !S_ISDIR(st.st_mode)) { + char buf[32]; + size_t n = snprintf(buf, 32, "%d", st.st_size); + dav_propfind_add_str_prop(rq, prop, buf, n); + } else { + dav_propfind_add_prop_error(rq, prop, 404); + } + } +}
--- a/src/server/webdav/webdav.h Sat Feb 25 12:43:26 2012 +0100 +++ b/src/server/webdav/webdav.h Sun Feb 26 19:51:14 2012 +0100 @@ -42,20 +42,24 @@ extern "C" { #endif -typedef struct PropfindResponse PropfindResponse; -typedef struct DAVPropertyBackend DAVPropertyBackend; +#define DAV_FOREACH(elem, list) \ + for (UcxDlist *elem = list ; elem != NULL ; elem = elem->next) + +typedef struct PropfindResponse PropfindResponse; +typedef struct DAVPropertyBackend DAVPropertyBackend; -typedef struct PropfindRequest PropfindRequest; -typedef struct DavProperty DavProperty; +typedef struct PropfindRequest PropfindRequest; +typedef struct DavProperty DavProperty; struct PropfindRequest { Session *sn; Request *rq; - UcxDlist *properties; /* DavProperty list */ + UcxDlist *properties; /* DavProperty list, requested props */ int8_t allprop; int8_t propname; - + + int8_t prop; UcxDlist *notFoundProps; UcxDlist *forbiddenProps; DAVPropertyBackend *propertyBackend; @@ -71,41 +75,60 @@ char *name; }; -typedef char*(*prop_get_func)( - DAVPropertyBackend*, - PropfindRequest*, - char*, - char*, - char*, - int*); +/* + * dav_res_propfind_f + * + * Gets all requested properties of a WebDAV resource(file). This function + * should add properties with dav_propfind_add_str_prop or + * dav_prop_add_xml_prop. Unavailable properties should be added with + * + * + * arg0: property backend + * arg1: current PropfindRequest object + * arg2: the webdav resource path + */ +typedef void(*dav_res_propfind_f)(DAVPropertyBackend*,PropfindRequest*,char*); struct DAVPropertyBackend { - prop_get_func get_property; + dav_res_propfind_f propfind; }; int webdav_service(pblock *pb, Session *sn, Request *rq); +int webdav_propfind(pblock *pb, Session *sn, Request *rq); -void dav_create_response(PropfindRequest *davrq); +void dav_resource_response(PropfindRequest *rq, sstr_t path, sstr_t uri); -int dav_foreach_reqprop(UcxDlist *list, PropfindRequest *davrq); +/* + * dav_propfind_add_str_prop + * + * Adds a string property to the current WebDAV resource response + */ +void dav_propfind_add_str_prop( + PropfindRequest *rq, + DavProperty* prop, + char *str, + size_t len); -char* dav_get_property( - DAVPropertyBackend *b, - PropfindRequest *davrq, - char *path, - char *xmlns, - char *name, - int *error); +//void dav_propfind_add_xml_prop(); + +/* + * dav_propfind_add_prop_error + * + * Adds a unavailable property to the response + * + * rq: propfind request object + * prop: unavailable property + * error: HTTP status code + */ +void dav_propfind_add_prop_error( + PropfindRequest *rq, + DavProperty *prop, + int error); + + DAVPropertyBackend* create_property_backend(); - -typedef struct { - DAVPropertyBackend backend; - char *path; - struct stat st; - int s; -} DAVDefaultBackend; - +void dav_rq_propfind(DAVPropertyBackend *b,PropfindRequest *rq ,char *path); #ifdef __cplusplus }