fs.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 }