"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/obs-imap.c" (16 Feb 2018, 116498 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 "obs-imap.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  *@ IMAP v4r1 client following RFC 2060.
    3  *
    4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    5  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
    6  */
    7 /*
    8  * Copyright (c) 2004
    9  * Gunnar Ritter.  All rights reserved.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. All advertising materials mentioning features or use of this software
   20  *    must display the following acknowledgement:
   21  *    This product includes software developed by Gunnar Ritter
   22  *    and his contributors.
   23  * 4. Neither the name of Gunnar Ritter nor the names of his contributors
   24  *    may be used to endorse or promote products derived from this software
   25  *    without specific prior written permission.
   26  *
   27  * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
   28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   30  * ARE DISCLAIMED.  IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
   31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   37  * SUCH DAMAGE.
   38  */
   39 #undef n_FILE
   40 #define n_FILE obs_imap
   41 
   42 #ifndef HAVE_AMALGAMATION
   43 # include "nail.h"
   44 #endif
   45 
   46 #ifdef HAVE_IMAP
   47 # include <sys/socket.h>
   48 
   49 # include <netdb.h>
   50 
   51 # include <netinet/in.h>
   52 
   53 # ifdef HAVE_ARPA_INET_H
   54 #  include <arpa/inet.h>
   55 # endif
   56 #endif
   57 
   58 EMPTY_FILE()
   59 #ifdef HAVE_IMAP
   60 #define IMAP_ANSWER() \
   61 {\
   62    if (mp->mb_type != MB_CACHE) {\
   63       enum okay ok = OKAY;\
   64       while (mp->mb_active & MB_COMD)\
   65          ok = imap_answer(mp, 1);\
   66       if (ok == STOP)\
   67          return STOP;\
   68    }\
   69 }
   70 
   71 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
   72  * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
   73  * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
   74  * TODO and (2) doesn't handle all I/O errors itself, yet, too.
   75  * TODO I.e., that should be a function, not a macro ... or so.
   76  * TODO This entire module needs MASSIVE work! */
   77 #define IMAP_OUT(X,Y,ACTION)  IMAP_XOUT(X, Y, ACTION, return STOP)
   78 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
   79 {\
   80    if (mp->mb_type != MB_CACHE) {\
   81       if (imap_finish(mp) == STOP) {\
   82          ACTIONBAIL;\
   83       }\
   84       if (n_poption & n_PO_VERBVERB)\
   85          n_err(">>> %s", X);\
   86       mp->mb_active |= Y;\
   87       if (swrite(&mp->mb_sock, X) == STOP) {\
   88          ACTIONERR;\
   89       }\
   90    } else {\
   91       if (queuefp != NULL)\
   92          fputs(X, queuefp);\
   93    }\
   94 }
   95 
   96 static struct record {
   97    struct record  *rec_next;
   98    unsigned long  rec_count;
   99    enum rec_type {
  100       REC_EXISTS,
  101       REC_EXPUNGE
  102    }              rec_type;
  103 } *record, *recend;
  104 
  105 static enum {
  106    RESPONSE_TAGGED,
  107    RESPONSE_DATA,
  108    RESPONSE_FATAL,
  109    RESPONSE_CONT,
  110    RESPONSE_ILLEGAL
  111 } response_type;
  112 
  113 static enum {
  114    RESPONSE_OK,
  115    RESPONSE_NO,
  116    RESPONSE_BAD,
  117    RESPONSE_PREAUTH,
  118    RESPONSE_BYE,
  119    RESPONSE_OTHER,
  120    RESPONSE_UNKNOWN
  121 } response_status;
  122 
  123 static char *responded_tag;
  124 static char *responded_text;
  125 static char *responded_other_text;
  126 static long responded_other_number;
  127 
  128 static enum {
  129    MAILBOX_DATA_FLAGS,
  130    MAILBOX_DATA_LIST,
  131    MAILBOX_DATA_LSUB,
  132    MAILBOX_DATA_MAILBOX,
  133    MAILBOX_DATA_SEARCH,
  134    MAILBOX_DATA_STATUS,
  135    MAILBOX_DATA_EXISTS,
  136    MAILBOX_DATA_RECENT,
  137    MESSAGE_DATA_EXPUNGE,
  138    MESSAGE_DATA_FETCH,
  139    CAPABILITY_DATA,
  140    RESPONSE_OTHER_UNKNOWN
  141 } response_other;
  142 
  143 static enum list_attributes {
  144    LIST_NONE         = 000,
  145    LIST_NOINFERIORS  = 001,
  146    LIST_NOSELECT     = 002,
  147    LIST_MARKED       = 004,
  148    LIST_UNMARKED     = 010
  149 } list_attributes;
  150 
  151 static int  list_hierarchy_delimiter;
  152 static char *list_name;
  153 
  154 struct list_item {
  155    struct list_item     *l_next;
  156    char                 *l_name;
  157    char                 *l_base;
  158    enum list_attributes l_attr;
  159    int                  l_delim;
  160    int                  l_level;
  161    int                  l_has_children;
  162 };
  163 
  164 static char             *imapbuf;   /* TODO not static, use pool */
  165 static size_t           imapbufsize;
  166 static sigjmp_buf       imapjmp;
  167 static sighandler_type  savealrm;
  168 static int              imapkeepalive;
  169 static long             had_exists = -1;
  170 static long             had_expunge = -1;
  171 static long             expunged_messages;
  172 static int volatile     imaplock;
  173 static int              same_imap_account;
  174 static bool_t           _imap_rdonly;
  175 
  176 static char *imap_quotestr(char const *s);
  177 static char *imap_unquotestr(char const *s);
  178 static void imap_delim_init(struct mailbox *mp, struct url const *urlp);
  179 static char const *imap_path_normalize(struct mailbox *mp, char const *cp);
  180 /* Returns NULL on error */
  181 static char *imap_path_quote(struct mailbox *mp, char const *cp);
  182 static void       imap_other_get(char *pp);
  183 static void       imap_response_get(const char **cp);
  184 static void       imap_response_parse(void);
  185 static enum okay  imap_answer(struct mailbox *mp, int errprnt);
  186 static enum okay  imap_parse_list(void);
  187 static enum okay  imap_finish(struct mailbox *mp);
  188 static void       imap_timer_off(void);
  189 static void       imapcatch(int s);
  190 static void       _imap_maincatch(int s);
  191 static enum okay  imap_noop1(struct mailbox *mp);
  192 static void       rec_queue(enum rec_type type, unsigned long cnt);
  193 static enum okay  rec_dequeue(void);
  194 static void       rec_rmqueue(void);
  195 static void       imapalarm(int s);
  196 static enum okay  imap_preauth(struct mailbox *mp, struct url const *urlp);
  197 static enum okay  imap_capability(struct mailbox *mp);
  198 static enum okay  imap_auth(struct mailbox *mp, struct ccred *ccred);
  199 #ifdef HAVE_MD5
  200 static enum okay  imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
  201 #endif
  202 static enum okay  imap_login(struct mailbox *mp, struct ccred *ccred);
  203 #ifdef HAVE_GSSAPI
  204 static enum okay  _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
  205 #endif
  206 static enum okay  imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
  207 static void       imap_init(struct mailbox *mp, int n);
  208 static void       imap_setptr(struct mailbox *mp, int nmail, int transparent,
  209                      int *prevcount);
  210 static bool_t     _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
  211                      struct url *urlp);
  212 static int        _imap_setfile1(struct url *urlp, enum fedit_mode fm,
  213                      int transparent);
  214 static int        imap_fetchdata(struct mailbox *mp, struct message *m,
  215                      size_t expected, int need, const char *head,
  216                      size_t headsize, long headlines);
  217 static void       imap_putstr(struct mailbox *mp, struct message *m,
  218                      const char *str, const char *head, size_t headsize,
  219                      long headlines);
  220 static enum okay  imap_get(struct mailbox *mp, struct message *m,
  221                      enum needspec need);
  222 static void       commitmsg(struct mailbox *mp, struct message *to,
  223                      struct message *from, enum content_info content_info);
  224 static enum okay  imap_fetchheaders(struct mailbox *mp, struct message *m,
  225                      int bot, int top);
  226 static enum okay  imap_exit(struct mailbox *mp);
  227 static enum okay  imap_delete(struct mailbox *mp, int n, struct message *m,
  228                      int needstat);
  229 static enum okay  imap_close(struct mailbox *mp);
  230 static enum okay  imap_update(struct mailbox *mp);
  231 static enum okay  imap_store(struct mailbox *mp, struct message *m, int n,
  232                      int c, const char *sp, int needstat);
  233 static enum okay  imap_unstore(struct message *m, int n, const char *flag);
  234 static const char *tag(int new);
  235 static char *     imap_putflags(int f);
  236 static void       imap_getflags(const char *cp, char const **xp, enum mflag *f);
  237 static enum okay  imap_append1(struct mailbox *mp, const char *name, FILE *fp,
  238                      off_t off1, long xsize, enum mflag flag, time_t t);
  239 static enum okay  imap_append0(struct mailbox *mp, const char *name, FILE *fp,
  240                      long offset);
  241 static enum okay  imap_list1(struct mailbox *mp, const char *base,
  242                      struct list_item **list, struct list_item **lend,
  243                      int level);
  244 static enum okay  imap_list(struct mailbox *mp, const char *base, int strip,
  245                      FILE *fp);
  246 static enum okay  imap_copy1(struct mailbox *mp, struct message *m, int n,
  247                      const char *name);
  248 static enum okay  imap_copyuid_parse(const char *cp,
  249                      unsigned long *uidvalidity, unsigned long *olduid,
  250                      unsigned long *newuid);
  251 static enum okay  imap_appenduid_parse(const char *cp,
  252                      unsigned long *uidvalidity, unsigned long *uid);
  253 static enum okay  imap_copyuid(struct mailbox *mp, struct message *m,
  254                      const char *name);
  255 static enum okay  imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
  256                      long off1, long xsize, long size, long lines, int flag,
  257                      const char *name);
  258 static enum okay  imap_appenduid_cached(struct mailbox *mp, FILE *fp);
  259 #ifdef HAVE_IMAP_SEARCH
  260 static enum okay  imap_search2(struct mailbox *mp, struct message *m, int cnt,
  261                      const char *spec, int f);
  262 #endif
  263 static enum okay  imap_remove1(struct mailbox *mp, const char *name);
  264 static enum okay  imap_rename1(struct mailbox *mp, const char *old,
  265                      const char *new);
  266 static char *     imap_strex(char const *cp, char const **xp);
  267 static enum okay  check_expunged(void);
  268 
  269 static char *
  270 imap_quotestr(char const *s)
  271 {
  272    char *n, *np;
  273    NYD2_ENTER;
  274 
  275    np = n = salloc(2 * strlen(s) + 3);
  276    *np++ = '"';
  277    while (*s) {
  278       if (*s == '"' || *s == '\\')
  279          *np++ = '\\';
  280       *np++ = *s++;
  281    }
  282    *np++ = '"';
  283    *np = '\0';
  284    NYD2_LEAVE;
  285    return n;
  286 }
  287 
  288 static char *
  289 imap_unquotestr(char const *s)
  290 {
  291    char *n, *np;
  292    NYD2_ENTER;
  293 
  294    if (*s != '"') {
  295       n = savestr(s);
  296       goto jleave;
  297    }
  298 
  299    np = n = salloc(strlen(s) + 1);
  300    while (*++s) {
  301       if (*s == '\\')
  302          s++;
  303       else if (*s == '"')
  304          break;
  305       *np++ = *s;
  306    }
  307    *np = '\0';
  308 jleave:
  309    NYD2_LEAVE;
  310    return n;
  311 }
  312 
  313 static void
  314 imap_delim_init(struct mailbox *mp, struct url const *urlp){
  315    size_t i;
  316    char const *cp;
  317    NYD2_ENTER;
  318 
  319    mp->mb_imap_delim[0] = '\0';
  320 
  321    if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
  322       i = strlen(cp);
  323 
  324       if(i == 0){
  325          cp = n_IMAP_DELIM;
  326          i = sizeof(n_IMAP_DELIM) -1;
  327          goto jcopy;
  328       }
  329 
  330       if(i < n_NELEM(mp->mb_imap_delim))
  331 jcopy:
  332          memcpy(&mb.mb_imap_delim[0], cp, i +1);
  333       else
  334          n_err(_("*imap-delim* for %s is too long: %s\n"),
  335             urlp->url_input, cp);
  336    }
  337    NYD2_LEAVE;
  338 }
  339 
  340 static char const *
  341 imap_path_normalize(struct mailbox *mp, char const *cp){
  342    char *rv_base, *rv, dc2, dc, c, lc;
  343    char const *dcp;
  344    NYD2_ENTER;
  345 
  346    /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
  347     * exactly what i have specified" */
  348    if(mp == NULL || mp->mb_imap_delim[0] == '\0')
  349       dcp = &n_IMAP_DELIM[0];
  350    else
  351       dcp = &mp->mb_imap_delim[0];
  352    dc2 = ((dc = *dcp) != '\0') ? *++dcp : dc;
  353 
  354    /* Plain names don't need path quoting */
  355    /* C99 */{
  356       size_t i, j;
  357       char const *cpx;
  358 
  359       for(cpx = cp;; ++cpx)
  360          if((c = *cpx) == '\0')
  361             goto jleave;
  362          else if(dc == '\0'){
  363             if(strchr(n_IMAP_DELIM, c)){
  364                dc = c;
  365                break;
  366             }
  367          }else if(c == dc)
  368             break;
  369          else if(dc2 && strchr(dcp, c) != NULL)
  370             break;
  371 
  372       /* And we don't need to reevaluate what we have seen yet */
  373       i = PTR2SIZE(cpx - cp);
  374       rv = rv_base = salloc(i + (j = strlen(cpx) +1));
  375       if(i > 0)
  376          memcpy(rv, cp, i);
  377       memcpy(&rv[i], cpx, j);
  378       rv += i;
  379       cp = cpx;
  380    }
  381 
  382    /* Squeeze adjacent delimiters, convert remain to dc */
  383    for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
  384       if(c == dc || (lc != '\0' && dc2 && strchr(dcp, c) != NULL))
  385          c = dc;
  386       if(c != dc || lc != dc)
  387          *rv++ = c;
  388    }
  389    *rv = '\0';
  390 
  391    cp = rv_base;
  392 jleave:
  393    NYD2_LEAVE;
  394    return cp;
  395 }
  396 
  397 FL char const *
  398 imap_path_encode(char const *cp, bool_t *err_or_null){
  399    /* To a large extend inspired by dovecot(1) */
  400    struct str in, out;
  401    bool_t err_def;
  402    ui8_t *be16p_base, *be16p;
  403    char const *emsg;
  404    char c;
  405    size_t l, l_plain;
  406    NYD2_ENTER;
  407 
  408    if(err_or_null == NULL)
  409       err_or_null = &err_def;
  410    *err_or_null = FAL0;
  411 
  412    /* Is this a string that works out as "plain US-ASCII"? */
  413    for(l = 0;; ++l)
  414       if((c = cp[l]) == '\0')
  415          goto jleave;
  416       else if(c <= 0x1F || c >= 0x7F || c == '&')
  417          break;
  418 
  419    *err_or_null = TRU1;
  420 
  421    /* We need to encode in mUTF-7!  For that, we first have to convert the
  422     * local charset to UTF-8, then convert all characters which need to be
  423     * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
  424     * We can skip the UTF-8 conversion occasionally, however */
  425    if(!(n_psonce & n_PSO_UNICODE)){
  426       int ir;
  427       iconv_t icd;
  428 
  429       emsg = N_("iconv(3) from locale charset to UTF-8 failed");
  430 
  431       if((icd = iconv_open("utf-8", ok_vlook(ttycharset))) == (iconv_t)-1)
  432          goto jerr;
  433 
  434       out.s = NULL, out.l = 0;
  435       in.s = n_UNCONST(cp); /* logical */
  436       l += strlen(&cp[l]);
  437       in.l = l;
  438       if((ir = n_iconv_str(icd, n_ICONV_NONE, &out, &in, NULL)) == 0)
  439          cp = savestrbuf(out.s, out.l);
  440 
  441       if(out.s != NULL)
  442          free(out.s);
  443       iconv_close(icd);
  444 
  445       if(ir != 0)
  446          goto jerr;
  447 
  448       /*
  449        * So: Why not start all over again?
  450        */
  451 
  452       /* Is this a string that works out as "plain US-ASCII"? */
  453       for(l = 0;; ++l)
  454          if((c = cp[l]) == '\0')
  455             goto jleave;
  456          else if(c <= 0x1F || c >= 0x7F || c == '&')
  457             break;
  458    }
  459 
  460    /* We need to encode, save what we have, encode the rest */
  461    l_plain = l;
  462 
  463    for(cp += l, l = 0; cp[l] != '\0'; ++l)
  464       ;
  465    be16p_base = salloc((l << 1) +1); /* XXX use n_string, resize */
  466 
  467    out.s = salloc(l_plain + (l << 2) +1); /* XXX use n_string, resize */
  468    if(l_plain > 0)
  469       memcpy(out.s, &cp[-l_plain], out.l = l_plain);
  470    else
  471       out.l = 0;
  472    DBG( l_plain += (l << 2); )
  473 
  474    while(l > 0){
  475       c = *cp++;
  476       --l;
  477 
  478       if(c == '&'){
  479          out.s[out.l + 0] = '&';
  480          out.s[out.l + 1] = '-';
  481          out.l += 2;
  482       }else if(c > 0x1F && c < 0x7F)
  483          out.s[out.l++] = c;
  484       else{
  485          static char const mb64ct[] =
  486             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
  487          ui32_t utf32;
  488 
  489          /* Convert consecutive non-representables */
  490          emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
  491 
  492          for(be16p = be16p_base, --cp, ++l;;){
  493             if((utf32 = n_utf8_to_utf32(&cp, &l)) == UI32_MAX)
  494                goto jerr;
  495 
  496             /* TODO S-CText: magic utf16 conversions */
  497             if(utf32 < 0x10000){
  498                be16p[1] = utf32 & 0xFF;
  499                be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
  500                be16p += 2;
  501             }else{
  502                ui16_t s7e;
  503 
  504                utf32 -= 0x10000;
  505                s7e = 0xD800u | (utf32 >> 10);
  506                be16p[1] = s7e & 0xFF;
  507                be16p[0] = (s7e >>= 8, s7e &= 0xFF);
  508                s7e = 0xDC00u | (utf32 &= 0x03FF);
  509                be16p[3] = s7e & 0xFF;
  510                be16p[2] = (s7e >>= 8, s7e &= 0xFF);
  511                be16p += 4;
  512             }
  513 
  514             if(l == 0)
  515                break;
  516             if((c = *cp) > 0x1F && c < 0x7F)
  517                break;
  518          }
  519 
  520          /* And then warp that UTF-16BE to mUTF-7 */
  521          out.s[out.l++] = '&';
  522          utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
  523          be16p = be16p_base;
  524 
  525          for(; utf32 >= 3; be16p += 3, utf32 -= 3){
  526             out.s[out.l+0] = mb64ct[                            be16p[0] >> 2 ];
  527             out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
  528             out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
  529             out.s[out.l+3] = mb64ct[  be16p[2] & 0x3F];
  530             out.l += 4;
  531          }
  532          if(utf32 > 0){
  533             out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
  534             if(--utf32 == 0){
  535                out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
  536                out.l += 2;
  537             }else{
  538                out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
  539                      (be16p[1] >> 4)];
  540                out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
  541                out.l += 3;
  542             }
  543          }
  544          out.s[out.l++] = '-';
  545       }
  546    }
  547    out.s[out.l] = '\0';
  548    assert(out.l <= l_plain);
  549    *err_or_null = FAL0;
  550    cp = out.s;
  551 jleave:
  552    NYD2_LEAVE;
  553    return cp;
  554 jerr:
  555    n_err(_("Cannot encode IMAP path %s\n  %s\n"), cp, V_(emsg));
  556    goto jleave;
  557 }
  558 
  559 FL char *
  560 imap_path_decode(char const *path, bool_t *err_or_null){
  561    /* To a large extend inspired by dovecot(1) TODO use string */
  562    struct str in, out;
  563    bool_t err_def;
  564    ui8_t *mb64p_base, *mb64p, *mb64xp;
  565    char const *emsg, *cp;
  566    char *rv_base, *rv, c;
  567    size_t l_orig, l, i;
  568    NYD2_ENTER;
  569 
  570    if(err_or_null == NULL)
  571       err_or_null = &err_def;
  572    *err_or_null = FAL0;
  573 
  574    l = l_orig = strlen(path);
  575    rv = rv_base = salloc(l << 1);
  576    memcpy(rv, path, l +1);
  577 
  578    /* xxx Don't check for invalid characters from malicious servers */
  579    if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
  580       goto jleave;
  581 
  582    *err_or_null = TRU1;
  583 
  584    emsg = N_("Invalid mUTF-7 encoding");
  585    i = PTR2SIZE(cp - path);
  586    rv += i;
  587    l -= i;
  588    mb64p_base = NULL;
  589 
  590    while(l > 0){
  591       if((c = *cp) != '&'){
  592          if(c <= 0x1F || c >= 0x7F){
  593             emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
  594             goto jerr;
  595          }
  596          *rv++ = c;
  597          ++cp;
  598          --l;
  599       }else if(--l == 0)
  600          goto jeincpl;
  601       else if(*++cp == '-'){
  602          *rv++ = '&';
  603          ++cp;
  604          --l;
  605       }else if(l < 3){
  606 jeincpl:
  607          emsg = N_("Invalid mUTF-7: incomplete input");
  608          goto jerr;
  609       }else{
  610          /* mUTF-7 -> UTF-16BE -> UTF-8 */
  611          static ui8_t const mb64dt[256] = {
  612 #undef XX
  613 #define XX 0xFFu
  614             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  615             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  616             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
  617             52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
  618             XX, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
  619             15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
  620             XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
  621             41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
  622             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  623             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  624             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  625             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  626             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  627             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  628             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
  629             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
  630          };
  631 
  632          if(mb64p_base == NULL)
  633             mb64p_base = salloc(l);
  634 
  635          /* Decode the mUTF-7 to what is indeed UTF-16BE */
  636          for(mb64p = mb64p_base;;){
  637             assert(l >= 3);
  638             if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
  639                   (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
  640                goto jerr;
  641             mb64p += 2;
  642 
  643             c = cp[2];
  644             cp += 3;
  645             l -= 3;
  646             if(c == '-')
  647                break;
  648             if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
  649                goto jerr;
  650 
  651             if(l == 0)
  652                goto jerr;
  653             --l;
  654             if((c = *cp++) == '-')
  655                break;
  656             if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
  657                goto jerr;
  658 
  659             if(l < 3){
  660                if(l > 0 && *cp == '-'){
  661                   --l;
  662                   ++cp;
  663                   break;
  664                }
  665                goto jerr;
  666             }
  667          }
  668 #undef XX
  669 
  670          if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
  671             emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
  672             goto jerr;
  673          }
  674 
  675          /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
  676          i = PTR2SIZE(mb64p - mb64p_base);
  677          mb64p = mb64xp = mb64p_base;
  678 
  679          while(i > 0){
  680             ui8_t ul, u0, u1, u2, u3;
  681 
  682             ul = (i >= 4) ? 4 : i & 0x3;
  683             i -= ul;
  684             u0 = mb64xp[0];
  685             u1 = mb64xp[1];
  686             u2 = (ul < 3) ? 0 : mb64xp[2];
  687             u3 = (ul < 4) ? 0 : mb64xp[3];
  688             mb64xp += ul;
  689             *mb64p++ = (u0 <<= 2) | (u1 >> 4);
  690             if(ul < 3)
  691                break;
  692             *mb64p++ = (u1 <<= 4) | (u2 >> 2);
  693             if(ul < 4)
  694                break;
  695             *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
  696          }
  697 
  698          /* UTF-16BE we convert to UTF-8 */
  699          i = PTR2SIZE(mb64p - mb64p_base);
  700          if(i & 1){
  701             emsg = N_("Odd bytecount for UTF-16BE input");
  702             goto jerr;
  703          }
  704 
  705          /* TODO S-CText: magic utf16 conversions */
  706          emsg = N_("Invalid UTF-16BE encoding");
  707 
  708          for(mb64p = mb64p_base; i > 0;){
  709             ui32_t utf32;
  710             ui16_t uhi, ulo;
  711 
  712             uhi = mb64p[0];
  713             uhi <<= 8;
  714             uhi |= mb64p[1];
  715 
  716             /* Not a surrogate? */
  717             if(uhi < 0xD800 || uhi > 0xDFFF){
  718                utf32 = uhi;
  719                mb64p += 2;
  720                i -= 2;
  721             }else if(uhi > 0xDBFF)
  722                goto jerr;
  723             else if(i < 4){
  724                emsg = N_("Incomplete UTF-16BE surrogate pair");
  725                goto jerr;
  726             }else{
  727                ulo = mb64p[2];
  728                ulo <<= 8;
  729                ulo |= mb64p[3];
  730                if(ulo < 0xDC00 || ulo > 0xDFFF)
  731                   goto jerr;
  732 
  733                utf32 = (uhi &= 0x03FF);
  734                utf32 <<= 10;
  735                utf32 += 0x10000;
  736                utf32 |= (ulo &= 0x03FF);
  737                mb64p += 4;
  738                i -= 4;
  739             }
  740 
  741             utf32 = n_utf32_to_utf8(utf32, rv);
  742             rv += utf32;
  743          }
  744       }
  745    }
  746    *rv = '\0';
  747 
  748    /* We can skip the UTF-8 conversion occasionally */
  749    if(!(n_psonce & n_PSO_UNICODE)){
  750       int ir;
  751       iconv_t icd;
  752 
  753       emsg = N_("iconv(3) from UTF-8 to locale charset failed");
  754 
  755       if((icd = iconv_open(ok_vlook(ttycharset), "utf-8")) == (iconv_t)-1)
  756          goto jerr;
  757 
  758       out.s = NULL, out.l = 0;
  759       in.l = strlen(in.s = rv_base);
  760       if((ir = n_iconv_str(icd, n_ICONV_NONE, &out, &in, NULL)) == 0)
  761          /* Because the length of this is unpredictable, copy */
  762          rv_base = savestrbuf(out.s, out.l);
  763 
  764       if(out.s != NULL)
  765          free(out.s);
  766       iconv_close(icd);
  767 
  768       if(ir != 0)
  769          goto jerr;
  770    }
  771 
  772    *err_or_null = FAL0;
  773    rv = rv_base;
  774 jleave:
  775    NYD2_LEAVE;
  776    return rv;
  777 jerr:
  778    n_err(_("Cannot decode IMAP path %s\n  %s\n"), path, V_(emsg));
  779    memcpy(rv = rv_base, path, ++l_orig);
  780    goto jleave;
  781 }
  782 
  783 static char *
  784 imap_path_quote(struct mailbox *mp, char const *cp){
  785    bool_t err;
  786    char *rv;
  787    NYD2_ENTER;
  788 
  789    cp = imap_path_normalize(mp, cp);
  790    cp = imap_path_encode(cp, &err);
  791    rv = err ? NULL : imap_quotestr(cp);
  792    NYD2_LEAVE;
  793    return rv;
  794 }
  795 
  796 static void
  797 imap_other_get(char *pp)
  798 {
  799    char *xp;
  800    NYD2_ENTER;
  801 
  802    if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
  803       pp += 6;
  804       response_other = MAILBOX_DATA_FLAGS;
  805    } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
  806       pp += 5;
  807       response_other = MAILBOX_DATA_LIST;
  808    } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
  809       pp += 5;
  810       response_other = MAILBOX_DATA_LSUB;
  811    } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
  812       pp += 8;
  813       response_other = MAILBOX_DATA_MAILBOX;
  814    } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
  815       pp += 7;
  816       response_other = MAILBOX_DATA_SEARCH;
  817    } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
  818       pp += 7;
  819       response_other = MAILBOX_DATA_STATUS;
  820    } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
  821       pp += 11;
  822       response_other = CAPABILITY_DATA;
  823    } else {
  824       responded_other_number = strtol(pp, &xp, 10);
  825       while (*xp == ' ')
  826          ++xp;
  827       if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
  828          response_other = MAILBOX_DATA_EXISTS;
  829       } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
  830          response_other = MAILBOX_DATA_RECENT;
  831       } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
  832          response_other = MESSAGE_DATA_EXPUNGE;
  833       } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
  834          pp = &xp[6];
  835          response_other = MESSAGE_DATA_FETCH;
  836       } else
  837          response_other = RESPONSE_OTHER_UNKNOWN;
  838    }
  839    responded_other_text = pp;
  840    NYD2_LEAVE;
  841 }
  842 
  843 static void
  844 imap_response_get(const char **cp)
  845 {
  846    NYD2_ENTER;
  847    if (ascncasecmp(*cp, "OK ", 3) == 0) {
  848       *cp += 3;
  849       response_status = RESPONSE_OK;
  850    } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
  851       *cp += 3;
  852       response_status = RESPONSE_NO;
  853    } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
  854       *cp += 4;
  855       response_status = RESPONSE_BAD;
  856    } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
  857       *cp += 8;
  858       response_status = RESPONSE_PREAUTH;
  859    } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
  860       *cp += 4;
  861       response_status = RESPONSE_BYE;
  862    } else
  863       response_status = RESPONSE_OTHER;
  864    NYD2_LEAVE;
  865 }
  866 
  867 static void
  868 imap_response_parse(void)
  869 {
  870    static char *parsebuf; /* TODO Use pool */
  871    static size_t  parsebufsize;
  872 
  873    const char *ip = imapbuf;
  874    char *pp;
  875    NYD2_ENTER;
  876 
  877    if (parsebufsize < imapbufsize + 1)
  878       parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
  879    memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
  880    pp = parsebuf;
  881    switch (*ip) {
  882    case '+':
  883       response_type = RESPONSE_CONT;
  884       ip++;
  885       pp++;
  886       while (*ip == ' ') {
  887          ip++;
  888          pp++;
  889       }
  890       break;
  891    case '*':
  892       ip++;
  893       pp++;
  894       while (*ip == ' ') {
  895          ip++;
  896          pp++;
  897       }
  898       imap_response_get(&ip);
  899       pp = &parsebuf[ip - imapbuf];
  900       switch (response_status) {
  901       case RESPONSE_BYE:
  902          response_type = RESPONSE_FATAL;
  903          break;
  904       default:
  905          response_type = RESPONSE_DATA;
  906       }
  907       break;
  908    default:
  909       responded_tag = parsebuf;
  910       while (*pp && *pp != ' ')
  911          pp++;
  912       if (*pp == '\0') {
  913          response_type = RESPONSE_ILLEGAL;
  914          break;
  915       }
  916       *pp++ = '\0';
  917       while (*pp && *pp == ' ')
  918          pp++;
  919       if (*pp == '\0') {
  920          response_type = RESPONSE_ILLEGAL;
  921          break;
  922       }
  923       ip = &imapbuf[pp - parsebuf];
  924       response_type = RESPONSE_TAGGED;
  925       imap_response_get(&ip);
  926       pp = &parsebuf[ip - imapbuf];
  927    }
  928    responded_text = pp;
  929    if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
  930          response_status == RESPONSE_OTHER)
  931       imap_other_get(pp);
  932    NYD2_LEAVE;
  933 }
  934 
  935 static enum okay
  936 imap_answer(struct mailbox *mp, int errprnt)
  937 {
  938    int i, complete;
  939    enum okay rv;
  940    NYD2_ENTER;
  941 
  942    rv = OKAY;
  943    if (mp->mb_type == MB_CACHE)
  944       goto jleave;
  945    rv = STOP;
  946 jagain:
  947    if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
  948       if (n_poption & n_PO_VERBVERB)
  949          fputs(imapbuf, stderr);
  950       imap_response_parse();
  951       if (response_type == RESPONSE_ILLEGAL)
  952          goto jagain;
  953       if (response_type == RESPONSE_CONT) {
  954          rv = OKAY;
  955          goto jleave;
  956       }
  957       if (response_status == RESPONSE_OTHER) {
  958          if (response_other == MAILBOX_DATA_EXISTS) {
  959             had_exists = responded_other_number;
  960             rec_queue(REC_EXISTS, responded_other_number);
  961             if (had_expunge > 0)
  962                had_expunge = 0;
  963          } else if (response_other == MESSAGE_DATA_EXPUNGE) {
  964             rec_queue(REC_EXPUNGE, responded_other_number);
  965             if (had_expunge < 0)
  966                had_expunge = 0;
  967             had_expunge++;
  968             expunged_messages++;
  969          }
  970       }
  971       complete = 0;
  972       if (response_type == RESPONSE_TAGGED) {
  973          if (asccasecmp(responded_tag, tag(0)) == 0)
  974             complete |= 1;
  975          else
  976             goto jagain;
  977       }
  978       switch (response_status) {
  979       case RESPONSE_PREAUTH:
  980          mp->mb_active &= ~MB_PREAUTH;
  981          /*FALLTHRU*/
  982       case RESPONSE_OK:
  983 jokay:
  984          rv = OKAY;
  985          complete |= 2;
  986          break;
  987       case RESPONSE_NO:
  988       case RESPONSE_BAD:
  989 jstop:
  990          complete |= 2;
  991          if (errprnt)
  992             n_err(_("IMAP error: %s"), responded_text);
  993          break;
  994       case RESPONSE_UNKNOWN:  /* does not happen */
  995       case RESPONSE_BYE:
  996          i = mp->mb_active;
  997          mp->mb_active = MB_NONE;
  998          if (i & MB_BYE)
  999             goto jokay;
 1000          goto jstop;
 1001       case RESPONSE_OTHER:
 1002          rv = OKAY;
 1003          break;
 1004       }
 1005       if (response_status != RESPONSE_OTHER &&
 1006             ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
 1007          n_err(_("IMAP alert: %s"), &responded_text[8]);
 1008       if (complete == 3)
 1009          mp->mb_active &= ~MB_COMD;
 1010    } else
 1011       mp->mb_active = MB_NONE;
 1012 jleave:
 1013    NYD2_LEAVE;
 1014    return rv;
 1015 }
 1016 
 1017 static enum okay
 1018 imap_parse_list(void)
 1019 {
 1020    char *cp;
 1021    enum okay rv;
 1022    NYD2_ENTER;
 1023 
 1024    rv = STOP;
 1025 
 1026    cp = responded_other_text;
 1027    list_attributes = LIST_NONE;
 1028    if (*cp == '(') {
 1029       while (*cp && *cp != ')') {
 1030          if (*cp == '\\') {
 1031             if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
 1032                list_attributes |= LIST_NOINFERIORS;
 1033                cp += 12;
 1034             } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
 1035                list_attributes |= LIST_NOSELECT;
 1036                cp += 9;
 1037             } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
 1038                list_attributes |= LIST_MARKED;
 1039                cp += 7;
 1040             } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
 1041                list_attributes |= LIST_UNMARKED;
 1042                cp += 9;
 1043             }
 1044          }
 1045          cp++;
 1046       }
 1047       if (*++cp != ' ')
 1048          goto jleave;
 1049       while (*cp == ' ')
 1050          cp++;
 1051    }
 1052 
 1053    list_hierarchy_delimiter = EOF;
 1054    if (*cp == '"') {
 1055       if (*++cp == '\\')
 1056          cp++;
 1057       list_hierarchy_delimiter = *cp++ & 0377;
 1058       if (cp[0] != '"' || cp[1] != ' ')
 1059          goto jleave;
 1060       cp++;
 1061    } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
 1062       list_hierarchy_delimiter = EOF;
 1063       cp += 3;
 1064    }
 1065 
 1066    while (*cp == ' ')
 1067       cp++;
 1068    list_name = cp;
 1069    while (*cp && *cp != '\r')
 1070       cp++;
 1071    *cp = '\0';
 1072    rv = OKAY;
 1073 jleave:
 1074    NYD2_LEAVE;
 1075    return rv;
 1076 }
 1077 
 1078 static enum okay
 1079 imap_finish(struct mailbox *mp)
 1080 {
 1081    NYD_ENTER;
 1082    while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
 1083       imap_answer(mp, 1);
 1084    NYD_LEAVE;
 1085    return OKAY;
 1086 }
 1087 
 1088 static void
 1089 imap_timer_off(void)
 1090 {
 1091    NYD_ENTER;
 1092    if (imapkeepalive > 0) {
 1093       alarm(0);
 1094       safe_signal(SIGALRM, savealrm);
 1095    }
 1096    NYD_LEAVE;
 1097 }
 1098 
 1099 static void
 1100 imapcatch(int s)
 1101 {
 1102    NYD_X; /*  Signal handler */
 1103    switch (s) {
 1104    case SIGINT:
 1105       n_err_sighdl(_("Interrupt\n"));
 1106       siglongjmp(imapjmp, 1);
 1107       /*NOTREACHED*/
 1108    case SIGPIPE:
 1109       n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
 1110       break;
 1111    }
 1112 }
 1113 
 1114 static void
 1115 _imap_maincatch(int s)
 1116 {
 1117    NYD_X; /*  Signal handler */
 1118    n_UNUSED(s);
 1119    if (interrupts++ == 0) {
 1120       n_err_sighdl(_("Interrupt\n"));
 1121       return;
 1122    }
 1123    n_go_onintr_for_imap();
 1124 }
 1125 
 1126 static enum okay
 1127 imap_noop1(struct mailbox *mp)
 1128 {
 1129    char o[LINESIZE];
 1130    FILE *queuefp = NULL;
 1131    NYD_X;
 1132 
 1133    snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
 1134    IMAP_OUT(o, MB_COMD, return STOP)
 1135    IMAP_ANSWER()
 1136    return OKAY;
 1137 }
 1138 
 1139 FL char const *
 1140 imap_fileof(char const *xcp)
 1141 {
 1142    char const *cp = xcp;
 1143    int state = 0;
 1144    NYD_ENTER;
 1145 
 1146    while (*cp) {
 1147       if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
 1148          cp += 3;
 1149          state = 1;
 1150       }
 1151       if (cp[0] == '/' && state == 1) {
 1152          ++cp;
 1153          goto jleave;
 1154       }
 1155       if (cp[0] == '/') {
 1156          cp = xcp;
 1157          goto jleave;
 1158       }
 1159       ++cp;
 1160    }
 1161 jleave:
 1162    NYD_LEAVE;
 1163    return cp;
 1164 }
 1165 
 1166 FL enum okay
 1167 imap_noop(void)
 1168 {
 1169    sighandler_type volatile oldint, oldpipe;
 1170    enum okay volatile rv = STOP;
 1171    NYD_ENTER;
 1172 
 1173    if (mb.mb_type != MB_IMAP)
 1174       goto jleave;
 1175 
 1176    imaplock = 1;
 1177    if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 1178       safe_signal(SIGINT, &_imap_maincatch);
 1179    oldpipe = safe_signal(SIGPIPE, SIG_IGN);
 1180    if (sigsetjmp(imapjmp, 1) == 0) {
 1181       if (oldpipe != SIG_IGN)
 1182          safe_signal(SIGPIPE, imapcatch);
 1183 
 1184       rv = imap_noop1(&mb);
 1185    }
 1186    safe_signal(SIGINT, oldint);
 1187    safe_signal(SIGPIPE, oldpipe);
 1188    imaplock = 0;
 1189 jleave:
 1190    NYD_LEAVE;
 1191    if (interrupts)
 1192       n_go_onintr_for_imap();
 1193    return rv;
 1194 }
 1195 
 1196 static void
 1197 rec_queue(enum rec_type rt, unsigned long cnt)
 1198 {
 1199    struct record *rp;
 1200    NYD_ENTER;
 1201 
 1202    rp = scalloc(1, sizeof *rp);
 1203    rp->rec_type = rt;
 1204    rp->rec_count = cnt;
 1205    if (record && recend) {
 1206       recend->rec_next = rp;
 1207       recend = rp;
 1208    } else
 1209       record = recend = rp;
 1210    NYD_LEAVE;
 1211 }
 1212 
 1213 static enum okay
 1214 rec_dequeue(void)
 1215 {
 1216    struct message *omessage;
 1217    struct record *rp, *rq;
 1218    uiz_t exists = 0, i;
 1219    enum okay rv = STOP;
 1220    NYD_ENTER;
 1221 
 1222    if (record == NULL)
 1223       goto jleave;
 1224 
 1225    omessage = message;
 1226    message = smalloc((msgCount+1) * sizeof *message);
 1227    if (msgCount)
 1228       memcpy(message, omessage, msgCount * sizeof *message);
 1229    memset(&message[msgCount], 0, sizeof *message);
 1230 
 1231    rp = record, rq = NULL;
 1232    rv = OKAY;
 1233    while (rp != NULL) {
 1234       switch (rp->rec_type) {
 1235       case REC_EXISTS:
 1236          exists = rp->rec_count;
 1237          break;
 1238       case REC_EXPUNGE:
 1239          if (rp->rec_count == 0) {
 1240             rv = STOP;
 1241             break;
 1242          }
 1243          if (rp->rec_count > (unsigned long)msgCount) {
 1244             if (exists == 0 || rp->rec_count > exists--)
 1245                rv = STOP;
 1246             break;
 1247          }
 1248          if (exists > 0)
 1249             exists--;
 1250          delcache(&mb, &message[rp->rec_count-1]);
 1251          memmove(&message[rp->rec_count-1], &message[rp->rec_count],
 1252             ((msgCount - rp->rec_count + 1) * sizeof *message));
 1253          --msgCount;
 1254          /* If the message was part of a collapsed thread,
 1255           * the m_collapsed field of one of its ancestors
 1256           * should be incremented. It seems hardly possible
 1257           * to do this with the current message structure,
 1258           * though. The result is that a '+' may be shown
 1259           * in the header summary even if no collapsed
 1260           * children exists */
 1261          break;
 1262       }
 1263       if (rq != NULL)
 1264          free(rq);
 1265       rq = rp;
 1266       rp = rp->rec_next;
 1267    }
 1268    if (rq != NULL)
 1269       free(rq);
 1270 
 1271    record = recend = NULL;
 1272    if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
 1273       message = srealloc(message, (exists + 1) * sizeof *message);
 1274       memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
 1275       for (i = msgCount; i < exists; ++i)
 1276          imap_init(&mb, i);
 1277       imap_flags(&mb, msgCount+1, exists);
 1278       msgCount = exists;
 1279    }
 1280 
 1281    if (rv == STOP) {
 1282       free(message);
 1283       message = omessage;
 1284    }
 1285 jleave:
 1286    NYD_LEAVE;
 1287    return rv;
 1288 }
 1289 
 1290 static void
 1291 rec_rmqueue(void)
 1292 {
 1293    struct record *rp;
 1294    NYD_ENTER;
 1295 
 1296    for (rp = record; rp != NULL;) {
 1297       struct record *tmp = rp;
 1298       rp = rp->rec_next;
 1299       free(tmp);
 1300    }
 1301    record = recend = NULL;
 1302    NYD_LEAVE;
 1303 }
 1304 
 1305 /*ARGSUSED*/
 1306 static void
 1307 imapalarm(int s)
 1308 {
 1309    sighandler_type volatile saveint, savepipe;
 1310    NYD_X; /* Signal handler */
 1311    n_UNUSED(s);
 1312 
 1313    if (imaplock++ == 0) {
 1314       if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 1315          safe_signal(SIGINT, &_imap_maincatch);
 1316       savepipe = safe_signal(SIGPIPE, SIG_IGN);
 1317       if (sigsetjmp(imapjmp, 1)) {
 1318          safe_signal(SIGINT, saveint);
 1319          safe_signal(SIGPIPE, savepipe);
 1320          goto jbrk;
 1321       }
 1322       if (savepipe != SIG_IGN)
 1323          safe_signal(SIGPIPE, imapcatch);
 1324       if (imap_noop1(&mb) != OKAY) {
 1325          safe_signal(SIGINT, saveint);
 1326          safe_signal(SIGPIPE, savepipe);
 1327          goto jleave;
 1328       }
 1329       safe_signal(SIGINT, saveint);
 1330       safe_signal(SIGPIPE, savepipe);
 1331    }
 1332 jbrk:
 1333    alarm(imapkeepalive);
 1334 jleave:
 1335    --imaplock;
 1336 }
 1337 
 1338 static enum okay
 1339 imap_preauth(struct mailbox *mp, struct url const *urlp)
 1340 {
 1341    NYD_X;
 1342 
 1343    mp->mb_active |= MB_PREAUTH;
 1344    imap_answer(mp, 1);
 1345 
 1346 #ifdef HAVE_SSL
 1347    if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
 1348       FILE *queuefp = NULL;
 1349       char o[LINESIZE];
 1350 
 1351       snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
 1352       IMAP_OUT(o, MB_COMD, return STOP)
 1353       IMAP_ANSWER()
 1354       if (ssl_open(urlp, &mp->mb_sock) != OKAY)
 1355          return STOP;
 1356    }
 1357 #else
 1358    if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
 1359       n_err(_("No SSL support compiled in\n"));
 1360       return STOP;
 1361    }
 1362 #endif
 1363 
 1364    imap_capability(mp);
 1365    return OKAY;
 1366 }
 1367 
 1368 static enum okay
 1369 imap_capability(struct mailbox *mp)
 1370 {
 1371    char o[LINESIZE];
 1372    FILE *queuefp = NULL;
 1373    enum okay ok = STOP;
 1374    const char *cp;
 1375    NYD_X;
 1376 
 1377    snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
 1378    IMAP_OUT(o, MB_COMD, return STOP)
 1379    while (mp->mb_active & MB_COMD) {
 1380       ok = imap_answer(mp, 0);
 1381       if (response_status == RESPONSE_OTHER &&
 1382             response_other == CAPABILITY_DATA) {
 1383          cp = responded_other_text;
 1384          while (*cp) {
 1385             while (spacechar(*cp))
 1386                ++cp;
 1387             if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
 1388                /* RFC 2359 */
 1389                mp->mb_flags |= MB_UIDPLUS;
 1390             while (*cp && !spacechar(*cp))
 1391                ++cp;
 1392          }
 1393       }
 1394    }
 1395    return ok;
 1396 }
 1397 
 1398 static enum okay
 1399 imap_auth(struct mailbox *mp, struct ccred *ccred)
 1400 {
 1401    enum okay rv;
 1402    NYD_ENTER;
 1403 
 1404    if (!(mp->mb_active & MB_PREAUTH)) {
 1405       rv = OKAY;
 1406       goto jleave;
 1407    }
 1408 
 1409    switch (ccred->cc_authtype) {
 1410    case AUTHTYPE_LOGIN:
 1411       rv = imap_login(mp, ccred);
 1412       break;
 1413 #ifdef HAVE_MD5
 1414    case AUTHTYPE_CRAM_MD5:
 1415       rv = imap_cram_md5(mp, ccred);
 1416       break;
 1417 #endif
 1418 #ifdef HAVE_GSSAPI
 1419    case AUTHTYPE_GSSAPI:
 1420       rv = _imap_gssapi(mp, ccred);
 1421       break;
 1422 #endif
 1423    default:
 1424       rv = STOP;
 1425       break;
 1426    }
 1427 jleave:
 1428    NYD_LEAVE;
 1429    return rv;
 1430 }
 1431 
 1432 #ifdef HAVE_MD5
 1433 static enum okay
 1434 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
 1435 {
 1436    char o[LINESIZE], *cp;
 1437    FILE *queuefp = NULL;
 1438    enum okay rv = STOP;
 1439    NYD_ENTER;
 1440 
 1441    snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
 1442    IMAP_XOUT(o, 0, goto jleave, goto jleave);
 1443    imap_answer(mp, 1);
 1444    if (response_type != RESPONSE_CONT)
 1445       goto jleave;
 1446 
 1447    cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
 1448    if(cp == NULL)
 1449       goto jleave;
 1450    IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
 1451    while (mp->mb_active & MB_COMD)
 1452       rv = imap_answer(mp, 1);
 1453 jleave:
 1454    NYD_LEAVE;
 1455    return rv;
 1456 }
 1457 #endif /* HAVE_MD5 */
 1458 
 1459 static enum okay
 1460 imap_login(struct mailbox *mp, struct ccred *ccred)
 1461 {
 1462    char o[LINESIZE];
 1463    FILE *queuefp = NULL;
 1464    enum okay rv = STOP;
 1465    NYD_ENTER;
 1466 
 1467    snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
 1468       tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
 1469    IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
 1470    while (mp->mb_active & MB_COMD)
 1471       rv = imap_answer(mp, 1);
 1472 jleave:
 1473    NYD_LEAVE;
 1474    return rv;
 1475 }
 1476 
 1477 #ifdef HAVE_GSSAPI
 1478 # include "obs-imap-gssapi.h"
 1479 #endif
 1480 
 1481 FL enum okay
 1482 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
 1483    enum fedit_mode fm)
 1484 {
 1485    char o[LINESIZE];
 1486    char const *qname, *cp;
 1487    FILE *queuefp;
 1488    enum okay ok;
 1489    NYD_X;
 1490    n_UNUSED(size);
 1491 
 1492    ok = STOP;
 1493    queuefp = NULL;
 1494 
 1495    if((qname = imap_path_quote(mp, mbx)) == NULL)
 1496       goto jleave;
 1497 
 1498    ok = OKAY;
 1499 
 1500    mp->mb_uidvalidity = 0;
 1501    snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
 1502       (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
 1503    IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
 1504    while (mp->mb_active & MB_COMD) {
 1505       ok = imap_answer(mp, 1);
 1506       if (response_status != RESPONSE_OTHER &&
 1507             (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
 1508          mp->mb_uidvalidity = atol(&cp[13]);
 1509    }
 1510    *cnt = (had_exists > 0) ? had_exists : 0;
 1511    if (response_status != RESPONSE_OTHER &&
 1512          ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
 1513       mp->mb_perm = 0;
 1514 jleave:
 1515    return ok;
 1516 }
 1517 
 1518 static enum okay
 1519 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
 1520 {
 1521    char o[LINESIZE];
 1522    FILE *queuefp = NULL;
 1523    char const *cp;
 1524    struct message *m;
 1525    unsigned x = X, y = Y, n;
 1526    NYD_X;
 1527 
 1528    snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
 1529    IMAP_OUT(o, MB_COMD, return STOP)
 1530    while (mp->mb_active & MB_COMD) {
 1531       imap_answer(mp, 1);
 1532       if (response_status == RESPONSE_OTHER &&
 1533             response_other == MESSAGE_DATA_FETCH) {
 1534          n = responded_other_number;
 1535          if (n < x || n > y)
 1536             continue;
 1537          m = &message[n-1];
 1538          m->m_xsize = 0;
 1539       } else
 1540          continue;
 1541 
 1542       if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
 1543          cp += 5;
 1544          while (*cp == ' ')
 1545             cp++;
 1546          if (*cp == '(')
 1547             imap_getflags(cp, &cp, &m->m_flag);
 1548       }
 1549 
 1550       if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
 1551          m->m_uid = strtoul(&cp[4], NULL, 10);
 1552       getcache1(mp, m, NEED_UNSPEC, 1);
 1553       m->m_flag &= ~MHIDDEN;
 1554    }
 1555 
 1556    while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
 1557       x++;
 1558    while (y > x && message[y-1].m_xsize && message[y-1].m_time)
 1559       y--;
 1560    if (x <= y) {
 1561       snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
 1562          tag(1), x, y);
 1563       IMAP_OUT(o, MB_COMD, return STOP)
 1564       while (mp->mb_active & MB_COMD) {
 1565          imap_answer(mp, 1);
 1566          if (response_status == RESPONSE_OTHER &&
 1567                response_other == MESSAGE_DATA_FETCH) {
 1568             n = responded_other_number;
 1569             if (n < x || n > y)
 1570                continue;
 1571             m = &message[n-1];
 1572          } else
 1573             continue;
 1574          if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
 1575             m->m_xsize = strtol(&cp[12], NULL, 10);
 1576          if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
 1577             m->m_time = imap_read_date_time(&cp[13]);
 1578       }
 1579    }
 1580 
 1581    srelax_hold();
 1582    for (n = X; n <= Y; ++n) {
 1583       putcache(mp, &message[n-1]);
 1584       srelax();
 1585    }
 1586    srelax_rele();
 1587    return OKAY;
 1588 }
 1589 
 1590 static void
 1591 imap_init(struct mailbox *mp, int n)
 1592 {
 1593    struct message *m;
 1594    NYD_ENTER;
 1595    n_UNUSED(mp);
 1596 
 1597    m = message + n;
 1598    m->m_flag = MUSED | MNOFROM;
 1599    m->m_block = 0;
 1600    m->m_offset = 0;
 1601    NYD_LEAVE;
 1602 }
 1603 
 1604 static void
 1605 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
 1606 {
 1607    struct message *omessage = 0;
 1608    int i, omsgCount = 0;
 1609    enum okay dequeued = STOP;
 1610    NYD_ENTER;
 1611 
 1612    if (nmail || transparent) {
 1613       omessage = message;
 1614       omsgCount = msgCount;
 1615    }
 1616    if (nmail)
 1617       dequeued = rec_dequeue();
 1618 
 1619    if (had_exists >= 0) {
 1620       if (dequeued != OKAY)
 1621          msgCount = had_exists;
 1622       had_exists = -1;
 1623    }
 1624    if (had_expunge >= 0) {
 1625       if (dequeued != OKAY)
 1626          msgCount -= had_expunge;
 1627       had_expunge = -1;
 1628    }
 1629 
 1630    if (nmail && expunged_messages)
 1631       printf("Expunged %ld message%s.\n", expunged_messages,
 1632          (expunged_messages != 1 ? "s" : ""));
 1633    *prevcount = omsgCount - expunged_messages;
 1634    expunged_messages = 0;
 1635    if (msgCount < 0) {
 1636       fputs("IMAP error: Negative message count\n", stderr);
 1637       msgCount = 0;
 1638    }
 1639 
 1640    if (dequeued != OKAY) {
 1641       message = scalloc(msgCount + 1, sizeof *message);
 1642       for (i = 0; i < msgCount; i++)
 1643          imap_init(mp, i);
 1644       if (!nmail && mp->mb_type == MB_IMAP)
 1645          initcache(mp);
 1646       if (msgCount > 0)
 1647          imap_flags(mp, 1, msgCount);
 1648       message[msgCount].m_size = 0;
 1649       message[msgCount].m_lines = 0;
 1650       rec_rmqueue();
 1651    }
 1652    if (nmail || transparent)
 1653       transflags(omessage, omsgCount, transparent);
 1654    else
 1655       setdot(message);
 1656    NYD_LEAVE;
 1657 }
 1658 
 1659 FL int
 1660 imap_setfile(const char *xserver, enum fedit_mode fm)
 1661 {
 1662    struct url url;
 1663    int rv;
 1664    NYD_ENTER;
 1665 
 1666    if (!url_parse(&url, CPROTO_IMAP, xserver)) {
 1667       rv = 1;
 1668       goto jleave;
 1669    }
 1670    if (!ok_blook(v15_compat) &&
 1671          (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
 1672       n_err(_("New-style URL used without *v15-compat* being set!\n"));
 1673 
 1674    _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
 1675    rv = _imap_setfile1(&url, fm, 0);
 1676 jleave:
 1677    NYD_LEAVE;
 1678    return rv;
 1679 }
 1680 
 1681 static bool_t
 1682 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
 1683 {
 1684    bool_t rv = FAL0;
 1685    NYD_ENTER;
 1686 
 1687    if (ok_blook(v15_compat))
 1688       rv = ccred_lookup(ccredp, urlp);
 1689    else {
 1690       char *var, *old,
 1691          *xuhp = ((urlp->url_flags & n_URL_HAD_USER) ? urlp->url_eu_h_p.s
 1692                : urlp->url_u_h_p.s);
 1693 
 1694       if ((var = mbp->mb_imap_pass) != NULL) {
 1695          var = savecat("password-", xuhp);
 1696          if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
 1697             old = sstrdup(old);
 1698          n_var_vset(var, (uintptr_t)mbp->mb_imap_pass);
 1699       }
 1700       rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
 1701       if (var != NULL) {
 1702          if (old != NULL) {
 1703             n_var_vset(var, (uintptr_t)old);
 1704             free(old);
 1705          } else
 1706             n_var_vclear(var);
 1707       }
 1708    }
 1709 
 1710    NYD_LEAVE;
 1711    return rv;
 1712 }
 1713 
 1714 static int
 1715 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
 1716    int volatile transparent)
 1717 {
 1718    struct sock so;
 1719    struct ccred ccred;
 1720    sighandler_type volatile saveint, savepipe;
 1721    char const *cp;
 1722    int rv;
 1723    int volatile prevcount = 0;
 1724    enum mbflags same_flags;
 1725    NYD_ENTER;
 1726 
 1727    if (fm & FEDIT_NEWMAIL) {
 1728       saveint = safe_signal(SIGINT, SIG_IGN);
 1729       savepipe = safe_signal(SIGPIPE, SIG_IGN);
 1730       if (saveint != SIG_IGN)
 1731          safe_signal(SIGINT, imapcatch);
 1732       if (savepipe != SIG_IGN)
 1733          safe_signal(SIGPIPE, imapcatch);
 1734       imaplock = 1;
 1735       goto jnmail;
 1736    }
 1737 
 1738    same_flags = mb.mb_flags;
 1739    same_imap_account = 0;
 1740    if (mb.mb_imap_account != NULL &&
 1741          (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
 1742       if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
 1743             !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
 1744             disconnected(mb.mb_imap_account) == 0) {
 1745          same_imap_account = 1;
 1746          if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
 1747 /*
 1748             goto jduppass;
 1749       } else if ((transparent || mb.mb_type == MB_CACHE) &&
 1750             !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
 1751             urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
 1752 jduppass:
 1753 */
 1754          urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
 1755       }
 1756    }
 1757 
 1758    if (!same_imap_account && mb.mb_imap_pass != NULL) {
 1759       free(mb.mb_imap_pass);
 1760       mb.mb_imap_pass = NULL;
 1761    }
 1762    if (!_imap_getcred(&mb, &ccred, urlp)) {
 1763       rv = -1;
 1764       goto jleave;
 1765    }
 1766 
 1767    memset(&so, 0, sizeof so);
 1768    so.s_fd = -1;
 1769    if (!same_imap_account) {
 1770       if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
 1771          rv = -1;
 1772          goto jleave;
 1773       }
 1774    } else
 1775       so = mb.mb_sock;
 1776    if (!transparent) {
 1777       if(!quit(FAL0)){
 1778          rv = -1;
 1779          goto jleave;
 1780       }
 1781    }
 1782 
 1783    if (fm & FEDIT_SYSBOX)
 1784       n_pstate &= ~n_PS_EDIT;
 1785    else
 1786       n_pstate |= n_PS_EDIT;
 1787    if (mb.mb_imap_account != NULL)
 1788       free(mb.mb_imap_account);
 1789    if (mb.mb_imap_pass != NULL)
 1790       free(mb.mb_imap_pass);
 1791    mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
 1792    /* TODO This is a hack to allow '@boxname'; in the end everything will be an
 1793     * TODO object, and mailbox will naturally have an URL and credentials */
 1794    mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
 1795 
 1796    if (!same_imap_account) {
 1797       if (mb.mb_sock.s_fd >= 0)
 1798          sclose(&mb.mb_sock);
 1799    }
 1800    same_imap_account = 0;
 1801 
 1802    if (!transparent) {
 1803       if (mb.mb_itf) {
 1804          fclose(mb.mb_itf);
 1805          mb.mb_itf = NULL;
 1806       }
 1807       if (mb.mb_otf) {
 1808          fclose(mb.mb_otf);
 1809          mb.mb_otf = NULL;
 1810       }
 1811       if (mb.mb_imap_mailbox != NULL)
 1812          free(mb.mb_imap_mailbox);
 1813       assert(urlp->url_path.s != NULL);
 1814       imap_delim_init(&mb, urlp);
 1815       mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
 1816       initbox(savecatsep(urlp->url_p_eu_h_p,
 1817          (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
 1818          mb.mb_imap_mailbox));
 1819    }
 1820    mb.mb_type = MB_VOID;
 1821    mb.mb_active = MB_NONE;
 1822 
 1823    imaplock = 1;
 1824    saveint = safe_signal(SIGINT, SIG_IGN);
 1825    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 1826    if (sigsetjmp(imapjmp, 1)) {
 1827       /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
 1828       sclose(&mb.mb_sock);
 1829       safe_signal(SIGINT, saveint);
 1830       safe_signal(SIGPIPE, savepipe);
 1831       imaplock = 0;
 1832 
 1833       mb.mb_type = MB_VOID;
 1834       mb.mb_active = MB_NONE;
 1835       rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
 1836       goto jleave;
 1837    }
 1838    if (saveint != SIG_IGN)
 1839       safe_signal(SIGINT, imapcatch);
 1840    if (savepipe != SIG_IGN)
 1841       safe_signal(SIGPIPE, imapcatch);
 1842 
 1843    if (mb.mb_sock.s_fd < 0) {
 1844       if (disconnected(mb.mb_imap_account)) {
 1845          if (cache_setptr(fm, transparent) == STOP)
 1846             n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
 1847          goto jdone;
 1848       }
 1849       if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
 1850          if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
 1851             savealrm = safe_signal(SIGALRM, imapalarm);
 1852             alarm(imapkeepalive);
 1853          }
 1854       }
 1855 
 1856       mb.mb_sock = so;
 1857       mb.mb_sock.s_desc = "IMAP";
 1858       mb.mb_sock.s_onclose = imap_timer_off;
 1859       if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
 1860          sclose(&mb.mb_sock);
 1861          imap_timer_off();
 1862          safe_signal(SIGINT, saveint);
 1863          safe_signal(SIGPIPE, savepipe);
 1864          imaplock = 0;
 1865          rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
 1866          goto jleave;
 1867       }
 1868    } else   /* same account */
 1869       mb.mb_flags |= same_flags;
 1870 
 1871    if (n_poption & n_PO_R_FLAG)
 1872       fm |= FEDIT_RDONLY;
 1873    mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
 1874    mb.mb_type = MB_IMAP;
 1875    cache_dequeue(&mb);
 1876    assert(urlp->url_path.s != NULL);
 1877    if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
 1878       /*sclose(&mb.mb_sock);
 1879       imap_timer_off();*/
 1880       safe_signal(SIGINT, saveint);
 1881       safe_signal(SIGPIPE, savepipe);
 1882       imaplock = 0;
 1883       mb.mb_type = MB_VOID;
 1884       rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
 1885       goto jleave;
 1886    }
 1887 
 1888 jnmail:
 1889    imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
 1890       n_UNVOLATILE(&prevcount));
 1891 jdone:
 1892    setmsize(msgCount);
 1893    safe_signal(SIGINT, saveint);
 1894    safe_signal(SIGPIPE, savepipe);
 1895    imaplock = 0;
 1896 
 1897    if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
 1898       purgecache(&mb, message, msgCount);
 1899    if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
 1900       mb.mb_threaded = 0;
 1901       c_sort((void*)-1);
 1902    }
 1903 
 1904    if (!(fm & FEDIT_NEWMAIL) && !transparent) {
 1905       n_pstate &= ~n_PS_SAW_COMMAND;
 1906       n_pstate |= n_PS_SETFILE_OPENED;
 1907    }
 1908 
 1909    if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
 1910          mb.mb_type == MB_CACHE)) {
 1911       rv = (msgCount == 0);
 1912       goto jleave;
 1913    }
 1914 
 1915    if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
 1916       if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
 1917             !ok_blook(emptystart))
 1918          n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
 1919       rv = 1;
 1920       goto jleave;
 1921    }
 1922 
 1923    if (fm & FEDIT_NEWMAIL)
 1924       newmailinfo(prevcount);
 1925    rv = 0;
 1926 jleave:
 1927    NYD_LEAVE;
 1928    return rv;
 1929 }
 1930 
 1931 static int
 1932 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
 1933    int need, const char *head, size_t headsize, long headlines)
 1934 {
 1935    char *line = NULL, *lp;
 1936    size_t linesize = 0, linelen, size = 0;
 1937    int emptyline = 0, lines = 0, excess = 0;
 1938    off_t offset;
 1939    NYD_ENTER;
 1940 
 1941    fseek(mp->mb_otf, 0L, SEEK_END);
 1942    offset = ftell(mp->mb_otf);
 1943 
 1944    if (head)
 1945       fwrite(head, 1, headsize, mp->mb_otf);
 1946 
 1947    while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
 1948       lp = line;
 1949       if (linelen > expected) {
 1950          excess = linelen - expected;
 1951          linelen = expected;
 1952       }
 1953       /* TODO >>
 1954        * Need to mask 'From ' lines. This cannot be done properly
 1955        * since some servers pass them as 'From ' and others as
 1956        * '>From '. Although one could identify the first kind of
 1957        * server in principle, it is not possible to identify the
 1958        * second as '>From ' may also come from a server of the
 1959        * first type as actual data. So do what is absolutely
 1960        * necessary only - mask 'From '.
 1961        *
 1962        * If the line is the first line of the message header, it
 1963        * is likely a real 'From ' line. In this case, it is just
 1964        * ignored since it violates all standards.
 1965        * TODO can the latter *really* happen??
 1966        * TODO <<
 1967        */
 1968       /* Since we simply copy over data without doing any transfer
 1969        * encoding reclassification/adjustment we *have* to perform
 1970        * RFC 4155 compliant From_ quoting here */
 1971       if (emptyline && is_head(lp, linelen, FAL0)) {
 1972          fputc('>', mp->mb_otf);
 1973          ++size;
 1974       }
 1975       emptyline = 0;
 1976       if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
 1977          if (linelen > 2) {
 1978             fwrite(lp, 1, linelen - 2, mp->mb_otf);
 1979             size += linelen - 1;
 1980          } else {
 1981             emptyline = 1;
 1982             ++size;
 1983          }
 1984          fputc('\n', mp->mb_otf);
 1985       } else {
 1986          fwrite(lp, 1, linelen, mp->mb_otf);
 1987          size += linelen;
 1988       }
 1989       ++lines;
 1990       if ((expected -= linelen) <= 0)
 1991          break;
 1992    }
 1993    if (!emptyline) {
 1994       /* This is very ugly; but some IMAP daemons don't end a
 1995        * message with \r\n\r\n, and we need \n\n for mbox format */
 1996       fputc('\n', mp->mb_otf);
 1997       ++lines;
 1998       ++size;
 1999    }
 2000    fflush(mp->mb_otf);
 2001 
 2002    if (m != NULL) {
 2003       m->m_size = size + headsize;
 2004       m->m_lines = lines + headlines;
 2005       m->m_block = mailx_blockof(offset);
 2006       m->m_offset = mailx_offsetof(offset);
 2007       switch (need) {
 2008       case NEED_HEADER:
 2009          m->m_content_info = CI_HAVE_HEADER;
 2010          break;
 2011       case NEED_BODY:
 2012          m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
 2013          m->m_xlines = m->m_lines;
 2014          m->m_xsize = m->m_size;
 2015          break;
 2016       }
 2017    }
 2018    free(line);
 2019    NYD_LEAVE;
 2020    return excess;
 2021 }
 2022 
 2023 static void
 2024 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
 2025    const char *head, size_t headsize, long headlines)
 2026 {
 2027    off_t offset;
 2028    size_t len;
 2029    NYD_ENTER;
 2030 
 2031    len = strlen(str);
 2032    fseek(mp->mb_otf, 0L, SEEK_END);
 2033    offset = ftell(mp->mb_otf);
 2034    if (head)
 2035       fwrite(head, 1, headsize, mp->mb_otf);
 2036    if (len > 0) {
 2037       fwrite(str, 1, len, mp->mb_otf);
 2038       fputc('\n', mp->mb_otf);
 2039       ++len;
 2040    }
 2041    fflush(mp->mb_otf);
 2042 
 2043    if (m != NULL) {
 2044       m->m_size = headsize + len;
 2045       m->m_lines = headlines + 1;
 2046       m->m_block = mailx_blockof(offset);
 2047       m->m_offset = mailx_offsetof(offset);
 2048       m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
 2049       m->m_xlines = m->m_lines;
 2050       m->m_xsize = m->m_size;
 2051    }
 2052    NYD_LEAVE;
 2053 }
 2054 
 2055 static enum okay
 2056 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
 2057 {
 2058    char o[LINESIZE];
 2059    struct message mt;
 2060    sighandler_type volatile saveint, savepipe;
 2061    char * volatile head;
 2062    char const *cp, *loc, * volatile item, * volatile resp;
 2063    size_t expected;
 2064    size_t volatile headsize;
 2065    int number;
 2066    FILE *queuefp;
 2067    long volatile headlines;
 2068    long n;
 2069    ul_i volatile u;
 2070    enum okay ok;
 2071    NYD_X;
 2072 
 2073    saveint = savepipe = SIG_IGN;
 2074    head = NULL;
 2075    cp = loc = item = resp = NULL;
 2076    headsize = 0;
 2077    number = (int)PTR2SIZE(m - message + 1);
 2078    queuefp = NULL;
 2079    headlines = 0;
 2080    u = 0;
 2081    ok = STOP;
 2082 
 2083    if (getcache(mp, m, need) == OKAY)
 2084       return OKAY;
 2085    if (mp->mb_type == MB_CACHE) {
 2086       n_err(_("Message %lu not available\n"), (ul_i)number);
 2087       return STOP;
 2088    }
 2089 
 2090    if (mp->mb_sock.s_fd < 0) {
 2091       n_err(_("IMAP connection closed\n"));
 2092       return STOP;
 2093    }
 2094 
 2095    switch (need) {
 2096    case NEED_HEADER:
 2097       resp = item = "RFC822.HEADER";
 2098       break;
 2099    case NEED_BODY:
 2100       item = "BODY.PEEK[]";
 2101       resp = "BODY[]";
 2102       if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
 2103          char *hdr = smalloc(m->m_size);
 2104          fflush(mp->mb_otf);
 2105          if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
 2106                SEEK_SET) < 0 ||
 2107                fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
 2108             free(hdr);
 2109             break;
 2110          }
 2111          head = hdr;
 2112          headsize = m->m_size;
 2113          headlines = m->m_lines;
 2114          item = "BODY.PEEK[TEXT]";
 2115          resp = "BODY[TEXT]";
 2116       }
 2117       break;
 2118    case NEED_UNSPEC:
 2119       return STOP;
 2120    }
 2121 
 2122    imaplock = 1;
 2123    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2124    if (sigsetjmp(imapjmp, 1)) {
 2125       safe_signal(SIGINT, saveint);
 2126       safe_signal(SIGPIPE, savepipe);
 2127       imaplock = 0;
 2128       return STOP;
 2129    }
 2130    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 2131       safe_signal(SIGINT, &_imap_maincatch);
 2132    if (savepipe != SIG_IGN)
 2133       safe_signal(SIGPIPE, imapcatch);
 2134 
 2135    if (m->m_uid)
 2136       snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
 2137          tag(1), m->m_uid, item);
 2138    else {
 2139       if (check_expunged() == STOP)
 2140          goto out;
 2141       snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
 2142    }
 2143    IMAP_OUT(o, MB_COMD, goto out)
 2144    for (;;) {
 2145       ok = imap_answer(mp, 1);
 2146       if (ok == STOP)
 2147          break;
 2148       if (response_status != RESPONSE_OTHER ||
 2149             response_other != MESSAGE_DATA_FETCH)
 2150          continue;
 2151       if ((loc = asccasestr(responded_other_text, resp)) == NULL)
 2152          continue;
 2153       if (m->m_uid) {
 2154          if ((cp = asccasestr(responded_other_text, "UID "))) {
 2155             u = atol(&cp[4]);
 2156             n = 0;
 2157          } else {
 2158             u = 0;
 2159             n = -1;
 2160          }
 2161       } else
 2162          n = responded_other_number;
 2163       if ((cp = strrchr(responded_other_text, '{')) == NULL) {
 2164          if (m->m_uid ? m->m_uid != u : n != number)
 2165             continue;
 2166          if ((cp = strchr(loc, '"')) != NULL) {
 2167             cp = imap_unquotestr(cp);
 2168             imap_putstr(mp, m, cp, head, headsize, headlines);
 2169          } else {
 2170             m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
 2171             m->m_xlines = m->m_lines;
 2172             m->m_xsize = m->m_size;
 2173          }
 2174          goto out;
 2175       }
 2176       expected = atol(&cp[1]);
 2177       if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
 2178          imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
 2179          continue;
 2180       }
 2181       mt = *m;
 2182       imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
 2183       if (n >= 0) {
 2184          commitmsg(mp, m, &mt, mt.m_content_info);
 2185          break;
 2186       }
 2187       if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
 2188          if (n_poption & n_PO_VERBVERB)
 2189             fputs(imapbuf, stderr);
 2190          if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
 2191             u = atol(&cp[4]);
 2192             if (u == m->m_uid) {
 2193                commitmsg(mp, m, &mt, mt.m_content_info);
 2194                break;
 2195             }
 2196          }
 2197       }
 2198    }
 2199 out:
 2200    while (mp->mb_active & MB_COMD)
 2201       ok = imap_answer(mp, 1);
 2202 
 2203    if (saveint != SIG_IGN)
 2204       safe_signal(SIGINT, saveint);
 2205    if (savepipe != SIG_IGN)
 2206       safe_signal(SIGPIPE, savepipe);
 2207    imaplock--;
 2208 
 2209    if (ok == OKAY)
 2210       putcache(mp, m);
 2211    if (head != NULL)
 2212       free(head);
 2213    if (interrupts)
 2214       n_go_onintr_for_imap();
 2215    return ok;
 2216 }
 2217 
 2218 FL enum okay
 2219 imap_header(struct message *m)
 2220 {
 2221    enum okay rv;
 2222    NYD_ENTER;
 2223 
 2224    rv = imap_get(&mb, m, NEED_HEADER);
 2225    NYD_LEAVE;
 2226    return rv;
 2227 }
 2228 
 2229 
 2230 FL enum okay
 2231 imap_body(struct message *m)
 2232 {
 2233    enum okay rv;
 2234    NYD_ENTER;
 2235 
 2236    rv = imap_get(&mb, m, NEED_BODY);
 2237    NYD_LEAVE;
 2238    return rv;
 2239 }
 2240 
 2241 static void
 2242 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
 2243    enum content_info content_info)
 2244 {
 2245    NYD_ENTER;
 2246    tomp->m_size = frommp->m_size;
 2247    tomp->m_lines = frommp->m_lines;
 2248    tomp->m_block = frommp->m_block;
 2249    tomp->m_offset = frommp->m_offset;
 2250    tomp->m_content_info = content_info & CI_HAVE_MASK;
 2251    if (content_info & CI_HAVE_BODY) {
 2252       tomp->m_xlines = frommp->m_lines;
 2253       tomp->m_xsize = frommp->m_size;
 2254    }
 2255    putcache(mp, tomp);
 2256    NYD_LEAVE;
 2257 }
 2258 
 2259 static enum okay
 2260 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
 2261 {
 2262    /* bot > topp */
 2263    char o[LINESIZE];
 2264    char const *cp;
 2265    struct message mt;
 2266    size_t expected;
 2267    int n = 0, u;
 2268    FILE *queuefp = NULL;
 2269    enum okay ok;
 2270    NYD_X;
 2271 
 2272    if (m[bot].m_uid)
 2273       snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
 2274          tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
 2275    else {
 2276       if (check_expunged() == STOP)
 2277          return STOP;
 2278       snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
 2279          tag(1), bot, topp);
 2280    }
 2281    IMAP_OUT(o, MB_COMD, return STOP)
 2282 
 2283    srelax_hold();
 2284    for (;;) {
 2285       ok = imap_answer(mp, 1);
 2286       if (response_status != RESPONSE_OTHER)
 2287          break;
 2288       if (response_other != MESSAGE_DATA_FETCH)
 2289          continue;
 2290       if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
 2291          srelax_rele();
 2292          return STOP;
 2293       }
 2294       if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
 2295          continue;
 2296       expected = atol(&cp[1]);
 2297       if (m[bot-1].m_uid) {
 2298          if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
 2299             u = atoi(&cp[4]);
 2300             for (n = bot; n <= topp; n++)
 2301                if ((unsigned long)u == m[n-1].m_uid)
 2302                   break;
 2303             if (n > topp) {
 2304                imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
 2305                continue;
 2306             }
 2307          } else
 2308             n = -1;
 2309       } else {
 2310          n = responded_other_number;
 2311          if (n <= 0 || n > msgCount) {
 2312             imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
 2313             continue;
 2314          }
 2315       }
 2316       imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
 2317       if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
 2318          commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
 2319       if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
 2320          if (n_poption & n_PO_VERBVERB)
 2321             fputs(imapbuf, stderr);
 2322          if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
 2323             u = atoi(&cp[4]);
 2324             for (n = bot; n <= topp; n++)
 2325                if ((unsigned long)u == m[n-1].m_uid)
 2326                   break;
 2327             if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
 2328                commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
 2329          }
 2330       }
 2331       srelax();
 2332    }
 2333    srelax_rele();
 2334 
 2335    while (mp->mb_active & MB_COMD)
 2336       ok = imap_answer(mp, 1);
 2337    return ok;
 2338 }
 2339 
 2340 FL void
 2341 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
 2342 {
 2343    sighandler_type saveint, savepipe;
 2344    /*enum okay ok = STOP;*/
 2345    int i, chunk = 256;
 2346    NYD_X;
 2347 
 2348    if (mb.mb_type == MB_CACHE)
 2349       return;
 2350    if (bot < 1)
 2351       bot = 1;
 2352    if (topp > msgCount)
 2353       topp = msgCount;
 2354    for (i = bot; i < topp; i++) {
 2355       if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
 2356             getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
 2357          bot = i+1;
 2358       else
 2359          break;
 2360    }
 2361    for (i = topp; i > bot; i--) {
 2362       if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
 2363             getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
 2364          topp = i-1;
 2365       else
 2366          break;
 2367    }
 2368    if (bot >= topp)
 2369       return;
 2370 
 2371    imaplock = 1;
 2372    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 2373       safe_signal(SIGINT, &_imap_maincatch);
 2374    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2375    if (sigsetjmp(imapjmp, 1) == 0) {
 2376       if (savepipe != SIG_IGN)
 2377          safe_signal(SIGPIPE, imapcatch);
 2378 
 2379       for (i = bot; i <= topp; i += chunk) {
 2380          int j = i + chunk - 1;
 2381          j = n_MIN(j, topp);
 2382          if (visible(message + j))
 2383             /*ok = */imap_fetchheaders(&mb, message, i, j);
 2384          if (interrupts)
 2385             n_go_onintr_for_imap(); /* XXX imaplock? */
 2386       }
 2387    }
 2388    safe_signal(SIGINT, saveint);
 2389    safe_signal(SIGPIPE, savepipe);
 2390    imaplock = 0;
 2391 }
 2392 
 2393 static enum okay
 2394 __imap_exit(struct mailbox *mp)
 2395 {
 2396    char o[LINESIZE];
 2397    FILE *queuefp = NULL;
 2398    NYD_X;
 2399 
 2400    mp->mb_active |= MB_BYE;
 2401    snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
 2402    IMAP_OUT(o, MB_COMD, return STOP)
 2403    IMAP_ANSWER()
 2404    return OKAY;
 2405 }
 2406 
 2407 static enum okay
 2408 imap_exit(struct mailbox *mp)
 2409 {
 2410    enum okay rv;
 2411    NYD_ENTER;
 2412 
 2413    rv = __imap_exit(mp);
 2414 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
 2415    free(mp->mb_imap_pass);
 2416    free(mp->mb_imap_account);
 2417    free(mp->mb_imap_mailbox);
 2418    if (mp->mb_cache_directory != NULL)
 2419       free(mp->mb_cache_directory);
 2420 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
 2421    mp->mb_imap_account =
 2422    mp->mb_imap_mailbox =
 2423    mp->mb_cache_directory = "";
 2424 #else
 2425    mp->mb_imap_account = NULL; /* for assert legacy time.. */
 2426    mp->mb_imap_mailbox = NULL;
 2427    mp->mb_cache_directory = NULL;
 2428 #endif
 2429 #endif
 2430    sclose(&mp->mb_sock);
 2431    NYD_LEAVE;
 2432    return rv;
 2433 }
 2434 
 2435 static enum okay
 2436 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
 2437 {
 2438    NYD_ENTER;
 2439    imap_store(mp, m, n, '+', "\\Deleted", needstat);
 2440    if (mp->mb_type == MB_IMAP)
 2441       delcache(mp, m);
 2442    NYD_LEAVE;
 2443    return OKAY;
 2444 }
 2445 
 2446 static enum okay
 2447 imap_close(struct mailbox *mp)
 2448 {
 2449    char o[LINESIZE];
 2450    FILE *queuefp = NULL;
 2451    NYD_X;
 2452 
 2453    snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
 2454    IMAP_OUT(o, MB_COMD, return STOP)
 2455    IMAP_ANSWER()
 2456    return OKAY;
 2457 }
 2458 
 2459 static enum okay
 2460 imap_update(struct mailbox *mp)
 2461 {
 2462    struct message *m;
 2463    int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
 2464    NYD_ENTER;
 2465 
 2466    if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
 2467       holdbits();
 2468       c = 0;
 2469       for (m = message; PTRCMP(m, <, message + msgCount); ++m)
 2470          if (m->m_flag & MBOX)
 2471             ++c;
 2472       if (c > 0)
 2473          if (makembox() == STOP)
 2474             goto jbypass;
 2475    }
 2476 
 2477    gotcha = held = 0;
 2478    for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
 2479       if (mp->mb_perm == 0)
 2480          dodel = 0;
 2481       else if (n_pstate & n_PS_EDIT)
 2482          dodel = ((m->m_flag & MDELETED) != 0);
 2483       else
 2484          dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
 2485 
 2486       /* Fetch the result after around each 800 STORE commands
 2487        * sent (approx. 32k data sent). Otherwise, servers will
 2488        * try to flush the return queue at some point, leading
 2489        * to a deadlock if we are still writing commands but not
 2490        * reading their results */
 2491       needstat = stored > 0 && stored % 800 == 0;
 2492       /* Even if this message has been deleted, continue
 2493        * to set further flags. This is necessary to support
 2494        * Gmail semantics, where "delete" actually means
 2495        * "archive", and the flags are applied to the copy
 2496        * in "All Mail" */
 2497       if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
 2498          imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
 2499          stored++;
 2500       }
 2501       if (m->m_flag & MFLAG) {
 2502          imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
 2503          stored++;
 2504       }
 2505       if (m->m_flag & MUNFLAG) {
 2506          imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
 2507          stored++;
 2508       }
 2509       if (m->m_flag & MANSWER) {
 2510          imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
 2511          stored++;
 2512       }
 2513       if (m->m_flag & MUNANSWER) {
 2514          imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
 2515          stored++;
 2516       }
 2517       if (m->m_flag & MDRAFT) {
 2518          imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
 2519          stored++;
 2520       }
 2521       if (m->m_flag & MUNDRAFT) {
 2522          imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
 2523          stored++;
 2524       }
 2525 
 2526       if (dodel) {
 2527          imap_delete(mp, m-message+1, m, needstat);
 2528          stored++;
 2529          gotcha++;
 2530       } else if (mp->mb_type != MB_CACHE ||
 2531             (!(n_pstate & n_PS_EDIT) &&
 2532              !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
 2533             (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
 2534                (MPRESERVE | MTOUCH) ||
 2535                ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
 2536          held++;
 2537       if (m->m_flag & MNEW) {
 2538          m->m_flag &= ~MNEW;
 2539          m->m_flag |= MSTATUS;
 2540       }
 2541    }
 2542 jbypass:
 2543    if (gotcha)
 2544       imap_close(mp);
 2545 
 2546    for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
 2547       if (!(m->m_flag & MUNLINKED) &&
 2548             m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
 2549                MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
 2550          putcache(mp, m);
 2551          modflags++;
 2552       }
 2553 
 2554    /* XXX should be readonly (but our IMAP code is weird...) */
 2555    if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
 2556          mb.mb_perm != 0) {
 2557       if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
 2558          printf(_("\"%s\" "), displayname);
 2559          printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
 2560             ? _("complete\n") : _("updated.\n"));
 2561       } else if (held && !(n_pstate & n_PS_EDIT)) {
 2562          if (held == 1)
 2563             printf(_("Held 1 message in %s\n"), displayname);
 2564          else
 2565             printf(_("Held %d messages in %s\n"), held, displayname);
 2566       }
 2567       fflush(stdout);
 2568    }
 2569    NYD_LEAVE;
 2570    return OKAY;
 2571 }
 2572 
 2573 FL bool_t
 2574 imap_quit(bool_t hold_sigs_on)
 2575 {
 2576    sighandler_type volatile saveint, savepipe;
 2577    bool_t rv;
 2578    NYD_ENTER;
 2579 
 2580    if(hold_sigs_on)
 2581       rele_sigs();
 2582 
 2583    if (mb.mb_type == MB_CACHE) {
 2584       rv = (imap_update(&mb) == OKAY);
 2585       goto jleave;
 2586    }
 2587 
 2588    rv = FAL0;
 2589 
 2590    if (mb.mb_sock.s_fd < 0) {
 2591       n_err(_("IMAP connection closed\n"));
 2592       goto jleave;
 2593    }
 2594 
 2595    imaplock = 1;
 2596    saveint = safe_signal(SIGINT, SIG_IGN);
 2597    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2598    if (sigsetjmp(imapjmp, 1)) {
 2599       safe_signal(SIGINT, saveint);
 2600       safe_signal(SIGPIPE, saveint);
 2601       imaplock = 0;
 2602       goto jleave;
 2603    }
 2604    if (saveint != SIG_IGN)
 2605       safe_signal(SIGINT, imapcatch);
 2606    if (savepipe != SIG_IGN)
 2607       safe_signal(SIGPIPE, imapcatch);
 2608 
 2609    rv = (imap_update(&mb) == OKAY);
 2610    if(!same_imap_account && imap_exit(&mb) != OKAY)
 2611       rv = FAL0;
 2612 
 2613    safe_signal(SIGINT, saveint);
 2614    safe_signal(SIGPIPE, savepipe);
 2615    imaplock = 0;
 2616 jleave:
 2617    if(hold_sigs_on)
 2618       hold_sigs();
 2619    NYD_LEAVE;
 2620    return rv;
 2621 }
 2622 
 2623 static enum okay
 2624 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
 2625    int needstat)
 2626 {
 2627    char o[LINESIZE];
 2628    FILE *queuefp = NULL;
 2629    NYD_X;
 2630 
 2631    if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
 2632       return STOP;
 2633    if (m->m_uid)
 2634       snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
 2635          tag(1), m->m_uid, c, sp);
 2636    else {
 2637       if (check_expunged() == STOP)
 2638          return STOP;
 2639       snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
 2640    }
 2641    IMAP_OUT(o, MB_COMD, return STOP)
 2642    if (needstat)
 2643       IMAP_ANSWER()
 2644    else
 2645       mb.mb_active &= ~MB_COMD;
 2646    if (queuefp != NULL)
 2647       Fclose(queuefp);
 2648    return OKAY;
 2649 }
 2650 
 2651 FL enum okay
 2652 imap_undelete(struct message *m, int n)
 2653 {
 2654    enum okay rv;
 2655    NYD_ENTER;
 2656 
 2657    rv = imap_unstore(m, n, "\\Deleted");
 2658    NYD_LEAVE;
 2659    return rv;
 2660 }
 2661 
 2662 FL enum okay
 2663 imap_unread(struct message *m, int n)
 2664 {
 2665    enum okay rv;
 2666    NYD_ENTER;
 2667 
 2668    rv = imap_unstore(m, n, "\\Seen");
 2669    NYD_LEAVE;
 2670    return rv;
 2671 }
 2672 
 2673 static enum okay
 2674 imap_unstore(struct message *m, int n, const char *flag)
 2675 {
 2676    sighandler_type saveint, savepipe;
 2677    enum okay volatile rv = STOP;
 2678    NYD_ENTER;
 2679 
 2680    imaplock = 1;
 2681    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 2682       safe_signal(SIGINT, &_imap_maincatch);
 2683    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2684    if (sigsetjmp(imapjmp, 1) == 0) {
 2685       if (savepipe != SIG_IGN)
 2686          safe_signal(SIGPIPE, imapcatch);
 2687 
 2688       rv = imap_store(&mb, m, n, '-', flag, 1);
 2689    }
 2690    safe_signal(SIGINT, saveint);
 2691    safe_signal(SIGPIPE, savepipe);
 2692    imaplock = 0;
 2693 
 2694    NYD_LEAVE;
 2695    if (interrupts)
 2696       n_go_onintr_for_imap();
 2697    return rv;
 2698 }
 2699 
 2700 static const char *
 2701 tag(int new)
 2702 {
 2703    static char ts[20];
 2704    static long n;
 2705    NYD2_ENTER;
 2706 
 2707    if (new)
 2708       ++n;
 2709    snprintf(ts, sizeof ts, "T%lu", n);
 2710    NYD2_LEAVE;
 2711    return ts;
 2712 }
 2713 
 2714 FL int
 2715 c_imapcodec(void *vp){
 2716    bool_t err;
 2717    size_t alen;
 2718    char const **argv, *varname, *varres, *act, *cp;
 2719    NYD_ENTER;
 2720 
 2721    argv = vp;
 2722    varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
 2723 
 2724    act = *argv;
 2725    for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
 2726       ;
 2727    if(act == cp)
 2728       goto jesynopsis;
 2729    alen = PTR2SIZE(cp - act);
 2730    if(*cp != '\0')
 2731       ++cp;
 2732 
 2733    n_pstate_err_no = n_ERR_NONE;
 2734    varres = imap_path_normalize(NULL, cp);
 2735 
 2736    if(is_ascncaseprefix(act, "encode", alen))
 2737       varres = imap_path_encode(varres, &err);
 2738    else if(is_ascncaseprefix(act, "decode", alen))
 2739       varres = imap_path_decode(varres, &err);
 2740    else
 2741       goto jesynopsis;
 2742 
 2743    if(err){
 2744       n_pstate_err_no = n_ERR_CANCELED;
 2745       varres = cp;
 2746       vp = NULL;
 2747    }
 2748 
 2749    if(varname != NULL){
 2750       if(!n_var_vset(varname, (uintptr_t)varres)){
 2751          n_pstate_err_no = n_ERR_NOTSUP;
 2752          vp = NULL;
 2753       }
 2754    }else{
 2755       struct str in, out;
 2756 
 2757       in.l = strlen(in.s = n_UNCONST(varres));
 2758       makeprint(&in, &out);
 2759       if(fprintf(n_stdout, "%s\n", out.s) < 0){
 2760          n_pstate_err_no = n_err_no;
 2761          vp = NULL;
 2762       }
 2763       free(out.s);
 2764    }
 2765 
 2766 jleave:
 2767    NYD_LEAVE;
 2768    return (vp != NULL ? 0 : 1);
 2769 jesynopsis:
 2770    n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
 2771    n_pstate_err_no = n_ERR_INVAL;
 2772    vp = NULL;
 2773    goto jleave;
 2774 }
 2775 
 2776 FL int
 2777 c_imap_imap(void *vp)
 2778 {
 2779    char o[LINESIZE];
 2780    sighandler_type saveint, savepipe;
 2781    struct mailbox *mp = &mb;
 2782    FILE *queuefp = NULL;
 2783    enum okay volatile ok = STOP;
 2784    NYD_X;
 2785 
 2786    if (mp->mb_type != MB_IMAP) {
 2787       printf("Not operating on an IMAP mailbox.\n");
 2788       return 1;
 2789    }
 2790    imaplock = 1;
 2791    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 2792       safe_signal(SIGINT, &_imap_maincatch);
 2793    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2794    if (sigsetjmp(imapjmp, 1) == 0) {
 2795       if (savepipe != SIG_IGN)
 2796          safe_signal(SIGPIPE, imapcatch);
 2797 
 2798       snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
 2799       IMAP_OUT(o, MB_COMD, goto out)
 2800       while (mp->mb_active & MB_COMD) {
 2801          ok = imap_answer(mp, 0);
 2802          fputs(responded_text, stdout);
 2803       }
 2804    }
 2805 out:
 2806    safe_signal(SIGINT, saveint);
 2807    safe_signal(SIGPIPE, savepipe);
 2808    imaplock = 0;
 2809 
 2810    if (interrupts)
 2811       n_go_onintr_for_imap();
 2812    return ok != OKAY;
 2813 }
 2814 
 2815 FL int
 2816 imap_newmail(int nmail)
 2817 {
 2818    NYD_ENTER;
 2819 
 2820    if (nmail && had_exists < 0 && had_expunge < 0) {
 2821       imaplock = 1;
 2822       imap_noop();
 2823       imaplock = 0;
 2824    }
 2825 
 2826    if (had_exists == msgCount && had_expunge < 0)
 2827       /* Some servers always respond with EXISTS to NOOP. If
 2828        * the mailbox has been changed but the number of messages
 2829        * has not, an EXPUNGE must also had been sent; otherwise,
 2830        * nothing has changed */
 2831       had_exists = -1;
 2832    NYD_LEAVE;
 2833    return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
 2834 }
 2835 
 2836 static char *
 2837 imap_putflags(int f)
 2838 {
 2839    const char *cp;
 2840    char *buf, *bp;
 2841    NYD2_ENTER;
 2842 
 2843    bp = buf = salloc(100);
 2844    if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
 2845       *bp++ = '(';
 2846       if (f & MREAD) {
 2847          if (bp[-1] != '(')
 2848             *bp++ = ' ';
 2849          for (cp = "\\Seen"; *cp; cp++)
 2850             *bp++ = *cp;
 2851       }
 2852       if (f & MFLAGGED) {
 2853          if (bp[-1] != '(')
 2854             *bp++ = ' ';
 2855          for (cp = "\\Flagged"; *cp; cp++)
 2856             *bp++ = *cp;
 2857       }
 2858       if (f & MANSWERED) {
 2859          if (bp[-1] != '(')
 2860             *bp++ = ' ';
 2861          for (cp = "\\Answered"; *cp; cp++)
 2862             *bp++ = *cp;
 2863       }
 2864       if (f & MDRAFT) {
 2865          if (bp[-1] != '(')
 2866             *bp++ = ' ';
 2867          for (cp = "\\Draft"; *cp; cp++)
 2868             *bp++ = *cp;
 2869       }
 2870       *bp++ = ')';
 2871       *bp++ = ' ';
 2872    }
 2873    *bp = '\0';
 2874    NYD2_LEAVE;
 2875    return buf;
 2876 }
 2877 
 2878 static void
 2879 imap_getflags(const char *cp, char const **xp, enum mflag *f)
 2880 {
 2881    NYD2_ENTER;
 2882    while (*cp != ')') {
 2883       if (*cp == '\\') {
 2884          if (ascncasecmp(cp, "\\Seen", 5) == 0)
 2885             *f |= MREAD;
 2886          else if (ascncasecmp(cp, "\\Recent", 7) == 0)
 2887             *f |= MNEW;
 2888          else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
 2889             *f |= MDELETED;
 2890          else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
 2891             *f |= MFLAGGED;
 2892          else if (ascncasecmp(cp, "\\Answered", 9) == 0)
 2893             *f |= MANSWERED;
 2894          else if (ascncasecmp(cp, "\\Draft", 6) == 0)
 2895             *f |= MDRAFTED;
 2896       }
 2897       cp++;
 2898    }
 2899 
 2900    if (xp != NULL)
 2901       *xp = cp;
 2902    NYD2_LEAVE;
 2903 }
 2904 
 2905 static enum okay
 2906 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
 2907    long xsize, enum mflag flag, time_t t)
 2908 {
 2909    char o[LINESIZE], *buf;
 2910    size_t bufsize, buflen, cnt;
 2911    long size, lines, ysize;
 2912    char const *qname;
 2913    bool_t twice;
 2914    FILE *queuefp;
 2915    enum okay rv;
 2916    NYD_ENTER;
 2917 
 2918    rv = STOP;
 2919    queuefp = NULL;
 2920    twice = FAL0;
 2921    buf = NULL;
 2922 
 2923    if((qname = imap_path_quote(mp, name)) == NULL)
 2924       goto jleave;
 2925 
 2926    if (mp->mb_type == MB_CACHE) {
 2927       queuefp = cache_queue(mp);
 2928       if (queuefp == NULL) {
 2929          buf = NULL;
 2930          goto jleave;
 2931       }
 2932       rv = OKAY;
 2933    }
 2934 
 2935    buf = smalloc(bufsize = LINESIZE);
 2936    buflen = 0;
 2937 jagain:
 2938    size = xsize;
 2939    cnt = fsize(fp);
 2940    if (fseek(fp, off1, SEEK_SET) < 0) {
 2941       rv = STOP;
 2942       goto jleave;
 2943    }
 2944 
 2945    snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
 2946          tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
 2947    IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
 2948    while (mp->mb_active & MB_COMD) {
 2949       rv = imap_answer(mp, twice);
 2950       if (response_type == RESPONSE_CONT)
 2951          break;
 2952    }
 2953 
 2954    if (mp->mb_type != MB_CACHE && rv == STOP) {
 2955       if (!twice)
 2956          goto jtrycreate;
 2957       else
 2958          goto jleave;
 2959    }
 2960 
 2961    lines = ysize = 0;
 2962    while (size > 0) {
 2963       fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
 2964       lines++;
 2965       ysize += buflen;
 2966       buf[buflen - 1] = '\r';
 2967       buf[buflen] = '\n';
 2968       if (mp->mb_type != MB_CACHE)
 2969          swrite1(&mp->mb_sock, buf, buflen+1, 1);
 2970       else if (queuefp)
 2971          fwrite(buf, 1, buflen+1, queuefp);
 2972       size -= buflen + 1;
 2973    }
 2974    if (mp->mb_type != MB_CACHE)
 2975       swrite(&mp->mb_sock, "\r\n");
 2976    else if (queuefp)
 2977       fputs("\r\n", queuefp);
 2978    while (mp->mb_active & MB_COMD) {
 2979       rv = imap_answer(mp, 0);
 2980       if (response_status == RESPONSE_NO /*&&
 2981             ascncasecmp(responded_text,
 2982                "[TRYCREATE] ", 12) == 0*/) {
 2983 jtrycreate:
 2984          if (twice) {
 2985             rv = STOP;
 2986             goto jleave;
 2987          }
 2988          twice = TRU1;
 2989          snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
 2990          IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
 2991          while (mp->mb_active & MB_COMD)
 2992             rv = imap_answer(mp, 1);
 2993          if (rv == STOP)
 2994             goto jleave;
 2995          imap_created_mailbox++;
 2996          goto jagain;
 2997       } else if (rv != OKAY)
 2998          n_err(_("IMAP error: %s"), responded_text);
 2999       else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
 3000          imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
 3001    }
 3002 jleave:
 3003    if (queuefp != NULL)
 3004       Fclose(queuefp);
 3005    if (buf != NULL)
 3006       free(buf);
 3007    NYD_LEAVE;
 3008    return rv;
 3009 }
 3010 
 3011 static enum okay
 3012 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
 3013 {
 3014    char *buf, *bp, *lp;
 3015    size_t bufsize, buflen, cnt;
 3016    off_t off1 = -1, offs;
 3017    int flag;
 3018    enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
 3019    time_t tim;
 3020    long size;
 3021    enum okay rv;
 3022    NYD_ENTER;
 3023 
 3024    buf = smalloc(bufsize = LINESIZE);
 3025    buflen = 0;
 3026    cnt = fsize(fp);
 3027    offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
 3028    time(&tim);
 3029    size = 0;
 3030 
 3031    for (flag = MNEW, state = _NLSEP;;) {
 3032       bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
 3033 
 3034       if (bp == NULL ||
 3035             ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
 3036              is_head(buf, buflen, FAL0))) {
 3037          if (off1 != (off_t)-1) {
 3038             rv = imap_append1(mp, name, fp, off1, size, flag, tim);
 3039             if (rv == STOP)
 3040                goto jleave;
 3041             fseek(fp, offs+buflen, SEEK_SET);
 3042          }
 3043          off1 = offs + buflen;
 3044          size = 0;
 3045          flag = MNEW;
 3046          state = _INHEAD;
 3047          if (bp == NULL)
 3048             break;
 3049          tim = unixtime(buf);
 3050       } else
 3051          size += buflen+1;
 3052       offs += buflen;
 3053 
 3054       state &= ~_NLSEP;
 3055       if (buf[0] == '\n') {
 3056          state &= ~_INHEAD;
 3057          state |= _NLSEP;
 3058       } else if (state & _INHEAD) {
 3059          if (ascncasecmp(buf, "status", 6) == 0) {
 3060             lp = &buf[6];
 3061             while (whitechar(*lp))
 3062                lp++;
 3063             if (*lp == ':')
 3064                while (*++lp != '\0')
 3065                   switch (*lp) {
 3066                   case 'R':
 3067                      flag |= MREAD;
 3068                      break;
 3069                   case 'O':
 3070                      flag &= ~MNEW;
 3071                      break;
 3072                   }
 3073          } else if (ascncasecmp(buf, "x-status", 8) == 0) {
 3074             lp = &buf[8];
 3075             while (whitechar(*lp))
 3076                lp++;
 3077             if (*lp == ':')
 3078                while (*++lp != '\0')
 3079                   switch (*lp) {
 3080                   case 'F':
 3081                      flag |= MFLAGGED;
 3082                      break;
 3083                   case 'A':
 3084                      flag |= MANSWERED;
 3085                      break;
 3086                   case 'T':
 3087                      flag |= MDRAFTED;
 3088                      break;
 3089                   }
 3090          }
 3091       }
 3092    }
 3093    rv = OKAY;
 3094 jleave:
 3095    free(buf);
 3096    NYD_LEAVE;
 3097    return rv;
 3098 }
 3099 
 3100 FL enum okay
 3101 imap_append(const char *xserver, FILE *fp, long offset)
 3102 {
 3103    sighandler_type volatile saveint, savepipe;
 3104    struct url url;
 3105    struct ccred ccred;
 3106    enum okay rv = STOP;
 3107    NYD_ENTER;
 3108 
 3109    if (!url_parse(&url, CPROTO_IMAP, xserver))
 3110       goto j_leave;
 3111    if (!ok_blook(v15_compat) &&
 3112          (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
 3113       n_err(_("New-style URL used without *v15-compat* being set!\n"));
 3114    assert(url.url_path.s != NULL);
 3115 
 3116    imaplock = 1;
 3117    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3118       safe_signal(SIGINT, &_imap_maincatch);
 3119    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3120    if (sigsetjmp(imapjmp, 1))
 3121       goto jleave;
 3122    if (savepipe != SIG_IGN)
 3123       safe_signal(SIGPIPE, imapcatch);
 3124 
 3125    if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
 3126          !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
 3127       rv = imap_append0(&mb, url.url_path.s, fp, offset);
 3128    } else {
 3129       struct mailbox mx;
 3130 
 3131       memset(&mx, 0, sizeof mx);
 3132 
 3133       if (!_imap_getcred(&mx, &ccred, &url))
 3134          goto jleave;
 3135 
 3136       imap_delim_init(&mx, &url);
 3137       mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
 3138 
 3139       if (disconnected(url.url_p_eu_h_p) == 0) {
 3140          if (!sopen(&mx.mb_sock, &url))
 3141             goto jfail;
 3142          mx.mb_sock.s_desc = "IMAP";
 3143          mx.mb_type = MB_IMAP;
 3144          mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
 3145          /* TODO the code now did
 3146           * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
 3147           * TODO though imap_mailbox is sfree()d and mbx
 3148           * TODO is possibly even a constant
 3149           * TODO i changed this to sstrdup() sofar, as is used
 3150           * TODO somewhere else in this file for this! */
 3151          if (imap_preauth(&mx, &url) != OKAY ||
 3152                imap_auth(&mx, &ccred) != OKAY) {
 3153             sclose(&mx.mb_sock);
 3154             goto jfail;
 3155          }
 3156          rv = imap_append0(&mx, url.url_path.s, fp, offset);
 3157          imap_exit(&mx);
 3158       } else {
 3159          mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
 3160          mx.mb_type = MB_CACHE;
 3161          rv = imap_append0(&mx, url.url_path.s, fp, offset);
 3162       }
 3163 jfail:
 3164       ;
 3165    }
 3166 
 3167 jleave:
 3168    safe_signal(SIGINT, saveint);
 3169    safe_signal(SIGPIPE, savepipe);
 3170    imaplock = 0;
 3171 j_leave:
 3172    NYD_LEAVE;
 3173    if (interrupts)
 3174       n_go_onintr_for_imap();
 3175    return rv;
 3176 }
 3177 
 3178 static enum okay
 3179 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
 3180    struct list_item **lend, int level)
 3181 {
 3182    char o[LINESIZE], *cp;
 3183    struct list_item *lp;
 3184    const char *qname, *bp;
 3185    FILE *queuefp;
 3186    enum okay ok;
 3187    NYD_X;
 3188 
 3189    ok = STOP;
 3190    queuefp = NULL;
 3191 
 3192    if((qname = imap_path_quote(mp, base)) == NULL)
 3193       goto jleave;
 3194 
 3195    *list = *lend = NULL;
 3196    snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
 3197    IMAP_OUT(o, MB_COMD, goto jleave)
 3198    while (mp->mb_active & MB_COMD) {
 3199       ok = imap_answer(mp, 1);
 3200       if (response_status == RESPONSE_OTHER &&
 3201             response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
 3202          cp = imap_path_decode(imap_unquotestr(list_name), NULL);
 3203          lp = csalloc(1, sizeof *lp);
 3204          lp->l_name = cp;
 3205          for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
 3206             ++cp;
 3207          lp->l_base = *cp ? cp : savestr(base);
 3208          lp->l_attr = list_attributes;
 3209          lp->l_level = level+1;
 3210          lp->l_delim = list_hierarchy_delimiter;
 3211          if (*list && *lend) {
 3212             (*lend)->l_next = lp;
 3213             *lend = lp;
 3214          } else
 3215             *list = *lend = lp;
 3216       }
 3217    }
 3218 jleave:
 3219    return ok;
 3220 }
 3221 
 3222 static enum okay
 3223 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
 3224 {
 3225    struct list_item *list, *lend, *lp, *lx, *ly;
 3226    int n, depth;
 3227    const char *bp;
 3228    char *cp;
 3229    enum okay rv;
 3230    NYD_ENTER;
 3231 
 3232    depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
 3233    if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
 3234       goto jleave;
 3235    rv = OKAY;
 3236    if (list == NULL || lend == NULL)
 3237       goto jleave;
 3238 
 3239    for (lp = list; lp; lp = lp->l_next)
 3240       if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
 3241             !(lp->l_attr & LIST_NOINFERIORS)) {
 3242          cp = salloc((n = strlen(lp->l_name)) + 2);
 3243          memcpy(cp, lp->l_name, n);
 3244          cp[n] = lp->l_delim;
 3245          cp[n+1] = '\0';
 3246          if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
 3247             lp->l_has_children = 1;
 3248             if (strcmp(cp, lx->l_name) == 0)
 3249                lx = lx->l_next;
 3250             if (lx) {
 3251                lend->l_next = lx;
 3252                lend = ly;
 3253             }
 3254          }
 3255       }
 3256 
 3257    for (lp = list; lp; lp = lp->l_next) {
 3258       if (strip) {
 3259          cp = lp->l_name;
 3260          for (bp = base; *bp && *bp == *cp; bp++)
 3261             cp++;
 3262       } else
 3263          cp = lp->l_name;
 3264       if (!(lp->l_attr & LIST_NOSELECT))
 3265          fprintf(fp, "%s\n", *cp ? cp : base);
 3266       else if (lp->l_has_children == 0)
 3267          fprintf(fp, "%s%c\n", *cp ? cp : base,
 3268             (lp->l_delim != EOF ? lp->l_delim : '\n'));
 3269    }
 3270 jleave:
 3271    NYD_LEAVE;
 3272    return rv;
 3273 }
 3274 
 3275 FL int
 3276 imap_folders(const char * volatile name, int strip)
 3277 {
 3278    sighandler_type saveint, savepipe;
 3279    const char * volatile fold, *cp, *sp;
 3280    FILE * volatile fp;
 3281    int rv = 1;
 3282    NYD_ENTER;
 3283 
 3284    cp = protbase(name);
 3285    sp = mb.mb_imap_account;
 3286    if (sp == NULL || strcmp(cp, sp)) {
 3287       n_err(
 3288          _("Cannot perform `folders' but when on the very IMAP "
 3289          "account; the current one is\n  `%s' -- "
 3290          "try `folders @'\n"),
 3291          (sp != NULL ? sp : _("[NONE]")));
 3292       goto jleave;
 3293    }
 3294 
 3295    fold = imap_fileof(name);
 3296    if (n_psonce & n_PSO_TTYOUT) {
 3297       if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
 3298             == NULL) {
 3299          n_perr(_("tmpfile"), 0);
 3300          goto jleave;
 3301       }
 3302    } else
 3303       fp = stdout;
 3304 
 3305    imaplock = 1;
 3306    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3307       safe_signal(SIGINT, &_imap_maincatch);
 3308    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3309    if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
 3310       goto junroll;
 3311    if (savepipe != SIG_IGN)
 3312       safe_signal(SIGPIPE, imapcatch);
 3313 
 3314    if (mb.mb_type == MB_CACHE)
 3315       cache_list(&mb, fold, strip, fp);
 3316    else
 3317       imap_list(&mb, fold, strip, fp);
 3318 
 3319    imaplock = 0;
 3320    if (interrupts) {
 3321       if (n_psonce & n_PSO_TTYOUT)
 3322          Fclose(fp);
 3323       rv = 0;
 3324       goto jleave;
 3325    }
 3326    fflush(fp);
 3327 
 3328    if (n_psonce & n_PSO_TTYOUT) {
 3329       rewind(fp);
 3330       if (fsize(fp) > 0){
 3331          page_or_print(fp, 0);
 3332          rv = 0;
 3333       }else
 3334          n_err(_("Folder not found\n"));
 3335    }else
 3336       rv = 0;
 3337 junroll:
 3338    safe_signal(SIGINT, saveint);
 3339    safe_signal(SIGPIPE, savepipe);
 3340    if (n_psonce & n_PSO_TTYOUT)
 3341       Fclose(fp);
 3342 jleave:
 3343    NYD_LEAVE;
 3344    if (interrupts)
 3345       n_go_onintr_for_imap();
 3346    return rv;
 3347 }
 3348 
 3349 static enum okay
 3350 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
 3351 {
 3352    char o[LINESIZE];
 3353    const char *qname;
 3354    bool_t twice, stored;
 3355    FILE *queuefp;
 3356    enum okay ok;
 3357    NYD_X;
 3358 
 3359    ok = STOP;
 3360    queuefp = NULL;
 3361    twice = stored = FAL0;
 3362 
 3363    /* C99 */{
 3364       size_t i;
 3365 
 3366       i = strlen(name = imap_fileof(name));
 3367       if(i == 0 || (i > 0 && name[i - 1] == '/'))
 3368          name = savecat(name, "INBOX");
 3369       if((qname = imap_path_quote(mp, name)) == NULL)
 3370          goto jleave;
 3371    }
 3372 
 3373    if (mp->mb_type == MB_CACHE) {
 3374       if ((queuefp = cache_queue(mp)) == NULL)
 3375          goto jleave;
 3376       ok = OKAY;
 3377    }
 3378 
 3379    /* Since it is not possible to set flags on the copy, recently
 3380     * set flags must be set on the original to include it in the copy */
 3381    if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
 3382       imap_store(mp, m, n, '+', "\\Seen", 0);
 3383    if (m->m_flag&MFLAG)
 3384       imap_store(mp, m, n, '+', "\\Flagged", 0);
 3385    if (m->m_flag&MUNFLAG)
 3386       imap_store(mp, m, n, '-', "\\Flagged", 0);
 3387    if (m->m_flag&MANSWER)
 3388       imap_store(mp, m, n, '+', "\\Answered", 0);
 3389    if (m->m_flag&MUNANSWER)
 3390       imap_store(mp, m, n, '-', "\\Flagged", 0);
 3391    if (m->m_flag&MDRAFT)
 3392       imap_store(mp, m, n, '+', "\\Draft", 0);
 3393    if (m->m_flag&MUNDRAFT)
 3394       imap_store(mp, m, n, '-', "\\Draft", 0);
 3395 again:
 3396    if (m->m_uid)
 3397       snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
 3398    else {
 3399       if (check_expunged() == STOP)
 3400          goto out;
 3401       snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
 3402    }
 3403    IMAP_OUT(o, MB_COMD, goto out)
 3404    while (mp->mb_active & MB_COMD)
 3405       ok = imap_answer(mp, twice);
 3406 
 3407    if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
 3408          response_status == RESPONSE_OK)
 3409       imap_copyuid(mp, m, name);
 3410 
 3411    if (response_status == RESPONSE_NO && !twice) {
 3412       snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
 3413       IMAP_OUT(o, MB_COMD, goto out)
 3414       while (mp->mb_active & MB_COMD)
 3415          ok = imap_answer(mp, 1);
 3416       if (ok == OKAY) {
 3417          imap_created_mailbox++;
 3418          goto again;
 3419       }
 3420    }
 3421 
 3422    if (queuefp != NULL)
 3423       Fclose(queuefp);
 3424 
 3425    /* ... and reset the flag to its initial value so that the 'exit'
 3426     * command still leaves the message unread */
 3427 out:
 3428    if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
 3429       imap_store(mp, m, n, '-', "\\Seen", 0);
 3430       stored = TRU1;
 3431    }
 3432    if (m->m_flag & MFLAG) {
 3433       imap_store(mp, m, n, '-', "\\Flagged", 0);
 3434       stored = TRU1;
 3435    }
 3436    if (m->m_flag & MUNFLAG) {
 3437       imap_store(mp, m, n, '+', "\\Flagged", 0);
 3438       stored = TRU1;
 3439    }
 3440    if (m->m_flag & MANSWER) {
 3441       imap_store(mp, m, n, '-', "\\Answered", 0);
 3442       stored = TRU1;
 3443    }
 3444    if (m->m_flag & MUNANSWER) {
 3445       imap_store(mp, m, n, '+', "\\Answered", 0);
 3446       stored = TRU1;
 3447    }
 3448    if (m->m_flag & MDRAFT) {
 3449       imap_store(mp, m, n, '-', "\\Draft", 0);
 3450       stored = TRU1;
 3451    }
 3452    if (m->m_flag & MUNDRAFT) {
 3453       imap_store(mp, m, n, '+', "\\Draft", 0);
 3454       stored = TRU1;
 3455    }
 3456    if (stored) {
 3457       mp->mb_active |= MB_COMD;
 3458       (void)imap_finish(mp);
 3459    }
 3460 jleave:
 3461    return ok;
 3462 }
 3463 
 3464 FL enum okay
 3465 imap_copy(struct message *m, int n, const char *name)
 3466 {
 3467    sighandler_type saveint, savepipe;
 3468    enum okay volatile rv = STOP;
 3469    NYD_ENTER;
 3470 
 3471    imaplock = 1;
 3472    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3473       safe_signal(SIGINT, &_imap_maincatch);
 3474    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3475    if (sigsetjmp(imapjmp, 1) == 0) {
 3476       if (savepipe != SIG_IGN)
 3477          safe_signal(SIGPIPE, imapcatch);
 3478 
 3479       rv = imap_copy1(&mb, m, n, name);
 3480    }
 3481    safe_signal(SIGINT, saveint);
 3482    safe_signal(SIGPIPE, savepipe);
 3483    imaplock = 0;
 3484 
 3485    NYD_LEAVE;
 3486    if (interrupts)
 3487       n_go_onintr_for_imap();
 3488    return rv;
 3489 }
 3490 
 3491 static enum okay
 3492 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
 3493    unsigned long *olduid, unsigned long *newuid)
 3494 {
 3495    char *xp, *yp, *zp;
 3496    enum okay rv;
 3497    NYD_ENTER;
 3498 
 3499    *uidvalidity = strtoul(cp, &xp, 10);
 3500    *olduid = strtoul(xp, &yp, 10);
 3501    *newuid = strtoul(yp, &zp, 10);
 3502    rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
 3503       yp > xp && *yp == ' ' && zp > yp && *zp == ']');
 3504    NYD_LEAVE;
 3505    return rv;
 3506 }
 3507 
 3508 static enum okay
 3509 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
 3510    unsigned long *uid)
 3511 {
 3512    char *xp, *yp;
 3513    enum okay rv;
 3514    NYD_ENTER;
 3515 
 3516    *uidvalidity = strtoul(cp, &xp, 10);
 3517    *uid = strtoul(xp, &yp, 10);
 3518    rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
 3519       *yp == ']');
 3520    NYD_LEAVE;
 3521    return rv;
 3522 }
 3523 
 3524 static enum okay
 3525 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
 3526 {
 3527    struct mailbox xmb;
 3528    struct message xm;
 3529    const char *cp;
 3530    unsigned long uidvalidity, olduid, newuid;
 3531    enum okay rv;
 3532    NYD_ENTER;
 3533 
 3534    rv = STOP;
 3535 
 3536    memset(&xmb, 0, sizeof xmb);
 3537 
 3538    if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
 3539          imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
 3540       goto jleave;
 3541 
 3542    rv = OKAY;
 3543 
 3544    xmb = *mp;
 3545    xmb.mb_cache_directory = NULL;
 3546    xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
 3547    xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
 3548    memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
 3549       sizeof(xmb.mb_imap_delim));
 3550    xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
 3551    if (mp->mb_cache_directory != NULL)
 3552       xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
 3553    xmb.mb_uidvalidity = uidvalidity;
 3554    initcache(&xmb);
 3555 
 3556    if (m == NULL) {
 3557       memset(&xm, 0, sizeof xm);
 3558       xm.m_uid = olduid;
 3559       if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
 3560          goto jleave;
 3561       getcache(mp, &xm, NEED_HEADER);
 3562       getcache(mp, &xm, NEED_BODY);
 3563    } else {
 3564       if ((m->m_content_info & CI_HAVE_HEADER) == 0)
 3565          getcache(mp, m, NEED_HEADER);
 3566       if ((m->m_content_info & CI_HAVE_BODY) == 0)
 3567          getcache(mp, m, NEED_BODY);
 3568       xm = *m;
 3569    }
 3570    xm.m_uid = newuid;
 3571    xm.m_flag &= ~MFULLYCACHED;
 3572    putcache(&xmb, &xm);
 3573 jleave:
 3574    if (xmb.mb_cache_directory != NULL)
 3575       free(xmb.mb_cache_directory);
 3576    if (xmb.mb_imap_mailbox != NULL)
 3577       free(xmb.mb_imap_mailbox);
 3578    if (xmb.mb_imap_pass != NULL)
 3579       free(xmb.mb_imap_pass);
 3580    if (xmb.mb_imap_account != NULL)
 3581       free(xmb.mb_imap_account);
 3582    NYD_LEAVE;
 3583    return rv;
 3584 }
 3585 
 3586 static enum okay
 3587 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
 3588    long size, long lines, int flag, const char *name)
 3589 {
 3590    struct mailbox xmb;
 3591    struct message xm;
 3592    const char *cp;
 3593    unsigned long uidvalidity, uid;
 3594    enum okay rv;
 3595    NYD_ENTER;
 3596 
 3597    rv = STOP;
 3598 
 3599    if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
 3600          imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
 3601       goto jleave;
 3602 
 3603    rv = OKAY;
 3604 
 3605    xmb = *mp;
 3606    xmb.mb_cache_directory = NULL;
 3607    /* XXX mb_imap_delim reused */
 3608    xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
 3609    xmb.mb_uidvalidity = uidvalidity;
 3610    xmb.mb_otf = xmb.mb_itf = fp;
 3611    initcache(&xmb);
 3612    memset(&xm, 0, sizeof xm);
 3613    xm.m_flag = (flag & MREAD) | MNEW;
 3614    xm.m_time = t;
 3615    xm.m_block = mailx_blockof(off1);
 3616    xm.m_offset = mailx_offsetof(off1);
 3617    xm.m_size = size;
 3618    xm.m_xsize = xsize;
 3619    xm.m_lines = xm.m_xlines = lines;
 3620    xm.m_uid = uid;
 3621    xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
 3622    putcache(&xmb, &xm);
 3623 
 3624    free(xmb.mb_imap_mailbox);
 3625 jleave:
 3626    NYD_LEAVE;
 3627    return rv;
 3628 }
 3629 
 3630 static enum okay
 3631 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
 3632 {
 3633    FILE *tp = NULL;
 3634    time_t t;
 3635    long size, xsize, ysize, lines;
 3636    enum mflag flag = MNEW;
 3637    char *name, *buf, *bp;
 3638    char const *cp;
 3639    size_t bufsize, buflen, cnt;
 3640    enum okay rv = STOP;
 3641    NYD_ENTER;
 3642 
 3643    buf = smalloc(bufsize = LINESIZE);
 3644    buflen = 0;
 3645    cnt = fsize(fp);
 3646    if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
 3647       goto jstop;
 3648 
 3649    for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
 3650       ;
 3651    while (*bp == ' ')
 3652       ++bp;
 3653 
 3654    if ((cp = strrchr(bp, '{')) == NULL)
 3655       goto jstop;
 3656 
 3657    xsize = atol(&cp[1]) + 2;
 3658    if ((name = imap_strex(&bp[7], &cp)) == NULL)
 3659       goto jstop;
 3660    while (*cp == ' ')
 3661       cp++;
 3662 
 3663    if (*cp == '(') {
 3664       imap_getflags(cp, &cp, &flag);
 3665       while (*++cp == ' ')
 3666          ;
 3667    }
 3668    t = imap_read_date_time(cp);
 3669 
 3670    if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
 3671          == NULL)
 3672       goto jstop;
 3673 
 3674    size = xsize;
 3675    ysize = lines = 0;
 3676    while (size > 0) {
 3677       if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
 3678          goto jstop;
 3679       size -= buflen;
 3680       buf[--buflen] = '\0';
 3681       buf[buflen-1] = '\n';
 3682       fwrite(buf, 1, buflen, tp);
 3683       ysize += buflen;
 3684       ++lines;
 3685    }
 3686    fflush(tp);
 3687    rewind(tp);
 3688 
 3689    imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
 3690       imap_unquotestr(name));
 3691    rv = OKAY;
 3692 jstop:
 3693    free(buf);
 3694    if (tp)
 3695       Fclose(tp);
 3696    NYD_LEAVE;
 3697    return rv;
 3698 }
 3699 
 3700 #ifdef HAVE_IMAP_SEARCH
 3701 static enum okay
 3702 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
 3703    int f)
 3704 {
 3705    char *o, *xp, *cs, c;
 3706    size_t osize;
 3707    FILE *queuefp = NULL;
 3708    int i;
 3709    unsigned long n;
 3710    const char *cp;
 3711    enum okay ok = STOP;
 3712    NYD_X;
 3713 
 3714    c = 0;
 3715    for (cp = spec; *cp; cp++)
 3716       c |= *cp;
 3717    if (c & 0200) {
 3718       cp = ok_vlook(ttycharset);
 3719 # ifdef HAVE_ICONV
 3720       if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
 3721          iconv_t  it;
 3722          char *nsp, *nspec;
 3723          size_t sz, nsz;
 3724 
 3725          if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
 3726             sz = strlen(spec) + 1;
 3727             nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
 3728             if (n_iconv_buf(it, n_ICONV_DEFAULT,
 3729                      (char const**)&spec, &sz, &nsp, &nsz) == 0 &&
 3730                   sz == 0) {
 3731                spec = nspec;
 3732                cp = "utf-8";
 3733             }
 3734             n_iconv_close(it);
 3735          }
 3736       }
 3737 # endif
 3738       cp = imap_quotestr(cp);
 3739       cs = salloc(n = strlen(cp) + 10);
 3740       snprintf(cs, n, "CHARSET %s ", cp);
 3741    } else
 3742       cs = n_UNCONST("");
 3743 
 3744    o = ac_alloc(osize = strlen(spec) + 60);
 3745    snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
 3746    IMAP_OUT(o, MB_COMD, goto out)
 3747    while (mp->mb_active & MB_COMD) {
 3748       ok = imap_answer(mp, 0);
 3749       if (response_status == RESPONSE_OTHER &&
 3750             response_other == MAILBOX_DATA_SEARCH) {
 3751          xp = responded_other_text;
 3752          while (*xp && *xp != '\r') {
 3753             n = strtoul(xp, &xp, 10);
 3754             for (i = 0; i < cnt; i++)
 3755                if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
 3756                      (f == MDELETED || !(m[i].m_flag & MDELETED)))
 3757                   mark(i+1, f);
 3758          }
 3759       }
 3760    }
 3761 out:
 3762    ac_free(o);
 3763    return ok;
 3764 }
 3765 
 3766 FL enum okay
 3767 imap_search1(const char * volatile spec, int f)
 3768 {
 3769    sighandler_type saveint, savepipe;
 3770    enum okay volatile rv = STOP;
 3771    NYD_ENTER;
 3772 
 3773    if (mb.mb_type != MB_IMAP)
 3774       goto jleave;
 3775 
 3776    imaplock = 1;
 3777    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3778       safe_signal(SIGINT, &_imap_maincatch);
 3779    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3780    if (sigsetjmp(imapjmp, 1) == 0) {
 3781       if (savepipe != SIG_IGN)
 3782          safe_signal(SIGPIPE, imapcatch);
 3783 
 3784       rv = imap_search2(&mb, message, msgCount, spec, f);
 3785    }
 3786    safe_signal(SIGINT, saveint);
 3787    safe_signal(SIGPIPE, savepipe);
 3788    imaplock = 0;
 3789 jleave:
 3790    NYD_LEAVE;
 3791    if (interrupts)
 3792       n_go_onintr_for_imap();
 3793    return rv;
 3794 }
 3795 #endif /* HAVE_IMAP_SEARCH */
 3796 
 3797 FL int
 3798 imap_thisaccount(const char *cp)
 3799 {
 3800    int rv;
 3801    NYD_ENTER;
 3802 
 3803    if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
 3804       rv = 0;
 3805    else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
 3806          mb.mb_imap_account == NULL)
 3807       rv = 0;
 3808    else
 3809       rv = !strcmp(protbase(cp), mb.mb_imap_account);
 3810    NYD_LEAVE;
 3811    return rv;
 3812 }
 3813 
 3814 FL enum okay
 3815 imap_remove(const char * volatile name)
 3816 {
 3817    sighandler_type volatile saveint, savepipe;
 3818    enum okay volatile rv = STOP;
 3819    NYD_ENTER;
 3820 
 3821    if (mb.mb_type != MB_IMAP) {
 3822       n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
 3823       goto jleave;
 3824    }
 3825 
 3826    if (!imap_thisaccount(name)) {
 3827       n_err(_("Can only remove mailboxes on current IMAP server: "
 3828          "\"%s\" not removed\n"), name);
 3829       goto jleave;
 3830    }
 3831 
 3832    imaplock = 1;
 3833    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3834       safe_signal(SIGINT, &_imap_maincatch);
 3835    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3836    if (sigsetjmp(imapjmp, 1) == 0) {
 3837       if (savepipe != SIG_IGN)
 3838          safe_signal(SIGPIPE, imapcatch);
 3839 
 3840       rv = imap_remove1(&mb, imap_fileof(name));
 3841    }
 3842    safe_signal(SIGINT, saveint);
 3843    safe_signal(SIGPIPE, savepipe);
 3844    imaplock = 0;
 3845 
 3846    if (rv == OKAY)
 3847       rv = cache_remove(name);
 3848 jleave:
 3849    NYD_LEAVE;
 3850    if (interrupts)
 3851       n_go_onintr_for_imap();
 3852    return rv;
 3853 }
 3854 
 3855 static enum okay
 3856 imap_remove1(struct mailbox *mp, const char *name)
 3857 {
 3858    char *o;
 3859    int os;
 3860    char const *qname;
 3861    FILE *queuefp;
 3862    enum okay ok;
 3863    NYD_X;
 3864 
 3865    ok = STOP;
 3866    queuefp = NULL;
 3867 
 3868    if((qname = imap_path_quote(mp, name)) != NULL){
 3869       o = ac_alloc(os = strlen(qname) + 100);
 3870       snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
 3871       IMAP_OUT(o, MB_COMD, goto out)
 3872       while (mp->mb_active & MB_COMD)
 3873          ok = imap_answer(mp, 1);
 3874 out:
 3875       ac_free(o);
 3876    }
 3877    return ok;
 3878 }
 3879 
 3880 FL enum okay
 3881 imap_rename(const char *old, const char *new)
 3882 {
 3883    sighandler_type saveint, savepipe;
 3884    enum okay volatile rv = STOP;
 3885    NYD_ENTER;
 3886 
 3887    if (mb.mb_type != MB_IMAP) {
 3888       n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
 3889       goto jleave;
 3890    }
 3891 
 3892    if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
 3893       n_err(_("Can only rename mailboxes on current IMAP "
 3894             "server: \"%s\" not renamed to \"%s\"\n"), old, new);
 3895       goto jleave;
 3896    }
 3897 
 3898    imaplock = 1;
 3899    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3900       safe_signal(SIGINT, &_imap_maincatch);
 3901    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3902    if (sigsetjmp(imapjmp, 1) == 0) {
 3903       if (savepipe != SIG_IGN)
 3904          safe_signal(SIGPIPE, imapcatch);
 3905 
 3906       rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
 3907    }
 3908    safe_signal(SIGINT, saveint);
 3909    safe_signal(SIGPIPE, savepipe);
 3910    imaplock = 0;
 3911 
 3912    if (rv == OKAY)
 3913       rv = cache_rename(old, new);
 3914 jleave:
 3915    NYD_LEAVE;
 3916    if (interrupts)
 3917       n_go_onintr_for_imap();
 3918    return rv;
 3919 }
 3920 
 3921 static enum okay
 3922 imap_rename1(struct mailbox *mp, const char *old, const char *new)
 3923 {
 3924    char *o;
 3925    int os;
 3926    char const *qoname, *qnname;
 3927    FILE *queuefp;
 3928    enum okay ok;
 3929    NYD_X;
 3930 
 3931    ok = STOP;
 3932    queuefp = NULL;
 3933 
 3934    if((qoname = imap_path_quote(mp, old)) != NULL &&
 3935          (qnname = imap_path_quote(mp, new)) != NULL){
 3936       o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
 3937       snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
 3938       IMAP_OUT(o, MB_COMD, goto out)
 3939       while (mp->mb_active & MB_COMD)
 3940          ok = imap_answer(mp, 1);
 3941 out:
 3942       ac_free(o);
 3943    }
 3944    return ok;
 3945 }
 3946 
 3947 FL enum okay
 3948 imap_dequeue(struct mailbox *mp, FILE *fp)
 3949 {
 3950    char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
 3951    size_t bufsize, buflen, cnt;
 3952    long offs, offs1, offs2, octets;
 3953    int twice, gotcha = 0;
 3954    FILE *queuefp = NULL;
 3955    enum okay ok = OKAY, rok = OKAY;
 3956    NYD_X;
 3957 
 3958    buf = smalloc(bufsize = LINESIZE);
 3959    buflen = 0;
 3960    cnt = fsize(fp);
 3961    while ((offs1 = ftell(fp)) >= 0 &&
 3962          fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
 3963       for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
 3964          ;
 3965       while (*bp == ' ')
 3966          ++bp;
 3967       twice = 0;
 3968       if ((offs = ftell(fp)) < 0)
 3969          goto fail;
 3970 again:
 3971       snprintf(o, sizeof o, "%s %s", tag(1), bp);
 3972       if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
 3973          cp = &bp[9];
 3974          while (digitchar(*cp))
 3975             cp++;
 3976          if (*cp != ' ')
 3977             goto fail;
 3978          while (*cp == ' ')
 3979             cp++;
 3980          if ((newname = imap_strex(cp, NULL)) == NULL)
 3981             goto fail;
 3982          IMAP_OUT(o, MB_COMD, continue)
 3983          while (mp->mb_active & MB_COMD)
 3984             ok = imap_answer(mp, twice);
 3985          if (response_status == RESPONSE_NO && twice++ == 0)
 3986             goto trycreate;
 3987          if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
 3988             imap_copyuid(mp, NULL, imap_unquotestr(newname));
 3989          }
 3990       } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
 3991          IMAP_OUT(o, MB_COMD, continue)
 3992          while (mp->mb_active & MB_COMD)
 3993             ok = imap_answer(mp, 1);
 3994          if (ok == OKAY)
 3995             gotcha++;
 3996       } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
 3997          if ((cp = strrchr(bp, '{')) == NULL)
 3998             goto fail;
 3999          octets = atol(&cp[1]) + 2;
 4000          if ((newname = imap_strex(&bp[7], NULL)) == NULL)
 4001             goto fail;
 4002          IMAP_OUT(o, MB_COMD, continue)
 4003          while (mp->mb_active & MB_COMD) {
 4004             ok = imap_answer(mp, twice);
 4005             if (response_type == RESPONSE_CONT)
 4006                break;
 4007          }
 4008          if (ok == STOP) {
 4009             if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
 4010                goto trycreate;
 4011             goto fail;
 4012          }
 4013          while (octets > 0) {
 4014             size_t n = (UICMP(z, octets, >, sizeof iob)
 4015                   ? sizeof iob : (size_t)octets);
 4016             octets -= n;
 4017             if (n != fread(iob, 1, n, fp))
 4018                goto fail;
 4019             swrite1(&mp->mb_sock, iob, n, 1);
 4020          }
 4021          swrite(&mp->mb_sock, "");
 4022          while (mp->mb_active & MB_COMD) {
 4023             ok = imap_answer(mp, 0);
 4024             if (response_status == RESPONSE_NO && twice++ == 0) {
 4025                if (fseek(fp, offs, SEEK_SET) < 0)
 4026                   goto fail;
 4027                goto trycreate;
 4028             }
 4029          }
 4030          if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
 4031             if ((offs2 = ftell(fp)) < 0)
 4032                goto fail;
 4033             fseek(fp, offs1, SEEK_SET);
 4034             if (imap_appenduid_cached(mp, fp) == STOP) {
 4035                (void)fseek(fp, offs2, SEEK_SET);
 4036                goto fail;
 4037             }
 4038          }
 4039       } else {
 4040 fail:
 4041          n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
 4042          rok = STOP;
 4043       }
 4044       continue;
 4045 trycreate:
 4046       snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
 4047       IMAP_OUT(o, MB_COMD, continue)
 4048       while (mp->mb_active & MB_COMD)
 4049          ok = imap_answer(mp, 1);
 4050       if (ok == OKAY)
 4051          goto again;
 4052    }
 4053    fflush(fp);
 4054    rewind(fp);
 4055    ftruncate(fileno(fp), 0);
 4056    if (gotcha)
 4057       imap_close(mp);
 4058    free(buf);
 4059    return rok;
 4060 }
 4061 
 4062 static char *
 4063 imap_strex(char const *cp, char const **xp)
 4064 {
 4065    char const *cq;
 4066    char *n = NULL;
 4067    NYD_ENTER;
 4068 
 4069    if (*cp != '"')
 4070       goto jleave;
 4071 
 4072    for (cq = cp + 1; *cq != '\0'; ++cq) {
 4073       if (*cq == '\\')
 4074          cq++;
 4075       else if (*cq == '"')
 4076          break;
 4077    }
 4078    if (*cq != '"')
 4079       goto jleave;
 4080 
 4081    n = salloc(cq - cp + 2);
 4082    memcpy(n, cp, cq - cp +1);
 4083    n[cq - cp + 1] = '\0';
 4084    if (xp != NULL)
 4085       *xp = cq + 1;
 4086 jleave:
 4087    NYD_LEAVE;
 4088    return n;
 4089 }
 4090 
 4091 static enum okay
 4092 check_expunged(void)
 4093 {
 4094    enum okay rv;
 4095    NYD_ENTER;
 4096 
 4097    if (expunged_messages > 0) {
 4098       n_err(_("Command not executed - messages have been expunged\n"));
 4099       rv = STOP;
 4100    } else
 4101       rv = OKAY;
 4102    NYD_LEAVE;
 4103    return rv;
 4104 }
 4105 
 4106 FL int
 4107 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
 4108 {
 4109    struct url url;
 4110    int rv, omsgCount = msgCount;
 4111    NYD_ENTER;
 4112    n_UNUSED(vp);
 4113 
 4114    if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
 4115       n_err(_("Already connected\n"));
 4116       rv = 1;
 4117       goto jleave;
 4118    }
 4119 
 4120    if (!url_parse(&url, CPROTO_IMAP, mailname)) {
 4121       rv = 1;
 4122       goto jleave;
 4123    }
 4124    ok_bclear(disconnected);
 4125    n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
 4126 
 4127    if (mb.mb_type == MB_CACHE) {
 4128       enum fedit_mode fm = FEDIT_NONE;
 4129       if (_imap_rdonly)
 4130          fm |= FEDIT_RDONLY;
 4131       if (!(n_pstate & n_PS_EDIT))
 4132          fm |= FEDIT_SYSBOX;
 4133       _imap_setfile1(&url, fm, 1);
 4134       if (msgCount > omsgCount)
 4135          newmailinfo(omsgCount);
 4136    }
 4137    rv = 0;
 4138 jleave:
 4139    NYD_LEAVE;
 4140    return rv;
 4141 }
 4142 
 4143 FL int
 4144 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
 4145 {
 4146    struct url url;
 4147    int rv = 1, *msgvec = vp;
 4148    NYD_ENTER;
 4149 
 4150    if (mb.mb_type == MB_CACHE) {
 4151       n_err(_("Not connected\n"));
 4152       goto jleave;
 4153    }
 4154    if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
 4155       n_err(_("The current mailbox is not cached\n"));
 4156       goto jleave;
 4157    }
 4158 
 4159    if (!url_parse(&url, CPROTO_IMAP, mailname))
 4160       goto jleave;
 4161 
 4162    if (*msgvec)
 4163       c_cache(vp);
 4164    ok_bset(disconnected);
 4165    if (mb.mb_type == MB_IMAP) {
 4166       enum fedit_mode fm = FEDIT_NONE;
 4167       if (_imap_rdonly)
 4168          fm |= FEDIT_RDONLY;
 4169       if (!(n_pstate & n_PS_EDIT))
 4170          fm |= FEDIT_SYSBOX;
 4171       sclose(&mb.mb_sock);
 4172       _imap_setfile1(&url, fm, 1);
 4173    }
 4174    rv = 0;
 4175 jleave:
 4176    NYD_LEAVE;
 4177    return rv;
 4178 }
 4179 
 4180 FL int
 4181 c_cache(void *vp)
 4182 {
 4183    int rv = 1, *msgvec = vp, *ip;
 4184    struct message *mp;
 4185    NYD_ENTER;
 4186 
 4187    if (mb.mb_type != MB_IMAP) {
 4188       n_err(_("Not connected to an IMAP server\n"));
 4189       goto jleave;
 4190    }
 4191    if (cached_uidvalidity(&mb) == 0) {
 4192       n_err(_("The current mailbox is not cached\n"));
 4193       goto jleave;
 4194    }
 4195 
 4196    srelax_hold();
 4197    for (ip = msgvec; *ip; ++ip) {
 4198       mp = &message[*ip - 1];
 4199       if (!(mp->m_content_info & CI_HAVE_BODY)) {
 4200          get_body(mp);
 4201          srelax();
 4202       }
 4203    }
 4204    srelax_rele();
 4205    rv = 0;
 4206 jleave:
 4207    NYD_LEAVE;
 4208    return rv;
 4209 }
 4210 
 4211 FL int
 4212 disconnected(const char *file)
 4213 {
 4214    struct url url;
 4215    int rv = 1;
 4216    NYD_ENTER;
 4217 
 4218    if (ok_blook(disconnected)) {
 4219       rv = 1;
 4220       goto jleave;
 4221    }
 4222 
 4223    if (!url_parse(&url, CPROTO_IMAP, file)) {
 4224       rv = 0;
 4225       goto jleave;
 4226    }
 4227    rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
 4228 
 4229 jleave:
 4230    NYD_LEAVE;
 4231    return rv;
 4232 }
 4233 
 4234 FL void
 4235 transflags(struct message *omessage, long omsgCount, int transparent)
 4236 {
 4237    struct message *omp, *nmp, *newdot, *newprevdot;
 4238    int hf;
 4239    NYD_ENTER;
 4240 
 4241    omp = omessage;
 4242    nmp = message;
 4243    newdot = message;
 4244    newprevdot = NULL;
 4245    while (PTRCMP(omp, <, omessage + omsgCount) &&
 4246          PTRCMP(nmp, <, message + msgCount)) {
 4247       if (dot && nmp->m_uid == dot->m_uid)
 4248          newdot = nmp;
 4249       if (prevdot && nmp->m_uid == prevdot->m_uid)
 4250          newprevdot = nmp;
 4251       if (omp->m_uid == nmp->m_uid) {
 4252          hf = nmp->m_flag & MHIDDEN;
 4253          if (transparent && mb.mb_type == MB_IMAP)
 4254             omp->m_flag &= ~MHIDDEN;
 4255          *nmp++ = *omp++;
 4256          if (transparent && mb.mb_type == MB_CACHE)
 4257             nmp[-1].m_flag |= hf;
 4258       } else if (omp->m_uid < nmp->m_uid)
 4259          ++omp;
 4260       else
 4261          ++nmp;
 4262    }
 4263    dot = newdot;
 4264    setdot(newdot);
 4265    prevdot = newprevdot;
 4266    free(omessage);
 4267    NYD_LEAVE;
 4268 }
 4269 
 4270 FL time_t
 4271 imap_read_date_time(const char *cp)
 4272 {
 4273    char buf[3];
 4274    time_t t;
 4275    int i, year, month, day, hour, minute, second, sign = -1;
 4276    NYD2_ENTER;
 4277 
 4278    /* "25-Jul-2004 15:33:44 +0200"
 4279     * |    |    |    |    |    |
 4280     * 0    5   10   15   20   25 */
 4281    if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
 4282       goto jinvalid;
 4283    day = strtol(&cp[1], NULL, 10);
 4284    for (i = 0;;) {
 4285       if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
 4286          break;
 4287       if (n_month_names[++i][0] == '\0')
 4288          goto jinvalid;
 4289    }
 4290    month = i + 1;
 4291    year = strtol(&cp[8], NULL, 10);
 4292    hour = strtol(&cp[13], NULL, 10);
 4293    minute = strtol(&cp[16], NULL, 10);
 4294    second = strtol(&cp[19], NULL, 10);
 4295    if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
 4296       goto jinvalid;
 4297    switch (cp[22]) {
 4298    case '-':
 4299       sign = 1;
 4300       break;
 4301    case '+':
 4302       break;
 4303    default:
 4304       goto jinvalid;
 4305    }
 4306    buf[2] = '\0';
 4307    buf[0] = cp[23];
 4308    buf[1] = cp[24];
 4309    t += strtol(buf, NULL, 10) * sign * 3600;
 4310    buf[0] = cp[25];
 4311    buf[1] = cp[26];
 4312    t += strtol(buf, NULL, 10) * sign * 60;
 4313 jleave:
 4314    NYD2_LEAVE;
 4315    return t;
 4316 jinvalid:
 4317    time(&t);
 4318    goto jleave;
 4319 }
 4320 
 4321 FL const char *
 4322 imap_make_date_time(time_t t)
 4323 {
 4324    static char s[40];
 4325    char const *mn;
 4326    si32_t y, md, th, tm, ts;
 4327    struct tm *tmp;
 4328    int tzdiff, tzdiff_hour, tzdiff_min;
 4329    time_t t2;
 4330    NYD2_ENTER;
 4331 
 4332 jredo:
 4333    if((t2 = mktime(gmtime(&t))) == (time_t)-1){
 4334       t = 0;
 4335       goto jredo;
 4336    }
 4337    tzdiff = t - t2;
 4338    if((tmp = localtime(&t)) == NULL){
 4339       t = 0;
 4340       goto jredo;
 4341    }
 4342 
 4343    tzdiff_hour = (int)(tzdiff / 60);
 4344    tzdiff_min = tzdiff_hour % 60;
 4345    tzdiff_hour /= 60;
 4346    if (tmp->tm_isdst > 0)
 4347       tzdiff_hour++;
 4348 
 4349    if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
 4350       y = 1970;
 4351       mn = n_month_names[0];
 4352       md = 1;
 4353       th = tm = ts = 0;
 4354    }else{
 4355       y += 1900;
 4356       mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
 4357             ? n_month_names[tmp->tm_mon] : n_qm;
 4358 
 4359       if((md = tmp->tm_mday) < 1 || md > 31)
 4360          md = 1;
 4361 
 4362       if((th = tmp->tm_hour) < 0 || th > 23)
 4363          th = 0;
 4364       if((tm = tmp->tm_min) < 0 || tm > 59)
 4365          tm = 0;
 4366       if((ts = tmp->tm_sec) < 0 || ts > 60)
 4367          ts = 0;
 4368    }
 4369 
 4370    snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
 4371          md, mn, y, th, tm, ts, tzdiff_hour, tzdiff_min);
 4372    NYD2_LEAVE;
 4373    return s;
 4374 }
 4375 
 4376 FL char *
 4377 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
 4378 {
 4379    char *n, *np;
 4380    NYD2_ENTER;
 4381 
 4382    np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
 4383          n_MEMORY_DEBUG_ARGSCALL);
 4384 
 4385    /* Just ignore the `is-system-mailbox' prefix XXX */
 4386    if (cp[0] == '%' && cp[1] == ':')
 4387       cp += 2;
 4388 
 4389    while (*cp != '\0') {
 4390       if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
 4391          *np++ = *cp++;
 4392          *np++ = *cp++;
 4393          *np++ = *cp++;
 4394       } else if (cp[0] == '/')
 4395          break;
 4396       else
 4397          *np++ = *cp++;
 4398    }
 4399    *np = '\0';
 4400    NYD2_LEAVE;
 4401    return n;
 4402 }
 4403 #endif /* HAVE_IMAP */
 4404 
 4405 /* s-it-mode */