URI: 
       tfs.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tfs.c (22454B)
       ---
            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.
           10 
           11 TO DO:
           12 
           13         decode subject, etc.
           14         decode body
           15 
           16         digest
           17         disposition
           18         filename
           19 
           20         ctl messages
           21 
           22         fetch mail on demand
           23 
           24  */
           25 
           26 #include "a.h"
           27 
           28 enum
           29 {
           30         /* directories */
           31         Qroot,
           32         Qbox,
           33         Qmsg,
           34 
           35         /* control files */
           36         Qctl,
           37         Qboxctl,
           38         Qsearch,
           39 
           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,
           51 
           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,
           63 
           64         /* part numbers - same order as struct Part */
           65         Qsize,
           66         Qlines,
           67 
           68         /* other message files */
           69         Qbody,
           70         Qheader,
           71         Qdigest,
           72         Qdisposition,
           73         Qflags,
           74         Qinfo,
           75         Qrawunix,
           76         Qunixdate,
           77         Qunixheader,
           78 
           79         Qfile0 = Qbody,
           80         Qnfile = Qunixheader+1-Qfile0
           81 };
           82 
           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";
           90 
           91 Channel *fsreqchan;
           92 Srv fs;
           93 Qid rootqid;
           94 ulong t0;
           95 
           96 #ifdef PLAN9PORT
           97 void
           98 responderror(Req *r)
           99 {
          100         char e[ERRMAX];
          101 
          102         rerrstr(e, sizeof e);
          103         respond(r, e);
          104 }
          105 #endif
          106 
          107 int
          108 qtype(Qid q)
          109 {
          110         return q.path&0x3F;
          111 }
          112 
          113 int
          114 qboxid(Qid q)
          115 {
          116         return (q.path>>40)&0xFFFF;
          117 }
          118 
          119 int
          120 qmsgid(Qid q)
          121 {
          122         return ((q.path>>32)&0xFF000000) | ((q.path>>16)&0xFFFFFF);
          123 }
          124 
          125 int
          126 qpartid(Qid q)
          127 {
          128         return ((q.path>>6)&0x3FF);
          129 }
          130 
          131 Qid
          132 qid(int ctl, Box *box, Msg *msg, Part *part)
          133 {
          134         Qid q;
          135 
          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;
          146 }
          147 
          148 int
          149 parseqid(Qid q, Box **box, Msg **msg, Part **part)
          150 {
          151         *msg = nil;
          152         *part = nil;
          153 
          154         *box = boxbyid(qboxid(q));
          155         if(*box){
          156                 *msg = msgbyid(*box, qmsgid(q));
          157         }
          158         if(*msg)
          159                 *part = partbyid(*msg, qpartid(q));
          160         return qtype(q);
          161 }
          162 
          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 };
          196 
          197 char*
          198 nameoftype(int t)
          199 {
          200         int i;
          201 
          202         for(i=0; i<nelem(typenames); i++)
          203                 if(typenames[i].type == t)
          204                         return typenames[i].name;
          205         return "???";
          206 }
          207 
          208 int
          209 typeofname(char *name)
          210 {
          211         int i;
          212 
          213         for(i=0; i<nelem(typenames); i++)
          214                 if(strcmp(typenames[i].name, name) == 0)
          215                         return typenames[i].type;
          216         return 0;
          217 }
          218 
          219 static void
          220 fsattach(Req *r)
          221 {
          222         r->fid->qid = rootqid;
          223         r->ofcall.qid = rootqid;
          224         respond(r, nil);
          225 }
          226 
          227 static int
          228 isnumber(char *s)
          229 {
          230         int n;
          231 
          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;
          238 }
          239 
          240 static char*
          241 fswalk1(Fid *fid, char *name, void *arg)
          242 {
          243         int a, type;
          244         Box *b, *box;
          245         Msg *msg;
          246         Part *p, *part;
          247 
          248         USED(arg);
          249 
          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;
          257                 }
          258                 if((box = boxbyname(name)) != nil){
          259                         fid->qid = qid(Qbox, box, nil, nil);
          260                         return nil;
          261                 }
          262                 break;
          263 
          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;
          275                         }
          276                         fid->qid = qid(Qbox, box, nil, nil);
          277                         return nil;
          278                 }
          279                 if(strcmp(name, "ctl") == 0){
          280                         fid->qid = qid(Qboxctl, box, nil, nil);
          281                         return nil;
          282                 }
          283                 if(strcmp(name, "search") == 0){
          284                         fid->qid = qid(Qsearch, box, nil, nil);
          285                         return nil;
          286                 }
          287                 if((b = subbox(box, name)) != nil){
          288                         fid->qid = qid(Qbox, b, nil, nil);
          289                         return nil;
          290                 }
          291                 if((a = isnumber(name)) != 0){
          292                         if((msg = msgbyid(box, a)) == nil){
          293                                 return Enomsg;
          294                         }
          295                         fid->qid = qid(Qmsg, box, msg, nil);
          296                         return nil;
          297                 }
          298                 break;
          299 
          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;
          305                         }
          306                         fid->qid = qid(Qmsg, box, msg, part->parent);
          307                         return nil;
          308                 }
          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;
          313                 }
          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;
          318                         }
          319                 }
          320                 break;
          321         }
          322         return "not found";
          323 }
          324 
          325 static void
          326 fswalk(Req *r)
          327 {
          328         walkandclone(r, fswalk1, nil, nil);
          329 }
          330 
          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 };
          343 
          344 static void
          345 addaddrs(Fmt *fmt, char *prefix, char *addrs)
          346 {
          347         char **f;
          348         int i, nf, inquote;
          349         char *p, *sep;
          350 
          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;
          361         }
          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 = ", ";
          373         }
          374         fmtprint(fmt, "\n");
          375         free(addrs);
          376 }
          377 
          378 static void
          379 mkbody(Part *p, Qid q)
          380 {
          381         char *t;
          382         int len;
          383 
          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;
          391 
          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);
          399 
          400         if(p->charset){
          401                 t = tcs(p->charset, t);
          402                 len = -1;
          403         }
          404         p->body = t;
          405         if(len == -1)
          406                 p->nbody = strlen(t);
          407         else
          408                 p->nbody = len;
          409 }
          410 
          411 static Qid ZQ;
          412 
          413 static int
          414 filedata(int type, Box *box, Msg *msg, Part *part, char **pp, int *len, int *freeme, int force, Qid q)
          415 {
          416         int i, inquote, n, t;
          417         char *from, *s;
          418         static char buf[256];
          419         Fmt fmt;
          420 
          421         *pp = nil;
          422         *freeme = 0;
          423         if(len)
          424                 *len = -1;
          425 
          426         if(msg == nil || part == nil){
          427                 werrstr(Emsggone);
          428                 return -1;
          429         }
          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;
          444                 }
          445                 *pp = ((char**)&part->hdr->date)[type-Qdate];
          446                 return 0;
          447 
          448         case Qunixdate:
          449                 strcpy(buf, ctime(msg->date));
          450                 *pp = buf;
          451                 return 0;
          452 
          453         case Qunixheader:
          454                 if(part->hdr == nil){
          455                         werrstr(Emsggone);
          456                         return -1;
          457                 }
          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;
          469                                 }
          470                         }
          471                         if(*from == 0)
          472                                 from = part->hdr->from;
          473                 }
          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;
          478                 }
          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;
          488 
          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;
          516                         }
          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];
          525                 }
          526                 return 0;
          527 
          528         case Qbody:
          529                 if(part->body){
          530                         *pp = part->body;
          531                         if(len)
          532                                 *len = part->nbody;
          533                         return 0;
          534                 }
          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;
          545                 }
          546                 mkbody(part, q);
          547                 *pp = part->body;
          548                 if(len)
          549                         *len = part->nbody;
          550                 return 0;
          551 
          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;
          558 
          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);
          568                         }
          569                 }
          570                 *pp = buf;
          571                 return 0;
          572 
          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);
          581                         }
          582                 }
          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);
          606                 }
          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;
          619 
          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;
          643 
          644         default:
          645                 werrstr(Egreg);
          646                 return -1;
          647         }
          648 }
          649 
          650 int
          651 filldir(Dir *d, int type, Box *box, Msg *msg, Part *part)
          652 {
          653         int freeme, len;
          654         char *s;
          655 
          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;
          663         }
          664         d->uid = estrdup9p("upas");
          665         d->gid = estrdup9p("upas");
          666         d->muid = estrdup9p("upas");
          667         d->qid = qid(type, box, msg, part);
          668 
          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;
          684 
          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);
          699                         }
          700                 }else if(type == Qraw && msg && part == msg->part[0])
          701                         d->length = msg->size;
          702                 break;
          703         }
          704 
          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;
          713                 }
          714                 d->name = estrdup9p(box->elem);
          715                 break;
          716         case Qmsg:
          717                 if(msg == nil){
          718                         werrstr(Enomsg);
          719                         return -1;
          720                 }
          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;
          736         }
          737         return 0;
          738 }
          739 
          740 static void
          741 fsstat(Req *r)
          742 {
          743         int type;
          744         Box *box;
          745         Msg *msg;
          746         Part *part;
          747 
          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);
          753 }
          754 
          755 int
          756 rootgen(int i, Dir *d, void *aux)
          757 {
          758         USED(aux);
          759 
          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;
          766 }
          767 
          768 int
          769 boxgen(int i, Dir *d, void *aux)
          770 {
          771         Box *box;
          772 
          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;
          786 }
          787 
          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 };
          811 
          812 int
          813 msggen(int i, Dir *d, void *aux)
          814 {
          815         Box *box;
          816         Msg *msg;
          817         Part *part;
          818 
          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);
          834         }
          835         if(i < part->nsub)
          836                 return filldir(d, Qmsg, box, msg, part->sub[i]);
          837         return -1;
          838 }
          839 
          840 enum
          841 {
          842         CMhangup
          843 };
          844 static Cmdtab ctltab[] =
          845 {
          846         CMhangup, "hangup", 2
          847 };
          848 
          849 enum
          850 {
          851         CMdelete,
          852         CMrefresh,
          853         CMreplied,
          854         CMread,
          855         CMsave,
          856         CMjunk,
          857         CMnonjunk
          858 };
          859 static Cmdtab boxctltab[] =
          860 {
          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 };
          869 
          870 static void
          871 fsread(Req *r)
          872 {
          873         char *s;
          874         int type, len, freeme;
          875         Box *box;
          876         Msg *msg;
          877         Part *part;
          878 
          879         switch(type = parseqid(r->fid->qid, &box, &msg, &part)){
          880         case Qroot:
          881                 dirread9p(r, rootgen, nil);
          882                 respond(r, nil);
          883                 return;
          884 
          885         case Qbox:
          886                 if(box == nil){
          887                         respond(r, Eboxgone);
          888                         return;
          889                 }
          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;
          896                 }
          897                 dirread9p(r, boxgen, box);
          898                 respond(r, nil);
          899                 return;
          900 
          901         case Qmsg:
          902                 if(msg == nil || part == nil){
          903                         respond(r, Emsggone);
          904                         return;
          905                 }
          906                 dirread9p(r, msggen, part);
          907                 respond(r, nil);
          908                 return;
          909 
          910         case Qctl:
          911         case Qboxctl:
          912                 respond(r, Egreg);
          913                 return;
          914 
          915         case Qsearch:
          916                 readstr(r, r->fid->aux);
          917                 respond(r, nil);
          918                 return;
          919 
          920         default:
          921                 if(filedata(type, box, msg, part, &s, &len, &freeme, 1, r->fid->qid) < 0){
          922                         responderror(r);
          923                         return;
          924                 }
          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;
          932         }
          933 }
          934 
          935 int
          936 mkmsglist(Box *box, char **f, int nf, Msg ***mm)
          937 {
          938         int i, nm;
          939         Msg **m;
          940 
          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;
          948 }
          949 
          950 static void
          951 fswrite(Req *r)
          952 {
          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;
          962 
          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;
          968 
          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;
          975                 }
          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;
          985                 }
          986                 free(cb);
          987                 return;
          988 
          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;
          995                 }
          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;
         1002                         }
         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;
         1008                         }
         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;
         1015 
         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;
         1031                         }
         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;
         1037                         }
         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;
         1044 
         1045                 case CMrefresh:
         1046                         imapcheckbox(imap, box);
         1047                         respond(r, nil);
         1048                         break;
         1049 
         1050                 case CMdelete:
         1051                         if(cb->nf <= 1){
         1052                                 respondcmderror(r, cb, Ebadctl);
         1053                                 break;
         1054                         }
         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;
         1062 
         1063                 default:
         1064                         respond(r, Egreg);
         1065                         break;
         1066                 }
         1067                 free(cb);
         1068                 return;
         1069 
         1070         case Qflags:
         1071                 if(msg == nil){
         1072                         respond(r, Enomsg);
         1073                         return;
         1074                 }
         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;
         1088                                 }
         1089                         }
         1090                         if(f == 0){
         1091                                 respondcmderror(r, cb, "unknown flag %s", cb->f[i]);
         1092                                 free(cb);
         1093                                 return;
         1094                         }
         1095                         if(c == '+')
         1096                                 flag |= f;
         1097                         else if(c == '-')
         1098                                 unflag |= f;
         1099                         else
         1100                                 flagset |= f;
         1101                 }
         1102                 free(cb);
         1103                 if((flagset!=0)+(unflag!=0)+(flag!=0) != 1){
         1104                         respondcmderror(r, cb, Ebadctl);
         1105                         return;
         1106                 }
         1107                 if(flag)
         1108                         i = 1;
         1109                 else if(unflag){
         1110                         i = -1;
         1111                         flag = unflag;
         1112                 }else{
         1113                         i = 0;
         1114                         flag = flagset;
         1115                 }
         1116                 if(imapflaglist(imap, i, flag, &msg, 1) < 0)
         1117                         responderror(r);
         1118                 else
         1119                         respond(r, nil);
         1120                 return;
         1121 
         1122         case Qsearch:
         1123                 if(box == nil){
         1124                         respond(r, Eboxgone);
         1125                         return;
         1126                 }
         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);
         1133                 }
         1134                 free(r->fid->aux);
         1135                 r->fid->aux = fmtstrflush(&fmt);
         1136                 respond(r, nil);
         1137                 return;
         1138         }
         1139 }
         1140 
         1141 static void
         1142 fsopen(Req *r)
         1143 {
         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;
         1150                 }
         1151                 respond(r, nil);
         1152                 return;
         1153 
         1154         case Qflags:
         1155         case Qsearch:
         1156                 if((r->ifcall.mode&~OTRUNC) > ORDWR){
         1157                         respond(r, Eperm);
         1158                         return;
         1159                 }
         1160                 respond(r, nil);
         1161                 return;
         1162 
         1163         default:
         1164                 if(r->ifcall.mode != OREAD){
         1165                         respond(r, Eperm);
         1166                         return;
         1167                 }
         1168                 respond(r, nil);
         1169                 return;
         1170         }
         1171 }
         1172 
         1173 static void
         1174 fsflush(Req *r)
         1175 {
         1176         /*
         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.
         1181          */
         1182         incref(&r->oldreq->ref);
         1183         respond(r->oldreq, "interrupted");
         1184         respond(r, nil);
         1185 }
         1186 
         1187 static void
         1188 fsthread(void *v)
         1189 {
         1190         Req *r;
         1191 
         1192         r = v;
         1193         switch(r->ifcall.type){
         1194         case Tread:
         1195                 fsread(r);
         1196                 break;
         1197         case Twrite:
         1198                 fswrite(r);
         1199                 break;
         1200         }
         1201 }
         1202 
         1203 static void
         1204 fsrecv(void *v)
         1205 {
         1206         Req *r;
         1207 
         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;
         1229                 }
         1230         }
         1231 }
         1232 
         1233 static void
         1234 fssend(Req *r)
         1235 {
         1236         sendp(fsreqchan, r);
         1237 }
         1238 
         1239 static void
         1240 fsdestroyfid(Fid *f)
         1241 {
         1242         free(f->aux);
         1243 }
         1244 
         1245 void
         1246 fsinit0(void)        /* bad planning - clash with lib9pclient */
         1247 {
         1248         t0 = time(0);
         1249 
         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;
         1258 
         1259         rootqid = qid(Qroot, nil, nil, nil);
         1260 
         1261         fsreqchan = chancreate(sizeof(void*), 0);
         1262         mailthread(fsrecv, nil);
         1263 }