1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include "imap4d.h"
6
7 static NamedInt flagChars[NFlags] =
8 {
9 {"s", MSeen},
10 {"a", MAnswered},
11 {"f", MFlagged},
12 {"D", MDeleted},
13 {"d", MDraft},
14 {"r", MRecent},
15 };
16
17 static int fsCtl = -1;
18
19 static void boxFlags(Box *box);
20 static int createImp(Box *box, Qid *qid);
21 static void fsInit(void);
22 static void mboxGone(Box *box);
23 static MbLock *openImp(Box *box, int new);
24 static int parseImp(Biobuf *b, Box *box);
25 static int readBox(Box *box);
26 static ulong uidRenumber(Msg *m, ulong uid, int force);
27 static int impFlags(Box *box, Msg *m, char *flags);
28
29 /*
30 * strategy:
31 * every mailbox file has an associated .imp file
32 * which maps upas/fs message digests to uids & message flags.
33 *
34 * the .imp files are locked by /mail/fs/usename/L.mbox.
35 * whenever the flags can be modified, the lock file
36 * should be opened, thereby locking the uid & flag state.
37 * for example, whenever new uids are assigned to messages,
38 * and whenever flags are changed internally, the lock file
39 * should be open and locked. this means the file must be
40 * opened during store command, and when changing the \seen
41 * flag for the fetch command.
42 *
43 * if no .imp file exists, a null one must be created before
44 * assigning uids.
45 *
46 * the .imp file has the following format
47 * imp : "imap internal mailbox description\n"
48 * uidvalidity " " uidnext "\n"
49 * messageLines
50 *
51 * messageLines :
52 * | messageLines digest " " uid " " flags "\n"
53 *
54 * uid, uidnext, and uidvalidity are 32 bit decimal numbers
55 * printed right justified in a field NUid characters long.
56 * the 0 uid implies that no uid has been assigned to the message,
57 * but the flags are valid. note that message lines are in mailbox
58 * order, except possibly for 0 uid messages.
59 *
60 * digest is an ascii hex string NDigest characters long.
61 *
62 * flags has a character for each of NFlag flag fields.
63 * if the flag is clear, it is represented by a "-".
64 * set flags are represented as a unique single ascii character.
65 * the currently assigned flags are, in order:
66 * MSeen s
67 * MAnswered a
68 * MFlagged f
69 * MDeleted D
70 * MDraft d
71 */
72 Box*
openBox(char * name,char * fsname,int writable)73 openBox(char *name, char *fsname, int writable)
74 {
75 Box *box;
76 MbLock *ml;
77 int n, new;
78
79 if(cistrcmp(name, "inbox") == 0)
80 if(access("msgs", AEXIST) == 0)
81 name = "msgs";
82 else
83 name = "mbox";
84 fsInit();
85 debuglog("imap4d open %s %s\n", name, fsname);
86
87 if(fprint(fsCtl, "open '/mail/box/%s/%s' %s", username, name, fsname) < 0){
88 //ZZZ
89 char err[ERRMAX];
90
91 rerrstr(err, sizeof err);
92 if(strstr(err, "file does not exist") == nil)
93 fprint(2,
94 "imap4d at %lud: upas/fs open %s/%s as %s failed: '%s' %s",
95 time(nil), username, name, fsname, err,
96 ctime(time(nil))); /* NB: ctime result ends with \n */
97 fprint(fsCtl, "close %s", fsname);
98 return nil;
99 }
100
101 /*
102 * read box to find all messages
103 * each one has a directory, and is in numerical order
104 */
105 box = MKZ(Box);
106 box->writable = writable;
107
108 n = strlen(name) + 1;
109 box->name = emalloc(n);
110 strcpy(box->name, name);
111
112 n += STRLEN(".imp");
113 box->imp = emalloc(n);
114 snprint(box->imp, n, "%s.imp", name);
115
116 n = strlen(fsname) + 1;
117 box->fs = emalloc(n);
118 strcpy(box->fs, fsname);
119
120 n = STRLEN("/mail/fs/") + strlen(fsname) + 1;
121 box->fsDir = emalloc(n);
122 snprint(box->fsDir, n, "/mail/fs/%s", fsname);
123
124 box->uidnext = 1;
125 new = readBox(box);
126 if(new >= 0){
127 ml = openImp(box, new);
128 if(ml != nil){
129 closeImp(box, ml);
130 return box;
131 }
132 }
133 closeBox(box, 0);
134 return nil;
135 }
136
137 /*
138 * check mailbox
139 * returns fd of open .imp file if imped.
140 * otherwise, return value is insignificant
141 *
142 * careful: called by idle polling proc
143 */
144 MbLock*
checkBox(Box * box,int imped)145 checkBox(Box *box, int imped)
146 {
147 MbLock *ml;
148 Dir *d;
149 int new;
150
151 if(box == nil)
152 return nil;
153
154 /*
155 * if stat fails, mailbox must be gone
156 */
157 d = cdDirstat(box->fsDir, ".");
158 if(d == nil){
159 mboxGone(box);
160 return nil;
161 }
162 new = 0;
163 if(box->qid.path != d->qid.path || box->qid.vers != d->qid.vers
164 || box->mtime != d->mtime){
165 new = readBox(box);
166 if(new < 0){
167 free(d);
168 return nil;
169 }
170 }
171 free(d);
172 ml = openImp(box, new);
173 if(ml == nil)
174 box->writable = 0;
175 else if(!imped){
176 closeImp(box, ml);
177 ml = nil;
178 }
179 return ml;
180 }
181
182 /*
183 * mailbox is unreachable, so mark all messages expunged
184 * clean up .imp files as well.
185 */
186 static void
mboxGone(Box * box)187 mboxGone(Box *box)
188 {
189 Msg *m;
190
191 if(cdExists(mboxDir, box->name) < 0)
192 cdRemove(mboxDir, box->imp);
193 for(m = box->msgs; m != nil; m = m->next)
194 m->expunged = 1;
195 box->writable = 0;
196 }
197
198 /*
199 * read messages in the mailbox
200 * mark message that no longer exist as expunged
201 * returns -1 for failure, 0 if no new messages, 1 if new messages.
202 */
203 static int
readBox(Box * box)204 readBox(Box *box)
205 {
206 Msg *msgs, *m, *last;
207 Dir *d;
208 char *s;
209 long max, id;
210 int i, nd, fd, new;
211
212 fd = cdOpen(box->fsDir, ".", OREAD);
213 if(fd < 0){
214 syslog(0, "mail",
215 "imap4d at %lud: upas/fs stat of %s/%s aka %s failed: %r",
216 time(nil), username, box->name, box->fsDir);
217 mboxGone(box);
218 return -1;
219 }
220
221 /*
222 * read box to find all messages
223 * each one has a directory, and is in numerical order
224 */
225 d = dirfstat(fd);
226 if(d == nil){
227 close(fd);
228 return -1;
229 }
230 box->mtime = d->mtime;
231 box->qid = d->qid;
232 last = nil;
233 msgs = box->msgs;
234 max = 0;
235 new = 0;
236 free(d);
237 while((nd = dirread(fd, &d)) > 0){
238 for(i = 0; i < nd; i++){
239 s = d[i].name;
240 id = strtol(s, &s, 10);
241 if(id <= max || *s != '\0'
242 || (d[i].mode & DMDIR) != DMDIR)
243 continue;
244
245 max = id;
246
247 while(msgs != nil){
248 last = msgs;
249 msgs = msgs->next;
250 if(last->id == id)
251 goto continueDir;
252 last->expunged = 1;
253 }
254
255 new = 1;
256 m = MKZ(Msg);
257 m->id = id;
258 m->fsDir = box->fsDir;
259 m->fs = emalloc(2 * (MsgNameLen + 1));
260 m->efs = seprint(m->fs, m->fs + (MsgNameLen + 1), "%lud/", id);
261 m->size = ~0UL;
262 m->lines = ~0UL;
263 m->prev = last;
264 m->flags = MRecent;
265 if(!msgInfo(m))
266 freeMsg(m);
267 else{
268 if(last == nil)
269 box->msgs = m;
270 else
271 last->next = m;
272 last = m;
273 }
274 continueDir:;
275 }
276 free(d);
277 }
278 close(fd);
279 for(; msgs != nil; msgs = msgs->next)
280 msgs->expunged = 1;
281
282 /*
283 * make up the imap message sequence numbers
284 */
285 id = 1;
286 for(m = box->msgs; m != nil; m = m->next){
287 if(m->seq && m->seq != id)
288 bye("internal error assigning message numbers");
289 m->seq = id++;
290 }
291 box->max = id - 1;
292
293 return new;
294 }
295
296 /*
297 * read in the .imp file, or make one if it doesn't exist.
298 * make sure all flags and uids are consistent.
299 * return the mailbox lock.
300 */
301 #define IMPMAGIC "imap internal mailbox description\n"
302 static MbLock*
openImp(Box * box,int new)303 openImp(Box *box, int new)
304 {
305 Qid qid;
306 Biobuf b;
307 MbLock *ml;
308 int fd;
309 //ZZZZ
310 int once;
311
312 ml = mbLock();
313 if(ml == nil)
314 return nil;
315 fd = cdOpen(mboxDir, box->imp, OREAD);
316 once = 0;
317 ZZZhack:
318 if(fd < 0 || fqid(fd, &qid) < 0){
319 if(fd < 0){
320 char buf[ERRMAX];
321
322 errstr(buf, sizeof buf);
323 if(cistrstr(buf, "does not exist") == nil)
324 fprint(2, "imap4d at %lud: imp open failed: %s\n", time(nil), buf);
325 if(!once && cistrstr(buf, "locked") != nil){
326 once = 1;
327 fprint(2, "imap4d at %lud: imp %s/%s %s locked when it shouldn't be; spinning\n", time(nil), username, box->name, box->imp);
328 fd = openLocked(mboxDir, box->imp, OREAD);
329 goto ZZZhack;
330 }
331 }
332 if(fd >= 0)
333 close(fd);
334 fd = createImp(box, &qid);
335 if(fd < 0){
336 mbUnlock(ml);
337 return nil;
338 }
339 box->dirtyImp = 1;
340 if(box->uidvalidity == 0)
341 box->uidvalidity = box->mtime;
342 box->impQid = qid;
343 new = 1;
344 }else if(qid.path != box->impQid.path || qid.vers != box->impQid.vers){
345 Binit(&b, fd, OREAD);
346 if(!parseImp(&b, box)){
347 box->dirtyImp = 1;
348 if(box->uidvalidity == 0)
349 box->uidvalidity = box->mtime;
350 }
351 Bterm(&b);
352 box->impQid = qid;
353 new = 1;
354 }
355 if(new)
356 boxFlags(box);
357 close(fd);
358 return ml;
359 }
360
361 /*
362 * close the .imp file, after writing out any changes
363 */
364 void
closeImp(Box * box,MbLock * ml)365 closeImp(Box *box, MbLock *ml)
366 {
367 Msg *m;
368 Qid qid;
369 Biobuf b;
370 char buf[NFlags+1];
371 int fd;
372
373 if(ml == nil)
374 return;
375 if(!box->dirtyImp){
376 mbUnlock(ml);
377 return;
378 }
379
380 fd = cdCreate(mboxDir, box->imp, OWRITE, 0664);
381 if(fd < 0){
382 mbUnlock(ml);
383 return;
384 }
385 Binit(&b, fd, OWRITE);
386
387 box->dirtyImp = 0;
388 Bprint(&b, "%s", IMPMAGIC);
389 Bprint(&b, "%.*lud %.*lud\n", NUid, box->uidvalidity, NUid, box->uidnext);
390 for(m = box->msgs; m != nil; m = m->next){
391 if(m->expunged)
392 continue;
393 wrImpFlags(buf, m->flags, strcmp(box->fs, "imap") == 0);
394 Bprint(&b, "%.*s %.*lud %s\n", NDigest, m->info[IDigest], NUid, m->uid, buf);
395 }
396 Bterm(&b);
397
398 if(fqid(fd, &qid) >= 0)
399 box->impQid = qid;
400 close(fd);
401 mbUnlock(ml);
402 }
403
404 void
wrImpFlags(char * buf,int flags,int killRecent)405 wrImpFlags(char *buf, int flags, int killRecent)
406 {
407 int i;
408
409 for(i = 0; i < NFlags; i++){
410 if((flags & flagChars[i].v)
411 && (flagChars[i].v != MRecent || !killRecent))
412 buf[i] = flagChars[i].name[0];
413 else
414 buf[i] = '-';
415 }
416 buf[i] = '\0';
417 }
418
419 int
emptyImp(char * mbox)420 emptyImp(char *mbox)
421 {
422 Dir *d;
423 long mode;
424 int fd;
425
426 fd = cdCreate(mboxDir, impName(mbox), OWRITE, 0664);
427 if(fd < 0)
428 return -1;
429 d = cdDirstat(mboxDir, mbox);
430 if(d == nil){
431 close(fd);
432 return -1;
433 }
434 fprint(fd, "%s%.*lud %.*lud\n", IMPMAGIC, NUid, d->mtime, NUid, 1UL);
435 mode = d->mode & 0777;
436 nulldir(d);
437 d->mode = mode;
438 dirfwstat(fd, d);
439 free(d);
440 return fd;
441 }
442
443 /*
444 * try to match permissions with mbox
445 */
446 static int
createImp(Box * box,Qid * qid)447 createImp(Box *box, Qid *qid)
448 {
449 Dir *d;
450 long mode;
451 int fd;
452
453 fd = cdCreate(mboxDir, box->imp, OREAD, 0664);
454 if(fd < 0)
455 return -1;
456 d = cdDirstat(mboxDir, box->name);
457 if(d != nil){
458 mode = d->mode & 0777;
459 nulldir(d);
460 d->mode = mode;
461 dirfwstat(fd, d);
462 free(d);
463 }
464 if(fqid(fd, qid) < 0){
465 close(fd);
466 return -1;
467 }
468
469 return fd;
470 }
471
472 /*
473 * read or re-read a .imp file.
474 * this is tricky:
475 * messages can be deleted by another agent
476 * we might still have a Msg for an expunged message,
477 * because we haven't told the client yet.
478 * we can have a Msg without a .imp entry.
479 * flag information is added at the end of the .imp by copy & append
480 * there can be duplicate messages (same digests).
481 *
482 * look up existing messages based on uid.
483 * look up new messages based on in order digest matching.
484 *
485 * note: in the face of duplicate messages, one of which is deleted,
486 * two active servers may decide different ones are valid, and so return
487 * different uids for the messages. this situation will stablize when the servers exit.
488 */
489 static int
parseImp(Biobuf * b,Box * box)490 parseImp(Biobuf *b, Box *box)
491 {
492 Msg *m, *mm;
493 char *s, *t, *toks[3];
494 ulong uid, u;
495 int match, n;
496
497 m = box->msgs;
498 s = Brdline(b, '\n');
499 if(s == nil || Blinelen(b) != STRLEN(IMPMAGIC)
500 || strncmp(s, IMPMAGIC, STRLEN(IMPMAGIC)) != 0)
501 return 0;
502
503 s = Brdline(b, '\n');
504 if(s == nil || Blinelen(b) != 2*NUid + 2)
505 return 0;
506 s[2*NUid + 1] = '\0';
507 u = strtoul(s, &t, 10);
508 if(u != box->uidvalidity && box->uidvalidity != 0)
509 return 0;
510 box->uidvalidity = u;
511 if(*t != ' ' || t != s + NUid)
512 return 0;
513 t++;
514 u = strtoul(t, &t, 10);
515 if(box->uidnext > u)
516 return 0;
517 box->uidnext = u;
518 if(t != s + 2*NUid+1 || box->uidnext == 0)
519 return 0;
520
521 uid = ~0;
522 while(m != nil){
523 s = Brdline(b, '\n');
524 if(s == nil)
525 break;
526 n = Blinelen(b) - 1;
527 if(n != NDigest + NUid + NFlags + 2
528 || s[NDigest] != ' ' || s[NDigest + NUid + 1] != ' ')
529 return 0;
530 toks[0] = s;
531 s[NDigest] = '\0';
532 toks[1] = s + NDigest + 1;
533 s[NDigest + NUid + 1] = '\0';
534 toks[2] = s + NDigest + NUid + 2;
535 s[n] = '\0';
536 t = toks[1];
537 u = strtoul(t, &t, 10);
538 if(*t != '\0' || uid != ~0 && (uid >= u && u || u && !uid))
539 return 0;
540 uid = u;
541
542 /*
543 * zero uid => added by append or copy, only flags valid
544 * can only match messages without uids, but this message
545 * may not be the next one, and may have been deleted.
546 */
547 if(!uid){
548 for(; m != nil && m->uid; m = m->next)
549 ;
550 for(mm = m; mm != nil; mm = mm->next){
551 if(mm->info[IDigest] != nil &&
552 strcmp(mm->info[IDigest], toks[0]) == 0){
553 if(!mm->uid)
554 mm->flags = 0;
555 if(!impFlags(box, mm, toks[2]))
556 return 0;
557 m = mm->next;
558 break;
559 }
560 }
561 continue;
562 }
563
564 /*
565 * ignore expunged messages,
566 * and messages already assigned uids which don't match this uid.
567 * such messages must have been deleted by another imap server,
568 * which updated the mailbox and .imp file since we read the mailbox,
569 * or because upas/fs got confused by consecutive duplicate messages,
570 * the first of which was deleted by another imap server.
571 */
572 for(; m != nil && (m->expunged || m->uid && m->uid < uid); m = m->next)
573 ;
574 if(m == nil)
575 break;
576
577 /*
578 * only check for digest match on the next message,
579 * since it comes before all other messages, and therefore
580 * must be in the .imp file if they should be.
581 */
582 match = m->info[IDigest] != nil &&
583 strcmp(m->info[IDigest], toks[0]) == 0;
584 if(uid && (m->uid == uid || !m->uid && match)){
585 if(!match)
586 bye("inconsistent uid");
587
588 /*
589 * wipe out recent flag if some other server saw this new message.
590 * it will be read from the .imp file if is really should be set,
591 * ie the message was only seen by a status command.
592 */
593 if(!m->uid)
594 m->flags = 0;
595
596 if(!impFlags(box, m, toks[2]))
597 return 0;
598 m->uid = uid;
599 m = m->next;
600 }
601 }
602 return 1;
603 }
604
605 /*
606 * parse .imp flags
607 */
608 static int
impFlags(Box * box,Msg * m,char * flags)609 impFlags(Box *box, Msg *m, char *flags)
610 {
611 int i, f;
612
613 f = 0;
614 for(i = 0; i < NFlags; i++){
615 if(flags[i] == '-')
616 continue;
617 if(flags[i] != flagChars[i].name[0])
618 return 0;
619 f |= flagChars[i].v;
620 }
621
622 /*
623 * recent flags are set until the first time message's box is selected or examined.
624 * it may be stored in the file as a side effect of a status or subscribe command;
625 * if so, clear it out.
626 */
627 if((f & MRecent) && strcmp(box->fs, "imap") == 0)
628 box->dirtyImp = 1;
629 f |= m->flags & MRecent;
630
631 /*
632 * all old messages with changed flags should be reported to the client
633 */
634 if(m->uid && m->flags != f){
635 box->sendFlags = 1;
636 m->sendFlags = 1;
637 }
638 m->flags = f;
639 return 1;
640 }
641
642 /*
643 * assign uids to any new messages
644 * which aren't already in the .imp file.
645 * sum up totals for flag values.
646 */
647 static void
boxFlags(Box * box)648 boxFlags(Box *box)
649 {
650 Msg *m;
651
652 box->recent = 0;
653 for(m = box->msgs; m != nil; m = m->next){
654 if(m->uid == 0){
655 box->dirtyImp = 1;
656 box->uidnext = uidRenumber(m, box->uidnext, 0);
657 }
658 if(m->flags & MRecent)
659 box->recent++;
660 }
661 }
662
663 static ulong
uidRenumber(Msg * m,ulong uid,int force)664 uidRenumber(Msg *m, ulong uid, int force)
665 {
666 for(; m != nil; m = m->next){
667 if(!force && m->uid != 0)
668 bye("uid renumbering with a valid uid");
669 m->uid = uid++;
670 }
671 return uid;
672 }
673
674 void
closeBox(Box * box,int opened)675 closeBox(Box *box, int opened)
676 {
677 Msg *m, *next;
678
679 /*
680 * make sure to leave the mailbox directory so upas/fs can close the mailbox
681 */
682 myChdir(mboxDir);
683
684 if(box->writable){
685 deleteMsgs(box);
686 if(expungeMsgs(box, 0))
687 closeImp(box, checkBox(box, 1));
688 }
689
690 if(fprint(fsCtl, "close %s", box->fs) < 0 && opened)
691 bye("can't talk to mail server");
692 for(m = box->msgs; m != nil; m = next){
693 next = m->next;
694 freeMsg(m);
695 }
696 free(box->name);
697 free(box->fs);
698 free(box->fsDir);
699 free(box->imp);
700 free(box);
701 }
702
703 int
deleteMsgs(Box * box)704 deleteMsgs(Box *box)
705 {
706 Msg *m;
707 char buf[BufSize], *p, *start;
708 int ok;
709
710 if(!box->writable)
711 return 0;
712
713 /*
714 * first pass: delete messages; gang the writes together for speed.
715 */
716 ok = 1;
717 start = seprint(buf, buf + sizeof(buf), "delete %s", box->fs);
718 p = start;
719 for(m = box->msgs; m != nil; m = m->next){
720 if((m->flags & MDeleted) && !m->expunged){
721 m->expunged = 1;
722 p = seprint(p, buf + sizeof(buf), " %lud", m->id);
723 if(p + 32 >= buf + sizeof(buf)){
724 if(write(fsCtl, buf, p - buf) < 0)
725 bye("can't talk to mail server");
726 p = start;
727 }
728 }
729 }
730 if(p != start && write(fsCtl, buf, p - buf) < 0)
731 bye("can't talk to mail server");
732
733 return ok;
734 }
735
736 /*
737 * second pass: remove the message structure,
738 * and renumber message sequence numbers.
739 * update messages counts in mailbox.
740 * returns true if anything changed.
741 */
742 int
expungeMsgs(Box * box,int send)743 expungeMsgs(Box *box, int send)
744 {
745 Msg *m, *next, *last;
746 ulong n;
747
748 n = 0;
749 last = nil;
750 for(m = box->msgs; m != nil; m = next){
751 m->seq -= n;
752 next = m->next;
753 if(m->expunged){
754 if(send)
755 Bprint(&bout, "* %lud expunge\r\n", m->seq);
756 if(m->flags & MRecent)
757 box->recent--;
758 n++;
759 if(last == nil)
760 box->msgs = next;
761 else
762 last->next = next;
763 freeMsg(m);
764 }else
765 last = m;
766 }
767 if(n){
768 box->max -= n;
769 box->dirtyImp = 1;
770 }
771 return n;
772 }
773
774 static void
fsInit(void)775 fsInit(void)
776 {
777 if(fsCtl >= 0)
778 return;
779 fsCtl = open("/mail/fs/ctl", ORDWR);
780 if(fsCtl < 0)
781 bye("can't open mail file system");
782 if(fprint(fsCtl, "close mbox") < 0)
783 bye("can't initialize mail file system");
784 }
785
786 static char *stoplist[] =
787 {
788 "mbox",
789 "pipeto",
790 "forward",
791 "names",
792 "pipefrom",
793 "headers",
794 "imap.ok",
795 0
796 };
797
798 enum {
799 Maxokbytes = 4096,
800 Maxfolders = Maxokbytes / 4,
801 };
802
803 static char *folders[Maxfolders];
804 static char *folderbuff;
805
806 static void
readokfolders(void)807 readokfolders(void)
808 {
809 int fd, nr;
810
811 fd = open("imap.ok", OREAD);
812 if(fd < 0)
813 return;
814 folderbuff = malloc(Maxokbytes);
815 if(folderbuff == nil) {
816 close(fd);
817 return;
818 }
819 nr = read(fd, folderbuff, Maxokbytes-1); /* once is ok */
820 close(fd);
821 if(nr < 0){
822 free(folderbuff);
823 folderbuff = nil;
824 return;
825 }
826 folderbuff[nr] = 0;
827 tokenize(folderbuff, folders, nelem(folders));
828 }
829
830 /*
831 * reject bad mailboxes based on mailbox name
832 */
833 int
okMbox(char * path)834 okMbox(char *path)
835 {
836 char *name;
837 int i;
838
839 if(folderbuff == nil && access("imap.ok", AREAD) == 0)
840 readokfolders();
841 name = strrchr(path, '/');
842 if(name == nil)
843 name = path;
844 else
845 name++;
846 if(folderbuff != nil){
847 for(i = 0; i < nelem(folders) && folders[i] != nil; i++)
848 if(cistrcmp(folders[i], name) == 0)
849 return 1;
850 return 0;
851 }
852 if(strlen(name) + STRLEN(".imp") >= MboxNameLen)
853 return 0;
854 for(i = 0; stoplist[i]; i++)
855 if(strcmp(name, stoplist[i]) == 0)
856 return 0;
857 if(isprefix("L.", name) || isprefix("imap-tmp.", name)
858 || issuffix(".imp", name)
859 || strcmp("imap.subscribed", name) == 0
860 || isdotdot(name) || name[0] == '/')
861 return 0;
862 return 1;
863 }
864