INSTALL: update for v14.6
[s-mailx.git] / imap_search.c
blob7afb55b84ec0576e107f30a3d3a613db6c2ca4c0
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 char const * around(char const *cp);
142 static enum okay
143 itparse(char const *spec, char const **xp, int sub)
145 int level = 0;
146 struct itnode n, *z, *ittree;
147 enum okay rv;
148 NYD_ENTER;
150 _it_tree = NULL;
151 while ((rv = itscan(spec, xp)) == OKAY && _it_token != ITBAD &&
152 _it_token != ITEOD) {
153 ittree = _it_tree;
154 memset(&n, 0, sizeof n);
155 spec = *xp;
156 switch (_it_token) {
157 case ITBOL:
158 ++level;
159 continue;
160 case ITEOL:
161 if (--level == 0)
162 goto jleave;
163 if (level < 0) {
164 if (sub > 0) {
165 --(*xp);
166 goto jleave;
168 fprintf(stderr, "Excess in \")\".\n");
169 rv = STOP;
170 goto jleave;
172 continue;
173 case ITNOT:
174 /* <search-key> */
175 n.n_token = ITNOT;
176 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
177 goto jleave;
178 spec = *xp;
179 if ((n.n_x = _it_tree) == NULL) {
180 fprintf(stderr, "Criterion for NOT missing: >>> %s <<<\n",
181 around(*xp));
182 rv = STOP;
183 goto jleave;
185 _it_token = ITNOT;
186 break;
187 case ITOR:
188 /* <search-key1> <search-key2> */
189 n.n_token = ITOR;
190 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
191 goto jleave;
192 if ((n.n_x = _it_tree) == NULL) {
193 fprintf(stderr, "First criterion for OR missing: >>> %s <<<\n",
194 around(*xp));
195 rv = STOP;
196 goto jleave;
198 spec = *xp;
199 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
200 goto jleave;
201 spec = *xp;
202 if ((n.n_y = _it_tree) == NULL) {
203 fprintf(stderr, "Second criterion for OR missing: >>> %s <<<\n",
204 around(*xp));
205 rv = STOP;
206 goto jleave;
208 break;
209 default:
210 n.n_token = _it_token;
211 n.n_n = _it_number;
212 n.n_v = _it_args[0];
213 n.n_w = _it_args[1];
216 _it_tree = ittree;
217 if (_it_tree == NULL) {
218 _it_tree = salloc(sizeof *_it_tree);
219 *_it_tree = n;
220 } else {
221 z = _it_tree;
222 _it_tree = salloc(sizeof *_it_tree);
223 _it_tree->n_token = ITAND;
224 _it_tree->n_x = z;
225 _it_tree->n_y = salloc(sizeof *_it_tree->n_y);
226 *_it_tree->n_y = n;
228 if (sub && level == 0)
229 break;
231 jleave:
232 NYD_LEAVE;
233 return rv;
236 static enum okay
237 itscan(char const *spec, char const **xp)
239 int i, n;
240 enum okay rv = OKAY;
241 NYD_ENTER;
243 while (spacechar(*spec))
244 ++spec;
245 if (*spec == '(') {
246 *xp = &spec[1];
247 _it_token = ITBOL;
248 goto jleave;
250 if (*spec == ')') {
251 *xp = &spec[1];
252 _it_token = ITEOL;
253 goto jleave;
255 while (spacechar(*spec))
256 ++spec;
257 if (*spec == '\0') {
258 _it_token = ITEOD;
259 goto jleave;
262 for (i = 0; _it_strings[i].s_string; i++) {
263 n = strlen(_it_strings[i].s_string);
264 if (!ascncasecmp(spec, _it_strings[i].s_string, n) &&
265 (spacechar(spec[n]) || spec[n] == '\0' ||
266 spec[n] == '(' || spec[n] == ')')) {
267 _it_token = _it_strings[i].s_token;
268 spec += n;
269 while (spacechar(*spec))
270 ++spec;
271 rv = itsplit(spec, xp);
272 goto jleave;
275 if (digitchar(*spec)) {
276 _it_number = strtoul(spec, UNCONST(xp), 10);
277 if (spacechar(**xp) || **xp == '\0' || **xp == '(' || **xp == ')') {
278 _it_token = ITSET;
279 goto jleave;
282 fprintf(stderr, "Bad SEARCH criterion \"");
283 while (*spec && !spacechar(*spec) && *spec != '(' && *spec != ')') {
284 putc(*spec & 0377, stderr);
285 ++spec;
287 fprintf(stderr, "\": >>> %s <<<\n", around(*xp));
288 _it_token = ITBAD;
289 rv = STOP;
290 jleave:
291 NYD_LEAVE;
292 return rv;
295 static enum okay
296 itsplit(char const *spec, char const **xp)
298 char *cp;
299 time_t t;
300 enum okay rv;
301 NYD_ENTER;
303 switch (_it_token) {
304 case ITBCC:
305 case ITBODY:
306 case ITCC:
307 case ITFROM:
308 case ITSUBJECT:
309 case ITTEXT:
310 case ITTO:
311 /* <string> */
312 ++_it_need_headers;
313 rv = itstring(&_it_args[0], spec, xp);
314 break;
315 case ITSENTBEFORE:
316 case ITSENTON:
317 case ITSENTSINCE:
318 ++_it_need_headers;
319 /*FALLTHRU*/
320 case ITBEFORE:
321 case ITON:
322 case ITSINCE:
323 /* <date> */
324 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
325 break;
326 if ((t = _read_imap_date(_it_args[0])) == (time_t)-1) {
327 fprintf(stderr, "Invalid date \"%s\": >>> %s <<<\n",
328 (char*)_it_args[0], around(*xp));
329 rv = STOP;
330 break;
332 _it_number = t;
333 rv = OKAY;
334 break;
335 case ITHEADER:
336 /* <field-name> <string> */
337 ++_it_need_headers;
338 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
339 break;
340 spec = *xp;
341 if ((rv = itstring(&_it_args[1], spec, xp)) != OKAY)
342 break;
343 break;
344 case ITKEYWORD:
345 case ITUNKEYWORD:
346 /* <flag> */
347 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
348 break;
349 if (!asccasecmp(_it_args[0], "\\Seen"))
350 _it_number = MREAD;
351 else if (!asccasecmp(_it_args[0], "\\Deleted"))
352 _it_number = MDELETED;
353 else if (!asccasecmp(_it_args[0], "\\Recent"))
354 _it_number = MNEW;
355 else if (!asccasecmp(_it_args[0], "\\Flagged"))
356 _it_number = MFLAGGED;
357 else if (!asccasecmp(_it_args[0], "\\Answered"))
358 _it_number = MANSWERED;
359 else if (!asccasecmp(_it_args[0], "\\Draft"))
360 _it_number = MDRAFT;
361 else
362 _it_number = 0;
363 break;
364 case ITLARGER:
365 case ITSMALLER:
366 /* <n> */
367 if ((rv = itstring(&_it_args[0], spec, xp)) != OKAY)
368 break;
369 _it_number = strtoul(_it_args[0], &cp, 10);
370 if (spacechar(*cp) || *cp == '\0')
371 break;
372 fprintf(stderr, "Invalid size: >>> %s <<<\n", around(*xp));
373 rv = STOP;
374 break;
375 case ITUID:
376 /* <message set> */
377 fprintf(stderr,
378 "Searching for UIDs is not supported: >>> %s <<<\n", around(*xp));
379 rv = STOP;
380 break;
381 default:
382 *xp = spec;
383 rv = OKAY;
384 break;
386 NYD_LEAVE;
387 return rv;
390 static enum okay
391 itstring(void **tp, char const *spec, char const **xp)
393 int inquote = 0;
394 char *ap;
395 enum okay rv = STOP;
396 NYD_ENTER;
398 while (spacechar(*spec))
399 spec++;
400 if (*spec == '\0' || *spec == '(' || *spec == ')') {
401 fprintf(stderr, "Missing string argument: >>> %s <<<\n",
402 around(&(*xp)[spec - *xp]));
403 goto jleave;
405 ap = *tp = salloc(strlen(spec) + 1);
406 *xp = spec;
407 do {
408 if (inquote && **xp == '\\')
409 *ap++ = *(*xp)++;
410 else if (**xp == '"')
411 inquote = !inquote;
412 else if (!inquote && (spacechar(**xp) || **xp == '(' || **xp == ')')) {
413 *ap++ = '\0';
414 break;
416 *ap++ = **xp;
417 } while (*(*xp)++);
419 *tp = imap_unquotestr(*tp);
420 rv = OKAY;
421 jleave:
422 NYD_LEAVE;
423 return rv;
426 static int
427 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
429 struct search_expr se;
430 char *cp, *line = NULL;
431 size_t linesize = 0;
432 FILE *ibuf;
433 int rv;
434 NYD_ENTER;
436 if (n == NULL) {
437 fprintf(stderr, "Internal error: Empty node in SEARCH tree.\n");
438 rv = 0;
439 goto jleave;
442 switch (n->n_token) {
443 case ITBEFORE:
444 case ITON:
445 case ITSINCE:
446 if (m->m_time == 0 && !(m->m_flag & MNOFROM) &&
447 (ibuf = setinput(mp, m, NEED_HEADER)) != NULL) {
448 if (readline_restart(ibuf, &line, &linesize, 0) > 0)
449 m->m_time = unixtime(line);
450 free(line);
452 break;
453 case ITSENTBEFORE:
454 case ITSENTON:
455 case ITSENTSINCE:
456 if (m->m_date == 0)
457 if ((cp = hfield1("date", m)) != NULL)
458 m->m_date = rfctime(cp);
459 break;
460 default:
461 break;
464 switch (n->n_token) {
465 default:
466 fprintf(stderr, "Internal SEARCH error: Lost token %d\n", n->n_token);
467 rv = 0;
468 break;
469 case ITAND:
470 rv = itexecute(mp, m, c, n->n_x) & itexecute(mp, m, c, n->n_y);
471 break;
472 case ITSET:
473 rv = UICMP(z, c, ==, n->n_n);
474 break;
475 case ITALL:
476 rv = 1;
477 break;
478 case ITANSWERED:
479 rv = ((m->m_flag & MANSWERED) != 0);
480 break;
481 case ITBCC:
482 rv = matchenvelope(m, "bcc", n->n_v);
483 break;
484 case ITBEFORE:
485 rv = UICMP(z, m->m_time, <, n->n_n);
486 break;
487 case ITBODY:
488 se.ss_sexpr = n->n_v;
489 se.ss_where = "body";
490 rv = message_match(m, &se, FAL0);
491 break;
492 case ITCC:
493 rv = matchenvelope(m, "cc", n->n_v);
494 break;
495 case ITDELETED:
496 rv = ((m->m_flag & MDELETED) != 0);
497 break;
498 case ITDRAFT:
499 rv = ((m->m_flag & MDRAFTED) != 0);
500 break;
501 case ITFLAGGED:
502 rv = ((m->m_flag & MFLAGGED) != 0);
503 break;
504 case ITFROM:
505 rv = matchenvelope(m, "from", n->n_v);
506 break;
507 case ITHEADER:
508 rv = matchfield(m, n->n_v, n->n_w);
509 break;
510 case ITKEYWORD:
511 rv = ((m->m_flag & n->n_n) != 0);
512 break;
513 case ITLARGER:
514 rv = (m->m_xsize > n->n_n);
515 break;
516 case ITNEW:
517 rv = ((m->m_flag & (MNEW | MREAD)) == MNEW);
518 break;
519 case ITNOT:
520 rv = !itexecute(mp, m, c, n->n_x);
521 break;
522 case ITOLD:
523 rv = !(m->m_flag & MNEW);
524 break;
525 case ITON:
526 rv = (UICMP(z, m->m_time, >=, n->n_n) &&
527 UICMP(z, m->m_time, <, n->n_n + 86400));
528 break;
529 case ITOR:
530 rv = itexecute(mp, m, c, n->n_x) | itexecute(mp, m, c, n->n_y);
531 break;
532 case ITRECENT:
533 rv = ((m->m_flag & MNEW) != 0);
534 break;
535 case ITSEEN:
536 rv = ((m->m_flag & MREAD) != 0);
537 break;
538 case ITSENTBEFORE:
539 rv = UICMP(z, m->m_date, <, n->n_n);
540 break;
541 case ITSENTON:
542 rv = (UICMP(z, m->m_date, >=, n->n_n) &&
543 UICMP(z, m->m_date, <, n->n_n + 86400));
544 break;
545 case ITSENTSINCE:
546 rv = UICMP(z, m->m_date, >=, n->n_n);
547 break;
548 case ITSINCE:
549 rv = UICMP(z, m->m_time, >=, n->n_n);
550 break;
551 case ITSMALLER:
552 rv = UICMP(z, m->m_xsize, <, n->n_n);
553 break;
554 case ITSUBJECT:
555 rv = matchfield(m, "subject", n->n_v);
556 break;
557 case ITTEXT:
558 se.ss_sexpr = n->n_v;
559 se.ss_where = "text";
560 rv = message_match(m, &se, TRU1);
561 break;
562 case ITTO:
563 rv = matchenvelope(m, "to", n->n_v);
564 break;
565 case ITUNANSWERED:
566 rv = !(m->m_flag & MANSWERED);
567 break;
568 case ITUNDELETED:
569 rv = !(m->m_flag & MDELETED);
570 break;
571 case ITUNDRAFT:
572 rv = !(m->m_flag & MDRAFTED);
573 break;
574 case ITUNFLAGGED:
575 rv = !(m->m_flag & MFLAGGED);
576 break;
577 case ITUNKEYWORD:
578 rv = !(m->m_flag & n->n_n);
579 break;
580 case ITUNSEEN:
581 rv = !(m->m_flag & MREAD);
582 break;
584 jleave:
585 NYD_LEAVE;
586 return rv;
589 static time_t
590 _read_imap_date(char const *cp)
592 time_t t = (time_t)-1;
593 int year, month, day, i, tzdiff;
594 struct tm *tmptr;
595 char *xp, *yp;
596 NYD_ENTER;
598 if (*cp == '"')
599 ++cp;
600 day = strtol(cp, &xp, 10);
601 if (day <= 0 || day > 31 || *xp++ != '-')
602 goto jleave;
604 for (i = 0;;) {
605 if (!ascncasecmp(xp, month_names[i], 3))
606 break;
607 if (month_names[++i][0] == '\0')
608 goto jleave;
610 month = i+1;
611 if (xp[3] != '-')
612 goto jleave;
613 year = strtol(&xp[4], &yp, 10);
614 if (year < 1970 || year > 2037 || yp != &xp[8])
615 goto jleave;
616 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
617 goto jleave;
618 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
619 goto jleave;
620 tzdiff = t - mktime(gmtime(&t));
621 tmptr = localtime(&t);
622 if (tmptr->tm_isdst > 0)
623 tzdiff += 3600;
624 t -= tzdiff;
625 jleave:
626 NYD_LEAVE;
627 return t;
630 static bool_t
631 matchfield(struct message *m, char const *field, const void *what)
633 struct str in, out;
634 bool_t rv = FAL0;
635 NYD_ENTER;
637 if ((in.s = hfieldX(field, m)) == NULL)
638 goto jleave;
640 in.l = strlen(in.s);
641 mime_fromhdr(&in, &out, TD_ICONV);
642 rv = substr(out.s, what);
643 free(out.s);
644 jleave:
645 NYD_LEAVE;
646 return rv;
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 if (!substr(np->n_name, what) && !substr(mkenvelope(np), what))
662 continue;
663 rv = 1;
664 break;
667 jleave:
668 NYD_LEAVE;
669 return rv;
672 static char *
673 mkenvelope(struct name *np)
675 size_t epsize;
676 char *ep, *realnam = NULL, *sourceaddr = NULL, *localpart = NULL,
677 *domainpart = NULL, *cp, *rp, *xp, *ip;
678 struct str in, out;
679 int level = 0, hadphrase = 0;
680 NYD_ENTER;
682 in.s = np->n_fullname;
683 in.l = strlen(in.s);
684 mime_fromhdr(&in, &out, TD_ICONV);
685 rp = ip = ac_alloc(strlen(out.s) + 1);
686 for (cp = out.s; *cp; cp++) {
687 switch (*cp) {
688 case '"':
689 while (*cp) {
690 if (*++cp == '"')
691 break;
692 if (*cp == '\\' && cp[1])
693 cp++;
694 *rp++ = *cp;
696 break;
697 case '<':
698 while (cp > out.s && blankchar(cp[-1]))
699 cp--;
700 rp = ip;
701 xp = out.s;
702 if (xp < &cp[-1] && *xp == '"' && cp[-1] == '"') {
703 xp++;
704 cp--;
706 while (xp < cp)
707 *rp++ = *xp++;
708 hadphrase = 1;
709 goto jdone;
710 case '(':
711 if (level++)
712 goto jdfl;
713 if (hadphrase++ == 0)
714 rp = ip;
715 break;
716 case ')':
717 if (--level)
718 goto jdfl;
719 break;
720 case '\\':
721 if (level && cp[1])
722 cp++;
723 goto jdfl;
724 default:
725 jdfl:
726 *rp++ = *cp;
729 jdone:
730 *rp = '\0';
731 if (hadphrase)
732 realnam = ip;
733 free(out.s);
734 localpart = savestr(np->n_name);
735 if ((cp = strrchr(localpart, '@')) != NULL) {
736 *cp = '\0';
737 domainpart = &cp[1];
740 ep = salloc(epsize = strlen(np->n_fullname) * 2 + 40);
741 snprintf(ep, epsize, "(%s %s %s %s)",
742 realnam ? imap_quotestr(realnam) : "NIL",
743 sourceaddr ? imap_quotestr(sourceaddr) : "NIL",
744 localpart ? imap_quotestr(localpart) : "NIL",
745 domainpart ? imap_quotestr(domainpart) : "NIL");
746 ac_free(ip);
747 NYD_LEAVE;
748 return ep;
751 #define SURROUNDING 16
752 static char const *
753 around(char const *cp)
755 static char ab[2 * SURROUNDING +1];
757 size_t i;
758 NYD_ENTER;
760 for (i = 0; i < SURROUNDING && cp > _it_begin; ++i)
761 --cp;
762 for (i = 0; i < sizeof(ab) - 1; ++i)
763 ab[i] = *cp++;
764 ab[i] = '\0';
765 NYD_LEAVE;
766 return ab;
769 FL enum okay
770 imap_search(char const *spec, int f)
772 static char *lastspec;
774 char const *xp;
775 size_t i;
776 enum okay rv = STOP;
777 NYD_ENTER;
779 if (strcmp(spec, "()")) {
780 if (lastspec != NULL)
781 free(lastspec);
782 i = strlen(spec);
783 lastspec = sbufdup(spec, i);
784 } else if (lastspec == NULL) {
785 fprintf(stderr, tr(524, "No last SEARCH criteria available.\n"));
786 goto jleave;
788 spec =
789 _it_begin = lastspec;
791 _it_need_headers = FAL0;
792 #ifdef HAVE_IMAP
793 if ((rv = imap_search1(spec, f) == OKAY))
794 goto jleave;
795 #endif
797 if (itparse(spec, &xp, 0) == STOP)
798 goto jleave;
799 if (_it_tree == NULL) {
800 rv = OKAY;
801 goto jleave;
804 #ifdef HAVE_IMAP
805 if (mb.mb_type == MB_IMAP && _it_need_headers)
806 imap_getheaders(1, msgCount);
807 #endif
808 srelax_hold();
809 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
810 if (message[i].m_flag & MHIDDEN)
811 continue;
812 if (f == MDELETED || !(message[i].m_flag & MDELETED)) {
813 size_t j = (int)(i + 1);
814 if (itexecute(&mb, &message[i], j, _it_tree))
815 mark((int)j, f);
816 srelax();
819 srelax_rele();
821 rv = OKAY;
822 jleave:
823 NYD_LEAVE;
824 return rv;
826 #endif /* HAVE_IMAP_SEARCH */
828 /* vim:set fenc=utf-8:s-it-mode */