send.c: forgot to commit one more longjmp(2) globber fix
[s-mailx.git] / maildir.c
blob55b583ab6b4abf3d1ecf1876d0e6d4c3d76b585e
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
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 "config.h"
42 #include "rcv.h"
43 #include "extern.h"
44 #include <time.h>
45 #include <dirent.h>
46 #include <unistd.h>
47 #include <sys/stat.h>
48 #include <errno.h>
51 * Mail -- a mail program
53 * Maildir folder support.
56 static struct mditem {
57 struct message *md_data;
58 unsigned md_hash;
59 } *mdtable;
60 static long mdprime;
62 static sigjmp_buf maildirjmp;
64 static int maildir_setfile1(const char *name, int newmail, int omsgCount);
65 static int mdcmp(const void *a, const void *b);
66 static int subdir(const char *name, const char *sub, int newmail);
67 static void cleantmp(const char *name);
68 static void append(const char *name, const char *sub, const char *fn);
69 static void readin(const char *name, struct message *m);
70 static void maildir_update(void);
71 static void move(struct message *m);
72 static char *mkname(time_t t, enum mflag f, const char *pref);
73 static void maildircatch(int s);
74 static enum okay maildir_append1(const char *name, FILE *fp, off_t off1,
75 long size, enum mflag flag);
76 static enum okay trycreate(const char *name);
77 static enum okay mkmaildir(const char *name);
78 static struct message *mdlook(const char *name, struct message *data);
79 static void mktable(void);
80 static enum okay subdir_remove(const char *name, const char *sub);
82 int
83 maildir_setfile(const char *name, int newmail, int isedit)
85 sighandler_type saveint;
86 struct cw cw;
87 int i = -1, omsgCount;
89 (void)&saveint;
90 (void)&i;
91 omsgCount = msgCount;
92 if (cwget(&cw) == STOP) {
93 fprintf(stderr, "Fatal: Cannot open current directory\n");
94 return -1;
96 if (!newmail)
97 quit();
98 saveint = safe_signal(SIGINT, SIG_IGN);
99 if (chdir(name) < 0) {
100 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);
101 cwrelse(&cw);
102 return -1;
104 if (!newmail) {
105 edit = isedit;
106 if (mb.mb_itf) {
107 fclose(mb.mb_itf);
108 mb.mb_itf = NULL;
110 if (mb.mb_otf) {
111 fclose(mb.mb_otf);
112 mb.mb_otf = NULL;
114 initbox(name);
115 mb.mb_type = MB_MAILDIR;
117 mdtable = NULL;
118 if (sigsetjmp(maildirjmp, 1) == 0) {
119 if (newmail)
120 mktable();
121 if (saveint != SIG_IGN)
122 safe_signal(SIGINT, maildircatch);
123 i = maildir_setfile1(name, newmail, omsgCount);
125 if (newmail)
126 free(mdtable);
127 safe_signal(SIGINT, saveint);
128 if (i < 0) {
129 mb.mb_type = MB_VOID;
130 *mailname = '\0';
131 msgCount = 0;
133 if (cwret(&cw) == STOP) {
134 fputs("Fatal: Cannot change back to current directory.\n",
135 stderr);
136 abort();
138 cwrelse(&cw);
139 setmsize(msgCount);
140 if (newmail && mb.mb_sorted && msgCount > omsgCount) {
141 mb.mb_threaded = 0;
142 sort((void *)-1);
144 if (!newmail)
145 sawcom = 0;
146 if (!newmail && !edit && msgCount == 0) {
147 if (mb.mb_type == MB_MAILDIR && value("emptystart") == NULL)
148 fprintf(stderr, "No mail at %s\n", name);
149 return 1;
151 if (newmail && msgCount > omsgCount)
152 newmailinfo(omsgCount);
153 return 0;
156 static int
157 maildir_setfile1(const char *name, int newmail, int omsgCount)
159 int i;
161 if (!newmail)
162 cleantmp(name);
163 mb.mb_perm = Rflag ? 0 : MB_DELE;
164 if ((i = subdir(name, "cur", newmail)) != 0)
165 return i;
166 if ((i = subdir(name, "new", newmail)) != 0)
167 return i;
168 append(name, NULL, NULL);
169 for (i = newmail?omsgCount:0; i < msgCount; i++)
170 readin(name, &message[i]);
171 if (newmail) {
172 if (msgCount > omsgCount)
173 qsort(&message[omsgCount],
174 msgCount - omsgCount,
175 sizeof *message, mdcmp);
176 } else {
177 if (msgCount)
178 qsort(message, msgCount, sizeof *message, mdcmp);
180 return msgCount;
184 * In combination with the names from mkname(), this comparison function
185 * ensures that the order of messages in a maildir folder created by mailx
186 * remains always the same. In effect, if a mbox folder is transferred to
187 * a maildir folder by 'copy *', the order of the messages in mailx will
188 * not change.
190 static int
191 mdcmp(const void *a, const void *b)
193 long i;
195 if ((i = ((struct message *)a)->m_time -
196 ((struct message *)b)->m_time) == 0)
197 i = strcmp(&((struct message *)a)->m_maildir_file[4],
198 &((struct message *)b)->m_maildir_file[4]);
199 return i;
202 static int
203 subdir(const char *name, const char *sub, int newmail)
205 DIR *dirfd;
206 struct dirent *dp;
208 if ((dirfd = opendir(sub)) == NULL) {
209 fprintf(stderr, "Cannot open directory \"%s/%s\".\n",
210 name, sub);
211 return -1;
213 if (access(sub, W_OK) < 0)
214 mb.mb_perm = 0;
215 while ((dp = readdir(dirfd)) != NULL) {
216 if (dp->d_name[0] == '.' &&
217 (dp->d_name[1] == '\0' ||
218 (dp->d_name[1] == '.' &&
219 dp->d_name[2] == '\0')))
220 continue;
221 if (dp->d_name[0] == '.')
222 continue;
223 if (!newmail || mdlook(dp->d_name, NULL) == NULL)
224 append(name, sub, dp->d_name);
226 closedir(dirfd);
227 return 0;
230 static void
231 cleantmp(const char *name)
233 struct stat st;
234 DIR *dirfd;
235 struct dirent *dp;
236 char *fn = NULL;
237 size_t fnsz = 0, ssz;
238 time_t now;
239 (void)name;
241 if ((dirfd = opendir("tmp")) == NULL)
242 return;
243 time(&now);
244 while ((dp = readdir(dirfd)) != NULL) {
245 if (dp->d_name[0] == '.' &&
246 (dp->d_name[1] == '\0' ||
247 (dp->d_name[1] == '.' &&
248 dp->d_name[2] == '\0')))
249 continue;
250 if (dp->d_name[0] == '.')
251 continue;
252 if ((ssz = strlen(dp->d_name)) + 5 > fnsz) {
253 free(fn);
254 fn = smalloc(fnsz = ssz + 40);
256 strcpy(fn, "tmp/");
257 strcpy(&fn[4], dp->d_name);
258 if (stat(fn, &st) < 0)
259 continue;
260 if (st.st_atime + 36*3600 < now)
261 unlink(fn);
263 free(fn);
264 closedir(dirfd);
267 static void
268 append(const char *name, const char *sub, const char *fn)
270 struct message *m;
271 size_t sz;
272 time_t t = 0;
273 enum mflag f = MUSED|MNOFROM|MNEWEST;
274 const char *cp;
275 char *xp;
276 (void)name;
278 if (fn && sub) {
279 if (strcmp(sub, "new") == 0)
280 f |= MNEW;
281 t = strtol(fn, &xp, 10);
282 if ((cp = strrchr(xp, ',')) != NULL &&
283 cp > &xp[2] && cp[-1] == '2' && cp[-2] == ':') {
284 while (*++cp) {
285 switch (*cp) {
286 case 'F':
287 f |= MFLAGGED;
288 break;
289 case 'R':
290 f |= MANSWERED;
291 break;
292 case 'S':
293 f |= MREAD;
294 break;
295 case 'T':
296 f |= MDELETED;
297 break;
298 case 'D':
299 f |= MDRAFT;
300 break;
305 if (msgCount + 1 >= msgspace) {
306 const int chunk = 64;
307 message = srealloc(message,
308 (msgspace += chunk) * sizeof *message);
309 memset(&message[msgCount], 0, chunk * sizeof *message);
311 if (fn == NULL || sub == NULL)
312 return;
313 m = &message[msgCount++];
314 m->m_maildir_file = smalloc((sz = strlen(sub)) + strlen(fn) + 2);
315 strcpy(m->m_maildir_file, sub);
316 m->m_maildir_file[sz] = '/';
317 strcpy(&m->m_maildir_file[sz+1], fn);
318 m->m_time = t;
319 m->m_flag = f;
320 m->m_maildir_hash = ~pjw(fn);
321 return;
324 static void
325 readin(const char *name, struct message *m)
327 char *buf, *bp;
328 size_t bufsize, buflen, count;
329 long size = 0, lines = 0;
330 off_t offset;
331 FILE *fp;
332 int emptyline = 0;
334 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
335 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
336 name, m->m_maildir_file,
337 (int)(m - &message[0] + 1));
338 m->m_flag |= MHIDDEN;
339 return;
341 buf = smalloc(bufsize = LINESIZE);
342 buflen = 0;
343 count = fsize(fp);
344 fseek(mb.mb_otf, 0L, SEEK_END);
345 offset = ftell(mb.mb_otf);
346 while (fgetline(&buf, &bufsize, &count, &buflen, fp, 1) != NULL) {
347 bp = buf;
348 if (buf[0] == 'F' && buf[1] == 'r' && buf[2] == 'o' &&
349 buf[3] == 'm' && buf[4] == ' ') {
350 putc('>', mb.mb_otf);
351 size++;
353 lines++;
354 size += fwrite(bp, 1, buflen, mb.mb_otf);
355 emptyline = *bp == '\n';
357 if (!emptyline) {
358 putc('\n', mb.mb_otf);
359 lines++;
360 size++;
362 Fclose(fp);
363 fflush(mb.mb_otf);
364 m->m_size = m->m_xsize = size;
365 m->m_lines = m->m_xlines = lines;
366 m->m_block = mailx_blockof(offset);
367 m->m_offset = mailx_offsetof(offset);
368 free(buf);
369 substdate(m);
372 void
373 maildir_quit(void)
375 sighandler_type saveint;
376 struct cw cw;
378 (void)&saveint;
379 if (cwget(&cw) == STOP) {
380 fprintf(stderr, "Fatal: Cannot open current directory\n");
381 return;
383 saveint = safe_signal(SIGINT, SIG_IGN);
384 if (chdir(mailname) < 0) {
385 fprintf(stderr, "Cannot change directory to \"%s\".\n",
386 mailname);
387 cwrelse(&cw);
388 return;
390 if (sigsetjmp(maildirjmp, 1) == 0) {
391 if (saveint != SIG_IGN)
392 safe_signal(SIGINT, maildircatch);
393 maildir_update();
395 safe_signal(SIGINT, saveint);
396 if (cwret(&cw) == STOP) {
397 fputs("Fatal: Cannot change back to current directory.\n",
398 stderr);
399 abort();
401 cwrelse(&cw);
404 static void
405 maildir_update(void)
407 FILE *readstat = NULL;
408 struct message *m;
409 int dodel, c, gotcha = 0, held = 0, modflags = 0;
411 if (mb.mb_perm == 0)
412 goto free;
413 if (Tflag != NULL) {
414 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
415 Tflag = NULL;
417 if (!edit) {
418 holdbits();
419 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
420 if (m->m_flag & MBOX)
421 c++;
423 if (c > 0)
424 if (makembox() == STOP)
425 goto bypass;
427 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
428 if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
429 char *id;
430 if ((id = hfield1("message-id", m)) != NULL ||
431 (id = hfieldX("article-id", m)) != NULL)
432 fprintf(readstat, "%s\n", id);
434 if (edit)
435 dodel = m->m_flag & MDELETED;
436 else
437 dodel = !((m->m_flag&MPRESERVE) ||
438 (m->m_flag&MTOUCH) == 0);
439 if (dodel) {
440 if (unlink(m->m_maildir_file) < 0)
441 fprintf(stderr, "Cannot delete file \"%s/%s\" "
442 "for message %d.\n",
443 mailname, m->m_maildir_file,
444 (int)(m - &message[0] + 1));
445 else
446 gotcha++;
447 } else {
448 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS) ||
449 m->m_flag & (MNEW|MBOXED|MSAVED|MSTATUS|
450 MFLAG|MUNFLAG|
451 MANSWER|MUNANSWER|
452 MDRAFT|MUNDRAFT)) {
453 move(m);
454 modflags++;
456 held++;
459 bypass: if (readstat != NULL)
460 Fclose(readstat);
461 if ((gotcha || modflags) && edit) {
462 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
463 printf(value("bsdcompat") || value("bsdmsgs") ?
464 catgets(catd, CATSET, 170, "complete\n") :
465 catgets(catd, CATSET, 212, "updated.\n"));
466 } else if (held && !edit && mb.mb_perm != 0) {
467 if (held == 1)
468 printf(catgets(catd, CATSET, 155,
469 "Held 1 message in %s\n"), mailname);
470 else if (held > 1)
471 printf(catgets(catd, CATSET, 156,
472 "Held %d messages in %s\n"), held, mailname);
474 fflush(stdout);
475 free: for (m = &message[0]; m < &message[msgCount]; m++)
476 free(m->m_maildir_file);
479 static void
480 move(struct message *m)
482 char *fn, *new;
484 fn = mkname(0, m->m_flag, &m->m_maildir_file[4]);
485 new = savecat("cur/", fn);
486 if (strcmp(m->m_maildir_file, new) == 0)
487 return;
488 if (link(m->m_maildir_file, new) < 0) {
489 fprintf(stderr, "Cannot link \"%s/%s\" to \"%s/%s\": "
490 "message %d not touched.\n",
491 mailname, m->m_maildir_file,
492 mailname, new,
493 (int)(m - &message[0] + 1));
494 return;
496 if (unlink(m->m_maildir_file) < 0)
497 fprintf(stderr, "Cannot unlink \"%s/%s\".\n",
498 mailname, m->m_maildir_file);
501 static char *
502 mkname(time_t t, enum mflag f, const char *pref)
504 static unsigned long count;
505 static pid_t mypid;
506 char *cp;
507 static char *node;
508 int size, n, i;
510 if (pref == NULL) {
511 if (mypid == 0)
512 mypid = getpid();
513 if (node == NULL) {
514 cp = nodename(0);
515 n = size = 0;
516 do {
517 if (n < size + 8)
518 node = srealloc(node, size += 20);
519 switch (*cp) {
520 case '/':
521 node[n++] = '\\', node[n++] = '0',
522 node[n++] = '5', node[n++] = '7';
523 break;
524 case ':':
525 node[n++] = '\\', node[n++] = '0',
526 node[n++] = '7', node[n++] = '2';
527 break;
528 default:
529 node[n++] = *cp;
531 } while (*cp++);
533 size = 60 + strlen(node);
534 cp = salloc(size);
535 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
536 (unsigned long)t,
537 (unsigned long)mypid, ++count, node);
538 } else {
539 size = (n = strlen(pref)) + 13;
540 cp = salloc(size);
541 strcpy(cp, pref);
542 for (i = n; i > 3; i--)
543 if (cp[i-1] == ',' && cp[i-2] == '2' &&
544 cp[i-3] == ':') {
545 n = i;
546 break;
548 if (i <= 3) {
549 strcpy(&cp[n], ":2,");
550 n += 3;
553 if (n < size - 7) {
554 if (f & MDRAFTED)
555 cp[n++] = 'D';
556 if (f & MFLAGGED)
557 cp[n++] = 'F';
558 if (f & MANSWERED)
559 cp[n++] = 'R';
560 if (f & MREAD)
561 cp[n++] = 'S';
562 if (f & MDELETED)
563 cp[n++] = 'T';
564 cp[n] = '\0';
566 return cp;
569 static void
570 maildircatch(int s)
572 siglongjmp(maildirjmp, s);
575 enum okay
576 maildir_append(const char *name, FILE *fp)
578 char *buf, *bp, *lp;
579 size_t bufsize, buflen, count;
580 off_t off1 = -1, offs;
581 int inhead = 1;
582 int flag = MNEW|MNEWEST;
583 long size = 0;
584 enum okay ok;
586 if (mkmaildir(name) != OKAY)
587 return STOP;
588 buf = smalloc(bufsize = LINESIZE);
589 buflen = 0;
590 count = fsize(fp);
591 offs = ftell(fp);
592 do {
593 bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
594 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
595 if (off1 != (off_t)-1) {
596 ok = maildir_append1(name, fp, off1,
597 size, flag);
598 if (ok == STOP)
599 return STOP;
600 fseek(fp, offs+buflen, SEEK_SET);
602 off1 = offs + buflen;
603 size = 0;
604 inhead = 1;
605 flag = MNEW;
606 } else
607 size += buflen;
608 offs += buflen;
609 if (bp && buf[0] == '\n')
610 inhead = 0;
611 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
612 lp = &buf[6];
613 while (whitechar(*lp&0377))
614 lp++;
615 if (*lp == ':')
616 while (*++lp != '\0')
617 switch (*lp) {
618 case 'R':
619 flag |= MREAD;
620 break;
621 case 'O':
622 flag &= ~MNEW;
623 break;
625 } else if (bp && inhead &&
626 ascncasecmp(buf, "x-status", 8) == 0) {
627 lp = &buf[8];
628 while (whitechar(*lp&0377))
629 lp++;
630 if (*lp == ':')
631 while (*++lp != '\0')
632 switch (*lp) {
633 case 'F':
634 flag |= MFLAGGED;
635 break;
636 case 'A':
637 flag |= MANSWERED;
638 break;
639 case 'T':
640 flag |= MDRAFTED;
641 break;
644 } while (bp != NULL);
645 free(buf);
646 return OKAY;
649 static enum okay
650 maildir_append1(const char *name, FILE *fp, off_t off1, long size,
651 enum mflag flag)
653 const int attempts = 43200;
654 struct stat st;
655 char buf[4096];
656 char *fn, *tmp, *new;
657 FILE *op;
658 long n, z;
659 int i;
660 time_t now;
662 for (i = 0; i < attempts; i++) {
663 time(&now);
664 fn = mkname(now, flag, NULL);
665 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
666 snprintf(tmp, n, "%s/tmp/%s", name, fn);
667 if (stat(tmp, &st) < 0 && errno == ENOENT)
668 break;
669 sleep(2);
671 if (i >= attempts) {
672 fprintf(stderr,
673 "Cannot create unique file name in \"%s/tmp\".\n",
674 name);
675 return STOP;
677 if ((op = Fopen(tmp, "w")) == NULL) {
678 fprintf(stderr, "Cannot write to \"%s\".\n", tmp);
679 return STOP;
681 fseek(fp, off1, SEEK_SET);
682 while (size > 0) {
683 z = size > (long)sizeof buf ? (long)sizeof buf : size;
684 if ((n = fread(buf, 1, z, fp)) != z ||
685 (size_t)n != fwrite(buf, 1, n, op)) {
686 fprintf(stderr, "Error writing to \"%s\".\n", tmp);
687 Fclose(op);
688 unlink(tmp);
689 return STOP;
691 size -= n;
693 Fclose(op);
694 new = salloc(n = strlen(name) + strlen(fn) + 6);
695 snprintf(new, n, "%s/new/%s", name, fn);
696 if (link(tmp, new) < 0) {
697 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);
698 return STOP;
700 if (unlink(tmp) < 0)
701 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp);
702 return OKAY;
705 static enum okay
706 trycreate(const char *name)
708 struct stat st;
710 if (stat(name, &st) == 0) {
711 if (!S_ISDIR(st.st_mode)) {
712 fprintf(stderr, "\"%s\" is not a directory.\n", name);
713 return STOP;
715 } else if (makedir(name) != OKAY) {
716 fprintf(stderr, "Cannot create directory \"%s\".\n", name);
717 return STOP;
718 } else
719 imap_created_mailbox++;
720 return OKAY;
723 static enum okay
724 mkmaildir(const char *name)
726 char *np;
727 size_t sz;
728 enum okay ok = STOP;
730 if (trycreate(name) == OKAY) {
731 np = ac_alloc((sz = strlen(name)) + 5);
732 strcpy(np, name);
733 strcpy(&np[sz], "/tmp");
734 if (trycreate(np) == OKAY) {
735 strcpy(&np[sz], "/new");
736 if (trycreate(np) == OKAY) {
737 strcpy(&np[sz], "/cur");
738 if (trycreate(np) == OKAY)
739 ok = OKAY;
742 ac_free(np);
744 return ok;
747 static struct message *
748 mdlook(const char *name, struct message *data)
750 struct mditem *md;
751 unsigned c, h, n = 0;
753 if (data && data->m_maildir_hash)
754 h = ~data->m_maildir_hash;
755 else
756 h = pjw(name);
757 h %= mdprime;
758 md = &mdtable[c = h];
759 while (md->md_data != NULL) {
760 if (strcmp(&md->md_data->m_maildir_file[4], name) == 0)
761 break;
762 c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
763 n++;
764 while (c >= (unsigned)mdprime)
765 c -= (unsigned)mdprime;
766 md = &mdtable[c];
768 if (data != NULL && md->md_data == NULL)
769 md->md_data = data;
770 return md->md_data ? md->md_data : NULL;
773 static void
774 mktable(void)
776 int i;
778 mdprime = nextprime(msgCount);
779 mdtable = scalloc(mdprime, sizeof *mdtable);
780 for (i = 0; i < msgCount; i++)
781 mdlook(&message[i].m_maildir_file[4], &message[i]);
784 static enum okay
785 subdir_remove(const char *name, const char *sub)
787 char *path;
788 int pathsize, pathend, namelen, sublen, n;
789 DIR *dirfd;
790 struct dirent *dp;
792 namelen = strlen(name);
793 sublen = strlen(sub);
794 path = smalloc(pathsize = namelen + sublen + 30);
795 strcpy(path, name);
796 path[namelen] = '/';
797 strcpy(&path[namelen+1], sub);
798 path[namelen+sublen+1] = '/';
799 path[pathend = namelen + sublen + 2] = '\0';
800 if ((dirfd = opendir(path)) == NULL) {
801 perror(path);
802 free(path);
803 return STOP;
805 while ((dp = readdir(dirfd)) != NULL) {
806 if (dp->d_name[0] == '.' &&
807 (dp->d_name[1] == '\0' ||
808 (dp->d_name[1] == '.' &&
809 dp->d_name[2] == '\0')))
810 continue;
811 if (dp->d_name[0] == '.')
812 continue;
813 n = strlen(dp->d_name);
814 if (pathend + n + 1 > pathsize)
815 path = srealloc(path, pathsize = pathend + n + 30);
816 strcpy(&path[pathend], dp->d_name);
817 if (unlink(path) < 0) {
818 perror(path);
819 closedir(dirfd);
820 free(path);
821 return STOP;
824 closedir(dirfd);
825 path[pathend] = '\0';
826 if (rmdir(path) < 0) {
827 perror(path);
828 free(path);
829 return STOP;
831 free(path);
832 return OKAY;
835 enum okay
836 maildir_remove(const char *name)
838 if (subdir_remove(name, "tmp") == STOP ||
839 subdir_remove(name, "new") == STOP ||
840 subdir_remove(name, "cur") == STOP)
841 return STOP;
842 if (rmdir(name) < 0) {
843 perror(name);
844 return STOP;
846 return OKAY;