Line data Source code
1 : /* recsel.c - Record selection
2 : * Copyright (C) 2014, 2016 Werner Koch
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * This file is free software; you can redistribute it and/or modify
7 : * it under the terms of either
8 : *
9 : * - the GNU Lesser General Public License as published by the Free
10 : * Software Foundation; either version 3 of the License, or (at
11 : * your option) any later version.
12 : *
13 : * or
14 : *
15 : * - the GNU General Public License as published by the Free
16 : * Software Foundation; either version 2 of the License, or (at
17 : * your option) any later version.
18 : *
19 : * or both in parallel, as here.
20 : *
21 : * This file is distributed in the hope that it will be useful,
22 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 : * GNU General Public License for more details.
25 : *
26 : * You should have received a copy of the GNU General Public License
27 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
28 : */
29 :
30 : #include <config.h>
31 : #include <stdio.h>
32 : #include <stdlib.h>
33 : #include <string.h>
34 : #include <unistd.h>
35 : #include <errno.h>
36 :
37 : #include "util.h"
38 : #include "recsel.h"
39 :
40 : /* Select operators. */
41 : typedef enum
42 : {
43 : SELECT_SAME,
44 : SELECT_SUB,
45 : SELECT_NONEMPTY,
46 : SELECT_ISTRUE,
47 : SELECT_EQ, /* Numerically equal. */
48 : SELECT_LE,
49 : SELECT_GE,
50 : SELECT_LT,
51 : SELECT_GT,
52 : SELECT_STRLE, /* String is less or equal. */
53 : SELECT_STRGE,
54 : SELECT_STRLT,
55 : SELECT_STRGT
56 : } select_op_t;
57 :
58 :
59 : /* Definition for a select expression. */
60 : struct recsel_expr_s
61 : {
62 : recsel_expr_t next;
63 : select_op_t op; /* Operation code. */
64 : unsigned int not:1; /* Negate operators. */
65 : unsigned int disjun:1;/* Start of a disjunction. */
66 : unsigned int xcase:1; /* String match is case sensitive. */
67 : const char *value; /* (Points into NAME.) */
68 : long numvalue; /* strtol of VALUE. */
69 : char name[1]; /* Name of the property. */
70 : };
71 :
72 :
73 : /* Helper */
74 : static inline gpg_error_t
75 0 : my_error_from_syserror (void)
76 : {
77 0 : return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
78 : }
79 :
80 : /* Helper */
81 : static inline gpg_error_t
82 0 : my_error (gpg_err_code_t ec)
83 : {
84 0 : return gpg_err_make (default_errsource, ec);
85 : }
86 :
87 :
88 : /* This is a case-sensitive version of our memistr. I wonder why no
89 : * standard function memstr exists but I better do not use the name
90 : * memstr to avoid future conflicts.
91 : *
92 : * FIXME: Move this to a stringhelp.c
93 : */
94 : static const char *
95 2 : my_memstr (const void *buffer, size_t buflen, const char *sub)
96 : {
97 2 : const unsigned char *buf = buffer;
98 2 : const unsigned char *t = (const unsigned char *)buf;
99 2 : const unsigned char *s = (const unsigned char *)sub;
100 2 : size_t n = buflen;
101 :
102 32 : for ( ; n ; t++, n-- )
103 : {
104 30 : if (*t == *s)
105 : {
106 0 : for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
107 : ;
108 0 : if (!*s)
109 0 : return (const char*)buf;
110 0 : t = (const unsigned char *)buf;
111 0 : s = (const unsigned char *)sub ;
112 0 : n = buflen;
113 : }
114 : }
115 2 : return NULL;
116 : }
117 :
118 :
119 : /* Return a pointer to the next logical connection operator or NULL if
120 : * none. */
121 : static char *
122 52 : find_next_lc (char *string)
123 : {
124 : char *p1, *p2;
125 :
126 52 : p1 = strchr (string, '&');
127 52 : if (p1 && p1[1] != '&')
128 0 : p1 = NULL;
129 52 : p2 = strchr (string, '|');
130 52 : if (p2 && p2[1] != '|')
131 0 : p2 = NULL;
132 52 : if (p1 && !p2)
133 1 : return p1;
134 51 : if (!p1)
135 49 : return p2;
136 2 : return p1 < p2 ? p1 : p2;
137 : }
138 :
139 :
140 : /* Parse an expression. The expression syntax is:
141 : *
142 : * [<lc>] {{<flag>} PROPNAME <op> VALUE [<lc>]}
143 : *
144 : * A [] indicates an optional part, a {} a repetition. PROPNAME and
145 : * VALUE may not be the empty string. White space between the
146 : * elements is ignored. Numerical values are computed as long int;
147 : * standard C notation applies. <lc> is the logical connection
148 : * operator; either "&&" for a conjunction or "||" for a disjunction.
149 : * A conjunction is assumed at the begin of an expression and
150 : * conjunctions have higher precedence than disjunctions. If VALUE
151 : * starts with one of the characters used in any <op> a space after
152 : * the <op> is required. A VALUE is terminated by an <lc> unless the
153 : * "--" <flag> is used in which case the VALUE spans to the end of the
154 : * expression. <op> may be any of
155 : *
156 : * =~ Substring must match
157 : * !~ Substring must not match
158 : * = The full string must match
159 : * <> The full string must not match
160 : * == The numerical value must match
161 : * != The numerical value must not match
162 : * <= The numerical value of the field must be LE than the value.
163 : * < The numerical value of the field must be LT than the value.
164 : * >= The numerical value of the field must be GT than the value.
165 : * >= The numerical value of the field must be GE than the value.
166 : * -n True if value is not empty (no VALUE parameter allowed).
167 : * -z True if value is empty (no VALUE parameter allowed).
168 : * -t Alias for "PROPNAME != 0" (no VALUE parameter allowed).
169 : * -f Alias for "PROPNAME == 0" (no VALUE parameter allowed).
170 : *
171 : * Values for <flag> must be space separated and any of:
172 : *
173 : * -- VALUE spans to the end of the expression.
174 : * -c The string match in this part is done case-sensitive.
175 : *
176 : * For example four calls to recsel_parse_expr() with these values for
177 : * EXPR
178 : *
179 : * "uid =~ Alfa"
180 : * "&& uid !~ Test"
181 : * "|| uid =~ Alpha"
182 : * "uid !~ Test"
183 : *
184 : * or the equivalent expression
185 : *
186 : * "uid =~ Alfa" && uid !~ Test" || uid =~ Alpha" && "uid !~ Test"
187 : *
188 : * are making a selector for records where the "uid" property contains
189 : * the strings "Alfa" or "Alpha" but not the String "test".
190 : *
191 : * The caller must pass the address of a selector variable to this
192 : * function and initialize the value of the function to NULL before
193 : * the first call. recset_release needs to be called to free the
194 : * selector.
195 : */
196 : gpg_error_t
197 49 : recsel_parse_expr (recsel_expr_t *selector, const char *expression)
198 : {
199 49 : recsel_expr_t se_head = NULL;
200 : recsel_expr_t se, se2;
201 : char *expr_buffer;
202 : char *expr;
203 : char *s0, *s;
204 49 : int toend = 0;
205 49 : int xcase = 0;
206 49 : int disjun = 0;
207 49 : char *next_lc = NULL;
208 :
209 99 : while (*expression == ' ' || *expression == '\t')
210 1 : expression++;
211 :
212 49 : expr_buffer = xtrystrdup (expression);
213 49 : if (!expr_buffer)
214 0 : return my_error_from_syserror ();
215 49 : expr = expr_buffer;
216 :
217 49 : if (*expr == '|' && expr[1] == '|')
218 : {
219 1 : disjun = 1;
220 1 : expr += 2;
221 : }
222 48 : else if (*expr == '&' && expr[1] == '&')
223 1 : expr += 2;
224 :
225 : next_term:
226 109 : while (*expr == ' ' || *expr == '\t')
227 5 : expr++;
228 :
229 108 : while (*expr == '-')
230 : {
231 4 : switch (*++expr)
232 : {
233 0 : case '-': toend = 1; break;
234 4 : case 'c': xcase = 1; break;
235 : default:
236 0 : log_error ("invalid flag '-%c' in expression\n", *expr);
237 0 : recsel_release (se_head);
238 0 : xfree (expr_buffer);
239 0 : return my_error (GPG_ERR_INV_FLAG);
240 : }
241 4 : expr++;
242 12 : while (*expr == ' ' || *expr == '\t')
243 4 : expr++;
244 : }
245 :
246 52 : next_lc = toend? NULL : find_next_lc (expr);
247 52 : if (next_lc)
248 3 : *next_lc = 0; /* Terminate this term. */
249 :
250 52 : se = xtrymalloc (sizeof *se + strlen (expr));
251 52 : if (!se)
252 0 : return my_error_from_syserror ();
253 52 : strcpy (se->name, expr);
254 52 : se->next = NULL;
255 52 : se->not = 0;
256 52 : se->disjun = disjun;
257 52 : se->xcase = xcase;
258 :
259 52 : if (!se_head)
260 49 : se_head = se;
261 : else
262 : {
263 3 : for (se2 = se_head; se2->next; se2 = se2->next)
264 : ;
265 3 : se2->next = se;
266 : }
267 :
268 :
269 52 : s = strpbrk (expr, "=<>!~-");
270 52 : if (!s || s == expr )
271 : {
272 0 : log_error ("no field name given in expression\n");
273 0 : recsel_release (se_head);
274 0 : xfree (expr_buffer);
275 0 : return my_error (GPG_ERR_NO_NAME);
276 : }
277 52 : s0 = s;
278 :
279 52 : if (!strncmp (s, "=~", 2))
280 : {
281 9 : se->op = SELECT_SUB;
282 9 : s += 2;
283 : }
284 43 : else if (!strncmp (s, "!~", 2))
285 : {
286 7 : se->op = SELECT_SUB;
287 7 : se->not = 1;
288 7 : s += 2;
289 : }
290 36 : else if (!strncmp (s, "<>", 2))
291 : {
292 0 : se->op = SELECT_SAME;
293 0 : se->not = 1;
294 0 : s += 2;
295 : }
296 36 : else if (!strncmp (s, "==", 2))
297 : {
298 1 : se->op = SELECT_EQ;
299 1 : s += 2;
300 : }
301 35 : else if (!strncmp (s, "!=", 2))
302 : {
303 1 : se->op = SELECT_EQ;
304 1 : se->not = 1;
305 1 : s += 2;
306 : }
307 34 : else if (!strncmp (s, "<=", 2))
308 : {
309 1 : se->op = SELECT_LE;
310 1 : s += 2;
311 : }
312 33 : else if (!strncmp (s, ">=", 2))
313 : {
314 1 : se->op = SELECT_GE;
315 1 : s += 2;
316 : }
317 32 : else if (!strncmp (s, "<", 1))
318 : {
319 2 : se->op = SELECT_LT;
320 2 : s += 1;
321 : }
322 30 : else if (!strncmp (s, ">", 1))
323 : {
324 2 : se->op = SELECT_GT;
325 2 : s += 1;
326 : }
327 28 : else if (!strncmp (s, "=", 1))
328 : {
329 3 : se->op = SELECT_SAME;
330 3 : s += 1;
331 : }
332 25 : else if (!strncmp (s, "-z", 2))
333 : {
334 4 : se->op = SELECT_NONEMPTY;
335 4 : se->not = 1;
336 4 : s += 2;
337 : }
338 21 : else if (!strncmp (s, "-n", 2))
339 : {
340 4 : se->op = SELECT_NONEMPTY;
341 4 : s += 2;
342 : }
343 17 : else if (!strncmp (s, "-f", 2))
344 : {
345 5 : se->op = SELECT_ISTRUE;
346 5 : se->not = 1;
347 5 : s += 2;
348 : }
349 12 : else if (!strncmp (s, "-t", 2))
350 : {
351 5 : se->op = SELECT_ISTRUE;
352 5 : s += 2;
353 : }
354 7 : else if (!strncmp (s, "-le", 3))
355 : {
356 1 : se->op = SELECT_STRLE;
357 1 : s += 3;
358 : }
359 6 : else if (!strncmp (s, "-ge", 3))
360 : {
361 1 : se->op = SELECT_STRGE;
362 1 : s += 3;
363 : }
364 5 : else if (!strncmp (s, "-lt", 3))
365 : {
366 3 : se->op = SELECT_STRLT;
367 3 : s += 3;
368 : }
369 2 : else if (!strncmp (s, "-gt", 3))
370 : {
371 2 : se->op = SELECT_STRGT;
372 2 : s += 3;
373 : }
374 : else
375 : {
376 0 : log_error ("invalid operator in expression\n");
377 0 : recsel_release (se_head);
378 0 : xfree (expr_buffer);
379 0 : return my_error (GPG_ERR_INV_OP);
380 : }
381 :
382 : /* We require that a space is used if the value starts with any of
383 : the operator characters. */
384 52 : if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)
385 : ;
386 34 : else if (strchr ("=<>!~", *s))
387 : {
388 0 : log_error ("invalid operator in expression\n");
389 0 : recsel_release (se_head);
390 0 : xfree (expr_buffer);
391 0 : return my_error (GPG_ERR_INV_OP);
392 : }
393 :
394 144 : while (*s == ' ' || *s == '\t')
395 40 : s++;
396 :
397 52 : if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)
398 : {
399 36 : if (*s)
400 : {
401 0 : log_error ("value given for -n or -z\n");
402 0 : recsel_release (se_head);
403 0 : xfree (expr_buffer);
404 0 : return my_error (GPG_ERR_SYNTAX);
405 : }
406 : }
407 : else
408 : {
409 34 : if (!*s)
410 : {
411 0 : log_error ("no value given in expression\n");
412 0 : recsel_release (se_head);
413 0 : xfree (expr_buffer);
414 0 : return my_error (GPG_ERR_MISSING_VALUE);
415 : }
416 : }
417 :
418 52 : se->name[s0 - expr] = 0;
419 52 : trim_spaces (se->name);
420 52 : if (!se->name[0])
421 : {
422 0 : log_error ("no field name given in expression\n");
423 0 : recsel_release (se_head);
424 0 : xfree (expr_buffer);
425 0 : return my_error (GPG_ERR_NO_NAME);
426 : }
427 :
428 52 : trim_spaces (se->name + (s - expr));
429 52 : se->value = se->name + (s - expr);
430 52 : if (!se->value[0] && !(se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE))
431 : {
432 0 : log_error ("no value given in expression\n");
433 0 : recsel_release (se_head);
434 0 : xfree (expr_buffer);
435 0 : return my_error (GPG_ERR_MISSING_VALUE);
436 : }
437 :
438 52 : se->numvalue = strtol (se->value, NULL, 0);
439 :
440 52 : if (next_lc)
441 : {
442 3 : disjun = next_lc[1] == '|';
443 3 : expr = next_lc + 2;
444 3 : goto next_term;
445 : }
446 :
447 : /* Read:y Append to passes last selector. */
448 49 : if (!*selector)
449 46 : *selector = se_head;
450 : else
451 : {
452 3 : for (se2 = *selector; se2->next; se2 = se2->next)
453 : ;
454 3 : se2->next = se_head;
455 : }
456 :
457 49 : xfree (expr_buffer);
458 49 : return 0;
459 : }
460 :
461 :
462 : void
463 46 : recsel_release (recsel_expr_t a)
464 : {
465 144 : while (a)
466 : {
467 52 : recsel_expr_t tmp = a->next;
468 52 : xfree (a);
469 52 : a = tmp;
470 : }
471 46 : }
472 :
473 :
474 : void
475 0 : recsel_dump (recsel_expr_t selector)
476 : {
477 : recsel_expr_t se;
478 :
479 0 : log_debug ("--- Begin selectors ---\n");
480 0 : for (se = selector; se; se = se->next)
481 : {
482 0 : log_debug ("%s %s %s %s '%s'\n",
483 0 : se==selector? " ": (se->disjun? "||":"&&"),
484 0 : se->xcase? "-c":" ",
485 0 : se->name,
486 0 : se->op == SELECT_SAME? (se->not? "<>":"= "):
487 0 : se->op == SELECT_SUB? (se->not? "!~":"=~"):
488 0 : se->op == SELECT_NONEMPTY?(se->not? "-z":"-n"):
489 0 : se->op == SELECT_ISTRUE? (se->not? "-f":"-t"):
490 0 : se->op == SELECT_EQ? (se->not? "!=":"=="):
491 0 : se->op == SELECT_LT? "< ":
492 0 : se->op == SELECT_LE? "<=":
493 0 : se->op == SELECT_GT? "> ":
494 0 : se->op == SELECT_GE? ">=":
495 0 : se->op == SELECT_STRLT? "-lt":
496 0 : se->op == SELECT_STRLE? "-le":
497 0 : se->op == SELECT_STRGT? "-gt":
498 0 : se->op == SELECT_STRGE? "-ge":
499 : /**/ "[oops]",
500 : se->value);
501 : }
502 0 : log_debug ("--- End selectors ---\n");
503 0 : }
504 :
505 :
506 : /* Return true if the record RECORD has been selected. The GETVAL
507 : * function is called with COOKIE and the NAME of a property used in
508 : * the expression. */
509 : int
510 66 : recsel_select (recsel_expr_t selector,
511 : const char *(*getval)(void *cookie, const char *propname),
512 : void *cookie)
513 : {
514 : recsel_expr_t se;
515 : const char *value;
516 : size_t selen, valuelen;
517 : long numvalue;
518 66 : int result = 1;
519 :
520 66 : se = selector;
521 188 : while (se)
522 : {
523 94 : value = getval? getval (cookie, se->name) : NULL;
524 94 : if (!value)
525 13 : value = "";
526 :
527 94 : if (!*value)
528 : {
529 : /* Field is empty. */
530 17 : result = 0;
531 : }
532 : else /* Field has a value. */
533 : {
534 77 : valuelen = strlen (value);
535 77 : numvalue = strtol (value, NULL, 0);
536 77 : selen = strlen (se->value);
537 :
538 77 : switch (se->op)
539 : {
540 : case SELECT_SAME:
541 3 : if (se->xcase)
542 1 : result = (valuelen==selen && !memcmp (value,se->value,selen));
543 : else
544 2 : result = (valuelen==selen && !memicmp (value,se->value,selen));
545 3 : break;
546 : case SELECT_SUB:
547 46 : if (se->xcase)
548 2 : result = !!my_memstr (value, valuelen, se->value);
549 : else
550 44 : result = !!memistr (value, valuelen, se->value);
551 46 : break;
552 : case SELECT_NONEMPTY:
553 6 : result = !!valuelen;
554 6 : break;
555 : case SELECT_ISTRUE:
556 7 : result = !!numvalue;
557 7 : break;
558 : case SELECT_EQ:
559 2 : result = (numvalue == se->numvalue);
560 2 : break;
561 : case SELECT_GT:
562 2 : result = (numvalue > se->numvalue);
563 2 : break;
564 : case SELECT_GE:
565 1 : result = (numvalue >= se->numvalue);
566 1 : break;
567 : case SELECT_LT:
568 2 : result = (numvalue < se->numvalue);
569 2 : break;
570 : case SELECT_LE:
571 1 : result = (numvalue <= se->numvalue);
572 1 : break;
573 : case SELECT_STRGT:
574 2 : if (se->xcase)
575 0 : result = strcmp (value, se->value) > 0;
576 : else
577 2 : result = strcasecmp (value, se->value) > 0;
578 2 : break;
579 : case SELECT_STRGE:
580 1 : if (se->xcase)
581 0 : result = strcmp (value, se->value) >= 0;
582 : else
583 1 : result = strcasecmp (value, se->value) >= 0;
584 1 : break;
585 : case SELECT_STRLT:
586 3 : if (se->xcase)
587 1 : result = strcmp (value, se->value) < 0;
588 : else
589 2 : result = strcasecmp (value, se->value) < 0;
590 3 : break;
591 : case SELECT_STRLE:
592 1 : if (se->xcase)
593 0 : result = strcmp (value, se->value) <= 0;
594 : else
595 1 : result = strcasecmp (value, se->value) <= 0;
596 1 : break;
597 : }
598 : }
599 :
600 94 : if (se->not)
601 29 : result = !result;
602 :
603 94 : if (result)
604 : {
605 : /* This expression evaluated to true. See wether there are
606 : remaining expressions in this conjunction. */
607 54 : if (!se->next || se->next->disjun)
608 : break; /* All expressions are true. Return True. */
609 16 : se = se->next; /* Test the next. */
610 : }
611 : else
612 : {
613 : /* This expression evaluated to false and thus the
614 : * conjunction evaluates to false. We skip over the
615 : * remaining expressions of this conjunction and continue
616 : * with the next disjunction if any. */
617 : do
618 58 : se = se->next;
619 58 : while (se && !se->disjun);
620 : }
621 : }
622 :
623 66 : return result;
624 : }
|