implemented
Showing
1 changed file
with
872 additions
and
1 deletions
... | @@ -21,12 +21,883 @@ | ... | @@ -21,12 +21,883 @@ |
21 | * This will be a royal pain in the arse to implement | 21 | * This will be a royal pain in the arse to implement |
22 | * Alain: True, but the new lib mailbox should coming handy with | 22 | * Alain: True, but the new lib mailbox should coming handy with |
23 | * some sort of query interface. | 23 | * some sort of query interface. |
24 | * Sergey: It was, indeed. | ||
24 | */ | 25 | */ |
25 | 26 | ||
27 | /* Implementation details: | ||
28 | |||
29 | The searching criteria are parsed and compiled into the array of | ||
30 | instructions. Each item of the array is either an instruction (inst_t | ||
31 | type) or the argument to previous instruction. The code array is | ||
32 | terminated by NULL. The result of `executing' the code array is | ||
33 | 0 or 1 depending on whether current message meets the search | ||
34 | conditions. | ||
35 | |||
36 | Note/FIXME: Implementing boolean shortcuts would speed up the long | ||
37 | search queries. */ | ||
38 | |||
39 | struct parsebuf; | ||
40 | typedef void (*inst_t) __P((struct parsebuf *pb)); | ||
41 | |||
42 | static void cond_and __P((struct parsebuf *pb)); | ||
43 | static void cond_or __P((struct parsebuf *pb)); | ||
44 | static void cond_not __P((struct parsebuf *pb)); | ||
45 | |||
46 | static void cond_all __P((struct parsebuf *pb)); | ||
47 | static void cond_bcc __P((struct parsebuf *pb)); | ||
48 | static void cond_before __P((struct parsebuf *pb)); | ||
49 | static void cond_body __P((struct parsebuf *pb)); | ||
50 | static void cond_cc __P((struct parsebuf *pb)); | ||
51 | static void cond_from __P((struct parsebuf *pb)); | ||
52 | static void cond_header __P((struct parsebuf *pb)); | ||
53 | static void cond_keyword __P((struct parsebuf *pb)); | ||
54 | static void cond_larger __P((struct parsebuf *pb)); | ||
55 | static void cond_on __P((struct parsebuf *pb)); | ||
56 | static void cond_sentbefore __P((struct parsebuf *pb)); | ||
57 | static void cond_senton __P((struct parsebuf *pb)); | ||
58 | static void cond_sentsince __P((struct parsebuf *pb)); | ||
59 | static void cond_since __P((struct parsebuf *pb)); | ||
60 | static void cond_smaller __P((struct parsebuf *pb)); | ||
61 | static void cond_subject __P((struct parsebuf *pb)); | ||
62 | static void cond_text __P((struct parsebuf *pb)); | ||
63 | static void cond_to __P((struct parsebuf *pb)); | ||
64 | static void cond_uid __P((struct parsebuf *pb)); | ||
65 | |||
66 | /* A basic condition structure */ | ||
67 | struct cond | ||
68 | { | ||
69 | char *name; /* Condition name */ | ||
70 | char *argtypes; /* String of argument types or NULL if takes no args */ | ||
71 | inst_t inst; /* Corresponding instruction */ | ||
72 | }; | ||
73 | |||
74 | /* Types are: s -- string | ||
75 | n -- number | ||
76 | d -- date | ||
77 | m -- message set | ||
78 | */ | ||
79 | |||
80 | /* List of basic conditions */ | ||
81 | struct cond condlist[] = | ||
82 | { | ||
83 | "ALL", NULL, cond_all, | ||
84 | "BCC", "s", cond_bcc, | ||
85 | "BEFORE", "d", cond_before, | ||
86 | "BODY", "s", cond_body, | ||
87 | "CC", "s", cond_cc, | ||
88 | "FROM", "s", cond_from, | ||
89 | "HEADER", "ss", cond_header, | ||
90 | "KEYWORD", "s", cond_keyword, | ||
91 | "LARGER", "n", cond_larger, | ||
92 | "ON", "d", cond_on, | ||
93 | "SENTBEFORE", "d", cond_sentbefore, | ||
94 | "SENTON", "d", cond_senton, | ||
95 | "SENTSINCE", "d", cond_sentsince, | ||
96 | "SINCE", "d", cond_since, | ||
97 | "SMALLER", "n", cond_smaller, | ||
98 | "SUBJECT", "s", cond_subject, | ||
99 | "TEXT", "s", cond_text, | ||
100 | "TO", "s", cond_to, | ||
101 | "UID", "m", cond_uid, | ||
102 | NULL, | ||
103 | }; | ||
104 | |||
105 | /* Other search keys described by rfc2060 are implemented on top of these | ||
106 | basic conditions. Condition equivalence structure defines the equivalent | ||
107 | condition in terms of basic ones. (Kind of macro substitution) */ | ||
108 | |||
109 | struct cond_equiv | ||
110 | { | ||
111 | char *name; /* RFC2060 search key name */ | ||
112 | char *equiv; /* Equivalent query in terms of basic conds */ | ||
113 | }; | ||
114 | |||
115 | struct cond_equiv equiv_list[] = | ||
116 | { | ||
117 | "ANSWERED", "KEYWORD \\Answered", | ||
118 | "DELETED", "KEYWORD \\Deleted", | ||
119 | "DRAFT", "KEYWORD \\Draft", | ||
120 | "FLAGGED", "KEYWORD \\Flagged", | ||
121 | "NEW", "(RECENT UNSEEN)", | ||
122 | "OLD", "NOT RECENT", | ||
123 | "RECENT", "KEYWORD \\Recent", | ||
124 | "SEEN", "KEYWORD \\Seen", | ||
125 | "UNANSWERED", "NOT KEYWORD \\Answered", | ||
126 | "UNDELETED", "NOT KEYWORD \\Deleted", | ||
127 | "UNDRAFT", "NOT KEYWORD \\Draft", | ||
128 | "UNFLAGGED", "NOT KEYWORD \\Flagged", | ||
129 | "UNKEYWORD", "NOT KEYWORD", | ||
130 | "UNSEEN", "NOT KEYWORD \\Seen", | ||
131 | NULL | ||
132 | }; | ||
133 | |||
134 | /* A memory allocation chain used to keep track of objects allocated during | ||
135 | the recursive-descent parsing. */ | ||
136 | struct mem_chain { | ||
137 | struct mem_chain *next; | ||
138 | void *mem; | ||
139 | }; | ||
140 | |||
141 | /* Code and stack sizes for execution of compiled search statement */ | ||
142 | #define CODESIZE 64 | ||
143 | #define CODEINCR 16 | ||
144 | #define STACKSIZE 64 | ||
145 | #define STACKINCR 16 | ||
146 | |||
147 | /* Maximum length of a token. Tokens longer than that are accepted, provided | ||
148 | that they are enclosed in doublequotes */ | ||
149 | #define MAXTOKEN 64 | ||
150 | |||
151 | /* Parse buffer structure */ | ||
152 | struct parsebuf | ||
153 | { | ||
154 | char *token; /* Current token. Either points to tokbuf | ||
155 | or is allocated within `alloc' chain */ | ||
156 | char tokbuf[MAXTOKEN+1]; /* Token buffer for short tokens */ | ||
157 | |||
158 | char *arg; /* Rest of command line to be parsed */ | ||
159 | char *err_mesg; /* Error message if a parse error occured */ | ||
160 | struct mem_chain *alloc; /* Chain of objects allocated during parsing */ | ||
161 | |||
162 | int codesize; /* Current size of allocated code */ | ||
163 | inst_t *code; /* Code buffer */ | ||
164 | int pc; /* Program counter. On parse time points | ||
165 | to the next free slot in `code' array. | ||
166 | On execution time, points to the next | ||
167 | instruction to be executed */ | ||
168 | |||
169 | int stacksize; /* Current size of allocated stack */ | ||
170 | int *stack; /* Stack buffer. */ | ||
171 | int tos; /* Top of stack */ | ||
172 | |||
173 | message_t msg; /* Execution time only: current message */ | ||
174 | }; | ||
175 | |||
176 | static void put_code __P((struct parsebuf *pb, inst_t inst)); | ||
177 | static void parse_free_mem __P((struct parsebuf *pb)); | ||
178 | static void *parse_regmem __P((struct parsebuf *pb, void *mem)); | ||
179 | static char *parse_strdup __P((struct parsebuf *pb, char *s)); | ||
180 | static void *parse_alloc __P((struct parsebuf *pb, size_t size)); | ||
181 | static int parse_search_key_list __P((struct parsebuf *pb)); | ||
182 | static int parse_search_key __P((struct parsebuf *pb)); | ||
183 | static int parse_gettoken __P((struct parsebuf *pb, int req)); | ||
184 | static int search_run __P((struct parsebuf *pb)); | ||
185 | static void do_search __P((struct parsebuf *pb)); | ||
186 | |||
26 | int | 187 | int |
27 | imap4d_search (struct imap4d_command *command, char *arg) | 188 | imap4d_search (struct imap4d_command *command, char *arg) |
28 | { | 189 | { |
190 | char *sp ; | ||
191 | char *str; | ||
192 | struct parsebuf parsebuf; | ||
193 | |||
29 | if (! (command->states & state)) | 194 | if (! (command->states & state)) |
30 | return util_finish (command, RESP_BAD, "Wrong state"); | 195 | return util_finish (command, RESP_BAD, "Wrong state"); |
31 | return util_finish (command, RESP_NO, "Not supported"); | 196 | |
197 | memset (&parsebuf, 0, sizeof(parsebuf)); | ||
198 | parsebuf.arg = arg; | ||
199 | parsebuf.err_mesg = NULL; | ||
200 | parsebuf.alloc = NULL; | ||
201 | |||
202 | if (!parse_gettoken (&parsebuf, 0)) | ||
203 | return util_finish (command, RESP_BAD, "Too few args"); | ||
204 | |||
205 | if (strcasecmp (parsebuf.token, "CHARSET") == 0) | ||
206 | { | ||
207 | if (!parse_gettoken (&parsebuf, 0)) | ||
208 | return util_finish (command, RESP_BAD, "Too few args"); | ||
209 | |||
210 | /* Currently only ASCII is supported */ | ||
211 | if (strcmp (parsebuf.token, "US-ASCII")) | ||
212 | return util_finish (command, RESP_NO, "Charset not supported"); | ||
213 | |||
214 | if (!parse_gettoken (&parsebuf, 0)) | ||
215 | return util_finish (command, RESP_BAD, "Too few args"); | ||
216 | } | ||
217 | |||
218 | /* Compile the expression */ | ||
219 | if (parse_search_key_list (&parsebuf)) | ||
220 | { | ||
221 | parse_free_mem (&parsebuf); | ||
222 | return util_finish (command, RESP_BAD, "%s (near %s)", | ||
223 | parsebuf.err_mesg, | ||
224 | *parsebuf.arg ? parsebuf.arg : "end"); | ||
225 | } | ||
226 | put_code (&parsebuf, NULL); | ||
227 | |||
228 | /* Execute compiled expression */ | ||
229 | do_search (&parsebuf); | ||
230 | |||
231 | parse_free_mem (&parsebuf); | ||
232 | return util_finish (command, RESP_OK, "Completed"); | ||
233 | } | ||
234 | |||
235 | /* For each message from the mailbox execute the query from `pb' and | ||
236 | output the message number if the query returned 1 */ | ||
237 | void | ||
238 | do_search (struct parsebuf *pb) | ||
239 | { | ||
240 | size_t i, count = 0; | ||
241 | size_t *set = NULL; | ||
242 | int n = 0; | ||
243 | |||
244 | mailbox_messages_count (mbox, &count); | ||
245 | |||
246 | util_send ("* SEARCH"); | ||
247 | for (i = 1; i <= count; i++) | ||
248 | { | ||
249 | if (mailbox_get_message (mbox, i, &pb->msg) == 0 | ||
250 | && search_run (pb)) | ||
251 | util_send (" %d", i); | ||
252 | } | ||
253 | util_send ("\r\n"); | ||
254 | } | ||
255 | |||
256 | /* Parse buffer functions */ | ||
257 | |||
258 | int | ||
259 | parse_gettoken (struct parsebuf *pb, int req) | ||
260 | { | ||
261 | int rc; | ||
262 | char *s; | ||
263 | |||
264 | pb->token = pb->tokbuf; | ||
265 | while (*pb->arg && *pb->arg == ' ') | ||
266 | pb->arg++; | ||
267 | switch (*pb->arg) | ||
268 | { | ||
269 | case '(': | ||
270 | case ')': | ||
271 | pb->token[0] = *pb->arg++; | ||
272 | pb->token[1] = 0; | ||
273 | rc = 1; | ||
274 | break; | ||
275 | case '"': | ||
276 | s = ++pb->arg; | ||
277 | while (*pb->arg && *pb->arg != '"') | ||
278 | pb->arg++; | ||
279 | rc = pb->arg - s; | ||
280 | if (*pb->arg) | ||
281 | pb->arg++; | ||
282 | |||
283 | if (rc >= sizeof(pb->tokbuf)) | ||
284 | pb->token = parse_alloc (pb, rc+1); | ||
285 | memcpy (pb->token, s, rc); | ||
286 | pb->token[rc] = 0; | ||
287 | break; | ||
288 | |||
289 | default: | ||
290 | rc = util_token (pb->token, sizeof(pb->tokbuf), &pb->arg); | ||
291 | break; | ||
292 | } | ||
293 | if (req && rc == 0) | ||
294 | pb->err_mesg = "Unexpected end of statement"; | ||
295 | return rc; | ||
296 | } | ||
297 | |||
298 | /* Memory handling */ | ||
299 | |||
300 | /* Free all memory allocated for parsebuf structure */ | ||
301 | void | ||
302 | parse_free_mem (struct parsebuf *pb) | ||
303 | { | ||
304 | struct mem_chain *alloc, *next; | ||
305 | alloc = pb->alloc; | ||
306 | while (alloc) | ||
307 | { | ||
308 | next = alloc->next; | ||
309 | free (alloc->mem); | ||
310 | free (alloc); | ||
311 | alloc = next; | ||
312 | } | ||
313 | if (pb->code) | ||
314 | free (pb->code); | ||
315 | if (pb->stack) | ||
316 | free (pb->stack); | ||
317 | } | ||
318 | |||
319 | /* Register a memory pointer mem with the parsebuf */ | ||
320 | void * | ||
321 | parse_regmem (struct parsebuf *pb, void *mem) | ||
322 | { | ||
323 | struct mem_chain *mp; | ||
324 | |||
325 | mp = malloc (sizeof(*mp)); | ||
326 | if (!mp) | ||
327 | imap4d_bye (ERR_NO_MEM); | ||
328 | mp->next = pb->alloc; | ||
329 | pb->alloc = mp; | ||
330 | mp->mem = mem; | ||
331 | return mem; | ||
332 | } | ||
333 | |||
334 | /* Allocate `size' bytes of memory within parsebuf structure */ | ||
335 | void * | ||
336 | parse_alloc (struct parsebuf *pb, size_t size) | ||
337 | { | ||
338 | void *p = malloc (size); | ||
339 | if (!p) | ||
340 | imap4d_bye (ERR_NO_MEM); | ||
341 | return parse_regmem (pb, p); | ||
342 | } | ||
343 | |||
344 | /* Create a copy of the string. */ | ||
345 | char * | ||
346 | parse_strdup (struct parsebuf *pb, char *s) | ||
347 | { | ||
348 | s = strdup (s); | ||
349 | if (!s) | ||
350 | imap4d_bye (ERR_NO_MEM); | ||
351 | return parse_regmem (pb, s); | ||
352 | } | ||
353 | |||
354 | /* A recursive-descent parser for the following grammar: | ||
355 | search_key_list : search_key | ||
356 | | search_key_list search_key | ||
357 | ; | ||
358 | |||
359 | search_key : simple_key | ||
360 | | NOT simple_key | ||
361 | | OR simple_key simple_key | ||
362 | | '(' search_key_list ')' | ||
363 | ; | ||
364 | */ | ||
365 | |||
366 | int | ||
367 | parse_search_key_list (struct parsebuf *pb) | ||
368 | { | ||
369 | int count = 0; | ||
370 | while (pb->token[0] && pb->token[0] != ')') | ||
371 | { | ||
372 | if (parse_search_key (pb)) | ||
373 | return 1; | ||
374 | if (count++) | ||
375 | put_code (pb, cond_and); | ||
376 | } | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | int | ||
381 | parse_search_key (struct parsebuf *pb) | ||
382 | { | ||
383 | if (strcmp (pb->token, "(") == 0) | ||
384 | { | ||
385 | if (parse_gettoken (pb, 1) == 0 | ||
386 | || parse_search_key_list (pb)) | ||
387 | return 1; | ||
388 | if (strcmp (pb->token, ")")) | ||
389 | { | ||
390 | pb->err_mesg = "Unbalanced parenthesis"; | ||
391 | return 1; | ||
392 | } | ||
393 | parse_gettoken (pb, 0); | ||
394 | return 0; | ||
395 | } | ||
396 | else if (strcasecmp (pb->token, "NOT") == 0) | ||
397 | { | ||
398 | if (parse_gettoken (pb, 1) == 0 | ||
399 | || parse_search_key (pb)) | ||
400 | return 1; | ||
401 | put_code (pb, cond_not); | ||
402 | return 0; | ||
403 | } | ||
404 | else if (strcasecmp (pb->token, "OR") == 0) | ||
405 | { | ||
406 | if (parse_gettoken (pb, 1) == 0 | ||
407 | || parse_search_key (pb) | ||
408 | || parse_search_key (pb)) | ||
409 | return 1; | ||
410 | put_code (pb, cond_or); | ||
411 | return 0; | ||
412 | } | ||
413 | else | ||
414 | return parse_equiv_key (pb); | ||
415 | } | ||
416 | |||
417 | int | ||
418 | parse_equiv_key (struct parsebuf *pb) | ||
419 | { | ||
420 | struct cond_equiv *condp; | ||
421 | char *arg; | ||
422 | char *save_arg; | ||
423 | |||
424 | for (condp = equiv_list; condp->name && strcasecmp (condp->name, pb->token); | ||
425 | condp++) | ||
426 | ; | ||
427 | |||
428 | if (!condp->name) | ||
429 | return parse_simple_key (pb); | ||
430 | |||
431 | save_arg = pb->arg; | ||
432 | arg = parse_strdup (pb, condp->equiv); | ||
433 | pb->arg = arg; | ||
434 | |||
435 | parse_gettoken (pb, 0); | ||
436 | |||
437 | if (parse_search_key_list (pb)) | ||
438 | { | ||
439 | /* shouldn't happen */ | ||
440 | syslog(LOG_CRIT, "%s:%d: INTERNAL ERROR", __FILE__, __LINE__); | ||
441 | abort (); | ||
442 | } | ||
443 | |||
444 | pb->arg = save_arg; | ||
445 | parse_gettoken (pb, 0); | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | int | ||
450 | parse_simple_key (struct parsebuf *pb) | ||
451 | { | ||
452 | struct cond *condp; | ||
453 | char *t; | ||
454 | time_t time; | ||
455 | |||
456 | for (condp = condlist; condp->name && strcasecmp (condp->name, pb->token); | ||
457 | condp++) | ||
458 | ; | ||
459 | |||
460 | if (!condp->name) | ||
461 | { | ||
462 | pb->err_mesg = "Unknown search criterion"; | ||
463 | return 1; | ||
464 | } | ||
465 | |||
466 | put_code (pb, condp->inst); | ||
467 | parse_gettoken (pb, 0); | ||
468 | if (condp->argtypes) | ||
469 | { | ||
470 | char *t = condp->argtypes; | ||
471 | char *s; | ||
472 | int n; | ||
473 | size_t *set; | ||
474 | |||
475 | for (; *t; t++, parse_gettoken (pb, 0)) | ||
476 | { | ||
477 | if (!pb->token[0]) | ||
478 | { | ||
479 | pb->err_mesg = "Not enough arguments for criterion"; | ||
480 | return 1; | ||
481 | } | ||
482 | |||
483 | switch (*t) | ||
484 | { | ||
485 | case 's': /* string */ | ||
486 | put_code (pb, (inst_t) parse_strdup (pb, pb->token)); | ||
487 | break; | ||
488 | case 'n': /* number */ | ||
489 | n = strtoul (pb->token, &s, 10); | ||
490 | if (*s) | ||
491 | { | ||
492 | pb->err_mesg = "Invalid number"; | ||
493 | return 1; | ||
494 | } | ||
495 | put_code (pb, (inst_t) n); | ||
496 | break; | ||
497 | case 'd': /* date */ | ||
498 | if (util_parse_internal_date (pb->token, &time)) | ||
499 | { | ||
500 | pb->err_mesg = "Bad date format"; | ||
501 | return 1; | ||
502 | } | ||
503 | put_code (pb, (inst_t) time); | ||
504 | break; | ||
505 | case 'm': /* message set */ | ||
506 | if (util_msgset (pb->token, &set, &n, 1)) /*FIXME: isuid?*/ | ||
507 | { | ||
508 | pb->err_mesg = "Bogus number set"; | ||
509 | return 1; | ||
510 | } | ||
511 | put_code (pb, (inst_t) n); | ||
512 | put_code (pb, (inst_t) parse_regmem (pb, set)); | ||
513 | break; | ||
514 | default: | ||
515 | syslog(LOG_CRIT, "%s:%d: INTERNAL ERROR", __FILE__, __LINE__); | ||
516 | abort (); /* should never happen */ | ||
517 | } | ||
518 | } | ||
519 | } | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | /* Code generator */ | ||
524 | |||
525 | void | ||
526 | put_code (struct parsebuf *pb, inst_t inst) | ||
527 | { | ||
528 | if (pb->codesize == 0) | ||
529 | { | ||
530 | pb->codesize = CODESIZE; | ||
531 | pb->code = calloc (CODESIZE, sizeof (pb->code[0])); | ||
532 | if (!pb->code) | ||
533 | imap4d_bye (ERR_NO_MEM); | ||
534 | pb->pc = 0; | ||
535 | } | ||
536 | else if (pb->pc >= pb->codesize) | ||
537 | { | ||
538 | inst_t *new_code; | ||
539 | |||
540 | pb->codesize += CODEINCR; | ||
541 | new_code = realloc (pb->code, pb->codesize*sizeof(pb->code[0])); | ||
542 | if (!new_code) | ||
543 | imap4d_bye (ERR_NO_MEM); | ||
544 | pb->code = new_code; | ||
545 | } | ||
546 | pb->code[pb->pc++] = inst; | ||
547 | } | ||
548 | |||
549 | /* The machine */ | ||
550 | static void * | ||
551 | _search_arg (struct parsebuf *pb) | ||
552 | { | ||
553 | return (void*)pb->code[pb->pc++]; | ||
554 | } | ||
555 | |||
556 | static int | ||
557 | _search_push (struct parsebuf *pb, int val) | ||
558 | { | ||
559 | if (pb->tos == pb->stacksize) | ||
560 | { | ||
561 | if (pb->stacksize == 0) | ||
562 | { | ||
563 | pb->stacksize = STACKSIZE; | ||
564 | pb->stack = calloc (STACKSIZE, sizeof(pb->stack[0])); | ||
565 | } | ||
566 | else | ||
567 | { | ||
568 | pb->stacksize += STACKINCR; | ||
569 | pb->stack = realloc (pb->stack, pb->stacksize*sizeof(pb->stack[0])); | ||
570 | } | ||
571 | if (!pb->stack) | ||
572 | imap4d_bye (ERR_NO_MEM); | ||
573 | } | ||
574 | pb->stack[pb->tos++] = val; | ||
575 | } | ||
576 | |||
577 | static int | ||
578 | _search_pop (struct parsebuf *pb) | ||
579 | { | ||
580 | if (pb->tos == 0) | ||
581 | { | ||
582 | syslog(LOG_CRIT, "%s:%d: INTERNAL ERROR", __FILE__, __LINE__); | ||
583 | abort (); /* shouldn't happen */ | ||
584 | } | ||
585 | return pb->stack[--pb->tos]; | ||
586 | } | ||
587 | |||
588 | /* Executes a query from parsebuf */ | ||
589 | int | ||
590 | search_run (struct parsebuf *pb) | ||
591 | { | ||
592 | pb->pc = 0; | ||
593 | while (pb->code[pb->pc] != NULL) | ||
594 | (*pb->code[pb->pc++]) (pb); | ||
595 | return _search_pop (pb); | ||
596 | } | ||
597 | |||
598 | /* Helper functions for evaluationg conditions */ | ||
599 | |||
600 | /* Scan the header of a message for the occurence of field named `name'. | ||
601 | Return true if any of the occurences contained substring `value' */ | ||
602 | static int | ||
603 | _scan_header (struct parsebuf *pb, char *name, char *value) | ||
604 | { | ||
605 | char buffer[512]; | ||
606 | header_t header = NULL; | ||
607 | |||
608 | message_get_header (pb->msg, &header); | ||
609 | if (!header_get_value (header, name, buffer, sizeof(buffer), NULL)) | ||
610 | { | ||
611 | return strstr (buffer, value) != NULL; | ||
612 | } | ||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | /* Get the value of Date: field and convert it to timestamp */ | ||
617 | static int | ||
618 | _header_date (struct parsebuf *pb, time_t *timep) | ||
619 | { | ||
620 | char buffer[512]; | ||
621 | header_t header = NULL; | ||
622 | |||
623 | message_get_header (pb->msg, &header); | ||
624 | if (!header_get_value (header, "Date", buffer, sizeof(buffer), NULL) | ||
625 | && util_parse_header_date (buffer, timep)) | ||
626 | return 0; | ||
627 | return 1; | ||
628 | } | ||
629 | |||
630 | /* Scan all header fields for the occurence of a substring `text' */ | ||
631 | static int | ||
632 | _scan_header_all (struct parsebuf *pb, char *text) | ||
633 | { | ||
634 | char buffer[512]; | ||
635 | header_t header = NULL; | ||
636 | size_t fcount = 0; | ||
637 | int i, rc; | ||
638 | |||
639 | message_get_header (pb->msg, &header); | ||
640 | header_get_field_count (header, &fcount); | ||
641 | for (i = rc = 0; i < fcount; i++) | ||
642 | { | ||
643 | if (header_get_field_value (header, i, buffer, sizeof(buffer), NULL)) | ||
644 | rc = strstr (buffer, text) != NULL; | ||
645 | } | ||
646 | return rc; | ||
647 | } | ||
648 | |||
649 | /* Scan body of the message for the occurence of a substring */ | ||
650 | static int | ||
651 | _scan_body (struct parsebuf *pb, char *text) | ||
652 | { | ||
653 | body_t body = NULL; | ||
654 | stream_t stream = NULL; | ||
655 | size_t size = 0, lines = 0; | ||
656 | char buffer[128]; | ||
657 | size_t n = 0; | ||
658 | off_t offset = 0; | ||
659 | int rc; | ||
660 | |||
661 | message_get_body (pb->msg, &body); | ||
662 | body_size (body, &size); | ||
663 | body_lines (body, &lines); | ||
664 | body_get_stream (body, &stream); | ||
665 | rc = 0; | ||
666 | while (rc == 0 | ||
667 | && stream_read (stream, buffer, sizeof(buffer)-1, offset, &n) == 0 | ||
668 | && n > 0) | ||
669 | { | ||
670 | offset += n; | ||
671 | rc = strstr (buffer, text) != NULL; | ||
672 | } | ||
673 | return rc; | ||
674 | } | ||
675 | |||
676 | /* Basic instructions */ | ||
677 | void | ||
678 | cond_and (struct parsebuf *pb) | ||
679 | { | ||
680 | int n1, n2; | ||
681 | |||
682 | n1 = _search_pop (pb); | ||
683 | n2 = _search_pop (pb); | ||
684 | _search_push (pb, n1 && n2); | ||
685 | } | ||
686 | |||
687 | void | ||
688 | cond_or (struct parsebuf *pb) | ||
689 | { | ||
690 | int n1, n2; | ||
691 | |||
692 | n1 = _search_pop (pb); | ||
693 | n2 = _search_pop (pb); | ||
694 | _search_push (pb, n1 || n2); | ||
695 | } | ||
696 | |||
697 | void | ||
698 | cond_not (struct parsebuf *pb) | ||
699 | { | ||
700 | _search_push (pb, !_search_pop (pb)); | ||
701 | } | ||
702 | |||
703 | void | ||
704 | cond_all (struct parsebuf *pb) | ||
705 | { | ||
706 | _search_push (pb, 1); | ||
707 | } | ||
708 | |||
709 | void | ||
710 | cond_bcc (struct parsebuf *pb) | ||
711 | { | ||
712 | _search_push (pb, _scan_header (pb, "bcc", _search_arg (pb))); | ||
713 | } | ||
714 | |||
715 | void | ||
716 | cond_before (struct parsebuf *pb) | ||
717 | { | ||
718 | time_t t = (time_t)_search_arg (pb); | ||
719 | time_t mesg_time; | ||
720 | char buffer[512]; | ||
721 | envelope_t env; | ||
722 | int rc = 0; | ||
723 | |||
724 | message_get_envelope (pb->msg, &env); | ||
725 | envelope_date (env, buffer, sizeof (buffer), NULL); | ||
726 | util_parse_rfc822_date (buffer, &mesg_time); | ||
727 | _search_push (pb, mesg_time < t); | ||
728 | } | ||
729 | |||
730 | void | ||
731 | cond_body (struct parsebuf *pb) | ||
732 | { | ||
733 | _search_push (pb, _scan_body (pb, _search_arg (pb))); | ||
734 | } | ||
735 | |||
736 | void | ||
737 | cond_cc (struct parsebuf *pb) | ||
738 | { | ||
739 | _search_push (pb, _scan_header (pb, "cc", _search_arg (pb))); | ||
740 | } | ||
741 | |||
742 | void | ||
743 | cond_from (struct parsebuf *pb) | ||
744 | { | ||
745 | char *s = _search_arg (pb); | ||
746 | envelope_t env; | ||
747 | char buffer[512]; | ||
748 | int rc = 0; | ||
749 | |||
750 | message_get_envelope (pb->msg, &env); | ||
751 | if (envelope_sender (env, buffer, sizeof (buffer), NULL) == 0) | ||
752 | rc = strstr (buffer, s) != NULL; | ||
753 | _search_push (pb, _scan_header (pb, "from", s)); | ||
754 | } | ||
755 | |||
756 | void | ||
757 | cond_header (struct parsebuf *pb) | ||
758 | { | ||
759 | char *name = _search_arg (pb); | ||
760 | char *value = _search_arg (pb); | ||
761 | |||
762 | _search_push (pb, _scan_header (pb, name, value)); | ||
763 | } | ||
764 | |||
765 | void | ||
766 | cond_keyword (struct parsebuf *pb) | ||
767 | { | ||
768 | char *s = _search_arg (pb); | ||
769 | int rc; | ||
770 | attribute_t attr = NULL; | ||
771 | |||
772 | message_get_attribute (pb->msg, &attr); | ||
773 | if (!strcmp (s, "\\Seen")) | ||
774 | rc = attribute_is_seen (attr); | ||
775 | else if (!strcmp (s, "\\Answered")) | ||
776 | rc = attribute_is_answered (attr); | ||
777 | else if (!strcmp (s, "\\Flagged")) | ||
778 | rc = attribute_is_flagged (attr); | ||
779 | else if (!strcmp (s, "\\Deleted")) | ||
780 | rc = attribute_is_deleted (attr); | ||
781 | else if (!strcmp (s, "\\Draft")) | ||
782 | rc = attribute_is_draft (attr); | ||
783 | else if (!strcmp (s, "\\Recent")) | ||
784 | rc = attribute_is_recent (attr); | ||
785 | else | ||
786 | rc = 0; | ||
787 | _search_push (pb, rc); | ||
788 | } | ||
789 | |||
790 | void | ||
791 | cond_larger (struct parsebuf *pb) | ||
792 | { | ||
793 | int n = (int) _search_arg (pb); | ||
794 | size_t size = 0; | ||
795 | |||
796 | message_size (pb->msg, &size); | ||
797 | _search_push (pb, size > n); | ||
798 | } | ||
799 | |||
800 | void | ||
801 | cond_on (struct parsebuf *pb) | ||
802 | { | ||
803 | time_t t = (time_t)_search_arg (pb); | ||
804 | time_t mesg_time; | ||
805 | char buffer[512]; | ||
806 | envelope_t env; | ||
807 | int rc = 0; | ||
808 | |||
809 | message_get_envelope (pb->msg, &env); | ||
810 | envelope_date (env, buffer, sizeof (buffer), NULL); | ||
811 | util_parse_rfc822_date (buffer, &mesg_time); | ||
812 | _search_push (pb, t <= mesg_time && mesg_time <= t + 86400); | ||
813 | } | ||
814 | |||
815 | void | ||
816 | cond_sentbefore (struct parsebuf *pb) | ||
817 | { | ||
818 | time_t t = (time_t)_search_arg (pb); | ||
819 | time_t mesg_time = 0; | ||
820 | |||
821 | _header_date (pb, &mesg_time); | ||
822 | _search_push (pb, mesg_time < t); | ||
823 | } | ||
824 | |||
825 | void | ||
826 | cond_senton (struct parsebuf *pb) | ||
827 | { | ||
828 | time_t t = (time_t)_search_arg (pb); | ||
829 | time_t mesg_time = 0; | ||
830 | |||
831 | _header_date (pb, &mesg_time); | ||
832 | _search_push (pb, t <= mesg_time && mesg_time <= t + 86400); | ||
833 | } | ||
834 | |||
835 | void | ||
836 | cond_sentsince (struct parsebuf *pb) | ||
837 | { | ||
838 | time_t t = (time_t)_search_arg (pb); | ||
839 | time_t mesg_time = 0; | ||
840 | |||
841 | _header_date (pb, &mesg_time); | ||
842 | _search_push (pb, mesg_time >= t); | ||
843 | } | ||
844 | |||
845 | void | ||
846 | cond_since (struct parsebuf *pb) | ||
847 | { | ||
848 | time_t t = (time_t)_search_arg (pb); | ||
849 | time_t mesg_time; | ||
850 | char buffer[512]; | ||
851 | envelope_t env; | ||
852 | int rc = 0; | ||
853 | |||
854 | message_get_envelope (pb->msg, &env); | ||
855 | envelope_date (env, buffer, sizeof (buffer), NULL); | ||
856 | util_parse_rfc822_date (buffer, &mesg_time); | ||
857 | _search_push (pb, mesg_time >= t); | ||
858 | } | ||
859 | |||
860 | void | ||
861 | cond_smaller (struct parsebuf *pb) | ||
862 | { | ||
863 | int n = (int) _search_arg (pb); | ||
864 | size_t size = 0; | ||
865 | |||
866 | message_size (pb->msg, &size); | ||
867 | _search_push (pb, size < n); | ||
868 | } | ||
869 | |||
870 | void | ||
871 | cond_subject (struct parsebuf *pb) | ||
872 | { | ||
873 | _search_push (pb, _scan_header (pb, "subject", _search_arg (pb))); | ||
874 | } | ||
875 | |||
876 | void | ||
877 | cond_text (struct parsebuf *pb) | ||
878 | { | ||
879 | char *s = _search_arg (pb); | ||
880 | _search_push (pb, _scan_header_all (pb, s) || _scan_body (pb, s)); | ||
881 | } | ||
882 | |||
883 | void | ||
884 | cond_to (struct parsebuf *pb) | ||
885 | { | ||
886 | _search_push (pb, _scan_header (pb, "to", _search_arg (pb))); | ||
32 | } | 887 | } |
888 | |||
889 | void | ||
890 | cond_uid (struct parsebuf *pb) | ||
891 | { | ||
892 | int n = (int)_search_arg (pb); | ||
893 | size_t *set = (size_t*)_search_arg (pb); | ||
894 | size_t uid = 0; | ||
895 | int i, rc; | ||
896 | |||
897 | message_get_uid (pb->msg, &uid); | ||
898 | for (i = rc = 0; rc == 0 && i < n; i++) | ||
899 | rc = set[i] == uid; | ||
900 | |||
901 | _search_push (pb, rc); | ||
902 | } | ||
903 | ... | ... |
-
Please register or sign in to post a comment