--- a/libidav/davql.c Tue Jul 07 20:47:02 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,770 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2015 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "davql.h" -#include "methods.h" -#include "utils.h" - -DavQuery dav_ql_parse(char *query, va_list ap) { - DavQuery davquery; - davquery.command = DAV_QUERY_ERROR; - davquery.command_data = NULL; - sstr_t q = sstr(query); - q = sstrtrim(q); - - // get query command - sstr_t cmd; - cmd.ptr = NULL; - int i; - for(i=0;i<q.length;i++) { - if(q.ptr[i] == ' ') { - cmd = sstrsubsl(q, 0, i); - break; - } - } - if(!cmd.ptr) { - fprintf(stderr, "DQL syntax error\n"); - return davquery; - } - - cmd = sstrtrim(cmd); - q = sstrtrim(sstrsubs(q, i)); - if(!sstrcmp(cmd, S("get"))) { - davquery.command = DAV_QUERY_GET; - davquery.command_data = dav_ql_parse_get(q, ap); - } - - return davquery; -} - -DavGetQuery* dav_ql_parse_get(sstr_t q, va_list ap) { - sstr_t property_query = q; - q = util_getsubstr_until_token(q, S("from"), &property_query); - - sstr_t from_query = q; - sstr_t cond = util_getsubstr_until_token(q, S("where"), &from_query); - sstr_t with = util_getsubstr_until_token(cond, S("with"), &cond); - int depth = 1; - - // insert variable values - UcxBuffer *fbuf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); - int var = 0; - for(int i=0;i<from_query.length;i++) { - char c = from_query.ptr[i]; - if(c == '%') { - if(var) { - ucx_buffer_putc(fbuf, '%'); // previous '%' - } else { - var = 1; - } - } else if(var) { - switch(c) { - case 's': { - char *arg = va_arg(ap, char*); - ucx_buffer_puts(fbuf, arg); - break; - } - default: { - ucx_buffer_putc(fbuf, '%'); - ucx_buffer_putc(fbuf, c); - } - } - var = 0; - } else { - ucx_buffer_putc(fbuf, c); - } - } - - // condition - DavQOp *condition = NULL; - size_t oplen = 0; - if(cond.ptr) { - //printf("cond: {%.*s}\n", cond.length, cond.ptr); - UcxList *ops = NULL; - if(dav_parse_condition(&ops, cond, ap)) { - // TODO: error - printf("parse error\n"); - return NULL; - } - oplen = ucx_list_size(ops); - condition = calloc(sizeof(DavQOp), oplen); - int l = 0; - UCX_FOREACH(elm, ops) { - condition[l] = *(DavQOp*)elm->data; - free(elm->data); - l++; - } - ucx_list_free(ops); - } - - // with - if(with.ptr) { - if(dav_parse_with(with, &depth, ap)) { - // TODO: error - printf("parse error\n"); - return NULL; - } - } - - DavGetQuery *getquery = malloc(sizeof(DavGetQuery)); - getquery->properties = sstrdup(property_query); - getquery->from = sstrdup(sstrn(fbuf->space, fbuf->pos)); - getquery->depth = depth; - if(condition) { - getquery->condition = condition; - getquery->condlen = oplen; - } else { - getquery->condition = NULL; - getquery->condlen = 0; - } - - ucx_buffer_free(fbuf); - return getquery; -} - -void free_get_query(DavGetQuery *q) { - free(q->from.ptr); - free(q->properties.ptr); - if(q->condition) { - free(q->condition); - } - 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; -} - -static int dav_str2depth(sstr_t str, int *depth) { - if(!sstrcmp(str, S("infinity"))) { - *depth = -1; - } else { - sstr_t cp = sstrdup(str); // terminate - *depth = atoi(cp.ptr); - free(cp.ptr); - } - return 0; -} - -int dav_parse_with(sstr_t with, int *depth, va_list ap) { - int i; - for(i=0;i<with.length;i++) { - if(with.ptr[i] == ' ') { - break; - } - } - - sstr_t name = sstrsubsl(with, 0, i); - sstr_t value = sstrtrim(sstrsubs(with, i)); - //printf("with {%.*s} {%.*s}\n", name.length, name.ptr, value.length, value.ptr); - - if(!sstrcmp(name, S("depth"))) { - if(value.length == 0) { - return 1; - } else if(value.ptr[0] == '%') { - switch(value.ptr[1]) { - default: return 1; - case 's': { - sstr_t v = sstr(va_arg(ap, char*)); - if(dav_str2depth(value, depth)) { - return 1; - } - break; - } - case 'd': { - *depth = va_arg(ap, int); - break; - } - } - } else { - if(dav_str2depth(value, depth)) { - return 1; - } - } - } - - return 0; -} - -int dav_parse_condition(UcxList **ops, sstr_t cond, va_list ap) { - sstr_t token; - DavQOp *op1 = NULL; // level 1 operator - DavQOp *op2 = NULL; // level 2 operator - DavQOp *op3 = NULL; // level 3 operator - while((token = condition_parser_next_token(&cond)).length > 0) { - //printf("token: {%.*s}[%d]\n", token.length, token.ptr, token.length); - int64_t type = 0; - int tkop = condition_operator_type(token, &type); - DavQOp *operation = malloc(sizeof(DavQOp)); - if(tkop > 0) { - // operator token - operation->type = DAVQOP_OPERATOR; - operation->val = NULL; - operation->intval = type; - switch(tkop) { - case 1: { - // operators: + - / * not - // add operation after next non operator token - op1 = operation; - break; - } - case 2: { - // operators: < > == != <= >= - if(op2) { - *ops = ucx_list_append(*ops, op2); - } - op2 = operation; - break; - } - case 3: { - // operators: and or xor - if(op2) { - *ops = ucx_list_append(*ops, op2); - op2 = NULL; - } - if(op3) { - *ops = ucx_list_append(*ops, op3); - } - op3 = operation; - break; - } - } - } else { - if(token.ptr[0] == '"' || token.ptr[0] == '\'') { - operation->type = DAVQOP_STRING; - operation->val = token.ptr+1; - operation->intval = token.length-2; - } else if(!sstrcmp(token, S("true")) || - !sstrcmp(token, S("false"))) - { - operation->type = DAVQOP_INTEGER; - operation->val = NULL; - operation->intval = util_getboolean(token.ptr); - } else if(token.length == 2 && token.ptr[0] == '%') { - switch(token.ptr[1]) { - case 's': { - char *arg = va_arg(ap, char*); - operation->type = DAVQOP_STRING; - operation->val = arg; - operation->intval = strlen(arg); - break; - } - case 'd': { - operation->type = DAVQOP_INTEGER; - operation->val = NULL; - operation->intval = va_arg(ap, int); - break; - } - case 't': { - operation->type = DAVQOP_INTEGER; - operation->val = NULL; - operation->intval = va_arg(ap, time_t); - break; - } - default: { - operation->type = DAVQOP_STRING; - operation->val = token.ptr; - operation->intval = token.length; - } - } - } else { - sstr_t d = sstrdup(token); - int64_t val = 0; - int intval = util_strtoint(d.ptr, &val); - free(d.ptr); - if(intval) { - operation->type = DAVQOP_INTEGER; - operation->val = NULL; - operation->intval = val; - } else { - if(!sstrcmp(token, S("contentlength"))) { - operation->type = DAVQOP_RESPROP; - } else if(!sstrcmp(token, S("lastmodified"))) { - operation->type = DAVQOP_RESPROP; - } else if(!sstrcmp(token, S("creationdate"))) { - operation->type = DAVQOP_RESPROP; - } else if(!sstrcmp(token, S("name"))) { - operation->type = DAVQOP_RESPROP; - } else if(!sstrcmp(token, S("path"))) { - operation->type = DAVQOP_RESPROP; - } else if(!sstrcmp(token, S("iscollection"))) { - operation->type = DAVQOP_RESPROP; - } else { - operation->type = DAVQOP_PROPERTY; - } - operation->val = token.ptr; - operation->intval = token.length; - } - } - - // add operation - *ops = ucx_list_append(*ops, operation); - if(op1) { - // add level 1 operator - *ops = ucx_list_append(*ops, op1); - op1 = NULL; - } - } - } - if(op1) { - *ops = ucx_list_append(*ops, op1); - } - if(op2) { - *ops = ucx_list_append(*ops, op2); - } - if(op3) { - *ops = ucx_list_append(*ops, op3); - } - return 0; -} - -sstr_t condition_parser_next_token(sstr_t *str) { - sstr_t s = *str; - sstr_t t; - t.ptr = NULL; - t.length = 0; - // remove leading space - int i; - for(i=0;i<s.length;i++) { - if(s.ptr[i] > 32) { - break; - } - } - s.length -= i; - s.ptr += i; - - if(s.length == 0) { - *str = s; - return t; - } - - // check for single char operators - switch(s.ptr[0]) { - case '<': - case '>': - case '+': - case '-': - case '*': - case '/': { - t.ptr = s.ptr; - t.length = 1; - str->ptr = s.ptr + 1; - str->length = s.length - 1; - return t; - } - } - - if(s.length > 1) { - // check for double char operators - int16_t op = *(int16_t*)s.ptr; - if(op == '==' || op == '!=' || op == '>=' || op == '=<') { - t.ptr = s.ptr; - t.length = 2; - str->ptr = s.ptr + 2; - str->length = s.length - 2; - return t; - } - } else { - t.ptr = s.ptr; - t.length = 1; - str->ptr = s.ptr + 1; - str->length = s.length - 1; - return t; - } - - // TODO: brackets - - // check for string literal - if(s.ptr[0] == '\'' || s.ptr[0] == '"') { - for(i=1;i<s.length;i++) { - if(s.ptr[0] == s.ptr[i]) { - i++; - break; - } - } - t.ptr = s.ptr; - t.length = i; - str->ptr = s.ptr + i; - str->length = s.length - i; - return t; - } - - for(i=0;i<s.length;i++) { - char c = s.ptr[i]; - if((c < 33) || (c > 41 && c < 48) || (c > 59 && c < 63)) { - break; - } - } - t.ptr = s.ptr; - t.length = i; - str->ptr = s.ptr + i; - str->length = s.length - i; - return t; -} - -int condition_operator_type(sstr_t token, int64_t *type) { - // returns the operator level and sets the type - - if(token.ptr[0] == '"' || token.ptr[0] == '\'' || token.ptr[0] == '(') { - return 0; - } - - if(token.length == 1) { - switch(token.ptr[0]) { - case '+': *type = 1; return 1; - case '-': *type = 2; return 1; - case '*': *type = 3; return 1; - case '/': *type = 4; return 1; - case '<': *type = 5; return 2; - case '>': *type = 6; return 2; - } - } - if(!sstrcmp(token, S("not"))) { - *type = 0; - return 1; - } - - if(!sstrcmp(token, S("=="))) { - *type = 7; - return 2; - } - if(!sstrcmp(token, S("!="))) { - *type = 8; - return 2; - } - if(!sstrcmp(token, S("<="))) { - *type = 9; - return 2; - } - if(!sstrcmp(token, S(">="))) { - *type = 10; - return 2; - } - - if(!sstrcmp(token, S("and"))) { - *type = 11; - return 3; - } - if(!sstrcmp(token, S("or"))) { - *type = 12; - return 3; - } - if(!sstrcmp(token, S("xor"))) { - *type = 13; - return 3; - } - - return 0; -} - -int condition_eval(DavResource *res, DavQOp *cond, size_t len) { - DavQOp stack[128]; - int stackpos = 0; - for(int i=0;i<len;i++) { - DavQOp op = cond[i]; - switch(op.type) { - case DAVQOP_OPERATOR: { - if(op.intval == 0) { - // not operator - if(stackpos < 1) { - // error - printf("no data on stack\n"); - return 0; - } - int pos = stackpos-1; - if(stack[pos].type == DAVQOP_INTEGER) { - //printf("not %" PRId64 "\n", stack[pos].intval); - stack[pos].intval = !stack[pos].intval; - } else { - // error - printf("wrong value for 'not' operator\n"); - return 0; - } - } else { - DavQOp val1 = stack[stackpos-2]; - DavQOp val2 = stack[stackpos-1]; - DavQOp result; - if(val1.type == DAVQOP_INTEGER) { - if(val2.type == DAVQOP_INTEGER) { - result = compare_intint( - op.intval, - val1.intval, - val2.intval); - } else { - result = compare_intstr(op.intval, val1, val2); - } - } else { - if(val2.type == DAVQOP_INTEGER) { - result = compare_strint(op.intval, val1, val2); - } else { - result = compare_strstr(op.intval, val1, val2); - } - } - stack[stackpos-2] = result; - stackpos--; - } - break; - } - case DAVQOP_STRING: - case DAVQOP_INTEGER: - case DAVQOP_TIME: { - if(op.type == DAVQOP_STRING) { - //printf("put on stack: '%s'\n", op.val); - } else { - //printf("put on stack[%d]: %" PRId64 "\n", stackpos, op.intval); - } - stack[stackpos++] = op; - break; - } - case DAVQOP_PROPERTY: { - sstr_t pname = sstrn(op.val, op.intval); - pname = sstrdup(pname); - char *property_value = dav_get_property(res, pname.ptr); - free(pname.ptr); - DavQOp value; - value.type = DAVQOP_STRING; - if(property_value) { - //printf("put on stack: \"%s\"\n", property_value); - value.val = property_value; - value.intval = strlen(property_value); - } else { - //printf("put on stack: null string\n"); - value.val = NULL; - value.intval = 0; - } - stack[stackpos++] = value; - break; - } - case DAVQOP_RESPROP: { - sstr_t name = sstrn(op.val, op.intval); - DavQOp value; - value.type = DAVQOP_INTEGER; - value.val = NULL; - if(!sstrcmp(name, S("contentlength"))) { - //printf("put contentlength\n"); - value.intval = res->contentlength; - } else if(!sstrcmp(name, S("lastmodified"))) { - //printf("put getlastmodified\n"); - value.intval = res->lastmodified; - } else if(!sstrcmp(name, S("creationdate"))) { - value.intval = res->creationdate; - } else if(!sstrcmp(name, S("name"))) { - value.type = DAVQOP_STRING; - value.val = res->name; - value.intval = strlen(res->name); - } else if(!sstrcmp(name, S("path"))) { - value.type = DAVQOP_STRING; - value.val = res->path; - value.intval = strlen(res->path); - } else if(!sstrcmp(name, S("iscollection"))) { - value.type = DAVQOP_INTEGER; - value.val = NULL; - value.intval = res->iscollection; - } - stack[stackpos++] = value; - break; - } - } - } - if(stackpos != 1) { - return 0; - } - DavQOp result = stack[0]; - //printf("result: %" PRId64 "\n", result.intval); - return (int)result.intval; -} - -DavQOp compare_intint(int op, int64_t v1, int64_t v2) { - DavQOp res; - res.type = DAVQOP_INTEGER; - res.val = NULL; - res.intval = 0; - switch(op) { - case 5: { - // < - //printf("compare: %" PRId64 " < %" PRId64 "\n", v1, v2); - res.intval = v1 < v2; - break; - } - case 6: { - // > - //printf("compare: %" PRId64 " > %" PRId64 "\n", v1, v2); - res.intval = v1 > v2; - break; - } - case 7: { - // == - //printf("compare: %" PRId64 " == %" PRId64 "\n", v1, v2); - res.intval = v1 == v2; - break; - } - case 8: { - // != - //printf("compare: %" PRId64 " != %" PRId64 "\n", v1, v2); - res.intval = v1 != v2; - break; - } - case 9: { - // <= - //printf("compare: %" PRId64 " <= %" PRId64 "\n", v1, v2); - res.intval = v1 <= v2; - break; - } - case 10: { - // >= - //printf("compare: %" PRId64 " >= %" PRId64 "\n", v1, v2); - res.intval = v1 >= v2; - break; - } - case 11: { - // and - //printf("compare: %" PRId64 " and %" PRId64 "\n", v1, v2); - res.intval = v1 && v2; - break; - } - case 12: { - // or - //printf("compare: %" PRId64 " or %" PRId64 "\n", v1, v2); - res.intval = v1 || v2; - break; - } - case 13: { - // xor - //printf("compare: %" PRId64 " xor %" PRId64 "\n", v1, v2); - res.intval = v1 ^ v2; - break; - } - } - return res; -} - -DavQOp compare_strint(int op, DavQOp v1, DavQOp v2) { - int64_t v1int; - sstr_t s1 = sstrn(v1.val, v1.intval); - s1 = sstrdup(s1); - if(util_strtoint(s1.ptr, &v1int)) { - free(s1.ptr); - return compare_intint(op, v1int, v2.intval); - } else { - free(s1.ptr); - // TODO - } -} - -DavQOp compare_intstr(int op, DavQOp v1, DavQOp v2) { - // TODO -} - -DavQOp compare_strstr(int op, DavQOp v1, DavQOp v2) { - DavQOp res; - res.type = DAVQOP_INTEGER; - res.val = NULL; - res.intval = 0; - sstr_t s1 = sstrn(v1.val, v1.intval); - sstr_t s2 = sstrn(v2.val, v2.intval); - switch(op) { - case 5: { - // < - //printf("str compare: %.*s < %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = s1.length < s2.length; - break; - } - case 6: { - // > - //printf("str compare: %.*s > %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = s1.length > s2.length; - break; - } - case 7: { - // == - //printf("str compare: %.*s == %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = sstrcmp(s1, s2) == 0; - break; - } - case 8: { - // != - //printf("str compare: %.*s != %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = sstrcmp(s1, s2) != 0; - break; - } - case 9: { - // <= - //printf("str compare: %.*s <= %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = s1.length <= s2.length; - break; - } - case 10: { - // >= - //printf("str compare: %.*s >= %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = s1.length >= s2.length; - break; - } - case 11: { - // and - //printf("str compare: %.*s and %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = s1.ptr && s2.ptr; - break; - } - case 12: { - // or - //printf("str compare: %.*s or %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = s1.ptr || s2.ptr; - break; - } - case 13: { - // xor - //printf("str compare: %.*s xor %.*s\n", s1.length, s1.ptr, s2.length, s2.ptr); - res.intval = (intptr_t)s1.ptr ^ (intptr_t)s2.ptr; - break; - } - } - return res; -}