"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/imap-search.c" (8 Aug 2018, 22432 Bytes) of package /linux/misc/s-nail-14.9.11.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.10_vs_14.9.11.

    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  * SPDX-License-Identifier: BSD-4-Clause
    9  */
   10 /*
   11  * Copyright (c) 2004
   12  * Gunnar Ritter.  All rights reserved.
   13  *
   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.
   29  *
   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.
   41  */
   42 #undef n_FILE
   43 #define n_FILE imap_search
   44 
   45 #ifndef HAVE_AMALGAMATION
   46 # include "nail.h"
   47 #endif
   48 
   49 EMPTY_FILE()
   50 #ifdef HAVE_IMAP_SEARCH
   51 
   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
   69 };
   70 
   71 struct itlex {
   72    char const     *s_string;
   73    enum itoken    s_token;
   74 };
   75 
   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;
   83 };
   84 
   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 }
  122 };
  123 
  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;
  130 
  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);
  137 
  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);
  141 
  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);
  148 
  149 static enum okay
  150 itparse(char const *spec, char const **xp, int sub)
  151 {
  152    int level = 0;
  153    struct itnode n, *z, *ittree;
  154    enum okay rv;
  155    NYD_ENTER;
  156 
  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;
  174             }
  175             n_err(_("Excess in )\n"));
  176             rv = STOP;
  177             goto jleave;
  178          }
  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;
  190          }
  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;
  203          }
  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;
  213          }
  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];
  220       }
  221 
  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;
  233       }
  234       if (sub && level == 0)
  235          break;
  236    }
  237 jleave:
  238    NYD_LEAVE;
  239    return rv;
  240 }
  241 
  242 static enum okay
  243 itscan(char const *spec, char const **xp)
  244 {
  245    int i, n;
  246    enum okay rv = OKAY;
  247    NYD_ENTER;
  248 
  249    while (spacechar(*spec))
  250       ++spec;
  251    if (*spec == '(') {
  252       *xp = &spec[1];
  253       _it_token = ITBOL;
  254       goto jleave;
  255    }
  256    if (*spec == ')') {
  257       *xp = &spec[1];
  258       _it_token = ITEOL;
  259       goto jleave;
  260    }
  261    while (spacechar(*spec))
  262       ++spec;
  263    if (*spec == '\0') {
  264       _it_token = ITEOD;
  265       goto jleave;
  266    }
  267 
  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;
  278       }
  279    }
  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;
  285       }
  286    }
  287 
  288    n_err(_("Bad SEARCH criterion: "));
  289    for (i = 0; __GO(spec[i]); ++i)
  290       ;
  291    n_err(_("%.*s: >>> %s <<<\n"), i, spec, around(*xp));
  292 #undef __GO
  293 
  294    _it_token = ITBAD;
  295    rv = STOP;
  296 jleave:
  297    NYD_LEAVE;
  298    return rv;
  299 }
  300 
  301 static enum okay
  302 itsplit(char const *spec, char const **xp)
  303 {
  304    char const *cp;
  305    time_t t;
  306    enum okay rv;
  307    NYD_ENTER;
  308 
  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;
  337       }
  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);
  377       }
  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;
  393    }
  394    NYD_LEAVE;
  395    return rv;
  396 }
  397 
  398 static enum okay
  399 itstring(void **tp, char const *spec, char const **xp) /* XXX lesser derefs */
  400 {
  401    int inquote = 0;
  402    char *ap;
  403    enum okay rv = STOP;
  404    NYD_ENTER;
  405 
  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;
  412    }
  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;
  423       }
  424       *ap++ = **xp;
  425    } while (*(*xp)++);
  426 
  427    *tp = _imap_unquotestr(*tp);
  428    rv = OKAY;
  429 jleave:
  430    NYD_LEAVE;
  431    return rv;
  432 }
  433 
  434 static int
  435 itexecute(struct mailbox *mp, struct message *m, size_t c, struct itnode *n)
  436 {
  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;
  443 
  444    if (n == NULL) {
  445       n_err(_("Internal error: Empty node in SEARCH tree\n"));
  446       rv = 0;
  447       goto jleave;
  448    }
  449 
  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);
  459       }
  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;
  470    }
  471 
  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;
  593    }
  594 jleave:
  595    NYD_LEAVE;
  596    return rv;
  597 }
  598 
  599 static time_t
  600 _imap_read_date(char const *cp)
  601 {
  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;
  607 
  608    if (*cp == '"')
  609       ++cp;
  610    n_idec_si32_cp(&day, cp, 10, &xp);
  611    if (day <= 0 || day > 31 || *xp++ != '-')
  612       goto jerr;
  613 
  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;
  619    }
  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;
  644 }
  645 
  646 static char *
  647 _imap_quotestr(char const *s)
  648 {
  649    char *n, *np;
  650    NYD2_ENTER;
  651 
  652    np = n = n_autorec_alloc(2 * strlen(s) + 3);
  653    *np++ = '"';
  654    while (*s) {
  655       if (*s == '"' || *s == '\\')
  656          *np++ = '\\';
  657       *np++ = *s++;
  658    }
  659    *np++ = '"';
  660    *np = '\0';
  661    NYD2_LEAVE;
  662    return n;
  663 }
  664 
  665 static char *
  666 _imap_unquotestr(char const *s)
  667 {
  668    char *n, *np;
  669    NYD2_ENTER;
  670 
  671    if (*s != '"') {
  672       n = savestr(s);
  673       goto jleave;
  674    }
  675 
  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;
  683    }
  684    *np = '\0';
  685 jleave:
  686    NYD2_LEAVE;
  687    return n;
  688 }
  689 
  690 static bool_t
  691 matchfield(struct message *m, char const *field, void const *what)
  692 {
  693    struct str in, out;
  694    bool_t rv = FAL0;
  695    NYD_ENTER;
  696 
  697    if ((in.s = hfieldX(field, m)) == NULL)
  698       goto jleave;
  699 
  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;
  707 }
  708 
  709 static int
  710 matchenvelope(struct message *m, char const *field, void const *what)
  711 {
  712    struct name *np;
  713    char *cp;
  714    int rv = 0;
  715    NYD_ENTER;
  716 
  717    if ((cp = hfieldX(field, m)) == NULL)
  718       goto jleave;
  719 
  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;
  725    }
  726 
  727 jleave:
  728    NYD_LEAVE;
  729    return rv;
  730 }
  731 
  732 static char *
  733 mkenvelope(struct name *np)
  734 {
  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;
  742 
  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;
  756          }
  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;
  766          }
  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;
  789       }
  790    }
  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;
  802 
  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;
  812 }
  813 
  814 #define SURROUNDING 16
  815 static char const *
  816 around(char const *cp)
  817 {
  818    static char ab[2 * SURROUNDING +1];
  819 
  820    size_t i;
  821    NYD_ENTER;
  822 
  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;
  830 }
  831 
  832 FL ssize_t
  833 imap_search(char const *spec, int f)
  834 {
  835    static char *lastspec;
  836 
  837    char const *xp;
  838    size_t i;
  839    ssize_t rv;
  840    NYD_ENTER;
  841 
  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;
  851    }
  852    spec =
  853    _it_begin = lastspec;
  854 
  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;
  863    }
  864 
  865    rv = 0;
  866 
  867    if (_it_tree == NULL)
  868       goto jleave;
  869 
  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;
  883          }
  884          srelax();
  885       }
  886    }
  887    srelax_rele();
  888 jleave:
  889    NYD_LEAVE;
  890    return rv;
  891 }
  892 #endif /* HAVE_IMAP_SEARCH */
  893 
  894 /* s-it-mode */