nail.1: improve `write', also regarding [7a09af6]
[s-mailx.git] / maildir.c
blobcf63d64eaa32e99bb30a3a4fa1475f580855f863
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, FAL0)) {
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 */;; sleep(1)) {
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 (makedir(name) != OKAY) {
569 n_err(_("Cannot create directory \"%s\"\n"), name);
570 goto jleave;
571 } else
572 ++imap_created_mailbox;
573 rv = OKAY;
574 jleave:
575 NYD_LEAVE;
576 return rv;
579 static enum okay
580 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
582 char *np;
583 size_t sz;
584 enum okay rv = STOP;
585 NYD_ENTER;
587 if (trycreate(name) == OKAY) {
588 np = ac_alloc((sz = strlen(name)) + 4 +1);
589 memcpy(np, name, sz);
590 memcpy(np + sz, "/tmp", 4 +1);
591 if (trycreate(np) == OKAY) {
592 memcpy(np + sz, "/new", 4 +1);
593 if (trycreate(np) == OKAY) {
594 memcpy(np + sz, "/cur", 4 +1);
595 rv = trycreate(np);
598 ac_free(np);
600 NYD_LEAVE;
601 return rv;
604 static struct message *
605 mdlook(char const *name, struct message *data)
607 struct mditem *md;
608 ui32_t c, h, n = 0;
609 NYD_ENTER;
611 if (data && data->m_maildir_hash)
612 h = ~data->m_maildir_hash;
613 else
614 h = pjw(name);
615 h %= _maildir_prime;
616 c = h;
617 md = _maildir_table + c;
619 while (md->md_data != NULL) {
620 if (!strcmp(md->md_data->m_maildir_file + 4, name))
621 break;
622 c += (n & 1) ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
623 n++;
624 while (c >= _maildir_prime)
625 c -= _maildir_prime;
626 md = _maildir_table + c;
628 if (data != NULL && md->md_data == NULL)
629 md->md_data = data;
630 NYD_LEAVE;
631 return md->md_data;
634 static void
635 mktable(void)
637 struct message *mp;
638 size_t i;
639 NYD_ENTER;
641 _maildir_prime = nextprime(msgCount);
642 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
643 for (mp = message, i = msgCount; i-- != 0; ++mp)
644 mdlook(mp->m_maildir_file + 4, mp);
645 NYD_LEAVE;
648 static enum okay
649 subdir_remove(char const *name, char const *sub)
651 char *path;
652 int pathsize, pathend, namelen, sublen, n;
653 DIR *dirp;
654 struct dirent *dp;
655 enum okay rv = STOP;
656 NYD_ENTER;
658 namelen = strlen(name);
659 sublen = strlen(sub);
660 path = smalloc(pathsize = namelen + sublen + 30 +1);
661 memcpy(path, name, namelen);
662 path[namelen] = '/';
663 memcpy(path + namelen + 1, sub, sublen);
664 path[namelen + sublen + 1] = '/';
665 path[pathend = namelen + sublen + 2] = '\0';
667 if ((dirp = opendir(path)) == NULL) {
668 n_perr(path, 0);
669 goto jleave;
671 while ((dp = readdir(dirp)) != NULL) {
672 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
673 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
674 continue;
675 if (dp->d_name[0] == '.')
676 continue;
677 n = strlen(dp->d_name);
678 if (UICMP(32, pathend + n +1, >, pathsize))
679 path = srealloc(path, pathsize = pathend + n + 30);
680 memcpy(path + pathend, dp->d_name, n +1);
681 if (unlink(path) == -1) {
682 n_perr(path, 0);
683 closedir(dirp);
684 goto jleave;
687 closedir(dirp);
689 path[pathend] = '\0';
690 if (rmdir(path) == -1) {
691 n_perr(path, 0);
692 goto jleave;
694 rv = OKAY;
695 jleave:
696 free(path);
697 NYD_LEAVE;
698 return rv;
701 FL int
702 maildir_setfile(char const * volatile name, enum fedit_mode fm)
704 sighandler_type volatile saveint;
705 struct cw cw;
706 int i = -1, omsgCount;
707 NYD_ENTER;
709 omsgCount = msgCount;
710 if (cwget(&cw) == STOP) {
711 n_alert(_("Cannot open current directory"));
712 goto jleave;
715 if (!(fm & FEDIT_NEWMAIL))
716 quit();
718 saveint = safe_signal(SIGINT, SIG_IGN);
720 if (!(fm & FEDIT_NEWMAIL)) {
721 if (fm & FEDIT_SYSBOX)
722 pstate &= ~PS_EDIT;
723 else
724 pstate |= PS_EDIT;
725 if (mb.mb_itf) {
726 fclose(mb.mb_itf);
727 mb.mb_itf = NULL;
729 if (mb.mb_otf) {
730 fclose(mb.mb_otf);
731 mb.mb_otf = NULL;
733 initbox(name);
734 mb.mb_type = MB_MAILDIR;
737 if (chdir(name) < 0) {
738 n_err(_("Cannot change directory to \"%s\"\n"), name);
739 mb.mb_type = MB_VOID;
740 *mailname = '\0';
741 msgCount = 0;
742 cwrelse(&cw);
743 safe_signal(SIGINT, saveint);
744 goto jleave;
747 _maildir_table = NULL;
748 if (sigsetjmp(_maildir_jmp, 1) == 0) {
749 if (fm & FEDIT_NEWMAIL)
750 mktable();
751 if (saveint != SIG_IGN)
752 safe_signal(SIGINT, &__maildircatch);
753 i = _maildir_setfile1(name, fm, omsgCount);
755 if ((fm & FEDIT_NEWMAIL) && _maildir_table != NULL)
756 free(_maildir_table);
758 safe_signal(SIGINT, saveint);
760 if (i < 0) {
761 mb.mb_type = MB_VOID;
762 *mailname = '\0';
763 msgCount = 0;
766 if (cwret(&cw) == STOP)
767 n_panic(_("Cannot change back to current directory"));
768 cwrelse(&cw);
770 setmsize(msgCount);
771 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted && msgCount > omsgCount) {
772 mb.mb_threaded = 0;
773 c_sort((void*)-1);
776 if (!(fm & FEDIT_NEWMAIL))
777 pstate &= ~PS_SAW_COMMAND;
779 if (options & OPT_EXISTONLY) {
780 i = (msgCount == 0);
781 goto jleave;
784 if (!(fm & FEDIT_NEWMAIL) && (fm & FEDIT_SYSBOX) && msgCount == 0) {
785 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
786 n_err(_("No mail at \"%s\"\n"), name);
787 i = 1;
788 goto jleave;
791 if ((fm & FEDIT_NEWMAIL) && msgCount > omsgCount)
792 newmailinfo(omsgCount);
793 i = 0;
794 jleave:
795 NYD_LEAVE;
796 return i;
799 FL void
800 maildir_quit(void)
802 sighandler_type saveint;
803 struct cw cw;
804 NYD_ENTER;
806 if (cwget(&cw) == STOP) {
807 n_alert(_("Cannot open current directory"));
808 goto jleave;
811 saveint = safe_signal(SIGINT, SIG_IGN);
813 if (chdir(mailname) == -1) {
814 n_err(_("Cannot change directory to \"%s\"\n"), mailname);
815 cwrelse(&cw);
816 safe_signal(SIGINT, saveint);
817 goto jleave;
820 if (sigsetjmp(_maildir_jmp, 1) == 0) {
821 if (saveint != SIG_IGN)
822 safe_signal(SIGINT, &__maildircatch_hold);
823 maildir_update();
826 safe_signal(SIGINT, saveint);
828 if (cwret(&cw) == STOP)
829 n_panic(_("Cannot change back to current directory"));
830 cwrelse(&cw);
831 jleave:
832 NYD_LEAVE;
835 FL enum okay
836 maildir_append(char const *name, FILE *fp, long offset)
838 char *buf, *bp, *lp;
839 size_t bufsize, buflen, cnt;
840 off_t off1 = -1, offs;
841 long size;
842 int flag;
843 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
844 enum okay rv;
845 NYD_ENTER;
847 if ((rv = mkmaildir(name)) != OKAY)
848 goto jleave;
850 buf = smalloc(bufsize = LINESIZE); /* TODO line pool; signals */
851 buflen = 0;
852 cnt = fsize(fp);
853 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
854 size = 0;
856 srelax_hold();
857 for (flag = MNEW, state = _NLSEP;;) {
858 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
860 if (bp == NULL ||
861 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
862 is_head(buf, buflen, FAL0))) {
863 if (off1 != (off_t)-1) {
864 if ((rv = maildir_append1(name, fp, off1, size, flag)) == STOP)
865 goto jfree;
866 srelax();
867 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
868 rv = STOP;
869 goto jfree;
872 off1 = offs + buflen;
873 size = 0;
874 state = _INHEAD;
875 flag = MNEW;
877 if (bp == NULL)
878 break;
879 } else
880 size += buflen;
881 offs += buflen;
883 state &= ~_NLSEP;
884 if (buf[0] == '\n') {
885 state &= ~_INHEAD;
886 state |= _NLSEP;
887 } else if (state & _INHEAD) {
888 if (!ascncasecmp(buf, "status", 6)) {
889 lp = buf + 6;
890 while (whitechar(*lp))
891 ++lp;
892 if (*lp == ':')
893 while (*++lp != '\0')
894 switch (*lp) {
895 case 'R':
896 flag |= MREAD;
897 break;
898 case 'O':
899 flag &= ~MNEW;
900 break;
902 } else if (!ascncasecmp(buf, "x-status", 8)) {
903 lp = buf + 8;
904 while (whitechar(*lp))
905 ++lp;
906 if (*lp == ':') {
907 while (*++lp != '\0')
908 switch (*lp) {
909 case 'F':
910 flag |= MFLAGGED;
911 break;
912 case 'A':
913 flag |= MANSWERED;
914 break;
915 case 'T':
916 flag |= MDRAFTED;
917 break;
923 assert(rv == OKAY);
924 jfree:
925 srelax_rele();
926 free(buf);
927 jleave:
928 NYD_LEAVE;
929 return rv;
932 FL enum okay
933 maildir_remove(char const *name)
935 enum okay rv = STOP;
936 NYD_ENTER;
938 if (subdir_remove(name, "tmp") == STOP ||
939 subdir_remove(name, "new") == STOP ||
940 subdir_remove(name, "cur") == STOP)
941 goto jleave;
942 if (rmdir(name) == -1) {
943 n_perr(name, 0);
944 goto jleave;
946 rv = OKAY;
947 jleave:
948 NYD_LEAVE;
949 return rv;
952 /* s-it-mode */