"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/imap-search.c" (16 Feb 2018, 22321 Bytes) of package /linux/misc/s-nail-14.9.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "imap-search.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.6_vs_14.9.7.

    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.
    5  *
    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.
   12  *
   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.
   28  *
   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.
   40  */
   41 #undef n_FILE
   42 #define n_FILE imap_search
   43 
   44 #ifndef HAVE_AMALGAMATION
   45 # include "nail.h"
   46 #endif
   47 
   48 EMPTY_FILE()
   49 #ifdef HAVE_IMAP_SEARCH
   50 
   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
   68 };
   69 
   70 struct itlex {
   71    char const     *s_string;
   72    enum itoken    s_token;
   73 };
   74 
   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;
   82 };
   83 
   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 }
  121 };
  122 
  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;
  129 
  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 
  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);
  140 
  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);
  147 
  148 static enum okay
  149 itparse(char const *spec, char const **xp, int sub)
  150 {
  151    int level = 0;
  152    struct itnode n, *z, *ittree;
  153    enum okay rv;
  154    NYD_ENTER;
  155 
  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;
  173             }
  174             n_err(_("Excess in )\n"));
  175             rv = STOP;
  176             goto jleave;
  177          }
  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;
  189          }
  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;
  202          }
  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;
  212          }
  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];
  219       }
  220 
  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;
  232       }
  233       if (sub && level == 0)
  234          break;
  235    }
  236 jleave:
  237    NYD_LEAVE;
  238    return rv;
  239 }
  240 
  241 static enum okay
  242 itscan(char const *spec, char const **xp)
  243 {
  244    int i, n;
  245    enum okay rv = OKAY;
  246    NYD_ENTER;
  247 
  248    while (spacechar(*spec))
  249       ++spec;
  250    if (*spec == '(') {
  251       *xp = &spec[1];
  252       _it_token = ITBOL;
  253       goto jleave;
  254    }
  255    if (*spec == ')') {
  256       *xp = &spec[1];
  257       _it_token = ITEOL;
  258       goto jleave;
  259    }
  260    while (spacechar(*spec))
  261       ++spec;
  262    if (*spec == '\0') {
  263       _it_token = ITEOD;
  264       goto jleave;
  265    }
  266 
  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;
  277       }
  278    }
  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;
  284       }
  285    }
  286 
  287    n_err(_("Bad SEARCH criterion: "));
  288    for (i = 0; __GO(spec[i]); ++i)
  289       ;
  290    n_err(_("%.*s: >>> %s <<<\n"), i, spec, around(*xp));
  291 #undef __GO
  292 
  293    _it_token = ITBAD;
  294    rv = STOP;
  295 jleave:
  296    NYD_LEAVE;
  297    return rv;
  298 }
  299 
  300 static enum okay
  301 itsplit(char const *spec, char const **xp)
  302 {
  303    char const *cp;
  304    time_t t;
  305    enum okay rv;
  306    NYD_ENTER;
  307 
  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;
  336       }
  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);
  376       }
  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;
  392    }
  393    NYD_LEAVE;
  394    return rv;
  395 }
  396 
  397 static enum okay
  398 itstring(void **tp, char const *spec, char const **xp) /* XXX lesser derefs */
  399 {
  400    int inquote = 0;
  401    char *ap;
  402    enum okay rv = STOP;
  403    NYD_ENTER;
  404 
  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;
  411    }
  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;
  422       }
  423       *ap++ = **xp;
  424    } while (*(*xp)++);
  425 
  426    *tp = _imap_unquotestr(*tp);
  427    rv = OKAY;
  428 jleave:
  429    NYD_LEAVE;
  430    return rv;
  431 }
  432 
  433 static int
  434 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
  435 {
  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;
  442 
  443    if (n == NULL) {
  444       n_err(_("Internal error: Empty node in SEARCH tree\n"));
  445       rv = 0;
  446       goto jleave;
  447    }
  448 
  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);
  458       }
  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;
  469    }
  470 
  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;
  592    }
  593 jleave:
  594    NYD_LEAVE;
  595    return rv;
  596 }
  597 
  598 static time_t
  599 _imap_read_date(char const *cp)
  600 {
  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;
  606 
  607    if (*cp == '"')
  608       ++cp;
  609    n_idec_si32_cp(&day, cp, 10, &xp);
  610    if (day <= 0 || day > 31 || *xp++ != '-')
  611       goto jerr;
  612 
  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;
  618    }
  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;
  643 }
  644 
  645 static char *
  646 _imap_quotestr(char const *s)
  647 {
  648    char *n, *np;
  649    NYD2_ENTER;
  650 
  651    np = n = salloc(2 * strlen(s) + 3);
  652    *np++ = '"';
  653    while (*s) {
  654       if (*s == '"' || *s == '\\')
  655          *np++ = '\\';
  656       *np++ = *s++;
  657    }
  658    *np++ = '"';
  659    *np = '\0';
  660    NYD2_LEAVE;
  661    return n;
  662 }
  663 
  664 static char *
  665 _imap_unquotestr(char const *s)
  666 {
  667    char *n, *np;
  668    NYD2_ENTER;
  669 
  670    if (*s != '"') {
  671       n = savestr(s);
  672       goto jleave;
  673    }
  674 
  675    np = n = salloc(strlen(s) + 1);
  676    while (*++s) {
  677       if (*s == '\\')
  678          s++;
  679       else if (*s == '"')
  680          break;
  681       *np++ = *s;
  682    }
  683    *np = '\0';
  684 jleave:
  685    NYD2_LEAVE;
  686    return n;
  687 }
  688 
  689 static bool_t
  690 matchfield(struct message *m, char const *field, void const *what)
  691 {
  692    struct str in, out;
  693    bool_t rv = FAL0;
  694    NYD_ENTER;
  695 
  696    if ((in.s = hfieldX(field, m)) == NULL)
  697       goto jleave;
  698 
  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;
  706 }
  707 
  708 static int
  709 matchenvelope(struct message *m, char const *field, void const *what)
  710 {
  711    struct name *np;
  712    char *cp;
  713    int rv = 0;
  714    NYD_ENTER;
  715 
  716    if ((cp = hfieldX(field, m)) == NULL)
  717       goto jleave;
  718 
  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;
  724    }
  725 
  726 jleave:
  727    NYD_LEAVE;
  728    return rv;
  729 }
  730 
  731 static char *
  732 mkenvelope(struct name *np)
  733 {
  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;
  741 
  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;
  755          }
  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;
  765          }
  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;
  788       }
  789    }
  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;
  801 
  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;
  811 }
  812 
  813 #define SURROUNDING 16
  814 static char const *
  815 around(char const *cp)
  816 {
  817    static char ab[2 * SURROUNDING +1];
  818 
  819    size_t i;
  820    NYD_ENTER;
  821 
  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;
  829 }
  830 
  831 FL ssize_t
  832 imap_search(char const *spec, int f)
  833 {
  834    static char *lastspec;
  835 
  836    char const *xp;
  837    size_t i;
  838    ssize_t rv;
  839    NYD_ENTER;
  840 
  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;
  850    }
  851    spec =
  852    _it_begin = lastspec;
  853 
  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;
  862    }
  863 
  864    rv = 0;
  865 
  866    if (_it_tree == NULL)
  867       goto jleave;
  868 
  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;
  882          }
  883          srelax();
  884       }
  885    }
  886    srelax_rele();
  887 jleave:
  888    NYD_LEAVE;
  889    return rv;
  890 }
  891 #endif /* HAVE_IMAP_SEARCH */
  892 
  893 /* s-it-mode */