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";
102 rerrstr(e, sizeof e);
116 return (q.path>>40)&0xFFFF;
122 return ((q.path>>32)&0xFF000000) | ((q.path>>16)&0xFFFFFF);
128 return ((q.path>>6)&0x3FF);
132 qid(int ctl, Box *box, Msg *msg, Part *part)
137 if(ctl == Qroot || ctl == Qbox || ctl == Qmsg)
139 q.path = (vlong)((msg ? msg->id : 0)&0xFF000000)<<32;
140 q.path |= (vlong)((msg ? msg->id : 0)&0xFFFFFF)<<16;
141 q.path |= (vlong)((box ? box->id : 0)&0xFFFF)<<40;
142 q.path |= ((part ? part->ix : 0)&0x3FF)<<6;
144 q.vers = box ? box->validity : 0;
149 parseqid(Qid q, Box **box, Msg **msg, Part **part)
154 *box = boxbyid(qboxid(q));
156 *msg = msgbyid(*box, qmsgid(q));
159 *part = partbyid(*msg, qpartid(q));
171 Qfilename, "filename",
176 Qinreplyto, "inreplyto",
178 Qmimeheader, "mimeheader",
179 Qmessageid, "messageid",
183 Qrawheader, "rawheader",
189 Qunixdate, "unixdate",
190 Qunixheader, "unixheader",
193 Qencoding, "encoding",
202 for(i=0; i<nelem(typenames); i++)
203 if(typenames[i].type == t)
204 return typenames[i].name;
209 typeofname(char *name)
213 for(i=0; i<nelem(typenames); i++)
214 if(strcmp(typenames[i].name, name) == 0)
215 return typenames[i].type;
222 r->fid->qid = rootqid;
223 r->ofcall.qid = rootqid;
232 if(*s < '1' || *s > '9')
234 n = strtol(s, &s, 10);
241 fswalk1(Fid *fid, char *name, void *arg)
250 switch(type = parseqid(fid->qid, &box, &msg, &part)){
252 if(strcmp(name, "..") == 0)
254 if(strcmp(name, "ctl") == 0){
255 fid->qid = qid(Qctl, nil, nil, nil);
258 if((box = boxbyname(name)) != nil){
259 fid->qid = qid(Qbox, box, nil, nil);
266 * Would be nice if .. could work even if the box is gone,
267 * but we don't know how deep the directory was.
271 if(strcmp(name, "..") == 0){
272 if((box = box->parent) == nil){
276 fid->qid = qid(Qbox, box, nil, nil);
279 if(strcmp(name, "ctl") == 0){
280 fid->qid = qid(Qboxctl, box, nil, nil);
283 if(strcmp(name, "search") == 0){
284 fid->qid = qid(Qsearch, box, nil, nil);
287 if((b = subbox(box, name)) != nil){
288 fid->qid = qid(Qbox, b, nil, nil);
291 if((a = isnumber(name)) != 0){
292 if((msg = msgbyid(box, a)) == nil){
295 fid->qid = qid(Qmsg, box, msg, nil);
301 if(strcmp(name, "..") == 0){
302 if(part == msg->part[0]){
303 fid->qid = qid(Qbox, box, nil, nil);
306 fid->qid = qid(Qmsg, box, msg, part->parent);
309 if((type = typeofname(name)) > 0){
310 /* XXX - should check that type makes sense (see msggen) */
311 fid->qid = qid(type, box, msg, part);
314 if((a = isnumber(name)) != 0){
315 if((p = subpart(part, a-1)) != nil){
316 fid->qid = qid(Qmsg, box, msg, p);
328 walkandclone(r, fswalk1, nil, nil);
336 FlagNonJunk, "notjunk",
337 FlagReplied, "replied",
338 FlagFlagged, "flagged",
339 /* FlagDeleted, "deleted", */
345 addaddrs(Fmt *fmt, char *prefix, char *addrs)
353 addrs = estrdup(addrs);
356 for(p=addrs; *p; p++){
357 if(*p == ' ' && !inquote)
363 f = emalloc(nf*sizeof f[0]);
364 nf = tokenize(addrs, f, nf);
365 fmtprint(fmt, "%s:", prefix);
367 for(i=0; i+1<nf; i+=2){
369 fmtprint(fmt, "%s%s <%s>", sep, f[i], f[i+1]);
371 fmtprint(fmt, "%s%s", sep, f[i+1]);
379 mkbody(Part *p, Qid q)
385 if(p->msg->part[0] == p)
393 if(p->encoding && cistrcmp(p->encoding, "quoted-printable") == 0)
394 t = decode(QuotedPrintable, t, &len);
395 else if(p->encoding && cistrcmp(p->encoding, "base64") == 0)
396 t = decode(Base64, t, &len);
401 t = tcs(p->charset, t);
406 p->nbody = strlen(t);
414 filedata(int type, Box *box, Msg *msg, Part *part, char **pp, int *len, int *freeme, int force, Qid q)
416 int i, inquote, n, t;
418 static char buf[256];
426 if(msg == nil || part == nil){
441 if(part->hdr == nil){
445 *pp = ((char**)&part->hdr->date)[type-Qdate];
449 strcpy(buf, ctime(msg->date));
454 if(part->hdr == nil){
458 from = part->hdr->from;
463 for(; *from; from++){
466 if(!inquote && *from == ' '){
472 from = part->hdr->from;
474 n = snprint(buf, sizeof buf, "From %s %s", from, ctime(msg->date));
475 if(n+1 < sizeof buf){
480 fmtprint(&fmt, "From %s %s", from, ctime(msg->date));
481 s = fmtstrflush(&fmt);
499 *pp = ((char**)&part->type)[type-Qtype];
500 if(*pp == nil && force){
503 imapfetchraw(imap, part);
506 imapfetchrawheader(imap, part);
509 imapfetchrawbody(imap, part);
512 imapfetchrawmime(imap, part);
518 * We ran fetchsomething, which might have changed
519 * the mailbox contents. Msg might even be gone.
521 t = parseqid(q, &box, &msg, &part);
522 if(t != type || msg == nil || part == nil)
524 *pp = ((char**)&part->type)[type-Qtype];
537 if(part->rawbody == nil){
538 if(part->msg->part[0] == part)
539 imapfetchrawbody(imap, part);
541 imapfetchraw(imap, part);
542 t = parseqid(q, &box, &msg, &part);
543 if(t != type || msg == nil || part == nil)
554 n = ((uint*)&part->size)[type-Qsize];
555 snprint(buf, sizeof buf, "%d", n);
562 for(i=0; i<nelem(flagtab); i++){
563 if(msg->flags&flagtab[i].flag){
566 strcpy(s, flagtab[i].name);
575 if(part == msg->part[0]){
577 fmtprint(&fmt, "unixdate %ud %s", msg->date, ctime(msg->date));
579 filedata(Qflags, box, msg, part, pp, nil, freeme, 0, ZQ);
580 fmtprint(&fmt, "flags %s\n", buf);
584 if(part->hdr->digest)
585 fmtprint(&fmt, "digest %s\n", part->hdr->digest);
587 fmtprint(&fmt, "from %s\n", part->hdr->from);
589 fmtprint(&fmt, "to %s\n", part->hdr->to);
591 fmtprint(&fmt, "cc %s\n", part->hdr->cc);
592 if(part->hdr->replyto)
593 fmtprint(&fmt, "replyto %s\n", part->hdr->replyto);
595 fmtprint(&fmt, "bcc %s\n", part->hdr->bcc);
596 if(part->hdr->inreplyto)
597 fmtprint(&fmt, "inreplyto %s\n", part->hdr->inreplyto);
599 fmtprint(&fmt, "date %s\n", part->hdr->date);
600 if(part->hdr->sender)
601 fmtprint(&fmt, "sender %s\n", part->hdr->sender);
602 if(part->hdr->messageid)
603 fmtprint(&fmt, "messageid %s\n", part->hdr->messageid);
604 if(part->hdr->subject)
605 fmtprint(&fmt, "subject %s\n", part->hdr->subject);
608 fmtprint(&fmt, "type %s\n", part->type);
610 fmtprint(&fmt, "lines %d\n", part->lines);
612 fmtprint(&fmt, "filename %s\n", part->filename);
613 s = fmtstrflush(&fmt);
624 if(part == msg->part[0])
625 fmtprint(&fmt, "Date: %s", ctime(msg->date));
627 fmtprint(&fmt, "Date: %s\n", part->hdr->date);
628 addaddrs(&fmt, "To", part->hdr->to);
629 addaddrs(&fmt, "From", part->hdr->from);
630 if(part->hdr->from==nil
631 || (part->hdr->sender && strcmp(part->hdr->sender, part->hdr->from) != 0))
632 addaddrs(&fmt, "Sender", part->hdr->sender);
633 if(part->hdr->from==nil
634 || (part->hdr->replyto && strcmp(part->hdr->replyto, part->hdr->from) != 0))
635 addaddrs(&fmt, "Reply-To", part->hdr->replyto);
636 fmtprint(&fmt, "Subject: %s\n", part->hdr->subject);
637 s = fmtstrflush(&fmt);
651 filldir(Dir *d, int type, Box *box, Msg *msg, Part *part)
656 memset(d, 0, sizeof *d);
658 d->atime = box->time;
659 d->mtime = box->time;
664 d->uid = estrdup9p("upas");
665 d->gid = estrdup9p("upas");
666 d->muid = estrdup9p("upas");
667 d->qid = qid(type, box, msg, part);
673 d->mode = 0555|DMDIR;
674 if(box && !(box->flags&FlagNoInferiors))
675 d->mode = 0775|DMDIR;
691 if(filedata(type, box, msg, part, &s, &len, &freeme, 0, ZQ) >= 0){
694 d->length = strlen(s);
700 }else if(type == Qraw && msg && part == msg->part[0])
701 d->length = msg->size;
707 d->name = estrdup9p("/");
714 d->name = estrdup9p(box->elem);
721 if(part == nil || part == msg->part[0])
722 d->name = esmprint("%d", msg->id);
724 d->name = esmprint("%d", part->pix+1);
728 d->name = estrdup9p("ctl");
731 d->name = estrdup9p("search");
734 d->name = estrdup9p(nameoftype(type));
748 type = parseqid(r->fid->qid, &box, &msg, &part);
749 if(filldir(&r->d, type, box, msg, part) < 0)
756 rootgen(int i, Dir *d, void *aux)
761 return filldir(d, Qctl, nil, nil, nil);
763 if(i < rootbox->nsub)
764 return filldir(d, Qbox, rootbox->sub[i], nil, nil);
769 boxgen(int i, Dir *d, void *aux)
775 return filldir(d, Qboxctl, box, nil, nil);
778 return filldir(d, Qsearch, box, nil, nil);
781 return filldir(d, Qbox, box->sub[i], nil, nil);
784 return filldir(d, Qmsg, box, box->msg[i], nil);
788 static int msgdir[] = {
790 Qbody, Qbcc, Qcc, Qdate, Qflags, Qfrom, Qheader, Qinfo,
791 Qinreplyto, Qlines, Qmimeheader, Qmessageid,
792 Qraw, Qrawunix, Qrawbody, Qrawheader,
793 Qreplyto, Qsender, Qsubject, Qto,
794 Qunixdate, Qunixheader
796 static int mimemsgdir[] = {
798 Qbody, Qbcc, Qcc, Qdate, Qfrom, Qheader, Qinfo,
799 Qinreplyto, Qlines, Qmimeheader, Qmessageid,
800 Qraw, Qrawunix, Qrawbody, Qrawheader,
801 Qreplyto, Qsender, Qsubject, Qto
803 static int mimedir[] = {
813 msggen(int i, Dir *d, void *aux)
823 if(i < nelem(msgdir))
824 return filldir(d, msgdir[i], box, msg, part);
826 }else if(part->type && strcmp(part->type, "message/rfc822") == 0){
827 if(i < nelem(mimemsgdir))
828 return filldir(d, mimemsgdir[i], box, msg, part);
829 i -= nelem(mimemsgdir);
831 if(i < nelem(mimedir))
832 return filldir(d, mimedir[i], box, msg, part);
836 return filldir(d, Qmsg, box, msg, part->sub[i]);
844 static Cmdtab ctltab[] =
846 CMhangup, "hangup", 2
859 static Cmdtab boxctltab[] =
861 CMdelete, "delete", 0,
862 CMrefresh, "refresh", 1,
863 CMreplied, "replied", 0,
867 CMnonjunk, "nonjunk", 0
874 int type, len, freeme;
879 switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
881 dirread9p(r, rootgen, nil);
887 respond(r, Eboxgone);
891 imapcheckbox(imap, box);
892 parseqid(r->fid->qid, &box, &msg, &part);
894 respond(r, Eboxgone);
897 dirread9p(r, boxgen, box);
902 if(msg == nil || part == nil){
903 respond(r, Emsggone);
906 dirread9p(r, msggen, part);
916 readstr(r, r->fid->aux);
921 if(filedata(type, box, msg, part, &s, &len, &freeme, 1, r->fid->qid) < 0){
936 mkmsglist(Box *box, char **f, int nf, Msg ***mm)
941 m = emalloc(nf*sizeof m[0]);
944 if((m[nm] = msgbyid(box, atoi(f[i]))) != nil)
953 int i, j, c, type, flag, unflag, flagset, f;
963 r->ofcall.count = r->ifcall.count;
964 switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
970 cb = parsecmd(r->ifcall.data, r->ifcall.count);
971 if((ct = lookupcmd(cb, ctltab, nelem(ctltab))) == nil){
972 respondcmderror(r, cb, "unknown message");
976 r->ofcall.count = r->ifcall.count;
979 imaphangup(imap, atoi(cb->f[1]));
990 cb = parsecmd(r->ifcall.data, r->ifcall.count);
991 if((ct = lookupcmd(cb, boxctltab, nelem(boxctltab))) == nil){
992 respondcmderror(r, cb, "bad message");
996 r->ofcall.count = r->ifcall.count;
1000 respondcmderror(r, cb, Ebadctl);
1003 nm = mkmsglist(box, cb->f+2, cb->nf-2, &m);
1009 if(nm > 0 && imapcopylist(imap, cb->f[1], m, nm) < 0)
1029 respondcmderror(r, cb, Ebadctl);
1032 nm = mkmsglist(box, cb->f+1, cb->nf-1, &m);
1038 if(nm > 0 && imapflaglist(imap, +1, flag, m, nm) < 0)
1046 imapcheckbox(imap, box);
1052 respondcmderror(r, cb, Ebadctl);
1055 nm = mkmsglist(box, cb->f+1, cb->nf-1, &m);
1056 if(nm > 0 && imapremovelist(imap, m, nm) < 0)
1075 cb = parsecmd(r->ifcall.data, r->ifcall.count);
1079 for(i=0; i<cb->nf; i++){
1082 if(c == '+' || c == '-')
1084 for(j=0; j<nelem(flagtab); j++){
1085 if(strcmp(flagtab[j].name, cb->f[i]) == 0){
1086 f = flagtab[j].flag;
1091 respondcmderror(r, cb, "unknown flag %s", cb->f[i]);
1103 if((flagset!=0)+(unflag!=0)+(flag!=0) != 1){
1104 respondcmderror(r, cb, Ebadctl);
1116 if(imapflaglist(imap, i, flag, &msg, 1) < 0)
1124 respond(r, Eboxgone);
1128 nm = imapsearchbox(imap, box, r->ifcall.data, &m);
1129 for(i=0; i<nm; i++){
1132 fmtprint(&fmt, "%d", m[i]->id);
1135 r->fid->aux = fmtstrflush(&fmt);
1144 switch(qtype(r->fid->qid)){
1147 if((r->ifcall.mode&~OTRUNC) != OWRITE){
1156 if((r->ifcall.mode&~OTRUNC) > ORDWR){
1164 if(r->ifcall.mode != OREAD){
1177 * We only handle reads and writes outside the main loop,
1178 * so we must be flushing one of those. In both cases it's
1179 * okay to just ignore the results of the request, whenever
1182 incref(&r->oldreq->ref);
1183 respond(r->oldreq, "interrupted");
1193 switch(r->ifcall.type){
1209 while((r = recvp(fsreqchan)) != nil){
1210 switch(r->ifcall.type){
1227 threadcreate(fsthread, r, STACK);
1236 sendp(fsreqchan, r);
1240 fsdestroyfid(Fid *f)
1246 fsinit0(void) /* bad planning - clash with lib9pclient */
1257 fs.destroyfid = fsdestroyfid;
1259 rootqid = qid(Qroot, nil, nil, nil);
1261 fsreqchan = chancreate(sizeof(void*), 0);
1262 mailthread(fsrecv, nil);