4 * Serve the bulk of requests out of memory, so they can
5 * be in the main loop (will never see their flushes).
6 * Some requests do block and they get handled in
7 * separate threads. They're all okay to give up on
8 * early, though, so we just respond saying interrupted
9 * and then when they finish, silently discard the request.
40 /* message header - same order as struct Hdr */
52 /* part data - same order as stuct Part */
56 Qencoding, /* only here temporarily! */
64 /* part numbers - same order as struct Part */
68 /* other message files */
80 Qnfile = Qunixheader+1-Qfile0
83 static char Egreg[] = "gone postal";
84 static char Enobox[] = "no such mailbox";
85 static char Enomsg[] = "no such message";
86 static char Eboxgone[] = "mailbox not available";
87 static char Emsggone[] = "message not available";
88 static char Eperm[] = "permission denied";
89 static char Ebadctl[] = "bad control message";
101 rerrstr(e, sizeof e);
114 return (q.path>>40)&0xFFFF;
120 return ((q.path>>32)&0xFF000000) | ((q.path>>16)&0xFFFFFF);
126 return ((q.path>>6)&0x3FF);
130 qid(int ctl, Box *box, Msg *msg, Part *part)
135 if(ctl == Qroot || ctl == Qbox || ctl == Qmsg)
137 q.path = (vlong)((msg ? msg->id : 0)&0xFF000000)<<32;
138 q.path |= (vlong)((msg ? msg->id : 0)&0xFFFFFF)<<16;
139 q.path |= (vlong)((box ? box->id : 0)&0xFFFF)<<40;
140 q.path |= ((part ? part->ix : 0)&0x3FF)<<6;
142 q.vers = box ? box->validity : 0;
147 parseqid(Qid q, Box **box, Msg **msg, Part **part)
152 *box = boxbyid(qboxid(q));
154 *msg = msgbyid(*box, qmsgid(q));
157 *part = partbyid(*msg, qpartid(q));
169 Qfilename, "filename",
174 Qinreplyto, "inreplyto",
176 Qmimeheader, "mimeheader",
177 Qmessageid, "messageid",
181 Qrawheader, "rawheader",
187 Qunixdate, "unixdate",
188 Qunixheader, "unixheader",
191 Qencoding, "encoding",
200 for(i=0; i<nelem(typenames); i++)
201 if(typenames[i].type == t)
202 return typenames[i].name;
207 typeofname(char *name)
211 for(i=0; i<nelem(typenames); i++)
212 if(strcmp(typenames[i].name, name) == 0)
213 return typenames[i].type;
220 r->fid->qid = rootqid;
221 r->ofcall.qid = rootqid;
230 if(*s < '1' || *s > '9')
232 n = strtol(s, &s, 10);
239 fswalk1(Fid *fid, char *name, void *arg)
248 switch(type = parseqid(fid->qid, &box, &msg, &part)){
250 if(strcmp(name, "..") == 0)
252 if(strcmp(name, "ctl") == 0){
253 fid->qid = qid(Qctl, nil, nil, nil);
256 if((box = boxbyname(name)) != nil){
257 fid->qid = qid(Qbox, box, nil, nil);
264 * Would be nice if .. could work even if the box is gone,
265 * but we don't know how deep the directory was.
269 if(strcmp(name, "..") == 0){
270 if((box = box->parent) == nil){
274 fid->qid = qid(Qbox, box, nil, nil);
277 if(strcmp(name, "ctl") == 0){
278 fid->qid = qid(Qboxctl, box, nil, nil);
281 if(strcmp(name, "search") == 0){
282 fid->qid = qid(Qsearch, box, nil, nil);
285 if((b = subbox(box, name)) != nil){
286 fid->qid = qid(Qbox, b, nil, nil);
289 if((a = isnumber(name)) != 0){
290 if((msg = msgbyid(box, a)) == nil){
293 fid->qid = qid(Qmsg, box, msg, nil);
299 if(strcmp(name, "..") == 0){
300 if(part == msg->part[0]){
301 fid->qid = qid(Qbox, box, nil, nil);
304 fid->qid = qid(Qmsg, box, msg, part->parent);
307 if((type = typeofname(name)) > 0){
308 /* XXX - should check that type makes sense (see msggen) */
309 fid->qid = qid(type, box, msg, part);
312 if((a = isnumber(name)) != 0){
313 if((p = subpart(part, a-1)) != nil){
314 fid->qid = qid(Qmsg, box, msg, p);
326 walkandclone(r, fswalk1, nil, nil);
334 FlagNonJunk, "notjunk",
335 FlagReplied, "replied",
336 FlagFlagged, "flagged",
337 /* FlagDeleted, "deleted", */
343 addaddrs(Fmt *fmt, char *prefix, char *addrs)
351 addrs = estrdup(addrs);
354 for(p=addrs; *p; p++){
355 if(*p == ' ' && !inquote)
361 f = emalloc(nf*sizeof f[0]);
362 nf = tokenize(addrs, f, nf);
363 fmtprint(fmt, "%s:", prefix);
365 for(i=0; i+1<nf; i+=2){
367 fmtprint(fmt, "%s%s <%s>", sep, f[i], f[i+1]);
369 fmtprint(fmt, "%s%s", sep, f[i+1]);
377 mkbody(Part *p, Qid q)
382 if(p->msg->part[0] == p)
390 if(p->encoding && cistrcmp(p->encoding, "quoted-printable") == 0)
391 t = decode(QuotedPrintable, t, &len);
392 else if(p->encoding && cistrcmp(p->encoding, "base64") == 0)
393 t = decode(Base64, t, &len);
398 t = tcs(p->charset, t);
403 p->nbody = strlen(t);
411 filedata(int type, Box *box, Msg *msg, Part *part, char **pp, int *len, int *freeme, int force, Qid q)
413 int i, inquote, n, t;
415 static char buf[256];
423 if(msg == nil || part == nil){
438 if(part->hdr == nil){
442 *pp = ((char**)&part->hdr->date)[type-Qdate];
446 strcpy(buf, ctime(msg->date));
451 if(part->hdr == nil){
455 from = part->hdr->from;
460 for(; *from; from++){
463 if(!inquote && *from == ' '){
469 from = part->hdr->from;
471 n = snprint(buf, sizeof buf, "From %s %s", from, ctime(msg->date));
472 if(n+1 < sizeof buf){
477 fmtprint(&fmt, "From %s %s", from, ctime(msg->date));
478 s = fmtstrflush(&fmt);
496 *pp = ((char**)&part->type)[type-Qtype];
497 if(*pp == nil && force){
500 imapfetchraw(imap, part);
503 imapfetchrawheader(imap, part);
506 imapfetchrawbody(imap, part);
509 imapfetchrawmime(imap, part);
515 * We ran fetchsomething, which might have changed
516 * the mailbox contents. Msg might even be gone.
518 t = parseqid(q, &box, &msg, &part);
519 if(t != type || msg == nil || part == nil)
521 *pp = ((char**)&part->type)[type-Qtype];
534 if(part->rawbody == nil){
535 if(part->msg->part[0] == part)
536 imapfetchrawbody(imap, part);
538 imapfetchraw(imap, part);
539 t = parseqid(q, &box, &msg, &part);
540 if(t != type || msg == nil || part == nil)
551 n = ((uint*)&part->size)[type-Qsize];
552 snprint(buf, sizeof buf, "%d", n);
559 for(i=0; i<nelem(flagtab); i++){
560 if(msg->flags&flagtab[i].flag){
563 strcpy(s, flagtab[i].name);
572 if(part == msg->part[0]){
574 fmtprint(&fmt, "unixdate %lud %s", msg->date, ctime(msg->date));
576 filedata(Qflags, box, msg, part, pp, nil, freeme, 0, ZQ);
577 fmtprint(&fmt, "flags %s\n", buf);
581 if(part->hdr->digest)
582 fmtprint(&fmt, "digest %s\n", part->hdr->digest);
584 fmtprint(&fmt, "from %s\n", part->hdr->from);
586 fmtprint(&fmt, "to %s\n", part->hdr->to);
588 fmtprint(&fmt, "cc %s\n", part->hdr->cc);
589 if(part->hdr->replyto)
590 fmtprint(&fmt, "replyto %s\n", part->hdr->replyto);
592 fmtprint(&fmt, "bcc %s\n", part->hdr->bcc);
593 if(part->hdr->inreplyto)
594 fmtprint(&fmt, "inreplyto %s\n", part->hdr->inreplyto);
596 fmtprint(&fmt, "date %s\n", part->hdr->date);
597 if(part->hdr->sender)
598 fmtprint(&fmt, "sender %s\n", part->hdr->sender);
599 if(part->hdr->messageid)
600 fmtprint(&fmt, "messageid %s\n", part->hdr->messageid);
601 if(part->hdr->subject)
602 fmtprint(&fmt, "subject %s\n", part->hdr->subject);
605 fmtprint(&fmt, "type %s\n", part->type);
607 fmtprint(&fmt, "lines %d\n", part->lines);
609 fmtprint(&fmt, "filename %s\n", part->filename);
610 s = fmtstrflush(&fmt);
621 if(part == msg->part[0])
622 fmtprint(&fmt, "Date: %s", ctime(msg->date));
624 fmtprint(&fmt, "Date: %s\n", part->hdr->date);
625 addaddrs(&fmt, "To", part->hdr->to);
626 addaddrs(&fmt, "From", part->hdr->from);
627 if(part->hdr->from==nil
628 || (part->hdr->sender && strcmp(part->hdr->sender, part->hdr->from) != 0))
629 addaddrs(&fmt, "Sender", part->hdr->sender);
630 if(part->hdr->from==nil
631 || (part->hdr->replyto && strcmp(part->hdr->replyto, part->hdr->from) != 0))
632 addaddrs(&fmt, "Reply-To", part->hdr->replyto);
633 fmtprint(&fmt, "Subject: %s\n", part->hdr->subject);
634 s = fmtstrflush(&fmt);
648 filldir(Dir *d, int type, Box *box, Msg *msg, Part *part)
653 memset(d, 0, sizeof *d);
655 d->atime = box->time;
656 d->mtime = box->time;
661 d->uid = estrdup9p("upas");
662 d->gid = estrdup9p("upas");
663 d->muid = estrdup9p("upas");
664 d->qid = qid(type, box, msg, part);
670 d->mode = 0555|DMDIR;
671 if(box && !(box->flags&FlagNoInferiors))
672 d->mode = 0775|DMDIR;
688 if(filedata(type, box, msg, part, &s, &len, &freeme, 0, ZQ) >= 0){
691 d->length = strlen(s);
697 }else if(type == Qraw && msg && part == msg->part[0])
698 d->length = msg->size;
704 d->name = estrdup9p("/");
711 d->name = estrdup9p(box->elem);
718 if(part == nil || part == msg->part[0])
719 d->name = esmprint("%d", msg->id);
721 d->name = esmprint("%d", part->pix+1);
725 d->name = estrdup9p("ctl");
728 d->name = estrdup9p("search");
731 d->name = estrdup9p(nameoftype(type));
745 type = parseqid(r->fid->qid, &box, &msg, &part);
746 if(filldir(&r->d, type, box, msg, part) < 0)
753 rootgen(int i, Dir *d, void *aux)
758 return filldir(d, Qctl, nil, nil, nil);
760 if(i < rootbox->nsub)
761 return filldir(d, Qbox, rootbox->sub[i], nil, nil);
766 boxgen(int i, Dir *d, void *aux)
772 return filldir(d, Qboxctl, box, nil, nil);
775 return filldir(d, Qsearch, box, nil, nil);
778 return filldir(d, Qbox, box->sub[i], nil, nil);
781 return filldir(d, Qmsg, box, box->msg[i], nil);
785 static int msgdir[] = {
787 Qbody, Qbcc, Qcc, Qdate, Qflags, Qfrom, Qheader, Qinfo,
788 Qinreplyto, Qlines, Qmimeheader, Qmessageid,
789 Qraw, Qrawunix, Qrawbody, Qrawheader,
790 Qreplyto, Qsender, Qsubject, Qto,
791 Qunixdate, Qunixheader
793 static int mimemsgdir[] = {
795 Qbody, Qbcc, Qcc, Qdate, Qfrom, Qheader, Qinfo,
796 Qinreplyto, Qlines, Qmimeheader, Qmessageid,
797 Qraw, Qrawunix, Qrawbody, Qrawheader,
798 Qreplyto, Qsender, Qsubject, Qto
800 static int mimedir[] = {
810 msggen(int i, Dir *d, void *aux)
820 if(i < nelem(msgdir))
821 return filldir(d, msgdir[i], box, msg, part);
823 }else if(part->type && strcmp(part->type, "message/rfc822") == 0){
824 if(i < nelem(mimemsgdir))
825 return filldir(d, mimemsgdir[i], box, msg, part);
826 i -= nelem(mimemsgdir);
828 if(i < nelem(mimedir))
829 return filldir(d, mimedir[i], box, msg, part);
833 return filldir(d, Qmsg, box, msg, part->sub[i]);
841 static Cmdtab ctltab[] =
843 CMhangup, "hangup", 2
856 static Cmdtab boxctltab[] =
858 CMdelete, "delete", 0,
859 CMrefresh, "refresh", 1,
860 CMreplied, "replied", 0,
864 CMnonjunk, "nonjunk", 0
871 int type, len, freeme;
876 switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
878 dirread9p(r, rootgen, nil);
884 respond(r, Eboxgone);
888 imapcheckbox(imap, box);
889 parseqid(r->fid->qid, &box, &msg, &part);
891 respond(r, Eboxgone);
894 dirread9p(r, boxgen, box);
899 if(msg == nil || part == nil){
900 respond(r, Emsggone);
903 dirread9p(r, msggen, part);
913 readstr(r, r->fid->aux);
918 if(filedata(type, box, msg, part, &s, &len, &freeme, 1, r->fid->qid) < 0){
933 mkmsglist(Box *box, char **f, int nf, Msg ***mm)
938 m = emalloc(nf*sizeof m[0]);
941 if((m[nm] = msgbyid(box, atoi(f[i]))) != nil)
950 int i, j, c, type, flag, unflag, flagset, f, reset;
960 r->ofcall.count = r->ifcall.count;
961 switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
967 cb = parsecmd(r->ifcall.data, r->ifcall.count);
968 if((ct = lookupcmd(cb, ctltab, nelem(ctltab))) == nil){
969 respondcmderror(r, cb, "unknown message");
973 r->ofcall.count = r->ifcall.count;
976 imaphangup(imap, atoi(cb->f[1]));
987 cb = parsecmd(r->ifcall.data, r->ifcall.count);
988 if((ct = lookupcmd(cb, boxctltab, nelem(boxctltab))) == nil){
989 respondcmderror(r, cb, "bad message");
993 r->ofcall.count = r->ifcall.count;
997 respondcmderror(r, cb, Ebadctl);
1000 nm = mkmsglist(box, cb->f+2, cb->nf-2, &m);
1006 if(nm > 0 && imapcopylist(imap, cb->f[1], m, nm) < 0)
1026 respondcmderror(r, cb, Ebadctl);
1029 nm = mkmsglist(box, cb->f+1, cb->nf-1, &m);
1035 if(nm > 0 && imapflaglist(imap, +1, flag, m, nm) < 0)
1043 imapcheckbox(imap, box);
1049 respondcmderror(r, cb, Ebadctl);
1052 nm = mkmsglist(box, cb->f+1, cb->nf-1, &m);
1053 if(nm > 0 && imapremovelist(imap, m, nm) < 0)
1072 cb = parsecmd(r->ifcall.data, r->ifcall.count);
1077 for(i=0; i<cb->nf; i++){
1080 if(c == '+' || c == '-')
1082 for(j=0; j<nelem(flagtab); j++){
1083 if(strcmp(flagtab[j].name, cb->f[i]) == 0){
1084 f = flagtab[j].flag;
1089 respondcmderror(r, cb, "unknown flag %s", cb->f[i]);
1101 if((flagset!=0)+(unflag!=0)+(flag!=0) != 1){
1102 respondcmderror(r, cb, Ebadctl);
1114 if(imapflaglist(imap, i, flag, &msg, 1) < 0)
1122 respond(r, Eboxgone);
1126 nm = imapsearchbox(imap, box, r->ifcall.data, &m);
1127 for(i=0; i<nm; i++){
1130 fmtprint(&fmt, "%d", m[i]->id);
1133 r->fid->aux = fmtstrflush(&fmt);
1142 switch(qtype(r->fid->qid)){
1145 if((r->ifcall.mode&~OTRUNC) != OWRITE){
1154 if((r->ifcall.mode&~OTRUNC) > ORDWR){
1162 if(r->ifcall.mode != OREAD){
1175 * We only handle reads and writes outside the main loop,
1176 * so we must be flushing one of those. In both cases it's
1177 * okay to just ignore the results of the request, whenever
1180 incref(&r->oldreq->ref);
1181 respond(r->oldreq, "interrupted");
1191 switch(r->ifcall.type){
1206 while((r = recvp(fsreqchan)) != nil){
1207 switch(r->ifcall.type){
1224 threadcreate(fsthread, r, STACK);
1233 sendp(fsreqchan, r);
1237 fsdestroyfid(Fid *f)
1243 fsinit0(void) /* bad planning - clash with lib9pclient */
1254 fs.destroyfid = fsdestroyfid;
1256 rootqid = qid(Qroot, nil, nil, nil);
1258 fsreqchan = chancreate(sizeof(void*), 0);
1259 mailthread(fsrecv, nil);