Blob


1 #include "common.h"
2 #include <auth.h>
3 #include <fcall.h>
4 #include <libsec.h>
5 #include <9pclient.h> /* jpc */
6 #include <thread.h> /* jpc */
7 #include "dat.h"
9 enum
10 {
11 OPERM = 0x3, // mask of all permission types in open mode
12 };
14 typedef struct Fid Fid;
16 struct Fid
17 {
18 Qid qid;
19 short busy;
20 short open;
21 int fid;
22 Fid *next;
23 Mailbox *mb;
24 Message *m;
25 Message *mtop; // top level message
27 //finger pointers to speed up reads of large directories
28 long foff; // offset/DIRLEN of finger
29 Message *fptr; // pointer to message at off
30 int fvers; // mailbox version when finger was saved
31 };
33 ulong path; // incremented for each new file
34 Fid *fids;
35 int mfd[2];
36 char user[Elemlen];
37 int messagesize = 4*1024*IOHDRSZ;
38 uchar mdata[8*1024*IOHDRSZ];
39 uchar mbuf[8*1024*IOHDRSZ];
40 Fcall thdr;
41 Fcall rhdr;
42 int fflg;
43 char *mntpt;
44 int biffing;
45 int plumbing = 1;
47 QLock mbllock;
48 Mailbox *mbl;
50 Fid *newfid(int);
51 void error(char*);
52 void io(void);
53 void *erealloc(void*, ulong);
54 void *emalloc(ulong);
55 void usage(void);
56 void run_io(void*);
57 void reader(void*);
58 int readheader(Message*, char*, int, int);
59 int cistrncmp(char*, char*, int);
60 int tokenconvert(String*, char*, int);
61 String* stringconvert(String*, char*, int);
62 void post(char*, char*, int);
64 char *rflush(Fid*), *rauth(Fid*),
65 *rattach(Fid*), *rwalk(Fid*),
66 *ropen(Fid*), *rcreate(Fid*),
67 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
68 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
69 *rversion(Fid*);
71 char *(*fcalls[])(Fid*) = {
72 [Tflush] rflush,
73 [Tversion] rversion,
74 [Tauth] rauth,
75 [Tattach] rattach,
76 [Twalk] rwalk,
77 [Topen] ropen,
78 [Tcreate] rcreate,
79 [Tread] rread,
80 [Twrite] rwrite,
81 [Tclunk] rclunk,
82 [Tremove] rremove,
83 [Tstat] rstat,
84 [Twstat] rwstat,
85 };
87 char Eperm[] = "permission denied";
88 char Enotdir[] = "not a directory";
89 char Enoauth[] = "upas/fs: authentication not required";
90 char Enotexist[] = "file does not exist";
91 char Einuse[] = "file in use";
92 char Eexist[] = "file exists";
93 char Enotowner[] = "not owner";
94 char Eisopen[] = "file already open for I/O";
95 char Excl[] = "exclusive use file already open";
96 char Ename[] = "illegal name";
97 char Ebadctl[] = "unknown control message";
99 char *dirtab[] =
101 [Qdir] ".",
102 [Qbody] "body",
103 [Qbcc] "bcc",
104 [Qcc] "cc",
105 [Qdate] "date",
106 [Qdigest] "digest",
107 [Qdisposition] "disposition",
108 [Qfilename] "filename",
109 [Qfrom] "from",
110 [Qheader] "header",
111 [Qinfo] "info",
112 [Qinreplyto] "inreplyto",
113 [Qlines] "lines",
114 [Qmimeheader] "mimeheader",
115 [Qmessageid] "messageid",
116 [Qraw] "raw",
117 [Qrawunix] "rawunix",
118 [Qrawbody] "rawbody",
119 [Qrawheader] "rawheader",
120 [Qreplyto] "replyto",
121 [Qsender] "sender",
122 [Qsubject] "subject",
123 [Qto] "to",
124 [Qtype] "type",
125 [Qunixdate] "unixdate",
126 [Qunixheader] "unixheader",
127 [Qctl] "ctl",
128 [Qmboxctl] "ctl",
129 };
131 enum
133 Hsize= 1277,
134 };
136 Hash *htab[Hsize];
138 int debug;
139 int fflag;
140 int logging;
142 void
143 usage(void)
145 fprint(2, "usage: %s [-b -m mountpoint]\n", argv0);
146 threadexits("usage");
149 void
150 notifyf(void *a, char *s)
152 USED(a);
153 if(strncmp(s, "interrupt", 9) == 0)
154 noted(NCONT);
155 noted(NDFLT);
158 void
159 threadmain(int argc, char *argv[])
161 int p[2], std, nodflt;
162 char maildir[128];
163 char mbox[128];
164 char *mboxfile, *err;
165 char srvfile[64];
166 int srvpost;
168 rfork(RFNOTEG);
169 mntpt = nil;
170 fflag = 0;
171 mboxfile = nil;
172 std = 0;
173 nodflt = 0;
174 srvpost = 0;
176 ARGBEGIN{
177 case 'b':
178 biffing = 1;
179 break;
180 case 'f':
181 fflag = 1;
182 mboxfile = ARGF();
183 break;
184 case 'm':
185 mntpt = ARGF();
186 break;
187 case 'd':
188 debug = 1;
189 break;
190 case 'p':
191 plumbing = 0;
192 break;
193 case 's':
194 srvpost = 1;
195 break;
196 case 'l':
197 logging = 1;
198 break;
199 case 'n':
200 nodflt = 1;
201 break;
202 default:
203 usage();
204 }ARGEND
206 if(pipe(p) < 0)
207 error("pipe failed");
208 mfd[0] = p[0];
209 mfd[1] = p[0];
211 notify(notifyf);
212 strcpy(user, getuser());
213 if(mntpt == nil){
214 snprint(maildir, sizeof(maildir), "/mail/fs");
215 mntpt = maildir;
217 if(mboxfile == nil && !nodflt){
218 snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user);
219 mboxfile = mbox;
220 std = 1;
223 if(debug)
224 fmtinstall('F', fcallfmt);
226 if(mboxfile != nil){
227 err = newmbox(mboxfile, "mbox", std);
228 if(err != nil)
229 sysfatal("opening mailbox: %s", err);
232 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ /* jpc removed RFEND */
233 case -1:
234 error("fork");
235 case 0:
236 henter(PATH(0, Qtop), dirtab[Qctl],
237 (Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
238 close(p[1]);
239 io();
240 postnote(PNGROUP, getpid(), "die yankee pig dog");
241 break;
242 default:
243 close(p[0]); /* don't deadlock if child fails */
244 if(srvpost){
245 sprint(srvfile, "/srv/upasfs.%s", user);
246 /* post(srvfile, "upasfs", p[1]); jpc */
247 post9pservice(p[1], "upasfs"); /* jpc */
248 } else {
249 error("tried to mount, fixme"); /* jpc */
250 /* if(mount(p[1], -1, mntpt, MREPL, "") < 0)
251 error("mount failed"); jpc */
254 threadexits(0);
257 void run_io(void *v) {
258 int *p;
260 p = v;
261 henter(PATH(0, Qtop), dirtab[Qctl],
262 (Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
263 close(p[1]);
264 io();
265 postnote(PNGROUP, getpid(), "die yankee pig dog");
268 static int
269 fileinfo(Message *m, int t, char **pp)
271 char *p;
272 int len;
274 p = "";
275 len = 0;
276 switch(t){
277 case Qbody:
278 p = m->body;
279 len = m->bend - m->body;
280 break;
281 case Qbcc:
282 if(m->bcc822){
283 p = s_to_c(m->bcc822);
284 len = strlen(p);
286 break;
287 case Qcc:
288 if(m->cc822){
289 p = s_to_c(m->cc822);
290 len = strlen(p);
292 break;
293 case Qdisposition:
294 switch(m->disposition){
295 case Dinline:
296 p = "inline";
297 break;
298 case Dfile:
299 p = "file";
300 break;
302 len = strlen(p);
303 break;
304 case Qdate:
305 if(m->date822){
306 p = s_to_c(m->date822);
307 len = strlen(p);
308 } else if(m->unixdate != nil){
309 p = s_to_c(m->unixdate);
310 len = strlen(p);
312 break;
313 case Qfilename:
314 if(m->filename){
315 p = s_to_c(m->filename);
316 len = strlen(p);
318 break;
319 case Qinreplyto:
320 if(m->inreplyto822){
321 p = s_to_c(m->inreplyto822);
322 len = strlen(p);
324 break;
325 case Qmessageid:
326 if(m->messageid822){
327 p = s_to_c(m->messageid822);
328 len = strlen(p);
330 break;
331 case Qfrom:
332 if(m->from822){
333 p = s_to_c(m->from822);
334 len = strlen(p);
335 } else if(m->unixfrom != nil){
336 p = s_to_c(m->unixfrom);
337 len = strlen(p);
339 break;
340 case Qheader:
341 p = m->header;
342 len = headerlen(m);
343 break;
344 case Qlines:
345 p = m->lines;
346 if(*p == 0)
347 countlines(m);
348 len = strlen(m->lines);
349 break;
350 case Qraw:
351 p = m->start;
352 if(strncmp(m->start, "From ", 5) == 0){
353 p = strchr(p, '\n');
354 if(p == nil)
355 p = m->start;
356 else
357 p++;
359 len = m->end - p;
360 break;
361 case Qrawunix:
362 p = m->start;
363 len = m->end - p;
364 break;
365 case Qrawbody:
366 p = m->rbody;
367 len = m->rbend - p;
368 break;
369 case Qrawheader:
370 p = m->header;
371 len = m->hend - p;
372 break;
373 case Qmimeheader:
374 p = m->mheader;
375 len = m->mhend - p;
376 break;
377 case Qreplyto:
378 p = nil;
379 if(m->replyto822 != nil){
380 p = s_to_c(m->replyto822);
381 len = strlen(p);
382 } else if(m->from822 != nil){
383 p = s_to_c(m->from822);
384 len = strlen(p);
385 } else if(m->sender822 != nil){
386 p = s_to_c(m->sender822);
387 len = strlen(p);
388 } else if(m->unixfrom != nil){
389 p = s_to_c(m->unixfrom);
390 len = strlen(p);
392 break;
393 case Qsender:
394 if(m->sender822){
395 p = s_to_c(m->sender822);
396 len = strlen(p);
398 break;
399 case Qsubject:
400 p = nil;
401 if(m->subject822){
402 p = s_to_c(m->subject822);
403 len = strlen(p);
405 break;
406 case Qto:
407 if(m->to822){
408 p = s_to_c(m->to822);
409 len = strlen(p);
411 break;
412 case Qtype:
413 if(m->type){
414 p = s_to_c(m->type);
415 len = strlen(p);
417 break;
418 case Qunixdate:
419 if(m->unixdate){
420 p = s_to_c(m->unixdate);
421 len = strlen(p);
423 break;
424 case Qunixheader:
425 if(m->unixheader){
426 p = s_to_c(m->unixheader);
427 len = s_len(m->unixheader);
429 break;
430 case Qdigest:
431 if(m->sdigest){
432 p = s_to_c(m->sdigest);
433 len = strlen(p);
435 break;
437 *pp = p;
438 return len;
441 int infofields[] = {
442 Qfrom,
443 Qto,
444 Qcc,
445 Qreplyto,
446 Qunixdate,
447 Qsubject,
448 Qtype,
449 Qdisposition,
450 Qfilename,
451 Qdigest,
452 Qbcc,
453 Qinreplyto,
454 Qdate,
455 Qsender,
456 Qmessageid,
457 Qlines,
458 -1,
459 };
461 static int
462 readinfo(Message *m, char *buf, long off, int count)
464 char *p;
465 int len, i, n;
466 String *s;
468 s = s_new();
469 len = 0;
470 for(i = 0; len < count && infofields[i] >= 0; i++){
471 n = fileinfo(m, infofields[i], &p);
472 s = stringconvert(s, p, n);
473 s_append(s, "\n");
474 p = s_to_c(s);
475 n = strlen(p);
476 if(off > 0){
477 if(off >= n){
478 off -= n;
479 continue;
481 p += off;
482 n -= off;
483 off = 0;
485 if(n > count - len)
486 n = count - len;
487 if(buf)
488 memmove(buf+len, p, n);
489 len += n;
491 s_free(s);
492 return len;
495 static void
496 mkstat(Dir *d, Mailbox *mb, Message *m, int t)
498 char *p;
500 d->uid = user;
501 d->gid = user;
502 d->muid = user;
503 d->mode = 0444;
504 d->qid.vers = 0;
505 d->qid.type = QTFILE;
506 d->type = 0;
507 d->dev = 0;
508 if(mb != nil && mb->d != nil){
509 d->atime = mb->d->atime;
510 d->mtime = mb->d->mtime;
511 } else {
512 d->atime = time(0);
513 d->mtime = d->atime;
516 switch(t){
517 case Qtop:
518 d->name = ".";
519 d->mode = DMDIR|0555;
520 d->atime = d->mtime = time(0);
521 d->length = 0;
522 d->qid.path = PATH(0, Qtop);
523 d->qid.type = QTDIR;
524 break;
525 case Qmbox:
526 d->name = mb->name;
527 d->mode = DMDIR|0555;
528 d->length = 0;
529 d->qid.path = PATH(mb->id, Qmbox);
530 d->qid.type = QTDIR;
531 d->qid.vers = mb->vers;
532 break;
533 case Qdir:
534 d->name = m->name;
535 d->mode = DMDIR|0555;
536 d->length = 0;
537 d->qid.path = PATH(m->id, Qdir);
538 d->qid.type = QTDIR;
539 break;
540 case Qctl:
541 d->name = dirtab[t];
542 d->mode = 0666;
543 d->atime = d->mtime = time(0);
544 d->length = 0;
545 d->qid.path = PATH(0, Qctl);
546 break;
547 case Qmboxctl:
548 d->name = dirtab[t];
549 d->mode = 0222;
550 d->atime = d->mtime = time(0);
551 d->length = 0;
552 d->qid.path = PATH(mb->id, Qmboxctl);
553 break;
554 case Qinfo:
555 d->name = dirtab[t];
556 d->length = readinfo(m, nil, 0, 1<<30);
557 d->qid.path = PATH(m->id, t);
558 break;
559 default:
560 d->name = dirtab[t];
561 d->length = fileinfo(m, t, &p);
562 d->qid.path = PATH(m->id, t);
563 break;
567 char*
568 rversion(Fid* dummy)
570 Fid *f;
572 if(thdr.msize < 256)
573 return "max messagesize too small";
574 if(thdr.msize < messagesize)
575 messagesize = thdr.msize;
576 rhdr.msize = messagesize;
577 if(strncmp(thdr.version, "9P2000", 6) != 0)
578 return "unknown 9P version";
579 else
580 rhdr.version = "9P2000";
581 for(f = fids; f; f = f->next)
582 if(f->busy)
583 rclunk(f);
584 return nil;
587 char*
588 rauth(Fid* dummy)
590 return Enoauth;
593 char*
594 rflush(Fid *f)
596 USED(f);
597 return 0;
600 char*
601 rattach(Fid *f)
603 f->busy = 1;
604 f->m = nil;
605 f->mb = nil;
606 f->qid.path = PATH(0, Qtop);
607 f->qid.type = QTDIR;
608 f->qid.vers = 0;
609 rhdr.qid = f->qid;
610 if(strcmp(thdr.uname, user) != 0)
611 return Eperm;
612 return 0;
615 static Fid*
616 doclone(Fid *f, int nfid)
618 Fid *nf;
620 nf = newfid(nfid);
621 if(nf->busy)
622 return nil;
623 nf->busy = 1;
624 nf->open = 0;
625 nf->m = f->m;
626 nf->mtop = f->mtop;
627 nf->mb = f->mb;
628 if(f->mb != nil)
629 mboxincref(f->mb);
630 if(f->mtop != nil){
631 qlock(&f->mb->ql);
632 msgincref(f->mtop);
633 qunlock(&f->mb->ql);
635 nf->qid = f->qid;
636 return nf;
639 char*
640 dowalk(Fid *f, char *name)
642 int t;
643 Mailbox *omb, *mb;
644 char *rv, *p;
645 Hash *h;
647 t = FILE(f->qid.path);
649 rv = Enotexist;
651 omb = f->mb;
652 if(omb)
653 qlock(&omb->ql);
654 else
655 qlock(&mbllock);
657 // this must catch everything except . and ..
658 retry:
659 h = hlook(f->qid.path, name);
660 if(h != nil){
661 f->mb = h->mb;
662 f->m = h->m;
663 switch(t){
664 case Qtop:
665 if(f->mb != nil)
666 mboxincref(f->mb);
667 break;
668 case Qmbox:
669 if(f->m){
670 msgincref(f->m);
671 f->mtop = f->m;
673 break;
675 f->qid = h->qid;
676 rv = nil;
677 } else if((p = strchr(name, '.')) != nil && *name != '.'){
678 *p = 0;
679 goto retry;
682 if(omb)
683 qunlock(&omb->ql);
684 else
685 qunlock(&mbllock);
686 if(rv == nil)
687 return rv;
689 if(strcmp(name, ".") == 0)
690 return nil;
692 if(f->qid.type != QTDIR)
693 return Enotdir;
695 if(strcmp(name, "..") == 0){
696 switch(t){
697 case Qtop:
698 f->qid.path = PATH(0, Qtop);
699 f->qid.type = QTDIR;
700 f->qid.vers = 0;
701 break;
702 case Qmbox:
703 f->qid.path = PATH(0, Qtop);
704 f->qid.type = QTDIR;
705 f->qid.vers = 0;
706 qlock(&mbllock);
707 mb = f->mb;
708 f->mb = nil;
709 mboxdecref(mb);
710 qunlock(&mbllock);
711 break;
712 case Qdir:
713 qlock(&f->mb->ql);
714 if(f->m->whole == f->mb->root){
715 f->qid.path = PATH(f->mb->id, Qmbox);
716 f->qid.type = QTDIR;
717 f->qid.vers = f->mb->d->qid.vers;
718 msgdecref(f->mb, f->mtop);
719 f->m = f->mtop = nil;
720 } else {
721 f->m = f->m->whole;
722 f->qid.path = PATH(f->m->id, Qdir);
723 f->qid.type = QTDIR;
725 qunlock(&f->mb->ql);
726 break;
728 rv = nil;
730 return rv;
733 char*
734 rwalk(Fid *f)
736 Fid *nf;
737 char *rv;
738 int i;
740 if(f->open)
741 return Eisopen;
743 rhdr.nwqid = 0;
744 nf = nil;
746 /* clone if requested */
747 if(thdr.newfid != thdr.fid){
748 nf = doclone(f, thdr.newfid);
749 if(nf == nil)
750 return "new fid in use";
751 f = nf;
754 /* if it's just a clone, return */
755 if(thdr.nwname == 0 && nf != nil)
756 return nil;
758 /* walk each element */
759 rv = nil;
760 for(i = 0; i < thdr.nwname; i++){
761 rv = dowalk(f, thdr.wname[i]);
762 if(rv != nil){
763 if(nf != nil)
764 rclunk(nf);
765 break;
767 rhdr.wqid[i] = f->qid;
769 rhdr.nwqid = i;
771 /* we only error out if no walk */
772 if(i > 0)
773 rv = nil;
775 return rv;
778 char *
779 ropen(Fid *f)
781 int file;
783 if(f->open)
784 return Eisopen;
786 file = FILE(f->qid.path);
787 if(thdr.mode != OREAD)
788 if(file != Qctl && file != Qmboxctl)
789 return Eperm;
791 // make sure we've decoded
792 if(file == Qbody){
793 if(f->m->decoded == 0)
794 decode(f->m);
795 if(f->m->converted == 0)
796 convert(f->m);
799 rhdr.iounit = 0;
800 rhdr.qid = f->qid;
801 f->open = 1;
802 return 0;
805 char *
806 rcreate(Fid* dummy)
808 return Eperm;
811 int
812 readtopdir(Fid* dummy, uchar *buf, long off, int cnt, int blen)
814 Dir d;
815 int m, n;
816 long pos;
817 Mailbox *mb;
819 n = 0;
820 pos = 0;
821 mkstat(&d, nil, nil, Qctl);
822 m = convD2M(&d, &buf[n], blen);
823 if(off <= pos){
824 if(m <= BIT16SZ || m > cnt)
825 return 0;
826 n += m;
827 cnt -= m;
829 pos += m;
831 for(mb = mbl; mb != nil; mb = mb->next){
832 mkstat(&d, mb, nil, Qmbox);
833 m = convD2M(&d, &buf[n], blen-n);
834 if(off <= pos){
835 if(m <= BIT16SZ || m > cnt)
836 break;
837 n += m;
838 cnt -= m;
840 pos += m;
842 return n;
845 int
846 readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)
848 Dir d;
849 int n, m;
850 long pos;
851 Message *msg;
853 n = 0;
854 if(f->mb->ctl){
855 mkstat(&d, f->mb, nil, Qmboxctl);
856 m = convD2M(&d, &buf[n], blen);
857 if(off == 0){
858 if(m <= BIT16SZ || m > cnt){
859 f->fptr = nil;
860 return 0;
862 n += m;
863 cnt -= m;
864 } else
865 off -= m;
868 // to avoid n**2 reads of the directory, use a saved finger pointer
869 if(f->mb->vers == f->fvers && off >= f->foff && f->fptr != nil){
870 msg = f->fptr;
871 pos = f->foff;
872 } else {
873 msg = f->mb->root->part;
874 pos = 0;
877 for(; cnt > 0 && msg != nil; msg = msg->next){
878 // act like deleted files aren't there
879 if(msg->deleted)
880 continue;
882 mkstat(&d, f->mb, msg, Qdir);
883 m = convD2M(&d, &buf[n], blen-n);
884 if(off <= pos){
885 if(m <= BIT16SZ || m > cnt)
886 break;
887 n += m;
888 cnt -= m;
890 pos += m;
893 // save a finger pointer for next read of the mbox directory
894 f->foff = pos;
895 f->fptr = msg;
896 f->fvers = f->mb->vers;
898 return n;
901 int
902 readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
904 Dir d;
905 int i, n, m;
906 long pos;
907 Message *msg;
909 n = 0;
910 pos = 0;
911 for(i = 0; i < Qmax; i++){
912 mkstat(&d, f->mb, f->m, i);
913 m = convD2M(&d, &buf[n], blen-n);
914 if(off <= pos){
915 if(m <= BIT16SZ || m > cnt)
916 return n;
917 n += m;
918 cnt -= m;
920 pos += m;
922 for(msg = f->m->part; msg != nil; msg = msg->next){
923 mkstat(&d, f->mb, msg, Qdir);
924 m = convD2M(&d, &buf[n], blen-n);
925 if(off <= pos){
926 if(m <= BIT16SZ || m > cnt)
927 break;
928 n += m;
929 cnt -= m;
931 pos += m;
934 return n;
937 char*
938 rread(Fid *f)
940 long off;
941 int t, i, n, cnt;
942 char *p;
944 rhdr.count = 0;
945 off = thdr.offset;
946 cnt = thdr.count;
948 if(cnt > messagesize - IOHDRSZ)
949 cnt = messagesize - IOHDRSZ;
951 rhdr.data = (char*)mbuf;
953 t = FILE(f->qid.path);
954 if(f->qid.type & QTDIR){
955 if(t == Qtop) {
956 qlock(&mbllock);
957 n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
958 qunlock(&mbllock);
959 } else if(t == Qmbox) {
960 qlock(&f->mb->ql);
961 if(off == 0)
962 syncmbox(f->mb, 1);
963 n = readmboxdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
964 qunlock(&f->mb->ql);
965 } else if(t == Qmboxctl) {
966 n = 0;
967 } else {
968 n = readmsgdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
971 rhdr.count = n;
972 return nil;
975 if(FILE(f->qid.path) == Qheader){
976 rhdr.count = readheader(f->m, (char*)mbuf, off, cnt);
977 return nil;
980 if(FILE(f->qid.path) == Qinfo){
981 rhdr.count = readinfo(f->m, (char*)mbuf, off, cnt);
982 return nil;
985 i = fileinfo(f->m, FILE(f->qid.path), &p);
986 if(off < i){
987 if((off + cnt) > i)
988 cnt = i - off;
989 memmove(mbuf, p + off, cnt);
990 rhdr.count = cnt;
992 return nil;
995 char*
996 rwrite(Fid *f)
998 char *err;
999 char *token[1024];
1000 int t, n;
1001 String *file;
1003 t = FILE(f->qid.path);
1004 rhdr.count = thdr.count;
1005 switch(t){
1006 case Qctl:
1007 if(thdr.count == 0)
1008 return Ebadctl;
1009 if(thdr.data[thdr.count-1] == '\n')
1010 thdr.data[thdr.count-1] = 0;
1011 else
1012 thdr.data[thdr.count] = 0;
1013 n = tokenize(thdr.data, token, nelem(token));
1014 if(n == 0)
1015 return Ebadctl;
1016 if(strcmp(token[0], "open") == 0){
1017 file = s_new();
1018 switch(n){
1019 case 1:
1020 err = Ebadctl;
1021 break;
1022 case 2:
1023 mboxpath(token[1], getlog(), file, 0);
1024 err = newmbox(s_to_c(file), nil, 0);
1025 break;
1026 default:
1027 mboxpath(token[1], getlog(), file, 0);
1028 if(strchr(token[2], '/') != nil)
1029 err = "/ not allowed in mailbox name";
1030 else
1031 err = newmbox(s_to_c(file), token[2], 0);
1032 break;
1034 s_free(file);
1035 return err;
1037 if(strcmp(token[0], "close") == 0){
1038 if(n < 2)
1039 return nil;
1040 freembox(token[1]);
1041 return nil;
1043 if(strcmp(token[0], "delete") == 0){
1044 if(n < 3)
1045 return nil;
1046 delmessages(n-1, &token[1]);
1047 return nil;
1049 return Ebadctl;
1050 case Qmboxctl:
1051 if(f->mb && f->mb->ctl){
1052 if(thdr.count == 0)
1053 return Ebadctl;
1054 if(thdr.data[thdr.count-1] == '\n')
1055 thdr.data[thdr.count-1] = 0;
1056 else
1057 thdr.data[thdr.count] = 0;
1058 n = tokenize(thdr.data, token, nelem(token));
1059 if(n == 0)
1060 return Ebadctl;
1061 return (*f->mb->ctl)(f->mb, n, token);
1064 return Eperm;
1067 char *
1068 rclunk(Fid *f)
1070 Mailbox *mb;
1072 f->busy = 0;
1073 f->open = 0;
1074 if(f->mtop != nil){
1075 qlock(&f->mb->ql);
1076 msgdecref(f->mb, f->mtop);
1077 qunlock(&f->mb->ql);
1079 f->m = f->mtop = nil;
1080 mb = f->mb;
1081 if(mb != nil){
1082 f->mb = nil;
1083 assert(mb->refs > 0);
1084 qlock(&mbllock);
1085 mboxdecref(mb);
1086 qunlock(&mbllock);
1088 f->fid = -1;
1089 return 0;
1092 char *
1093 rremove(Fid *f)
1095 if(f->m != nil){
1096 if(f->m->deleted == 0)
1097 mailplumb(f->mb, f->m, 1);
1098 f->m->deleted = 1;
1100 return rclunk(f);
1103 char *
1104 rstat(Fid *f)
1106 Dir d;
1108 if(FILE(f->qid.path) == Qmbox){
1109 qlock(&f->mb->ql);
1110 syncmbox(f->mb, 1);
1111 qunlock(&f->mb->ql);
1113 mkstat(&d, f->mb, f->m, FILE(f->qid.path));
1114 rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ);
1115 rhdr.stat = mbuf;
1116 return 0;
1119 char *
1120 rwstat(Fid* dummy)
1122 return Eperm;
1125 Fid *
1126 newfid(int fid)
1128 Fid *f, *ff;
1130 ff = 0;
1131 for(f = fids; f; f = f->next)
1132 if(f->fid == fid)
1133 return f;
1134 else if(!ff && !f->busy)
1135 ff = f;
1136 if(ff){
1137 ff->fid = fid;
1138 ff->fptr = nil;
1139 return ff;
1141 f = emalloc(sizeof *f);
1142 f->fid = fid;
1143 f->fptr = nil;
1144 f->next = fids;
1145 fids = f;
1146 return f;
1149 int
1150 fidmboxrefs(Mailbox *mb)
1152 Fid *f;
1153 int refs = 0;
1155 for(f = fids; f; f = f->next){
1156 if(f->mb == mb)
1157 refs++;
1159 return refs;
1162 void
1163 io(void)
1165 char *err;
1166 int n, nw;
1168 /* start a process to watch the mailboxes*/
1169 if(plumbing){
1170 proccreate(reader, nil, 16000);
1171 #if 0 /* jpc */
1172 switch(rfork(RFPROC|RFMEM)){
1173 case -1:
1174 /* oh well */
1175 break;
1176 case 0:
1177 reader();
1178 threadexits(nil);
1179 default:
1180 break;
1182 #endif /* jpc */
1185 for(;;){
1187 * reading from a pipe or a network device
1188 * will give an error after a few eof reads
1189 * however, we cannot tell the difference
1190 * between a zero-length read and an interrupt
1191 * on the processes writing to us,
1192 * so we wait for the error
1194 checkmboxrefs();
1195 n = read9pmsg(mfd[0], mdata, messagesize);
1196 if(n == 0)
1197 continue;
1198 if(n < 0)
1199 return;
1200 if(convM2S(mdata, n, &thdr) == 0)
1201 continue;
1203 if(debug)
1204 fprint(2, "%s:<-%F\n", argv0, &thdr);
1206 rhdr.data = (char*)mdata + messagesize;
1207 if(!fcalls[thdr.type])
1208 err = "bad fcall type";
1209 else
1210 err = (*fcalls[thdr.type])(newfid(thdr.fid));
1211 if(err){
1212 rhdr.type = Rerror;
1213 rhdr.ename = err;
1214 }else{
1215 rhdr.type = thdr.type + 1;
1216 rhdr.fid = thdr.fid;
1218 rhdr.tag = thdr.tag;
1219 if(debug)
1220 fprint(2, "%s:->%F\n", argv0, &rhdr);/**/
1221 n = convS2M(&rhdr, mdata, messagesize);
1222 if((nw = write(mfd[1], mdata, n)) != n) {
1223 fprint(2,"wrote %d bytes\n",nw);
1224 error("mount write");
1229 void
1230 reader(void *dummy)
1232 ulong t;
1233 Dir *d;
1234 Mailbox *mb;
1236 sleep(15*1000);
1237 for(;;){
1238 t = time(0);
1239 qlock(&mbllock);
1240 for(mb = mbl; mb != nil; mb = mb->next){
1241 assert(mb->refs > 0);
1242 if(mb->waketime != 0 && t > mb->waketime){
1243 qlock(&mb->ql);
1244 mb->waketime = 0;
1245 break;
1248 d = dirstat(mb->path);
1249 if(d == nil)
1250 continue;
1252 qlock(&mb->ql);
1253 if(mb->d)
1254 if(d->qid.path != mb->d->qid.path
1255 || d->qid.vers != mb->d->qid.vers){
1256 free(d);
1257 break;
1259 qunlock(&mb->ql);
1260 free(d);
1262 qunlock(&mbllock);
1263 if(mb != nil){
1264 syncmbox(mb, 1);
1265 qunlock(&mb->ql);
1266 } else
1267 sleep(15*1000);
1271 int
1272 newid(void)
1274 int rv;
1275 static int id;
1276 static Lock idlock;
1278 lock(&idlock);
1279 rv = ++id;
1280 unlock(&idlock);
1282 return rv;
1285 void
1286 error(char *s)
1288 postnote(PNGROUP, getpid(), "die yankee pig dog");
1289 fprint(2, "%s: %s: %r\n", argv0, s);
1290 threadexits(s);
1294 typedef struct Ignorance Ignorance;
1295 struct Ignorance
1297 Ignorance *next;
1298 char *str; /* string */
1299 int partial; /* true if not exact match */
1301 Ignorance *ignorance;
1304 * read the file of headers to ignore
1306 void
1307 readignore(void)
1309 char *p;
1310 Ignorance *i;
1311 Biobuf *b;
1313 if(ignorance != nil)
1314 return;
1316 b = Bopen("/mail/lib/ignore", OREAD);
1317 if(b == 0)
1318 return;
1319 while(p = Brdline(b, '\n')){
1320 p[Blinelen(b)-1] = 0;
1321 while(*p && (*p == ' ' || *p == '\t'))
1322 p++;
1323 if(*p == '#')
1324 continue;
1325 i = malloc(sizeof(Ignorance));
1326 if(i == 0)
1327 break;
1328 i->partial = strlen(p);
1329 i->str = strdup(p);
1330 if(i->str == 0){
1331 free(i);
1332 break;
1334 i->next = ignorance;
1335 ignorance = i;
1337 Bterm(b);
1340 int
1341 ignore(char *p)
1343 Ignorance *i;
1345 readignore();
1346 for(i = ignorance; i != nil; i = i->next)
1347 if(cistrncmp(i->str, p, i->partial) == 0)
1348 return 1;
1349 return 0;
1352 int
1353 hdrlen(char *p, char *e)
1355 char *ep;
1357 ep = p;
1358 do {
1359 ep = strchr(ep, '\n');
1360 if(ep == nil){
1361 ep = e;
1362 break;
1364 ep++;
1365 if(ep >= e){
1366 ep = e;
1367 break;
1369 } while(*ep == ' ' || *ep == '\t');
1370 return ep - p;
1373 // rfc2047 non-ascii
1374 typedef struct Charset Charset;
1375 struct Charset {
1376 char *name;
1377 int len;
1378 int convert;
1379 char *tcsname;
1380 } charsets[] =
1382 { "us-ascii", 8, 1, nil, },
1383 { "utf-8", 5, 0, nil, },
1384 { "iso-8859-1", 10, 1, nil, },
1385 { "iso-8859-2", 10, 2, "8859-2", },
1386 { "big5", 4, 2, "big5", },
1387 { "iso-2022-jp", 11, 2, "jis", },
1388 { "windows-1251", 12, 2, "cp1251"},
1389 { "koi8-r", 6, 2, "koi8"},
1392 int
1393 rfc2047convert(String *s, char *token, int len)
1395 char decoded[1024];
1396 char utfbuf[2*1024];
1397 int i;
1398 char *e, *x;
1400 if(len == 0)
1401 return -1;
1403 e = token+len-2;
1404 token += 2;
1406 // bail if we don't understand the character set
1407 for(i = 0; i < nelem(charsets); i++)
1408 if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0)
1409 if(token[charsets[i].len] == '?'){
1410 token += charsets[i].len + 1;
1411 break;
1413 if(i >= nelem(charsets))
1414 return -1;
1416 // bail if it doesn't fit
1417 if(e-token > sizeof(decoded)-1)
1418 return -1;
1420 // bail if we don't understand the encoding
1421 if(cistrncmp(token, "b?", 2) == 0){
1422 token += 2;
1423 len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
1424 decoded[len] = 0;
1425 } else if(cistrncmp(token, "q?", 2) == 0){
1426 token += 2;
1427 len = decquoted(decoded, token, e);
1428 if(len > 0 && decoded[len-1] == '\n')
1429 len--;
1430 decoded[len] = 0;
1431 } else
1432 return -1;
1434 switch(charsets[i].convert){
1435 case 0:
1436 s_append(s, decoded);
1437 break;
1438 case 1:
1439 latin1toutf(utfbuf, decoded, decoded+len);
1440 s_append(s, utfbuf);
1441 break;
1442 case 2:
1443 if(xtoutf(charsets[i].tcsname, &x, decoded, decoded+len) <= 0){
1444 s_append(s, decoded);
1445 } else {
1446 s_append(s, x);
1447 free(x);
1449 break;
1452 return 0;
1455 char*
1456 rfc2047start(char *start, char *end)
1458 int quests;
1460 if(*--end != '=')
1461 return nil;
1462 if(*--end != '?')
1463 return nil;
1465 quests = 0;
1466 for(end--; end >= start; end--){
1467 switch(*end){
1468 case '=':
1469 if(quests == 3 && *(end+1) == '?')
1470 return end;
1471 break;
1472 case '?':
1473 ++quests;
1474 break;
1475 case ' ':
1476 case '\t':
1477 case '\n':
1478 case '\r':
1479 /* can't have white space in a token */
1480 return nil;
1483 return nil;
1486 // convert a header line
1487 String*
1488 stringconvert(String *s, char *uneaten, int len)
1490 char *token;
1491 char *p;
1492 int i;
1494 s = s_reset(s);
1495 p = uneaten;
1496 for(i = 0; i < len; i++){
1497 if(*p++ == '='){
1498 token = rfc2047start(uneaten, p);
1499 if(token != nil){
1500 s_nappend(s, uneaten, token-uneaten);
1501 if(rfc2047convert(s, token, p - token) < 0)
1502 s_nappend(s, token, p - token);
1503 uneaten = p;
1507 if(p > uneaten)
1508 s_nappend(s, uneaten, p-uneaten);
1509 return s;
1512 int
1513 readheader(Message *m, char *buf, int off, int cnt)
1515 char *p, *e;
1516 int n, ns;
1517 char *to = buf;
1518 String *s;
1520 p = m->header;
1521 e = m->hend;
1522 s = nil;
1524 // copy in good headers
1525 while(cnt > 0 && p < e){
1526 n = hdrlen(p, e);
1527 if(ignore(p)){
1528 p += n;
1529 continue;
1532 // rfc2047 processing
1533 s = stringconvert(s, p, n);
1534 ns = s_len(s);
1535 if(off > 0){
1536 if(ns <= off){
1537 off -= ns;
1538 p += n;
1539 continue;
1541 ns -= off;
1543 if(ns > cnt)
1544 ns = cnt;
1545 memmove(to, s_to_c(s)+off, ns);
1546 to += ns;
1547 p += n;
1548 cnt -= ns;
1549 off = 0;
1552 s_free(s);
1553 return to - buf;
1556 int
1557 headerlen(Message *m)
1559 char buf[1024];
1560 int i, n;
1562 if(m->hlen >= 0)
1563 return m->hlen;
1564 for(n = 0; ; n += i){
1565 i = readheader(m, buf, n, sizeof(buf));
1566 if(i <= 0)
1567 break;
1569 m->hlen = n;
1570 return n;
1573 QLock hashlock;
1575 uint
1576 hash(ulong ppath, char *name)
1578 uchar *p;
1579 uint h;
1581 h = 0;
1582 for(p = (uchar*)name; *p; p++)
1583 h = h*7 + *p;
1584 h += ppath;
1586 return h % Hsize;
1589 Hash*
1590 hlook(ulong ppath, char *name)
1592 int h;
1593 Hash *hp;
1595 qlock(&hashlock);
1596 h = hash(ppath, name);
1597 for(hp = htab[h]; hp != nil; hp = hp->next)
1598 if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1599 qunlock(&hashlock);
1600 return hp;
1602 qunlock(&hashlock);
1603 return nil;
1606 void
1607 henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
1609 int h;
1610 Hash *hp, **l;
1612 qlock(&hashlock);
1613 h = hash(ppath, name);
1614 for(l = &htab[h]; *l != nil; l = &(*l)->next){
1615 hp = *l;
1616 if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1617 hp->m = m;
1618 hp->mb = mb;
1619 hp->qid = qid;
1620 qunlock(&hashlock);
1621 return;
1625 *l = hp = emalloc(sizeof(*hp));
1626 hp->m = m;
1627 hp->mb = mb;
1628 hp->qid = qid;
1629 hp->name = name;
1630 hp->ppath = ppath;
1631 qunlock(&hashlock);
1634 void
1635 hfree(ulong ppath, char *name)
1637 int h;
1638 Hash *hp, **l;
1640 qlock(&hashlock);
1641 h = hash(ppath, name);
1642 for(l = &htab[h]; *l != nil; l = &(*l)->next){
1643 hp = *l;
1644 if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1645 hp->mb = nil;
1646 *l = hp->next;
1647 free(hp);
1648 break;
1651 qunlock(&hashlock);
1654 int
1655 hashmboxrefs(Mailbox *mb)
1657 int h;
1658 Hash *hp;
1659 int refs = 0;
1661 qlock(&hashlock);
1662 for(h = 0; h < Hsize; h++){
1663 for(hp = htab[h]; hp != nil; hp = hp->next)
1664 if(hp->mb == mb)
1665 refs++;
1667 qunlock(&hashlock);
1668 return refs;
1671 void
1672 checkmboxrefs(void)
1674 int f, refs;
1675 Mailbox *mb;
1677 qlock(&mbllock);
1678 for(mb=mbl; mb; mb=mb->next){
1679 qlock(&mb->ql);
1680 refs = (f=fidmboxrefs(mb))+1;
1681 if(refs != mb->refs){
1682 fprint(2, "mbox %s %s ref mismatch actual %d (%d+1) expected %d\n", mb->name, mb->path, refs, f, mb->refs);
1683 abort();
1685 qunlock(&mb->ql);
1687 qunlock(&mbllock);
1690 void
1691 post(char *name, char *envname, int srvfd)
1693 int fd;
1694 char buf[32];
1696 fd = create(name, OWRITE, 0600);
1697 if(fd < 0)
1698 error("post failed");
1699 sprint(buf, "%d",srvfd);
1700 if(write(fd, buf, strlen(buf)) != strlen(buf))
1701 error("srv write");
1702 close(fd);
1703 putenv(envname, name);