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 void
97 responderror(Req *r)
98 {
99 char e[ERRMAX];
101 rerrstr(e, sizeof e);
102 respond(r, e);
105 int
106 qtype(Qid q)
108 return q.path&0x3F;
111 int
112 qboxid(Qid q)
114 return (q.path>>40)&0xFFFF;
117 int
118 qmsgid(Qid q)
120 return ((q.path>>32)&0xFF000000) | ((q.path>>16)&0xFFFFFF);
123 int
124 qpartid(Qid q)
126 return ((q.path>>6)&0x3FF);
129 Qid
130 qid(int ctl, Box *box, Msg *msg, Part *part)
132 Qid q;
134 q.type = 0;
135 if(ctl == Qroot || ctl == Qbox || ctl == Qmsg)
136 q.type = QTDIR;
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;
141 q.path |= ctl&0x3F;
142 q.vers = box ? box->validity : 0;
143 return q;
146 int
147 parseqid(Qid q, Box **box, Msg **msg, Part **part)
149 *msg = nil;
150 *part = nil;
152 *box = boxbyid(qboxid(q));
153 if(*box){
154 *msg = msgbyid(*box, qmsgid(q));
156 if(*msg)
157 *part = partbyid(*msg, qpartid(q));
158 return qtype(q);
161 static struct {
162 int type;
163 char *name;
164 } typenames[] = {
165 Qbody, "body",
166 Qbcc, "bcc",
167 Qcc, "cc",
168 Qdate, "date",
169 Qfilename, "filename",
170 Qflags, "flags",
171 Qfrom, "from",
172 Qheader, "header",
173 Qinfo, "info",
174 Qinreplyto, "inreplyto",
175 Qlines, "lines",
176 Qmimeheader, "mimeheader",
177 Qmessageid, "messageid",
178 Qraw, "raw",
179 Qrawunix, "rawunix",
180 Qrawbody, "rawbody",
181 Qrawheader, "rawheader",
182 Qreplyto, "replyto",
183 Qsender, "sender",
184 Qsubject, "subject",
185 Qto, "to",
186 Qtype, "type",
187 Qunixdate, "unixdate",
188 Qunixheader, "unixheader",
189 Qidstr, "idstr",
190 Qdesc, "desc",
191 Qencoding, "encoding",
192 Qcharset, "charset"
193 };
195 char*
196 nameoftype(int t)
198 int i;
200 for(i=0; i<nelem(typenames); i++)
201 if(typenames[i].type == t)
202 return typenames[i].name;
203 return "???";
206 int
207 typeofname(char *name)
209 int i;
211 for(i=0; i<nelem(typenames); i++)
212 if(strcmp(typenames[i].name, name) == 0)
213 return typenames[i].type;
214 return 0;
217 static void
218 fsattach(Req *r)
220 r->fid->qid = rootqid;
221 r->ofcall.qid = rootqid;
222 respond(r, nil);
225 static int
226 isnumber(char *s)
228 int n;
230 if(*s < '1' || *s > '9')
231 return 0;
232 n = strtol(s, &s, 10);
233 if(*s != 0)
234 return 0;
235 return n;
238 static char*
239 fswalk1(Fid *fid, char *name, void *arg)
241 int a, type;
242 Box *b, *box;
243 Msg *msg;
244 Part *p, *part;
246 USED(arg);
248 switch(type = parseqid(fid->qid, &box, &msg, &part)){
249 case Qroot:
250 if(strcmp(name, "..") == 0)
251 return nil;
252 if(strcmp(name, "ctl") == 0){
253 fid->qid = qid(Qctl, nil, nil, nil);
254 return nil;
256 if((box = boxbyname(name)) != nil){
257 fid->qid = qid(Qbox, box, nil, nil);
258 return nil;
260 break;
262 case Qbox:
263 /*
264 * Would be nice if .. could work even if the box is gone,
265 * but we don't know how deep the directory was.
266 */
267 if(box == nil)
268 return Eboxgone;
269 if(strcmp(name, "..") == 0){
270 if((box = box->parent) == nil){
271 fid->qid = rootqid;
272 return nil;
274 fid->qid = qid(Qbox, box, nil, nil);
275 return nil;
277 if(strcmp(name, "ctl") == 0){
278 fid->qid = qid(Qboxctl, box, nil, nil);
279 return nil;
281 if(strcmp(name, "search") == 0){
282 fid->qid = qid(Qsearch, box, nil, nil);
283 return nil;
285 if((b = subbox(box, name)) != nil){
286 fid->qid = qid(Qbox, b, nil, nil);
287 return nil;
289 if((a = isnumber(name)) != 0){
290 if((msg = msgbyid(box, a)) == nil){
291 return Enomsg;
293 fid->qid = qid(Qmsg, box, msg, nil);
294 return nil;
296 break;
298 case Qmsg:
299 if(strcmp(name, "..") == 0){
300 if(part == msg->part[0]){
301 fid->qid = qid(Qbox, box, nil, nil);
302 return nil;
304 fid->qid = qid(Qmsg, box, msg, part->parent);
305 return nil;
307 if((type = typeofname(name)) > 0){
308 /* XXX - should check that type makes sense (see msggen) */
309 fid->qid = qid(type, box, msg, part);
310 return nil;
312 if((a = isnumber(name)) != 0){
313 if((p = subpart(part, a-1)) != nil){
314 fid->qid = qid(Qmsg, box, msg, p);
315 return nil;
318 break;
320 return "not found";
323 static void
324 fswalk(Req *r)
326 walkandclone(r, fswalk1, nil, nil);
329 static struct {
330 int flag;
331 char *name;
332 } flagtab[] = {
333 FlagJunk, "junk",
334 FlagNonJunk, "notjunk",
335 FlagReplied, "replied",
336 FlagFlagged, "flagged",
337 /* FlagDeleted, "deleted", */
338 FlagDraft, "draft",
339 FlagSeen, "seen"
340 };
342 static void
343 addaddrs(Fmt *fmt, char *prefix, char *addrs)
345 char **f;
346 int i, nf, inquote;
347 char *p, *sep;
349 if(addrs == nil)
350 return;
351 addrs = estrdup(addrs);
352 nf = 0;
353 inquote = 0;
354 for(p=addrs; *p; p++){
355 if(*p == ' ' && !inquote)
356 nf++;
357 if(*p == '\'')
358 inquote = !inquote;
360 nf += 10;
361 f = emalloc(nf*sizeof f[0]);
362 nf = tokenize(addrs, f, nf);
363 fmtprint(fmt, "%s:", prefix);
364 sep = " ";
365 for(i=0; i+1<nf; i+=2){
366 if(f[i][0])
367 fmtprint(fmt, "%s%s <%s>", sep, f[i], f[i+1]);
368 else
369 fmtprint(fmt, "%s%s", sep, f[i+1]);
370 sep = ", ";
372 fmtprint(fmt, "\n");
373 free(addrs);
376 static void
377 mkbody(Part *p, Qid q)
379 char *t;
380 int len;
382 if(p->msg->part[0] == p)
383 t = p->rawbody;
384 else
385 t = p->raw;
386 if(t == nil)
387 return;
389 len = -1;
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);
394 else
395 t = estrdup(t);
397 if(p->charset){
398 t = tcs(p->charset, t);
399 len = -1;
401 p->body = t;
402 if(len == -1)
403 p->nbody = strlen(t);
404 else
405 p->nbody = len;
408 static Qid ZQ;
410 static int
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;
414 char *from, *s;
415 static char buf[256];
416 Fmt fmt;
418 *pp = nil;
419 *freeme = 0;
420 if(len)
421 *len = -1;
423 if(msg == nil || part == nil){
424 werrstr(Emsggone);
425 return -1;
427 switch(type){
428 case Qdate:
429 case Qsubject:
430 case Qfrom:
431 case Qsender:
432 case Qreplyto:
433 case Qto:
434 case Qcc:
435 case Qbcc:
436 case Qinreplyto:
437 case Qmessageid:
438 if(part->hdr == nil){
439 werrstr(Emsggone);
440 return -1;
442 *pp = ((char**)&part->hdr->date)[type-Qdate];
443 return 0;
445 case Qunixdate:
446 strcpy(buf, ctime(msg->date));
447 *pp = buf;
448 return 0;
450 case Qunixheader:
451 if(part->hdr == nil){
452 werrstr(Emsggone);
453 return -1;
455 from = part->hdr->from;
456 if(from == nil)
457 from = "???";
458 else{
459 inquote = 0;
460 for(; *from; from++){
461 if(*from == '\'')
462 inquote = !inquote;
463 if(!inquote && *from == ' '){
464 from++;
465 break;
468 if(*from == 0)
469 from = part->hdr->from;
471 n = snprint(buf, sizeof buf, "From %s %s", from, ctime(msg->date));
472 if(n+1 < sizeof buf){
473 *pp = buf;
474 return 0;
476 fmtstrinit(&fmt);
477 fmtprint(&fmt, "From %s %s", from, ctime(msg->date));
478 s = fmtstrflush(&fmt);
479 if(s){
480 *pp = s;
481 *freeme = 1;
482 }else
483 *pp = buf;
484 return 0;
486 case Qtype:
487 case Qidstr:
488 case Qdesc:
489 case Qencoding:
490 case Qcharset:
491 case Qfilename:
492 case Qraw:
493 case Qrawheader:
494 case Qrawbody:
495 case Qmimeheader:
496 *pp = ((char**)&part->type)[type-Qtype];
497 if(*pp == nil && force){
498 switch(type){
499 case Qraw:
500 imapfetchraw(imap, part);
501 break;
502 case Qrawheader:
503 imapfetchrawheader(imap, part);
504 break;
505 case Qrawbody:
506 imapfetchrawbody(imap, part);
507 break;
508 case Qmimeheader:
509 imapfetchrawmime(imap, part);
510 break;
511 default:
512 return 0;
514 /*
515 * We ran fetchsomething, which might have changed
516 * the mailbox contents. Msg might even be gone.
517 */
518 t = parseqid(q, &box, &msg, &part);
519 if(t != type || msg == nil || part == nil)
520 return 0;
521 *pp = ((char**)&part->type)[type-Qtype];
523 return 0;
525 case Qbody:
526 if(part->body){
527 *pp = part->body;
528 if(len)
529 *len = part->nbody;
530 return 0;
532 if(!force)
533 return 0;
534 if(part->rawbody == nil){
535 if(part->msg->part[0] == part)
536 imapfetchrawbody(imap, part);
537 else
538 imapfetchraw(imap, part);
539 t = parseqid(q, &box, &msg, &part);
540 if(t != type || msg == nil || part == nil)
541 return 0;
543 mkbody(part, q);
544 *pp = part->body;
545 if(len)
546 *len = part->nbody;
547 return 0;
549 case Qsize:
550 case Qlines:
551 n = ((uint*)&part->size)[type-Qsize];
552 snprint(buf, sizeof buf, "%d", n);
553 *pp = buf;
554 return 0;
556 case Qflags:
557 s = buf;
558 *s = 0;
559 for(i=0; i<nelem(flagtab); i++){
560 if(msg->flags&flagtab[i].flag){
561 if(s > buf)
562 *s++ = ' ';
563 strcpy(s, flagtab[i].name);
564 s += strlen(s);
567 *pp = buf;
568 return 0;
570 case Qinfo:
571 fmtstrinit(&fmt);
572 if(part == msg->part[0]){
573 if(msg->date)
574 fmtprint(&fmt, "unixdate %lud %s", msg->date, ctime(msg->date));
575 if(msg->flags){
576 filedata(Qflags, box, msg, part, pp, nil, freeme, 0, ZQ);
577 fmtprint(&fmt, "flags %s\n", buf);
580 if(part->hdr){
581 if(part->hdr->digest)
582 fmtprint(&fmt, "digest %s\n", part->hdr->digest);
583 if(part->hdr->from)
584 fmtprint(&fmt, "from %s\n", part->hdr->from);
585 if(part->hdr->to)
586 fmtprint(&fmt, "to %s\n", part->hdr->to);
587 if(part->hdr->cc)
588 fmtprint(&fmt, "cc %s\n", part->hdr->cc);
589 if(part->hdr->replyto)
590 fmtprint(&fmt, "replyto %s\n", part->hdr->replyto);
591 if(part->hdr->bcc)
592 fmtprint(&fmt, "bcc %s\n", part->hdr->bcc);
593 if(part->hdr->inreplyto)
594 fmtprint(&fmt, "inreplyto %s\n", part->hdr->inreplyto);
595 if(part->hdr->date)
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);
604 if(part->type)
605 fmtprint(&fmt, "type %s\n", part->type);
606 if(part->lines)
607 fmtprint(&fmt, "lines %d\n", part->lines);
608 if(part->filename)
609 fmtprint(&fmt, "filename %s\n", part->filename);
610 s = fmtstrflush(&fmt);
611 if(s == nil)
612 s = estrdup("");
613 *freeme = 1;
614 *pp = s;
615 return 0;
617 case Qheader:
618 if(part->hdr == nil)
619 return 0;
620 fmtstrinit(&fmt);
621 if(part == msg->part[0])
622 fmtprint(&fmt, "Date: %s", ctime(msg->date));
623 else
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);
635 if(s == nil)
636 s = estrdup("");
637 *freeme = 1;
638 *pp = s;
639 return 0;
641 default:
642 werrstr(Egreg);
643 return -1;
647 int
648 filldir(Dir *d, int type, Box *box, Msg *msg, Part *part)
650 int freeme, len;
651 char *s;
653 memset(d, 0, sizeof *d);
654 if(box){
655 d->atime = box->time;
656 d->mtime = box->time;
657 }else{
658 d->atime = t0;
659 d->mtime = t0;
661 d->uid = estrdup9p("upas");
662 d->gid = estrdup9p("upas");
663 d->muid = estrdup9p("upas");
664 d->qid = qid(type, box, msg, part);
666 switch(type){
667 case Qroot:
668 case Qbox:
669 case Qmsg:
670 d->mode = 0555|DMDIR;
671 if(box && !(box->flags&FlagNoInferiors))
672 d->mode = 0775|DMDIR;
673 break;
674 case Qctl:
675 case Qboxctl:
676 d->mode = 0222;
677 break;
678 case Qsearch:
679 d->mode = 0666;
680 break;
682 case Qflags:
683 d->mode = 0666;
684 goto msgfile;
685 default:
686 d->mode = 0444;
687 msgfile:
688 if(filedata(type, box, msg, part, &s, &len, &freeme, 0, ZQ) >= 0){
689 if(s){
690 if(len == -1)
691 d->length = strlen(s);
692 else
693 d->length = len;
694 if(freeme)
695 free(s);
697 }else if(type == Qraw && msg && part == msg->part[0])
698 d->length = msg->size;
699 break;
702 switch(type){
703 case Qroot:
704 d->name = estrdup9p("/");
705 break;
706 case Qbox:
707 if(box == nil){
708 werrstr(Enobox);
709 return -1;
711 d->name = estrdup9p(box->elem);
712 break;
713 case Qmsg:
714 if(msg == nil){
715 werrstr(Enomsg);
716 return -1;
718 if(part == nil || part == msg->part[0])
719 d->name = esmprint("%d", msg->id);
720 else
721 d->name = esmprint("%d", part->pix+1);
722 break;
723 case Qctl:
724 case Qboxctl:
725 d->name = estrdup9p("ctl");
726 break;
727 case Qsearch:
728 d->name = estrdup9p("search");
729 break;
730 default:
731 d->name = estrdup9p(nameoftype(type));
732 break;
734 return 0;
737 static void
738 fsstat(Req *r)
740 int type;
741 Box *box;
742 Msg *msg;
743 Part *part;
745 type = parseqid(r->fid->qid, &box, &msg, &part);
746 if(filldir(&r->d, type, box, msg, part) < 0)
747 responderror(r);
748 else
749 respond(r, nil);
752 int
753 rootgen(int i, Dir *d, void *aux)
755 USED(aux);
757 if(i == 0)
758 return filldir(d, Qctl, nil, nil, nil);
759 i--;
760 if(i < rootbox->nsub)
761 return filldir(d, Qbox, rootbox->sub[i], nil, nil);
762 return -1;
765 int
766 boxgen(int i, Dir *d, void *aux)
768 Box *box;
770 box = aux;
771 if(i == 0)
772 return filldir(d, Qboxctl, box, nil, nil);
773 i--;
774 if(i == 0)
775 return filldir(d, Qsearch, box, nil, nil);
776 i--;
777 if(i < box->nsub)
778 return filldir(d, Qbox, box->sub[i], nil, nil);
779 i -= box->nsub;
780 if(i < box->nmsg)
781 return filldir(d, Qmsg, box, box->msg[i], nil);
782 return -1;
785 static int msgdir[] = {
786 Qtype,
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
792 };
793 static int mimemsgdir[] = {
794 Qtype,
795 Qbody, Qbcc, Qcc, Qdate, Qfrom, Qheader, Qinfo,
796 Qinreplyto, Qlines, Qmimeheader, Qmessageid,
797 Qraw, Qrawunix, Qrawbody, Qrawheader,
798 Qreplyto, Qsender, Qsubject, Qto
799 };
800 static int mimedir[] = {
801 Qtype,
802 Qbody,
803 Qfilename,
804 Qcharset,
805 Qmimeheader,
806 Qraw
807 };
809 int
810 msggen(int i, Dir *d, void *aux)
812 Box *box;
813 Msg *msg;
814 Part *part;
816 part = aux;
817 msg = part->msg;
818 box = msg->box;
819 if(part->ix == 0){
820 if(i < nelem(msgdir))
821 return filldir(d, msgdir[i], box, msg, part);
822 i -= nelem(msgdir);
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);
827 }else{
828 if(i < nelem(mimedir))
829 return filldir(d, mimedir[i], box, msg, part);
830 i -= nelem(mimedir);
832 if(i < part->nsub)
833 return filldir(d, Qmsg, box, msg, part->sub[i]);
834 return -1;
837 enum
839 CMhangup
840 };
841 static Cmdtab ctltab[] =
843 CMhangup, "hangup", 2
844 };
846 enum
848 CMdelete,
849 CMrefresh,
850 CMreplied,
851 CMread,
852 CMsave,
853 CMjunk,
854 CMnonjunk
855 };
856 static Cmdtab boxctltab[] =
858 CMdelete, "delete", 0,
859 CMrefresh, "refresh", 1,
860 CMreplied, "replied", 0,
861 CMread, "read", 0,
862 CMsave, "save", 0,
863 CMjunk, "junk", 0,
864 CMnonjunk, "nonjunk", 0
865 };
867 static void
868 fsread(Req *r)
870 char *s;
871 int type, len, freeme;
872 Box *box;
873 Msg *msg;
874 Part *part;
876 switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
877 case Qroot:
878 dirread9p(r, rootgen, nil);
879 respond(r, nil);
880 return;
882 case Qbox:
883 if(box == nil){
884 respond(r, Eboxgone);
885 return;
887 if(box->nmsg == 0)
888 imapcheckbox(imap, box);
889 parseqid(r->fid->qid, &box, &msg, &part);
890 if(box == nil){
891 respond(r, Eboxgone);
892 return;
894 dirread9p(r, boxgen, box);
895 respond(r, nil);
896 return;
898 case Qmsg:
899 if(msg == nil || part == nil){
900 respond(r, Emsggone);
901 return;
903 dirread9p(r, msggen, part);
904 respond(r, nil);
905 return;
907 case Qctl:
908 case Qboxctl:
909 respond(r, Egreg);
910 return;
912 case Qsearch:
913 readstr(r, r->fid->aux);
914 respond(r, nil);
915 return;
917 default:
918 if(filedata(type, box, msg, part, &s, &len, &freeme, 1, r->fid->qid) < 0){
919 responderror(r);
920 return;
922 if(s && len == -1)
923 len = strlen(s);
924 readbuf(r, s, len);
925 if(freeme)
926 free(s);
927 respond(r, nil);
928 return;
932 int
933 mkmsglist(Box *box, char **f, int nf, Msg ***mm)
935 int i, nm;
936 Msg **m;
938 m = emalloc(nf*sizeof m[0]);
939 nm = 0;
940 for(i=0; i<nf; i++)
941 if((m[nm] = msgbyid(box, atoi(f[i]))) != nil)
942 nm++;
943 *mm = m;
944 return nm;
947 static void
948 fswrite(Req *r)
950 int i, j, c, type, flag, unflag, flagset, f, reset;
951 Box *box;
952 Msg *msg;
953 Part *part;
954 Cmdbuf *cb;
955 Cmdtab *ct;
956 Msg **m;
957 int nm;
958 Fmt fmt;
960 r->ofcall.count = r->ifcall.count;
961 switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
962 default:
963 respond(r, Egreg);
964 break;
966 case Qctl:
967 cb = parsecmd(r->ifcall.data, r->ifcall.count);
968 if((ct = lookupcmd(cb, ctltab, nelem(ctltab))) == nil){
969 respondcmderror(r, cb, "unknown message");
970 free(cb);
971 return;
973 r->ofcall.count = r->ifcall.count;
974 switch(ct->index){
975 case CMhangup:
976 imaphangup(imap, atoi(cb->f[1]));
977 respond(r, nil);
978 break;
979 default:
980 respond(r, Egreg);
981 break;
983 free(cb);
984 return;
986 case Qboxctl:
987 cb = parsecmd(r->ifcall.data, r->ifcall.count);
988 if((ct = lookupcmd(cb, boxctltab, nelem(boxctltab))) == nil){
989 respondcmderror(r, cb, "bad message");
990 free(cb);
991 return;
993 r->ofcall.count = r->ifcall.count;
994 switch(ct->index){
995 case CMsave:
996 if(cb->nf <= 2){
997 respondcmderror(r, cb, Ebadctl);
998 break;
1000 nm = mkmsglist(box, cb->f+2, cb->nf-2, &m);
1001 if(nm != cb->nf-2){
1002 /* free(m); */
1003 respond(r, Enomsg);
1004 break;
1006 if(nm > 0 && imapcopylist(imap, cb->f[1], m, nm) < 0)
1007 responderror(r);
1008 else
1009 respond(r, nil);
1010 free(m);
1011 break;
1013 case CMjunk:
1014 flag = FlagJunk;
1015 goto flagit;
1016 case CMnonjunk:
1017 flag = FlagNonJunk;
1018 goto flagit;
1019 case CMreplied:
1020 flag = FlagReplied;
1021 goto flagit;
1022 case CMread:
1023 flag = FlagSeen;
1024 flagit:
1025 if(cb->nf <= 1){
1026 respondcmderror(r, cb, Ebadctl);
1027 break;
1029 nm = mkmsglist(box, cb->f+1, cb->nf-1, &m);
1030 if(nm != cb->nf-1){
1031 free(m);
1032 respond(r, Enomsg);
1033 break;
1035 if(nm > 0 && imapflaglist(imap, +1, flag, m, nm) < 0)
1036 responderror(r);
1037 else
1038 respond(r, nil);
1039 free(m);
1040 break;
1042 case CMrefresh:
1043 imapcheckbox(imap, box);
1044 respond(r, nil);
1045 break;
1047 case CMdelete:
1048 if(cb->nf <= 1){
1049 respondcmderror(r, cb, Ebadctl);
1050 break;
1052 nm = mkmsglist(box, cb->f+1, cb->nf-1, &m);
1053 if(nm > 0 && imapremovelist(imap, m, nm) < 0)
1054 responderror(r);
1055 else
1056 respond(r, nil);
1057 free(m);
1058 break;
1060 default:
1061 respond(r, Egreg);
1062 break;
1064 free(cb);
1065 return;
1067 case Qflags:
1068 if(msg == nil){
1069 respond(r, Enomsg);
1070 return;
1072 cb = parsecmd(r->ifcall.data, r->ifcall.count);
1073 flag = 0;
1074 unflag = 0;
1075 flagset = 0;
1076 reset = 0;
1077 for(i=0; i<cb->nf; i++){
1078 f = 0;
1079 c = cb->f[i][0];
1080 if(c == '+' || c == '-')
1081 cb->f[i]++;
1082 for(j=0; j<nelem(flagtab); j++){
1083 if(strcmp(flagtab[j].name, cb->f[i]) == 0){
1084 f = flagtab[j].flag;
1085 break;
1088 if(f == 0){
1089 respondcmderror(r, cb, "unknown flag %s", cb->f[i]);
1090 free(cb);
1091 return;
1093 if(c == '+')
1094 flag |= f;
1095 else if(c == '-')
1096 unflag |= f;
1097 else
1098 flagset |= f;
1100 free(cb);
1101 if((flagset!=0)+(unflag!=0)+(flag!=0) != 1){
1102 respondcmderror(r, cb, Ebadctl);
1103 return;
1105 if(flag)
1106 i = 1;
1107 else if(unflag){
1108 i = -1;
1109 flag = unflag;
1110 }else{
1111 i = 0;
1112 flag = flagset;
1114 if(imapflaglist(imap, i, flag, &msg, 1) < 0)
1115 responderror(r);
1116 else
1117 respond(r, nil);
1118 return;
1120 case Qsearch:
1121 if(box == nil){
1122 respond(r, Eboxgone);
1123 return;
1125 fmtstrinit(&fmt);
1126 nm = imapsearchbox(imap, box, r->ifcall.data, &m);
1127 for(i=0; i<nm; i++){
1128 if(i>0)
1129 fmtrune(&fmt, ' ');
1130 fmtprint(&fmt, "%d", m[i]->id);
1132 free(r->fid->aux);
1133 r->fid->aux = fmtstrflush(&fmt);
1134 respond(r, nil);
1135 return;
1139 static void
1140 fsopen(Req *r)
1142 switch(qtype(r->fid->qid)){
1143 case Qctl:
1144 case Qboxctl:
1145 if((r->ifcall.mode&~OTRUNC) != OWRITE){
1146 respond(r, Eperm);
1147 return;
1149 respond(r, nil);
1150 return;
1152 case Qflags:
1153 case Qsearch:
1154 if((r->ifcall.mode&~OTRUNC) > ORDWR){
1155 respond(r, Eperm);
1156 return;
1158 respond(r, nil);
1159 return;
1161 default:
1162 if(r->ifcall.mode != OREAD){
1163 respond(r, Eperm);
1164 return;
1166 respond(r, nil);
1167 return;
1171 static void
1172 fsflush(Req *r)
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
1178 * they're ready.
1180 incref(&r->oldreq->ref);
1181 respond(r->oldreq, "interrupted");
1182 respond(r, nil);
1185 static void
1186 fsthread(void *v)
1188 Req *r;
1190 r = v;
1191 switch(r->ifcall.type){
1192 case Tread:
1193 fsread(r);
1194 break;
1195 case Twrite:
1196 fswrite(r);
1197 break;
1201 static void
1202 fsrecv(void *v)
1204 Req *r;
1206 while((r = recvp(fsreqchan)) != nil){
1207 switch(r->ifcall.type){
1208 case Tattach:
1209 fsattach(r);
1210 break;
1211 case Tflush:
1212 fsflush(r);
1213 break;
1214 case Topen:
1215 fsopen(r);
1216 break;
1217 case Twalk:
1218 fswalk(r);
1219 break;
1220 case Tstat:
1221 fsstat(r);
1222 break;
1223 default:
1224 threadcreate(fsthread, r, STACK);
1225 break;
1230 static void
1231 fssend(Req *r)
1233 sendp(fsreqchan, r);
1236 static void
1237 fsdestroyfid(Fid *f)
1239 free(f->aux);
1242 void
1243 fsinit0(void) /* bad planning - clash with lib9pclient */
1245 t0 = time(0);
1247 fs.attach = fssend;
1248 fs.flush = fssend;
1249 fs.open = fssend;
1250 fs.walk = fssend;
1251 fs.read = fssend;
1252 fs.write = fssend;
1253 fs.stat = fssend;
1254 fs.destroyfid = fsdestroyfid;
1256 rootqid = qid(Qroot, nil, nil, nil);
1258 fsreqchan = chancreate(sizeof(void*), 0);
1259 mailthread(fsrecv, nil);