1 /* comparator.c -- comparator functions
2  * Larry Greenfield
3  * $Id$
4  */
5 /***********************************************************
6         Copyright 1999 by Carnegie Mellon University
7 
8                       All Rights Reserved
9 
10 Permission to use, copy, modify, and distribute this software and its
11 documentation for any purpose and without fee is hereby granted,
12 provided that the above copyright notice appear in all copies and that
13 both that copyright notice and this permission notice appear in
14 supporting documentation, and that the name of Carnegie Mellon
15 University not be used in advertising or publicity pertaining to
16 distribution of the software without specific, written prior
17 permission.
18 
19 CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
20 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
22 ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
24 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
25 OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 ******************************************************************/
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <string.h>
35 
36 #include "comparator.h"
37 #include "src/sv_interface/tree.h"
38 #include "sieve.h"
39 #include "src/sv_util/util.h"
40 #include "src/sv_interface/callbacks2.h"
41 
42 #define THIS_MODULE "sv_comparator"
43 
44 /* --- i;octet comparators --- */
45 
46 /* just compare the two; these should be NULL terminated */
octet_is(struct sieve2_context * context,const char * pat,const char * text)47 static int octet_is(struct sieve2_context *context, const char *pat, const char *text)
48 {
49     size_t sl;
50     sl = strlen(pat);
51 
52     return (sl == strlen(text)) && !memcmp(pat, text, sl);
53 }
54 
55 /* we do a brute force attack */
octet_contains(struct sieve2_context * context,const char * pat,const char * text)56 static int octet_contains(struct sieve2_context *context, const char *pat, const char *text)
57 {
58     return (strstr(text, pat) != NULL);
59 }
60 
octet_matches_(struct sieve2_context * context,const char * pat,const char * text,int casemap)61 static int octet_matches_(struct sieve2_context *context, const char *pat, const char *text, int casemap)
62 {
63     const char *p;
64     const char *t;
65     char c;
66 
67     t = text;
68     p = pat;
69     for (;;) {
70 	if (*p == '\0') {
71 	    /* ran out of pattern */
72 	    return (*t == '\0');
73 	}
74 	c = *p++;
75 	switch (c) {
76 	case '?':
77 	    if (*t == '\0') {
78 		return 0;
79 	    }
80 	    t++;
81 	    break;
82 	case '*':
83 	    while (*p == '*' || *p == '?') {
84 		if (*p == '?') {
85 		    /* eat the character now */
86 		    if (*t == '\0') {
87 			return 0;
88 		    }
89 		    t++;
90 		}
91 		/* coalesce into a single wildcard */
92 		p++;
93 	    }
94 	    if (*p == '\0') {
95 		/* wildcard at end of string, any remaining text is ok */
96 		return 1;
97 	    }
98 
99 	    while (*t != '\0') {
100 		/* recurse */
101 	        if (octet_matches_(context, p, t, casemap)) return 1;
102 		t++;
103 	    }
104 	case '\\':
105 	    p++;
106 	    /* falls through */
107 	default:
108 	    if (casemap && (toupper((int)(unsigned char)c) ==
109 			    toupper((int)(unsigned char)*t))) {
110 		t++;
111 	    } else if (!casemap && (c == *t)) {
112 		t++;
113 	    } else {
114 		/* literal char doesn't match */
115 		return 0;
116 	    }
117 	}
118     }
119     /* never reaches */
120     abort();
121 }
122 
octet_matches(struct sieve2_context * context,const char * pat,const char * text)123 static int octet_matches(struct sieve2_context *context, const char *pat, const char *text)
124 {
125     return octet_matches_(context, pat, text, 0);
126 }
127 
octet_regex(struct sieve2_context * context,const char * pat,const char * text)128 static int octet_regex(struct sieve2_context *context, const char *pat, const char *text)
129 {
130     return (!libsieve_regexec((const regex_t *)pat, text, 0, NULL, 0));
131 }
132 
133 
134 /* --- i;ascii-casemap comparators --- */
135 
136 /* sheer brute force */
ascii_casemap_contains(struct sieve2_context * context,const char * pat,const char * text)137 static int ascii_casemap_contains(struct sieve2_context *context, const char *pat, const char *text)
138 {
139     int N, M, i, j;
140 
141     N = strlen(text);
142     M = strlen(pat);
143     i = 0;
144     j = 0;
145     while ((j < M) && (i < N)) {
146 	if (toupper((int)(unsigned char)text[i]) ==
147 	    toupper((int)(unsigned char)pat[j])) {
148 	    i++; j++;
149 	} else {
150 	    i = i - j + 1;
151 	    j = 0;
152 	}
153     }
154     return (j == M); /* we found a match! */
155 }
156 
ascii_casemap_matches(struct sieve2_context * context,const char * pat,const char * text)157 static int ascii_casemap_matches(struct sieve2_context *context, const char *pat, const char *text)
158 {
159     return octet_matches_(context, pat, text, 1);
160 }
161 
ascii_numeric_unknown(struct sieve2_context * context,const char * pat,const char * text)162 static int ascii_numeric_unknown(struct sieve2_context *context, const char *pat, const char *text)
163 {
164     TRACE_DEBUG("Unknown numeric comparison requested");
165     return 0;
166 }
167 
168 /* i;ascii-numeric
169  * Note that the inequalities are backwards.
170  * This is because pat is the RHS and text is the LHS.
171  */
ascii_numeric(struct sieve2_context * context,enum num num,const char * pat,const char * text)172 static int ascii_numeric(struct sieve2_context *context, enum num num, const char *pat, const char *text)
173 {
174     TRACE_DEBUG("Testing [%s] [%d] [%s]", pat, num, text);
175     if (isdigit((int)(unsigned char)*pat)) {
176 	if (isdigit((int)(unsigned char)*text)) {
177             TRACE_DEBUG("Testing [%d] [%d] [%d]", atoi(pat), num, atoi(text));
178 	    switch (num) {
179 	    case gt:
180 	        return atoi(pat) <  atoi(text);
181 	    case ge:
182 	        return atoi(pat) <= atoi(text);
183 	    case lt:
184 	        return atoi(pat) >  atoi(text);
185 	    case le:
186 	        return atoi(pat) >= atoi(text);
187 	    case eq:
188 	        return atoi(pat) == atoi(text);
189 	    case ne:
190 	        return atoi(pat) != atoi(text);
191             default:
192 	        return 0;
193             }
194 	} else {
195 	    return 0;
196 	}
197     } else if (isdigit((int)(unsigned char)*text)) return 0;
198     else return 1; /* both not digits */
199 }
200 
ascii_numeric_gt(struct sieve2_context * context,const char * pat,const char * text)201 static int ascii_numeric_gt(struct sieve2_context *context, const char *pat, const char *text)
202     { return ascii_numeric(context, gt, pat, text); }
ascii_numeric_ge(struct sieve2_context * context,const char * pat,const char * text)203 static int ascii_numeric_ge(struct sieve2_context *context, const char *pat, const char *text)
204     { return ascii_numeric(context, ge, pat, text); }
ascii_numeric_lt(struct sieve2_context * context,const char * pat,const char * text)205 static int ascii_numeric_lt(struct sieve2_context *context, const char *pat, const char *text)
206     { return ascii_numeric(context, lt, pat, text); }
ascii_numeric_le(struct sieve2_context * context,const char * pat,const char * text)207 static int ascii_numeric_le(struct sieve2_context *context, const char *pat, const char *text)
208     { return ascii_numeric(context, le, pat, text); }
ascii_numeric_eq(struct sieve2_context * context,const char * pat,const char * text)209 static int ascii_numeric_eq(struct sieve2_context *context, const char *pat, const char *text)
210     { return ascii_numeric(context, eq, pat, text); }
ascii_numeric_ne(struct sieve2_context * context,const char * pat,const char * text)211 static int ascii_numeric_ne(struct sieve2_context *context, const char *pat, const char *text)
212     { return ascii_numeric(context, ne, pat, text); }
213 
ascii_casemap_unknown(struct sieve2_context * context,const char * pat,const char * text)214 static int ascii_casemap_unknown(struct sieve2_context *context, const char *pat, const char *text)
215 {
216     TRACE_DEBUG("Unknown casemap comparison requested");
217     return 0;
218 }
219 
220 /* i;ascii-casemap
221  * Note that the inequalities are backwards.
222  * This is because pat is the RHS and text is the LHS.
223  */
ascii_casemap(struct sieve2_context * context,enum num num,const char * pat,const char * text)224 static int ascii_casemap(struct sieve2_context* context, enum num num, const char *pat, const char *text)
225 {
226     TRACE_DEBUG("Testing [%s] [%d] [%s]", pat, num, text);
227     switch (num) {
228     case gt:
229         return strcasecmp(pat, text) <  0;
230     case ge:
231         return strcasecmp(pat, text) <= 0;
232     case lt:
233         return strcasecmp(pat, text) >  0;
234     case le:
235         return strcasecmp(pat, text) >= 0;
236     case eq:
237         return strcasecmp(pat, text) == 0;
238     case ne:
239         return strcasecmp(pat, text) != 0;
240     default:
241         return 0;
242     }
243 }
244 
ascii_casemap_gt(struct sieve2_context * context,const char * pat,const char * text)245 static int ascii_casemap_gt(struct sieve2_context *context, const char *pat, const char *text)
246     { return ascii_casemap(context, gt, pat, text); }
ascii_casemap_ge(struct sieve2_context * context,const char * pat,const char * text)247 static int ascii_casemap_ge(struct sieve2_context *context, const char *pat, const char *text)
248     { return ascii_casemap(context, ge, pat, text); }
ascii_casemap_lt(struct sieve2_context * context,const char * pat,const char * text)249 static int ascii_casemap_lt(struct sieve2_context *context, const char *pat, const char *text)
250     { return ascii_casemap(context, lt, pat, text); }
ascii_casemap_le(struct sieve2_context * context,const char * pat,const char * text)251 static int ascii_casemap_le(struct sieve2_context *context, const char *pat, const char *text)
252     { return ascii_casemap(context, le, pat, text); }
ascii_casemap_eq(struct sieve2_context * context,const char * pat,const char * text)253 static int ascii_casemap_eq(struct sieve2_context *context, const char *pat, const char *text)
254     { return ascii_casemap(context, eq, pat, text); }
ascii_casemap_ne(struct sieve2_context * context,const char * pat,const char * text)255 static int ascii_casemap_ne(struct sieve2_context *context, const char *pat, const char *text)
256     { return ascii_casemap(context, ne, pat, text); }
257 
libsieve_relational_lookup(const char * rel)258 int libsieve_relational_lookup(const char *rel)
259 {
260     enum num num;
261 
262     if (!rel) {
263         return 0;
264     } else if (!strcmp(rel, "gt")) {
265         return (num = gt) << 10;
266     } else if (!strcmp(rel, "ge")) {
267         return (num = ge) << 10;
268     } else if (!strcmp(rel, "lt")) {
269         return (num = lt) << 10;
270     } else if (!strcmp(rel, "le")) {
271         return (num = le) << 10;
272     } else if (!strcmp(rel, "eq")) {
273         return (num = eq) << 10;
274     } else if (!strcmp(rel, "ne")) {
275         return (num = ne) << 10;
276     } else {
277         return 0;
278     }
279 }
280 
libsieve_comparator_lookup(struct sieve2_context * context,const char * comp,int mode)281 VISIBLE comparator_t *libsieve_comparator_lookup(struct sieve2_context *context, const char *comp, int mode)
282 {
283     comparator_t *ret;
284 
285     ret = NULL;
286     if (!strcmp(comp, "i;octet")) {
287 	switch (mode) {
288 	case IS:
289 	    ret = &octet_is;
290 	    break;
291 	case CONTAINS:
292 	    ret = &octet_contains;
293 	    break;
294 	case MATCHES:
295 	    ret = &octet_matches;
296 	    break;
297 	case REGEX:
298 	    ret = &octet_regex;
299 	    break;
300 	}
301     } else if (!strcmp(comp, "i;ascii-casemap")) {
302 	switch (mode) {
303 	case IS:
304 	    ret = &ascii_casemap_eq;
305 	    break;
306 	case CONTAINS:
307 	    ret = &ascii_casemap_contains;
308 	    break;
309 	case MATCHES:
310 	    ret = &ascii_casemap_matches;
311 	    break;
312 	case REGEX:
313 	    /* the ascii-casemap destinction is made during
314 	       the compilation of the regex in verify_regex() */
315 	    ret = &octet_regex;
316 	    break;
317 	case VALUE:
318 	    TRACE_DEBUG("Value comparison requested with default relation");
319 	    goto casemap_switch;
320 	case COUNT:
321 	    TRACE_DEBUG("Count comparison requested with default relation");
322 	default:
323         casemap_switch:
324 	    switch ((enum num)(mode >> 10)) {
325             case gt:
326 	        ret = &ascii_casemap_gt;
327 		break;
328             case ge:
329 	        ret = &ascii_casemap_ge;
330 		break;
331             case lt:
332 	        ret = &ascii_casemap_lt;
333 		break;
334             case le:
335 	        ret = &ascii_casemap_le;
336 		break;
337             case eq:
338 	        ret = &ascii_casemap_eq;
339 		break;
340             case ne:
341 	        ret = &ascii_casemap_ne;
342 		break;
343 	    default:
344 	    	ret = &ascii_casemap_unknown;
345 	    }
346 	}
347     } else if (!strcmp(comp, "i;ascii-numeric")) {
348 	switch (mode) {
349 	case IS:
350 	    ret = &ascii_numeric_eq;
351 	    break;
352 	case VALUE:
353 	    TRACE_DEBUG("Value comparison requested with default relation");
354 	    goto numeric_switch;
355 	case COUNT:
356 	    TRACE_DEBUG("Count comparison requested with default relation");
357         default:
358         numeric_switch:
359 	    switch ((enum num)(mode >> 10)) {
360             case gt:
361 	        ret = &ascii_numeric_gt;
362 		break;
363             case ge:
364 	        ret = &ascii_numeric_ge;
365 		break;
366             case lt:
367 	        ret = &ascii_numeric_lt;
368 		break;
369             case le:
370 	        ret = &ascii_numeric_le;
371 		break;
372             case eq:
373 	        ret = &ascii_numeric_eq;
374 		break;
375             case ne:
376 	        ret = &ascii_numeric_ne;
377 		break;
378 	    default:
379 	    	ret = &ascii_numeric_unknown;
380             }
381 	}
382     }
383     return ret;
384 }
385 
libsieve_relational_count(struct sieve2_context * context,int mode)386 int libsieve_relational_count(struct sieve2_context *context, int mode)
387 {
388     if ((mode & COUNT) == COUNT) {
389         TRACE_DEBUG("Count relation [%d]", mode >> 10);
390         return 1;
391     }
392     if ((mode & VALUE) == VALUE) {
393         TRACE_DEBUG("Value relation [%d]", mode >> 10);
394         return 0;
395     }
396     return 0;
397 }
398