From 941e17134e92de7f12977f1899860b57bbf83330 Mon Sep 17 00:00:00 2001 From: rsc Date: Wed, 15 Feb 2006 12:39:09 +0000 Subject: imap-based new upas/fs --- src/cmd/upas/nfs/fs.c | 1259 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1259 insertions(+) create mode 100644 src/cmd/upas/nfs/fs.c (limited to 'src/cmd/upas/nfs/fs.c') diff --git a/src/cmd/upas/nfs/fs.c b/src/cmd/upas/nfs/fs.c new file mode 100644 index 00000000..eda3d666 --- /dev/null +++ b/src/cmd/upas/nfs/fs.c @@ -0,0 +1,1259 @@ +/* + * Mail file system. + * + * Serve the bulk of requests out of memory, so they can + * be in the main loop (will never see their flushes). + * Some requests do block and they get handled in + * separate threads. They're all okay to give up on + * early, though, so we just respond saying interrupted + * and then when they finish, silently discard the request. + +TO DO: + + decode subject, etc. + decode body + + digest + disposition + filename + + ctl messages + + fetch mail on demand + + */ + +#include "a.h" + +enum +{ + /* directories */ + Qroot, + Qbox, + Qmsg, + + /* control files */ + Qctl, + Qboxctl, + Qsearch, + + /* message header - same order as struct Hdr */ + Qdate, + Qsubject, + Qfrom, + Qsender, + Qreplyto, + Qto, + Qcc, + Qbcc, + Qinreplyto, + Qmessageid, + + /* part data - same order as stuct Part */ + Qtype, + Qidstr, + Qdesc, + Qencoding, /* only here temporarily! */ + Qcharset, + Qraw, + Qrawheader, + Qrawbody, + Qmimeheader, + + /* part numbers - same order as struct Part */ + Qsize, + Qlines, + + /* other message files */ + Qbody, + Qheader, + Qdigest, + Qdisposition, + Qfilename, + Qflags, + Qinfo, + Qrawunix, + Qunixdate, + Qunixheader, + + Qfile0 = Qbody, + Qnfile = Qunixheader+1-Qfile0, +}; + +static char Egreg[] = "gone postal"; +static char Enobox[] = "no such mailbox"; +static char Enomsg[] = "no such message"; +static char Eboxgone[] = "mailbox not available"; +static char Emsggone[] = "message not available"; +static char Eperm[] = "permission denied"; +static char Ebadctl[] = "bad control message"; + +Channel *fsreqchan; +Srv fs; +Qid rootqid; +ulong t0; + +void +responderror(Req *r) +{ + char e[ERRMAX]; + + rerrstr(e, sizeof e); + respond(r, e); +} + +int +qtype(Qid q) +{ + return q.path&0x3F; +} + +int +qboxid(Qid q) +{ + return (q.path>>40)&0xFFFF; +} + +int +qmsgid(Qid q) +{ + return ((q.path>>32)&0xFF000000) | ((q.path>>16)&0xFFFFFF); +} + +int +qpartid(Qid q) +{ + return ((q.path>>6)&0x3FF); +} + +Qid +qid(int ctl, Box *box, Msg *msg, Part *part) +{ + Qid q; + + q.type = 0; + if(ctl == Qroot || ctl == Qbox || ctl == Qmsg) + q.type = QTDIR; + q.path = (vlong)((msg ? msg->id : 0)&0xFF000000)<<32; + q.path |= (vlong)((msg ? msg->id : 0)&0xFFFFFF)<<16; + q.path |= (vlong)((box ? box->id : 0)&0xFFFF)<<40; + q.path |= ((part ? part->ix : 0)&0x3FF)<<6; + q.path |= ctl&0x3F; + q.vers = box ? box->validity : 0; + return q; +} + +int +parseqid(Qid q, Box **box, Msg **msg, Part **part) +{ + *msg = nil; + *part = nil; + + *box = boxbyid(qboxid(q)); + if(*box){ + *msg = msgbyid(*box, qmsgid(q)); + } + if(*msg) + *part = partbyid(*msg, qpartid(q)); + return qtype(q); +} + +static struct { + int type; + char *name; +} typenames[] = { + Qbody, "body", + Qbcc, "bcc", + Qcc, "cc", + Qdate, "date", + Qfilename, "filename", + Qflags, "flags", + Qfrom, "from", + Qheader, "header", + Qinfo, "info", + Qinreplyto, "inreplyto", + Qlines, "lines", + Qmimeheader, "mimeheader", + Qmessageid, "messageid", + Qraw, "raw", + Qrawunix, "rawunix", + Qrawbody, "rawbody", + Qrawheader, "rawheader", + Qreplyto, "replyto", + Qsender, "sender", + Qsubject, "subject", + Qto, "to", + Qtype, "type", + Qunixdate, "unixdate", + Qunixheader, "unixheader", + Qidstr, "idstr", + Qdesc, "desc", + Qencoding, "encoding", + Qcharset, "charset", +}; + +char* +nameoftype(int t) +{ + int i; + + for(i=0; ifid->qid = rootqid; + r->ofcall.qid = rootqid; + respond(r, nil); +} + +int +isnumber(char *s) +{ + int n; + + if(*s < '1' || *s > '9') + return 0; + n = strtol(s, &s, 10); + if(*s != 0) + return 0; + return n; +} + +static char* +fswalk1(Fid *fid, char *name, void *arg) +{ + int a, type; + Box *b, *box; + Msg *msg; + Part *p, *part; + + USED(arg); + + switch(type = parseqid(fid->qid, &box, &msg, &part)){ + case Qroot: + if(strcmp(name, "..") == 0) + return nil; + if(strcmp(name, "ctl") == 0){ + fid->qid = qid(Qctl, nil, nil, nil); + return nil; + } + if((box = boxbyname(name)) != nil){ + fid->qid = qid(Qbox, box, nil, nil); + return nil; + } + break; + + case Qbox: + /* + * Would be nice if .. could work even if the box is gone, + * but we don't know how deep the directory was. + */ + if(box == nil) + return Eboxgone; + if(strcmp(name, "..") == 0){ + if((box = box->parent) == nil){ + fid->qid = rootqid; + return nil; + } + fid->qid = qid(Qbox, box, nil, nil); + return nil; + } + if(strcmp(name, "ctl") == 0){ + fid->qid = qid(Qboxctl, box, nil, nil); + return nil; + } + if(strcmp(name, "search") == 0){ + fid->qid = qid(Qsearch, box, nil, nil); + return nil; + } + if((b = subbox(box, name)) != nil){ + fid->qid = qid(Qbox, b, nil, nil); + return nil; + } + if((a = isnumber(name)) != 0){ + if((msg = msgbyid(box, a)) == nil){ + return Enomsg; + } + fid->qid = qid(Qmsg, box, msg, nil); + return nil; + } + break; + + case Qmsg: + if(strcmp(name, "..") == 0){ + if(part == msg->part[0]){ + fid->qid = qid(Qbox, box, nil, nil); + return nil; + } + fid->qid = qid(Qmsg, box, msg, part->parent); + return nil; + } + if((type = typeofname(name)) > 0){ + /* XXX - should check that type makes sense (see msggen) */ + fid->qid = qid(type, box, msg, part); + return nil; + } + if((a = isnumber(name)) != 0){ + if((p = subpart(part, a-1)) != nil){ + fid->qid = qid(Qmsg, box, msg, p); + return nil; + } + } + break; + } + return "not found"; +} + +static void +fswalk(Req *r) +{ + walkandclone(r, fswalk1, nil, nil); +} + +static struct { + int flag; + char *name; +} flagtab[] = { + FlagJunk, "junk", + FlagNonJunk, "notjunk", + FlagReplied, "replied", + FlagFlagged, "flagged", +// FlagDeleted, "deleted", + FlagDraft, "draft", + FlagSeen, "seen", +}; + +static void +addaddrs(Fmt *fmt, char *prefix, char *addrs) +{ + char **f; + int i, nf, inquote; + char *p, *sep; + + if(addrs == nil) + return; + addrs = estrdup(addrs); + nf = 0; + inquote = 0; + for(p=addrs; *p; p++){ + if(*p == ' ' && !inquote) + nf++; + if(*p == '\'') + inquote = !inquote; + } + nf += 10; + f = emalloc(nf*sizeof f[0]); + nf = tokenize(addrs, f, nf); + fmtprint(fmt, "%s:", prefix); + sep = " "; + for(i=0; i+1", sep, f[i], f[i+1]); + else + fmtprint(fmt, "%s%s", sep, f[i+1]); + sep = ", "; + } + fmtprint(fmt, "\n"); + free(addrs); +} + +static void +mkbody(Part *p, Qid q) +{ + char *t; + int len; + + if(p->msg->part[0] == p) + t = p->rawbody; + else + t = p->raw; + if(t == nil) + return; + + len = -1; + if(p->encoding && cistrcmp(p->encoding, "quoted-printable") == 0) + t = decode(QuotedPrintable, t, &len); + else if(p->encoding && cistrcmp(p->encoding, "base64") == 0) + t = decode(Base64, t, &len); + else + t = estrdup(t); + + if(p->charset){ + t = tcs(p->charset, t); + len = -1; + } + p->body = t; + if(len == -1) + p->nbody = strlen(t); + else + p->nbody = len; +} + +static Qid ZQ; + +static int +filedata(int type, Box *box, Msg *msg, Part *part, char **pp, int *len, int *freeme, int force, Qid q) +{ + int i, inquote, n, t; + char *from, *s; + static char buf[256]; + Fmt fmt; + + *pp = nil; + *freeme = 0; + if(len) + *len = -1; + + if(msg == nil || part == nil){ + werrstr(Emsggone); + return -1; + } + switch(type){ + case Qdate: + case Qsubject: + case Qfrom: + case Qsender: + case Qreplyto: + case Qto: + case Qcc: + case Qbcc: + case Qinreplyto: + case Qmessageid: + if(part->hdr == nil){ + werrstr(Emsggone); + return -1; + } + *pp = ((char**)&part->hdr->date)[type-Qdate]; + return 0; + + case Qunixdate: + strcpy(buf, ctime(msg->date)); + *pp = buf; + return 0; + + case Qunixheader: + if(part->hdr == nil){ + werrstr(Emsggone); + return -1; + } + from = part->hdr->from; + if(from == nil) + from = "???"; + else{ + inquote = 0; + for(; *from; from++){ + if(*from == '\'') + inquote = !inquote; + if(!inquote && *from == ' '){ + from++; + break; + } + } + if(*from == 0) + from = part->hdr->from; + } + n = snprint(buf, sizeof buf, "From %s %s", from, ctime(msg->date)); + if(n+1 < sizeof buf){ + *pp = buf; + return 0; + } + fmtstrinit(&fmt); + fmtprint(&fmt, "From %s %s", from, ctime(msg->date)); + s = fmtstrflush(&fmt); + if(s){ + *pp = s; + *freeme = 1; + }else + *pp = buf; + return 0; + + case Qtype: + case Qidstr: + case Qdesc: + case Qencoding: + case Qcharset: + case Qraw: + case Qrawheader: + case Qrawbody: + case Qmimeheader: + *pp = ((char**)&part->type)[type-Qtype]; + if(*pp == nil && force){ + switch(type){ + case Qraw: + imapfetchraw(imap, part); + break; + case Qrawheader: + imapfetchrawheader(imap, part); + break; + case Qrawbody: + imapfetchrawbody(imap, part); + break; + case Qmimeheader: + imapfetchrawmime(imap, part); + break; + default: + return 0; + } + /* + * We ran fetchsomething, which might have changed + * the mailbox contents. Msg might even be gone. + */ + t = parseqid(q, &box, &msg, &part); + if(t != type || msg == nil || part == nil) + return 0; + *pp = ((char**)&part->type)[type-Qtype]; + } + return 0; + + case Qbody: + if(part->body){ + *pp = part->body; + if(len) + *len = part->nbody; + return 0; + } + if(!force) + return 0; + if(part->rawbody == nil){ + if(part->msg->part[0] == part) + imapfetchrawbody(imap, part); + else + imapfetchraw(imap, part); + t = parseqid(q, &box, &msg, &part); + if(t != type || msg == nil || part == nil) + return 0; + } + mkbody(part, q); + *pp = part->body; + if(len) + *len = part->nbody; + return 0; + + case Qsize: + case Qlines: + n = ((uint*)&part->size)[type-Qsize]; + snprint(buf, sizeof buf, "%d", n); + *pp = buf; + return 0; + + case Qflags: + s = buf; + *s = 0; + for(i=0; iflags&flagtab[i].flag){ + if(s > buf) + *s++ = ' '; + strcpy(s, flagtab[i].name); + s += strlen(s); + } + } + *pp = buf; + return 0; + + case Qinfo: + fmtstrinit(&fmt); + if(part == msg->part[0]){ + if(msg->date) + fmtprint(&fmt, "unixdate %lud %s", msg->date, ctime(msg->date)); + if(msg->flags){ + filedata(Qflags, box, msg, part, pp, nil, freeme, 0, ZQ); + fmtprint(&fmt, "flags %s\n", buf); + } + } + if(part->hdr){ + if(part->hdr->digest) + fmtprint(&fmt, "digest %s\n", part->hdr->digest); + if(part->hdr->from) + fmtprint(&fmt, "from %s\n", part->hdr->from); + if(part->hdr->to) + fmtprint(&fmt, "to %s\n", part->hdr->to); + if(part->hdr->cc) + fmtprint(&fmt, "cc %s\n", part->hdr->cc); + if(part->hdr->replyto) + fmtprint(&fmt, "replyto %s\n", part->hdr->replyto); + if(part->hdr->bcc) + fmtprint(&fmt, "bcc %s\n", part->hdr->bcc); + if(part->hdr->inreplyto) + fmtprint(&fmt, "inreplyto %s\n", part->hdr->inreplyto); + if(part->hdr->date) + fmtprint(&fmt, "date %s\n", part->hdr->date); + if(part->hdr->sender) + fmtprint(&fmt, "sender %s\n", part->hdr->sender); + if(part->hdr->messageid) + fmtprint(&fmt, "messageid %s\n", part->hdr->messageid); + if(part->hdr->subject) + fmtprint(&fmt, "subject %s\n", part->hdr->subject); + } + if(part->type) + fmtprint(&fmt, "type %s\n", part->type); + if(part->lines) + fmtprint(&fmt, "lines %d\n", part->lines); + // fmtprint(&fmt, "disposition %s\n", "" /* disposition */); + // fmtprint(&fmt, "filename %s\n", "" /* filename */); + // fmtprint(&fmt, "digest %s\n", "" /* digest */); + s = fmtstrflush(&fmt); + if(s == nil) + s = estrdup(""); + *freeme = 1; + *pp = s; + return 0; + + case Qheader: + if(part->hdr == nil) + return 0; + fmtstrinit(&fmt); + if(part == msg->part[0]) + fmtprint(&fmt, "Date: %s", ctime(msg->date)); + else + fmtprint(&fmt, "Date: %s\n", part->hdr->date); + addaddrs(&fmt, "To", part->hdr->to); + addaddrs(&fmt, "From", part->hdr->from); + if(part->hdr->from==nil + || (part->hdr->sender && strcmp(part->hdr->sender, part->hdr->from) != 0)) + addaddrs(&fmt, "Sender", part->hdr->sender); + if(part->hdr->from==nil + || (part->hdr->replyto && strcmp(part->hdr->replyto, part->hdr->from) != 0)) + addaddrs(&fmt, "Reply-To", part->hdr->replyto); + addaddrs(&fmt, "Subject", part->hdr->subject); + s = fmtstrflush(&fmt); + if(s == nil) + s = estrdup(""); + *freeme = 1; + *pp = s; + return 0; + + default: + werrstr(Egreg); + return -1; + } +} + +int +filldir(Dir *d, int type, Box *box, Msg *msg, Part *part) +{ + int freeme, len; + char *s; + + memset(d, 0, sizeof *d); + if(box){ + d->atime = box->time; + d->mtime = box->time; + }else{ + d->atime = t0; + d->mtime = t0; + } + d->uid = estrdup9p("upas"); + d->gid = estrdup9p("upas"); + d->muid = estrdup9p("upas"); + d->qid = qid(type, box, msg, part); + + switch(type){ + case Qroot: + case Qbox: + case Qmsg: + d->mode = 0555|DMDIR; + if(box && !(box->flags&FlagNoInferiors)) + d->mode = 0775|DMDIR; + break; + case Qctl: + case Qboxctl: + d->mode = 0222; + break; + case Qsearch: + d->mode = 0666; + break; + + case Qflags: + d->mode = 0666; + goto msgfile; + default: + d->mode = 0444; + msgfile: + if(filedata(type, box, msg, part, &s, &len, &freeme, 0, ZQ) >= 0){ + if(s){ + if(len == -1) + d->length = strlen(s); + else + d->length = len; + if(freeme) + free(s); + } + }else if(type == Qraw && msg && part == msg->part[0]) + d->length = msg->size; + break; + } + + switch(type){ + case Qroot: + d->name = estrdup9p("/"); + break; + case Qbox: + if(box == nil){ + werrstr(Enobox); + return -1; + } + d->name = estrdup9p(box->elem); + break; + case Qmsg: + if(msg == nil){ + werrstr(Enomsg); + return -1; + } + if(part == nil || part == msg->part[0]) + d->name = esmprint("%d", msg->id); + else + d->name = esmprint("%d", part->pix+1); + break; + case Qctl: + case Qboxctl: + d->name = estrdup9p("ctl"); + break; + case Qsearch: + d->name = estrdup9p("search"); + break; + default: + d->name = estrdup9p(nameoftype(type)); + break; + } + return 0; +} + +static void +fsstat(Req *r) +{ + int type; + Box *box; + Msg *msg; + Part *part; + + type = parseqid(r->fid->qid, &box, &msg, &part); + if(filldir(&r->d, type, box, msg, part) < 0) + responderror(r); + else + respond(r, nil); +} + +int +rootgen(int i, Dir *d, void *aux) +{ + USED(aux); + + if(i == 0) + return filldir(d, Qctl, nil, nil, nil); + i--; + if(i < rootbox->nsub) + return filldir(d, Qbox, rootbox->sub[i], nil, nil); + return -1; +} + +int +boxgen(int i, Dir *d, void *aux) +{ + Box *box; + + box = aux; +if(i==0) fprint(2, "boxgen %s %d nsub=%d nmsg=%d\n", box->name, i, box->nsub, box->nmsg); + if(i == 0) + return filldir(d, Qboxctl, box, nil, nil); + i--; + if(i == 0) + return filldir(d, Qsearch, box, nil, nil); + if(i < box->nsub) + return filldir(d, Qbox, box->sub[i], nil, nil); + i -= box->nsub; + if(i < box->nmsg) + return filldir(d, Qmsg, box, box->msg[i], nil); + return -1; +} + +static int msgdir[] = { + Qtype, + Qbody, Qbcc, Qcc, Qdate, Qflags, Qfrom, Qheader, Qinfo, + Qinreplyto, Qlines, Qmimeheader, Qmessageid, + Qraw, Qrawunix, Qrawbody, Qrawheader, + Qreplyto, Qsender, Qsubject, Qto, + Qunixdate, Qunixheader +}; +static int mimemsgdir[] = { + Qtype, + Qbody, Qbcc, Qcc, Qdate, Qfrom, Qheader, Qinfo, + Qinreplyto, Qlines, Qmimeheader, Qmessageid, + Qraw, Qrawunix, Qrawbody, Qrawheader, + Qreplyto, Qsender, Qsubject, Qto, +}; +static int mimedir[] = { + Qtype, + Qbody, + Qmimeheader, + Qraw, +}; + +int +msggen(int i, Dir *d, void *aux) +{ + Box *box; + Msg *msg; + Part *part; + + part = aux; + msg = part->msg; + box = msg->box; + if(part->ix == 0){ + if(i < nelem(msgdir)) + return filldir(d, msgdir[i], box, msg, part); + i -= nelem(msgdir); + }else if(part->type && strcmp(part->type, "message/rfc822") == 0){ + if(i < nelem(mimemsgdir)) + return filldir(d, mimemsgdir[i], box, msg, part); + i -= nelem(mimemsgdir); + }else{ + if(i < nelem(mimedir)) + return filldir(d, mimedir[i], box, msg, part); + i -= nelem(mimedir); + } + if(i < part->nsub) + return filldir(d, Qmsg, box, msg, part->sub[i]); + return -1; +} + +enum +{ + CMhangup, +}; +static Cmdtab ctltab[] = +{ + CMhangup, "hangup", 2, +}; + +enum +{ + CMdelete, + CMrefresh, + CMreplied, + CMread, + CMsave, + CMjunk, + CMnonjunk, +}; +static Cmdtab boxctltab[] = +{ + CMdelete, "delete", 0, + CMrefresh, "refresh", 1, + CMreplied, "replied", 0, + CMread, "read", 0, + CMsave, "save", 0, + CMjunk, "junk", 0, + CMnonjunk, "nonjunk", 0, +}; + +static void +fsread(Req *r) +{ + char *s; + int type, len, freeme; + Box *box; + Msg *msg; + Part *part; + + switch(type = parseqid(r->fid->qid, &box, &msg, &part)){ + case Qroot: + dirread9p(r, rootgen, nil); + respond(r, nil); + return; + + case Qbox: + if(box == nil){ + respond(r, Eboxgone); + return; + } + if(box->nmsg == 0) + imapcheckbox(imap, box); + parseqid(r->fid->qid, &box, &msg, &part); + if(box == nil){ + respond(r, Eboxgone); + return; + } + dirread9p(r, boxgen, box); + respond(r, nil); + return; + + case Qmsg: + if(msg == nil || part == nil){ + respond(r, Emsggone); + return; + } + dirread9p(r, msggen, part); + respond(r, nil); + return; + + case Qctl: + case Qboxctl: + respond(r, Egreg); + return; + + case Qsearch: + readstr(r, r->fid->aux); + respond(r, nil); + return; + + default: + if(filedata(type, box, msg, part, &s, &len, &freeme, 1, r->fid->qid) < 0){ + responderror(r); + return; + } + if(s && len == -1) + len = strlen(s); + readbuf(r, s, len); + if(freeme) + free(s); + respond(r, nil); + return; + } +} + +int +mkmsglist(Box *box, char **f, int nf, Msg ***mm) +{ + int i, nm; + Msg **m; + + m = emalloc(nf*sizeof m[0]); + nm = 0; + for(i=0; iofcall.count = r->ifcall.count; + switch(type = parseqid(r->fid->qid, &box, &msg, &part)){ + default: + respond(r, Egreg); + break; + + case Qctl: + cb = parsecmd(r->ifcall.data, r->ifcall.count); + if((ct = lookupcmd(cb, ctltab, nelem(ctltab))) == nil){ + respondcmderror(r, cb, "unknown message"); + free(cb); + return; + } + r->ofcall.count = r->ifcall.count; + switch(ct->index){ + case CMhangup: + imaphangup(imap, atoi(cb->f[1])); + respond(r, nil); + break; + default: + respond(r, Egreg); + break; + } + free(cb); + return; + + case Qboxctl: + cb = parsecmd(r->ifcall.data, r->ifcall.count); + if((ct = lookupcmd(cb, boxctltab, nelem(boxctltab))) == nil){ + respondcmderror(r, cb, "bad message"); + free(cb); + return; + } + r->ofcall.count = r->ifcall.count; + switch(ct->index){ + case CMsave: + if(cb->nf <= 2){ + respondcmderror(r, cb, Ebadctl); + break; + } + nm = mkmsglist(box, cb->f+2, cb->nf-2, &m); + if(nm != cb->nf-2){ + // free(m); + respond(r, Enomsg); + break; + } + if(nm > 0 && imapcopylist(imap, cb->f[1], m, nm) < 0) + responderror(r); + else + respond(r, nil); + free(m); + break; + + case CMjunk: + flag = FlagJunk; + goto flagit; + case CMnonjunk: + flag = FlagNonJunk; + goto flagit; + case CMreplied: + flag = FlagReplied; + goto flagit; + case CMread: + flag = FlagSeen; + flagit: + if(cb->nf <= 1){ + respondcmderror(r, cb, Ebadctl); + break; + } + nm = mkmsglist(box, cb->f+1, cb->nf-1, &m); + if(nm != cb->nf-1){ + free(m); + respond(r, Enomsg); + break; + } + if(nm > 0 && imapflaglist(imap, +1, flag, m, nm) < 0) + responderror(r); + else + respond(r, nil); + free(m); + break; + + case CMrefresh: + imapcheckbox(imap, box); + respond(r, nil); + break; + + case CMdelete: + if(cb->nf <= 1){ + respondcmderror(r, cb, Ebadctl); + break; + } + nm = mkmsglist(box, cb->f+1, cb->nf-1, &m); + if(nm > 0 && imapremovelist(imap, m, nm) < 0) + responderror(r); + else + respond(r, nil); + free(m); + break; + + default: + respond(r, Egreg); + break; + } + free(cb); + return; + + case Qflags: + if(msg == nil){ + respond(r, Enomsg); + return; + } + cb = parsecmd(r->ifcall.data, r->ifcall.count); + flag = 0; + unflag = 0; + flagset = 0; + reset = 0; + for(i=0; inf; i++){ + f = 0; + c = cb->f[i][0]; + if(c == '+' || c == '-') + cb->f[i]++; + for(j=0; jf[i]) == 0){ + f = flagtab[j].flag; + break; + } + } + if(f == 0){ + respondcmderror(r, cb, "unknown flag %s", cb->f[i]); + free(cb); + return; + } + if(c == '+') + flag |= f; + else if(c == '-') + unflag |= f; + else + flagset |= f; + } + free(cb); + if((flagset!=0)+(unflag!=0)+(flag!=0) != 1){ + respondcmderror(r, cb, Ebadctl); + return; + } + if(flag) + i = 1; + else if(unflag){ + i = -1; + flag = unflag; + }else{ + i = 0; + flag = flagset; + } + if(imapflaglist(imap, i, flag, &msg, 1) < 0) + responderror(r); + else + respond(r, nil); + return; + + case Qsearch: + if(box == nil){ + respond(r, Eboxgone); + return; + } + fmtstrinit(&fmt); + nm = imapsearchbox(imap, box, r->ifcall.data, &m); + for(i=0; i0) + fmtrune(&fmt, ' '); + fmtprint(&fmt, "%d", m[i]->id); + } + free(r->fid->aux); + r->fid->aux = fmtstrflush(&fmt); + respond(r, nil); + return; + } +} + +static void +fsopen(Req *r) +{ + switch(qtype(r->fid->qid)){ + case Qctl: + case Qboxctl: + if((r->ifcall.mode&~OTRUNC) != OWRITE){ + respond(r, Eperm); + return; + } + respond(r, nil); + return; + + case Qflags: + case Qsearch: + if((r->ifcall.mode&~OTRUNC) > ORDWR){ + respond(r, Eperm); + return; + } + respond(r, nil); + return; + + default: + if(r->ifcall.mode != OREAD){ + respond(r, Eperm); + return; + } + respond(r, nil); + return; + } +} + +static void +fsflush(Req *r) +{ + /* + * We only handle reads and writes outside the main loop, + * so we must be flushing one of those. In both cases it's + * okay to just ignore the results of the request, whenever + * they're ready. + */ + incref(&r->oldreq->ref); + respond(r->oldreq, "interrupted"); + respond(r, nil); +} + +static void +fsthread(void *v) +{ + Req *r; + + r = v; + switch(r->ifcall.type){ + case Tread: + fsread(r); + break; + case Twrite: + fswrite(r); + break; + } +} + +static void +fsrecv(void *v) +{ + Req *r; + + while((r = recvp(fsreqchan)) != nil){ + switch(r->ifcall.type){ + case Tattach: + fsattach(r); + break; + case Tflush: + fsflush(r); + break; + case Topen: + fsopen(r); + break; + case Twalk: + fswalk(r); + break; + case Tstat: + fsstat(r); + break; + default: + threadcreate(fsthread, r, STACK); + break; + } + } +} + +static void +fssend(Req *r) +{ + sendp(fsreqchan, r); +} + +static void +fsdestroyfid(Fid *f) +{ + free(f->aux); +} + +void +fsinit0(void) /* bad planning - clash with lib9pclient */ +{ + t0 = time(0); + + fs.attach = fssend; + fs.flush = fssend; + fs.open = fssend; + fs.walk = fssend; + fs.read = fssend; + fs.write = fssend; + fs.stat = fssend; + fs.destroyfid = fsdestroyfid; + + rootqid = qid(Qroot, nil, nil, nil); + + fsreqchan = chancreate(sizeof(void*), 0); + mailthread(fsrecv, nil); +} + -- cgit v1.2.3