Wed, 21 Aug 2013 13:08:22 +0200
added support for where clauses
dav/davql.c | file | annotate | diff | comparison | revisions | |
dav/davql.h | file | annotate | diff | comparison | revisions | |
dav/main.c | file | annotate | diff | comparison | revisions | |
dav/methods.c | file | annotate | diff | comparison | revisions | |
dav/methods.h | file | annotate | diff | comparison | revisions | |
dav/webdav.c | file | annotate | diff | comparison | revisions | |
dav/webdav.h | file | annotate | diff | comparison | revisions |
--- a/dav/davql.c Tue Aug 20 11:34:44 2013 +0200 +++ b/dav/davql.c Wed Aug 21 13:08:22 2013 +0200 @@ -28,10 +28,10 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "davql.h" #include "methods.h" -#include "webdav.h" #include "utils.h" DavQuery dav_ql_parse(char *query, va_list ap) { @@ -102,10 +102,38 @@ } } + // 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); - // TODO: condition + if(condition) { + getquery->condition = condition; + getquery->condlen = oplen; + } else { + getquery->condition = NULL; + getquery->condlen = 0; + } return getquery; } @@ -137,3 +165,524 @@ 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); + char *end; + int64_t val = strtoll(d.ptr, &end, 0); + int intval = strlen(end) == 0 ? 1 : 0; + 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 { + 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 NULL; + } + 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 NULL; + } + } 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); + } + 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) { + // 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 = (int)s1.ptr ^ (int)s2.ptr; + break; + } + } + return res; +}
--- a/dav/davql.h Tue Aug 20 11:34:44 2013 +0200 +++ b/dav/davql.h Wed Aug 21 13:08:22 2013 +0200 @@ -26,10 +26,14 @@ * 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 @@ -41,6 +45,19 @@ 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; @@ -50,7 +67,8 @@ typedef struct { sstr_t properties; sstr_t from; - // TODO: condition + DavQOp *condition; + size_t condlen; } DavGetQuery; DavQuery dav_ql_parse(char *query, va_list ap); @@ -59,6 +77,15 @@ 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 }
--- a/dav/main.c Tue Aug 20 11:34:44 2013 +0200 +++ b/dav/main.c Wed Aug 21 13:08:22 2013 +0200 @@ -531,7 +531,7 @@ char *res_url = util_concat_path( res->session->base_url, res->path); - printf("Skip resource: %s\n", res_url); + printf("Skipping resource: %s\n", res_url); free(res_url); return 0; } else {
--- a/dav/methods.c Tue Aug 20 11:34:44 2013 +0200 +++ b/dav/methods.c Wed Aug 21 13:08:22 2013 +0200 @@ -32,6 +32,7 @@ #include "utils.h" #include "methods.h" +#include "davql.h" #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) @@ -150,7 +151,7 @@ return buf; } -DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response) { +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) { @@ -169,7 +170,7 @@ while(node) { if(node->type == XML_ELEMENT_NODE) { if(xstreq(node->name, "response")) { - parse_response_tag(root, node); + parse_response_tag(root, node, cond, len); } } @@ -179,7 +180,7 @@ return root; } -int parse_response_tag(DavResource *resource, xmlNode *node) { +int parse_response_tag(DavResource *resource, xmlNode *node, DavQOp *cond, size_t clen) { DavResource *res = resource; node = node->children; while(node) { @@ -195,7 +196,7 @@ res = resource; } else { res = resource_new_href(resource->session, (char*)href_content->content); - resource_add_child(resource, res); + res->parent = resource; } } else if(xstreq(node->name, "propstat")) { xmlNode *n = node->children; @@ -258,6 +259,15 @@ } 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; }
--- a/dav/methods.h Tue Aug 20 11:34:44 2013 +0200 +++ b/dav/methods.h Wed Aug 21 13:08:22 2013 +0200 @@ -53,8 +53,8 @@ UcxBuffer* create_allprop_propfind_request(); UcxBuffer* create_propfind_request(UcxList *properties); -DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response); -int parse_response_tag(DavResource *resource, xmlNode *node); +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(DavNodeData *data);
--- a/dav/webdav.c Tue Aug 20 11:34:44 2013 +0200 +++ b/dav/webdav.c Wed Aug 21 13:08:22 2013 +0200 @@ -237,7 +237,7 @@ 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); + resource = parse_propfind_response(sn, NULL, rpbuf, NULL, 0); sn->error = DAV_OK; } else { session_set_error(sn, ret, status); @@ -245,7 +245,7 @@ return resource; } -DavResource* dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, char *path) { +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); @@ -258,7 +258,7 @@ 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); + resource = parse_propfind_response(sn, resource, rpbuf, cond, len); sn->error = DAV_OK; } else { session_set_error(sn, ret, status); @@ -300,7 +300,7 @@ //fwrite(rqbuf->space, 1, rqbuf->size, stdout); //printf("\n"); - DavResource *resource = dav_propfind(sn, NULL, rqbuf, path); + DavResource *resource = dav_propfind(sn, NULL, rqbuf, path, query->condition, query->condlen); free(path); int error = 0; if(resource && depth == -1) { @@ -310,7 +310,7 @@ 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); + sr = dav_propfind(sn, sr, rqbuf, sr->path, query->condition, query->condlen); if(!sr) { error = 1; printf("subrequest failed\n"); @@ -507,7 +507,7 @@ while(node) { if(node->type == XML_ELEMENT_NODE) { if(xstreq(node->name, "response")) { - parse_response_tag(res, node); + parse_response_tag(res, node, NULL, 0); } } node = node->next; @@ -798,7 +798,7 @@ while(node) { if(node->type == XML_ELEMENT_NODE) { if(xstreq(node->name, "response")) { - parse_response_tag(res, node); + parse_response_tag(res, node, NULL, 0); } } node = node->next;
--- a/dav/webdav.h Tue Aug 20 11:34:44 2013 +0200 +++ b/dav/webdav.h Wed Aug 21 13:08:22 2013 +0200 @@ -26,8 +26,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NODE_H -#define NODE_H +#ifndef WEBDAV_H +#define WEBDAV_H #include <inttypes.h> #include <ucx/map.h> @@ -36,7 +36,6 @@ #include <ucx/buffer.h> #include <curl/curl.h> #include <libxml/tree.h> -#include "davql.h" #ifdef __cplusplus extern "C" { @@ -50,6 +49,8 @@ typedef struct DavNodeData DavNodeData; 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*); @@ -199,5 +200,5 @@ } #endif -#endif /* NODE_H */ +#endif /* WEBDAV_H */