Fix false resource release <-> double free (Bob Tennent)..
[s-mailx.git] / maildir.c
blob4ecd9cf7e5d172dfe6e06128271652e651d00d39
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 fprintf(stderr, _("\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 time(&now);
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) < 0)
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 fprintf(stderr, "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 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
301 name, m->m_maildir_file, (int)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 fprintf(stderr,
370 _("Cannot delete file \"%s/%s\" for message %" PRIuZ ".\n"),
371 mailname, m->m_maildir_file, PTR2SIZE(m - message + 1));
372 else
373 ++gotcha;
374 } else {
375 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS) ||
376 (m->m_flag & (MNEW | MBOXED | MSAVED | MSTATUS | MFLAG |
377 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))) {
378 _maildir_move(m);
379 ++modflags;
381 ++held;
384 jbypass:
385 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
386 printf(_("\"%s\" "), displayname);
387 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
388 ? _("complete\n") : _("updated.\n"));
389 } else if (held && !(pstate & PS_EDIT) && mb.mb_perm != 0) {
390 if (held == 1)
391 printf(_("Held 1 message in %s\n"), displayname);
392 else
393 printf(_("Held %d messages in %s\n"), held, displayname);
395 fflush(stdout);
396 jfree:
397 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
398 free(m->m_maildir_file);
399 NYD_LEAVE;
402 static void
403 _maildir_move(struct message *m)
405 char *fn, *new;
406 NYD_ENTER;
408 fn = mkname(0, m->m_flag, m->m_maildir_file + 4);
409 new = savecat("cur/", fn);
410 if (!strcmp(m->m_maildir_file, new))
411 goto jleave;
412 if (link(m->m_maildir_file, new) == -1) {
413 fprintf(stderr, /* TODO tr */
414 "Cannot link \"%s/%s\" to \"%s/%s\": message %d not touched.\n",
415 mailname, m->m_maildir_file, mailname, new,
416 (int)PTR2SIZE(m - message + 1));
417 goto jleave;
419 if (unlink(m->m_maildir_file) == -1)
420 fprintf(stderr, /* TODO tr */"Cannot unlink \"%s/%s\".\n",
421 mailname, m->m_maildir_file);
422 jleave:
423 NYD_LEAVE;
426 static char *
427 mkname(time_t t, enum mflag f, char const *pref)
429 static unsigned long cnt;
430 static pid_t mypid; /* XXX This should possibly be global, somehow */
431 static char *node;
433 char *cp;
434 int size, n, i;
435 NYD_ENTER;
437 if (pref == NULL) {
438 if (mypid == 0)
439 mypid = getpid();
440 if (node == NULL) {
441 cp = nodename(0);
442 n = size = 0;
443 do {
444 if (UICMP(32, n, <, size + 8))
445 node = srealloc(node, size += 20);
446 switch (*cp) {
447 case '/':
448 node[n++] = '\\', node[n++] = '0',
449 node[n++] = '5', node[n++] = '7';
450 break;
451 case ':':
452 node[n++] = '\\', node[n++] = '0',
453 node[n++] = '7', node[n++] = '2';
454 break;
455 default:
456 node[n++] = *cp;
458 } while (*cp++ != '\0');
460 size = 60 + strlen(node);
461 cp = salloc(size);
462 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
463 (ul_i)t, (ul_i)mypid, ++cnt, node);
464 } else {
465 size = (n = strlen(pref)) + 13;
466 cp = salloc(size);
467 memcpy(cp, pref, n +1);
468 for (i = n; i > 3; --i)
469 if (cp[i - 1] == ',' && cp[i - 2] == '2' && cp[i - 3] == ':') {
470 n = i;
471 break;
473 if (i <= 3) {
474 memcpy(cp + n, ":2,", 3 +1);
475 n += 3;
478 if (n < size - 7) {
479 if (f & MDRAFTED)
480 cp[n++] = 'D';
481 if (f & MFLAGGED)
482 cp[n++] = 'F';
483 if (f & MANSWERED)
484 cp[n++] = 'R';
485 if (f & MREAD)
486 cp[n++] = 'S';
487 if (f & MDELETED)
488 cp[n++] = 'T';
489 cp[n] = '\0';
491 NYD_LEAVE;
492 return cp;
495 static enum okay
496 maildir_append1(char const *name, FILE *fp, off_t off1, long size,
497 enum mflag flag)
499 char buf[4096], *fn, *tfn, *nfn;
500 struct stat st;
501 FILE *op;
502 time_t now;
503 size_t nlen, flen, n;
504 enum okay rv = STOP;
505 NYD_ENTER;
507 nlen = strlen(name);
509 /* Create a unique temporary file */
510 for (nfn = (char*)0xA /* XXX no magic */;; sleep(1)) {
511 time(&now);
512 flen = strlen(fn = mkname(now, flag, NULL));
513 tfn = salloc(n = nlen + flen + 6);
514 snprintf(tfn, n, "%s/tmp/%s", name, fn);
516 /* Use "wx" for O_EXCL XXX stat(2) rather redundant; coverity:TOCTOU */
517 if ((!stat(tfn, &st) || errno == ENOENT) &&
518 (op = Fopen(tfn, "wx")) != NULL)
519 break;
521 nfn = (char*)(PTR2SIZE(nfn) - 1);
522 if (nfn == NULL) {
523 fprintf(stderr, _("Can't create an unique file name in \"%s/tmp\".\n"),
524 name);
525 goto jleave;
529 if (fseek(fp, off1, SEEK_SET) == -1)
530 goto jtmperr;
531 while (size > 0) {
532 size_t z = UICMP(z, size, >, sizeof buf) ? sizeof buf : (size_t)size;
534 if (z != (n = fread(buf, 1, z, fp)) || n != fwrite(buf, 1, n, op)) {
535 jtmperr:
536 fprintf(stderr, _("Error writing to \"%s\".\n"), tfn);
537 Fclose(op);
538 goto jerr;
540 size -= n;
542 Fclose(op);
544 nfn = salloc(n = nlen + flen + 6);
545 snprintf(nfn, n, "%s/new/%s", name, fn);
546 if (link(tfn, nfn) == -1) {
547 fprintf(stderr, _("Cannot link \"%s\" to \"%s\".\n"), tfn, nfn);
548 goto jerr;
550 rv = OKAY;
551 jerr:
552 if (unlink(tfn) == -1)
553 fprintf(stderr, _("Cannot unlink \"%s\".\n"), tfn);
554 jleave:
555 NYD_LEAVE;
556 return rv;
559 static enum okay
560 trycreate(char const *name)
562 struct stat st;
563 enum okay rv = STOP;
564 NYD_ENTER;
566 if (!stat(name, &st)) {
567 if (!S_ISDIR(st.st_mode)) {
568 fprintf(stderr, "\"%s\" is not a directory.\n", name);/* TODO tr */
569 goto jleave;
571 } else if (makedir(name) != OKAY) {
572 fprintf(stderr, "Cannot create directory \"%s\".\n", name);/* TODO tr */
573 goto jleave;
574 } else
575 ++imap_created_mailbox;
576 rv = OKAY;
577 jleave:
578 NYD_LEAVE;
579 return rv;
582 static enum okay
583 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
585 char *np;
586 size_t sz;
587 enum okay rv = STOP;
588 NYD_ENTER;
590 if (trycreate(name) == OKAY) {
591 np = ac_alloc((sz = strlen(name)) + 4 +1);
592 memcpy(np, name, sz);
593 memcpy(np + sz, "/tmp", 4 +1);
594 if (trycreate(np) == OKAY) {
595 memcpy(np + sz, "/new", 4 +1);
596 if (trycreate(np) == OKAY) {
597 memcpy(np + sz, "/cur", 4 +1);
598 rv = trycreate(np);
601 ac_free(np);
603 NYD_LEAVE;
604 return rv;
607 static struct message *
608 mdlook(char const *name, struct message *data)
610 struct mditem *md;
611 ui32_t c, h, n = 0;
612 NYD_ENTER;
614 if (data && data->m_maildir_hash)
615 h = ~data->m_maildir_hash;
616 else
617 h = pjw(name);
618 h %= _maildir_prime;
619 c = h;
620 md = _maildir_table + c;
622 while (md->md_data != NULL) {
623 if (!strcmp(md->md_data->m_maildir_file + 4, name))
624 break;
625 c += (n & 1) ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
626 n++;
627 while (c >= _maildir_prime)
628 c -= _maildir_prime;
629 md = _maildir_table + c;
631 if (data != NULL && md->md_data == NULL)
632 md->md_data = data;
633 NYD_LEAVE;
634 return md->md_data;
637 static void
638 mktable(void)
640 struct message *mp;
641 size_t i;
642 NYD_ENTER;
644 _maildir_prime = nextprime(msgCount);
645 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
646 for (mp = message, i = msgCount; i-- != 0; ++mp)
647 mdlook(mp->m_maildir_file + 4, mp);
648 NYD_LEAVE;
651 static enum okay
652 subdir_remove(char const *name, char const *sub)
654 char *path;
655 int pathsize, pathend, namelen, sublen, n;
656 DIR *dirp;
657 struct dirent *dp;
658 enum okay rv = STOP;
659 NYD_ENTER;
661 namelen = strlen(name);
662 sublen = strlen(sub);
663 path = smalloc(pathsize = namelen + sublen + 30 +1);
664 memcpy(path, name, namelen);
665 path[namelen] = '/';
666 memcpy(path + namelen + 1, sub, sublen);
667 path[namelen + sublen + 1] = '/';
668 path[pathend = namelen + sublen + 2] = '\0';
670 if ((dirp = opendir(path)) == NULL) {
671 perror(path);
672 goto jleave;
674 while ((dp = readdir(dirp)) != NULL) {
675 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
676 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
677 continue;
678 if (dp->d_name[0] == '.')
679 continue;
680 n = strlen(dp->d_name);
681 if (UICMP(32, pathend + n +1, >, pathsize))
682 path = srealloc(path, pathsize = pathend + n + 30);
683 memcpy(path + pathend, dp->d_name, n +1);
684 if (unlink(path) == -1) {
685 perror(path);
686 closedir(dirp);
687 goto jleave;
690 closedir(dirp);
692 path[pathend] = '\0';
693 if (rmdir(path) == -1) {
694 perror(path);
695 goto jleave;
697 rv = OKAY;
698 jleave:
699 free(path);
700 NYD_LEAVE;
701 return rv;
704 FL int
705 maildir_setfile(char const * volatile name, enum fedit_mode fm)
707 sighandler_type volatile saveint;
708 struct cw cw;
709 int i = -1, omsgCount;
710 NYD_ENTER;
712 omsgCount = msgCount;
713 if (cwget(&cw) == STOP) {
714 alert("Cannot open current directory");
715 goto jleave;
718 if (!(fm & FEDIT_NEWMAIL))
719 quit();
721 saveint = safe_signal(SIGINT, SIG_IGN);
723 if (!(fm & FEDIT_NEWMAIL)) {
724 if (fm & FEDIT_SYSBOX)
725 pstate &= ~PS_EDIT;
726 else
727 pstate |= PS_EDIT;
728 if (mb.mb_itf) {
729 fclose(mb.mb_itf);
730 mb.mb_itf = NULL;
732 if (mb.mb_otf) {
733 fclose(mb.mb_otf);
734 mb.mb_otf = NULL;
736 initbox(name);
737 mb.mb_type = MB_MAILDIR;
740 if (chdir(name) < 0) {
741 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);/*TODO tr*/
742 mb.mb_type = MB_VOID;
743 *mailname = '\0';
744 msgCount = 0;
745 cwrelse(&cw);
746 safe_signal(SIGINT, saveint);
747 goto jleave;
750 _maildir_table = NULL;
751 if (sigsetjmp(_maildir_jmp, 1) == 0) {
752 if (fm & FEDIT_NEWMAIL)
753 mktable();
754 if (saveint != SIG_IGN)
755 safe_signal(SIGINT, &__maildircatch);
756 i = _maildir_setfile1(name, fm, omsgCount);
758 if ((fm & FEDIT_NEWMAIL) && _maildir_table != NULL)
759 free(_maildir_table);
761 safe_signal(SIGINT, saveint);
763 if (i < 0) {
764 mb.mb_type = MB_VOID;
765 *mailname = '\0';
766 msgCount = 0;
769 if (cwret(&cw) == STOP)
770 panic("Cannot change back to current directory.");/* TODO tr */
771 cwrelse(&cw);
773 setmsize(msgCount);
774 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted && msgCount > omsgCount) {
775 mb.mb_threaded = 0;
776 c_sort((void*)-1);
778 if (!(fm & FEDIT_NEWMAIL))
779 pstate &= ~PS_SAW_COMMAND;
780 if (!(fm & FEDIT_NEWMAIL) && (fm & FEDIT_SYSBOX) && msgCount == 0) {
781 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
782 fprintf(stderr, _("No mail at %s\n"), name);
783 i = 1;
784 goto jleave;
786 if ((fm & FEDIT_NEWMAIL) && msgCount > omsgCount)
787 newmailinfo(omsgCount);
788 i = 0;
789 jleave:
790 NYD_LEAVE;
791 return i;
794 FL void
795 maildir_quit(void)
797 sighandler_type saveint;
798 struct cw cw;
799 NYD_ENTER;
801 if (cwget(&cw) == STOP) {
802 alert("Cannot open current directory");/* TODO tr */
803 goto jleave;
806 saveint = safe_signal(SIGINT, SIG_IGN);
808 if (chdir(mailname) == -1) {
809 fprintf(stderr, "Cannot change directory to \"%s\".\n",/* TODO tr */
810 mailname);
811 cwrelse(&cw);
812 safe_signal(SIGINT, saveint);
813 goto jleave;
816 if (sigsetjmp(_maildir_jmp, 1) == 0) {
817 if (saveint != SIG_IGN)
818 safe_signal(SIGINT, &__maildircatch_hold);
819 maildir_update();
822 safe_signal(SIGINT, saveint);
824 if (cwret(&cw) == STOP)
825 panic("Cannot change back to current directory."); /* TODO tr */
826 cwrelse(&cw);
827 jleave:
828 NYD_LEAVE;
831 FL enum okay
832 maildir_append(char const *name, FILE *fp)
834 char *buf, *bp, *lp;
835 size_t bufsize, buflen, cnt;
836 off_t off1 = -1, offs;
837 long size;
838 int flag;
839 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
840 enum okay rv;
841 NYD_ENTER;
843 if ((rv = mkmaildir(name)) != OKAY)
844 goto jleave;
846 buf = smalloc(bufsize = LINESIZE); /* TODO line pool; signals */
847 buflen = 0;
848 cnt = fsize(fp);
849 offs = ftell(fp);
850 size = 0;
852 srelax_hold();
853 for (flag = MNEW, state = _NLSEP;;) {
854 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
856 if (bp == NULL ||
857 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
858 is_head(buf, buflen, FAL0))) {
859 if (off1 != (off_t)-1) {
860 if ((rv = maildir_append1(name, fp, off1, size, flag)) == STOP)
861 goto jfree;
862 srelax();
863 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
864 rv = STOP;
865 goto jfree;
868 off1 = offs + buflen;
869 size = 0;
870 state = _INHEAD;
871 flag = MNEW;
873 if (bp == NULL)
874 break;
875 } else
876 size += buflen;
877 offs += buflen;
879 state &= ~_NLSEP;
880 if (buf[0] == '\n') {
881 state &= ~_INHEAD;
882 state |= _NLSEP;
883 } else if (state & _INHEAD) {
884 if (!ascncasecmp(buf, "status", 6)) {
885 lp = buf + 6;
886 while (whitechar(*lp))
887 ++lp;
888 if (*lp == ':')
889 while (*++lp != '\0')
890 switch (*lp) {
891 case 'R':
892 flag |= MREAD;
893 break;
894 case 'O':
895 flag &= ~MNEW;
896 break;
898 } else if (!ascncasecmp(buf, "x-status", 8)) {
899 lp = buf + 8;
900 while (whitechar(*lp))
901 ++lp;
902 if (*lp == ':') {
903 while (*++lp != '\0')
904 switch (*lp) {
905 case 'F':
906 flag |= MFLAGGED;
907 break;
908 case 'A':
909 flag |= MANSWERED;
910 break;
911 case 'T':
912 flag |= MDRAFTED;
913 break;
919 assert(rv == OKAY);
920 jfree:
921 srelax_rele();
922 free(buf);
923 jleave:
924 NYD_LEAVE;
925 return rv;
928 FL enum okay
929 maildir_remove(char const *name)
931 enum okay rv = STOP;
932 NYD_ENTER;
934 if (subdir_remove(name, "tmp") == STOP ||
935 subdir_remove(name, "new") == STOP ||
936 subdir_remove(name, "cur") == STOP)
937 goto jleave;
938 if (rmdir(name) == -1) {
939 perror(name);
940 goto jleave;
942 rv = OKAY;
943 jleave:
944 NYD_LEAVE;
945 return rv;
948 /* s-it-mode */