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 enum10 {11 OPERM = 0x3, /* mask of all permission types in open mode */12 };14 typedef struct Fid Fid;16 struct Fid17 {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] rwstat85 };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[] =100 {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 enum132 {133 Hsize= 1277134 };136 Hash *htab[Hsize];138 int debug;139 int fflag;140 int logging;142 void143 usage(void)144 {145 fprint(2, "usage: %s [-b -m mountpoint]\n", argv0);146 threadexits("usage");147 }149 void150 notifyf(void *a, char *s)151 {152 USED(a);153 if(strncmp(s, "interrupt", 9) == 0)154 noted(NCONT);155 noted(NDFLT);156 }158 void159 threadmain(int argc, char *argv[])160 {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 }ARGEND206 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;216 }217 if(mboxfile == nil && !nodflt){218 snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user);219 mboxfile = mbox;220 std = 1;221 }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);230 }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 */252 }253 }254 threadexits(0);255 }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");266 }268 static int269 fileinfo(Message *m, int t, char **pp)270 {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);285 }286 break;287 case Qcc:288 if(m->cc822){289 p = s_to_c(m->cc822);290 len = strlen(p);291 }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;301 }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);311 }312 break;313 case Qfilename:314 if(m->filename){315 p = s_to_c(m->filename);316 len = strlen(p);317 }318 break;319 case Qinreplyto:320 if(m->inreplyto822){321 p = s_to_c(m->inreplyto822);322 len = strlen(p);323 }324 break;325 case Qmessageid:326 if(m->messageid822){327 p = s_to_c(m->messageid822);328 len = strlen(p);329 }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);338 }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 else357 p++;358 }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);391 }392 break;393 case Qsender:394 if(m->sender822){395 p = s_to_c(m->sender822);396 len = strlen(p);397 }398 break;399 case Qsubject:400 p = nil;401 if(m->subject822){402 p = s_to_c(m->subject822);403 len = strlen(p);404 }405 break;406 case Qto:407 if(m->to822){408 p = s_to_c(m->to822);409 len = strlen(p);410 }411 break;412 case Qtype:413 if(m->type){414 p = s_to_c(m->type);415 len = strlen(p);416 }417 break;418 case Qunixdate:419 if(m->unixdate){420 p = s_to_c(m->unixdate);421 len = strlen(p);422 }423 break;424 case Qunixheader:425 if(m->unixheader){426 p = s_to_c(m->unixheader);427 len = s_len(m->unixheader);428 }429 break;430 case Qdigest:431 if(m->sdigest){432 p = s_to_c(m->sdigest);433 len = strlen(p);434 }435 break;436 }437 *pp = p;438 return len;439 }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 -1459 };461 static int462 readinfo(Message *m, char *buf, long off, int count)463 {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;480 }481 p += off;482 n -= off;483 off = 0;484 }485 if(n > count - len)486 n = count - len;487 if(buf)488 memmove(buf+len, p, n);489 len += n;490 }491 s_free(s);492 return len;493 }495 static void496 mkstat(Dir *d, Mailbox *mb, Message *m, int t)497 {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;514 }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;564 }565 }567 char*568 rversion(Fid* dummy)569 {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 else580 rhdr.version = "9P2000";581 for(f = fids; f; f = f->next)582 if(f->busy)583 rclunk(f);584 return nil;585 }587 char*588 rauth(Fid* dummy)589 {590 return Enoauth;591 }593 char*594 rflush(Fid *f)595 {596 USED(f);597 return 0;598 }600 char*601 rattach(Fid *f)602 {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;613 }615 static Fid*616 doclone(Fid *f, int nfid)617 {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);634 }635 nf->qid = f->qid;636 return nf;637 }639 char*640 dowalk(Fid *f, char *name)641 {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 else655 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;672 }673 break;674 }675 f->qid = h->qid;676 rv = nil;677 } else if((p = strchr(name, '.')) != nil && *name != '.'){678 *p = 0;679 goto retry;680 }682 if(omb)683 qunlock(&omb->ql);684 else685 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;724 }725 qunlock(&f->mb->ql);726 break;727 }728 rv = nil;729 }730 return rv;731 }733 char*734 rwalk(Fid *f)735 {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;752 }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;766 }767 rhdr.wqid[i] = f->qid;768 }769 rhdr.nwqid = i;771 /* we only error out if no walk */772 if(i > 0)773 rv = nil;775 return rv;776 }778 char *779 ropen(Fid *f)780 {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);797 }799 rhdr.iounit = 0;800 rhdr.qid = f->qid;801 f->open = 1;802 return 0;803 }805 char *806 rcreate(Fid* dummy)807 {808 return Eperm;809 }811 int812 readtopdir(Fid* dummy, uchar *buf, long off, int cnt, int blen)813 {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;828 }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;839 }840 pos += m;841 }842 return n;843 }845 int846 readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)847 {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;861 }862 n += m;863 cnt -= m;864 } else865 off -= m;866 }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;875 }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;889 }890 pos += m;891 }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;899 }901 int902 readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)903 {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;919 }920 pos += m;921 }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;930 }931 pos += m;932 }934 return n;935 }937 char*938 rread(Fid *f)939 {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);969 }971 rhdr.count = n;972 return nil;973 }975 if(FILE(f->qid.path) == Qheader){976 rhdr.count = readheader(f->m, (char*)mbuf, off, cnt);977 return nil;978 }980 if(FILE(f->qid.path) == Qinfo){981 rhdr.count = readinfo(f->m, (char*)mbuf, off, cnt);982 return nil;983 }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;991 }992 return nil;993 }995 char*996 rwrite(Fid *f)997 {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 else1012 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 else1031 err = newmbox(s_to_c(file), token[2], 0);1032 break;1033 }1034 s_free(file);1035 return err;1036 }1037 if(strcmp(token[0], "close") == 0){1038 if(n < 2)1039 return nil;1040 freembox(token[1]);1041 return nil;1042 }1043 if(strcmp(token[0], "delete") == 0){1044 if(n < 3)1045 return nil;1046 delmessages(n-1, &token[1]);1047 return nil;1048 }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 else1057 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);1062 }1063 }1064 return Eperm;1065 }1067 char *1068 rclunk(Fid *f)1069 {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);1078 }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);1087 }1088 f->fid = -1;1089 return 0;1090 }1092 char *1093 rremove(Fid *f)1094 {1095 if(f->m != nil){1096 if(f->m->deleted == 0)1097 mailplumb(f->mb, f->m, 1);1098 f->m->deleted = 1;1099 }1100 return rclunk(f);1101 }1103 char *1104 rstat(Fid *f)1105 {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);1112 }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;1117 }1119 char *1120 rwstat(Fid* dummy)1121 {1122 return Eperm;1123 }1125 Fid *1126 newfid(int fid)1127 {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;1140 }1141 f = emalloc(sizeof *f);1142 f->fid = fid;1143 f->fptr = nil;1144 f->next = fids;1145 fids = f;1146 return f;1147 }1149 int1150 fidmboxrefs(Mailbox *mb)1151 {1152 Fid *f;1153 int refs = 0;1155 for(f = fids; f; f = f->next){1156 if(f->mb == mb)1157 refs++;1158 }1159 return refs;1160 }1162 void1163 io(void)1164 {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;1181 }1182 #endif /* jpc */1183 }1185 for(;;){1186 /*1187 * reading from a pipe or a network device1188 * will give an error after a few eof reads1189 * however, we cannot tell the difference1190 * between a zero-length read and an interrupt1191 * on the processes writing to us,1192 * so we wait for the error1193 */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 else1210 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;1217 }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");1225 }1226 }1227 }1229 void1230 reader(void *dummy)1231 {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;1246 }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.path1255 || d->qid.vers != mb->d->qid.vers){1256 free(d);1257 break;1258 }1259 qunlock(&mb->ql);1260 free(d);1261 }1262 qunlock(&mbllock);1263 if(mb != nil){1264 syncmbox(mb, 1);1265 qunlock(&mb->ql);1266 } else1267 sleep(15*1000);1268 }1269 }1271 int1272 newid(void)1273 {1274 int rv;1275 static int id;1276 static Lock idlock;1278 lock(&idlock);1279 rv = ++id;1280 unlock(&idlock);1282 return rv;1283 }1285 void1286 error(char *s)1287 {1288 postnote(PNGROUP, getpid(), "die yankee pig dog");1289 fprint(2, "%s: %s: %r\n", argv0, s);1290 threadexits(s);1291 }1294 typedef struct Ignorance Ignorance;1295 struct Ignorance1296 {1297 Ignorance *next;1298 char *str; /* string */1299 int partial; /* true if not exact match */1300 };1301 Ignorance *ignorance;1303 /*1304 * read the file of headers to ignore1305 */1306 void1307 readignore(void)1308 {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;1333 }1334 i->next = ignorance;1335 ignorance = i;1336 }1337 Bterm(b);1338 }1340 int1341 ignore(char *p)1342 {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;1350 }1352 int1353 hdrlen(char *p, char *e)1354 {1355 char *ep;1357 ep = p;1358 do {1359 ep = strchr(ep, '\n');1360 if(ep == nil){1361 ep = e;1362 break;1363 }1364 ep++;1365 if(ep >= e){1366 ep = e;1367 break;1368 }1369 } while(*ep == ' ' || *ep == '\t');1370 return ep - p;1371 }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[] =1381 {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"}1390 };1392 int1393 rfc2047convert(String *s, char *token, int len)1394 {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;1412 }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 } else1432 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);1448 }1449 break;1450 }1452 return 0;1453 }1455 char*1456 rfc2047start(char *start, char *end)1457 {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;1481 }1482 }1483 return nil;1484 }1486 /* convert a header line */1487 String*1488 stringconvert(String *s, char *uneaten, int len)1489 {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;1504 }1505 }1506 }1507 if(p > uneaten)1508 s_nappend(s, uneaten, p-uneaten);1509 return s;1510 }1512 int1513 readheader(Message *m, char *buf, int off, int cnt)1514 {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;1530 }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;1540 }1541 ns -= off;1542 }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;1550 }1552 s_free(s);1553 return to - buf;1554 }1556 int1557 headerlen(Message *m)1558 {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;1568 }1569 m->hlen = n;1570 return n;1571 }1573 QLock hashlock;1575 uint1576 hash(ulong ppath, char *name)1577 {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;1587 }1589 Hash*1590 hlook(ulong ppath, char *name)1591 {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;1601 }1602 qunlock(&hashlock);1603 return nil;1604 }1606 void1607 henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb)1608 {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;1622 }1623 }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);1632 }1634 void1635 hfree(ulong ppath, char *name)1636 {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;1649 }1650 }1651 qunlock(&hashlock);1652 }1654 int1655 hashmboxrefs(Mailbox *mb)1656 {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++;1666 }1667 qunlock(&hashlock);1668 return refs;1669 }1671 void1672 checkmboxrefs(void)1673 {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();1684 }1685 qunlock(&mb->ql);1686 }1687 qunlock(&mbllock);1688 }1690 void1691 post(char *name, char *envname, int srvfd)1692 {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);1704 }