"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/obs-imap.c" (25 Mar 2018, 115978 Bytes) of package /linux/misc/s-nail-14.9.10.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.9_vs_14.9.10.

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