"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/obs-imap-cache.c" (25 Mar 2018, 24542 Bytes) of package /linux/misc/s-nail-14.9.10.tar.xz:


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

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ 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, ui64_t uid);
   53 static FILE *           clean(struct mailbox *mp, struct cw *cw);
   54 static ui64_t *         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, ui64_t uid)
  134 {
  135    char buf[64], *cp;
  136    NYD2_ENTER;
  137 
  138    snprintf(buf, sizeof buf, "%" PRIu64, 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 ui64_t *
  483 builds(long *contentelem)
  484 {
  485    ui64_t n, *contents = NULL;
  486    long contentalloc = 0;
  487    char const *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 
  500       n_idec_ui64_cp(&n, dp->d_name, 10, &x);/* TODO errors? */
  501       if (*x != '\0')
  502          continue;
  503       if (*contentelem >= contentalloc - 1)
  504          contents = n_realloc(contents,
  505                (contentalloc += 200) * sizeof *contents);
  506       contents[(*contentelem)++] = n;
  507    }
  508    closedir(dirp);
  509    if (*contentelem > 0) {
  510       contents[*contentelem] = 0;
  511       qsort(contents, *contentelem, sizeof *contents, longlt);
  512    }
  513 jleave:
  514    NYD_LEAVE;
  515    return contents;
  516 }
  517 
  518 static void
  519 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
  520    const char *name)
  521 {
  522    ui64_t *contents;
  523    long i, j, contentelem;
  524    NYD_ENTER;
  525    n_UNUSED(mp);
  526 
  527    if (chdir(name) < 0)
  528       goto jleave;
  529    contents = builds(&contentelem);
  530    if (contents != NULL) {
  531       i = j = 0;
  532       while (j < contentelem) {
  533          if (i < mc && m[i].m_uid == contents[j]) {
  534             i++;
  535             j++;
  536          } else if (i < mc && m[i].m_uid < contents[j])
  537             i++;
  538          else
  539             remve(contents[j++]);
  540       }
  541       free(contents);
  542    }
  543    if (cwret(cw) == STOP) {
  544       n_err(_("Fatal: Cannot change back to current directory.\n"));
  545       abort();
  546    }
  547 jleave:
  548    NYD_LEAVE;
  549 }
  550 
  551 static int
  552 longlt(const void *a, const void *b)
  553 {
  554    union {long l; int i;} u;
  555    NYD_ENTER;
  556 
  557    u.l = *(long const*)a - *(long const*)b;
  558    u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
  559    NYD_LEAVE;
  560    return u.i;
  561 }
  562 
  563 static void
  564 remve(unsigned long n)
  565 {
  566    char buf[30];
  567    NYD_ENTER;
  568 
  569    snprintf(buf, sizeof buf, "%lu", n);
  570    unlink(buf);
  571    NYD_LEAVE;
  572 }
  573 
  574 FL void
  575 delcache(struct mailbox *mp, struct message *m)
  576 {
  577    char *fn;
  578    NYD_ENTER;
  579 
  580    fn = encuid(mp, m->m_uid);
  581    if (fn && unlink(fn) == 0)
  582       m->m_flag |= MUNLINKED;
  583    NYD_LEAVE;
  584 }
  585 
  586 FL enum okay
  587 cache_setptr(enum fedit_mode fm, int transparent)
  588 {
  589    struct cw cw;
  590    int i, omsgCount = 0;
  591    char *name;
  592    ui64_t *contents;
  593    long contentelem;
  594    struct message *omessage;
  595    enum okay rv = STOP;
  596    NYD_ENTER;
  597 
  598    omessage = message;
  599    omsgCount = msgCount;
  600 
  601    if (mb.mb_cache_directory != NULL) {
  602       free(mb.mb_cache_directory);
  603       mb.mb_cache_directory = NULL;
  604    }
  605    if ((name = encname(&mb, "", 1, NULL)) == NULL)
  606       goto jleave;
  607    mb.mb_cache_directory = sstrdup(name);
  608    if (cwget(&cw) == STOP)
  609       goto jleave;
  610    if (chdir(name) < 0)
  611       goto jleave;
  612    contents = builds(&contentelem);
  613    msgCount = contentelem;
  614    message = scalloc(msgCount + 1, sizeof *message);
  615    if (cwret(&cw) == STOP) {
  616       n_err(_("Fatal: Cannot change back to current directory.\n"));
  617       abort();
  618    }
  619    cwrelse(&cw);
  620 
  621    srelax_hold();
  622    for (i = 0; i < msgCount; i++) {
  623       message[i].m_uid = contents[i];
  624       getcache1(&mb, &message[i], NEED_UNSPEC, 3);
  625       srelax();
  626    }
  627    srelax_rele();
  628 
  629    if (contents != NULL)
  630       free(contents);
  631    mb.mb_type = MB_CACHE;
  632    mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY)
  633          ) ? 0 : MB_DELE;
  634    if(omessage != NULL){
  635       if(transparent)
  636          /* This frees the message */
  637          transflags(omessage, omsgCount, 1);
  638       else
  639          n_free(omessage);
  640    }
  641    setdot(message);
  642    rv = OKAY;
  643 jleave:
  644    NYD_LEAVE;
  645    return rv;
  646 }
  647 
  648 FL enum okay
  649 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
  650 {
  651    char *name, *cachedir, *eaccount;
  652    DIR *dirp;
  653    struct dirent *dp;
  654    const char *cp, *bp, *sp;
  655    int namesz;
  656    enum okay rv = STOP;
  657    NYD_ENTER;
  658 
  659    if ((cachedir = ok_vlook(imap_cache)) == NULL ||
  660          (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
  661       goto jleave;
  662    eaccount = urlxenc(mp->mb_imap_account, TRU1);
  663    name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
  664    snprintf(name, namesz, "%s/%s", cachedir, eaccount);
  665    if ((dirp = opendir(name)) == NULL)
  666       goto jleave;
  667    while ((dp = readdir(dirp)) != NULL) {
  668       if (dp->d_name[0] == '.')
  669          continue;
  670       cp = sp = imap_path_decode(urlxdec(dp->d_name), NULL);
  671       for (bp = base; *bp && *bp == *sp; bp++)
  672          sp++;
  673       if (*bp)
  674          continue;
  675       cp = strip ? sp : cp;
  676       fprintf(fp, "%s\n", *cp ? cp : "INBOX");
  677    }
  678    closedir(dirp);
  679    rv = OKAY;
  680 jleave:
  681    NYD_LEAVE;
  682    return rv;
  683 }
  684 
  685 FL enum okay
  686 cache_remove(const char *name)
  687 {
  688    struct stat st;
  689    DIR *dirp;
  690    struct dirent *dp;
  691    char *path, *dir;
  692    int pathsize, pathend, n;
  693    enum okay rv = OKAY;
  694    NYD_ENTER;
  695 
  696    if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
  697       goto jleave;
  698    pathend = strlen(dir);
  699    path = smalloc(pathsize = pathend + 30);
  700    memcpy(path, dir, pathend);
  701    path[pathend++] = '/';
  702    path[pathend] = '\0';
  703    if ((dirp = opendir(path)) == NULL) {
  704       free(path);
  705       goto jleave;
  706    }
  707    while ((dp = readdir(dirp)) != NULL) {
  708       if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
  709             (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
  710          continue;
  711       n = strlen(dp->d_name) + 1;
  712       if (pathend + n > pathsize)
  713          path = srealloc(path, pathsize = pathend + n + 30);
  714       memcpy(path + pathend, dp->d_name, n);
  715       if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
  716          continue;
  717       if (unlink(path) < 0) {
  718          n_perr(path, 0);
  719          closedir(dirp);
  720          free(path);
  721          rv = STOP;
  722          goto jleave;
  723       }
  724    }
  725    closedir(dirp);
  726    path[pathend] = '\0';
  727    rmdir(path);   /* no error on failure, might contain submailboxes */
  728    free(path);
  729 jleave:
  730    NYD_LEAVE;
  731    return rv;
  732 }
  733 
  734 FL enum okay
  735 cache_rename(const char *old, const char *new)
  736 {
  737    char *olddir, *newdir;
  738    enum okay rv = OKAY;
  739    NYD_ENTER;
  740 
  741    if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
  742          (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
  743       goto jleave;
  744    if (rename(olddir, newdir) < 0) {
  745       n_perr(olddir, 0);
  746       rv = STOP;
  747    }
  748 jleave:
  749    NYD_LEAVE;
  750    return rv;
  751 }
  752 
  753 FL unsigned long
  754 cached_uidvalidity(struct mailbox *mp)
  755 {
  756    FILE *uvfp;
  757    char *uvname;
  758    unsigned long uv;
  759    NYD_ENTER;
  760 
  761    if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
  762       uv = 0;
  763       goto jleave;
  764    }
  765    if ((uvfp = Fopen(uvname, "r")) == NULL ||
  766          (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
  767          fscanf(uvfp, "%lu", &uv) != 1)
  768       uv = 0;
  769    if (uvfp != NULL)
  770       Fclose(uvfp);
  771 jleave:
  772    NYD_LEAVE;
  773    return uv;
  774 }
  775 
  776 static FILE *
  777 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
  778 {
  779    char *name;
  780    FILE *fp = NULL;
  781    NYD_ENTER;
  782 
  783    if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
  784       goto jleave;
  785    if ((fp = Fopen(name, mode)) != NULL)
  786       n_file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
  787    if (xname)
  788       *xname = name;
  789 jleave:
  790    NYD_LEAVE;
  791    return fp;
  792 }
  793 
  794 FL FILE *
  795 cache_queue(struct mailbox *mp)
  796 {
  797    FILE *fp;
  798    NYD_ENTER;
  799 
  800    fp = cache_queue1(mp, "a", NULL);
  801    if (fp == NULL)
  802       n_err(_("Cannot queue IMAP command. Retry when online.\n"));
  803    NYD_LEAVE;
  804    return fp;
  805 }
  806 
  807 FL enum okay
  808 cache_dequeue(struct mailbox *mp)
  809 {
  810    int bufsz;
  811    char *cachedir, *eaccount, *buf, *oldbox;
  812    DIR *dirp;
  813    struct dirent *dp;
  814    enum okay rv = OKAY;
  815    NYD_ENTER;
  816 
  817    if ((cachedir = ok_vlook(imap_cache)) == NULL ||
  818          (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
  819       goto jleave;
  820    eaccount = urlxenc(mp->mb_imap_account, TRU1);
  821    buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
  822    snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
  823    if ((dirp = opendir(buf)) == NULL)
  824       goto jleave;
  825    oldbox = mp->mb_imap_mailbox;
  826    while ((dp = readdir(dirp)) != NULL) {
  827       if (dp->d_name[0] == '.')
  828          continue;
  829       /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
  830        * (but wuuuuh, what a shit!) */
  831       mp->mb_imap_mailbox = sstrdup(
  832             imap_path_decode(urlxdec(dp->d_name), NULL));
  833       dequeue1(mp);
  834       {  char *x = mp->mb_imap_mailbox;
  835          mp->mb_imap_mailbox = oldbox;
  836          free(x);
  837       }
  838    }
  839    closedir(dirp);
  840 jleave:
  841    NYD_LEAVE;
  842    return rv;
  843 }
  844 
  845 static enum okay
  846 dequeue1(struct mailbox *mp)
  847 {
  848    FILE *fp = NULL, *uvfp = NULL;
  849    char *qname, *uvname;
  850    unsigned long uv;
  851    off_t is_size;
  852    int is_count;
  853    enum okay rv = OKAY;
  854    NYD_ENTER;
  855 
  856    fp = cache_queue1(mp, "r+", &qname);
  857    if (fp != NULL && fsize(fp) > 0) {
  858       if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
  859             != OKAY) {
  860          n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
  861          goto jsave;
  862       }
  863       if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
  864             (uvfp = Fopen(uvname, "r")) == NULL ||
  865             (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
  866             fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
  867          n_err(_("Unique identifiers for \"%s\" are out of date. "
  868             "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
  869 jsave:
  870          n_err(_("Saving IMAP commands to *DEAD*\n"));
  871          savedeadletter(fp, 0);
  872          ftruncate(fileno(fp), 0);
  873          Fclose(fp);
  874          if (uvfp)
  875             Fclose(uvfp);
  876          rv = STOP;
  877          goto jleave;
  878       }
  879       Fclose(uvfp);
  880       printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
  881       imap_dequeue(mp, fp);
  882    }
  883    if (fp) {
  884       Fclose(fp);
  885       unlink(qname);
  886    }
  887 jleave:
  888    NYD_LEAVE;
  889    return rv;
  890 }
  891 #endif /* HAVE_IMAP */
  892 
  893 /* s-it-mode */