substr(): return bool_t
[s-mailx.git] / imap_search.c
blob28ecf16ef113377eb9c22d5ae15b1cf3e3dfc0ed
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 EMPTY_FILE(imap_search)
47 #ifdef HAVE_IMAP_SEARCH
49 enum itoken {
50 ITBAD, ITEOD, ITBOL, ITEOL, ITAND, ITSET, ITALL, ITANSWERED,
51 ITBCC, ITBEFORE, ITBODY,
52 ITCC,
53 ITDELETED, ITDRAFT,
54 ITFLAGGED, ITFROM,
55 ITHEADER,
56 ITKEYWORD,
57 ITLARGER,
58 ITNEW, ITNOT,
59 ITOLD, ITON, ITOR,
60 ITRECENT,
61 ITSEEN, ITSENTBEFORE, ITSENTON, ITSENTSINCE, ITSINCE, ITSMALLER,
62 ITSUBJECT,
63 ITTEXT, ITTO,
64 ITUID, ITUNANSWERED, ITUNDELETED, ITUNDRAFT, ITUNFLAGGED, ITUNKEYWORD,
65 ITUNSEEN
68 struct itlex {
69 char const *s_string;
70 enum itoken s_token;
73 struct itnode {
74 enum itoken n_token;
75 unsigned long n_n;
76 void *n_v;
77 void *n_w;
78 struct itnode *n_x;
79 struct itnode *n_y;
82 static struct itlex const _it_strings[] = {
83 { "ALL", ITALL },
84 { "ANSWERED", ITANSWERED },
85 { "BCC", ITBCC },
86 { "BEFORE", ITBEFORE },
87 { "BODY", ITBODY },
88 { "CC", ITCC },
89 { "DELETED", ITDELETED },
90 { "DRAFT", ITDRAFT },
91 { "FLAGGED", ITFLAGGED },
92 { "FROM", ITFROM },
93 { "HEADER", ITHEADER },
94 { "KEYWORD", ITKEYWORD },
95 { "LARGER", ITLARGER },
96 { "NEW", ITNEW },
97 { "NOT", ITNOT },
98 { "OLD", ITOLD },
99 { "ON", ITON },
100 { "OR", ITOR },
101 { "RECENT", ITRECENT },
102 { "SEEN", ITSEEN },
103 { "SENTBEFORE", ITSENTBEFORE },
104 { "SENTON", ITSENTON },
105 { "SENTSINCE", ITSENTSINCE },
106 { "SINCE", ITSINCE },
107 { "SMALLER", ITSMALLER },
108 { "SUBJECT", ITSUBJECT },
109 { "TEXT", ITTEXT },
110 { "TO", ITTO },
111 { "UID", ITUID },
112 { "UNANSWERED", ITUNANSWERED },
113 { "UNDELETED", ITUNDELETED },
114 { "UNDRAFT", ITUNDRAFT },
115 { "UNFLAGGED", ITUNFLAGGED },
116 { "UNKEYWORD", ITUNKEYWORD },
117 { "UNSEEN", ITUNSEEN },
118 { NULL, ITBAD }
121 static struct itnode *_it_tree;
122 static char *_it_begin;
123 static enum itoken _it_token;
124 static unsigned long _it_number;
125 static void *_it_args[2];
126 static size_t _it_need_headers;
128 static enum okay itparse(char const *spec, char const **xp, int sub);
129 static enum okay itscan(char const *spec, char const **xp);
130 static enum okay itsplit(char const *spec, char const **xp);
131 static enum okay itstring(void **tp, char const *spec, char const **xp);
132 static int itexecute(struct mailbox *mp, struct message *m,
133 size_t c, struct itnode *n);
134 static time_t _read_imap_date(char const *cp);
135 static bool_t matchfield(struct message *m, char const *field,
136 const void *what);
137 static int matchenvelope(struct message *m, char const *field,
138 const void *what);
139 static char * mkenvelope(struct name *np);
140 static bool_t _matchmsg(struct message *m, const void *what,
141 int withheader);
142 static char const * around(char const *cp);
144 static enum okay
145 itparse(char const *spec, char const **xp, int sub)
147 int level = 0;
148 struct itnode n, *z, *ittree;
149 enum okay rv;
150 NYD_ENTER;
152 _it_tree = NULL;
153 while ((rv = itscan(spec, xp)) == OKAY && _it_token != ITBAD &&
154 _it_token != ITEOD) {
155 ittree = _it_tree;
156 memset(&n, 0, sizeof n);
157 spec = *xp;
158 switch (_it_token) {
159 case ITBOL:
160 ++level;
161 continue;
162 case ITEOL:
163 if (--level == 0)
164 goto jleave;
165 if (level < 0) {
166 if (sub > 0) {
167 --(*xp);
168 goto jleave;
170 fprintf(stderr, "Excess in \")\".\n");
171 rv = STOP;
172 goto jleave;
174 continue;
175 case ITNOT:
176 /* <search-key> */
177 n.n_token = ITNOT;
178 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
179 goto jleave;
180 spec = *xp;
181 if ((n.n_x = _it_tree) == NULL) {
182 fprintf(stderr, "Criterion for NOT missing: >>> %s <<<\n",
183 around(*xp));
184 rv = STOP;
185 goto jleave;
187 _it_token = ITNOT;
188 break;
189 case ITOR:
190 /* <search-key1> <search-key2> */
191 n.n_token = ITOR;
192 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
193 goto jleave;
194 if ((n.n_x = _it_tree) == NULL) {
195 fprintf(stderr, "First criterion for OR missing: >>> %s <<<\n",
196 around(*xp));
197 rv = STOP;
198 goto jleave;
200 spec = *xp;
201 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
202 goto jleave;
203 spec = *xp;
204 if ((n.n_y = _it_tree) == NULL) {
205 fprintf(stderr, "Second criterion for OR missing: >>> %s <<<\n",
206 around(*xp));
207 rv = STOP;
208 goto jleave;
210 break;
211 default:
212 n.n_token = _it_token;
213 n.n_n = _it_number;
214 n.n_v = _it_args[0];
215 n.n_w = _it_args[1];
218 _it_tree = ittree;
219 if (_it_tree == NULL) {
220 _it_tree = salloc(sizeof *_it_tree);
221 *_it_tree = n;
222 } else {
223 z = _it_tree;
224 _it_tree = salloc(sizeof *_it_tree);
225 _it_tree->n_token = ITAND;
226 _it_tree->n_x = z;
227 _it_tree->n_y = salloc(sizeof *_it_tree->n_y);
228 *_it_tree->n_y = n;
230 if (sub && level == 0)
231 break;
233 jleave:
234 NYD_LEAVE;
235 return rv;
238 static enum okay
239 itscan(char const *spec, char const **xp)
241 int i, n;
242 enum okay rv = OKAY;
243 NYD_ENTER;
245 while (spacechar(*spec))
246 ++spec;
247 if (*spec == '(') {
248 *xp = &spec[1];
249 _it_token = ITBOL;
250 goto jleave;
252 if (*spec == ')') {
253 *xp = &spec[1];
254 _it_token = ITEOL;
255 goto jleave;
257 while (spacechar(*spec))
258 ++spec;
259 if (*spec == '\0') {
260 _it_token = ITEOD;
261 goto jleave;
264 for (i = 0; _it_strings[i].s_string; i++) {
265 n = strlen(_it_strings[i].s_string);
266 if (ascncasecmp(spec, _it_strings[i].s_string, n) == 0 &&
267 (spacechar(spec[n]) || spec[n] == '\0' ||
268 spec[n] == '(' || spec[n] == ')')) {
269 _it_token = _it_strings[i].s_token;
270 spec += n;
271 while (spacechar(*spec))
272 ++spec;
273 rv = itsplit(spec, xp);
274 goto jleave;
277 if (digitchar(*spec)) {
278 _it_number = strtoul(spec, UNCONST(xp), 10);
279 if (spacechar(**xp) || **xp == '\0' || **xp == '(' || **xp == ')') {
280 _it_token = ITSET;
281 goto jleave;
284 fprintf(stderr, "Bad SEARCH criterion \"");
285 while (*spec && !spacechar(*spec) && *spec != '(' && *spec != ')') {
286 putc(*spec & 0377, stderr);
287 ++spec;
289 fprintf(stderr, "\": >>> %s <<<\n", around(*xp));
290 _it_token = ITBAD;
291 rv = STOP;
292 jleave:
293 NYD_LEAVE;
294 return rv;
297 static enum okay
298 itsplit(char const *spec, char const **xp)
300 char *cp;
301 time_t t;
302 enum okay rv;
303 NYD_ENTER;
305 switch (_it_token) {
306 case ITBCC:
307 case ITBODY:
308 case ITCC:
309 case ITFROM:
310 case ITSUBJECT:
311 case ITTEXT:
312 case ITTO:
313 /* <string> */
314 ++_it_need_headers;
315 rv = itstring(&_it_args[0], spec, xp);
316 break;
317 case ITSENTBEFORE:
318 case ITSENTON:
319 case ITSENTSINCE:
320 ++_it_need_headers;
321 /*FALLTHRU*/
322 case ITBEFORE:
323 case ITON:
324 case ITSINCE:
325 /* <date> */
326 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
327 break;
328 if ((t = _read_imap_date(_it_args[0])) == (time_t)-1) {
329 fprintf(stderr, "Invalid date \"%s\": >>> %s <<<\n",
330 (char*)_it_args[0], around(*xp));
331 rv = STOP;
332 break;
334 _it_number = t;
335 rv = OKAY;
336 break;
337 case ITHEADER:
338 /* <field-name> <string> */
339 ++_it_need_headers;
340 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
341 break;
342 spec = *xp;
343 if ((rv = itstring(&_it_args[1], spec, xp)) != OKAY)
344 break;
345 break;
346 case ITKEYWORD:
347 case ITUNKEYWORD:
348 /* <flag> */
349 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
350 break;
351 if (asccasecmp(_it_args[0], "\\Seen") == 0)
352 _it_number = MREAD;
353 else if (asccasecmp(_it_args[0], "\\Deleted") == 0)
354 _it_number = MDELETED;
355 else if (asccasecmp(_it_args[0], "\\Recent") == 0)
356 _it_number = MNEW;
357 else if (asccasecmp(_it_args[0], "\\Flagged") == 0)
358 _it_number = MFLAGGED;
359 else if (asccasecmp(_it_args[0], "\\Answered") == 0)
360 _it_number = MANSWERED;
361 else if (asccasecmp(_it_args[0], "\\Draft") == 0)
362 _it_number = MDRAFT;
363 else
364 _it_number = 0;
365 break;
366 case ITLARGER:
367 case ITSMALLER:
368 /* <n> */
369 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
370 break;
371 _it_number = strtoul(_it_args[0], &cp, 10);
372 if (spacechar(*cp) || *cp == '\0')
373 break;
374 fprintf(stderr, "Invalid size: >>> %s <<<\n", around(*xp));
375 rv = STOP;
376 break;
377 case ITUID:
378 /* <message set> */
379 fprintf(stderr,
380 "Searching for UIDs is not supported: >>> %s <<<\n", around(*xp));
381 rv = STOP;
382 break;
383 default:
384 *xp = spec;
385 rv = OKAY;
386 break;
388 NYD_LEAVE;
389 return rv;
392 static enum okay
393 itstring(void **tp, char const *spec, char const **xp)
395 int inquote = 0;
396 char *ap;
397 enum okay rv = STOP;
398 NYD_ENTER;
400 while (spacechar(*spec))
401 spec++;
402 if (*spec == '\0' || *spec == '(' || *spec == ')') {
403 fprintf(stderr, "Missing string argument: >>> %s <<<\n",
404 around(&(*xp)[spec - *xp]));
405 goto jleave;
407 ap = *tp = salloc(strlen(spec) + 1);
408 *xp = spec;
409 do {
410 if (inquote && **xp == '\\')
411 *ap++ = *(*xp)++;
412 else if (**xp == '"')
413 inquote = !inquote;
414 else if (!inquote && (spacechar(**xp) || **xp == '(' || **xp == ')')) {
415 *ap++ = '\0';
416 break;
418 *ap++ = **xp;
419 } while (*(*xp)++);
421 *tp = imap_unquotestr(*tp);
422 rv = OKAY;
423 jleave:
424 NYD_LEAVE;
425 return rv;
428 static int
429 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
431 char *cp, *line = NULL;
432 size_t linesize = 0;
433 FILE *ibuf;
434 int rv;
435 NYD_ENTER;
437 if (n == NULL) {
438 fprintf(stderr, "Internal error: Empty node in SEARCH tree.\n");
439 rv = 0;
440 goto jleave;
443 switch (n->n_token) {
444 case ITBEFORE:
445 case ITON:
446 case ITSINCE:
447 if (m->m_time == 0 && !(m->m_flag & MNOFROM) &&
448 (ibuf = setinput(mp, m, NEED_HEADER)) != NULL) {
449 if (readline_restart(ibuf, &line, &linesize, 0) > 0)
450 m->m_time = unixtime(line);
451 free(line);
453 break;
454 case ITSENTBEFORE:
455 case ITSENTON:
456 case ITSENTSINCE:
457 if (m->m_date == 0)
458 if ((cp = hfield1("date", m)) != NULL)
459 m->m_date = rfctime(cp);
460 break;
461 default:
462 break;
465 switch (n->n_token) {
466 default:
467 fprintf(stderr, "Internal SEARCH error: Lost token %d\n", n->n_token);
468 rv = 0;
469 break;
470 case ITAND:
471 rv = itexecute(mp, m, c, n->n_x) & itexecute(mp, m, c, n->n_y);
472 break;
473 case ITSET:
474 rv = UICMP(z, c, ==, n->n_n);
475 break;
476 case ITALL:
477 rv = 1;
478 break;
479 case ITANSWERED:
480 rv = ((m->m_flag & MANSWERED) != 0);
481 break;
482 case ITBCC:
483 rv = matchenvelope(m, "bcc", n->n_v);
484 break;
485 case ITBEFORE:
486 rv = UICMP(z, m->m_time, <, n->n_n);
487 break;
488 case ITBODY:
489 rv = _matchmsg(m, n->n_v, 0);
490 break;
491 case ITCC:
492 rv = matchenvelope(m, "cc", n->n_v);
493 break;
494 case ITDELETED:
495 rv = ((m->m_flag & MDELETED) != 0);
496 break;
497 case ITDRAFT:
498 rv = ((m->m_flag & MDRAFTED) != 0);
499 break;
500 case ITFLAGGED:
501 rv = ((m->m_flag & MFLAGGED) != 0);
502 break;
503 case ITFROM:
504 rv = matchenvelope(m, "from", n->n_v);
505 break;
506 case ITHEADER:
507 rv = matchfield(m, n->n_v, n->n_w);
508 break;
509 case ITKEYWORD:
510 rv = ((m->m_flag & n->n_n) != 0);
511 break;
512 case ITLARGER:
513 rv = (m->m_xsize > n->n_n);
514 break;
515 case ITNEW:
516 rv = ((m->m_flag & (MNEW | MREAD)) == MNEW);
517 break;
518 case ITNOT:
519 rv = !itexecute(mp, m, c, n->n_x);
520 break;
521 case ITOLD:
522 rv = !(m->m_flag & MNEW);
523 break;
524 case ITON:
525 rv = (UICMP(z, m->m_time, >=, n->n_n) &&
526 UICMP(z, m->m_time, <, n->n_n + 86400));
527 break;
528 case ITOR:
529 rv = itexecute(mp, m, c, n->n_x) | itexecute(mp, m, c, n->n_y);
530 break;
531 case ITRECENT:
532 rv = ((m->m_flag & MNEW) != 0);
533 break;
534 case ITSEEN:
535 rv = ((m->m_flag & MREAD) != 0);
536 break;
537 case ITSENTBEFORE:
538 rv = UICMP(z, m->m_date, <, n->n_n);
539 break;
540 case ITSENTON:
541 rv = (UICMP(z, m->m_date, >=, n->n_n) &&
542 UICMP(z, m->m_date, <, n->n_n + 86400));
543 break;
544 case ITSENTSINCE:
545 rv = UICMP(z, m->m_date, >=, n->n_n);
546 break;
547 case ITSINCE:
548 rv = UICMP(z, m->m_time, >=, n->n_n);
549 break;
550 case ITSMALLER:
551 rv = UICMP(z, m->m_xsize, <, n->n_n);
552 break;
553 case ITSUBJECT:
554 rv = matchfield(m, "subject", n->n_v);
555 break;
556 case ITTEXT:
557 rv = _matchmsg(m, n->n_v, 1);
558 break;
559 case ITTO:
560 rv = matchenvelope(m, "to", n->n_v);
561 break;
562 case ITUNANSWERED:
563 rv = !(m->m_flag & MANSWERED);
564 break;
565 case ITUNDELETED:
566 rv = !(m->m_flag & MDELETED);
567 break;
568 case ITUNDRAFT:
569 rv = !(m->m_flag & MDRAFTED);
570 break;
571 case ITUNFLAGGED:
572 rv = !(m->m_flag & MFLAGGED);
573 break;
574 case ITUNKEYWORD:
575 rv = !(m->m_flag & n->n_n);
576 break;
577 case ITUNSEEN:
578 rv = !(m->m_flag & MREAD);
579 break;
581 jleave:
582 NYD_LEAVE;
583 return rv;
586 static time_t
587 _read_imap_date(char const *cp)
589 time_t t = (time_t)-1;
590 int year, month, day, i, tzdiff;
591 struct tm *tmptr;
592 char *xp, *yp;
593 NYD_ENTER;
595 if (*cp == '"')
596 ++cp;
597 day = strtol(cp, &xp, 10);
598 if (day <= 0 || day > 31 || *xp++ != '-')
599 goto jleave;
601 for (i = 0;;) {
602 if (ascncasecmp(xp, month_names[i], 3) == 0)
603 break;
604 if (month_names[++i][0] == '\0')
605 goto jleave;
607 month = i+1;
608 if (xp[3] != '-')
609 goto jleave;
610 year = strtol(&xp[4], &yp, 10);
611 if (year < 1970 || year > 2037 || yp != &xp[8])
612 goto jleave;
613 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
614 goto jleave;
615 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
616 goto jleave;
617 tzdiff = t - mktime(gmtime(&t));
618 tmptr = localtime(&t);
619 if (tmptr->tm_isdst > 0)
620 tzdiff += 3600;
621 t -= tzdiff;
622 jleave:
623 NYD_LEAVE;
624 return t;
627 static bool_t
628 matchfield(struct message *m, char const *field, const void *what)
630 struct str in, out;
631 bool_t rv = FAL0;
632 NYD_ENTER;
634 if ((in.s = hfieldX(field, m)) == NULL)
635 goto jleave;
637 in.l = strlen(in.s);
638 mime_fromhdr(&in, &out, TD_ICONV);
639 rv = substr(out.s, what);
640 free(out.s);
641 jleave:
642 NYD_LEAVE;
643 return rv;
646 static int
647 matchenvelope(struct message *m, char const *field, const void *what)
649 struct name *np;
650 char *cp;
651 int rv = 0;
652 NYD_ENTER;
654 if ((cp = hfieldX(field, m)) == NULL)
655 goto jleave;
657 for (np = lextract(cp, GFULL); np != NULL; np = np->n_flink) {
658 if (!substr(np->n_name, what) && !substr(mkenvelope(np), what))
659 continue;
660 rv = 1;
661 break;
664 jleave:
665 NYD_LEAVE;
666 return rv;
669 static char *
670 mkenvelope(struct name *np)
672 size_t epsize;
673 char *ep, *realnam = NULL, *sourceaddr = NULL, *localpart = NULL,
674 *domainpart = NULL, *cp, *rp, *xp, *ip;
675 struct str in, out;
676 int level = 0, hadphrase = 0;
677 NYD_ENTER;
679 in.s = np->n_fullname;
680 in.l = strlen(in.s);
681 mime_fromhdr(&in, &out, TD_ICONV);
682 rp = ip = ac_alloc(strlen(out.s) + 1);
683 for (cp = out.s; *cp; cp++) {
684 switch (*cp) {
685 case '"':
686 while (*cp) {
687 if (*++cp == '"')
688 break;
689 if (*cp == '\\' && cp[1])
690 cp++;
691 *rp++ = *cp;
693 break;
694 case '<':
695 while (cp > out.s && blankchar(cp[-1]))
696 cp--;
697 rp = ip;
698 xp = out.s;
699 if (xp < &cp[-1] && *xp == '"' && cp[-1] == '"') {
700 xp++;
701 cp--;
703 while (xp < cp)
704 *rp++ = *xp++;
705 hadphrase = 1;
706 goto jdone;
707 case '(':
708 if (level++)
709 goto jdfl;
710 if (hadphrase++ == 0)
711 rp = ip;
712 break;
713 case ')':
714 if (--level)
715 goto jdfl;
716 break;
717 case '\\':
718 if (level && cp[1])
719 cp++;
720 goto jdfl;
721 default:
722 jdfl:
723 *rp++ = *cp;
726 jdone:
727 *rp = '\0';
728 if (hadphrase)
729 realnam = ip;
730 free(out.s);
731 localpart = savestr(np->n_name);
732 if ((cp = strrchr(localpart, '@')) != NULL) {
733 *cp = '\0';
734 domainpart = &cp[1];
737 ep = salloc(epsize = strlen(np->n_fullname) * 2 + 40);
738 snprintf(ep, epsize, "(%s %s %s %s)",
739 realnam ? imap_quotestr(realnam) : "NIL",
740 sourceaddr ? imap_quotestr(sourceaddr) : "NIL",
741 localpart ? imap_quotestr(localpart) : "NIL",
742 domainpart ? imap_quotestr(domainpart) : "NIL");
743 ac_free(ip);
744 NYD_LEAVE;
745 return ep;
748 static bool_t
749 _matchmsg(struct message *m, const void *what, int withheader)
751 char *line = NULL;
752 size_t linesize = 0, cnt;
753 FILE *fp;
754 bool_t rv = FAL0;
755 NYD_ENTER;
757 if ((fp = Ftmp(NULL, "imasrch", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
758 NULL)
759 goto j_leave;
760 if (sendmp(m, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0)
761 goto jleave;
762 fflush_rewind(fp);
764 cnt = fsize(fp);
766 if (!withheader)
767 while (fgetline(&line, &linesize, &cnt, NULL, fp, 0))
768 if (*line == '\n')
769 break;
771 while (fgetline(&line, &linesize, &cnt, NULL, fp, 0)) {
772 if (!substr(line, what))
773 continue;
774 rv = TRU1;
775 break;
778 jleave:
779 if (line != NULL)
780 free(line);
781 Fclose(fp);
782 j_leave:
783 NYD_LEAVE;
784 return rv;
787 #define SURROUNDING 16
788 static char const *
789 around(char const *cp)
791 static char ab[2 * SURROUNDING +1];
793 size_t i;
794 NYD_ENTER;
796 for (i = 0; i < SURROUNDING && cp > _it_begin; ++i)
797 --cp;
798 for (i = 0; i < sizeof(ab) - 1; ++i)
799 ab[i] = *cp++;
800 ab[i] = '\0';
801 NYD_LEAVE;
802 return ab;
805 FL enum okay
806 imap_search(char const *spec, int f)
808 static char *lastspec;
810 char const *xp;
811 size_t i;
812 enum okay rv = STOP;
813 NYD_ENTER;
815 if (strcmp(spec, "()")) {
816 if (lastspec != NULL)
817 free(lastspec);
818 i = strlen(spec);
819 lastspec = sbufdup(spec, i);
820 } else if (lastspec == NULL) {
821 fprintf(stderr, tr(524, "No last SEARCH criteria available.\n"));
822 goto jleave;
824 spec =
825 _it_begin = lastspec;
827 _it_need_headers = FAL0;
828 #ifdef HAVE_IMAP
829 if ((rv = imap_search1(spec, f) == OKAY))
830 goto jleave;
831 #endif
833 if (itparse(spec, &xp, 0) == STOP)
834 goto jleave;
835 if (_it_tree == NULL) {
836 rv = OKAY;
837 goto jleave;
840 #ifdef HAVE_IMAP
841 if (mb.mb_type == MB_IMAP && _it_need_headers)
842 imap_getheaders(1, msgCount);
843 #endif
844 srelax_hold();
845 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
846 if (message[i].m_flag & MHIDDEN)
847 continue;
848 if (f == MDELETED || !(message[i].m_flag & MDELETED)) {
849 size_t j = (int)(i + 1);
850 if (itexecute(&mb, &message[i], j, _it_tree))
851 mark((int)j, f);
852 srelax();
855 srelax_rele();
857 rv = OKAY;
858 jleave:
859 NYD_LEAVE;
860 return rv;
862 #endif /* HAVE_IMAP_SEARCH */
864 /* vim:set fenc=utf-8:s-it-mode */