"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/maildir.c" (8 Aug 2018, 31311 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 "maildir.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  *@ Maildir folder support. FIXME rewrite - why do we chdir(2)??
    3  *@ FIXME Simply truncating paths isn't really it.
    4  *
    5  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    6  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    7  * SPDX-License-Identifier: BSD-4-Clause
    8  */
    9 /*
   10  * Copyright (c) 2004
   11  * Gunnar Ritter.  All rights reserved.
   12  *
   13  * Redistribution and use in source and binary forms, with or without
   14  * modification, are permitted provided that the following conditions
   15  * are met:
   16  * 1. Redistributions of source code must retain the above copyright
   17  *    notice, this list of conditions and the following disclaimer.
   18  * 2. Redistributions in binary form must reproduce the above copyright
   19  *    notice, this list of conditions and the following disclaimer in the
   20  *    documentation and/or other materials provided with the distribution.
   21  * 3. All advertising materials mentioning features or use of this software
   22  *    must display the following acknowledgement:
   23  *    This product includes software developed by Gunnar Ritter
   24  *    and his contributors.
   25  * 4. Neither the name of Gunnar Ritter nor the names of his contributors
   26  *    may be used to endorse or promote products derived from this software
   27  *    without specific prior written permission.
   28  *
   29  * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
   30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   32  * ARE DISCLAIMED.  IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
   33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   39  * SUCH DAMAGE.
   40  */
   41 #undef n_FILE
   42 #define n_FILE maildir
   43 
   44 #ifndef HAVE_AMALGAMATION
   45 # include "nail.h"
   46 #endif
   47 
   48 #ifdef HAVE_MAILDIR
   49 # include <dirent.h>
   50 #endif
   51 
   52 EMPTY_FILE()
   53 #ifdef HAVE_MAILDIR
   54 
   55 /* a_maildir_tbl should be a hash-indexed array of trees! */
   56 static struct message **a_maildir_tbl, **a_maildir_tbl_top;
   57 static ui32_t a_maildir_tbl_prime, a_maildir_tbl_maxdist;
   58 static sigjmp_buf    _maildir_jmp;
   59 
   60 static void             __maildircatch(int s);
   61 static void             __maildircatch_hold(int s);
   62 
   63 /* Do some cleanup in the tmp/ subdir */
   64 static void             _cleantmp(void);
   65 
   66 static int              _maildir_setfile1(char const *name, enum fedit_mode fm,
   67                            int omsgCount);
   68 
   69 static int a_maildir_cmp(void const *a, void const *b);
   70 
   71 static int              _maildir_subdir(char const *name, char const *sub,
   72                            enum fedit_mode fm);
   73 
   74 static void             _maildir_append(char const *name, char const *sub,
   75                            char const *fn);
   76 
   77 static void             readin(char const *name, struct message *m);
   78 
   79 static void             maildir_update(void);
   80 
   81 static void             _maildir_move(struct n_timespec const *tsp,
   82                            struct message *m);
   83 
   84 static char *           mkname(struct n_timespec const *tsp, enum mflag f,
   85                            char const *pref);
   86 
   87 static enum okay        maildir_append1(struct n_timespec const *tsp,
   88                            char const *name, FILE *fp, off_t off1,
   89                            long size, enum mflag flag);
   90 
   91 static enum okay        trycreate(char const *name);
   92 
   93 static enum okay        mkmaildir(char const *name);
   94 
   95 static struct message * mdlook(char const *name, struct message *data);
   96 
   97 static void             mktable(void);
   98 
   99 static enum okay        subdir_remove(char const *name, char const *sub);
  100 
  101 static void
  102 __maildircatch(int s)
  103 {
  104    NYD_X; /* Signal handler */
  105    siglongjmp(_maildir_jmp, s);
  106 }
  107 
  108 static void
  109 __maildircatch_hold(int s)
  110 {
  111    NYD_X; /* Signal handler */
  112    n_UNUSED(s);
  113    /* TODO no STDIO in signal handler, no _() tr's -- pre-translate interrupt
  114     * TODO globally; */
  115    n_err_sighdl(_("\nImportant operation in progress: "
  116       "interrupt again to forcefully abort\n"));
  117    safe_signal(SIGINT, &__maildircatch);
  118 }
  119 
  120 static void
  121 _cleantmp(void)
  122 {
  123    struct stat st;
  124    struct n_string s, *sp;
  125    si64_t now;
  126    DIR *dirp;
  127    struct dirent *dp;
  128    NYD_ENTER;
  129 
  130    if ((dirp = opendir("tmp")) == NULL)
  131       goto jleave;
  132 
  133    now = n_time_now(FAL0)->ts_sec;
  134    sp = n_string_creat_auto(&s);
  135 
  136    while ((dp = readdir(dirp)) != NULL) {
  137       if (dp->d_name[0] == '.')
  138          continue;
  139 
  140       sp = n_string_trunc(sp, 0);
  141       sp = n_string_push_buf(sp, "tmp/", sizeof("tmp/") -1);
  142       sp = n_string_push_cp(sp, dp->d_name);
  143       if (stat(n_string_cp(sp), &st) == -1)
  144          continue;
  145       if (st.st_atime + 36*3600 < now)
  146          unlink(sp->s_dat);
  147    }
  148    closedir(dirp);
  149 jleave:
  150    NYD_LEAVE;
  151 }
  152 
  153 static int
  154 _maildir_setfile1(char const *name, enum fedit_mode fm, int omsgCount)
  155 {
  156    int i;
  157    NYD_ENTER;
  158 
  159    if (!(fm & FEDIT_NEWMAIL))
  160       _cleantmp();
  161 
  162    mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY))
  163          ? 0 : MB_DELE;
  164    if ((i = _maildir_subdir(name, "cur", fm)) != 0)
  165       goto jleave;
  166    if ((i = _maildir_subdir(name, "new", fm)) != 0)
  167       goto jleave;
  168    _maildir_append(name, NULL, NULL);
  169 
  170    n_autorec_relax_create();
  171    for (i = ((fm & FEDIT_NEWMAIL) ? omsgCount : 0); i < msgCount; ++i) {
  172       readin(name, message + i);
  173       n_autorec_relax_unroll();
  174    }
  175    n_autorec_relax_gut();
  176 
  177    if (fm & FEDIT_NEWMAIL) {
  178       if (msgCount > omsgCount)
  179          qsort(&message[omsgCount], msgCount - omsgCount, sizeof *message,
  180             &a_maildir_cmp);
  181    } else if (msgCount)
  182       qsort(message, msgCount, sizeof *message, &a_maildir_cmp);
  183    i = msgCount;
  184 jleave:
  185    NYD_LEAVE;
  186    return i;
  187 }
  188 
  189 static int
  190 a_maildir_cmp(void const *xa, void const *xb){
  191    char const *cpa, *cpa_pid, *cpb, *cpb_pid;
  192    union {struct message const *mp; char const *cp;} a, b;
  193    si64_t at, bt;
  194    int rv;
  195    NYD2_ENTER;
  196 
  197    a.mp = xa;
  198    b.mp = xb;
  199 
  200    /* We could have parsed the time somewhen in the past, do a quick shot */
  201    at = (si64_t)a.mp->m_time;
  202    bt = (si64_t)b.mp->m_time;
  203    if(at != 0 && bt != 0 && (at -= bt) != 0)
  204       goto jret;
  205 
  206    /* Otherwise we need to parse the name */
  207    a.cp = &a.mp->m_maildir_file[4];
  208    b.cp = &b.mp->m_maildir_file[4];
  209 
  210    /* Interpret time stored in name, and use it for comparison */
  211    if(((n_idec_si64_cp(&at, a.cp, 10, &cpa)
  212             ) & n_IDEC_STATE_EMASK) != n_IDEC_STATE_EBASE || *cpa != '.' ||
  213          a.cp == cpa)
  214       goto jm1; /* Fishy */
  215    if(((n_idec_si64_cp(&bt, b.cp, 10, &cpb)
  216             ) & n_IDEC_STATE_EMASK) != n_IDEC_STATE_EBASE || *cpb != '.' ||
  217          b.cp == cpb)
  218       goto j1; /* Fishy */
  219 
  220    if((at -= bt) != 0)
  221       goto jret;
  222 
  223    /* If the seconds part does not work, go deeper.
  224     * We use de-facto standard "maildir — E-mail directory" from the Courier
  225     * mail server, also used by, e.g., Dovecot: sec.MusecPpid.hostname:2,flags.
  226     * However, a different name convention exists which uses
  227     * sec.pid_counter.hostname:2,flags.
  228     * First go for usec/counter, then pid */
  229 
  230    /* A: exact "standard"? */
  231    cpa_pid = NULL;
  232    a.cp = ++cpa;
  233    if((rv = *a.cp) == 'M')
  234       ;
  235    /* Known compat? */
  236    else if(digitchar(rv)){
  237       cpa_pid = a.cp++;
  238       while((rv = *a.cp) != '\0' && rv != '_')
  239          ++a.cp;
  240       if(rv == '\0')
  241          goto jm1; /* Fishy */
  242    }
  243    /* This is compatible to what dovecot does, it surely does not do so
  244     * for nothing, but i have no idea, but am too stupid to ask */
  245    else for(;; rv = *++a.cp){
  246       if(rv == 'M')
  247          break;
  248       if(rv == '\0' || rv == '.' || rv == n_MAILDIR_SEPARATOR)
  249          goto jm1; /* Fishy */
  250    }
  251    ++a.cp;
  252    if(((n_idec_si64_cp(&at, a.cp, 10, &cpa)
  253             ) & n_IDEC_STATE_EMASK) != n_IDEC_STATE_EBASE)
  254       goto jm1; /* Fishy */
  255 
  256    /* B: as above */
  257    cpb_pid = NULL;
  258    b.cp = ++cpb;
  259    if((rv = *b.cp) == 'M')
  260       ;
  261    else if(digitchar(rv)){
  262       cpb_pid = b.cp++;
  263       while((rv = *b.cp) != '\0' && rv != '_')
  264          ++b.cp;
  265       if(rv == '\0')
  266          goto j1;
  267    }else for(;; rv = *++b.cp){
  268       if(rv == 'M')
  269          break;
  270       if(rv == '\0' || rv == '.' || rv == n_MAILDIR_SEPARATOR)
  271          goto jm1;
  272    }
  273    ++b.cp;
  274    if(((n_idec_si64_cp(&bt, b.cp, 10, &cpb)
  275             ) & n_IDEC_STATE_EMASK) != n_IDEC_STATE_EBASE)
  276       goto j1;
  277 
  278    if((at -= bt) != 0)
  279       goto jret;
  280 
  281    /* So this gets hairy: sort by PID, then hostname */
  282    if(cpa_pid != NULL){
  283       a.cp = cpa_pid;
  284       xa = cpa;
  285    }else{
  286       a.cp = cpa;
  287       if(*a.cp++ != 'P')
  288          goto jm1; /* Fishy */
  289    }
  290    if(((n_idec_si64_cp(&at, a.cp, 10, &cpa)
  291             ) & n_IDEC_STATE_EMASK) != n_IDEC_STATE_EBASE)
  292       goto jm1; /* Fishy */
  293 
  294    if(cpb_pid != NULL){
  295       b.cp = cpb_pid;
  296       xb = cpb;
  297    }else{
  298       b.cp = cpb;
  299       if(*b.cp++ != 'P')
  300          goto j1; /* Fishy */
  301    }
  302    if(((n_idec_si64_cp(&bt, b.cp, 10, &cpb)
  303             ) & n_IDEC_STATE_EMASK) != n_IDEC_STATE_EBASE)
  304       goto jm1; /* Fishy */
  305 
  306    if((at -= bt) != 0)
  307       goto jret;
  308 
  309    /* Hostname */
  310    a.cp = (cpa_pid != NULL) ? xa : cpa;
  311    b.cp = (cpb_pid != NULL) ? xb : cpb;
  312    for(;; ++a.cp, ++b.cp){
  313       char ac, bc;
  314 
  315       ac = *a.cp;
  316       at = (ac != '\0' && ac != n_MAILDIR_SEPARATOR);
  317       bc = *b.cp;
  318       bt = (bc != '\0' && bc != n_MAILDIR_SEPARATOR);
  319       if((at -= bt) != 0)
  320          break;
  321       at = ac;
  322       if((at -= bc) != 0)
  323          break;
  324       if(ac == '\0')
  325          break;
  326    }
  327 
  328 jret:
  329    rv = (at == 0 ? 0 : (at < 0 ? -1 : 1));
  330 jleave:
  331    NYD2_LEAVE;
  332    return rv;
  333 jm1:
  334    rv = -1;
  335    goto jleave;
  336 j1:
  337    rv = 1;
  338    goto jleave;
  339 }
  340 
  341 static int
  342 _maildir_subdir(char const *name, char const *sub, enum fedit_mode fm)
  343 {
  344    DIR *dirp;
  345    struct dirent *dp;
  346    int rv;
  347    NYD_ENTER;
  348 
  349    if ((dirp = opendir(sub)) == NULL) {
  350       n_err(_("Cannot open directory %s\n"),
  351          n_shexp_quote_cp(savecatsep(name, '/', sub), FAL0));
  352       rv = -1;
  353       goto jleave;
  354    }
  355    if (access(sub, W_OK) == -1)
  356       mb.mb_perm = 0;
  357    while ((dp = readdir(dirp)) != NULL) {
  358       if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
  359             (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
  360          continue;
  361       if (dp->d_name[0] == '.')
  362          continue;
  363       if (!(fm & FEDIT_NEWMAIL) || mdlook(dp->d_name, NULL) == NULL)
  364          _maildir_append(name, sub, dp->d_name);
  365    }
  366    closedir(dirp);
  367    rv = 0;
  368 jleave:
  369    NYD_LEAVE;
  370    return rv;
  371 }
  372 
  373 static void
  374 _maildir_append(char const *name, char const *sub, char const *fn)
  375 {
  376    struct message *m;
  377    time_t t = 0;
  378    enum mflag f = MUSED | MNOFROM | MNEWEST;
  379    char const *cp, *xp;
  380    NYD_ENTER;
  381    n_UNUSED(name);
  382 
  383    if (fn != NULL && sub != NULL) {
  384       if (!strcmp(sub, "new"))
  385          f |= MNEW;
  386 
  387       /* C99 */{
  388          si64_t tib;
  389 
  390          (void)/*TODO*/n_idec_si64_cp(&tib, fn, 10, &xp);
  391          t = (time_t)tib;
  392       }
  393 
  394       if ((cp = strrchr(xp, ',')) != NULL && PTRCMP(cp, >, xp + 2) &&
  395             cp[-1] == '2' && cp[-2] == n_MAILDIR_SEPARATOR) {
  396          while (*++cp != '\0') {
  397             switch (*cp) {
  398             case 'F':
  399                f |= MFLAGGED;
  400                break;
  401             case 'R':
  402                f |= MANSWERED;
  403                break;
  404             case 'S':
  405                f |= MREAD;
  406                break;
  407             case 'T':
  408                f |= MDELETED;
  409                break;
  410             case 'D':
  411                f |= MDRAFT;
  412                break;
  413             }
  414          }
  415       }
  416    }
  417 
  418    /* Ensure room (and a NULLified last entry) */
  419    ++msgCount;
  420    message_append(NULL);
  421    --msgCount;
  422 
  423    if (fn == NULL || sub == NULL)
  424       goto jleave;
  425 
  426    m = &message[msgCount++];
  427    /* C99 */{
  428       char *tmp;
  429       size_t sz, i;
  430 
  431       i = strlen(fn) +1;
  432       sz = strlen(sub);
  433       m->m_maildir_file = tmp = n_alloc(sz + 1 + i);
  434       memcpy(tmp, sub, sz);
  435       tmp[sz++] = '/';
  436       memcpy(&tmp[sz], fn, i);
  437    }
  438    m->m_time = t;
  439    m->m_flag = f;
  440    m->m_maildir_hash = n_torek_hash(fn);
  441 jleave:
  442    NYD_LEAVE;
  443    return;
  444 }
  445 
  446 static void
  447 readin(char const *name, struct message *m)
  448 {
  449    char *buf;
  450    size_t bufsize, buflen, cnt;
  451    long size = 0, lines = 0;
  452    off_t offset;
  453    FILE *fp;
  454    int emptyline = 0;
  455    NYD_ENTER;
  456 
  457    if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
  458       n_err(_("Cannot read %s for message %lu\n"),
  459          n_shexp_quote_cp(savecatsep(name, '/', m->m_maildir_file), FAL0),
  460          (ul_i)PTR2SIZE(m - message + 1));
  461       m->m_flag |= MHIDDEN;
  462       goto jleave;
  463    }
  464 
  465    cnt = fsize(fp);
  466    fseek(mb.mb_otf, 0L, SEEK_END);
  467    offset = ftell(mb.mb_otf);
  468    buf = n_alloc(bufsize = LINESIZE);
  469    buflen = 0;
  470    while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
  471       /* Since we simply copy over data without doing any transfer
  472        * encoding reclassification/adjustment we *have* to perform
  473        * RFC 4155 compliant From_ quoting here */
  474       if (emptyline && is_head(buf, buflen, FAL0)) {
  475          putc('>', mb.mb_otf);
  476          ++size;
  477       }
  478       size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
  479       emptyline = (*buf == '\n');
  480       ++lines;
  481    }
  482    n_free(buf);
  483    if (!emptyline) {
  484       /* TODO we need \n\n for mbox format.
  485        * TODO That is to say we do it wrong here in order to get it right
  486        * TODO when send.c stuff or with MBOX handling, even though THIS
  487        * TODO line is solely a property of the MBOX database format! */
  488       putc('\n', mb.mb_otf);
  489       ++lines;
  490       ++size;
  491    }
  492 
  493    Fclose(fp);
  494    fflush(mb.mb_otf);
  495    m->m_size = m->m_xsize = size;
  496    m->m_lines = m->m_xlines = lines;
  497    m->m_block = mailx_blockof(offset);
  498    m->m_offset = mailx_offsetof(offset);
  499    substdate(m);
  500 jleave:
  501    NYD_LEAVE;
  502 }
  503 
  504 static void
  505 maildir_update(void)
  506 {
  507    struct message *m;
  508    struct n_timespec const *tsp;
  509    int dodel, c, gotcha = 0, held = 0, modflags = 0;
  510    NYD_ENTER;
  511 
  512    if (mb.mb_perm == 0)
  513       goto jfree;
  514 
  515    if (!(n_pstate & n_PS_EDIT)) {
  516       holdbits();
  517       for (m = message, c = 0; PTRCMP(m, <, message + msgCount); ++m) {
  518          if (m->m_flag & MBOX)
  519             c++;
  520       }
  521       if (c > 0)
  522          if (makembox() == STOP)
  523             goto jbypass;
  524    }
  525 
  526    tsp = n_time_now(TRU1); /* TODO FAL0, eventloop update! */
  527 
  528    n_autorec_relax_create();
  529    for (m = message, gotcha = 0, held = 0; PTRCMP(m, <, message + msgCount);
  530          ++m) {
  531       if (n_pstate & n_PS_EDIT)
  532          dodel = m->m_flag & MDELETED;
  533       else
  534          dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
  535       if (dodel) {
  536          if (unlink(m->m_maildir_file) < 0)
  537             n_err(_("Cannot delete file %s for message %lu\n"),
  538                n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file),
  539                   FAL0), (ul_i)PTR2SIZE(m - message + 1));
  540          else
  541             ++gotcha;
  542       } else {
  543          if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS) ||
  544                (m->m_flag & (MNEW | MBOXED | MSAVED | MSTATUS | MFLAG |
  545                MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))) {
  546             _maildir_move(tsp, m);
  547             n_autorec_relax_unroll();
  548             ++modflags;
  549          }
  550          ++held;
  551       }
  552    }
  553    n_autorec_relax_gut();
  554 
  555 jbypass:
  556    if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
  557       fprintf(n_stdout, "%s %s\n",
  558          n_shexp_quote_cp(displayname, FAL0),
  559          ((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
  560           ? _("complete") : _("updated.")));
  561    } else if (held && !(n_pstate & n_PS_EDIT) && mb.mb_perm != 0) {
  562       if (held == 1)
  563          fprintf(n_stdout, _("Held 1 message in %s\n"), displayname);
  564       else
  565          fprintf(n_stdout, _("Held %d messages in %s\n"), held, displayname);
  566    }
  567    fflush(n_stdout);
  568 jfree:
  569    for (m = message; PTRCMP(m, <, message + msgCount); ++m)
  570       n_free(n_UNCONST(m->m_maildir_file));
  571    NYD_LEAVE;
  572 }
  573 
  574 static void
  575 _maildir_move(struct n_timespec const *tsp, struct message *m)
  576 {
  577    char *fn, *newfn;
  578    NYD_ENTER;
  579 
  580    fn = mkname(tsp, m->m_flag, m->m_maildir_file + 4);
  581    newfn = savecat("cur/", fn);
  582    if (!strcmp(m->m_maildir_file, newfn))
  583       goto jleave;
  584    if (link(m->m_maildir_file, newfn) == -1) {
  585       n_err(_("Cannot link %s to %s: message %lu not touched\n"),
  586          n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file), FAL0),
  587          n_shexp_quote_cp(savecatsep(mailname, '/', newfn), FAL0),
  588          (ul_i)PTR2SIZE(m - message + 1));
  589       goto jleave;
  590    }
  591    if (unlink(m->m_maildir_file) == -1)
  592       n_err(_("Cannot unlink %s\n"),
  593          n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file), FAL0));
  594 jleave:
  595    NYD_LEAVE;
  596 }
  597 
  598 static char *
  599 mkname(struct n_timespec const *tsp, enum mflag f, char const *pref)
  600 {
  601    static char *node;
  602    static struct n_timespec ts;
  603 
  604    char *cp;
  605    int size, n, i;
  606    NYD_ENTER;
  607 
  608    if (pref == NULL) {
  609       si64_t s;
  610 
  611       if(n_pid == 0)
  612          n_pid = getpid();
  613 
  614       if (node == NULL) {
  615          cp = n_nodename(FAL0);
  616          n = size = 0;
  617          do {
  618             if (UICMP(32, n, <, size + 8))
  619                node = n_realloc(node, size += 20);
  620             switch (*cp) {
  621             case '/':
  622                node[n++] = '\\', node[n++] = '0',
  623                node[n++] = '5', node[n++] = '7';
  624                break;
  625             case ':':
  626                node[n++] = '\\', node[n++] = '0',
  627                node[n++] = '7', node[n++] = '2';
  628                break;
  629             default:
  630                node[n++] = *cp;
  631             }
  632          } while (*cp++ != '\0');
  633       }
  634 
  635       /* Problem: Courier spec uses microseconds, not nanoseconds */
  636       if((s = tsp->ts_sec) > ts.ts_sec){
  637          ts.ts_sec = s;
  638          ts.ts_nsec = tsp->ts_nsec / (n_DATE_NANOSSEC / n_DATE_MICROSSEC);
  639       }else{
  640          s = tsp->ts_nsec / (n_DATE_NANOSSEC / n_DATE_MICROSSEC);
  641          if(s <= ts.ts_nsec)
  642             s = ts.ts_nsec + 1;
  643          if(s < n_DATE_MICROSSEC)
  644             ts.ts_nsec = s;
  645          else{
  646             ++ts.ts_sec;
  647             ts.ts_nsec = 0;
  648          }
  649       }
  650 
  651       /* Create a name according to Courier spec */
  652       size = 60 + strlen(node);
  653       cp = n_autorec_alloc(size);
  654       n = snprintf(cp, size, "%" PRId64 ".M%" PRIdZ "P%ld.%s:2,",
  655             ts.ts_sec, ts.ts_nsec, (long)n_pid, node);
  656    } else {
  657       size = (n = strlen(pref)) + 13;
  658       cp = n_autorec_alloc(size);
  659       memcpy(cp, pref, n +1);
  660       for (i = n; i > 3; --i)
  661          if (cp[i - 1] == ',' && cp[i - 2] == '2' &&
  662                cp[i - 3] == n_MAILDIR_SEPARATOR) {
  663             n = i;
  664             break;
  665          }
  666       if (i <= 3) {
  667          memcpy(cp + n, ":2,", 3 +1);
  668          n += 3;
  669       }
  670    }
  671    if (n < size - 7) {
  672       if (f & MDRAFTED)
  673          cp[n++] = 'D';
  674       if (f & MFLAGGED)
  675          cp[n++] = 'F';
  676       if (f & MANSWERED)
  677          cp[n++] = 'R';
  678       if (f & MREAD)
  679          cp[n++] = 'S';
  680       if (f & MDELETED)
  681          cp[n++] = 'T';
  682       cp[n] = '\0';
  683    }
  684    NYD_LEAVE;
  685    return cp;
  686 }
  687 
  688 static enum okay
  689 maildir_append1(struct n_timespec const *tsp, char const *name, FILE *fp,
  690    off_t off1, long size, enum mflag flag)
  691 {
  692    char buf[4096], *fn, *tfn, *nfn;
  693    struct stat st;
  694    FILE *op;
  695    size_t nlen, flen, n;
  696    enum okay rv = STOP;
  697    NYD_ENTER;
  698 
  699    nlen = strlen(name);
  700 
  701    /* Create a unique temporary file */
  702    for (nfn = (char*)0xA /* XXX no magic */;; n_msleep(500, FAL0)) {
  703       flen = strlen(fn = mkname(tsp, flag, NULL));
  704       tfn = n_autorec_alloc(n = nlen + flen + 6);
  705       snprintf(tfn, n, "%s/tmp/%s", name, fn);
  706 
  707       /* Use "wx" for O_EXCL XXX stat(2) rather redundant; coverity:TOCTOU */
  708       if ((!stat(tfn, &st) || n_err_no == n_ERR_NOENT) &&
  709             (op = Fopen(tfn, "wx")) != NULL)
  710          break;
  711 
  712       nfn = (char*)(PTR2SIZE(nfn) - 1);
  713       if (nfn == NULL) {
  714          n_err(_("Can't create an unique file name in %s\n"),
  715             n_shexp_quote_cp(savecat(name, "/tmp"), FAL0));
  716          goto jleave;
  717       }
  718    }
  719 
  720    if (fseek(fp, off1, SEEK_SET) == -1)
  721       goto jtmperr;
  722    while (size > 0) {
  723       size_t z = UICMP(z, size, >, sizeof buf) ? sizeof buf : (size_t)size;
  724 
  725       if (z != (n = fread(buf, 1, z, fp)) || n != fwrite(buf, 1, n, op)) {
  726 jtmperr:
  727          n_err(_("Error writing to %s\n"), n_shexp_quote_cp(tfn, FAL0));
  728          Fclose(op);
  729          goto jerr;
  730       }
  731       size -= n;
  732    }
  733    Fclose(op);
  734 
  735    nfn = n_autorec_alloc(n = nlen + flen + 6);
  736    snprintf(nfn, n, "%s/new/%s", name, fn);
  737    if (link(tfn, nfn) == -1) {
  738       n_err(_("Cannot link %s to %s\n"), n_shexp_quote_cp(tfn, FAL0),
  739          n_shexp_quote_cp(nfn, FAL0));
  740       goto jerr;
  741    }
  742    rv = OKAY;
  743 jerr:
  744    if (unlink(tfn) == -1)
  745       n_err(_("Cannot unlink %s\n"), n_shexp_quote_cp(tfn, FAL0));
  746 jleave:
  747    NYD_LEAVE;
  748    return rv;
  749 }
  750 
  751 static enum okay
  752 trycreate(char const *name)
  753 {
  754    struct stat st;
  755    enum okay rv = STOP;
  756    NYD_ENTER;
  757 
  758    if (!stat(name, &st)) {
  759       if (!S_ISDIR(st.st_mode)) {
  760          n_err(_("%s is not a directory\n"), n_shexp_quote_cp(name, FAL0));
  761          goto jleave;
  762       }
  763    } else if (!n_path_mkdir(name)) {
  764       n_err(_("Cannot create directory %s\n"), n_shexp_quote_cp(name, FAL0));
  765       goto jleave;
  766    }
  767    rv = OKAY;
  768 jleave:
  769    NYD_LEAVE;
  770    return rv;
  771 }
  772 
  773 static enum okay
  774 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
  775 {
  776    char *np;
  777    size_t sz;
  778    enum okay rv = STOP;
  779    NYD_ENTER;
  780 
  781    if (trycreate(name) == OKAY) {
  782       np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
  783       memcpy(np, name, sz);
  784       memcpy(np + sz, "/tmp", 4 +1);
  785       if (trycreate(np) == OKAY) {
  786          memcpy(np + sz, "/new", 4 +1);
  787          if (trycreate(np) == OKAY) {
  788             memcpy(np + sz, "/cur", 4 +1);
  789             rv = trycreate(np);
  790          }
  791       }
  792       n_lofi_free(np);
  793    }
  794    NYD_LEAVE;
  795    return rv;
  796 }
  797 
  798 static struct message *
  799 mdlook(char const *name, struct message *data)
  800 {
  801    struct message **mpp, *mp;
  802    ui32_t h, i;
  803    NYD_ENTER;
  804 
  805    if(data != NULL)
  806       i = data->m_maildir_hash;
  807    else
  808       i = n_torek_hash(name);
  809    h = i;
  810    mpp = &a_maildir_tbl[i %= a_maildir_tbl_prime];
  811 
  812    for(i = 0;;){
  813       if((mp = *mpp) == NULL){
  814          if(n_UNLIKELY(data != NULL)){
  815             *mpp = mp = data;
  816             if(i > a_maildir_tbl_maxdist)
  817                a_maildir_tbl_maxdist = i;
  818          }
  819          break;
  820       }else if(mp->m_maildir_hash == h && !strcmp(&mp->m_maildir_file[4], name))
  821          break;
  822 
  823       if(n_UNLIKELY(mpp++ == a_maildir_tbl_top))
  824          mpp = a_maildir_tbl;
  825       if(++i > a_maildir_tbl_maxdist && n_UNLIKELY(data == NULL)){
  826          mp = NULL;
  827          break;
  828       }
  829    }
  830    NYD_LEAVE;
  831    return mp;
  832 }
  833 
  834 static void
  835 mktable(void)
  836 {
  837    struct message *mp;
  838    size_t i;
  839    NYD_ENTER;
  840 
  841    i = a_maildir_tbl_prime = msgCount;
  842    i <<= 1;
  843    do
  844       a_maildir_tbl_prime = n_prime_next(a_maildir_tbl_prime);
  845    while(a_maildir_tbl_prime < i);
  846    a_maildir_tbl = n_calloc(a_maildir_tbl_prime, sizeof *a_maildir_tbl);
  847    a_maildir_tbl_top = &a_maildir_tbl[a_maildir_tbl_prime - 1];
  848    a_maildir_tbl_maxdist = 0;
  849    for(mp = message, i = msgCount; i-- != 0; ++mp)
  850       mdlook(&mp->m_maildir_file[4], mp);
  851    NYD_LEAVE;
  852 }
  853 
  854 static enum okay
  855 subdir_remove(char const *name, char const *sub)
  856 {
  857    char *path;
  858    int pathsize, pathend, namelen, sublen, n;
  859    DIR *dirp;
  860    struct dirent *dp;
  861    enum okay rv = STOP;
  862    NYD_ENTER;
  863 
  864    namelen = strlen(name);
  865    sublen = strlen(sub);
  866    path = n_alloc(pathsize = namelen + sublen + 30 +1);
  867    memcpy(path, name, namelen);
  868    path[namelen] = '/';
  869    memcpy(path + namelen + 1, sub, sublen);
  870    path[namelen + sublen + 1] = '/';
  871    path[pathend = namelen + sublen + 2] = '\0';
  872 
  873    if ((dirp = opendir(path)) == NULL) {
  874       n_perr(path, 0);
  875       goto jleave;
  876    }
  877    while ((dp = readdir(dirp)) != NULL) {
  878       if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
  879             (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
  880          continue;
  881       if (dp->d_name[0] == '.')
  882          continue;
  883       n = strlen(dp->d_name);
  884       if (UICMP(32, pathend + n +1, >, pathsize))
  885          path = n_realloc(path, pathsize = pathend + n + 30);
  886       memcpy(path + pathend, dp->d_name, n +1);
  887       if (unlink(path) == -1) {
  888          n_perr(path, 0);
  889          closedir(dirp);
  890          goto jleave;
  891       }
  892    }
  893    closedir(dirp);
  894 
  895    path[pathend] = '\0';
  896    if (rmdir(path) == -1) {
  897       n_perr(path, 0);
  898       goto jleave;
  899    }
  900    rv = OKAY;
  901 jleave:
  902    n_free(path);
  903    NYD_LEAVE;
  904    return rv;
  905 }
  906 
  907 FL int
  908 maildir_setfile(char const *who, char const * volatile name,
  909    enum fedit_mode fm)
  910 {
  911    sighandler_type volatile saveint;
  912    struct cw cw;
  913    char const *emsg;
  914    int omsgCount;
  915    int volatile i = -1;
  916    NYD_ENTER;
  917 
  918    omsgCount = msgCount;
  919    if (cwget(&cw) == STOP) {
  920       n_alert(_("Cannot open current directory"));
  921       goto jleave;
  922    }
  923 
  924    if (!(fm & FEDIT_NEWMAIL) && !quit(FAL0))
  925       goto jleave;
  926 
  927    saveint = safe_signal(SIGINT, SIG_IGN);
  928 
  929    if (!(fm & FEDIT_NEWMAIL)) {
  930       if (fm & FEDIT_SYSBOX)
  931          n_pstate &= ~n_PS_EDIT;
  932       else
  933          n_pstate |= n_PS_EDIT;
  934       if (mb.mb_itf) {
  935          fclose(mb.mb_itf);
  936          mb.mb_itf = NULL;
  937       }
  938       if (mb.mb_otf) {
  939          fclose(mb.mb_otf);
  940          mb.mb_otf = NULL;
  941       }
  942       initbox(name);
  943       mb.mb_type = MB_MAILDIR;
  944    }
  945 
  946    if(!n_is_dir(name, FAL0)){
  947       emsg = N_("Not a maildir: %s\n");
  948       goto jerr;
  949    }else if(chdir(name) < 0){
  950       emsg = N_("Cannot enter maildir://%s\n");
  951 jerr:
  952       n_err(V_(emsg), n_shexp_quote_cp(name, FAL0));
  953       mb.mb_type = MB_VOID;
  954       *mailname = '\0';
  955       msgCount = 0;
  956       cwrelse(&cw);
  957       safe_signal(SIGINT, saveint);
  958       goto jleave;
  959    }
  960 
  961    a_maildir_tbl = NULL;
  962    if (sigsetjmp(_maildir_jmp, 1) == 0) {
  963       if (fm & FEDIT_NEWMAIL)
  964          mktable();
  965       if (saveint != SIG_IGN)
  966          safe_signal(SIGINT, &__maildircatch);
  967       i = _maildir_setfile1(name, fm, omsgCount);
  968    }
  969    if ((fm & FEDIT_NEWMAIL) && a_maildir_tbl != NULL)
  970       n_free(a_maildir_tbl);
  971 
  972    safe_signal(SIGINT, saveint);
  973 
  974    if (i < 0) {
  975       mb.mb_type = MB_VOID;
  976       *mailname = '\0';
  977       msgCount = 0;
  978    }
  979 
  980    if (cwret(&cw) == STOP)
  981       n_panic(_("Cannot change back to current directory"));
  982    cwrelse(&cw);
  983 
  984    setmsize(msgCount);
  985    if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted && msgCount > omsgCount) {
  986       mb.mb_threaded = 0;
  987       c_sort((void*)-1);
  988    }
  989 
  990    if (!(fm & FEDIT_NEWMAIL)) {
  991       n_pstate &= ~n_PS_SAW_COMMAND;
  992       n_pstate |= n_PS_SETFILE_OPENED;
  993    }
  994 
  995    if ((n_poption & n_PO_EXISTONLY) && !(n_poption & n_PO_HEADERLIST)) {
  996       i = (msgCount == 0);
  997       goto jleave;
  998    }
  999 
 1000    if (!(fm & FEDIT_NEWMAIL) && (fm & FEDIT_SYSBOX) && msgCount == 0) {
 1001       if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
 1002          n_err(_("No mail for %s at %s\n"), who, n_shexp_quote_cp(name, FAL0));
 1003       i = 1;
 1004       goto jleave;
 1005    }
 1006 
 1007    if ((fm & FEDIT_NEWMAIL) && msgCount > omsgCount)
 1008       newmailinfo(omsgCount);
 1009    i = 0;
 1010 jleave:
 1011    NYD_LEAVE;
 1012    return i;
 1013 }
 1014 
 1015 FL bool_t
 1016 maildir_quit(bool_t hold_sigs_on)
 1017 {
 1018    sighandler_type saveint;
 1019    struct cw cw;
 1020    bool_t rv;
 1021    NYD_ENTER;
 1022 
 1023    if(hold_sigs_on)
 1024       rele_sigs();
 1025 
 1026    rv = FAL0;
 1027 
 1028    if (cwget(&cw) == STOP) {
 1029       n_alert(_("Cannot open current directory"));
 1030       goto jleave;
 1031    }
 1032 
 1033    saveint = safe_signal(SIGINT, SIG_IGN);
 1034 
 1035    if (chdir(mailname) == -1) {
 1036       n_err(_("Cannot change directory to %s\n"),
 1037          n_shexp_quote_cp(mailname, FAL0));
 1038       cwrelse(&cw);
 1039       safe_signal(SIGINT, saveint);
 1040       goto jleave;
 1041    }
 1042 
 1043    if (sigsetjmp(_maildir_jmp, 1) == 0) {
 1044       if (saveint != SIG_IGN)
 1045          safe_signal(SIGINT, &__maildircatch_hold);
 1046       maildir_update();
 1047    }
 1048 
 1049    safe_signal(SIGINT, saveint);
 1050 
 1051    if (cwret(&cw) == STOP)
 1052       n_panic(_("Cannot change back to current directory"));
 1053    cwrelse(&cw);
 1054    rv = TRU1;
 1055 jleave:
 1056    if(hold_sigs_on)
 1057       hold_sigs();
 1058    NYD_LEAVE;
 1059    return rv;
 1060 }
 1061 
 1062 FL enum okay
 1063 maildir_append(char const *name, FILE *fp, long offset)
 1064 {
 1065    struct n_timespec const *tsp;
 1066    char *buf, *bp, *lp;
 1067    size_t bufsize, buflen, cnt;
 1068    off_t off1 = -1, offs;
 1069    long size;
 1070    int flag;
 1071    enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
 1072    enum okay rv;
 1073    NYD_ENTER;
 1074 
 1075    if ((rv = mkmaildir(name)) != OKAY)
 1076       goto jleave;
 1077 
 1078    buf = n_alloc(bufsize = LINESIZE); /* TODO line pool; signals */
 1079    buflen = 0;
 1080    cnt = fsize(fp);
 1081    offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
 1082    size = 0;
 1083    tsp = n_time_now(TRU1); /* TODO -> eventloop */
 1084 
 1085    n_autorec_relax_create();
 1086    for (flag = MNEW, state = _NLSEP;;) {
 1087       bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
 1088 
 1089       if (bp == NULL ||
 1090             ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
 1091              is_head(buf, buflen, FAL0))) {
 1092          if (off1 != (off_t)-1) {
 1093             if ((rv = maildir_append1(tsp, name, fp, off1, size, flag)) == STOP)
 1094                goto jfree;
 1095             n_autorec_relax_unroll();
 1096             if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
 1097                rv = STOP;
 1098                goto jfree;
 1099             }
 1100          }
 1101          off1 = offs + buflen;
 1102          size = 0;
 1103          state = _INHEAD;
 1104          flag = MNEW;
 1105 
 1106          if (bp == NULL)
 1107             break;
 1108       } else
 1109          size += buflen;
 1110       offs += buflen;
 1111 
 1112       state &= ~_NLSEP;
 1113       if (buf[0] == '\n') {
 1114          state &= ~_INHEAD;
 1115          state |= _NLSEP;
 1116       } else if (state & _INHEAD) {
 1117          if (!ascncasecmp(buf, "status", 6)) {
 1118             lp = buf + 6;
 1119             while (whitechar(*lp))
 1120                ++lp;
 1121             if (*lp == ':')
 1122                while (*++lp != '\0')
 1123                   switch (*lp) {
 1124                   case 'R':
 1125                      flag |= MREAD;
 1126                      break;
 1127                   case 'O':
 1128                      flag &= ~MNEW;
 1129                      break;
 1130                   }
 1131          } else if (!ascncasecmp(buf, "x-status", 8)) {
 1132             lp = buf + 8;
 1133             while (whitechar(*lp))
 1134                ++lp;
 1135             if (*lp == ':') {
 1136                while (*++lp != '\0')
 1137                   switch (*lp) {
 1138                   case 'F':
 1139                      flag |= MFLAGGED;
 1140                      break;
 1141                   case 'A':
 1142                      flag |= MANSWERED;
 1143                      break;
 1144                   case 'T':
 1145                      flag |= MDRAFTED;
 1146                      break;
 1147                   }
 1148             }
 1149          }
 1150       }
 1151    }
 1152    assert(rv == OKAY);
 1153 jfree:
 1154    n_autorec_relax_gut();
 1155    n_free(buf);
 1156 jleave:
 1157    NYD_LEAVE;
 1158    return rv;
 1159 }
 1160 
 1161 FL enum okay
 1162 maildir_remove(char const *name)
 1163 {
 1164    enum okay rv = STOP;
 1165    NYD_ENTER;
 1166 
 1167    if (subdir_remove(name, "tmp") == STOP ||
 1168          subdir_remove(name, "new") == STOP ||
 1169          subdir_remove(name, "cur") == STOP)
 1170       goto jleave;
 1171    if (rmdir(name) == -1) {
 1172       n_perr(name, 0);
 1173       goto jleave;
 1174    }
 1175    rv = OKAY;
 1176 jleave:
 1177    NYD_LEAVE;
 1178    return rv;
 1179 }
 1180 #endif /* HAVE_MAILDIR */
 1181 
 1182 /* s-it-mode */