make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / imap-search.c
blob67e4d31692e7e2db6ff771b6d1756b02dd26ed96
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 * SPDX-License-Identifier: BSD-4-Clause
9 */
11 * Copyright (c) 2004
12 * Gunnar Ritter. All rights reserved.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by Gunnar Ritter
25 * and his contributors.
26 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
42 #undef n_FILE
43 #define n_FILE imap_search
45 #ifndef HAVE_AMALGAMATION
46 # include "nail.h"
47 #endif
49 EMPTY_FILE()
50 #ifdef HAVE_IMAP_SEARCH
52 enum itoken {
53 ITBAD, ITEOD, ITBOL, ITEOL, ITAND, ITSET, ITALL, ITANSWERED,
54 ITBCC, ITBEFORE, ITBODY,
55 ITCC,
56 ITDELETED, ITDRAFT,
57 ITFLAGGED, ITFROM,
58 ITHEADER,
59 ITKEYWORD,
60 ITLARGER,
61 ITNEW, ITNOT,
62 ITOLD, ITON, ITOR,
63 ITRECENT,
64 ITSEEN, ITSENTBEFORE, ITSENTON, ITSENTSINCE, ITSINCE, ITSMALLER,
65 ITSUBJECT,
66 ITTEXT, ITTO,
67 ITUID, ITUNANSWERED, ITUNDELETED, ITUNDRAFT, ITUNFLAGGED, ITUNKEYWORD,
68 ITUNSEEN
71 struct itlex {
72 char const *s_string;
73 enum itoken s_token;
76 struct itnode {
77 enum itoken n_token;
78 uiz_t n_n;
79 void *n_v;
80 void *n_w;
81 struct itnode *n_x;
82 struct itnode *n_y;
85 static struct itlex const _it_strings[] = {
86 { "ALL", ITALL },
87 { "ANSWERED", ITANSWERED },
88 { "BCC", ITBCC },
89 { "BEFORE", ITBEFORE },
90 { "BODY", ITBODY },
91 { "CC", ITCC },
92 { "DELETED", ITDELETED },
93 { "DRAFT", ITDRAFT },
94 { "FLAGGED", ITFLAGGED },
95 { "FROM", ITFROM },
96 { "HEADER", ITHEADER },
97 { "KEYWORD", ITKEYWORD },
98 { "LARGER", ITLARGER },
99 { "NEW", ITNEW },
100 { "NOT", ITNOT },
101 { "OLD", ITOLD },
102 { "ON", ITON },
103 { "OR", ITOR },
104 { "RECENT", ITRECENT },
105 { "SEEN", ITSEEN },
106 { "SENTBEFORE", ITSENTBEFORE },
107 { "SENTON", ITSENTON },
108 { "SENTSINCE", ITSENTSINCE },
109 { "SINCE", ITSINCE },
110 { "SMALLER", ITSMALLER },
111 { "SUBJECT", ITSUBJECT },
112 { "TEXT", ITTEXT },
113 { "TO", ITTO },
114 { "UID", ITUID },
115 { "UNANSWERED", ITUNANSWERED },
116 { "UNDELETED", ITUNDELETED },
117 { "UNDRAFT", ITUNDRAFT },
118 { "UNFLAGGED", ITUNFLAGGED },
119 { "UNKEYWORD", ITUNKEYWORD },
120 { "UNSEEN", ITUNSEEN },
121 { NULL, ITBAD }
124 static struct itnode *_it_tree;
125 static char *_it_begin;
126 static enum itoken _it_token;
127 static uiz_t _it_number;
128 static void *_it_args[2];
129 static size_t _it_need_headers;
131 static enum okay itparse(char const *spec, char const **xp, int sub);
132 static enum okay itscan(char const *spec, char const **xp);
133 static enum okay itsplit(char const *spec, char const **xp);
134 static enum okay itstring(void **tp, char const *spec, char const **xp);
135 static int itexecute(struct mailbox *mp, struct message *m,
136 size_t c, struct itnode *n);
138 static time_t _imap_read_date(char const *cp);
139 static char * _imap_quotestr(char const *s);
140 static char * _imap_unquotestr(char const *s);
142 static bool_t matchfield(struct message *m, char const *field,
143 void const *what);
144 static int matchenvelope(struct message *m, char const *field,
145 void const *what);
146 static char * mkenvelope(struct name *np);
147 static char const * around(char const *cp);
149 static enum okay
150 itparse(char const *spec, char const **xp, int sub)
152 int level = 0;
153 struct itnode n, *z, *ittree;
154 enum okay rv;
155 NYD_ENTER;
157 _it_tree = NULL;
158 while ((rv = itscan(spec, xp)) == OKAY && _it_token != ITBAD &&
159 _it_token != ITEOD) {
160 ittree = _it_tree;
161 memset(&n, 0, sizeof n);
162 spec = *xp;
163 switch (_it_token) {
164 case ITBOL:
165 ++level;
166 continue;
167 case ITEOL:
168 if (--level == 0)
169 goto jleave;
170 if (level < 0) {
171 if (sub > 0) {
172 --(*xp);
173 goto jleave;
175 n_err(_("Excess in )\n"));
176 rv = STOP;
177 goto jleave;
179 continue;
180 case ITNOT:
181 /* <search-key> */
182 n.n_token = ITNOT;
183 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
184 goto jleave;
185 spec = *xp;
186 if ((n.n_x = _it_tree) == NULL) {
187 n_err(_("Criterion for NOT missing: >>> %s <<<\n"), around(*xp));
188 rv = STOP;
189 goto jleave;
191 _it_token = ITNOT;
192 break;
193 case ITOR:
194 /* <search-key1> <search-key2> */
195 n.n_token = ITOR;
196 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
197 goto jleave;
198 if ((n.n_x = _it_tree) == NULL) {
199 n_err(_("First criterion for OR missing: >>> %s <<<\n"),
200 around(*xp));
201 rv = STOP;
202 goto jleave;
204 spec = *xp;
205 if ((rv = itparse(spec, xp, sub + 1)) == STOP)
206 goto jleave;
207 spec = *xp;
208 if ((n.n_y = _it_tree) == NULL) {
209 n_err(_("Second criterion for OR missing: >>> %s <<<\n"),
210 around(*xp));
211 rv = STOP;
212 goto jleave;
214 break;
215 default:
216 n.n_token = _it_token;
217 n.n_n = _it_number;
218 n.n_v = _it_args[0];
219 n.n_w = _it_args[1];
222 _it_tree = ittree;
223 if (_it_tree == NULL) {
224 _it_tree = n_autorec_alloc(sizeof *_it_tree);
225 *_it_tree = n;
226 } else {
227 z = _it_tree;
228 _it_tree = n_autorec_alloc(sizeof *_it_tree);
229 _it_tree->n_token = ITAND;
230 _it_tree->n_x = z;
231 _it_tree->n_y = n_autorec_alloc(sizeof *_it_tree->n_y);
232 *_it_tree->n_y = n;
234 if (sub && level == 0)
235 break;
237 jleave:
238 NYD_LEAVE;
239 return rv;
242 static enum okay
243 itscan(char const *spec, char const **xp)
245 int i, n;
246 enum okay rv = OKAY;
247 NYD_ENTER;
249 while (spacechar(*spec))
250 ++spec;
251 if (*spec == '(') {
252 *xp = &spec[1];
253 _it_token = ITBOL;
254 goto jleave;
256 if (*spec == ')') {
257 *xp = &spec[1];
258 _it_token = ITEOL;
259 goto jleave;
261 while (spacechar(*spec))
262 ++spec;
263 if (*spec == '\0') {
264 _it_token = ITEOD;
265 goto jleave;
268 #define __GO(C) ((C) != '\0' && (C) != '(' && (C) != ')' && !spacechar(C))
269 for (i = 0; _it_strings[i].s_string != NULL; ++i) {
270 n = strlen(_it_strings[i].s_string);
271 if (!ascncasecmp(spec, _it_strings[i].s_string, n) && !__GO(spec[n])) {
272 _it_token = _it_strings[i].s_token;
273 spec += n;
274 while (spacechar(*spec))
275 ++spec;
276 rv = itsplit(spec, xp);
277 goto jleave;
280 if (digitchar(*spec)) {
281 n_idec_uiz_cp(&_it_number, spec, 10, xp);
282 if (!__GO(**xp)) {
283 _it_token = ITSET;
284 goto jleave;
288 n_err(_("Bad SEARCH criterion: "));
289 for (i = 0; __GO(spec[i]); ++i)
291 n_err(_("%.*s: >>> %s <<<\n"), i, spec, around(*xp));
292 #undef __GO
294 _it_token = ITBAD;
295 rv = STOP;
296 jleave:
297 NYD_LEAVE;
298 return rv;
301 static enum okay
302 itsplit(char const *spec, char const **xp)
304 char const *cp;
305 time_t t;
306 enum okay rv;
307 NYD_ENTER;
309 switch (_it_token) {
310 case ITBCC:
311 case ITBODY:
312 case ITCC:
313 case ITFROM:
314 case ITSUBJECT:
315 case ITTEXT:
316 case ITTO:
317 /* <string> */
318 ++_it_need_headers;
319 rv = itstring(_it_args, spec, xp);
320 break;
321 case ITSENTBEFORE:
322 case ITSENTON:
323 case ITSENTSINCE:
324 ++_it_need_headers;
325 /*FALLTHRU*/
326 case ITBEFORE:
327 case ITON:
328 case ITSINCE:
329 /* <date> */
330 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
331 break;
332 if ((t = _imap_read_date(_it_args[0])) == (time_t)-1) {
333 n_err(_("Invalid date %s: >>> %s <<<\n"),
334 (char*)_it_args[0], around(*xp));
335 rv = STOP;
336 break;
338 _it_number = t;
339 rv = OKAY;
340 break;
341 case ITHEADER:
342 /* <field-name> <string> */
343 ++_it_need_headers;
344 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
345 break;
346 spec = *xp;
347 if ((rv = itstring(_it_args + 1, spec, xp)) != OKAY)
348 break;
349 break;
350 case ITKEYWORD:
351 case ITUNKEYWORD:
352 /* <flag> */ /* TODO use table->flag map search instead */
353 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
354 break;
355 if (!asccasecmp(_it_args[0], "\\Seen"))
356 _it_number = MREAD;
357 else if (!asccasecmp(_it_args[0], "\\Deleted"))
358 _it_number = MDELETED;
359 else if (!asccasecmp(_it_args[0], "\\Recent"))
360 _it_number = MNEW;
361 else if (!asccasecmp(_it_args[0], "\\Flagged"))
362 _it_number = MFLAGGED;
363 else if (!asccasecmp(_it_args[0], "\\Answered"))
364 _it_number = MANSWERED;
365 else if (!asccasecmp(_it_args[0], "\\Draft"))
366 _it_number = MDRAFT;
367 else
368 _it_number = 0;
369 break;
370 case ITLARGER:
371 case ITSMALLER:
372 /* <n> */
373 if ((rv = itstring(_it_args, spec, xp)) != OKAY)
374 break;
375 else{
376 n_idec_uiz_cp(&_it_number, _it_args[0], 10, &cp);
378 if (spacechar(*cp) || *cp == '\0')
379 break;
380 n_err(_("Invalid size: >>> %s <<<\n"), around(*xp));
381 rv = STOP;
382 break;
383 case ITUID:
384 /* <message set> */
385 n_err(_("Searching for UIDs is not supported: >>> %s <<<\n"),
386 around(*xp));
387 rv = STOP;
388 break;
389 default:
390 *xp = spec;
391 rv = OKAY;
392 break;
394 NYD_LEAVE;
395 return rv;
398 static enum okay
399 itstring(void **tp, char const *spec, char const **xp) /* XXX lesser derefs */
401 int inquote = 0;
402 char *ap;
403 enum okay rv = STOP;
404 NYD_ENTER;
406 while (spacechar(*spec))
407 ++spec;
408 if (*spec == '\0' || *spec == '(' || *spec == ')') {
409 n_err(_("Missing string argument: >>> %s <<<\n"),
410 around(&(*xp)[spec - *xp]));
411 goto jleave;
413 ap = *tp = n_autorec_alloc(strlen(spec) +1);
414 *xp = spec;
415 do {
416 if (inquote && **xp == '\\')
417 *ap++ = *(*xp)++;
418 else if (**xp == '"')
419 inquote = !inquote;
420 else if (!inquote && (spacechar(**xp) || **xp == '(' || **xp == ')')) {
421 *ap++ = '\0';
422 break;
424 *ap++ = **xp;
425 } while (*(*xp)++);
427 *tp = _imap_unquotestr(*tp);
428 rv = OKAY;
429 jleave:
430 NYD_LEAVE;
431 return rv;
434 static int
435 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
437 struct search_expr se;
438 char *cp, *line = NULL; /* TODO line pool */
439 size_t linesize = 0;
440 FILE *ibuf;
441 int rv;
442 NYD_ENTER;
444 if (n == NULL) {
445 n_err(_("Internal error: Empty node in SEARCH tree\n"));
446 rv = 0;
447 goto jleave;
450 switch (n->n_token) {
451 case ITBEFORE:
452 case ITON:
453 case ITSINCE:
454 if (m->m_time == 0 && !(m->m_flag & MNOFROM) &&
455 (ibuf = setinput(mp, m, NEED_HEADER)) != NULL) {
456 if (readline_restart(ibuf, &line, &linesize, 0) > 0)
457 m->m_time = unixtime(line);
458 n_free(line);
460 break;
461 case ITSENTBEFORE:
462 case ITSENTON:
463 case ITSENTSINCE:
464 if (m->m_date == 0)
465 if ((cp = hfield1("date", m)) != NULL)
466 m->m_date = rfctime(cp);
467 break;
468 default:
469 break;
472 switch (n->n_token) {
473 default:
474 n_err(_("Internal SEARCH error: Lost token %d\n"), n->n_token);
475 rv = 0;
476 break;
477 case ITAND:
478 rv = itexecute(mp, m, c, n->n_x) & itexecute(mp, m, c, n->n_y);
479 break;
480 case ITSET:
481 rv = (c == n->n_n);
482 break;
483 case ITALL:
484 rv = 1;
485 break;
486 case ITANSWERED:
487 rv = ((m->m_flag & MANSWERED) != 0);
488 break;
489 case ITBCC:
490 rv = matchenvelope(m, "bcc", n->n_v);
491 break;
492 case ITBEFORE:
493 rv = UICMP(z, m->m_time, <, n->n_n);
494 break;
495 case ITBODY:
496 memset(&se, 0, sizeof se);
497 se.ss_field = "body";
498 se.ss_body = n->n_v;
499 rv = message_match(m, &se, FAL0);
500 break;
501 case ITCC:
502 rv = matchenvelope(m, "cc", n->n_v);
503 break;
504 case ITDELETED:
505 rv = ((m->m_flag & MDELETED) != 0);
506 break;
507 case ITDRAFT:
508 rv = ((m->m_flag & MDRAFTED) != 0);
509 break;
510 case ITFLAGGED:
511 rv = ((m->m_flag & MFLAGGED) != 0);
512 break;
513 case ITFROM:
514 rv = matchenvelope(m, "from", n->n_v);
515 break;
516 case ITHEADER:
517 rv = matchfield(m, n->n_v, n->n_w);
518 break;
519 case ITKEYWORD:
520 rv = ((m->m_flag & n->n_n) != 0);
521 break;
522 case ITLARGER:
523 rv = (m->m_xsize > n->n_n);
524 break;
525 case ITNEW:
526 rv = ((m->m_flag & (MNEW | MREAD)) == MNEW);
527 break;
528 case ITNOT:
529 rv = !itexecute(mp, m, c, n->n_x);
530 break;
531 case ITOLD:
532 rv = !(m->m_flag & MNEW);
533 break;
534 case ITON:
535 rv = (UICMP(z, m->m_time, >=, n->n_n) &&
536 UICMP(z, m->m_time, <, n->n_n + 86400));
537 break;
538 case ITOR:
539 rv = itexecute(mp, m, c, n->n_x) | itexecute(mp, m, c, n->n_y);
540 break;
541 case ITRECENT:
542 rv = ((m->m_flag & MNEW) != 0);
543 break;
544 case ITSEEN:
545 rv = ((m->m_flag & MREAD) != 0);
546 break;
547 case ITSENTBEFORE:
548 rv = UICMP(z, m->m_date, <, n->n_n);
549 break;
550 case ITSENTON:
551 rv = (UICMP(z, m->m_date, >=, n->n_n) &&
552 UICMP(z, m->m_date, <, n->n_n + 86400));
553 break;
554 case ITSENTSINCE:
555 rv = UICMP(z, m->m_date, >=, n->n_n);
556 break;
557 case ITSINCE:
558 rv = UICMP(z, m->m_time, >=, n->n_n);
559 break;
560 case ITSMALLER:
561 rv = UICMP(z, m->m_xsize, <, n->n_n);
562 break;
563 case ITSUBJECT:
564 rv = matchfield(m, "subject", n->n_v);
565 break;
566 case ITTEXT:
567 memset(&se, 0, sizeof se);
568 se.ss_field = "text";
569 se.ss_body = n->n_v;
570 rv = message_match(m, &se, TRU1);
571 break;
572 case ITTO:
573 rv = matchenvelope(m, "to", n->n_v);
574 break;
575 case ITUNANSWERED:
576 rv = !(m->m_flag & MANSWERED);
577 break;
578 case ITUNDELETED:
579 rv = !(m->m_flag & MDELETED);
580 break;
581 case ITUNDRAFT:
582 rv = !(m->m_flag & MDRAFTED);
583 break;
584 case ITUNFLAGGED:
585 rv = !(m->m_flag & MFLAGGED);
586 break;
587 case ITUNKEYWORD:
588 rv = !(m->m_flag & n->n_n);
589 break;
590 case ITUNSEEN:
591 rv = !(m->m_flag & MREAD);
592 break;
594 jleave:
595 NYD_LEAVE;
596 return rv;
599 static time_t
600 _imap_read_date(char const *cp)
602 time_t t, t2;
603 si32_t year, month, day, i, tzdiff;
604 struct tm *tmptr;
605 char const *xp, *yp;
606 NYD_ENTER;
608 if (*cp == '"')
609 ++cp;
610 n_idec_si32_cp(&day, cp, 10, &xp);
611 if (day <= 0 || day > 31 || *xp++ != '-')
612 goto jerr;
614 for (i = 0;;) {
615 if (!ascncasecmp(xp, n_month_names[i], 3))
616 break;
617 if (n_month_names[++i][0] == '\0')
618 goto jerr;
620 month = i + 1;
621 if (xp[3] != '-')
622 goto jerr;
623 n_idec_si32_cp(&year, &xp[4], 10, &yp);
624 if (year < 1970 || year > 2037 || PTRCMP(yp, !=, xp + 8))
625 goto jerr;
626 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
627 goto jerr;
628 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
629 goto jleave/*jerr*/;
630 if((t2 = mktime(gmtime(&t))) == (time_t)-1)
631 goto jerr;
632 tzdiff = t - t2;
633 if((tmptr = localtime(&t)) == NULL)
634 goto jerr;
635 if (tmptr->tm_isdst > 0)
636 tzdiff += 3600;
637 t -= tzdiff;
638 jleave:
639 NYD_LEAVE;
640 return t;
641 jerr:
642 t = (time_t)-1;
643 goto jleave;
646 static char *
647 _imap_quotestr(char const *s)
649 char *n, *np;
650 NYD2_ENTER;
652 np = n = n_autorec_alloc(2 * strlen(s) + 3);
653 *np++ = '"';
654 while (*s) {
655 if (*s == '"' || *s == '\\')
656 *np++ = '\\';
657 *np++ = *s++;
659 *np++ = '"';
660 *np = '\0';
661 NYD2_LEAVE;
662 return n;
665 static char *
666 _imap_unquotestr(char const *s)
668 char *n, *np;
669 NYD2_ENTER;
671 if (*s != '"') {
672 n = savestr(s);
673 goto jleave;
676 np = n = n_autorec_alloc(strlen(s) + 1);
677 while (*++s) {
678 if (*s == '\\')
679 s++;
680 else if (*s == '"')
681 break;
682 *np++ = *s;
684 *np = '\0';
685 jleave:
686 NYD2_LEAVE;
687 return n;
690 static bool_t
691 matchfield(struct message *m, char const *field, void const *what)
693 struct str in, out;
694 bool_t rv = FAL0;
695 NYD_ENTER;
697 if ((in.s = hfieldX(field, m)) == NULL)
698 goto jleave;
700 in.l = strlen(in.s);
701 mime_fromhdr(&in, &out, TD_ICONV);
702 rv = substr(out.s, what);
703 n_free(out.s);
704 jleave:
705 NYD_LEAVE;
706 return rv;
709 static int
710 matchenvelope(struct message *m, char const *field, void const *what)
712 struct name *np;
713 char *cp;
714 int rv = 0;
715 NYD_ENTER;
717 if ((cp = hfieldX(field, m)) == NULL)
718 goto jleave;
720 for (np = lextract(cp, GFULL); np != NULL; np = np->n_flink) {
721 if (!substr(np->n_name, what) && !substr(mkenvelope(np), what))
722 continue;
723 rv = 1;
724 break;
727 jleave:
728 NYD_LEAVE;
729 return rv;
732 static char *
733 mkenvelope(struct name *np)
735 size_t epsize;
736 char *ep, *realnam = NULL, /**sourceaddr = NULL,*/ *localpart,
737 *domainpart, *cp, *rp, *xp, *ip;
738 struct str in, out;
739 int level = 0;
740 bool_t hadphrase = FAL0;
741 NYD_ENTER;
743 in.s = np->n_fullname;
744 in.l = strlen(in.s);
745 mime_fromhdr(&in, &out, TD_ICONV);
746 rp = ip = n_lofi_alloc(strlen(out.s) + 1);
747 for (cp = out.s; *cp; cp++) {
748 switch (*cp) {
749 case '"':
750 while (*cp) {
751 if (*++cp == '"')
752 break;
753 if (cp[0] == '\\' && cp[1] != '\0')
754 ++cp;
755 *rp++ = *cp;
757 break;
758 case '<':
759 while (cp > out.s && blankchar(cp[-1]))
760 --cp;
761 rp = ip;
762 xp = out.s;
763 if (PTRCMP(xp, <, cp - 1) && *xp == '"' && cp[-1] == '"') {
764 ++xp;
765 --cp;
767 while (xp < cp)
768 *rp++ = *xp++;
769 hadphrase = TRU1;
770 goto jdone;
771 case '(':
772 if (level++)
773 goto jdfl;
774 if (!hadphrase)
775 rp = ip;
776 hadphrase = TRU1;
777 break;
778 case ')':
779 if (--level)
780 goto jdfl;
781 break;
782 case '\\':
783 if (level && cp[1] != '\0')
784 cp++;
785 goto jdfl;
786 default:
787 jdfl:
788 *rp++ = *cp;
791 jdone:
792 *rp = '\0';
793 if (hadphrase)
794 realnam = ip;
795 n_free(out.s);
796 localpart = savestr(np->n_name);
797 if ((cp = strrchr(localpart, '@')) != NULL) {
798 *cp = '\0';
799 domainpart = cp + 1;
800 }else
801 domainpart = NULL;
803 ep = n_autorec_alloc(epsize = strlen(np->n_fullname) * 2 + 40);
804 snprintf(ep, epsize, "(%s %s %s %s)",
805 realnam ? _imap_quotestr(realnam) : "NIL",
806 /*sourceaddr ? _imap_quotestr(sourceaddr) :*/ "NIL",
807 _imap_quotestr(localpart),
808 domainpart ? _imap_quotestr(domainpart) : "NIL");
809 n_lofi_free(ip);
810 NYD_LEAVE;
811 return ep;
814 #define SURROUNDING 16
815 static char const *
816 around(char const *cp)
818 static char ab[2 * SURROUNDING +1];
820 size_t i;
821 NYD_ENTER;
823 for (i = 0; i < SURROUNDING && cp > _it_begin; ++i)
824 --cp;
825 for (i = 0; i < sizeof(ab) -1; ++i)
826 ab[i] = *cp++;
827 ab[i] = '\0';
828 NYD_LEAVE;
829 return ab;
832 FL ssize_t
833 imap_search(char const *spec, int f)
835 static char *lastspec;
837 char const *xp;
838 size_t i;
839 ssize_t rv;
840 NYD_ENTER;
842 if (strcmp(spec, "()")) {
843 if (lastspec != NULL)
844 n_free(lastspec);
845 i = strlen(spec);
846 lastspec = sbufdup(spec, i);
847 } else if (lastspec == NULL) {
848 n_err(_("No last SEARCH criteria available\n"));
849 rv = -1;
850 goto jleave;
852 spec =
853 _it_begin = lastspec;
855 _it_need_headers = FAL0;
856 #ifdef HAVE_IMAP
857 if ((rv = imap_search1(spec, f) == OKAY))
858 goto jleave;
859 #endif
860 if (itparse(spec, &xp, 0) == STOP){
861 rv = -1;
862 goto jleave;
865 rv = 0;
867 if (_it_tree == NULL)
868 goto jleave;
870 #ifdef HAVE_IMAP
871 if (mb.mb_type == MB_IMAP && _it_need_headers)
872 imap_getheaders(1, msgCount);
873 #endif
874 srelax_hold();
875 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
876 if (message[i].m_flag & MHIDDEN)
877 continue;
878 if (f == MDELETED || !(message[i].m_flag & MDELETED)) {
879 size_t j = (int)(i + 1);
880 if (itexecute(&mb, message + i, j, _it_tree)){
881 mark((int)j, f);
882 ++rv;
884 srelax();
887 srelax_rele();
888 jleave:
889 NYD_LEAVE;
890 return rv;
892 #endif /* HAVE_IMAP_SEARCH */
894 /* s-it-mode */