"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/obs-imap.c" (8 Aug 2018, 116697 Bytes) of package /linux/misc/s-nail-14.9.11.tar.xz:


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

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ 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  * SPDX-License-Identifier: BSD-4-Clause
    7  */
    8 /*
    9  * Copyright (c) 2004
   10  * Gunnar Ritter.  All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. All advertising materials mentioning features or use of this software
   21  *    must display the following acknowledgement:
   22  *    This product includes software developed by Gunnar Ritter
   23  *    and his contributors.
   24  * 4. Neither the name of Gunnar Ritter nor the names of his contributors
   25  *    may be used to endorse or promote products derived from this software
   26  *    without specific prior written permission.
   27  *
   28  * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
   29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   31  * ARE DISCLAIMED.  IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
   32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   38  * SUCH DAMAGE.
   39  */
   40 #undef n_FILE
   41 #define n_FILE obs_imap
   42 
   43 #ifndef HAVE_AMALGAMATION
   44 # include "nail.h"
   45 #endif
   46 
   47 #ifdef HAVE_IMAP
   48 # include <sys/socket.h>
   49 
   50 # include <netdb.h>
   51 
   52 # include <netinet/in.h>
   53 
   54 # ifdef HAVE_ARPA_INET_H
   55 #  include <arpa/inet.h>
   56 # endif
   57 #endif
   58 
   59 EMPTY_FILE()
   60 #ifdef HAVE_IMAP
   61 #define IMAP_ANSWER() \
   62 {\
   63    if (mp->mb_type != MB_CACHE) {\
   64       enum okay ok = OKAY;\
   65       while (mp->mb_active & MB_COMD)\
   66          ok = imap_answer(mp, 1);\
   67       if (ok == STOP)\
   68          return STOP;\
   69    }\
   70 }
   71 
   72 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
   73  * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
   74  * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
   75  * TODO and (2) doesn't handle all I/O errors itself, yet, too.
   76  * TODO I.e., that should be a function, not a macro ... or so.
   77  * TODO This entire module needs MASSIVE work! */
   78 #define IMAP_OUT(X,Y,ACTION)  IMAP_XOUT(X, Y, ACTION, return STOP)
   79 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
   80 {\
   81    if (mp->mb_type != MB_CACHE) {\
   82       if (imap_finish(mp) == STOP) {\
   83          ACTIONBAIL;\
   84       }\
   85       if (n_poption & n_PO_D_VV)\
   86          n_err(">>> %s", X);\
   87       mp->mb_active |= Y;\
   88       if (swrite(&mp->mb_sock, X) == STOP) {\
   89          ACTIONERR;\
   90       }\
   91    } else {\
   92       if (queuefp != NULL)\
   93          fputs(X, queuefp);\
   94    }\
   95 }
   96 
   97 static struct record {
   98    struct record  *rec_next;
   99    unsigned long  rec_count;
  100    enum rec_type {
  101       REC_EXISTS,
  102       REC_EXPUNGE
  103    }              rec_type;
  104 } *record, *recend;
  105 
  106 static enum {
  107    RESPONSE_TAGGED,
  108    RESPONSE_DATA,
  109    RESPONSE_FATAL,
  110    RESPONSE_CONT,
  111    RESPONSE_ILLEGAL
  112 } response_type;
  113 
  114 static enum {
  115    RESPONSE_OK,
  116    RESPONSE_NO,
  117    RESPONSE_BAD,
  118    RESPONSE_PREAUTH,
  119    RESPONSE_BYE,
  120    RESPONSE_OTHER,
  121    RESPONSE_UNKNOWN
  122 } response_status;
  123 
  124 static char *responded_tag;
  125 static char *responded_text;
  126 static char *responded_other_text;
  127 static long responded_other_number;
  128 
  129 static enum {
  130    MAILBOX_DATA_FLAGS,
  131    MAILBOX_DATA_LIST,
  132    MAILBOX_DATA_LSUB,
  133    MAILBOX_DATA_MAILBOX,
  134    MAILBOX_DATA_SEARCH,
  135    MAILBOX_DATA_STATUS,
  136    MAILBOX_DATA_EXISTS,
  137    MAILBOX_DATA_RECENT,
  138    MESSAGE_DATA_EXPUNGE,
  139    MESSAGE_DATA_FETCH,
  140    CAPABILITY_DATA,
  141    RESPONSE_OTHER_UNKNOWN
  142 } response_other;
  143 
  144 static enum list_attributes {
  145    LIST_NONE         = 000,
  146    LIST_NOINFERIORS  = 001,
  147    LIST_NOSELECT     = 002,
  148    LIST_MARKED       = 004,
  149    LIST_UNMARKED     = 010
  150 } list_attributes;
  151 
  152 static int  list_hierarchy_delimiter;
  153 static char *list_name;
  154 
  155 struct list_item {
  156    struct list_item     *l_next;
  157    char                 *l_name;
  158    char                 *l_base;
  159    enum list_attributes l_attr;
  160    int                  l_delim;
  161    int                  l_level;
  162    int                  l_has_children;
  163 };
  164 
  165 static char             *imapbuf;   /* TODO not static, use pool */
  166 static size_t           imapbufsize;
  167 static sigjmp_buf       imapjmp;
  168 static sighandler_type  savealrm;
  169 static int              imapkeepalive;
  170 static long             had_exists = -1;
  171 static long             had_expunge = -1;
  172 static long             expunged_messages;
  173 static int volatile     imaplock;
  174 static int              same_imap_account;
  175 static bool_t           _imap_rdonly;
  176 
  177 static char *imap_quotestr(char const *s);
  178 static char *imap_unquotestr(char const *s);
  179 static void imap_delim_init(struct mailbox *mp, struct url const *urlp);
  180 static char const *imap_path_normalize(struct mailbox *mp, char const *cp);
  181 /* Returns NULL on error */
  182 static char *imap_path_quote(struct mailbox *mp, char const *cp);
  183 static void       imap_other_get(char *pp);
  184 static void       imap_response_get(const char **cp);
  185 static void       imap_response_parse(void);
  186 static enum okay  imap_answer(struct mailbox *mp, int errprnt);
  187 static enum okay  imap_parse_list(void);
  188 static enum okay  imap_finish(struct mailbox *mp);
  189 static void       imap_timer_off(void);
  190 static void       imapcatch(int s);
  191 static void       _imap_maincatch(int s);
  192 static enum okay  imap_noop1(struct mailbox *mp);
  193 static void       rec_queue(enum rec_type type, unsigned long cnt);
  194 static enum okay  rec_dequeue(void);
  195 static void       rec_rmqueue(void);
  196 static void       imapalarm(int s);
  197 static enum okay  imap_preauth(struct mailbox *mp, struct url *urlp);
  198 static enum okay  imap_capability(struct mailbox *mp);
  199 static enum okay  imap_auth(struct mailbox *mp, struct ccred *ccred);
  200 #ifdef HAVE_MD5
  201 static enum okay  imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
  202 #endif
  203 static enum okay  imap_login(struct mailbox *mp, struct ccred *ccred);
  204 #ifdef HAVE_GSSAPI
  205 static enum okay  _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
  206 #endif
  207 static enum okay  imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
  208 static void       imap_init(struct mailbox *mp, int n);
  209 static void       imap_setptr(struct mailbox *mp, int nmail, int transparent,
  210                      int *prevcount);
  211 static bool_t     _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
  212                      struct url *urlp);
  213 static int _imap_setfile1(char const *who, struct url *urlp,
  214             enum fedit_mode fm, int transparent);
  215 static int        imap_fetchdata(struct mailbox *mp, struct message *m,
  216                      size_t expected, int need, const char *head,
  217                      size_t headsize, long headlines);
  218 static void       imap_putstr(struct mailbox *mp, struct message *m,
  219                      const char *str, const char *head, size_t headsize,
  220                      long headlines);
  221 static enum okay  imap_get(struct mailbox *mp, struct message *m,
  222                      enum needspec need);
  223 static void       commitmsg(struct mailbox *mp, struct message *to,
  224                      struct message *from, enum content_info content_info);
  225 static enum okay  imap_fetchheaders(struct mailbox *mp, struct message *m,
  226                      int bot, int top);
  227 static enum okay  imap_exit(struct mailbox *mp);
  228 static enum okay  imap_delete(struct mailbox *mp, int n, struct message *m,
  229                      int needstat);
  230 static enum okay  imap_close(struct mailbox *mp);
  231 static enum okay  imap_update(struct mailbox *mp);
  232 static enum okay  imap_store(struct mailbox *mp, struct message *m, int n,
  233                      int c, const char *sp, int needstat);
  234 static enum okay  imap_unstore(struct message *m, int n, const char *flag);
  235 static const char *tag(int new);
  236 static char *     imap_putflags(int f);
  237 static void       imap_getflags(const char *cp, char const **xp, enum mflag *f);
  238 static enum okay  imap_append1(struct mailbox *mp, const char *name, FILE *fp,
  239                      off_t off1, long xsize, enum mflag flag, time_t t);
  240 static enum okay  imap_append0(struct mailbox *mp, const char *name, FILE *fp,
  241                      long offset);
  242 static enum okay  imap_list1(struct mailbox *mp, const char *base,
  243                      struct list_item **list, struct list_item **lend,
  244                      int level);
  245 static enum okay  imap_list(struct mailbox *mp, const char *base, int strip,
  246                      FILE *fp);
  247 static enum okay  imap_copy1(struct mailbox *mp, struct message *m, int n,
  248                      const char *name);
  249 static enum okay  imap_copyuid_parse(const char *cp,
  250                      ui64_t *uidvalidity, ui64_t *olduid, ui64_t *newuid);
  251 static enum okay  imap_appenduid_parse(const char *cp,
  252                      ui64_t *uidvalidity, ui64_t *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 = n_autorec_alloc(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 = n_autorec_alloc(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 = n_autorec_alloc(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 = n_autorec_alloc((l << 1) +1); /* XXX use n_string, resize */
  452 
  453    out.s = n_autorec_alloc(l_plain + (l << 2) +1); /* XXX use n_string.. */
  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 = n_autorec_alloc(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 = n_autorec_alloc(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 = n_realloc(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 = n_calloc(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 = n_alloc((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          n_free(rq);
 1236       rq = rp;
 1237       rp = rp->rec_next;
 1238    }
 1239    if (rq != NULL)
 1240       n_free(rq);
 1241 
 1242    record = recend = NULL;
 1243    if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
 1244       message = n_realloc(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       n_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       n_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 *urlp)
 1311 {
 1312    NYD_X;
 1313 
 1314    mp->mb_active |= MB_PREAUTH;
 1315    imap_answer(mp, 1);
 1316 
 1317 #ifdef HAVE_TLS
 1318    if (!mp->mb_sock.s_use_tls && 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(!n_tls_open(urlp, &mp->mb_sock))
 1326          return STOP;
 1327    }
 1328 #else
 1329    if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
 1330       n_err(_("No TLS 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          n_idec_ui64_cp(&mp->mb_uidvalidity, &cp[13], 10, NULL);/* TODO err? */
 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 = n_calloc(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(char const * volatile who, const char *xserver,
 1632    enum fedit_mode fm)
 1633 {
 1634    struct url url;
 1635    int rv;
 1636    NYD_ENTER;
 1637 
 1638    if (!url_parse(&url, CPROTO_IMAP, xserver)) {
 1639       rv = 1;
 1640       goto jleave;
 1641    }
 1642    if (!ok_blook(v15_compat) &&
 1643          (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
 1644       n_err(_("New-style URL used without *v15-compat* being set!\n"));
 1645 
 1646    _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
 1647    rv = _imap_setfile1(who, &url, fm, 0);
 1648 jleave:
 1649    NYD_LEAVE;
 1650    return rv;
 1651 }
 1652 
 1653 static bool_t
 1654 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
 1655 {
 1656    bool_t rv = FAL0;
 1657    NYD_ENTER;
 1658 
 1659    if (ok_blook(v15_compat))
 1660       rv = ccred_lookup(ccredp, urlp);
 1661    else {
 1662       char *var, *old,
 1663          *xuhp = ((urlp->url_flags & n_URL_HAD_USER) ? urlp->url_eu_h_p.s
 1664                : urlp->url_u_h_p.s);
 1665 
 1666       if ((var = mbp->mb_imap_pass) != NULL) {
 1667          var = savecat("password-", xuhp);
 1668          if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
 1669             old = sstrdup(old);
 1670          n_var_vset(var, (uintptr_t)mbp->mb_imap_pass);
 1671       }
 1672       rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
 1673       if (var != NULL) {
 1674          if (old != NULL) {
 1675             n_var_vset(var, (uintptr_t)old);
 1676             n_free(old);
 1677          } else
 1678             n_var_vclear(var);
 1679       }
 1680    }
 1681 
 1682    NYD_LEAVE;
 1683    return rv;
 1684 }
 1685 
 1686 static int
 1687 _imap_setfile1(char const * volatile who, struct url *urlp,
 1688    enum fedit_mode volatile fm, int volatile transparent)
 1689 {
 1690    struct sock so;
 1691    struct ccred ccred;
 1692    sighandler_type volatile saveint, savepipe;
 1693    char const *cp;
 1694    int rv;
 1695    int volatile prevcount = 0;
 1696    enum mbflags same_flags;
 1697    NYD_ENTER;
 1698 
 1699    if (fm & FEDIT_NEWMAIL) {
 1700       saveint = safe_signal(SIGINT, SIG_IGN);
 1701       savepipe = safe_signal(SIGPIPE, SIG_IGN);
 1702       if (saveint != SIG_IGN)
 1703          safe_signal(SIGINT, imapcatch);
 1704       if (savepipe != SIG_IGN)
 1705          safe_signal(SIGPIPE, imapcatch);
 1706       imaplock = 1;
 1707       goto jnmail;
 1708    }
 1709 
 1710    same_flags = mb.mb_flags;
 1711    same_imap_account = 0;
 1712    if (mb.mb_imap_account != NULL &&
 1713          (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
 1714       if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
 1715             !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
 1716             disconnected(mb.mb_imap_account) == 0) {
 1717          same_imap_account = 1;
 1718          if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
 1719 /*
 1720             goto jduppass;
 1721       } else if ((transparent || mb.mb_type == MB_CACHE) &&
 1722             !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
 1723             urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
 1724 jduppass:
 1725 */
 1726          urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
 1727       }
 1728    }
 1729 
 1730    if (!same_imap_account && mb.mb_imap_pass != NULL) {
 1731       n_free(mb.mb_imap_pass);
 1732       mb.mb_imap_pass = NULL;
 1733    }
 1734    if (!_imap_getcred(&mb, &ccred, urlp)) {
 1735       rv = -1;
 1736       goto jleave;
 1737    }
 1738 
 1739    memset(&so, 0, sizeof so);
 1740    so.s_fd = -1;
 1741    if (!same_imap_account) {
 1742       if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
 1743          rv = -1;
 1744          goto jleave;
 1745       }
 1746    } else
 1747       so = mb.mb_sock;
 1748    if (!transparent) {
 1749       if(!quit(FAL0)){
 1750          rv = -1;
 1751          goto jleave;
 1752       }
 1753    }
 1754 
 1755    if (fm & FEDIT_SYSBOX)
 1756       n_pstate &= ~n_PS_EDIT;
 1757    else
 1758       n_pstate |= n_PS_EDIT;
 1759    if (mb.mb_imap_account != NULL)
 1760       n_free(mb.mb_imap_account);
 1761    if (mb.mb_imap_pass != NULL)
 1762       n_free(mb.mb_imap_pass);
 1763    mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
 1764    /* TODO This is a hack to allow '@boxname'; in the end everything will be an
 1765     * TODO object, and mailbox will naturally have an URL and credentials */
 1766    mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
 1767 
 1768    if (!same_imap_account) {
 1769       if (mb.mb_sock.s_fd >= 0)
 1770          sclose(&mb.mb_sock);
 1771    }
 1772    same_imap_account = 0;
 1773 
 1774    if (!transparent) {
 1775       if (mb.mb_itf) {
 1776          fclose(mb.mb_itf);
 1777          mb.mb_itf = NULL;
 1778       }
 1779       if (mb.mb_otf) {
 1780          fclose(mb.mb_otf);
 1781          mb.mb_otf = NULL;
 1782       }
 1783       if (mb.mb_imap_mailbox != NULL)
 1784          n_free(mb.mb_imap_mailbox);
 1785       assert(urlp->url_path.s != NULL);
 1786       imap_delim_init(&mb, urlp);
 1787       mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
 1788       initbox(savecatsep(urlp->url_p_eu_h_p,
 1789          (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
 1790          mb.mb_imap_mailbox));
 1791    }
 1792    mb.mb_type = MB_VOID;
 1793    mb.mb_active = MB_NONE;
 1794 
 1795    imaplock = 1;
 1796    saveint = safe_signal(SIGINT, SIG_IGN);
 1797    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 1798    if (sigsetjmp(imapjmp, 1)) {
 1799       /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
 1800       sclose(&mb.mb_sock);
 1801       safe_signal(SIGINT, saveint);
 1802       safe_signal(SIGPIPE, savepipe);
 1803       imaplock = 0;
 1804 
 1805       mb.mb_type = MB_VOID;
 1806       mb.mb_active = MB_NONE;
 1807       rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
 1808       goto jleave;
 1809    }
 1810    if (saveint != SIG_IGN)
 1811       safe_signal(SIGINT, imapcatch);
 1812    if (savepipe != SIG_IGN)
 1813       safe_signal(SIGPIPE, imapcatch);
 1814 
 1815    if (mb.mb_sock.s_fd < 0) {
 1816       if (disconnected(mb.mb_imap_account)) {
 1817          if (cache_setptr(fm, transparent) == STOP)
 1818             n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
 1819          goto jdone;
 1820       }
 1821       if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
 1822          if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
 1823             savealrm = safe_signal(SIGALRM, imapalarm);
 1824             alarm(imapkeepalive);
 1825          }
 1826       }
 1827 
 1828       mb.mb_sock = so;
 1829       mb.mb_sock.s_desc = "IMAP";
 1830       mb.mb_sock.s_onclose = imap_timer_off;
 1831       if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
 1832          sclose(&mb.mb_sock);
 1833          imap_timer_off();
 1834          safe_signal(SIGINT, saveint);
 1835          safe_signal(SIGPIPE, savepipe);
 1836          imaplock = 0;
 1837          rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
 1838          goto jleave;
 1839       }
 1840    } else   /* same account */
 1841       mb.mb_flags |= same_flags;
 1842 
 1843    if (n_poption & n_PO_R_FLAG)
 1844       fm |= FEDIT_RDONLY;
 1845    mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
 1846    mb.mb_type = MB_IMAP;
 1847    cache_dequeue(&mb);
 1848    assert(urlp->url_path.s != NULL);
 1849    if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
 1850       /*sclose(&mb.mb_sock);
 1851       imap_timer_off();*/
 1852       safe_signal(SIGINT, saveint);
 1853       safe_signal(SIGPIPE, savepipe);
 1854       imaplock = 0;
 1855       mb.mb_type = MB_VOID;
 1856       rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
 1857       goto jleave;
 1858    }
 1859 
 1860 jnmail:
 1861    imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
 1862       n_UNVOLATILE(&prevcount));
 1863 jdone:
 1864    setmsize(msgCount);
 1865    safe_signal(SIGINT, saveint);
 1866    safe_signal(SIGPIPE, savepipe);
 1867    imaplock = 0;
 1868 
 1869    if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
 1870       purgecache(&mb, message, msgCount);
 1871    if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
 1872       mb.mb_threaded = 0;
 1873       c_sort((void*)-1);
 1874    }
 1875 
 1876    if (!(fm & FEDIT_NEWMAIL) && !transparent) {
 1877       n_pstate &= ~n_PS_SAW_COMMAND;
 1878       n_pstate |= n_PS_SETFILE_OPENED;
 1879    }
 1880 
 1881    if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
 1882          mb.mb_type == MB_CACHE)) {
 1883       rv = (msgCount == 0);
 1884       goto jleave;
 1885    }
 1886 
 1887    if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
 1888       if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
 1889             !ok_blook(emptystart)){
 1890          char const *intro;
 1891 
 1892          if(who == NULL)
 1893             intro = who = n_empty;
 1894          else
 1895             intro = _(" for ");
 1896          n_err(_("No mail%s%s at %s\n"), intro, who, urlp->url_p_eu_h_p_p);
 1897       }
 1898       rv = 1;
 1899       goto jleave;
 1900    }
 1901 
 1902    if (fm & FEDIT_NEWMAIL)
 1903       newmailinfo(prevcount);
 1904    rv = 0;
 1905 jleave:
 1906    NYD_LEAVE;
 1907    return rv;
 1908 }
 1909 
 1910 static int
 1911 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
 1912    int need, const char *head, size_t headsize, long headlines)
 1913 {
 1914    char *line = NULL, *lp;
 1915    size_t linesize = 0, linelen, size = 0;
 1916    int emptyline = 0, lines = 0, excess = 0;
 1917    off_t offset;
 1918    NYD_ENTER;
 1919 
 1920    fseek(mp->mb_otf, 0L, SEEK_END);
 1921    offset = ftell(mp->mb_otf);
 1922 
 1923    if (head)
 1924       fwrite(head, 1, headsize, mp->mb_otf);
 1925 
 1926    while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
 1927       lp = line;
 1928       if (linelen > expected) {
 1929          excess = linelen - expected;
 1930          linelen = expected;
 1931       }
 1932       /* TODO >>
 1933        * Need to mask 'From ' lines. This cannot be done properly
 1934        * since some servers pass them as 'From ' and others as
 1935        * '>From '. Although one could identify the first kind of
 1936        * server in principle, it is not possible to identify the
 1937        * second as '>From ' may also come from a server of the
 1938        * first type as actual data. So do what is absolutely
 1939        * necessary only - mask 'From '.
 1940        *
 1941        * If the line is the first line of the message header, it
 1942        * is likely a real 'From ' line. In this case, it is just
 1943        * ignored since it violates all standards.
 1944        * TODO can the latter *really* happen??
 1945        * TODO <<
 1946        */
 1947       /* Since we simply copy over data without doing any transfer
 1948        * encoding reclassification/adjustment we *have* to perform
 1949        * RFC 4155 compliant From_ quoting here */
 1950       if (emptyline && is_head(lp, linelen, FAL0)) {
 1951          fputc('>', mp->mb_otf);
 1952          ++size;
 1953       }
 1954       emptyline = 0;
 1955       if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
 1956          if (linelen > 2) {
 1957             fwrite(lp, 1, linelen - 2, mp->mb_otf);
 1958             size += linelen - 1;
 1959          } else {
 1960             emptyline = 1;
 1961             ++size;
 1962          }
 1963          fputc('\n', mp->mb_otf);
 1964       } else {
 1965          fwrite(lp, 1, linelen, mp->mb_otf);
 1966          size += linelen;
 1967       }
 1968       ++lines;
 1969       if ((expected -= linelen) <= 0)
 1970          break;
 1971    }
 1972    if (!emptyline) {
 1973       /* TODO This is very ugly; but some IMAP daemons don't end a
 1974        * TODO message with \r\n\r\n, and we need \n\n for mbox format.
 1975        * TODO That is to say we do it wrong here in order to get it right
 1976        * TODO when send.c stuff or with MBOX handling, even though THIS
 1977        * TODO line is solely a property of the MBOX database format! */
 1978       fputc('\n', mp->mb_otf);
 1979       ++lines;
 1980       ++size;
 1981    }
 1982    fflush(mp->mb_otf);
 1983 
 1984    if (m != NULL) {
 1985       m->m_size = size + headsize;
 1986       m->m_lines = lines + headlines;
 1987       m->m_block = mailx_blockof(offset);
 1988       m->m_offset = mailx_offsetof(offset);
 1989       switch (need) {
 1990       case NEED_HEADER:
 1991          m->m_content_info = CI_HAVE_HEADER;
 1992          break;
 1993       case NEED_BODY:
 1994          m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
 1995          m->m_xlines = m->m_lines;
 1996          m->m_xsize = m->m_size;
 1997          break;
 1998       }
 1999    }
 2000    n_free(line);
 2001    NYD_LEAVE;
 2002    return excess;
 2003 }
 2004 
 2005 static void
 2006 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
 2007    const char *head, size_t headsize, long headlines)
 2008 {
 2009    off_t offset;
 2010    size_t len;
 2011    NYD_ENTER;
 2012 
 2013    len = strlen(str);
 2014    fseek(mp->mb_otf, 0L, SEEK_END);
 2015    offset = ftell(mp->mb_otf);
 2016    if (head)
 2017       fwrite(head, 1, headsize, mp->mb_otf);
 2018    if (len > 0) {
 2019       fwrite(str, 1, len, mp->mb_otf);
 2020       fputc('\n', mp->mb_otf);
 2021       ++len;
 2022    }
 2023    fflush(mp->mb_otf);
 2024 
 2025    if (m != NULL) {
 2026       m->m_size = headsize + len;
 2027       m->m_lines = headlines + 1;
 2028       m->m_block = mailx_blockof(offset);
 2029       m->m_offset = mailx_offsetof(offset);
 2030       m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
 2031       m->m_xlines = m->m_lines;
 2032       m->m_xsize = m->m_size;
 2033    }
 2034    NYD_LEAVE;
 2035 }
 2036 
 2037 static enum okay
 2038 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
 2039 {
 2040    char o[LINESIZE];
 2041    struct message mt;
 2042    sighandler_type volatile saveint, savepipe;
 2043    char * volatile head;
 2044    char const *cp, *loc, * volatile item, * volatile resp;
 2045    size_t expected;
 2046    size_t volatile headsize;
 2047    int number;
 2048    FILE *queuefp;
 2049    long volatile headlines;
 2050    long n;
 2051    enum okay ok;
 2052    NYD_X;
 2053 
 2054    saveint = savepipe = SIG_IGN;
 2055    head = NULL;
 2056    cp = loc = item = resp = NULL;
 2057    headsize = 0;
 2058    number = (int)PTR2SIZE(m - message + 1);
 2059    queuefp = NULL;
 2060    headlines = 0;
 2061    ok = STOP;
 2062 
 2063    if (getcache(mp, m, need) == OKAY)
 2064       return OKAY;
 2065    if (mp->mb_type == MB_CACHE) {
 2066       n_err(_("Message %lu not available\n"), (ul_i)number);
 2067       return STOP;
 2068    }
 2069 
 2070    if (mp->mb_sock.s_fd < 0) {
 2071       n_err(_("IMAP connection closed\n"));
 2072       return STOP;
 2073    }
 2074 
 2075    switch (need) {
 2076    case NEED_HEADER:
 2077       resp = item = "RFC822.HEADER";
 2078       break;
 2079    case NEED_BODY:
 2080       item = "BODY.PEEK[]";
 2081       resp = "BODY[]";
 2082       if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
 2083          char *hdr = n_alloc(m->m_size);
 2084          fflush(mp->mb_otf);
 2085          if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
 2086                SEEK_SET) < 0 ||
 2087                fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
 2088             n_free(hdr);
 2089             break;
 2090          }
 2091          head = hdr;
 2092          headsize = m->m_size;
 2093          headlines = m->m_lines;
 2094          item = "BODY.PEEK[TEXT]";
 2095          resp = "BODY[TEXT]";
 2096       }
 2097       break;
 2098    case NEED_UNSPEC:
 2099       return STOP;
 2100    }
 2101 
 2102    imaplock = 1;
 2103    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2104    if (sigsetjmp(imapjmp, 1)) {
 2105       safe_signal(SIGINT, saveint);
 2106       safe_signal(SIGPIPE, savepipe);
 2107       imaplock = 0;
 2108       return STOP;
 2109    }
 2110    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 2111       safe_signal(SIGINT, &_imap_maincatch);
 2112    if (savepipe != SIG_IGN)
 2113       safe_signal(SIGPIPE, imapcatch);
 2114 
 2115    if (m->m_uid)
 2116       snprintf(o, sizeof o, "%s UID FETCH %" PRIu64 " (%s)\r\n",
 2117          tag(1), m->m_uid, item);
 2118    else {
 2119       if (check_expunged() == STOP)
 2120          goto out;
 2121       snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
 2122    }
 2123    IMAP_OUT(o, MB_COMD, goto out)
 2124    for (;;) {
 2125       ui64_t uid;
 2126 
 2127       ok = imap_answer(mp, 1);
 2128       if (ok == STOP)
 2129          break;
 2130       if (response_status != RESPONSE_OTHER ||
 2131             response_other != MESSAGE_DATA_FETCH)
 2132          continue;
 2133       if ((loc = asccasestr(responded_other_text, resp)) == NULL)
 2134          continue;
 2135       uid = 0;
 2136       if (m->m_uid) {
 2137          if ((cp = asccasestr(responded_other_text, "UID "))) {
 2138             n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
 2139             n = 0;
 2140          } else
 2141             n = -1;
 2142       } else
 2143          n = responded_other_number;
 2144       if ((cp = strrchr(responded_other_text, '{')) == NULL) {
 2145          if (m->m_uid ? m->m_uid != uid : n != number)
 2146             continue;
 2147          if ((cp = strchr(loc, '"')) != NULL) {
 2148             cp = imap_unquotestr(cp);
 2149             imap_putstr(mp, m, cp, head, headsize, headlines);
 2150          } else {
 2151             m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
 2152             m->m_xlines = m->m_lines;
 2153             m->m_xsize = m->m_size;
 2154          }
 2155          goto out;
 2156       }
 2157       expected = atol(&cp[1]);
 2158       if (m->m_uid ? n == 0 && m->m_uid != uid : n != number) {
 2159          imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
 2160          continue;
 2161       }
 2162       mt = *m;
 2163       imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
 2164       if (n >= 0) {
 2165          commitmsg(mp, m, &mt, mt.m_content_info);
 2166          break;
 2167       }
 2168       if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
 2169          if (n_poption & n_PO_VERBVERB)
 2170             fputs(imapbuf, stderr);
 2171          if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
 2172             n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
 2173             if (uid == m->m_uid) {
 2174                commitmsg(mp, m, &mt, mt.m_content_info);
 2175                break;
 2176             }
 2177          }
 2178       }
 2179    }
 2180 out:
 2181    while (mp->mb_active & MB_COMD)
 2182       ok = imap_answer(mp, 1);
 2183 
 2184    if (saveint != SIG_IGN)
 2185       safe_signal(SIGINT, saveint);
 2186    if (savepipe != SIG_IGN)
 2187       safe_signal(SIGPIPE, savepipe);
 2188    imaplock--;
 2189 
 2190    if (ok == OKAY)
 2191       putcache(mp, m);
 2192    if (head != NULL)
 2193       n_free(head);
 2194    if (interrupts)
 2195       n_go_onintr_for_imap();
 2196    return ok;
 2197 }
 2198 
 2199 FL enum okay
 2200 imap_header(struct message *m)
 2201 {
 2202    enum okay rv;
 2203    NYD_ENTER;
 2204 
 2205    rv = imap_get(&mb, m, NEED_HEADER);
 2206    NYD_LEAVE;
 2207    return rv;
 2208 }
 2209 
 2210 
 2211 FL enum okay
 2212 imap_body(struct message *m)
 2213 {
 2214    enum okay rv;
 2215    NYD_ENTER;
 2216 
 2217    rv = imap_get(&mb, m, NEED_BODY);
 2218    NYD_LEAVE;
 2219    return rv;
 2220 }
 2221 
 2222 static void
 2223 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
 2224    enum content_info content_info)
 2225 {
 2226    NYD_ENTER;
 2227    tomp->m_size = frommp->m_size;
 2228    tomp->m_lines = frommp->m_lines;
 2229    tomp->m_block = frommp->m_block;
 2230    tomp->m_offset = frommp->m_offset;
 2231    tomp->m_content_info = content_info & CI_HAVE_MASK;
 2232    if (content_info & CI_HAVE_BODY) {
 2233       tomp->m_xlines = frommp->m_lines;
 2234       tomp->m_xsize = frommp->m_size;
 2235    }
 2236    putcache(mp, tomp);
 2237    NYD_LEAVE;
 2238 }
 2239 
 2240 static enum okay
 2241 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
 2242 {
 2243    /* bot > topp */
 2244    char o[LINESIZE];
 2245    char const *cp;
 2246    struct message mt;
 2247    size_t expected;
 2248    int n = 0;
 2249    FILE *queuefp = NULL;
 2250    enum okay ok;
 2251    NYD_X;
 2252 
 2253    if (m[bot].m_uid)
 2254       snprintf(o, sizeof o,
 2255          "%s UID FETCH %" PRIu64 ":%" PRIu64 " (RFC822.HEADER)\r\n",
 2256          tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
 2257    else {
 2258       if (check_expunged() == STOP)
 2259          return STOP;
 2260       snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
 2261          tag(1), bot, topp);
 2262    }
 2263    IMAP_OUT(o, MB_COMD, return STOP)
 2264 
 2265    srelax_hold();
 2266    for (;;) {
 2267       ok = imap_answer(mp, 1);
 2268       if (response_status != RESPONSE_OTHER)
 2269          break;
 2270       if (response_other != MESSAGE_DATA_FETCH)
 2271          continue;
 2272       if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
 2273          srelax_rele();
 2274          return STOP;
 2275       }
 2276       if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
 2277          continue;
 2278       expected = atol(&cp[1]);
 2279       if (m[bot-1].m_uid) {
 2280          if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
 2281             ui64_t uid;
 2282 
 2283             n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
 2284             for (n = bot; n <= topp; n++)
 2285                if (uid == m[n-1].m_uid)
 2286                   break;
 2287             if (n > topp) {
 2288                imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
 2289                continue;
 2290             }
 2291          } else
 2292             n = -1;
 2293       } else {
 2294          n = responded_other_number;
 2295          if (n <= 0 || n > msgCount) {
 2296             imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
 2297             continue;
 2298          }
 2299       }
 2300       imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
 2301       if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
 2302          commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
 2303       if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
 2304          if (n_poption & n_PO_VERBVERB)
 2305             fputs(imapbuf, stderr);
 2306          if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
 2307             ui64_t uid;
 2308 
 2309             n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
 2310             for (n = bot; n <= topp; n++)
 2311                if (uid == m[n-1].m_uid)
 2312                   break;
 2313             if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
 2314                commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
 2315          }
 2316       }
 2317       srelax();
 2318    }
 2319    srelax_rele();
 2320 
 2321    while (mp->mb_active & MB_COMD)
 2322       ok = imap_answer(mp, 1);
 2323    return ok;
 2324 }
 2325 
 2326 FL void
 2327 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
 2328 {
 2329    sighandler_type saveint, savepipe;
 2330    /*enum okay ok = STOP;*/
 2331    int i, chunk = 256;
 2332    NYD_X;
 2333 
 2334    if (mb.mb_type == MB_CACHE)
 2335       return;
 2336    if (bot < 1)
 2337       bot = 1;
 2338    if (topp > msgCount)
 2339       topp = msgCount;
 2340    for (i = bot; i < topp; i++) {
 2341       if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
 2342             getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
 2343          bot = i+1;
 2344       else
 2345          break;
 2346    }
 2347    for (i = topp; i > bot; i--) {
 2348       if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
 2349             getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
 2350          topp = i-1;
 2351       else
 2352          break;
 2353    }
 2354    if (bot >= topp)
 2355       return;
 2356 
 2357    imaplock = 1;
 2358    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 2359       safe_signal(SIGINT, &_imap_maincatch);
 2360    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2361    if (sigsetjmp(imapjmp, 1) == 0) {
 2362       if (savepipe != SIG_IGN)
 2363          safe_signal(SIGPIPE, imapcatch);
 2364 
 2365       for (i = bot; i <= topp; i += chunk) {
 2366          int j = i + chunk - 1;
 2367          j = n_MIN(j, topp);
 2368          if (visible(message + j))
 2369             /*ok = */imap_fetchheaders(&mb, message, i, j);
 2370          if (interrupts)
 2371             n_go_onintr_for_imap(); /* XXX imaplock? */
 2372       }
 2373    }
 2374    safe_signal(SIGINT, saveint);
 2375    safe_signal(SIGPIPE, savepipe);
 2376    imaplock = 0;
 2377 }
 2378 
 2379 static enum okay
 2380 __imap_exit(struct mailbox *mp)
 2381 {
 2382    char o[LINESIZE];
 2383    FILE *queuefp = NULL;
 2384    NYD_X;
 2385 
 2386    mp->mb_active |= MB_BYE;
 2387    snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
 2388    IMAP_OUT(o, MB_COMD, return STOP)
 2389    IMAP_ANSWER()
 2390    return OKAY;
 2391 }
 2392 
 2393 static enum okay
 2394 imap_exit(struct mailbox *mp)
 2395 {
 2396    enum okay rv;
 2397    NYD_ENTER;
 2398 
 2399    rv = __imap_exit(mp);
 2400 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
 2401    n_free(mp->mb_imap_pass);
 2402    n_free(mp->mb_imap_account);
 2403    n_free(mp->mb_imap_mailbox);
 2404    if (mp->mb_cache_directory != NULL)
 2405       n_free(mp->mb_cache_directory);
 2406 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
 2407    mp->mb_imap_account =
 2408    mp->mb_imap_mailbox =
 2409    mp->mb_cache_directory = "";
 2410 #else
 2411    mp->mb_imap_account = NULL; /* for assert legacy time.. */
 2412    mp->mb_imap_mailbox = NULL;
 2413    mp->mb_cache_directory = NULL;
 2414 #endif
 2415 #endif
 2416    sclose(&mp->mb_sock);
 2417    NYD_LEAVE;
 2418    return rv;
 2419 }
 2420 
 2421 static enum okay
 2422 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
 2423 {
 2424    NYD_ENTER;
 2425    imap_store(mp, m, n, '+', "\\Deleted", needstat);
 2426    if (mp->mb_type == MB_IMAP)
 2427       delcache(mp, m);
 2428    NYD_LEAVE;
 2429    return OKAY;
 2430 }
 2431 
 2432 static enum okay
 2433 imap_close(struct mailbox *mp)
 2434 {
 2435    char o[LINESIZE];
 2436    FILE *queuefp = NULL;
 2437    NYD_X;
 2438 
 2439    snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
 2440    IMAP_OUT(o, MB_COMD, return STOP)
 2441    IMAP_ANSWER()
 2442    return OKAY;
 2443 }
 2444 
 2445 static enum okay
 2446 imap_update(struct mailbox *mp)
 2447 {
 2448    struct message *m;
 2449    int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
 2450    NYD_ENTER;
 2451 
 2452    if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
 2453       holdbits();
 2454       c = 0;
 2455       for (m = message; PTRCMP(m, <, message + msgCount); ++m)
 2456          if (m->m_flag & MBOX)
 2457             ++c;
 2458       if (c > 0)
 2459          if (makembox() == STOP)
 2460             goto jbypass;
 2461    }
 2462 
 2463    gotcha = held = 0;
 2464    for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
 2465       if (mp->mb_perm == 0)
 2466          dodel = 0;
 2467       else if (n_pstate & n_PS_EDIT)
 2468          dodel = ((m->m_flag & MDELETED) != 0);
 2469       else
 2470          dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
 2471 
 2472       /* Fetch the result after around each 800 STORE commands
 2473        * sent (approx. 32k data sent). Otherwise, servers will
 2474        * try to flush the return queue at some point, leading
 2475        * to a deadlock if we are still writing commands but not
 2476        * reading their results */
 2477       needstat = stored > 0 && stored % 800 == 0;
 2478       /* Even if this message has been deleted, continue
 2479        * to set further flags. This is necessary to support
 2480        * Gmail semantics, where "delete" actually means
 2481        * "archive", and the flags are applied to the copy
 2482        * in "All Mail" */
 2483       if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
 2484          imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
 2485          stored++;
 2486       }
 2487       if (m->m_flag & MFLAG) {
 2488          imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
 2489          stored++;
 2490       }
 2491       if (m->m_flag & MUNFLAG) {
 2492          imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
 2493          stored++;
 2494       }
 2495       if (m->m_flag & MANSWER) {
 2496          imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
 2497          stored++;
 2498       }
 2499       if (m->m_flag & MUNANSWER) {
 2500          imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
 2501          stored++;
 2502       }
 2503       if (m->m_flag & MDRAFT) {
 2504          imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
 2505          stored++;
 2506       }
 2507       if (m->m_flag & MUNDRAFT) {
 2508          imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
 2509          stored++;
 2510       }
 2511 
 2512       if (dodel) {
 2513          imap_delete(mp, m-message+1, m, needstat);
 2514          stored++;
 2515          gotcha++;
 2516       } else if (mp->mb_type != MB_CACHE ||
 2517             (!(n_pstate & n_PS_EDIT) &&
 2518              !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
 2519             (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
 2520                (MPRESERVE | MTOUCH) ||
 2521                ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
 2522          held++;
 2523       if (m->m_flag & MNEW) {
 2524          m->m_flag &= ~MNEW;
 2525          m->m_flag |= MSTATUS;
 2526       }
 2527    }
 2528 jbypass:
 2529    if (gotcha)
 2530       imap_close(mp);
 2531 
 2532    for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
 2533       if (!(m->m_flag & MUNLINKED) &&
 2534             m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
 2535                MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
 2536          putcache(mp, m);
 2537          modflags++;
 2538       }
 2539 
 2540    /* XXX should be readonly (but our IMAP code is weird...) */
 2541    if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
 2542          mb.mb_perm != 0) {
 2543       if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
 2544          printf(_("\"%s\" "), displayname);
 2545          printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
 2546             ? _("complete\n") : _("updated.\n"));
 2547       } else if (held && !(n_pstate & n_PS_EDIT)) {
 2548          if (held == 1)
 2549             printf(_("Held 1 message in %s\n"), displayname);
 2550          else
 2551             printf(_("Held %d messages in %s\n"), held, displayname);
 2552       }
 2553       fflush(stdout);
 2554    }
 2555    NYD_LEAVE;
 2556    return OKAY;
 2557 }
 2558 
 2559 FL bool_t
 2560 imap_quit(bool_t hold_sigs_on)
 2561 {
 2562    sighandler_type volatile saveint, savepipe;
 2563    bool_t rv;
 2564    NYD_ENTER;
 2565 
 2566    if(hold_sigs_on)
 2567       rele_sigs();
 2568 
 2569    if (mb.mb_type == MB_CACHE) {
 2570       rv = (imap_update(&mb) == OKAY);
 2571       goto jleave;
 2572    }
 2573 
 2574    rv = FAL0;
 2575 
 2576    if (mb.mb_sock.s_fd < 0) {
 2577       n_err(_("IMAP connection closed\n"));
 2578       goto jleave;
 2579    }
 2580 
 2581    imaplock = 1;
 2582    saveint = safe_signal(SIGINT, SIG_IGN);
 2583    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2584    if (sigsetjmp(imapjmp, 1)) {
 2585       safe_signal(SIGINT, saveint);
 2586       safe_signal(SIGPIPE, saveint);
 2587       imaplock = 0;
 2588       goto jleave;
 2589    }
 2590    if (saveint != SIG_IGN)
 2591       safe_signal(SIGINT, imapcatch);
 2592    if (savepipe != SIG_IGN)
 2593       safe_signal(SIGPIPE, imapcatch);
 2594 
 2595    rv = (imap_update(&mb) == OKAY);
 2596    if(!same_imap_account && imap_exit(&mb) != OKAY)
 2597       rv = FAL0;
 2598 
 2599    safe_signal(SIGINT, saveint);
 2600    safe_signal(SIGPIPE, savepipe);
 2601    imaplock = 0;
 2602 jleave:
 2603    if(hold_sigs_on)
 2604       hold_sigs();
 2605    NYD_LEAVE;
 2606    return rv;
 2607 }
 2608 
 2609 static enum okay
 2610 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
 2611    int needstat)
 2612 {
 2613    char o[LINESIZE];
 2614    FILE *queuefp = NULL;
 2615    NYD_X;
 2616 
 2617    if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
 2618       return STOP;
 2619    if (m->m_uid)
 2620       snprintf(o, sizeof o, "%s UID STORE %" PRIu64 " %cFLAGS (%s)\r\n",
 2621          tag(1), m->m_uid, c, sp);
 2622    else {
 2623       if (check_expunged() == STOP)
 2624          return STOP;
 2625       snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
 2626    }
 2627    IMAP_OUT(o, MB_COMD, return STOP)
 2628    if (needstat)
 2629       IMAP_ANSWER()
 2630    else
 2631       mb.mb_active &= ~MB_COMD;
 2632    if (queuefp != NULL)
 2633       Fclose(queuefp);
 2634    return OKAY;
 2635 }
 2636 
 2637 FL enum okay
 2638 imap_undelete(struct message *m, int n)
 2639 {
 2640    enum okay rv;
 2641    NYD_ENTER;
 2642 
 2643    rv = imap_unstore(m, n, "\\Deleted");
 2644    NYD_LEAVE;
 2645    return rv;
 2646 }
 2647 
 2648 FL enum okay
 2649 imap_unread(struct message *m, int n)
 2650 {
 2651    enum okay rv;
 2652    NYD_ENTER;
 2653 
 2654    rv = imap_unstore(m, n, "\\Seen");
 2655    NYD_LEAVE;
 2656    return rv;
 2657 }
 2658 
 2659 static enum okay
 2660 imap_unstore(struct message *m, int n, const char *flag)
 2661 {
 2662    sighandler_type saveint, savepipe;
 2663    enum okay volatile rv = STOP;
 2664    NYD_ENTER;
 2665 
 2666    imaplock = 1;
 2667    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 2668       safe_signal(SIGINT, &_imap_maincatch);
 2669    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2670    if (sigsetjmp(imapjmp, 1) == 0) {
 2671       if (savepipe != SIG_IGN)
 2672          safe_signal(SIGPIPE, imapcatch);
 2673 
 2674       rv = imap_store(&mb, m, n, '-', flag, 1);
 2675    }
 2676    safe_signal(SIGINT, saveint);
 2677    safe_signal(SIGPIPE, savepipe);
 2678    imaplock = 0;
 2679 
 2680    NYD_LEAVE;
 2681    if (interrupts)
 2682       n_go_onintr_for_imap();
 2683    return rv;
 2684 }
 2685 
 2686 static const char *
 2687 tag(int new)
 2688 {
 2689    static char ts[20];
 2690    static long n;
 2691    NYD2_ENTER;
 2692 
 2693    if (new)
 2694       ++n;
 2695    snprintf(ts, sizeof ts, "T%lu", n);
 2696    NYD2_LEAVE;
 2697    return ts;
 2698 }
 2699 
 2700 FL int
 2701 c_imapcodec(void *vp){
 2702    bool_t err;
 2703    size_t alen;
 2704    char const **argv, *varname, *varres, *act, *cp;
 2705    NYD_ENTER;
 2706 
 2707    argv = vp;
 2708    varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
 2709 
 2710    act = *argv;
 2711    for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
 2712       ;
 2713    if(act == cp)
 2714       goto jesynopsis;
 2715    alen = PTR2SIZE(cp - act);
 2716    if(*cp != '\0')
 2717       ++cp;
 2718 
 2719    n_pstate_err_no = n_ERR_NONE;
 2720    varres = imap_path_normalize(NULL, cp);
 2721 
 2722    if(is_ascncaseprefix(act, "encode", alen))
 2723       varres = imap_path_encode(varres, &err);
 2724    else if(is_ascncaseprefix(act, "decode", alen))
 2725       varres = imap_path_decode(varres, &err);
 2726    else
 2727       goto jesynopsis;
 2728 
 2729    if(err){
 2730       n_pstate_err_no = n_ERR_CANCELED;
 2731       varres = cp;
 2732       vp = NULL;
 2733    }
 2734 
 2735    if(varname != NULL){
 2736       if(!n_var_vset(varname, (uintptr_t)varres)){
 2737          n_pstate_err_no = n_ERR_NOTSUP;
 2738          vp = NULL;
 2739       }
 2740    }else{
 2741       struct str in, out;
 2742 
 2743       in.l = strlen(in.s = n_UNCONST(varres));
 2744       makeprint(&in, &out);
 2745       if(fprintf(n_stdout, "%s\n", out.s) < 0){
 2746          n_pstate_err_no = n_err_no;
 2747          vp = NULL;
 2748       }
 2749       n_free(out.s);
 2750    }
 2751 
 2752 jleave:
 2753    NYD_LEAVE;
 2754    return (vp != NULL ? 0 : 1);
 2755 jesynopsis:
 2756    n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
 2757    n_pstate_err_no = n_ERR_INVAL;
 2758    vp = NULL;
 2759    goto jleave;
 2760 }
 2761 
 2762 FL int
 2763 c_imap_imap(void *vp)
 2764 {
 2765    char o[LINESIZE];
 2766    sighandler_type saveint, savepipe;
 2767    struct mailbox *mp = &mb;
 2768    FILE *queuefp = NULL;
 2769    enum okay volatile ok = STOP;
 2770    NYD_X;
 2771 
 2772    if (mp->mb_type != MB_IMAP) {
 2773       printf("Not operating on an IMAP mailbox.\n");
 2774       return 1;
 2775    }
 2776    imaplock = 1;
 2777    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 2778       safe_signal(SIGINT, &_imap_maincatch);
 2779    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 2780    if (sigsetjmp(imapjmp, 1) == 0) {
 2781       if (savepipe != SIG_IGN)
 2782          safe_signal(SIGPIPE, imapcatch);
 2783 
 2784       snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
 2785       IMAP_OUT(o, MB_COMD, goto out)
 2786       while (mp->mb_active & MB_COMD) {
 2787          ok = imap_answer(mp, 0);
 2788          fputs(responded_text, stdout);
 2789       }
 2790    }
 2791 out:
 2792    safe_signal(SIGINT, saveint);
 2793    safe_signal(SIGPIPE, savepipe);
 2794    imaplock = 0;
 2795 
 2796    if (interrupts)
 2797       n_go_onintr_for_imap();
 2798    return ok != OKAY;
 2799 }
 2800 
 2801 FL int
 2802 imap_newmail(int nmail)
 2803 {
 2804    NYD_ENTER;
 2805 
 2806    if (nmail && had_exists < 0 && had_expunge < 0) {
 2807       imaplock = 1;
 2808       imap_noop();
 2809       imaplock = 0;
 2810    }
 2811 
 2812    if (had_exists == msgCount && had_expunge < 0)
 2813       /* Some servers always respond with EXISTS to NOOP. If
 2814        * the mailbox has been changed but the number of messages
 2815        * has not, an EXPUNGE must also had been sent; otherwise,
 2816        * nothing has changed */
 2817       had_exists = -1;
 2818    NYD_LEAVE;
 2819    return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
 2820 }
 2821 
 2822 static char *
 2823 imap_putflags(int f)
 2824 {
 2825    const char *cp;
 2826    char *buf, *bp;
 2827    NYD2_ENTER;
 2828 
 2829    bp = buf = n_autorec_alloc(100);
 2830    if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
 2831       *bp++ = '(';
 2832       if (f & MREAD) {
 2833          if (bp[-1] != '(')
 2834             *bp++ = ' ';
 2835          for (cp = "\\Seen"; *cp; cp++)
 2836             *bp++ = *cp;
 2837       }
 2838       if (f & MFLAGGED) {
 2839          if (bp[-1] != '(')
 2840             *bp++ = ' ';
 2841          for (cp = "\\Flagged"; *cp; cp++)
 2842             *bp++ = *cp;
 2843       }
 2844       if (f & MANSWERED) {
 2845          if (bp[-1] != '(')
 2846             *bp++ = ' ';
 2847          for (cp = "\\Answered"; *cp; cp++)
 2848             *bp++ = *cp;
 2849       }
 2850       if (f & MDRAFT) {
 2851          if (bp[-1] != '(')
 2852             *bp++ = ' ';
 2853          for (cp = "\\Draft"; *cp; cp++)
 2854             *bp++ = *cp;
 2855       }
 2856       *bp++ = ')';
 2857       *bp++ = ' ';
 2858    }
 2859    *bp = '\0';
 2860    NYD2_LEAVE;
 2861    return buf;
 2862 }
 2863 
 2864 static void
 2865 imap_getflags(const char *cp, char const **xp, enum mflag *f)
 2866 {
 2867    NYD2_ENTER;
 2868    while (*cp != ')') {
 2869       if (*cp == '\\') {
 2870          if (ascncasecmp(cp, "\\Seen", 5) == 0)
 2871             *f |= MREAD;
 2872          else if (ascncasecmp(cp, "\\Recent", 7) == 0)
 2873             *f |= MNEW;
 2874          else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
 2875             *f |= MDELETED;
 2876          else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
 2877             *f |= MFLAGGED;
 2878          else if (ascncasecmp(cp, "\\Answered", 9) == 0)
 2879             *f |= MANSWERED;
 2880          else if (ascncasecmp(cp, "\\Draft", 6) == 0)
 2881             *f |= MDRAFTED;
 2882       }
 2883       cp++;
 2884    }
 2885 
 2886    if (xp != NULL)
 2887       *xp = cp;
 2888    NYD2_LEAVE;
 2889 }
 2890 
 2891 static enum okay
 2892 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
 2893    long xsize, enum mflag flag, time_t t)
 2894 {
 2895    char o[LINESIZE], *buf;
 2896    size_t bufsize, buflen, cnt;
 2897    long size, lines, ysize;
 2898    char const *qname;
 2899    bool_t twice;
 2900    FILE *queuefp;
 2901    enum okay rv;
 2902    NYD_ENTER;
 2903 
 2904    rv = STOP;
 2905    queuefp = NULL;
 2906    twice = FAL0;
 2907    buf = NULL;
 2908 
 2909    if((qname = imap_path_quote(mp, name)) == NULL)
 2910       goto jleave;
 2911 
 2912    if (mp->mb_type == MB_CACHE) {
 2913       queuefp = cache_queue(mp);
 2914       if (queuefp == NULL) {
 2915          buf = NULL;
 2916          goto jleave;
 2917       }
 2918       rv = OKAY;
 2919    }
 2920 
 2921    buf = n_alloc(bufsize = LINESIZE);
 2922    buflen = 0;
 2923 jagain:
 2924    size = xsize;
 2925    cnt = fsize(fp);
 2926    if (fseek(fp, off1, SEEK_SET) < 0) {
 2927       rv = STOP;
 2928       goto jleave;
 2929    }
 2930 
 2931    snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
 2932          tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
 2933    IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
 2934    while (mp->mb_active & MB_COMD) {
 2935       rv = imap_answer(mp, twice);
 2936       if (response_type == RESPONSE_CONT)
 2937          break;
 2938    }
 2939 
 2940    if (mp->mb_type != MB_CACHE && rv == STOP) {
 2941       if (!twice)
 2942          goto jtrycreate;
 2943       else
 2944          goto jleave;
 2945    }
 2946 
 2947    lines = ysize = 0;
 2948    while (size > 0) {
 2949       fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
 2950       lines++;
 2951       ysize += buflen;
 2952       buf[buflen - 1] = '\r';
 2953       buf[buflen] = '\n';
 2954       if (mp->mb_type != MB_CACHE)
 2955          swrite1(&mp->mb_sock, buf, buflen+1, 1);
 2956       else if (queuefp)
 2957          fwrite(buf, 1, buflen+1, queuefp);
 2958       size -= buflen + 1;
 2959    }
 2960    if (mp->mb_type != MB_CACHE)
 2961       swrite(&mp->mb_sock, "\r\n");
 2962    else if (queuefp)
 2963       fputs("\r\n", queuefp);
 2964    while (mp->mb_active & MB_COMD) {
 2965       rv = imap_answer(mp, 0);
 2966       if (response_status == RESPONSE_NO /*&&
 2967             ascncasecmp(responded_text,
 2968                "[TRYCREATE] ", 12) == 0*/) {
 2969 jtrycreate:
 2970          if (twice) {
 2971             rv = STOP;
 2972             goto jleave;
 2973          }
 2974          twice = TRU1;
 2975          snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
 2976          IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
 2977          while (mp->mb_active & MB_COMD)
 2978             rv = imap_answer(mp, 1);
 2979          if (rv == STOP)
 2980             goto jleave;
 2981          imap_created_mailbox++;
 2982          goto jagain;
 2983       } else if (rv != OKAY)
 2984          n_err(_("IMAP error: %s"), responded_text);
 2985       else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
 2986          imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
 2987    }
 2988 jleave:
 2989    if (queuefp != NULL)
 2990       Fclose(queuefp);
 2991    if (buf != NULL)
 2992       n_free(buf);
 2993    NYD_LEAVE;
 2994    return rv;
 2995 }
 2996 
 2997 static enum okay
 2998 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
 2999 {
 3000    char *buf, *bp, *lp;
 3001    size_t bufsize, buflen, cnt;
 3002    off_t off1 = -1, offs;
 3003    int flag;
 3004    enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
 3005    time_t tim;
 3006    long size;
 3007    enum okay rv;
 3008    NYD_ENTER;
 3009 
 3010    buf = n_alloc(bufsize = LINESIZE);
 3011    buflen = 0;
 3012    cnt = fsize(fp);
 3013    offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
 3014    time(&tim);
 3015    size = 0;
 3016 
 3017    for (flag = MNEW, state = _NLSEP;;) {
 3018       bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
 3019 
 3020       if (bp == NULL ||
 3021             ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
 3022              is_head(buf, buflen, FAL0))) {
 3023          if (off1 != (off_t)-1) {
 3024             rv = imap_append1(mp, name, fp, off1, size, flag, tim);
 3025             if (rv == STOP)
 3026                goto jleave;
 3027             fseek(fp, offs+buflen, SEEK_SET);
 3028          }
 3029          off1 = offs + buflen;
 3030          size = 0;
 3031          flag = MNEW;
 3032          state = _INHEAD;
 3033          if (bp == NULL)
 3034             break;
 3035          tim = unixtime(buf);
 3036       } else
 3037          size += buflen+1;
 3038       offs += buflen;
 3039 
 3040       state &= ~_NLSEP;
 3041       if (buf[0] == '\n') {
 3042          state &= ~_INHEAD;
 3043          state |= _NLSEP;
 3044       } else if (state & _INHEAD) {
 3045          if (ascncasecmp(buf, "status", 6) == 0) {
 3046             lp = &buf[6];
 3047             while (whitechar(*lp))
 3048                lp++;
 3049             if (*lp == ':')
 3050                while (*++lp != '\0')
 3051                   switch (*lp) {
 3052                   case 'R':
 3053                      flag |= MREAD;
 3054                      break;
 3055                   case 'O':
 3056                      flag &= ~MNEW;
 3057                      break;
 3058                   }
 3059          } else if (ascncasecmp(buf, "x-status", 8) == 0) {
 3060             lp = &buf[8];
 3061             while (whitechar(*lp))
 3062                lp++;
 3063             if (*lp == ':')
 3064                while (*++lp != '\0')
 3065                   switch (*lp) {
 3066                   case 'F':
 3067                      flag |= MFLAGGED;
 3068                      break;
 3069                   case 'A':
 3070                      flag |= MANSWERED;
 3071                      break;
 3072                   case 'T':
 3073                      flag |= MDRAFTED;
 3074                      break;
 3075                   }
 3076          }
 3077       }
 3078    }
 3079    rv = OKAY;
 3080 jleave:
 3081    n_free(buf);
 3082    NYD_LEAVE;
 3083    return rv;
 3084 }
 3085 
 3086 FL enum okay
 3087 imap_append(const char *xserver, FILE *fp, long offset)
 3088 {
 3089    sighandler_type volatile saveint, savepipe;
 3090    struct url url;
 3091    struct ccred ccred;
 3092    enum okay rv = STOP;
 3093    NYD_ENTER;
 3094 
 3095    if (!url_parse(&url, CPROTO_IMAP, xserver))
 3096       goto j_leave;
 3097    if (!ok_blook(v15_compat) &&
 3098          (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
 3099       n_err(_("New-style URL used without *v15-compat* being set!\n"));
 3100    assert(url.url_path.s != NULL);
 3101 
 3102    imaplock = 1;
 3103    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3104       safe_signal(SIGINT, &_imap_maincatch);
 3105    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3106    if (sigsetjmp(imapjmp, 1))
 3107       goto jleave;
 3108    if (savepipe != SIG_IGN)
 3109       safe_signal(SIGPIPE, imapcatch);
 3110 
 3111    if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
 3112          !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
 3113       rv = imap_append0(&mb, url.url_path.s, fp, offset);
 3114    } else {
 3115       struct mailbox mx;
 3116 
 3117       memset(&mx, 0, sizeof mx);
 3118 
 3119       if (!_imap_getcred(&mx, &ccred, &url))
 3120          goto jleave;
 3121 
 3122       imap_delim_init(&mx, &url);
 3123       mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
 3124 
 3125       if (disconnected(url.url_p_eu_h_p) == 0) {
 3126          if (!sopen(&mx.mb_sock, &url))
 3127             goto jfail;
 3128          mx.mb_sock.s_desc = "IMAP";
 3129          mx.mb_type = MB_IMAP;
 3130          mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
 3131          /* TODO the code now did
 3132           * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
 3133           * TODO though imap_mailbox is sfree()d and mbx
 3134           * TODO is possibly even a constant
 3135           * TODO i changed this to sstrdup() sofar, as is used
 3136           * TODO somewhere else in this file for this! */
 3137          if (imap_preauth(&mx, &url) != OKAY ||
 3138                imap_auth(&mx, &ccred) != OKAY) {
 3139             sclose(&mx.mb_sock);
 3140             goto jfail;
 3141          }
 3142          rv = imap_append0(&mx, url.url_path.s, fp, offset);
 3143          imap_exit(&mx);
 3144       } else {
 3145          mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
 3146          mx.mb_type = MB_CACHE;
 3147          rv = imap_append0(&mx, url.url_path.s, fp, offset);
 3148       }
 3149 jfail:
 3150       ;
 3151    }
 3152 
 3153 jleave:
 3154    safe_signal(SIGINT, saveint);
 3155    safe_signal(SIGPIPE, savepipe);
 3156    imaplock = 0;
 3157 j_leave:
 3158    NYD_LEAVE;
 3159    if (interrupts)
 3160       n_go_onintr_for_imap();
 3161    return rv;
 3162 }
 3163 
 3164 static enum okay
 3165 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
 3166    struct list_item **lend, int level)
 3167 {
 3168    char o[LINESIZE], *cp;
 3169    struct list_item *lp;
 3170    const char *qname, *bp;
 3171    FILE *queuefp;
 3172    enum okay ok;
 3173    NYD_X;
 3174 
 3175    ok = STOP;
 3176    queuefp = NULL;
 3177 
 3178    if((qname = imap_path_quote(mp, base)) == NULL)
 3179       goto jleave;
 3180 
 3181    *list = *lend = NULL;
 3182    snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
 3183    IMAP_OUT(o, MB_COMD, goto jleave)
 3184    while (mp->mb_active & MB_COMD) {
 3185       ok = imap_answer(mp, 1);
 3186       if (response_status == RESPONSE_OTHER &&
 3187             response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
 3188          cp = imap_path_decode(imap_unquotestr(list_name), NULL);
 3189          lp = n_autorec_calloc(1, sizeof *lp);
 3190          lp->l_name = cp;
 3191          for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
 3192             ++cp;
 3193          lp->l_base = *cp ? cp : savestr(base);
 3194          lp->l_attr = list_attributes;
 3195          lp->l_level = level+1;
 3196          lp->l_delim = list_hierarchy_delimiter;
 3197          if (*list && *lend) {
 3198             (*lend)->l_next = lp;
 3199             *lend = lp;
 3200          } else
 3201             *list = *lend = lp;
 3202       }
 3203    }
 3204 jleave:
 3205    return ok;
 3206 }
 3207 
 3208 static enum okay
 3209 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
 3210 {
 3211    struct list_item *list, *lend, *lp, *lx, *ly;
 3212    int n, depth;
 3213    const char *bp;
 3214    char *cp;
 3215    enum okay rv;
 3216    NYD_ENTER;
 3217 
 3218    depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
 3219    if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
 3220       goto jleave;
 3221    rv = OKAY;
 3222    if (list == NULL || lend == NULL)
 3223       goto jleave;
 3224 
 3225    for (lp = list; lp; lp = lp->l_next)
 3226       if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
 3227             !(lp->l_attr & LIST_NOINFERIORS)) {
 3228          cp = n_autorec_alloc((n = strlen(lp->l_name)) + 2);
 3229          memcpy(cp, lp->l_name, n);
 3230          cp[n] = lp->l_delim;
 3231          cp[n+1] = '\0';
 3232          if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
 3233             lp->l_has_children = 1;
 3234             if (strcmp(cp, lx->l_name) == 0)
 3235                lx = lx->l_next;
 3236             if (lx) {
 3237                lend->l_next = lx;
 3238                lend = ly;
 3239             }
 3240          }
 3241       }
 3242 
 3243    for (lp = list; lp; lp = lp->l_next) {
 3244       if (strip) {
 3245          cp = lp->l_name;
 3246          for (bp = base; *bp && *bp == *cp; bp++)
 3247             cp++;
 3248       } else
 3249          cp = lp->l_name;
 3250       if (!(lp->l_attr & LIST_NOSELECT))
 3251          fprintf(fp, "%s\n", *cp ? cp : base);
 3252       else if (lp->l_has_children == 0)
 3253          fprintf(fp, "%s%c\n", *cp ? cp : base,
 3254             (lp->l_delim != EOF ? lp->l_delim : '\n'));
 3255    }
 3256 jleave:
 3257    NYD_LEAVE;
 3258    return rv;
 3259 }
 3260 
 3261 FL int
 3262 imap_folders(const char * volatile name, int strip)
 3263 {
 3264    sighandler_type saveint, savepipe;
 3265    const char * volatile fold, *cp, *sp;
 3266    FILE * volatile fp;
 3267    int rv = 1;
 3268    NYD_ENTER;
 3269 
 3270    cp = protbase(name);
 3271    sp = mb.mb_imap_account;
 3272    if (sp == NULL || strcmp(cp, sp)) {
 3273       n_err(
 3274          _("Cannot perform `folders' but when on the very IMAP "
 3275          "account; the current one is\n  `%s' -- "
 3276          "try `folders @'\n"),
 3277          (sp != NULL ? sp : _("[NONE]")));
 3278       goto jleave;
 3279    }
 3280 
 3281    fold = imap_fileof(name);
 3282    if (n_psonce & n_PSO_TTYOUT) {
 3283       if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
 3284             == NULL) {
 3285          n_perr(_("tmpfile"), 0);
 3286          goto jleave;
 3287       }
 3288    } else
 3289       fp = stdout;
 3290 
 3291    imaplock = 1;
 3292    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3293       safe_signal(SIGINT, &_imap_maincatch);
 3294    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3295    if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
 3296       goto junroll;
 3297    if (savepipe != SIG_IGN)
 3298       safe_signal(SIGPIPE, imapcatch);
 3299 
 3300    if (mb.mb_type == MB_CACHE)
 3301       cache_list(&mb, fold, strip, fp);
 3302    else
 3303       imap_list(&mb, fold, strip, fp);
 3304 
 3305    imaplock = 0;
 3306    if (interrupts) {
 3307       if (n_psonce & n_PSO_TTYOUT)
 3308          Fclose(fp);
 3309       rv = 0;
 3310       goto jleave;
 3311    }
 3312    fflush(fp);
 3313 
 3314    if (n_psonce & n_PSO_TTYOUT) {
 3315       rewind(fp);
 3316       if (fsize(fp) > 0){
 3317          page_or_print(fp, 0);
 3318          rv = 0;
 3319       }else
 3320          n_err(_("Folder not found\n"));
 3321    }else
 3322       rv = 0;
 3323 junroll:
 3324    safe_signal(SIGINT, saveint);
 3325    safe_signal(SIGPIPE, savepipe);
 3326    if (n_psonce & n_PSO_TTYOUT)
 3327       Fclose(fp);
 3328 jleave:
 3329    NYD_LEAVE;
 3330    if (interrupts)
 3331       n_go_onintr_for_imap();
 3332    return rv;
 3333 }
 3334 
 3335 static enum okay
 3336 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
 3337 {
 3338    char o[LINESIZE];
 3339    const char *qname;
 3340    bool_t twice, stored;
 3341    FILE *queuefp;
 3342    enum okay ok;
 3343    NYD_X;
 3344 
 3345    ok = STOP;
 3346    queuefp = NULL;
 3347    twice = stored = FAL0;
 3348 
 3349    /* C99 */{
 3350       size_t i;
 3351 
 3352       i = strlen(name = imap_fileof(name));
 3353       if(i == 0 || (i > 0 && name[i - 1] == '/'))
 3354          name = savecat(name, "INBOX");
 3355       if((qname = imap_path_quote(mp, name)) == NULL)
 3356          goto jleave;
 3357    }
 3358 
 3359    if (mp->mb_type == MB_CACHE) {
 3360       if ((queuefp = cache_queue(mp)) == NULL)
 3361          goto jleave;
 3362       ok = OKAY;
 3363    }
 3364 
 3365    /* Since it is not possible to set flags on the copy, recently
 3366     * set flags must be set on the original to include it in the copy */
 3367    if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
 3368       imap_store(mp, m, n, '+', "\\Seen", 0);
 3369    if (m->m_flag&MFLAG)
 3370       imap_store(mp, m, n, '+', "\\Flagged", 0);
 3371    if (m->m_flag&MUNFLAG)
 3372       imap_store(mp, m, n, '-', "\\Flagged", 0);
 3373    if (m->m_flag&MANSWER)
 3374       imap_store(mp, m, n, '+', "\\Answered", 0);
 3375    if (m->m_flag&MUNANSWER)
 3376       imap_store(mp, m, n, '-', "\\Flagged", 0);
 3377    if (m->m_flag&MDRAFT)
 3378       imap_store(mp, m, n, '+', "\\Draft", 0);
 3379    if (m->m_flag&MUNDRAFT)
 3380       imap_store(mp, m, n, '-', "\\Draft", 0);
 3381 again:
 3382    if (m->m_uid)
 3383       snprintf(o, sizeof o, "%s UID COPY %" PRIu64 " %s\r\n",
 3384          tag(1), m->m_uid, qname);
 3385    else {
 3386       if (check_expunged() == STOP)
 3387          goto out;
 3388       snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
 3389    }
 3390    IMAP_OUT(o, MB_COMD, goto out)
 3391    while (mp->mb_active & MB_COMD)
 3392       ok = imap_answer(mp, twice);
 3393 
 3394    if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
 3395          response_status == RESPONSE_OK)
 3396       imap_copyuid(mp, m, name);
 3397 
 3398    if (response_status == RESPONSE_NO && !twice) {
 3399       snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
 3400       IMAP_OUT(o, MB_COMD, goto out)
 3401       while (mp->mb_active & MB_COMD)
 3402          ok = imap_answer(mp, 1);
 3403       if (ok == OKAY) {
 3404          imap_created_mailbox++;
 3405          goto again;
 3406       }
 3407    }
 3408 
 3409    if (queuefp != NULL)
 3410       Fclose(queuefp);
 3411 
 3412    /* ... and reset the flag to its initial value so that the 'exit'
 3413     * command still leaves the message unread */
 3414 out:
 3415    if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
 3416       imap_store(mp, m, n, '-', "\\Seen", 0);
 3417       stored = TRU1;
 3418    }
 3419    if (m->m_flag & MFLAG) {
 3420       imap_store(mp, m, n, '-', "\\Flagged", 0);
 3421       stored = TRU1;
 3422    }
 3423    if (m->m_flag & MUNFLAG) {
 3424       imap_store(mp, m, n, '+', "\\Flagged", 0);
 3425       stored = TRU1;
 3426    }
 3427    if (m->m_flag & MANSWER) {
 3428       imap_store(mp, m, n, '-', "\\Answered", 0);
 3429       stored = TRU1;
 3430    }
 3431    if (m->m_flag & MUNANSWER) {
 3432       imap_store(mp, m, n, '+', "\\Answered", 0);
 3433       stored = TRU1;
 3434    }
 3435    if (m->m_flag & MDRAFT) {
 3436       imap_store(mp, m, n, '-', "\\Draft", 0);
 3437       stored = TRU1;
 3438    }
 3439    if (m->m_flag & MUNDRAFT) {
 3440       imap_store(mp, m, n, '+', "\\Draft", 0);
 3441       stored = TRU1;
 3442    }
 3443    if (stored) {
 3444       mp->mb_active |= MB_COMD;
 3445       (void)imap_finish(mp);
 3446    }
 3447 jleave:
 3448    return ok;
 3449 }
 3450 
 3451 FL enum okay
 3452 imap_copy(struct message *m, int n, const char *name)
 3453 {
 3454    sighandler_type saveint, savepipe;
 3455    enum okay volatile rv = STOP;
 3456    NYD_ENTER;
 3457 
 3458    imaplock = 1;
 3459    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3460       safe_signal(SIGINT, &_imap_maincatch);
 3461    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3462    if (sigsetjmp(imapjmp, 1) == 0) {
 3463       if (savepipe != SIG_IGN)
 3464          safe_signal(SIGPIPE, imapcatch);
 3465 
 3466       rv = imap_copy1(&mb, m, n, name);
 3467    }
 3468    safe_signal(SIGINT, saveint);
 3469    safe_signal(SIGPIPE, savepipe);
 3470    imaplock = 0;
 3471 
 3472    NYD_LEAVE;
 3473    if (interrupts)
 3474       n_go_onintr_for_imap();
 3475    return rv;
 3476 }
 3477 
 3478 static enum okay
 3479 imap_copyuid_parse(const char *cp, ui64_t *uidvalidity, ui64_t *olduid,
 3480    ui64_t *newuid)
 3481 {
 3482    char const *xp, *yp, *zp;
 3483    enum okay rv;
 3484    NYD_ENTER;
 3485 
 3486    n_idec_ui64_cp(uidvalidity, cp, 10, &xp); /* TODO errors */
 3487    n_idec_ui64_cp(olduid, xp, 10, &yp); /* TODO errors */
 3488    n_idec_ui64_cp(newuid, yp, 10, &zp); /* TODO errors */
 3489    rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
 3490       yp > xp && *yp == ' ' && zp > yp && *zp == ']');
 3491    NYD_LEAVE;
 3492    return rv;
 3493 }
 3494 
 3495 static enum okay
 3496 imap_appenduid_parse(const char *cp, ui64_t *uidvalidity, ui64_t *uid)
 3497 {
 3498    char const *xp, *yp;
 3499    enum okay rv;
 3500    NYD_ENTER;
 3501 
 3502    n_idec_ui64_cp(uidvalidity, cp, 10, &xp); /* TODO errors */
 3503    n_idec_ui64_cp(uid, xp, 10, &yp); /* TODO errors */
 3504    rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
 3505       *yp == ']');
 3506    NYD_LEAVE;
 3507    return rv;
 3508 }
 3509 
 3510 static enum okay
 3511 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
 3512 {
 3513    struct mailbox xmb;
 3514    struct message xm;
 3515    const char *cp;
 3516    ui64_t uidvalidity, olduid, newuid;
 3517    enum okay rv;
 3518    NYD_ENTER;
 3519 
 3520    rv = STOP;
 3521 
 3522    memset(&xmb, 0, sizeof xmb);
 3523 
 3524    if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
 3525          imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
 3526       goto jleave;
 3527 
 3528    rv = OKAY;
 3529 
 3530    xmb = *mp;
 3531    xmb.mb_cache_directory = NULL;
 3532    xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
 3533    xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
 3534    memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
 3535       sizeof(xmb.mb_imap_delim));
 3536    xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
 3537    if (mp->mb_cache_directory != NULL)
 3538       xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
 3539    xmb.mb_uidvalidity = uidvalidity;
 3540    initcache(&xmb);
 3541 
 3542    if (m == NULL) {
 3543       memset(&xm, 0, sizeof xm);
 3544       xm.m_uid = olduid;
 3545       if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
 3546          goto jleave;
 3547       getcache(mp, &xm, NEED_HEADER);
 3548       getcache(mp, &xm, NEED_BODY);
 3549    } else {
 3550       if ((m->m_content_info & CI_HAVE_HEADER) == 0)
 3551          getcache(mp, m, NEED_HEADER);
 3552       if ((m->m_content_info & CI_HAVE_BODY) == 0)
 3553          getcache(mp, m, NEED_BODY);
 3554       xm = *m;
 3555    }
 3556    xm.m_uid = newuid;
 3557    xm.m_flag &= ~MFULLYCACHED;
 3558    putcache(&xmb, &xm);
 3559 jleave:
 3560    if (xmb.mb_cache_directory != NULL)
 3561       n_free(xmb.mb_cache_directory);
 3562    if (xmb.mb_imap_mailbox != NULL)
 3563       n_free(xmb.mb_imap_mailbox);
 3564    if (xmb.mb_imap_pass != NULL)
 3565       n_free(xmb.mb_imap_pass);
 3566    if (xmb.mb_imap_account != NULL)
 3567       n_free(xmb.mb_imap_account);
 3568    NYD_LEAVE;
 3569    return rv;
 3570 }
 3571 
 3572 static enum okay
 3573 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
 3574    long size, long lines, int flag, const char *name)
 3575 {
 3576    struct mailbox xmb;
 3577    struct message xm;
 3578    const char *cp;
 3579    ui64_t uidvalidity, uid;
 3580    enum okay rv;
 3581    NYD_ENTER;
 3582 
 3583    rv = STOP;
 3584 
 3585    if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
 3586          imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
 3587       goto jleave;
 3588 
 3589    rv = OKAY;
 3590 
 3591    xmb = *mp;
 3592    xmb.mb_cache_directory = NULL;
 3593    /* XXX mb_imap_delim reused */
 3594    xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
 3595    xmb.mb_uidvalidity = uidvalidity;
 3596    xmb.mb_otf = xmb.mb_itf = fp;
 3597    initcache(&xmb);
 3598    memset(&xm, 0, sizeof xm);
 3599    xm.m_flag = (flag & MREAD) | MNEW;
 3600    xm.m_time = t;
 3601    xm.m_block = mailx_blockof(off1);
 3602    xm.m_offset = mailx_offsetof(off1);
 3603    xm.m_size = size;
 3604    xm.m_xsize = xsize;
 3605    xm.m_lines = xm.m_xlines = lines;
 3606    xm.m_uid = uid;
 3607    xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
 3608    putcache(&xmb, &xm);
 3609 
 3610    n_free(xmb.mb_imap_mailbox);
 3611 jleave:
 3612    NYD_LEAVE;
 3613    return rv;
 3614 }
 3615 
 3616 static enum okay
 3617 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
 3618 {
 3619    FILE *tp = NULL;
 3620    time_t t;
 3621    long size, xsize, ysize, lines;
 3622    enum mflag flag = MNEW;
 3623    char *name, *buf, *bp;
 3624    char const *cp;
 3625    size_t bufsize, buflen, cnt;
 3626    enum okay rv = STOP;
 3627    NYD_ENTER;
 3628 
 3629    buf = n_alloc(bufsize = LINESIZE);
 3630    buflen = 0;
 3631    cnt = fsize(fp);
 3632    if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
 3633       goto jstop;
 3634 
 3635    for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
 3636       ;
 3637    while (*bp == ' ')
 3638       ++bp;
 3639 
 3640    if ((cp = strrchr(bp, '{')) == NULL)
 3641       goto jstop;
 3642 
 3643    xsize = atol(&cp[1]) + 2;
 3644    if ((name = imap_strex(&bp[7], &cp)) == NULL)
 3645       goto jstop;
 3646    while (*cp == ' ')
 3647       cp++;
 3648 
 3649    if (*cp == '(') {
 3650       imap_getflags(cp, &cp, &flag);
 3651       while (*++cp == ' ')
 3652          ;
 3653    }
 3654    t = imap_read_date_time(cp);
 3655 
 3656    if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
 3657          == NULL)
 3658       goto jstop;
 3659 
 3660    size = xsize;
 3661    ysize = lines = 0;
 3662    while (size > 0) {
 3663       if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
 3664          goto jstop;
 3665       size -= buflen;
 3666       buf[--buflen] = '\0';
 3667       buf[buflen-1] = '\n';
 3668       fwrite(buf, 1, buflen, tp);
 3669       ysize += buflen;
 3670       ++lines;
 3671    }
 3672    fflush(tp);
 3673    rewind(tp);
 3674 
 3675    imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
 3676       imap_unquotestr(name));
 3677    rv = OKAY;
 3678 jstop:
 3679    n_free(buf);
 3680    if (tp)
 3681       Fclose(tp);
 3682    NYD_LEAVE;
 3683    return rv;
 3684 }
 3685 
 3686 #ifdef HAVE_IMAP_SEARCH
 3687 static enum okay
 3688 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
 3689    int f)
 3690 {
 3691    char *o, *cs, c;
 3692    size_t n;
 3693    FILE *queuefp = NULL;
 3694    int i;
 3695    const char *cp, *xp;
 3696    enum okay ok = STOP;
 3697    NYD_X;
 3698 
 3699    c = 0;
 3700    for (cp = spec; *cp; cp++)
 3701       c |= *cp;
 3702    if (c & 0200) {
 3703       cp = ok_vlook(ttycharset);
 3704 # ifdef HAVE_ICONV
 3705       if(asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")){ /* XXX */
 3706          char const *nspec;
 3707 
 3708          if((nspec = n_iconv_onetime_cp(n_ICONV_DEFAULT, "utf-8", cp, spec)
 3709                ) != NULL){
 3710             spec = nspec;
 3711             cp = "utf-8";
 3712          }
 3713       }
 3714 # endif
 3715       cp = imap_quotestr(cp);
 3716       cs = n_lofi_alloc(n = strlen(cp) + 10);
 3717       snprintf(cs, n, "CHARSET %s ", cp);
 3718    } else
 3719       cs = n_UNCONST(n_empty);
 3720 
 3721    o = n_lofi_alloc(n = strlen(spec) + 60);
 3722    snprintf(o, n, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
 3723    IMAP_OUT(o, MB_COMD, goto out)
 3724    while (mp->mb_active & MB_COMD) {
 3725       ok = imap_answer(mp, 0);
 3726       if (response_status == RESPONSE_OTHER &&
 3727             response_other == MAILBOX_DATA_SEARCH) {
 3728          xp = responded_other_text;
 3729          while (*xp && *xp != '\r') {
 3730             ui64_t uid;
 3731 
 3732             n_idec_ui64_cp(&uid, xp, 10, &xp);/* TODO errors? */
 3733             for (i = 0; i < cnt; i++)
 3734                if (m[i].m_uid == uid && !(m[i].m_flag & MHIDDEN) &&
 3735                      (f == MDELETED || !(m[i].m_flag & MDELETED)))
 3736                   mark(i+1, f);
 3737          }
 3738       }
 3739    }
 3740 out:
 3741    n_lofi_free(o);
 3742    if(cs != n_empty)
 3743       n_lofi_free(cs);
 3744    return ok;
 3745 }
 3746 
 3747 FL enum okay
 3748 imap_search1(const char * volatile spec, int f)
 3749 {
 3750    sighandler_type saveint, savepipe;
 3751    enum okay volatile rv = STOP;
 3752    NYD_ENTER;
 3753 
 3754    if (mb.mb_type != MB_IMAP)
 3755       goto jleave;
 3756 
 3757    imaplock = 1;
 3758    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3759       safe_signal(SIGINT, &_imap_maincatch);
 3760    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3761    if (sigsetjmp(imapjmp, 1) == 0) {
 3762       if (savepipe != SIG_IGN)
 3763          safe_signal(SIGPIPE, imapcatch);
 3764 
 3765       rv = imap_search2(&mb, message, msgCount, spec, f);
 3766    }
 3767    safe_signal(SIGINT, saveint);
 3768    safe_signal(SIGPIPE, savepipe);
 3769    imaplock = 0;
 3770 jleave:
 3771    NYD_LEAVE;
 3772    if (interrupts)
 3773       n_go_onintr_for_imap();
 3774    return rv;
 3775 }
 3776 #endif /* HAVE_IMAP_SEARCH */
 3777 
 3778 FL int
 3779 imap_thisaccount(const char *cp)
 3780 {
 3781    int rv;
 3782    NYD_ENTER;
 3783 
 3784    if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
 3785       rv = 0;
 3786    else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
 3787          mb.mb_imap_account == NULL)
 3788       rv = 0;
 3789    else
 3790       rv = !strcmp(protbase(cp), mb.mb_imap_account);
 3791    NYD_LEAVE;
 3792    return rv;
 3793 }
 3794 
 3795 FL enum okay
 3796 imap_remove(const char * volatile name)
 3797 {
 3798    sighandler_type volatile saveint, savepipe;
 3799    enum okay volatile rv = STOP;
 3800    NYD_ENTER;
 3801 
 3802    if (mb.mb_type != MB_IMAP) {
 3803       n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
 3804       goto jleave;
 3805    }
 3806 
 3807    if (!imap_thisaccount(name)) {
 3808       n_err(_("Can only remove mailboxes on current IMAP server: "
 3809          "\"%s\" not removed\n"), name);
 3810       goto jleave;
 3811    }
 3812 
 3813    imaplock = 1;
 3814    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3815       safe_signal(SIGINT, &_imap_maincatch);
 3816    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3817    if (sigsetjmp(imapjmp, 1) == 0) {
 3818       if (savepipe != SIG_IGN)
 3819          safe_signal(SIGPIPE, imapcatch);
 3820 
 3821       rv = imap_remove1(&mb, imap_fileof(name));
 3822    }
 3823    safe_signal(SIGINT, saveint);
 3824    safe_signal(SIGPIPE, savepipe);
 3825    imaplock = 0;
 3826 
 3827    if (rv == OKAY)
 3828       rv = cache_remove(name);
 3829 jleave:
 3830    NYD_LEAVE;
 3831    if (interrupts)
 3832       n_go_onintr_for_imap();
 3833    return rv;
 3834 }
 3835 
 3836 static enum okay
 3837 imap_remove1(struct mailbox *mp, const char *name)
 3838 {
 3839    char *o;
 3840    int os;
 3841    char const *qname;
 3842    FILE *queuefp;
 3843    enum okay ok;
 3844    NYD_X;
 3845 
 3846    ok = STOP;
 3847    queuefp = NULL;
 3848 
 3849    if((qname = imap_path_quote(mp, name)) != NULL){
 3850       o = n_lofi_alloc(os = strlen(qname) + 100);
 3851       snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
 3852       IMAP_OUT(o, MB_COMD, goto out)
 3853       while (mp->mb_active & MB_COMD)
 3854          ok = imap_answer(mp, 1);
 3855 out:
 3856       n_lofi_free(o);
 3857    }
 3858    return ok;
 3859 }
 3860 
 3861 FL enum okay
 3862 imap_rename(const char *old, const char *new)
 3863 {
 3864    sighandler_type saveint, savepipe;
 3865    enum okay volatile rv = STOP;
 3866    NYD_ENTER;
 3867 
 3868    if (mb.mb_type != MB_IMAP) {
 3869       n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
 3870       goto jleave;
 3871    }
 3872 
 3873    if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
 3874       n_err(_("Can only rename mailboxes on current IMAP "
 3875             "server: \"%s\" not renamed to \"%s\"\n"), old, new);
 3876       goto jleave;
 3877    }
 3878 
 3879    imaplock = 1;
 3880    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 3881       safe_signal(SIGINT, &_imap_maincatch);
 3882    savepipe = safe_signal(SIGPIPE, SIG_IGN);
 3883    if (sigsetjmp(imapjmp, 1) == 0) {
 3884       if (savepipe != SIG_IGN)
 3885          safe_signal(SIGPIPE, imapcatch);
 3886 
 3887       rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
 3888    }
 3889    safe_signal(SIGINT, saveint);
 3890    safe_signal(SIGPIPE, savepipe);
 3891    imaplock = 0;
 3892 
 3893    if (rv == OKAY)
 3894       rv = cache_rename(old, new);
 3895 jleave:
 3896    NYD_LEAVE;
 3897    if (interrupts)
 3898       n_go_onintr_for_imap();
 3899    return rv;
 3900 }
 3901 
 3902 static enum okay
 3903 imap_rename1(struct mailbox *mp, const char *old, const char *new)
 3904 {
 3905    char *o;
 3906    int os;
 3907    char const *qoname, *qnname;
 3908    FILE *queuefp;
 3909    enum okay ok;
 3910    NYD_X;
 3911 
 3912    ok = STOP;
 3913    queuefp = NULL;
 3914 
 3915    if((qoname = imap_path_quote(mp, old)) != NULL &&
 3916          (qnname = imap_path_quote(mp, new)) != NULL){
 3917       o = n_lofi_alloc(os = strlen(qoname) + strlen(qnname) + 100);
 3918       snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
 3919       IMAP_OUT(o, MB_COMD, goto out)
 3920       while (mp->mb_active & MB_COMD)
 3921          ok = imap_answer(mp, 1);
 3922 out:
 3923       n_lofi_free(o);
 3924    }
 3925    return ok;
 3926 }
 3927 
 3928 FL enum okay
 3929 imap_dequeue(struct mailbox *mp, FILE *fp)
 3930 {
 3931    char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
 3932    size_t bufsize, buflen, cnt;
 3933    long offs, offs1, offs2, octets;
 3934    int twice, gotcha = 0;
 3935    FILE *queuefp = NULL;
 3936    enum okay ok = OKAY, rok = OKAY;
 3937    NYD_X;
 3938 
 3939    buf = n_alloc(bufsize = LINESIZE);
 3940    buflen = 0;
 3941    cnt = fsize(fp);
 3942    while ((offs1 = ftell(fp)) >= 0 &&
 3943          fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
 3944       for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
 3945          ;
 3946       while (*bp == ' ')
 3947          ++bp;
 3948       twice = 0;
 3949       if ((offs = ftell(fp)) < 0)
 3950          goto fail;
 3951 again:
 3952       snprintf(o, sizeof o, "%s %s", tag(1), bp);
 3953       if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
 3954          cp = &bp[9];
 3955          while (digitchar(*cp))
 3956             cp++;
 3957          if (*cp != ' ')
 3958             goto fail;
 3959          while (*cp == ' ')
 3960             cp++;
 3961          if ((newname = imap_strex(cp, NULL)) == NULL)
 3962             goto fail;
 3963          IMAP_OUT(o, MB_COMD, continue)
 3964          while (mp->mb_active & MB_COMD)
 3965             ok = imap_answer(mp, twice);
 3966          if (response_status == RESPONSE_NO && twice++ == 0)
 3967             goto trycreate;
 3968          if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
 3969             imap_copyuid(mp, NULL, imap_unquotestr(newname));
 3970          }
 3971       } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
 3972          IMAP_OUT(o, MB_COMD, continue)
 3973          while (mp->mb_active & MB_COMD)
 3974             ok = imap_answer(mp, 1);
 3975          if (ok == OKAY)
 3976             gotcha++;
 3977       } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
 3978          if ((cp = strrchr(bp, '{')) == NULL)
 3979             goto fail;
 3980          octets = atol(&cp[1]) + 2;
 3981          if ((newname = imap_strex(&bp[7], NULL)) == NULL)
 3982             goto fail;
 3983          IMAP_OUT(o, MB_COMD, continue)
 3984          while (mp->mb_active & MB_COMD) {
 3985             ok = imap_answer(mp, twice);
 3986             if (response_type == RESPONSE_CONT)
 3987                break;
 3988          }
 3989          if (ok == STOP) {
 3990             if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
 3991                goto trycreate;
 3992             goto fail;
 3993          }
 3994          while (octets > 0) {
 3995             size_t n = (UICMP(z, octets, >, sizeof iob)
 3996                   ? sizeof iob : (size_t)octets);
 3997             octets -= n;
 3998             if (n != fread(iob, 1, n, fp))
 3999                goto fail;
 4000             swrite1(&mp->mb_sock, iob, n, 1);
 4001          }
 4002          swrite(&mp->mb_sock, "");
 4003          while (mp->mb_active & MB_COMD) {
 4004             ok = imap_answer(mp, 0);
 4005             if (response_status == RESPONSE_NO && twice++ == 0) {
 4006                if (fseek(fp, offs, SEEK_SET) < 0)
 4007                   goto fail;
 4008                goto trycreate;
 4009             }
 4010          }
 4011          if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
 4012             if ((offs2 = ftell(fp)) < 0)
 4013                goto fail;
 4014             fseek(fp, offs1, SEEK_SET);
 4015             if (imap_appenduid_cached(mp, fp) == STOP) {
 4016                (void)fseek(fp, offs2, SEEK_SET);
 4017                goto fail;
 4018             }
 4019          }
 4020       } else {
 4021 fail:
 4022          n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
 4023          rok = STOP;
 4024       }
 4025       continue;
 4026 trycreate:
 4027       snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
 4028       IMAP_OUT(o, MB_COMD, continue)
 4029       while (mp->mb_active & MB_COMD)
 4030          ok = imap_answer(mp, 1);
 4031       if (ok == OKAY)
 4032          goto again;
 4033    }
 4034    fflush(fp);
 4035    rewind(fp);
 4036    ftruncate(fileno(fp), 0);
 4037    if (gotcha)
 4038       imap_close(mp);
 4039    n_free(buf);
 4040    return rok;
 4041 }
 4042 
 4043 static char *
 4044 imap_strex(char const *cp, char const **xp)
 4045 {
 4046    char const *cq;
 4047    char *n = NULL;
 4048    NYD_ENTER;
 4049 
 4050    if (*cp != '"')
 4051       goto jleave;
 4052 
 4053    for (cq = cp + 1; *cq != '\0'; ++cq) {
 4054       if (*cq == '\\')
 4055          cq++;
 4056       else if (*cq == '"')
 4057          break;
 4058    }
 4059    if (*cq != '"')
 4060       goto jleave;
 4061 
 4062    n = n_autorec_alloc(cq - cp + 2);
 4063    memcpy(n, cp, cq - cp +1);
 4064    n[cq - cp + 1] = '\0';
 4065    if (xp != NULL)
 4066       *xp = cq + 1;
 4067 jleave:
 4068    NYD_LEAVE;
 4069    return n;
 4070 }
 4071 
 4072 static enum okay
 4073 check_expunged(void)
 4074 {
 4075    enum okay rv;
 4076    NYD_ENTER;
 4077 
 4078    if (expunged_messages > 0) {
 4079       n_err(_("Command not executed - messages have been expunged\n"));
 4080       rv = STOP;
 4081    } else
 4082       rv = OKAY;
 4083    NYD_LEAVE;
 4084    return rv;
 4085 }
 4086 
 4087 FL int
 4088 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
 4089 {
 4090    struct url url;
 4091    int rv, omsgCount = msgCount;
 4092    NYD_ENTER;
 4093    n_UNUSED(vp);
 4094 
 4095    if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
 4096       n_err(_("Already connected\n"));
 4097       rv = 1;
 4098       goto jleave;
 4099    }
 4100 
 4101    if (!url_parse(&url, CPROTO_IMAP, mailname)) {
 4102       rv = 1;
 4103       goto jleave;
 4104    }
 4105    ok_bclear(disconnected);
 4106    n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
 4107 
 4108    if (mb.mb_type == MB_CACHE) {
 4109       enum fedit_mode fm = FEDIT_NONE;
 4110       if (_imap_rdonly)
 4111          fm |= FEDIT_RDONLY;
 4112       if (!(n_pstate & n_PS_EDIT))
 4113          fm |= FEDIT_SYSBOX;
 4114       _imap_setfile1(NULL, &url, fm, 1);
 4115       if (msgCount > omsgCount)
 4116          newmailinfo(omsgCount);
 4117    }
 4118    rv = 0;
 4119 jleave:
 4120    NYD_LEAVE;
 4121    return rv;
 4122 }
 4123 
 4124 FL int
 4125 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
 4126 {
 4127    struct url url;
 4128    int rv = 1, *msgvec = vp;
 4129    NYD_ENTER;
 4130 
 4131    if (mb.mb_type == MB_CACHE) {
 4132       n_err(_("Not connected\n"));
 4133       goto jleave;
 4134    }
 4135    if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
 4136       n_err(_("The current mailbox is not cached\n"));
 4137       goto jleave;
 4138    }
 4139 
 4140    if (!url_parse(&url, CPROTO_IMAP, mailname))
 4141       goto jleave;
 4142 
 4143    if (*msgvec)
 4144       c_cache(vp);
 4145    ok_bset(disconnected);
 4146    if (mb.mb_type == MB_IMAP) {
 4147       enum fedit_mode fm = FEDIT_NONE;
 4148       if (_imap_rdonly)
 4149          fm |= FEDIT_RDONLY;
 4150       if (!(n_pstate & n_PS_EDIT))
 4151          fm |= FEDIT_SYSBOX;
 4152       sclose(&mb.mb_sock);
 4153       _imap_setfile1(NULL, &url, fm, 1);
 4154    }
 4155    rv = 0;
 4156 jleave:
 4157    NYD_LEAVE;
 4158    return rv;
 4159 }
 4160 
 4161 FL int
 4162 c_cache(void *vp)
 4163 {
 4164    int rv = 1, *msgvec = vp, *ip;
 4165    struct message *mp;
 4166    NYD_ENTER;
 4167 
 4168    if (mb.mb_type != MB_IMAP) {
 4169       n_err(_("Not connected to an IMAP server\n"));
 4170       goto jleave;
 4171    }
 4172    if (cached_uidvalidity(&mb) == 0) {
 4173       n_err(_("The current mailbox is not cached\n"));
 4174       goto jleave;
 4175    }
 4176 
 4177    srelax_hold();
 4178    for (ip = msgvec; *ip; ++ip) {
 4179       mp = &message[*ip - 1];
 4180       if (!(mp->m_content_info & CI_HAVE_BODY)) {
 4181          get_body(mp);
 4182          srelax();
 4183       }
 4184    }
 4185    srelax_rele();
 4186    rv = 0;
 4187 jleave:
 4188    NYD_LEAVE;
 4189    return rv;
 4190 }
 4191 
 4192 FL int
 4193 disconnected(const char *file)
 4194 {
 4195    struct url url;
 4196    int rv = 1;
 4197    NYD_ENTER;
 4198 
 4199    if (ok_blook(disconnected)) {
 4200       rv = 1;
 4201       goto jleave;
 4202    }
 4203 
 4204    if (!url_parse(&url, CPROTO_IMAP, file)) {
 4205       rv = 0;
 4206       goto jleave;
 4207    }
 4208    rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
 4209 
 4210 jleave:
 4211    NYD_LEAVE;
 4212    return rv;
 4213 }
 4214 
 4215 FL void
 4216 transflags(struct message *omessage, long omsgCount, int transparent)
 4217 {
 4218    struct message *omp, *nmp, *newdot, *newprevdot;
 4219    int hf;
 4220    NYD_ENTER;
 4221 
 4222    omp = omessage;
 4223    nmp = message;
 4224    newdot = message;
 4225    newprevdot = NULL;
 4226    while (PTRCMP(omp, <, omessage + omsgCount) &&
 4227          PTRCMP(nmp, <, message + msgCount)) {
 4228       if (dot && nmp->m_uid == dot->m_uid)
 4229          newdot = nmp;
 4230       if (prevdot && nmp->m_uid == prevdot->m_uid)
 4231          newprevdot = nmp;
 4232       if (omp->m_uid == nmp->m_uid) {
 4233          hf = nmp->m_flag & MHIDDEN;
 4234          if (transparent && mb.mb_type == MB_IMAP)
 4235             omp->m_flag &= ~MHIDDEN;
 4236          *nmp++ = *omp++;
 4237          if (transparent && mb.mb_type == MB_CACHE)
 4238             nmp[-1].m_flag |= hf;
 4239       } else if (omp->m_uid < nmp->m_uid)
 4240          ++omp;
 4241       else
 4242          ++nmp;
 4243    }
 4244    dot = newdot;
 4245    setdot(newdot);
 4246    prevdot = newprevdot;
 4247    n_free(omessage);
 4248    NYD_LEAVE;
 4249 }
 4250 
 4251 FL time_t
 4252 imap_read_date_time(const char *cp)
 4253 {
 4254    char buf[3];
 4255    time_t t;
 4256    int i, year, month, day, hour, minute, second, sign = -1;
 4257    NYD2_ENTER;
 4258 
 4259    /* "25-Jul-2004 15:33:44 +0200"
 4260     * |    |    |    |    |    |
 4261     * 0    5   10   15   20   25 */
 4262    if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
 4263       goto jinvalid;
 4264    day = strtol(&cp[1], NULL, 10);
 4265    for (i = 0;;) {
 4266       if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
 4267          break;
 4268       if (n_month_names[++i][0] == '\0')
 4269          goto jinvalid;
 4270    }
 4271    month = i + 1;
 4272    year = strtol(&cp[8], NULL, 10);
 4273    hour = strtol(&cp[13], NULL, 10);
 4274    minute = strtol(&cp[16], NULL, 10);
 4275    second = strtol(&cp[19], NULL, 10);
 4276    if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
 4277       goto jinvalid;
 4278    switch (cp[22]) {
 4279    case '-':
 4280       sign = 1;
 4281       break;
 4282    case '+':
 4283       break;
 4284    default:
 4285       goto jinvalid;
 4286    }
 4287    buf[2] = '\0';
 4288    buf[0] = cp[23];
 4289    buf[1] = cp[24];
 4290    t += strtol(buf, NULL, 10) * sign * 3600;
 4291    buf[0] = cp[25];
 4292    buf[1] = cp[26];
 4293    t += strtol(buf, NULL, 10) * sign * 60;
 4294 jleave:
 4295    NYD2_LEAVE;
 4296    return t;
 4297 jinvalid:
 4298    time(&t);
 4299    goto jleave;
 4300 }
 4301 
 4302 FL const char *
 4303 imap_make_date_time(time_t t)
 4304 {
 4305    static char s[40];
 4306    char const *mn;
 4307    si32_t y, md, th, tm, ts;
 4308    struct tm *tmp;
 4309    int tzdiff, tzdiff_hour, tzdiff_min;
 4310    time_t t2;
 4311    NYD2_ENTER;
 4312 
 4313 jredo:
 4314    if((t2 = mktime(gmtime(&t))) == (time_t)-1){
 4315       t = 0;
 4316       goto jredo;
 4317    }
 4318    tzdiff = t - t2;
 4319    if((tmp = localtime(&t)) == NULL){
 4320       t = 0;
 4321       goto jredo;
 4322    }
 4323 
 4324    tzdiff_hour = (int)(tzdiff / 60);
 4325    tzdiff_min = tzdiff_hour % 60;
 4326    tzdiff_hour /= 60;
 4327    if (tmp->tm_isdst > 0)
 4328       tzdiff_hour++;
 4329 
 4330    if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
 4331       y = 1970;
 4332       mn = n_month_names[0];
 4333       md = 1;
 4334       th = tm = ts = 0;
 4335    }else{
 4336       y += 1900;
 4337       mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
 4338             ? n_month_names[tmp->tm_mon] : n_qm;
 4339 
 4340       if((md = tmp->tm_mday) < 1 || md > 31)
 4341          md = 1;
 4342 
 4343       if((th = tmp->tm_hour) < 0 || th > 23)
 4344          th = 0;
 4345       if((tm = tmp->tm_min) < 0 || tm > 59)
 4346          tm = 0;
 4347       if((ts = tmp->tm_sec) < 0 || ts > 60)
 4348          ts = 0;
 4349    }
 4350 
 4351    snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
 4352          md, mn, y, th, tm, ts, tzdiff_hour, tzdiff_min);
 4353    NYD2_LEAVE;
 4354    return s;
 4355 }
 4356 
 4357 FL char *
 4358 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
 4359 {
 4360    char *n, *np;
 4361    NYD2_ENTER;
 4362 
 4363    np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
 4364          n_MEMORY_DEBUG_ARGSCALL);
 4365 
 4366    /* Just ignore the `is-system-mailbox' prefix XXX */
 4367    if (cp[0] == '%' && cp[1] == ':')
 4368       cp += 2;
 4369 
 4370    while (*cp != '\0') {
 4371       if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
 4372          *np++ = *cp++;
 4373          *np++ = *cp++;
 4374          *np++ = *cp++;
 4375       } else if (cp[0] == '/')
 4376          break;
 4377       else
 4378          *np++ = *cp++;
 4379    }
 4380    *np = '\0';
 4381    NYD2_LEAVE;
 4382    return n;
 4383 }
 4384 #endif /* HAVE_IMAP */
 4385 
 4386 /* s-it-mode */