NYD: names.c
[s-mailx.git] / imap_search.c
blobcd4ca23de3f175c017adf0cd143cac37241305d8
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Client-side implementation of the IMAP SEARCH command. This is used
3 *@ for folders not located on IMAP servers, or for IMAP servers that do
4 *@ not implement the SEARCH command.
6 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
7 * Copyright (c) 2012 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
8 */
9 /*
10 * Copyright (c) 2004
11 * Gunnar Ritter. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by Gunnar Ritter
24 * and his contributors.
25 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 #ifdef HAVE_REGEX
47 # include <regex.h>
48 #endif
50 enum itoken {
51 ITBAD, ITEOD, ITBOL, ITEOL, ITAND, ITSET, ITALL, ITANSWERED,
52 ITBCC, ITBEFORE, ITBODY,
53 ITCC,
54 ITDELETED, ITDRAFT,
55 ITFLAGGED, ITFROM,
56 ITHEADER,
57 ITKEYWORD,
58 ITLARGER,
59 ITNEW, ITNOT,
60 ITOLD, ITON, ITOR,
61 ITRECENT,
62 ITSEEN, ITSENTBEFORE, ITSENTON, ITSENTSINCE, ITSINCE, ITSMALLER,
63 ITSUBJECT,
64 ITTEXT, ITTO,
65 ITUID, ITUNANSWERED, ITUNDELETED, ITUNDRAFT, ITUNFLAGGED, ITUNKEYWORD,
66 ITUNSEEN
69 struct itlex {
70 char const *s_string;
71 enum itoken s_token;
74 #ifdef HAVE_REGEX
75 struct itregex {
76 struct itregex *re_next;
77 regex_t re_regex;
79 #endif
81 struct itnode {
82 enum itoken n_token;
83 unsigned long n_n;
84 void *n_v;
85 void *n_w;
86 struct itnode *n_x;
87 struct itnode *n_y;
90 static struct itlex const _it_strings[] = {
91 { "ALL", ITALL },
92 { "ANSWERED", ITANSWERED },
93 { "BCC", ITBCC },
94 { "BEFORE", ITBEFORE },
95 { "BODY", ITBODY },
96 { "CC", ITCC },
97 { "DELETED", ITDELETED },
98 { "DRAFT", ITDRAFT },
99 { "FLAGGED", ITFLAGGED },
100 { "FROM", ITFROM },
101 { "HEADER", ITHEADER },
102 { "KEYWORD", ITKEYWORD },
103 { "LARGER", ITLARGER },
104 { "NEW", ITNEW },
105 { "NOT", ITNOT },
106 { "OLD", ITOLD },
107 { "ON", ITON },
108 { "OR", ITOR },
109 { "RECENT", ITRECENT },
110 { "SEEN", ITSEEN },
111 { "SENTBEFORE", ITSENTBEFORE },
112 { "SENTON", ITSENTON },
113 { "SENTSINCE", ITSENTSINCE },
114 { "SINCE", ITSINCE },
115 { "SMALLER", ITSMALLER },
116 { "SUBJECT", ITSUBJECT },
117 { "TEXT", ITTEXT },
118 { "TO", ITTO },
119 { "UID", ITUID },
120 { "UNANSWERED", ITUNANSWERED },
121 { "UNDELETED", ITUNDELETED },
122 { "UNDRAFT", ITUNDRAFT },
123 { "UNFLAGGED", ITUNFLAGGED },
124 { "UNKEYWORD", ITUNKEYWORD },
125 { "UNSEEN", ITUNSEEN },
126 { NULL, ITBAD }
129 static struct itnode *_it_tree;
130 #ifdef HAVE_REGEX
131 static struct itregex *_it_regex;
132 #endif
133 static char *_it_begin;
134 static enum itoken _it_token;
135 static unsigned long _it_number;
136 static void *_it_args[2];
137 static size_t _it_need_headers;
138 static bool_t _it_need_regex;
140 static enum okay itparse(char const *spec, char const **xp, int sub);
141 static enum okay itscan(char const *spec, char const **xp);
142 static enum okay itsplit(char const *spec, char const **xp);
143 static enum okay itstring(void **tp, char const *spec, char const **xp);
144 static int itexecute(struct mailbox *mp, struct message *m,
145 size_t c, struct itnode *n);
146 static int matchfield(struct message *m, char const *field,
147 const void *what);
148 static int matchenvelope(struct message *m, char const *field,
149 const void *what);
150 static char * mkenvelope(struct name *np);
151 static int matchmsg(struct message *m, const void *what,
152 int withheader);
153 static char const * around(char const *cp);
155 static enum okay
156 itparse(char const *spec, char const **xp, int sub)
158 int level = 0;
159 struct itnode n, *z, *ittree;
160 enum okay rv;
161 NYD_ENTER;
163 _it_tree = NULL;
164 while ((rv = itscan(spec, xp)) == OKAY && _it_token != ITBAD &&
165 _it_token != ITEOD) {
166 ittree = _it_tree;
167 memset(&n, 0, sizeof n);
168 spec = *xp;
169 switch (_it_token) {
170 case ITBOL:
171 level++;
172 continue;
173 case ITEOL:
174 if (--level == 0)
175 goto jleave;
176 if (level < 0) {
177 if (sub > 0) {
178 (*xp)--;
179 goto jleave;
181 fprintf(stderr, "Excess in \")\".\n");
182 rv = STOP;
183 goto jleave;
185 continue;
186 case ITNOT:
187 /* <search-key> */
188 n.n_token = ITNOT;
189 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
190 goto jleave;
191 spec = *xp;
192 if ((n.n_x = _it_tree) == NULL) {
193 fprintf(stderr, "Criterion for NOT missing: >>> %s <<<\n",
194 around(*xp));
195 rv = STOP;
196 goto jleave;
198 _it_token = ITNOT;
199 break;
200 case ITOR:
201 /* <search-key1> <search-key2> */
202 n.n_token = ITOR;
203 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
204 goto jleave;
205 if ((n.n_x = _it_tree) == NULL) {
206 fprintf(stderr, "First criterion for OR missing: >>> %s <<<\n",
207 around(*xp));
208 rv = STOP;
209 goto jleave;
211 spec = *xp;
212 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
213 goto jleave;
214 spec = *xp;
215 if ((n.n_y = _it_tree) == NULL) {
216 fprintf(stderr, "Second criterion for OR missing: >>> %s <<<\n",
217 around(*xp));
218 rv = STOP;
219 goto jleave;
221 break;
222 default:
223 n.n_token = _it_token;
224 n.n_n = _it_number;
225 n.n_v = _it_args[0];
226 n.n_w = _it_args[1];
229 _it_tree = ittree;
230 if (_it_tree == NULL) {
231 _it_tree = salloc(sizeof *_it_tree);
232 *_it_tree = n;
233 } else {
234 z = _it_tree;
235 _it_tree = salloc(sizeof *_it_tree);
236 _it_tree->n_token = ITAND;
237 _it_tree->n_x = z;
238 _it_tree->n_y = salloc(sizeof *_it_tree->n_y);
239 *_it_tree->n_y = n;
241 if (sub && level == 0)
242 break;
244 jleave:
245 NYD_LEAVE;
246 return rv;
249 static enum okay
250 itscan(char const *spec, char const **xp)
252 int i, n;
253 enum okay rv = OKAY;
254 NYD_ENTER;
256 while (spacechar(*spec))
257 ++spec;
258 if (*spec == '(') {
259 *xp = &spec[1];
260 _it_token = ITBOL;
261 goto jleave;
263 if (*spec == ')') {
264 *xp = &spec[1];
265 _it_token = ITEOL;
266 goto jleave;
268 while (spacechar(*spec))
269 ++spec;
270 if (*spec == '\0') {
271 _it_token = ITEOD;
272 goto jleave;
275 for (i = 0; _it_strings[i].s_string; i++) {
276 n = strlen(_it_strings[i].s_string);
277 if (ascncasecmp(spec, _it_strings[i].s_string, n) == 0 &&
278 (spacechar(spec[n]) || spec[n] == '\0' ||
279 spec[n] == '(' || spec[n] == ')')) {
280 _it_token = _it_strings[i].s_token;
281 spec += n;
282 while (spacechar(*spec))
283 ++spec;
284 rv = itsplit(spec, xp);
285 goto jleave;
288 if (digitchar(*spec)) {
289 _it_number = strtoul(spec, UNCONST(xp), 10);
290 if (spacechar(**xp) || **xp == '\0' || **xp == '(' || **xp == ')') {
291 _it_token = ITSET;
292 goto jleave;
295 fprintf(stderr, "Bad SEARCH criterion \"");
296 while (*spec && !spacechar(*spec) && *spec != '(' && *spec != ')') {
297 putc(*spec & 0377, stderr);
298 ++spec;
300 fprintf(stderr, "\": >>> %s <<<\n", around(*xp));
301 _it_token = ITBAD;
302 rv = STOP;
303 jleave:
304 NYD_LEAVE;
305 return rv;
308 static enum okay
309 itsplit(char const *spec, char const **xp)
311 char *cp;
312 time_t t;
313 enum okay rv;
314 NYD_ENTER;
316 switch (_it_token) {
317 case ITBCC:
318 case ITBODY:
319 case ITCC:
320 case ITFROM:
321 case ITSUBJECT:
322 case ITTEXT:
323 case ITTO:
324 /* <string> */
325 _it_need_headers++;
326 rv = itstring(&_it_args[0], spec, xp);
327 #ifdef HAVE_REGEX
328 if (rv == OKAY && _it_need_regex) {
329 _it_number = 0;
330 goto jregcomp;
332 #endif
333 break;
334 case ITSENTBEFORE:
335 case ITSENTON:
336 case ITSENTSINCE:
337 _it_need_headers++;
338 /*FALLTHRU*/
339 case ITBEFORE:
340 case ITON:
341 case ITSINCE:
342 /* <date> */
343 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
344 break;
345 if ((t = imap_read_date(_it_args[0])) == (time_t)-1) {
346 fprintf(stderr, "Invalid date \"%s\": >>> %s <<<\n",
347 (char*)_it_args[0], around(*xp));
348 rv = STOP;
349 break;
351 _it_number = t;
352 rv = OKAY;
353 break;
354 case ITHEADER:
355 /* <field-name> <string> */
356 _it_need_headers++;
357 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
358 break;
359 spec = *xp;
360 if ((rv = itstring(&_it_args[1], spec, xp)) != OKAY)
361 break;
362 #ifdef HAVE_REGEX
363 _it_number = 1;
364 jregcomp:
365 if (_it_need_regex) {
366 struct itregex *itre = salloc(sizeof *_it_regex);
367 itre->re_next = _it_regex;
368 _it_regex = itre;
370 cp = _it_args[_it_number];
371 _it_args[_it_number] = &itre->re_regex;
372 if (regcomp(&itre->re_regex, cp, REG_EXTENDED | REG_ICASE | REG_NOSUB)
373 != 0) {
374 fprintf(stderr, tr(526,
375 "Invalid regular expression \"%s\": >>> %s <<<\n"),
376 cp, around(*xp));
377 rv = STOP;
378 break;
381 #endif
382 break;
383 case ITKEYWORD:
384 case ITUNKEYWORD:
385 /* <flag> */
386 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
387 break;
388 if (asccasecmp(_it_args[0], "\\Seen") == 0)
389 _it_number = MREAD;
390 else if (asccasecmp(_it_args[0], "\\Deleted") == 0)
391 _it_number = MDELETED;
392 else if (asccasecmp(_it_args[0], "\\Recent") == 0)
393 _it_number = MNEW;
394 else if (asccasecmp(_it_args[0], "\\Flagged") == 0)
395 _it_number = MFLAGGED;
396 else if (asccasecmp(_it_args[0], "\\Answered") == 0)
397 _it_number = MANSWERED;
398 else if (asccasecmp(_it_args[0], "\\Draft") == 0)
399 _it_number = MDRAFT;
400 else
401 _it_number = 0;
402 break;
403 case ITLARGER:
404 case ITSMALLER:
405 /* <n> */
406 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
407 break;
408 _it_number = strtoul(_it_args[0], &cp, 10);
409 if (spacechar(*cp) || *cp == '\0')
410 break;
411 fprintf(stderr, "Invalid size: >>> %s <<<\n", around(*xp));
412 rv = STOP;
413 break;
414 case ITUID:
415 /* <message set> */
416 fprintf(stderr,
417 "Searching for UIDs is not supported: >>> %s <<<\n", around(*xp));
418 rv = STOP;
419 break;
420 default:
421 *xp = spec;
422 rv = OKAY;
423 break;
425 NYD_LEAVE;
426 return rv;
429 static enum okay
430 itstring(void **tp, char const *spec, char const **xp)
432 int inquote = 0;
433 char *ap;
434 enum okay rv = STOP;
435 NYD_ENTER;
437 while (spacechar(*spec))
438 spec++;
439 if (*spec == '\0' || *spec == '(' || *spec == ')') {
440 fprintf(stderr, "Missing string argument: >>> %s <<<\n",
441 around(&(*xp)[spec - *xp]));
442 goto jleave;
444 ap = *tp = salloc(strlen(spec) + 1);
445 *xp = spec;
446 do {
447 if (inquote && **xp == '\\')
448 *ap++ = *(*xp)++;
449 else if (**xp == '"')
450 inquote = !inquote;
451 else if (!inquote && (spacechar(**xp) || **xp == '(' || **xp == ')')) {
452 *ap++ = '\0';
453 break;
455 *ap++ = **xp;
456 } while (*(*xp)++);
458 *tp = imap_unquotestr(*tp);
459 rv = OKAY;
460 jleave:
461 NYD_LEAVE;
462 return rv;
465 static int
466 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
468 char *cp, *line = NULL;
469 size_t linesize = 0;
470 FILE *ibuf;
471 int rv;
472 NYD_ENTER;
474 if (n == NULL) {
475 fprintf(stderr, "Internal error: Empty node in SEARCH tree.\n");
476 rv = 0;
477 goto jleave;
480 switch (n->n_token) {
481 case ITBEFORE:
482 case ITON:
483 case ITSINCE:
484 if (m->m_time == 0 && !(m->m_flag & MNOFROM) &&
485 (ibuf = setinput(mp, m, NEED_HEADER)) != NULL) {
486 if (readline_restart(ibuf, &line, &linesize, 0) > 0)
487 m->m_time = unixtime(line);
488 free(line);
490 break;
491 case ITSENTBEFORE:
492 case ITSENTON:
493 case ITSENTSINCE:
494 if (m->m_date == 0)
495 if ((cp = hfield1("date", m)) != NULL)
496 m->m_date = rfctime(cp);
497 break;
498 default:
499 break;
502 switch (n->n_token) {
503 default:
504 fprintf(stderr, "Internal SEARCH error: Lost token %d\n", n->n_token);
505 rv = 0;
506 break;
507 case ITAND:
508 rv = itexecute(mp, m, c, n->n_x) & itexecute(mp, m, c, n->n_y);
509 break;
510 case ITSET:
511 rv = UICMP(z, c, ==, n->n_n);
512 break;
513 case ITALL:
514 rv = 1;
515 break;
516 case ITANSWERED:
517 rv = ((m->m_flag & MANSWERED) != 0);
518 break;
519 case ITBCC:
520 rv = matchenvelope(m, "bcc", n->n_v);
521 break;
522 case ITBEFORE:
523 rv = UICMP(z, m->m_time, <, n->n_n);
524 break;
525 case ITBODY:
526 rv = matchmsg(m, n->n_v, 0);
527 break;
528 case ITCC:
529 rv = matchenvelope(m, "cc", n->n_v);
530 break;
531 case ITDELETED:
532 rv = ((m->m_flag & MDELETED) != 0);
533 break;
534 case ITDRAFT:
535 rv = ((m->m_flag & MDRAFTED) != 0);
536 break;
537 case ITFLAGGED:
538 rv = ((m->m_flag & MFLAGGED) != 0);
539 break;
540 case ITFROM:
541 rv = matchenvelope(m, "from", n->n_v);
542 break;
543 case ITHEADER:
544 rv = matchfield(m, n->n_v, n->n_w);
545 break;
546 case ITKEYWORD:
547 rv = ((m->m_flag & n->n_n) != 0);
548 break;
549 case ITLARGER:
550 rv = (m->m_xsize > n->n_n);
551 break;
552 case ITNEW:
553 rv = ((m->m_flag & (MNEW | MREAD)) == MNEW);
554 break;
555 case ITNOT:
556 rv = !itexecute(mp, m, c, n->n_x);
557 break;
558 case ITOLD:
559 rv = !(m->m_flag & MNEW);
560 break;
561 case ITON:
562 rv = (UICMP(z, m->m_time, >=, n->n_n) &&
563 UICMP(z, m->m_time, <, n->n_n + 86400));
564 break;
565 case ITOR:
566 rv = itexecute(mp, m, c, n->n_x) | itexecute(mp, m, c, n->n_y);
567 break;
568 case ITRECENT:
569 rv = ((m->m_flag & MNEW) != 0);
570 break;
571 case ITSEEN:
572 rv = ((m->m_flag & MREAD) != 0);
573 break;
574 case ITSENTBEFORE:
575 rv = UICMP(z, m->m_date, <, n->n_n);
576 break;
577 case ITSENTON:
578 rv = (UICMP(z, m->m_date, >=, n->n_n) &&
579 UICMP(z, m->m_date, <, n->n_n + 86400));
580 break;
581 case ITSENTSINCE:
582 rv = UICMP(z, m->m_date, >=, n->n_n);
583 break;
584 case ITSINCE:
585 rv = UICMP(z, m->m_time, >=, n->n_n);
586 break;
587 case ITSMALLER:
588 rv = UICMP(z, m->m_xsize, <, n->n_n);
589 break;
590 case ITSUBJECT:
591 rv = matchfield(m, "subject", n->n_v);
592 break;
593 case ITTEXT:
594 rv = matchmsg(m, n->n_v, 1);
595 break;
596 case ITTO:
597 rv = matchenvelope(m, "to", n->n_v);
598 break;
599 case ITUNANSWERED:
600 rv = !(m->m_flag & MANSWERED);
601 break;
602 case ITUNDELETED:
603 rv = !(m->m_flag & MDELETED);
604 break;
605 case ITUNDRAFT:
606 rv = !(m->m_flag & MDRAFTED);
607 break;
608 case ITUNFLAGGED:
609 rv = !(m->m_flag & MFLAGGED);
610 break;
611 case ITUNKEYWORD:
612 rv = !(m->m_flag & n->n_n);
613 break;
614 case ITUNSEEN:
615 rv = !(m->m_flag & MREAD);
616 break;
618 jleave:
619 NYD_LEAVE;
620 return rv;
623 static int
624 matchfield(struct message *m, char const *field, const void *what)
626 struct str in, out;
627 int i = 0;
628 NYD_ENTER;
630 if ((in.s = hfieldX(field, m)) == NULL)
631 goto jleave;
633 in.l = strlen(in.s);
634 mime_fromhdr(&in, &out, TD_ICONV);
636 #ifdef HAVE_REGEX
637 if (_it_need_regex)
638 i = (regexec(what, out.s, 0,NULL, 0) != REG_NOMATCH);
639 else
640 #endif
641 i = substr(out.s, what);
643 free(out.s);
644 jleave:
645 NYD_LEAVE;
646 return i;
649 static int
650 matchenvelope(struct message *m, char const *field, const void *what)
652 struct name *np;
653 char *cp;
654 int rv = 0;
655 NYD_ENTER;
657 if ((cp = hfieldX(field, m)) == NULL)
658 goto jleave;
660 for (np = lextract(cp, GFULL); np != NULL; np = np->n_flink) {
661 #ifdef HAVE_REGEX
662 if (_it_need_regex) {
663 if (regexec(what, np->n_name, 0,NULL, 0) == REG_NOMATCH &&
664 regexec(what, mkenvelope(np), 0,NULL, 0) == REG_NOMATCH)
665 continue;
666 } else
667 #endif
668 if (!substr(np->n_name, what) && !substr(mkenvelope(np), what))
669 continue;
670 rv = 1;
671 break;
674 jleave:
675 NYD_LEAVE;
676 return rv;
679 static char *
680 mkenvelope(struct name *np)
682 size_t epsize;
683 char *ep, *realnam = NULL, *sourceaddr = NULL, *localpart = NULL,
684 *domainpart = NULL, *cp, *rp, *xp, *ip;
685 struct str in, out;
686 int level = 0, hadphrase = 0;
687 NYD_ENTER;
689 in.s = np->n_fullname;
690 in.l = strlen(in.s);
691 mime_fromhdr(&in, &out, TD_ICONV);
692 rp = ip = ac_alloc(strlen(out.s) + 1);
693 for (cp = out.s; *cp; cp++) {
694 switch (*cp) {
695 case '"':
696 while (*cp) {
697 if (*++cp == '"')
698 break;
699 if (*cp == '\\' && cp[1])
700 cp++;
701 *rp++ = *cp;
703 break;
704 case '<':
705 while (cp > out.s && blankchar(cp[-1]))
706 cp--;
707 rp = ip;
708 xp = out.s;
709 if (xp < &cp[-1] && *xp == '"' && cp[-1] == '"') {
710 xp++;
711 cp--;
713 while (xp < cp)
714 *rp++ = *xp++;
715 hadphrase = 1;
716 goto jdone;
717 case '(':
718 if (level++)
719 goto jdfl;
720 if (hadphrase++ == 0)
721 rp = ip;
722 break;
723 case ')':
724 if (--level)
725 goto jdfl;
726 break;
727 case '\\':
728 if (level && cp[1])
729 cp++;
730 goto jdfl;
731 default:
732 jdfl:
733 *rp++ = *cp;
736 jdone:
737 *rp = '\0';
738 if (hadphrase)
739 realnam = ip;
740 free(out.s);
741 localpart = savestr(np->n_name);
742 if ((cp = strrchr(localpart, '@')) != NULL) {
743 *cp = '\0';
744 domainpart = &cp[1];
747 ep = salloc(epsize = strlen(np->n_fullname) * 2 + 40);
748 snprintf(ep, epsize, "(%s %s %s %s)",
749 realnam ? imap_quotestr(realnam) : "NIL",
750 sourceaddr ? imap_quotestr(sourceaddr) : "NIL",
751 localpart ? imap_quotestr(localpart) : "NIL",
752 domainpart ? imap_quotestr(domainpart) : "NIL");
753 ac_free(ip);
754 NYD_LEAVE;
755 return ep;
758 static int
759 matchmsg(struct message *m, const void *what, int withheader)
761 char *line = NULL;
762 size_t linesize, linelen, cnt;
763 FILE *fp;
764 int yes = 0;
765 NYD_ENTER;
767 if ((fp = Ftmp(NULL, "imasrch", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
768 NULL)
769 goto j_leave;
770 if (sendmp(m, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0)
771 goto jleave;
772 fflush(fp);
773 rewind(fp);
775 cnt = fsize(fp);
776 line = smalloc(linesize = LINESIZE);
777 linelen = 0;
779 if (!withheader)
780 while (fgetline(&line, &linesize, &cnt, &linelen, fp, 0))
781 if (*line == '\n')
782 break;
784 while (fgetline(&line, &linesize, &cnt, &linelen, fp, 0)) {
785 #ifdef HAVE_REGEX
786 if (_it_need_regex) {
787 if (regexec(what, line, 0,NULL, 0) == REG_NOMATCH)
788 continue;
789 } else
790 #endif
791 if (!substr(line, what))
792 continue;
793 yes = 1;
794 break;
797 jleave:
798 free(line);
799 Fclose(fp);
800 j_leave:
801 NYD_LEAVE;
802 return yes;
805 #define SURROUNDING 16
806 static char const *
807 around(char const *cp)
809 static char ab[2 * SURROUNDING +1];
811 size_t i;
812 NYD_ENTER;
814 for (i = 0; i < SURROUNDING && cp > _it_begin; ++i)
815 --cp;
816 for (i = 0; i < sizeof(ab) - 1; ++i)
817 ab[i] = *cp++;
818 ab[i] = '\0';
819 NYD_LEAVE;
820 return ab;
823 FL enum okay
824 imap_search(char const *spec, int f)
826 static char *lastspec;
828 char const *xp;
829 size_t i;
830 enum okay rv = STOP;
831 NYD_ENTER;
833 if (strcmp(spec, "()")) {
834 if (lastspec != NULL)
835 free(lastspec);
836 _it_need_regex = (spec[0] == '(' && spec[1] == '/');
837 i = strlen(spec);
838 lastspec = sbufdup(spec + _it_need_regex, i - _it_need_regex);
839 if (_it_need_regex)
840 lastspec[0] = '(';
841 } else if (lastspec == NULL) {
842 fprintf(stderr, tr(524, "No last SEARCH criteria available.\n"));
843 goto jleave;
845 spec =
846 _it_begin = lastspec;
848 /* Regular expression searches are always local */
849 _it_need_headers = FAL0;
850 if (!_it_need_regex) {
851 #ifdef HAVE_IMAP
852 if ((rv = imap_search1(spec, f) == OKAY))
853 goto jleave;
854 #endif
856 #ifndef HAVE_REGEX
857 else {
858 fprintf(stderr, tr(525, "No regular expression support for SEARCHes.\n"));
859 goto jleave;
861 #endif
863 if (itparse(spec, &xp, 0) == STOP)
864 goto jleave;
865 if (_it_tree == NULL) {
866 rv = OKAY;
867 goto jleave;
870 #ifdef HAVE_IMAP
871 if (mb.mb_type == MB_IMAP && _it_need_headers)
872 imap_getheaders(1, msgCount);
873 #endif
874 srelax_hold();
875 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
876 if (message[i].m_flag & MHIDDEN)
877 continue;
878 if (f == MDELETED || !(message[i].m_flag & MDELETED)) {
879 size_t j = (int)(i + 1);
880 if (itexecute(&mb, &message[i], j, _it_tree))
881 mark((int)j, f);
882 srelax();
885 srelax_rele();
887 rv = OKAY;
888 jleave:
889 #ifdef HAVE_REGEX
890 for (; _it_regex != NULL; _it_regex = _it_regex->re_next)
891 regfree(&_it_regex->re_regex);
892 _it_regex = NULL;
893 #endif
894 NYD_LEAVE;
895 return rv;
898 /* vim:set fenc=utf-8:s-it-mode */