Minimal webdav support

Sun, 26 Feb 2012 19:51:14 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 26 Feb 2012 19:51:14 +0100
changeset 26
37ff8bf54b89
parent 25
5dee29c7c530
child 27
05b7576dca2b

Minimal webdav support

src/server/Makefile file | annotate | diff | comparison | revisions
src/server/daemon/httprequest.c file | annotate | diff | comparison | revisions
src/server/webdav/davparser.cpp file | annotate | diff | comparison | revisions
src/server/webdav/webdav.c file | annotate | diff | comparison | revisions
src/server/webdav/webdav.h file | annotate | diff | comparison | revisions
--- 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
 }

mercurial