nail.1: tweak text of -a
[s-mailx.git] / maildir.c
blob69786537d8d492d055264f2d3ba7b8c96f539cc2
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 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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 static unsigned a_maildir_hash(char const *cp);
63 /* Do some cleanup in the tmp/ subdir */
64 static void _cleantmp(void);
66 static int _maildir_setfile1(char const *name, enum fedit_mode fm,
67 int omsgCount);
69 /* In combination with the names from mkname(), this comparison function
70 * ensures that the order of messages in a maildir folder created by mailx
71 * remains always the same. In effect, if a mbox folder is transferred to
72 * a maildir folder by 'copy *', the message order wont' change */
73 static int mdcmp(void const *a, void const *b);
75 static int _maildir_subdir(char const *name, char const *sub,
76 enum fedit_mode fm);
78 static void _maildir_append(char const *name, char const *sub,
79 char const *fn);
81 static void readin(char const *name, struct message *m);
83 static void maildir_update(void);
85 static void _maildir_move(struct message *m);
87 static char * mkname(time_t t, enum mflag f, char const *pref);
89 static enum okay maildir_append1(char const *name, FILE *fp, off_t off1,
90 long size, enum mflag flag);
92 static enum okay trycreate(char const *name);
94 static enum okay mkmaildir(char const *name);
96 static struct message * mdlook(char const *name, struct message *data);
98 static void mktable(void);
100 static enum okay subdir_remove(char const *name, char const *sub);
102 static void
103 __maildircatch(int s)
105 NYD_X; /* Signal handler */
106 siglongjmp(_maildir_jmp, s);
109 static void
110 __maildircatch_hold(int s)
112 NYD_X; /* Signal handler */
113 n_UNUSED(s);
114 /* TODO no STDIO in signal handler, no _() tr's -- pre-translate interrupt
115 * TODO globally; */
116 n_err_sighdl(_("\nImportant operation in progress: "
117 "interrupt again to forcefully abort\n"));
118 safe_signal(SIGINT, &__maildircatch);
121 static unsigned
122 a_maildir_hash(char const *cp) /* TODO obsolete that -> torek_hash */
124 unsigned h = 0, g;
125 NYD_ENTER;
127 cp--;
128 while (*++cp) {
129 h = (h << 4 & 0xffffffff) + (*cp&0377);
130 if ((g = h & 0xf0000000) != 0) {
131 h = h ^ g >> 24;
132 h = h ^ g;
135 NYD_LEAVE;
136 return h;
139 static void
140 _cleantmp(void)
142 char dep[PATH_MAX];
143 struct stat st;
144 time_t now;
145 DIR *dirp;
146 struct dirent *dp;
147 NYD_ENTER;
149 if ((dirp = opendir("tmp")) == NULL)
150 goto jleave;
152 now = n_time_epoch();
153 while ((dp = readdir(dirp)) != NULL) {
154 if (dp->d_name[0] == '.')
155 continue;
156 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
157 if (stat(dep, &st) == -1)
158 continue;
159 if (st.st_atime + 36*3600 < now)
160 unlink(dep);
162 closedir(dirp);
163 jleave:
164 NYD_LEAVE;
167 static int
168 _maildir_setfile1(char const *name, enum fedit_mode fm, int omsgCount)
170 int i;
171 NYD_ENTER;
173 if (!(fm & FEDIT_NEWMAIL))
174 _cleantmp();
176 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
177 if ((i = _maildir_subdir(name, "cur", fm)) != 0)
178 goto jleave;
179 if ((i = _maildir_subdir(name, "new", fm)) != 0)
180 goto jleave;
181 _maildir_append(name, NULL, NULL);
183 srelax_hold();
184 for (i = ((fm & FEDIT_NEWMAIL) ? omsgCount : 0); i < msgCount; ++i) {
185 readin(name, message + i);
186 srelax();
188 srelax_rele();
190 if (fm & FEDIT_NEWMAIL) {
191 if (msgCount > omsgCount)
192 qsort(&message[omsgCount], msgCount - omsgCount, sizeof *message,
193 &mdcmp);
194 } else if (msgCount)
195 qsort(message, msgCount, sizeof *message, &mdcmp);
196 i = msgCount;
197 jleave:
198 NYD_LEAVE;
199 return i;
202 static int
203 mdcmp(void const *a, void const *b)
205 struct message const *mpa = a, *mpb = b;
206 long i;
207 NYD_ENTER;
209 if ((i = mpa->m_time - mpb->m_time) == 0)
210 i = strcmp(mpa->m_maildir_file + 4, mpb->m_maildir_file + 4);
211 NYD_LEAVE;
212 return i;
215 static int
216 _maildir_subdir(char const *name, char const *sub, enum fedit_mode fm)
218 DIR *dirp;
219 struct dirent *dp;
220 int rv;
221 NYD_ENTER;
223 if ((dirp = opendir(sub)) == NULL) {
224 n_err(_("Cannot open directory %s\n"),
225 n_shexp_quote_cp(savecatsep(name, '/', sub), FAL0));
226 rv = -1;
227 goto jleave;
229 if (access(sub, W_OK) == -1)
230 mb.mb_perm = 0;
231 while ((dp = readdir(dirp)) != NULL) {
232 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
233 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
234 continue;
235 if (dp->d_name[0] == '.')
236 continue;
237 if (!(fm & FEDIT_NEWMAIL) || mdlook(dp->d_name, NULL) == NULL)
238 _maildir_append(name, sub, dp->d_name);
240 closedir(dirp);
241 rv = 0;
242 jleave:
243 NYD_LEAVE;
244 return rv;
247 static void
248 _maildir_append(char const *name, char const *sub, char const *fn)
250 struct message *m;
251 size_t sz, i;
252 time_t t = 0;
253 enum mflag f = MUSED | MNOFROM | MNEWEST;
254 char const *cp;
255 char *xp;
256 NYD_ENTER;
257 n_UNUSED(name);
259 if (fn != NULL && sub != NULL) {
260 if (!strcmp(sub, "new"))
261 f |= MNEW;
262 t = strtol(fn, &xp, 10);
263 if ((cp = strrchr(xp, ',')) != NULL && PTRCMP(cp, >, xp + 2) &&
264 cp[-1] == '2' && cp[-2] == ':') {
265 while (*++cp != '\0') {
266 switch (*cp) {
267 case 'F':
268 f |= MFLAGGED;
269 break;
270 case 'R':
271 f |= MANSWERED;
272 break;
273 case 'S':
274 f |= MREAD;
275 break;
276 case 'T':
277 f |= MDELETED;
278 break;
279 case 'D':
280 f |= MDRAFT;
281 break;
287 /* Ensure room (and a NULLified last entry) */
288 ++msgCount;
289 message_append(NULL);
290 --msgCount;
292 if (fn == NULL || sub == NULL)
293 goto jleave;
295 m = message + msgCount++;
296 i = strlen(fn);
297 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 1 +1);
298 memcpy(m->m_maildir_file, sub, sz);
299 m->m_maildir_file[sz] = '/';
300 memcpy(m->m_maildir_file + sz + 1, fn, i +1);
301 m->m_time = t;
302 m->m_flag = f;
303 m->m_maildir_hash = ~a_maildir_hash(fn);
304 jleave:
305 NYD_LEAVE;
306 return;
309 static void
310 readin(char const *name, struct message *m)
312 char *buf;
313 size_t bufsize, buflen, cnt;
314 long size = 0, lines = 0;
315 off_t offset;
316 FILE *fp;
317 int emptyline = 0;
318 NYD_ENTER;
320 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
321 n_err(_("Cannot read %s for message %lu\n"),
322 n_shexp_quote_cp(savecatsep(name, '/', m->m_maildir_file), FAL0),
323 (ul_i)PTR2SIZE(m - message + 1));
324 m->m_flag |= MHIDDEN;
325 goto jleave;
328 cnt = fsize(fp);
329 fseek(mb.mb_otf, 0L, SEEK_END);
330 offset = ftell(mb.mb_otf);
331 buf = smalloc(bufsize = LINESIZE);
332 buflen = 0;
333 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
334 /* Since we simply copy over data without doing any transfer
335 * encoding reclassification/adjustment we *have* to perform
336 * RFC 4155 compliant From_ quoting here */
337 if (emptyline && is_head(buf, buflen, FAL0)) {
338 putc('>', mb.mb_otf);
339 ++size;
341 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
342 emptyline = (*buf == '\n');
343 ++lines;
345 free(buf);
346 if (!emptyline) {
347 putc('\n', mb.mb_otf);
348 ++lines;
349 ++size;
352 Fclose(fp);
353 fflush(mb.mb_otf);
354 m->m_size = m->m_xsize = size;
355 m->m_lines = m->m_xlines = lines;
356 m->m_block = mailx_blockof(offset);
357 m->m_offset = mailx_offsetof(offset);
358 substdate(m);
359 jleave:
360 NYD_LEAVE;
363 static void
364 maildir_update(void)
366 struct message *m;
367 int dodel, c, gotcha = 0, held = 0, modflags = 0;
368 NYD_ENTER;
370 if (mb.mb_perm == 0)
371 goto jfree;
373 if (!(pstate & PS_EDIT)) {
374 holdbits();
375 for (m = message, c = 0; PTRCMP(m, <, message + msgCount); ++m) {
376 if (m->m_flag & MBOX)
377 c++;
379 if (c > 0)
380 if (makembox() == STOP)
381 goto jbypass;
383 for (m = message, gotcha = 0, held = 0; PTRCMP(m, <, message + msgCount);
384 ++m) {
385 if (pstate & PS_EDIT)
386 dodel = m->m_flag & MDELETED;
387 else
388 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
389 if (dodel) {
390 if (unlink(m->m_maildir_file) < 0)
391 n_err(_("Cannot delete file %s for message %lu\n"),
392 n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file),
393 FAL0), (ul_i)PTR2SIZE(m - message + 1));
394 else
395 ++gotcha;
396 } else {
397 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS) ||
398 (m->m_flag & (MNEW | MBOXED | MSAVED | MSTATUS | MFLAG |
399 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))) {
400 _maildir_move(m);
401 ++modflags;
403 ++held;
406 jbypass:
407 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
408 printf(_("%s "), n_shexp_quote_cp(displayname, FAL0));
409 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
410 ? _("complete\n") : _("updated.\n"));
411 } else if (held && !(pstate & PS_EDIT) && mb.mb_perm != 0) {
412 if (held == 1)
413 printf(_("Held 1 message in %s\n"), displayname);
414 else
415 printf(_("Held %d messages in %s\n"), held, displayname);
417 fflush(stdout);
418 jfree:
419 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
420 free(m->m_maildir_file);
421 NYD_LEAVE;
424 static void
425 _maildir_move(struct message *m)
427 char *fn, *new;
428 NYD_ENTER;
430 fn = mkname(0, m->m_flag, m->m_maildir_file + 4);
431 new = savecat("cur/", fn);
432 if (!strcmp(m->m_maildir_file, new))
433 goto jleave;
434 if (link(m->m_maildir_file, new) == -1) {
435 n_err(_("Cannot link %s to %s: message %lu not touched\n"),
436 n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file), FAL0),
437 n_shexp_quote_cp(savecatsep(mailname, '/', new), FAL0),
438 (ul_i)PTR2SIZE(m - message + 1));
439 goto jleave;
441 if (unlink(m->m_maildir_file) == -1)
442 n_err(_("Cannot unlink %s\n"),
443 n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file), FAL0));
444 jleave:
445 NYD_LEAVE;
448 static char *
449 mkname(time_t t, enum mflag f, char const *pref)
451 static unsigned long cnt;
452 static pid_t mypid; /* XXX This should possibly be global, somehow */
453 static char *node;
455 char *cp;
456 int size, n, i;
457 NYD_ENTER;
459 if (pref == NULL) {
460 if (mypid == 0)
461 mypid = getpid();
462 if (node == NULL) {
463 cp = nodename(0);
464 n = size = 0;
465 do {
466 if (UICMP(32, n, <, size + 8))
467 node = srealloc(node, size += 20);
468 switch (*cp) {
469 case '/':
470 node[n++] = '\\', node[n++] = '0',
471 node[n++] = '5', node[n++] = '7';
472 break;
473 case ':':
474 node[n++] = '\\', node[n++] = '0',
475 node[n++] = '7', node[n++] = '2';
476 break;
477 default:
478 node[n++] = *cp;
480 } while (*cp++ != '\0');
482 size = 60 + strlen(node);
483 cp = salloc(size);
484 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
485 (ul_i)t, (ul_i)mypid, ++cnt, node);
486 } else {
487 size = (n = strlen(pref)) + 13;
488 cp = salloc(size);
489 memcpy(cp, pref, n +1);
490 for (i = n; i > 3; --i)
491 if (cp[i - 1] == ',' && cp[i - 2] == '2' && cp[i - 3] == ':') {
492 n = i;
493 break;
495 if (i <= 3) {
496 memcpy(cp + n, ":2,", 3 +1);
497 n += 3;
500 if (n < size - 7) {
501 if (f & MDRAFTED)
502 cp[n++] = 'D';
503 if (f & MFLAGGED)
504 cp[n++] = 'F';
505 if (f & MANSWERED)
506 cp[n++] = 'R';
507 if (f & MREAD)
508 cp[n++] = 'S';
509 if (f & MDELETED)
510 cp[n++] = 'T';
511 cp[n] = '\0';
513 NYD_LEAVE;
514 return cp;
517 static enum okay
518 maildir_append1(char const *name, FILE *fp, off_t off1, long size,
519 enum mflag flag)
521 char buf[4096], *fn, *tfn, *nfn;
522 struct stat st;
523 FILE *op;
524 time_t now;
525 size_t nlen, flen, n;
526 enum okay rv = STOP;
527 NYD_ENTER;
529 nlen = strlen(name);
531 /* Create a unique temporary file */
532 for (nfn = (char*)0xA /* XXX no magic */;; n_msleep(500, FAL0)) {
533 now = n_time_epoch();
534 flen = strlen(fn = mkname(now, flag, NULL));
535 tfn = salloc(n = nlen + flen + 6);
536 snprintf(tfn, n, "%s/tmp/%s", name, fn);
538 /* Use "wx" for O_EXCL XXX stat(2) rather redundant; coverity:TOCTOU */
539 if ((!stat(tfn, &st) || errno == ENOENT) &&
540 (op = Fopen(tfn, "wx")) != NULL)
541 break;
543 nfn = (char*)(PTR2SIZE(nfn) - 1);
544 if (nfn == NULL) {
545 n_err(_("Can't create an unique file name in %s\n"),
546 n_shexp_quote_cp(savecat(name, "/tmp"), FAL0));
547 goto jleave;
551 if (fseek(fp, off1, SEEK_SET) == -1)
552 goto jtmperr;
553 while (size > 0) {
554 size_t z = UICMP(z, size, >, sizeof buf) ? sizeof buf : (size_t)size;
556 if (z != (n = fread(buf, 1, z, fp)) || n != fwrite(buf, 1, n, op)) {
557 jtmperr:
558 n_err(_("Error writing to %s\n"), n_shexp_quote_cp(tfn, FAL0));
559 Fclose(op);
560 goto jerr;
562 size -= n;
564 Fclose(op);
566 nfn = salloc(n = nlen + flen + 6);
567 snprintf(nfn, n, "%s/new/%s", name, fn);
568 if (link(tfn, nfn) == -1) {
569 n_err(_("Cannot link %s to %s\n"), n_shexp_quote_cp(tfn, FAL0),
570 n_shexp_quote_cp(nfn, FAL0));
571 goto jerr;
573 rv = OKAY;
574 jerr:
575 if (unlink(tfn) == -1)
576 n_err(_("Cannot unlink %s\n"), n_shexp_quote_cp(tfn, FAL0));
577 jleave:
578 NYD_LEAVE;
579 return rv;
582 static enum okay
583 trycreate(char const *name)
585 struct stat st;
586 enum okay rv = STOP;
587 NYD_ENTER;
589 if (!stat(name, &st)) {
590 if (!S_ISDIR(st.st_mode)) {
591 n_err(_("%s is not a directory\n"), n_shexp_quote_cp(name, FAL0));
592 goto jleave;
594 } else if (!n_path_mkdir(name)) {
595 n_err(_("Cannot create directory %s\n"), n_shexp_quote_cp(name, FAL0));
596 goto jleave;
598 rv = OKAY;
599 jleave:
600 NYD_LEAVE;
601 return rv;
604 static enum okay
605 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
607 char *np;
608 size_t sz;
609 enum okay rv = STOP;
610 NYD_ENTER;
612 if (trycreate(name) == OKAY) {
613 np = ac_alloc((sz = strlen(name)) + 4 +1);
614 memcpy(np, name, sz);
615 memcpy(np + sz, "/tmp", 4 +1);
616 if (trycreate(np) == OKAY) {
617 memcpy(np + sz, "/new", 4 +1);
618 if (trycreate(np) == OKAY) {
619 memcpy(np + sz, "/cur", 4 +1);
620 rv = trycreate(np);
623 ac_free(np);
625 NYD_LEAVE;
626 return rv;
629 static struct message *
630 mdlook(char const *name, struct message *data)
632 struct mditem *md;
633 ui32_t c, h, n = 0;
634 NYD_ENTER;
636 if (data && data->m_maildir_hash)
637 h = ~data->m_maildir_hash;
638 else
639 h = a_maildir_hash(name);
640 h %= _maildir_prime;
641 c = h;
642 md = _maildir_table + c;
644 while (md->md_data != NULL) {
645 if (!strcmp(md->md_data->m_maildir_file + 4, name))
646 break;
647 c += (n & 1) ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
648 n++;
649 while (c >= _maildir_prime)
650 c -= _maildir_prime;
651 md = _maildir_table + c;
653 if (data != NULL && md->md_data == NULL)
654 md->md_data = data;
655 NYD_LEAVE;
656 return md->md_data;
659 static void
660 mktable(void)
662 struct message *mp;
663 size_t i;
664 NYD_ENTER;
666 _maildir_prime = nextprime(msgCount);
667 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
668 for (mp = message, i = msgCount; i-- != 0; ++mp)
669 mdlook(mp->m_maildir_file + 4, mp);
670 NYD_LEAVE;
673 static enum okay
674 subdir_remove(char const *name, char const *sub)
676 char *path;
677 int pathsize, pathend, namelen, sublen, n;
678 DIR *dirp;
679 struct dirent *dp;
680 enum okay rv = STOP;
681 NYD_ENTER;
683 namelen = strlen(name);
684 sublen = strlen(sub);
685 path = smalloc(pathsize = namelen + sublen + 30 +1);
686 memcpy(path, name, namelen);
687 path[namelen] = '/';
688 memcpy(path + namelen + 1, sub, sublen);
689 path[namelen + sublen + 1] = '/';
690 path[pathend = namelen + sublen + 2] = '\0';
692 if ((dirp = opendir(path)) == NULL) {
693 n_perr(path, 0);
694 goto jleave;
696 while ((dp = readdir(dirp)) != NULL) {
697 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
698 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
699 continue;
700 if (dp->d_name[0] == '.')
701 continue;
702 n = strlen(dp->d_name);
703 if (UICMP(32, pathend + n +1, >, pathsize))
704 path = srealloc(path, pathsize = pathend + n + 30);
705 memcpy(path + pathend, dp->d_name, n +1);
706 if (unlink(path) == -1) {
707 n_perr(path, 0);
708 closedir(dirp);
709 goto jleave;
712 closedir(dirp);
714 path[pathend] = '\0';
715 if (rmdir(path) == -1) {
716 n_perr(path, 0);
717 goto jleave;
719 rv = OKAY;
720 jleave:
721 free(path);
722 NYD_LEAVE;
723 return rv;
726 FL int
727 maildir_setfile(char const * volatile name, enum fedit_mode fm)
729 sighandler_type volatile saveint;
730 struct cw cw;
731 int omsgCount;
732 int volatile i = -1;
733 NYD_ENTER;
735 omsgCount = msgCount;
736 if (cwget(&cw) == STOP) {
737 n_alert(_("Cannot open current directory"));
738 goto jleave;
741 if (!(fm & FEDIT_NEWMAIL) && !quit())
742 goto jleave;
744 saveint = safe_signal(SIGINT, SIG_IGN);
746 if (!(fm & FEDIT_NEWMAIL)) {
747 if (fm & FEDIT_SYSBOX)
748 pstate &= ~PS_EDIT;
749 else
750 pstate |= PS_EDIT;
751 if (mb.mb_itf) {
752 fclose(mb.mb_itf);
753 mb.mb_itf = NULL;
755 if (mb.mb_otf) {
756 fclose(mb.mb_otf);
757 mb.mb_otf = NULL;
759 initbox(name);
760 mb.mb_type = MB_MAILDIR;
763 if (chdir(name) < 0) {
764 n_err(_("Cannot change directory to %s\n"), n_shexp_quote_cp(name, FAL0));
765 mb.mb_type = MB_VOID;
766 *mailname = '\0';
767 msgCount = 0;
768 cwrelse(&cw);
769 safe_signal(SIGINT, saveint);
770 goto jleave;
773 _maildir_table = NULL;
774 if (sigsetjmp(_maildir_jmp, 1) == 0) {
775 if (fm & FEDIT_NEWMAIL)
776 mktable();
777 if (saveint != SIG_IGN)
778 safe_signal(SIGINT, &__maildircatch);
779 i = _maildir_setfile1(name, fm, omsgCount);
781 if ((fm & FEDIT_NEWMAIL) && _maildir_table != NULL)
782 free(_maildir_table);
784 safe_signal(SIGINT, saveint);
786 if (i < 0) {
787 mb.mb_type = MB_VOID;
788 *mailname = '\0';
789 msgCount = 0;
792 if (cwret(&cw) == STOP)
793 n_panic(_("Cannot change back to current directory"));
794 cwrelse(&cw);
796 setmsize(msgCount);
797 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted && msgCount > omsgCount) {
798 mb.mb_threaded = 0;
799 c_sort((void*)-1);
802 if (!(fm & FEDIT_NEWMAIL)) {
803 pstate &= ~PS_SAW_COMMAND;
804 pstate |= PS_SETFILE_OPENED;
807 if ((options & OPT_EXISTONLY) && !(options & OPT_HEADERLIST)) {
808 i = (msgCount == 0);
809 goto jleave;
812 if (!(fm & FEDIT_NEWMAIL) && (fm & FEDIT_SYSBOX) && msgCount == 0) {
813 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
814 n_err(_("No mail at %s\n"), n_shexp_quote_cp(name, FAL0));
815 i = 1;
816 goto jleave;
819 if ((fm & FEDIT_NEWMAIL) && msgCount > omsgCount)
820 newmailinfo(omsgCount);
821 i = 0;
822 jleave:
823 NYD_LEAVE;
824 return i;
827 FL bool_t
828 maildir_quit(void)
830 sighandler_type saveint;
831 struct cw cw;
832 bool_t rv;
833 NYD_ENTER;
835 rv = FAL0;
837 if (cwget(&cw) == STOP) {
838 n_alert(_("Cannot open current directory"));
839 goto jleave;
842 saveint = safe_signal(SIGINT, SIG_IGN);
844 if (chdir(mailname) == -1) {
845 n_err(_("Cannot change directory to %s\n"),
846 n_shexp_quote_cp(mailname, FAL0));
847 cwrelse(&cw);
848 safe_signal(SIGINT, saveint);
849 goto jleave;
852 if (sigsetjmp(_maildir_jmp, 1) == 0) {
853 if (saveint != SIG_IGN)
854 safe_signal(SIGINT, &__maildircatch_hold);
855 maildir_update();
858 safe_signal(SIGINT, saveint);
860 if (cwret(&cw) == STOP)
861 n_panic(_("Cannot change back to current directory"));
862 cwrelse(&cw);
863 rv = TRU1;
864 jleave:
865 NYD_LEAVE;
866 return rv;
869 FL enum okay
870 maildir_append(char const *name, FILE *fp, long offset)
872 char *buf, *bp, *lp;
873 size_t bufsize, buflen, cnt;
874 off_t off1 = -1, offs;
875 long size;
876 int flag;
877 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
878 enum okay rv;
879 NYD_ENTER;
881 if ((rv = mkmaildir(name)) != OKAY)
882 goto jleave;
884 buf = smalloc(bufsize = LINESIZE); /* TODO line pool; signals */
885 buflen = 0;
886 cnt = fsize(fp);
887 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
888 size = 0;
890 srelax_hold();
891 for (flag = MNEW, state = _NLSEP;;) {
892 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
894 if (bp == NULL ||
895 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
896 is_head(buf, buflen, FAL0))) {
897 if (off1 != (off_t)-1) {
898 if ((rv = maildir_append1(name, fp, off1, size, flag)) == STOP)
899 goto jfree;
900 srelax();
901 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
902 rv = STOP;
903 goto jfree;
906 off1 = offs + buflen;
907 size = 0;
908 state = _INHEAD;
909 flag = MNEW;
911 if (bp == NULL)
912 break;
913 } else
914 size += buflen;
915 offs += buflen;
917 state &= ~_NLSEP;
918 if (buf[0] == '\n') {
919 state &= ~_INHEAD;
920 state |= _NLSEP;
921 } else if (state & _INHEAD) {
922 if (!ascncasecmp(buf, "status", 6)) {
923 lp = buf + 6;
924 while (whitechar(*lp))
925 ++lp;
926 if (*lp == ':')
927 while (*++lp != '\0')
928 switch (*lp) {
929 case 'R':
930 flag |= MREAD;
931 break;
932 case 'O':
933 flag &= ~MNEW;
934 break;
936 } else if (!ascncasecmp(buf, "x-status", 8)) {
937 lp = buf + 8;
938 while (whitechar(*lp))
939 ++lp;
940 if (*lp == ':') {
941 while (*++lp != '\0')
942 switch (*lp) {
943 case 'F':
944 flag |= MFLAGGED;
945 break;
946 case 'A':
947 flag |= MANSWERED;
948 break;
949 case 'T':
950 flag |= MDRAFTED;
951 break;
957 assert(rv == OKAY);
958 jfree:
959 srelax_rele();
960 free(buf);
961 jleave:
962 NYD_LEAVE;
963 return rv;
966 FL enum okay
967 maildir_remove(char const *name)
969 enum okay rv = STOP;
970 NYD_ENTER;
972 if (subdir_remove(name, "tmp") == STOP ||
973 subdir_remove(name, "new") == STOP ||
974 subdir_remove(name, "cur") == STOP)
975 goto jleave;
976 if (rmdir(name) == -1) {
977 n_perr(name, 0);
978 goto jleave;
980 rv = OKAY;
981 jleave:
982 NYD_LEAVE;
983 return rv;
986 /* s-it-mode */