#include "a.h" char Eperm[] = "permission denied"; char Enotdir[] = "not a directory"; char Enoauth[] = "upas/fs: authentication not required"; char Enotexist[] = "file does not exist"; char Einuse[] = "file in use"; char Eexist[] = "file exists"; char Enotowner[] = "not owner"; char Eisopen[] = "file already open for I/O"; char Excl[] = "exclusive use file already open"; char Ename[] = "illegal name"; char Ebadctl[] = "unknown control message"; char Eioerror[] = "error reading or writting file"; char Enomess[] = "message do not exist"; char Enomailbox[]= "mailbox do not exist"; char Eunknown[]= "unknown error"; enum { Qbody, Qbcc, Qcc, Qdate, Qdigest, Qdisposition, Qfilename, Qfrom, Qheader, Qinreplyto, Qlines, Qmimeheader, Qmessageid, Qraw, Qrawbody, Qrawheader, Qrawunix, Qreplyto, Qsender, Qsubject, Qto, Qtype, Qunixheader, Qinfo, Qunixdate, Qctl, Qmboxctl }; char *dirtab[] = { [Qbody] "body", [Qbcc] "bcc", [Qcc] "cc", [Qdate] "date", [Qdigest] "digest", [Qdisposition] "disposition", [Qfilename] "filename", [Qfrom] "from", [Qheader] "header", [Qinfo] "info", [Qinreplyto] "inreplyto", [Qlines] "lines", [Qmimeheader] "mimeheader", [Qmessageid] "message-id", [Qraw] "raw", [Qrawunix] "rawunix", [Qrawbody] "rawbody", [Qrawheader] "rawheader", [Qreplyto] "replyto", [Qsender] "sender", [Qsubject] "subject", [Qto] "to", [Qtype] "type", [Qunixdate] "unixdate", [Qunixheader] "unixheader", [Qctl] "ctl", [Qmboxctl] "ctl", }; /* no auth, just mount the selected box */ void fsattach(Req *r) { File *f; f = fstree->root; r->fid->file = f; incref(r->fid->file); r->ofcall.qid = f->qid; r->fid->qid = r->ofcall.qid; respond(r,nil); return; } void strrespond(Req *r,char *s,vlong n) { r->ofcall.count = r->ifcall.count; if(r->ifcall.offset >= n){ r->ofcall.count = 0; respond(r,nil); return; } if(r->ifcall.offset+r->ofcall.count > n) r->ofcall.count = n - r->ifcall.offset; memmove(r->ofcall.data, (char*)s+r->ifcall.offset, r->ofcall.count); respond(r,nil); } void buffrespond(Req *r,char *s,vlong n) { if(n <= 0){ r->ofcall.count = 0; respond(r,nil); return; } r->ofcall.count = n; memmove(r->ofcall.data, s, n); respond(r,nil); } void fsread(Req *r) { File *pf; Fileinfo info; Message *m; char *resp; vlong len; pf = r->fid->file; if ( pf->aux == nil) { respond(nil,Eunknown); return; } memcpy(&info,pf->aux,sizeof info); m=info.m; if (m == nil) { respond(nil,Eunknown); return; } resp=nil; switch(info.Q) { case Qto: len=m->to(m,&resp); strrespond(r,resp,len); break; case Qfrom: len=m->from(m,&resp); strrespond(r,resp,len); break; case Qbcc: len=m->bcc(m,&resp); strrespond(r,resp,len); break; case Qcc: len=m->cc(m,&resp); strrespond(r,resp,len); break; case Qbody: len=m->body(m,&resp,r->ifcall.offset, r->ifcall.count); buffrespond(r,resp,len); break; case Qsubject: len=m->subject(m,&resp); strrespond(r,resp,len); break; case Qdate: len=m->date(m,&resp); strrespond(r,resp,len); break; case Qdigest: len=m->digest(m,&resp); strrespond(r,resp,len); break; case Qdisposition: len=m->disposition(m,&resp); strrespond(r,resp,len); break; case Qfilename: len=m->filename(m,&resp); strrespond(r,resp,len); break; case Qheader: len=m->header(m,&resp); strrespond(r,resp,len); break; case Qinreplyto: len=m->inreplyto(m,&resp); strrespond(r,resp,len); break; case Qlines: len=m->lines(m,&resp); strrespond(r,resp,len); break; case Qmimeheader: len=m->mimeheader(m,&resp); strrespond(r,resp,len); break; case Qmessageid: len=m->messageid(m,&resp); strrespond(r,resp,len); break; case Qraw: len=m->raw(m,&resp,r->ifcall.offset, r->ifcall.count); buffrespond(r,resp,len); break; case Qrawbody: len=m->rawbody(m,&resp,r->ifcall.offset, r->ifcall.count); buffrespond(r,resp,len); break; case Qrawheader: len=m->rawheader(m,&resp,r->ifcall.offset, r->ifcall.count); buffrespond(r,resp,len); break; case Qrawunix: len=m->rawunix(m,&resp,r->ifcall.offset, r->ifcall.count); buffrespond(r,resp,len); break; case Qreplyto: len=m->replyto(m,&resp); strrespond(r,resp,len); break; case Qsender: len=m->sender(m,&resp); strrespond(r,resp,len); break; case Qtype: len=m->type(m,&resp); strrespond(r,resp,len); break; case Qunixheader: len=m->unixheader(m,&resp); strrespond(r,resp,len); break; case Qinfo: len=m->info(m,&resp); strrespond(r,resp,len); break; case Qunixdate: len=m->unixdate(m,&resp); strrespond(r,resp,len); break; case Qctl: case Qmboxctl: strrespond(r,nil,0); break; default: respond(r,Enotexist); break; } free(resp); return; } void fswrite(Req *r) { File *pf; Fileinfo info; Message *m; char *cmd; vlong len; char *token[1024]; int t, n; Mailbox *b; pf = r->fid->file; if ( pf->aux == nil) { respond(nil,Eunknown); return; } memcpy(&info,pf->aux,sizeof info); m=info.m; if (m == nil) { respond(nil,Eunknown); return; } switch(info.Q) { case Qctl: len=r->ifcall.count; if ( r->ifcall.offset > 0 || len == 0) { respond(nil,Ebadctl); return; } cmd=emalloc(len); memmove(cmd,r->ifcall.data,len); if(cmd[len-1] == '\n') cmd[len-1] = 0; else cmd[len] = 0; n = tokenize(cmd, token, nelem(token)); if(n == 0) { respond(nil,Ebadctl); return; } if(strcmp(token[0], "open") == 0){ switch(n){ case 1: respond(nil,Ebadctl); break; case 2: respond(nil,Ebadctl); break; } } } respond(nil,Ebadctl); return; } void fscreate(Req *r) { respond(r, Eperm); return; } Srv fs = { .attach= fsattach, .read= fsread, .write= fswrite, .create= fscreate, }; /* initial parse of mailbox */ void fsinittab(Message *m, File *mess, char *user) { File *f; Fileinfo info; char *aux; for(int fi=Qbody; fi<=Qctl;fi++) { f = createfile(mess,dirtab[fi],user,0400,nil); info.Q=fi; info.m=m; f->aux=emalloc(sizeof info); memcpy(f->aux,&info,sizeof info); aux=nil; /* file info, required to be compatible with acme mail and others */ switch(fi) { case Qto: f->length=m->to(m,&aux); break; case Qfrom: f->length=m->from(m,&aux); break; case Qbcc: f->length=m->bcc(m,&aux); break; case Qcc: f->length=m->cc(m,&aux); break; case Qsubject: f->length=m->subject(m,&aux); break; case Qdate: f->length=m->date(m,&aux); break; case Qdigest: f->length=m->digest(m,&aux); break; case Qdisposition: f->length=m->disposition(m,&aux); break; case Qfilename: f->length=m->filename(m,&aux); break; case Qheader: f->length=m->header(m,&aux); break; case Qinreplyto: f->length=m->inreplyto(m,&aux); break; case Qlines: f->length=m->lines(m,&aux); break; case Qmimeheader: f->length=m->mimeheader(m,&aux); break; case Qmessageid: f->length=m->messageid(m,&aux); break; case Qreplyto: f->length=m->replyto(m,&aux); break; case Qsender: f->length=m->sender(m,&aux); break; case Qtype: f->length=m->type(m,&aux); break; case Qunixheader: f->length=m->unixheader(m,&aux); break; case Qinfo: f->length=m->info(m,&aux); break; case Qunixdate: f->length=m->unixdate(m,&aux); break; case Qbody: case Qrawbody: f->length=m->bend-m->bstart; break; case Qraw: case Qrawunix: f->length=m->end-m->start; break; case Qrawheader: f->length=m->hend-m->hstart; break; case Qctl: case Qmboxctl: f->length=0; break; } free(aux); } } int fsinitmsg(Message *m, File *parent, char *user) { Message *p; File *fm; File *fp; char *mname; for(;m!=nil;m=m->next) { mname=smprint("%d",m->id); fm = createfile(parent,mname,user,DMDIR|0700,nil); if ( fm == nil ) { error(DEBUG,"fs M 0x%luX cannot create dir %s\n",m,mname); free(mname); return 0; } free(mname); fsinittab(m,fm,user); for(p=m->part;p!=nil;p=p->next) { mname=smprint("%d",p->id); fp = createfile(fm,mname,user,DMDIR|0700,nil); if ( fm == nil ) { error(DEBUG,"fs P 0x%luX cannot create dir %s\n",p,mname); free(mname); return 0; } free(mname); fsinittab(p,fp,user); if ( p->part != nil ) fsinitmsg(p->part,fp,user); } } return 1; } int fsinit(Mailbox *b, char *user) { Message *m; File *box; Qid q; fs.tree = alloctree(nil, nil, DMDIR|0777, nil); fstree = fs.tree; q = fs.tree->root->qid; box=createfile(fs.tree->root,b->name,user,DMDIR|0700,nil); createfile(fs.tree->root,dirtab[Qmboxctl],user,0600,nil); m=b->msgs; fsinitmsg(m,box,user); return 1; } void usage(void) { fprint(2, "usage: upas/fs [-bdlnps] [-f mboxfile] [-m mountpoint]\n"); exits("usage"); } void threadmain(int argc, char **argv) { int nodflt; char maildir[128]; char mbox[128]; char *mboxfile, *user, *mntpt; char *srvfile, *pr; fmtinstall('D',dirfmt); fmtinstall('M',dirmodefmt); fmtinstall('F',fcallfmt); rfork(RFNOTEG); mntpt = nil; fflag = 0; mboxfile = nil; nodflt = 0; srvfile=nil; ARGBEGIN{ case 'f': fflag = 1; mboxfile = EARGF(usage()); break; case 'm': mntpt = EARGF(usage()); break; case 'd': debug = 1; chatty9p++; break; case 's': srvfile=EARGF(usage()); break; case 'l': logging = 1; break; case 'n': nodflt = 1; break; default: usage(); }ARGEND if(argc) usage(); user=getuser(); if(mntpt == nil){ snprint(maildir, sizeof(maildir), "/mail/fs"); mntpt = maildir; } if(mboxfile == nil && !nodflt){ snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user); mboxfile = mbox; } if(mboxfile != nil) Box = newbox(mboxfile); else usage(); initbox(Box); Box->open(Box); Box->fetch(Box); fsinit(Box,user); if ( debug == 1 ) printbox(Box); if(srvfile || mntpt) threadpostmountsrv(&fs, srvfile, mntpt, MREPL|MCREATE); exits(0); }