Nits for silencing -Wshadow
[s-mailx.git] / maildir.c
blobaf831bc80297ca8e705efeba6b58a29c9413358e
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 "rcv.h"
42 #include <sys/stat.h>
43 #include <errno.h>
44 #include <dirent.h>
45 #include <time.h>
46 #include <unistd.h>
48 #include "extern.h"
50 static struct mditem {
51 struct message *md_data;
52 unsigned md_hash;
53 } *mdtable;
54 static long mdprime;
56 static sigjmp_buf maildirjmp;
58 /* Do some cleanup in the tmp/ subdir */
59 static void _cleantmp(void);
61 static int maildir_setfile1(const char *name, int nmail, int omsgCount);
62 static int mdcmp(const void *a, const void *b);
63 static int subdir(const char *name, const char *sub, int nmail);
64 static void append(const char *name, const char *sub, const char *fn);
65 static void readin(const char *name, struct message *m);
66 static void maildir_update(void);
67 static void move(struct message *m);
68 static char *mkname(time_t t, enum mflag f, const char *pref);
69 static void maildircatch(int s);
70 static enum okay maildir_append1(const char *name, FILE *fp, off_t off1,
71 long size, enum mflag flag);
72 static enum okay trycreate(const char *name);
73 static enum okay mkmaildir(const char *name);
74 static struct message *mdlook(const char *name, struct message *data);
75 static void mktable(void);
76 static enum okay subdir_remove(const char *name, const char *sub);
78 static void
79 _cleantmp(void)
81 char dep[MAXPATHLEN];
82 struct stat st;
83 time_t now;
84 DIR *dirp;
85 struct dirent *dp;
87 if ((dirp = opendir("tmp")) == NULL)
88 goto jleave;
90 time(&now);
91 while ((dp = readdir(dirp)) != NULL) {
92 if (dp->d_name[0] == '.')
93 continue;
94 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
95 if (stat(dep, &st) < 0)
96 continue;
97 if (st.st_atime + 36*3600 < now)
98 unlink(dep);
100 closedir(dirp);
101 jleave: ;
104 int
105 maildir_setfile(const char *name, int nmail, int isedit)
107 sighandler_type saveint;
108 struct cw cw;
109 int i = -1, omsgCount;
111 (void)&saveint;
112 (void)&i;
113 omsgCount = msgCount;
114 if (cwget(&cw) == STOP) {
115 fprintf(stderr, "Fatal: Cannot open current directory\n");
116 return -1;
118 if (!nmail)
119 quit();
120 saveint = safe_signal(SIGINT, SIG_IGN);
121 if (chdir(name) < 0) {
122 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);
123 cwrelse(&cw);
124 return -1;
126 if (!nmail) {
127 edit = (isedit != 0);
128 if (mb.mb_itf) {
129 fclose(mb.mb_itf);
130 mb.mb_itf = NULL;
132 if (mb.mb_otf) {
133 fclose(mb.mb_otf);
134 mb.mb_otf = NULL;
136 initbox(name);
137 mb.mb_type = MB_MAILDIR;
139 mdtable = NULL;
140 if (sigsetjmp(maildirjmp, 1) == 0) {
141 if (nmail)
142 mktable();
143 if (saveint != SIG_IGN)
144 safe_signal(SIGINT, maildircatch);
145 i = maildir_setfile1(name, nmail, omsgCount);
147 if (nmail)
148 free(mdtable);
149 safe_signal(SIGINT, saveint);
150 if (i < 0) {
151 mb.mb_type = MB_VOID;
152 *mailname = '\0';
153 msgCount = 0;
155 if (cwret(&cw) == STOP) {
156 fputs("Fatal: Cannot change back to current directory.\n",
157 stderr);
158 abort();
160 cwrelse(&cw);
161 setmsize(msgCount);
162 if (nmail && mb.mb_sorted && msgCount > omsgCount) {
163 mb.mb_threaded = 0;
164 sort((void *)-1);
166 if (!nmail)
167 sawcom = FAL0;
168 if (!nmail && !edit && msgCount == 0) {
169 if (mb.mb_type == MB_MAILDIR && value("emptystart") == NULL)
170 fprintf(stderr, "No mail at %s\n", name);
171 return 1;
173 if (nmail && msgCount > omsgCount)
174 newmailinfo(omsgCount);
175 return 0;
178 static int
179 maildir_setfile1(const char *name, int nmail, int omsgCount)
181 int i;
183 if (! nmail)
184 _cleantmp();
185 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
186 if ((i = subdir(name, "cur", nmail)) != 0)
187 return i;
188 if ((i = subdir(name, "new", nmail)) != 0)
189 return i;
190 append(name, NULL, NULL);
191 for (i = nmail?omsgCount:0; i < msgCount; i++)
192 readin(name, &message[i]);
193 if (nmail) {
194 if (msgCount > omsgCount)
195 qsort(&message[omsgCount],
196 msgCount - omsgCount,
197 sizeof *message, mdcmp);
198 } else {
199 if (msgCount)
200 qsort(message, msgCount, sizeof *message, mdcmp);
202 return msgCount;
206 * In combination with the names from mkname(), this comparison function
207 * ensures that the order of messages in a maildir folder created by mailx
208 * remains always the same. In effect, if a mbox folder is transferred to
209 * a maildir folder by 'copy *', the order of the messages in mailx will
210 * not change.
212 static int
213 mdcmp(const void *a, const void *b)
215 long i;
217 if ((i = ((struct message const*)a)->m_time -
218 ((struct message const*)b)->m_time) == 0)
219 i = strcmp(&((struct message const*)a)->m_maildir_file[4],
220 &((struct message const*)b)->m_maildir_file[4]);
221 return i;
224 static int
225 subdir(const char *name, const char *sub, int nmail)
227 DIR *dirp;
228 struct dirent *dp;
230 if ((dirp = opendir(sub)) == NULL) {
231 fprintf(stderr, "Cannot open directory \"%s/%s\".\n",
232 name, sub);
233 return -1;
235 if (access(sub, W_OK) < 0)
236 mb.mb_perm = 0;
237 while ((dp = readdir(dirp)) != NULL) {
238 if (dp->d_name[0] == '.' &&
239 (dp->d_name[1] == '\0' ||
240 (dp->d_name[1] == '.' &&
241 dp->d_name[2] == '\0')))
242 continue;
243 if (dp->d_name[0] == '.')
244 continue;
245 if (!nmail || mdlook(dp->d_name, NULL) == NULL)
246 append(name, sub, dp->d_name);
248 closedir(dirp);
249 return 0;
252 static void
253 append(const char *name, const char *sub, const char *fn)
255 struct message *m;
256 size_t sz, i;
257 time_t t = 0;
258 enum mflag f = MUSED|MNOFROM|MNEWEST;
259 const char *cp;
260 char *xp;
261 (void)name;
263 if (fn && sub) {
264 if (strcmp(sub, "new") == 0)
265 f |= MNEW;
266 t = strtol(fn, &xp, 10);
267 if ((cp = strrchr(xp, ',')) != NULL &&
268 cp > &xp[2] && cp[-1] == '2' && cp[-2] == ':') {
269 while (*++cp) {
270 switch (*cp) {
271 case 'F':
272 f |= MFLAGGED;
273 break;
274 case 'R':
275 f |= MANSWERED;
276 break;
277 case 'S':
278 f |= MREAD;
279 break;
280 case 'T':
281 f |= MDELETED;
282 break;
283 case 'D':
284 f |= MDRAFT;
285 break;
290 if (msgCount + 1 >= msgspace) {
291 const int chunk = 64;
292 message = srealloc(message,
293 (msgspace += chunk) * sizeof *message);
294 memset(&message[msgCount], 0, chunk * sizeof *message);
296 if (fn == NULL || sub == NULL)
297 return;
298 m = &message[msgCount++];
299 i = strlen(fn);
300 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 2);
301 memcpy(m->m_maildir_file, sub, sz);
302 m->m_maildir_file[sz] = '/';
303 memcpy(m->m_maildir_file + sz + 1, fn, i + 1);
304 m->m_time = t;
305 m->m_flag = f;
306 m->m_maildir_hash = ~pjw(fn);
307 return;
310 static void
311 readin(const char *name, struct message *m)
313 char *buf;
314 size_t bufsize, buflen, cnt;
315 long size = 0, lines = 0;
316 off_t offset;
317 FILE *fp;
318 int emptyline = 0;
320 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
321 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
322 name, m->m_maildir_file,
323 (int)(m - &message[0] + 1));
324 m->m_flag |= MHIDDEN;
325 return;
327 buf = smalloc(bufsize = LINESIZE);
328 buflen = 0;
329 cnt = fsize(fp);
330 fseek(mb.mb_otf, 0L, SEEK_END);
331 offset = ftell(mb.mb_otf);
332 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
334 * Since we simply copy over data without doing any transfer
335 * encoding reclassification/adjustment we *have* to perform
336 * RFC 4155 compliant From_ quoting here
338 if (is_head(buf, buflen)) {
339 putc('>', mb.mb_otf);
340 ++size;
342 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
343 emptyline = (*buf == '\n');
344 ++lines;
346 if (!emptyline) {
347 putc('\n', mb.mb_otf);
348 lines++;
349 size++;
351 Fclose(fp);
352 fflush(mb.mb_otf);
353 m->m_size = m->m_xsize = size;
354 m->m_lines = m->m_xlines = lines;
355 m->m_block = mailx_blockof(offset);
356 m->m_offset = mailx_offsetof(offset);
357 free(buf);
358 substdate(m);
361 void
362 maildir_quit(void)
364 sighandler_type saveint;
365 struct cw cw;
367 (void)&saveint;
368 if (cwget(&cw) == STOP) {
369 fprintf(stderr, "Fatal: Cannot open current directory\n");
370 return;
372 saveint = safe_signal(SIGINT, SIG_IGN);
373 if (chdir(mailname) < 0) {
374 fprintf(stderr, "Cannot change directory to \"%s\".\n",
375 mailname);
376 cwrelse(&cw);
377 return;
379 if (sigsetjmp(maildirjmp, 1) == 0) {
380 if (saveint != SIG_IGN)
381 safe_signal(SIGINT, maildircatch);
382 maildir_update();
384 safe_signal(SIGINT, saveint);
385 if (cwret(&cw) == STOP) {
386 fputs("Fatal: Cannot change back to current directory.\n",
387 stderr);
388 abort();
390 cwrelse(&cw);
393 static void
394 maildir_update(void)
396 struct message *m;
397 int dodel, c, gotcha = 0, held = 0, modflags = 0;
399 if (mb.mb_perm == 0)
400 goto free;
401 if (!edit) {
402 holdbits();
403 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
404 if (m->m_flag & MBOX)
405 c++;
407 if (c > 0)
408 if (makembox() == STOP)
409 goto bypass;
411 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
412 if (edit)
413 dodel = m->m_flag & MDELETED;
414 else
415 dodel = !((m->m_flag&MPRESERVE) ||
416 (m->m_flag&MTOUCH) == 0);
417 if (dodel) {
418 if (unlink(m->m_maildir_file) < 0)
419 fprintf(stderr, "Cannot delete file \"%s/%s\" "
420 "for message %d.\n",
421 mailname, m->m_maildir_file,
422 (int)(m - &message[0] + 1));
423 else
424 gotcha++;
425 } else {
426 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS) ||
427 m->m_flag & (MNEW|MBOXED|MSAVED|MSTATUS|
428 MFLAG|MUNFLAG|
429 MANSWER|MUNANSWER|
430 MDRAFT|MUNDRAFT)) {
431 move(m);
432 modflags++;
434 held++;
437 bypass:
438 if ((gotcha || modflags) && edit) {
439 printf(tr(168, "\"%s\" "), displayname);
440 printf(value("bsdcompat") || value("bsdmsgs") ?
441 catgets(catd, CATSET, 170, "complete\n") :
442 catgets(catd, CATSET, 212, "updated.\n"));
443 } else if (held && !edit && mb.mb_perm != 0) {
444 if (held == 1)
445 printf(tr(155, "Held 1 message in %s\n"), displayname);
446 else if (held > 1)
447 printf(tr(156, "Held %d messages in %s\n"), held,
448 displayname);
450 fflush(stdout);
451 free: for (m = &message[0]; m < &message[msgCount]; m++)
452 free(m->m_maildir_file);
455 static void
456 move(struct message *m)
458 char *fn, *new;
460 fn = mkname(0, m->m_flag, &m->m_maildir_file[4]);
461 new = savecat("cur/", fn);
462 if (strcmp(m->m_maildir_file, new) == 0)
463 return;
464 if (link(m->m_maildir_file, new) < 0) {
465 fprintf(stderr, "Cannot link \"%s/%s\" to \"%s/%s\": "
466 "message %d not touched.\n",
467 mailname, m->m_maildir_file,
468 mailname, new,
469 (int)(m - &message[0] + 1));
470 return;
472 if (unlink(m->m_maildir_file) < 0)
473 fprintf(stderr, "Cannot unlink \"%s/%s\".\n",
474 mailname, m->m_maildir_file);
477 static char *
478 mkname(time_t t, enum mflag f, const char *pref)
480 static unsigned long cnt;
481 static pid_t mypid;
482 char *cp;
483 static char *node;
484 int size, n, i;
486 if (pref == NULL) {
487 if (mypid == 0)
488 mypid = getpid();
489 if (node == NULL) {
490 cp = nodename(0);
491 n = size = 0;
492 do {
493 if (n < size + 8)
494 node = srealloc(node, size += 20);
495 switch (*cp) {
496 case '/':
497 node[n++] = '\\', node[n++] = '0',
498 node[n++] = '5', node[n++] = '7';
499 break;
500 case ':':
501 node[n++] = '\\', node[n++] = '0',
502 node[n++] = '7', node[n++] = '2';
503 break;
504 default:
505 node[n++] = *cp;
507 } while (*cp++);
509 size = 60 + strlen(node);
510 cp = salloc(size);
511 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
512 (unsigned long)t,
513 (unsigned long)mypid, ++cnt, node);
514 } else {
515 size = (n = strlen(pref)) + 13;
516 cp = salloc(size);
517 memcpy(cp, pref, n + 1);
518 for (i = n; i > 3; i--)
519 if (cp[i-1] == ',' && cp[i-2] == '2' &&
520 cp[i-3] == ':') {
521 n = i;
522 break;
524 if (i <= 3) {
525 memcpy(cp + n, ":2,", 4);
526 n += 3;
529 if (n < size - 7) {
530 if (f & MDRAFTED)
531 cp[n++] = 'D';
532 if (f & MFLAGGED)
533 cp[n++] = 'F';
534 if (f & MANSWERED)
535 cp[n++] = 'R';
536 if (f & MREAD)
537 cp[n++] = 'S';
538 if (f & MDELETED)
539 cp[n++] = 'T';
540 cp[n] = '\0';
542 return cp;
545 static void
546 maildircatch(int s)
548 siglongjmp(maildirjmp, s);
551 enum okay
552 maildir_append(const char *name, FILE *fp)
554 char *buf, *bp, *lp;
555 size_t bufsize, buflen, cnt;
556 off_t off1 = -1, offs;
557 int inhead = 1;
558 int flag = MNEW|MNEWEST;
559 long size = 0;
560 enum okay ok;
562 if (mkmaildir(name) != OKAY)
563 return STOP;
564 buf = smalloc(bufsize = LINESIZE);
565 buflen = 0;
566 cnt = fsize(fp);
567 offs = ftell(fp);
568 do {
569 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
570 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
571 if (off1 != (off_t)-1) {
572 ok = maildir_append1(name, fp, off1,
573 size, flag);
574 if (ok == STOP)
575 return STOP;
576 if (fseek(fp, offs+buflen, SEEK_SET) < 0)
577 return STOP;
579 off1 = offs + buflen;
580 size = 0;
581 inhead = 1;
582 flag = MNEW;
583 } else
584 size += buflen;
585 offs += buflen;
586 if (bp && buf[0] == '\n')
587 inhead = 0;
588 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
589 lp = &buf[6];
590 while (whitechar(*lp&0377))
591 lp++;
592 if (*lp == ':')
593 while (*++lp != '\0')
594 switch (*lp) {
595 case 'R':
596 flag |= MREAD;
597 break;
598 case 'O':
599 flag &= ~MNEW;
600 break;
602 } else if (bp && inhead &&
603 ascncasecmp(buf, "x-status", 8) == 0) {
604 lp = &buf[8];
605 while (whitechar(*lp&0377))
606 lp++;
607 if (*lp == ':')
608 while (*++lp != '\0')
609 switch (*lp) {
610 case 'F':
611 flag |= MFLAGGED;
612 break;
613 case 'A':
614 flag |= MANSWERED;
615 break;
616 case 'T':
617 flag |= MDRAFTED;
618 break;
621 } while (bp != NULL);
622 free(buf);
623 return OKAY;
626 static enum okay
627 maildir_append1(const char *name, FILE *fp, off_t off1, long size,
628 enum mflag flag)
630 int const attempts = 43200;
631 char buf[4096], *fn, *tmp, *new;
632 struct stat st;
633 FILE *op;
634 long n, z;
635 int i;
636 time_t now;
638 /* Create a unique temporary file */
639 for (i = 0;; sleep(1), ++i) {
640 if (i >= attempts) {
641 fprintf(stderr, tr(198,
642 "Can't create an unique file name in "
643 "\"%s/tmp\".\n"), name);
644 return STOP;
647 time(&now);
648 fn = mkname(now, flag, NULL);
649 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
650 snprintf(tmp, n, "%s/tmp/%s", name, fn);
651 if (stat(tmp, &st) >= 0 || errno != ENOENT)
652 continue;
654 /* Use "wx" for O_EXCL */
655 if ((op = Fopen(tmp, "wx")) != NULL)
656 break;
659 if (fseek(fp, off1, SEEK_SET) < 0)
660 goto jtmperr;
661 while (size > 0) {
662 z = size > (long)sizeof buf ? (long)sizeof buf : size;
663 if ((n = fread(buf, 1, z, fp)) != z ||
664 (size_t)n != fwrite(buf, 1, n, op)) {
665 jtmperr:
666 fprintf(stderr, "Error writing to \"%s\".\n", tmp);
667 Fclose(op);
668 unlink(tmp);
669 return STOP;
671 size -= n;
673 Fclose(op);
675 new = salloc(n = strlen(name) + strlen(fn) + 6);
676 snprintf(new, n, "%s/new/%s", name, fn);
677 if (link(tmp, new) < 0) {
678 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);
679 return STOP;
681 if (unlink(tmp) < 0)
682 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp);
683 return OKAY;
686 static enum okay
687 trycreate(const char *name)
689 struct stat st;
691 if (stat(name, &st) == 0) {
692 if (!S_ISDIR(st.st_mode)) {
693 fprintf(stderr, "\"%s\" is not a directory.\n", name);
694 return STOP;
696 } else if (makedir(name) != OKAY) {
697 fprintf(stderr, "Cannot create directory \"%s\".\n", name);
698 return STOP;
699 } else
700 imap_created_mailbox++;
701 return OKAY;
704 static enum okay
705 mkmaildir(const char *name)
707 char *np;
708 size_t sz;
709 enum okay ok = STOP;
711 if (trycreate(name) == OKAY) {
712 np = ac_alloc((sz = strlen(name)) + 5);
713 memcpy(np, name, sz);
714 memcpy(np + sz, "/tmp", 5);
715 if (trycreate(np) == OKAY) {
716 strcpy(&np[sz], "/new");
717 if (trycreate(np) == OKAY) {
718 strcpy(&np[sz], "/cur");
719 if (trycreate(np) == OKAY)
720 ok = OKAY;
723 ac_free(np);
725 return ok;
728 static struct message *
729 mdlook(const char *name, struct message *data)
731 struct mditem *md;
732 unsigned c, h, n = 0;
734 if (data && data->m_maildir_hash)
735 h = ~data->m_maildir_hash;
736 else
737 h = pjw(name);
738 h %= mdprime;
739 md = &mdtable[c = h];
740 while (md->md_data != NULL) {
741 if (strcmp(&md->md_data->m_maildir_file[4], name) == 0)
742 break;
743 c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
744 n++;
745 while (c >= (unsigned)mdprime)
746 c -= (unsigned)mdprime;
747 md = &mdtable[c];
749 if (data != NULL && md->md_data == NULL)
750 md->md_data = data;
751 return md->md_data ? md->md_data : NULL;
754 static void
755 mktable(void)
757 int i;
759 mdprime = nextprime(msgCount);
760 mdtable = scalloc(mdprime, sizeof *mdtable);
761 for (i = 0; i < msgCount; i++)
762 mdlook(&message[i].m_maildir_file[4], &message[i]);
765 static enum okay
766 subdir_remove(const char *name, const char *sub)
768 char *path;
769 int pathsize, pathend, namelen, sublen, n;
770 DIR *dirp;
771 struct dirent *dp;
773 namelen = strlen(name);
774 sublen = strlen(sub);
775 path = smalloc(pathsize = namelen + sublen + 30);
776 memcpy(path, name, namelen);
777 path[namelen] = '/';
778 memcpy(path + namelen + 1, sub, sublen);
779 path[namelen+sublen+1] = '/';
780 path[pathend = namelen + sublen + 2] = '\0';
781 if ((dirp = opendir(path)) == NULL) {
782 perror(path);
783 free(path);
784 return STOP;
786 while ((dp = readdir(dirp)) != NULL) {
787 if (dp->d_name[0] == '.' &&
788 (dp->d_name[1] == '\0' ||
789 (dp->d_name[1] == '.' &&
790 dp->d_name[2] == '\0')))
791 continue;
792 if (dp->d_name[0] == '.')
793 continue;
794 n = strlen(dp->d_name);
795 if (pathend + n + 1 > pathsize)
796 path = srealloc(path, pathsize = pathend + n + 30);
797 memcpy(path + pathend, dp->d_name, n + 1);
798 if (unlink(path) < 0) {
799 perror(path);
800 closedir(dirp);
801 free(path);
802 return STOP;
805 closedir(dirp);
806 path[pathend] = '\0';
807 if (rmdir(path) < 0) {
808 perror(path);
809 free(path);
810 return STOP;
812 free(path);
813 return OKAY;
816 enum okay
817 maildir_remove(const char *name)
819 if (subdir_remove(name, "tmp") == STOP ||
820 subdir_remove(name, "new") == STOP ||
821 subdir_remove(name, "cur") == STOP)
822 return STOP;
823 if (rmdir(name) < 0) {
824 perror(name);
825 return STOP;
827 return OKAY;