some code cleanup and bug fixes

Mon, 26 Aug 2013 14:42:09 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 26 Aug 2013 14:42:09 +0200
changeset 33
0bbbb0341606
parent 32
c9d37bb97ea8
child 34
1b87b3b1921e

some code cleanup and bug fixes

Makefile file | annotate | diff | comparison | revisions
dav/Makefile file | annotate | diff | comparison | revisions
dav/config.c file | annotate | diff | comparison | revisions
dav/crypto.h file | annotate | diff | comparison | revisions
dav/main.c file | annotate | diff | comparison | revisions
dav/main.h file | annotate | diff | comparison | revisions
libidav/Makefile file | annotate | diff | comparison | revisions
libidav/davql.c file | annotate | diff | comparison | revisions
libidav/davql.h file | annotate | diff | comparison | revisions
libidav/methods.c file | annotate | diff | comparison | revisions
libidav/methods.h file | annotate | diff | comparison | revisions
libidav/resource.c file | annotate | diff | comparison | revisions
libidav/resource.h file | annotate | diff | comparison | revisions
libidav/utils.c file | annotate | diff | comparison | revisions
libidav/utils.h file | annotate | diff | comparison | revisions
libidav/webdav.c file | annotate | diff | comparison | revisions
libidav/webdav.h file | annotate | diff | comparison | revisions
ucx/Makefile file | annotate | diff | comparison | revisions
--- a/Makefile	Fri Aug 23 11:04:07 2013 +0200
+++ b/Makefile	Mon Aug 26 14:42:09 2013 +0200
@@ -39,22 +39,27 @@
 
 include $(CONF).mk
 
-all: build/tool ucx dav
+all: build ucx libidav dav
 
-build/tool:
+build:
 	mkdir -p build/tool
+	mkdir -p build/ucx
+	mkdir -p build/libidav
 
-ucx: FORCE build
+ucx: FORCE
 	cd ucx; $(MAKE) CONF=$(CONF) all
+
+libidav: FORCE
+	cd libidav; $(MAKE) CONF=$(CONF) all
 	
-dav: FORCE ucx
+dav: FORCE libidav ucx
 	cd dav; $(MAKE) CONF=$(CONF) all
 
 run: FORCE dav
 	./build/dav$(APP_EXT)
 
 clean: FORCE
-	$(RM) $(RMFLAGS) build/*.${OBJ_EXT} build/tool/*.${OBJ_EXT}
+	$(RM) $(RMFLAGS) -R build/
 
 FORCE:
 
--- a/dav/Makefile	Fri Aug 23 11:04:07 2013 +0200
+++ b/dav/Makefile	Mon Aug 26 14:42:09 2013 +0200
@@ -29,10 +29,6 @@
 include ../$(CONF).mk
 
 SRC  = main.c
-SRC += utils.c
-SRC += webdav.c
-SRC += methods.c
-SRC += davql.c
 SRC += config.c
 SRC += crypto.c
 SRC += optparser.c
@@ -44,7 +40,8 @@
 
 ../build/dav: $(OBJ)
 	$(LD) -o ../build/dav$(APP_EXT) $(OBJ) \
-		../build/libucx.$(LIB_EXT) $(LDFLAGS) $(DAV_LDFLAGS)
+		../build/libidav.$(LIB_EXT) ../build/libucx.$(LIB_EXT)  \
+		$(LDFLAGS) $(DAV_LDFLAGS)
 
 ../build/tool/%.$(OBJ_EXT): %.c 
 	$(CC) $(CFLAGS) $(DAV_CFLAGS) -I../ -o $@ $<
--- a/dav/config.c	Fri Aug 23 11:04:07 2013 +0200
+++ b/dav/config.c	Mon Aug 26 14:42:09 2013 +0200
@@ -35,7 +35,7 @@
 #include <libxml/tree.h>
 
 #include "config.h"
-#include "utils.h"
+#include <libidav/utils.h>
 
 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
 
--- a/dav/crypto.h	Fri Aug 23 11:04:07 2013 +0200
+++ b/dav/crypto.h	Mon Aug 26 14:42:09 2013 +0200
@@ -29,7 +29,7 @@
 #ifndef DAV_CRYPTO_H
 #define	DAV_CRYPTO_H
 
-#include "webdav.h"
+#include <libidav/webdav.h>
 #include "config.h"
 #include <openssl/evp.h>
 
--- a/dav/main.c	Fri Aug 23 11:04:07 2013 +0200
+++ b/dav/main.c	Mon Aug 26 14:42:09 2013 +0200
@@ -38,13 +38,11 @@
 #include <dirent.h>
 
 
-#include "utils.h"
+#include <libidav/utils.h>
 #include "config.h"
 #include "crypto.h"
 #include "main.h"
 
-#include "davql.h"
-
 static DavContext *ctx;
 
 void xmlerrorfnc(void * c, const char * msg, ... ) {
@@ -52,20 +50,28 @@
 }
 
 int main(int argc, char **argv) {
+    
+    char *xargv[4];
+    xargv[0] = "dav";
+    xargv[1] = "put";
+    xargv[2] = "sunfire";
+    xargv[3] = "config.xml";
+    int xargc = 4;
+    
     xmlGenericErrorFunc fnc = xmlerrorfnc;
     initGenericErrorDefaultFunc(&fnc);
     load_config();
     ctx = dav_context_new();
     dav_add_namespace(ctx, "U", "http://www.uap-core.de/");
     
-    if(argc < 2) {
+    if(xargc < 2) {
         fprintf(stderr, "Missing command\n");
         print_usage(argv[0]);
         return -1;
     }
     
-    char *cmd = argv[1];
-    CmdArgs *args = cmd_parse_args(argc - 2, argv + 2);
+    char *cmd = xargv[1];
+    CmdArgs *args = cmd_parse_args(xargc - 2, xargv + 2);
     if(!args) {
         print_usage(argv[0]);
         return -1;
@@ -77,6 +83,7 @@
     } else if(!strcasecmp(cmd, "get")) {
         ret = cmd_get(args);
     } else if(!strcasecmp(cmd, "put")) {
+        printf("put\n");
         ret = cmd_put(args);
     } else if(
             !strcasecmp(cmd, "remove") ||
@@ -99,11 +106,11 @@
 void print_usage(char *cmd) {
     fprintf(stderr, "Usage: %s command [options] arguments...\n\n", cmd);
     fprintf(stderr, "Commands:\n");
-    fprintf(stderr, "list [-altR] [-u <date>] <url>\n");
+    fprintf(stderr, "        list [-altR] [-u <date>] <url>\n");
     fprintf(
             stderr,
             "        get [-pR] [-k <key>] [-o <file>] [-u <date>] <url>\n");
-    fprintf(stderr, "        put [-p] [-k <key>] <url> <file>\n");
+    fprintf(stderr, "        put [-pR] [-k <key>] <url> <file>\n");
     fprintf(stderr, "        mkdir <url>\n");
     fprintf(stderr, "        remove <url>\n");
     fprintf(stderr, "        date [url]\n");
@@ -643,7 +650,7 @@
         FILE *in = stdin;
         ret = put_file(repo, a, sn, path, "stdin", in);
     } else {
-        ret = put_entry(repo, a, sn, path, file);        
+        ret = put_entry(repo, a, sn, path, file); 
     }
     
     return ret;
@@ -698,7 +705,7 @@
         char *filename = util_resource_name(file);
         //path = util_concat_path(path, filename);
         ret = put_file(repo, a, sn, path, filename, in);
-        free(path);
+        //free(path);
         fclose(in);
     }
     
@@ -888,3 +895,12 @@
     }
     return 0;
 }
+
+int cmd_sync(CmdArgs *a) {
+    if(a->argc < 2) {
+        fprintf(stderr, "Too few arguments\n");
+        return -1;
+    }
+    
+    return 0;
+}
--- a/dav/main.h	Fri Aug 23 11:04:07 2013 +0200
+++ b/dav/main.h	Mon Aug 26 14:42:09 2013 +0200
@@ -31,7 +31,7 @@
 
 #include <curl/curl.h>
 #include "optparser.h"
-#include "webdav.h"
+#include <libidav/webdav.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -55,6 +55,8 @@
 
 int cmd_date(CmdArgs *args);
 
+int cmd_sync(CmdArgs *args);
+
 #ifdef	__cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/Makefile	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,49 @@
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2013 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   1. Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above copyright
+#      notice, this list of conditions and the following disclaimer in the
+#      documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+include ../$(CONF).mk
+
+# list of source files
+SRC  = webdav.c
+SRC += resource.c
+SRC += methods.c
+SRC += utils.c
+SRC += davql.c
+
+OBJ = $(SRC:%.c=../build/libidav/%.$(OBJ_EXT))
+
+all: libidav
+
+libidav: $(OBJ)
+	$(AR) $(ARFLAGS) $(AOFLAGS)../build/libidav.$(LIB_EXT) $(OBJ)
+
+../build/libidav/%.$(OBJ_EXT): %.c ../build/ucx
+	$(CC) $(CFLAGS) $(DAV_CFLAGS) -I../ $(COFLAGS)$@ $<
+
+../build/idav:
+	mkdir -p ../build/idav
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/davql.c	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,702 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "davql.h"
+#include "methods.h"
+#include "utils.h"
+
+DavQuery dav_ql_parse(char *query, va_list ap) {
+    DavQuery davquery;
+    davquery.command = DAV_QUERY_ERROR;
+    davquery.command_data = NULL;
+    sstr_t q = sstr(query);
+    q = sstrtrim(q);
+    
+    // get query command
+    sstr_t cmd;
+    cmd.ptr = NULL;
+    int i;
+    for(i=0;i<q.length;i++) {
+        if(q.ptr[i] == ' ') {
+            cmd = sstrsubsl(q, 0, i);
+            break;
+        }
+    }
+    if(!cmd.ptr) {
+        fprintf(stderr, "DQL syntax error\n");
+        return davquery;
+    }
+    
+    cmd = sstrtrim(cmd);
+    q = sstrtrim(sstrsubs(q, i));
+    if(!sstrcmp(cmd, S("get"))) {
+        davquery.command = DAV_QUERY_GET;
+        davquery.command_data = dav_ql_parse_get(q, ap);
+    }
+    
+    return davquery;
+}
+
+DavGetQuery* dav_ql_parse_get(sstr_t q, va_list ap) {  
+    sstr_t property_query = q;
+    q = util_getsubstr_until_token(q, S("from"), &property_query);
+    
+    sstr_t from_query = q;
+    sstr_t cond = util_getsubstr_until_token(q, S("where"), &from_query);
+    
+    // insert variable values
+    UcxBuffer *fbuf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
+    int var = 0;
+    for(int i=0;i<from_query.length;i++) {
+        char c = from_query.ptr[i];
+        if(c == '%') {
+            if(var) {
+                ucx_buffer_putc(fbuf, '%'); // previous '%'
+            } else {
+                var = 1;
+            }
+        } else if(var) {
+            switch(c) {
+                case 's': {
+                    char *arg = va_arg(ap, char*);
+                    ucx_buffer_puts(fbuf, arg);
+                    break;
+                }
+                default: {
+                    ucx_buffer_putc(fbuf, '%');
+                    ucx_buffer_putc(fbuf, c);
+                }
+            }
+            var = 0;
+        } else {
+            ucx_buffer_putc(fbuf, c);
+        }
+    }
+    
+    // condition
+    DavQOp *condition = NULL;
+    size_t oplen = 0;
+    if(cond.ptr) {
+        //printf("cond: {%.*s}\n", cond.length, cond.ptr);
+        UcxList *ops = NULL;
+        if(dav_parse_condition(&ops, cond, ap)) {
+            // TODO: error
+            printf("parse error\n");
+            return NULL;
+        }
+        oplen = ucx_list_size(ops);
+        condition = calloc(sizeof(DavQOp), oplen);
+        int l = 0;
+        UCX_FOREACH(elm, ops) {
+            condition[l] = *(DavQOp*)elm->data;
+            free(elm->data);
+            l++;
+        }
+        ucx_list_free(ops);
+    }
+    
+    DavGetQuery *getquery = malloc(sizeof(DavGetQuery));
+    getquery->properties = sstrdup(property_query);
+    getquery->from = sstrn(fbuf->space, fbuf->pos);
+    if(condition) {
+        getquery->condition = condition;
+        getquery->condlen = oplen;
+    } else {
+        getquery->condition = NULL;
+        getquery->condlen = 0;
+    }
+    return getquery;
+}
+
+void free_get_query(DavGetQuery *q) {
+    free(q->from.ptr);
+    free(q->properties.ptr);
+    free(q);
+}
+
+int parse_path_query(sstr_t query, char **path, int *depth) {
+    if(query.length == 1) {
+        if(query.ptr[0] == '/') {
+            *path = sstrdup(query).ptr;
+            *depth = 1;
+            return 0;
+        } else {
+            *path = NULL;
+            return 1;
+        }
+    }
+    
+    if(query.ptr[query.length-1] == '*') {
+        *depth = -1;
+        *path = sstrdup(sstrsubsl(query, 0, query.length-1)).ptr;
+    } else {
+        *path = sstrdup(query).ptr;
+        *depth = 1;
+    }
+    
+    return 0;
+}
+
+int dav_parse_condition(UcxList **ops, sstr_t cond, va_list ap) {
+    sstr_t token;
+    DavQOp *op1 = NULL; // level 1 operator
+    DavQOp *op2 = NULL; // level 2 operator
+    DavQOp *op3 = NULL; // level 3 operator
+    while((token = condition_parser_next_token(&cond)).length > 0) {
+        //printf("token: {%.*s}[%d]\n", token.length, token.ptr, token.length);
+        int64_t type = 0;
+        int tkop = condition_operator_type(token, &type);
+        DavQOp *operation = malloc(sizeof(DavQOp));
+        if(tkop > 0) {
+            // operator token
+            operation->type = DAVQOP_OPERATOR;
+            operation->val = NULL;
+            operation->intval = type;
+            switch(tkop) {
+                case 1: {
+                    // operators: + - / * not
+                    // add operation after next non operator token
+                    op1 = operation;
+                    break;
+                }
+                case 2: {
+                    // operators: < > == != <= >=
+                    if(op2) {
+                        *ops = ucx_list_append(*ops, op2);
+                    }
+                    op2 = operation;
+                    break;
+                }
+                case 3: {
+                    // operators: and or xor
+                    if(op2) {
+                        *ops = ucx_list_append(*ops, op2);
+                        op2 = NULL;
+                    }
+                    if(op3) {
+                        *ops = ucx_list_append(*ops, op3);
+                    }
+                    op3 = operation;
+                    break;
+                }
+            }
+        } else {
+            if(token.ptr[0] == '"' || token.ptr[0] == '\'') {
+                operation->type = DAVQOP_STRING;
+                operation->val = token.ptr+1;
+                operation->intval = token.length-2;
+            } else if(!sstrcmp(token, S("true")) ||
+                    !sstrcmp(token, S("false")))
+            {
+                operation->type = DAVQOP_INTEGER;
+                operation->val = NULL;
+                operation->intval = util_getboolean(token.ptr);
+            } else if(token.length == 2 && token.ptr[0] == '%') {
+                switch(token.ptr[1]) {
+                    case 's': {
+                        char *arg = va_arg(ap, char*);
+                        operation->type = DAVQOP_STRING;
+                        operation->val = arg;
+                        operation->intval = strlen(arg);
+                        break;
+                    }
+                    case 'd': {
+                        operation->type = DAVQOP_INTEGER;
+                        operation->val = NULL;
+                        operation->intval = va_arg(ap, int);
+                        break;
+                    }
+                    case 't': {
+                        operation->type = DAVQOP_INTEGER;
+                        operation->val = NULL;
+                        operation->intval = va_arg(ap, time_t);
+                        break;
+                    }
+                    default: {
+                        operation->type = DAVQOP_STRING;
+                        operation->val = token.ptr;
+                        operation->intval = token.length;
+                    }
+                }
+            } else {
+                sstr_t d = sstrdup(token);
+                int64_t val = 0;
+                int intval = util_strtoint(d.ptr, &val);
+                free(d.ptr);
+                if(intval) {
+                    operation->type = DAVQOP_INTEGER;
+                    operation->val = NULL;
+                    operation->intval = val;
+                } else {
+                    if(!sstrcmp(token, S("contentlength"))) {
+                        operation->type = DAVQOP_RESPROP;
+                    } else if(!sstrcmp(token, S("lastmodified"))) {
+                        operation->type = DAVQOP_RESPROP;
+                    } else if(!sstrcmp(token, S("creationdate"))) {
+                        operation->type = DAVQOP_RESPROP;
+                    } else if(!sstrcmp(token, S("name"))) {
+                        operation->type = DAVQOP_RESPROP;
+                    } else if(!sstrcmp(token, S("path"))) {
+                        operation->type = DAVQOP_RESPROP;
+                    } else if(!sstrcmp(token, S("iscollection"))) {
+                        operation->type = DAVQOP_RESPROP;
+                    } else {
+                        operation->type = DAVQOP_PROPERTY;
+                    }
+                    operation->val = token.ptr;
+                    operation->intval = token.length;
+                }
+            }
+            
+            // add operation
+            *ops = ucx_list_append(*ops, operation);
+            if(op1) {
+                // add level 1 operator
+                *ops = ucx_list_append(*ops, op1);
+                op1 = NULL;
+            }
+        }
+    }
+    if(op1) {
+        *ops = ucx_list_append(*ops, op1);
+    }
+    if(op2) {
+        *ops = ucx_list_append(*ops, op2);
+    }
+    if(op3) {
+        *ops = ucx_list_append(*ops, op3);
+    }
+    return 0;
+}
+
+sstr_t condition_parser_next_token(sstr_t *str) {
+    sstr_t s = *str;
+    sstr_t t;
+    t.ptr = NULL;
+    t.length = 0;
+    // remove leading space
+    int i;
+    for(i=0;i<s.length;i++) {
+        if(s.ptr[i] > 32) {
+            break;
+        }
+    }
+    s.length -= i;
+    s.ptr += i;
+    
+    if(s.length == 0) {
+        *str = s;
+        return t;
+    }
+    
+    // check for single char operators
+    switch(s.ptr[0]) {
+        case '<':
+        case '>':
+        case '+':
+        case '-':
+        case '*':
+        case '/': {
+            t.ptr = s.ptr;
+            t.length = 1;
+            str->ptr = s.ptr + 1;
+            str->length = s.length - 1;
+            return t;
+        }
+    }
+    
+    if(s.length > 1) {
+        // check for double char operators
+        int16_t op = *(int16_t*)s.ptr;
+        if(op == '==' || op == '!=' || op == '>=' || op == '=<') {
+            t.ptr = s.ptr;
+            t.length = 2;
+            str->ptr = s.ptr + 2;
+            str->length = s.length - 2;
+            return t;
+        }
+    } else {
+        t.ptr = s.ptr;
+        t.length = 1;
+        str->ptr = s.ptr + 1;
+        str->length = s.length - 1;
+        return t;
+    }
+    
+    // TODO: brackets
+    
+    // check for string literal
+    if(s.ptr[0] == '\'' || s.ptr[0] == '"') {
+        for(i=1;i<s.length;i++) {
+            if(s.ptr[0] == s.ptr[i]) {
+                i++;
+                break;
+            }
+        }
+        t.ptr = s.ptr;
+        t.length = i;
+        str->ptr = s.ptr + i;
+        str->length = s.length - i;
+        return t;
+    }
+    
+    for(i=0;i<s.length;i++) {
+        char c = s.ptr[i];
+        if((c < 33) || (c > 41 && c < 48) || (c > 59 && c < 63)) {
+            break;
+        }
+    }
+    t.ptr = s.ptr;
+    t.length = i;
+    str->ptr = s.ptr + i;
+    str->length = s.length - i;
+    return t;
+}
+
+int condition_operator_type(sstr_t token, int64_t *type) {
+    // returns the operator level and sets the type
+    
+    if(token.ptr[0] == '"' || token.ptr[0] == '\'' || token.ptr[0] == '(') {
+        return 0;
+    }
+    
+    if(token.length == 1) {
+        switch(token.ptr[0]) {
+            case '+': *type = 1; return 1;
+            case '-': *type = 2; return 1;
+            case '*': *type = 3; return 1;
+            case '/': *type = 4; return 1;
+            case '<': *type = 5; return 2;
+            case '>': *type = 6; return 2;
+        }
+    }
+    if(!sstrcmp(token, S("not"))) {
+        *type = 0;
+        return 1;
+    }
+    
+    if(!sstrcmp(token, S("=="))) {
+        *type = 7;
+        return 2;
+    }
+    if(!sstrcmp(token, S("!="))) {
+        *type = 8;
+        return 2;
+    }
+    if(!sstrcmp(token, S("<="))) {
+        *type = 9;
+        return 2;
+    }
+    if(!sstrcmp(token, S(">="))) {
+        *type = 10;
+        return 2;
+    }
+    
+    if(!sstrcmp(token, S("and"))) {
+        *type = 11;
+        return 3;
+    }
+    if(!sstrcmp(token, S("or"))) {
+        *type = 12;
+        return 3;
+    }
+    if(!sstrcmp(token, S("xor"))) {
+        *type = 13;
+        return 3;
+    }
+    
+    return 0;
+}
+
+int condition_eval(DavResource *res, DavQOp *cond, size_t len) {
+    DavQOp stack[128];
+    int stackpos = 0;
+    for(int i=0;i<len;i++) {
+        DavQOp op = cond[i];
+        switch(op.type) {
+            case DAVQOP_OPERATOR: {
+                if(op.intval == 0) {
+                    // not operator
+                    if(stackpos < 1) {
+                        // error
+                        printf("no data on stack\n");
+                        return 0;
+                    }
+                    int pos = stackpos-1;
+                    if(stack[pos].type == DAVQOP_INTEGER) {
+                        //printf("not %" PRId64 "\n", stack[pos].intval);
+                        stack[pos].intval = !stack[pos].intval;
+                    } else {
+                        // error
+                        printf("wrong value for 'not' operator\n");
+                        return 0;
+                    }
+                } else {
+                    DavQOp val1 = stack[stackpos-2];
+                    DavQOp val2 = stack[stackpos-1];
+                    DavQOp result;
+                    if(val1.type == DAVQOP_INTEGER) {
+                        if(val2.type == DAVQOP_INTEGER) {
+                            result = compare_intint(
+                                    op.intval,
+                                    val1.intval,
+                                    val2.intval);
+                        } else {
+                            result = compare_intstr(op.intval, val1, val2);
+                        }
+                    } else {
+                        if(val2.type == DAVQOP_INTEGER) {
+                            result = compare_strint(op.intval, val1, val2);
+                        } else {
+                            result = compare_strstr(op.intval, val1, val2);
+                        }
+                    } 
+                    stack[stackpos-2] = result;
+                    stackpos--;
+                }
+                break;
+            }
+            case DAVQOP_STRING:
+            case DAVQOP_INTEGER:
+            case DAVQOP_TIME: {
+                if(op.type == DAVQOP_STRING) {
+                    //printf("put on stack: '%s'\n", op.val);
+                } else {
+                    //printf("put on stack[%d]: %" PRId64 "\n", stackpos, op.intval);
+                }
+                stack[stackpos++] = op;
+                break;
+            }
+            case DAVQOP_PROPERTY: {
+                sstr_t pname = sstrn(op.val, op.intval);
+                pname = sstrdup(pname);
+                char *property_value = dav_get_property(res, pname.ptr);
+                free(pname.ptr);
+                DavQOp value;
+                value.type = DAVQOP_STRING;
+                if(property_value) {
+                    //printf("put on stack: \"%s\"\n", property_value);
+                    value.val = property_value;
+                    value.intval = strlen(property_value);
+                } else {
+                    //printf("put on stack: null string\n");
+                    value.val = NULL;
+                    value.intval = 0;
+                }
+                stack[stackpos++] = value;
+                break;
+            }
+            case DAVQOP_RESPROP: {
+                sstr_t name = sstrn(op.val, op.intval);
+                DavQOp value;
+                value.type = DAVQOP_INTEGER;
+                value.val = NULL;
+                if(!sstrcmp(name, S("contentlength"))) {
+                    //printf("put contentlength\n");
+                    value.intval = res->contentlength;
+                } else if(!sstrcmp(name, S("lastmodified"))) {
+                    //printf("put getlastmodified\n");
+                    value.intval = res->lastmodified;
+                } else if(!sstrcmp(name, S("creationdate"))) {
+                    value.intval = res->creationdate;
+                } else if(!sstrcmp(name, S("name"))) {
+                    value.type = DAVQOP_STRING;
+                    value.val = res->name;
+                    value.intval = strlen(res->name);
+                } else if(!sstrcmp(name, S("path"))) {
+                    value.type = DAVQOP_STRING;
+                    value.val = res->path;
+                    value.intval = strlen(res->path);
+                } else if(!sstrcmp(name, S("iscollection"))) {
+                    value.type = DAVQOP_INTEGER;
+                    value.val = NULL;
+                    value.intval = res->iscollection;
+                }
+                stack[stackpos++] = value;
+                break;
+            }
+        }
+    }
+    if(stackpos != 1) {
+        return 0;
+    }
+    DavQOp result = stack[0];
+    //printf("result: %" PRId64 "\n", result.intval);
+    return (int)result.intval;
+}
+
+DavQOp compare_intint(int op, int64_t v1, int64_t v2) {
+    DavQOp res;
+    res.type = DAVQOP_INTEGER;
+    res.val = NULL;
+    res.intval = 0;
+    switch(op) {
+        case 5: {
+            // <
+            //printf("compare: %" PRId64 " < %" PRId64 "\n", v1, v2);
+            res.intval = v1 < v2;
+            break;
+        }
+        case 6: {
+            // >
+            //printf("compare: %" PRId64 " > %" PRId64 "\n", v1, v2);
+            res.intval = v1 > v2;
+            break;
+        }
+        case 7: {
+            // ==
+            //printf("compare: %" PRId64 " == %" PRId64 "\n", v1, v2);
+            res.intval = v1 == v2;
+            break;
+        }
+        case 8: {
+            // !=
+            //printf("compare: %" PRId64 " != %" PRId64 "\n", v1, v2);
+            res.intval = v1 != v2;
+            break;
+        }
+        case 9: {
+            // <=
+            //printf("compare: %" PRId64 " <= %" PRId64 "\n", v1, v2);
+            res.intval = v1 <= v2;
+            break;
+        }
+        case 10: {
+            // >=
+            //printf("compare: %" PRId64 " >= %" PRId64 "\n", v1, v2);
+            res.intval = v1 >= v2;
+            break;
+        }
+        case 11: {
+            // and
+            //printf("compare: %" PRId64 " and %" PRId64 "\n", v1, v2);
+            res.intval = v1 && v2;
+            break;
+        }
+        case 12: {
+            // or
+            //printf("compare: %" PRId64 " or %" PRId64 "\n", v1, v2);
+            res.intval = v1 || v2;
+            break;
+        }
+        case 13: {
+            // xor
+            //printf("compare: %" PRId64 " xor %" PRId64 "\n", v1, v2);
+            res.intval = v1 ^ v2;
+            break;
+        }
+    }
+    return res;
+}
+
+DavQOp compare_strint(int op, DavQOp v1, DavQOp v2) {
+    int64_t v1int;
+    sstr_t s1 = sstrn(v1.val, v1.intval);
+    s1 = sstrdup(s1);
+    if(util_strtoint(s1.ptr, &v1int)) {
+        free(s1.ptr);
+        return compare_intint(op, v1int, v2.intval);
+    } else {
+        free(s1.ptr);
+        // TODO
+    }
+}
+
+DavQOp compare_intstr(int op, DavQOp v1, DavQOp v2) {
+    // TODO
+}
+
+DavQOp compare_strstr(int op, DavQOp v1, DavQOp v2) {
+    DavQOp res;
+    res.type = DAVQOP_INTEGER;
+    res.val = NULL;
+    res.intval = 0;
+    sstr_t s1 = sstrn(v1.val, v1.intval);
+    sstr_t s2 = sstrn(v2.val, v2.intval);
+    switch(op) {
+        case 5: {
+            // <
+            //printf("str compare: %.*s < %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = s1.length < s2.length;
+            break;
+        }
+        case 6: {
+            // >
+            //printf("str compare: %.*s > %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = s1.length > s2.length;
+            break;
+        }
+        case 7: {
+            // ==
+            //printf("str compare: %.*s == %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = sstrcmp(s1, s2) == 0;
+            break;
+        }
+        case 8: {
+            // !=
+            //printf("str compare: %.*s != %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = sstrcmp(s1, s2) != 0;
+            break;
+        }
+        case 9: {
+            // <=
+            //printf("str compare: %.*s <= %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = s1.length <= s2.length;
+            break;
+        }
+        case 10: {
+            // >=
+            //printf("str compare: %.*s >= %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = s1.length >= s2.length;
+            break;
+        }
+        case 11: {
+            // and
+            //printf("str compare: %.*s and %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = s1.ptr && s2.ptr;
+            break;
+        }
+        case 12: {
+            // or
+            //printf("str compare: %.*s or %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = s1.ptr || s2.ptr;
+            break;
+        }
+        case 13: {
+            // xor
+            //printf("str compare: %.*s xor %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr);
+            res.intval = (intptr_t)s1.ptr ^ (intptr_t)s2.ptr;
+            break;
+        }
+    }
+    return res;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/davql.h	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,95 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "webdav.h"
+
+#ifndef DAVQL_H
+#define	DAVQL_H
+
+#include <ucx/string.h>
+#include <ucx/list.h>
+#include <inttypes.h>
+#include <stdarg.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+enum DavQueryType {
+    DAV_QUERY_ERROR = 0,
+    DAV_QUERY_GET
+};
+typedef enum DavQueryType DavQueryType;
+
+#define DAVQOP_OPERATOR    0
+#define DAVQOP_STRING      1
+#define DAVQOP_INTEGER     2
+#define DAVQOP_TIME        3
+#define DAVQOP_PROPERTY    4
+#define DAVQOP_RESPROP     5
+
+typedef struct {
+    int      type;
+    void     *val;
+    int64_t intval;
+} DavQOp;
+    
+typedef struct {
+    DavQueryType command;
+    void         *command_data;
+} DavQuery;
+
+typedef struct {
+    sstr_t properties;
+    sstr_t from;
+    DavQOp *condition;
+    size_t condlen;
+} DavGetQuery;
+    
+DavQuery dav_ql_parse(char *query, va_list ap);
+DavGetQuery* dav_ql_parse_get(sstr_t q, va_list ap);
+void free_get_query(DavGetQuery *q);
+
+int parse_path_query(sstr_t query, char **path, int *depth);
+
+int dav_parse_condition(UcxList **ops, sstr_t cond, va_list ap);
+sstr_t condition_parser_next_token(sstr_t *str);
+int condition_operator_type(sstr_t token, int64_t *type);
+
+int condition_eval(DavResource *res, DavQOp *cond, size_t len);
+DavQOp compare_intint(int op, int64_t v1, int64_t v2);
+DavQOp compare_strint(int op, DavQOp v1, DavQOp v2);
+DavQOp compare_intstr(int op, DavQOp v1, DavQOp v2);
+DavQOp compare_strstr(int op, DavQOp v1, DavQOp v2);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* DAVQL_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/methods.c	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,521 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils.h"
+#include "methods.h"
+#include "davql.h"
+
+#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
+
+/* ----------------------------- PROPFIND ----------------------------- */
+
+CURLcode do_propfind_request(
+        CURL *handle,
+        UcxBuffer *request,
+        UcxBuffer *response)
+{
+    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
+    
+    struct curl_slist *headers = NULL;
+    headers = curl_slist_append(headers, "Content-Type: text/xml");
+    headers = curl_slist_append(headers, "Depth: 1");
+    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    
+    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
+    
+    curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
+    curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
+    curl_easy_setopt(handle, CURLOPT_READDATA, request); 
+    curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
+    
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
+    
+    ucx_buffer_seek(request, 0, SEEK_SET);
+    return curl_easy_perform(handle);
+}
+
+UcxBuffer* create_allprop_propfind_request() {
+    UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
+    sstr_t s;
+    
+    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("<D:propfind xmlns:D=\"DAV:\">\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("<D:allprop/></D:propfind>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    return buf;
+}
+
+UcxBuffer* create_propfind_request(UcxList *properties) {
+    UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
+    sstr_t s;
+    
+    UcxMap *namespaces = ucx_map_new(8);
+    UCX_FOREACH(elm, properties) {
+        DavProperty *p = elm->data;
+        if(strcmp(p->ns->name, "DAV:")) {
+            ucx_map_cstr_put(namespaces, p->ns->prefix, p->ns);
+        }
+    }
+    
+    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    // write root element and namespaces
+    s = S("<D:propfind xmlns:D=\"DAV:\"");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    UcxMapIterator mapi = ucx_map_iterator(namespaces);
+    UcxKey key;
+    DavNamespace *ns;
+    UCX_MAP_FOREACH(key, ns, mapi) {
+        s = S(" xmlns:");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = sstr(ns->prefix);
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = S("=\"");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = sstr(ns->name);
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = S("\"");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    }
+    s = S(">\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    // default properties
+    s = S("<D:prop>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("<D:creationdate />\n<D:getlastmodified />\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("<D:getcontentlength />\n<D:getcontenttype />\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    s = S("<D:resourcetype />\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    // extra properties
+    UCX_FOREACH(elm, properties) {
+        DavProperty *prop = elm->data;
+        s = S("<");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = sstr(prop->ns->prefix);
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = S(":");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = sstr(prop->name);
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = S(" />\n");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    }
+    
+    // end
+    s = S("</D:prop>\n</D:propfind>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    return buf;
+}
+
+DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response, DavQOp *cond, size_t len) {
+    char *url = NULL;
+    curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url);
+    if(!root) {
+        root = dav_resource_new_href(sn, util_url_path(url));
+    }
+    
+    xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0);
+    if(!doc) {
+        // TODO: free stuff
+        sn->error = DAV_ERROR;
+        return NULL;
+    }
+    
+    xmlNode *xml_root = xmlDocGetRootElement(doc);
+    xmlNode *node = xml_root->children;
+    while(node) {
+        if(node->type == XML_ELEMENT_NODE) {
+            if(xstreq(node->name, "response")) {
+                parse_response_tag(root, node, cond, len);
+            }
+        }
+        
+        node = node->next;
+    }
+    
+    return root;
+}
+
+int parse_response_tag(DavResource *resource, xmlNode *node, DavQOp *cond, size_t clen) {
+    DavResource *res = resource;
+    node = node->children;
+    while(node) {
+        if(node->type == XML_ELEMENT_NODE) {
+            if(xstreq(node->name, "href")) {
+                xmlNode *href_node = node->children;
+                if(href_node->type != XML_TEXT_NODE) {
+                    // error
+                    resource->session->error = DAV_ERROR;
+                    return 1;
+                }
+                char *href = (char*)href_node->content;
+                href = util_url_path(href);
+                if(xstreq(resource->href, href)) {
+                    res = resource;
+                } else {
+                    res = dav_resource_new_href(resource->session, href);
+                    res->parent = resource;
+                }
+            } else if(xstreq(node->name, "propstat")) {
+                xmlNode *n = node->children;
+                xmlNode *prop_node = NULL;
+                int ok = 0;
+                // get the status code
+                while(n) {
+                    if(n->type == XML_ELEMENT_NODE) {
+                        if(xstreq(n->name, "prop")) {
+                            prop_node = n;
+                        } else if(xstreq(n->name, "status")) {
+                            xmlNode *status_node = n->children;
+                            if(status_node->type != XML_TEXT_NODE) {
+                                resource->session->error = DAV_ERROR;
+                                return 1;
+                            }
+                            sstr_t status_str = sstr((char*)status_node->content);
+                            if(status_str.length < 13) {
+                                resource->session->error = DAV_ERROR;
+                                return 1;
+                            }
+                            status_str = sstrsubsl(status_str, 9, 3);
+                            if(!sstrcmp(status_str, S("200"))) {
+                                ok = 1;
+                            }
+                        }
+                    }    
+                    n = n->next;
+                }
+                // if status is ok, get all properties
+                if(ok) {
+                    n = prop_node->children;
+                    while(n) {
+                        if(n->type == XML_ELEMENT_NODE) {
+                            if(xstreq(n->name, "resourcetype")) {
+                                xmlNode *rsnode = n->children;
+                                if(rsnode && rsnode->type == XML_ELEMENT_NODE) {
+                                    // TODO: this is a ugly lazy hack
+                                    resource_add_property(res, "DAV:", (char*)n->name, "collection");
+                                    res->iscollection = 1;
+                                }
+                            } else {
+                                xmlNode *content = n->children;
+                                if(content) {
+                                    resource_add_property(
+                                            res,
+                                            (char*)n->ns->href,
+                                            (char*)n->name,
+                                            (char*)content->content);
+                                }
+                            }
+                        }
+                        n = n->next;
+                    }
+                }
+            }
+        }
+        
+        node = node->next;
+    }
+    
+    set_davprops(res);
+    if(res != resource) {
+        if(clen > 0) {
+            if(!condition_eval(res, cond, clen)) {
+                // skip resource
+                return 0;
+            }
+        }
+        resource_add_child(resource, res);
+    }
+    
+    return 0;
+}
+
+void set_davprops(DavResource *res) {
+    char *cl = dav_get_property_ns(res, "DAV:", "getcontentlength");
+    char *ct = dav_get_property_ns(res, "DAV:", "getcontenttype");
+    char *cd = dav_get_property_ns(res, "DAV:", "creationdate");
+    char *lm = dav_get_property_ns(res, "DAV:", "getlastmodified");
+    
+    res->contenttype = ct;
+    if(cl) {
+        char *end = NULL;
+        res->contentlength = strtoull(cl, &end, 0);
+    }
+    res->creationdate = util_parse_creationdate(cd);
+    res->lastmodified = util_parse_lastmodified(lm);
+}
+
+
+/* ----------------------------- PROPPATCH ----------------------------- */
+
+CURLcode do_proppatch_request(
+        CURL *handle,
+        UcxBuffer *request,
+        UcxBuffer *response)
+{
+    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH");
+    
+    struct curl_slist *headers = NULL;
+    headers = curl_slist_append(headers, "Content-Type: text/xml");
+    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    
+    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
+    
+    curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
+    curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
+    curl_easy_setopt(handle, CURLOPT_READDATA, request); 
+    curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
+    
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
+    
+    ucx_buffer_seek(request, 0, SEEK_SET);
+    return curl_easy_perform(handle);
+}
+
+UcxBuffer* create_proppatch_request(DavResourceData *data) {
+    UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
+    sstr_t s;
+    
+    UcxMap *namespaces = ucx_map_new(8);
+    char prefix[8];
+    int pfxnum = 0;
+    UCX_FOREACH(elm, data->set) {
+        DavProperty *p = elm->data;
+        if(strcmp(p->ns->name, "DAV:")) {
+            snprintf(prefix, 8, "x%d\0", pfxnum++);
+            ucx_map_cstr_put(namespaces, p->ns->name, prefix);
+        }
+    }
+    UCX_FOREACH(elm, data->remove) {
+        DavProperty *p = elm->data;
+        if(strcmp(p->ns->name, "DAV:")) {
+            snprintf(prefix, 8, "x%d\0", pfxnum++);
+            ucx_map_cstr_put(namespaces, p->ns->name, prefix);
+        }
+    }
+    
+    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    // write root element and namespaces
+    s = S("<D:propertyupdate xmlns:D=\"DAV:\"");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    UcxMapIterator mapi = ucx_map_iterator(namespaces);
+    UcxKey key;
+    char *pfxval;
+    UCX_MAP_FOREACH(key, pfxval, mapi) {
+        s = S(" xmlns:");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = sstr(pfxval);
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = S("=\"");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = sstrn(key.data, key.len);
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = S("\"");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    }
+    s = S(">\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    if(data->set) {
+        s = S("<D:set>\n<D:prop>\n");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        UCX_FOREACH(elm, data->set) {
+            DavProperty *property = elm->data;
+            char *prefix = ucx_map_cstr_get(namespaces, property->ns->name);
+            
+            s = S("<");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(prefix);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S(":");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(property->name);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S(">");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(property->value);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S("</");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(prefix);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S(":");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(property->name);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S(">\n");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+        }
+        s = S("</D:prop>\n</D:set>\n");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    }
+    if(data->remove) {
+        s = S("<D:set>\n<D:prop>\n");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        UCX_FOREACH(elm, data->set) {
+            DavProperty *property = elm->data;
+            char *prefix = ucx_map_cstr_get(namespaces, property->ns->name);
+            
+            s = S("<");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(prefix);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S(":");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(property->name);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S(">");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(property->value);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S("</");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(prefix);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S(":");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = sstr(property->name);
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = S(">\n");
+            ucx_buffer_write(s.ptr, 1, s.length, buf);
+        }
+        s = S("</D:prop>\n</D:set>\n");
+        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    }
+    
+    s = S("</D:propertyupdate>\n");
+    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    
+    return buf;
+}
+
+/* ----------------------------- PUT ----------------------------- */
+
+static size_t dummy_write(void *buf, size_t s, size_t n, void *data) {
+    fwrite(buf, s, n, stdout);
+    return s*n;
+}
+
+CURLcode do_put_request(CURL *handle, void *data, dav_read_func read_func, size_t length) {
+    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL);
+    curl_easy_setopt(handle, CURLOPT_PUT, 1L);  
+    curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
+    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
+    
+    UcxBuffer *buf = NULL;
+    if(!read_func) {
+        buf = ucx_buffer_new(data, length, 0);
+        buf->size = length;
+        data = buf;
+        read_func = (dav_read_func)ucx_buffer_read;
+        curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
+    } else if(length == 0) {
+        struct curl_slist *headers = NULL;
+        headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+        curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)1);
+        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+    } else {
+        curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
+    }
+    
+    curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_func);
+    curl_easy_setopt(handle, CURLOPT_READDATA, data);
+    
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
+    
+    CURLcode ret = curl_easy_perform(handle);
+    if(buf) {
+        ucx_buffer_free(buf);
+    }
+    return ret;
+}
+
+CURLcode do_delete_request(CURL *handle, UcxBuffer *response) {
+    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE");
+    curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
+    curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
+    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
+    
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
+    
+    CURLcode ret = curl_easy_perform(handle);
+    return ret;
+}
+
+CURLcode do_mkcol_request(CURL *handle) { 
+    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MKCOL");
+    curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
+    curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
+    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
+    
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
+    
+    CURLcode ret = curl_easy_perform(handle);
+    return ret;
+}
+
+
+CURLcode do_head_request(CURL *handle) {
+    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "HEAD");
+    curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
+    curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
+    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
+    curl_easy_setopt(handle, CURLOPT_NOBODY, 1L);
+    
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
+    
+    CURLcode ret = curl_easy_perform(handle);
+    curl_easy_setopt(handle, CURLOPT_NOBODY, 0L);
+    return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/methods.h	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,74 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef METHODS_H
+#define	METHODS_H
+
+#include "webdav.h"
+#include "resource.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+CURLcode do_propfind_request(
+        CURL *handle,
+        UcxBuffer *request,
+        UcxBuffer *response);
+
+CURLcode do_proppatch_request(
+        CURL *handle,
+        UcxBuffer *request,
+        UcxBuffer *response);
+
+CURLcode do_put_request(
+        CURL *handle,
+        void *data,
+        dav_read_func read_func,
+        size_t length);
+
+UcxBuffer* create_allprop_propfind_request();
+UcxBuffer* create_propfind_request(UcxList *properties);
+DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response, DavQOp *cond, size_t len);
+int parse_response_tag(DavResource *resource, xmlNode *node, DavQOp *cond, size_t len);
+void set_davprops(DavResource *res);
+
+UcxBuffer* create_proppatch_request(DavResourceData *data);
+
+CURLcode do_delete_request(CURL *handle, UcxBuffer *response);
+
+CURLcode do_mkcol_request(CURL *handle);
+
+CURLcode do_head_request(CURL *handle);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* METHODS_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/resource.c	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,498 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/tree.h>
+
+#include "utils.h"
+#include "methods.h"
+#include "davql.h"
+#include "ucx/buffer.h"
+#include "ucx/utils.h"
+
+#include "resource.h"
+
+#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
+
+DavResource* dav_resource_new(DavSession *sn, char *path) {
+    char *url = util_concat_path(sn->base_url, path);
+    char *href = util_url_path(url);
+    DavResource *res = dav_resource_new_href(sn, href);
+    free(url);
+    return res;
+}
+
+
+DavResource* dav_resource_new_href(DavSession *sn, char *href) {
+    UcxMempool *mp = sn->mp;
+    UcxAllocator *a = sn->allocator;
+    
+    DavResource *res = ucx_mempool_calloc(mp, 1, sizeof(DavResource));
+    res->session = sn;
+    
+    // set name, path and href
+    resource_set_info(res, href);
+    
+    // initialize resource data
+    res->data = resource_data_new(sn);
+    
+    return res;
+}
+
+void resource_set_info(DavResource *res, char *href_str) {
+    char *url_str = NULL;
+    curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str);
+    sstr_t name = sstr(util_resource_name(href_str));
+    sstr_t href = sstr(href_str);
+    
+    sstr_t base_href = sstr(util_url_path(res->session->base_url));
+    sstr_t path = sstrsubs(href, base_href.length - 1);
+    
+    UcxAllocator *a = res->session->allocator;
+    res->name = sstrdup_a(a, name).ptr;
+    res->href = sstrdup_a(a, href).ptr;
+    res->path = sstrdup_a(a, path).ptr;
+}
+
+DavResourceData* resource_data_new(DavSession *sn) {
+    DavResourceData *data = ucx_mempool_malloc(
+            sn->mp,
+            sizeof(DavResourceData));
+    if(!data) {
+        return NULL;
+    }
+    data->properties = ucx_map_new_a(sn->allocator, 32);
+    data->set = NULL;
+    data->remove = NULL;
+    data->content = NULL;
+    data->read = NULL;
+    data->length = 0;
+    return data;
+}
+
+void resource_add_property(DavResource *res, char *ns, char *name, char *val) {
+    if(!val) {
+        return;
+    }
+    UcxAllocator *a = res->session->allocator;
+    
+    UcxKey key = dav_property_key(ns, name);
+    sstr_t v = sstrdup_a(a, sstr(val));
+    ucx_map_put(((DavResourceData*)res->data)->properties, key, v.ptr);
+    free(key.data);
+}
+
+char* resource_get_property(DavResource *res, char *ns, char *name) {
+    UcxKey key = dav_property_key(ns, name);
+    DavResourceData *data = (DavResourceData*)res->data;
+    char *property = ucx_map_get(data->properties, key);
+    free(key.data);
+    return property;
+}
+
+UcxKey dav_property_key(char *ns, char *name) {
+    sstr_t ns_str = sstr(ns);
+    sstr_t name_str = sstr(name);
+    
+    sstr_t key;
+    key.length = ns_str.length + name_str.length + 1;
+    key.ptr = malloc(key.length + 1);
+    key = sstrncat(key, 3, ns_str, S(" "), name_str);
+    
+    return ucx_key(key.ptr, key.length);
+}
+
+
+
+
+void resource_add_child(DavResource *parent, DavResource *child) {
+    child->next = NULL;
+    if(parent->children) {
+        DavResource *last = parent->children;
+        while(last->next) {
+            last = last->next;
+        }
+        last->next = child;
+        child->prev = last;
+    } else {
+        child->prev = NULL;
+        parent->children = child;
+    }
+    child->parent = parent;
+}
+
+char* dav_get_property(DavResource *res, char *name) {
+    char *pns;
+    char *pname;
+    dav_get_property_namespace(res->session->context, name, &pns, &pname);
+    return dav_get_property_ns(res, pns, pname);
+}
+
+char* dav_get_property_ns(DavResource *res, char *ns, char *name) {
+    char *property = resource_get_property(res, ns, name);
+    DavResourceData *data = res->data;
+    // resource_get_property only returns persistent properties
+    // check the remove and set list
+    if(property) {
+        // if the property is in the remove list, we return NULL
+        UCX_FOREACH(elm, data->remove) {
+            DavProperty *p = elm->data;
+            if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) {
+                return NULL;
+            }
+        }
+    }
+    // the set list contains property updates
+    // we return an updated property if possible
+    UCX_FOREACH(elm, data->set) {
+        DavProperty *p = elm->data;
+        if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) {
+            return p->value;
+        }
+    }
+    // no property update
+    return property;
+}
+
+void dav_set_property(DavResource *res, char *name, char *value) {
+    char *pns;
+    char *pname;
+    dav_get_property_namespace(res->session->context, name, &pns, &pname);
+    dav_set_property_ns(res, pns, pname, value);
+}
+
+void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value) {
+    UcxAllocator *a = res->session->allocator;
+    DavResourceData *data = res->data;
+    
+    DavProperty *property = a->malloc(a->pool, sizeof(DavProperty));
+    property->name = sstrdup_a(a, sstr(name)).ptr;
+    property->value = sstrdup_a(a, sstr(value)).ptr;
+    DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace));
+    namespace->prefix = NULL;
+    namespace->name = sstrdup_a(a, sstr(ns)).ptr;
+    property->ns = namespace;
+    
+    data->set = ucx_list_append_a(a, data->set, property);
+}
+
+void dav_remove_property(DavResource *res, char *name) {
+    char *pns;
+    char *pname;
+    dav_get_property_namespace(res->session->context, name, &pns, &pname);
+    dav_remove_property_ns(res, pns, pname);
+}
+
+void dav_remove_property_ns(DavResource *res, char *ns, char *name) {
+    UcxAllocator *a = res->session->allocator;
+    DavResourceData *data = res->data;
+    
+    DavProperty *property = a->malloc(a->pool, sizeof(DavProperty));
+    property->name = sstrdup_a(a, sstr(name)).ptr;
+    property->value = NULL;
+    DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace));
+    namespace->prefix = NULL;
+    namespace->name = sstrdup_a(a, sstr(ns)).ptr;
+    property->ns = namespace;
+    
+    data->remove = ucx_list_append_a(a, data->remove, property);
+}
+
+
+void dav_set_content(DavResource *res, void *stream, dav_read_func read_func) {
+    DavResourceData *data = res->data;
+    data->content = stream;
+    data->read = read_func;
+    data->length = 0;
+}
+
+void dav_set_content_data(DavResource *res, char *content, size_t length) {
+    DavSession *sn = res->session;
+    DavResourceData *data = res->data;
+    data->content = content;
+    data->read = NULL;
+    data->length = length;
+}
+
+
+int dav_load(DavResource *res) {
+    DavSession *sn = res->session;
+    DavResourceData *data = res->data;
+    // clean map
+    UcxKey key;
+    void *value;
+    UcxMapIterator i = ucx_map_iterator(data->properties);
+    UCX_MAP_FOREACH(key, value, i) {
+        ucx_map_remove(data->properties, key);
+    }
+    
+    char *url = util_concat_path(sn->base_url, res->path);
+    
+    CURL *handle = sn->handle;
+    curl_easy_setopt(handle, CURLOPT_URL, url);
+    free(url);
+    
+    UcxBuffer *rqbuf = create_allprop_propfind_request();
+    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    
+    //fwrite(rpbuf->space, 1, rpbuf->size, stdout);
+    //printf("\n");
+    
+    CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf);
+    int status = 0;
+    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK) {
+        //printf("response\n%s\n", rpbuf->space);
+        // TODO: use parse_propfind_response()
+        xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, url, NULL, 0);
+        if(!doc) {
+            return 1;
+        }
+
+        xmlNode *xml_root = xmlDocGetRootElement(doc);
+        xmlNode *node = xml_root->children;
+        while(node) {
+            if(node->type == XML_ELEMENT_NODE) {
+                if(xstreq(node->name, "response")) {
+                    parse_response_tag(res, node, NULL, 0);
+                }
+            }
+            node = node->next;
+        }
+        
+        set_davprops(res);
+    } else {
+        session_set_error(sn, ret, status);
+    }
+    return 0;
+}
+
+int dav_store(DavResource *res) {
+    DavSession *sn = res->session;
+    DavResourceData *data = res->data;
+    
+    char *url = util_concat_path(sn->base_url, res->path);
+    CURL *handle = res->session->handle;
+    curl_easy_setopt(handle, CURLOPT_URL, url);
+    free(url);
+    
+    // store content
+    if(data->content) {
+        CURLcode ret = do_put_request(handle, data->content, data->read, data->length);
+        int status = 0;
+        curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
+        if(ret == CURLE_OK && (status >= 200 && status < 300)) {
+            res->session->error = 0;
+            // cleanup node data
+            if(!data->read) {
+                ucx_mempool_free(sn->mp, data->content);
+            }
+            data->content = NULL;
+            data->read = NULL;
+            data->length = 0;
+        } else {
+            session_set_error(sn, ret, status);
+            return 1;
+        }
+    }
+    
+    // store properties
+    if(data->set || data->remove) {
+        UcxBuffer *request = create_proppatch_request(data);
+        UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
+
+        CURLcode ret = do_proppatch_request(handle, request, response);
+        int status = 0;
+        curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+        if(ret == CURLE_OK && status == 207) {
+            //printf("%s\n", response->space);
+            // TODO: parse response
+            // TODO: cleanup node data correctly
+            data->set = NULL;
+            data->remove = NULL;
+        } else {
+            session_set_error(sn, ret, status);
+            return 1;
+        }
+    }
+    sn->error = DAV_OK;
+    return 0;
+}
+
+int dav_get_content(DavResource *res, void *stream, dav_write_func write_fnc) { 
+    char *url = util_concat_path(res->session->base_url, res->path);
+    CURL *handle = res->session->handle;
+    curl_easy_setopt(handle, CURLOPT_URL, url);
+    free(url);
+    
+    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
+    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL);
+    curl_easy_setopt(handle, CURLOPT_PUT, 0L);
+    curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
+    
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_fnc);
+    curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream);
+    
+    CURLcode ret = curl_easy_perform(handle);
+    int status = 0;
+    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK && (status >= 200 && status < 300)) {
+        res->session->error = DAV_OK;
+        return 0;
+    } else {
+        session_set_error(res->session, ret, status);
+        return 1;
+    }
+}
+
+DavResource* dav_create_child(DavResource *parent, char *name) {
+    // TODO
+    return NULL;
+}
+
+int dav_delete(DavResource *res) {
+    char *url = util_concat_path(res->session->base_url, res->path);
+    CURL *handle = res->session->handle;
+    curl_easy_setopt(handle, CURLOPT_URL, url);
+    free(url);
+    
+    UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    CURLcode ret = do_delete_request(handle, response);
+    int status = 0;
+    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK && (status >= 200 && status < 300)) {
+        res->session->error = DAV_OK;
+        
+        // TODO: parse response
+        // TODO: free res
+        
+        return 0;
+    } else {
+        session_set_error(res->session, ret, status);
+        return 1;
+    }
+}
+
+int dav_create(DavResource *res) {
+    char *url = util_concat_path(res->session->base_url, res->path);
+    char *parent = util_parent_path(res->path);
+    
+    DavSession *sn = res->session;
+    DavResource *parent_res = dav_get(sn, parent, NULL);
+    if(!parent_res && sn->error == DAV_NOT_FOUND) {
+        parent_res = dav_resource_new(sn, parent);
+        parent_res->iscollection = 1;
+        int r = dav_create(parent_res);
+        if(r) {
+            free(parent);
+            return r;
+        }
+    } else if(parent_res && !parent_res->iscollection) {
+        sn->error = DAV_FORBIDDEN;
+        return 1;
+    } else if(sn->error != DAV_OK) {
+        return 1;
+    }
+    
+    CURL *handle = res->session->handle;
+    curl_easy_setopt(handle, CURLOPT_URL, url);
+    free(url);
+    free(parent);
+    
+    // create new collection or do an empty put request
+    CURLcode ret;
+    if(res->iscollection) {
+        ret = do_mkcol_request(handle);
+    } else {
+        ret = do_put_request(handle, "", NULL, 0); 
+    }
+    int status = 0;
+    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK && (status >= 200 && status < 300)) {
+        res->session->error = DAV_OK;
+    } else {
+        session_set_error(res->session, ret, status);
+        return 1;
+    }
+    
+    // do an minimal propfind request
+    UcxBuffer *rqbuf = create_propfind_request(NULL);
+    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    
+    //fwrite(rpbuf->space, 1, rpbuf->size, stdout);
+    //printf("\n");
+    
+    ret = do_propfind_request(handle, rqbuf, rpbuf);
+    status = 0;
+    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK && (status >= 200 && status < 300)) {
+        //printf("response\n%s\n", rpbuf->space);
+        // TODO: use parse_propfind_response()
+        xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, url, NULL, 0);
+        if(!doc) {
+            return 1;
+        }
+
+        xmlNode *xml_root = xmlDocGetRootElement(doc);
+        xmlNode *node = xml_root->children;
+        while(node) {
+            if(node->type == XML_ELEMENT_NODE) {
+                if(xstreq(node->name, "response")) {
+                    parse_response_tag(res, node, NULL, 0);
+                }
+            }
+            node = node->next;
+        }
+        
+        set_davprops(res);
+        return 0;
+    } else {
+        session_set_error(sn, ret, status);
+        return 1;
+    }
+}
+
+int dav_exists(DavResource *res) {
+    DavSession *sn = res->session;
+    char *url = util_concat_path(sn->base_url, res->path);  
+    CURL *handle = sn->handle;
+    curl_easy_setopt(handle, CURLOPT_URL, url);
+    free(url);
+    
+    CURLcode ret = do_head_request(handle);
+    int status = 0;
+    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK && (status >= 200 && status < 300)) {
+        return 1;
+    } else {
+        session_set_error(sn, ret, status);
+        return 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/resource.h	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,70 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RESOURCE_H
+#define	RESOURCE_H
+
+#include "webdav.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct DavResourceData   DavResourceData;
+
+struct DavResourceData {
+    UcxMap  *properties;
+    UcxList *set;
+    UcxList *remove;
+    
+    /*
+     * char* or stream
+     */
+    void      *content;
+    /*
+     * if NULL, content is a char*
+     */
+    read_func read;
+    /*
+     * content length
+     */
+    size_t    length;
+};
+
+void resource_set_info(DavResource *res, char *href_str);
+DavResourceData* resource_data_new(DavSession *sn);
+void resource_add_property(DavResource *res, char *ns, char *name, char *val);
+char* resource_get_property(DavResource *res, char *ns, char *name);
+void resource_add_child(DavResource *parent, DavResource *child);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* RESOURCE_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/utils.c	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,259 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucx/string.h>
+#include <libxml/tree.h>
+#include <curl/curl.h>
+
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+
+#include "utils.h"
+
+
+time_t util_parse_creationdate(char *str) {
+    // example: 2012-11-29T21:35:35Z
+    if(!str) {
+        return 0;
+    }
+    // TODO
+    return 0;
+}
+
+time_t util_parse_lastmodified(char *str) {
+    // example: Thu, 29 Nov 2012 21:35:35 GMT
+    if(!str) {
+        return 0;
+    } else {
+        return curl_getdate(str, NULL);
+    }
+}
+
+int util_getboolean(char *v) {
+    if(v[0] == 'T' || v[0] == 't') {
+        return 1;
+    }
+    return 0;
+}
+
+int util_strtoint(char *str, int64_t *value) {
+    char *end;
+    int64_t val = strtoll(str, &end, 0);
+    if(strlen(end) == 0) {
+        *value = val;
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+char* util_url_path(char *url) { 
+    char *path = NULL;
+    size_t len = strlen(url);
+    int slashcount = 0;
+    int slmax;
+    if(len > 7 && !strncasecmp(url, "http://", 7)) {
+        slmax = 3;
+    } else if(len > 8 && !strncasecmp(url, "https://", 8)) {
+        slmax = 3;
+    } else {
+        slmax = 1;
+    }
+    char c;
+    for(int i=0;i<len;i++) {
+        c = url[i];
+        if(c == '/') {
+            slashcount++;
+            if(slashcount == slmax) {
+                path = url + i;
+                break;
+            }
+        }
+    } 
+    return path;
+}
+
+char* util_resource_name(char *url) {
+    int si = 0;
+    int osi = 0;
+    int i = 0;
+    int p = 0;
+    char c;
+    while((c = url[i]) != 0) {
+        if(c == '/') {
+            osi = si;
+            si = i;
+            p = 1;
+        }
+        i++;
+    }
+    
+    char *name = url + si + p;
+    if(name[0] == 0) {
+        name = url + osi + p;
+        if(name[0] == 0) {
+            return url;
+        }
+    }
+    
+    return name;
+}
+
+int util_mkdir(char *path, mode_t mode) {
+#ifdef _WIN32
+    return mkdir(path);
+#else
+    return mkdir(path, mode);
+#endif
+}
+
+char* util_concat_path(char *url_base, char *p) {
+    sstr_t base = sstr(url_base);
+    sstr_t path;
+    if(p) {
+        path = sstr(p);
+    } else {
+        path = sstrn("", 0);
+    }
+    
+    int add_separator = 0;
+    if(base.ptr[base.length-1] == '/') {
+        if(path.ptr[0] == '/') {
+            base.length--;
+        }
+    } else {
+        if(path.length == 0 || path.ptr[0] != '/') {
+            add_separator = 1;
+        }
+    }
+    
+    sstr_t url;
+    url.length = base.length + path.length + add_separator;
+    url.ptr = malloc(url.length + 1);
+    url.ptr[url.length] = '\0';
+    
+    if(add_separator) {
+        url = sstrncat(url, 3, base, sstr("/"), path);
+    } else {
+        url = sstrncat(url, 2, base, path);
+    }
+    
+    return url.ptr;
+}
+
+char* util_parent_path(char *path) {
+    char *name = util_resource_name(path);
+    size_t namelen = strlen(name);
+    size_t pathlen = strlen(path);
+    size_t parentlen = pathlen - namelen;
+    char *parent = malloc(parentlen + 1);
+    memcpy(parent, path, parentlen);
+    parent[parentlen] = '\0';
+    return parent;
+}
+
+
+char* util_xml_get_text(xmlNode *elm) {
+    xmlNode *node = elm->children;
+    while(node) {
+        if(node->type == XML_TEXT_NODE) {
+            return (char*)node->content;
+        }
+        node = node->next;
+    }
+    return NULL;
+}
+
+
+
+char* util_base64decode(char* in) {
+    size_t len = strlen(in);
+    char *out = calloc(1, len);
+    
+    BIO* b = BIO_new_mem_buf(in, len);
+    BIO *d = BIO_new(BIO_f_base64());
+    BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL);
+    b = BIO_push(d, b);
+
+    BIO_read(b, out, len);
+    BIO_free_all(b);
+    
+    return out;
+}
+
+/*
+ * gets a substring from 0 to the appearance of the token
+ * tokens are separated by space
+ * sets sub to the substring and returns the remaining string
+ */
+sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) {  
+    int i;
+    int token_start = -1;
+    int token_end = -1;
+    for(i=0;i<=str.length;i++) {
+        int c;
+        if(i == str.length) {
+            c = ' ';
+        } else {
+            c = str.ptr[i];
+        }
+        if(c < 33) {
+            if(token_start != -1) {
+                token_end = i;
+                size_t len = token_end - token_start;
+                sstr_t tk = sstrsubsl(str, token_start, len);
+                //printf("token: {%.*s}\n", token.length, token.ptr);
+                if(!sstrcmp(tk, token)) {
+                    *sub = sstrtrim(sstrsubsl(str, 0, token_start));
+                    break;
+                }
+                token_start = -1;
+                token_end = -1;
+            }
+        } else {
+            if(token_start == -1) {
+                token_start = i;
+            }
+        }
+    }
+    
+    if(i < str.length) {
+        return sstrtrim(sstrsubs(str, i));
+    } else {
+        str.ptr = NULL;
+        str.length = 0;
+        return str;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/utils.h	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,78 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UTILS_H
+#define	UTILS_H
+
+#include <sys/types.h>
+#include <libxml/tree.h>
+#include <ucx/string.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+
+#ifdef _WIN32
+#include <io.h>
+#define S_IRWXG 070
+#define S_IRGRP 040
+#define S_IWGRP 020
+#define S_IXGRP 010
+#define S_IRWXO  07
+#define S_IROTH  04
+#define S_IWOTH  02
+#define S_IXOTH  01
+#endif /* _WIN32 */
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+time_t util_parse_creationdate(char *str);
+time_t util_parse_lastmodified(char *str);
+
+int util_mkdir(char *path, mode_t mode);
+
+char* util_url_path(char *url);
+char* util_resource_name(char *url);
+char* util_concat_path(char *url_base, char *path);
+char* util_parent_path(char *path);
+
+int util_getboolean(char *v);
+int util_strtoint(char *str, int64_t *value);
+
+char* util_xml_get_text(xmlNode *elm);
+
+char* util_base64decode(char* in);
+
+sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UTILS_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/webdav.c	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,399 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/tree.h>
+
+#include "utils.h"
+#include "webdav.h"
+#include "methods.h"
+#include "davql.h"
+#include "ucx/buffer.h"
+#include "ucx/utils.h"
+
+
+
+DavContext* dav_context_new() {
+    DavContext *context = malloc(sizeof(DavContext));
+    if(!context) {
+        return NULL;
+    }
+    context->sessions = NULL;
+    context->namespaces = ucx_map_new(16);
+    context->http_proxy = NULL;
+    context->https_proxy = NULL;
+    context->no_proxy = NULL;
+    if(!context->namespaces) {
+        free(context);
+        return NULL;
+    }
+    DavNamespace *davns = malloc(sizeof(DavNamespace));
+    if(!davns) {
+        ucx_map_free(context->namespaces);
+        free(context);
+        return NULL;
+    }
+    davns->prefix = "D";
+    davns->name = "DAV:";
+    if(ucx_map_cstr_put(context->namespaces, "D", davns)) {
+        free(davns);
+        ucx_map_free(context->namespaces);
+        free(context);
+        return NULL;
+    }
+    
+    return context;
+}
+
+void dav_context_destroy(DavContext *ctx) {
+    // destroy all sessions assoziated with this context
+    UCX_FOREACH(elm, ctx->sessions) {
+        dav_session_destroy(elm->data);
+    }
+    
+    UcxMapIterator i = ucx_map_iterator(ctx->namespaces);
+    UcxKey k;
+    DavNamespace *ns;
+    // TODO: free map elements
+    ucx_map_free(ctx->namespaces);
+    free(ctx);
+}
+
+int dav_add_namespace(DavContext *context, char *prefix, char *name) {
+    DavNamespace *namespace = malloc(sizeof(DavNamespace));
+    if(!namespace) {
+        return 1;
+    }
+    namespace->prefix = strdup(prefix);
+    namespace->name = strdup(name);
+    return ucx_map_cstr_put(context->namespaces, prefix, namespace);
+}
+
+DavNamespace* dav_get_namespace(DavContext *context, char *prefix) {
+    return ucx_map_cstr_get(context->namespaces, prefix);
+}
+
+DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) {
+    return ucx_map_sstr_get(context->namespaces, prefix);
+}
+
+void dav_get_property_namespace(
+        DavContext *ctx,
+        char *prefixed_name,
+        char **ns,
+        char **name)
+{
+    char *pname = strchr(prefixed_name, ':');
+    char *pns = "DAV:";
+    if(pname) {
+        DavNamespace *ns = dav_get_namespace_s(
+                ctx,
+                sstrn(prefixed_name, pname-prefixed_name));
+        pns = ns->name;
+        pname++;
+    } else {
+        pname = prefixed_name;
+    }
+    *ns = pns;
+    *name = pname;
+}
+
+DavSession* dav_session_new(DavContext *context, char *base_url) {
+    if(!base_url) {
+        return NULL;
+    }
+    sstr_t url = sstr(base_url);
+    if(url.length == 0) {
+        return NULL;
+    }
+    DavSession *sn = malloc(sizeof(DavSession));
+    sn->errorstr = NULL;
+    sn->error = DAV_OK;
+    if(url.ptr[url.length - 1] == '/') {
+        sn->base_url = strdup(base_url);
+    } else {
+        char *url_str = malloc(url.length + 2);
+        memcpy(url_str, base_url, url.length);
+        url_str[url.length]     = '/';
+        url_str[url.length + 1] = '\0';
+        sn->base_url = url_str;
+    }
+    sn->context = context;
+    sn->handle = curl_easy_init();
+    //curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
+    //curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
+    
+    // set proxy
+    if(sstrprefix(url, S("https"))) {
+        if(context->https_proxy) {
+            //printf("use https_proxy: %s\n", context->https_proxy);
+            curl_easy_setopt(sn->handle, CURLOPT_PROXY, context->https_proxy);
+        }
+    } else {
+        if(context->http_proxy) {
+            //printf("use http_proxy: %s\n", context->http_proxy);
+            curl_easy_setopt(sn->handle, CURLOPT_PROXY, context->http_proxy);
+        }
+    }
+    if(context->no_proxy) {
+        //printf("use no_proxy: %s\n", context->no_proxy);
+        curl_easy_setopt(sn->handle, CURLOPT_NOPROXY, context->no_proxy);
+    }
+    // set url
+    curl_easy_setopt(sn->handle, CURLOPT_URL, base_url);
+    
+    sn->mp = ucx_mempool_new(1024);
+    sn->allocator = ucx_mempool_allocator(sn->mp);
+    
+    context->sessions = ucx_list_append(context->sessions, sn);
+    
+    return sn;
+}
+
+DavSession* dav_session_new_auth(
+        DavContext *context,
+        char *base_url,
+        char *user,
+        char *password)
+{
+    DavSession *sn = dav_session_new(context, base_url);
+    if(!sn) {
+        return NULL;
+    }
+    dav_session_set_auth(sn, user, password);
+    return sn;
+}
+
+void dav_session_set_auth(DavSession *sn, char *user, char *password) {
+    if(user && password) {
+        size_t ulen = strlen(user);
+        size_t plen = strlen(password);
+        size_t upwdlen = ulen + plen + 2;
+        char *upwdbuf = malloc(upwdlen);
+        snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password);
+        curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf);
+        free(upwdbuf);
+    }
+}
+
+void session_set_error(DavSession *sn, CURLcode c, int status) {
+    if(status > 0) {
+        switch(status) {
+            default: sn->error = DAV_ERROR; break;
+            case 401: sn->error = DAV_UNAUTHORIZED; break;
+            case 403: sn->error = DAV_FORBIDDEN; break;
+            case 404: sn->error = DAV_NOT_FOUND; break;
+            case 405: sn->error = DAV_METHOD_NOT_ALLOWED; break;
+            case 409: sn->error = DAV_CONFLICT; break;
+        }
+    } else {
+        sn->error = DAV_ERROR;
+    }
+    if(c != CURLE_OK) {
+        sn->errorstr = curl_easy_strerror(c);
+    } else {
+        sn->errorstr = NULL;
+    }
+}
+
+void dav_session_destroy(DavSession *sn) { 
+    // remove session from context
+    UcxList *sessions = sn->context->sessions;
+    ssize_t i = ucx_list_find(sessions, sn, ucx_ptrcmp, NULL);
+    if(i > 0)  {
+        UcxList *elm = ucx_list_get(sessions, i);
+        if(elm) {
+            sn->context->sessions = ucx_list_remove(sessions, elm);
+        }
+    }
+    
+    ucx_mempool_destroy(sn->mp);
+    curl_easy_cleanup(sn->handle);
+    free(sn->base_url);
+    free(sn);
+}
+
+DavResource* dav_get(DavSession *sn, char *path, char *properties) {
+    char *url = util_concat_path(sn->base_url, path);
+    
+    CURL *handle = sn->handle;
+    curl_easy_setopt(handle, CURLOPT_URL, url);
+    free(url);
+    
+    UcxList *proplist = NULL;
+    if(properties) {
+        proplist = parse_properties_string(sn->context, sstr(properties));
+    }
+    UcxBuffer *rqbuf = create_propfind_request(proplist);
+    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    
+    //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
+    //printf("\n");
+    
+    DavResource *resource = NULL;
+    CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf);
+    int status = 0;
+    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK && status == 207) {
+        //printf("response\n%s\n", rpbuf->space); 
+        resource = parse_propfind_response(sn, NULL, rpbuf, NULL, 0);
+        sn->error = DAV_OK;
+    } else  {
+        session_set_error(sn, ret, status);
+    }
+    return resource;
+}
+
+DavResource* dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, char *path, DavQOp *cond, size_t len) {
+    char *url = util_concat_path(sn->base_url, path);  
+    CURL *handle = sn->handle;
+    curl_easy_setopt(handle, CURLOPT_URL, url);
+    free(url);
+    
+    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    DavResource *resource = root;
+    CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf);
+    int status = 0;
+    curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
+    if(ret == CURLE_OK && status == 207) {
+        //printf("response\n%s\n", rpbuf->space); 
+        resource = parse_propfind_response(sn, resource, rpbuf, cond, len);
+        sn->error = DAV_OK;
+    } else  {
+        session_set_error(sn, ret, status);
+        resource = NULL;
+    }
+    ucx_buffer_free(rpbuf);
+    return resource;
+}
+
+UcxList* propfind_stack_push(UcxList *stack, DavResource *children) {
+    while(children) {
+        if(children->iscollection) {
+            stack = ucx_list_prepend(stack, children);
+        }
+        children = children->next;
+    }
+    return stack;
+}
+
+DavResource* dav_get2(DavSession *sn, DavGetQuery *query) {
+    char *path;
+    int depth = 0;
+    if(parse_path_query(query->from, &path, &depth)) {
+        sn->error = DAV_ERROR;
+        return NULL;
+    }
+    
+    sstr_t ps = query->properties;
+    UcxBuffer *rqbuf;
+    if(!sstrcmp(ps, S("*"))) {
+        rqbuf = create_allprop_propfind_request();
+    } else if(!sstrcmp(ps, S("-"))) {
+        rqbuf = create_propfind_request(NULL);
+    } else {
+        UcxList *proplist = parse_properties_string(sn->context, ps);
+        rqbuf = create_propfind_request(proplist);
+    }
+    
+    //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
+    //printf("\n");
+    
+    DavResource *resource = dav_propfind(sn, NULL, rqbuf, path, query->condition, query->condlen);
+    free(path);
+    int error = 0;
+    if(resource && depth == -1) {
+        UcxList *stack = NULL; // stack with davResource* elements
+        stack = propfind_stack_push(stack, resource->children);
+        while(stack) {
+            DavResource *sr = stack->data; // get first element from the stack
+            stack = ucx_list_remove(stack, stack); // remove first element
+            // do propfind request for sr
+            sr = dav_propfind(sn, sr, rqbuf, sr->path, query->condition, query->condlen);
+            if(!sr) {
+                error = 1;
+                printf("subrequest failed\n");
+                break;
+            }
+            stack = propfind_stack_push(stack, sr->children); // add children
+        }
+    }
+    return resource;
+}
+
+UcxList* parse_properties_string(DavContext *context, sstr_t str) {
+    UcxList *proplist = NULL;
+    size_t nprops;
+    sstr_t *props = sstrsplit(str, S(","), &nprops);
+    for(int i=0;i<nprops;i++) {
+        sstr_t s = props[i];
+        sstr_t nsname = sstrchr(s, ':');
+        if(nsname.length > 0) {
+            sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr);
+            nsname.ptr++;
+            nsname.length--;
+            
+            DavProperty *dp = malloc(sizeof(DavProperty));
+            sstr_t pre = sstrdup(sstrtrim(nspre));
+            dp->ns = dav_get_namespace(context, pre.ptr);
+            free(pre.ptr);
+            dp->name = sstrdup(nsname).ptr;
+            if(dp->ns && dp->name) {
+                proplist = ucx_list_append(proplist, dp);
+            } else {
+                free(dp->name);
+                free(dp);
+            }
+        }
+        free(s.ptr);
+    }
+    free(props);
+    return proplist;
+}
+
+DavResource* dav_query(DavSession *sn, char *query, ...) {
+    va_list ap;
+    va_start(ap, query);
+    DavQuery q = dav_ql_parse(query, ap);
+    va_end(ap);
+    DavResource *res = NULL;
+    switch(q.command) {
+        case DAV_QUERY_GET: {
+            res = dav_get2(sn, q.command_data);
+            free_get_query(q.command_data);
+            break;
+        }
+    }
+    return res;
+}
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/webdav.h	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,174 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WEBDAV_H
+#define	WEBDAV_H
+
+#include <inttypes.h>
+#include <ucx/map.h>
+#include <ucx/mempool.h>
+#include <ucx/list.h>
+#include <ucx/buffer.h>
+#include <curl/curl.h>
+#include <libxml/tree.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct DavContext    DavContext;
+typedef struct DavSession    DavSession;
+typedef struct DavResource   DavResource;
+typedef struct DavRequest    DavRequest;
+typedef struct DavNamespace  DavNamespace;
+typedef struct DavProperty   DavProperty;
+
+#include "davql.h"
+
+typedef size_t(*dav_read_func)(void*, size_t, size_t, void*);
+typedef size_t(*dav_write_func)(const void*, size_t, size_t, void*);
+
+enum DavError {
+    DAV_OK = 0,
+    DAV_ERROR,
+    DAV_NOT_FOUND,
+    DAV_UNAUTHORIZED,
+    DAV_FORBIDDEN,
+    DAV_METHOD_NOT_ALLOWED,
+    DAV_CONFLICT
+};
+
+typedef enum DavError DavError;
+
+struct DavNamespace {
+    char *prefix;
+    char *name;
+};
+
+struct DavResource {
+    DavSession    *session;
+    DavResource   *prev;
+    DavResource   *next;
+    DavResource   *parent;
+    DavResource   *children;
+    char          *name;
+    char          *path;
+    char          *href;
+    uint64_t      contentlength;
+    char          *contenttype;
+    time_t        creationdate;
+    time_t        lastmodified;
+    void          *data;
+    int           iscollection;
+};
+
+struct DavSession {
+    DavContext    *context;
+    CURL          *handle;
+    char          *base_url;
+    UcxMempool    *mp;
+    UcxAllocator  *allocator;
+    DavError      error;
+    const char    *errorstr;
+};
+
+struct DavContext {
+    UcxMap  *namespaces;
+    UcxList *sessions;
+    char    *http_proxy;
+    char    *https_proxy;
+    char    *no_proxy;
+};
+
+struct DavProperty {
+    DavNamespace *ns;
+    char         *name;
+    char         *value;
+};
+
+DavContext* dav_context_new();
+void dav_context_destroy(DavContext *ctx);
+int dav_add_namespace(DavContext *context, char *prefix, char *ns);
+DavNamespace* dav_get_namespace(DavContext *context, char *prefix);
+
+DavSession* dav_session_new(DavContext *context, char *base_url);
+DavSession* dav_session_new_auth(
+        DavContext *context,
+        char *base_url,
+        char *user,
+        char *password);
+void dav_session_set_auth(DavSession *sn, char *user, char *password);
+
+void session_set_error(DavSession *sn, CURLcode c, int status);
+
+void dav_session_destroy(DavSession *sn);
+
+DavResource* dav_get(DavSession *sn, char *path, char *properties);
+DavResource* dav_get2(DavSession *sn, DavGetQuery *query);
+
+UcxList* parse_properties_string(DavContext *context, sstr_t str);
+
+DavResource* dav_query(DavSession *sn, char *query, ...);
+
+UcxKey dav_property_key(char *ns, char *name);
+void dav_get_property_namespace(
+        DavContext *ctx,
+        char *prefixed_name,
+        char **ns,
+        char **name);
+
+/* ------------------------ resource functions ------------------------ */
+
+DavResource* dav_resource_new(DavSession *sn, char *path);
+DavResource* dav_resource_new_href(DavSession *sn, char *href);
+
+DavResource* dav_create_child(DavResource *parent, char *name);
+int dav_delete(DavResource *res);
+int dav_create(DavResource *res);
+int dav_exists(DavResource *res);
+
+char* dav_get_property(DavResource *res, char *name);
+char* dav_get_property_ns(DavResource *res, char *ns, char *name);
+void dav_set_property(DavResource *res, char *name, char *value);
+void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value);
+void dav_remove_property(DavResource *res, char *name);
+void dav_remove_property_ns(DavResource *res, char *ns, char *name);
+
+void dav_set_content(DavResource *res, void *stream, dav_read_func read_func);
+void dav_set_content_data(DavResource *res, char *content, size_t length);
+
+int dav_load(DavResource *res);
+int dav_store(DavResource *res);
+int dav_get_content(DavResource *res, void *stream, dav_write_func write_func);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* WEBDAV_H */
+
--- a/ucx/Makefile	Fri Aug 23 11:04:07 2013 +0200
+++ b/ucx/Makefile	Mon Aug 26 14:42:09 2013 +0200
@@ -40,15 +40,15 @@
 SRC += logging.c
 SRC += buffer.c
 
-OBJ = $(SRC:%.c=../build/%.$(OBJ_EXT))
+OBJ = $(SRC:%.c=../build/ucx/%.$(OBJ_EXT))
 
 all: libucx
 
 libucx: $(OBJ)
 	$(AR) $(ARFLAGS) $(AOFLAGS)../build/libucx.$(LIB_EXT) $(OBJ)
 
-../build/%.$(OBJ_EXT): %.c ../build
+../build/ucx/%.$(OBJ_EXT): %.c ../build/ucx
 	$(CC) $(CFLAGS) $(COFLAGS)$@ $<
 
-../build:
-	mkdir -p ../build
+../build/ucx:
+	mkdir -p ../build/ucx

mercurial