NYD: acmava.c
[s-mailx.git] / maildir.c
blob3d45f7742501c66f49fe94ef490507b9a45d2819
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 long _maildir_prime;
53 static sigjmp_buf _maildir_jmp;
55 /* Do some cleanup in the tmp/ subdir */
56 static void _cleantmp(void);
58 static int maildir_setfile1(const char *name, int nmail, int omsgCount);
59 static int mdcmp(const void *a, const void *b);
60 static int subdir(const char *name, const char *sub, int nmail);
61 static void _maildir_append(const char *name, const char *sub, const char *fn);
62 static void readin(const char *name, struct message *m);
63 static void maildir_update(void);
64 static void move(struct message *m);
65 static char *mkname(time_t t, enum mflag f, const char *pref);
66 static void maildircatch(int s);
67 static enum okay maildir_append1(const char *name, FILE *fp, off_t off1,
68 long size, enum mflag flag);
69 static enum okay trycreate(const char *name);
70 static enum okay mkmaildir(const char *name);
71 static struct message *mdlook(const char *name, struct message *data);
72 static void mktable(void);
73 static enum okay subdir_remove(const char *name, const char *sub);
75 static void
76 _cleantmp(void)
78 char dep[MAXPATHLEN];
79 struct stat st;
80 time_t now;
81 DIR *dirp;
82 struct dirent *dp;
84 if ((dirp = opendir("tmp")) == NULL)
85 goto jleave;
87 time(&now);
88 while ((dp = readdir(dirp)) != NULL) {
89 if (dp->d_name[0] == '.')
90 continue;
91 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
92 if (stat(dep, &st) < 0)
93 continue;
94 if (st.st_atime + 36*3600 < now)
95 unlink(dep);
97 closedir(dirp);
98 jleave: ;
101 FL int
102 maildir_setfile(char const * volatile name, int nmail, int isedit)
104 sighandler_type volatile saveint;
105 struct cw cw;
106 int i = -1, omsgCount;
108 /* TODO ince we have a VOID box... */
109 omsgCount = msgCount;
110 if (cwget(&cw) == STOP) {
111 alert("Cannot open current directory");
112 goto jleave;
115 if (!nmail)
116 quit();
118 saveint = safe_signal(SIGINT, SIG_IGN);
120 if (!nmail) {
121 edit = (isedit != 0);
122 if (mb.mb_itf) {
123 fclose(mb.mb_itf);
124 mb.mb_itf = NULL;
126 if (mb.mb_otf) {
127 fclose(mb.mb_otf);
128 mb.mb_otf = NULL;
130 initbox(name);
131 mb.mb_type = MB_MAILDIR;
134 if (chdir(name) < 0) {
135 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);/*TODO tr*/
136 mb.mb_type = MB_VOID;
137 *mailname = '\0';
138 msgCount = 0;
139 cwrelse(&cw);
140 safe_signal(SIGINT, saveint);
141 goto jleave;
144 _maildir_table = NULL;
145 if (sigsetjmp(_maildir_jmp, 1) == 0) {
146 if (nmail)
147 mktable();
148 if (saveint != SIG_IGN)
149 safe_signal(SIGINT, &maildircatch);
150 i = maildir_setfile1(name, nmail, omsgCount);
152 if (nmail && _maildir_table != NULL)
153 free(_maildir_table);
155 safe_signal(SIGINT, saveint);
157 if (i < 0) {
158 mb.mb_type = MB_VOID;
159 *mailname = '\0';
160 msgCount = 0;
163 if (cwret(&cw) == STOP)
164 panic("Cannot change back to current directory.");/* TODO tr */
165 cwrelse(&cw);
167 setmsize(msgCount);
168 if (nmail && mb.mb_sorted && msgCount > omsgCount) {
169 mb.mb_threaded = 0;
170 sort((void*)-1);
172 if (!nmail)
173 sawcom = FAL0;
174 if (!nmail && !edit && msgCount == 0) {
175 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
176 fprintf(stderr, tr(258, "No mail at %s\n"), name);
177 i = 1;
178 goto jleave;
180 if (nmail && msgCount > omsgCount)
181 newmailinfo(omsgCount);
182 i = 0;
183 jleave:
184 return i;
187 static int
188 maildir_setfile1(const char *name, int nmail, int omsgCount)
190 int i;
192 if (! nmail)
193 _cleantmp();
194 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
195 if ((i = subdir(name, "cur", nmail)) != 0)
196 return i;
197 if ((i = subdir(name, "new", nmail)) != 0)
198 return i;
199 _maildir_append(name, NULL, NULL);
200 for (i = nmail?omsgCount:0; i < msgCount; i++)
201 readin(name, &message[i]);
202 if (nmail) {
203 if (msgCount > omsgCount)
204 qsort(&message[omsgCount],
205 msgCount - omsgCount,
206 sizeof *message, mdcmp);
207 } else {
208 if (msgCount)
209 qsort(message, msgCount, sizeof *message, mdcmp);
211 return msgCount;
215 * In combination with the names from mkname(), this comparison function
216 * ensures that the order of messages in a maildir folder created by mailx
217 * remains always the same. In effect, if a mbox folder is transferred to
218 * a maildir folder by 'copy *', the order of the messages in mailx will
219 * not change.
221 static int
222 mdcmp(const void *a, const void *b)
224 long i;
226 if ((i = ((struct message const*)a)->m_time -
227 ((struct message const*)b)->m_time) == 0)
228 i = strcmp(&((struct message const*)a)->m_maildir_file[4],
229 &((struct message const*)b)->m_maildir_file[4]);
230 return i;
233 static int
234 subdir(const char *name, const char *sub, int nmail)
236 DIR *dirp;
237 struct dirent *dp;
239 if ((dirp = opendir(sub)) == NULL) {
240 fprintf(stderr, "Cannot open directory \"%s/%s\".\n",
241 name, sub);
242 return -1;
244 if (access(sub, W_OK) < 0)
245 mb.mb_perm = 0;
246 while ((dp = readdir(dirp)) != NULL) {
247 if (dp->d_name[0] == '.' &&
248 (dp->d_name[1] == '\0' ||
249 (dp->d_name[1] == '.' &&
250 dp->d_name[2] == '\0')))
251 continue;
252 if (dp->d_name[0] == '.')
253 continue;
254 if (!nmail || mdlook(dp->d_name, NULL) == NULL)
255 _maildir_append(name, sub, dp->d_name);
257 closedir(dirp);
258 return 0;
261 static void
262 _maildir_append(const char *name, const char *sub, const char *fn)
264 struct message *m;
265 size_t sz, i;
266 time_t t = 0;
267 enum mflag f = MUSED|MNOFROM|MNEWEST;
268 const char *cp;
269 char *xp;
270 (void)name;
272 if (fn && sub) {
273 if (strcmp(sub, "new") == 0)
274 f |= MNEW;
275 t = strtol(fn, &xp, 10);
276 if ((cp = strrchr(xp, ',')) != NULL &&
277 cp > &xp[2] && cp[-1] == '2' && cp[-2] == ':') {
278 while (*++cp) {
279 switch (*cp) {
280 case 'F':
281 f |= MFLAGGED;
282 break;
283 case 'R':
284 f |= MANSWERED;
285 break;
286 case 'S':
287 f |= MREAD;
288 break;
289 case 'T':
290 f |= MDELETED;
291 break;
292 case 'D':
293 f |= MDRAFT;
294 break;
299 if (msgCount + 1 >= msgspace) {
300 const int chunk = 64;
301 message = srealloc(message,
302 (msgspace += chunk) * sizeof *message);
303 memset(&message[msgCount], 0, chunk * sizeof *message);
305 if (fn == NULL || sub == NULL)
306 return;
307 m = &message[msgCount++];
308 i = strlen(fn);
309 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 2);
310 memcpy(m->m_maildir_file, sub, sz);
311 m->m_maildir_file[sz] = '/';
312 memcpy(m->m_maildir_file + sz + 1, fn, i + 1);
313 m->m_time = t;
314 m->m_flag = f;
315 m->m_maildir_hash = ~pjw(fn);
316 return;
319 static void
320 readin(const char *name, struct message *m)
322 char *buf;
323 size_t bufsize, buflen, cnt;
324 long size = 0, lines = 0;
325 off_t offset;
326 FILE *fp;
327 int emptyline = 0;
329 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
330 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
331 name, m->m_maildir_file,
332 (int)(m - &message[0] + 1));
333 m->m_flag |= MHIDDEN;
334 return;
336 buf = smalloc(bufsize = LINESIZE);
337 buflen = 0;
338 cnt = fsize(fp);
339 fseek(mb.mb_otf, 0L, SEEK_END);
340 offset = ftell(mb.mb_otf);
341 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
343 * Since we simply copy over data without doing any transfer
344 * encoding reclassification/adjustment we *have* to perform
345 * RFC 4155 compliant From_ quoting here
347 if (is_head(buf, buflen)) {
348 putc('>', mb.mb_otf);
349 ++size;
351 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
352 emptyline = (*buf == '\n');
353 ++lines;
355 if (!emptyline) {
356 putc('\n', mb.mb_otf);
357 lines++;
358 size++;
360 Fclose(fp);
361 fflush(mb.mb_otf);
362 m->m_size = m->m_xsize = size;
363 m->m_lines = m->m_xlines = lines;
364 m->m_block = mailx_blockof(offset);
365 m->m_offset = mailx_offsetof(offset);
366 free(buf);
367 substdate(m);
370 FL void
371 maildir_quit(void)
373 sighandler_type saveint;
374 struct cw cw;
376 (void)&saveint;
377 if (cwget(&cw) == STOP) {
378 fprintf(stderr, "Fatal: Cannot open current directory\n");
379 return;
381 saveint = safe_signal(SIGINT, SIG_IGN);
382 if (chdir(mailname) < 0) {
383 fprintf(stderr, "Cannot change directory to \"%s\".\n",
384 mailname);
385 cwrelse(&cw);
386 return;
388 if (sigsetjmp(_maildir_jmp, 1) == 0) {
389 if (saveint != SIG_IGN)
390 safe_signal(SIGINT, maildircatch);
391 maildir_update();
393 safe_signal(SIGINT, saveint);
394 if (cwret(&cw) == STOP) {
395 fputs("Fatal: Cannot change back to current directory.\n",
396 stderr);
397 abort();
399 cwrelse(&cw);
402 static void
403 maildir_update(void)
405 struct message *m;
406 int dodel, c, gotcha = 0, held = 0, modflags = 0;
408 if (mb.mb_perm == 0)
409 goto free;
410 if (!edit) {
411 holdbits();
412 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
413 if (m->m_flag & MBOX)
414 c++;
416 if (c > 0)
417 if (makembox() == STOP)
418 goto bypass;
420 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
421 if (edit)
422 dodel = m->m_flag & MDELETED;
423 else
424 dodel = !((m->m_flag&MPRESERVE) ||
425 (m->m_flag&MTOUCH) == 0);
426 if (dodel) {
427 if (unlink(m->m_maildir_file) < 0)
428 fprintf(stderr, "Cannot delete file \"%s/%s\" "
429 "for message %d.\n",
430 mailname, m->m_maildir_file,
431 (int)(m - &message[0] + 1));
432 else
433 gotcha++;
434 } else {
435 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS) ||
436 m->m_flag & (MNEW|MBOXED|MSAVED|MSTATUS|
437 MFLAG|MUNFLAG|
438 MANSWER|MUNANSWER|
439 MDRAFT|MUNDRAFT)) {
440 move(m);
441 modflags++;
443 held++;
446 bypass:
447 if ((gotcha || modflags) && edit) {
448 printf(tr(168, "\"%s\" "), displayname);
449 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
450 ? tr(170, "complete\n") : tr(212, "updated.\n"));
451 } else if (held && !edit && mb.mb_perm != 0) {
452 if (held == 1)
453 printf(tr(155, "Held 1 message in %s\n"), displayname);
454 else
455 printf(tr(156, "Held %d messages in %s\n"), held,
456 displayname);
458 fflush(stdout);
459 free: for (m = &message[0]; m < &message[msgCount]; m++)
460 free(m->m_maildir_file);
463 static void
464 move(struct message *m)
466 char *fn, *new;
468 fn = mkname(0, m->m_flag, &m->m_maildir_file[4]);
469 new = savecat("cur/", fn);
470 if (strcmp(m->m_maildir_file, new) == 0)
471 return;
472 if (link(m->m_maildir_file, new) < 0) {
473 fprintf(stderr, "Cannot link \"%s/%s\" to \"%s/%s\": "
474 "message %d not touched.\n",
475 mailname, m->m_maildir_file,
476 mailname, new,
477 (int)(m - &message[0] + 1));
478 return;
480 if (unlink(m->m_maildir_file) < 0)
481 fprintf(stderr, "Cannot unlink \"%s/%s\".\n",
482 mailname, m->m_maildir_file);
485 static char *
486 mkname(time_t t, enum mflag f, const char *pref)
488 static unsigned long cnt;
489 static pid_t mypid;
490 char *cp;
491 static char *node;
492 int size, n, i;
494 if (pref == NULL) {
495 if (mypid == 0)
496 mypid = getpid();
497 if (node == NULL) {
498 cp = nodename(0);
499 n = size = 0;
500 do {
501 if (UICMP(32, n, <, size + 8))
502 node = srealloc(node, size += 20);
503 switch (*cp) {
504 case '/':
505 node[n++] = '\\', node[n++] = '0',
506 node[n++] = '5', node[n++] = '7';
507 break;
508 case ':':
509 node[n++] = '\\', node[n++] = '0',
510 node[n++] = '7', node[n++] = '2';
511 break;
512 default:
513 node[n++] = *cp;
515 } while (*cp++);
517 size = 60 + strlen(node);
518 cp = salloc(size);
519 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
520 (unsigned long)t,
521 (unsigned long)mypid, ++cnt, node);
522 } else {
523 size = (n = strlen(pref)) + 13;
524 cp = salloc(size);
525 memcpy(cp, pref, n + 1);
526 for (i = n; i > 3; i--)
527 if (cp[i-1] == ',' && cp[i-2] == '2' &&
528 cp[i-3] == ':') {
529 n = i;
530 break;
532 if (i <= 3) {
533 memcpy(cp + n, ":2,", 4);
534 n += 3;
537 if (n < size - 7) {
538 if (f & MDRAFTED)
539 cp[n++] = 'D';
540 if (f & MFLAGGED)
541 cp[n++] = 'F';
542 if (f & MANSWERED)
543 cp[n++] = 'R';
544 if (f & MREAD)
545 cp[n++] = 'S';
546 if (f & MDELETED)
547 cp[n++] = 'T';
548 cp[n] = '\0';
550 return cp;
553 static void
554 maildircatch(int s)
556 siglongjmp(_maildir_jmp, s);
559 FL enum okay
560 maildir_append(const char *name, FILE *fp)
562 char *buf, *bp, *lp;
563 size_t bufsize, buflen, cnt;
564 off_t off1 = -1, offs;
565 int inhead = 1;
566 int flag = MNEW|MNEWEST;
567 long size = 0;
568 enum okay ok;
570 if (mkmaildir(name) != OKAY)
571 return STOP;
572 buf = smalloc(bufsize = LINESIZE);
573 buflen = 0;
574 cnt = fsize(fp);
575 offs = ftell(fp);
576 do {
577 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
578 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
579 if (off1 != (off_t)-1) {
580 ok = maildir_append1(name, fp, off1,
581 size, flag);
582 if (ok == STOP)
583 return STOP;
584 if (fseek(fp, offs+buflen, SEEK_SET) < 0)
585 return STOP;
587 off1 = offs + buflen;
588 size = 0;
589 inhead = 1;
590 flag = MNEW;
591 } else
592 size += buflen;
593 offs += buflen;
594 if (bp && buf[0] == '\n')
595 inhead = 0;
596 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
597 lp = &buf[6];
598 while (whitechar(*lp&0377))
599 lp++;
600 if (*lp == ':')
601 while (*++lp != '\0')
602 switch (*lp) {
603 case 'R':
604 flag |= MREAD;
605 break;
606 case 'O':
607 flag &= ~MNEW;
608 break;
610 } else if (bp && inhead &&
611 ascncasecmp(buf, "x-status", 8) == 0) {
612 lp = &buf[8];
613 while (whitechar(*lp&0377))
614 lp++;
615 if (*lp == ':')
616 while (*++lp != '\0')
617 switch (*lp) {
618 case 'F':
619 flag |= MFLAGGED;
620 break;
621 case 'A':
622 flag |= MANSWERED;
623 break;
624 case 'T':
625 flag |= MDRAFTED;
626 break;
629 } while (bp != NULL);
630 free(buf);
631 return OKAY;
634 static enum okay
635 maildir_append1(const char *name, FILE *fp, off_t off1, long size,
636 enum mflag flag)
638 int const attempts = 43200;
639 char buf[4096], *fn, *tmp, *new;
640 struct stat st;
641 FILE *op;
642 long n, z;
643 int i;
644 time_t now;
646 /* Create a unique temporary file */
647 for (i = 0;; sleep(1), ++i) {
648 if (i >= attempts) {
649 fprintf(stderr, tr(198,
650 "Can't create an unique file name in "
651 "\"%s/tmp\".\n"), name);
652 return STOP;
655 time(&now);
656 fn = mkname(now, flag, NULL);
657 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
658 snprintf(tmp, n, "%s/tmp/%s", name, fn);
659 if (stat(tmp, &st) >= 0 || errno != ENOENT)
660 continue;
662 /* Use "wx" for O_EXCL */
663 if ((op = Fopen(tmp, "wx")) != NULL)
664 break;
667 if (fseek(fp, off1, SEEK_SET) < 0)
668 goto jtmperr;
669 while (size > 0) {
670 z = size > (long)sizeof buf ? (long)sizeof buf : size;
671 if ((n = fread(buf, 1, z, fp)) != z ||
672 (size_t)n != fwrite(buf, 1, n, op)) {
673 jtmperr:
674 fprintf(stderr, "Error writing to \"%s\".\n", tmp);
675 Fclose(op);
676 unlink(tmp);
677 return STOP;
679 size -= n;
681 Fclose(op);
683 new = salloc(n = strlen(name) + strlen(fn) + 6);
684 snprintf(new, n, "%s/new/%s", name, fn);
685 if (link(tmp, new) < 0) {
686 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);
687 return STOP;
689 if (unlink(tmp) < 0)
690 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp);
691 return OKAY;
694 static enum okay
695 trycreate(const char *name)
697 struct stat st;
699 if (stat(name, &st) == 0) {
700 if (!S_ISDIR(st.st_mode)) {
701 fprintf(stderr, "\"%s\" is not a directory.\n", name);
702 return STOP;
704 } else if (makedir(name) != OKAY) {
705 fprintf(stderr, "Cannot create directory \"%s\".\n", name);
706 return STOP;
707 } else
708 imap_created_mailbox++;
709 return OKAY;
712 static enum okay
713 mkmaildir(const char *name)
715 char *np;
716 size_t sz;
717 enum okay ok = STOP;
719 if (trycreate(name) == OKAY) {
720 np = ac_alloc((sz = strlen(name)) + 5);
721 memcpy(np, name, sz);
722 memcpy(np + sz, "/tmp", 5);
723 if (trycreate(np) == OKAY) {
724 strcpy(&np[sz], "/new");
725 if (trycreate(np) == OKAY) {
726 strcpy(&np[sz], "/cur");
727 if (trycreate(np) == OKAY)
728 ok = OKAY;
731 ac_free(np);
733 return ok;
736 static struct message *
737 mdlook(const char *name, struct message *data)
739 struct mditem *md;
740 unsigned c, h, n = 0;
742 if (data && data->m_maildir_hash)
743 h = ~data->m_maildir_hash;
744 else
745 h = pjw(name);
746 h %= _maildir_prime;
747 md = &_maildir_table[c = h];
748 while (md->md_data != NULL) {
749 if (strcmp(&md->md_data->m_maildir_file[4], name) == 0)
750 break;
751 c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
752 n++;
753 while (c >= (unsigned)_maildir_prime)
754 c -= (unsigned)_maildir_prime;
755 md = &_maildir_table[c];
757 if (data != NULL && md->md_data == NULL)
758 md->md_data = data;
759 return md->md_data ? md->md_data : NULL;
762 static void
763 mktable(void)
765 int i;
767 _maildir_prime = nextprime(msgCount);
768 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
769 for (i = 0; i < msgCount; i++)
770 mdlook(&message[i].m_maildir_file[4], &message[i]);
773 static enum okay
774 subdir_remove(const char *name, const char *sub)
776 char *path;
777 int pathsize, pathend, namelen, sublen, n;
778 DIR *dirp;
779 struct dirent *dp;
781 namelen = strlen(name);
782 sublen = strlen(sub);
783 path = smalloc(pathsize = namelen + sublen + 30);
784 memcpy(path, name, namelen);
785 path[namelen] = '/';
786 memcpy(path + namelen + 1, sub, sublen);
787 path[namelen+sublen+1] = '/';
788 path[pathend = namelen + sublen + 2] = '\0';
789 if ((dirp = opendir(path)) == NULL) {
790 perror(path);
791 free(path);
792 return STOP;
794 while ((dp = readdir(dirp)) != NULL) {
795 if (dp->d_name[0] == '.' &&
796 (dp->d_name[1] == '\0' ||
797 (dp->d_name[1] == '.' &&
798 dp->d_name[2] == '\0')))
799 continue;
800 if (dp->d_name[0] == '.')
801 continue;
802 n = strlen(dp->d_name);
803 if (UICMP(32, pathend + n + 1, >, pathsize))
804 path = srealloc(path, pathsize = pathend + n + 30);
805 memcpy(path + pathend, dp->d_name, n + 1);
806 if (unlink(path) < 0) {
807 perror(path);
808 closedir(dirp);
809 free(path);
810 return STOP;
813 closedir(dirp);
814 path[pathend] = '\0';
815 if (rmdir(path) < 0) {
816 perror(path);
817 free(path);
818 return STOP;
820 free(path);
821 return OKAY;
824 FL enum okay
825 maildir_remove(const char *name)
827 if (subdir_remove(name, "tmp") == STOP ||
828 subdir_remove(name, "new") == STOP ||
829 subdir_remove(name, "cur") == STOP)
830 return STOP;
831 if (rmdir(name) < 0) {
832 perror(name);
833 return STOP;
835 return OKAY;