mk-conf.sh, mk-mk.in: use variables for *all* utilities
[s-mailx.git] / maildir.c
blobb1ef9d1d39f86918b068f7f7abcdcde9ad667164
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 #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 (void)&saveint;
109 (void)&i;
110 omsgCount = msgCount;
111 if (cwget(&cw) == STOP) {
112 fprintf(stderr, "Fatal: Cannot open current directory\n");
113 return -1;
115 if (!nmail)
116 quit();
117 saveint = safe_signal(SIGINT, SIG_IGN);
118 if (chdir(name) < 0) {
119 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);
120 cwrelse(&cw);
121 return -1;
123 if (!nmail) {
124 edit = (isedit != 0);
125 if (mb.mb_itf) {
126 fclose(mb.mb_itf);
127 mb.mb_itf = NULL;
129 if (mb.mb_otf) {
130 fclose(mb.mb_otf);
131 mb.mb_otf = NULL;
133 initbox(name);
134 mb.mb_type = MB_MAILDIR;
136 _maildir_table = NULL;
137 if (sigsetjmp(_maildir_jmp, 1) == 0) {
138 if (nmail)
139 mktable();
140 if (saveint != SIG_IGN)
141 safe_signal(SIGINT, maildircatch);
142 i = maildir_setfile1(name, nmail, omsgCount);
144 if (nmail && _maildir_table != NULL)
145 free(_maildir_table);
146 safe_signal(SIGINT, saveint);
147 if (i < 0) {
148 mb.mb_type = MB_VOID;
149 *mailname = '\0';
150 msgCount = 0;
152 if (cwret(&cw) == STOP) {
153 fputs("Fatal: Cannot change back to current directory.\n",
154 stderr);
155 abort();
157 cwrelse(&cw);
158 setmsize(msgCount);
159 if (nmail && mb.mb_sorted && msgCount > omsgCount) {
160 mb.mb_threaded = 0;
161 sort((void *)-1);
163 if (!nmail)
164 sawcom = FAL0;
165 if (!nmail && !edit && msgCount == 0) {
166 if (mb.mb_type == MB_MAILDIR && value("emptystart") == NULL)
167 fprintf(stderr, "No mail at %s\n", name);
168 return 1;
170 if (nmail && msgCount > omsgCount)
171 newmailinfo(omsgCount);
172 return 0;
175 static int
176 maildir_setfile1(const char *name, int nmail, int omsgCount)
178 int i;
180 if (! nmail)
181 _cleantmp();
182 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
183 if ((i = subdir(name, "cur", nmail)) != 0)
184 return i;
185 if ((i = subdir(name, "new", nmail)) != 0)
186 return i;
187 _maildir_append(name, NULL, NULL);
188 for (i = nmail?omsgCount:0; i < msgCount; i++)
189 readin(name, &message[i]);
190 if (nmail) {
191 if (msgCount > omsgCount)
192 qsort(&message[omsgCount],
193 msgCount - omsgCount,
194 sizeof *message, mdcmp);
195 } else {
196 if (msgCount)
197 qsort(message, msgCount, sizeof *message, mdcmp);
199 return msgCount;
203 * In combination with the names from mkname(), this comparison function
204 * ensures that the order of messages in a maildir folder created by mailx
205 * remains always the same. In effect, if a mbox folder is transferred to
206 * a maildir folder by 'copy *', the order of the messages in mailx will
207 * not change.
209 static int
210 mdcmp(const void *a, const void *b)
212 long i;
214 if ((i = ((struct message const*)a)->m_time -
215 ((struct message const*)b)->m_time) == 0)
216 i = strcmp(&((struct message const*)a)->m_maildir_file[4],
217 &((struct message const*)b)->m_maildir_file[4]);
218 return i;
221 static int
222 subdir(const char *name, const char *sub, int nmail)
224 DIR *dirp;
225 struct dirent *dp;
227 if ((dirp = opendir(sub)) == NULL) {
228 fprintf(stderr, "Cannot open directory \"%s/%s\".\n",
229 name, sub);
230 return -1;
232 if (access(sub, W_OK) < 0)
233 mb.mb_perm = 0;
234 while ((dp = readdir(dirp)) != NULL) {
235 if (dp->d_name[0] == '.' &&
236 (dp->d_name[1] == '\0' ||
237 (dp->d_name[1] == '.' &&
238 dp->d_name[2] == '\0')))
239 continue;
240 if (dp->d_name[0] == '.')
241 continue;
242 if (!nmail || mdlook(dp->d_name, NULL) == NULL)
243 _maildir_append(name, sub, dp->d_name);
245 closedir(dirp);
246 return 0;
249 static void
250 _maildir_append(const char *name, const char *sub, const char *fn)
252 struct message *m;
253 size_t sz, i;
254 time_t t = 0;
255 enum mflag f = MUSED|MNOFROM|MNEWEST;
256 const char *cp;
257 char *xp;
258 (void)name;
260 if (fn && sub) {
261 if (strcmp(sub, "new") == 0)
262 f |= MNEW;
263 t = strtol(fn, &xp, 10);
264 if ((cp = strrchr(xp, ',')) != NULL &&
265 cp > &xp[2] && cp[-1] == '2' && cp[-2] == ':') {
266 while (*++cp) {
267 switch (*cp) {
268 case 'F':
269 f |= MFLAGGED;
270 break;
271 case 'R':
272 f |= MANSWERED;
273 break;
274 case 'S':
275 f |= MREAD;
276 break;
277 case 'T':
278 f |= MDELETED;
279 break;
280 case 'D':
281 f |= MDRAFT;
282 break;
287 if (msgCount + 1 >= msgspace) {
288 const int chunk = 64;
289 message = srealloc(message,
290 (msgspace += chunk) * sizeof *message);
291 memset(&message[msgCount], 0, chunk * sizeof *message);
293 if (fn == NULL || sub == NULL)
294 return;
295 m = &message[msgCount++];
296 i = strlen(fn);
297 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 2);
298 memcpy(m->m_maildir_file, sub, sz);
299 m->m_maildir_file[sz] = '/';
300 memcpy(m->m_maildir_file + sz + 1, fn, i + 1);
301 m->m_time = t;
302 m->m_flag = f;
303 m->m_maildir_hash = ~pjw(fn);
304 return;
307 static void
308 readin(const char *name, struct message *m)
310 char *buf;
311 size_t bufsize, buflen, cnt;
312 long size = 0, lines = 0;
313 off_t offset;
314 FILE *fp;
315 int emptyline = 0;
317 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
318 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
319 name, m->m_maildir_file,
320 (int)(m - &message[0] + 1));
321 m->m_flag |= MHIDDEN;
322 return;
324 buf = smalloc(bufsize = LINESIZE);
325 buflen = 0;
326 cnt = fsize(fp);
327 fseek(mb.mb_otf, 0L, SEEK_END);
328 offset = ftell(mb.mb_otf);
329 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
331 * Since we simply copy over data without doing any transfer
332 * encoding reclassification/adjustment we *have* to perform
333 * RFC 4155 compliant From_ quoting here
335 if (is_head(buf, buflen)) {
336 putc('>', mb.mb_otf);
337 ++size;
339 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
340 emptyline = (*buf == '\n');
341 ++lines;
343 if (!emptyline) {
344 putc('\n', mb.mb_otf);
345 lines++;
346 size++;
348 Fclose(fp);
349 fflush(mb.mb_otf);
350 m->m_size = m->m_xsize = size;
351 m->m_lines = m->m_xlines = lines;
352 m->m_block = mailx_blockof(offset);
353 m->m_offset = mailx_offsetof(offset);
354 free(buf);
355 substdate(m);
358 FL void
359 maildir_quit(void)
361 sighandler_type saveint;
362 struct cw cw;
364 (void)&saveint;
365 if (cwget(&cw) == STOP) {
366 fprintf(stderr, "Fatal: Cannot open current directory\n");
367 return;
369 saveint = safe_signal(SIGINT, SIG_IGN);
370 if (chdir(mailname) < 0) {
371 fprintf(stderr, "Cannot change directory to \"%s\".\n",
372 mailname);
373 cwrelse(&cw);
374 return;
376 if (sigsetjmp(_maildir_jmp, 1) == 0) {
377 if (saveint != SIG_IGN)
378 safe_signal(SIGINT, maildircatch);
379 maildir_update();
381 safe_signal(SIGINT, saveint);
382 if (cwret(&cw) == STOP) {
383 fputs("Fatal: Cannot change back to current directory.\n",
384 stderr);
385 abort();
387 cwrelse(&cw);
390 static void
391 maildir_update(void)
393 struct message *m;
394 int dodel, c, gotcha = 0, held = 0, modflags = 0;
396 if (mb.mb_perm == 0)
397 goto free;
398 if (!edit) {
399 holdbits();
400 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
401 if (m->m_flag & MBOX)
402 c++;
404 if (c > 0)
405 if (makembox() == STOP)
406 goto bypass;
408 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
409 if (edit)
410 dodel = m->m_flag & MDELETED;
411 else
412 dodel = !((m->m_flag&MPRESERVE) ||
413 (m->m_flag&MTOUCH) == 0);
414 if (dodel) {
415 if (unlink(m->m_maildir_file) < 0)
416 fprintf(stderr, "Cannot delete file \"%s/%s\" "
417 "for message %d.\n",
418 mailname, m->m_maildir_file,
419 (int)(m - &message[0] + 1));
420 else
421 gotcha++;
422 } else {
423 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS) ||
424 m->m_flag & (MNEW|MBOXED|MSAVED|MSTATUS|
425 MFLAG|MUNFLAG|
426 MANSWER|MUNANSWER|
427 MDRAFT|MUNDRAFT)) {
428 move(m);
429 modflags++;
431 held++;
434 bypass:
435 if ((gotcha || modflags) && edit) {
436 printf(tr(168, "\"%s\" "), displayname);
437 printf((value("bsdcompat") || value("bsdmsgs"))
438 ? tr(170, "complete\n") : tr(212, "updated.\n"));
439 } else if (held && !edit && mb.mb_perm != 0) {
440 if (held == 1)
441 printf(tr(155, "Held 1 message in %s\n"), displayname);
442 else
443 printf(tr(156, "Held %d messages in %s\n"), held,
444 displayname);
446 fflush(stdout);
447 free: for (m = &message[0]; m < &message[msgCount]; m++)
448 free(m->m_maildir_file);
451 static void
452 move(struct message *m)
454 char *fn, *new;
456 fn = mkname(0, m->m_flag, &m->m_maildir_file[4]);
457 new = savecat("cur/", fn);
458 if (strcmp(m->m_maildir_file, new) == 0)
459 return;
460 if (link(m->m_maildir_file, new) < 0) {
461 fprintf(stderr, "Cannot link \"%s/%s\" to \"%s/%s\": "
462 "message %d not touched.\n",
463 mailname, m->m_maildir_file,
464 mailname, new,
465 (int)(m - &message[0] + 1));
466 return;
468 if (unlink(m->m_maildir_file) < 0)
469 fprintf(stderr, "Cannot unlink \"%s/%s\".\n",
470 mailname, m->m_maildir_file);
473 static char *
474 mkname(time_t t, enum mflag f, const char *pref)
476 static unsigned long cnt;
477 static pid_t mypid;
478 char *cp;
479 static char *node;
480 int size, n, i;
482 if (pref == NULL) {
483 if (mypid == 0)
484 mypid = getpid();
485 if (node == NULL) {
486 cp = nodename(0);
487 n = size = 0;
488 do {
489 if (UICMP(32, n, <, size + 8))
490 node = srealloc(node, size += 20);
491 switch (*cp) {
492 case '/':
493 node[n++] = '\\', node[n++] = '0',
494 node[n++] = '5', node[n++] = '7';
495 break;
496 case ':':
497 node[n++] = '\\', node[n++] = '0',
498 node[n++] = '7', node[n++] = '2';
499 break;
500 default:
501 node[n++] = *cp;
503 } while (*cp++);
505 size = 60 + strlen(node);
506 cp = salloc(size);
507 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
508 (unsigned long)t,
509 (unsigned long)mypid, ++cnt, node);
510 } else {
511 size = (n = strlen(pref)) + 13;
512 cp = salloc(size);
513 memcpy(cp, pref, n + 1);
514 for (i = n; i > 3; i--)
515 if (cp[i-1] == ',' && cp[i-2] == '2' &&
516 cp[i-3] == ':') {
517 n = i;
518 break;
520 if (i <= 3) {
521 memcpy(cp + n, ":2,", 4);
522 n += 3;
525 if (n < size - 7) {
526 if (f & MDRAFTED)
527 cp[n++] = 'D';
528 if (f & MFLAGGED)
529 cp[n++] = 'F';
530 if (f & MANSWERED)
531 cp[n++] = 'R';
532 if (f & MREAD)
533 cp[n++] = 'S';
534 if (f & MDELETED)
535 cp[n++] = 'T';
536 cp[n] = '\0';
538 return cp;
541 static void
542 maildircatch(int s)
544 siglongjmp(_maildir_jmp, s);
547 FL enum okay
548 maildir_append(const char *name, FILE *fp)
550 char *buf, *bp, *lp;
551 size_t bufsize, buflen, cnt;
552 off_t off1 = -1, offs;
553 int inhead = 1;
554 int flag = MNEW|MNEWEST;
555 long size = 0;
556 enum okay ok;
558 if (mkmaildir(name) != OKAY)
559 return STOP;
560 buf = smalloc(bufsize = LINESIZE);
561 buflen = 0;
562 cnt = fsize(fp);
563 offs = ftell(fp);
564 do {
565 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
566 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
567 if (off1 != (off_t)-1) {
568 ok = maildir_append1(name, fp, off1,
569 size, flag);
570 if (ok == STOP)
571 return STOP;
572 if (fseek(fp, offs+buflen, SEEK_SET) < 0)
573 return STOP;
575 off1 = offs + buflen;
576 size = 0;
577 inhead = 1;
578 flag = MNEW;
579 } else
580 size += buflen;
581 offs += buflen;
582 if (bp && buf[0] == '\n')
583 inhead = 0;
584 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
585 lp = &buf[6];
586 while (whitechar(*lp&0377))
587 lp++;
588 if (*lp == ':')
589 while (*++lp != '\0')
590 switch (*lp) {
591 case 'R':
592 flag |= MREAD;
593 break;
594 case 'O':
595 flag &= ~MNEW;
596 break;
598 } else if (bp && inhead &&
599 ascncasecmp(buf, "x-status", 8) == 0) {
600 lp = &buf[8];
601 while (whitechar(*lp&0377))
602 lp++;
603 if (*lp == ':')
604 while (*++lp != '\0')
605 switch (*lp) {
606 case 'F':
607 flag |= MFLAGGED;
608 break;
609 case 'A':
610 flag |= MANSWERED;
611 break;
612 case 'T':
613 flag |= MDRAFTED;
614 break;
617 } while (bp != NULL);
618 free(buf);
619 return OKAY;
622 static enum okay
623 maildir_append1(const char *name, FILE *fp, off_t off1, long size,
624 enum mflag flag)
626 int const attempts = 43200;
627 char buf[4096], *fn, *tmp, *new;
628 struct stat st;
629 FILE *op;
630 long n, z;
631 int i;
632 time_t now;
634 /* Create a unique temporary file */
635 for (i = 0;; sleep(1), ++i) {
636 if (i >= attempts) {
637 fprintf(stderr, tr(198,
638 "Can't create an unique file name in "
639 "\"%s/tmp\".\n"), name);
640 return STOP;
643 time(&now);
644 fn = mkname(now, flag, NULL);
645 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
646 snprintf(tmp, n, "%s/tmp/%s", name, fn);
647 if (stat(tmp, &st) >= 0 || errno != ENOENT)
648 continue;
650 /* Use "wx" for O_EXCL */
651 if ((op = Fopen(tmp, "wx")) != NULL)
652 break;
655 if (fseek(fp, off1, SEEK_SET) < 0)
656 goto jtmperr;
657 while (size > 0) {
658 z = size > (long)sizeof buf ? (long)sizeof buf : size;
659 if ((n = fread(buf, 1, z, fp)) != z ||
660 (size_t)n != fwrite(buf, 1, n, op)) {
661 jtmperr:
662 fprintf(stderr, "Error writing to \"%s\".\n", tmp);
663 Fclose(op);
664 unlink(tmp);
665 return STOP;
667 size -= n;
669 Fclose(op);
671 new = salloc(n = strlen(name) + strlen(fn) + 6);
672 snprintf(new, n, "%s/new/%s", name, fn);
673 if (link(tmp, new) < 0) {
674 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);
675 return STOP;
677 if (unlink(tmp) < 0)
678 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp);
679 return OKAY;
682 static enum okay
683 trycreate(const char *name)
685 struct stat st;
687 if (stat(name, &st) == 0) {
688 if (!S_ISDIR(st.st_mode)) {
689 fprintf(stderr, "\"%s\" is not a directory.\n", name);
690 return STOP;
692 } else if (makedir(name) != OKAY) {
693 fprintf(stderr, "Cannot create directory \"%s\".\n", name);
694 return STOP;
695 } else
696 imap_created_mailbox++;
697 return OKAY;
700 static enum okay
701 mkmaildir(const char *name)
703 char *np;
704 size_t sz;
705 enum okay ok = STOP;
707 if (trycreate(name) == OKAY) {
708 np = ac_alloc((sz = strlen(name)) + 5);
709 memcpy(np, name, sz);
710 memcpy(np + sz, "/tmp", 5);
711 if (trycreate(np) == OKAY) {
712 strcpy(&np[sz], "/new");
713 if (trycreate(np) == OKAY) {
714 strcpy(&np[sz], "/cur");
715 if (trycreate(np) == OKAY)
716 ok = OKAY;
719 ac_free(np);
721 return ok;
724 static struct message *
725 mdlook(const char *name, struct message *data)
727 struct mditem *md;
728 unsigned c, h, n = 0;
730 if (data && data->m_maildir_hash)
731 h = ~data->m_maildir_hash;
732 else
733 h = pjw(name);
734 h %= _maildir_prime;
735 md = &_maildir_table[c = h];
736 while (md->md_data != NULL) {
737 if (strcmp(&md->md_data->m_maildir_file[4], name) == 0)
738 break;
739 c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
740 n++;
741 while (c >= (unsigned)_maildir_prime)
742 c -= (unsigned)_maildir_prime;
743 md = &_maildir_table[c];
745 if (data != NULL && md->md_data == NULL)
746 md->md_data = data;
747 return md->md_data ? md->md_data : NULL;
750 static void
751 mktable(void)
753 int i;
755 _maildir_prime = nextprime(msgCount);
756 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
757 for (i = 0; i < msgCount; i++)
758 mdlook(&message[i].m_maildir_file[4], &message[i]);
761 static enum okay
762 subdir_remove(const char *name, const char *sub)
764 char *path;
765 int pathsize, pathend, namelen, sublen, n;
766 DIR *dirp;
767 struct dirent *dp;
769 namelen = strlen(name);
770 sublen = strlen(sub);
771 path = smalloc(pathsize = namelen + sublen + 30);
772 memcpy(path, name, namelen);
773 path[namelen] = '/';
774 memcpy(path + namelen + 1, sub, sublen);
775 path[namelen+sublen+1] = '/';
776 path[pathend = namelen + sublen + 2] = '\0';
777 if ((dirp = opendir(path)) == NULL) {
778 perror(path);
779 free(path);
780 return STOP;
782 while ((dp = readdir(dirp)) != NULL) {
783 if (dp->d_name[0] == '.' &&
784 (dp->d_name[1] == '\0' ||
785 (dp->d_name[1] == '.' &&
786 dp->d_name[2] == '\0')))
787 continue;
788 if (dp->d_name[0] == '.')
789 continue;
790 n = strlen(dp->d_name);
791 if (UICMP(32, pathend + n + 1, >, pathsize))
792 path = srealloc(path, pathsize = pathend + n + 30);
793 memcpy(path + pathend, dp->d_name, n + 1);
794 if (unlink(path) < 0) {
795 perror(path);
796 closedir(dirp);
797 free(path);
798 return STOP;
801 closedir(dirp);
802 path[pathend] = '\0';
803 if (rmdir(path) < 0) {
804 perror(path);
805 free(path);
806 return STOP;
808 free(path);
809 return OKAY;
812 FL enum okay
813 maildir_remove(const char *name)
815 if (subdir_remove(name, "tmp") == STOP ||
816 subdir_remove(name, "new") == STOP ||
817 subdir_remove(name, "cur") == STOP)
818 return STOP;
819 if (rmdir(name) < 0) {
820 perror(name);
821 return STOP;
823 return OKAY;