Mercurial > dovecot > original-hg > dovecot-1.2
comparison src/lib-storage/mail-search.c @ 0:3b1985cbc908 HEAD
Initial revision
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Aug 2002 12:15:38 +0300 |
parents | |
children | 82b7de533f98 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3b1985cbc908 |
---|---|
1 /* Copyright (C) 2002 Timo Sirainen */ | |
2 | |
3 #include "lib.h" | |
4 #include "mail-search.h" | |
5 | |
6 typedef struct { | |
7 Pool pool; | |
8 const char *error; | |
9 } SearchBuildData; | |
10 | |
11 static MailSearchArg *search_arg_new(Pool pool, MailSearchArgType type) | |
12 { | |
13 MailSearchArg *arg; | |
14 | |
15 arg = p_new(pool, MailSearchArg, 1); | |
16 arg->type = type; | |
17 | |
18 return arg; | |
19 } | |
20 | |
21 #define ARG_NEW(type, value) \ | |
22 arg_new(data, args, next_sarg, type, value) | |
23 | |
24 static int arg_new(SearchBuildData *data, ImapArgList **args, | |
25 MailSearchArg **next_sarg, MailSearchArgType type, int value) | |
26 { | |
27 MailSearchArg *sarg; | |
28 | |
29 *next_sarg = sarg = search_arg_new(data->pool, type); | |
30 if (value == 0) | |
31 return TRUE; | |
32 | |
33 /* first arg */ | |
34 if (*args == NULL) { | |
35 data->error = "Missing parameter for argument"; | |
36 return FALSE; | |
37 } | |
38 | |
39 sarg->value.str = str_ucase((*args)->arg.data.str); | |
40 *args = (*args)->next; | |
41 | |
42 /* second arg */ | |
43 if (value == 2) { | |
44 if (*args == NULL) { | |
45 data->error = "Missing parameter for argument"; | |
46 return FALSE; | |
47 } | |
48 | |
49 sarg->hdr_value = str_ucase((*args)->arg.data.str); | |
50 *args = (*args)->next; | |
51 } | |
52 | |
53 return TRUE; | |
54 } | |
55 | |
56 static int search_arg_build(SearchBuildData *data, ImapArgList **args, | |
57 MailSearchArg **next_sarg) | |
58 { | |
59 MailSearchArg **subargs; | |
60 ImapArg *arg; | |
61 char *str; | |
62 | |
63 if (*args == NULL) { | |
64 data->error = "Missing argument"; | |
65 return FALSE; | |
66 } | |
67 | |
68 arg = &(*args)->arg; | |
69 | |
70 if (arg->type == IMAP_ARG_NIL) { | |
71 /* NIL not allowed */ | |
72 data->error = "NIL not allowed"; | |
73 return FALSE; | |
74 } | |
75 | |
76 if (arg->type == IMAP_ARG_LIST) { | |
77 ImapArgList *list = arg->data.list; | |
78 | |
79 *next_sarg = search_arg_new(data->pool, SEARCH_SUB); | |
80 subargs = &(*next_sarg)->value.subargs; | |
81 while (list != NULL) { | |
82 if (!search_arg_build(data, &list, subargs)) | |
83 return FALSE; | |
84 subargs = &(*subargs)->next; | |
85 } | |
86 | |
87 *args = (*args)->next; | |
88 return TRUE; | |
89 } | |
90 | |
91 i_assert(arg->type == IMAP_ARG_ATOM || | |
92 arg->type == IMAP_ARG_STRING); | |
93 | |
94 /* string argument - get the name and jump to next */ | |
95 str = arg->data.str; | |
96 *args = (*args)->next; | |
97 str_ucase(str); | |
98 | |
99 switch (*str) { | |
100 case 'A': | |
101 if (strcmp(str, "ANSWERED") == 0) | |
102 return ARG_NEW(SEARCH_ANSWERED, 0); | |
103 else if (strcmp(str, "ALL") == 0) | |
104 return ARG_NEW(SEARCH_ALL, 0); | |
105 break; | |
106 case 'B': | |
107 if (strcmp(str, "BODY") == 0) { | |
108 /* <string> */ | |
109 return ARG_NEW(SEARCH_BODY, 1); | |
110 } else if (strcmp(str, "BEFORE") == 0) { | |
111 /* <date> */ | |
112 return ARG_NEW(SEARCH_BEFORE, 1); | |
113 } else if (strcmp(str, "BCC") == 0) { | |
114 /* <string> */ | |
115 return ARG_NEW(SEARCH_BCC, 1); | |
116 } | |
117 break; | |
118 case 'C': | |
119 if (strcmp(str, "CC") == 0) { | |
120 /* <string> */ | |
121 return ARG_NEW(SEARCH_CC, 1); | |
122 } | |
123 break; | |
124 case 'D': | |
125 if (strcmp(str, "DELETED") == 0) | |
126 return ARG_NEW(SEARCH_DELETED, 0); | |
127 else if (strcmp(str, "DRAFT") == 0) | |
128 return ARG_NEW(SEARCH_DRAFT, 0); | |
129 break; | |
130 case 'F': | |
131 if (strcmp(str, "FLAGGED") == 0) | |
132 return ARG_NEW(SEARCH_FLAGGED, 0); | |
133 else if (strcmp(str, "FROM") == 0) { | |
134 /* <string> */ | |
135 return ARG_NEW(SEARCH_FROM, 1); | |
136 } | |
137 break; | |
138 case 'H': | |
139 if (strcmp(str, "HEADER") == 0) { | |
140 /* <field-name> <string> */ | |
141 const char *key; | |
142 | |
143 if (*args == NULL) { | |
144 data->error = "Missing parameter for HEADER"; | |
145 return FALSE; | |
146 } | |
147 key = str_ucase((*args)->arg.data.str); | |
148 | |
149 if (strcmp(key, "FROM") == 0) { | |
150 *args = (*args)->next; | |
151 return ARG_NEW(SEARCH_FROM, 1); | |
152 } else if (strcmp(key, "TO") == 0) { | |
153 *args = (*args)->next; | |
154 return ARG_NEW(SEARCH_TO, 1); | |
155 } else if (strcmp(key, "CC") == 0) { | |
156 *args = (*args)->next; | |
157 return ARG_NEW(SEARCH_CC, 1); | |
158 } else if (strcmp(key, "BCC") == 0) { | |
159 *args = (*args)->next; | |
160 return ARG_NEW(SEARCH_BCC, 1); | |
161 } else if (strcmp(key, "SUBJECT") == 0) { | |
162 *args = (*args)->next; | |
163 return ARG_NEW(SEARCH_SUBJECT, 1); | |
164 } else { | |
165 return ARG_NEW(SEARCH_HEADER, 2); | |
166 } | |
167 } | |
168 break; | |
169 case 'K': | |
170 if (strcmp(str, "KEYWORD") == 0) { | |
171 /* <flag> */ | |
172 return ARG_NEW(SEARCH_KEYWORD, 1); | |
173 } | |
174 break; | |
175 case 'L': | |
176 if (strcmp(str, "LARGER") == 0) { | |
177 /* <n> */ | |
178 return ARG_NEW(SEARCH_LARGER, 1); | |
179 } | |
180 break; | |
181 case 'N': | |
182 if (strcmp(str, "NOT") == 0) { | |
183 if (!search_arg_build(data, args, next_sarg)) | |
184 return FALSE; | |
185 (*next_sarg)->not = !(*next_sarg)->not; | |
186 return TRUE; | |
187 } else if (strcmp(str, "NEW") == 0) { | |
188 /* NEW == (RECENT UNSEEN) */ | |
189 *next_sarg = search_arg_new(data->pool, SEARCH_SUB); | |
190 | |
191 subargs = &(*next_sarg)->value.subargs; | |
192 *subargs = search_arg_new(data->pool, SEARCH_RECENT); | |
193 (*subargs)->next = search_arg_new(data->pool, | |
194 SEARCH_SEEN); | |
195 (*subargs)->next->not = TRUE; | |
196 return TRUE; | |
197 } | |
198 break; | |
199 case 'O': | |
200 if (strcmp(str, "OR") == 0) { | |
201 /* <search-key1> <search-key2> */ | |
202 *next_sarg = search_arg_new(data->pool, SEARCH_OR); | |
203 | |
204 subargs = &(*next_sarg)->value.subargs; | |
205 for (;;) { | |
206 if (!search_arg_build(data, args, subargs)) | |
207 return FALSE; | |
208 | |
209 subargs = &(*subargs)->next; | |
210 | |
211 /* <key> OR <key> OR ... <key> - put them all | |
212 under one SEARCH_OR list. */ | |
213 if (*args == NULL) | |
214 break; | |
215 | |
216 arg = &(*args)->arg; | |
217 if (arg->type != IMAP_ARG_ATOM || | |
218 strcasecmp(arg->data.str, "OR") != 0) | |
219 break; | |
220 | |
221 *args = (*args)->next; | |
222 } | |
223 | |
224 if (!search_arg_build(data, args, subargs)) | |
225 return FALSE; | |
226 return TRUE; | |
227 } if (strcmp(str, "ON") == 0) { | |
228 /* <date> */ | |
229 return ARG_NEW(SEARCH_ON, 1); | |
230 } if (strcmp(str, "OLD") == 0) { | |
231 /* OLD == NOT RECENT */ | |
232 if (!ARG_NEW(SEARCH_RECENT, 0)) | |
233 return FALSE; | |
234 | |
235 (*next_sarg)->not = TRUE; | |
236 return TRUE; | |
237 } | |
238 break; | |
239 case 'R': | |
240 if (strcmp(str, "RECENT") == 0) | |
241 return ARG_NEW(SEARCH_RECENT, 0); | |
242 break; | |
243 case 'S': | |
244 if (strcmp(str, "SEEN") == 0) | |
245 return ARG_NEW(SEARCH_SEEN, 0); | |
246 else if (strcmp(str, "SUBJECT") == 0) { | |
247 /* <string> */ | |
248 return ARG_NEW(SEARCH_SUBJECT, 1); | |
249 } else if (strcmp(str, "SENTBEFORE") == 0) { | |
250 /* <date> */ | |
251 return ARG_NEW(SEARCH_SENTBEFORE, 1); | |
252 } else if (strcmp(str, "SENTON") == 0) { | |
253 /* <date> */ | |
254 return ARG_NEW(SEARCH_SENTON, 1); | |
255 } else if (strcmp(str, "SENTSINCE") == 0) { | |
256 /* <date> */ | |
257 return ARG_NEW(SEARCH_SENTSINCE, 1); | |
258 } else if (strcmp(str, "SINCE") == 0) { | |
259 /* <date> */ | |
260 return ARG_NEW(SEARCH_SINCE, 1); | |
261 } else if (strcmp(str, "SMALLER") == 0) { | |
262 /* <n> */ | |
263 return ARG_NEW(SEARCH_SMALLER, 1); | |
264 } | |
265 break; | |
266 case 'T': | |
267 if (strcmp(str, "TEXT") == 0) { | |
268 /* <string> */ | |
269 return ARG_NEW(SEARCH_TEXT, 1); | |
270 } else if (strcmp(str, "TO") == 0) { | |
271 /* <string> */ | |
272 return ARG_NEW(SEARCH_TO, 1); | |
273 } | |
274 break; | |
275 case 'U': | |
276 if (strcmp(str, "UID") == 0) { | |
277 /* <message set> */ | |
278 return ARG_NEW(SEARCH_UID, 1); | |
279 } else if (strcmp(str, "UNANSWERED") == 0) { | |
280 if (!ARG_NEW(SEARCH_ANSWERED, 0)) | |
281 return FALSE; | |
282 (*next_sarg)->not = TRUE; | |
283 return TRUE; | |
284 } else if (strcmp(str, "UNDELETED") == 0) { | |
285 if (!ARG_NEW(SEARCH_DELETED, 0)) | |
286 return FALSE; | |
287 (*next_sarg)->not = TRUE; | |
288 return TRUE; | |
289 } else if (strcmp(str, "UNDRAFT") == 0) { | |
290 if (!ARG_NEW(SEARCH_DRAFT, 0)) | |
291 return FALSE; | |
292 (*next_sarg)->not = TRUE; | |
293 return TRUE; | |
294 } else if (strcmp(str, "UNFLAGGED") == 0) { | |
295 if (!ARG_NEW(SEARCH_FLAGGED, 0)) | |
296 return FALSE; | |
297 (*next_sarg)->not = TRUE; | |
298 return TRUE; | |
299 } else if (strcmp(str, "UNKEYWORD") == 0) { | |
300 if (!ARG_NEW(SEARCH_KEYWORD, 0)) | |
301 return FALSE; | |
302 (*next_sarg)->not = TRUE; | |
303 return TRUE; | |
304 } else if (strcmp(str, "UNSEEN") == 0) { | |
305 if (!ARG_NEW(SEARCH_SEEN, 0)) | |
306 return FALSE; | |
307 (*next_sarg)->not = TRUE; | |
308 return TRUE; | |
309 } | |
310 break; | |
311 default: | |
312 if (*str == '*' || (*str >= '0' && *str <= '9')) { | |
313 /* <message-set> */ | |
314 if (!ARG_NEW(SEARCH_SET, 0)) | |
315 return FALSE; | |
316 | |
317 (*next_sarg)->value.str = str; | |
318 return TRUE; | |
319 } | |
320 break; | |
321 } | |
322 | |
323 data->error = t_strconcat("Unknown argument ", str, NULL); | |
324 return FALSE; | |
325 } | |
326 | |
327 MailSearchArg *mail_search_args_build(Pool pool, ImapArg *args, int args_count, | |
328 const char **error) | |
329 { | |
330 SearchBuildData data; | |
331 MailSearchArg *first_sarg, **sargs; | |
332 ImapArgList *list, **listp; | |
333 int i; | |
334 | |
335 /* first we need to conver the imap arguments into ImapArgList */ | |
336 list = NULL; listp = &list; | |
337 for (i = 0; i < args_count; i++) { | |
338 *listp = t_new(ImapArgList, 1); | |
339 memcpy(&(*listp)->arg, &args[i], sizeof(ImapArg)); | |
340 listp = &(*listp)->next; | |
341 } | |
342 | |
343 data.pool = pool; | |
344 data.error = NULL; | |
345 | |
346 /* get the first arg */ | |
347 first_sarg = NULL; sargs = &first_sarg; | |
348 while (list != NULL) { | |
349 if (!search_arg_build(&data, &list, sargs)) { | |
350 *error = data.error; | |
351 return NULL; | |
352 } | |
353 sargs = &(*sargs)->next; | |
354 } | |
355 | |
356 *error = NULL; | |
357 return first_sarg; | |
358 } | |
359 | |
360 void mail_search_args_reset(MailSearchArg *args) | |
361 { | |
362 while (args != NULL) { | |
363 if (args->type == SEARCH_OR || args->type == SEARCH_SUB) | |
364 mail_search_args_reset(args->value.subargs); | |
365 args->result = 0; | |
366 | |
367 args = args->next; | |
368 } | |
369 } | |
370 | |
371 static void search_arg_foreach(MailSearchArg *arg, MailSearchForeachFunc func, | |
372 void *user_data) | |
373 { | |
374 MailSearchArg *subarg; | |
375 | |
376 if (arg->result != 0) | |
377 return; | |
378 | |
379 if (arg->type == SEARCH_SUB) { | |
380 /* sublist of conditions */ | |
381 i_assert(arg->value.subargs != NULL); | |
382 | |
383 arg->result = 1; | |
384 subarg = arg->value.subargs; | |
385 while (subarg != NULL) { | |
386 if (subarg->result == 0) | |
387 search_arg_foreach(subarg, func, user_data); | |
388 | |
389 if (subarg->result == -1) { | |
390 /* failed */ | |
391 arg->result = -1; | |
392 break; | |
393 } | |
394 | |
395 if (subarg->result == 0) | |
396 arg->result = 0; | |
397 | |
398 subarg = subarg->next; | |
399 } | |
400 } else if (arg->type == SEARCH_OR) { | |
401 /* OR-list of conditions */ | |
402 i_assert(arg->value.subargs != NULL); | |
403 | |
404 subarg = arg->value.subargs; | |
405 arg->result = -1; | |
406 while (subarg != NULL) { | |
407 if (subarg->result == 0) | |
408 search_arg_foreach(subarg, func, user_data); | |
409 | |
410 if (subarg->result == 1) { | |
411 /* matched */ | |
412 arg->result = 1; | |
413 break; | |
414 } | |
415 | |
416 if (subarg->result == 0) | |
417 arg->result = 0; | |
418 | |
419 subarg = subarg->next; | |
420 } | |
421 } else { | |
422 /* just a single condition */ | |
423 func(arg, user_data); | |
424 } | |
425 } | |
426 | |
427 int mail_search_args_foreach(MailSearchArg *args, MailSearchForeachFunc func, | |
428 void *user_data) | |
429 { | |
430 int result; | |
431 | |
432 result = 1; | |
433 for (; args != NULL; args = args->next) { | |
434 search_arg_foreach(args, func, user_data); | |
435 | |
436 if (args->result == -1) { | |
437 /* failed, abort */ | |
438 return -1; | |
439 } | |
440 | |
441 if (args->result == 0) | |
442 result = 0; | |
443 } | |
444 | |
445 return result; | |
446 } | |
447 | |
448 static void search_arg_analyze(MailSearchArg *arg, int *have_headers, | |
449 int *have_body, int *have_text) | |
450 { | |
451 MailSearchArg *subarg; | |
452 | |
453 if (arg->result != 0) | |
454 return; | |
455 | |
456 switch (arg->type) { | |
457 case SEARCH_OR: | |
458 case SEARCH_SUB: | |
459 subarg = arg->value.subargs; | |
460 while (subarg != NULL) { | |
461 if (subarg->result == 0) { | |
462 search_arg_analyze(subarg, have_headers, | |
463 have_body, have_text); | |
464 } | |
465 | |
466 subarg = subarg->next; | |
467 } | |
468 break; | |
469 case SEARCH_FROM: | |
470 case SEARCH_TO: | |
471 case SEARCH_CC: | |
472 case SEARCH_BCC: | |
473 case SEARCH_SUBJECT: | |
474 case SEARCH_HEADER: | |
475 *have_headers = TRUE; | |
476 break; | |
477 case SEARCH_BODY: | |
478 *have_body = TRUE; | |
479 break; | |
480 case SEARCH_TEXT: | |
481 *have_text = TRUE; | |
482 break; | |
483 default: | |
484 break; | |
485 } | |
486 } | |
487 | |
488 void mail_search_args_analyze(MailSearchArg *args, int *have_headers, | |
489 int *have_body, int *have_text) | |
490 { | |
491 *have_headers = *have_body = *have_text = FALSE; | |
492 | |
493 for (; args != NULL; args = args->next) | |
494 search_arg_analyze(args, have_headers, have_body, have_text); | |
495 } | |
496 |