"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/obs-imap-cache.c" (16 Feb 2018, 24502 Bytes) of package /linux/misc/s-nail-14.9.7.tar.xz:


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

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