marshal.c (34357B)
1 #include "common.h" 2 #include <thread.h> 3 #include <9pclient.h> 4 #include <ctype.h> 5 6 enum 7 { 8 STACK = 32768 9 }; 10 11 #define inline _inline 12 13 typedef struct Attach Attach; 14 typedef struct Alias Alias; 15 typedef struct Addr Addr; 16 typedef struct Ctype Ctype; 17 18 struct Attach { 19 Attach *next; 20 char *path; 21 int fd; 22 char *type; 23 int inline; 24 Ctype *ctype; 25 }; 26 27 struct Alias 28 { 29 Alias *next; 30 int n; 31 Addr *addr; 32 }; 33 34 struct Addr 35 { 36 Addr *next; 37 char *v; 38 }; 39 40 enum { 41 Hfrom, 42 Hto, 43 Hcc, 44 Hbcc, 45 Hsender, 46 Hreplyto, 47 Hinreplyto, 48 Hdate, 49 Hsubject, 50 Hmime, 51 Hpriority, 52 Hmsgid, 53 Hcontent, 54 Hx, 55 Hprecedence, 56 Nhdr 57 }; 58 59 enum { 60 PGPsign = 1, 61 PGPencrypt = 2 62 }; 63 64 char *hdrs[Nhdr] = { 65 [Hfrom] "from:", 66 [Hto] "to:", 67 [Hcc] "cc:", 68 [Hbcc] "bcc:", 69 [Hreplyto] "reply-to:", 70 [Hinreplyto] "in-reply-to:", 71 [Hsender] "sender:", 72 [Hdate] "date:", 73 [Hsubject] "subject:", 74 [Hpriority] "priority:", 75 [Hmsgid] "message-id:", 76 [Hmime] "mime-", 77 [Hcontent] "content-", 78 [Hx] "x-", 79 [Hprecedence] "precedence" 80 }; 81 82 struct Ctype { 83 char *type; 84 char *ext; 85 int display; 86 }; 87 88 Ctype ctype[] = { 89 { "text/plain", "txt", 1, }, 90 { "text/html", "html", 1, }, 91 { "text/html", "htm", 1, }, 92 { "text/tab-separated-values", "tsv", 1, }, 93 { "text/richtext", "rtx", 1, }, 94 { "message/rfc822", "txt", 1, }, 95 { "", 0, 0, } 96 }; 97 98 Ctype *mimetypes; 99 100 int pid = -1; 101 int pgppid = -1; 102 103 Attach* mkattach(char*, char*, int); 104 int readheaders(Biobuf*, int*, String**, Addr**, int); 105 void body(Biobuf*, Biobuf*, int); 106 char* mkboundary(void); 107 int printdate(Biobuf*); 108 int printfrom(Biobuf*); 109 int printto(Biobuf*, Addr*); 110 int printcc(Biobuf*, Addr*); 111 int printsubject(Biobuf*, char*); 112 int printinreplyto(Biobuf*, char*); 113 int sendmail(Addr*, Addr*, int*, char*); 114 void attachment(Attach*, Biobuf*); 115 int cistrncmp(char*, char*, int); 116 int cistrcmp(char*, char*); 117 char* waitforsubprocs(void); 118 int enc64(char*, int, uchar*, int); 119 Addr* expand(int, char**); 120 Alias* readaliases(void); 121 Addr* expandline(String**, Addr*); 122 void Bdrain(Biobuf*); 123 void freeaddr(Addr *); 124 int pgpopts(char*); 125 int pgpfilter(int*, int, int); 126 void readmimetypes(void); 127 char* estrdup(char*); 128 void* emalloc(int); 129 void* erealloc(void*, int); 130 void freeaddr(Addr*); 131 void freeaddrs(Addr*); 132 void freealias(Alias*); 133 void freealiases(Alias*); 134 int doublequote(Fmt*); 135 int mountmail(void); 136 int nprocexec; 137 int rfc2047fmt(Fmt*); 138 char* mksubject(char*); 139 140 int rflag, lbflag, xflag, holding, nflag, Fflag, eightflag, dflag; 141 int pgpflag = 0; 142 char *user; 143 char *login; 144 Alias *aliases; 145 int rfc822syntaxerror; 146 char lastchar; 147 char *replymsg; 148 149 CFsys *mailfs; 150 151 enum 152 { 153 Ok = 0, 154 Nomessage = 1, 155 Nobody = 2, 156 Error = -1 157 }; 158 159 #pragma varargck type "Z" char* 160 161 void 162 usage(void) 163 { 164 fprint(2, "usage: %s [-Fr#xn] [-s subject] [-c ccrecipient] [-t type] [-aA attachment] [-p[es]] [-R replymsg] -8 | recipient-list\n", 165 argv0); 166 threadexitsall("usage"); 167 } 168 169 void 170 fatal(char *fmt, ...) 171 { 172 char buf[1024]; 173 va_list arg; 174 175 if(pid >= 0) 176 postnote(PNPROC, pid, "die"); 177 if(pgppid >= 0) 178 postnote(PNPROC, pgppid, "die"); 179 180 va_start(arg, fmt); 181 vseprint(buf, buf+sizeof(buf), fmt, arg); 182 va_end(arg); 183 fprint(2, "%s: %s\n", argv0, buf); 184 holdoff(holding); 185 threadexitsall(buf); 186 } 187 188 void 189 threadmain(int argc, char **argv) 190 { 191 Attach *first, **l, *a; 192 char *subject, *type, *boundary; 193 int flags, fd; 194 Biobuf in, out, *b; 195 Addr *to; 196 Addr *cc; 197 String *file, *hdrstring; 198 int noinput, headersrv; 199 int ccargc; 200 char *ccargv[32]; 201 202 noinput = 0; 203 subject = nil; 204 first = nil; 205 l = &first; 206 type = nil; 207 hdrstring = nil; 208 ccargc = 0; 209 210 quotefmtinstall(); 211 fmtinstall('Z', doublequote); 212 fmtinstall('U', rfc2047fmt); 213 threadwaitchan(); 214 215 ARGBEGIN{ 216 case 't': 217 type = EARGF(usage()); 218 break; 219 case 'a': 220 flags = 0; 221 goto aflag; 222 case 'A': 223 flags = 1; 224 aflag: 225 a = mkattach(EARGF(usage()), type, flags); 226 if(a == nil) 227 threadexitsall("bad args"); 228 type = nil; 229 *l = a; 230 l = &a->next; 231 break; 232 case 'C': 233 if(ccargc >= nelem(ccargv)-1) 234 sysfatal("too many cc's"); 235 ccargv[ccargc] = ARGF(); 236 if(ccargv[ccargc] == nil) 237 usage(); 238 ccargc++; 239 break; 240 case 'R': 241 replymsg = EARGF(usage()); 242 break; 243 case 's': 244 subject = EARGF(usage()); 245 break; 246 case 'F': 247 Fflag = 1; /* file message */ 248 break; 249 case 'r': 250 rflag = 1; /* for sendmail */ 251 break; 252 case 'd': 253 dflag = 1; /* for sendmail */ 254 break; 255 case '#': 256 lbflag = 1; /* for sendmail */ 257 break; 258 case 'x': 259 xflag = 1; /* for sendmail */ 260 break; 261 case 'n': /* no standard input */ 262 nflag = 1; 263 break; 264 case '8': /* read recipients from rfc822 header */ 265 eightflag = 1; 266 break; 267 case 'p': /* pgp flag: encrypt, sign, or both */ 268 if(pgpopts(EARGF(usage())) < 0) 269 sysfatal("bad pgp options"); 270 break; 271 default: 272 usage(); 273 break; 274 }ARGEND; 275 276 login = getlog(); 277 user = getenv("upasname"); 278 if(user == nil || *user == 0) 279 user = login; 280 if(user == nil || *user == 0) 281 sysfatal("can't read user name"); 282 283 if(Binit(&in, 0, OREAD) < 0) 284 sysfatal("can't Binit 0: %r"); 285 286 if(nflag && eightflag) 287 sysfatal("can't use both -n and -8"); 288 if(eightflag && argc >= 1) 289 usage(); 290 else if(!eightflag && argc < 1) 291 usage(); 292 293 aliases = readaliases(); 294 if(!eightflag){ 295 to = expand(argc, argv); 296 cc = expand(ccargc, ccargv); 297 } else { 298 to = nil; 299 cc = nil; 300 } 301 302 flags = 0; 303 headersrv = Nomessage; 304 if(!nflag && !xflag && !lbflag &&!dflag) { 305 /* pass through headers, keeping track of which we've seen, */ 306 /* perhaps building to list. */ 307 holding = holdon(); 308 headersrv = readheaders(&in, &flags, &hdrstring, eightflag ? &to : nil, 1); 309 if(rfc822syntaxerror){ 310 Bdrain(&in); 311 fatal("rfc822 syntax error, message not sent"); 312 } 313 if(to == nil){ 314 Bdrain(&in); 315 fatal("no addresses found, message not sent"); 316 } 317 318 switch(headersrv){ 319 case Error: /* error */ 320 fatal("reading"); 321 break; 322 case Nomessage: /* no message, just exit mimicking old behavior */ 323 noinput = 1; 324 if(first == nil) 325 threadexitsall(0); 326 break; 327 } 328 } 329 330 fd = sendmail(to, cc, &pid, Fflag ? argv[0] : nil); 331 if(fd < 0) 332 sysfatal("execing sendmail: %r\n:"); 333 if(xflag || lbflag || dflag){ 334 close(fd); 335 threadexitsall(waitforsubprocs()); 336 } 337 338 if(Binit(&out, fd, OWRITE) < 0) 339 fatal("can't Binit 1: %r"); 340 341 if(!nflag){ 342 if(Bwrite(&out, s_to_c(hdrstring), s_len(hdrstring)) != s_len(hdrstring)) 343 fatal("write error"); 344 s_free(hdrstring); 345 hdrstring = nil; 346 347 /* read user's standard headers */ 348 file = s_new(); 349 mboxpath("headers", user, file, 0); 350 b = Bopen(s_to_c(file), OREAD); 351 if(b != nil){ 352 switch(readheaders(b, &flags, &hdrstring, nil, 0)){ 353 case Error: /* error */ 354 fatal("reading"); 355 } 356 Bterm(b); 357 if(Bwrite(&out, s_to_c(hdrstring), s_len(hdrstring)) != s_len(hdrstring)) 358 fatal("write error"); 359 s_free(hdrstring); 360 hdrstring = nil; 361 } 362 } 363 364 /* add any headers we need */ 365 if((flags & (1<<Hdate)) == 0) 366 if(printdate(&out) < 0) 367 fatal("writing"); 368 if((flags & (1<<Hfrom)) == 0) 369 if(printfrom(&out) < 0) 370 fatal("writing"); 371 if((flags & (1<<Hto)) == 0) 372 if(printto(&out, to) < 0) 373 fatal("writing"); 374 if((flags & (1<<Hcc)) == 0) 375 if(printcc(&out, cc) < 0) 376 fatal("writing"); 377 if((flags & (1<<Hsubject)) == 0 && subject != nil) 378 if(printsubject(&out, subject) < 0) 379 fatal("writing"); 380 if(replymsg != nil) 381 printinreplyto(&out, replymsg); /* ignore errors */ 382 Bprint(&out, "MIME-Version: 1.0\n"); 383 384 if(pgpflag){ /* interpose pgp process between us and sendmail to handle body */ 385 Bflush(&out); 386 Bterm(&out); 387 fd = pgpfilter(&pgppid, fd, pgpflag); 388 if(Binit(&out, fd, OWRITE) < 0) 389 fatal("can't Binit 1: %r"); 390 } 391 392 /* if attachments, stick in multipart headers */ 393 boundary = nil; 394 if(first != nil){ 395 boundary = mkboundary(); 396 Bprint(&out, "Content-Type: multipart/mixed;\n"); 397 Bprint(&out, "\tboundary=\"%s\"\n\n", boundary); 398 Bprint(&out, "This is a multi-part message in MIME format.\n"); 399 Bprint(&out, "--%s\n", boundary); 400 Bprint(&out, "Content-Disposition: inline\n"); 401 } 402 403 if(!nflag){ 404 if(!noinput && headersrv == Ok){ 405 body(&in, &out, 1); 406 } 407 } else 408 Bprint(&out, "\n"); 409 holdoff(holding); 410 411 Bflush(&out); 412 for(a = first; a != nil; a = a->next){ 413 if(lastchar != '\n') 414 Bprint(&out, "\n"); 415 Bprint(&out, "--%s\n", boundary); 416 attachment(a, &out); 417 } 418 419 if(first != nil){ 420 if(lastchar != '\n') 421 Bprint(&out, "\n"); 422 Bprint(&out, "--%s--\n", boundary); 423 } 424 425 Bterm(&out); 426 close(fd); 427 threadexitsall(waitforsubprocs()); 428 } 429 430 /* evaluate pgp option string */ 431 int 432 pgpopts(char *s) 433 { 434 if(s == nil || s[0] == '\0') 435 return -1; 436 while(*s){ 437 switch(*s++){ 438 case 's': case 'S': 439 pgpflag |= PGPsign; 440 break; 441 case 'e': case 'E': 442 pgpflag |= PGPencrypt; 443 break; 444 default: 445 return -1; 446 } 447 } 448 return 0; 449 } 450 451 /* read headers from stdin into a String, expanding local aliases, */ 452 /* keep track of which headers are there, which addresses we have */ 453 /* remove Bcc: line. */ 454 int 455 readheaders(Biobuf *in, int *fp, String **sp, Addr **top, int strict) 456 { 457 Addr *to; 458 String *s, *sline; 459 char *p; 460 int i, seen, hdrtype; 461 462 s = s_new(); 463 sline = nil; 464 to = nil; 465 hdrtype = -1; 466 seen = 0; 467 for(;;) { 468 if((p = Brdline(in, '\n')) != nil) { 469 seen = 1; 470 p[Blinelen(in)-1] = 0; 471 472 /* coalesce multiline headers */ 473 if((*p == ' ' || *p == '\t') && sline){ 474 s_append(sline, "\n"); 475 s_append(sline, p); 476 p[Blinelen(in)-1] = '\n'; 477 continue; 478 } 479 } 480 481 /* process the current header, it's all been read */ 482 if(sline) { 483 assert(hdrtype != -1); 484 if(top){ 485 switch(hdrtype){ 486 case Hto: 487 case Hcc: 488 case Hbcc: 489 to = expandline(&sline, to); 490 break; 491 } 492 } 493 if(hdrtype == Hsubject){ 494 s_append(s, mksubject(s_to_c(sline))); 495 s_append(s, "\n"); 496 }else if(top==nil || hdrtype!=Hbcc){ 497 s_append(s, s_to_c(sline)); 498 s_append(s, "\n"); 499 } 500 s_free(sline); 501 sline = nil; 502 } 503 504 if(p == nil) 505 break; 506 507 /* if no :, it's not a header, seek back and break */ 508 if(strchr(p, ':') == nil){ 509 p[Blinelen(in)-1] = '\n'; 510 Bseek(in, -Blinelen(in), 1); 511 break; 512 } 513 514 sline = s_copy(p); 515 516 /* classify the header. If we don't recognize it, break. This is */ 517 /* to take care of user's that start messages with lines that contain */ 518 /* ':'s but that aren't headers. This is a bit hokey. Since I decided */ 519 /* to let users type headers, I need some way to distinguish. Therefore, */ 520 /* marshal tries to know all likely headers and will indeed screw up if */ 521 /* the user types an unlikely one. -- presotto */ 522 hdrtype = -1; 523 for(i = 0; i < nelem(hdrs); i++){ 524 if(cistrncmp(hdrs[i], p, strlen(hdrs[i])) == 0){ 525 *fp |= 1<<i; 526 hdrtype = i; 527 break; 528 } 529 } 530 if(strict){ 531 if(hdrtype == -1){ 532 p[Blinelen(in)-1] = '\n'; 533 Bseek(in, -Blinelen(in), 1); 534 break; 535 } 536 } else 537 hdrtype = 0; 538 p[Blinelen(in)-1] = '\n'; 539 } 540 541 *sp = s; 542 if(top) 543 *top = to; 544 545 if(seen == 0){ 546 if(Blinelen(in) == 0) 547 return Nomessage; 548 else 549 return Ok; 550 } 551 if(p == nil) 552 return Nobody; 553 return Ok; 554 } 555 556 /* pass the body to sendmail, make sure body starts and ends with a newline */ 557 void 558 body(Biobuf *in, Biobuf *out, int docontenttype) 559 { 560 char *buf, *p; 561 int i, n, len; 562 563 n = 0; 564 len = 16*1024; 565 buf = emalloc(len); 566 567 /* first char must be newline */ 568 i = Bgetc(in); 569 if(i > 0){ 570 if(i != '\n') 571 buf[n++] = '\n'; 572 buf[n++] = i; 573 } else { 574 buf[n++] = '\n'; 575 } 576 577 /* read into memory */ 578 if(docontenttype){ 579 while(docontenttype){ 580 if(n == len){ 581 len += len>>2; 582 buf = realloc(buf, len); 583 if(buf == nil) 584 sysfatal("%r"); 585 } 586 p = buf+n; 587 i = Bread(in, p, len - n); 588 if(i < 0) 589 fatal("input error2"); 590 if(i == 0) 591 break; 592 n += i; 593 for(; i > 0; i--) 594 if((*p++ & 0x80) && docontenttype){ 595 Bprint(out, "Content-Type: text/plain; charset=\"UTF-8\"\n"); 596 Bprint(out, "Content-Transfer-Encoding: 8bit\n"); 597 docontenttype = 0; 598 break; 599 } 600 } 601 if(docontenttype){ 602 Bprint(out, "Content-Type: text/plain; charset=\"US-ASCII\"\n"); 603 Bprint(out, "Content-Transfer-Encoding: 7bit\n"); 604 } 605 } 606 607 /* write what we already read */ 608 if(Bwrite(out, buf, n) < 0) 609 fatal("output error"); 610 if(n > 0) 611 lastchar = buf[n-1]; 612 else 613 lastchar = '\n'; 614 615 616 /* pass the rest */ 617 for(;;){ 618 n = Bread(in, buf, len); 619 if(n < 0) 620 fatal("input error2"); 621 if(n == 0) 622 break; 623 if(Bwrite(out, buf, n) < 0) 624 fatal("output error"); 625 lastchar = buf[n-1]; 626 } 627 } 628 629 /* pass the body to sendmail encoding with base64 */ 630 /* */ 631 /* the size of buf is very important to enc64. Anything other than */ 632 /* a multiple of 3 will cause enc64 to output a termination sequence. */ 633 /* To ensure that a full buf corresponds to a multiple of complete lines, */ 634 /* we make buf a multiple of 3*18 since that's how many enc64 sticks on */ 635 /* a single line. This avoids short lines in the output which is pleasing */ 636 /* but not necessary. */ 637 /* */ 638 void 639 body64(Biobuf *in, Biobuf *out) 640 { 641 uchar buf[3*18*54]; 642 char obuf[3*18*54*2]; 643 int m, n; 644 645 Bprint(out, "\n"); 646 for(;;){ 647 n = Bread(in, buf, sizeof(buf)); 648 if(n < 0) 649 fatal("input error"); 650 if(n == 0) 651 break; 652 m = enc64(obuf, sizeof(obuf), buf, n); 653 if((n=Bwrite(out, obuf, m)) < 0) 654 fatal("output error"); 655 } 656 lastchar = '\n'; 657 } 658 659 /* pass message to sendmail, make sure body starts with a newline */ 660 void 661 copy(Biobuf *in, Biobuf *out) 662 { 663 char buf[4*1024]; 664 int n; 665 666 for(;;){ 667 n = Bread(in, buf, sizeof(buf)); 668 if(n < 0) 669 fatal("input error"); 670 if(n == 0) 671 break; 672 if(Bwrite(out, buf, n) < 0) 673 fatal("output error"); 674 } 675 } 676 677 void 678 attachment(Attach *a, Biobuf *out) 679 { 680 Biobuf *f; 681 char *p; 682 683 f = emalloc(sizeof *f); 684 Binit(f, a->fd, OREAD); 685 /* if it's already mime encoded, just copy */ 686 if(strcmp(a->type, "mime") == 0){ 687 copy(f, out); 688 Bterm(f); 689 free(f); 690 return; 691 } 692 693 /* if it's not already mime encoded ... */ 694 if(strcmp(a->type, "text/plain") != 0) 695 Bprint(out, "Content-Type: %s\n", a->type); 696 697 if(a->inline){ 698 Bprint(out, "Content-Disposition: inline\n"); 699 } else { 700 p = strrchr(a->path, '/'); 701 if(p == nil) 702 p = a->path; 703 else 704 p++; 705 Bprint(out, "Content-Disposition: attachment; filename=%Z\n", p); 706 } 707 708 /* dump our local 'From ' line when passing along mail messages */ 709 if(strcmp(a->type, "message/rfc822") == 0){ 710 p = Brdline(f, '\n'); 711 if(strncmp(p, "From ", 5) != 0) 712 Bseek(f, 0, 0); 713 } 714 if(a->ctype->display){ 715 body(f, out, strcmp(a->type, "text/plain") == 0); 716 } else { 717 Bprint(out, "Content-Transfer-Encoding: base64\n"); 718 body64(f, out); 719 } 720 Bterm(f); 721 free(f); 722 } 723 724 char *ascwday[] = 725 { 726 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 727 }; 728 729 char *ascmon[] = 730 { 731 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 732 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 733 }; 734 735 int 736 printdate(Biobuf *b) 737 { 738 Tm *tm; 739 int tz; 740 741 tm = localtime(time(0)); 742 tz = (tm->tzoff/3600)*100 + ((tm->tzoff/60)%60); 743 744 return Bprint(b, "Date: %s, %d %s %d %2.2d:%2.2d:%2.2d %s%.4d\n", 745 ascwday[tm->wday], tm->mday, ascmon[tm->mon], 1900+tm->year, 746 tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz); 747 } 748 749 int 750 printfrom(Biobuf *b) 751 { 752 return Bprint(b, "From: %s\n", user); 753 } 754 755 int 756 printto(Biobuf *b, Addr *a) 757 { 758 int i; 759 760 if(Bprint(b, "To: %s", a->v) < 0) 761 return -1; 762 i = 0; 763 for(a = a->next; a != nil; a = a->next) 764 if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0) 765 return -1; 766 if(Bprint(b, "\n") < 0) 767 return -1; 768 return 0; 769 } 770 771 int 772 printcc(Biobuf *b, Addr *a) 773 { 774 int i; 775 776 if(a == nil) 777 return 0; 778 if(Bprint(b, "CC: %s", a->v) < 0) 779 return -1; 780 i = 0; 781 for(a = a->next; a != nil; a = a->next) 782 if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0) 783 return -1; 784 if(Bprint(b, "\n") < 0) 785 return -1; 786 return 0; 787 } 788 789 int 790 printsubject(Biobuf *b, char *subject) 791 { 792 return Bprint(b, "Subject: %s\n", subject); 793 } 794 795 int 796 printinreplyto(Biobuf *out, char *dir) 797 { 798 String *s; 799 char buf[256]; 800 int fd; 801 int n; 802 803 if(mountmail() < 0) 804 return -1; 805 if(strncmp(dir, "Mail/", 5) != 0) 806 return -1; 807 s = s_copy(dir+5); 808 s_append(s, "/messageid"); 809 fd = fsopenfd(mailfs, s_to_c(s), OREAD); 810 s_free(s); 811 if(fd < 0) 812 return -1; 813 n = readn(fd, buf, sizeof(buf)-1); 814 close(fd); 815 if(n <= 0) 816 return -1; 817 buf[n] = 0; 818 return Bprint(out, "In-Reply-To: %s\n", buf); 819 } 820 821 int 822 mopen(char *file, int mode) 823 { 824 int fd; 825 826 if((fd = open(file, mode)) >= 0) 827 return fd; 828 if(strncmp(file, "Mail/", 5) == 0 && mountmail() >= 0 && (fd = fsopenfd(mailfs, file+5, mode)) >= 0) 829 return fd; 830 return -1; 831 } 832 833 Attach* 834 mkattach(char *file, char *type, int inline) 835 { 836 Ctype *c; 837 Attach *a; 838 char ftype[64]; 839 char *p; 840 int fd, n, pfd[2], xfd[3]; 841 842 if(file == nil) 843 return nil; 844 if((fd = mopen(file, OREAD)) < 0) 845 return nil; 846 a = emalloc(sizeof(*a)); 847 a->fd = fd; 848 a->path = file; 849 a->next = nil; 850 a->type = type; 851 a->inline = inline; 852 a->ctype = nil; 853 if(type != nil){ 854 for(c = ctype; ; c++) 855 if(strncmp(type, c->type, strlen(c->type)) == 0){ 856 a->ctype = c; 857 break; 858 } 859 return a; 860 } 861 862 /* pick a type depending on extension */ 863 p = strchr(file, '.'); 864 if(p != nil) 865 p++; 866 867 /* check the builtin extensions */ 868 if(p != nil){ 869 for(c = ctype; c->ext != nil; c++) 870 if(strcmp(p, c->ext) == 0){ 871 a->type = c->type; 872 a->ctype = c; 873 return a; 874 } 875 } 876 877 /* try the mime types file */ 878 if(p != nil){ 879 if(mimetypes == nil) 880 readmimetypes(); 881 for(c = mimetypes; c != nil && c->ext != nil; c++) 882 if(strcmp(p, c->ext) == 0){ 883 a->type = c->type; 884 a->ctype = c; 885 return a; 886 } 887 } 888 889 /* run file to figure out the type */ 890 a->type = "application/octet-stream"; /* safest default */ 891 if(pipe(pfd) < 0) 892 return a; 893 894 xfd[0] = mopen(file, OREAD); 895 xfd[1] = pfd[0]; 896 xfd[2] = dup(2, -1); 897 if((pid=threadspawnl(xfd, unsharp("#9/bin/file"), "file", "-m", nil)) < 0){ 898 close(xfd[0]); 899 close(xfd[1]); 900 close(xfd[2]); 901 return a; 902 } 903 /* threadspawnl closed pfd[0] */ 904 905 n = readn(pfd[1], ftype, sizeof(ftype)); 906 if(n > 0){ 907 ftype[n-1] = 0; 908 a->type = estrdup(ftype); 909 } 910 close(pfd[1]); 911 procwait(pid); 912 913 for(c = ctype; ; c++) 914 if(strncmp(a->type, c->type, strlen(c->type)) == 0){ 915 a->ctype = c; 916 break; 917 } 918 919 return a; 920 } 921 922 char* 923 mkboundary(void) 924 { 925 char buf[32]; 926 int i; 927 928 srand((time(0)<<16)|getpid()); 929 strcpy(buf, "upas-"); 930 for(i = 5; i < sizeof(buf)-1; i++) 931 buf[i] = 'a' + nrand(26); 932 buf[i] = 0; 933 return estrdup(buf); 934 } 935 936 /* copy types to two fd's */ 937 static void 938 tee(int in, int out1, int out2) 939 { 940 char buf[8*1024]; 941 int n; 942 943 for(;;){ 944 n = read(in, buf, sizeof(buf)); 945 if(n <= 0) 946 break; 947 if(write(out1, buf, n) < 0) 948 break; 949 if(write(out2, buf, n) < 0) 950 break; 951 } 952 } 953 954 static void 955 teeproc(void *v) 956 { 957 int *a; 958 959 a = v; 960 tee(a[0], a[1], a[2]); 961 write(a[2], "\n", 1); 962 } 963 964 /* print the unix from line */ 965 int 966 printunixfrom(int fd) 967 { 968 Tm *tm; 969 int tz; 970 971 tm = localtime(time(0)); 972 tz = (tm->tzoff/3600)*100 + ((tm->tzoff/60)%60); 973 974 return fprint(fd, "From %s %s %s %d %2.2d:%2.2d:%2.2d %s%.4d %d\n", 975 user, 976 ascwday[tm->wday], ascmon[tm->mon], tm->mday, 977 tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz, 1900+tm->year); 978 } 979 980 char *specialfile[] = 981 { 982 "pipeto", 983 "pipefrom", 984 "L.mbox", 985 "forward", 986 "names" 987 }; 988 989 /* return 1 if this is a special file */ 990 static int 991 special(String *s) 992 { 993 char *p; 994 int i; 995 996 p = strrchr(s_to_c(s), '/'); 997 if(p == nil) 998 p = s_to_c(s); 999 else 1000 p++; 1001 for(i = 0; i < nelem(specialfile); i++) 1002 if(strcmp(p, specialfile[i]) == 0) 1003 return 1; 1004 return 0; 1005 } 1006 1007 /* open the folder using the recipients account name */ 1008 static int 1009 openfolder(char *rcvr) 1010 { 1011 char *p; 1012 int c; 1013 String *file; 1014 Dir *d; 1015 int fd; 1016 int scarey; 1017 1018 file = s_new(); 1019 mboxpath("f", user, file, 0); 1020 1021 /* if $mail/f exists, store there, otherwise in $mail */ 1022 d = dirstat(s_to_c(file)); 1023 if(d == nil || d->qid.type != QTDIR){ 1024 scarey = 1; 1025 file->ptr -= 1; 1026 } else { 1027 s_putc(file, '/'); 1028 scarey = 0; 1029 } 1030 free(d); 1031 1032 p = strrchr(rcvr, '!'); 1033 if(p != nil) 1034 rcvr = p+1; 1035 1036 while(*rcvr && *rcvr != '@'){ 1037 c = *rcvr++; 1038 if(c == '/') 1039 c = '_'; 1040 s_putc(file, c); 1041 } 1042 s_terminate(file); 1043 1044 if(scarey && special(file)){ 1045 fprint(2, "%s: won't overwrite %s\n", argv0, s_to_c(file)); 1046 s_free(file); 1047 return -1; 1048 } 1049 1050 fd = open(s_to_c(file), OWRITE); 1051 if(fd < 0) 1052 fd = create(s_to_c(file), OWRITE, 0660); 1053 1054 s_free(file); 1055 return fd; 1056 } 1057 1058 /* start up sendmail and return an fd to talk to it with */ 1059 int 1060 sendmail(Addr *to, Addr *cc, int *pid, char *rcvr) 1061 { 1062 char **av, **v; 1063 int ac, fd, *targ; 1064 int pfd[2], sfd, xfd[3]; 1065 String *cmd; 1066 char *x; 1067 Addr *a; 1068 1069 fd = -1; 1070 if(rcvr != nil) 1071 fd = openfolder(rcvr); 1072 1073 ac = 0; 1074 for(a = to; a != nil; a = a->next) 1075 ac++; 1076 for(a = cc; a != nil; a = a->next) 1077 ac++; 1078 v = av = emalloc(sizeof(char*)*(ac+20)); 1079 ac = 0; 1080 v[ac++] = "sendmail"; 1081 if(xflag) 1082 v[ac++] = "-x"; 1083 if(rflag) 1084 v[ac++] = "-r"; 1085 if(lbflag) 1086 v[ac++] = "-#"; 1087 if(dflag) 1088 v[ac++] = "-d"; 1089 for(a = to; a != nil; a = a->next) 1090 v[ac++] = a->v; 1091 for(a = cc; a != nil; a = a->next) 1092 v[ac++] = a->v; 1093 v[ac] = 0; 1094 1095 if(pipe(pfd) < 0) 1096 fatal("pipe: %r"); 1097 1098 xfd[0] = pfd[0]; 1099 xfd[1] = dup(1, -1); 1100 xfd[2] = dup(2, -1); 1101 1102 if(replymsg != nil) 1103 putenv("replymsg", replymsg); 1104 cmd = mboxpath("pipefrom", login, s_new(), 0); 1105 1106 if((*pid = threadspawn(xfd, x=s_to_c(cmd), av)) < 0 1107 && (*pid = threadspawn(xfd, x="myupassend", av)) < 0 1108 && (*pid = threadspawn(xfd, x=unsharp("#9/bin/upas/send"), av)) < 0) 1109 fatal("exec: %r"); 1110 /* threadspawn closed pfd[0] (== xfd[0]) */ 1111 sfd = pfd[1]; 1112 1113 if(rcvr != nil){ 1114 if(pipe(pfd) < 0) 1115 fatal("pipe: %r"); 1116 seek(fd, 0, 2); 1117 printunixfrom(fd); 1118 targ = emalloc(3*sizeof targ[0]); 1119 targ[0] = sfd; 1120 targ[1] = pfd[0]; 1121 targ[2] = fd; 1122 proccreate(teeproc, targ, STACK); 1123 sfd = pfd[1]; 1124 } 1125 1126 return sfd; 1127 } 1128 1129 /* start up pgp process and return an fd to talk to it with. */ 1130 /* its standard output will be the original fd, which goes to sendmail. */ 1131 int 1132 pgpfilter(int *pid, int fd, int pgpflag) 1133 { 1134 char **av, **v; 1135 int ac; 1136 int pfd[2]; 1137 1138 v = av = emalloc(sizeof(char*)*8); 1139 ac = 0; 1140 v[ac++] = "pgp"; 1141 v[ac++] = "-fat"; /* operate as a filter, generate text */ 1142 if(pgpflag & PGPsign) 1143 v[ac++] = "-s"; 1144 if(pgpflag & PGPencrypt) 1145 v[ac++] = "-e"; 1146 v[ac] = 0; 1147 1148 if(pipe(pfd) < 0) 1149 fatal("%r"); 1150 switch(*pid = fork()){ 1151 case -1: 1152 fatal("%r"); 1153 break; 1154 case 0: 1155 close(pfd[1]); 1156 dup(pfd[0], 0); 1157 close(pfd[0]); 1158 dup(fd, 1); 1159 close(fd); 1160 /* add newline to avoid confusing pgp output with 822 headers */ 1161 write(1, "\n", 1); 1162 1163 exec("pgp", av); 1164 fatal("execing: %r"); 1165 break; 1166 default: 1167 close(pfd[0]); 1168 break; 1169 } 1170 close(fd); 1171 return pfd[1]; 1172 } 1173 1174 /* wait for sendmail and pgp to exit; exit here if either failed */ 1175 char* 1176 waitforsubprocs(void) 1177 { 1178 Waitmsg *w; 1179 char *err; 1180 1181 err = nil; 1182 if(pgppid >= 0 && (w=procwait(pgppid)) && w->msg[0]) 1183 err = w->msg; 1184 if(pid >= 0 && (w=procwait(pid)) && w->msg[0]) 1185 err = w->msg; 1186 return err; 1187 } 1188 1189 int 1190 cistrncmp(char *a, char *b, int n) 1191 { 1192 while(n-- > 0){ 1193 if(tolower(*a++) != tolower(*b++)) 1194 return -1; 1195 } 1196 return 0; 1197 } 1198 1199 int 1200 cistrcmp(char *a, char *b) 1201 { 1202 for(;;){ 1203 if(tolower(*a) != tolower(*b++)) 1204 return -1; 1205 if(*a++ == 0) 1206 break; 1207 } 1208 return 0; 1209 } 1210 1211 static uchar t64d[256]; 1212 static char t64e[64]; 1213 1214 static void 1215 init64(void) 1216 { 1217 int c, i; 1218 1219 memset(t64d, 255, 256); 1220 memset(t64e, '=', 64); 1221 i = 0; 1222 for(c = 'A'; c <= 'Z'; c++){ 1223 t64e[i] = c; 1224 t64d[c] = i++; 1225 } 1226 for(c = 'a'; c <= 'z'; c++){ 1227 t64e[i] = c; 1228 t64d[c] = i++; 1229 } 1230 for(c = '0'; c <= '9'; c++){ 1231 t64e[i] = c; 1232 t64d[c] = i++; 1233 } 1234 t64e[i] = '+'; 1235 t64d['+'] = i++; 1236 t64e[i] = '/'; 1237 t64d['/'] = i; 1238 } 1239 1240 int 1241 enc64(char *out, int lim, uchar *in, int n) 1242 { 1243 int i; 1244 ulong b24; 1245 char *start = out; 1246 char *e = out + lim; 1247 1248 if(t64e[0] == 0) 1249 init64(); 1250 for(i = 0; i < n/3; i++){ 1251 b24 = (*in++)<<16; 1252 b24 |= (*in++)<<8; 1253 b24 |= *in++; 1254 if(out + 5 >= e) 1255 goto exhausted; 1256 *out++ = t64e[(b24>>18)]; 1257 *out++ = t64e[(b24>>12)&0x3f]; 1258 *out++ = t64e[(b24>>6)&0x3f]; 1259 *out++ = t64e[(b24)&0x3f]; 1260 if((i%18) == 17) 1261 *out++ = '\n'; 1262 } 1263 1264 switch(n%3){ 1265 case 2: 1266 b24 = (*in++)<<16; 1267 b24 |= (*in)<<8; 1268 if(out + 4 >= e) 1269 goto exhausted; 1270 *out++ = t64e[(b24>>18)]; 1271 *out++ = t64e[(b24>>12)&0x3f]; 1272 *out++ = t64e[(b24>>6)&0x3f]; 1273 break; 1274 case 1: 1275 b24 = (*in)<<16; 1276 if(out + 4 >= e) 1277 goto exhausted; 1278 *out++ = t64e[(b24>>18)]; 1279 *out++ = t64e[(b24>>12)&0x3f]; 1280 *out++ = '='; 1281 break; 1282 case 0: 1283 if((i%18) != 0) 1284 *out++ = '\n'; 1285 *out = 0; 1286 return out - start; 1287 } 1288 exhausted: 1289 *out++ = '='; 1290 *out++ = '\n'; 1291 *out = 0; 1292 return out - start; 1293 } 1294 1295 void 1296 freealias(Alias *a) 1297 { 1298 freeaddrs(a->addr); 1299 free(a); 1300 } 1301 1302 void 1303 freealiases(Alias *a) 1304 { 1305 Alias *next; 1306 1307 while(a != nil){ 1308 next = a->next; 1309 freealias(a); 1310 a = next; 1311 } 1312 } 1313 1314 /* */ 1315 /* read alias file */ 1316 /* */ 1317 Alias* 1318 readaliases(void) 1319 { 1320 Alias *a, **l, *first; 1321 Addr *addr, **al; 1322 String *file, *line, *token; 1323 Sinstack *sp; 1324 1325 first = nil; 1326 file = s_new(); 1327 line = s_new(); 1328 token = s_new(); 1329 1330 /* open and get length */ 1331 mboxpath("names", login, file, 0); 1332 sp = s_allocinstack(s_to_c(file)); 1333 if(sp == nil) 1334 goto out; 1335 1336 l = &first; 1337 1338 /* read a line at a time. */ 1339 while(s_rdinstack(sp, s_restart(line))!=nil) { 1340 s_restart(line); 1341 a = emalloc(sizeof(Alias)); 1342 al = &a->addr; 1343 for(;;){ 1344 if(s_parse(line, s_restart(token))==0) 1345 break; 1346 addr = emalloc(sizeof(Addr)); 1347 addr->v = strdup(s_to_c(token)); 1348 addr->next = 0; 1349 *al = addr; 1350 al = &addr->next; 1351 } 1352 if(a->addr == nil || a->addr->next == nil){ 1353 freealias(a); 1354 continue; 1355 } 1356 a->next = nil; 1357 *l = a; 1358 l = &a->next; 1359 } 1360 s_freeinstack(sp); 1361 1362 out: 1363 s_free(file); 1364 s_free(line); 1365 s_free(token); 1366 return first; 1367 } 1368 1369 Addr* 1370 newaddr(char *name) 1371 { 1372 Addr *a; 1373 1374 a = emalloc(sizeof(*a)); 1375 a->next = nil; 1376 a->v = estrdup(name); 1377 if(a->v == nil) 1378 sysfatal("%r"); 1379 return a; 1380 } 1381 1382 /* */ 1383 /* expand personal aliases since the names are meaningless in */ 1384 /* other contexts */ 1385 /* */ 1386 Addr* 1387 _expand(Addr *old, int *changedp) 1388 { 1389 Alias *al; 1390 Addr *first, *next, **l, *a; 1391 1392 *changedp = 0; 1393 first = nil; 1394 l = &first; 1395 for(;old != nil; old = next){ 1396 next = old->next; 1397 for(al = aliases; al != nil; al = al->next){ 1398 if(strcmp(al->addr->v, old->v) == 0){ 1399 for(a = al->addr->next; a != nil; a = a->next){ 1400 *l = newaddr(a->v); 1401 if(*l == nil) 1402 sysfatal("%r"); 1403 l = &(*l)->next; 1404 *changedp = 1; 1405 } 1406 break; 1407 } 1408 } 1409 if(al != nil){ 1410 freeaddr(old); 1411 continue; 1412 } 1413 *l = old; 1414 old->next = nil; 1415 l = &(*l)->next; 1416 } 1417 return first; 1418 } 1419 1420 Addr* 1421 rexpand(Addr *old) 1422 { 1423 int i, changed; 1424 1425 changed = 0; 1426 for(i=0; i<32; i++){ 1427 old = _expand(old, &changed); 1428 if(changed == 0) 1429 break; 1430 } 1431 return old; 1432 } 1433 1434 Addr* 1435 unique(Addr *first) 1436 { 1437 Addr *a, **l, *x; 1438 1439 for(a = first; a != nil; a = a->next){ 1440 for(l = &a->next; *l != nil;){ 1441 if(strcmp(a->v, (*l)->v) == 0){ 1442 x = *l; 1443 *l = x->next; 1444 freeaddr(x); 1445 } else 1446 l = &(*l)->next; 1447 } 1448 } 1449 return first; 1450 } 1451 1452 Addr* 1453 expand(int ac, char **av) 1454 { 1455 Addr *first, **l; 1456 int i; 1457 1458 first = nil; 1459 1460 /* make a list of the starting addresses */ 1461 l = &first; 1462 for(i = 0; i < ac; i++){ 1463 *l = newaddr(av[i]); 1464 if(*l == nil) 1465 sysfatal("%r"); 1466 l = &(*l)->next; 1467 } 1468 1469 /* recurse till we don't change any more */ 1470 return unique(rexpand(first)); 1471 } 1472 1473 Addr* 1474 concataddr(Addr *a, Addr *b) 1475 { 1476 Addr *oa; 1477 1478 if(a == nil) 1479 return b; 1480 1481 oa = a; 1482 for(; a->next; a=a->next) 1483 ; 1484 a->next = b; 1485 return oa; 1486 } 1487 1488 void 1489 freeaddr(Addr *ap) 1490 { 1491 free(ap->v); 1492 free(ap); 1493 } 1494 1495 void 1496 freeaddrs(Addr *ap) 1497 { 1498 Addr *next; 1499 1500 for(; ap; ap=next) { 1501 next = ap->next; 1502 freeaddr(ap); 1503 } 1504 } 1505 1506 String* 1507 s_copyn(char *s, int n) 1508 { 1509 return s_nappend(s_reset(nil), s, n); 1510 } 1511 1512 /* fetch the next token from an RFC822 address string */ 1513 /* we assume the header is RFC822-conformant in that */ 1514 /* we recognize escaping anywhere even though it is only */ 1515 /* supposed to be in quoted-strings, domain-literals, and comments. */ 1516 /* */ 1517 /* i'd use yylex or yyparse here, but we need to preserve */ 1518 /* things like comments, which i think it tosses away. */ 1519 /* */ 1520 /* we're not strictly RFC822 compliant. we misparse such nonsense as */ 1521 /* */ 1522 /* To: gre @ (Grace) plan9 . (Emlin) bell-labs.com */ 1523 /* */ 1524 /* make sure there's no whitespace in your addresses and */ 1525 /* you'll be fine. */ 1526 /* */ 1527 enum { 1528 Twhite, 1529 Tcomment, 1530 Twords, 1531 Tcomma, 1532 Tleftangle, 1533 Trightangle, 1534 Terror, 1535 Tend 1536 }; 1537 /*char *ty82[] = {"white", "comment", "words", "comma", "<", ">", "err", "end"}; */ 1538 #define ISWHITE(p) ((p)==' ' || (p)=='\t' || (p)=='\n' || (p)=='\r') 1539 int 1540 get822token(String **tok, char *p, char **pp) 1541 { 1542 char *op; 1543 int type; 1544 int quoting; 1545 1546 op = p; 1547 switch(*p){ 1548 case '\0': 1549 *tok = nil; 1550 *pp = nil; 1551 return Tend; 1552 1553 case ' ': /* get whitespace */ 1554 case '\t': 1555 case '\n': 1556 case '\r': 1557 type = Twhite; 1558 while(ISWHITE(*p)) 1559 p++; 1560 break; 1561 1562 case '(': /* get comment */ 1563 type = Tcomment; 1564 for(p++; *p && *p != ')'; p++) 1565 if(*p == '\\') { 1566 if(*(p+1) == '\0') { 1567 *tok = nil; 1568 return Terror; 1569 } 1570 p++; 1571 } 1572 1573 if(*p != ')') { 1574 *tok = nil; 1575 return Terror; 1576 } 1577 p++; 1578 break; 1579 case ',': 1580 type = Tcomma; 1581 p++; 1582 break; 1583 case '<': 1584 type = Tleftangle; 1585 p++; 1586 break; 1587 case '>': 1588 type = Trightangle; 1589 p++; 1590 break; 1591 default: /* bunch of letters, perhaps quoted strings tossed in */ 1592 type = Twords; 1593 quoting = 0; 1594 for(; *p && (quoting || (!ISWHITE(*p) && *p != '>' && *p != '<' && *p != ',')); p++) { 1595 if(*p == '"') 1596 quoting = !quoting; 1597 if(*p == '\\') { 1598 if(*(p+1) == '\0') { 1599 *tok = nil; 1600 return Terror; 1601 } 1602 p++; 1603 } 1604 } 1605 break; 1606 } 1607 1608 if(pp) 1609 *pp = p; 1610 *tok = s_copyn(op, p-op); 1611 return type; 1612 } 1613 1614 /* expand local aliases in an RFC822 mail line */ 1615 /* add list of expanded addresses to to. */ 1616 Addr* 1617 expandline(String **s, Addr *to) 1618 { 1619 Addr *na, *nto, *ap; 1620 char *p; 1621 int tok, inangle, hadangle, nword; 1622 String *os, *ns, *stok, *lastword, *sinceword; 1623 1624 os = s_copy(s_to_c(*s)); 1625 p = strchr(s_to_c(*s), ':'); 1626 assert(p != nil); 1627 p++; 1628 1629 ns = s_copyn(s_to_c(*s), p-s_to_c(*s)); 1630 stok = nil; 1631 nto = nil; 1632 /* */ 1633 /* the only valid mailbox namings are word */ 1634 /* and word* < addr > */ 1635 /* without comments this would be simple. */ 1636 /* we keep the following: */ 1637 /* lastword - current guess at the address */ 1638 /* sinceword - whitespace and comment seen since lastword */ 1639 /* */ 1640 lastword = s_new(); 1641 sinceword = s_new(); 1642 inangle = 0; 1643 nword = 0; 1644 hadangle = 0; 1645 for(;;) { 1646 stok = nil; 1647 switch(tok = get822token(&stok, p, &p)){ 1648 default: 1649 abort(); 1650 case Tcomma: 1651 case Tend: 1652 if(inangle) 1653 goto Error; 1654 if(nword != 1) 1655 goto Error; 1656 na = rexpand(newaddr(s_to_c(lastword))); 1657 s_append(ns, na->v); 1658 s_append(ns, s_to_c(sinceword)); 1659 for(ap=na->next; ap; ap=ap->next) { 1660 s_append(ns, ", "); 1661 s_append(ns, ap->v); 1662 } 1663 nto = concataddr(na, nto); 1664 if(tok == Tcomma){ 1665 s_append(ns, ","); 1666 s_free(stok); 1667 } 1668 if(tok == Tend) 1669 goto Break2; 1670 inangle = 0; 1671 nword = 0; 1672 hadangle = 0; 1673 s_reset(sinceword); 1674 s_reset(lastword); 1675 break; 1676 case Twhite: 1677 case Tcomment: 1678 s_append(sinceword, s_to_c(stok)); 1679 s_free(stok); 1680 break; 1681 case Trightangle: 1682 if(!inangle) 1683 goto Error; 1684 inangle = 0; 1685 hadangle = 1; 1686 s_append(sinceword, s_to_c(stok)); 1687 s_free(stok); 1688 break; 1689 case Twords: 1690 case Tleftangle: 1691 if(hadangle) 1692 goto Error; 1693 if(tok != Tleftangle && inangle && s_len(lastword)) 1694 goto Error; 1695 if(tok == Tleftangle) { 1696 inangle = 1; 1697 nword = 1; 1698 } 1699 s_append(ns, s_to_c(lastword)); 1700 s_append(ns, s_to_c(sinceword)); 1701 s_reset(sinceword); 1702 if(tok == Tleftangle) { 1703 s_append(ns, "<"); 1704 s_reset(lastword); 1705 } else { 1706 s_free(lastword); 1707 lastword = stok; 1708 } 1709 if(!inangle) 1710 nword++; 1711 break; 1712 case Terror: /* give up, use old string, addrs */ 1713 Error: 1714 ns = os; 1715 os = nil; 1716 freeaddrs(nto); 1717 nto = nil; 1718 werrstr("rfc822 syntax error"); 1719 rfc822syntaxerror = 1; 1720 goto Break2; 1721 } 1722 } 1723 Break2: 1724 s_free(*s); 1725 s_free(os); 1726 *s = ns; 1727 nto = concataddr(nto, to); 1728 return nto; 1729 } 1730 1731 void 1732 Bdrain(Biobuf *b) 1733 { 1734 char buf[8192]; 1735 1736 while(Bread(b, buf, sizeof buf) > 0) 1737 ; 1738 } 1739 1740 void 1741 readmimetypes(void) 1742 { 1743 Biobuf *b; 1744 char *p; 1745 char *f[6]; 1746 char type[256]; 1747 static int alloced, inuse; 1748 1749 if(mimetypes == 0){ 1750 alloced = 256; 1751 mimetypes = emalloc(alloced*sizeof(Ctype)); 1752 mimetypes[0].ext = ""; 1753 } 1754 1755 b = Bopen(unsharp("#9/lib/mimetype"), OREAD); 1756 if(b == nil) 1757 return; 1758 for(;;){ 1759 p = Brdline(b, '\n'); 1760 if(p == nil) 1761 break; 1762 p[Blinelen(b)-1] = 0; 1763 if(tokenize(p, f, 6) < 4) 1764 continue; 1765 if(strcmp(f[0], "-") == 0 || strcmp(f[1], "-") == 0 || strcmp(f[2], "-") == 0) 1766 continue; 1767 if(inuse + 1 >= alloced){ 1768 alloced += 256; 1769 mimetypes = erealloc(mimetypes, alloced*sizeof(Ctype)); 1770 } 1771 snprint(type, sizeof(type), "%s/%s", f[1], f[2]); 1772 mimetypes[inuse].type = estrdup(type); 1773 mimetypes[inuse].ext = estrdup(f[0]+1); 1774 mimetypes[inuse].display = !strcmp(type, "text/plain"); 1775 inuse++; 1776 1777 /* always make sure there's a terminator */ 1778 mimetypes[inuse].ext = 0; 1779 } 1780 Bterm(b); 1781 } 1782 1783 char* 1784 estrdup(char *x) 1785 { 1786 x = strdup(x); 1787 if(x == nil) 1788 fatal("memory"); 1789 return x; 1790 } 1791 1792 void* 1793 emalloc(int n) 1794 { 1795 void *x; 1796 1797 x = malloc(n); 1798 if(x == nil) 1799 fatal("%r"); 1800 return x; 1801 } 1802 1803 void* 1804 erealloc(void *x, int n) 1805 { 1806 x = realloc(x, n); 1807 if(x == nil) 1808 fatal("%r"); 1809 return x; 1810 } 1811 1812 /* */ 1813 /* Formatter for %" */ 1814 /* Use double quotes to protect white space, frogs, \ and " */ 1815 /* */ 1816 enum 1817 { 1818 Qok = 0, 1819 Qquote, 1820 Qbackslash 1821 }; 1822 1823 static int 1824 needtoquote(Rune r) 1825 { 1826 if(r >= Runeself) 1827 return Qquote; 1828 if(r <= ' ') 1829 return Qquote; 1830 if(r=='\\' || r=='"') 1831 return Qbackslash; 1832 return Qok; 1833 } 1834 1835 int 1836 doublequote(Fmt *f) 1837 { 1838 char *s, *t; 1839 int w, quotes; 1840 Rune r; 1841 1842 s = va_arg(f->args, char*); 1843 if(s == nil || *s == '\0') 1844 return fmtstrcpy(f, "\"\""); 1845 1846 quotes = 0; 1847 for(t=s; *t; t+=w){ 1848 w = chartorune(&r, t); 1849 quotes |= needtoquote(r); 1850 } 1851 if(quotes == 0) 1852 return fmtstrcpy(f, s); 1853 1854 fmtrune(f, '"'); 1855 for(t=s; *t; t+=w){ 1856 w = chartorune(&r, t); 1857 if(needtoquote(r) == Qbackslash) 1858 fmtrune(f, '\\'); 1859 fmtrune(f, r); 1860 } 1861 return fmtrune(f, '"'); 1862 } 1863 1864 int 1865 mountmail(void) 1866 { 1867 if(mailfs != nil) 1868 return 0; 1869 if((mailfs = nsmount("mail", nil)) == nil) 1870 return -1; 1871 return 0; 1872 } 1873 1874 int 1875 rfc2047fmt(Fmt *fmt) 1876 { 1877 char *s, *p; 1878 1879 s = va_arg(fmt->args, char*); 1880 if(s == nil) 1881 return fmtstrcpy(fmt, ""); 1882 for(p=s; *p; p++) 1883 if((uchar)*p >= 0x80) 1884 goto hard; 1885 return fmtstrcpy(fmt, s); 1886 1887 hard: 1888 fmtprint(fmt, "=?utf-8?q?"); 1889 for(p=s; *p; p++){ 1890 if(*p == ' ') 1891 fmtrune(fmt, '_'); 1892 else if(*p == '_' || *p == '\t' || *p == '=' || *p == '?' || (uchar)*p >= 0x80) 1893 fmtprint(fmt, "=%.2uX", (uchar)*p); 1894 else 1895 fmtrune(fmt, (uchar)*p); 1896 } 1897 fmtprint(fmt, "?="); 1898 return 0; 1899 } 1900 1901 char* 1902 mksubject(char *line) 1903 { 1904 char *p, *q; 1905 static char buf[1024]; 1906 1907 p = strchr(line, ':')+1; 1908 while(*p == ' ') 1909 p++; 1910 for(q=p; *q; q++) 1911 if((uchar)*q >= 0x80) 1912 goto hard; 1913 return line; 1914 1915 hard: 1916 snprint(buf, sizeof buf, "Subject: %U", p); 1917 return buf; 1918 }