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