sendout.c:__savemail(): try to file_lock() *record*
[s-mailx.git] / maildir.c
blobbf66ffa52cc2c30b9d2bda55ac5cfa9f2d25cd04
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Maildir folder support. FIXME rewrite - why do we chdir(2)??
3 *@ FIXME indeed - my S-Postman Python (!) is faster dealing with maildir!!
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
7 */
8 /*
9 * Copyright (c) 2004
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
40 #undef n_FILE
41 #define n_FILE maildir
43 #ifndef HAVE_AMALGAMATION
44 # include "nail.h"
45 #endif
47 #include <dirent.h>
49 struct mditem {
50 struct message *md_data;
51 unsigned md_hash;
54 static struct mditem *_maildir_table;
55 static ui32_t _maildir_prime;
56 static sigjmp_buf _maildir_jmp;
58 static void __maildircatch(int s);
59 static void __maildircatch_hold(int s);
61 /* Do some cleanup in the tmp/ subdir */
62 static void _cleantmp(void);
64 static int _maildir_setfile1(char const *name, enum fedit_mode fm,
65 int omsgCount);
67 /* In combination with the names from mkname(), this comparison function
68 * ensures that the order of messages in a maildir folder created by mailx
69 * remains always the same. In effect, if a mbox folder is transferred to
70 * a maildir folder by 'copy *', the message order wont' change */
71 static int mdcmp(void const *a, void const *b);
73 static int _maildir_subdir(char const *name, char const *sub,
74 enum fedit_mode fm);
76 static void _maildir_append(char const *name, char const *sub,
77 char const *fn);
79 static void readin(char const *name, struct message *m);
81 static void maildir_update(void);
83 static void _maildir_move(struct message *m);
85 static char * mkname(time_t t, enum mflag f, char const *pref);
87 static enum okay maildir_append1(char const *name, FILE *fp, off_t off1,
88 long size, enum mflag flag);
90 static enum okay trycreate(char const *name);
92 static enum okay mkmaildir(char const *name);
94 static struct message * mdlook(char const *name, struct message *data);
96 static void mktable(void);
98 static enum okay subdir_remove(char const *name, char const *sub);
100 static void
101 __maildircatch(int s)
103 NYD_X; /* Signal handler */
104 siglongjmp(_maildir_jmp, s);
107 static void
108 __maildircatch_hold(int s)
110 NYD_X; /* Signal handler */
111 UNUSED(s);
112 /* TODO no STDIO in signal handler, no _() tr's -- pre-translate interrupt
113 * TODO globally; */
114 n_err_sighdl(_("\nImportant operation in progress: "
115 "interrupt again to forcefully abort\n"));
116 safe_signal(SIGINT, &__maildircatch);
119 static void
120 _cleantmp(void)
122 char dep[PATH_MAX];
123 struct stat st;
124 time_t now;
125 DIR *dirp;
126 struct dirent *dp;
127 NYD_ENTER;
129 if ((dirp = opendir("tmp")) == NULL)
130 goto jleave;
132 now = n_time_epoch();
133 while ((dp = readdir(dirp)) != NULL) {
134 if (dp->d_name[0] == '.')
135 continue;
136 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
137 if (stat(dep, &st) == -1)
138 continue;
139 if (st.st_atime + 36*3600 < now)
140 unlink(dep);
142 closedir(dirp);
143 jleave:
144 NYD_LEAVE;
147 static int
148 _maildir_setfile1(char const *name, enum fedit_mode fm, int omsgCount)
150 int i;
151 NYD_ENTER;
153 if (!(fm & FEDIT_NEWMAIL))
154 _cleantmp();
156 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
157 if ((i = _maildir_subdir(name, "cur", fm)) != 0)
158 goto jleave;
159 if ((i = _maildir_subdir(name, "new", fm)) != 0)
160 goto jleave;
161 _maildir_append(name, NULL, NULL);
163 srelax_hold();
164 for (i = ((fm & FEDIT_NEWMAIL) ? omsgCount : 0); i < msgCount; ++i) {
165 readin(name, message + i);
166 srelax();
168 srelax_rele();
170 if (fm & FEDIT_NEWMAIL) {
171 if (msgCount > omsgCount)
172 qsort(&message[omsgCount], msgCount - omsgCount, sizeof *message,
173 &mdcmp);
174 } else if (msgCount)
175 qsort(message, msgCount, sizeof *message, &mdcmp);
176 i = msgCount;
177 jleave:
178 NYD_LEAVE;
179 return i;
182 static int
183 mdcmp(void const *a, void const *b)
185 struct message const *mpa = a, *mpb = b;
186 long i;
187 NYD_ENTER;
189 if ((i = mpa->m_time - mpb->m_time) == 0)
190 i = strcmp(mpa->m_maildir_file + 4, mpb->m_maildir_file + 4);
191 NYD_LEAVE;
192 return i;
195 static int
196 _maildir_subdir(char const *name, char const *sub, enum fedit_mode fm)
198 DIR *dirp;
199 struct dirent *dp;
200 int rv;
201 NYD_ENTER;
203 if ((dirp = opendir(sub)) == NULL) {
204 n_err(_("Cannot open directory \"%s/%s\"\n"), name, sub);
205 rv = -1;
206 goto jleave;
208 if (access(sub, W_OK) == -1)
209 mb.mb_perm = 0;
210 while ((dp = readdir(dirp)) != NULL) {
211 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
212 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
213 continue;
214 if (dp->d_name[0] == '.')
215 continue;
216 if (!(fm & FEDIT_NEWMAIL) || mdlook(dp->d_name, NULL) == NULL)
217 _maildir_append(name, sub, dp->d_name);
219 closedir(dirp);
220 rv = 0;
221 jleave:
222 NYD_LEAVE;
223 return rv;
226 static void
227 _maildir_append(char const *name, char const *sub, char const *fn)
229 struct message *m;
230 size_t sz, i;
231 time_t t = 0;
232 enum mflag f = MUSED | MNOFROM | MNEWEST;
233 char const *cp;
234 char *xp;
235 NYD_ENTER;
236 UNUSED(name);
238 if (fn != NULL && sub != NULL) {
239 if (!strcmp(sub, "new"))
240 f |= MNEW;
241 t = strtol(fn, &xp, 10);
242 if ((cp = strrchr(xp, ',')) != NULL && PTRCMP(cp, >, xp + 2) &&
243 cp[-1] == '2' && cp[-2] == ':') {
244 while (*++cp != '\0') {
245 switch (*cp) {
246 case 'F':
247 f |= MFLAGGED;
248 break;
249 case 'R':
250 f |= MANSWERED;
251 break;
252 case 'S':
253 f |= MREAD;
254 break;
255 case 'T':
256 f |= MDELETED;
257 break;
258 case 'D':
259 f |= MDRAFT;
260 break;
266 /* Ensure room (and a NULLified last entry) */
267 ++msgCount;
268 message_append(NULL);
269 --msgCount;
271 if (fn == NULL || sub == NULL)
272 goto jleave;
274 m = message + msgCount++;
275 i = strlen(fn);
276 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 1 +1);
277 memcpy(m->m_maildir_file, sub, sz);
278 m->m_maildir_file[sz] = '/';
279 memcpy(m->m_maildir_file + sz + 1, fn, i +1);
280 m->m_time = t;
281 m->m_flag = f;
282 m->m_maildir_hash = ~pjw(fn);
283 jleave:
284 NYD_LEAVE;
285 return;
288 static void
289 readin(char const *name, struct message *m)
291 char *buf;
292 size_t bufsize, buflen, cnt;
293 long size = 0, lines = 0;
294 off_t offset;
295 FILE *fp;
296 int emptyline = 0;
297 NYD_ENTER;
299 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
300 n_err(_("Cannot read \"%s/%s\" for message %lu\n"),
301 name, m->m_maildir_file, (ul_i)PTR2SIZE(m - message + 1));
302 m->m_flag |= MHIDDEN;
303 goto jleave;
306 cnt = fsize(fp);
307 fseek(mb.mb_otf, 0L, SEEK_END);
308 offset = ftell(mb.mb_otf);
309 buf = smalloc(bufsize = LINESIZE);
310 buflen = 0;
311 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
312 /* Since we simply copy over data without doing any transfer
313 * encoding reclassification/adjustment we *have* to perform
314 * RFC 4155 compliant From_ quoting here */
315 if (emptyline && is_head(buf, buflen, TRU1)) {
316 putc('>', mb.mb_otf);
317 ++size;
319 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
320 emptyline = (*buf == '\n');
321 ++lines;
323 free(buf);
324 if (!emptyline) {
325 putc('\n', mb.mb_otf);
326 ++lines;
327 ++size;
330 Fclose(fp);
331 fflush(mb.mb_otf);
332 m->m_size = m->m_xsize = size;
333 m->m_lines = m->m_xlines = lines;
334 m->m_block = mailx_blockof(offset);
335 m->m_offset = mailx_offsetof(offset);
336 substdate(m);
337 jleave:
338 NYD_LEAVE;
341 static void
342 maildir_update(void)
344 struct message *m;
345 int dodel, c, gotcha = 0, held = 0, modflags = 0;
346 NYD_ENTER;
348 if (mb.mb_perm == 0)
349 goto jfree;
351 if (!(pstate & PS_EDIT)) {
352 holdbits();
353 for (m = message, c = 0; PTRCMP(m, <, message + msgCount); ++m) {
354 if (m->m_flag & MBOX)
355 c++;
357 if (c > 0)
358 if (makembox() == STOP)
359 goto jbypass;
361 for (m = message, gotcha = 0, held = 0; PTRCMP(m, <, message + msgCount);
362 ++m) {
363 if (pstate & PS_EDIT)
364 dodel = m->m_flag & MDELETED;
365 else
366 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
367 if (dodel) {
368 if (unlink(m->m_maildir_file) < 0)
369 n_err(_("Cannot delete file \"%s/%s\" for message %lu\n"),
370 mailname, m->m_maildir_file, (ul_i)PTR2SIZE(m - message + 1));
371 else
372 ++gotcha;
373 } else {
374 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS) ||
375 (m->m_flag & (MNEW | MBOXED | MSAVED | MSTATUS | MFLAG |
376 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))) {
377 _maildir_move(m);
378 ++modflags;
380 ++held;
383 jbypass:
384 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
385 printf(_("\"%s\" "), displayname);
386 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
387 ? _("complete\n") : _("updated.\n"));
388 } else if (held && !(pstate & PS_EDIT) && mb.mb_perm != 0) {
389 if (held == 1)
390 printf(_("Held 1 message in %s\n"), displayname);
391 else
392 printf(_("Held %d messages in %s\n"), held, displayname);
394 fflush(stdout);
395 jfree:
396 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
397 free(m->m_maildir_file);
398 NYD_LEAVE;
401 static void
402 _maildir_move(struct message *m)
404 char *fn, *new;
405 NYD_ENTER;
407 fn = mkname(0, m->m_flag, m->m_maildir_file + 4);
408 new = savecat("cur/", fn);
409 if (!strcmp(m->m_maildir_file, new))
410 goto jleave;
411 if (link(m->m_maildir_file, new) == -1) {
412 n_err(_("Cannot link \"%s/%s\" to \"%s/%s\": "
413 "message %lu not touched\n"),
414 mailname, m->m_maildir_file, mailname, new,
415 (ul_i)PTR2SIZE(m - message + 1));
416 goto jleave;
418 if (unlink(m->m_maildir_file) == -1)
419 n_err(_("Cannot unlink \"%s/%s\"\n"), mailname, m->m_maildir_file);
420 jleave:
421 NYD_LEAVE;
424 static char *
425 mkname(time_t t, enum mflag f, char const *pref)
427 static unsigned long cnt;
428 static pid_t mypid; /* XXX This should possibly be global, somehow */
429 static char *node;
431 char *cp;
432 int size, n, i;
433 NYD_ENTER;
435 if (pref == NULL) {
436 if (mypid == 0)
437 mypid = getpid();
438 if (node == NULL) {
439 cp = nodename(0);
440 n = size = 0;
441 do {
442 if (UICMP(32, n, <, size + 8))
443 node = srealloc(node, size += 20);
444 switch (*cp) {
445 case '/':
446 node[n++] = '\\', node[n++] = '0',
447 node[n++] = '5', node[n++] = '7';
448 break;
449 case ':':
450 node[n++] = '\\', node[n++] = '0',
451 node[n++] = '7', node[n++] = '2';
452 break;
453 default:
454 node[n++] = *cp;
456 } while (*cp++ != '\0');
458 size = 60 + strlen(node);
459 cp = salloc(size);
460 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
461 (ul_i)t, (ul_i)mypid, ++cnt, node);
462 } else {
463 size = (n = strlen(pref)) + 13;
464 cp = salloc(size);
465 memcpy(cp, pref, n +1);
466 for (i = n; i > 3; --i)
467 if (cp[i - 1] == ',' && cp[i - 2] == '2' && cp[i - 3] == ':') {
468 n = i;
469 break;
471 if (i <= 3) {
472 memcpy(cp + n, ":2,", 3 +1);
473 n += 3;
476 if (n < size - 7) {
477 if (f & MDRAFTED)
478 cp[n++] = 'D';
479 if (f & MFLAGGED)
480 cp[n++] = 'F';
481 if (f & MANSWERED)
482 cp[n++] = 'R';
483 if (f & MREAD)
484 cp[n++] = 'S';
485 if (f & MDELETED)
486 cp[n++] = 'T';
487 cp[n] = '\0';
489 NYD_LEAVE;
490 return cp;
493 static enum okay
494 maildir_append1(char const *name, FILE *fp, off_t off1, long size,
495 enum mflag flag)
497 char buf[4096], *fn, *tfn, *nfn;
498 struct stat st;
499 FILE *op;
500 time_t now;
501 size_t nlen, flen, n;
502 enum okay rv = STOP;
503 NYD_ENTER;
505 nlen = strlen(name);
507 /* Create a unique temporary file */
508 for (nfn = (char*)0xA /* XXX no magic */;; n_msleep(500, FAL0)) {
509 now = n_time_epoch();
510 flen = strlen(fn = mkname(now, flag, NULL));
511 tfn = salloc(n = nlen + flen + 6);
512 snprintf(tfn, n, "%s/tmp/%s", name, fn);
514 /* Use "wx" for O_EXCL XXX stat(2) rather redundant; coverity:TOCTOU */
515 if ((!stat(tfn, &st) || errno == ENOENT) &&
516 (op = Fopen(tfn, "wx")) != NULL)
517 break;
519 nfn = (char*)(PTR2SIZE(nfn) - 1);
520 if (nfn == NULL) {
521 n_err(_("Can't create an unique file name in \"%s/tmp\"\n"), name);
522 goto jleave;
526 if (fseek(fp, off1, SEEK_SET) == -1)
527 goto jtmperr;
528 while (size > 0) {
529 size_t z = UICMP(z, size, >, sizeof buf) ? sizeof buf : (size_t)size;
531 if (z != (n = fread(buf, 1, z, fp)) || n != fwrite(buf, 1, n, op)) {
532 jtmperr:
533 n_err(_("Error writing to \"%s\"\n"), tfn);
534 Fclose(op);
535 goto jerr;
537 size -= n;
539 Fclose(op);
541 nfn = salloc(n = nlen + flen + 6);
542 snprintf(nfn, n, "%s/new/%s", name, fn);
543 if (link(tfn, nfn) == -1) {
544 n_err(_("Cannot link \"%s\" to \"%s\"\n"), tfn, nfn);
545 goto jerr;
547 rv = OKAY;
548 jerr:
549 if (unlink(tfn) == -1)
550 n_err(_("Cannot unlink \"%s\"\n"), tfn);
551 jleave:
552 NYD_LEAVE;
553 return rv;
556 static enum okay
557 trycreate(char const *name)
559 struct stat st;
560 enum okay rv = STOP;
561 NYD_ENTER;
563 if (!stat(name, &st)) {
564 if (!S_ISDIR(st.st_mode)) {
565 n_err(_("\"%s\" is not a directory\n"), name);
566 goto jleave;
568 } else if (!n_path_mkdir(name)) {
569 n_err(_("Cannot create directory \"%s\"\n"), name);
570 goto jleave;
572 rv = OKAY;
573 jleave:
574 NYD_LEAVE;
575 return rv;
578 static enum okay
579 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
581 char *np;
582 size_t sz;
583 enum okay rv = STOP;
584 NYD_ENTER;
586 if (trycreate(name) == OKAY) {
587 np = ac_alloc((sz = strlen(name)) + 4 +1);
588 memcpy(np, name, sz);
589 memcpy(np + sz, "/tmp", 4 +1);
590 if (trycreate(np) == OKAY) {
591 memcpy(np + sz, "/new", 4 +1);
592 if (trycreate(np) == OKAY) {
593 memcpy(np + sz, "/cur", 4 +1);
594 rv = trycreate(np);
597 ac_free(np);
599 NYD_LEAVE;
600 return rv;
603 static struct message *
604 mdlook(char const *name, struct message *data)
606 struct mditem *md;
607 ui32_t c, h, n = 0;
608 NYD_ENTER;
610 if (data && data->m_maildir_hash)
611 h = ~data->m_maildir_hash;
612 else
613 h = pjw(name);
614 h %= _maildir_prime;
615 c = h;
616 md = _maildir_table + c;
618 while (md->md_data != NULL) {
619 if (!strcmp(md->md_data->m_maildir_file + 4, name))
620 break;
621 c += (n & 1) ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
622 n++;
623 while (c >= _maildir_prime)
624 c -= _maildir_prime;
625 md = _maildir_table + c;
627 if (data != NULL && md->md_data == NULL)
628 md->md_data = data;
629 NYD_LEAVE;
630 return md->md_data;
633 static void
634 mktable(void)
636 struct message *mp;
637 size_t i;
638 NYD_ENTER;
640 _maildir_prime = nextprime(msgCount);
641 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
642 for (mp = message, i = msgCount; i-- != 0; ++mp)
643 mdlook(mp->m_maildir_file + 4, mp);
644 NYD_LEAVE;
647 static enum okay
648 subdir_remove(char const *name, char const *sub)
650 char *path;
651 int pathsize, pathend, namelen, sublen, n;
652 DIR *dirp;
653 struct dirent *dp;
654 enum okay rv = STOP;
655 NYD_ENTER;
657 namelen = strlen(name);
658 sublen = strlen(sub);
659 path = smalloc(pathsize = namelen + sublen + 30 +1);
660 memcpy(path, name, namelen);
661 path[namelen] = '/';
662 memcpy(path + namelen + 1, sub, sublen);
663 path[namelen + sublen + 1] = '/';
664 path[pathend = namelen + sublen + 2] = '\0';
666 if ((dirp = opendir(path)) == NULL) {
667 n_perr(path, 0);
668 goto jleave;
670 while ((dp = readdir(dirp)) != NULL) {
671 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
672 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
673 continue;
674 if (dp->d_name[0] == '.')
675 continue;
676 n = strlen(dp->d_name);
677 if (UICMP(32, pathend + n +1, >, pathsize))
678 path = srealloc(path, pathsize = pathend + n + 30);
679 memcpy(path + pathend, dp->d_name, n +1);
680 if (unlink(path) == -1) {
681 n_perr(path, 0);
682 closedir(dirp);
683 goto jleave;
686 closedir(dirp);
688 path[pathend] = '\0';
689 if (rmdir(path) == -1) {
690 n_perr(path, 0);
691 goto jleave;
693 rv = OKAY;
694 jleave:
695 free(path);
696 NYD_LEAVE;
697 return rv;
700 FL int
701 maildir_setfile(char const * volatile name, enum fedit_mode fm)
703 sighandler_type volatile saveint;
704 struct cw cw;
705 int i = -1, omsgCount;
706 NYD_ENTER;
708 omsgCount = msgCount;
709 if (cwget(&cw) == STOP) {
710 n_alert(_("Cannot open current directory"));
711 goto jleave;
714 if (!(fm & FEDIT_NEWMAIL) && !quit())
715 goto jleave;
717 saveint = safe_signal(SIGINT, SIG_IGN);
719 if (!(fm & FEDIT_NEWMAIL)) {
720 if (fm & FEDIT_SYSBOX)
721 pstate &= ~PS_EDIT;
722 else
723 pstate |= PS_EDIT;
724 if (mb.mb_itf) {
725 fclose(mb.mb_itf);
726 mb.mb_itf = NULL;
728 if (mb.mb_otf) {
729 fclose(mb.mb_otf);
730 mb.mb_otf = NULL;
732 initbox(name);
733 mb.mb_type = MB_MAILDIR;
736 if (chdir(name) < 0) {
737 n_err(_("Cannot change directory to \"%s\"\n"), name);
738 mb.mb_type = MB_VOID;
739 *mailname = '\0';
740 msgCount = 0;
741 cwrelse(&cw);
742 safe_signal(SIGINT, saveint);
743 goto jleave;
746 _maildir_table = NULL;
747 if (sigsetjmp(_maildir_jmp, 1) == 0) {
748 if (fm & FEDIT_NEWMAIL)
749 mktable();
750 if (saveint != SIG_IGN)
751 safe_signal(SIGINT, &__maildircatch);
752 i = _maildir_setfile1(name, fm, omsgCount);
754 if ((fm & FEDIT_NEWMAIL) && _maildir_table != NULL)
755 free(_maildir_table);
757 safe_signal(SIGINT, saveint);
759 if (i < 0) {
760 mb.mb_type = MB_VOID;
761 *mailname = '\0';
762 msgCount = 0;
765 if (cwret(&cw) == STOP)
766 n_panic(_("Cannot change back to current directory"));
767 cwrelse(&cw);
769 setmsize(msgCount);
770 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted && msgCount > omsgCount) {
771 mb.mb_threaded = 0;
772 c_sort((void*)-1);
775 if (!(fm & FEDIT_NEWMAIL)) {
776 pstate &= ~PS_SAW_COMMAND;
777 pstate |= PS_SETFILE_OPENED;
780 if ((options & OPT_EXISTONLY) && !(options & OPT_HEADERLIST)) {
781 i = (msgCount == 0);
782 goto jleave;
785 if (!(fm & FEDIT_NEWMAIL) && (fm & FEDIT_SYSBOX) && msgCount == 0) {
786 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
787 n_err(_("No mail at \"%s\"\n"), name);
788 i = 1;
789 goto jleave;
792 if ((fm & FEDIT_NEWMAIL) && msgCount > omsgCount)
793 newmailinfo(omsgCount);
794 i = 0;
795 jleave:
796 NYD_LEAVE;
797 return i;
800 FL bool_t
801 maildir_quit(void)
803 sighandler_type saveint;
804 struct cw cw;
805 bool_t rv;
806 NYD_ENTER;
808 rv = FAL0;
810 if (cwget(&cw) == STOP) {
811 n_alert(_("Cannot open current directory"));
812 goto jleave;
815 saveint = safe_signal(SIGINT, SIG_IGN);
817 if (chdir(mailname) == -1) {
818 n_err(_("Cannot change directory to \"%s\"\n"), mailname);
819 cwrelse(&cw);
820 safe_signal(SIGINT, saveint);
821 goto jleave;
824 if (sigsetjmp(_maildir_jmp, 1) == 0) {
825 if (saveint != SIG_IGN)
826 safe_signal(SIGINT, &__maildircatch_hold);
827 maildir_update();
830 safe_signal(SIGINT, saveint);
832 if (cwret(&cw) == STOP)
833 n_panic(_("Cannot change back to current directory"));
834 cwrelse(&cw);
835 rv = TRU1;
836 jleave:
837 NYD_LEAVE;
838 return rv;
841 FL enum okay
842 maildir_append(char const *name, FILE *fp, long offset)
844 char *buf, *bp, *lp;
845 size_t bufsize, buflen, cnt;
846 off_t off1 = -1, offs;
847 long size;
848 int flag;
849 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
850 enum okay rv;
851 NYD_ENTER;
853 if ((rv = mkmaildir(name)) != OKAY)
854 goto jleave;
856 buf = smalloc(bufsize = LINESIZE); /* TODO line pool; signals */
857 buflen = 0;
858 cnt = fsize(fp);
859 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
860 size = 0;
862 srelax_hold();
863 for (flag = MNEW, state = _NLSEP;;) {
864 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
866 if (bp == NULL ||
867 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
868 is_head(buf, buflen, FAL0))) {
869 if (off1 != (off_t)-1) {
870 if ((rv = maildir_append1(name, fp, off1, size, flag)) == STOP)
871 goto jfree;
872 srelax();
873 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
874 rv = STOP;
875 goto jfree;
878 off1 = offs + buflen;
879 size = 0;
880 state = _INHEAD;
881 flag = MNEW;
883 if (bp == NULL)
884 break;
885 } else
886 size += buflen;
887 offs += buflen;
889 state &= ~_NLSEP;
890 if (buf[0] == '\n') {
891 state &= ~_INHEAD;
892 state |= _NLSEP;
893 } else if (state & _INHEAD) {
894 if (!ascncasecmp(buf, "status", 6)) {
895 lp = buf + 6;
896 while (whitechar(*lp))
897 ++lp;
898 if (*lp == ':')
899 while (*++lp != '\0')
900 switch (*lp) {
901 case 'R':
902 flag |= MREAD;
903 break;
904 case 'O':
905 flag &= ~MNEW;
906 break;
908 } else if (!ascncasecmp(buf, "x-status", 8)) {
909 lp = buf + 8;
910 while (whitechar(*lp))
911 ++lp;
912 if (*lp == ':') {
913 while (*++lp != '\0')
914 switch (*lp) {
915 case 'F':
916 flag |= MFLAGGED;
917 break;
918 case 'A':
919 flag |= MANSWERED;
920 break;
921 case 'T':
922 flag |= MDRAFTED;
923 break;
929 assert(rv == OKAY);
930 jfree:
931 srelax_rele();
932 free(buf);
933 jleave:
934 NYD_LEAVE;
935 return rv;
938 FL enum okay
939 maildir_remove(char const *name)
941 enum okay rv = STOP;
942 NYD_ENTER;
944 if (subdir_remove(name, "tmp") == STOP ||
945 subdir_remove(name, "new") == STOP ||
946 subdir_remove(name, "cur") == STOP)
947 goto jleave;
948 if (rmdir(name) == -1) {
949 n_perr(name, 0);
950 goto jleave;
952 rv = OKAY;
953 jleave:
954 NYD_LEAVE;
955 return rv;
958 /* s-it-mode */