move control socket handling to separate file
[mizunara.git] / libidav / davqlexec.c
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright 2018 Olaf Wintermann. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above copyright
13  *      notice, this list of conditions and the following disclaimer in the
14  *      documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <inttypes.h>
33
34 #include <ucx/utils.h>
35 #include <ucx/map.h>
36 #include "davqlexec.h"
37 #include "utils.h"
38 #include "methods.h"
39 #include "session.h"
40 #include "resource.h"
41
42 DavQLArgList* dav_ql_get_args(DavQLStatement *st, va_list ap) {
43     DavQLArgList *args = malloc(sizeof(DavQLArgList));
44     if(!args) {
45         return NULL;
46     }
47     args->first = NULL;
48     
49     DavQLArg *cur = NULL;
50     UCX_FOREACH(elm, st->args) {
51         intptr_t type = (intptr_t)elm->data;
52         DavQLArg *arg = calloc(1, sizeof(DavQLArg));
53         if(!arg) {
54             dav_ql_free_arglist(args);
55             return NULL;
56         }
57         arg->type = type;
58         switch(type) {
59             case 'd': {
60                 arg->value.d = va_arg(ap, int);
61                 break;
62             }
63             case 'u': {
64                 arg->value.u = va_arg(ap, unsigned int);
65                 break;
66             }
67             case 's': {
68                 arg->value.s = va_arg(ap, char*);
69                 break;
70             }
71             case 't': {
72                 arg->value.t = va_arg(ap, time_t);
73                 break;
74             }
75             default: {
76                 free(arg);
77                 dav_ql_free_arglist(args);
78                 return NULL;
79             }
80         }
81         if(cur) {
82             cur->next = arg;
83         } else {
84             args->first = arg;
85         }
86         cur = arg;
87     }
88     args->current = args->first;
89     return args;
90 }
91
92 void dav_ql_free_arglist(DavQLArgList *args) {
93     DavQLArg *arg = args->first;
94     while(arg) {
95         DavQLArg *next = arg->next;
96         free(arg);
97         arg = next;
98     }
99     free(args);
100 }
101
102 static DavQLArg* arglist_get(DavQLArgList *args) {
103     DavQLArg *a = args->current;
104     if(a) {
105         args->current = a->next;
106     }
107     return a;
108 }
109
110 int dav_ql_getarg_int(DavQLArgList *args) {
111     DavQLArg *a = arglist_get(args);
112     if(a && a->type == 'd') {
113         return a->value.d;
114     }
115     return 0;
116 }
117
118 unsigned int dav_ql_getarg_uint(DavQLArgList *args) {
119     DavQLArg *a = arglist_get(args);
120     if(a && a->type == 'u') {
121         return a->value.u;
122     }
123     return 0;
124 }
125
126 char* dav_ql_getarg_str(DavQLArgList *args) {
127     DavQLArg *a = arglist_get(args);
128     if(a && a->type == 's') {
129         return a->value.s;
130     }
131     return "";
132 }
133
134 time_t dav_ql_getarg_time(DavQLArgList *args) {
135     DavQLArg *a = arglist_get(args);
136     if(a && a->type == 't') {
137         return a->value.t;
138     }
139     return 0;
140 }
141
142
143 DavResult dav_statement_exec(DavSession *sn, DavQLStatement *st, ...) {
144     va_list ap;
145     va_start(ap, st);
146     DavResult result = dav_statement_execv(sn, st, ap);
147     va_end(ap);
148     return result;
149 }
150
151 DavResult dav_statement_execv(DavSession *sn, DavQLStatement *st, va_list ap) {
152     DavResult result;
153     result.result = NULL;
154     result.status = 1;
155     
156     // make sure the statement was successfully parsed
157     if(st->type == DAVQL_ERROR) {
158         return result;
159     }
160     
161     if(st->type == DAVQL_SELECT) {
162         return dav_exec_select(sn, st, ap);
163     } else {
164         // TODO
165     }
166     
167     return result;
168 }
169
170 sstr_t dav_format_string(UcxAllocator *a, sstr_t fstr, DavQLArgList *ap, davqlerror_t *error) {
171     UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
172     
173     int placeholder = 0;
174     for(int i=0;i<fstr.length;i++) {
175         char c = fstr.ptr[i];
176         if(placeholder) {
177             if(c == '%') {
178                 // no placeholder, %% transposes to %
179                 ucx_buffer_putc(buf, c);
180             } else {
181                 // detect placeholder type and insert arg
182                 int err = 0;
183                 switch(c) {
184                     case 's': {
185                         char *arg = dav_ql_getarg_str(ap);
186                         ucx_buffer_puts(buf, arg);
187                         break;
188                     }
189                     case 'd': {
190                         int arg = dav_ql_getarg_int(ap);
191                         ucx_bprintf(buf, "%d", arg);
192                         break;
193                     }
194                     case 'u': {
195                         unsigned int arg = dav_ql_getarg_uint(ap);
196                         ucx_bprintf(buf, "%u", arg);
197                         break;
198                     }
199                     case 't': {
200                         // time arguments not supported for strings
201                         err = 1;
202                         break;
203                     }
204                     default: {
205                         *error = DAVQL_UNKNOWN_FORMATCHAR;
206                         err = 1;
207                     }
208                 }
209                 if(err) {
210                     ucx_buffer_free(buf);
211                     sstr_t n;
212                     n.ptr = NULL;
213                     n.length = 0;
214                     return n;
215                 }
216             }
217             placeholder = 0;
218         } else {
219             if(c == '%') {
220                 placeholder = 1;
221             } else {
222                 ucx_buffer_putc(buf, c);
223             }
224         }
225     }
226     *error = DAVQL_OK;
227     
228     sstr_t ret = sstrdup_a(a, sstrn(buf->space, buf->size));
229     ucx_buffer_free(buf);
230     return ret;
231 }
232
233 static int fl_add_properties(DavSession *sn, UcxMempool *mp, UcxMap *map, DavQLExpression *expression) {
234     if(!expression) {
235         return 0;
236     }
237     
238     if(expression->type == DAVQL_IDENTIFIER) {
239         DavProperty *property = ucx_mempool_malloc(mp, sizeof(DavProperty));
240
241         char *name;
242         DavNamespace *ns = dav_get_property_namespace(
243                 sn->context,
244                 sstrdup_a(mp->allocator, expression->srctext).ptr,
245                 &name);
246         if(!ns) {
247             return -1;
248         }
249         
250         property->ns = ns;
251         property->name = name;
252         property->value = NULL;
253         
254         ucx_map_sstr_put(map, expression->srctext, property);
255     }
256     
257     if(expression->left) {
258         if(fl_add_properties(sn, mp, map, expression->left)) {
259             return -1;
260         }
261     }
262     if(expression->right) {
263         if(fl_add_properties(sn, mp, map, expression->right)) {
264             return -1;
265         }
266     }
267     
268     return 0;
269 }
270
271 static UcxBuffer* fieldlist2propfindrequest(DavSession *sn, UcxMempool *mp, UcxList *fields, int *isallprop) {
272     UcxMap *properties = ucx_map_new(32);
273     *isallprop = 0;
274     
275     UCX_FOREACH(elm, fields) {
276         DavQLField *field = elm->data;
277         if(!sstrcmp(field->name, S("*"))) {
278             ucx_map_free(properties);
279             *isallprop = 1;
280             return create_allprop_propfind_request();
281         } else if(!sstrcmp(field->name, S("-"))) {
282             ucx_map_free(properties);
283             return create_propfind_request(sn, NULL, "propfind", 0);
284         } else {
285             if(fl_add_properties(sn, mp, properties, field->expr)) {
286                 // TODO: set error
287                 ucx_map_free(properties);
288                 return NULL;
289             }
290         }
291     }
292     
293     UcxMapIterator i = ucx_map_iterator(properties);
294     UcxKey key;
295     DavProperty *value;
296     UcxList *list = NULL;
297     UCX_MAP_FOREACH(key, value, i) {
298         list = ucx_list_append(list, value);
299     }
300     
301     UcxBuffer *reqbuf = create_propfind_request(sn, list, "propfind", 0);
302     ucx_list_free(list);
303     ucx_map_free(properties);
304     return reqbuf;
305 }
306
307 static int reset_properties(DavSession *sn, DavResult *result, DavResource *res, UcxList *fields) {
308     UcxMap *new_properties = ucx_map_new_a(sn->mp->allocator, 32);
309     DavResourceData *data = (DavResourceData*)res->data;
310     
311     // add basic properties
312     void *value;
313     
314     sstr_t cl_keystr = dav_property_key("DAV:", "getcontentlength");
315     UcxKey cl_key = ucx_key(cl_keystr.ptr, cl_keystr.length);
316     value = ucx_map_get(data->properties, cl_key);
317     if(value) {
318         ucx_map_put(new_properties, cl_key, value);
319     }
320     
321     sstr_t cd_keystr = dav_property_key("DAV:", "creationdate");
322     UcxKey cd_key = ucx_key(cd_keystr.ptr, cd_keystr.length);
323     value = ucx_map_get(data->properties, cd_key);
324     if(value) {
325         ucx_map_put(new_properties, cd_key, value);
326     }
327     
328     sstr_t lm_keystr = dav_property_key("DAV:", "getlastmodified");
329     UcxKey lm_key = ucx_key(lm_keystr.ptr, lm_keystr.length);
330     value = ucx_map_get(data->properties, lm_key);
331     if(value) {
332         ucx_map_put(new_properties, lm_key, value);
333     }
334     
335     sstr_t ct_keystr = dav_property_key("DAV:", "getcontenttype");
336     UcxKey ct_key = ucx_key(ct_keystr.ptr, ct_keystr.length);
337     value = ucx_map_get(data->properties, ct_key);
338     if(value) {
339         ucx_map_put(new_properties, ct_key, value);
340     }
341     
342     sstr_t rt_keystr = dav_property_key("DAV:", "resourcetype");
343     UcxKey rt_key = ucx_key(rt_keystr.ptr, rt_keystr.length);
344     value = ucx_map_get(data->properties, rt_key);
345     if(value) {
346         ucx_map_put(new_properties, rt_key, value);
347     }
348     
349     sstr_t cn_keystr = dav_property_key(DAV_NS, "crypto-name");
350     UcxKey cn_key = ucx_key(cn_keystr.ptr, cn_keystr.length);
351     value = ucx_map_get(data->properties, cn_key);
352     if(value) {
353         ucx_map_put(new_properties, cn_key, value);
354     }
355     
356     sstr_t ck_keystr = dav_property_key(DAV_NS, "crypto-key");
357     UcxKey ck_key = ucx_key(ck_keystr.ptr, ck_keystr.length);
358     value = ucx_map_get(data->properties, ck_key);
359     if(value) {
360         ucx_map_put(new_properties, ck_key, value);
361     }
362     
363     sstr_t ch_keystr = dav_property_key(DAV_NS, "crypto-hash");
364     UcxKey ch_key = ucx_key(ch_keystr.ptr, ch_keystr.length);
365     value = ucx_map_get(data->properties, ch_key);
366     if(value) {
367         ucx_map_put(new_properties, ch_key, value);
368     }
369     
370     // add properties from field list
371     UCX_FOREACH(elm, fields) {
372         DavCompiledField *field = elm->data;
373         DavQLStackObj field_result;
374         if(!dav_exec_expr(field->code, res, &field_result)) {
375             sstr_t str;
376             str.ptr = NULL;
377             str.length = 0;
378             DavXmlNode *node = NULL;
379             if(field_result.type == 0) {
380                 str = ucx_asprintf(
381                         sn->mp->allocator,
382                         "%d",
383                         field_result.data.integer);
384             } else if(field_result.type == 1) {
385                 if(field_result.data.string) {
386                     str = sstrdup_a(sn->mp->allocator, sstrn(
387                             field_result.data.string,
388                             field_result.length));
389                 }
390             } else if(field_result.type == 2) {
391                 node = dav_copy_node(field_result.data.node);
392             } else {
393                 // unknown type
394                 // TODO: error
395                 resource_free_properties(sn, new_properties);
396                 return -1;
397             }
398             if(str.ptr) {
399                 node = dav_session_malloc(sn, sizeof(DavXmlNode));
400                 memset(node, 0, sizeof(DavXmlNode));
401                 node->type = DAV_XML_TEXT;
402                 node->content = str.ptr;
403                 node->contentlength = str.length;
404             }
405             if(node) {
406                 sstr_t key = dav_property_key(field->ns, field->name);
407                 
408                 DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace));
409                 namespace->prefix = NULL;
410                 namespace->name = dav_session_strdup(sn, field->ns);
411
412                 DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty));
413                 prop->name = dav_session_strdup(sn, field->name);
414                 prop->ns = namespace;
415                 prop->value = node;
416                 
417                 ucx_map_sstr_put(new_properties, key, prop);
418                 free(key.ptr);
419             }
420         } else {
421             // TODO: error
422             resource_free_properties(sn, new_properties);
423             return -1;
424         }
425     }
426     
427     ucx_map_remove(data->properties, cl_key);
428     ucx_map_remove(data->properties, cd_key);
429     ucx_map_remove(data->properties, lm_key);
430     ucx_map_remove(data->properties, ct_key);
431     ucx_map_remove(data->properties, rt_key);
432     ucx_map_remove(data->properties, cn_key);
433     ucx_map_remove(data->properties, ck_key);
434     ucx_map_remove(data->properties, ch_key);
435     
436     resource_free_properties(sn, data->properties);
437     data->properties = new_properties;
438     
439     free(cl_keystr.ptr);
440     free(cd_keystr.ptr);
441     free(lm_keystr.ptr);
442     free(ct_keystr.ptr);
443     free(rt_keystr.ptr);
444     free(cn_keystr.ptr);
445     free(ck_keystr.ptr);
446     free(ch_keystr.ptr);
447     
448     return 0;
449 }
450
451 /*
452  * execute a davql select statement
453  */
454 DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) {
455     UcxMempool *mp = ucx_mempool_new(128);
456     DavResult result;
457     result.result = NULL;
458     result.status = 1;
459     
460     DavQLArgList *args = dav_ql_get_args(st, ap);
461     if(!args) {
462         return result;
463     }
464     ucx_mempool_reg_destr(mp, args, (ucx_destructor)dav_ql_free_arglist);
465     
466     int isallprop;
467     UcxBuffer *rqbuf = fieldlist2propfindrequest(sn, mp, st->fields, &isallprop);
468     if(!rqbuf) {
469         ucx_mempool_destroy(mp);
470         return result;
471     }
472     ucx_mempool_reg_destr(mp, rqbuf, (ucx_destructor)ucx_buffer_free);
473     
474     // compile field list
475     UcxList *cfieldlist = NULL;
476     UCX_FOREACH(elm, st->fields) {
477         DavQLField *field = elm->data;
478         if(sstrcmp(field->name, S("*")) && sstrcmp(field->name, S("-"))) {
479             // compile field expression
480             UcxBuffer *code = dav_compile_expr(
481                     sn->context,
482                     mp->allocator,
483                     field->expr,
484                     args);
485             if(!code) {
486                 // TODO: set error string
487                 return result;
488             }
489             ucx_mempool_reg_destr(mp, code, (ucx_destructor)ucx_buffer_free);
490             DavCompiledField *cfield = ucx_mempool_malloc(
491                     mp,
492                     sizeof(DavCompiledField));
493             
494             char *ns;
495             char *name;
496             dav_get_property_namespace_str(
497                 sn->context,
498                 sstrdup_a(mp->allocator, field->name).ptr,
499                 &ns,
500                 &name);
501             if(!ns || !name) {
502                 // TODO: set error string
503                 return result;
504             }
505             cfield->ns = ns;
506             cfield->name = name;
507             cfield->code = code;
508             cfieldlist = ucx_list_append_a(mp->allocator, cfieldlist, cfield);
509         } 
510     }
511     
512     // get path string
513     davqlerror_t error;
514     sstr_t path = dav_format_string(mp->allocator, st->path, args, &error);
515     if(error) {
516         // TODO: cleanup
517         ucx_mempool_destroy(mp);
518         return result;
519     }
520     
521     int depth = st->depth == DAV_DEPTH_PLACEHOLDER ?
522             dav_ql_getarg_int(args) : st->depth;
523     
524     UcxBuffer *where = dav_compile_expr(sn->context, mp->allocator, st->where, args);
525     if(st->where && !where) {
526         // TODO: cleanup
527         ucx_mempool_destroy(mp);
528         return result;
529     }
530     if(where) {
531         ucx_mempool_reg_destr(mp, where, (ucx_destructor)ucx_buffer_free);
532     }
533     
534     // compile order criterion
535     UcxList *ordercr = NULL;
536     UCX_FOREACH(elm, st->orderby) {
537         DavQLOrderCriterion *oc = elm->data;
538         DavQLExpression *column = oc->column;
539         //printf("%.*s %s\n", column->srctext.length, column->srctext.ptr, oc->descending ? "desc" : "asc");
540         if(column->type == DAVQL_IDENTIFIER) {
541             // TODO: remove code duplication (add_cmd)
542             davqlresprop_t resprop;
543             sstr_t propertyname = sstrchr(column->srctext, ':');
544             if(propertyname.length > 0) {
545                 char *ns;
546                 char *name;
547                 dav_get_property_namespace_str(
548                         sn->context,
549                         sstrdup_a(mp->allocator, column->srctext).ptr,
550                         &ns,
551                         &name);
552                 if(ns && name) {
553                     DavOrderCriterion *cr = ucx_mempool_malloc(mp, sizeof(DavOrderCriterion));
554                     cr->type = 1;
555                     sstr_t keystr = dav_property_key_a(mp->allocator, ns, name);
556                     cr->column.property = ucx_key(keystr.ptr, keystr.length);
557                     cr->descending = oc->descending;
558                     ordercr = ucx_list_append_a(mp->allocator, ordercr, cr);
559                 } else {
560                     // error
561                     // TODO: cleanup
562                     ucx_mempool_destroy(mp);
563                     return result;
564                 }
565             } else if(dav_identifier2resprop(column->srctext, &resprop)) {
566                 DavOrderCriterion *cr = ucx_mempool_malloc(mp, sizeof(DavOrderCriterion));
567                 cr->type = 0;
568                 cr->column.resprop = resprop;
569                 cr->descending = oc->descending;
570                 ordercr = ucx_list_append_a(mp->allocator, ordercr, cr);
571             } else {
572                 // error
573                 // TODO: cleanup
574                 ucx_mempool_destroy(mp);
575                 return result;
576             }
577             
578         } else if(column->type == DAVQL_NUMBER) {
579             // TODO: implement
580             fprintf(stderr, "order by number not supported\n");
581             return result;
582         } else {
583             // something is broken
584             // TODO: cleanup
585             ucx_mempool_destroy(mp);
586             return result;
587         }
588     }
589     
590     DavResource *selroot = dav_resource_new(sn, path.ptr);
591     
592     UcxList *stack = NULL; // stack with DavResource* elements
593     // initialize the stack with the requested resource
594     DavQLRes *res = ucx_mempool_malloc(mp, sizeof(DavQLRes));
595     res->resource = selroot;
596     res->depth = 0;
597     stack = ucx_list_prepend(stack, res);
598     
599     // reuseable response buffer
600     UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
601     if(!rpbuf) {
602         // TODO: cleanup
603         ucx_mempool_destroy(mp);
604         return result;
605     }
606     ucx_mempool_reg_destr(mp, rpbuf, (ucx_destructor)ucx_buffer_free);
607     
608     result.result = selroot;
609     result.status = 0;
610     
611     // do a propfind request for each resource on the stack
612     while(stack) {
613         DavQLRes *sr = stack->data; // get first element from the stack
614         stack = ucx_list_remove(stack, stack); // remove first element
615         DavResource *root = sr->resource;
616         
617         util_set_url(sn, dav_resource_get_href(sr->resource));
618         CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
619         long http_status = 0;
620         curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status);
621         //printf("rpbuf: %s %s\n%.*s\n\n", sr->resource->path, sr->resource->href, rpbuf->pos, rpbuf->space);
622         
623         if(ret == CURLE_OK && http_status == 207) {
624             // in case of an redirect we have to adjust resource->href
625             dav_set_effective_href(sn, root);
626             
627             // propfind request successful, now parse the response
628             char *url = "http://url/";
629             PropfindParser *parser = create_propfind_parser(rpbuf, url);
630             // TODO: test if parser is null
631             ResponseTag response;
632             int r;
633             while((r = get_propfind_response(parser, &response)) != 0) {
634                 if(r == -1) {
635                     // error
636                     result.status = -1;
637                     // TODO: free resources
638                     cleanup_response(&response);
639                     break;
640                 }
641                 
642                 // the propfind multistatus response contains responses
643                 // for the requested resource and all childs
644                 // determine if the response is a child or not
645                 if(hrefeq(sn, root->href, response.href)) {
646                     // response is the currently requested resource
647                     // and not a child
648                     
649                     // add properties
650                     add_properties(root, &response);
651                     cleanup_response(&response);
652                     
653                     if(root == selroot) {                     
654                         // The current root is the root of the select query.
655                         // In this case we have to check the where clause.
656                         // If root is not selroot, the where clause was
657                         // already checked for the resource before it was
658                         // added to the stack.
659                         DavQLStackObj where_result;
660                         if(!dav_exec_expr(where, root, &where_result)) {                           
661                             if(where_result.data.integer != 0) { 
662                                 if(!reset_properties(sn, &result, root, cfieldlist)) {
663                                     continue;
664                                 }
665                                 result.status = -1;
666                             }
667                         }
668                         result.result = NULL;
669                         result.status = -1;
670                         dav_resource_free_all(selroot);
671                         ucx_list_free(stack);
672                         break;
673                     }
674                 } else {
675                     DavResource *child = response2resource(
676                             sn,
677                             &response,
678                             root->path);
679                     cleanup_response(&response);
680                     // check where clause
681                     DavQLStackObj where_result;
682                     if(!dav_exec_expr(where, child, &where_result)) {
683                         if(where_result.data.integer != 0) {
684                             if(!reset_properties(sn, &result, child, cfieldlist)) {
685                                 //resource_add_child(root, child);
686                                 resource_add_ordered_child(root, child, ordercr);
687                                 if(child->iscollection &&
688                                     (depth < 0 || depth > sr->depth+1))
689                                 {
690                                     DavQLRes *rs = ucx_mempool_malloc(
691                                             mp,
692                                             sizeof(DavQLRes));
693                                     rs->resource = child;
694                                     rs->depth = sr->depth + 1;
695                                     stack = ucx_list_prepend(stack, rs);
696                                 }
697                             } else {
698                                 dav_resource_free(child);
699                             }
700                         } else {
701                             dav_resource_free(child);
702                         }
703                     }
704                 }    
705             }
706             destroy_propfind_parser(parser);
707         } else  {
708             dav_session_set_error(sn, ret, http_status);
709             result.result = NULL;
710             result.status = -1;
711             dav_resource_free_all(selroot);
712             break;
713         }
714         
715         // reset response buffer
716         ucx_buffer_seek(rpbuf, SEEK_SET, 0);
717     }
718     
719     ucx_mempool_destroy(mp);
720     return result;
721 }
722
723 static int count_func_args(DavQLExpression *expr) {
724     int count = 0;
725     DavQLExpression *arg = expr->right;
726     while(arg) {
727         count++;
728         if(arg->op == DAVQL_ARGLIST) {
729             arg = arg->right;
730         } else {
731             break;
732         }
733     }
734     return count;
735 }
736
737 int dav_identifier2resprop(sstr_t src, davqlresprop_t *prop) {
738     if(!sstrcmp(src, S("name"))) {
739         *prop = DAVQL_RES_NAME;
740     } else if(!sstrcmp(src, S("path"))) {
741         *prop = DAVQL_RES_PATH;
742     } else if(!sstrcmp(src, S("href"))) {
743         *prop = DAVQL_RES_HREF;
744     } else if(!sstrcmp(src, S("contentlength"))) {
745         *prop = DAVQL_RES_CONTENTLENGTH;
746     } else if(!sstrcmp(src, S("contenttype"))) {
747         *prop = DAVQL_RES_CONTENTTYPE;
748     } else if(!sstrcmp(src, S("creationdate"))) {
749         *prop = DAVQL_RES_CREATIONDATE;
750     } else if(!sstrcmp(src, S("lastmodified"))) {
751         *prop = DAVQL_RES_LASTMODIFIED;
752     } else if(!sstrcmp(src, S("iscollection"))) {
753         *prop = DAVQL_RES_ISCOLLECTION;
754     } else {
755         return 0;
756     }
757     return 1;
758 }
759
760 static int add_cmd(DavContext *ctx, UcxAllocator *a, UcxBuffer *bcode, DavQLExpression *expr, DavQLArgList *ap) {
761     if(!expr) {
762         return 0;
763     }
764      
765     int numcmd = 1;
766     DavQLCmd cmd;
767     memset(&cmd, 0, sizeof(DavQLCmd));
768     davqlerror_t error;
769     
770     sstr_t src = expr->srctext;
771     switch(expr->type) {
772         default: break;
773         case DAVQL_NUMBER: {   
774             cmd.type = DAVQL_CMD_INT;
775             if(src.ptr[0] == '%') {
776                 cmd.data.integer = dav_ql_getarg_int(ap);
777             } else if(util_strtoint(src.ptr, &cmd.data.integer)) {
778                 ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
779             } else {
780                 // error
781                 return -1;
782             }
783             
784             break;
785         }
786         case DAVQL_STRING: {
787             cmd.type = DAVQL_CMD_STRING;
788             cmd.data.string = dav_format_string(a, src, ap, &error);
789             ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
790             break;
791         }
792         case DAVQL_TIMESTAMP: {
793             if(src.ptr[0] == '%') {
794                 cmd.type = DAVQL_CMD_TIMESTAMP;
795                 cmd.data.timestamp = dav_ql_getarg_time(ap);
796                 ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
797             } else {
798                 // error
799                 return -1;
800             }
801             break;
802         }
803         case DAVQL_IDENTIFIER: {
804             sstr_t propertyname = sstrchr(src, ':');
805             cmd.type = DAVQL_CMD_RES_IDENTIFIER;
806             if(propertyname.length > 0) {
807                 cmd.type = DAVQL_CMD_PROP_IDENTIFIER;
808                 char *ns;
809                 char *name;
810                 dav_get_property_namespace_str(
811                         ctx,
812                         sstrdup_a(a, src).ptr,
813                         &ns,
814                         &name);
815                 if(ns && name) {
816                     cmd.data.property.ns = ns;
817                     cmd.data.property.name = name;
818                 } else {
819                     // error
820                     return -1;
821                 }
822             } else if(!dav_identifier2resprop(src, &cmd.data.resprop)) {
823                 if(!sstrcmp(src, S("true"))) {
824                     cmd.type = DAVQL_CMD_INT;
825                     cmd.data.integer = 1;
826                 } else if(!sstrcmp(src, S("false"))) {
827                     cmd.type = DAVQL_CMD_INT;
828                     cmd.data.integer = 0;
829                 } else {
830                     // error, unknown identifier
831                     return -1;
832                 }
833             }
834             ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
835             break;
836         }
837         case DAVQL_UNARY: {
838             numcmd += add_cmd(ctx, a, bcode, expr->left, ap);
839             switch(expr->op) {
840                 case DAVQL_ADD: {
841                     // noop
842                     numcmd = 0;
843                     break;
844                 }
845                 case DAVQL_SUB: {
846                     cmd.type = DAVQL_CMD_OP_UNARY_SUB;
847                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
848                     break;
849                 }
850                 case DAVQL_NEG: {
851                     cmd.type = DAVQL_CMD_OP_UNARY_NEG;
852                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
853                     break;
854                 }
855                 default: break;
856             }
857             break;
858         }
859         case DAVQL_BINARY: {
860             numcmd += add_cmd(ctx, a, bcode, expr->left, ap);
861             numcmd += add_cmd(ctx, a, bcode, expr->right, ap);
862             switch(expr->op) {
863                 case DAVQL_ADD: {
864                     cmd.type = DAVQL_CMD_OP_BINARY_ADD;
865                     break;
866                 }
867                 case DAVQL_SUB: {
868                     cmd.type = DAVQL_CMD_OP_BINARY_SUB;
869                     break;
870                 }
871                 case DAVQL_MUL: {
872                     cmd.type = DAVQL_CMD_OP_BINARY_MUL;
873                     break;
874                 }
875                 case DAVQL_DIV: {
876                     cmd.type = DAVQL_CMD_OP_BINARY_DIV;
877                     break;
878                 }
879                 case DAVQL_AND: {
880                     cmd.type = DAVQL_CMD_OP_BINARY_AND;
881                     break;
882                 }
883                 case DAVQL_OR: {
884                     cmd.type = DAVQL_CMD_OP_BINARY_OR;
885                     break;
886                 }
887                 case DAVQL_XOR: {
888                     cmd.type = DAVQL_CMD_OP_BINARY_XOR;
889                     break;
890                 }
891                 default: break;
892             }
893             ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
894             break;
895         }
896         case DAVQL_LOGICAL: {
897             if(expr->left && expr->right && expr->op != DAVQL_LOR) {
898                 numcmd += add_cmd(ctx, a, bcode, expr->left, ap);
899                 numcmd += add_cmd(ctx, a, bcode, expr->right, ap);
900             }
901             
902             switch(expr->op) {
903                 case DAVQL_NOT: {
904                     numcmd += add_cmd(ctx, a, bcode, expr->left, ap);
905                     cmd.type = DAVQL_CMD_OP_LOGICAL_NOT;
906                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
907                     break;
908                 }
909                 case DAVQL_LAND: {
910                     cmd.type = DAVQL_CMD_OP_LOGICAL_AND;
911                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
912                     break;
913                 }
914                 case DAVQL_LOR: {
915                     int nleft = add_cmd(ctx, a, bcode, expr->left, ap);
916                     
917                     cmd.type = DAVQL_CMD_OP_LOGICAL_OR_L;
918                     DavQLCmd *or_l = (DavQLCmd*)(bcode->space + bcode->pos);
919                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
920                     
921                     int nright = add_cmd(ctx, a, bcode, expr->right, ap);
922                     or_l->data.integer = nright + 1;
923                     
924                     cmd.type = DAVQL_CMD_OP_LOGICAL_OR;
925                     cmd.data.integer = 0;
926                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
927                     
928                     numcmd += nleft + nright;
929                     break;
930                 }
931                 case DAVQL_LXOR: {
932                     cmd.type = DAVQL_CMD_OP_LOGICAL_XOR;
933                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
934                     break;
935                 }
936                 case DAVQL_EQ: {
937                     cmd.type = DAVQL_CMD_OP_EQ;
938                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
939                     break;
940                 }
941                 case DAVQL_NEQ: {
942                     cmd.type = DAVQL_CMD_OP_NEQ;
943                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
944                     break;
945                 }
946                 case DAVQL_LT: {
947                     cmd.type = DAVQL_CMD_OP_LT;
948                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
949                     break;
950                 }
951                 case DAVQL_GT: {
952                     cmd.type = DAVQL_CMD_OP_GT;
953                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
954                     break;
955                 }
956                 case DAVQL_LE: {
957                     cmd.type = DAVQL_CMD_OP_LE;
958                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
959                     break;
960                 }
961                 case DAVQL_GE: {
962                     cmd.type = DAVQL_CMD_OP_GE;
963                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
964                     break;
965                 }
966                 case DAVQL_LIKE: {
967                     cmd.type = DAVQL_CMD_OP_LIKE;
968                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
969                     break;
970                 }
971                 case DAVQL_UNLIKE: {
972                     cmd.type = DAVQL_CMD_OP_UNLIKE;
973                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
974                     break;
975                 }
976                 default: break;
977             }
978             break;
979         }
980         case DAVQL_FUNCCALL: {
981             switch(expr->op) {
982                 case DAVQL_CALL: {
983                     int nright = add_cmd(ctx, a, bcode, expr->right, ap);
984                     // TODO: count args
985                     DavQLExpression *funcid = expr->left;
986                     if(!funcid && funcid->type != DAVQL_IDENTIFIER) {
987                         // fail
988                         return -1;
989                     }
990                     
991                     // numargs
992                     cmd.type = DAVQL_CMD_INT;
993                     cmd.data.integer = count_func_args(expr);
994                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
995                     
996                     // TODO: resolve function name
997                     cmd.type = DAVQL_CMD_CALL;
998                     cmd.data.func = NULL;
999                     ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
1000                     
1001                     numcmd = 2;
1002                     numcmd += nright;
1003                     break;
1004                 }
1005                 case DAVQL_ARGLIST: {
1006                     numcmd = 0;
1007                     numcmd += add_cmd(ctx, a, bcode, expr->left, ap);
1008                     numcmd += add_cmd(ctx, a, bcode, expr->right, ap);
1009                     break;
1010                 }
1011                 default: break;
1012             }
1013             break;
1014         }
1015     }
1016     return numcmd;
1017 }
1018
1019 UcxBuffer* dav_compile_expr(DavContext *ctx, UcxAllocator *a, DavQLExpression *lexpr, DavQLArgList  *ap) {
1020     UcxBuffer *bcode = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
1021     if(!bcode) {
1022         return NULL;
1023     }
1024     
1025     if(add_cmd(ctx, a, bcode, lexpr, ap) <= 0) {
1026         ucx_buffer_free(bcode);
1027         return NULL;
1028     }
1029     
1030     return bcode;
1031 }
1032
1033 static int cmd_str_cmp(DavQLStackObj obj1, DavQLStackObj obj2, davqlcmdtype_t cmd) {
1034     sstr_t s1 = obj1.type == 1 ?
1035         sstrn(obj1.data.string, obj1.length) :
1036         ucx_sprintf("%" PRId64, obj1.data.integer);
1037     sstr_t s2 = obj1.type == 1 ?
1038         sstrn(obj2.data.string, obj2.length) :
1039         ucx_sprintf("%" PRId64, obj2.data.integer);
1040     
1041     int res = 0;
1042     switch(cmd) {
1043         case DAVQL_CMD_OP_EQ: {
1044             res = sstrcmp(s1, s2) == 0;
1045             break;
1046         }
1047         case DAVQL_CMD_OP_NEQ: {
1048             res = sstrcmp(s1, s2) != 0;
1049             break;
1050         }
1051         case DAVQL_CMD_OP_LT: {
1052             res = sstrcmp(s1, s2) < 0;
1053             break;
1054         }
1055         case DAVQL_CMD_OP_GT: {
1056             res = sstrcmp(s1, s2) > 0;
1057             break;
1058         }
1059         case DAVQL_CMD_OP_LE: {
1060             res  = sstrcmp(s1, s2) <= 0;
1061             break;
1062         }
1063         case DAVQL_CMD_OP_GE: {
1064             res = sstrcmp(s1, s2) >= 0;
1065             break;
1066         }
1067         default: break;
1068     }
1069     
1070     if(obj1.type == 0) {
1071         free(s1.ptr);
1072     }
1073     if(obj2.type == 0) {
1074         free(s2.ptr);
1075     }
1076     
1077     return res;
1078 }
1079
1080 int dav_exec_expr(UcxBuffer *bcode, DavResource *res, DavQLStackObj *result) {
1081     if(!bcode) {
1082         result->type = 0;
1083         result->length = 0;
1084         result->data.integer = 1;
1085         return 0;
1086     }
1087     
1088     size_t count = bcode->pos / sizeof(DavQLCmd);
1089     DavQLCmd *cmds = (DavQLCmd*)bcode->space;
1090     
1091     // create execution stack
1092     size_t stsize = 64;
1093     size_t stpos = 0;
1094     DavQLStackObj *stack = calloc(stsize, sizeof(DavQLStackObj));
1095 #define DAVQL_PUSH(obj) \
1096         if(stpos == stsize) { \
1097             stsize += 64; \
1098             DavQLStackObj *stack_newptr; \
1099             stack_newptr = realloc(stack, stsize * sizeof(DavQLStackObj)); \
1100             if(stack_newptr) { \
1101                 stack = stack_newptr; \
1102             } else { \
1103                 free(stack); \
1104                 return -1; \
1105             }\
1106         } \
1107         stack[stpos++] = obj;
1108 #define DAVQL_PUSH_INT(intval) \
1109         { \
1110             DavQLStackObj intobj; \
1111             intobj.type = 0; \
1112             intobj.length = 0; \
1113             intobj.data.integer = intval; \
1114             DAVQL_PUSH(intobj); \
1115         }
1116 #define DAVQL_POP() stack[--stpos]
1117     
1118     DavQLStackObj obj;
1119     int ret = 0;
1120     for(size_t i=0;i<count;i++) {
1121         DavQLCmd cmd = cmds[i];
1122         switch(cmd.type) {
1123             case DAVQL_CMD_INT: {
1124                 //printf("int %lld\n", cmd.data.integer);
1125                 obj.type = 0;
1126                 obj.length = 0;
1127                 obj.data.integer = cmd.data.integer;
1128                 DAVQL_PUSH(obj);
1129                 break;
1130             }
1131             case DAVQL_CMD_STRING: {
1132                 //printf("string \"%.*s\"\n", cmd.data.string.length, cmd.data.string.ptr);
1133                 obj.type = 1;
1134                 obj.length = cmd.data.string.length;
1135                 obj.data.string = cmd.data.string.ptr;
1136                 DAVQL_PUSH(obj);
1137                 break;
1138             }
1139             case DAVQL_CMD_TIMESTAMP: {
1140                 //printf("timestamp %d\n", cmd.data.timestamp);
1141                 obj.type = 0;
1142                 obj.length = 0;
1143                 obj.data.integer = (int64_t)cmd.data.timestamp;
1144                 DAVQL_PUSH(obj);
1145                 break;
1146             }
1147             case DAVQL_CMD_RES_IDENTIFIER: {
1148                 //char *rid[8] = {"name", "path", "href", "contentlength", "contenttype", "creationdate", "lastmodified", "iscollection"};
1149                 //printf("resprop %s\n", rid[cmd.data.resprop]);
1150                 switch(cmd.data.resprop) {
1151                     case DAVQL_RES_NAME: {
1152                         obj.type = 1;
1153                         obj.length = strlen(res->name);
1154                         obj.data.string = res->name;
1155                         break;
1156                     }
1157                     case DAVQL_RES_PATH: {
1158                         obj.type = 1;
1159                         obj.length = strlen(res->path);
1160                         obj.data.string = res->path;
1161                         break;
1162                     }
1163                     case DAVQL_RES_HREF: {
1164                         obj.type = 1;
1165                         obj.length = strlen(res->href);
1166                         obj.data.string = res->href;
1167                         break;
1168                     }
1169                     case DAVQL_RES_CONTENTLENGTH: {
1170                         obj.type = 0;
1171                         obj.length = 0;
1172                         obj.data.integer = res->contentlength;
1173                         break;
1174                     }
1175                     case DAVQL_RES_CONTENTTYPE: {
1176                         obj.type = 1;
1177                         obj.length = strlen(res->contenttype);
1178                         obj.data.string = res->contenttype;
1179                         break;
1180                     }
1181                     case DAVQL_RES_CREATIONDATE: {
1182                         obj.type = 0;
1183                         obj.length = 0;
1184                         obj.data.integer = res->creationdate;
1185                         break;
1186                     }
1187                     case DAVQL_RES_LASTMODIFIED: {
1188                         obj.type = 0;
1189                         obj.length = 0;
1190                         obj.data.integer = res->lastmodified;
1191                         break;
1192                     }
1193                     case DAVQL_RES_ISCOLLECTION: {
1194                         obj.type = 0;
1195                         obj.length = 0;
1196                         obj.data.integer = res->iscollection;
1197                         break;
1198                     }
1199                 }
1200                 DAVQL_PUSH(obj);
1201                 break;
1202             }
1203             case DAVQL_CMD_PROP_IDENTIFIER: {
1204                 //printf("property %s:%s\n", cmd.data.property.ns, cmd.data.property.name);
1205                 //char *value = dav_get_string_property_ns(res, cmd.data.property.ns, cmd.data.property.name);
1206                 DavXmlNode *value = dav_get_property_ns(res, cmd.data.property.ns, cmd.data.property.name);
1207                 if(dav_xml_isstring(value)) {
1208                     obj.type = 1;
1209                     obj.length = (uint32_t)value->contentlength;
1210                     obj.data.string = value->content;
1211                 } else {
1212                     obj.type = 2;
1213                     obj.length = 0;
1214                     obj.data.node = value;
1215                 }
1216                 DAVQL_PUSH(obj);
1217                 break;
1218             }
1219             //case DAVQL_CMD_OP_UNARY_ADD: {
1220             //    printf("uadd\n");
1221             //    break;
1222             //}
1223             case DAVQL_CMD_OP_UNARY_SUB: {
1224                 //printf("usub\n");
1225                 obj = DAVQL_POP();
1226                 if(obj.type == 0) {
1227                     obj.data.integer = -obj.data.integer;
1228                     DAVQL_PUSH(obj);
1229                 } else {
1230                     ret = -1;
1231                     i = count; // end loop
1232                 }
1233                 break;
1234             }
1235             case DAVQL_CMD_OP_UNARY_NEG: {
1236                 //printf("uneg\n");
1237                 obj = DAVQL_POP();
1238                 if(obj.type == 0) {
1239                     obj.data.integer = obj.data.integer == 0 ? 1 : 0;
1240                     DAVQL_PUSH(obj);
1241                 } else {
1242                     ret = -1;
1243                     i = count; // end loop
1244                 }
1245                 break;
1246             }
1247             case DAVQL_CMD_OP_BINARY_ADD: {
1248                 //printf("add\n");
1249                 DavQLStackObj obj2 = DAVQL_POP();
1250                 DavQLStackObj obj1 = DAVQL_POP();
1251                 if(obj1.type == 0 && obj2.type == 0) {
1252                     DAVQL_PUSH_INT(obj1.data.integer + obj2.data.integer);
1253                 } else {
1254                     // TODO: string concat
1255                 }
1256                 break;
1257             }
1258             case DAVQL_CMD_OP_BINARY_SUB: {
1259                 //printf("sub\n");
1260                 DavQLStackObj obj2 = DAVQL_POP();
1261                 DavQLStackObj obj1 = DAVQL_POP();
1262                 if(obj1.type == 0 && obj2.type == 0) {
1263                     DAVQL_PUSH_INT(obj1.data.integer - obj2.data.integer);
1264                 } else {
1265                     // error
1266                     ret = -1;
1267                     i = count; // end loop
1268                 }
1269                 break;
1270             }
1271             case DAVQL_CMD_OP_BINARY_MUL: {
1272                 //printf("mul\n");
1273                 DavQLStackObj obj2 = DAVQL_POP();
1274                 DavQLStackObj obj1 = DAVQL_POP();
1275                 if(obj1.type == 0 && obj2.type == 0) {
1276                     DAVQL_PUSH_INT(obj1.data.integer * obj2.data.integer);
1277                 } else {
1278                     // error
1279                     ret = -1;
1280                     i = count; // end loop
1281                 }
1282                 break;
1283             }
1284             case DAVQL_CMD_OP_BINARY_DIV: {
1285                 //printf("div\n");
1286                 DavQLStackObj obj2 = DAVQL_POP();
1287                 DavQLStackObj obj1 = DAVQL_POP();
1288                 if(obj1.type == 0 && obj2.type == 0) {
1289                     DAVQL_PUSH_INT(obj1.data.integer / obj2.data.integer);
1290                 } else {
1291                     // error
1292                     ret = -1;
1293                     i = count; // end loop
1294                 }
1295                 break;
1296             }
1297             case DAVQL_CMD_OP_BINARY_AND: {
1298                 //printf("and\n");
1299                 DavQLStackObj obj2 = DAVQL_POP();
1300                 DavQLStackObj obj1 = DAVQL_POP();
1301                 if(obj1.type == 0 && obj2.type == 0) {
1302                     DAVQL_PUSH_INT(obj1.data.integer & obj2.data.integer);
1303                 } else {
1304                     // error
1305                     ret = -1;
1306                     i = count; // end loop
1307                 }
1308                 break;
1309             }
1310             case DAVQL_CMD_OP_BINARY_OR: {
1311                 //printf("or\n");
1312                 DavQLStackObj obj2 = DAVQL_POP();
1313                 DavQLStackObj obj1 = DAVQL_POP();
1314                 if(obj1.type == 0 && obj2.type == 0) {
1315                     DAVQL_PUSH_INT(obj1.data.integer | obj2.data.integer);
1316                 } else {
1317                     // error
1318                     ret = -1;
1319                     i = count; // end loop
1320                 }
1321                 break;
1322             }
1323             case DAVQL_CMD_OP_BINARY_XOR: {
1324                 //printf("xor\n");
1325                 DavQLStackObj obj2 = DAVQL_POP();
1326                 DavQLStackObj obj1 = DAVQL_POP();
1327                 if(obj1.type == 0 && obj2.type == 0) {
1328                     DAVQL_PUSH_INT(obj1.data.integer ^ obj2.data.integer);
1329                 } else {
1330                     // error
1331                     ret = -1;
1332                     i = count; // end loop
1333                 }
1334                 break;
1335             }
1336             case DAVQL_CMD_OP_LOGICAL_NOT: {
1337                 //printf("not\n");
1338                 break;
1339             }
1340             case DAVQL_CMD_OP_LOGICAL_AND: {
1341                 //printf("land\n");
1342                 DavQLStackObj obj2 = DAVQL_POP();
1343                 DavQLStackObj obj1 = DAVQL_POP();
1344                 int v1 = obj1.type == 0 ? (int)obj1.data.integer : (obj1.data.string ? 1 : 0);
1345                 int v2 = obj2.type == 0 ? (int)obj2.data.integer : (obj2.data.string ? 1 : 0);
1346                 DAVQL_PUSH_INT(v1 && v2);
1347                 break;
1348             }
1349             case DAVQL_CMD_OP_LOGICAL_OR_L: {
1350                 //printf("or_l %d\n", cmd.data.integer);
1351                 DavQLStackObj obj1 = stack[stpos];
1352                 if((obj1.type == 0 && obj1.data.integer) || (obj1.type == 1 && obj1.data.string)) {
1353                     stpos--;
1354                     DAVQL_PUSH_INT(1);
1355                     i += cmd.data.integer; // jump, skip right subtree of 'or'
1356                 }
1357                 break;
1358             }
1359             case DAVQL_CMD_OP_LOGICAL_OR: {
1360                 //printf("or\n");
1361                 DavQLStackObj obj2 = DAVQL_POP();
1362                 DavQLStackObj obj1 = DAVQL_POP();
1363                 int v1 = obj1.type == 0 ? (int)obj1.data.integer : (obj1.data.string ? 1 : 0);
1364                 int v2 = obj2.type == 0 ? (int)obj2.data.integer : (obj2.data.string ? 1 : 0);
1365                 DAVQL_PUSH_INT(v1 || v2);
1366                 break;
1367             }
1368             case DAVQL_CMD_OP_LOGICAL_XOR: {
1369                 //printf("lxor\n");
1370                 DavQLStackObj obj2 = DAVQL_POP();
1371                 DavQLStackObj obj1 = DAVQL_POP();
1372                 int v1 = obj1.type == 0 ? (int)obj1.data.integer : (obj1.data.string ? 1 : 0);
1373                 int v2 = obj2.type == 0 ? (int)obj2.data.integer : (obj2.data.string ? 1 : 0);
1374                 DAVQL_PUSH_INT(!v1 != !v2);
1375                 break;
1376             }
1377             case DAVQL_CMD_OP_EQ: {
1378                 //printf("eq\n");
1379                 DavQLStackObj obj2 = DAVQL_POP();
1380                 DavQLStackObj obj1 = DAVQL_POP();
1381                 if(obj1.type == 0 && obj2.type == 0) {
1382                     DAVQL_PUSH_INT(obj1.data.integer == obj2.data.integer);
1383                 } else {
1384                     DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type));
1385                 }
1386                 break;
1387             }
1388             case DAVQL_CMD_OP_NEQ: {
1389                 //printf("neq\n");
1390                 DavQLStackObj obj2 = DAVQL_POP();
1391                 DavQLStackObj obj1 = DAVQL_POP();
1392                 if(obj1.type == 0 && obj2.type == 0) {
1393                     DAVQL_PUSH_INT(obj1.data.integer != obj2.data.integer);
1394                 } else {
1395                     DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type));
1396                 }
1397                 break;
1398             }
1399             case DAVQL_CMD_OP_LT: {
1400                 //printf("lt\n");
1401                 DavQLStackObj obj2 = DAVQL_POP();
1402                 DavQLStackObj obj1 = DAVQL_POP();
1403                 if(obj1.type == 0 && obj2.type == 0) {
1404                     DAVQL_PUSH_INT(obj1.data.integer < obj2.data.integer);
1405                 } else {
1406                     DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type));
1407                 }
1408                 break;
1409             }
1410             case DAVQL_CMD_OP_GT: {
1411                 //printf("gt\n");
1412                 DavQLStackObj obj2 = DAVQL_POP();
1413                 DavQLStackObj obj1 = DAVQL_POP();
1414                 if(obj1.type == 0 && obj2.type == 0) {
1415                     DAVQL_PUSH_INT(obj1.data.integer > obj2.data.integer);
1416                 } else {
1417                     DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type));
1418                 }
1419                 break;
1420             }
1421             case DAVQL_CMD_OP_LE: {
1422                 //printf("le\n");
1423                 DavQLStackObj obj2 = DAVQL_POP();
1424                 DavQLStackObj obj1 = DAVQL_POP();
1425                 if(obj1.type == 0 && obj2.type == 0) {
1426                     DAVQL_PUSH_INT(obj1.data.integer <= obj2.data.integer);
1427                 } else {
1428                     DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type));
1429                 }
1430                 break;
1431             }
1432             case DAVQL_CMD_OP_GE: {
1433                 //printf("ge\n");
1434                 DavQLStackObj obj2 = DAVQL_POP();
1435                 DavQLStackObj obj1 = DAVQL_POP();
1436                 if(obj1.type == 0 && obj2.type == 0) {
1437                     DAVQL_PUSH_INT(obj1.data.integer >= obj2.data.integer);
1438                 } else {
1439                     DAVQL_PUSH_INT(cmd_str_cmp(obj1, obj2, cmd.type));
1440                 }
1441                 break;
1442             }
1443             case DAVQL_CMD_OP_LIKE: {
1444                 //printf("like\n");
1445                 break;
1446             }
1447             case DAVQL_CMD_OP_UNLIKE: {
1448                 //printf("unlike\n");
1449                 break;
1450             }
1451             case DAVQL_CMD_CALL: {
1452                 //printf("call %x\n", cmd.data.func);
1453                 break;
1454             }
1455         }
1456     }
1457     
1458     if(stpos == 1) {
1459         *result = stack[0];
1460     } else {
1461         ret = -1;
1462     }
1463     free(stack);
1464     
1465     return ret;
1466 }