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