setfile(): -# uses /dev/null: make this sole !ISREG exception..
[s-mailx.git] / maildir.c
blob7c352733770dce656934a6276bad5b7b426b82bc
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Maildir folder support. FIXME rewrite - why do we chdir(2)??
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <dirent.h>
46 struct mditem {
47 struct message *md_data;
48 unsigned md_hash;
51 static struct mditem *_maildir_table;
52 static ui32_t _maildir_prime;
53 static sigjmp_buf _maildir_jmp;
55 static void __maildircatch(int s);
57 /* Do some cleanup in the tmp/ subdir */
58 static void _cleantmp(void);
60 static int maildir_setfile1(char const *name, int nmail,
61 int omsgCount);
63 /* In combination with the names from mkname(), this comparison function
64 * ensures that the order of messages in a maildir folder created by mailx
65 * remains always the same. In effect, if a mbox folder is transferred to
66 * a maildir folder by 'copy *', the message order wont' change */
67 static int mdcmp(void const *a, void const *b);
69 static int subdir(char const *name, char const *sub, int nmail);
71 static void _maildir_append(char const *name, char const *sub,
72 char const *fn);
74 static void readin(char const *name, struct message *m);
76 static void maildir_update(void);
78 static void move(struct message *m);
80 static char * mkname(time_t t, enum mflag f, char const *pref);
82 static enum okay maildir_append1(char const *name, FILE *fp, off_t off1,
83 long size, enum mflag flag);
85 static enum okay trycreate(char const *name);
87 static enum okay mkmaildir(char const *name);
89 static struct message * mdlook(char const *name, struct message *data);
91 static void mktable(void);
93 static enum okay subdir_remove(char const *name, char const *sub);
95 static void
96 __maildircatch(int s)
98 NYD_X; /* Signal handler */
99 siglongjmp(_maildir_jmp, s);
102 static void
103 _cleantmp(void)
105 char dep[PATH_MAX];
106 struct stat st;
107 time_t now;
108 DIR *dirp;
109 struct dirent *dp;
110 NYD_ENTER;
112 if ((dirp = opendir("tmp")) == NULL)
113 goto jleave;
115 time(&now);
116 while ((dp = readdir(dirp)) != NULL) {
117 if (dp->d_name[0] == '.')
118 continue;
119 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
120 if (stat(dep, &st) < 0)
121 continue;
122 if (st.st_atime + 36*3600 < now)
123 unlink(dep);
125 closedir(dirp);
126 jleave:
127 NYD_LEAVE;
130 static int
131 maildir_setfile1(char const *name, int nmail, int omsgCount)
133 int i;
134 NYD_ENTER;
136 if (!nmail)
137 _cleantmp();
139 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
140 if ((i = subdir(name, "cur", nmail)) != 0)
141 goto jleave;
142 if ((i = subdir(name, "new", nmail)) != 0)
143 goto jleave;
144 _maildir_append(name, NULL, NULL);
145 for (i = nmail ? omsgCount : 0; i < msgCount; ++i)
146 readin(name, &message[i]);
147 if (nmail) {
148 if (msgCount > omsgCount)
149 qsort(&message[omsgCount], msgCount - omsgCount, sizeof *message,
150 &mdcmp);
151 } else if (msgCount)
152 qsort(message, msgCount, sizeof *message, &mdcmp);
153 i = msgCount;
154 jleave:
155 NYD_LEAVE;
156 return i;
159 static int
160 mdcmp(void const *a, void const *b)
162 struct message const *mpa = a, *mpb = b;
163 long i;
164 NYD_ENTER;
166 if ((i = mpa->m_time - mpb->m_time) == 0)
167 i = strcmp(mpa->m_maildir_file + 4, mpb->m_maildir_file + 4);
168 NYD_LEAVE;
169 return i;
172 static int
173 subdir(char const *name, char const *sub, int nmail)
175 DIR *dirp;
176 struct dirent *dp;
177 int rv;
178 NYD_ENTER;
180 if ((dirp = opendir(sub)) == NULL) {
181 fprintf(stderr, "Cannot open directory \"%s/%s\".\n", name, sub);
182 rv = -1;
183 goto jleave;
185 if (access(sub, W_OK) < 0)
186 mb.mb_perm = 0;
187 while ((dp = readdir(dirp)) != NULL) {
188 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
189 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
190 continue;
191 if (dp->d_name[0] == '.')
192 continue;
193 if (!nmail || mdlook(dp->d_name, NULL) == NULL)
194 _maildir_append(name, sub, dp->d_name);
196 closedir(dirp);
197 rv = 0;
198 jleave:
199 NYD_LEAVE;
200 return rv;
203 static void
204 _maildir_append(char const *name, char const *sub, char const *fn)
206 struct message *m;
207 size_t sz, i;
208 time_t t = 0;
209 enum mflag f = MUSED | MNOFROM | MNEWEST;
210 char const *cp;
211 char *xp;
212 NYD_ENTER;
213 UNUSED(name);
215 if (fn != NULL && sub != NULL) {
216 if (!strcmp(sub, "new"))
217 f |= MNEW;
218 t = strtol(fn, &xp, 10);
219 if ((cp = strrchr(xp, ',')) != NULL && PTRCMP(cp, >, xp + 2) &&
220 cp[-1] == '2' && cp[-2] == ':') {
221 while (*++cp != '\0') {
222 switch (*cp) {
223 case 'F':
224 f |= MFLAGGED;
225 break;
226 case 'R':
227 f |= MANSWERED;
228 break;
229 case 'S':
230 f |= MREAD;
231 break;
232 case 'T':
233 f |= MDELETED;
234 break;
235 case 'D':
236 f |= MDRAFT;
237 break;
243 /* Ensure room (and a NULLified last entry) */
244 ++msgCount;
245 message_append(NULL);
246 --msgCount;
248 if (fn == NULL || sub == NULL)
249 goto jleave;
251 m = message + msgCount++;
252 i = strlen(fn);
253 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 2);
254 memcpy(m->m_maildir_file, sub, sz);
255 m->m_maildir_file[sz] = '/';
256 memcpy(m->m_maildir_file + sz + 1, fn, i + 1);
257 m->m_time = t;
258 m->m_flag = f;
259 m->m_maildir_hash = ~pjw(fn);
260 jleave:
261 NYD_LEAVE;
262 return;
265 static void
266 readin(char const *name, struct message *m)
268 char *buf;
269 size_t bufsize, buflen, cnt;
270 long size = 0, lines = 0;
271 off_t offset;
272 FILE *fp;
273 int emptyline = 0;
274 NYD_ENTER;
276 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
277 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
278 name, m->m_maildir_file, (int)PTR2SIZE(m - message + 1));
279 m->m_flag |= MHIDDEN;
280 goto jleave;
283 cnt = fsize(fp);
284 fseek(mb.mb_otf, 0L, SEEK_END);
285 offset = ftell(mb.mb_otf);
286 buf = smalloc(bufsize = LINESIZE);
287 buflen = 0;
288 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
289 /* Since we simply copy over data without doing any transfer
290 * encoding reclassification/adjustment we *have* to perform
291 * RFC 4155 compliant From_ quoting here */
292 if (is_head(buf, buflen)) {
293 putc('>', mb.mb_otf);
294 ++size;
296 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
297 emptyline = (*buf == '\n');
298 ++lines;
300 free(buf);
301 if (!emptyline) {
302 putc('\n', mb.mb_otf);
303 ++lines;
304 ++size;
307 Fclose(fp);
308 fflush(mb.mb_otf);
309 m->m_size = m->m_xsize = size;
310 m->m_lines = m->m_xlines = lines;
311 m->m_block = mailx_blockof(offset);
312 m->m_offset = mailx_offsetof(offset);
313 substdate(m);
314 jleave:
315 NYD_LEAVE;
318 static void
319 maildir_update(void)
321 struct message *m;
322 int dodel, c, gotcha = 0, held = 0, modflags = 0;
323 NYD_ENTER;
325 if (mb.mb_perm == 0)
326 goto jfree;
328 if (!edit) {
329 holdbits();
330 for (m = message, c = 0; PTRCMP(m, <, message + msgCount); ++m) {
331 if (m->m_flag & MBOX)
332 c++;
334 if (c > 0)
335 if (makembox() == STOP)
336 goto jbypass;
338 for (m = message, gotcha = 0, held = 0; PTRCMP(m, <, message + msgCount);
339 ++m) {
340 if (edit)
341 dodel = m->m_flag & MDELETED;
342 else
343 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
344 if (dodel) {
345 if (unlink(m->m_maildir_file) < 0)
346 fprintf(stderr, /* TODO tr */
347 "Cannot delete file \"%s/%s\" for message %d.\n",
348 mailname, m->m_maildir_file, (int)PTR2SIZE(m - message + 1));
349 else
350 ++gotcha;
351 } else {
352 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS) ||
353 (m->m_flag & (MNEW | MBOXED | MSAVED | MSTATUS | MFLAG |
354 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))) {
355 move(m);
356 ++modflags;
358 ++held;
361 jbypass:
362 if ((gotcha || modflags) && edit) {
363 printf(tr(168, "\"%s\" "), displayname);
364 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
365 ? tr(170, "complete\n") : tr(212, "updated.\n"));
366 } else if (held && !edit && mb.mb_perm != 0) {
367 if (held == 1)
368 printf(tr(155, "Held 1 message in %s\n"), displayname);
369 else
370 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
372 fflush(stdout);
373 jfree:
374 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
375 free(m->m_maildir_file);
376 NYD_LEAVE;
379 static void
380 move(struct message *m)
382 char *fn, *new;
383 NYD_ENTER;
385 fn = mkname(0, m->m_flag, &m->m_maildir_file[4]);
386 new = savecat("cur/", fn);
387 if (!strcmp(m->m_maildir_file, new))
388 goto jleave;
389 if (link(m->m_maildir_file, new) < 0) {
390 fprintf(stderr, /* TODO tr */
391 "Cannot link \"%s/%s\" to \"%s/%s\": message %d not touched.\n",
392 mailname, m->m_maildir_file, mailname, new,
393 (int)PTR2SIZE(m - message + 1));
394 goto jleave;
396 if (unlink(m->m_maildir_file) < 0)
397 fprintf(stderr, /* TODO tr */"Cannot unlink \"%s/%s\".\n",
398 mailname, m->m_maildir_file);
399 jleave:
400 NYD_LEAVE;
403 static char *
404 mkname(time_t t, enum mflag f, char const *pref)
406 static unsigned long cnt;
407 static pid_t mypid; /* XXX This should possibly be global, somehow */
408 static char *node;
410 char *cp;
411 int size, n, i;
412 NYD_ENTER;
414 if (pref == NULL) {
415 if (mypid == 0)
416 mypid = getpid();
417 if (node == NULL) {
418 cp = nodename(0);
419 n = size = 0;
420 do {
421 if (UICMP(32, n, <, size + 8))
422 node = srealloc(node, size += 20);
423 switch (*cp) {
424 case '/':
425 node[n++] = '\\', node[n++] = '0',
426 node[n++] = '5', node[n++] = '7';
427 break;
428 case ':':
429 node[n++] = '\\', node[n++] = '0',
430 node[n++] = '7', node[n++] = '2';
431 break;
432 default:
433 node[n++] = *cp;
435 } while (*cp++);
437 size = 60 + strlen(node);
438 cp = salloc(size);
439 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
440 (ul_it)t, (ul_it)mypid, ++cnt, node);
441 } else {
442 size = (n = strlen(pref)) + 13;
443 cp = salloc(size);
444 memcpy(cp, pref, n + 1);
445 for (i = n; i > 3; --i)
446 if (cp[i - 1] == ',' && cp[i - 2] == '2' && cp[i - 3] == ':') {
447 n = i;
448 break;
450 if (i <= 3) {
451 memcpy(cp + n, ":2,", 4);
452 n += 3;
455 if (n < size - 7) {
456 if (f & MDRAFTED)
457 cp[n++] = 'D';
458 if (f & MFLAGGED)
459 cp[n++] = 'F';
460 if (f & MANSWERED)
461 cp[n++] = 'R';
462 if (f & MREAD)
463 cp[n++] = 'S';
464 if (f & MDELETED)
465 cp[n++] = 'T';
466 cp[n] = '\0';
468 NYD_LEAVE;
469 return cp;
472 static enum okay
473 maildir_append1(char const *name, FILE *fp, off_t off1, long size,
474 enum mflag flag)
476 int const attempts = 43200; /* XXX no magic */
477 char buf[4096], *fn, *tmp, *new;
478 struct stat st;
479 FILE *op;
480 long n, z;
481 int i;
482 time_t now;
483 enum okay rv = STOP;
484 NYD_ENTER;
486 /* Create a unique temporary file */
487 for (i = 0;; sleep(1), ++i) {
488 if (i >= attempts) {
489 fprintf(stderr, tr(198,
490 "Can't create an unique file name in \"%s/tmp\".\n"), name);
491 goto jleave;
494 time(&now);
495 fn = mkname(now, flag, NULL);
496 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
497 snprintf(tmp, n, "%s/tmp/%s", name, fn);
498 if (stat(tmp, &st) >= 0 || errno != ENOENT)
499 continue;
501 /* Use "wx" for O_EXCL */
502 if ((op = Fopen(tmp, "wx")) != NULL)
503 break;
506 if (fseek(fp, off1, SEEK_SET) < 0)
507 goto jtmperr;
508 while (size > 0) {
509 z = size > (long)sizeof buf ? (long)sizeof buf : size;
510 if ((n = fread(buf, 1, z, fp)) != z ||
511 (size_t)n != fwrite(buf, 1, n, op)) {
512 jtmperr:
513 fprintf(stderr, "Error writing to \"%s\".\n", tmp); /* TODO tr */
514 Fclose(op);
515 unlink(tmp);
516 goto jleave;
518 size -= n;
520 Fclose(op);
522 new = salloc(n = strlen(name) + strlen(fn) + 6);
523 snprintf(new, n, "%s/new/%s", name, fn);
524 if (link(tmp, new) < 0) {
525 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);/* TODO tr */
526 goto jleave;
528 if (unlink(tmp) < 0)
529 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp); /* TODO tr */
530 rv = OKAY;
531 jleave:
532 NYD_LEAVE;
533 return rv;
536 static enum okay
537 trycreate(char const *name)
539 struct stat st;
540 enum okay rv = STOP;
541 NYD_ENTER;
543 if (stat(name, &st) == 0) {
544 if (!S_ISDIR(st.st_mode)) {
545 fprintf(stderr, "\"%s\" is not a directory.\n", name);/* TODO tr */
546 goto jleave;
548 } else if (makedir(name) != OKAY) {
549 fprintf(stderr, "Cannot create directory \"%s\".\n", name);/* TODO tr */
550 goto jleave;
551 } else
552 ++imap_created_mailbox;
553 rv = OKAY;
554 jleave:
555 NYD_LEAVE;
556 return rv;
559 static enum okay
560 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
562 char *np;
563 size_t sz;
564 enum okay rv = STOP;
565 NYD_ENTER;
567 if (trycreate(name) == OKAY) {
568 np = ac_alloc((sz = strlen(name)) + 5);
569 memcpy(np, name, sz);
570 memcpy(np + sz, "/tmp", 5);
571 if (trycreate(np) == OKAY) {
572 strcpy(&np[sz], "/new");
573 if (trycreate(np) == OKAY) {
574 strcpy(&np[sz], "/cur");
575 rv = trycreate(np);
578 ac_free(np);
580 NYD_LEAVE;
581 return rv;
584 static struct message *
585 mdlook(char const *name, struct message *data)
587 struct mditem *md;
588 ui32_t c, h, n = 0;
589 NYD_ENTER;
591 if (data && data->m_maildir_hash)
592 h = ~data->m_maildir_hash;
593 else
594 h = pjw(name);
595 h %= _maildir_prime;
596 md = &_maildir_table[c = h];
598 while (md->md_data != NULL) {
599 if (!strcmp(&md->md_data->m_maildir_file[4], name))
600 break;
601 c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
602 n++;
603 while (c >= _maildir_prime)
604 c -= _maildir_prime;
605 md = &_maildir_table[c];
607 if (data != NULL && md->md_data == NULL)
608 md->md_data = data;
609 NYD_LEAVE;
610 return md->md_data ? md->md_data : NULL;
613 static void
614 mktable(void)
616 int i;
617 NYD_ENTER;
619 _maildir_prime = nextprime(msgCount);
620 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
621 for (i = 0; i < msgCount; i++)
622 mdlook(&message[i].m_maildir_file[4], &message[i]);
623 NYD_LEAVE;
626 static enum okay
627 subdir_remove(char const *name, char const *sub)
629 char *path;
630 int pathsize, pathend, namelen, sublen, n;
631 DIR *dirp;
632 struct dirent *dp;
633 enum okay rv = STOP;
634 NYD_ENTER;
636 namelen = strlen(name);
637 sublen = strlen(sub);
638 path = smalloc(pathsize = namelen + sublen + 30);
639 memcpy(path, name, namelen);
640 path[namelen] = '/';
641 memcpy(path + namelen + 1, sub, sublen);
642 path[namelen+sublen+1] = '/';
643 path[pathend = namelen + sublen + 2] = '\0';
645 if ((dirp = opendir(path)) == NULL) {
646 perror(path);
647 goto jleave;
649 while ((dp = readdir(dirp)) != NULL) {
650 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
651 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
652 continue;
653 if (dp->d_name[0] == '.')
654 continue;
655 n = strlen(dp->d_name);
656 if (UICMP(32, pathend + n + 1, >, pathsize))
657 path = srealloc(path, pathsize = pathend + n + 30);
658 memcpy(path + pathend, dp->d_name, n + 1);
659 if (unlink(path) < 0) {
660 perror(path);
661 closedir(dirp);
662 goto jleave;
665 closedir(dirp);
667 path[pathend] = '\0';
668 if (rmdir(path) < 0) {
669 perror(path);
670 free(path);
671 goto jleave;
673 rv = OKAY;
674 jleave:
675 free(path);
676 NYD_LEAVE;
677 return rv;
680 FL int
681 maildir_setfile(char const * volatile name, int nmail, int isedit)
683 sighandler_type volatile saveint;
684 struct cw cw;
685 int i = -1, omsgCount;
686 NYD_ENTER;
688 omsgCount = msgCount;
689 if (cwget(&cw) == STOP) {
690 alert("Cannot open current directory");
691 goto jleave;
694 if (!nmail)
695 quit();
697 saveint = safe_signal(SIGINT, SIG_IGN);
699 if (!nmail) {
700 edit = (isedit != 0);
701 if (mb.mb_itf) {
702 fclose(mb.mb_itf);
703 mb.mb_itf = NULL;
705 if (mb.mb_otf) {
706 fclose(mb.mb_otf);
707 mb.mb_otf = NULL;
709 initbox(name);
710 mb.mb_type = MB_MAILDIR;
713 if (chdir(name) < 0) {
714 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);/*TODO tr*/
715 mb.mb_type = MB_VOID;
716 *mailname = '\0';
717 msgCount = 0;
718 cwrelse(&cw);
719 safe_signal(SIGINT, saveint);
720 goto jleave;
723 _maildir_table = NULL;
724 if (sigsetjmp(_maildir_jmp, 1) == 0) {
725 if (nmail)
726 mktable();
727 if (saveint != SIG_IGN)
728 safe_signal(SIGINT, &__maildircatch);
729 i = maildir_setfile1(name, nmail, omsgCount);
731 if (nmail && _maildir_table != NULL)
732 free(_maildir_table);
734 safe_signal(SIGINT, saveint);
736 if (i < 0) {
737 mb.mb_type = MB_VOID;
738 *mailname = '\0';
739 msgCount = 0;
742 if (cwret(&cw) == STOP)
743 panic("Cannot change back to current directory.");/* TODO tr */
744 cwrelse(&cw);
746 setmsize(msgCount);
747 if (nmail && mb.mb_sorted && msgCount > omsgCount) {
748 mb.mb_threaded = 0;
749 c_sort((void*)-1);
751 if (!nmail)
752 sawcom = FAL0;
753 if (!nmail && !edit && msgCount == 0) {
754 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
755 fprintf(stderr, tr(258, "No mail at %s\n"), name);
756 i = 1;
757 goto jleave;
759 if (nmail && msgCount > omsgCount)
760 newmailinfo(omsgCount);
761 i = 0;
762 jleave:
763 NYD_LEAVE;
764 return i;
767 FL void
768 maildir_quit(void)
770 sighandler_type saveint;
771 struct cw cw;
772 NYD_ENTER;
774 if (cwget(&cw) == STOP) {
775 alert("Cannot open current directory");/* TODO tr */
776 goto jleave;
779 saveint = safe_signal(SIGINT, SIG_IGN);
781 if (chdir(mailname) < 0) {
782 fprintf(stderr, "Cannot change directory to \"%s\".\n",/* TODO tr */
783 mailname);
784 cwrelse(&cw);
785 safe_signal(SIGINT, saveint);
786 goto jleave;
789 if (sigsetjmp(_maildir_jmp, 1) == 0) {
790 if (saveint != SIG_IGN)
791 safe_signal(SIGINT, &__maildircatch);
792 maildir_update();
795 safe_signal(SIGINT, saveint);
797 if (cwret(&cw) == STOP)
798 panic("Cannot change back to current directory."); /* TODO tr */
799 cwrelse(&cw);
800 jleave:
801 NYD_LEAVE;
804 FL enum okay
805 maildir_append(char const *name, FILE *fp)
807 char *buf, *bp, *lp;
808 size_t bufsize, buflen, cnt;
809 off_t off1 = -1, offs;
810 int inhead = 1, flag = MNEW | MNEWEST;
811 long size = 0;
812 enum okay rv;
813 NYD_ENTER;
815 if ((rv = mkmaildir(name)) != OKAY)
816 goto jleave;
818 buf = smalloc(bufsize = LINESIZE);
819 buflen = 0;
820 cnt = fsize(fp);
821 offs = ftell(fp);
822 do /* while (bp != NULL); */ {
823 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
824 if (bp == NULL || !strncmp(buf, "From ", 5)) {
825 if (off1 != (off_t)-1) {
826 rv = maildir_append1(name, fp, off1, size, flag);
827 if (rv == STOP)
828 goto jleave;
829 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
830 rv = STOP;
831 goto jleave;
834 off1 = offs + buflen;
835 size = 0;
836 inhead = 1;
837 flag = MNEW;
838 } else
839 size += buflen;
840 offs += buflen;
841 if (bp && buf[0] == '\n')
842 inhead = 0;
843 else if (bp && inhead && !ascncasecmp(buf, "status", 6)) {
844 lp = &buf[6];
845 while (whitechar(*lp))
846 lp++;
847 if (*lp == ':')
848 while (*++lp != '\0')
849 switch (*lp) {
850 case 'R':
851 flag |= MREAD;
852 break;
853 case 'O':
854 flag &= ~MNEW;
855 break;
857 } else if (bp && inhead && !ascncasecmp(buf, "x-status", 8)) {
858 lp = &buf[8];
859 while (whitechar(*lp))
860 lp++;
861 if (*lp == ':')
862 while (*++lp != '\0')
863 switch (*lp) {
864 case 'F':
865 flag |= MFLAGGED;
866 break;
867 case 'A':
868 flag |= MANSWERED;
869 break;
870 case 'T':
871 flag |= MDRAFTED;
872 break;
875 } while (bp != NULL);
876 free(buf);
877 assert(rv == OKAY);
878 jleave:
879 NYD_LEAVE;
880 return rv;
883 FL enum okay
884 maildir_remove(char const *name)
886 enum okay rv = STOP;
887 NYD_ENTER;
889 if (subdir_remove(name, "tmp") == STOP ||
890 subdir_remove(name, "new") == STOP ||
891 subdir_remove(name, "cur") == STOP)
892 goto jleave;
893 if (rmdir(name) < 0) {
894 perror(name);
895 goto jleave;
897 rv = OKAY;
898 jleave:
899 NYD_LEAVE;
900 return rv;
903 /* vim:set fenc=utf-8:s-it-mode */