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