]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/imap4d/mbox.c
ip/torrent: remove unneeded assignment
[plan9front.git] / sys / src / cmd / ip / imap4d / mbox.c
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*
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*
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
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
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*
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }