nail.1: tweaks
[s-mailx.git] / imap-search.c
blob1c0367ea92e8768bf3c50509ebf1546da685890b
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 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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 uiz_t 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 uiz_t _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);
137 static time_t _imap_read_date(char const *cp);
138 static char * _imap_quotestr(char const *s);
139 static char * _imap_unquotestr(char const *s);
141 static bool_t matchfield(struct message *m, char const *field,
142 void const *what);
143 static int matchenvelope(struct message *m, char const *field,
144 void const *what);
145 static char * mkenvelope(struct name *np);
146 static char const * around(char const *cp);
148 static enum okay
149 itparse(char const *spec, char const **xp, int sub)
151 int level = 0;
152 struct itnode n, *z, *ittree;
153 enum okay rv;
154 NYD_ENTER;
156 _it_tree = NULL;
157 while ((rv = itscan(spec, xp)) == OKAY && _it_token != ITBAD &&
158 _it_token != ITEOD) {
159 ittree = _it_tree;
160 memset(&n, 0, sizeof n);
161 spec = *xp;
162 switch (_it_token) {
163 case ITBOL:
164 ++level;
165 continue;
166 case ITEOL:
167 if (--level == 0)
168 goto jleave;
169 if (level < 0) {
170 if (sub > 0) {
171 --(*xp);
172 goto jleave;
174 n_err(_("Excess in )\n"));
175 rv = STOP;
176 goto jleave;
178 continue;
179 case ITNOT:
180 /* <search-key> */
181 n.n_token = ITNOT;
182 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
183 goto jleave;
184 spec = *xp;
185 if ((n.n_x = _it_tree) == NULL) {
186 n_err(_("Criterion for NOT missing: >>> %s <<<\n"), around(*xp));
187 rv = STOP;
188 goto jleave;
190 _it_token = ITNOT;
191 break;
192 case ITOR:
193 /* <search-key1> <search-key2> */
194 n.n_token = ITOR;
195 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
196 goto jleave;
197 if ((n.n_x = _it_tree) == NULL) {
198 n_err(_("First criterion for OR missing: >>> %s <<<\n"),
199 around(*xp));
200 rv = STOP;
201 goto jleave;
203 spec = *xp;
204 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
205 goto jleave;
206 spec = *xp;
207 if ((n.n_y = _it_tree) == NULL) {
208 n_err(_("Second criterion for OR missing: >>> %s <<<\n"),
209 around(*xp));
210 rv = STOP;
211 goto jleave;
213 break;
214 default:
215 n.n_token = _it_token;
216 n.n_n = _it_number;
217 n.n_v = _it_args[0];
218 n.n_w = _it_args[1];
221 _it_tree = ittree;
222 if (_it_tree == NULL) {
223 _it_tree = salloc(sizeof *_it_tree);
224 *_it_tree = n;
225 } else {
226 z = _it_tree;
227 _it_tree = salloc(sizeof *_it_tree);
228 _it_tree->n_token = ITAND;
229 _it_tree->n_x = z;
230 _it_tree->n_y = salloc(sizeof *_it_tree->n_y);
231 *_it_tree->n_y = n;
233 if (sub && level == 0)
234 break;
236 jleave:
237 NYD_LEAVE;
238 return rv;
241 static enum okay
242 itscan(char const *spec, char const **xp)
244 int i, n;
245 enum okay rv = OKAY;
246 NYD_ENTER;
248 while (spacechar(*spec))
249 ++spec;
250 if (*spec == '(') {
251 *xp = &spec[1];
252 _it_token = ITBOL;
253 goto jleave;
255 if (*spec == ')') {
256 *xp = &spec[1];
257 _it_token = ITEOL;
258 goto jleave;
260 while (spacechar(*spec))
261 ++spec;
262 if (*spec == '\0') {
263 _it_token = ITEOD;
264 goto jleave;
267 #define __GO(C) ((C) != '\0' && (C) != '(' && (C) != ')' && !spacechar(C))
268 for (i = 0; _it_strings[i].s_string != NULL; ++i) {
269 n = strlen(_it_strings[i].s_string);
270 if (!ascncasecmp(spec, _it_strings[i].s_string, n) && !__GO(spec[n])) {
271 _it_token = _it_strings[i].s_token;
272 spec += n;
273 while (spacechar(*spec))
274 ++spec;
275 rv = itsplit(spec, xp);
276 goto jleave;
279 if (digitchar(*spec)) {
280 n_idec_uiz_cp(&_it_number, spec, 10, xp);
281 if (!__GO(**xp)) {
282 _it_token = ITSET;
283 goto jleave;
287 n_err(_("Bad SEARCH criterion: "));
288 for (i = 0; __GO(spec[i]); ++i)
290 n_err(_("%.*s: >>> %s <<<\n"), i, spec, around(*xp));
291 #undef __GO
293 _it_token = ITBAD;
294 rv = STOP;
295 jleave:
296 NYD_LEAVE;
297 return rv;
300 static enum okay
301 itsplit(char const *spec, char const **xp)
303 char const *cp;
304 time_t t;
305 enum okay rv;
306 NYD_ENTER;
308 switch (_it_token) {
309 case ITBCC:
310 case ITBODY:
311 case ITCC:
312 case ITFROM:
313 case ITSUBJECT:
314 case ITTEXT:
315 case ITTO:
316 /* <string> */
317 ++_it_need_headers;
318 rv = itstring(_it_args, spec, xp);
319 break;
320 case ITSENTBEFORE:
321 case ITSENTON:
322 case ITSENTSINCE:
323 ++_it_need_headers;
324 /*FALLTHRU*/
325 case ITBEFORE:
326 case ITON:
327 case ITSINCE:
328 /* <date> */
329 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
330 break;
331 if ((t = _imap_read_date(_it_args[0])) == (time_t)-1) {
332 n_err(_("Invalid date %s: >>> %s <<<\n"),
333 (char*)_it_args[0], around(*xp));
334 rv = STOP;
335 break;
337 _it_number = t;
338 rv = OKAY;
339 break;
340 case ITHEADER:
341 /* <field-name> <string> */
342 ++_it_need_headers;
343 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
344 break;
345 spec = *xp;
346 if ((rv = itstring(_it_args + 1, spec, xp)) != OKAY)
347 break;
348 break;
349 case ITKEYWORD:
350 case ITUNKEYWORD:
351 /* <flag> */ /* TODO use table->flag map search instead */
352 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
353 break;
354 if (!asccasecmp(_it_args[0], "\\Seen"))
355 _it_number = MREAD;
356 else if (!asccasecmp(_it_args[0], "\\Deleted"))
357 _it_number = MDELETED;
358 else if (!asccasecmp(_it_args[0], "\\Recent"))
359 _it_number = MNEW;
360 else if (!asccasecmp(_it_args[0], "\\Flagged"))
361 _it_number = MFLAGGED;
362 else if (!asccasecmp(_it_args[0], "\\Answered"))
363 _it_number = MANSWERED;
364 else if (!asccasecmp(_it_args[0], "\\Draft"))
365 _it_number = MDRAFT;
366 else
367 _it_number = 0;
368 break;
369 case ITLARGER:
370 case ITSMALLER:
371 /* <n> */
372 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
373 break;
374 else{
375 n_idec_uiz_cp(&_it_number, _it_args[0], 10, &cp);
377 if (spacechar(*cp) || *cp == '\0')
378 break;
379 n_err(_("Invalid size: >>> %s <<<\n"), around(*xp));
380 rv = STOP;
381 break;
382 case ITUID:
383 /* <message set> */
384 n_err(_("Searching for UIDs is not supported: >>> %s <<<\n"),
385 around(*xp));
386 rv = STOP;
387 break;
388 default:
389 *xp = spec;
390 rv = OKAY;
391 break;
393 NYD_LEAVE;
394 return rv;
397 static enum okay
398 itstring(void **tp, char const *spec, char const **xp) /* XXX lesser derefs */
400 int inquote = 0;
401 char *ap;
402 enum okay rv = STOP;
403 NYD_ENTER;
405 while (spacechar(*spec))
406 ++spec;
407 if (*spec == '\0' || *spec == '(' || *spec == ')') {
408 n_err(_("Missing string argument: >>> %s <<<\n"),
409 around(&(*xp)[spec - *xp]));
410 goto jleave;
412 ap = *tp = salloc(strlen(spec) +1);
413 *xp = spec;
414 do {
415 if (inquote && **xp == '\\')
416 *ap++ = *(*xp)++;
417 else if (**xp == '"')
418 inquote = !inquote;
419 else if (!inquote && (spacechar(**xp) || **xp == '(' || **xp == ')')) {
420 *ap++ = '\0';
421 break;
423 *ap++ = **xp;
424 } while (*(*xp)++);
426 *tp = _imap_unquotestr(*tp);
427 rv = OKAY;
428 jleave:
429 NYD_LEAVE;
430 return rv;
433 static int
434 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
436 struct search_expr se;
437 char *cp, *line = NULL; /* TODO line pool */
438 size_t linesize = 0;
439 FILE *ibuf;
440 int rv;
441 NYD_ENTER;
443 if (n == NULL) {
444 n_err(_("Internal error: Empty node in SEARCH tree\n"));
445 rv = 0;
446 goto jleave;
449 switch (n->n_token) {
450 case ITBEFORE:
451 case ITON:
452 case ITSINCE:
453 if (m->m_time == 0 && !(m->m_flag & MNOFROM) &&
454 (ibuf = setinput(mp, m, NEED_HEADER)) != NULL) {
455 if (readline_restart(ibuf, &line, &linesize, 0) > 0)
456 m->m_time = unixtime(line);
457 free(line);
459 break;
460 case ITSENTBEFORE:
461 case ITSENTON:
462 case ITSENTSINCE:
463 if (m->m_date == 0)
464 if ((cp = hfield1("date", m)) != NULL)
465 m->m_date = rfctime(cp);
466 break;
467 default:
468 break;
471 switch (n->n_token) {
472 default:
473 n_err(_("Internal SEARCH error: Lost token %d\n"), n->n_token);
474 rv = 0;
475 break;
476 case ITAND:
477 rv = itexecute(mp, m, c, n->n_x) & itexecute(mp, m, c, n->n_y);
478 break;
479 case ITSET:
480 rv = (c == n->n_n);
481 break;
482 case ITALL:
483 rv = 1;
484 break;
485 case ITANSWERED:
486 rv = ((m->m_flag & MANSWERED) != 0);
487 break;
488 case ITBCC:
489 rv = matchenvelope(m, "bcc", n->n_v);
490 break;
491 case ITBEFORE:
492 rv = UICMP(z, m->m_time, <, n->n_n);
493 break;
494 case ITBODY:
495 memset(&se, 0, sizeof se);
496 se.ss_field = "body";
497 se.ss_body = n->n_v;
498 rv = message_match(m, &se, FAL0);
499 break;
500 case ITCC:
501 rv = matchenvelope(m, "cc", n->n_v);
502 break;
503 case ITDELETED:
504 rv = ((m->m_flag & MDELETED) != 0);
505 break;
506 case ITDRAFT:
507 rv = ((m->m_flag & MDRAFTED) != 0);
508 break;
509 case ITFLAGGED:
510 rv = ((m->m_flag & MFLAGGED) != 0);
511 break;
512 case ITFROM:
513 rv = matchenvelope(m, "from", n->n_v);
514 break;
515 case ITHEADER:
516 rv = matchfield(m, n->n_v, n->n_w);
517 break;
518 case ITKEYWORD:
519 rv = ((m->m_flag & n->n_n) != 0);
520 break;
521 case ITLARGER:
522 rv = (m->m_xsize > n->n_n);
523 break;
524 case ITNEW:
525 rv = ((m->m_flag & (MNEW | MREAD)) == MNEW);
526 break;
527 case ITNOT:
528 rv = !itexecute(mp, m, c, n->n_x);
529 break;
530 case ITOLD:
531 rv = !(m->m_flag & MNEW);
532 break;
533 case ITON:
534 rv = (UICMP(z, m->m_time, >=, n->n_n) &&
535 UICMP(z, m->m_time, <, n->n_n + 86400));
536 break;
537 case ITOR:
538 rv = itexecute(mp, m, c, n->n_x) | itexecute(mp, m, c, n->n_y);
539 break;
540 case ITRECENT:
541 rv = ((m->m_flag & MNEW) != 0);
542 break;
543 case ITSEEN:
544 rv = ((m->m_flag & MREAD) != 0);
545 break;
546 case ITSENTBEFORE:
547 rv = UICMP(z, m->m_date, <, n->n_n);
548 break;
549 case ITSENTON:
550 rv = (UICMP(z, m->m_date, >=, n->n_n) &&
551 UICMP(z, m->m_date, <, n->n_n + 86400));
552 break;
553 case ITSENTSINCE:
554 rv = UICMP(z, m->m_date, >=, n->n_n);
555 break;
556 case ITSINCE:
557 rv = UICMP(z, m->m_time, >=, n->n_n);
558 break;
559 case ITSMALLER:
560 rv = UICMP(z, m->m_xsize, <, n->n_n);
561 break;
562 case ITSUBJECT:
563 rv = matchfield(m, "subject", n->n_v);
564 break;
565 case ITTEXT:
566 memset(&se, 0, sizeof se);
567 se.ss_field = "text";
568 se.ss_body = n->n_v;
569 rv = message_match(m, &se, TRU1);
570 break;
571 case ITTO:
572 rv = matchenvelope(m, "to", n->n_v);
573 break;
574 case ITUNANSWERED:
575 rv = !(m->m_flag & MANSWERED);
576 break;
577 case ITUNDELETED:
578 rv = !(m->m_flag & MDELETED);
579 break;
580 case ITUNDRAFT:
581 rv = !(m->m_flag & MDRAFTED);
582 break;
583 case ITUNFLAGGED:
584 rv = !(m->m_flag & MFLAGGED);
585 break;
586 case ITUNKEYWORD:
587 rv = !(m->m_flag & n->n_n);
588 break;
589 case ITUNSEEN:
590 rv = !(m->m_flag & MREAD);
591 break;
593 jleave:
594 NYD_LEAVE;
595 return rv;
598 static time_t
599 _imap_read_date(char const *cp)
601 time_t t, t2;
602 si32_t year, month, day, i, tzdiff;
603 struct tm *tmptr;
604 char const *xp, *yp;
605 NYD_ENTER;
607 if (*cp == '"')
608 ++cp;
609 n_idec_si32_cp(&day, cp, 10, &xp);
610 if (day <= 0 || day > 31 || *xp++ != '-')
611 goto jerr;
613 for (i = 0;;) {
614 if (!ascncasecmp(xp, n_month_names[i], 3))
615 break;
616 if (n_month_names[++i][0] == '\0')
617 goto jerr;
619 month = i + 1;
620 if (xp[3] != '-')
621 goto jerr;
622 n_idec_si32_cp(&year, &xp[4], 10, &yp);
623 if (year < 1970 || year > 2037 || PTRCMP(yp, !=, xp + 8))
624 goto jerr;
625 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
626 goto jerr;
627 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
628 goto jleave/*jerr*/;
629 if((t2 = mktime(gmtime(&t))) == (time_t)-1)
630 goto jerr;
631 tzdiff = t - t2;
632 if((tmptr = localtime(&t)) == NULL)
633 goto jerr;
634 if (tmptr->tm_isdst > 0)
635 tzdiff += 3600;
636 t -= tzdiff;
637 jleave:
638 NYD_LEAVE;
639 return t;
640 jerr:
641 t = (time_t)-1;
642 goto jleave;
645 static char *
646 _imap_quotestr(char const *s)
648 char *n, *np;
649 NYD2_ENTER;
651 np = n = salloc(2 * strlen(s) + 3);
652 *np++ = '"';
653 while (*s) {
654 if (*s == '"' || *s == '\\')
655 *np++ = '\\';
656 *np++ = *s++;
658 *np++ = '"';
659 *np = '\0';
660 NYD2_LEAVE;
661 return n;
664 static char *
665 _imap_unquotestr(char const *s)
667 char *n, *np;
668 NYD2_ENTER;
670 if (*s != '"') {
671 n = savestr(s);
672 goto jleave;
675 np = n = salloc(strlen(s) + 1);
676 while (*++s) {
677 if (*s == '\\')
678 s++;
679 else if (*s == '"')
680 break;
681 *np++ = *s;
683 *np = '\0';
684 jleave:
685 NYD2_LEAVE;
686 return n;
689 static bool_t
690 matchfield(struct message *m, char const *field, void const *what)
692 struct str in, out;
693 bool_t rv = FAL0;
694 NYD_ENTER;
696 if ((in.s = hfieldX(field, m)) == NULL)
697 goto jleave;
699 in.l = strlen(in.s);
700 mime_fromhdr(&in, &out, TD_ICONV);
701 rv = substr(out.s, what);
702 free(out.s);
703 jleave:
704 NYD_LEAVE;
705 return rv;
708 static int
709 matchenvelope(struct message *m, char const *field, void const *what)
711 struct name *np;
712 char *cp;
713 int rv = 0;
714 NYD_ENTER;
716 if ((cp = hfieldX(field, m)) == NULL)
717 goto jleave;
719 for (np = lextract(cp, GFULL); np != NULL; np = np->n_flink) {
720 if (!substr(np->n_name, what) && !substr(mkenvelope(np), what))
721 continue;
722 rv = 1;
723 break;
726 jleave:
727 NYD_LEAVE;
728 return rv;
731 static char *
732 mkenvelope(struct name *np)
734 size_t epsize;
735 char *ep, *realnam = NULL, /**sourceaddr = NULL,*/ *localpart,
736 *domainpart, *cp, *rp, *xp, *ip;
737 struct str in, out;
738 int level = 0;
739 bool_t hadphrase = FAL0;
740 NYD_ENTER;
742 in.s = np->n_fullname;
743 in.l = strlen(in.s);
744 mime_fromhdr(&in, &out, TD_ICONV);
745 rp = ip = ac_alloc(strlen(out.s) + 1);
746 for (cp = out.s; *cp; cp++) {
747 switch (*cp) {
748 case '"':
749 while (*cp) {
750 if (*++cp == '"')
751 break;
752 if (cp[0] == '\\' && cp[1] != '\0')
753 ++cp;
754 *rp++ = *cp;
756 break;
757 case '<':
758 while (cp > out.s && blankchar(cp[-1]))
759 --cp;
760 rp = ip;
761 xp = out.s;
762 if (PTRCMP(xp, <, cp - 1) && *xp == '"' && cp[-1] == '"') {
763 ++xp;
764 --cp;
766 while (xp < cp)
767 *rp++ = *xp++;
768 hadphrase = TRU1;
769 goto jdone;
770 case '(':
771 if (level++)
772 goto jdfl;
773 if (!hadphrase)
774 rp = ip;
775 hadphrase = TRU1;
776 break;
777 case ')':
778 if (--level)
779 goto jdfl;
780 break;
781 case '\\':
782 if (level && cp[1] != '\0')
783 cp++;
784 goto jdfl;
785 default:
786 jdfl:
787 *rp++ = *cp;
790 jdone:
791 *rp = '\0';
792 if (hadphrase)
793 realnam = ip;
794 free(out.s);
795 localpart = savestr(np->n_name);
796 if ((cp = strrchr(localpart, '@')) != NULL) {
797 *cp = '\0';
798 domainpart = cp + 1;
799 }else
800 domainpart = NULL;
802 ep = n_autorec_alloc(epsize = strlen(np->n_fullname) * 2 + 40);
803 snprintf(ep, epsize, "(%s %s %s %s)",
804 realnam ? _imap_quotestr(realnam) : "NIL",
805 /*sourceaddr ? _imap_quotestr(sourceaddr) :*/ "NIL",
806 _imap_quotestr(localpart),
807 domainpart ? _imap_quotestr(domainpart) : "NIL");
808 ac_free(ip);
809 NYD_LEAVE;
810 return ep;
813 #define SURROUNDING 16
814 static char const *
815 around(char const *cp)
817 static char ab[2 * SURROUNDING +1];
819 size_t i;
820 NYD_ENTER;
822 for (i = 0; i < SURROUNDING && cp > _it_begin; ++i)
823 --cp;
824 for (i = 0; i < sizeof(ab) -1; ++i)
825 ab[i] = *cp++;
826 ab[i] = '\0';
827 NYD_LEAVE;
828 return ab;
831 FL ssize_t
832 imap_search(char const *spec, int f)
834 static char *lastspec;
836 char const *xp;
837 size_t i;
838 ssize_t rv;
839 NYD_ENTER;
841 if (strcmp(spec, "()")) {
842 if (lastspec != NULL)
843 free(lastspec);
844 i = strlen(spec);
845 lastspec = sbufdup(spec, i);
846 } else if (lastspec == NULL) {
847 n_err(_("No last SEARCH criteria available\n"));
848 rv = -1;
849 goto jleave;
851 spec =
852 _it_begin = lastspec;
854 _it_need_headers = FAL0;
855 #ifdef HAVE_IMAP
856 if ((rv = imap_search1(spec, f) == OKAY))
857 goto jleave;
858 #endif
859 if (itparse(spec, &xp, 0) == STOP){
860 rv = -1;
861 goto jleave;
864 rv = 0;
866 if (_it_tree == NULL)
867 goto jleave;
869 #ifdef HAVE_IMAP
870 if (mb.mb_type == MB_IMAP && _it_need_headers)
871 imap_getheaders(1, msgCount);
872 #endif
873 srelax_hold();
874 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
875 if (message[i].m_flag & MHIDDEN)
876 continue;
877 if (f == MDELETED || !(message[i].m_flag & MDELETED)) {
878 size_t j = (int)(i + 1);
879 if (itexecute(&mb, message + i, j, _it_tree)){
880 mark((int)j, f);
881 ++rv;
883 srelax();
886 srelax_rele();
887 jleave:
888 NYD_LEAVE;
889 return rv;
891 #endif /* HAVE_IMAP_SEARCH */
893 /* s-it-mode */