# HG changeset patch # User Olaf Wintermann # Date 1377520929 -7200 # Node ID 0bbbb0341606eda87de8a23e1bda569197174cf9 # Parent c9d37bb97ea850cf45e8f2b364d951eb334f39fc some code cleanup and bug fixes diff -r c9d37bb97ea8 -r 0bbbb0341606 Makefile --- 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: diff -r c9d37bb97ea8 -r 0bbbb0341606 dav/Makefile --- 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 $@ $< diff -r c9d37bb97ea8 -r 0bbbb0341606 dav/config.c --- 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 #include "config.h" -#include "utils.h" +#include #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) diff -r c9d37bb97ea8 -r 0bbbb0341606 dav/crypto.h --- 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 #include "config.h" #include diff -r c9d37bb97ea8 -r 0bbbb0341606 dav/main.c --- 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 -#include "utils.h" +#include #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 ] \n"); + fprintf(stderr, " list [-altR] [-u ] \n"); fprintf( stderr, " get [-pR] [-k ] [-o ] [-u ] \n"); - fprintf(stderr, " put [-p] [-k ] \n"); + fprintf(stderr, " put [-pR] [-k ] \n"); fprintf(stderr, " mkdir \n"); fprintf(stderr, " remove \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; +} diff -r c9d37bb97ea8 -r 0bbbb0341606 dav/main.h --- 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 #include "optparser.h" -#include "webdav.h" +#include #ifdef __cplusplus extern "C" { @@ -55,6 +55,8 @@ int cmd_date(CmdArgs *args); +int cmd_sync(CmdArgs *args); + #ifdef __cplusplus } #endif diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/Makefile --- /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 diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/davql.c --- /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 +#include +#include + +#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;idata; + 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 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;iptr = s.ptr + i; + str->length = s.length - i; + return t; + } + + for(i=0;i 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;icontentlength; + } 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; +} diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/davql.h --- /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 +#include +#include +#include + +#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 */ + diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/methods.c --- /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 +#include +#include + +#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("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\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("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + // write root element and namespaces + s = S("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("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\n\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\n\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\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("\n\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("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + // write root element and namespaces + s = S("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + if(data->set) { + s = S("\n\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("name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S("\n\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + if(data->remove) { + s = S("\n\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("name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S("\n\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + + s = S("\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; +} diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/methods.h --- /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 */ + diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/resource.c --- /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 +#include +#include +#include + +#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; + } +} diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/resource.h --- /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 */ + diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/utils.c --- /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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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;ichildren; + 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; + } +} diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/utils.h --- /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 +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#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 */ + diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/webdav.c --- /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 +#include +#include +#include + +#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 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; +} + + + + diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/webdav.h --- /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 +#include +#include +#include +#include +#include +#include + +#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 */ + diff -r c9d37bb97ea8 -r 0bbbb0341606 ucx/Makefile --- 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