1 /*
2 'search' for cadaver
3 Copyright (C) 2004-2006, Joe Orton <joe@manyfish.co.uk>
4 Copyright (C) 2002, GRASE Lab, UCSC <grase@cse.ucsc.edu>,
5 except where otherwise indicated.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "config.h"
23
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31
32 #include <time.h>
33
34 #include <ne_basic.h>
35 #include <ne_request.h>
36 #include <ne_props.h>
37 #include <ne_uri.h>
38 #include <ne_alloc.h>
39 #include <ne_dates.h>
40
41 #include "i18n.h"
42 #include "commands.h"
43 #include "cadaver.h"
44 #include "options.h"
45 #include "utils.h"
46
47 /* From ne_props.c
48 * Need to move util.c or something? */
49 #define NSPACE(x) ((x) ? (x) : "")
50
51 /*
52 * The Macro definations that 'wordtype' might be.
53 */
54 #define QUOT 101 /*''' */
55 #define COMMA 102 /*',' */
56 #define LPAR 103 /*'(' */
57 #define RPAR 104 /*')' */
58 #define EQ 105 /*'=' */
59 #define LE 106 /*'<=' */
60 #define LT 107 /*'<' */
61 #define GE 108 /*'>=' */
62 #define GT 109 /*'>' */
63 #define NEQ 110 /*'<>' */
64 #define IDEN 111 /*Integer */
65 #define INTEGER 112 /*identifier */
66 #define UNKNOWN 113 /*unknown charactor */
67 #define ENDBUF 114 /*End of the buffer */
68
69 #define WORDLEN 256 /*Max length of a identifier(token) in the search command */
70
71 #undef EOL
72 #define EOL "\n"
73
74 /* Dead prop */
75 typedef struct dead_prop
76 {
77 char *name;
78 char *nspace;
79 char *value;
80 struct dead_prop *next;
81 }
82 dead_prop;
83
84 typedef struct search_res
85 {
86 char *href;
87 /* live props */
88 char *creationdate;
89 char *displayname;
90 char *getcontentlanguage;
91 char *getcontentlength;
92 char *getcontenttype;
93 char *getetag;
94 char *getlastmodified;
95 char *lockdiscovery;
96 char *resourcetype;
97 char *source;
98 char *supportedlock;
99 char *collection;
100
101 dead_prop *root;
102 dead_prop *curr;
103 int dead_prop_num;
104
105 struct search_res *next;
106 }
107 search_res;
108
109 /* Search XML parser context */
110 typedef struct
111 {
112 search_res *root;
113 search_res *curr;
114 int result_num;
115 int start_prop;
116 int err_code;
117
118 ne_buffer *cdata;
119 }
120 search_ctx;
121
122 enum
123 {
124 ELEM_multistatus = 1,
125 ELEM_response,
126 ELEM_href,
127 ELEM_prop,
128 ELEM_propstat,
129 ELEM_status,
130 ELEM_responsedescription,
131
132 /* props from RFC 2518 , 23 Appendices 23.1 */
133 ELEM_creationdate,
134 ELEM_displayname,
135 ELEM_getcontentlanguage,
136 ELEM_getcontentlength,
137 ELEM_getcontenttype,
138 ELEM_getetag,
139 ELEM_getlastmodified,
140 ELEM_lockdiscovery,
141 ELEM_resourcetype,
142 ELEM_source,
143 ELEM_supportedlock,
144 ELEM_collection,
145
146 ELEM_ignore
147 };
148
149 static const struct ne_xml_idmap search_elements[] = {
150 {"DAV:", "multistatus", ELEM_multistatus},
151 {"DAV:", "response", ELEM_response},
152 {"DAV:", "responsedescription", ELEM_responsedescription},
153 {"DAV:", "href", ELEM_href},
154 {"DAV:", "propstat", ELEM_propstat},
155 {"DAV:", "prop", ELEM_prop},
156 {"DAV:", "status", ELEM_status},
157
158 /* Live props */
159 {"DAV:", "creationdate", ELEM_creationdate},
160 {"DAV:", "displayname", ELEM_displayname},
161 {"DAV:", "getcontentlanguage", ELEM_getcontentlanguage},
162 {"DAV:", "getcontentlength", ELEM_getcontentlength},
163 {"DAV:", "getcontenttype", ELEM_getcontenttype},
164 {"DAV:", "getetag", ELEM_getetag},
165 {"DAV:", "getlastmodified", ELEM_getlastmodified},
166 {"DAV:", "lockdiscovery", ELEM_lockdiscovery},
167 {"DAV:", "resourcetype", ELEM_resourcetype},
168 {"DAV:", "source", ELEM_source},
169 {"DAV:", "supportedlock", ELEM_supportedlock},
170 {"DAV:", "collection", ELEM_collection},
171
172 /* Ignore it for now */
173 {"DAV:", "lockentry", ELEM_ignore},
174 {"DAV:", "lockscope", ELEM_ignore},
175 {"DAV:", "locktype", ELEM_ignore},
176 {"DAV:", "exclusive", ELEM_ignore},
177 {"DAV:", "shared", ELEM_ignore},
178 {"DAV:", "read", ELEM_ignore},
179 {"DAV:", "write", ELEM_ignore},
180 };
181
182 /*
183 * Basic search parser functions
184 * return NE_OK or error_no
185 * basic_search must be allcated before the function call.
186 */
187 static int search_select_gen(const ne_propname * props,
188 ne_buffer * basic_search);
189 static int search_from_gen(const char *href, const int depth,
190 ne_buffer * basic_search);
191 static int search_where_gen(const char *query, ne_buffer * basic_search);
192 static int search_orderby_gen(const ne_propname * asc,
193 const ne_propname * des,
194 ne_buffer * basic_search);
195
196 /*
197 * Static functions for where condition parser
198 */
199 static int read_aword(char **string_parsed, char *word_fetched);
200 static int first_word_equal(const char *string_parsed,
201 const char *word_to_compare);
202 static int match_fetch(char **string_parsed, const char *str_expected);
203 static int search_condition(char **string_parsed, ne_buffer * result_buf);
204 static int boolean_term(char **strparsed, ne_buffer * result_buf);
205 static int boolean_factor(char **string_parsed, ne_buffer * result_buf);
206 static int boolean_primary(char **string_parsed, ne_buffer * result_buf);
207 static int operator_translate(const char *operator, char *XML_operator);
208 static int predicate(char **string_parsed, ne_buffer * result_buf);
209 static int contains_predicate(char **string_parsed, ne_buffer * result_buf);
210 static int quoted_string(char **string_parsed, ne_buffer * result_buf);
211 static int comparison_value(char **string_parsed, ne_buffer * result_buf);
212 static int word_string(char **string_parsed, ne_buffer * result_buf);
213
214 /* Set xml parser error */
set_xml_error(search_ctx * sctx,const char * format,...)215 static void set_xml_error(search_ctx *sctx, const char *format, ...)
216 {
217 va_list ap;
218 char buf[512];
219
220 va_start(ap, format);
221 ne_vsnprintf(buf, sizeof buf, format, ap);
222 va_end(ap);
223
224 ne_set_error(session.sess, "%s", buf);
225 sctx->err_code = NE_ERROR;
226 }
227
start_element(void * userdata,int parent,const char * nspace,const char * name,const char ** atts)228 static int start_element(void *userdata, int parent,
229 const char *nspace,
230 const char *name,
231 const char **atts)
232 {
233 search_ctx *sctx = (search_ctx *) userdata;
234 int state = ne_xml_mapid(search_elements,
235 NE_XML_MAPLEN(search_elements), nspace, name);
236
237 /* Error occured, ignore remain part */
238 if (sctx->err_code != NE_OK)
239 return sctx->err_code;
240
241 ne_buffer_clear(sctx->cdata);
242
243 switch (state) {
244 case ELEM_ignore:
245 break;
246
247 case ELEM_response: /* Start of new response */
248 sctx->curr = ne_calloc(sizeof(search_res));
249 sctx->result_num++;
250 break;
251
252 case ELEM_href: /* href */
253 break;
254
255 case ELEM_propstat: /* expecting props */
256 break;
257
258 case ELEM_prop: /* Start of prop */
259 if (sctx->curr == NULL) {
260 set_xml_error(sctx, "XML : <%s> is in the wrong place",
261 name);
262 break;
263 }
264 sctx->start_prop = 1;
265 break;
266 default:
267 if (get_bool_option(opt_searchall) && /* check searchall option */
268 sctx->curr && sctx->start_prop == 1) { /* It's prop */
269 search_res *res = sctx->curr;
270 res->dead_prop_num++;
271 res->curr = (dead_prop *) ne_calloc(sizeof(dead_prop));
272 res->curr->name = ne_strdup(name);
273 res->curr->nspace = ne_strdup(nspace);
274 }
275 break;
276 }
277
278 return state;
279 }
280
281 #define SEARCH_CP_ELEM(sctx, curr, name, desc, src) \
282 do { \
283 if ((curr) == NULL) \
284 set_xml_error((sctx), "XML : </%s> is in the wrong place", (name));\
285 else if (src)\
286 (desc) = ne_strdup(src);\
287 } while (0)
288
289
cdata_search(void * userdata,int state,const char * buf,size_t len)290 static int cdata_search(void *userdata, int state, const char *buf, size_t len)
291 {
292 search_ctx *sctx = (search_ctx *) userdata;
293 ne_buffer_append(sctx->cdata, buf, len);
294 return 0;
295 }
296
297
298 static int
end_element(void * userdata,int state,const char * nspace,const char * name)299 end_element(void *userdata, int state, const char *nspace, const char *name)
300 {
301 search_ctx *sctx = (search_ctx *) userdata;
302 const char *cdata = sctx->cdata->data;
303
304 /* Error occured, ignore remain part */
305 if (sctx->err_code != NE_OK)
306 return sctx->err_code;
307
308 switch (state) {
309 case ELEM_ignore:
310 break;
311
312 case ELEM_response: /* End of new response */
313 /* Nothing to add */
314 if (sctx->curr == NULL) {
315 set_xml_error(sctx, "XML : </%s> is in the wrong place",
316 name);
317 break;
318 }
319
320 /* No HREF */
321 if (sctx->curr->href == NULL) {
322 set_xml_error(sctx, "XML : No href info in the <%s>...</%s>",
323 name, name);
324 break;
325 }
326 /* make link */
327 sctx->curr->next = sctx->root;
328 sctx->root = sctx->curr;
329 sctx->curr = NULL;
330 break;
331
332 case ELEM_href: /* href */
333 SEARCH_CP_ELEM(sctx, sctx->curr, name, sctx->curr->href, cdata);
334 break;
335
336 /* live props */
337 case ELEM_creationdate:
338 SEARCH_CP_ELEM(sctx, sctx->curr, name,
339 sctx->curr->creationdate, cdata);
340 break;
341
342 case ELEM_displayname:
343 SEARCH_CP_ELEM(sctx, sctx->curr, name,
344 sctx->curr->displayname, cdata);
345 break;
346
347 case ELEM_getcontentlanguage:
348 SEARCH_CP_ELEM(sctx, sctx->curr, name,
349 sctx->curr->getcontentlanguage, cdata);
350 break;
351
352 case ELEM_getcontentlength:
353 SEARCH_CP_ELEM(sctx, sctx->curr, name,
354 sctx->curr->getcontentlength, cdata);
355 break;
356
357 case ELEM_getcontenttype:
358 SEARCH_CP_ELEM(sctx, sctx->curr, name,
359 sctx->curr->getcontenttype, cdata);
360 break;
361
362 case ELEM_getetag:
363 SEARCH_CP_ELEM(sctx, sctx->curr, name,
364 sctx->curr->getetag, cdata);
365 break;
366
367 case ELEM_getlastmodified:
368 SEARCH_CP_ELEM(sctx, sctx->curr, name,
369 sctx->curr->getlastmodified, cdata);
370 break;
371
372 case ELEM_lockdiscovery:
373 SEARCH_CP_ELEM(sctx, sctx->curr, name,
374 sctx->curr->lockdiscovery, cdata);
375 break;
376
377 case ELEM_resourcetype:
378 SEARCH_CP_ELEM(sctx, sctx->curr, name,
379 sctx->curr->resourcetype, cdata);
380 break;
381
382 case ELEM_source:
383 SEARCH_CP_ELEM(sctx, sctx->curr, name,
384 sctx->curr->source, cdata);
385 break;
386
387 case ELEM_supportedlock:
388 SEARCH_CP_ELEM(sctx, sctx->curr, name,
389 sctx->curr->supportedlock, cdata);
390 break;
391
392 case ELEM_collection:
393 SEARCH_CP_ELEM(sctx, sctx->curr, name,
394 sctx->curr->collection, cdata);
395 break;
396
397 case ELEM_propstat: /* expecting props */
398 break;
399
400 case ELEM_prop: /* Start of prop */
401 if (sctx->curr == NULL)
402 set_xml_error(sctx, "XML : </%s> is in the wrong place",
403 name);
404 else /* stop to props */
405 sctx->start_prop = 0;
406 break;
407
408 default:
409 if (get_bool_option(opt_searchall) && /* check searchall option */
410 sctx->curr && sctx->start_prop == 1) { /* It's dead prop */
411 search_res *res = sctx->curr;
412 res->curr->value = ne_strdup(cdata);
413 res->curr->next = res->root;
414 res->root = res->curr;
415 res->curr = NULL;
416 }
417 break;
418 }
419
420 return NE_OK;
421 }
422
423 #define RESULT_PER_PAGE 15
424
425 /* displays search results */
display_results(search_ctx * sctx)426 static int display_results(search_ctx * sctx)
427 {
428 search_res *res;
429 dead_prop *dprop;
430 int i;
431
432 if (sctx->err_code) {
433 return sctx->err_code;
434 }
435
436 for (i=1, res = sctx->root; res; res = res->next, i++) {
437 long modtime = res->getlastmodified ?
438 ne_httpdate_parse(res->getlastmodified) : 0;
439 int size = res->getcontentlength ? atol(res->getcontentlength) : 0;
440 char exec_char = ' ';
441
442 if (i%RESULT_PER_PAGE ==1) {
443 printf("Found %d results (%d-%d)\n\n",
444 sctx->result_num, i,
445 sctx->result_num<i+9?sctx->result_num:i+9);
446 }
447
448 printf("[%d] %-40s%c %10d %s <%.10s>\n", i, res->href, exec_char,
449 size, format_time(modtime),
450 res->getcontenttype?res->getcontenttype:"");
451
452 for (dprop = res->root;
453 get_bool_option(opt_searchall) && dprop; dprop = dprop->next)
454 printf("\t- %s:%s = %s\n", /* better way to show ? */
455 dprop->nspace, dprop->name, dprop->value);
456
457 if (i%RESULT_PER_PAGE == 0) {
458 char buf[256];
459 printf("-- Enter to More, 'q' to QUIT --");
460 fgets(buf, 255, stdin);
461 if (*buf=='q') break;
462 }
463
464 }
465
466 return sctx->err_code;
467 }
468
search_ctx_destroy(search_ctx * sctx)469 static void search_ctx_destroy(search_ctx * sctx)
470 {
471 search_res *res, *res_free;
472 dead_prop *dprop, *dprop_free;
473
474 ne_buffer_destroy(sctx->cdata);
475
476 for (res = sctx->root; res;) {
477 if (res->href) ne_free(res->href);
478 /* live props */
479 if (res->creationdate) ne_free(res->creationdate);
480 if (res->displayname) ne_free(res->displayname);
481 if (res->getcontentlanguage) ne_free(res->getcontentlanguage);
482 if (res->getcontentlength) ne_free(res->getcontentlength);
483 if (res->getcontenttype) ne_free(res->getcontenttype);
484 if (res->getetag) ne_free(res->getetag);
485 if (res->getlastmodified) ne_free(res->getlastmodified);
486 if (res->lockdiscovery) ne_free(res->lockdiscovery);
487 if (res->resourcetype) ne_free(res->resourcetype);
488 if (res->source) ne_free(res->source);
489 if (res->supportedlock) ne_free(res->supportedlock);
490 if (res->collection) ne_free(res->collection);
491
492 for (dprop = res->root; dprop;) {
493 if (dprop->nspace) ne_free(dprop->nspace);
494 if (dprop->name) ne_free(dprop->name);
495 if (dprop->value) ne_free(dprop->value);
496
497 dprop_free = dprop;
498 dprop = dprop->next;
499 ne_free(dprop_free);
500 }
501 res_free = res;
502 res = res->next;
503 ne_free(res_free);
504 }
505 }
506
507 /* create propname struct from searchorder setting */
order_props_create(const char * str)508 static ne_propname *order_props_create(const char *str)
509 {
510 int n;
511 char *buf;
512 char *tok;
513 char *delm = " \t\n\r";
514 int num_props = 0;
515 ne_propname *props;
516
517 /* No props */
518 if (str == NULL)
519 return NULL;
520
521 buf = ne_strdup(str);
522 if (strtok(buf, delm)) {
523 num_props = 1;
524 while (strtok(NULL, delm))
525 num_props++;
526 }
527
528 /* No props */
529 if (num_props == 0)
530 return NULL;
531
532 /* One more for last NULL */
533 props = ne_calloc(sizeof(ne_propname) * (num_props + 1));
534
535 /* Set first token */
536 ne_free(buf);
537 buf = ne_strdup(str);
538 tok = strtok(buf, delm);
539
540 /* Other idea? */
541 props[0].nspace = ne_strdup("DAV:");
542 props[0].name = ne_strdup(tok);
543
544 for (n = 1; (tok = strtok(NULL, delm)); n++) {
545 props[n].nspace = ne_strdup("DAV:");
546 props[n].name = ne_strdup(tok);
547 }
548
549 ne_free(buf);
550 return props;
551 }
552
order_props_destroy(ne_propname * props)553 static void order_props_destroy(ne_propname * props)
554 {
555 int n;
556
557 if (props == NULL)
558 return;
559
560 for (n = 0; props[n].name != NULL; n++) {
561 free((char *)props[n].name);
562 free((char *)props[n].nspace);
563 }
564
565 free(props);
566 props = NULL;
567 }
568
569 /* Run search and get the data to sctx */
run_search(ne_session * sess,const char * uri,int depth,ne_buffer * query,search_ctx * sctx)570 static int run_search(ne_session * sess, const char *uri,
571 int depth, ne_buffer * query, search_ctx * sctx)
572 {
573 int ret;
574 ne_request *req;
575 ne_buffer *basic_search = ne_buffer_create();
576 ne_xml_parser *search_parser;
577 const char *searchorder = (const char *) get_option(opt_searchorder);
578 const char *searchdorder = (const char *) get_option(opt_searchdorder);
579 ne_propname *asc = order_props_create(searchorder);
580 ne_propname *des = order_props_create(searchdorder);
581
582 /* create/prep the request */
583 if ((req = ne_request_create(sess, "SEARCH", uri)) == NULL)
584 return NE_ERROR;
585
586 /* Create the request body */
587 ne_buffer_zappend(basic_search,
588 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" EOL
589 "<D:searchrequest xmlns:D=\"DAV:\"><D:basicsearch>"
590 EOL);
591
592 if (search_select_gen(NULL, basic_search) != NE_OK)
593 return NE_ERROR;
594
595 if (search_from_gen(uri, depth, basic_search) != NE_OK)
596 return NE_ERROR;
597
598 if (search_where_gen(query->data, basic_search) != NE_OK)
599 return NE_ERROR;
600
601 if (search_orderby_gen(asc, des, basic_search) != NE_OK)
602 return NE_ERROR;
603
604 ne_buffer_zappend(basic_search, "</D:basicsearch></D:searchrequest>" EOL);
605
606 ne_set_request_body_buffer(req, basic_search->data,
607 ne_buffer_size(basic_search));
608
609 /* Plug our XML parser */
610 search_parser = ne_xml_create();
611 ne_xml_push_handler(search_parser, start_element,
612 cdata_search, end_element,
613 sctx);
614
615 ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
616 ne_add_depth_header(req, depth);
617
618 ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v,
619 search_parser);
620
621 /* run the request, see what comes back. */
622 if ((ret = ne_request_dispatch(req)) != NE_OK)
623 return ret;
624
625 /* Check Errors from XML parser */
626 if (sctx->err_code != NE_OK)
627 return NE_ERROR;
628
629 /* Get response code */
630 if (ne_get_status(req)->code != 207)
631 return NE_ERROR;
632
633 /* destroy request, parse, and etc */
634 order_props_destroy(asc);
635 order_props_destroy(des);
636
637 ne_buffer_destroy(basic_search);
638 ne_request_destroy(req);
639 ne_xml_destroy(search_parser);
640
641 return NE_OK;
642 }
643
644 /* Main execute routine for search */
execute_search(int count,const char ** args)645 void execute_search(int count, const char **args)
646 {
647 int ret;
648 const char **pnt;
649 ne_buffer *query = ne_buffer_create();
650
651 search_ctx *sctx = ne_calloc(sizeof(search_ctx));
652 sctx->cdata = ne_buffer_create();
653
654 /* default is success */
655 sctx->err_code = NE_OK;
656
657 for (pnt = args; *pnt != NULL; pnt++) {
658 /* Need quota */
659 if (strchr(*pnt, ' ') || strchr(*pnt, '\t'))
660 ne_buffer_concat(query, "'", *pnt, "' ", NULL);
661 else
662 ne_buffer_concat(query, *pnt, " ", NULL);
663 }
664
665 printf(_("Using query: "));
666 printf("%s, ", query->data);
667
668 /* Run search and get data to sctx */
669 ret = run_search(session.sess, session.uri.path, searchdepth, query, sctx);
670 if (ret == NE_OK) {
671 display_results(sctx);
672 }
673
674 out_result(ret);
675
676 search_ctx_destroy(sctx);
677 ne_buffer_destroy(query);
678 }
679
680 /* Generate select part of the query. allprop if the props arg is NULL. */
search_select_gen(const ne_propname * props,ne_buffer * basic_search)681 static int search_select_gen(const ne_propname * props,
682 ne_buffer * basic_search)
683 {
684 int n;
685
686 if (!basic_search) {
687 ne_set_error(session.sess, "select_gen: no buffer");
688 return NE_ERROR;
689 }
690
691 if (props == NULL) {
692 ne_buffer_zappend(basic_search,
693 "<D:select><D:allprop/></D:select>" EOL);
694 return NE_OK;
695 }
696
697 ne_buffer_zappend(basic_search, "<D:select><D:prop>" EOL);
698
699 for (n = 0; props[n].name != NULL; n++) {
700 ne_buffer_concat(basic_search, "<", props[n].name, " xmlns=\"",
701 NSPACE(props[n].nspace), "\"/>" EOL, NULL);
702 }
703
704 ne_buffer_zappend(basic_search, "</D:prop></D:select>" EOL);
705
706 return NE_OK;
707 }
708
search_from_gen(const char * href,const int depth,ne_buffer * basic_search)709 static int search_from_gen(const char *href, const int depth,
710 ne_buffer * basic_search)
711 {
712 const char *depth_str;
713
714 if (!basic_search || !href) {
715 ne_set_error(session.sess, "from_gen: no buffer or no href");
716 return NE_ERROR;
717 }
718
719 switch (depth) {
720 case NE_DEPTH_ONE:
721 depth_str = "1";
722 break;
723 case NE_DEPTH_ZERO:
724 depth_str = "0";
725 break;
726 default:
727 depth_str = "infinity";
728 break;
729 }
730
731 ne_buffer_concat(basic_search, "<D:from><D:scope><D:href>",
732 href, "</D:href><D:depth>", depth_str,
733 "</D:depth></D:scope></D:from>" EOL, NULL);
734
735 return NE_OK;
736 }
737
738 /* Parse a searchquery. It will call the search_condition() function and
739 * check the ending status. If the ending status is not ENDBUF, then there will
740 * be a syntax error.
741 * The parsing result will be saved in 'result_buf'.
742 *
743 * Returns:
744 * NE_OK: success
745 * NE_ERROR: syntax error
746 * */
search_where_gen(const char * condition_str,ne_buffer * basic_search)747 static int search_where_gen(const char *condition_str,
748 ne_buffer * basic_search)
749 {
750 char identifier[WORDLEN + 1] = "";
751 char *string_parsed = ne_strdup(condition_str);
752 char *ptr_backup = string_parsed;
753 /* The buffer storing the parsing result of search condition */
754 ne_buffer *result_buf;
755
756 if (!basic_search || !condition_str) {
757 ne_set_error(session.sess, "where_gen: no buffer or no query");
758 return NE_ERROR;
759 }
760
761 result_buf = ne_buffer_create();
762
763 /* Fill boby from <D:where> to </D:where> */
764 ne_buffer_zappend(basic_search, "<D:where>" EOL);
765
766 if (search_condition(&string_parsed, result_buf) == NE_ERROR) {
767 ne_free(ptr_backup);
768 ne_buffer_destroy(result_buf);
769 return NE_ERROR; /*Parsing error */
770 }
771
772 /*The ending of a condition must be an ENDBUF */
773 if (read_aword(&string_parsed, identifier) != ENDBUF) {
774 ne_set_error(session.sess, "Syntax error in the search condition.");
775 ne_free(ptr_backup);
776 ne_buffer_destroy(result_buf);
777 return NE_ERROR;
778 }
779
780 /*Append the parsing result of the search condition */
781 ne_buffer_zappend(basic_search, result_buf->data);
782
783 ne_buffer_zappend(basic_search, "</D:where>" EOL);
784
785 ne_free(ptr_backup);
786 ne_buffer_destroy(result_buf);
787
788 return NE_OK;
789 } /*End of ne_search_where_gen */
790
search_orderby_gen(const ne_propname * asc,const ne_propname * des,ne_buffer * basic_search)791 static int search_orderby_gen(const ne_propname * asc,
792 const ne_propname * des,
793 ne_buffer * basic_search)
794 {
795 int n;
796
797 if (!basic_search) {
798 ne_set_error(session.sess, "orderby_gen: no buffer or no query");
799 return NE_ERROR;
800 }
801
802 /* No order information */
803 if (asc == NULL && des == NULL)
804 return NE_OK;
805
806 ne_buffer_zappend(basic_search, "<D:orderby>" EOL);
807
808 for (n = 0; asc && asc[n].name != NULL; n++) {
809 ne_buffer_zappend(basic_search, "<D:order><D:prop>" EOL);
810 ne_buffer_concat(basic_search, "<", asc[n].name, " xmlns=\"",
811 NSPACE(asc[n].nspace), "\"/>" EOL, NULL);
812 ne_buffer_zappend(basic_search,
813 "</D:prop><D:ascending/></D:order>" EOL);
814 }
815
816 for (n = 0; des && des[n].name != NULL; n++) {
817 ne_buffer_zappend(basic_search, "<D:order><D:prop>" EOL);
818 ne_buffer_concat(basic_search, "<", des[n].name, " xmlns=\"",
819 NSPACE(des[n].nspace), "\"/>" EOL, NULL);
820 ne_buffer_zappend(basic_search,
821 "</D:prop><D:descending/></D:order>" EOL);
822 }
823
824 ne_buffer_zappend(basic_search, "</D:orderby>" EOL);
825
826 return NE_OK;
827 }
828
829
830 /* Search command parser
831 *
832 * This is the Search command parser implementation.
833 * The goal of this code is to translate
834 * dasl search command into XML format.
835 *
836 * The user interface of search command is:
837 * search resource_URI display_fields condition orderby
838 *
839 * This code will parse the three parts of the search command,
840 * dispolay_fields,
841 *
842 * condition and orderby, and translate them into XML, which is a part of a
843 * dasl request.
844 *
845 * The BNF of display_fields, condition and orderby is as below:
846 *
847 *<display_fields> ::= identifier {,identifier}
848 *<orderby clause> ::= identifier ["asc"|"desc"] {,identifier ["asc"|"desc"]}
849 *
850 *<search condition> ::= <boolean term> | <search condition> " or " <boolean term>
851 *<boolean term> ::= <boolean factor> | <boolean term> " and " <boolean factor>
852 *<boolean factor> ::= [not] <boolean primary>
853 *<boolean primary> ::= <predicate>|"("<search condition>")"
854 *<predicate> ::= <comparison predicate>|<like predicate>|<isdefined predicate>
855 *<comparison predicate> ::= <column_name> <comparaison_op>
856 * ( <number> | <quoted_string> | <wordstring>)
857 *<like predicate> ::= <column_name> like <quoted_string> -----Formally,
858 * [not] like match_string
859 *<contains predicate> ::= contains (<quoted_string> | <wordstring>)
860 *<column_name>
861 * ::= identifier
862 *<comparaison_op>
863 * ::= "=" | "<" | ">" | "<>" | "!=" | "<=" | ">="
864 *<quoted_string> ::= "'" {<any_character>} "'"
865 *<wordstring> ::= <any char except for blank, tab and ')'>{<any_character except for blank, tab and ')'>}
866 *<match_string>
867 * ::= "'" { <any_character> | "_" | "%" } "'"
868 *<identifier> ::= <letter> { <letter>|<digital>|<underline>}
869 *<number> ::= [+|-]<digital>{<digital>}
870 */
871
872 /* Read a word from the buffer. The word is fetched into 'word_fetched'.
873 * Every time a word is read, '*string_parsed' is changed to the pointer
874 * pointing to the char just after 'word_fetched'.
875 *
876 * Parameters:
877 * string_parsed: -- *string_parsed points to the buffer. '*string_parsed'
878 * will be changed every time a word is fetched.
879 * word_fetched: -- The word that is fetched.
880 * Returns:
881 * ENDBUF , if the end of buffer, '\0', has been reached.
882 * UNKNOWN, if the word is invalid.
883 * According word type, if the word is valid.
884 * */
read_aword(char ** string_parsed,char * word_fetched)885 static int read_aword(char **string_parsed, char *word_fetched)
886 {
887 int i = 0;
888 int ifetch = 0;
889 char *stringptr = *string_parsed;
890
891 word_fetched[0] = '\0';
892
893 /*Find the left bound of a word */
894 while (isspace(stringptr[i]))
895 i++;
896
897 if (stringptr[i] == '\0')
898 return ENDBUF;
899
900 /*
901 * Check whether the next word is a identifier. An identifier may
902 * be a keyword or a column name.
903 */
904 if (isalpha(stringptr[i])) {
905 do {
906 word_fetched[ifetch++] = stringptr[i++];
907 } while (isalnum(stringptr[i]) || (stringptr[i] == '_'));
908
909 *string_parsed = *string_parsed + i;
910 word_fetched[ifetch] = '\0';
911 return IDEN; /*Identifier */
912 }
913
914 /* Check whether the next word is an integer */
915 /* A problem here: + , or - !!!!! PK */
916 if (stringptr[i] == '+' || stringptr[i] == '-' || isdigit(stringptr[i])) {
917 do {
918 word_fetched[ifetch++] = stringptr[i++];
919 } while (isdigit(stringptr[i]));
920
921 *string_parsed = *string_parsed + i;
922 word_fetched[ifetch] = '\0';
923 return INTEGER; /*Identifier */
924 }
925
926 /* Check whether the next word is "<=", ">=", or "<>" */
927 if ((stringptr[i] == '>') && (stringptr[i + 1] == '=')) {
928 strcpy(word_fetched, ">=");
929 *string_parsed = *string_parsed + i + 2;
930 return GE; /*Indicates >= */
931 }
932
933 if ((stringptr[i] == '<') && (stringptr[i + 1] == '=')) {
934 strcpy(word_fetched, "<=");
935 *string_parsed = *string_parsed + i + 2;
936 return LE; /*Indicates <= */
937 }
938
939 if ((stringptr[i] == '<') && (stringptr[i + 1] == '>')) {
940 strcpy(word_fetched, "<>");
941 *string_parsed = *string_parsed + i + 2;
942 return NEQ; /*Indicates <> */
943 }
944
945 /*Check whether the next word is "'", ",", "(", ")", ">",
946 * "<", "=", */
947 switch (stringptr[i]) {
948 case '\'':
949 strcpy(word_fetched, "'");
950 *string_parsed = *string_parsed + i + 1;
951 return QUOT;
952 case ',':
953 strcpy(word_fetched, ",");
954 *string_parsed = *string_parsed + i + 1;
955 return COMMA;
956 case '(':
957 strcpy(word_fetched, "(");
958 *string_parsed = *string_parsed + i + 1;
959 return LPAR;
960 case ')':
961 strcpy(word_fetched, ")");
962 *string_parsed = *string_parsed + i + 1;
963 return RPAR;
964 case '>':
965 strcpy(word_fetched, ">");
966 *string_parsed = *string_parsed + i + 1;
967 return GT;
968 case '<':
969 strcpy(word_fetched, "<");
970 *string_parsed = *string_parsed + i + 1;
971 return LT;
972 case '=':
973 strcpy(word_fetched, "=");
974 *string_parsed = *string_parsed + i + 1;
975 return EQ;
976 default:
977 word_fetched[0] = stringptr[i];
978 word_fetched[1] = '\0';
979 *string_parsed = *string_parsed + i + 1;
980 return UNKNOWN;
981 } /*End of switch */
982 } /*End of read_aword */
983
984 /*Read the first word from the string buffer, string_parsed. If the
985 * first is equal to the 'word_to_compare', return 1, else return 0.
986 * The first word will be saved to 'the_first_word', but the pointer
987 * to the buffer, string_parsed, will not be changed.
988 * Returns:
989 * 1: The first word is equal to the word expected.
990 * 0: otherwise
991 * */
first_word_equal(const char * string_parsed,const char * word_to_compare)992 static int first_word_equal(const char *string_parsed,
993 const char *word_to_compare)
994 {
995 char *string_buffer = NULL;
996 char *ptr_backup = NULL;
997 char first_word[WORDLEN + 1] = "";
998
999 string_buffer = ne_strdup(string_parsed);
1000 ptr_backup = string_buffer;
1001
1002 read_aword(&string_buffer, first_word);
1003
1004 ne_free(ptr_backup);
1005
1006 if (strcasecmp(first_word, word_to_compare) == 0)
1007 return 1; /*equal */
1008 else
1009 return 0; /*Not equal */
1010 } /*End of first_word_compare */
1011
1012 /*Read the first word from the string buffer, string_parsed. If the
1013 * first is an interger, return 1, else return 0.
1014 * The first word will be saved to 'the_first_word', but the pointer
1015 * to the buffer, string_parsed, will not be changed.
1016 * Returns:
1017 * 1: The first word is equal to the word expected.
1018 * 0: otherwise
1019 * */
first_word_integer(const char * string_parsed)1020 static int first_word_integer(const char *string_parsed)
1021 {
1022 char *string_buffer = NULL;
1023 char *ptr_backup = NULL;
1024 char first_word[WORDLEN + 1] = "";
1025 int ret;
1026
1027 string_buffer = ne_strdup(string_parsed);
1028 ptr_backup = string_buffer;
1029
1030 if (read_aword(&string_buffer, first_word) == INTEGER)
1031 ret = 1;
1032 else
1033 ret = 0;
1034
1035 ne_free(ptr_backup);
1036
1037 return ret;
1038 } /*End of first_word_compare */
1039
1040 /* The function reads the first word in the string buffer, *string_parsed, and
1041 * changes *string_parsed.
1042 * Returns:
1043 * NE_ERROR: If the word read is equal to 'str_expected'.
1044 * NE_OK: Otherwise
1045 * */
match_fetch(char ** string_parsed,const char * str_expected)1046 static int match_fetch(char **string_parsed, const char *str_expected)
1047 {
1048 char first_word[WORDLEN + 1] = "";
1049
1050 read_aword(string_parsed, first_word);
1051
1052 if (strcmp(first_word, str_expected) == 0)
1053 return NE_OK;
1054 else
1055 return NE_ERROR;
1056
1057 } /*End of match_fetch */
1058
1059 /*
1060 * Parse the search condition
1061 * <search condition> ::= <boolean term> | <search condition> or <boolean term>
1062 *
1063 * The parsing result is saved into result_buf.
1064 * '*string_parsed' is changed to the pointer pointing to the position after
1065 * <search condition>.
1066 * Returns:
1067 * NE_OK: success
1068 * NE_ERROR: syntax error
1069 * */
search_condition(char ** string_parsed,ne_buffer * result_buf)1070 static int search_condition(char **string_parsed, ne_buffer * result_buf)
1071 {
1072 char identifier[WORDLEN + 1] = "";
1073 /* Indicates whether there is any 'or' in the search condition */
1074 int added_or = 0;
1075 ne_buffer *term_result;
1076
1077 ne_buffer_clear(result_buf);
1078
1079 term_result = ne_buffer_create();
1080
1081 if (boolean_term(string_parsed, term_result) == NE_ERROR) {
1082 ne_buffer_destroy(term_result); /*Free the buffer */
1083 return NE_ERROR; /*parsing error */
1084 }
1085
1086 if (first_word_equal(*string_parsed, "or") == 1) {
1087 added_or = 1;
1088 ne_buffer_concat(result_buf, "<D:or>" EOL, term_result->data, NULL);
1089 }
1090 else
1091 ne_buffer_zappend(result_buf, term_result->data);
1092
1093 /*For or <boolean term> or <boolean term> ... */
1094 while (first_word_equal(*string_parsed, "or") == 1) {
1095 read_aword(string_parsed, identifier); /*Read 'or' */
1096
1097 if (boolean_term(string_parsed, term_result) == NE_ERROR) {
1098 ne_buffer_destroy(term_result); /*Free the buffer */
1099 return NE_ERROR; /*Parsing error */
1100 }
1101 ne_buffer_zappend(result_buf, term_result->data);
1102 } /*End of while */
1103
1104 if (added_or == 1)
1105 ne_buffer_zappend(result_buf, "</D:or>" EOL);
1106
1107 ne_buffer_destroy(term_result); /*Free the buffer */
1108
1109 return NE_OK; /*success */
1110 } /*End of search_condition */
1111
1112 /*
1113 * Parse a boolean term.
1114 * <boolean term> ::= <boolean factor> | <boolean term> and <boolean factor>
1115 *
1116 * The parsing result is saved into result_buf.
1117 * '*string_parsed' is changed to the pointer pointing to the position after
1118 * <boolean term>.
1119 *
1120 * Returns:
1121 * NE_OK: success
1122 * NE_ERROR: syntax error
1123 */
boolean_term(char ** string_parsed,ne_buffer * result_buf)1124 static int boolean_term(char **string_parsed, ne_buffer * result_buf)
1125 {
1126 char identifier[WORDLEN + 1] = "";
1127 /*Indicates whether there is any 'and' in the boolean term. */
1128 int added_and = 0;
1129 ne_buffer *factor_result;
1130
1131 ne_buffer_clear(result_buf);
1132 factor_result = ne_buffer_create();
1133
1134 if (boolean_factor(string_parsed, factor_result) == NE_ERROR) {
1135 ne_buffer_destroy(factor_result);
1136 return NE_ERROR; /*parsing error */
1137 }
1138
1139 if (first_word_equal(*string_parsed, "and") == 1) {
1140 added_and = 1;
1141 ne_buffer_concat(result_buf, "<D:and>" EOL, factor_result->data,
1142 NULL);
1143 }
1144 else
1145 ne_buffer_zappend(result_buf, factor_result->data);
1146
1147 while (first_word_equal(*string_parsed, "and") == 1) {
1148 read_aword(string_parsed, identifier); /*Read 'and' */
1149
1150 if (boolean_factor(string_parsed, factor_result) == NE_ERROR) {
1151 ne_buffer_destroy(factor_result);
1152 return NE_ERROR; /*Parsing error */
1153 }
1154 ne_buffer_zappend(result_buf, factor_result->data);
1155 } /*End of while */
1156
1157 if (added_and == 1)
1158 ne_buffer_zappend(result_buf, "</D:and>" EOL);
1159
1160 ne_buffer_destroy(factor_result);
1161
1162 return NE_OK; /*success */
1163 } /*End of boolean_term */
1164
1165 /*
1166 * Parse a boolean factor.
1167 * <boolean factor> ::= [not] <boolean primary>
1168 *
1169 * The parsing result is saved into result_buf.
1170 * '*string_parsed' is changed to the pointer pointing to the position after
1171 * <boolean factor>.
1172 *
1173 * Returns:
1174 * NE_OK: success
1175 * NE_ERROR: syntax error
1176 */
boolean_factor(char ** string_parsed,ne_buffer * result_buf)1177 static int boolean_factor(char **string_parsed, ne_buffer * result_buf)
1178 {
1179 char identifier[WORDLEN + 1] = "";
1180 int added_not = 0; /*Indicates whether there is any 'not'
1181 in the search condition */
1182 ne_buffer *boolean_primary_result;
1183
1184 ne_buffer_clear(result_buf);
1185 boolean_primary_result = ne_buffer_create();
1186
1187 if (first_word_equal(*string_parsed, "not") == 1) {
1188 read_aword(string_parsed, identifier); /*Read "not" */
1189 ne_buffer_zappend(result_buf, "<D:not>" EOL);
1190 added_not = 1;
1191 }
1192
1193 if (boolean_primary(string_parsed, boolean_primary_result) == NE_ERROR) {
1194 ne_buffer_destroy(boolean_primary_result);
1195 return NE_ERROR; /*parsing error */
1196 }
1197
1198 ne_buffer_zappend(result_buf, boolean_primary_result->data);
1199
1200 if (added_not == 1)
1201 ne_buffer_zappend(result_buf, "</D:not>" EOL);
1202
1203 ne_buffer_destroy(boolean_primary_result);
1204
1205 return NE_OK; /*success */
1206 } /*End of boolean_factor */
1207
1208 /*
1209 * Parse a boolean primary.
1210 * <boolean primary> ::= <predicate> | "("<search condition>")"
1211 *
1212 * The parsing result is saved into result_buf.
1213 * '*string_parsed' is changed to the pointer pointing to the position after
1214 * <boolean primary>.
1215 *
1216 * Returns:
1217 * NE_OK: success
1218 * NE_ERROR: syntax error
1219 */
boolean_primary(char ** string_parsed,ne_buffer * result_buf)1220 static int boolean_primary(char **string_parsed, ne_buffer * result_buf)
1221 {
1222 char identifier[WORDLEN + 1] = "";
1223
1224 ne_buffer *sub_result;
1225
1226 ne_buffer_clear(result_buf);
1227 sub_result = ne_buffer_create();
1228
1229 if (first_word_equal(*string_parsed, "(") == 1) {
1230 /*It is the case of "("<search condition>")" */
1231
1232 read_aword(string_parsed, identifier); /*Read "(" */
1233
1234 if (search_condition(string_parsed, sub_result) == NE_ERROR) {
1235 ne_buffer_destroy(sub_result);
1236 return NE_ERROR; /*Parsing error */
1237 }
1238
1239 /*Read and match ")" */
1240 if (match_fetch(string_parsed, ")") == NE_ERROR) {
1241 ne_set_error(session.sess,
1242 "Syntax error: A ')' is expected in the search condition.");
1243 ne_buffer_destroy(sub_result);
1244 return NE_ERROR; /*parsing error */
1245 }
1246 ne_buffer_zappend(result_buf, sub_result->data);
1247 }
1248 else { /*It is the case of <predicate> */
1249 if (predicate(string_parsed, sub_result) == NE_ERROR) {
1250 ne_buffer_destroy(sub_result);
1251 return NE_ERROR; /*parsing error */
1252 }
1253 ne_buffer_zappend(result_buf, sub_result->data);
1254 }
1255
1256 ne_buffer_destroy(sub_result);
1257
1258 return NE_OK; /*success */
1259 } /*End of boolean_primary */
1260
1261 /*
1262 * Translate comparison operator in the search condition to XML form.
1263 * Returns:
1264 * 1: if the operator is valid.
1265 * 0: if the operator is not valid.
1266 */
operator_translate(const char * operator,char * XML_operator)1267 static int operator_translate(const char *operator, char *XML_operator)
1268 {
1269 int operator_valid = 1;
1270
1271 XML_operator[0] = '\0';
1272
1273 if (strcmp(operator, "=") == 0)
1274 strcpy(XML_operator, "eq");
1275 else if (strcmp(operator, ">=") == 0)
1276 strcpy(XML_operator, "gte");
1277 else if (strcmp(operator, "<=") == 0)
1278 strcpy(XML_operator, "lte");
1279 else if (strcmp(operator, ">") == 0)
1280 strcpy(XML_operator, "gt");
1281 else if (strcmp(operator, "<") == 0)
1282 strcpy(XML_operator, "lt");
1283 else if (strcmp(operator, "!=") == 0)
1284 strcpy(XML_operator, "not");
1285 else if (strcmp(operator, "<>") == 0)
1286 strcpy(XML_operator, "not");
1287 else if (strcmp(operator, "like") == 0)
1288 strcpy(XML_operator, "like");
1289 else
1290 operator_valid = 0; /*Invalid operator */
1291
1292 return operator_valid;
1293 } /*End of operator_translate */
1294
1295 /*
1296 * Parse a predicate.
1297 * <predicate> ::= <comparison predicate> | <like predicate> | <contains predicate>
1298 * <comparison predicate> ::= <column_name> <comparaison_op> ( <number> | <quoted_string> | <wordstring>)
1299 * <like predicate> ::= <column_name> like <match_string>
1300 * <contains predicate> ::= contains (<quoted_string> | <wordstring>)
1301 * <comparaison_op>
1302 * ::= "=" | "<" | ">" | "<>" | "!=" | "<=" | ">="
1303 *
1304 * The parsing result is saved into result_buf.
1305 * '*string_parsed' is changed to the pointer pointing to the position after
1306 * the predicate.
1307 *
1308 * Returns:
1309 * NE_OK: success
1310 * NE_ERROR: syntax error
1311 */
predicate(char ** string_parsed,ne_buffer * result_buf)1312 static int predicate(char **string_parsed, ne_buffer * result_buf)
1313 {
1314 char column_name[WORDLEN + 1] = "";
1315 char operator[WORDLEN + 1] = "";
1316 char XML_operator[WORDLEN + 1] = "";
1317 ne_buffer *comparing_value;
1318
1319 ne_buffer_clear(result_buf);
1320
1321 if (first_word_equal(*string_parsed, "contains") == 1) {
1322 /* It is the case of <contains predicate> */
1323 ne_buffer *contains_result;
1324 contains_result = ne_buffer_create();
1325 if (contains_predicate(string_parsed, contains_result)
1326 == NE_ERROR) {
1327 ne_buffer_destroy(contains_result);
1328 return NE_ERROR; /*Parsing error */
1329 }
1330 ne_buffer_zappend(result_buf, contains_result->data);
1331 return NE_OK;
1332 }
1333
1334 comparing_value = ne_buffer_create();
1335
1336 /*Read the column name */
1337 if (read_aword(string_parsed, column_name) != IDEN) {
1338 ne_set_error(session.sess,
1339 "A column name is expected in the search condition.");
1340 ne_buffer_destroy(comparing_value);
1341 return NE_ERROR; /*Parsing error */
1342 }
1343
1344 /*Read the 'operator' */
1345 read_aword(string_parsed, operator);
1346
1347 /*Translate the operator to XML form */
1348 if (operator_translate(operator, XML_operator) == 0) {
1349 ne_set_error(session.sess,
1350 "Syntax error: Invalid operator in the search condition.");
1351 ne_buffer_destroy(comparing_value);
1352 return NE_ERROR;
1353 }
1354
1355 if (strcasecmp(operator, "like") == 0) {
1356 /*It is the case of like predicate and then parse the match string */
1357 if (first_word_equal(*string_parsed, "'")) {
1358 /* For the case of a quoted string */
1359 if (quoted_string(string_parsed, comparing_value) == NE_ERROR) {
1360 ne_buffer_destroy(comparing_value);
1361 return NE_ERROR; /*Parsing error */
1362 }
1363 }
1364 else {
1365 /* For the case of word string. */
1366 if (word_string(string_parsed, comparing_value) == NE_ERROR) {
1367 ne_set_error(session.sess,
1368 "Syntax error: A quoted string or a word string is expected in the search condition.");
1369 ne_buffer_destroy(comparing_value);
1370 return NE_ERROR; /*Parsing error */
1371 }
1372 }
1373 }
1374 else {
1375 /* It is the case of comparison predicate and then
1376 * parse the comparison value */
1377 if (comparison_value(string_parsed, comparing_value) == NE_ERROR) {
1378 ne_buffer_destroy(comparing_value);
1379 return NE_ERROR; /*parsing error */
1380 }
1381 }
1382
1383 ne_buffer_concat(result_buf, "<D:",
1384 XML_operator,
1385 ">" EOL
1386 "<D:prop><D:",
1387 column_name,
1388 "/></D:prop>" EOL
1389 "<D:literal>",
1390 comparing_value->data,
1391 "</D:literal>" EOL "</D:", XML_operator, ">" EOL, NULL);
1392
1393 ne_buffer_destroy(comparing_value);
1394
1395 return NE_OK; /*success */
1396 } /*End of predicate */
1397
1398 /*
1399 * Parse a contains predicate.
1400 * <contains predicate> ::= contains <quoted_string>
1401 *
1402 * The parsing result is saved into result_str.
1403 * '*string_parsed' is changed to the pointer pointing to the position after
1404 * the predicate.
1405 *
1406 * Returns:
1407 * 1: success
1408 * 0: syntax error
1409 */
contains_predicate(char ** string_parsed,ne_buffer * result_buf)1410 int contains_predicate(char **string_parsed, ne_buffer * result_buf)
1411 {
1412 ne_buffer *contain_string;
1413
1414 ne_buffer_clear(result_buf);
1415 contain_string = ne_buffer_create();
1416
1417 /*Read 'contains' */
1418 if (match_fetch(string_parsed, "contains") == NE_ERROR) {
1419 /*The case of <contains predicate> */
1420 ne_set_error(session.sess,
1421 "Syntax error: A 'contains' is expected in the search condition.");
1422 ne_buffer_destroy(contain_string);
1423 return NE_ERROR;
1424 }
1425
1426 /*Now parse the containing string */
1427 if (first_word_equal(*string_parsed, "'") == 1) {
1428 /*For the case of <quoted string> */
1429 if (quoted_string(string_parsed, contain_string) == NE_ERROR) {
1430 ne_buffer_destroy(contain_string);
1431 return NE_ERROR; /*Parsing error */
1432 }
1433 }
1434 else {
1435 /* For the case of <wordstring> */
1436 if (word_string(string_parsed, contain_string) == NE_ERROR) {
1437 ne_set_error(session.sess,
1438 "Syntax error: A quoted string or a word string is expected in the search condition.");
1439 ne_buffer_destroy(contain_string);
1440 return NE_ERROR; /*Parsing error */
1441 }
1442 }
1443
1444 ne_buffer_concat(result_buf, "<D:contains>" EOL,
1445 contain_string->data, "</D:contains>" EOL, NULL);
1446
1447 return NE_OK; /*success */
1448 } /*End of contains_predicate */
1449
1450 /*
1451 * Parse a quoted string.
1452 * <quoted_string> ::= "'" {<any_character>} "'"
1453 *
1454 * The parsing result is saved into result_buf.
1455 * '*string_parsed' is changed to the pointer pointing to the position after
1456 * the predicate.
1457 *
1458 * Returns:
1459 * 1: success
1460 * 0: syntax error
1461 */
quoted_string(char ** string_parsed,ne_buffer * result_buf)1462 static int quoted_string(char **string_parsed, ne_buffer * result_buf)
1463 {
1464 char previous_char;
1465 char current_char;
1466 char tmp_str[2] = "";
1467 int i = 0;
1468
1469 ne_buffer_clear(result_buf);
1470
1471 /*Read a quotation mark */
1472 if (match_fetch(string_parsed, "'") == NE_ERROR) {
1473 ne_set_error(session.sess,
1474 "Syntax error: A ' is expected in the search condition.");
1475 return NE_ERROR; /*Parsing error */
1476 }
1477
1478 /*To parse {"any_character"}. Considering '\'' case when parsing */
1479 current_char = previous_char = (*string_parsed)[0];
1480 while ( ((current_char != '\'') && (current_char != '\0')) ||
1481 ((current_char == '\'') && (previous_char == '\\')) ) {
1482 tmp_str[0] = current_char;
1483 tmp_str[1] = '\0';
1484 ne_buffer_zappend(result_buf, tmp_str);
1485 previous_char = current_char;
1486 i++;
1487 current_char = (*string_parsed)[i];
1488 }
1489
1490 if (current_char != '\'') { /*There should be a ending ' */
1491 ne_set_error(session.sess,
1492 "An ending ' is expected in the search condition.");
1493 return NE_ERROR; /*parsing error */
1494 }
1495
1496 *string_parsed = *string_parsed + i + 1; /*1 for ending ' */
1497
1498 return NE_OK; /*success */
1499 } /*End of auoted_string */
1500
1501 /*
1502 * Parse a word string.
1503 * <wordstring> ::= <any char except for blank, tab and ')'>{<any_character except for blank, tab and ')'>}
1504 *
1505 * The parsing result is saved into result_buf.
1506 * '*string_parsed' is changed to the pointer pointing to the position after
1507 * the predicate.
1508 *
1509 * Returns:
1510 * 1: success
1511 * 0: syntax error
1512 */
word_string(char ** string_parsed,ne_buffer * result_buf)1513 static int word_string(char **string_parsed, ne_buffer * result_buf)
1514 {
1515 char previous_char;
1516 char current_char;
1517 char tmp_str[2] = "";
1518 int i = 0;
1519
1520 ne_buffer_clear(result_buf);
1521
1522 /*Find the left bound of a word */
1523 while (isspace((*string_parsed)[i]))
1524 i++;
1525
1526 if ((*string_parsed)[i] == '\0' || (*string_parsed)[i] == ')') /* Empty string */
1527 return NE_ERROR;
1528
1529 current_char = previous_char = (*string_parsed)[i];
1530 while ((current_char != ' ') &&
1531 (current_char != '\t') &&
1532 (current_char != ')') && (current_char != '\0')) {
1533 tmp_str[0] = current_char;
1534 tmp_str[1] = '\0';
1535 ne_buffer_zappend(result_buf, tmp_str);
1536 previous_char = current_char;
1537 i++;
1538 current_char = (*string_parsed)[i];
1539 }
1540
1541 *string_parsed = *string_parsed + i;
1542
1543 return NE_OK; /*success */
1544 } /*End of auoted_string */
1545
1546 /*
1547 * Parse a comparison_value.
1548 *<comparison_value>
1549 * ::= <number> | <quoted_string> | <wordstring>
1550 *
1551 * The parsing result is saved into result_buf.
1552 * '*string_parsed' is changed to the pointer pointing to the position after
1553 * the predicate.
1554 *
1555 * Returns:
1556 * NE_OK: success
1557 * NE_ERROR: syntax error
1558 */
comparison_value(char ** string_parsed,ne_buffer * result_buf)1559 static int comparison_value(char **string_parsed, ne_buffer * result_buf)
1560 {
1561 char identifier[WORDLEN + 1] = "";
1562 ne_buffer *comparing_value;
1563
1564 ne_buffer_clear(result_buf);
1565 comparing_value = ne_buffer_create();
1566
1567 if (first_word_equal(*string_parsed, "'") == 1) {
1568 /*It is the case of quoted string */
1569 if (quoted_string(string_parsed, comparing_value) == NE_ERROR) {
1570 ne_buffer_destroy(comparing_value);
1571 return NE_ERROR; /*Parsing error */
1572 }
1573 ne_buffer_zappend(result_buf, comparing_value->data);
1574 }
1575 else { /* It is the case of <number> or <wordstring> */
1576 /*An integer or a word string is expected */
1577 if (first_word_integer(*string_parsed) == 1) {
1578 read_aword(string_parsed, identifier);
1579 ne_buffer_zappend(result_buf, identifier);
1580 }
1581 else { /* It is the case of a word string */
1582 if (word_string(string_parsed, comparing_value) == NE_ERROR) {
1583 ne_set_error(session.sess,
1584 "Syntax error: An integer, quoted string or word string is expected in the search condition.");
1585 ne_buffer_destroy(comparing_value);
1586 return NE_ERROR;
1587 }
1588 }
1589 ne_buffer_zappend(result_buf, comparing_value->data);
1590 }
1591
1592 ne_buffer_destroy(comparing_value);
1593
1594 return NE_OK; /*success */
1595 } /*End of comparison_value */
1596