1 /*
2 ** Copyright 1998 - 2010 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #if HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <errno.h>
14 #include <courier-unicode.h>
15 #include "searchinfo.h"
16 #include "imapwrite.h"
17 #include "imaptoken.h"
18
19
alloc_search(struct searchinfo ** head)20 struct searchinfo *alloc_search(struct searchinfo **head)
21 {
22 struct searchinfo *si=(struct searchinfo *)malloc(sizeof(**head));
23
24 if (si == 0) write_error_exit(0);
25 memset(si, 0, sizeof(*si));
26 maildir_search_init(&si->sei);
27 si->next= *head;
28 *head=si;
29 return (si);
30 }
31
free_search(struct searchinfo * si)32 void free_search(struct searchinfo *si)
33 {
34 struct searchinfo *p;
35
36 while (si)
37 {
38 p=si->next;
39 if (si->as) free(si->as);
40 if (si->bs) free(si->bs);
41 if (si->cs) free(si->cs);
42
43 maildir_search_destroy(&si->sei);
44
45 free(si);
46 si=p;
47 }
48 }
49
50 static struct searchinfo *alloc_search_andlist(struct searchinfo **);
51 static struct searchinfo *alloc_search_notkey(struct searchinfo **);
52 static struct searchinfo *alloc_search_key(struct searchinfo **);
53
alloc_parsesearch(struct searchinfo ** head)54 struct searchinfo *alloc_parsesearch(struct searchinfo **head)
55 {
56 struct searchinfo *si;
57
58 *head=0;
59 if ((si=alloc_search_andlist(head)) == 0)
60 {
61 free_search(*head);
62 return (0);
63 }
64 return (si);
65 }
66
alloc_searchextra(struct searchinfo * top,struct searchinfo ** head,search_type t)67 struct searchinfo *alloc_searchextra(struct searchinfo *top,
68 struct searchinfo **head, search_type t)
69 {
70 struct searchinfo *si;
71
72 if (t == search_references1)
73 {
74 /* Automatically add third and second dummy node */
75
76 top=alloc_searchextra(top, head, search_references4);
77 top=alloc_searchextra(top, head, search_references3);
78 top=alloc_searchextra(top, head, search_references2);
79 }
80 si=alloc_search(head);
81 si->type=t;
82 si->a=top;
83 return (si);
84 }
85
alloc_search_andlist(struct searchinfo ** head)86 static struct searchinfo *alloc_search_andlist(struct searchinfo **head)
87 {
88 struct searchinfo *si, *a, *b;
89 struct imaptoken *t;
90
91 si=alloc_search_notkey(head);
92 if (!si) return (0);
93 while ((t=currenttoken())->tokentype != IT_RPAREN && t->tokentype !=
94 IT_EOL)
95 {
96 if ((a=alloc_search_notkey(head)) == 0) return (0);
97 b=alloc_search(head);
98 b->type=search_and;
99 b->a=si;
100 b->b=a;
101 si=b;
102 }
103 return (si);
104 }
105
alloc_search_notkey(struct searchinfo ** head)106 static struct searchinfo *alloc_search_notkey(struct searchinfo **head)
107 {
108 struct imaptoken *t=currenttoken();
109
110 if (t->tokentype == IT_ATOM && strcmp(t->tokenbuf, "NOT") == 0)
111 {
112 struct searchinfo *si=alloc_search(head);
113
114 si->type=search_not;
115 nexttoken();
116 if ((si->a=alloc_search_key(head)) == 0)
117 return (0);
118 return (si);
119 }
120 return (alloc_search_key(head));
121 }
122
alloc_search_key(struct searchinfo ** head)123 static struct searchinfo *alloc_search_key(struct searchinfo **head)
124 {
125 struct imaptoken *t=currenttoken();
126 struct searchinfo *si;
127 const char *keyword;
128
129 if (t->tokentype == IT_LPAREN)
130 {
131 nexttoken();
132 if ((si=alloc_search_andlist(head)) == 0 ||
133 currenttoken()->tokentype != IT_RPAREN)
134 return (0);
135 nexttoken();
136 return (si);
137 }
138
139 if (t->tokentype != IT_ATOM && t->tokentype != IT_NUMBER)
140 return (0);
141
142 keyword=t->tokenbuf;
143
144 if (strcmp(keyword, "ALL") == 0)
145 {
146 struct searchinfo *si;
147
148 (si=alloc_search(head))->type=search_all;
149 nexttoken();
150 return (si);
151 }
152
153 if (strcmp(keyword, "OR") == 0)
154 {
155 struct searchinfo *si;
156
157 si=alloc_search(head);
158 si->type=search_or;
159 nexttoken();
160 if ((si->a=alloc_search_notkey(head)) == 0 ||
161 (si->b=alloc_search_notkey(head)) == 0) return (0);
162 return (si);
163 }
164
165 if (strcmp(keyword, "HEADER") == 0)
166 {
167 struct imaptoken *t;
168 struct searchinfo *si;
169
170 si=alloc_search(head);
171 si->type=search_header;
172 t=nexttoken_okbracket();
173 if (t->tokentype != IT_ATOM &&
174 t->tokentype != IT_NUMBER &&
175 t->tokentype != IT_QUOTED_STRING)
176 return (0);
177 si->cs=strdup(t->tokenbuf);
178 if (!si->cs)
179 write_error_exit(0);
180 t=nexttoken_okbracket();
181 if (t->tokentype != IT_ATOM &&
182 t->tokentype != IT_NUMBER &&
183 t->tokentype != IT_QUOTED_STRING)
184 return (0);
185 si->as=my_strdup(t->tokenbuf);
186 nexttoken();
187 return (si);
188 }
189
190 if (strcmp(keyword, "BCC") == 0 ||
191 strcmp(keyword, "CC") == 0 ||
192 strcmp(keyword, "FROM") == 0 ||
193 strcmp(keyword, "TO") == 0 ||
194 strcmp(keyword, "SUBJECT") == 0)
195 {
196 struct imaptoken *t;
197 struct searchinfo *si;
198
199 si=alloc_search(head);
200 si->type=search_header;
201 si->cs=my_strdup(keyword);
202 t=nexttoken_okbracket();
203 if (t->tokentype != IT_ATOM &&
204 t->tokentype != IT_NUMBER &&
205 t->tokentype != IT_QUOTED_STRING)
206 return (0);
207 si->as=my_strdup(t->tokenbuf);
208 nexttoken();
209 return (si);
210 }
211
212 if (strcmp(keyword, "BEFORE") == 0)
213 {
214 struct imaptoken *t;
215 struct searchinfo *si;
216
217 si=alloc_search(head);
218 si->type=search_before;
219 t=nexttoken();
220 if (t->tokentype != IT_ATOM &&
221 t->tokentype != IT_NUMBER &&
222 t->tokentype != IT_QUOTED_STRING)
223 return (0);
224 si->as=my_strdup(t->tokenbuf);
225 nexttoken();
226 return (si);
227 }
228
229 if (strcmp(keyword, "BODY") == 0)
230 {
231 struct imaptoken *t;
232 struct searchinfo *si;
233
234 si=alloc_search(head);
235 si->type=search_body;
236 t=nexttoken_okbracket();
237 if (t->tokentype != IT_ATOM &&
238 t->tokentype != IT_NUMBER &&
239 t->tokentype != IT_QUOTED_STRING)
240 return (0);
241 si->as=my_strdup(t->tokenbuf);
242 nexttoken();
243 return (si);
244 }
245 if (strcmp(keyword, "LARGER") == 0)
246 {
247 struct imaptoken *t;
248 struct searchinfo *si;
249
250 si=alloc_search(head);
251 si->type=search_larger;
252 t=nexttoken();
253 if (t->tokentype != IT_NUMBER)
254 return (0);
255 si->as=my_strdup(t->tokenbuf);
256 nexttoken();
257 return (si);
258 }
259
260 if (strcmp(keyword, "ON") == 0)
261 {
262 struct imaptoken *t;
263 struct searchinfo *si;
264
265 si=alloc_search(head);
266 si->type=search_on;
267 t=nexttoken();
268 if (t->tokentype != IT_ATOM &&
269 t->tokentype != IT_NUMBER &&
270 t->tokentype != IT_QUOTED_STRING)
271 return (0);
272 si->as=my_strdup(t->tokenbuf);
273 nexttoken();
274 return (si);
275 }
276
277 if (strcmp(keyword, "SENTBEFORE") == 0)
278 {
279 struct imaptoken *t;
280 struct searchinfo *si;
281
282 si=alloc_search(head);
283 si->type=search_sentbefore;
284 t=nexttoken();
285 if (t->tokentype != IT_ATOM &&
286 t->tokentype != IT_NUMBER &&
287 t->tokentype != IT_QUOTED_STRING)
288 return (0);
289 si->as=my_strdup(t->tokenbuf);
290 nexttoken();
291 return (si);
292 }
293
294 if (strcmp(keyword, "SENTON") == 0)
295 {
296 struct imaptoken *t;
297 struct searchinfo *si;
298
299 si=alloc_search(head);
300 si->type=search_senton;
301 t=nexttoken();
302 if (t->tokentype != IT_ATOM &&
303 t->tokentype != IT_NUMBER &&
304 t->tokentype != IT_QUOTED_STRING)
305 return (0);
306 si->as=my_strdup(keyword);
307 nexttoken();
308 return (si);
309 }
310
311 if (strcmp(keyword, "SENTSINCE") == 0)
312 {
313 struct imaptoken *t;
314 struct searchinfo *si;
315
316 si=alloc_search(head);
317 si->type=search_sentsince;
318 t=nexttoken();
319 if (t->tokentype != IT_ATOM &&
320 t->tokentype != IT_NUMBER &&
321 t->tokentype != IT_QUOTED_STRING)
322 return (0);
323 si->as=my_strdup(t->tokenbuf);
324 nexttoken();
325 return (si);
326 }
327
328 if (strcmp(keyword, "SINCE") == 0)
329 {
330 struct imaptoken *t;
331 struct searchinfo *si;
332
333 si=alloc_search(head);
334 si->type=search_since;
335 t=nexttoken();
336 if (t->tokentype != IT_ATOM &&
337 t->tokentype != IT_NUMBER &&
338 t->tokentype != IT_QUOTED_STRING)
339 return (0);
340 si->as=my_strdup(t->tokenbuf);
341 nexttoken();
342 return (si);
343 }
344
345 if (strcmp(keyword, "SMALLER") == 0)
346 {
347 struct imaptoken *t;
348 struct searchinfo *si;
349
350 si=alloc_search(head);
351 si->type=search_smaller;
352 t=nexttoken();
353 if (t->tokentype != IT_NUMBER)
354 return (0);
355 si->as=my_strdup(t->tokenbuf);
356 nexttoken();
357 return (si);
358 }
359
360 if (strcmp(keyword, "TEXT") == 0)
361 {
362 struct imaptoken *t;
363 struct searchinfo *si;
364
365 si=alloc_search(head);
366 si->type=search_text;
367 t=nexttoken_okbracket();
368 if (t->tokentype != IT_ATOM &&
369 t->tokentype != IT_NUMBER &&
370 t->tokentype != IT_QUOTED_STRING)
371 return (0);
372 si->as=my_strdup(t->tokenbuf);
373 nexttoken();
374 return (si);
375 }
376
377 if (strcmp(keyword, "UID") == 0)
378 {
379 struct searchinfo *si;
380 struct imaptoken *t;
381
382 si=alloc_search(head);
383 si->type=search_uid;
384 t=nexttoken();
385 if (!ismsgset(t))
386 return (0);
387 si->as=my_strdup(t->tokenbuf);
388 nexttoken();
389 return (si);
390 }
391
392 if (strcmp(keyword, "KEYWORD") == 0
393 || strcmp(keyword, "UNKEYWORD") == 0)
394 {
395 int isnot= *keyword == 'U';
396 struct imaptoken *t;
397 struct searchinfo *si;
398
399 si=alloc_search(head);
400 si->type=search_msgkeyword;
401 t=nexttoken_okbracket();
402 if (t->tokentype != IT_ATOM &&
403 t->tokentype != IT_NUMBER &&
404 t->tokentype != IT_QUOTED_STRING)
405 return (0);
406 si->as=my_strdup(t->tokenbuf);
407 nexttoken();
408
409 if (isnot)
410 {
411 struct searchinfo *si2=alloc_search(head);
412
413 si2->type=search_not;
414 si2->a=si;
415 si=si2;
416 }
417 return (si);
418 }
419 if (strcmp(keyword, "ANSWERED") == 0 ||
420 strcmp(keyword, "DELETED") == 0 ||
421 strcmp(keyword, "DRAFT") == 0 ||
422 strcmp(keyword, "FLAGGED") == 0 ||
423 strcmp(keyword, "RECENT") == 0 ||
424 strcmp(keyword, "SEEN") == 0)
425 {
426 struct searchinfo *si;
427
428 si=alloc_search(head);
429 si->type=search_msgflag;
430 if ((si->as=malloc(strlen(keyword)+2)) == 0)
431 write_error_exit(0);
432 si->as[0]='\\';
433 strcpy(si->as+1, keyword);
434 nexttoken();
435 return (si);
436 }
437
438 if (strcmp(keyword, "UNANSWERED") == 0 ||
439 strcmp(keyword, "UNDELETED") == 0 ||
440 strcmp(keyword, "UNDRAFT") == 0 ||
441 strcmp(keyword, "UNFLAGGED") == 0 ||
442 strcmp(keyword, "UNSEEN") == 0)
443 {
444 struct searchinfo *si;
445 struct searchinfo *si2;
446
447 si=alloc_search(head);
448 si->type=search_msgflag;
449 if ((si->as=malloc(strlen(keyword))) == 0)
450 write_error_exit(0);
451 si->as[0]='\\';
452 strcpy(si->as+1, keyword+2);
453 nexttoken();
454
455 si2=alloc_search(head);
456 si2->type=search_not;
457 si2->a=si;
458 return (si2);
459 }
460
461 if (strcmp(keyword, "NEW") == 0)
462 {
463 struct searchinfo *si, *si2;
464
465 si=alloc_search(head);
466 si->type=search_and;
467 si2=si->a=alloc_search(head);
468 si2->type=search_msgflag;
469 si2->as=my_strdup("\\RECENT");
470 si2=si->b=alloc_search(head);
471 si2->type=search_not;
472 si2=si2->a=alloc_search(head);
473 si2->type=search_msgflag;
474 si2->as=my_strdup("\\SEEN");
475 nexttoken();
476 return (si);
477 }
478
479 if (strcmp(keyword, "OLD") == 0)
480 {
481 struct searchinfo *si, *si2;
482
483 si=alloc_search(head);
484 si->type=search_not;
485 si2=si->a=alloc_search(head);
486 si2->type=search_msgflag;
487 si2->as=my_strdup("\\RECENT");
488 nexttoken();
489 return (si);
490 }
491
492 if (ismsgset(t))
493 {
494 si=alloc_search(head);
495 si->type=search_messageset;
496 si->as=my_strdup(t->tokenbuf);
497 nexttoken();
498 return (si);
499 }
500
501 return (0);
502 }
503
504 /*
505 ** We are about to search in charset 'textcharset'. Make sure that all
506 ** search_text nodes in the search string are in that character set.
507 */
508
search_set_charset_conv(struct searchinfo * si,const char * charset)509 void search_set_charset_conv(struct searchinfo *si, const char *charset)
510 {
511 for (; si; si=si->next)
512 {
513 if (si->type != search_text && si->type != search_body
514 && si->type != search_header)
515 continue;
516 if (si->value > 0)
517 continue; /* Already found, no need to do this again */
518
519 if (maildir_search_start_str_chset(&si->sei,
520 si->as ? si->as:"",
521 charset))
522 {
523 si->value=0;
524 continue;
525 }
526 }
527 }
528
529
530 #if 0
531
532 void debug_search(struct searchinfo *si)
533 {
534 if (!si) return;
535
536 switch (si->type) {
537 case search_messageset:
538 writes("MESSAGE SET: ");
539 writes(si->as);
540 return;
541 case search_all:
542 writes("ALL");
543 return;
544 case search_msgflag:
545 writes("FLAG \"");
546 writeqs(si->as);
547 writes("\"");
548 return;
549 case search_msgkeyword:
550 writes("KEYWORD \"");
551 writeqs(si->as);
552 writes("\"");
553 return;
554 case search_not:
555 writes("NOT (");
556 debug_search(si->a);
557 writes(")");
558 return;
559 case search_and:
560 writes("AND (");
561 debug_search(si->a);
562 writes(", ");
563 debug_search(si->b);
564 writes(")");
565 return;
566 case search_or:
567 writes("OR (");
568 debug_search(si->a);
569 writes(", ");
570 debug_search(si->b);
571 writes(")");
572 return;
573 case search_header:
574 writes("HEADER \"");
575 writeqs(si->cs);
576 writes("\" \"");
577 writeqs(si->bs);
578 writes("\"");
579 return;
580 case search_before:
581 writes("BEFORE \"");
582 writeqs(si->as);
583 writes("\"");
584 return;
585 case search_body:
586 writes("BODY \"");
587 writeqs(si->as);
588 writes("\"");
589 return;
590 case search_larger:
591 writes("LARGER \"");
592 writeqs(si->as);
593 writes("\"");
594 return;
595 case search_on:
596 writes("ON \"");
597 writeqs(si->as);
598 writes("\"");
599 return;
600 case search_sentbefore:
601 writes("SENTBEFORE \"");
602 writeqs(si->as);
603 writes("\"");
604 return;
605 case search_senton:
606 writes("SENTON \"");
607 writeqs(si->as);
608 writes("\"");
609 return;
610 case search_sentsince:
611 writes("SENTSINCE \"");
612 writeqs(si->as);
613 writes("\"");
614 return;
615 case search_since:
616 writes("SINCE \"");
617 writeqs(si->as);
618 writes("\"");
619 return;
620 case search_smaller:
621 writes("SMALLER \"");
622 writeqs(si->as);
623 writes("\"");
624 return;
625 case search_text:
626 writes("TEXT \"");
627 writeqs(si->as);
628 writes("\"");
629 return;
630 case search_uid:
631 writes("UID \"");
632 writeqs(si->as);
633 writes("\"");
634 return;
635 case search_orderedsubj:
636 writes("ORDEREDSUBJ ");
637 debug_search(si->a);
638 return;
639 case search_references1:
640 writes("REFERENCES[References/In-Reply-To]=");
641 writeqs(si->as ? si->as:"");
642 writes("/");
643 writeqs(si->bs ? si->bs:"");
644 writes(" ");
645 debug_search(si->a);
646 return;
647 case search_references2:
648 writes("REFERENCES[Date:]=");
649 writeqs(si->as ? si->as:"");
650 writes(" ");
651 debug_search(si->a);
652 return;
653 case search_references3:
654 writes("REFERENCES[Subject]=");
655 writeqs(si->as ? si->as:"");
656 writes(" ");
657 debug_search(si->a);
658 return;
659 case search_references4:
660 writes("REFERENCES[Message-ID]=");
661 writeqs(si->as ? si->as:"");
662 writes(" ");
663 debug_search(si->a);
664 return;
665 case search_arrival:
666 writes("ARRIVAL");
667 return;
668 case search_cc:
669 writes("CC");
670 return;
671 case search_date:
672 writes("DATE");
673 return;
674 case search_from:
675 writes("FROM");
676 return;
677 case search_reverse:
678 writes("REVERSE");
679 return;
680 case search_size:
681 writes("SIZE");
682 return;
683 case search_to:
684 writes("TO");
685 return;
686 }
687 }
688
689 #endif
690