Blob


1 /*
2 * Mail file system.
3 *
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.
11 TO DO:
13 decode subject, etc.
14 decode body
16 digest
17 disposition
18 filename
20 ctl messages
22 fetch mail on demand
24 */
26 #include "a.h"
28 enum
29 {
30 /* directories */
31 Qroot,
32 Qbox,
33 Qmsg,
35 /* control files */
36 Qctl,
37 Qboxctl,
38 Qsearch,
40 /* message header - same order as struct Hdr */
41 Qdate,
42 Qsubject,
43 Qfrom,
44 Qsender,
45 Qreplyto,
46 Qto,
47 Qcc,
48 Qbcc,
49 Qinreplyto,
50 Qmessageid,
52 /* part data - same order as stuct Part */
53 Qtype,
54 Qidstr,
55 Qdesc,
56 Qencoding, /* only here temporarily! */
57 Qcharset,
58 Qfilename,
59 Qraw,
60 Qrawheader,
61 Qrawbody,
62 Qmimeheader,
64 /* part numbers - same order as struct Part */
65 Qsize,
66 Qlines,
68 /* other message files */
69 Qbody,
70 Qheader,
71 Qdigest,
72 Qdisposition,
73 Qflags,
74 Qinfo,
75 Qrawunix,
76 Qunixdate,
77 Qunixheader,
79 Qfile0 = Qbody,
80 Qnfile = Qunixheader+1-Qfile0
81 };
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";
91 Channel *fsreqchan;
92 Srv fs;
93 Qid rootqid;
94 ulong t0;
96 #ifdef PLAN9PORT
97 void
98 responderror(Req *r)
99 {
100 char e[ERRMAX];
102 rerrstr(e, sizeof e);
103 respond(r, e);
105 #endif
107 int
108 qtype(Qid q)
110 return q.path&0x3F;
113 int
114 qboxid(Qid q)
116 return (q.path>>40)&0xFFFF;
119 int
120 qmsgid(Qid q)
122 return ((q.path>>32)&0xFF000000) | ((q.path>>16)&0xFFFFFF);
125 int
126 qpartid(Qid q)
128 return ((q.path>>6)&0x3FF);
131 Qid
132 qid(int ctl, Box *box, Msg *msg, Part *part)
134 Qid q;
136 q.type = 0;
137 if(ctl == Qroot || ctl == Qbox || ctl == Qmsg)
138 q.type = QTDIR;
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;
143 q.path |= ctl&0x3F;
144 q.vers = box ? box->validity : 0;
145 return q;
148 int
149 parseqid(Qid q, Box **box, Msg **msg, Part **part)
151 *msg = nil;
152 *part = nil;
154 *box = boxbyid(qboxid(q));
155 if(*box){
156 *msg = msgbyid(*box, qmsgid(q));
158 if(*msg)
159 *part = partbyid(*msg, qpartid(q));
160 return qtype(q);
163 static struct {
164 int type;
165 char *name;
166 } typenames[] = {
167 Qbody, "body",
168 Qbcc, "bcc",
169 Qcc, "cc",
170 Qdate, "date",
171 Qfilename, "filename",
172 Qflags, "flags",
173 Qfrom, "from",
174 Qheader, "header",
175 Qinfo, "info",
176 Qinreplyto, "inreplyto",
177 Qlines, "lines",
178 Qmimeheader, "mimeheader",
179 Qmessageid, "messageid",
180 Qraw, "raw",
181 Qrawunix, "rawunix",
182 Qrawbody, "rawbody",
183 Qrawheader, "rawheader",
184 Qreplyto, "replyto",
185 Qsender, "sender",
186 Qsubject, "subject",
187 Qto, "to",
188 Qtype, "type",
189 Qunixdate, "unixdate",
190 Qunixheader, "unixheader",
191 Qidstr, "idstr",
192 Qdesc, "desc",
193 Qencoding, "encoding",
194 Qcharset, "charset"
195 };
197 char*
198 nameoftype(int t)
200 int i;
202 for(i=0; i<nelem(typenames); i++)
203 if(typenames[i].type == t)
204 return typenames[i].name;
205 return "???";
208 int
209 typeofname(char *name)
211 int i;
213 for(i=0; i<nelem(typenames); i++)
214 if(strcmp(typenames[i].name, name) == 0)
215 return typenames[i].type;
216 return 0;
219 static void
220 fsattach(Req *r)
222 r->fid->qid = rootqid;
223 r->ofcall.qid = rootqid;
224 respond(r, nil);
227 static int
228 isnumber(char *s)
230 int n;
232 if(*s < '1' || *s > '9')
233 return 0;
234 n = strtol(s, &s, 10);
235 if(*s != 0)
236 return 0;
237 return n;
240 static char*
241 fswalk1(Fid *fid, char *name, void *arg)
243 int a, type;
244 Box *b, *box;
245 Msg *msg;
246 Part *p, *part;
248 USED(arg);
250 switch(type = parseqid(fid->qid, &box, &msg, &part)){
251 case Qroot:
252 if(strcmp(name, "..") == 0)
253 return nil;
254 if(strcmp(name, "ctl") == 0){
255 fid->qid = qid(Qctl, nil, nil, nil);
256 return nil;
258 if((box = boxbyname(name)) != nil){
259 fid->qid = qid(Qbox, box, nil, nil);
260 return nil;
262 break;
264 case Qbox:
265 /*
266 * Would be nice if .. could work even if the box is gone,
267 * but we don't know how deep the directory was.
268 */
269 if(box == nil)
270 return Eboxgone;
271 if(strcmp(name, "..") == 0){
272 if((box = box->parent) == nil){
273 fid->qid = rootqid;
274 return nil;
276 fid->qid = qid(Qbox, box, nil, nil);
277 return nil;
279 if(strcmp(name, "ctl") == 0){
280 fid->qid = qid(Qboxctl, box, nil, nil);
281 return nil;
283 if(strcmp(name, "search") == 0){
284 fid->qid = qid(Qsearch, box, nil, nil);
285 return nil;
287 if((b = subbox(box, name)) != nil){
288 fid->qid = qid(Qbox, b, nil, nil);
289 return nil;
291 if((a = isnumber(name)) != 0){
292 if((msg = msgbyid(box, a)) == nil){
293 return Enomsg;
295 fid->qid = qid(Qmsg, box, msg, nil);
296 return nil;
298 break;
300 case Qmsg:
301 if(strcmp(name, "..") == 0){
302 if(part == msg->part[0]){
303 fid->qid = qid(Qbox, box, nil, nil);
304 return nil;
306 fid->qid = qid(Qmsg, box, msg, part->parent);
307 return nil;
309 if((type = typeofname(name)) > 0){
310 /* XXX - should check that type makes sense (see msggen) */
311 fid->qid = qid(type, box, msg, part);
312 return nil;
314 if((a = isnumber(name)) != 0){
315 if((p = subpart(part, a-1)) != nil){
316 fid->qid = qid(Qmsg, box, msg, p);
317 return nil;
320 break;
322 return "not found";
325 static void
326 fswalk(Req *r)
328 walkandclone(r, fswalk1, nil, nil);
331 static struct {
332 int flag;
333 char *name;
334 } flagtab[] = {
335 FlagJunk, "junk",
336 FlagNonJunk, "notjunk",
337 FlagReplied, "replied",
338 FlagFlagged, "flagged",
339 /* FlagDeleted, "deleted", */
340 FlagDraft, "draft",
341 FlagSeen, "seen"
342 };
344 static void
345 addaddrs(Fmt *fmt, char *prefix, char *addrs)
347 char **f;
348 int i, nf, inquote;
349 char *p, *sep;
351 if(addrs == nil)
352 return;
353 addrs = estrdup(addrs);
354 nf = 0;
355 inquote = 0;
356 for(p=addrs; *p; p++){
357 if(*p == ' ' && !inquote)
358 nf++;
359 if(*p == '\'')
360 inquote = !inquote;
362 nf += 10;
363 f = emalloc(nf*sizeof f[0]);
364 nf = tokenize(addrs, f, nf);
365 fmtprint(fmt, "%s:", prefix);
366 sep = " ";
367 for(i=0; i+1<nf; i+=2){
368 if(f[i][0])
369 fmtprint(fmt, "%s%s <%s>", sep, f[i], f[i+1]);
370 else
371 fmtprint(fmt, "%s%s", sep, f[i+1]);
372 sep = ", ";
374 fmtprint(fmt, "\n");
375 free(addrs);
378 static void
379 mkbody(Part *p, Qid q)
381 char *t;
382 int len;
384 USED(q);
385 if(p->msg->part[0] == p)
386 t = p->rawbody;
387 else
388 t = p->raw;
389 if(t == nil)
390 return;
392 len = -1;
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);
397 else
398 t = estrdup(t);
400 if(p->charset){
401 t = tcs(p->charset, t);
402 len = -1;
404 p->body = t;
405 if(len == -1)
406 p->nbody = strlen(t);
407 else
408 p->nbody = len;
411 static Qid ZQ;
413 static int
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;
417 char *from, *s;
418 static char buf[256];
419 Fmt fmt;
421 *pp = nil;
422 *freeme = 0;
423 if(len)
424 *len = -1;
426 if(msg == nil || part == nil){
427 werrstr(Emsggone);
428 return -1;
430 switch(type){
431 case Qdate:
432 case Qsubject:
433 case Qfrom:
434 case Qsender:
435 case Qreplyto:
436 case Qto:
437 case Qcc:
438 case Qbcc:
439 case Qinreplyto:
440 case Qmessageid:
441 if(part->hdr == nil){
442 werrstr(Emsggone);
443 return -1;
445 *pp = ((char**)&part->hdr->date)[type-Qdate];
446 return 0;
448 case Qunixdate:
449 strcpy(buf, ctime(msg->date));
450 *pp = buf;
451 return 0;
453 case Qunixheader:
454 if(part->hdr == nil){
455 werrstr(Emsggone);
456 return -1;
458 from = part->hdr->from;
459 if(from == nil)
460 from = "???";
461 else{
462 inquote = 0;
463 for(; *from; from++){
464 if(*from == '\'')
465 inquote = !inquote;
466 if(!inquote && *from == ' '){
467 from++;
468 break;
471 if(*from == 0)
472 from = part->hdr->from;
474 n = snprint(buf, sizeof buf, "From %s %s", from, ctime(msg->date));
475 if(n+1 < sizeof buf){
476 *pp = buf;
477 return 0;
479 fmtstrinit(&fmt);
480 fmtprint(&fmt, "From %s %s", from, ctime(msg->date));
481 s = fmtstrflush(&fmt);
482 if(s){
483 *pp = s;
484 *freeme = 1;
485 }else
486 *pp = buf;
487 return 0;
489 case Qtype:
490 case Qidstr:
491 case Qdesc:
492 case Qencoding:
493 case Qcharset:
494 case Qfilename:
495 case Qraw:
496 case Qrawheader:
497 case Qrawbody:
498 case Qmimeheader:
499 *pp = ((char**)&part->type)[type-Qtype];
500 if(*pp == nil && force){
501 switch(type){
502 case Qraw:
503 imapfetchraw(imap, part);
504 break;
505 case Qrawheader:
506 imapfetchrawheader(imap, part);
507 break;
508 case Qrawbody:
509 imapfetchrawbody(imap, part);
510 break;
511 case Qmimeheader:
512 imapfetchrawmime(imap, part);
513 break;
514 default:
515 return 0;
517 /*
518 * We ran fetchsomething, which might have changed
519 * the mailbox contents. Msg might even be gone.
520 */
521 t = parseqid(q, &box, &msg, &part);
522 if(t != type || msg == nil || part == nil)
523 return 0;
524 *pp = ((char**)&part->type)[type-Qtype];
526 return 0;
528 case Qbody:
529 if(part->body){
530 *pp = part->body;
531 if(len)
532 *len = part->nbody;
533 return 0;
535 if(!force)
536 return 0;
537 if(part->rawbody == nil){
538 if(part->msg->part[0] == part)
539 imapfetchrawbody(imap, part);
540 else
541 imapfetchraw(imap, part);
542 t = parseqid(q, &box, &msg, &part);
543 if(t != type || msg == nil || part == nil)
544 return 0;
546 mkbody(part, q);
547 *pp = part->body;
548 if(len)
549 *len = part->nbody;
550 return 0;
552 case Qsize:
553 case Qlines:
554 n = ((uint*)&part->size)[type-Qsize];
555 snprint(buf, sizeof buf, "%d", n);
556 *pp = buf;
557 return 0;
559 case Qflags:
560 s = buf;
561 *s = 0;
562 for(i=0; i<nelem(flagtab); i++){
563 if(msg->flags&flagtab[i].flag){
564 if(s > buf)
565 *s++ = ' ';
566 strcpy(s, flagtab[i].name);
567 s += strlen(s);
570 *pp = buf;
571 return 0;
573 case Qinfo:
574 fmtstrinit(&fmt);
575 if(part == msg->part[0]){
576 if(msg->date)
577 fmtprint(&fmt, "unixdate %ud %s", msg->date, ctime(msg->date));
578 if(msg->flags){
579 filedata(Qflags, box, msg, part, pp, nil, freeme, 0, ZQ);
580 fmtprint(&fmt, "flags %s\n", buf);
583 if(part->hdr){
584 if(part->hdr->digest)
585 fmtprint(&fmt, "digest %s\n", part->hdr->digest);
586 if(part->hdr->from)
587 fmtprint(&fmt, "from %s\n", part->hdr->from);
588 if(part->hdr->to)
589 fmtprint(&fmt, "to %s\n", part->hdr->to);
590 if(part->hdr->cc)
591 fmtprint(&fmt, "cc %s\n", part->hdr->cc);
592 if(part->hdr->replyto)
593 fmtprint(&fmt, "replyto %s\n", part->hdr->replyto);
594 if(part->hdr->bcc)
595 fmtprint(&fmt, "bcc %s\n", part->hdr->bcc);
596 if(part->hdr->inreplyto)
597 fmtprint(&fmt, "inreplyto %s\n", part->hdr->inreplyto);
598 if(part->hdr->date)
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);
607 if(part->type)
608 fmtprint(&fmt, "type %s\n", part->type);
609 if(part->lines)
610 fmtprint(&fmt, "lines %d\n", part->lines);
611 if(part->filename)
612 fmtprint(&fmt, "filename %s\n", part->filename);
613 s = fmtstrflush(&fmt);
614 if(s == nil)
615 s = estrdup("");
616 *freeme = 1;
617 *pp = s;
618 return 0;
620 case Qheader:
621 if(part->hdr == nil)
622 return 0;
623 fmtstrinit(&fmt);
624 if(part == msg->part[0])
625 fmtprint(&fmt, "Date: %s", ctime(msg->date));
626 else
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);
638 if(s == nil)
639 s = estrdup("");
640 *freeme = 1;
641 *pp = s;
642 return 0;
644 default:
645 werrstr(Egreg);
646 return -1;
650 int
651 filldir(Dir *d, int type, Box *box, Msg *msg, Part *part)
653 int freeme, len;
654 char *s;
656 memset(d, 0, sizeof *d);
657 if(box){
658 d->atime = box->time;
659 d->mtime = box->time;
660 }else{
661 d->atime = t0;
662 d->mtime = t0;
664 d->uid = estrdup9p("upas");
665 d->gid = estrdup9p("upas");
666 d->muid = estrdup9p("upas");
667 d->qid = qid(type, box, msg, part);
669 switch(type){
670 case Qroot:
671 case Qbox:
672 case Qmsg:
673 d->mode = 0555|DMDIR;
674 if(box && !(box->flags&FlagNoInferiors))
675 d->mode = 0775|DMDIR;
676 break;
677 case Qctl:
678 case Qboxctl:
679 d->mode = 0222;
680 break;
681 case Qsearch:
682 d->mode = 0666;
683 break;
685 case Qflags:
686 d->mode = 0666;
687 goto msgfile;
688 default:
689 d->mode = 0444;
690 msgfile:
691 if(filedata(type, box, msg, part, &s, &len, &freeme, 0, ZQ) >= 0){
692 if(s){
693 if(len == -1)
694 d->length = strlen(s);
695 else
696 d->length = len;
697 if(freeme)
698 free(s);
700 }else if(type == Qraw && msg && part == msg->part[0])
701 d->length = msg->size;
702 break;
705 switch(type){
706 case Qroot:
707 d->name = estrdup9p("/");
708 break;
709 case Qbox:
710 if(box == nil){
711 werrstr(Enobox);
712 return -1;
714 d->name = estrdup9p(box->elem);
715 break;
716 case Qmsg:
717 if(msg == nil){
718 werrstr(Enomsg);
719 return -1;
721 if(part == nil || part == msg->part[0])
722 d->name = esmprint("%d", msg->id);
723 else
724 d->name = esmprint("%d", part->pix+1);
725 break;
726 case Qctl:
727 case Qboxctl:
728 d->name = estrdup9p("ctl");
729 break;
730 case Qsearch:
731 d->name = estrdup9p("search");
732 break;
733 default:
734 d->name = estrdup9p(nameoftype(type));
735 break;
737 return 0;
740 static void
741 fsstat(Req *r)
743 int type;
744 Box *box;
745 Msg *msg;
746 Part *part;
748 type = parseqid(r->fid->qid, &box, &msg, &part);
749 if(filldir(&r->d, type, box, msg, part) < 0)
750 responderror(r);
751 else
752 respond(r, nil);
755 int
756 rootgen(int i, Dir *d, void *aux)
758 USED(aux);
760 if(i == 0)
761 return filldir(d, Qctl, nil, nil, nil);
762 i--;
763 if(i < rootbox->nsub)
764 return filldir(d, Qbox, rootbox->sub[i], nil, nil);
765 return -1;
768 int
769 boxgen(int i, Dir *d, void *aux)
771 Box *box;
773 box = aux;
774 if(i == 0)
775 return filldir(d, Qboxctl, box, nil, nil);
776 i--;
777 if(i == 0)
778 return filldir(d, Qsearch, box, nil, nil);
779 i--;
780 if(i < box->nsub)
781 return filldir(d, Qbox, box->sub[i], nil, nil);
782 i -= box->nsub;
783 if(i < box->nmsg)
784 return filldir(d, Qmsg, box, box->msg[i], nil);
785 return -1;
788 static int msgdir[] = {
789 Qtype,
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
795 };
796 static int mimemsgdir[] = {
797 Qtype,
798 Qbody, Qbcc, Qcc, Qdate, Qfrom, Qheader, Qinfo,
799 Qinreplyto, Qlines, Qmimeheader, Qmessageid,
800 Qraw, Qrawunix, Qrawbody, Qrawheader,
801 Qreplyto, Qsender, Qsubject, Qto
802 };
803 static int mimedir[] = {
804 Qtype,
805 Qbody,
806 Qfilename,
807 Qcharset,
808 Qmimeheader,
809 Qraw
810 };
812 int
813 msggen(int i, Dir *d, void *aux)
815 Box *box;
816 Msg *msg;
817 Part *part;
819 part = aux;
820 msg = part->msg;
821 box = msg->box;
822 if(part->ix == 0){
823 if(i < nelem(msgdir))
824 return filldir(d, msgdir[i], box, msg, part);
825 i -= nelem(msgdir);
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);
830 }else{
831 if(i < nelem(mimedir))
832 return filldir(d, mimedir[i], box, msg, part);
833 i -= nelem(mimedir);
835 if(i < part->nsub)
836 return filldir(d, Qmsg, box, msg, part->sub[i]);
837 return -1;
840 enum
842 CMhangup
843 };
844 static Cmdtab ctltab[] =
846 CMhangup, "hangup", 2
847 };
849 enum
851 CMdelete,
852 CMrefresh,
853 CMreplied,
854 CMread,
855 CMsave,
856 CMjunk,
857 CMnonjunk
858 };
859 static Cmdtab boxctltab[] =
861 CMdelete, "delete", 0,
862 CMrefresh, "refresh", 1,
863 CMreplied, "replied", 0,
864 CMread, "read", 0,
865 CMsave, "save", 0,
866 CMjunk, "junk", 0,
867 CMnonjunk, "nonjunk", 0
868 };
870 static void
871 fsread(Req *r)
873 char *s;
874 int type, len, freeme;
875 Box *box;
876 Msg *msg;
877 Part *part;
879 switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
880 case Qroot:
881 dirread9p(r, rootgen, nil);
882 respond(r, nil);
883 return;
885 case Qbox:
886 if(box == nil){
887 respond(r, Eboxgone);
888 return;
890 if(box->nmsg == 0)
891 imapcheckbox(imap, box);
892 parseqid(r->fid->qid, &box, &msg, &part);
893 if(box == nil){
894 respond(r, Eboxgone);
895 return;
897 dirread9p(r, boxgen, box);
898 respond(r, nil);
899 return;
901 case Qmsg:
902 if(msg == nil || part == nil){
903 respond(r, Emsggone);
904 return;
906 dirread9p(r, msggen, part);
907 respond(r, nil);
908 return;
910 case Qctl:
911 case Qboxctl:
912 respond(r, Egreg);
913 return;
915 case Qsearch:
916 readstr(r, r->fid->aux);
917 respond(r, nil);
918 return;
920 default:
921 if(filedata(type, box, msg, part, &s, &len, &freeme, 1, r->fid->qid) < 0){
922 responderror(r);
923 return;
925 if(s && len == -1)
926 len = strlen(s);
927 readbuf(r, s, len);
928 if(freeme)
929 free(s);
930 respond(r, nil);
931 return;
935 int
936 mkmsglist(Box *box, char **f, int nf, Msg ***mm)
938 int i, nm;
939 Msg **m;
941 m = emalloc(nf*sizeof m[0]);
942 nm = 0;
943 for(i=0; i<nf; i++)
944 if((m[nm] = msgbyid(box, atoi(f[i]))) != nil)
945 nm++;
946 *mm = m;
947 return nm;
950 static void
951 fswrite(Req *r)
953 int i, j, c, type, flag, unflag, flagset, f;
954 Box *box;
955 Msg *msg;
956 Part *part;
957 Cmdbuf *cb;
958 Cmdtab *ct;
959 Msg **m;
960 int nm;
961 Fmt fmt;
963 r->ofcall.count = r->ifcall.count;
964 switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
965 default:
966 respond(r, Egreg);
967 break;
969 case Qctl:
970 cb = parsecmd(r->ifcall.data, r->ifcall.count);
971 if((ct = lookupcmd(cb, ctltab, nelem(ctltab))) == nil){
972 respondcmderror(r, cb, "unknown message");
973 free(cb);
974 return;
976 r->ofcall.count = r->ifcall.count;
977 switch(ct->index){
978 case CMhangup:
979 imaphangup(imap, atoi(cb->f[1]));
980 respond(r, nil);
981 break;
982 default:
983 respond(r, Egreg);
984 break;
986 free(cb);
987 return;
989 case Qboxctl:
990 cb = parsecmd(r->ifcall.data, r->ifcall.count);
991 if((ct = lookupcmd(cb, boxctltab, nelem(boxctltab))) == nil){
992 respondcmderror(r, cb, "bad message");
993 free(cb);
994 return;
996 r->ofcall.count = r->ifcall.count;
997 switch(ct->index){
998 case CMsave:
999 if(cb->nf <= 2){
1000 respondcmderror(r, cb, Ebadctl);
1001 break;
1003 nm = mkmsglist(box, cb->f+2, cb->nf-2, &m);
1004 if(nm != cb->nf-2){
1005 /* free(m); */
1006 respond(r, Enomsg);
1007 break;
1009 if(nm > 0 && imapcopylist(imap, cb->f[1], m, nm) < 0)
1010 responderror(r);
1011 else
1012 respond(r, nil);
1013 free(m);
1014 break;
1016 case CMjunk:
1017 flag = FlagJunk;
1018 goto flagit;
1019 case CMnonjunk:
1020 flag = FlagNonJunk;
1021 goto flagit;
1022 case CMreplied:
1023 flag = FlagReplied;
1024 goto flagit;
1025 case CMread:
1026 flag = FlagSeen;
1027 flagit:
1028 if(cb->nf <= 1){
1029 respondcmderror(r, cb, Ebadctl);
1030 break;
1032 nm = mkmsglist(box, cb->f+1, cb->nf-1, &m);
1033 if(nm != cb->nf-1){
1034 free(m);
1035 respond(r, Enomsg);
1036 break;
1038 if(nm > 0 && imapflaglist(imap, +1, flag, m, nm) < 0)
1039 responderror(r);
1040 else
1041 respond(r, nil);
1042 free(m);
1043 break;
1045 case CMrefresh:
1046 imapcheckbox(imap, box);
1047 respond(r, nil);
1048 break;
1050 case CMdelete:
1051 if(cb->nf <= 1){
1052 respondcmderror(r, cb, Ebadctl);
1053 break;
1055 nm = mkmsglist(box, cb->f+1, cb->nf-1, &m);
1056 if(nm > 0 && imapremovelist(imap, m, nm) < 0)
1057 responderror(r);
1058 else
1059 respond(r, nil);
1060 free(m);
1061 break;
1063 default:
1064 respond(r, Egreg);
1065 break;
1067 free(cb);
1068 return;
1070 case Qflags:
1071 if(msg == nil){
1072 respond(r, Enomsg);
1073 return;
1075 cb = parsecmd(r->ifcall.data, r->ifcall.count);
1076 flag = 0;
1077 unflag = 0;
1078 flagset = 0;
1079 for(i=0; i<cb->nf; i++){
1080 f = 0;
1081 c = cb->f[i][0];
1082 if(c == '+' || c == '-')
1083 cb->f[i]++;
1084 for(j=0; j<nelem(flagtab); j++){
1085 if(strcmp(flagtab[j].name, cb->f[i]) == 0){
1086 f = flagtab[j].flag;
1087 break;
1090 if(f == 0){
1091 respondcmderror(r, cb, "unknown flag %s", cb->f[i]);
1092 free(cb);
1093 return;
1095 if(c == '+')
1096 flag |= f;
1097 else if(c == '-')
1098 unflag |= f;
1099 else
1100 flagset |= f;
1102 free(cb);
1103 if((flagset!=0)+(unflag!=0)+(flag!=0) != 1){
1104 respondcmderror(r, cb, Ebadctl);
1105 return;
1107 if(flag)
1108 i = 1;
1109 else if(unflag){
1110 i = -1;
1111 flag = unflag;
1112 }else{
1113 i = 0;
1114 flag = flagset;
1116 if(imapflaglist(imap, i, flag, &msg, 1) < 0)
1117 responderror(r);
1118 else
1119 respond(r, nil);
1120 return;
1122 case Qsearch:
1123 if(box == nil){
1124 respond(r, Eboxgone);
1125 return;
1127 fmtstrinit(&fmt);
1128 nm = imapsearchbox(imap, box, r->ifcall.data, &m);
1129 for(i=0; i<nm; i++){
1130 if(i>0)
1131 fmtrune(&fmt, ' ');
1132 fmtprint(&fmt, "%d", m[i]->id);
1134 free(r->fid->aux);
1135 r->fid->aux = fmtstrflush(&fmt);
1136 respond(r, nil);
1137 return;
1141 static void
1142 fsopen(Req *r)
1144 switch(qtype(r->fid->qid)){
1145 case Qctl:
1146 case Qboxctl:
1147 if((r->ifcall.mode&~OTRUNC) != OWRITE){
1148 respond(r, Eperm);
1149 return;
1151 respond(r, nil);
1152 return;
1154 case Qflags:
1155 case Qsearch:
1156 if((r->ifcall.mode&~OTRUNC) > ORDWR){
1157 respond(r, Eperm);
1158 return;
1160 respond(r, nil);
1161 return;
1163 default:
1164 if(r->ifcall.mode != OREAD){
1165 respond(r, Eperm);
1166 return;
1168 respond(r, nil);
1169 return;
1173 static void
1174 fsflush(Req *r)
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
1180 * they're ready.
1182 incref(&r->oldreq->ref);
1183 respond(r->oldreq, "interrupted");
1184 respond(r, nil);
1187 static void
1188 fsthread(void *v)
1190 Req *r;
1192 r = v;
1193 switch(r->ifcall.type){
1194 case Tread:
1195 fsread(r);
1196 break;
1197 case Twrite:
1198 fswrite(r);
1199 break;
1203 static void
1204 fsrecv(void *v)
1206 Req *r;
1208 USED(v);
1209 while((r = recvp(fsreqchan)) != nil){
1210 switch(r->ifcall.type){
1211 case Tattach:
1212 fsattach(r);
1213 break;
1214 case Tflush:
1215 fsflush(r);
1216 break;
1217 case Topen:
1218 fsopen(r);
1219 break;
1220 case Twalk:
1221 fswalk(r);
1222 break;
1223 case Tstat:
1224 fsstat(r);
1225 break;
1226 default:
1227 threadcreate(fsthread, r, STACK);
1228 break;
1233 static void
1234 fssend(Req *r)
1236 sendp(fsreqchan, r);
1239 static void
1240 fsdestroyfid(Fid *f)
1242 free(f->aux);
1245 void
1246 fsinit0(void) /* bad planning - clash with lib9pclient */
1248 t0 = time(0);
1250 fs.attach = fssend;
1251 fs.flush = fssend;
1252 fs.open = fssend;
1253 fs.walk = fssend;
1254 fs.read = fssend;
1255 fs.write = fssend;
1256 fs.stat = fssend;
1257 fs.destroyfid = fsdestroyfid;
1259 rootqid = qid(Qroot, nil, nil, nil);
1261 fsreqchan = chancreate(sizeof(void*), 0);
1262 mailthread(fsrecv, nil);