src/server/webdav/webdav.c

changeset 30
27c7511c0e34
parent 29
e8619defde14
child 31
280250e45ba6
--- 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, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+    sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\n");
+    
+    sbuf_puts(davrq->out, "<D:response>\n<D:href>");
+    sbuf_puts(davrq->out, uri);
+    sbuf_puts(davrq->out, "</D:href>\n");
+    
+    /* do proppatch operation */
+    davrq->backend->proppatch(davrq->backend, davrq);
+    
+    propstat_write(davrq->propstat, davrq->out, 0);
+    
+    sbuf_puts(davrq->out, "</D:response>\n");
+    sbuf_puts(davrq->out, "</D:multistatus>\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("</", 2));
+                sbuf_append(out, sstrn(elm->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("</", 2));
+            sbuf_append(out, sstrn(elm->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, "<D:propstat>\n<D:prop>\n");
+        
+        DAV_FOREACH(prop, propstat->okprop) {    
+            xmlelm_write((XmlElement*)prop->data, out, wv);
+        }
+        
+        sbuf_puts(out, "\n</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n");
+        sbuf_puts(out, "</D:propstat>\n");
+    }
+    
+    for(int i=0;i<propstat->map->size;i++) {
+        UcxMapElement *elm = &propstat->map->map[i];
+        while(elm) {
+            UcxDlist *proplist = (UcxDlist*)elm->data;
+            
+            if(proplist) {
+                sbuf_puts(out, "<D:propstat>\n<D:prop>\n");
+                
+                DAV_FOREACH(prop, proplist) {
+                    xmlelm_write((XmlElement*)prop->data, out, wv);
+                }
+                
+                sbuf_puts(out, "\n</D:prop>\n<D:status>");
+                
+                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, "</D:status>\n</D:propstat>\n");
+            }
+            
+            elm = elm->next;
+        }
+    }
+}

mercurial