NYD: maildir.c
[s-mailx.git] / maildir.c
blob09b171bf5f2a37d1994cd94e52ac90eeebceb3ea
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 if (msgCount + 1 >= msgspace) {
244 int const chunk = 64;
245 message = srealloc(message, (msgspace += chunk) * sizeof *message);
246 memset(&message[msgCount], 0, chunk * sizeof *message);
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 + 2);
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) < 0) {
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) < 0)
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++);
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,", 4);
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) < 0)
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) < 0) {
526 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);/* TODO tr */
527 goto jleave;
529 if (unlink(tmp) < 0)
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) == 0) {
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)) + 5);
570 memcpy(np, name, sz);
571 memcpy(np + sz, "/tmp", 5);
572 if (trycreate(np) == OKAY) {
573 strcpy(&np[sz], "/new");
574 if (trycreate(np) == OKAY) {
575 strcpy(&np[sz], "/cur");
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 md = &_maildir_table[c = h];
599 while (md->md_data != NULL) {
600 if (!strcmp(&md->md_data->m_maildir_file[4], name))
601 break;
602 c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
603 n++;
604 while (c >= _maildir_prime)
605 c -= _maildir_prime;
606 md = &_maildir_table[c];
608 if (data != NULL && md->md_data == NULL)
609 md->md_data = data;
610 NYD_LEAVE;
611 return md->md_data ? md->md_data : NULL;
614 static void
615 mktable(void)
617 int i;
618 NYD_ENTER;
620 _maildir_prime = nextprime(msgCount);
621 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
622 for (i = 0; i < msgCount; i++)
623 mdlook(&message[i].m_maildir_file[4], &message[i]);
624 NYD_LEAVE;
627 static enum okay
628 subdir_remove(char const *name, char const *sub)
630 char *path;
631 int pathsize, pathend, namelen, sublen, n;
632 DIR *dirp;
633 struct dirent *dp;
634 enum okay rv = STOP;
635 NYD_ENTER;
637 namelen = strlen(name);
638 sublen = strlen(sub);
639 path = smalloc(pathsize = namelen + sublen + 30);
640 memcpy(path, name, namelen);
641 path[namelen] = '/';
642 memcpy(path + namelen + 1, sub, sublen);
643 path[namelen+sublen+1] = '/';
644 path[pathend = namelen + sublen + 2] = '\0';
646 if ((dirp = opendir(path)) == NULL) {
647 perror(path);
648 goto jleave;
650 while ((dp = readdir(dirp)) != NULL) {
651 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
652 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
653 continue;
654 if (dp->d_name[0] == '.')
655 continue;
656 n = strlen(dp->d_name);
657 if (UICMP(32, pathend + n + 1, >, pathsize))
658 path = srealloc(path, pathsize = pathend + n + 30);
659 memcpy(path + pathend, dp->d_name, n + 1);
660 if (unlink(path) < 0) {
661 perror(path);
662 closedir(dirp);
663 goto jleave;
666 closedir(dirp);
668 path[pathend] = '\0';
669 if (rmdir(path) < 0) {
670 perror(path);
671 free(path);
672 goto jleave;
674 rv = OKAY;
675 jleave:
676 free(path);
677 NYD_LEAVE;
678 return rv;
681 FL int
682 maildir_setfile(char const * volatile name, int nmail, int isedit)
684 sighandler_type volatile saveint;
685 struct cw cw;
686 int i = -1, omsgCount;
687 NYD_ENTER;
689 omsgCount = msgCount;
690 if (cwget(&cw) == STOP) {
691 alert("Cannot open current directory");
692 goto jleave;
695 if (!nmail)
696 quit();
698 saveint = safe_signal(SIGINT, SIG_IGN);
700 if (!nmail) {
701 edit = (isedit != 0);
702 if (mb.mb_itf) {
703 fclose(mb.mb_itf);
704 mb.mb_itf = NULL;
706 if (mb.mb_otf) {
707 fclose(mb.mb_otf);
708 mb.mb_otf = NULL;
710 initbox(name);
711 mb.mb_type = MB_MAILDIR;
714 if (chdir(name) < 0) {
715 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);/*TODO tr*/
716 mb.mb_type = MB_VOID;
717 *mailname = '\0';
718 msgCount = 0;
719 cwrelse(&cw);
720 safe_signal(SIGINT, saveint);
721 goto jleave;
724 _maildir_table = NULL;
725 if (sigsetjmp(_maildir_jmp, 1) == 0) {
726 if (nmail)
727 mktable();
728 if (saveint != SIG_IGN)
729 safe_signal(SIGINT, &__maildircatch);
730 i = maildir_setfile1(name, nmail, omsgCount);
732 if (nmail && _maildir_table != NULL)
733 free(_maildir_table);
735 safe_signal(SIGINT, saveint);
737 if (i < 0) {
738 mb.mb_type = MB_VOID;
739 *mailname = '\0';
740 msgCount = 0;
743 if (cwret(&cw) == STOP)
744 panic("Cannot change back to current directory.");/* TODO tr */
745 cwrelse(&cw);
747 setmsize(msgCount);
748 if (nmail && mb.mb_sorted && msgCount > omsgCount) {
749 mb.mb_threaded = 0;
750 sort((void*)-1);
752 if (!nmail)
753 sawcom = FAL0;
754 if (!nmail && !edit && msgCount == 0) {
755 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
756 fprintf(stderr, tr(258, "No mail at %s\n"), name);
757 i = 1;
758 goto jleave;
760 if (nmail && msgCount > omsgCount)
761 newmailinfo(omsgCount);
762 i = 0;
763 jleave:
764 NYD_LEAVE;
765 return i;
768 FL void
769 maildir_quit(void)
771 sighandler_type saveint;
772 struct cw cw;
773 NYD_ENTER;
775 if (cwget(&cw) == STOP) {
776 alert("Cannot open current directory");/* TODO tr */
777 goto jleave;
780 saveint = safe_signal(SIGINT, SIG_IGN);
782 if (chdir(mailname) < 0) {
783 fprintf(stderr, "Cannot change directory to \"%s\".\n",/* TODO tr */
784 mailname);
785 cwrelse(&cw);
786 safe_signal(SIGINT, saveint);
787 goto jleave;
790 if (sigsetjmp(_maildir_jmp, 1) == 0) {
791 if (saveint != SIG_IGN)
792 safe_signal(SIGINT, &__maildircatch);
793 maildir_update();
796 safe_signal(SIGINT, saveint);
798 if (cwret(&cw) == STOP)
799 panic("Cannot change back to current directory."); /* TODO tr */
800 cwrelse(&cw);
801 jleave:
802 NYD_LEAVE;
805 FL enum okay
806 maildir_append(char const *name, FILE *fp)
808 char *buf, *bp, *lp;
809 size_t bufsize, buflen, cnt;
810 off_t off1 = -1, offs;
811 int inhead = 1, flag = MNEW | MNEWEST;
812 long size = 0;
813 enum okay rv;
814 NYD_ENTER;
816 if ((rv = mkmaildir(name)) != OKAY)
817 goto jleave;
819 buf = smalloc(bufsize = LINESIZE);
820 buflen = 0;
821 cnt = fsize(fp);
822 offs = ftell(fp);
823 do /* while (bp != NULL); */ {
824 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
825 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
826 if (off1 != (off_t)-1) {
827 rv = maildir_append1(name, fp, off1, size, flag);
828 if (rv == STOP)
829 goto jleave;
830 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
831 rv = STOP;
832 goto jleave;
835 off1 = offs + buflen;
836 size = 0;
837 inhead = 1;
838 flag = MNEW;
839 } else
840 size += buflen;
841 offs += buflen;
842 if (bp && buf[0] == '\n')
843 inhead = 0;
844 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
845 lp = &buf[6];
846 while (whitechar(*lp))
847 lp++;
848 if (*lp == ':')
849 while (*++lp != '\0')
850 switch (*lp) {
851 case 'R':
852 flag |= MREAD;
853 break;
854 case 'O':
855 flag &= ~MNEW;
856 break;
858 } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) {
859 lp = &buf[8];
860 while (whitechar(*lp))
861 lp++;
862 if (*lp == ':')
863 while (*++lp != '\0')
864 switch (*lp) {
865 case 'F':
866 flag |= MFLAGGED;
867 break;
868 case 'A':
869 flag |= MANSWERED;
870 break;
871 case 'T':
872 flag |= MDRAFTED;
873 break;
876 } while (bp != NULL);
877 free(buf);
878 assert(rv == OKAY);
879 jleave:
880 NYD_LEAVE;
881 return rv;
884 FL enum okay
885 maildir_remove(char const *name)
887 enum okay rv = STOP;
888 NYD_ENTER;
890 if (subdir_remove(name, "tmp") == STOP ||
891 subdir_remove(name, "new") == STOP ||
892 subdir_remove(name, "cur") == STOP)
893 goto jleave;
894 if (rmdir(name) < 0) {
895 perror(name);
896 goto jleave;
898 rv = OKAY;
899 jleave:
900 NYD_LEAVE;
901 return rv;
904 /* vim:set fenc=utf-8:s-it-mode */