Commit 83f6a439 83f6a4390dd7ae002ee4dfd7ed788f4169bee9ea by Sergey Poznyakoff

implemented

1 parent e01b13b1
...@@ -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++];
32 } 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)));
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
......