cmd_tab.h: add the [I]nteractive bit to a few commands
[s-mailx.git] / imap_search.c
blob6f608035b58dc4a2d01a133c364c419edfe17bc9
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 const char *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, const char *field, const void *what);
147 static int matchenvelope(struct message *m, const char *field,
148 const void *what);
149 static char *mkenvelope(struct name *np);
150 static int matchmsg(struct message *m, const void *what, int withheader);
151 static const char *around(const char *cp);
153 FL enum okay
154 imap_search(const char *spec, int f)
156 static char *lastspec;
158 enum okay rv = STOP;
159 char const *xp;
160 size_t i;
162 if (strcmp(spec, "()")) {
163 if (lastspec != NULL)
164 free(lastspec);
165 _it_need_regex = (spec[0] == '(' && spec[1] == '/');
166 i = strlen(spec);
167 lastspec = sbufdup(spec + _it_need_regex, i - _it_need_regex);
168 if (_it_need_regex)
169 lastspec[0] = '(';
170 } else if (lastspec == NULL) {
171 fprintf(stderr, tr(524, "No last SEARCH criteria available.\n"));
172 goto jleave;
174 spec =
175 _it_begin = lastspec;
177 /* Regular expression searches are always local */
178 _it_need_headers = FAL0;
179 if (!_it_need_regex) {
180 #ifdef HAVE_IMAP
181 if ((rv = imap_search1(spec, f) == OKAY))
182 goto jleave;
183 #endif
185 #ifndef HAVE_REGEX
186 else {
187 fprintf(stderr, tr(525, "No regular expression support for SEARCHes.\n"));
188 goto jleave;
190 #endif
192 if (itparse(spec, &xp, 0) == STOP)
193 goto jleave;
194 if (_it_tree == NULL) {
195 rv = OKAY;
196 goto jleave;
199 #ifdef HAVE_IMAP
200 if (mb.mb_type == MB_IMAP && _it_need_headers)
201 imap_getheaders(1, msgCount);
202 #endif
203 srelax_hold();
204 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
205 if (message[i].m_flag & MHIDDEN)
206 continue;
207 if (f == MDELETED || !(message[i].m_flag & MDELETED)) {
208 size_t j = (int)(i + 1);
209 if (itexecute(&mb, &message[i], j, _it_tree))
210 mark((int)j, f);
211 srelax();
214 srelax_rele();
216 rv = OKAY;
217 jleave:
218 #ifdef HAVE_REGEX
219 for (; _it_regex != NULL; _it_regex = _it_regex->re_next)
220 regfree(&_it_regex->re_regex);
221 _it_regex = NULL;
222 #endif
223 return rv;
226 static enum okay
227 itparse(char const *spec, char const **xp, int sub)
229 int level = 0;
230 struct itnode n, *z, *ittree;
231 enum okay ok;
233 _it_tree = NULL;
234 while ((ok = itscan(spec, xp)) == OKAY && _it_token != ITBAD &&
235 _it_token != ITEOD) {
236 ittree = _it_tree;
237 memset(&n, 0, sizeof n);
238 spec = *xp;
239 switch (_it_token) {
240 case ITBOL:
241 level++;
242 continue;
243 case ITEOL:
244 if (--level == 0) {
245 return OKAY;
247 if (level < 0) {
248 if (sub > 0) {
249 (*xp)--;
250 return OKAY;
252 fprintf(stderr, "Excess in \")\".\n");
253 return STOP;
255 continue;
256 case ITNOT:
257 /* <search-key> */
258 n.n_token = ITNOT;
259 if (itparse(spec, xp, sub+1) == STOP)
260 return STOP;
261 spec = *xp;
262 if ((n.n_x = _it_tree) == NULL) {
263 fprintf(stderr,
264 "Criterion for NOT missing: >>> %s <<<\n",
265 around(*xp));
266 return STOP;
268 _it_token = ITNOT;
269 break;
270 case ITOR:
271 /* <search-key1> <search-key2> */
272 n.n_token = ITOR;
273 if (itparse(spec, xp, sub+1) == STOP)
274 return STOP;
275 if ((n.n_x = _it_tree) == NULL) {
276 fprintf(stderr, "First criterion for OR "
277 "missing: >>> %s <<<\n",
278 around(*xp));
279 return STOP;
281 spec = *xp;
282 if (itparse(spec, xp, sub+1) == STOP)
283 return STOP;
284 spec = *xp;
285 if ((n.n_y = _it_tree) == NULL) {
286 fprintf(stderr, "Second criterion for OR "
287 "missing: >>> %s <<<\n",
288 around(*xp));
289 return STOP;
291 break;
292 default:
293 n.n_token = _it_token;
294 n.n_n = _it_number;
295 n.n_v = _it_args[0];
296 n.n_w = _it_args[1];
298 _it_tree = ittree;
299 if (_it_tree == NULL) {
300 _it_tree = salloc(sizeof *_it_tree);
301 *_it_tree = n;
302 } else {
303 z = _it_tree;
304 _it_tree = salloc(sizeof *_it_tree);
305 _it_tree->n_token = ITAND;
306 _it_tree->n_x = z;
307 _it_tree->n_y = salloc(sizeof *_it_tree->n_y);
308 *_it_tree->n_y = n;
310 if (sub && level == 0)
311 break;
313 return ok;
316 static enum okay
317 itscan(char const *spec, char const **xp)
319 int i, n;
321 while (spacechar(*spec))
322 spec++;
323 if (*spec == '(') {
324 *xp = &spec[1];
325 _it_token = ITBOL;
326 return OKAY;
328 if (*spec == ')') {
329 *xp = &spec[1];
330 _it_token = ITEOL;
331 return OKAY;
333 while (spacechar(*spec))
334 spec++;
335 if (*spec == '\0') {
336 _it_token = ITEOD;
337 return OKAY;
339 for (i = 0; _it_strings[i].s_string; i++) {
340 n = strlen(_it_strings[i].s_string);
341 if (ascncasecmp(spec, _it_strings[i].s_string, n) == 0 &&
342 (spacechar(spec[n]) || spec[n] == '\0' ||
343 spec[n] == '(' || spec[n] == ')')) {
344 _it_token = _it_strings[i].s_token;
345 spec += n;
346 while (spacechar(*spec))
347 spec++;
348 return itsplit(spec, xp);
351 if (digitchar(*spec)) {
352 _it_number = strtoul(spec, UNCONST(xp), 10);
353 if (spacechar(**xp) || **xp == '\0' ||
354 **xp == '(' || **xp == ')') {
355 _it_token = ITSET;
356 return OKAY;
359 fprintf(stderr, "Bad SEARCH criterion \"");
360 while (*spec && !spacechar(*spec) && *spec != '(' && *spec != ')') {
361 putc(*spec&0377, stderr);
362 spec++;
364 fprintf(stderr, "\": >>> %s <<<\n", around(*xp));
365 _it_token = ITBAD;
366 return STOP;
369 static enum okay
370 itsplit(char const *spec, char const **xp)
372 enum okay rv;
373 char *cp;
374 time_t t;
376 switch (_it_token) {
377 case ITBCC:
378 case ITBODY:
379 case ITCC:
380 case ITFROM:
381 case ITSUBJECT:
382 case ITTEXT:
383 case ITTO:
384 /* <string> */
385 _it_need_headers++;
386 rv = itstring(&_it_args[0], spec, xp);
387 #ifdef HAVE_REGEX
388 if (rv == OKAY && _it_need_regex) {
389 _it_number = 0;
390 goto jregcomp;
392 #endif
393 break;
394 case ITSENTBEFORE:
395 case ITSENTON:
396 case ITSENTSINCE:
397 _it_need_headers++;
398 /*FALLTHRU*/
399 case ITBEFORE:
400 case ITON:
401 case ITSINCE:
402 /* <date> */
403 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
404 break;
405 if ((t = imap_read_date(_it_args[0])) == (time_t)-1) {
406 fprintf(stderr, "Invalid date \"%s\": >>> %s <<<\n",
407 (char*)_it_args[0], around(*xp));
408 rv = STOP;
409 break;
411 _it_number = t;
412 rv = OKAY;
413 break;
414 case ITHEADER:
415 /* <field-name> <string> */
416 _it_need_headers++;
417 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
418 break;
419 spec = *xp;
420 if ((rv = itstring(&_it_args[1], spec, xp)) != OKAY)
421 break;
422 #ifdef HAVE_REGEX
423 _it_number = 1;
424 jregcomp:
425 if (_it_need_regex) {
426 struct itregex *itre = salloc(sizeof *_it_regex);
427 itre->re_next = _it_regex;
428 _it_regex = itre;
430 cp = _it_args[_it_number];
431 _it_args[_it_number] = &itre->re_regex;
432 if (regcomp(&itre->re_regex, cp, REG_EXTENDED | REG_ICASE | REG_NOSUB)
433 != 0) {
434 fprintf(stderr, tr(526,
435 "Invalid regular expression \"%s\": >>> %s <<<\n"),
436 cp, around(*xp));
437 rv = STOP;
438 break;
441 #endif
442 break;
443 case ITKEYWORD:
444 case ITUNKEYWORD:
445 /* <flag> */
446 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
447 break;
448 if (asccasecmp(_it_args[0], "\\Seen") == 0)
449 _it_number = MREAD;
450 else if (asccasecmp(_it_args[0], "\\Deleted") == 0)
451 _it_number = MDELETED;
452 else if (asccasecmp(_it_args[0], "\\Recent") == 0)
453 _it_number = MNEW;
454 else if (asccasecmp(_it_args[0], "\\Flagged") == 0)
455 _it_number = MFLAGGED;
456 else if (asccasecmp(_it_args[0], "\\Answered") == 0)
457 _it_number = MANSWERED;
458 else if (asccasecmp(_it_args[0], "\\Draft") == 0)
459 _it_number = MDRAFT;
460 else
461 _it_number = 0;
462 break;
463 case ITLARGER:
464 case ITSMALLER:
465 /* <n> */
466 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
467 break;
468 _it_number = strtoul(_it_args[0], &cp, 10);
469 if (spacechar(*cp) || *cp == '\0')
470 break;
471 fprintf(stderr, "Invalid size: >>> %s <<<\n",
472 around(*xp));
473 rv = STOP;
474 break;
475 case ITUID:
476 /* <message set> */
477 fprintf(stderr,
478 "Searching for UIDs is not supported: >>> %s <<<\n",
479 around(*xp));
480 rv = STOP;
481 break;
482 default:
483 *xp = spec;
484 rv = OKAY;
485 break;
487 return rv;
490 static enum okay
491 itstring(void **tp, char const *spec, char const **xp)
493 int inquote = 0;
494 char *ap;
496 while (spacechar(*spec))
497 spec++;
498 if (*spec == '\0' || *spec == '(' || *spec == ')') {
499 fprintf(stderr, "Missing string argument: >>> %s <<<\n",
500 around(&(*xp)[spec - *xp]));
501 return STOP;
503 ap = *tp = salloc(strlen(spec) + 1);
504 *xp = spec;
505 do {
506 if (inquote && **xp == '\\')
507 *ap++ = *(*xp)++;
508 else if (**xp == '"')
509 inquote = !inquote;
510 else if (!inquote && (spacechar(**xp) ||
511 **xp == '(' || **xp == ')')) {
512 *ap++ = '\0';
513 break;
515 *ap++ = **xp;
516 } while (*(*xp)++);
518 *tp = imap_unquotestr(*tp);
519 return OKAY;
522 static int
523 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
525 char *cp, *line = NULL;
526 size_t linesize = 0;
527 FILE *ibuf;
529 if (n == NULL) {
530 fprintf(stderr, "Internal error: Empty node in SEARCH tree.\n");
531 return 0;
533 switch (n->n_token) {
534 case ITBEFORE:
535 case ITON:
536 case ITSINCE:
537 if (m->m_time == 0 && (m->m_flag&MNOFROM) == 0 &&
538 (ibuf = setinput(mp, m, NEED_HEADER)) != NULL) {
539 if (readline_restart(ibuf, &line, &linesize, 0) > 0)
540 m->m_time = unixtime(line);
541 free(line);
543 break;
544 case ITSENTBEFORE:
545 case ITSENTON:
546 case ITSENTSINCE:
547 if (m->m_date == 0)
548 if ((cp = hfield1("date", m)) != NULL)
549 m->m_date = rfctime(cp);
550 break;
551 default:
552 break;
554 switch (n->n_token) {
555 default:
556 fprintf(stderr, "Internal SEARCH error: Lost token %d\n",
557 n->n_token);
558 return 0;
559 case ITAND:
560 return itexecute(mp, m, c, n->n_x) &
561 itexecute(mp, m, c, n->n_y);
562 case ITSET:
563 return (unsigned long)c == n->n_n;
564 case ITALL:
565 return 1;
566 case ITANSWERED:
567 return (m->m_flag&MANSWERED) != 0;
568 case ITBCC:
569 return matchenvelope(m, "bcc", n->n_v);
570 case ITBEFORE:
571 return (unsigned long)m->m_time < n->n_n;
572 case ITBODY:
573 return matchmsg(m, n->n_v, 0);
574 case ITCC:
575 return matchenvelope(m, "cc", n->n_v);
576 case ITDELETED:
577 return (m->m_flag&MDELETED) != 0;
578 case ITDRAFT:
579 return (m->m_flag&MDRAFTED) != 0;
580 case ITFLAGGED:
581 return (m->m_flag&MFLAGGED) != 0;
582 case ITFROM:
583 return matchenvelope(m, "from", n->n_v);
584 case ITHEADER:
585 return matchfield(m, n->n_v, n->n_w);
586 case ITKEYWORD:
587 return (m->m_flag & n->n_n) != 0;
588 case ITLARGER:
589 return m->m_xsize > n->n_n;
590 case ITNEW:
591 return (m->m_flag&(MNEW|MREAD)) == MNEW;
592 case ITNOT:
593 return !itexecute(mp, m, c, n->n_x);
594 case ITOLD:
595 return (m->m_flag&MNEW) == 0;
596 case ITON:
597 return ((unsigned long)m->m_time >= n->n_n &&
598 (unsigned long)m->m_time < n->n_n + 86400);
599 case ITOR:
600 return itexecute(mp, m, c, n->n_x) |
601 itexecute(mp, m, c, n->n_y);
602 case ITRECENT:
603 return (m->m_flag&MNEW) != 0;
604 case ITSEEN:
605 return (m->m_flag&MREAD) != 0;
606 case ITSENTBEFORE:
607 return (unsigned long)m->m_date < n->n_n;
608 case ITSENTON:
609 return ((unsigned long)m->m_date >= n->n_n &&
610 (unsigned long)m->m_date < n->n_n + 86400);
611 case ITSENTSINCE:
612 return (unsigned long)m->m_date >= n->n_n;
613 case ITSINCE:
614 return (unsigned long)m->m_time >= n->n_n;
615 case ITSMALLER:
616 return (unsigned long)m->m_xsize < n->n_n;
617 case ITSUBJECT:
618 return matchfield(m, "subject", n->n_v);
619 case ITTEXT:
620 return matchmsg(m, n->n_v, 1);
621 case ITTO:
622 return matchenvelope(m, "to", n->n_v);
623 case ITUNANSWERED:
624 return (m->m_flag&MANSWERED) == 0;
625 case ITUNDELETED:
626 return (m->m_flag&MDELETED) == 0;
627 case ITUNDRAFT:
628 return (m->m_flag&MDRAFTED) == 0;
629 case ITUNFLAGGED:
630 return (m->m_flag&MFLAGGED) == 0;
631 case ITUNKEYWORD:
632 return (m->m_flag & n->n_n) == 0;
633 case ITUNSEEN:
634 return (m->m_flag&MREAD) == 0;
638 static int
639 matchfield(struct message *m, const char *field, const void *what)
641 struct str in, out;
642 int i = 0;
644 if ((in.s = hfieldX(field, m)) == NULL)
645 goto jleave;
647 in.l = strlen(in.s);
648 mime_fromhdr(&in, &out, TD_ICONV);
650 #ifdef HAVE_REGEX
651 if (_it_need_regex)
652 i = (regexec(what, out.s, 0,NULL, 0) != REG_NOMATCH);
653 else
654 #endif
655 i = substr(out.s, what);
657 free(out.s);
658 jleave:
659 return i;
662 static int
663 matchenvelope(struct message *m, const char *field, const void *what)
665 struct name *np;
666 char *cp;
667 int rv = 0;
669 if ((cp = hfieldX(field, m)) == NULL)
670 goto jleave;
672 for (np = lextract(cp, GFULL); np != NULL; np = np->n_flink) {
673 #ifdef HAVE_REGEX
674 if (_it_need_regex) {
675 if (regexec(what, np->n_name, 0,NULL, 0) == REG_NOMATCH &&
676 regexec(what, mkenvelope(np), 0,NULL, 0) == REG_NOMATCH)
677 continue;
678 } else
679 #endif
680 if (!substr(np->n_name, what) && !substr(mkenvelope(np), what))
681 continue;
682 rv = 1;
683 break;
686 jleave:
687 return rv;
690 static char *
691 mkenvelope(struct name *np)
693 size_t epsize;
694 char *ep;
695 char *realnam = NULL, *sourceaddr = NULL,
696 *localpart = NULL, *domainpart = NULL,
697 *cp, *rp, *xp, *ip;
698 struct str in, out;
699 int level = 0, hadphrase = 0;
701 in.s = np->n_fullname;
702 in.l = strlen(in.s);
703 mime_fromhdr(&in, &out, TD_ICONV);
704 rp = ip = ac_alloc(strlen(out.s) + 1);
705 for (cp = out.s; *cp; cp++) {
706 switch (*cp) {
707 case '"':
708 while (*cp) {
709 if (*++cp == '"')
710 break;
711 if (*cp == '\\' && cp[1])
712 cp++;
713 *rp++ = *cp;
715 break;
716 case '<':
717 while (cp > out.s && blankchar(cp[-1]&0377))
718 cp--;
719 rp = ip;
720 xp = out.s;
721 if (xp < &cp[-1] && *xp == '"' && cp[-1] == '"') {
722 xp++;
723 cp--;
725 while (xp < cp)
726 *rp++ = *xp++;
727 hadphrase = 1;
728 goto done;
729 case '(':
730 if (level++)
731 goto dfl;
732 if (hadphrase++ == 0)
733 rp = ip;
734 break;
735 case ')':
736 if (--level)
737 goto dfl;
738 break;
739 case '\\':
740 if (level && cp[1])
741 cp++;
742 goto dfl;
743 default:
744 dfl:
745 *rp++ = *cp;
748 done: *rp = '\0';
749 if (hadphrase)
750 realnam = ip;
751 free(out.s);
752 localpart = savestr(np->n_name);
753 if ((cp = strrchr(localpart, '@')) != NULL) {
754 *cp = '\0';
755 domainpart = &cp[1];
757 ep = salloc(epsize = strlen(np->n_fullname) * 2 + 40);
758 snprintf(ep, epsize, "(%s %s %s %s)",
759 realnam ? imap_quotestr(realnam) : "NIL",
760 sourceaddr ? imap_quotestr(sourceaddr) : "NIL",
761 localpart ? imap_quotestr(localpart) : "NIL",
762 domainpart ? imap_quotestr(domainpart) : "NIL");
763 ac_free(ip);
764 return ep;
767 static int
768 matchmsg(struct message *m, const void *what, int withheader)
770 char *tempFile, *line = NULL;
771 size_t linesize, linelen, cnt;
772 FILE *fp;
773 int yes = 0;
775 if ((fp = Ftemp(&tempFile, "Ra", "w+", 0600, 1)) == NULL)
776 goto j_leave;
777 rm(tempFile);
778 Ftfree(&tempFile);
779 if (sendmp(m, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0)
780 goto jleave;
781 fflush(fp);
782 rewind(fp);
784 cnt = fsize(fp);
785 line = smalloc(linesize = LINESIZE);
786 linelen = 0;
788 if (!withheader)
789 while (fgetline(&line, &linesize, &cnt, &linelen, fp, 0))
790 if (*line == '\n')
791 break;
793 while (fgetline(&line, &linesize, &cnt, &linelen, fp, 0)) {
794 #ifdef HAVE_REGEX
795 if (_it_need_regex) {
796 if (regexec(what, line, 0,NULL, 0) == REG_NOMATCH)
797 continue;
798 } else
799 #endif
800 if (!substr(line, what))
801 continue;
802 yes = 1;
803 break;
806 jleave:
807 free(line);
808 Fclose(fp);
809 j_leave:
810 return yes;
813 #define SURROUNDING 16
814 static const char *
815 around(const char *cp)
817 static char ab[2 * SURROUNDING +1];
819 size_t i;
821 for (i = 0; i < SURROUNDING && cp > _it_begin; ++i)
822 --cp;
823 for (i = 0; i < sizeof(ab) - 1; ++i)
824 ab[i] = *cp++;
825 ab[i] = '\0';
826 return ab;
829 /* vim:set fenc=utf-8:s-it-mode (TODO only partial true) */