Fix false resource release <-> double free (Bob Tennent)..
[s-mailx.git] / imap_search.c
blob213adbc84cc86f783b4532e3e2857d044f0e7e7d
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 - 2015 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.
41 #undef n_FILE
42 #define n_FILE imap_search
44 #ifndef HAVE_AMALGAMATION
45 # include "nail.h"
46 #endif
48 EMPTY_FILE()
49 #ifdef HAVE_IMAP_SEARCH
51 enum itoken {
52 ITBAD, ITEOD, ITBOL, ITEOL, ITAND, ITSET, ITALL, ITANSWERED,
53 ITBCC, ITBEFORE, ITBODY,
54 ITCC,
55 ITDELETED, ITDRAFT,
56 ITFLAGGED, ITFROM,
57 ITHEADER,
58 ITKEYWORD,
59 ITLARGER,
60 ITNEW, ITNOT,
61 ITOLD, ITON, ITOR,
62 ITRECENT,
63 ITSEEN, ITSENTBEFORE, ITSENTON, ITSENTSINCE, ITSINCE, ITSMALLER,
64 ITSUBJECT,
65 ITTEXT, ITTO,
66 ITUID, ITUNANSWERED, ITUNDELETED, ITUNDRAFT, ITUNFLAGGED, ITUNKEYWORD,
67 ITUNSEEN
70 struct itlex {
71 char const *s_string;
72 enum itoken s_token;
75 struct itnode {
76 enum itoken n_token;
77 unsigned long n_n;
78 void *n_v;
79 void *n_w;
80 struct itnode *n_x;
81 struct itnode *n_y;
84 static struct itlex const _it_strings[] = {
85 { "ALL", ITALL },
86 { "ANSWERED", ITANSWERED },
87 { "BCC", ITBCC },
88 { "BEFORE", ITBEFORE },
89 { "BODY", ITBODY },
90 { "CC", ITCC },
91 { "DELETED", ITDELETED },
92 { "DRAFT", ITDRAFT },
93 { "FLAGGED", ITFLAGGED },
94 { "FROM", ITFROM },
95 { "HEADER", ITHEADER },
96 { "KEYWORD", ITKEYWORD },
97 { "LARGER", ITLARGER },
98 { "NEW", ITNEW },
99 { "NOT", ITNOT },
100 { "OLD", ITOLD },
101 { "ON", ITON },
102 { "OR", ITOR },
103 { "RECENT", ITRECENT },
104 { "SEEN", ITSEEN },
105 { "SENTBEFORE", ITSENTBEFORE },
106 { "SENTON", ITSENTON },
107 { "SENTSINCE", ITSENTSINCE },
108 { "SINCE", ITSINCE },
109 { "SMALLER", ITSMALLER },
110 { "SUBJECT", ITSUBJECT },
111 { "TEXT", ITTEXT },
112 { "TO", ITTO },
113 { "UID", ITUID },
114 { "UNANSWERED", ITUNANSWERED },
115 { "UNDELETED", ITUNDELETED },
116 { "UNDRAFT", ITUNDRAFT },
117 { "UNFLAGGED", ITUNFLAGGED },
118 { "UNKEYWORD", ITUNKEYWORD },
119 { "UNSEEN", ITUNSEEN },
120 { NULL, ITBAD }
123 static struct itnode *_it_tree;
124 static char *_it_begin;
125 static enum itoken _it_token;
126 static unsigned long _it_number;
127 static void *_it_args[2];
128 static size_t _it_need_headers;
130 static enum okay itparse(char const *spec, char const **xp, int sub);
131 static enum okay itscan(char const *spec, char const **xp);
132 static enum okay itsplit(char const *spec, char const **xp);
133 static enum okay itstring(void **tp, char const *spec, char const **xp);
134 static int itexecute(struct mailbox *mp, struct message *m,
135 size_t c, struct itnode *n);
136 static time_t _read_imap_date(char const *cp);
137 static bool_t matchfield(struct message *m, char const *field,
138 void const *what);
139 static int matchenvelope(struct message *m, char const *field,
140 void const *what);
141 static char * mkenvelope(struct name *np);
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 #define __GO(C) ((C) != '\0' && (C) != '(' && (C) != ')' && !spacechar(C))
265 for (i = 0; _it_strings[i].s_string != NULL; ++i) {
266 n = strlen(_it_strings[i].s_string);
267 if (!ascncasecmp(spec, _it_strings[i].s_string, n) && !__GO(spec[n])) {
268 _it_token = _it_strings[i].s_token;
269 spec += n;
270 while (spacechar(*spec))
271 ++spec;
272 rv = itsplit(spec, xp);
273 goto jleave;
276 if (digitchar(*spec)) {
277 _it_number = strtoul(spec, UNCONST(xp), 10);
278 if (!__GO(**xp)) {
279 _it_token = ITSET;
280 goto jleave;
283 fprintf(stderr, "Bad SEARCH criterion \"");
284 while (__GO(*spec)) {
285 putc(*spec & 0377, stderr);
286 ++spec;
288 #undef __GO
290 fprintf(stderr, "\": >>> %s <<<\n", around(*xp));
291 _it_token = ITBAD;
292 rv = STOP;
293 jleave:
294 NYD_LEAVE;
295 return rv;
298 static enum okay
299 itsplit(char const *spec, char const **xp)
301 char *cp;
302 time_t t;
303 enum okay rv;
304 NYD_ENTER;
306 switch (_it_token) {
307 case ITBCC:
308 case ITBODY:
309 case ITCC:
310 case ITFROM:
311 case ITSUBJECT:
312 case ITTEXT:
313 case ITTO:
314 /* <string> */
315 ++_it_need_headers;
316 rv = itstring(_it_args, spec, xp);
317 break;
318 case ITSENTBEFORE:
319 case ITSENTON:
320 case ITSENTSINCE:
321 ++_it_need_headers;
322 /*FALLTHRU*/
323 case ITBEFORE:
324 case ITON:
325 case ITSINCE:
326 /* <date> */
327 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
328 break;
329 if ((t = _read_imap_date(_it_args[0])) == (time_t)-1) {
330 fprintf(stderr, "Invalid date \"%s\": >>> %s <<<\n",
331 (char*)_it_args[0], around(*xp));
332 rv = STOP;
333 break;
335 _it_number = t;
336 rv = OKAY;
337 break;
338 case ITHEADER:
339 /* <field-name> <string> */
340 ++_it_need_headers;
341 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
342 break;
343 spec = *xp;
344 if ((rv = itstring(_it_args + 1, spec, xp)) != OKAY)
345 break;
346 break;
347 case ITKEYWORD:
348 case ITUNKEYWORD:
349 /* <flag> */ /* TODO use table->flag map search instead */
350 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
351 break;
352 if (!asccasecmp(_it_args[0], "\\Seen"))
353 _it_number = MREAD;
354 else if (!asccasecmp(_it_args[0], "\\Deleted"))
355 _it_number = MDELETED;
356 else if (!asccasecmp(_it_args[0], "\\Recent"))
357 _it_number = MNEW;
358 else if (!asccasecmp(_it_args[0], "\\Flagged"))
359 _it_number = MFLAGGED;
360 else if (!asccasecmp(_it_args[0], "\\Answered"))
361 _it_number = MANSWERED;
362 else if (!asccasecmp(_it_args[0], "\\Draft"))
363 _it_number = MDRAFT;
364 else
365 _it_number = 0;
366 break;
367 case ITLARGER:
368 case ITSMALLER:
369 /* <n> */
370 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
371 break;
372 _it_number = strtoul(_it_args[0], &cp, 10);
373 if (spacechar(*cp) || *cp == '\0')
374 break;
375 fprintf(stderr, "Invalid size: >>> %s <<<\n", around(*xp));
376 rv = STOP;
377 break;
378 case ITUID:
379 /* <message set> */
380 fprintf(stderr,
381 "Searching for UIDs is not supported: >>> %s <<<\n", around(*xp));
382 rv = STOP;
383 break;
384 default:
385 *xp = spec;
386 rv = OKAY;
387 break;
389 NYD_LEAVE;
390 return rv;
393 static enum okay
394 itstring(void **tp, char const *spec, char const **xp) /* XXX lesser derefs */
396 int inquote = 0;
397 char *ap;
398 enum okay rv = STOP;
399 NYD_ENTER;
401 while (spacechar(*spec))
402 ++spec;
403 if (*spec == '\0' || *spec == '(' || *spec == ')') {
404 fprintf(stderr, "Missing string argument: >>> %s <<<\n",
405 around(&(*xp)[spec - *xp]));
406 goto jleave;
408 ap = *tp = salloc(strlen(spec) +1);
409 *xp = spec;
410 do {
411 if (inquote && **xp == '\\')
412 *ap++ = *(*xp)++;
413 else if (**xp == '"')
414 inquote = !inquote;
415 else if (!inquote && (spacechar(**xp) || **xp == '(' || **xp == ')')) {
416 *ap++ = '\0';
417 break;
419 *ap++ = **xp;
420 } while (*(*xp)++);
422 *tp = imap_unquotestr(*tp);
423 rv = OKAY;
424 jleave:
425 NYD_LEAVE;
426 return rv;
429 static int
430 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
432 struct search_expr se;
433 char *cp, *line = NULL; /* TODO line pool */
434 size_t linesize = 0;
435 FILE *ibuf;
436 int rv;
437 NYD_ENTER;
439 if (n == NULL) {
440 fprintf(stderr, "Internal error: Empty node in SEARCH tree.\n");
441 rv = 0;
442 goto jleave;
445 switch (n->n_token) {
446 case ITBEFORE:
447 case ITON:
448 case ITSINCE:
449 if (m->m_time == 0 && !(m->m_flag & MNOFROM) &&
450 (ibuf = setinput(mp, m, NEED_HEADER)) != NULL) {
451 if (readline_restart(ibuf, &line, &linesize, 0) > 0)
452 m->m_time = unixtime(line);
453 free(line);
455 break;
456 case ITSENTBEFORE:
457 case ITSENTON:
458 case ITSENTSINCE:
459 if (m->m_date == 0)
460 if ((cp = hfield1("date", m)) != NULL)
461 m->m_date = rfctime(cp);
462 break;
463 default:
464 break;
467 switch (n->n_token) {
468 default:
469 fprintf(stderr, "Internal SEARCH error: Lost token %d\n", n->n_token);
470 rv = 0;
471 break;
472 case ITAND:
473 rv = itexecute(mp, m, c, n->n_x) & itexecute(mp, m, c, n->n_y);
474 break;
475 case ITSET:
476 rv = UICMP(z, c, ==, n->n_n);
477 break;
478 case ITALL:
479 rv = 1;
480 break;
481 case ITANSWERED:
482 rv = ((m->m_flag & MANSWERED) != 0);
483 break;
484 case ITBCC:
485 rv = matchenvelope(m, "bcc", n->n_v);
486 break;
487 case ITBEFORE:
488 rv = UICMP(z, m->m_time, <, n->n_n);
489 break;
490 case ITBODY:
491 se.ss_sexpr = n->n_v;
492 se.ss_where = "body";
493 rv = message_match(m, &se, FAL0);
494 break;
495 case ITCC:
496 rv = matchenvelope(m, "cc", n->n_v);
497 break;
498 case ITDELETED:
499 rv = ((m->m_flag & MDELETED) != 0);
500 break;
501 case ITDRAFT:
502 rv = ((m->m_flag & MDRAFTED) != 0);
503 break;
504 case ITFLAGGED:
505 rv = ((m->m_flag & MFLAGGED) != 0);
506 break;
507 case ITFROM:
508 rv = matchenvelope(m, "from", n->n_v);
509 break;
510 case ITHEADER:
511 rv = matchfield(m, n->n_v, n->n_w);
512 break;
513 case ITKEYWORD:
514 rv = ((m->m_flag & n->n_n) != 0);
515 break;
516 case ITLARGER:
517 rv = (m->m_xsize > n->n_n);
518 break;
519 case ITNEW:
520 rv = ((m->m_flag & (MNEW | MREAD)) == MNEW);
521 break;
522 case ITNOT:
523 rv = !itexecute(mp, m, c, n->n_x);
524 break;
525 case ITOLD:
526 rv = !(m->m_flag & MNEW);
527 break;
528 case ITON:
529 rv = (UICMP(z, m->m_time, >=, n->n_n) &&
530 UICMP(z, m->m_time, <, n->n_n + 86400));
531 break;
532 case ITOR:
533 rv = itexecute(mp, m, c, n->n_x) | itexecute(mp, m, c, n->n_y);
534 break;
535 case ITRECENT:
536 rv = ((m->m_flag & MNEW) != 0);
537 break;
538 case ITSEEN:
539 rv = ((m->m_flag & MREAD) != 0);
540 break;
541 case ITSENTBEFORE:
542 rv = UICMP(z, m->m_date, <, n->n_n);
543 break;
544 case ITSENTON:
545 rv = (UICMP(z, m->m_date, >=, n->n_n) &&
546 UICMP(z, m->m_date, <, n->n_n + 86400));
547 break;
548 case ITSENTSINCE:
549 rv = UICMP(z, m->m_date, >=, n->n_n);
550 break;
551 case ITSINCE:
552 rv = UICMP(z, m->m_time, >=, n->n_n);
553 break;
554 case ITSMALLER:
555 rv = UICMP(z, m->m_xsize, <, n->n_n);
556 break;
557 case ITSUBJECT:
558 rv = matchfield(m, "subject", n->n_v);
559 break;
560 case ITTEXT:
561 se.ss_sexpr = n->n_v;
562 se.ss_where = "text";
563 rv = message_match(m, &se, TRU1);
564 break;
565 case ITTO:
566 rv = matchenvelope(m, "to", n->n_v);
567 break;
568 case ITUNANSWERED:
569 rv = !(m->m_flag & MANSWERED);
570 break;
571 case ITUNDELETED:
572 rv = !(m->m_flag & MDELETED);
573 break;
574 case ITUNDRAFT:
575 rv = !(m->m_flag & MDRAFTED);
576 break;
577 case ITUNFLAGGED:
578 rv = !(m->m_flag & MFLAGGED);
579 break;
580 case ITUNKEYWORD:
581 rv = !(m->m_flag & n->n_n);
582 break;
583 case ITUNSEEN:
584 rv = !(m->m_flag & MREAD);
585 break;
587 jleave:
588 NYD_LEAVE;
589 return rv;
592 static time_t
593 _read_imap_date(char const *cp)
595 time_t t = (time_t)-1;
596 int year, month, day, i, tzdiff;
597 struct tm *tmptr;
598 char *xp, *yp;
599 NYD_ENTER;
601 if (*cp == '"')
602 ++cp;
603 day = strtol(cp, &xp, 10);
604 if (day <= 0 || day > 31 || *xp++ != '-')
605 goto jleave;
607 for (i = 0;;) {
608 if (!ascncasecmp(xp, month_names[i], 3))
609 break;
610 if (month_names[++i][0] == '\0')
611 goto jleave;
613 month = i + 1;
614 if (xp[3] != '-')
615 goto jleave;
616 year = strtol(xp + 4, &yp, 10);
617 if (year < 1970 || year > 2037 || PTRCMP(yp, !=, xp + 8))
618 goto jleave;
619 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
620 goto jleave;
621 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
622 goto jleave;
623 tzdiff = t - mktime(gmtime(&t));
624 tmptr = localtime(&t);
625 if (tmptr->tm_isdst > 0)
626 tzdiff += 3600;
627 t -= tzdiff;
628 jleave:
629 NYD_LEAVE;
630 return t;
633 static bool_t
634 matchfield(struct message *m, char const *field, void const *what)
636 struct str in, out;
637 bool_t rv = FAL0;
638 NYD_ENTER;
640 if ((in.s = hfieldX(field, m)) == NULL)
641 goto jleave;
643 in.l = strlen(in.s);
644 mime_fromhdr(&in, &out, TD_ICONV);
645 rv = substr(out.s, what);
646 free(out.s);
647 jleave:
648 NYD_LEAVE;
649 return rv;
652 static int
653 matchenvelope(struct message *m, char const *field, void const *what)
655 struct name *np;
656 char *cp;
657 int rv = 0;
658 NYD_ENTER;
660 if ((cp = hfieldX(field, m)) == NULL)
661 goto jleave;
663 for (np = lextract(cp, GFULL); np != NULL; np = np->n_flink) {
664 if (!substr(np->n_name, what) && !substr(mkenvelope(np), what))
665 continue;
666 rv = 1;
667 break;
670 jleave:
671 NYD_LEAVE;
672 return rv;
675 static char *
676 mkenvelope(struct name *np)
678 size_t epsize;
679 char *ep, *realnam = NULL, *sourceaddr = NULL, *localpart = NULL,
680 *domainpart = NULL, *cp, *rp, *xp, *ip;
681 struct str in, out;
682 int level = 0;
683 bool_t hadphrase = FAL0;
684 NYD_ENTER;
686 in.s = np->n_fullname;
687 in.l = strlen(in.s);
688 mime_fromhdr(&in, &out, TD_ICONV);
689 rp = ip = ac_alloc(strlen(out.s) + 1);
690 for (cp = out.s; *cp; cp++) {
691 switch (*cp) {
692 case '"':
693 while (*cp) {
694 if (*++cp == '"')
695 break;
696 if (cp[0] == '\\' && cp[1] != '\0')
697 ++cp;
698 *rp++ = *cp;
700 break;
701 case '<':
702 while (cp > out.s && blankchar(cp[-1]))
703 --cp;
704 rp = ip;
705 xp = out.s;
706 if (PTRCMP(xp, <, cp - 1) && *xp == '"' && cp[-1] == '"') {
707 ++xp;
708 --cp;
710 while (xp < cp)
711 *rp++ = *xp++;
712 hadphrase = TRU1;
713 goto jdone;
714 case '(':
715 if (level++)
716 goto jdfl;
717 if (!hadphrase)
718 rp = ip;
719 hadphrase = TRU1;
720 break;
721 case ')':
722 if (--level)
723 goto jdfl;
724 break;
725 case '\\':
726 if (level && cp[1] != '\0')
727 cp++;
728 goto jdfl;
729 default:
730 jdfl:
731 *rp++ = *cp;
734 jdone:
735 *rp = '\0';
736 if (hadphrase)
737 realnam = ip;
738 free(out.s);
739 localpart = savestr(np->n_name);
740 if ((cp = strrchr(localpart, '@')) != NULL) {
741 *cp = '\0';
742 domainpart = cp + 1;
745 ep = salloc(epsize = strlen(np->n_fullname) * 2 + 40);
746 snprintf(ep, epsize, "(%s %s %s %s)",
747 realnam ? imap_quotestr(realnam) : "NIL",
748 sourceaddr ? imap_quotestr(sourceaddr) : "NIL",
749 localpart ? imap_quotestr(localpart) : "NIL",
750 domainpart ? imap_quotestr(domainpart) : "NIL");
751 ac_free(ip);
752 NYD_LEAVE;
753 return ep;
756 #define SURROUNDING 16
757 static char const *
758 around(char const *cp)
760 static char ab[2 * SURROUNDING +1];
762 size_t i;
763 NYD_ENTER;
765 for (i = 0; i < SURROUNDING && cp > _it_begin; ++i)
766 --cp;
767 for (i = 0; i < sizeof(ab) -1; ++i)
768 ab[i] = *cp++;
769 ab[i] = '\0';
770 NYD_LEAVE;
771 return ab;
774 FL enum okay
775 imap_search(char const *spec, int f)
777 static char *lastspec;
779 char const *xp;
780 size_t i;
781 enum okay rv = STOP;
782 NYD_ENTER;
784 if (strcmp(spec, "()")) {
785 if (lastspec != NULL)
786 free(lastspec);
787 i = strlen(spec);
788 lastspec = sbufdup(spec, i);
789 } else if (lastspec == NULL) {
790 fprintf(stderr, _("No last SEARCH criteria available.\n"));
791 goto jleave;
793 spec =
794 _it_begin = lastspec;
796 _it_need_headers = FAL0;
797 #ifdef HAVE_IMAP
798 if ((rv = imap_search1(spec, f) == OKAY))
799 goto jleave;
800 #endif
802 if (itparse(spec, &xp, 0) == STOP)
803 goto jleave;
804 if (_it_tree == NULL) {
805 rv = OKAY;
806 goto jleave;
809 #ifdef HAVE_IMAP
810 if (mb.mb_type == MB_IMAP && _it_need_headers)
811 imap_getheaders(1, msgCount);
812 #endif
813 srelax_hold();
814 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
815 if (message[i].m_flag & MHIDDEN)
816 continue;
817 if (f == MDELETED || !(message[i].m_flag & MDELETED)) {
818 size_t j = (int)(i + 1);
819 if (itexecute(&mb, message + i, j, _it_tree))
820 mark((int)j, f);
821 srelax();
824 srelax_rele();
826 rv = OKAY;
827 jleave:
828 NYD_LEAVE;
829 return rv;
831 #endif /* HAVE_IMAP_SEARCH */
833 /* s-it-mode */