Bump s-nail v14.6.1
[s-mailx.git] / maildir.c
blobd2a8d1fba432a3903a2d2c8682e5cc289eb32fde
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, int nmail,
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 subdir(char const *name, char const *sub, int nmail);
72 static void _maildir_append(char const *name, char const *sub,
73 char const *fn);
75 static void readin(char const *name, struct message *m);
77 static void maildir_update(void);
79 static void move(struct message *m);
81 static char * mkname(time_t t, enum mflag f, char const *pref);
83 static enum okay maildir_append1(char const *name, FILE *fp, off_t off1,
84 long size, enum mflag flag);
86 static enum okay trycreate(char const *name);
88 static enum okay mkmaildir(char const *name);
90 static struct message * mdlook(char const *name, struct message *data);
92 static void mktable(void);
94 static enum okay subdir_remove(char const *name, char const *sub);
96 static void
97 __maildircatch(int s)
99 NYD_X; /* Signal handler */
100 siglongjmp(_maildir_jmp, s);
103 static void
104 _cleantmp(void)
106 char dep[PATH_MAX];
107 struct stat st;
108 time_t now;
109 DIR *dirp;
110 struct dirent *dp;
111 NYD_ENTER;
113 if ((dirp = opendir("tmp")) == NULL)
114 goto jleave;
116 time(&now);
117 while ((dp = readdir(dirp)) != NULL) {
118 if (dp->d_name[0] == '.')
119 continue;
120 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
121 if (stat(dep, &st) < 0)
122 continue;
123 if (st.st_atime + 36*3600 < now)
124 unlink(dep);
126 closedir(dirp);
127 jleave:
128 NYD_LEAVE;
131 static int
132 maildir_setfile1(char const *name, int nmail, int omsgCount)
134 int i;
135 NYD_ENTER;
137 if (!nmail)
138 _cleantmp();
140 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
141 if ((i = subdir(name, "cur", nmail)) != 0)
142 goto jleave;
143 if ((i = subdir(name, "new", nmail)) != 0)
144 goto jleave;
145 _maildir_append(name, NULL, NULL);
146 for (i = (nmail ? omsgCount : 0); i < msgCount; ++i)
147 readin(name, message + i);
148 if (nmail) {
149 if (msgCount > omsgCount)
150 qsort(&message[omsgCount], msgCount - omsgCount, sizeof *message,
151 &mdcmp);
152 } else if (msgCount)
153 qsort(message, msgCount, sizeof *message, &mdcmp);
154 i = msgCount;
155 jleave:
156 NYD_LEAVE;
157 return i;
160 static int
161 mdcmp(void const *a, void const *b)
163 struct message const *mpa = a, *mpb = b;
164 long i;
165 NYD_ENTER;
167 if ((i = mpa->m_time - mpb->m_time) == 0)
168 i = strcmp(mpa->m_maildir_file + 4, mpb->m_maildir_file + 4);
169 NYD_LEAVE;
170 return i;
173 static int
174 subdir(char const *name, char const *sub, int nmail)
176 DIR *dirp;
177 struct dirent *dp;
178 int rv;
179 NYD_ENTER;
181 if ((dirp = opendir(sub)) == NULL) {
182 fprintf(stderr, "Cannot open directory \"%s/%s\".\n", name, sub);
183 rv = -1;
184 goto jleave;
186 if (access(sub, W_OK) == -1)
187 mb.mb_perm = 0;
188 while ((dp = readdir(dirp)) != NULL) {
189 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
190 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
191 continue;
192 if (dp->d_name[0] == '.')
193 continue;
194 if (!nmail || mdlook(dp->d_name, NULL) == NULL)
195 _maildir_append(name, sub, dp->d_name);
197 closedir(dirp);
198 rv = 0;
199 jleave:
200 NYD_LEAVE;
201 return rv;
204 static void
205 _maildir_append(char const *name, char const *sub, char const *fn)
207 struct message *m;
208 size_t sz, i;
209 time_t t = 0;
210 enum mflag f = MUSED | MNOFROM | MNEWEST;
211 char const *cp;
212 char *xp;
213 NYD_ENTER;
214 UNUSED(name);
216 if (fn != NULL && sub != NULL) {
217 if (!strcmp(sub, "new"))
218 f |= MNEW;
219 t = strtol(fn, &xp, 10);
220 if ((cp = strrchr(xp, ',')) != NULL && PTRCMP(cp, >, xp + 2) &&
221 cp[-1] == '2' && cp[-2] == ':') {
222 while (*++cp != '\0') {
223 switch (*cp) {
224 case 'F':
225 f |= MFLAGGED;
226 break;
227 case 'R':
228 f |= MANSWERED;
229 break;
230 case 'S':
231 f |= MREAD;
232 break;
233 case 'T':
234 f |= MDELETED;
235 break;
236 case 'D':
237 f |= MDRAFT;
238 break;
244 /* Ensure room (and a NULLified last entry) */
245 ++msgCount;
246 message_append(NULL);
247 --msgCount;
249 if (fn == NULL || sub == NULL)
250 goto jleave;
252 m = message + msgCount++;
253 i = strlen(fn);
254 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 1 +1);
255 memcpy(m->m_maildir_file, sub, sz);
256 m->m_maildir_file[sz] = '/';
257 memcpy(m->m_maildir_file + sz + 1, fn, i +1);
258 m->m_time = t;
259 m->m_flag = f;
260 m->m_maildir_hash = ~pjw(fn);
261 jleave:
262 NYD_LEAVE;
263 return;
266 static void
267 readin(char const *name, struct message *m)
269 char *buf;
270 size_t bufsize, buflen, cnt;
271 long size = 0, lines = 0;
272 off_t offset;
273 FILE *fp;
274 int emptyline = 0;
275 NYD_ENTER;
277 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
278 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
279 name, m->m_maildir_file, (int)PTR2SIZE(m - message + 1));
280 m->m_flag |= MHIDDEN;
281 goto jleave;
284 cnt = fsize(fp);
285 fseek(mb.mb_otf, 0L, SEEK_END);
286 offset = ftell(mb.mb_otf);
287 buf = smalloc(bufsize = LINESIZE);
288 buflen = 0;
289 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
290 /* Since we simply copy over data without doing any transfer
291 * encoding reclassification/adjustment we *have* to perform
292 * RFC 4155 compliant From_ quoting here */
293 if (is_head(buf, buflen)) {
294 putc('>', mb.mb_otf);
295 ++size;
297 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
298 emptyline = (*buf == '\n');
299 ++lines;
301 free(buf);
302 if (!emptyline) {
303 putc('\n', mb.mb_otf);
304 ++lines;
305 ++size;
308 Fclose(fp);
309 fflush(mb.mb_otf);
310 m->m_size = m->m_xsize = size;
311 m->m_lines = m->m_xlines = lines;
312 m->m_block = mailx_blockof(offset);
313 m->m_offset = mailx_offsetof(offset);
314 substdate(m);
315 jleave:
316 NYD_LEAVE;
319 static void
320 maildir_update(void)
322 struct message *m;
323 int dodel, c, gotcha = 0, held = 0, modflags = 0;
324 NYD_ENTER;
326 if (mb.mb_perm == 0)
327 goto jfree;
329 if (!edit) {
330 holdbits();
331 for (m = message, c = 0; PTRCMP(m, <, message + msgCount); ++m) {
332 if (m->m_flag & MBOX)
333 c++;
335 if (c > 0)
336 if (makembox() == STOP)
337 goto jbypass;
339 for (m = message, gotcha = 0, held = 0; PTRCMP(m, <, message + msgCount);
340 ++m) {
341 if (edit)
342 dodel = m->m_flag & MDELETED;
343 else
344 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
345 if (dodel) {
346 if (unlink(m->m_maildir_file) < 0)
347 fprintf(stderr, /* TODO tr */
348 "Cannot delete file \"%s/%s\" for message %d.\n",
349 mailname, m->m_maildir_file, (int)PTR2SIZE(m - message + 1));
350 else
351 ++gotcha;
352 } else {
353 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS) ||
354 (m->m_flag & (MNEW | MBOXED | MSAVED | MSTATUS | MFLAG |
355 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))) {
356 move(m);
357 ++modflags;
359 ++held;
362 jbypass:
363 if ((gotcha || modflags) && edit) {
364 printf(tr(168, "\"%s\" "), displayname);
365 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
366 ? tr(170, "complete\n") : tr(212, "updated.\n"));
367 } else if (held && !edit && mb.mb_perm != 0) {
368 if (held == 1)
369 printf(tr(155, "Held 1 message in %s\n"), displayname);
370 else
371 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
373 fflush(stdout);
374 jfree:
375 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
376 free(m->m_maildir_file);
377 NYD_LEAVE;
380 static void
381 move(struct message *m)
383 char *fn, *new;
384 NYD_ENTER;
386 fn = mkname(0, m->m_flag, m->m_maildir_file + 4);
387 new = savecat("cur/", fn);
388 if (!strcmp(m->m_maildir_file, new))
389 goto jleave;
390 if (link(m->m_maildir_file, new) == -1) {
391 fprintf(stderr, /* TODO tr */
392 "Cannot link \"%s/%s\" to \"%s/%s\": message %d not touched.\n",
393 mailname, m->m_maildir_file, mailname, new,
394 (int)PTR2SIZE(m - message + 1));
395 goto jleave;
397 if (unlink(m->m_maildir_file) == -1)
398 fprintf(stderr, /* TODO tr */"Cannot unlink \"%s/%s\".\n",
399 mailname, m->m_maildir_file);
400 jleave:
401 NYD_LEAVE;
404 static char *
405 mkname(time_t t, enum mflag f, char const *pref)
407 static unsigned long cnt;
408 static pid_t mypid; /* XXX This should possibly be global, somehow */
409 static char *node;
411 char *cp;
412 int size, n, i;
413 NYD_ENTER;
415 if (pref == NULL) {
416 if (mypid == 0)
417 mypid = getpid();
418 if (node == NULL) {
419 cp = nodename(0);
420 n = size = 0;
421 do {
422 if (UICMP(32, n, <, size + 8))
423 node = srealloc(node, size += 20);
424 switch (*cp) {
425 case '/':
426 node[n++] = '\\', node[n++] = '0',
427 node[n++] = '5', node[n++] = '7';
428 break;
429 case ':':
430 node[n++] = '\\', node[n++] = '0',
431 node[n++] = '7', node[n++] = '2';
432 break;
433 default:
434 node[n++] = *cp;
436 } while (*cp++ != '\0');
438 size = 60 + strlen(node);
439 cp = salloc(size);
440 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
441 (ul_it)t, (ul_it)mypid, ++cnt, node);
442 } else {
443 size = (n = strlen(pref)) + 13;
444 cp = salloc(size);
445 memcpy(cp, pref, n +1);
446 for (i = n; i > 3; --i)
447 if (cp[i - 1] == ',' && cp[i - 2] == '2' && cp[i - 3] == ':') {
448 n = i;
449 break;
451 if (i <= 3) {
452 memcpy(cp + n, ":2,", 3 +1);
453 n += 3;
456 if (n < size - 7) {
457 if (f & MDRAFTED)
458 cp[n++] = 'D';
459 if (f & MFLAGGED)
460 cp[n++] = 'F';
461 if (f & MANSWERED)
462 cp[n++] = 'R';
463 if (f & MREAD)
464 cp[n++] = 'S';
465 if (f & MDELETED)
466 cp[n++] = 'T';
467 cp[n] = '\0';
469 NYD_LEAVE;
470 return cp;
473 static enum okay
474 maildir_append1(char const *name, FILE *fp, off_t off1, long size,
475 enum mflag flag)
477 int const attempts = 43200; /* XXX no magic */
478 char buf[4096], *fn, *tmp, *new;
479 struct stat st;
480 FILE *op;
481 long n, z;
482 int i;
483 time_t now;
484 enum okay rv = STOP;
485 NYD_ENTER;
487 /* Create a unique temporary file */
488 for (i = 0;; sleep(1), ++i) {
489 if (i >= attempts) {
490 fprintf(stderr, tr(198,
491 "Can't create an unique file name in \"%s/tmp\".\n"), name);
492 goto jleave;
495 time(&now);
496 fn = mkname(now, flag, NULL);
497 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
498 snprintf(tmp, n, "%s/tmp/%s", name, fn);
499 if (stat(tmp, &st) >= 0 || errno != ENOENT)
500 continue;
502 /* Use "wx" for O_EXCL */
503 if ((op = Fopen(tmp, "wx")) != NULL)
504 break;
507 if (fseek(fp, off1, SEEK_SET) == -1)
508 goto jtmperr;
509 while (size > 0) {
510 z = size > (long)sizeof buf ? (long)sizeof buf : size;
511 if ((n = fread(buf, 1, z, fp)) != z ||
512 (size_t)n != fwrite(buf, 1, n, op)) {
513 jtmperr:
514 fprintf(stderr, "Error writing to \"%s\".\n", tmp); /* TODO tr */
515 Fclose(op);
516 unlink(tmp);
517 goto jleave;
519 size -= n;
521 Fclose(op);
523 new = salloc(n = strlen(name) + strlen(fn) + 6);
524 snprintf(new, n, "%s/new/%s", name, fn);
525 if (link(tmp, new) == -1) {
526 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);/* TODO tr */
527 goto jleave;
529 if (unlink(tmp) == -1)
530 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp); /* TODO tr */
531 rv = OKAY;
532 jleave:
533 NYD_LEAVE;
534 return rv;
537 static enum okay
538 trycreate(char const *name)
540 struct stat st;
541 enum okay rv = STOP;
542 NYD_ENTER;
544 if (!stat(name, &st)) {
545 if (!S_ISDIR(st.st_mode)) {
546 fprintf(stderr, "\"%s\" is not a directory.\n", name);/* TODO tr */
547 goto jleave;
549 } else if (makedir(name) != OKAY) {
550 fprintf(stderr, "Cannot create directory \"%s\".\n", name);/* TODO tr */
551 goto jleave;
552 } else
553 ++imap_created_mailbox;
554 rv = OKAY;
555 jleave:
556 NYD_LEAVE;
557 return rv;
560 static enum okay
561 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
563 char *np;
564 size_t sz;
565 enum okay rv = STOP;
566 NYD_ENTER;
568 if (trycreate(name) == OKAY) {
569 np = ac_alloc((sz = strlen(name)) + 4 +1);
570 memcpy(np, name, sz);
571 memcpy(np + sz, "/tmp", 4 +1);
572 if (trycreate(np) == OKAY) {
573 memcpy(np + sz, "/new", 4 +1);
574 if (trycreate(np) == OKAY) {
575 memcpy(np + sz, "/cur", 4 +1);
576 rv = trycreate(np);
579 ac_free(np);
581 NYD_LEAVE;
582 return rv;
585 static struct message *
586 mdlook(char const *name, struct message *data)
588 struct mditem *md;
589 ui32_t c, h, n = 0;
590 NYD_ENTER;
592 if (data && data->m_maildir_hash)
593 h = ~data->m_maildir_hash;
594 else
595 h = pjw(name);
596 h %= _maildir_prime;
597 c = h;
598 md = _maildir_table + c;
600 while (md->md_data != NULL) {
601 if (!strcmp(md->md_data->m_maildir_file + 4, name))
602 break;
603 c += (n & 1) ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
604 n++;
605 while (c >= _maildir_prime)
606 c -= _maildir_prime;
607 md = _maildir_table + c;
609 if (data != NULL && md->md_data == NULL)
610 md->md_data = data;
611 NYD_LEAVE;
612 return md->md_data;
615 static void
616 mktable(void)
618 struct message *mp;
619 size_t i;
620 NYD_ENTER;
622 _maildir_prime = nextprime(msgCount);
623 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
624 for (mp = message, i = msgCount; i-- != 0;)
625 mdlook(mp->m_maildir_file + 4, mp);
626 NYD_LEAVE;
629 static enum okay
630 subdir_remove(char const *name, char const *sub)
632 char *path;
633 int pathsize, pathend, namelen, sublen, n;
634 DIR *dirp;
635 struct dirent *dp;
636 enum okay rv = STOP;
637 NYD_ENTER;
639 namelen = strlen(name);
640 sublen = strlen(sub);
641 path = smalloc(pathsize = namelen + sublen + 30 +1);
642 memcpy(path, name, namelen);
643 path[namelen] = '/';
644 memcpy(path + namelen + 1, sub, sublen);
645 path[namelen + sublen + 1] = '/';
646 path[pathend = namelen + sublen + 2] = '\0';
648 if ((dirp = opendir(path)) == NULL) {
649 perror(path);
650 goto jleave;
652 while ((dp = readdir(dirp)) != NULL) {
653 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
654 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
655 continue;
656 if (dp->d_name[0] == '.')
657 continue;
658 n = strlen(dp->d_name);
659 if (UICMP(32, pathend + n +1, >, pathsize))
660 path = srealloc(path, pathsize = pathend + n + 30);
661 memcpy(path + pathend, dp->d_name, n +1);
662 if (unlink(path) == -1) {
663 perror(path);
664 closedir(dirp);
665 goto jleave;
668 closedir(dirp);
670 path[pathend] = '\0';
671 if (rmdir(path) == -1) {
672 perror(path);
673 free(path);
674 goto jleave;
676 rv = OKAY;
677 jleave:
678 free(path);
679 NYD_LEAVE;
680 return rv;
683 FL int
684 maildir_setfile(char const * volatile name, int nmail, int isedit)
686 sighandler_type volatile saveint;
687 struct cw cw;
688 int i = -1, omsgCount;
689 NYD_ENTER;
691 omsgCount = msgCount;
692 if (cwget(&cw) == STOP) {
693 alert("Cannot open current directory");
694 goto jleave;
697 if (!nmail)
698 quit();
700 saveint = safe_signal(SIGINT, SIG_IGN);
702 if (!nmail) {
703 edit = (isedit != 0);
704 if (mb.mb_itf) {
705 fclose(mb.mb_itf);
706 mb.mb_itf = NULL;
708 if (mb.mb_otf) {
709 fclose(mb.mb_otf);
710 mb.mb_otf = NULL;
712 initbox(name);
713 mb.mb_type = MB_MAILDIR;
716 if (chdir(name) < 0) {
717 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);/*TODO tr*/
718 mb.mb_type = MB_VOID;
719 *mailname = '\0';
720 msgCount = 0;
721 cwrelse(&cw);
722 safe_signal(SIGINT, saveint);
723 goto jleave;
726 _maildir_table = NULL;
727 if (sigsetjmp(_maildir_jmp, 1) == 0) {
728 if (nmail)
729 mktable();
730 if (saveint != SIG_IGN)
731 safe_signal(SIGINT, &__maildircatch);
732 i = maildir_setfile1(name, nmail, omsgCount);
734 if (nmail && _maildir_table != NULL)
735 free(_maildir_table);
737 safe_signal(SIGINT, saveint);
739 if (i < 0) {
740 mb.mb_type = MB_VOID;
741 *mailname = '\0';
742 msgCount = 0;
745 if (cwret(&cw) == STOP)
746 panic("Cannot change back to current directory.");/* TODO tr */
747 cwrelse(&cw);
749 setmsize(msgCount);
750 if (nmail && mb.mb_sorted && msgCount > omsgCount) {
751 mb.mb_threaded = 0;
752 c_sort((void*)-1);
754 if (!nmail)
755 sawcom = FAL0;
756 if (!nmail && !edit && msgCount == 0) {
757 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
758 fprintf(stderr, tr(258, "No mail at %s\n"), name);
759 i = 1;
760 goto jleave;
762 if (nmail && msgCount > omsgCount)
763 newmailinfo(omsgCount);
764 i = 0;
765 jleave:
766 NYD_LEAVE;
767 return i;
770 FL void
771 maildir_quit(void)
773 sighandler_type saveint;
774 struct cw cw;
775 NYD_ENTER;
777 if (cwget(&cw) == STOP) {
778 alert("Cannot open current directory");/* TODO tr */
779 goto jleave;
782 saveint = safe_signal(SIGINT, SIG_IGN);
784 if (chdir(mailname) == -1) {
785 fprintf(stderr, "Cannot change directory to \"%s\".\n",/* TODO tr */
786 mailname);
787 cwrelse(&cw);
788 safe_signal(SIGINT, saveint);
789 goto jleave;
792 if (sigsetjmp(_maildir_jmp, 1) == 0) {
793 if (saveint != SIG_IGN)
794 safe_signal(SIGINT, &__maildircatch);
795 maildir_update();
798 safe_signal(SIGINT, saveint);
800 if (cwret(&cw) == STOP)
801 panic("Cannot change back to current directory."); /* TODO tr */
802 cwrelse(&cw);
803 jleave:
804 NYD_LEAVE;
807 FL enum okay
808 maildir_append(char const *name, FILE *fp)
810 char *buf, *bp, *lp;
811 size_t bufsize, buflen, cnt;
812 off_t off1 = -1, offs;
813 int inhead = 1, flag = MNEW | MNEWEST;
814 long size = 0;
815 enum okay rv;
816 NYD_ENTER;
818 if ((rv = mkmaildir(name)) != OKAY)
819 goto jleave;
821 buf = smalloc(bufsize = LINESIZE);
822 buflen = 0;
823 cnt = fsize(fp);
824 offs = ftell(fp);
825 do /* while (bp != NULL); */ {
826 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
827 if (bp == NULL || !strncmp(buf, "From ", 5)) {
828 if (off1 != (off_t)-1) {
829 rv = maildir_append1(name, fp, off1, size, flag);
830 if (rv == STOP)
831 goto jleave;
832 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
833 rv = STOP;
834 goto jleave;
837 off1 = offs + buflen;
838 size = 0;
839 inhead = 1;
840 flag = MNEW;
841 } else
842 size += buflen;
843 offs += buflen;
844 if (bp && buf[0] == '\n')
845 inhead = 0;
846 else if (bp && inhead && !ascncasecmp(buf, "status", 6)) {
847 lp = buf + 6;
848 while (whitechar(*lp))
849 ++lp;
850 if (*lp == ':')
851 while (*++lp != '\0')
852 switch (*lp) {
853 case 'R':
854 flag |= MREAD;
855 break;
856 case 'O':
857 flag &= ~MNEW;
858 break;
860 } else if (bp && inhead && !ascncasecmp(buf, "x-status", 8)) {
861 lp = buf + 8;
862 while (whitechar(*lp))
863 ++lp;
864 if (*lp == ':')
865 while (*++lp != '\0')
866 switch (*lp) {
867 case 'F':
868 flag |= MFLAGGED;
869 break;
870 case 'A':
871 flag |= MANSWERED;
872 break;
873 case 'T':
874 flag |= MDRAFTED;
875 break;
878 } while (bp != NULL);
879 free(buf);
880 assert(rv == OKAY);
881 jleave:
882 NYD_LEAVE;
883 return rv;
886 FL enum okay
887 maildir_remove(char const *name)
889 enum okay rv = STOP;
890 NYD_ENTER;
892 if (subdir_remove(name, "tmp") == STOP ||
893 subdir_remove(name, "new") == STOP ||
894 subdir_remove(name, "cur") == STOP)
895 goto jleave;
896 if (rmdir(name) == -1) {
897 perror(name);
898 goto jleave;
900 rv = OKAY;
901 jleave:
902 NYD_LEAVE;
903 return rv;
906 /* vim:set fenc=utf-8:s-it-mode */