diff -r e8619defde14 -r 27c7511c0e34 src/server/webdav/webdav.c --- a/src/server/webdav/webdav.c Wed May 16 12:47:28 2012 +0200 +++ b/src/server/webdav/webdav.c Thu May 24 12:51:52 2012 +0200 @@ -36,6 +36,8 @@ #include "../util/pblock.h" #include "../util/date.h" +#include "../daemon/protocol.h" + #include "davparser.h" int webdav_service(pblock *pb, Session *sn, Request *rq) { @@ -50,6 +52,10 @@ return webdav_proppatch(pb, sn, rq); } else if(!strcmp(method, "PUT")) { return webdav_put(pb, sn, rq); + } else if(!strcmp(method, "DELETE")) { + return webdav_delete(pb, sn, rq); + } else if(!strcmp(method, "MKCOL")) { + return webdav_mkcol(pb, sn, rq); } return REQ_NOACTION; @@ -106,6 +112,64 @@ return REQ_PROCEED; } +int webdav_delete(pblock *pb, Session *sn, Request *rq) { + char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); + char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); + + int status = 204; + + struct stat st; + if(stat(ppath, &st) != 0) { + /* ERROR */ + status = 403; /* TODO: check errno */ + } + + if(!strcmp(uri, "/")) { + status = 403; + } else if((st.st_mode & S_IFDIR) == S_IFDIR) { + if(rmdir(ppath) != 0) { + /* ERROR */ + status = 403; + } + } else { + if(unlink(ppath) != 0) { + /* ERROR */ + status = 403; /* TODO: check errno */ + } + } + + protocol_status(sn, rq, status, NULL); + pblock_removekey(pb_key_content_type, rq->srvhdrs); + pblock_nninsert("content-length", 0, rq->srvhdrs); + http_start_response(sn, rq); + + return REQ_PROCEED; +} + +int webdav_mkcol(pblock *pb, Session *sn, Request *rq) { + char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); + + int status = 201; + if(mkdir(ppath, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + status = 403; + } + + protocol_status(sn, rq, status, NULL); + pblock_removekey(pb_key_content_type, rq->srvhdrs); + pblock_nninsert("content-length", 0, rq->srvhdrs); + http_start_response(sn, rq); + + return REQ_ABORTED; +} + +int webdav_copy(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_move(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + int webdav_propfind(pblock *pb, Session *sn, Request *rq) { /* TODO: clean up if errors occurs */ @@ -162,6 +226,7 @@ struct stat st; if(stat(ppath, &st) != 0) { perror("webdav_propfind: stat"); + fprintf(stderr, " file: %s\n", ppath); return REQ_ABORTED; } @@ -246,8 +311,14 @@ } int webdav_proppatch(pblock *pb, Session *sn, Request *rq) { + printf("webdav-proppatch\n"); /* TODO: clean up if errors occurs */ /* TODO: this is the same code as in propfind */ + char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); + if(uri == NULL) { + /* TODO: error */ + return REQ_ABORTED; + } /* Get request body which contains the webdav XML request */ char *xml_body; @@ -282,7 +353,58 @@ xl -= r; } + /* + * parse the xml request and create the proppatch object + */ + ProppatchRequest *davrq = dav_parse_proppatch(sn, rq, xml_body, xml_len); + davrq->sn = sn; + davrq->rq = rq; + davrq->out = sbuf_new(512); + davrq->backend = create_property_backend(); + davrq->propstat = propstat_create(sn->pool); + /* TODO: create prefixes for every namespace */ + XmlNs *ns = xmlnsmap_get(davrq->nsmap, "DAV:"); + ns->prefix = "D"; + ns->prelen = 1; + + /* + * begin multistatus response + * + * The webdav backend does the most work. The backend->proppatch function + * modifies the properties and adds status informations to the propstat + * member of the ProppatchRequest. All we have to do here is to create + * the xml response and send it to the client + */ + + /* write xml response header */ + /* TODO: add possible xml namespaces */ + sbuf_puts(davrq->out, "\n"); + sbuf_puts(davrq->out, "\n"); + + sbuf_puts(davrq->out, "\n"); + sbuf_puts(davrq->out, uri); + sbuf_puts(davrq->out, "\n"); + + /* do proppatch operation */ + davrq->backend->proppatch(davrq->backend, davrq); + + propstat_write(davrq->propstat, davrq->out, 0); + + sbuf_puts(davrq->out, "\n"); + sbuf_puts(davrq->out, "\n"); + + + /* 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); + + pblock_nvinsert("connection", "close", rq->srvhdrs); + http_start_response(sn, rq); + + net_write(sn->csd, davrq->out->ptr, davrq->out->length); return REQ_PROCEED; } @@ -368,7 +490,8 @@ if(pb == NULL) { // } - pb->propfind = dav_rq_propfind; + pb->propfind = dav_rq_propfind; + pb->proppatch = dav_rq_proppatch; return pb; } @@ -404,3 +527,174 @@ } } } + +void dav_rq_proppatch(DAVPropertyBackend *b, ProppatchRequest *rq) { + DAV_FOREACH(p, rq->setProps) { + XmlElement *prop = (XmlElement*)p->data; + propstat_add(rq->propstat, 403, prop); + } + + DAV_FOREACH(p, rq->removeProps) { + XmlElement *prop = (XmlElement*)p->data; + propstat_add(rq->propstat, 403, prop); + } +} + + + +/*---------------------------------- utils ----------------------------------*/ + +/* XmlNsMap */ + +XmlNsMap* xmlnsmap_create() { + XmlNsMap *map = malloc(sizeof(XmlNsMap)); + UcxMap *uxm = ucx_map_new(16); + if(map == NULL || uxm == NULL) { + return NULL; + } + map->map = uxm; + return map; +} + +void xmlnsmap_free(XmlNsMap *map) { + /* TODO: implement */ +} + +XmlNs* xmlnsmap_put(XmlNsMap *map, char *ns) { + XmlNs *xmlns = xmlnsmap_get(map, ns); + if(xmlns != NULL) { + return xmlns; + } + + xmlns = malloc(sizeof(XmlNs)); + if(xmlns == NULL) { + return NULL; + } + + xmlns->xmlns = ns; + xmlns->nslen = strlen(ns); + + xmlns->prefix = NULL; + xmlns->prelen = 0; + + ucx_map_cstr_put(map->map, ns, xmlns); /* TODO: check return value */ + return xmlns; +} + +XmlNs* xmlnsmap_get(XmlNsMap *map, char *ns) { + return ucx_map_cstr_get(map->map, ns); +} + + +/* XmlElement */ + +void xmlelm_add_child(XmlElement *parent, XmlElement *child) { + if(parent->ctlen == 0) { + parent->content = ucx_dlist_append(parent->content, child); + } +} + +void xmlelm_write(XmlElement *elm, sbuf_t *out, int wv) { + sbuf_append(out, sstrn("<", 1)); + sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); + sbuf_append(out, sstrn(":", 1)); + sbuf_append(out, elm->name); + + if(wv) { + if(elm->ctlen == 0) { + if(elm->content == NULL) { + sbuf_append(out, sstrn(" />", 3)); + } else { + sbuf_append(out, sstrn(">", 1)); + DAV_FOREACH(pr, (UcxDlist*)elm->content) { + xmlelm_write((XmlElement*)pr->data, out, 1); + } + sbuf_append(out, sstrn("xmlns->prefix, elm->xmlns->prelen)); + sbuf_append(out, sstrn(":", 1)); + sbuf_append(out, elm->name); + sbuf_append(out, sstrn(">", 1)); + } + } else { + sbuf_append(out, sstrn(" />", 3)); + sbuf_append(out, sstrn((char*)elm->content, elm->ctlen)); + sbuf_append(out, sstrn("xmlns->prefix, elm->xmlns->prelen)); + sbuf_append(out, sstrn(":", 1)); + sbuf_append(out, elm->name); + sbuf_append(out, sstrn(">", 1)); + } + } else { + sbuf_append(out, sstrn(" />", 3)); + } +} + + +/* PropstatMap */ + +Propstat* propstat_create(pool_handle_t *pool) { + Propstat *propstat = (Propstat*)pool_malloc(pool, sizeof(Propstat)); + propstat->map = ucx_map_new(8); + propstat->okprop = NULL; + propstat->pool = pool; + return propstat; +} + +void propstat_add(Propstat *propstat, int status, XmlElement *prop) { + if(status == 200) { + propstat->okprop = ucx_dlist_append(propstat->okprop, prop); + } else { + UcxKey key; + key.data = &status; + key.len = sizeof(int); + + UcxDlist *list = ucx_map_get(propstat->map, key); + list = ucx_dlist_append(list, prop); + + ucx_map_put(propstat->map, key, list); + } +} + +void propstat_write(Propstat *propstat, sbuf_t *out, int wv) { + if(propstat->okprop) { + sbuf_puts(out, "\n\n"); + + DAV_FOREACH(prop, propstat->okprop) { + xmlelm_write((XmlElement*)prop->data, out, wv); + } + + sbuf_puts(out, "\n\nHTTP/1.1 200 OK\n"); + sbuf_puts(out, "\n"); + } + + for(int i=0;imap->size;i++) { + UcxMapElement *elm = &propstat->map->map[i]; + while(elm) { + UcxDlist *proplist = (UcxDlist*)elm->data; + + if(proplist) { + sbuf_puts(out, "\n\n"); + + DAV_FOREACH(prop, proplist) { + xmlelm_write((XmlElement*)prop->data, out, wv); + } + + sbuf_puts(out, "\n\n"); + + int status = *(int*)elm->key.data; + if(status < 1000 && status > 0) { + char buf[5]; + buf[4] = 0; + sprintf(buf, "%d ", status); + sbuf_puts(out, "HTTP/1.1 "); + sbuf_puts(out, buf); + sbuf_puts(out, (char*)protocol_status_message(status)); + } + + sbuf_puts(out, "\n\n"); + } + + elm = elm->next; + } + } +}