Fix *newmail* (oops, ",$s//" no-no-no, "%s//" yes!)..
[s-mailx.git] / maildir.c
blob8e1b885df3354d9df16f7678dfbec929ac96e2e8
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 - 2014 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.
41 #ifndef HAVE_AMALGAMATION
42 # include "nail.h"
43 #endif
45 #include <dirent.h>
47 struct mditem {
48 struct message *md_data;
49 unsigned md_hash;
52 static struct mditem *_maildir_table;
53 static ui32_t _maildir_prime;
54 static sigjmp_buf _maildir_jmp;
56 static void __maildircatch(int s);
58 /* Do some cleanup in the tmp/ subdir */
59 static void _cleantmp(void);
61 static int _maildir_setfile1(char const *name, enum fedit_mode fm,
62 int omsgCount);
64 /* In combination with the names from mkname(), this comparison function
65 * ensures that the order of messages in a maildir folder created by mailx
66 * remains always the same. In effect, if a mbox folder is transferred to
67 * a maildir folder by 'copy *', the message order wont' change */
68 static int mdcmp(void const *a, void const *b);
70 static int _maildir_subdir(char const *name, char const *sub,
71 enum fedit_mode fm);
73 static void _maildir_append(char const *name, char const *sub,
74 char const *fn);
76 static void readin(char const *name, struct message *m);
78 static void maildir_update(void);
80 static void move(struct message *m);
82 static char * mkname(time_t t, enum mflag f, char const *pref);
84 static enum okay maildir_append1(char const *name, FILE *fp, off_t off1,
85 long size, enum mflag flag);
87 static enum okay trycreate(char const *name);
89 static enum okay mkmaildir(char const *name);
91 static struct message * mdlook(char const *name, struct message *data);
93 static void mktable(void);
95 static enum okay subdir_remove(char const *name, char const *sub);
97 static void
98 __maildircatch(int s)
100 NYD_X; /* Signal handler */
101 siglongjmp(_maildir_jmp, s);
104 static void
105 _cleantmp(void)
107 char dep[PATH_MAX];
108 struct stat st;
109 time_t now;
110 DIR *dirp;
111 struct dirent *dp;
112 NYD_ENTER;
114 if ((dirp = opendir("tmp")) == NULL)
115 goto jleave;
117 time(&now);
118 while ((dp = readdir(dirp)) != NULL) {
119 if (dp->d_name[0] == '.')
120 continue;
121 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
122 if (stat(dep, &st) < 0)
123 continue;
124 if (st.st_atime + 36*3600 < now)
125 unlink(dep);
127 closedir(dirp);
128 jleave:
129 NYD_LEAVE;
132 static int
133 _maildir_setfile1(char const *name, enum fedit_mode fm, int omsgCount)
135 int i;
136 NYD_ENTER;
138 if (!(fm & FEDIT_NEWMAIL))
139 _cleantmp();
141 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
142 if ((i = _maildir_subdir(name, "cur", fm)) != 0)
143 goto jleave;
144 if ((i = _maildir_subdir(name, "new", fm)) != 0)
145 goto jleave;
146 _maildir_append(name, NULL, NULL);
147 for (i = ((fm & FEDIT_NEWMAIL) ? omsgCount : 0); i < msgCount; ++i)
148 readin(name, message + i);
149 if (fm & FEDIT_NEWMAIL) {
150 if (msgCount > omsgCount)
151 qsort(&message[omsgCount], msgCount - omsgCount, sizeof *message,
152 &mdcmp);
153 } else if (msgCount)
154 qsort(message, msgCount, sizeof *message, &mdcmp);
155 i = msgCount;
156 jleave:
157 NYD_LEAVE;
158 return i;
161 static int
162 mdcmp(void const *a, void const *b)
164 struct message const *mpa = a, *mpb = b;
165 long i;
166 NYD_ENTER;
168 if ((i = mpa->m_time - mpb->m_time) == 0)
169 i = strcmp(mpa->m_maildir_file + 4, mpb->m_maildir_file + 4);
170 NYD_LEAVE;
171 return i;
174 static int
175 _maildir_subdir(char const *name, char const *sub, enum fedit_mode fm)
177 DIR *dirp;
178 struct dirent *dp;
179 int rv;
180 NYD_ENTER;
182 if ((dirp = opendir(sub)) == NULL) {
183 fprintf(stderr, "Cannot open directory \"%s/%s\".\n", name, sub);
184 rv = -1;
185 goto jleave;
187 if (access(sub, W_OK) == -1)
188 mb.mb_perm = 0;
189 while ((dp = readdir(dirp)) != NULL) {
190 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
191 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
192 continue;
193 if (dp->d_name[0] == '.')
194 continue;
195 if (!(fm & FEDIT_NEWMAIL) || mdlook(dp->d_name, NULL) == NULL)
196 _maildir_append(name, sub, dp->d_name);
198 closedir(dirp);
199 rv = 0;
200 jleave:
201 NYD_LEAVE;
202 return rv;
205 static void
206 _maildir_append(char const *name, char const *sub, char const *fn)
208 struct message *m;
209 size_t sz, i;
210 time_t t = 0;
211 enum mflag f = MUSED | MNOFROM | MNEWEST;
212 char const *cp;
213 char *xp;
214 NYD_ENTER;
215 UNUSED(name);
217 if (fn != NULL && sub != NULL) {
218 if (!strcmp(sub, "new"))
219 f |= MNEW;
220 t = strtol(fn, &xp, 10);
221 if ((cp = strrchr(xp, ',')) != NULL && PTRCMP(cp, >, xp + 2) &&
222 cp[-1] == '2' && cp[-2] == ':') {
223 while (*++cp != '\0') {
224 switch (*cp) {
225 case 'F':
226 f |= MFLAGGED;
227 break;
228 case 'R':
229 f |= MANSWERED;
230 break;
231 case 'S':
232 f |= MREAD;
233 break;
234 case 'T':
235 f |= MDELETED;
236 break;
237 case 'D':
238 f |= MDRAFT;
239 break;
245 /* Ensure room (and a NULLified last entry) */
246 ++msgCount;
247 message_append(NULL);
248 --msgCount;
250 if (fn == NULL || sub == NULL)
251 goto jleave;
253 m = message + msgCount++;
254 i = strlen(fn);
255 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 1 +1);
256 memcpy(m->m_maildir_file, sub, sz);
257 m->m_maildir_file[sz] = '/';
258 memcpy(m->m_maildir_file + sz + 1, fn, i +1);
259 m->m_time = t;
260 m->m_flag = f;
261 m->m_maildir_hash = ~pjw(fn);
262 jleave:
263 NYD_LEAVE;
264 return;
267 static void
268 readin(char const *name, struct message *m)
270 char *buf;
271 size_t bufsize, buflen, cnt;
272 long size = 0, lines = 0;
273 off_t offset;
274 FILE *fp;
275 int emptyline = 0;
276 NYD_ENTER;
278 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
279 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
280 name, m->m_maildir_file, (int)PTR2SIZE(m - message + 1));
281 m->m_flag |= MHIDDEN;
282 goto jleave;
285 cnt = fsize(fp);
286 fseek(mb.mb_otf, 0L, SEEK_END);
287 offset = ftell(mb.mb_otf);
288 buf = smalloc(bufsize = LINESIZE);
289 buflen = 0;
290 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
291 /* Since we simply copy over data without doing any transfer
292 * encoding reclassification/adjustment we *have* to perform
293 * RFC 4155 compliant From_ quoting here */
294 if (is_head(buf, buflen)) {
295 putc('>', mb.mb_otf);
296 ++size;
298 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
299 emptyline = (*buf == '\n');
300 ++lines;
302 free(buf);
303 if (!emptyline) {
304 putc('\n', mb.mb_otf);
305 ++lines;
306 ++size;
309 Fclose(fp);
310 fflush(mb.mb_otf);
311 m->m_size = m->m_xsize = size;
312 m->m_lines = m->m_xlines = lines;
313 m->m_block = mailx_blockof(offset);
314 m->m_offset = mailx_offsetof(offset);
315 substdate(m);
316 jleave:
317 NYD_LEAVE;
320 static void
321 maildir_update(void)
323 struct message *m;
324 int dodel, c, gotcha = 0, held = 0, modflags = 0;
325 NYD_ENTER;
327 if (mb.mb_perm == 0)
328 goto jfree;
330 if (!edit) {
331 holdbits();
332 for (m = message, c = 0; PTRCMP(m, <, message + msgCount); ++m) {
333 if (m->m_flag & MBOX)
334 c++;
336 if (c > 0)
337 if (makembox() == STOP)
338 goto jbypass;
340 for (m = message, gotcha = 0, held = 0; PTRCMP(m, <, message + msgCount);
341 ++m) {
342 if (edit)
343 dodel = m->m_flag & MDELETED;
344 else
345 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
346 if (dodel) {
347 if (unlink(m->m_maildir_file) < 0)
348 fprintf(stderr, /* TODO tr */
349 "Cannot delete file \"%s/%s\" for message %d.\n",
350 mailname, m->m_maildir_file, (int)PTR2SIZE(m - message + 1));
351 else
352 ++gotcha;
353 } else {
354 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS) ||
355 (m->m_flag & (MNEW | MBOXED | MSAVED | MSTATUS | MFLAG |
356 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))) {
357 move(m);
358 ++modflags;
360 ++held;
363 jbypass:
364 if ((gotcha || modflags) && edit) {
365 printf(_("\"%s\" "), displayname);
366 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
367 ? _("complete\n") : _("updated.\n"));
368 } else if (held && !edit && mb.mb_perm != 0) {
369 if (held == 1)
370 printf(_("Held 1 message in %s\n"), displayname);
371 else
372 printf(_("Held %d messages in %s\n"), held, displayname);
374 fflush(stdout);
375 jfree:
376 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
377 free(m->m_maildir_file);
378 NYD_LEAVE;
381 static void
382 move(struct message *m)
384 char *fn, *new;
385 NYD_ENTER;
387 fn = mkname(0, m->m_flag, m->m_maildir_file + 4);
388 new = savecat("cur/", fn);
389 if (!strcmp(m->m_maildir_file, new))
390 goto jleave;
391 if (link(m->m_maildir_file, new) == -1) {
392 fprintf(stderr, /* TODO tr */
393 "Cannot link \"%s/%s\" to \"%s/%s\": message %d not touched.\n",
394 mailname, m->m_maildir_file, mailname, new,
395 (int)PTR2SIZE(m - message + 1));
396 goto jleave;
398 if (unlink(m->m_maildir_file) == -1)
399 fprintf(stderr, /* TODO tr */"Cannot unlink \"%s/%s\".\n",
400 mailname, m->m_maildir_file);
401 jleave:
402 NYD_LEAVE;
405 static char *
406 mkname(time_t t, enum mflag f, char const *pref)
408 static unsigned long cnt;
409 static pid_t mypid; /* XXX This should possibly be global, somehow */
410 static char *node;
412 char *cp;
413 int size, n, i;
414 NYD_ENTER;
416 if (pref == NULL) {
417 if (mypid == 0)
418 mypid = getpid();
419 if (node == NULL) {
420 cp = nodename(0);
421 n = size = 0;
422 do {
423 if (UICMP(32, n, <, size + 8))
424 node = srealloc(node, size += 20);
425 switch (*cp) {
426 case '/':
427 node[n++] = '\\', node[n++] = '0',
428 node[n++] = '5', node[n++] = '7';
429 break;
430 case ':':
431 node[n++] = '\\', node[n++] = '0',
432 node[n++] = '7', node[n++] = '2';
433 break;
434 default:
435 node[n++] = *cp;
437 } while (*cp++ != '\0');
439 size = 60 + strlen(node);
440 cp = salloc(size);
441 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
442 (ul_it)t, (ul_it)mypid, ++cnt, node);
443 } else {
444 size = (n = strlen(pref)) + 13;
445 cp = salloc(size);
446 memcpy(cp, pref, n +1);
447 for (i = n; i > 3; --i)
448 if (cp[i - 1] == ',' && cp[i - 2] == '2' && cp[i - 3] == ':') {
449 n = i;
450 break;
452 if (i <= 3) {
453 memcpy(cp + n, ":2,", 3 +1);
454 n += 3;
457 if (n < size - 7) {
458 if (f & MDRAFTED)
459 cp[n++] = 'D';
460 if (f & MFLAGGED)
461 cp[n++] = 'F';
462 if (f & MANSWERED)
463 cp[n++] = 'R';
464 if (f & MREAD)
465 cp[n++] = 'S';
466 if (f & MDELETED)
467 cp[n++] = 'T';
468 cp[n] = '\0';
470 NYD_LEAVE;
471 return cp;
474 static enum okay
475 maildir_append1(char const *name, FILE *fp, off_t off1, long size,
476 enum mflag flag)
478 int const attempts = 43200; /* XXX no magic */
479 char buf[4096], *fn, *tmp, *new;
480 struct stat st;
481 FILE *op;
482 long n, z;
483 int i;
484 time_t now;
485 enum okay rv = STOP;
486 NYD_ENTER;
488 /* Create a unique temporary file */
489 for (i = 0;; sleep(1), ++i) {
490 if (i >= attempts) {
491 fprintf(stderr, _(
492 "Can't create an unique file name in \"%s/tmp\".\n"), name);
493 goto jleave;
496 time(&now);
497 fn = mkname(now, flag, NULL);
498 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
499 snprintf(tmp, n, "%s/tmp/%s", name, fn);
500 if (stat(tmp, &st) >= 0 || errno != ENOENT)
501 continue;
503 /* Use "wx" for O_EXCL */
504 if ((op = Fopen(tmp, "wx")) != NULL)
505 break;
508 if (fseek(fp, off1, SEEK_SET) == -1)
509 goto jtmperr;
510 while (size > 0) {
511 z = size > (long)sizeof buf ? (long)sizeof buf : size;
512 if ((n = fread(buf, 1, z, fp)) != z ||
513 (size_t)n != fwrite(buf, 1, n, op)) {
514 jtmperr:
515 fprintf(stderr, "Error writing to \"%s\".\n", tmp); /* TODO tr */
516 Fclose(op);
517 unlink(tmp);
518 goto jleave;
520 size -= n;
522 Fclose(op);
524 new = salloc(n = strlen(name) + strlen(fn) + 6);
525 snprintf(new, n, "%s/new/%s", name, fn);
526 if (link(tmp, new) == -1) {
527 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);/* TODO tr */
528 goto jleave;
530 if (unlink(tmp) == -1)
531 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp); /* TODO tr */
532 rv = OKAY;
533 jleave:
534 NYD_LEAVE;
535 return rv;
538 static enum okay
539 trycreate(char const *name)
541 struct stat st;
542 enum okay rv = STOP;
543 NYD_ENTER;
545 if (!stat(name, &st)) {
546 if (!S_ISDIR(st.st_mode)) {
547 fprintf(stderr, "\"%s\" is not a directory.\n", name);/* TODO tr */
548 goto jleave;
550 } else if (makedir(name) != OKAY) {
551 fprintf(stderr, "Cannot create directory \"%s\".\n", name);/* TODO tr */
552 goto jleave;
553 } else
554 ++imap_created_mailbox;
555 rv = OKAY;
556 jleave:
557 NYD_LEAVE;
558 return rv;
561 static enum okay
562 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
564 char *np;
565 size_t sz;
566 enum okay rv = STOP;
567 NYD_ENTER;
569 if (trycreate(name) == OKAY) {
570 np = ac_alloc((sz = strlen(name)) + 4 +1);
571 memcpy(np, name, sz);
572 memcpy(np + sz, "/tmp", 4 +1);
573 if (trycreate(np) == OKAY) {
574 memcpy(np + sz, "/new", 4 +1);
575 if (trycreate(np) == OKAY) {
576 memcpy(np + sz, "/cur", 4 +1);
577 rv = trycreate(np);
580 ac_free(np);
582 NYD_LEAVE;
583 return rv;
586 static struct message *
587 mdlook(char const *name, struct message *data)
589 struct mditem *md;
590 ui32_t c, h, n = 0;
591 NYD_ENTER;
593 if (data && data->m_maildir_hash)
594 h = ~data->m_maildir_hash;
595 else
596 h = pjw(name);
597 h %= _maildir_prime;
598 c = h;
599 md = _maildir_table + c;
601 while (md->md_data != NULL) {
602 if (!strcmp(md->md_data->m_maildir_file + 4, name))
603 break;
604 c += (n & 1) ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
605 n++;
606 while (c >= _maildir_prime)
607 c -= _maildir_prime;
608 md = _maildir_table + c;
610 if (data != NULL && md->md_data == NULL)
611 md->md_data = data;
612 NYD_LEAVE;
613 return md->md_data;
616 static void
617 mktable(void)
619 struct message *mp;
620 size_t i;
621 NYD_ENTER;
623 _maildir_prime = nextprime(msgCount);
624 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
625 for (mp = message, i = msgCount; i-- != 0;)
626 mdlook(mp->m_maildir_file + 4, mp);
627 NYD_LEAVE;
630 static enum okay
631 subdir_remove(char const *name, char const *sub)
633 char *path;
634 int pathsize, pathend, namelen, sublen, n;
635 DIR *dirp;
636 struct dirent *dp;
637 enum okay rv = STOP;
638 NYD_ENTER;
640 namelen = strlen(name);
641 sublen = strlen(sub);
642 path = smalloc(pathsize = namelen + sublen + 30 +1);
643 memcpy(path, name, namelen);
644 path[namelen] = '/';
645 memcpy(path + namelen + 1, sub, sublen);
646 path[namelen + sublen + 1] = '/';
647 path[pathend = namelen + sublen + 2] = '\0';
649 if ((dirp = opendir(path)) == NULL) {
650 perror(path);
651 goto jleave;
653 while ((dp = readdir(dirp)) != NULL) {
654 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
655 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
656 continue;
657 if (dp->d_name[0] == '.')
658 continue;
659 n = strlen(dp->d_name);
660 if (UICMP(32, pathend + n +1, >, pathsize))
661 path = srealloc(path, pathsize = pathend + n + 30);
662 memcpy(path + pathend, dp->d_name, n +1);
663 if (unlink(path) == -1) {
664 perror(path);
665 closedir(dirp);
666 goto jleave;
669 closedir(dirp);
671 path[pathend] = '\0';
672 if (rmdir(path) == -1) {
673 perror(path);
674 free(path);
675 goto jleave;
677 rv = OKAY;
678 jleave:
679 free(path);
680 NYD_LEAVE;
681 return rv;
684 FL int
685 maildir_setfile(char const * volatile name, enum fedit_mode fm)
687 sighandler_type volatile saveint;
688 struct cw cw;
689 int i = -1, omsgCount;
690 NYD_ENTER;
692 omsgCount = msgCount;
693 if (cwget(&cw) == STOP) {
694 alert("Cannot open current directory");
695 goto jleave;
698 if (!(fm & FEDIT_NEWMAIL))
699 quit();
701 saveint = safe_signal(SIGINT, SIG_IGN);
703 if (!(fm & FEDIT_NEWMAIL)) {
704 edit = !(fm & FEDIT_SYSBOX);
705 if (mb.mb_itf) {
706 fclose(mb.mb_itf);
707 mb.mb_itf = NULL;
709 if (mb.mb_otf) {
710 fclose(mb.mb_otf);
711 mb.mb_otf = NULL;
713 initbox(name);
714 mb.mb_type = MB_MAILDIR;
717 if (chdir(name) < 0) {
718 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);/*TODO tr*/
719 mb.mb_type = MB_VOID;
720 *mailname = '\0';
721 msgCount = 0;
722 cwrelse(&cw);
723 safe_signal(SIGINT, saveint);
724 goto jleave;
727 _maildir_table = NULL;
728 if (sigsetjmp(_maildir_jmp, 1) == 0) {
729 if (fm & FEDIT_NEWMAIL)
730 mktable();
731 if (saveint != SIG_IGN)
732 safe_signal(SIGINT, &__maildircatch);
733 i = _maildir_setfile1(name, fm, omsgCount);
735 if ((fm & FEDIT_NEWMAIL) && _maildir_table != NULL)
736 free(_maildir_table);
738 safe_signal(SIGINT, saveint);
740 if (i < 0) {
741 mb.mb_type = MB_VOID;
742 *mailname = '\0';
743 msgCount = 0;
746 if (cwret(&cw) == STOP)
747 panic("Cannot change back to current directory.");/* TODO tr */
748 cwrelse(&cw);
750 setmsize(msgCount);
751 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted && msgCount > omsgCount) {
752 mb.mb_threaded = 0;
753 c_sort((void*)-1);
755 if (!(fm & FEDIT_NEWMAIL))
756 sawcom = FAL0;
757 if (!(fm & FEDIT_NEWMAIL) && (fm & FEDIT_SYSBOX) && msgCount == 0) {
758 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
759 fprintf(stderr, _("No mail at %s\n"), name);
760 i = 1;
761 goto jleave;
763 if ((fm & FEDIT_NEWMAIL) && msgCount > omsgCount)
764 newmailinfo(omsgCount);
765 i = 0;
766 jleave:
767 NYD_LEAVE;
768 return i;
771 FL void
772 maildir_quit(void)
774 sighandler_type saveint;
775 struct cw cw;
776 NYD_ENTER;
778 if (cwget(&cw) == STOP) {
779 alert("Cannot open current directory");/* TODO tr */
780 goto jleave;
783 saveint = safe_signal(SIGINT, SIG_IGN);
785 if (chdir(mailname) == -1) {
786 fprintf(stderr, "Cannot change directory to \"%s\".\n",/* TODO tr */
787 mailname);
788 cwrelse(&cw);
789 safe_signal(SIGINT, saveint);
790 goto jleave;
793 if (sigsetjmp(_maildir_jmp, 1) == 0) {
794 if (saveint != SIG_IGN)
795 safe_signal(SIGINT, &__maildircatch);
796 maildir_update();
799 safe_signal(SIGINT, saveint);
801 if (cwret(&cw) == STOP)
802 panic("Cannot change back to current directory."); /* TODO tr */
803 cwrelse(&cw);
804 jleave:
805 NYD_LEAVE;
808 FL enum okay
809 maildir_append(char const *name, FILE *fp)
811 char *buf, *bp, *lp;
812 size_t bufsize, buflen, cnt;
813 off_t off1 = -1, offs;
814 int inhead = 1, flag = MNEW | MNEWEST;
815 long size = 0;
816 enum okay rv;
817 NYD_ENTER;
819 if ((rv = mkmaildir(name)) != OKAY)
820 goto jleave;
822 buf = smalloc(bufsize = LINESIZE);
823 buflen = 0;
824 cnt = fsize(fp);
825 offs = ftell(fp);
826 do /* while (bp != NULL); */ {
827 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
828 if (bp == NULL || !strncmp(buf, "From ", 5)) {
829 if (off1 != (off_t)-1) {
830 rv = maildir_append1(name, fp, off1, size, flag);
831 if (rv == STOP)
832 goto jleave;
833 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
834 rv = STOP;
835 goto jleave;
838 off1 = offs + buflen;
839 size = 0;
840 inhead = 1;
841 flag = MNEW;
842 } else
843 size += buflen;
844 offs += buflen;
845 if (bp && buf[0] == '\n')
846 inhead = 0;
847 else if (bp && inhead && !ascncasecmp(buf, "status", 6)) {
848 lp = buf + 6;
849 while (whitechar(*lp))
850 ++lp;
851 if (*lp == ':')
852 while (*++lp != '\0')
853 switch (*lp) {
854 case 'R':
855 flag |= MREAD;
856 break;
857 case 'O':
858 flag &= ~MNEW;
859 break;
861 } else if (bp && inhead && !ascncasecmp(buf, "x-status", 8)) {
862 lp = buf + 8;
863 while (whitechar(*lp))
864 ++lp;
865 if (*lp == ':')
866 while (*++lp != '\0')
867 switch (*lp) {
868 case 'F':
869 flag |= MFLAGGED;
870 break;
871 case 'A':
872 flag |= MANSWERED;
873 break;
874 case 'T':
875 flag |= MDRAFTED;
876 break;
879 } while (bp != NULL);
880 free(buf);
881 assert(rv == OKAY);
882 jleave:
883 NYD_LEAVE;
884 return rv;
887 FL enum okay
888 maildir_remove(char const *name)
890 enum okay rv = STOP;
891 NYD_ENTER;
893 if (subdir_remove(name, "tmp") == STOP ||
894 subdir_remove(name, "new") == STOP ||
895 subdir_remove(name, "cur") == STOP)
896 goto jleave;
897 if (rmdir(name) == -1) {
898 perror(name);
899 goto jleave;
901 rv = OKAY;
902 jleave:
903 NYD_LEAVE;
904 return rv;
907 /* s-it-mode */