Localize cmdtab[] in lex.c..
[s-mailx.git] / maildir.c
blob441add5ff042a752c6bf7c902b18282a03efb6ab
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Maildir folder support.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 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 #include "nail.h"
42 #include <dirent.h>
44 static struct mditem {
45 struct message *md_data;
46 unsigned md_hash;
47 } *mdtable;
48 static long mdprime;
50 static sigjmp_buf maildirjmp;
52 /* Do some cleanup in the tmp/ subdir */
53 static void _cleantmp(void);
55 static int maildir_setfile1(const char *name, int nmail, int omsgCount);
56 static int mdcmp(const void *a, const void *b);
57 static int subdir(const char *name, const char *sub, int nmail);
58 static void append(const char *name, const char *sub, const char *fn);
59 static void readin(const char *name, struct message *m);
60 static void maildir_update(void);
61 static void move(struct message *m);
62 static char *mkname(time_t t, enum mflag f, const char *pref);
63 static void maildircatch(int s);
64 static enum okay maildir_append1(const char *name, FILE *fp, off_t off1,
65 long size, enum mflag flag);
66 static enum okay trycreate(const char *name);
67 static enum okay mkmaildir(const char *name);
68 static struct message *mdlook(const char *name, struct message *data);
69 static void mktable(void);
70 static enum okay subdir_remove(const char *name, const char *sub);
72 static void
73 _cleantmp(void)
75 char dep[MAXPATHLEN];
76 struct stat st;
77 time_t now;
78 DIR *dirp;
79 struct dirent *dp;
81 if ((dirp = opendir("tmp")) == NULL)
82 goto jleave;
84 time(&now);
85 while ((dp = readdir(dirp)) != NULL) {
86 if (dp->d_name[0] == '.')
87 continue;
88 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
89 if (stat(dep, &st) < 0)
90 continue;
91 if (st.st_atime + 36*3600 < now)
92 unlink(dep);
94 closedir(dirp);
95 jleave: ;
98 int
99 maildir_setfile(char const * volatile name, int nmail, int isedit)
101 sighandler_type volatile saveint;
102 struct cw cw;
103 int i = -1, omsgCount;
105 (void)&saveint;
106 (void)&i;
107 omsgCount = msgCount;
108 if (cwget(&cw) == STOP) {
109 fprintf(stderr, "Fatal: Cannot open current directory\n");
110 return -1;
112 if (!nmail)
113 quit();
114 saveint = safe_signal(SIGINT, SIG_IGN);
115 if (chdir(name) < 0) {
116 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);
117 cwrelse(&cw);
118 return -1;
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;
133 mdtable = NULL;
134 if (sigsetjmp(maildirjmp, 1) == 0) {
135 if (nmail)
136 mktable();
137 if (saveint != SIG_IGN)
138 safe_signal(SIGINT, maildircatch);
139 i = maildir_setfile1(name, nmail, omsgCount);
141 if (nmail && mdtable != NULL)
142 free(mdtable);
143 safe_signal(SIGINT, saveint);
144 if (i < 0) {
145 mb.mb_type = MB_VOID;
146 *mailname = '\0';
147 msgCount = 0;
149 if (cwret(&cw) == STOP) {
150 fputs("Fatal: Cannot change back to current directory.\n",
151 stderr);
152 abort();
154 cwrelse(&cw);
155 setmsize(msgCount);
156 if (nmail && mb.mb_sorted && msgCount > omsgCount) {
157 mb.mb_threaded = 0;
158 sort((void *)-1);
160 if (!nmail)
161 sawcom = FAL0;
162 if (!nmail && !edit && msgCount == 0) {
163 if (mb.mb_type == MB_MAILDIR && value("emptystart") == NULL)
164 fprintf(stderr, "No mail at %s\n", name);
165 return 1;
167 if (nmail && msgCount > omsgCount)
168 newmailinfo(omsgCount);
169 return 0;
172 static int
173 maildir_setfile1(const char *name, int nmail, int omsgCount)
175 int i;
177 if (! nmail)
178 _cleantmp();
179 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
180 if ((i = subdir(name, "cur", nmail)) != 0)
181 return i;
182 if ((i = subdir(name, "new", nmail)) != 0)
183 return i;
184 append(name, NULL, NULL);
185 for (i = nmail?omsgCount:0; i < msgCount; i++)
186 readin(name, &message[i]);
187 if (nmail) {
188 if (msgCount > omsgCount)
189 qsort(&message[omsgCount],
190 msgCount - omsgCount,
191 sizeof *message, mdcmp);
192 } else {
193 if (msgCount)
194 qsort(message, msgCount, sizeof *message, mdcmp);
196 return msgCount;
200 * In combination with the names from mkname(), this comparison function
201 * ensures that the order of messages in a maildir folder created by mailx
202 * remains always the same. In effect, if a mbox folder is transferred to
203 * a maildir folder by 'copy *', the order of the messages in mailx will
204 * not change.
206 static int
207 mdcmp(const void *a, const void *b)
209 long i;
211 if ((i = ((struct message const*)a)->m_time -
212 ((struct message const*)b)->m_time) == 0)
213 i = strcmp(&((struct message const*)a)->m_maildir_file[4],
214 &((struct message const*)b)->m_maildir_file[4]);
215 return i;
218 static int
219 subdir(const char *name, const char *sub, int nmail)
221 DIR *dirp;
222 struct dirent *dp;
224 if ((dirp = opendir(sub)) == NULL) {
225 fprintf(stderr, "Cannot open directory \"%s/%s\".\n",
226 name, sub);
227 return -1;
229 if (access(sub, W_OK) < 0)
230 mb.mb_perm = 0;
231 while ((dp = readdir(dirp)) != NULL) {
232 if (dp->d_name[0] == '.' &&
233 (dp->d_name[1] == '\0' ||
234 (dp->d_name[1] == '.' &&
235 dp->d_name[2] == '\0')))
236 continue;
237 if (dp->d_name[0] == '.')
238 continue;
239 if (!nmail || mdlook(dp->d_name, NULL) == NULL)
240 append(name, sub, dp->d_name);
242 closedir(dirp);
243 return 0;
246 static void
247 append(const char *name, const char *sub, const char *fn)
249 struct message *m;
250 size_t sz, i;
251 time_t t = 0;
252 enum mflag f = MUSED|MNOFROM|MNEWEST;
253 const char *cp;
254 char *xp;
255 (void)name;
257 if (fn && sub) {
258 if (strcmp(sub, "new") == 0)
259 f |= MNEW;
260 t = strtol(fn, &xp, 10);
261 if ((cp = strrchr(xp, ',')) != NULL &&
262 cp > &xp[2] && cp[-1] == '2' && cp[-2] == ':') {
263 while (*++cp) {
264 switch (*cp) {
265 case 'F':
266 f |= MFLAGGED;
267 break;
268 case 'R':
269 f |= MANSWERED;
270 break;
271 case 'S':
272 f |= MREAD;
273 break;
274 case 'T':
275 f |= MDELETED;
276 break;
277 case 'D':
278 f |= MDRAFT;
279 break;
284 if (msgCount + 1 >= msgspace) {
285 const int chunk = 64;
286 message = srealloc(message,
287 (msgspace += chunk) * sizeof *message);
288 memset(&message[msgCount], 0, chunk * sizeof *message);
290 if (fn == NULL || sub == NULL)
291 return;
292 m = &message[msgCount++];
293 i = strlen(fn);
294 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 2);
295 memcpy(m->m_maildir_file, sub, sz);
296 m->m_maildir_file[sz] = '/';
297 memcpy(m->m_maildir_file + sz + 1, fn, i + 1);
298 m->m_time = t;
299 m->m_flag = f;
300 m->m_maildir_hash = ~pjw(fn);
301 return;
304 static void
305 readin(const char *name, struct message *m)
307 char *buf;
308 size_t bufsize, buflen, cnt;
309 long size = 0, lines = 0;
310 off_t offset;
311 FILE *fp;
312 int emptyline = 0;
314 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
315 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
316 name, m->m_maildir_file,
317 (int)(m - &message[0] + 1));
318 m->m_flag |= MHIDDEN;
319 return;
321 buf = smalloc(bufsize = LINESIZE);
322 buflen = 0;
323 cnt = fsize(fp);
324 fseek(mb.mb_otf, 0L, SEEK_END);
325 offset = ftell(mb.mb_otf);
326 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
328 * Since we simply copy over data without doing any transfer
329 * encoding reclassification/adjustment we *have* to perform
330 * RFC 4155 compliant From_ quoting here
332 if (is_head(buf, buflen)) {
333 putc('>', mb.mb_otf);
334 ++size;
336 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
337 emptyline = (*buf == '\n');
338 ++lines;
340 if (!emptyline) {
341 putc('\n', mb.mb_otf);
342 lines++;
343 size++;
345 Fclose(fp);
346 fflush(mb.mb_otf);
347 m->m_size = m->m_xsize = size;
348 m->m_lines = m->m_xlines = lines;
349 m->m_block = mailx_blockof(offset);
350 m->m_offset = mailx_offsetof(offset);
351 free(buf);
352 substdate(m);
355 void
356 maildir_quit(void)
358 sighandler_type saveint;
359 struct cw cw;
361 (void)&saveint;
362 if (cwget(&cw) == STOP) {
363 fprintf(stderr, "Fatal: Cannot open current directory\n");
364 return;
366 saveint = safe_signal(SIGINT, SIG_IGN);
367 if (chdir(mailname) < 0) {
368 fprintf(stderr, "Cannot change directory to \"%s\".\n",
369 mailname);
370 cwrelse(&cw);
371 return;
373 if (sigsetjmp(maildirjmp, 1) == 0) {
374 if (saveint != SIG_IGN)
375 safe_signal(SIGINT, maildircatch);
376 maildir_update();
378 safe_signal(SIGINT, saveint);
379 if (cwret(&cw) == STOP) {
380 fputs("Fatal: Cannot change back to current directory.\n",
381 stderr);
382 abort();
384 cwrelse(&cw);
387 static void
388 maildir_update(void)
390 struct message *m;
391 int dodel, c, gotcha = 0, held = 0, modflags = 0;
393 if (mb.mb_perm == 0)
394 goto free;
395 if (!edit) {
396 holdbits();
397 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
398 if (m->m_flag & MBOX)
399 c++;
401 if (c > 0)
402 if (makembox() == STOP)
403 goto bypass;
405 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
406 if (edit)
407 dodel = m->m_flag & MDELETED;
408 else
409 dodel = !((m->m_flag&MPRESERVE) ||
410 (m->m_flag&MTOUCH) == 0);
411 if (dodel) {
412 if (unlink(m->m_maildir_file) < 0)
413 fprintf(stderr, "Cannot delete file \"%s/%s\" "
414 "for message %d.\n",
415 mailname, m->m_maildir_file,
416 (int)(m - &message[0] + 1));
417 else
418 gotcha++;
419 } else {
420 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS) ||
421 m->m_flag & (MNEW|MBOXED|MSAVED|MSTATUS|
422 MFLAG|MUNFLAG|
423 MANSWER|MUNANSWER|
424 MDRAFT|MUNDRAFT)) {
425 move(m);
426 modflags++;
428 held++;
431 bypass:
432 if ((gotcha || modflags) && edit) {
433 printf(tr(168, "\"%s\" "), displayname);
434 printf((value("bsdcompat") || value("bsdmsgs"))
435 ? tr(170, "complete\n") : tr(212, "updated.\n"));
436 } else if (held && !edit && mb.mb_perm != 0) {
437 if (held == 1)
438 printf(tr(155, "Held 1 message in %s\n"), displayname);
439 else if (held > 1)
440 printf(tr(156, "Held %d messages in %s\n"), held,
441 displayname);
443 fflush(stdout);
444 free: for (m = &message[0]; m < &message[msgCount]; m++)
445 free(m->m_maildir_file);
448 static void
449 move(struct message *m)
451 char *fn, *new;
453 fn = mkname(0, m->m_flag, &m->m_maildir_file[4]);
454 new = savecat("cur/", fn);
455 if (strcmp(m->m_maildir_file, new) == 0)
456 return;
457 if (link(m->m_maildir_file, new) < 0) {
458 fprintf(stderr, "Cannot link \"%s/%s\" to \"%s/%s\": "
459 "message %d not touched.\n",
460 mailname, m->m_maildir_file,
461 mailname, new,
462 (int)(m - &message[0] + 1));
463 return;
465 if (unlink(m->m_maildir_file) < 0)
466 fprintf(stderr, "Cannot unlink \"%s/%s\".\n",
467 mailname, m->m_maildir_file);
470 static char *
471 mkname(time_t t, enum mflag f, const char *pref)
473 static unsigned long cnt;
474 static pid_t mypid;
475 char *cp;
476 static char *node;
477 int size, n, i;
479 if (pref == NULL) {
480 if (mypid == 0)
481 mypid = getpid();
482 if (node == NULL) {
483 cp = nodename(0);
484 n = size = 0;
485 do {
486 if (n < size + 8)
487 node = srealloc(node, size += 20);
488 switch (*cp) {
489 case '/':
490 node[n++] = '\\', node[n++] = '0',
491 node[n++] = '5', node[n++] = '7';
492 break;
493 case ':':
494 node[n++] = '\\', node[n++] = '0',
495 node[n++] = '7', node[n++] = '2';
496 break;
497 default:
498 node[n++] = *cp;
500 } while (*cp++);
502 size = 60 + strlen(node);
503 cp = salloc(size);
504 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
505 (unsigned long)t,
506 (unsigned long)mypid, ++cnt, node);
507 } else {
508 size = (n = strlen(pref)) + 13;
509 cp = salloc(size);
510 memcpy(cp, pref, n + 1);
511 for (i = n; i > 3; i--)
512 if (cp[i-1] == ',' && cp[i-2] == '2' &&
513 cp[i-3] == ':') {
514 n = i;
515 break;
517 if (i <= 3) {
518 memcpy(cp + n, ":2,", 4);
519 n += 3;
522 if (n < size - 7) {
523 if (f & MDRAFTED)
524 cp[n++] = 'D';
525 if (f & MFLAGGED)
526 cp[n++] = 'F';
527 if (f & MANSWERED)
528 cp[n++] = 'R';
529 if (f & MREAD)
530 cp[n++] = 'S';
531 if (f & MDELETED)
532 cp[n++] = 'T';
533 cp[n] = '\0';
535 return cp;
538 static void
539 maildircatch(int s)
541 siglongjmp(maildirjmp, s);
544 enum okay
545 maildir_append(const char *name, FILE *fp)
547 char *buf, *bp, *lp;
548 size_t bufsize, buflen, cnt;
549 off_t off1 = -1, offs;
550 int inhead = 1;
551 int flag = MNEW|MNEWEST;
552 long size = 0;
553 enum okay ok;
555 if (mkmaildir(name) != OKAY)
556 return STOP;
557 buf = smalloc(bufsize = LINESIZE);
558 buflen = 0;
559 cnt = fsize(fp);
560 offs = ftell(fp);
561 do {
562 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
563 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
564 if (off1 != (off_t)-1) {
565 ok = maildir_append1(name, fp, off1,
566 size, flag);
567 if (ok == STOP)
568 return STOP;
569 if (fseek(fp, offs+buflen, SEEK_SET) < 0)
570 return STOP;
572 off1 = offs + buflen;
573 size = 0;
574 inhead = 1;
575 flag = MNEW;
576 } else
577 size += buflen;
578 offs += buflen;
579 if (bp && buf[0] == '\n')
580 inhead = 0;
581 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
582 lp = &buf[6];
583 while (whitechar(*lp&0377))
584 lp++;
585 if (*lp == ':')
586 while (*++lp != '\0')
587 switch (*lp) {
588 case 'R':
589 flag |= MREAD;
590 break;
591 case 'O':
592 flag &= ~MNEW;
593 break;
595 } else if (bp && inhead &&
596 ascncasecmp(buf, "x-status", 8) == 0) {
597 lp = &buf[8];
598 while (whitechar(*lp&0377))
599 lp++;
600 if (*lp == ':')
601 while (*++lp != '\0')
602 switch (*lp) {
603 case 'F':
604 flag |= MFLAGGED;
605 break;
606 case 'A':
607 flag |= MANSWERED;
608 break;
609 case 'T':
610 flag |= MDRAFTED;
611 break;
614 } while (bp != NULL);
615 free(buf);
616 return OKAY;
619 static enum okay
620 maildir_append1(const char *name, FILE *fp, off_t off1, long size,
621 enum mflag flag)
623 int const attempts = 43200;
624 char buf[4096], *fn, *tmp, *new;
625 struct stat st;
626 FILE *op;
627 long n, z;
628 int i;
629 time_t now;
631 /* Create a unique temporary file */
632 for (i = 0;; sleep(1), ++i) {
633 if (i >= attempts) {
634 fprintf(stderr, tr(198,
635 "Can't create an unique file name in "
636 "\"%s/tmp\".\n"), name);
637 return STOP;
640 time(&now);
641 fn = mkname(now, flag, NULL);
642 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
643 snprintf(tmp, n, "%s/tmp/%s", name, fn);
644 if (stat(tmp, &st) >= 0 || errno != ENOENT)
645 continue;
647 /* Use "wx" for O_EXCL */
648 if ((op = Fopen(tmp, "wx")) != NULL)
649 break;
652 if (fseek(fp, off1, SEEK_SET) < 0)
653 goto jtmperr;
654 while (size > 0) {
655 z = size > (long)sizeof buf ? (long)sizeof buf : size;
656 if ((n = fread(buf, 1, z, fp)) != z ||
657 (size_t)n != fwrite(buf, 1, n, op)) {
658 jtmperr:
659 fprintf(stderr, "Error writing to \"%s\".\n", tmp);
660 Fclose(op);
661 unlink(tmp);
662 return STOP;
664 size -= n;
666 Fclose(op);
668 new = salloc(n = strlen(name) + strlen(fn) + 6);
669 snprintf(new, n, "%s/new/%s", name, fn);
670 if (link(tmp, new) < 0) {
671 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);
672 return STOP;
674 if (unlink(tmp) < 0)
675 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp);
676 return OKAY;
679 static enum okay
680 trycreate(const char *name)
682 struct stat st;
684 if (stat(name, &st) == 0) {
685 if (!S_ISDIR(st.st_mode)) {
686 fprintf(stderr, "\"%s\" is not a directory.\n", name);
687 return STOP;
689 } else if (makedir(name) != OKAY) {
690 fprintf(stderr, "Cannot create directory \"%s\".\n", name);
691 return STOP;
692 } else
693 imap_created_mailbox++;
694 return OKAY;
697 static enum okay
698 mkmaildir(const char *name)
700 char *np;
701 size_t sz;
702 enum okay ok = STOP;
704 if (trycreate(name) == OKAY) {
705 np = ac_alloc((sz = strlen(name)) + 5);
706 memcpy(np, name, sz);
707 memcpy(np + sz, "/tmp", 5);
708 if (trycreate(np) == OKAY) {
709 strcpy(&np[sz], "/new");
710 if (trycreate(np) == OKAY) {
711 strcpy(&np[sz], "/cur");
712 if (trycreate(np) == OKAY)
713 ok = OKAY;
716 ac_free(np);
718 return ok;
721 static struct message *
722 mdlook(const char *name, struct message *data)
724 struct mditem *md;
725 unsigned c, h, n = 0;
727 if (data && data->m_maildir_hash)
728 h = ~data->m_maildir_hash;
729 else
730 h = pjw(name);
731 h %= mdprime;
732 md = &mdtable[c = h];
733 while (md->md_data != NULL) {
734 if (strcmp(&md->md_data->m_maildir_file[4], name) == 0)
735 break;
736 c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
737 n++;
738 while (c >= (unsigned)mdprime)
739 c -= (unsigned)mdprime;
740 md = &mdtable[c];
742 if (data != NULL && md->md_data == NULL)
743 md->md_data = data;
744 return md->md_data ? md->md_data : NULL;
747 static void
748 mktable(void)
750 int i;
752 mdprime = nextprime(msgCount);
753 mdtable = scalloc(mdprime, sizeof *mdtable);
754 for (i = 0; i < msgCount; i++)
755 mdlook(&message[i].m_maildir_file[4], &message[i]);
758 static enum okay
759 subdir_remove(const char *name, const char *sub)
761 char *path;
762 int pathsize, pathend, namelen, sublen, n;
763 DIR *dirp;
764 struct dirent *dp;
766 namelen = strlen(name);
767 sublen = strlen(sub);
768 path = smalloc(pathsize = namelen + sublen + 30);
769 memcpy(path, name, namelen);
770 path[namelen] = '/';
771 memcpy(path + namelen + 1, sub, sublen);
772 path[namelen+sublen+1] = '/';
773 path[pathend = namelen + sublen + 2] = '\0';
774 if ((dirp = opendir(path)) == NULL) {
775 perror(path);
776 free(path);
777 return STOP;
779 while ((dp = readdir(dirp)) != NULL) {
780 if (dp->d_name[0] == '.' &&
781 (dp->d_name[1] == '\0' ||
782 (dp->d_name[1] == '.' &&
783 dp->d_name[2] == '\0')))
784 continue;
785 if (dp->d_name[0] == '.')
786 continue;
787 n = strlen(dp->d_name);
788 if (pathend + n + 1 > pathsize)
789 path = srealloc(path, pathsize = pathend + n + 30);
790 memcpy(path + pathend, dp->d_name, n + 1);
791 if (unlink(path) < 0) {
792 perror(path);
793 closedir(dirp);
794 free(path);
795 return STOP;
798 closedir(dirp);
799 path[pathend] = '\0';
800 if (rmdir(path) < 0) {
801 perror(path);
802 free(path);
803 return STOP;
805 free(path);
806 return OKAY;
809 enum okay
810 maildir_remove(const char *name)
812 if (subdir_remove(name, "tmp") == STOP ||
813 subdir_remove(name, "new") == STOP ||
814 subdir_remove(name, "cur") == STOP)
815 return STOP;
816 if (rmdir(name) < 0) {
817 perror(name);
818 return STOP;
820 return OKAY;