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