libidav/davql.c

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

mercurial