"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/obs-imap-cache.c" (8 Aug 2018, 24654 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-cache.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  *@ A cache for IMAP.
    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_cache
   42 
   43 #ifndef HAVE_AMALGAMATION
   44 # include "nail.h"
   45 #endif
   46 
   47 EMPTY_FILE()
   48 #ifdef HAVE_IMAP
   49 # include <dirent.h>
   50 
   51 static char *           encname(struct mailbox *mp, const char *name, int same,
   52                            const char *box);
   53 static char *           encuid(struct mailbox *mp, ui64_t uid);
   54 static FILE *           clean(struct mailbox *mp, struct cw *cw);
   55 static ui64_t *         builds(long *contentelem);
   56 static void             purge(struct mailbox *mp, struct message *m, long mc,
   57                            struct cw *cw, const char *name);
   58 static int              longlt(const void *a, const void *b);
   59 static void             remve(unsigned long n);
   60 static FILE *           cache_queue1(struct mailbox *mp, char const *mode,
   61                            char **xname);
   62 static enum okay        dequeue1(struct mailbox *mp);
   63 
   64 static const char infofmt[] = "%c %lu %d %lu %ld";
   65 #define INITSKIP 128L
   66 #define USEBITS(f)  \
   67    ((f) & (MSAVED|MDELETED|MREAD|MBOXED|MNEW|MFLAGGED|MANSWERED|MDRAFTED))
   68 
   69 static const char README1[] = "\
   70 This is a cache directory maintained by " VAL_UAGENT "(1).\n\
   71 You should not change any files within.\n\
   72 Nevertheless, the structure is as follows: Each subdirectory of the\n\
   73 current directory represents an IMAP account, and each subdirectory\n\
   74 below that represents a mailbox. Each mailbox directory contains a file\n\
   75 named UIDVALIDITY which describes the validity in relation to the version\n\
   76 on the server. Other files have names corresponding to their IMAP UID.\n";
   77 static const char README2[] = "\n\
   78 The first 128 bytes of these files are used to store message attributes; the\n\
   79 following data is equivalent to compress(1) output. So if you have to save a\n\
   80 message by hand because of an emergency, throw away the first 128 bytes and\n\
   81 decompress the rest, as e.g. \"dd if=FILE skip=1 bs=128 | zcat\" does.\n";
   82 static const char README3[] = "\n\
   83 Files named QUEUE contain data that will be sent do the IMAP server next\n\
   84 time a connection is made in online mode.\n";
   85 static const char README4[] = "\n\
   86 You can safely delete any file or directory here, unless it contains a QUEUE\n\
   87 file that is not empty; " VAL_UAGENT
   88    " will download the data again and will also\n\
   89 write new cache entries if configured in this way. If you do not wish to use\n\
   90 the cache anymore, delete the entire directory and unset the *imap-cache*\n\
   91 variable in " VAL_UAGENT "(1).\n";
   92 
   93 static char *
   94 encname(struct mailbox *mp, const char *name, int same, const char *box)
   95 {
   96    char *cachedir, *eaccount, *ename, *res;
   97    int resz;
   98    NYD2_ENTER;
   99 
  100    ename = urlxenc(name, TRU1);
  101    if (mp->mb_cache_directory && same && box == NULL) {
  102       res = n_autorec_alloc(resz = strlen(mp->mb_cache_directory) +
  103             strlen(ename) + 2);
  104       snprintf(res, resz, "%s%s%s", mp->mb_cache_directory,
  105          (*ename ? "/" : ""), ename);
  106    } else {
  107       res = NULL;
  108 
  109       if ((cachedir = ok_vlook(imap_cache)) == NULL ||
  110             (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
  111          goto jleave;
  112       eaccount = urlxenc(mp->mb_imap_account, TRU1);
  113 
  114       if (box != NULL || asccasecmp(box = mp->mb_imap_mailbox, "INBOX")) {
  115          bool_t err;
  116 
  117          box = imap_path_encode(box, &err);
  118          if(err)
  119             goto jleave;
  120          box = urlxenc(box, TRU1);
  121       } else
  122          box = "INBOX";
  123 
  124       res = n_autorec_alloc(resz = strlen(cachedir) + strlen(eaccount) +
  125             strlen(box) + strlen(ename) + 4);
  126       snprintf(res, resz, "%s/%s/%s%s%s", cachedir, eaccount, box,
  127             (*ename ? "/" : ""), ename);
  128    }
  129 jleave:
  130    NYD2_LEAVE;
  131    return res;
  132 }
  133 
  134 static char *
  135 encuid(struct mailbox *mp, ui64_t uid)
  136 {
  137    char buf[64], *cp;
  138    NYD2_ENTER;
  139 
  140    snprintf(buf, sizeof buf, "%" PRIu64, uid);
  141    cp = encname(mp, buf, 1, NULL);
  142    NYD2_LEAVE;
  143    return cp;
  144 }
  145 
  146 FL enum okay
  147 getcache1(struct mailbox *mp, struct message *m, enum needspec need,
  148    int setflags)
  149 {
  150    FILE *fp;
  151    long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0;
  152    int lastc = EOF, i, xflag, inheader = 1;
  153    char b, iob[32768];
  154    off_t offset;
  155    void *zp;
  156    enum okay rv = STOP;
  157    NYD2_ENTER;
  158 
  159    if (setflags == 0 && ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) ||
  160          m->m_uid == 0))
  161       goto jleave;
  162    if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL)
  163       goto jleave;
  164 
  165    n_file_lock(fileno(fp), FLT_READ, 0,0, 0);
  166    if (fscanf(fp, infofmt, &b, (unsigned long*)&xsize, &xflag,
  167          (unsigned long*)&xtime, &xlines) < 4)
  168       goto jfail;
  169    if (need != NEED_UNSPEC) {
  170       switch (b) {
  171       case 'H':
  172          if (need == NEED_HEADER)
  173             goto jsuccess;
  174          goto jfail;
  175       case 'B':
  176          if (need == NEED_HEADER || need == NEED_BODY)
  177             goto jsuccess;
  178          goto jfail;
  179       default:
  180          goto jfail;
  181       }
  182    }
  183 jsuccess:
  184    if (b == 'N')
  185       goto jflags;
  186    if (fseek(fp, INITSKIP, SEEK_SET) < 0)
  187       goto jfail;
  188    zp = zalloc(fp);
  189    if (fseek(mp->mb_otf, 0L, SEEK_END) < 0) {
  190       zfree(zp);
  191       goto jfail;
  192    }
  193    offset = ftell(mp->mb_otf);
  194    while (inheader && (n = zread(zp, iob, sizeof iob)) > 0) {
  195       size += n;
  196       for (i = 0; i < n; i++) {
  197          if (iob[i] == '\n') {
  198             lines++;
  199             if (lastc == '\n')
  200                inheader = 0;
  201          }
  202          lastc = iob[i]&0377;
  203       }
  204       fwrite(iob, 1, n, mp->mb_otf);
  205    }
  206    if (n > 0 && need == NEED_BODY) {
  207       while ((n = zread(zp, iob, sizeof iob)) > 0) {
  208          size += n;
  209          for (i = 0; i < n; i++)
  210             if (iob[i] == '\n')
  211                lines++;
  212          fwrite(iob, 1, n, mp->mb_otf);
  213       }
  214    }
  215    fflush(mp->mb_otf);
  216    if (zfree(zp) < 0 || n < 0 || ferror(fp) || ferror(mp->mb_otf))
  217       goto jfail;
  218 
  219    m->m_size = size;
  220    m->m_lines = lines;
  221    m->m_block = mailx_blockof(offset);
  222    m->m_offset = mailx_offsetof(offset);
  223 jflags:
  224    if (setflags) {
  225       m->m_xsize = xsize;
  226       m->m_time = xtime;
  227       if (setflags & 2) {
  228          m->m_flag = xflag | MNOFROM;
  229          if (b != 'B')
  230             m->m_flag |= MHIDDEN;
  231       }
  232    }
  233    if (xlines > 0 && m->m_xlines <= 0)
  234       m->m_xlines = xlines;
  235    switch (b) {
  236    case 'B':
  237       m->m_xsize = xsize;
  238       if (xflag == MREAD && xlines > 0)
  239          m->m_flag |= MFULLYCACHED;
  240       if (need == NEED_BODY) {
  241          m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
  242          if (m->m_lines > 0)
  243             m->m_xlines = m->m_lines;
  244          break;
  245       }
  246       /*FALLTHRU*/
  247    case 'H':
  248       m->m_content_info |= CI_HAVE_HEADER;
  249       break;
  250    case 'N':
  251       break;
  252    }
  253    rv = OKAY;
  254 jfail:
  255    Fclose(fp);
  256 jleave:
  257    NYD2_LEAVE;
  258    return rv;
  259 }
  260 
  261 FL enum okay
  262 getcache(struct mailbox *mp, struct message *m, enum needspec need)
  263 {
  264    enum okay rv;
  265    NYD_ENTER;
  266 
  267    rv = getcache1(mp, m, need, 0);
  268    NYD_LEAVE;
  269    return rv;
  270 }
  271 
  272 FL void
  273 putcache(struct mailbox *mp, struct message *m)
  274 {
  275    char iob[32768], *name, ob;
  276    FILE *ibuf, *obuf;
  277    int c, oflag;
  278    long n, cnt, oldoffset, osize, otime, olines = -1;
  279    void *zp;
  280    NYD_ENTER;
  281 
  282    if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0 ||
  283          m->m_time == 0 || (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED)
  284       goto jleave;
  285    if (m->m_content_info & CI_HAVE_BODY)
  286       c = 'B';
  287    else if (m->m_content_info & CI_HAVE_HEADER)
  288       c = 'H';
  289    else if (!(m->m_content_info & CI_HAVE_MASK))
  290       c = 'N';
  291    else
  292       goto jleave;
  293    if ((oldoffset = ftell(mp->mb_itf)) < 0) /* XXX weird err hdling */
  294       oldoffset = 0;
  295    if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) {
  296       if ((obuf = Fopen(name, "w")) == NULL)
  297          goto jleave;
  298       n_file_lock(fileno(obuf), FLT_WRITE, 0,0, 0); /* XXX err hdl */
  299    } else {
  300       n_file_lock(fileno(obuf), FLT_READ, 0,0, 0); /* XXX err hdl */
  301       if (fscanf(obuf, infofmt, &ob, (unsigned long*)&osize, &oflag,
  302             (unsigned long*)&otime, &olines) >= 4 && ob != '\0' &&
  303             (ob == 'B' || (ob == 'H' && c != 'B'))) {
  304          if (m->m_xlines <= 0 && olines > 0)
  305             m->m_xlines = olines;
  306          if ((c != 'N' && (size_t)osize != m->m_xsize) ||
  307                oflag != (int)USEBITS(m->m_flag) || otime != m->m_time ||
  308                (m->m_xlines > 0 && olines != m->m_xlines)) {
  309             fflush(obuf);
  310             rewind(obuf);
  311             fprintf(obuf, infofmt, ob, (unsigned long)m->m_xsize,
  312                USEBITS(m->m_flag), (unsigned long)m->m_time, m->m_xlines);
  313             putc('\n', obuf);
  314          }
  315          Fclose(obuf);
  316          goto jleave;
  317       }
  318       fflush(obuf);
  319       rewind(obuf);
  320       ftruncate(fileno(obuf), 0);
  321    }
  322    if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) {
  323       Fclose(obuf);
  324       goto jleave;
  325    }
  326    if (c == 'N')
  327       goto jdone;
  328    fseek(obuf, INITSKIP, SEEK_SET);
  329    zp = zalloc(obuf);
  330    cnt = m->m_size;
  331    while (cnt > 0) {
  332       n = (cnt > (long)sizeof iob) ? (long)sizeof iob : cnt;
  333       cnt -= n;
  334       if ((size_t)n != fread(iob, 1, n, ibuf) ||
  335             n != (long)zwrite(zp, iob, n)) {
  336          unlink(name);
  337          zfree(zp);
  338          goto jout;
  339       }
  340    }
  341    if (zfree(zp) < 0) {
  342       unlink(name);
  343       goto jout;
  344    }
  345 jdone:
  346    rewind(obuf);
  347    fprintf(obuf, infofmt, c, (unsigned long)m->m_xsize, USEBITS(m->m_flag),
  348          (unsigned long)m->m_time, m->m_xlines);
  349    putc('\n', obuf);
  350    if (ferror(obuf)) {
  351       unlink(name);
  352       goto jout;
  353    }
  354    if (c == 'B' && USEBITS(m->m_flag) == MREAD)
  355       m->m_flag |= MFULLYCACHED;
  356 jout:
  357    if (Fclose(obuf) != 0) {
  358       m->m_flag &= ~MFULLYCACHED;
  359       unlink(name);
  360    }
  361    (void)fseek(mp->mb_itf, oldoffset, SEEK_SET);
  362 jleave:
  363    NYD_LEAVE;
  364 }
  365 
  366 FL void
  367 initcache(struct mailbox *mp)
  368 {
  369    char *name, *uvname;
  370    FILE *uvfp;
  371    ui64_t uv;
  372    struct cw cw;
  373    NYD_ENTER;
  374 
  375    if (mp->mb_cache_directory != NULL)
  376       n_free(mp->mb_cache_directory);
  377    mp->mb_cache_directory = NULL;
  378    if ((name = encname(mp, "", 1, NULL)) == NULL)
  379       goto jleave;
  380    mp->mb_cache_directory = sstrdup(name);
  381    if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
  382       goto jleave;
  383    if (cwget(&cw) == STOP)
  384       goto jleave;
  385    if ((uvfp = Fopen(uvname, "r+")) == NULL ||
  386          (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
  387          fscanf(uvfp, "%" PRIu64 , &uv) != 1 || uv != mp->mb_uidvalidity) {
  388       if ((uvfp = clean(mp, &cw)) == NULL)
  389          goto jout;
  390    } else {
  391       fflush(uvfp);
  392       rewind(uvfp);
  393    }
  394    n_file_lock(fileno(uvfp), FLT_WRITE, 0,0, 0);
  395    fprintf(uvfp, "%" PRIu64 "\n", mp->mb_uidvalidity);
  396    if (ferror(uvfp) || Fclose(uvfp) != 0) {
  397       unlink(uvname);
  398       mp->mb_uidvalidity = 0;
  399    }
  400 jout:
  401    cwrelse(&cw);
  402 jleave:
  403    NYD_LEAVE;
  404 }
  405 
  406 FL void
  407 purgecache(struct mailbox *mp, struct message *m, long mc)
  408 {
  409    char *name;
  410    struct cw cw;
  411    NYD_ENTER;
  412 
  413    if ((name = encname(mp, "", 1, NULL)) == NULL)
  414       goto jleave;
  415    if (cwget(&cw) == STOP)
  416       goto jleave;
  417    purge(mp, m, mc, &cw, name);
  418    cwrelse(&cw);
  419 jleave:
  420    NYD_LEAVE;
  421 }
  422 
  423 static FILE *
  424 clean(struct mailbox *mp, struct cw *cw)
  425 {
  426    char *cachedir, *eaccount, *buf;
  427    char const *emailbox;
  428    int bufsz;
  429    DIR *dirp;
  430    struct dirent *dp;
  431    FILE *fp = NULL;
  432    NYD_ENTER;
  433 
  434    if ((cachedir = ok_vlook(imap_cache)) == NULL ||
  435          (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
  436       goto jleave;
  437    eaccount = urlxenc(mp->mb_imap_account, TRU1);
  438    if (asccasecmp(emailbox = mp->mb_imap_mailbox, "INBOX")) {
  439       bool_t err;
  440 
  441       emailbox = imap_path_encode(emailbox, &err);
  442       if(err)
  443          goto jleave;
  444       emailbox = urlxenc(emailbox, TRU1);
  445    }
  446    buf = n_autorec_alloc(bufsz = strlen(cachedir) + strlen(eaccount) +
  447          strlen(emailbox) + 40);
  448    if (!n_path_mkdir(cachedir))
  449       goto jleave;
  450    snprintf(buf, bufsz, "%s/README", cachedir);
  451    if ((fp = Fopen(buf, "wx")) != NULL) {
  452       fputs(README1, fp);
  453       fputs(README2, fp);
  454       fputs(README3, fp);
  455       fputs(README4, fp);
  456       Fclose(fp);
  457    }
  458    fp = NULL;
  459    snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
  460    if (!n_path_mkdir(buf))
  461       goto jleave;
  462    if (chdir(buf) < 0)
  463       goto jleave;
  464    if ((dirp = opendir(".")) == NULL)
  465       goto jout;
  466    while ((dp = readdir(dirp)) != NULL) {
  467       if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
  468             (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
  469          continue;
  470       unlink(dp->d_name);
  471    }
  472    closedir(dirp);
  473    fp = Fopen("UIDVALIDITY", "w");
  474 jout:
  475    if (cwret(cw) == STOP) {
  476       n_err(_("Fatal: Cannot change back to current directory.\n"));
  477       abort();
  478    }
  479 jleave:
  480    NYD_LEAVE;
  481    return fp;
  482 }
  483 
  484 static ui64_t *
  485 builds(long *contentelem)
  486 {
  487    ui64_t n, *contents = NULL;
  488    long contentalloc = 0;
  489    char const *x;
  490    DIR *dirp;
  491    struct dirent *dp;
  492    NYD_ENTER;
  493 
  494    *contentelem = 0;
  495    if ((dirp = opendir(".")) == NULL)
  496       goto jleave;
  497    while ((dp = readdir(dirp)) != NULL) {
  498       if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
  499             (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
  500          continue;
  501 
  502       n_idec_ui64_cp(&n, dp->d_name, 10, &x);/* TODO errors? */
  503       if (*x != '\0')
  504          continue;
  505       if (*contentelem >= contentalloc - 1)
  506          contents = n_realloc(contents,
  507                (contentalloc += 200) * sizeof *contents);
  508       contents[(*contentelem)++] = n;
  509    }
  510    closedir(dirp);
  511    if (*contentelem > 0) {
  512       contents[*contentelem] = 0;
  513       qsort(contents, *contentelem, sizeof *contents, longlt);
  514    }
  515 jleave:
  516    NYD_LEAVE;
  517    return contents;
  518 }
  519 
  520 static void
  521 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
  522    const char *name)
  523 {
  524    ui64_t *contents;
  525    long i, j, contentelem;
  526    NYD_ENTER;
  527    n_UNUSED(mp);
  528 
  529    if (chdir(name) < 0)
  530       goto jleave;
  531    contents = builds(&contentelem);
  532    if (contents != NULL) {
  533       i = j = 0;
  534       while (j < contentelem) {
  535          if (i < mc && m[i].m_uid == contents[j]) {
  536             i++;
  537             j++;
  538          } else if (i < mc && m[i].m_uid < contents[j])
  539             i++;
  540          else
  541             remve(contents[j++]);
  542       }
  543       n_free(contents);
  544    }
  545    if (cwret(cw) == STOP) {
  546       n_err(_("Fatal: Cannot change back to current directory.\n"));
  547       abort();
  548    }
  549 jleave:
  550    NYD_LEAVE;
  551 }
  552 
  553 static int
  554 longlt(const void *a, const void *b)
  555 {
  556    union {long l; int i;} u;
  557    NYD_ENTER;
  558 
  559    u.l = *(long const*)a - *(long const*)b;
  560    u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
  561    NYD_LEAVE;
  562    return u.i;
  563 }
  564 
  565 static void
  566 remve(unsigned long n)
  567 {
  568    char buf[30];
  569    NYD_ENTER;
  570 
  571    snprintf(buf, sizeof buf, "%lu", n);
  572    unlink(buf);
  573    NYD_LEAVE;
  574 }
  575 
  576 FL void
  577 delcache(struct mailbox *mp, struct message *m)
  578 {
  579    char *fn;
  580    NYD_ENTER;
  581 
  582    fn = encuid(mp, m->m_uid);
  583    if (fn && unlink(fn) == 0)
  584       m->m_flag |= MUNLINKED;
  585    NYD_LEAVE;
  586 }
  587 
  588 FL enum okay
  589 cache_setptr(enum fedit_mode fm, int transparent)
  590 {
  591    struct cw cw;
  592    int i, omsgCount = 0;
  593    char *name;
  594    ui64_t *contents;
  595    long contentelem;
  596    struct message *omessage;
  597    enum okay rv = STOP;
  598    NYD_ENTER;
  599 
  600    omessage = message;
  601    omsgCount = msgCount;
  602 
  603    if (mb.mb_cache_directory != NULL) {
  604       n_free(mb.mb_cache_directory);
  605       mb.mb_cache_directory = NULL;
  606    }
  607    if ((name = encname(&mb, "", 1, NULL)) == NULL)
  608       goto jleave;
  609    mb.mb_cache_directory = sstrdup(name);
  610    if (cwget(&cw) == STOP)
  611       goto jleave;
  612    if (chdir(name) < 0)
  613       goto jleave;
  614    contents = builds(&contentelem);
  615    msgCount = contentelem;
  616    message = n_calloc(msgCount + 1, sizeof *message);
  617    if (cwret(&cw) == STOP) {
  618       n_err(_("Fatal: Cannot change back to current directory.\n"));
  619       abort();
  620    }
  621    cwrelse(&cw);
  622 
  623    srelax_hold();
  624    for (i = 0; i < msgCount; i++) {
  625       message[i].m_uid = contents[i];
  626       getcache1(&mb, &message[i], NEED_UNSPEC, 3);
  627       srelax();
  628    }
  629    srelax_rele();
  630 
  631    if (contents != NULL)
  632       n_free(contents);
  633    mb.mb_type = MB_CACHE;
  634    mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY)
  635          ) ? 0 : MB_DELE;
  636    if(omessage != NULL){
  637       if(transparent)
  638          /* This frees the message */
  639          transflags(omessage, omsgCount, 1);
  640       else
  641          n_free(omessage);
  642    }
  643    setdot(message);
  644    rv = OKAY;
  645 jleave:
  646    NYD_LEAVE;
  647    return rv;
  648 }
  649 
  650 FL enum okay
  651 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
  652 {
  653    char *name, *cachedir, *eaccount;
  654    DIR *dirp;
  655    struct dirent *dp;
  656    const char *cp, *bp, *sp;
  657    int namesz;
  658    enum okay rv = STOP;
  659    NYD_ENTER;
  660 
  661    if ((cachedir = ok_vlook(imap_cache)) == NULL ||
  662          (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
  663       goto jleave;
  664    eaccount = urlxenc(mp->mb_imap_account, TRU1);
  665    name = n_autorec_alloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
  666    snprintf(name, namesz, "%s/%s", cachedir, eaccount);
  667    if ((dirp = opendir(name)) == NULL)
  668       goto jleave;
  669    while ((dp = readdir(dirp)) != NULL) {
  670       if (dp->d_name[0] == '.')
  671          continue;
  672       cp = sp = imap_path_decode(urlxdec(dp->d_name), NULL);
  673       for (bp = base; *bp && *bp == *sp; bp++)
  674          sp++;
  675       if (*bp)
  676          continue;
  677       cp = strip ? sp : cp;
  678       fprintf(fp, "%s\n", *cp ? cp : "INBOX");
  679    }
  680    closedir(dirp);
  681    rv = OKAY;
  682 jleave:
  683    NYD_LEAVE;
  684    return rv;
  685 }
  686 
  687 FL enum okay
  688 cache_remove(const char *name)
  689 {
  690    struct stat st;
  691    DIR *dirp;
  692    struct dirent *dp;
  693    char *path, *dir;
  694    int pathsize, pathend, n;
  695    enum okay rv = OKAY;
  696    NYD_ENTER;
  697 
  698    if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
  699       goto jleave;
  700    pathend = strlen(dir);
  701    path = n_alloc(pathsize = pathend + 30);
  702    memcpy(path, dir, pathend);
  703    path[pathend++] = '/';
  704    path[pathend] = '\0';
  705    if ((dirp = opendir(path)) == NULL) {
  706       n_free(path);
  707       goto jleave;
  708    }
  709    while ((dp = readdir(dirp)) != NULL) {
  710       if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
  711             (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
  712          continue;
  713       n = strlen(dp->d_name) + 1;
  714       if (pathend + n > pathsize)
  715          path = n_realloc(path, pathsize = pathend + n + 30);
  716       memcpy(path + pathend, dp->d_name, n);
  717       if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
  718          continue;
  719       if (unlink(path) < 0) {
  720          n_perr(path, 0);
  721          closedir(dirp);
  722          n_free(path);
  723          rv = STOP;
  724          goto jleave;
  725       }
  726    }
  727    closedir(dirp);
  728    path[pathend] = '\0';
  729    rmdir(path);   /* no error on failure, might contain submailboxes */
  730    n_free(path);
  731 jleave:
  732    NYD_LEAVE;
  733    return rv;
  734 }
  735 
  736 FL enum okay
  737 cache_rename(const char *old, const char *new)
  738 {
  739    char *olddir, *newdir;
  740    enum okay rv = OKAY;
  741    NYD_ENTER;
  742 
  743    if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
  744          (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
  745       goto jleave;
  746    if (rename(olddir, newdir) < 0) {
  747       n_perr(olddir, 0);
  748       rv = STOP;
  749    }
  750 jleave:
  751    NYD_LEAVE;
  752    return rv;
  753 }
  754 
  755 FL ui64_t
  756 cached_uidvalidity(struct mailbox *mp)
  757 {
  758    FILE *uvfp;
  759    char *uvname;
  760    ui64_t uv;
  761    NYD_ENTER;
  762 
  763    if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
  764       uv = 0;
  765       goto jleave;
  766    }
  767    if ((uvfp = Fopen(uvname, "r")) == NULL ||
  768          (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
  769          fscanf(uvfp, "%" PRIu64, &uv) != 1)
  770       uv = 0;
  771    if (uvfp != NULL)
  772       Fclose(uvfp);
  773 jleave:
  774    NYD_LEAVE;
  775    return uv;
  776 }
  777 
  778 static FILE *
  779 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
  780 {
  781    char *name;
  782    FILE *fp = NULL;
  783    NYD_ENTER;
  784 
  785    if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
  786       goto jleave;
  787    if ((fp = Fopen(name, mode)) != NULL)
  788       n_file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
  789    if (xname)
  790       *xname = name;
  791 jleave:
  792    NYD_LEAVE;
  793    return fp;
  794 }
  795 
  796 FL FILE *
  797 cache_queue(struct mailbox *mp)
  798 {
  799    FILE *fp;
  800    NYD_ENTER;
  801 
  802    fp = cache_queue1(mp, "a", NULL);
  803    if (fp == NULL)
  804       n_err(_("Cannot queue IMAP command. Retry when online.\n"));
  805    NYD_LEAVE;
  806    return fp;
  807 }
  808 
  809 FL enum okay
  810 cache_dequeue(struct mailbox *mp)
  811 {
  812    int bufsz;
  813    char *cachedir, *eaccount, *buf, *oldbox;
  814    DIR *dirp;
  815    struct dirent *dp;
  816    enum okay rv = OKAY;
  817    NYD_ENTER;
  818 
  819    if ((cachedir = ok_vlook(imap_cache)) == NULL ||
  820          (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
  821       goto jleave;
  822    eaccount = urlxenc(mp->mb_imap_account, TRU1);
  823    buf = n_autorec_alloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
  824    snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
  825    if ((dirp = opendir(buf)) == NULL)
  826       goto jleave;
  827    oldbox = mp->mb_imap_mailbox;
  828    while ((dp = readdir(dirp)) != NULL) {
  829       if (dp->d_name[0] == '.')
  830          continue;
  831       /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
  832        * (but wuuuuh, what a shit!) */
  833       mp->mb_imap_mailbox = sstrdup(
  834             imap_path_decode(urlxdec(dp->d_name), NULL));
  835       dequeue1(mp);
  836       {  char *x = mp->mb_imap_mailbox;
  837          mp->mb_imap_mailbox = oldbox;
  838          n_free(x);
  839       }
  840    }
  841    closedir(dirp);
  842 jleave:
  843    NYD_LEAVE;
  844    return rv;
  845 }
  846 
  847 static enum okay
  848 dequeue1(struct mailbox *mp)
  849 {
  850    FILE *fp = NULL, *uvfp = NULL;
  851    char *qname, *uvname;
  852    ui64_t uv;
  853    off_t is_size;
  854    int is_count;
  855    enum okay rv = OKAY;
  856    NYD_ENTER;
  857 
  858    fp = cache_queue1(mp, "r+", &qname);
  859    if (fp != NULL && fsize(fp) > 0) {
  860       if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
  861             != OKAY) {
  862          n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
  863          goto jsave;
  864       }
  865       if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
  866             (uvfp = Fopen(uvname, "r")) == NULL ||
  867             (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
  868             fscanf(uvfp, "%" PRIu64, &uv) != 1 || uv != mp->mb_uidvalidity) {
  869          n_err(_("Unique identifiers for \"%s\" are out of date. "
  870             "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
  871 jsave:
  872          n_err(_("Saving IMAP commands to *DEAD*\n"));
  873          savedeadletter(fp, 0);
  874          ftruncate(fileno(fp), 0);
  875          Fclose(fp);
  876          if (uvfp)
  877             Fclose(uvfp);
  878          rv = STOP;
  879          goto jleave;
  880       }
  881       Fclose(uvfp);
  882       printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
  883       imap_dequeue(mp, fp);
  884    }
  885    if (fp) {
  886       Fclose(fp);
  887       unlink(qname);
  888    }
  889 jleave:
  890    NYD_LEAVE;
  891    return rv;
  892 }
  893 #endif /* HAVE_IMAP */
  894 
  895 /* s-it-mode */