19a747e4fSDavid du Colombier #include "common.h" 29a747e4fSDavid du Colombier #include <ctype.h> 39a747e4fSDavid du Colombier 49a747e4fSDavid du Colombier typedef struct Attach Attach; 59a747e4fSDavid du Colombier typedef struct Alias Alias; 69a747e4fSDavid du Colombier typedef struct Addr Addr; 79a747e4fSDavid du Colombier typedef struct Ctype Ctype; 89a747e4fSDavid du Colombier 99a747e4fSDavid du Colombier struct Attach { 109a747e4fSDavid du Colombier Attach *next; 119a747e4fSDavid du Colombier char *path; 129a747e4fSDavid du Colombier char *type; 139a747e4fSDavid du Colombier int inline; 149a747e4fSDavid du Colombier Ctype *ctype; 159a747e4fSDavid du Colombier }; 169a747e4fSDavid du Colombier 179a747e4fSDavid du Colombier struct Alias 189a747e4fSDavid du Colombier { 199a747e4fSDavid du Colombier Alias *next; 209a747e4fSDavid du Colombier int n; 21d9306527SDavid du Colombier Addr *addr; 229a747e4fSDavid du Colombier }; 239a747e4fSDavid du Colombier 249a747e4fSDavid du Colombier struct Addr 259a747e4fSDavid du Colombier { 269a747e4fSDavid du Colombier Addr *next; 279a747e4fSDavid du Colombier char *v; 289a747e4fSDavid du Colombier }; 299a747e4fSDavid du Colombier 309a747e4fSDavid du Colombier enum { 319a747e4fSDavid du Colombier Hfrom, 329a747e4fSDavid du Colombier Hto, 339a747e4fSDavid du Colombier Hcc, 349a747e4fSDavid du Colombier Hbcc, 359a747e4fSDavid du Colombier Hsender, 369a747e4fSDavid du Colombier Hreplyto, 376b6b9ac8SDavid du Colombier Hinreplyto, 389a747e4fSDavid du Colombier Hdate, 399a747e4fSDavid du Colombier Hsubject, 409a747e4fSDavid du Colombier Hmime, 419a747e4fSDavid du Colombier Hpriority, 429a747e4fSDavid du Colombier Hmsgid, 439a747e4fSDavid du Colombier Hcontent, 449a747e4fSDavid du Colombier Hx, 45379e2210SDavid du Colombier Hprecedence, 469a747e4fSDavid du Colombier Nhdr, 479a747e4fSDavid du Colombier }; 489a747e4fSDavid du Colombier 493ff48bf5SDavid du Colombier enum { 503ff48bf5SDavid du Colombier PGPsign = 1, 513ff48bf5SDavid du Colombier PGPencrypt = 2, 523ff48bf5SDavid du Colombier }; 533ff48bf5SDavid du Colombier 549a747e4fSDavid du Colombier char *hdrs[Nhdr] = { 559a747e4fSDavid du Colombier [Hfrom] "from:", 569a747e4fSDavid du Colombier [Hto] "to:", 579a747e4fSDavid du Colombier [Hcc] "cc:", 589a747e4fSDavid du Colombier [Hbcc] "bcc:", 599a747e4fSDavid du Colombier [Hreplyto] "reply-to:", 606b6b9ac8SDavid du Colombier [Hinreplyto] "in-reply-to:", 619a747e4fSDavid du Colombier [Hsender] "sender:", 629a747e4fSDavid du Colombier [Hdate] "date:", 639a747e4fSDavid du Colombier [Hsubject] "subject:", 649a747e4fSDavid du Colombier [Hpriority] "priority:", 659a747e4fSDavid du Colombier [Hmsgid] "message-id:", 669a747e4fSDavid du Colombier [Hmime] "mime-", 679a747e4fSDavid du Colombier [Hcontent] "content-", 689a747e4fSDavid du Colombier [Hx] "x-", 69379e2210SDavid du Colombier [Hprecedence] "precedence", 709a747e4fSDavid du Colombier }; 719a747e4fSDavid du Colombier 729a747e4fSDavid du Colombier struct Ctype { 739a747e4fSDavid du Colombier char *type; 749a747e4fSDavid du Colombier char *ext; 759a747e4fSDavid du Colombier int display; 769a747e4fSDavid du Colombier }; 779a747e4fSDavid du Colombier 789a747e4fSDavid du Colombier Ctype ctype[] = { 799a747e4fSDavid du Colombier { "text/plain", "txt", 1, }, 809a747e4fSDavid du Colombier { "text/html", "html", 1, }, 819a747e4fSDavid du Colombier { "text/html", "htm", 1, }, 829a747e4fSDavid du Colombier { "text/tab-separated-values", "tsv", 1, }, 839a747e4fSDavid du Colombier { "text/richtext", "rtx", 1, }, 849a747e4fSDavid du Colombier { "message/rfc822", "txt", 1, }, 859a747e4fSDavid du Colombier { "", 0, 0, }, 869a747e4fSDavid du Colombier }; 879a747e4fSDavid du Colombier 88d9306527SDavid du Colombier Ctype *mimetypes; 89d9306527SDavid du Colombier 909a747e4fSDavid du Colombier int pid = -1; 913ff48bf5SDavid du Colombier int pgppid = -1; 929a747e4fSDavid du Colombier 939a747e4fSDavid du Colombier Attach* mkattach(char*, char*, int); 94dc5a79c1SDavid du Colombier int readheaders(Biobuf*, int*, String**, Addr**, int); 959a747e4fSDavid du Colombier void body(Biobuf*, Biobuf*, int); 969a747e4fSDavid du Colombier char* mkboundary(void); 979a747e4fSDavid du Colombier int printdate(Biobuf*); 989a747e4fSDavid du Colombier int printfrom(Biobuf*); 999a747e4fSDavid du Colombier int printto(Biobuf*, Addr*); 1003ff48bf5SDavid du Colombier int printcc(Biobuf*, Addr*); 1019a747e4fSDavid du Colombier int printsubject(Biobuf*, char*); 102106486e8SDavid du Colombier int printinreplyto(Biobuf*, char*); 1033ff48bf5SDavid du Colombier int sendmail(Addr*, Addr*, int*, char*); 1049a747e4fSDavid du Colombier void attachment(Attach*, Biobuf*); 1059a747e4fSDavid du Colombier int cistrncmp(char*, char*, int); 1069a747e4fSDavid du Colombier int cistrcmp(char*, char*); 1073ff48bf5SDavid du Colombier char* waitforsubprocs(void); 1089a747e4fSDavid du Colombier int enc64(char*, int, uchar*, int); 1099a747e4fSDavid du Colombier Addr* expand(int, char**); 1109a747e4fSDavid du Colombier Alias* readaliases(void); 1119a747e4fSDavid du Colombier Addr* expandline(String**, Addr*); 1129a747e4fSDavid du Colombier void Bdrain(Biobuf*); 1139a747e4fSDavid du Colombier void freeaddr(Addr *); 1143ff48bf5SDavid du Colombier int pgpopts(char*); 1153ff48bf5SDavid du Colombier int pgpfilter(int*, int, int); 116d9306527SDavid du Colombier void readmimetypes(void); 117d9306527SDavid du Colombier char* estrdup(char*); 118d9306527SDavid du Colombier void* emalloc(int); 119d9306527SDavid du Colombier void* erealloc(void*, int); 120d9306527SDavid du Colombier void freeaddr(Addr*); 121d9306527SDavid du Colombier void freeaddrs(Addr*); 122d9306527SDavid du Colombier void freealias(Alias*); 123d9306527SDavid du Colombier void freealiases(Alias*); 124d9306527SDavid du Colombier int doublequote(Fmt*); 1259a747e4fSDavid du Colombier 126e288d156SDavid du Colombier int rflag, lbflag, xflag, holding, nflag, Fflag, eightflag, dflag; 1273ff48bf5SDavid du Colombier int pgpflag = 0; 1289a747e4fSDavid du Colombier char *user; 1299a747e4fSDavid du Colombier char *login; 1309a747e4fSDavid du Colombier Alias *aliases; 1319a747e4fSDavid du Colombier int rfc822syntaxerror; 1329a747e4fSDavid du Colombier char lastchar; 133106486e8SDavid du Colombier char *replymsg; 1349a747e4fSDavid du Colombier 1359a747e4fSDavid du Colombier enum 1369a747e4fSDavid du Colombier { 1379a747e4fSDavid du Colombier Ok = 0, 1389a747e4fSDavid du Colombier Nomessage = 1, 1399a747e4fSDavid du Colombier Nobody = 2, 1409a747e4fSDavid du Colombier Error = -1, 1419a747e4fSDavid du Colombier }; 1429a747e4fSDavid du Colombier 143d9306527SDavid du Colombier #pragma varargck type "Z" char* 144d9306527SDavid du Colombier 1459a747e4fSDavid du Colombier void 1469a747e4fSDavid du Colombier usage(void) 1479a747e4fSDavid du Colombier { 148106486e8SDavid du Colombier fprint(2, "usage: %s [-Fr#xn] [-s subject] [-c ccrecipient] [-t type] [-aA attachment] [-p[es]] [-R replymsg] -8 | recipient-list\n", 1499a747e4fSDavid du Colombier argv0); 1509a747e4fSDavid du Colombier exits("usage"); 1519a747e4fSDavid du Colombier } 1529a747e4fSDavid du Colombier 1539a747e4fSDavid du Colombier void 1549a747e4fSDavid du Colombier fatal(char *fmt, ...) 1559a747e4fSDavid du Colombier { 1569a747e4fSDavid du Colombier char buf[1024]; 1579a747e4fSDavid du Colombier va_list arg; 1589a747e4fSDavid du Colombier 1599a747e4fSDavid du Colombier if(pid >= 0) 1609a747e4fSDavid du Colombier postnote(PNPROC, pid, "die"); 1613ff48bf5SDavid du Colombier if(pgppid >= 0) 1623ff48bf5SDavid du Colombier postnote(PNPROC, pgppid, "die"); 1639a747e4fSDavid du Colombier 1649a747e4fSDavid du Colombier va_start(arg, fmt); 1659a747e4fSDavid du Colombier vseprint(buf, buf+sizeof(buf), fmt, arg); 1669a747e4fSDavid du Colombier va_end(arg); 1679a747e4fSDavid du Colombier fprint(2, "%s: %s\n", argv0, buf); 1689a747e4fSDavid du Colombier holdoff(holding); 1699a747e4fSDavid du Colombier exits(buf); 1709a747e4fSDavid du Colombier } 1719a747e4fSDavid du Colombier 1729a747e4fSDavid du Colombier void 1739a747e4fSDavid du Colombier main(int argc, char **argv) 1749a747e4fSDavid du Colombier { 1759a747e4fSDavid du Colombier Attach *first, **l, *a; 1769a747e4fSDavid du Colombier char *subject, *type, *boundary; 1779a747e4fSDavid du Colombier int flags, fd; 1789a747e4fSDavid du Colombier Biobuf in, out, *b; 1799a747e4fSDavid du Colombier Addr *to; 1803ff48bf5SDavid du Colombier Addr *cc; 1819a747e4fSDavid du Colombier String *file, *hdrstring; 1829a747e4fSDavid du Colombier int noinput, headersrv; 1833ff48bf5SDavid du Colombier int ccargc; 1843ff48bf5SDavid du Colombier char *ccargv[32]; 1859a747e4fSDavid du Colombier 1869a747e4fSDavid du Colombier noinput = 0; 1879a747e4fSDavid du Colombier subject = nil; 1889a747e4fSDavid du Colombier first = nil; 1899a747e4fSDavid du Colombier l = &first; 1909a747e4fSDavid du Colombier type = nil; 1919a747e4fSDavid du Colombier hdrstring = nil; 1923ff48bf5SDavid du Colombier ccargc = 0; 1933ff48bf5SDavid du Colombier 194d9306527SDavid du Colombier quotefmtinstall(); 195d9306527SDavid du Colombier fmtinstall('Z', doublequote); 196d9306527SDavid du Colombier 1979a747e4fSDavid du Colombier ARGBEGIN{ 1989a747e4fSDavid du Colombier case 't': 1999a747e4fSDavid du Colombier type = ARGF(); 2009a747e4fSDavid du Colombier if(type == nil) 2019a747e4fSDavid du Colombier usage(); 2029a747e4fSDavid du Colombier break; 2039a747e4fSDavid du Colombier case 'a': 2049a747e4fSDavid du Colombier flags = 0; 2059a747e4fSDavid du Colombier goto aflag; 2069a747e4fSDavid du Colombier case 'A': 2079a747e4fSDavid du Colombier flags = 1; 2089a747e4fSDavid du Colombier aflag: 2099a747e4fSDavid du Colombier a = mkattach(ARGF(), type, flags); 2109a747e4fSDavid du Colombier if(a == nil) 2119a747e4fSDavid du Colombier exits("bad args"); 2129a747e4fSDavid du Colombier type = nil; 2139a747e4fSDavid du Colombier *l = a; 2149a747e4fSDavid du Colombier l = &a->next; 2159a747e4fSDavid du Colombier break; 2163ff48bf5SDavid du Colombier case 'C': 2173ff48bf5SDavid du Colombier if(ccargc >= nelem(ccargv)-1) 2183ff48bf5SDavid du Colombier sysfatal("too many cc's"); 2193ff48bf5SDavid du Colombier ccargv[ccargc] = ARGF(); 2203ff48bf5SDavid du Colombier if(ccargv[ccargc] == nil) 2213ff48bf5SDavid du Colombier usage(); 2223ff48bf5SDavid du Colombier ccargc++; 2233ff48bf5SDavid du Colombier break; 224106486e8SDavid du Colombier case 'R': 225106486e8SDavid du Colombier replymsg = ARGF(); 226106486e8SDavid du Colombier break; 2279a747e4fSDavid du Colombier case 's': 2289a747e4fSDavid du Colombier subject = ARGF(); 2299a747e4fSDavid du Colombier break; 2309a747e4fSDavid du Colombier case 'F': 2319a747e4fSDavid du Colombier Fflag = 1; // file message 2329a747e4fSDavid du Colombier break; 2339a747e4fSDavid du Colombier case 'r': 2349a747e4fSDavid du Colombier rflag = 1; // for sendmail 2359a747e4fSDavid du Colombier break; 236e288d156SDavid du Colombier case 'd': 237e288d156SDavid du Colombier dflag = 1; // for sendmail 238e288d156SDavid du Colombier break; 2399a747e4fSDavid du Colombier case '#': 2409a747e4fSDavid du Colombier lbflag = 1; // for sendmail 2419a747e4fSDavid du Colombier break; 2429a747e4fSDavid du Colombier case 'x': 2439a747e4fSDavid du Colombier xflag = 1; // for sendmail 2449a747e4fSDavid du Colombier break; 2459a747e4fSDavid du Colombier case 'n': // no standard input 2469a747e4fSDavid du Colombier nflag = 1; 2479a747e4fSDavid du Colombier break; 2489a747e4fSDavid du Colombier case '8': // read recipients from rfc822 header 2499a747e4fSDavid du Colombier eightflag = 1; 2509a747e4fSDavid du Colombier break; 2513ff48bf5SDavid du Colombier case 'p': // pgp flag: encrypt, sign, or both 2523ff48bf5SDavid du Colombier if(pgpopts(ARGF()) < 0) 2533ff48bf5SDavid du Colombier sysfatal("bad pgp options"); 2543ff48bf5SDavid du Colombier break; 2559a747e4fSDavid du Colombier default: 2569a747e4fSDavid du Colombier usage(); 2579a747e4fSDavid du Colombier break; 2589a747e4fSDavid du Colombier }ARGEND; 2599a747e4fSDavid du Colombier 2609a747e4fSDavid du Colombier login = getlog(); 2619a747e4fSDavid du Colombier user = getenv("upasname"); 2629a747e4fSDavid du Colombier if(user == nil || *user == 0) 2639a747e4fSDavid du Colombier user = login; 2649a747e4fSDavid du Colombier if(user == nil || *user == 0) 2659a747e4fSDavid du Colombier sysfatal("can't read user name"); 2669a747e4fSDavid du Colombier 2679a747e4fSDavid du Colombier if(Binit(&in, 0, OREAD) < 0) 2689a747e4fSDavid du Colombier sysfatal("can't Binit 0: %r"); 2699a747e4fSDavid du Colombier 2709a747e4fSDavid du Colombier if(nflag && eightflag) 2719a747e4fSDavid du Colombier sysfatal("can't use both -n and -8"); 2729a747e4fSDavid du Colombier if(eightflag && argc >= 1) 2739a747e4fSDavid du Colombier usage(); 2749a747e4fSDavid du Colombier else if(!eightflag && argc < 1) 2759a747e4fSDavid du Colombier usage(); 2769a747e4fSDavid du Colombier 2779a747e4fSDavid du Colombier aliases = readaliases(); 2783ff48bf5SDavid du Colombier if(!eightflag){ 2799a747e4fSDavid du Colombier to = expand(argc, argv); 2803ff48bf5SDavid du Colombier cc = expand(ccargc, ccargv); 2813ff48bf5SDavid du Colombier } else { 2829a747e4fSDavid du Colombier to = nil; 2833ff48bf5SDavid du Colombier cc = nil; 2843ff48bf5SDavid du Colombier } 2859a747e4fSDavid du Colombier 2869a747e4fSDavid du Colombier flags = 0; 2879a747e4fSDavid du Colombier headersrv = Nomessage; 288e288d156SDavid du Colombier if(!nflag && !xflag && !lbflag &&!dflag) { 2899a747e4fSDavid du Colombier // pass through headers, keeping track of which we've seen, 2909a747e4fSDavid du Colombier // perhaps building to list. 2919a747e4fSDavid du Colombier holding = holdon(); 292dc5a79c1SDavid du Colombier headersrv = readheaders(&in, &flags, &hdrstring, eightflag ? &to : nil, 1); 2939a747e4fSDavid du Colombier if(rfc822syntaxerror){ 2949a747e4fSDavid du Colombier Bdrain(&in); 2959a747e4fSDavid du Colombier fatal("rfc822 syntax error, message not sent"); 2969a747e4fSDavid du Colombier } 2979a747e4fSDavid du Colombier if(to == nil){ 2989a747e4fSDavid du Colombier Bdrain(&in); 2999a747e4fSDavid du Colombier fatal("no addresses found, message not sent"); 3009a747e4fSDavid du Colombier } 3019a747e4fSDavid du Colombier 3029a747e4fSDavid du Colombier switch(headersrv){ 3039a747e4fSDavid du Colombier case Error: // error 3049a747e4fSDavid du Colombier fatal("reading"); 3059a747e4fSDavid du Colombier break; 3069a747e4fSDavid du Colombier case Nomessage: // no message, just exit mimicking old behavior 3079a747e4fSDavid du Colombier noinput = 1; 3089a747e4fSDavid du Colombier if(first == nil) 3099a747e4fSDavid du Colombier exits(0); 3109a747e4fSDavid du Colombier break; 3119a747e4fSDavid du Colombier } 3129a747e4fSDavid du Colombier } 3139a747e4fSDavid du Colombier 3143ff48bf5SDavid du Colombier fd = sendmail(to, cc, &pid, Fflag ? argv[0] : nil); 3159a747e4fSDavid du Colombier if(fd < 0) 3169a747e4fSDavid du Colombier sysfatal("execing sendmail: %r\n:"); 317e288d156SDavid du Colombier if(xflag || lbflag || dflag){ 3189a747e4fSDavid du Colombier close(fd); 3193ff48bf5SDavid du Colombier exits(waitforsubprocs()); 3209a747e4fSDavid du Colombier } 3219a747e4fSDavid du Colombier 3229a747e4fSDavid du Colombier if(Binit(&out, fd, OWRITE) < 0) 3239a747e4fSDavid du Colombier fatal("can't Binit 1: %r"); 3249a747e4fSDavid du Colombier 3259a747e4fSDavid du Colombier if(!nflag){ 3269a747e4fSDavid du Colombier if(Bwrite(&out, s_to_c(hdrstring), s_len(hdrstring)) != s_len(hdrstring)) 3279a747e4fSDavid du Colombier fatal("write error"); 3289a747e4fSDavid du Colombier s_free(hdrstring); 3299a747e4fSDavid du Colombier hdrstring = nil; 3309a747e4fSDavid du Colombier 3319a747e4fSDavid du Colombier // read user's standard headers 3329a747e4fSDavid du Colombier file = s_new(); 3339a747e4fSDavid du Colombier mboxpath("headers", user, file, 0); 3349a747e4fSDavid du Colombier b = Bopen(s_to_c(file), OREAD); 3359a747e4fSDavid du Colombier if(b != nil){ 336dc5a79c1SDavid du Colombier switch(readheaders(b, &flags, &hdrstring, nil, 0)){ 3379a747e4fSDavid du Colombier case Error: // error 3389a747e4fSDavid du Colombier fatal("reading"); 3399a747e4fSDavid du Colombier } 3409a747e4fSDavid du Colombier Bterm(b); 3419a747e4fSDavid du Colombier if(Bwrite(&out, s_to_c(hdrstring), s_len(hdrstring)) != s_len(hdrstring)) 3429a747e4fSDavid du Colombier fatal("write error"); 3439a747e4fSDavid du Colombier s_free(hdrstring); 3449a747e4fSDavid du Colombier hdrstring = nil; 3459a747e4fSDavid du Colombier } 3469a747e4fSDavid du Colombier } 3479a747e4fSDavid du Colombier 3489a747e4fSDavid du Colombier // add any headers we need 3499a747e4fSDavid du Colombier if((flags & (1<<Hdate)) == 0) 3509a747e4fSDavid du Colombier if(printdate(&out) < 0) 3519a747e4fSDavid du Colombier fatal("writing"); 3529a747e4fSDavid du Colombier if((flags & (1<<Hfrom)) == 0) 3539a747e4fSDavid du Colombier if(printfrom(&out) < 0) 3549a747e4fSDavid du Colombier fatal("writing"); 3559a747e4fSDavid du Colombier if((flags & (1<<Hto)) == 0) 3569a747e4fSDavid du Colombier if(printto(&out, to) < 0) 3579a747e4fSDavid du Colombier fatal("writing"); 3583ff48bf5SDavid du Colombier if((flags & (1<<Hcc)) == 0) 3593ff48bf5SDavid du Colombier if(printcc(&out, cc) < 0) 3603ff48bf5SDavid du Colombier fatal("writing"); 3619a747e4fSDavid du Colombier if((flags & (1<<Hsubject)) == 0 && subject != nil) 3629a747e4fSDavid du Colombier if(printsubject(&out, subject) < 0) 3639a747e4fSDavid du Colombier fatal("writing"); 364106486e8SDavid du Colombier if(replymsg != nil) 365106486e8SDavid du Colombier if(printinreplyto(&out, replymsg) < 0) 366106486e8SDavid du Colombier fatal("writing"); 3679a747e4fSDavid du Colombier Bprint(&out, "MIME-Version: 1.0\n"); 3689a747e4fSDavid du Colombier 3693ff48bf5SDavid du Colombier if(pgpflag){ // interpose pgp process between us and sendmail to handle body 3703ff48bf5SDavid du Colombier Bflush(&out); 3713ff48bf5SDavid du Colombier Bterm(&out); 3723ff48bf5SDavid du Colombier fd = pgpfilter(&pgppid, fd, pgpflag); 3733ff48bf5SDavid du Colombier if(Binit(&out, fd, OWRITE) < 0) 3743ff48bf5SDavid du Colombier fatal("can't Binit 1: %r"); 3753ff48bf5SDavid du Colombier } 3763ff48bf5SDavid du Colombier 3779a747e4fSDavid du Colombier // if attachments, stick in multipart headers 3789a747e4fSDavid du Colombier boundary = nil; 3799a747e4fSDavid du Colombier if(first != nil){ 3809a747e4fSDavid du Colombier boundary = mkboundary(); 3819a747e4fSDavid du Colombier Bprint(&out, "Content-Type: multipart/mixed;\n"); 3829a747e4fSDavid du Colombier Bprint(&out, "\tboundary=\"%s\"\n\n", boundary); 3839a747e4fSDavid du Colombier Bprint(&out, "This is a multi-part message in MIME format.\n"); 3849a747e4fSDavid du Colombier Bprint(&out, "--%s\n", boundary); 3859a747e4fSDavid du Colombier Bprint(&out, "Content-Disposition: inline\n"); 3869a747e4fSDavid du Colombier } 3879a747e4fSDavid du Colombier 3889a747e4fSDavid du Colombier if(!nflag){ 3899a747e4fSDavid du Colombier if(!noinput && headersrv == Ok){ 3909a747e4fSDavid du Colombier body(&in, &out, 1); 3919a747e4fSDavid du Colombier } 3929a747e4fSDavid du Colombier } else 3939a747e4fSDavid du Colombier Bprint(&out, "\n"); 3949a747e4fSDavid du Colombier holdoff(holding); 3959a747e4fSDavid du Colombier 3969a747e4fSDavid du Colombier Bflush(&out); 3979a747e4fSDavid du Colombier for(a = first; a != nil; a = a->next){ 3989a747e4fSDavid du Colombier if(lastchar != '\n') 3999a747e4fSDavid du Colombier Bprint(&out, "\n"); 4009a747e4fSDavid du Colombier Bprint(&out, "--%s\n", boundary); 4019a747e4fSDavid du Colombier attachment(a, &out); 4029a747e4fSDavid du Colombier } 4039a747e4fSDavid du Colombier 4047a02f3c0SDavid du Colombier if(first != nil){ 4057a02f3c0SDavid du Colombier if(lastchar != '\n') 4067a02f3c0SDavid du Colombier Bprint(&out, "\n"); 4079a747e4fSDavid du Colombier Bprint(&out, "--%s--\n", boundary); 4087a02f3c0SDavid du Colombier } 4099a747e4fSDavid du Colombier 4109a747e4fSDavid du Colombier Bterm(&out); 4119a747e4fSDavid du Colombier close(fd); 4123ff48bf5SDavid du Colombier exits(waitforsubprocs()); 4133ff48bf5SDavid du Colombier } 4143ff48bf5SDavid du Colombier 4153ff48bf5SDavid du Colombier // evaluate pgp option string 4163ff48bf5SDavid du Colombier int 4173ff48bf5SDavid du Colombier pgpopts(char *s) 4183ff48bf5SDavid du Colombier { 4193ff48bf5SDavid du Colombier if(s == nil || s[0] == '\0') 4203ff48bf5SDavid du Colombier return -1; 4213ff48bf5SDavid du Colombier while(*s){ 4223ff48bf5SDavid du Colombier switch(*s++){ 4233ff48bf5SDavid du Colombier case 's': case 'S': 4243ff48bf5SDavid du Colombier pgpflag |= PGPsign; 4253ff48bf5SDavid du Colombier break; 4263ff48bf5SDavid du Colombier case 'e': case 'E': 4273ff48bf5SDavid du Colombier pgpflag |= PGPencrypt; 4283ff48bf5SDavid du Colombier break; 4293ff48bf5SDavid du Colombier default: 4303ff48bf5SDavid du Colombier return -1; 4313ff48bf5SDavid du Colombier } 4323ff48bf5SDavid du Colombier } 4333ff48bf5SDavid du Colombier return 0; 4349a747e4fSDavid du Colombier } 4359a747e4fSDavid du Colombier 4369a747e4fSDavid du Colombier // read headers from stdin into a String, expanding local aliases, 4379a747e4fSDavid du Colombier // keep track of which headers are there, which addresses we have 4389a747e4fSDavid du Colombier // remove Bcc: line. 4399a747e4fSDavid du Colombier int 440dc5a79c1SDavid du Colombier readheaders(Biobuf *in, int *fp, String **sp, Addr **top, int strict) 4419a747e4fSDavid du Colombier { 4429a747e4fSDavid du Colombier Addr *to; 4439a747e4fSDavid du Colombier String *s, *sline; 4449a747e4fSDavid du Colombier char *p; 4459a747e4fSDavid du Colombier int i, seen, hdrtype; 4469a747e4fSDavid du Colombier 4479a747e4fSDavid du Colombier s = s_new(); 4489a747e4fSDavid du Colombier sline = nil; 4499a747e4fSDavid du Colombier to = nil; 4509a747e4fSDavid du Colombier hdrtype = -1; 4519a747e4fSDavid du Colombier seen = 0; 4529a747e4fSDavid du Colombier for(;;) { 4539a747e4fSDavid du Colombier if((p = Brdline(in, '\n')) != nil) { 4549a747e4fSDavid du Colombier seen = 1; 4559a747e4fSDavid du Colombier p[Blinelen(in)-1] = 0; 4566b6b9ac8SDavid du Colombier 4576b6b9ac8SDavid du Colombier // coalesce multiline headers 4589a747e4fSDavid du Colombier if((*p == ' ' || *p == '\t') && sline){ 4599a747e4fSDavid du Colombier s_append(sline, "\n"); 4609a747e4fSDavid du Colombier s_append(sline, p); 4619a747e4fSDavid du Colombier p[Blinelen(in)-1] = '\n'; 4629a747e4fSDavid du Colombier continue; 4639a747e4fSDavid du Colombier } 4649a747e4fSDavid du Colombier } 4659a747e4fSDavid du Colombier 4666b6b9ac8SDavid du Colombier // process the current header, it's all been read 4679a747e4fSDavid du Colombier if(sline) { 4689a747e4fSDavid du Colombier assert(hdrtype != -1); 4699a747e4fSDavid du Colombier if(top){ 4709a747e4fSDavid du Colombier switch(hdrtype){ 4719a747e4fSDavid du Colombier case Hto: 4729a747e4fSDavid du Colombier case Hcc: 4739a747e4fSDavid du Colombier case Hbcc: 4749a747e4fSDavid du Colombier to = expandline(&sline, to); 4759a747e4fSDavid du Colombier break; 4769a747e4fSDavid du Colombier } 4779a747e4fSDavid du Colombier } 4789a747e4fSDavid du Colombier if(top==nil || hdrtype!=Hbcc){ 4799a747e4fSDavid du Colombier s_append(s, s_to_c(sline)); 4809a747e4fSDavid du Colombier s_append(s, "\n"); 4819a747e4fSDavid du Colombier } 4829a747e4fSDavid du Colombier s_free(sline); 4839a747e4fSDavid du Colombier sline = nil; 4849a747e4fSDavid du Colombier } 4859a747e4fSDavid du Colombier 4869a747e4fSDavid du Colombier if(p == nil) 4879a747e4fSDavid du Colombier break; 4889a747e4fSDavid du Colombier 4896b6b9ac8SDavid du Colombier // if no :, it's not a header, seek back and break 4909a747e4fSDavid du Colombier if(strchr(p, ':') == nil){ 4919a747e4fSDavid du Colombier p[Blinelen(in)-1] = '\n'; 4929a747e4fSDavid du Colombier Bseek(in, -Blinelen(in), 1); 4939a747e4fSDavid du Colombier break; 4949a747e4fSDavid du Colombier } 4959a747e4fSDavid du Colombier 4969a747e4fSDavid du Colombier sline = s_copy(p); 4979a747e4fSDavid du Colombier 4986b6b9ac8SDavid du Colombier // classify the header. If we don't recognize it, break. This is 4996b6b9ac8SDavid du Colombier // to take care of user's that start messages with lines that contain 5006b6b9ac8SDavid du Colombier // ':'s but that aren't headers. This is a bit hokey. Since I decided 5016b6b9ac8SDavid du Colombier // to let users type headers, I need some way to distinguish. Therefore, 5026b6b9ac8SDavid du Colombier // marshal tries to know all likely headers and will indeed screw up if 5036b6b9ac8SDavid du Colombier // the user types an unlikely one. -- presotto 5049a747e4fSDavid du Colombier hdrtype = -1; 5059a747e4fSDavid du Colombier for(i = 0; i < nelem(hdrs); i++){ 5069a747e4fSDavid du Colombier if(cistrncmp(hdrs[i], p, strlen(hdrs[i])) == 0){ 5079a747e4fSDavid du Colombier *fp |= 1<<i; 5089a747e4fSDavid du Colombier hdrtype = i; 5099a747e4fSDavid du Colombier break; 5109a747e4fSDavid du Colombier } 5119a747e4fSDavid du Colombier } 512e58cd47aSDavid du Colombier if(strict){ 5139a747e4fSDavid du Colombier if(hdrtype == -1){ 5149a747e4fSDavid du Colombier p[Blinelen(in)-1] = '\n'; 5159a747e4fSDavid du Colombier Bseek(in, -Blinelen(in), 1); 5169a747e4fSDavid du Colombier break; 5179a747e4fSDavid du Colombier } 518dc5a79c1SDavid du Colombier } else 519dc5a79c1SDavid du Colombier hdrtype = 0; 5209a747e4fSDavid du Colombier p[Blinelen(in)-1] = '\n'; 5219a747e4fSDavid du Colombier } 5229a747e4fSDavid du Colombier 5239a747e4fSDavid du Colombier *sp = s; 5249a747e4fSDavid du Colombier if(top) 5259a747e4fSDavid du Colombier *top = to; 5269a747e4fSDavid du Colombier 5276b6b9ac8SDavid du Colombier if(seen == 0){ 5286b6b9ac8SDavid du Colombier if(Blinelen(in) == 0) 5299a747e4fSDavid du Colombier return Nomessage; 5306b6b9ac8SDavid du Colombier else 5316b6b9ac8SDavid du Colombier return Ok; 5326b6b9ac8SDavid du Colombier } 5339a747e4fSDavid du Colombier if(p == nil) 5349a747e4fSDavid du Colombier return Nobody; 5359a747e4fSDavid du Colombier return Ok; 5369a747e4fSDavid du Colombier } 5379a747e4fSDavid du Colombier 5387a02f3c0SDavid du Colombier // pass the body to sendmail, make sure body starts and ends with a newline 5399a747e4fSDavid du Colombier void 5409a747e4fSDavid du Colombier body(Biobuf *in, Biobuf *out, int docontenttype) 5419a747e4fSDavid du Colombier { 5429a747e4fSDavid du Colombier char *buf, *p; 5439a747e4fSDavid du Colombier int i, n, len; 5449a747e4fSDavid du Colombier 5459a747e4fSDavid du Colombier n = 0; 5469a747e4fSDavid du Colombier len = 16*1024; 547d9306527SDavid du Colombier buf = emalloc(len); 5489a747e4fSDavid du Colombier 5499a747e4fSDavid du Colombier // first char must be newline 5509a747e4fSDavid du Colombier i = Bgetc(in); 551d9306527SDavid du Colombier if(i > 0){ 5529a747e4fSDavid du Colombier if(i != '\n') 5539a747e4fSDavid du Colombier buf[n++] = '\n'; 5549a747e4fSDavid du Colombier buf[n++] = i; 555d9306527SDavid du Colombier } else { 556d9306527SDavid du Colombier buf[n++] = '\n'; 557d9306527SDavid du Colombier } 5589a747e4fSDavid du Colombier 5599a747e4fSDavid du Colombier // read into memory 5609a747e4fSDavid du Colombier if(docontenttype){ 5619a747e4fSDavid du Colombier while(docontenttype){ 5629a747e4fSDavid du Colombier if(n == len){ 5639a747e4fSDavid du Colombier len += len>>2; 5649a747e4fSDavid du Colombier buf = realloc(buf, len); 5659a747e4fSDavid du Colombier if(buf == nil) 5669a747e4fSDavid du Colombier sysfatal("%r"); 5679a747e4fSDavid du Colombier } 5689a747e4fSDavid du Colombier p = buf+n; 5699a747e4fSDavid du Colombier i = Bread(in, p, len - n); 5709a747e4fSDavid du Colombier if(i < 0) 5719a747e4fSDavid du Colombier fatal("input error2"); 5729a747e4fSDavid du Colombier if(i == 0) 5739a747e4fSDavid du Colombier break; 5749a747e4fSDavid du Colombier n += i; 5759a747e4fSDavid du Colombier for(; i > 0; i--) 5767a02f3c0SDavid du Colombier if((*p++ & 0x80) && docontenttype){ 5779a747e4fSDavid du Colombier Bprint(out, "Content-Type: text/plain; charset=\"UTF-8\"\n"); 5789a747e4fSDavid du Colombier Bprint(out, "Content-Transfer-Encoding: 8bit\n"); 5799a747e4fSDavid du Colombier docontenttype = 0; 5809a747e4fSDavid du Colombier break; 5819a747e4fSDavid du Colombier } 5829a747e4fSDavid du Colombier } 5839a747e4fSDavid du Colombier if(docontenttype){ 5849a747e4fSDavid du Colombier Bprint(out, "Content-Type: text/plain; charset=\"US-ASCII\"\n"); 5859a747e4fSDavid du Colombier Bprint(out, "Content-Transfer-Encoding: 7bit\n"); 5869a747e4fSDavid du Colombier } 5879a747e4fSDavid du Colombier } 5889a747e4fSDavid du Colombier 5899a747e4fSDavid du Colombier // write what we already read 5909a747e4fSDavid du Colombier if(Bwrite(out, buf, n) < 0) 5919a747e4fSDavid du Colombier fatal("output error"); 5929a747e4fSDavid du Colombier if(n > 0) 5939a747e4fSDavid du Colombier lastchar = buf[n-1]; 5949a747e4fSDavid du Colombier else 5959a747e4fSDavid du Colombier lastchar = '\n'; 5969a747e4fSDavid du Colombier 5979a747e4fSDavid du Colombier 5989a747e4fSDavid du Colombier // pass the rest 5999a747e4fSDavid du Colombier for(;;){ 6009a747e4fSDavid du Colombier n = Bread(in, buf, len); 6019a747e4fSDavid du Colombier if(n < 0) 6029a747e4fSDavid du Colombier fatal("input error2"); 6039a747e4fSDavid du Colombier if(n == 0) 6049a747e4fSDavid du Colombier break; 6059a747e4fSDavid du Colombier if(Bwrite(out, buf, n) < 0) 6069a747e4fSDavid du Colombier fatal("output error"); 6079a747e4fSDavid du Colombier lastchar = buf[n-1]; 6089a747e4fSDavid du Colombier } 6099a747e4fSDavid du Colombier } 6109a747e4fSDavid du Colombier 6119a747e4fSDavid du Colombier // pass the body to sendmail encoding with base64 6129a747e4fSDavid du Colombier // 6139a747e4fSDavid du Colombier // the size of buf is very important to enc64. Anything other than 6149a747e4fSDavid du Colombier // a multiple of 3 will cause enc64 to output a termination sequence. 6159a747e4fSDavid du Colombier // To ensure that a full buf corresponds to a multiple of complete lines, 6169a747e4fSDavid du Colombier // we make buf a multiple of 3*18 since that's how many enc64 sticks on 6179a747e4fSDavid du Colombier // a single line. This avoids short lines in the output which is pleasing 6189a747e4fSDavid du Colombier // but not necessary. 6199a747e4fSDavid du Colombier // 6209a747e4fSDavid du Colombier void 6219a747e4fSDavid du Colombier body64(Biobuf *in, Biobuf *out) 6229a747e4fSDavid du Colombier { 6239a747e4fSDavid du Colombier uchar buf[3*18*54]; 6249a747e4fSDavid du Colombier char obuf[3*18*54*2]; 6259a747e4fSDavid du Colombier int m, n; 6269a747e4fSDavid du Colombier 6279a747e4fSDavid du Colombier Bprint(out, "\n"); 6289a747e4fSDavid du Colombier for(;;){ 6299a747e4fSDavid du Colombier n = Bread(in, buf, sizeof(buf)); 6309a747e4fSDavid du Colombier if(n < 0) 6319a747e4fSDavid du Colombier fatal("input error"); 6329a747e4fSDavid du Colombier if(n == 0) 6339a747e4fSDavid du Colombier break; 6349a747e4fSDavid du Colombier m = enc64(obuf, sizeof(obuf), buf, n); 6359a747e4fSDavid du Colombier if(Bwrite(out, obuf, m) < 0) 6369a747e4fSDavid du Colombier fatal("output error"); 6379a747e4fSDavid du Colombier } 6389a747e4fSDavid du Colombier lastchar = '\n'; 6399a747e4fSDavid du Colombier } 6409a747e4fSDavid du Colombier 6419a747e4fSDavid du Colombier // pass message to sendmail, make sure body starts with a newline 6429a747e4fSDavid du Colombier void 6439a747e4fSDavid du Colombier copy(Biobuf *in, Biobuf *out) 6449a747e4fSDavid du Colombier { 6459a747e4fSDavid du Colombier char buf[4*1024]; 6469a747e4fSDavid du Colombier int n; 6479a747e4fSDavid du Colombier 6489a747e4fSDavid du Colombier for(;;){ 6499a747e4fSDavid du Colombier n = Bread(in, buf, sizeof(buf)); 6509a747e4fSDavid du Colombier if(n < 0) 6519a747e4fSDavid du Colombier fatal("input error"); 6529a747e4fSDavid du Colombier if(n == 0) 6539a747e4fSDavid du Colombier break; 6549a747e4fSDavid du Colombier if(Bwrite(out, buf, n) < 0) 6559a747e4fSDavid du Colombier fatal("output error"); 6569a747e4fSDavid du Colombier } 6579a747e4fSDavid du Colombier } 6589a747e4fSDavid du Colombier 6599a747e4fSDavid du Colombier void 6609a747e4fSDavid du Colombier attachment(Attach *a, Biobuf *out) 6619a747e4fSDavid du Colombier { 6629a747e4fSDavid du Colombier Biobuf *f; 6639a747e4fSDavid du Colombier char *p; 6649a747e4fSDavid du Colombier 6659a747e4fSDavid du Colombier // if it's already mime encoded, just copy 6669a747e4fSDavid du Colombier if(strcmp(a->type, "mime") == 0){ 6679a747e4fSDavid du Colombier f = Bopen(a->path, OREAD); 6689a747e4fSDavid du Colombier if(f == nil){ 6699a747e4fSDavid du Colombier /* hack: give marshal time to stdin, before we kill it (for dead.letter) */ 6709a747e4fSDavid du Colombier sleep(500); 6719a747e4fSDavid du Colombier postnote(PNPROC, pid, "interrupt"); 6729a747e4fSDavid du Colombier sysfatal("opening %s: %r", a->path); 6739a747e4fSDavid du Colombier } 6749a747e4fSDavid du Colombier copy(f, out); 6759a747e4fSDavid du Colombier Bterm(f); 6769a747e4fSDavid du Colombier } 6779a747e4fSDavid du Colombier 6789a747e4fSDavid du Colombier // if it's not already mime encoded ... 6799a747e4fSDavid du Colombier if(strcmp(a->type, "text/plain") != 0) 6809a747e4fSDavid du Colombier Bprint(out, "Content-Type: %s\n", a->type); 6819a747e4fSDavid du Colombier 6829a747e4fSDavid du Colombier if(a->inline){ 6839a747e4fSDavid du Colombier Bprint(out, "Content-Disposition: inline\n"); 6849a747e4fSDavid du Colombier } else { 6859a747e4fSDavid du Colombier p = strrchr(a->path, '/'); 6869a747e4fSDavid du Colombier if(p == nil) 6879a747e4fSDavid du Colombier p = a->path; 6889a747e4fSDavid du Colombier else 6899a747e4fSDavid du Colombier p++; 690d9306527SDavid du Colombier Bprint(out, "Content-Disposition: attachment; filename=%Z\n", p); 6919a747e4fSDavid du Colombier } 6929a747e4fSDavid du Colombier 6939a747e4fSDavid du Colombier f = Bopen(a->path, OREAD); 6949a747e4fSDavid du Colombier if(f == nil){ 6959a747e4fSDavid du Colombier /* hack: give marshal time to stdin, before we kill it (for dead.letter) */ 6969a747e4fSDavid du Colombier sleep(500); 6979a747e4fSDavid du Colombier postnote(PNPROC, pid, "interrupt"); 6989a747e4fSDavid du Colombier sysfatal("opening %s: %r", a->path); 6999a747e4fSDavid du Colombier } 700d9306527SDavid du Colombier 701d9306527SDavid du Colombier /* dump our local 'From ' line when passing along mail messages */ 702d9306527SDavid du Colombier if(strcmp(a->type, "message/rfc822") == 0){ 703d9306527SDavid du Colombier p = Brdline(f, '\n'); 704d9306527SDavid du Colombier if(strncmp(p, "From ", 5) != 0) 705d9306527SDavid du Colombier Bseek(f, 0, 0); 706d9306527SDavid du Colombier } 7079a747e4fSDavid du Colombier if(a->ctype->display){ 7089a747e4fSDavid du Colombier body(f, out, strcmp(a->type, "text/plain") == 0); 7099a747e4fSDavid du Colombier } else { 7109a747e4fSDavid du Colombier Bprint(out, "Content-Transfer-Encoding: base64\n"); 7119a747e4fSDavid du Colombier body64(f, out); 7129a747e4fSDavid du Colombier } 7139a747e4fSDavid du Colombier Bterm(f); 7149a747e4fSDavid du Colombier } 7159a747e4fSDavid du Colombier 7169a747e4fSDavid du Colombier char *ascwday[] = 7179a747e4fSDavid du Colombier { 7189a747e4fSDavid du Colombier "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 7199a747e4fSDavid du Colombier }; 7209a747e4fSDavid du Colombier 7219a747e4fSDavid du Colombier char *ascmon[] = 7229a747e4fSDavid du Colombier { 7239a747e4fSDavid du Colombier "Jan", "Feb", "Mar", "Apr", "May", "Jun", 7249a747e4fSDavid du Colombier "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 7259a747e4fSDavid du Colombier }; 7269a747e4fSDavid du Colombier 7279a747e4fSDavid du Colombier int 7289a747e4fSDavid du Colombier printdate(Biobuf *b) 7299a747e4fSDavid du Colombier { 7309a747e4fSDavid du Colombier Tm *tm; 7319a747e4fSDavid du Colombier int tz; 7329a747e4fSDavid du Colombier 7339a747e4fSDavid du Colombier tm = localtime(time(0)); 7349a747e4fSDavid du Colombier tz = (tm->tzoff/3600)*100 + ((tm->tzoff/60)%60); 7359a747e4fSDavid du Colombier 7369a747e4fSDavid du Colombier return Bprint(b, "Date: %s, %d %s %d %2.2d:%2.2d:%2.2d %s%.4d\n", 7379a747e4fSDavid du Colombier ascwday[tm->wday], tm->mday, ascmon[tm->mon], 1900+tm->year, 7387c881178SDavid du Colombier tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz); 7399a747e4fSDavid du Colombier } 7409a747e4fSDavid du Colombier 7419a747e4fSDavid du Colombier int 7429a747e4fSDavid du Colombier printfrom(Biobuf *b) 7439a747e4fSDavid du Colombier { 7449a747e4fSDavid du Colombier return Bprint(b, "From: %s\n", user); 7459a747e4fSDavid du Colombier } 7469a747e4fSDavid du Colombier 7479a747e4fSDavid du Colombier int 7483ff48bf5SDavid du Colombier printto(Biobuf *b, Addr *a) 7499a747e4fSDavid du Colombier { 7507c881178SDavid du Colombier int i; 7517c881178SDavid du Colombier 7523ff48bf5SDavid du Colombier if(Bprint(b, "To: %s", a->v) < 0) 7539a747e4fSDavid du Colombier return -1; 7547c881178SDavid du Colombier i = 0; 7553ff48bf5SDavid du Colombier for(a = a->next; a != nil; a = a->next) 7567c881178SDavid du Colombier if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0) 7579a747e4fSDavid du Colombier return -1; 7589a747e4fSDavid du Colombier if(Bprint(b, "\n") < 0) 7599a747e4fSDavid du Colombier return -1; 7603ff48bf5SDavid du Colombier return 0; 7613ff48bf5SDavid du Colombier } 7623ff48bf5SDavid du Colombier 7633ff48bf5SDavid du Colombier int 7643ff48bf5SDavid du Colombier printcc(Biobuf *b, Addr *a) 7653ff48bf5SDavid du Colombier { 7667c881178SDavid du Colombier int i; 7677c881178SDavid du Colombier 7683ff48bf5SDavid du Colombier if(a == nil) 7693ff48bf5SDavid du Colombier return 0; 7703ff48bf5SDavid du Colombier if(Bprint(b, "CC: %s", a->v) < 0) 7713ff48bf5SDavid du Colombier return -1; 7727c881178SDavid du Colombier i = 0; 7733ff48bf5SDavid du Colombier for(a = a->next; a != nil; a = a->next) 7747c881178SDavid du Colombier if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0) 7753ff48bf5SDavid du Colombier return -1; 7763ff48bf5SDavid du Colombier if(Bprint(b, "\n") < 0) 7773ff48bf5SDavid du Colombier return -1; 7783ff48bf5SDavid du Colombier return 0; 7799a747e4fSDavid du Colombier } 7809a747e4fSDavid du Colombier 7819a747e4fSDavid du Colombier int 7829a747e4fSDavid du Colombier printsubject(Biobuf *b, char *subject) 7839a747e4fSDavid du Colombier { 7849a747e4fSDavid du Colombier return Bprint(b, "Subject: %s\n", subject); 7859a747e4fSDavid du Colombier } 7869a747e4fSDavid du Colombier 787106486e8SDavid du Colombier int 788106486e8SDavid du Colombier printinreplyto(Biobuf *out, char *dir) 789106486e8SDavid du Colombier { 790106486e8SDavid du Colombier String *s = s_copy(dir); 791106486e8SDavid du Colombier char buf[256]; 792106486e8SDavid du Colombier int fd; 793106486e8SDavid du Colombier int n; 794106486e8SDavid du Colombier 795106486e8SDavid du Colombier s_append(s, "/messageid"); 796106486e8SDavid du Colombier fd = open(s_to_c(s), OREAD); 797106486e8SDavid du Colombier s_free(s); 798106486e8SDavid du Colombier if(fd < 0) 799106486e8SDavid du Colombier return 0; 800106486e8SDavid du Colombier n = read(fd, buf, sizeof(buf)-1); 801106486e8SDavid du Colombier close(fd); 802106486e8SDavid du Colombier if(n <= 0) 803106486e8SDavid du Colombier return 0; 804106486e8SDavid du Colombier buf[n] = 0; 805106486e8SDavid du Colombier return Bprint(out, "In-Reply-To: %s\n", buf); 806106486e8SDavid du Colombier } 807106486e8SDavid du Colombier 8089a747e4fSDavid du Colombier Attach* 8099a747e4fSDavid du Colombier mkattach(char *file, char *type, int inline) 8109a747e4fSDavid du Colombier { 8119a747e4fSDavid du Colombier Ctype *c; 8129a747e4fSDavid du Colombier Attach *a; 8139a747e4fSDavid du Colombier char ftype[64]; 8149a747e4fSDavid du Colombier char *p; 8159a747e4fSDavid du Colombier int n, pfd[2]; 8169a747e4fSDavid du Colombier 8179a747e4fSDavid du Colombier if(file == nil) 8189a747e4fSDavid du Colombier return nil; 819375daca8SDavid du Colombier if(access(file, 4) == -1){ 820375daca8SDavid du Colombier fprint(2, "%s: %s can't read file\n", argv0, file); 821375daca8SDavid du Colombier return nil; 822375daca8SDavid du Colombier } 823d9306527SDavid du Colombier a = emalloc(sizeof(*a)); 8249a747e4fSDavid du Colombier a->path = file; 8259a747e4fSDavid du Colombier a->next = nil; 8269a747e4fSDavid du Colombier a->type = type; 8279a747e4fSDavid du Colombier a->inline = inline; 8289a747e4fSDavid du Colombier a->ctype = nil; 8299a747e4fSDavid du Colombier if(type != nil){ 8309a747e4fSDavid du Colombier for(c = ctype; ; c++) 8319a747e4fSDavid du Colombier if(strncmp(type, c->type, strlen(c->type)) == 0){ 8329a747e4fSDavid du Colombier a->ctype = c; 8339a747e4fSDavid du Colombier break; 8349a747e4fSDavid du Colombier } 8359a747e4fSDavid du Colombier return a; 8369a747e4fSDavid du Colombier } 8379a747e4fSDavid du Colombier 8389a747e4fSDavid du Colombier // pick a type depending on extension 8399a747e4fSDavid du Colombier p = strchr(file, '.'); 840d9306527SDavid du Colombier if(p != nil) 8419a747e4fSDavid du Colombier p++; 842d9306527SDavid du Colombier 843d9306527SDavid du Colombier // check the builtin extensions 844d9306527SDavid du Colombier if(p != nil){ 8459a747e4fSDavid du Colombier for(c = ctype; c->ext != nil; c++) 8469a747e4fSDavid du Colombier if(strcmp(p, c->ext) == 0){ 8479a747e4fSDavid du Colombier a->type = c->type; 8489a747e4fSDavid du Colombier a->ctype = c; 8499a747e4fSDavid du Colombier return a; 8509a747e4fSDavid du Colombier } 8519a747e4fSDavid du Colombier } 8529a747e4fSDavid du Colombier 853d9306527SDavid du Colombier // try the mime types file 854d9306527SDavid du Colombier if(p != nil){ 855d9306527SDavid du Colombier if(mimetypes == nil) 856d9306527SDavid du Colombier readmimetypes(); 857d9306527SDavid du Colombier for(c = mimetypes; c != nil && c->ext != nil; c++) 858d9306527SDavid du Colombier if(strcmp(p, c->ext) == 0){ 859d9306527SDavid du Colombier a->type = c->type; 860d9306527SDavid du Colombier a->ctype = c; 861d9306527SDavid du Colombier return a; 862d9306527SDavid du Colombier } 863d9306527SDavid du Colombier } 864d9306527SDavid du Colombier 8659a747e4fSDavid du Colombier // run file to figure out the type 8669a747e4fSDavid du Colombier a->type = "application/octet-stream"; // safest default 8679a747e4fSDavid du Colombier if(pipe(pfd) < 0) 8689a747e4fSDavid du Colombier return a; 8699a747e4fSDavid du Colombier switch(fork()){ 8709a747e4fSDavid du Colombier case -1: 8719a747e4fSDavid du Colombier break; 8729a747e4fSDavid du Colombier case 0: 8739a747e4fSDavid du Colombier close(pfd[1]); 8749a747e4fSDavid du Colombier close(0); 8759a747e4fSDavid du Colombier dup(pfd[0], 0); 8769a747e4fSDavid du Colombier close(1); 8779a747e4fSDavid du Colombier dup(pfd[0], 1); 878f19e7b74SDavid du Colombier execl("/bin/file", "file", "-m", file, nil); 8799a747e4fSDavid du Colombier exits(0); 8809a747e4fSDavid du Colombier default: 8819a747e4fSDavid du Colombier close(pfd[0]); 8829a747e4fSDavid du Colombier n = read(pfd[1], ftype, sizeof(ftype)); 8839a747e4fSDavid du Colombier if(n > 0){ 8849a747e4fSDavid du Colombier ftype[n-1] = 0; 885d9306527SDavid du Colombier a->type = estrdup(ftype); 8869a747e4fSDavid du Colombier } 8879a747e4fSDavid du Colombier close(pfd[1]); 8889a747e4fSDavid du Colombier waitpid(); 8899a747e4fSDavid du Colombier break; 8909a747e4fSDavid du Colombier } 8919a747e4fSDavid du Colombier 8929a747e4fSDavid du Colombier for(c = ctype; ; c++) 8939a747e4fSDavid du Colombier if(strncmp(a->type, c->type, strlen(c->type)) == 0){ 8949a747e4fSDavid du Colombier a->ctype = c; 8959a747e4fSDavid du Colombier break; 8969a747e4fSDavid du Colombier } 8979a747e4fSDavid du Colombier 8989a747e4fSDavid du Colombier return a; 8999a747e4fSDavid du Colombier } 9009a747e4fSDavid du Colombier 9019a747e4fSDavid du Colombier char* 9029a747e4fSDavid du Colombier mkboundary(void) 9039a747e4fSDavid du Colombier { 9049a747e4fSDavid du Colombier char buf[32]; 9059a747e4fSDavid du Colombier int i; 9069a747e4fSDavid du Colombier 9079a747e4fSDavid du Colombier srand((time(0)<<16)|getpid()); 9089a747e4fSDavid du Colombier strcpy(buf, "upas-"); 9099a747e4fSDavid du Colombier for(i = 5; i < sizeof(buf)-1; i++) 9109a747e4fSDavid du Colombier buf[i] = 'a' + nrand(26); 9119a747e4fSDavid du Colombier buf[i] = 0; 912d9306527SDavid du Colombier return estrdup(buf); 9139a747e4fSDavid du Colombier } 9149a747e4fSDavid du Colombier 9159a747e4fSDavid du Colombier // copy types to two fd's 9169a747e4fSDavid du Colombier static void 9179a747e4fSDavid du Colombier tee(int in, int out1, int out2) 9189a747e4fSDavid du Colombier { 9199a747e4fSDavid du Colombier char buf[8*1024]; 9209a747e4fSDavid du Colombier int n; 9219a747e4fSDavid du Colombier 9229a747e4fSDavid du Colombier for(;;){ 9239a747e4fSDavid du Colombier n = read(in, buf, sizeof(buf)); 9249a747e4fSDavid du Colombier if(n <= 0) 9259a747e4fSDavid du Colombier break; 9269a747e4fSDavid du Colombier if(write(out1, buf, n) < 0) 9279a747e4fSDavid du Colombier break; 9289a747e4fSDavid du Colombier if(write(out2, buf, n) < 0) 9299a747e4fSDavid du Colombier break; 9309a747e4fSDavid du Colombier } 9319a747e4fSDavid du Colombier } 9329a747e4fSDavid du Colombier 9339a747e4fSDavid du Colombier // print the unix from line 9349a747e4fSDavid du Colombier int 9359a747e4fSDavid du Colombier printunixfrom(int fd) 9369a747e4fSDavid du Colombier { 9379a747e4fSDavid du Colombier Tm *tm; 9389a747e4fSDavid du Colombier int tz; 9399a747e4fSDavid du Colombier 9409a747e4fSDavid du Colombier tm = localtime(time(0)); 9419a747e4fSDavid du Colombier tz = (tm->tzoff/3600)*100 + ((tm->tzoff/60)%60); 9429a747e4fSDavid du Colombier 9439a747e4fSDavid du Colombier return fprint(fd, "From %s %s %s %d %2.2d:%2.2d:%2.2d %s%.4d %d\n", 9449a747e4fSDavid du Colombier user, 9459a747e4fSDavid du Colombier ascwday[tm->wday], ascmon[tm->mon], tm->mday, 9467c881178SDavid du Colombier tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz, 1900+tm->year); 9479a747e4fSDavid du Colombier } 9489a747e4fSDavid du Colombier 9496b6b9ac8SDavid du Colombier char *specialfile[] = 9506b6b9ac8SDavid du Colombier { 9516b6b9ac8SDavid du Colombier "pipeto", 9526b6b9ac8SDavid du Colombier "pipefrom", 9536b6b9ac8SDavid du Colombier "L.mbox", 9546b6b9ac8SDavid du Colombier "forward", 9556b6b9ac8SDavid du Colombier "names" 9566b6b9ac8SDavid du Colombier }; 9576b6b9ac8SDavid du Colombier 9586b6b9ac8SDavid du Colombier // return 1 if this is a special file 9596b6b9ac8SDavid du Colombier static int 9606b6b9ac8SDavid du Colombier special(String *s) 9619a747e4fSDavid du Colombier { 9629a747e4fSDavid du Colombier char *p; 9636b6b9ac8SDavid du Colombier int i; 9646b6b9ac8SDavid du Colombier 9656b6b9ac8SDavid du Colombier p = strrchr(s_to_c(s), '/'); 9666b6b9ac8SDavid du Colombier if(p == nil) 9676b6b9ac8SDavid du Colombier p = s_to_c(s); 9686b6b9ac8SDavid du Colombier else 9696b6b9ac8SDavid du Colombier p++; 9706b6b9ac8SDavid du Colombier for(i = 0; i < nelem(specialfile); i++) 9716b6b9ac8SDavid du Colombier if(strcmp(p, specialfile[i]) == 0) 9726b6b9ac8SDavid du Colombier return 1; 9736b6b9ac8SDavid du Colombier return 0; 9746b6b9ac8SDavid du Colombier } 9756b6b9ac8SDavid du Colombier 9766b6b9ac8SDavid du Colombier // open the folder using the recipients account name 9776b6b9ac8SDavid du Colombier static int 9786b6b9ac8SDavid du Colombier openfolder(char *rcvr) 9796b6b9ac8SDavid du Colombier { 9806b6b9ac8SDavid du Colombier char *p; 9816b6b9ac8SDavid du Colombier int c; 9826b6b9ac8SDavid du Colombier String *file; 9836b6b9ac8SDavid du Colombier Dir *d; 9846b6b9ac8SDavid du Colombier int fd; 9856b6b9ac8SDavid du Colombier int scarey; 9866b6b9ac8SDavid du Colombier 9876b6b9ac8SDavid du Colombier file = s_new(); 9886b6b9ac8SDavid du Colombier mboxpath("f", user, file, 0); 9896b6b9ac8SDavid du Colombier 9906b6b9ac8SDavid du Colombier // if $mail/f exists, store there, otherwise in $mail 9916b6b9ac8SDavid du Colombier d = dirstat(s_to_c(file)); 9926b6b9ac8SDavid du Colombier if(d == nil || d->qid.type != QTDIR){ 9936b6b9ac8SDavid du Colombier scarey = 1; 9946b6b9ac8SDavid du Colombier file->ptr -= 1; 9956b6b9ac8SDavid du Colombier } else { 9966b6b9ac8SDavid du Colombier s_putc(file, '/'); 9976b6b9ac8SDavid du Colombier scarey = 0; 9986b6b9ac8SDavid du Colombier } 9996b6b9ac8SDavid du Colombier free(d); 10009a747e4fSDavid du Colombier 10019a747e4fSDavid du Colombier p = strrchr(rcvr, '!'); 10029a747e4fSDavid du Colombier if(p != nil) 10039a747e4fSDavid du Colombier rcvr = p+1; 10049a747e4fSDavid du Colombier 10056b6b9ac8SDavid du Colombier while(*rcvr && *rcvr != '@'){ 10066b6b9ac8SDavid du Colombier c = *rcvr++; 10076b6b9ac8SDavid du Colombier if(c == '/') 10086b6b9ac8SDavid du Colombier c = '_'; 10096b6b9ac8SDavid du Colombier s_putc(file, c); 10106b6b9ac8SDavid du Colombier } 10116b6b9ac8SDavid du Colombier s_terminate(file); 10126b6b9ac8SDavid du Colombier 10136b6b9ac8SDavid du Colombier if(scarey && special(file)){ 10146b6b9ac8SDavid du Colombier fprint(2, "%s: won't overwrite %s\n", argv0, s_to_c(file)); 10156b6b9ac8SDavid du Colombier s_free(file); 10166b6b9ac8SDavid du Colombier return -1; 10176b6b9ac8SDavid du Colombier } 10186b6b9ac8SDavid du Colombier 10196b6b9ac8SDavid du Colombier fd = open(s_to_c(file), OWRITE); 10206b6b9ac8SDavid du Colombier if(fd < 0) 10216b6b9ac8SDavid du Colombier fd = create(s_to_c(file), OWRITE, 0660); 10226b6b9ac8SDavid du Colombier 10236b6b9ac8SDavid du Colombier s_free(file); 10246b6b9ac8SDavid du Colombier return fd; 10259a747e4fSDavid du Colombier } 10269a747e4fSDavid du Colombier 10279a747e4fSDavid du Colombier // start up sendmail and return an fd to talk to it with 10289a747e4fSDavid du Colombier int 10293ff48bf5SDavid du Colombier sendmail(Addr *to, Addr *cc, int *pid, char *rcvr) 10309a747e4fSDavid du Colombier { 10319a747e4fSDavid du Colombier char **av, **v; 10329a747e4fSDavid du Colombier int ac, fd; 10339a747e4fSDavid du Colombier int pfd[2]; 10346b6b9ac8SDavid du Colombier String *cmd; 10359a747e4fSDavid du Colombier Addr *a; 10369a747e4fSDavid du Colombier 10379a747e4fSDavid du Colombier fd = -1; 10386b6b9ac8SDavid du Colombier if(rcvr != nil) 10396b6b9ac8SDavid du Colombier fd = openfolder(rcvr); 10409a747e4fSDavid du Colombier 10419a747e4fSDavid du Colombier ac = 0; 10429a747e4fSDavid du Colombier for(a = to; a != nil; a = a->next) 10439a747e4fSDavid du Colombier ac++; 10443ff48bf5SDavid du Colombier for(a = cc; a != nil; a = a->next) 10453ff48bf5SDavid du Colombier ac++; 1046e288d156SDavid du Colombier v = av = emalloc(sizeof(char*)*(ac+20)); 10479a747e4fSDavid du Colombier ac = 0; 10489a747e4fSDavid du Colombier v[ac++] = "sendmail"; 10499a747e4fSDavid du Colombier if(xflag) 10509a747e4fSDavid du Colombier v[ac++] = "-x"; 10519a747e4fSDavid du Colombier if(rflag) 10529a747e4fSDavid du Colombier v[ac++] = "-r"; 10539a747e4fSDavid du Colombier if(lbflag) 10549a747e4fSDavid du Colombier v[ac++] = "-#"; 1055e288d156SDavid du Colombier if(dflag) 1056e288d156SDavid du Colombier v[ac++] = "-d"; 10579a747e4fSDavid du Colombier for(a = to; a != nil; a = a->next) 10589a747e4fSDavid du Colombier v[ac++] = a->v; 10593ff48bf5SDavid du Colombier for(a = cc; a != nil; a = a->next) 10603ff48bf5SDavid du Colombier v[ac++] = a->v; 10619a747e4fSDavid du Colombier v[ac] = 0; 10629a747e4fSDavid du Colombier 10639a747e4fSDavid du Colombier if(pipe(pfd) < 0) 10649a747e4fSDavid du Colombier fatal("%r"); 1065106486e8SDavid du Colombier switch(*pid = rfork(RFFDG|RFREND|RFPROC|RFENVG)){ 10669a747e4fSDavid du Colombier case -1: 10679a747e4fSDavid du Colombier fatal("%r"); 10689a747e4fSDavid du Colombier break; 10699a747e4fSDavid du Colombier case 0: 10709a747e4fSDavid du Colombier if(holding) 10719a747e4fSDavid du Colombier close(holding); 10729a747e4fSDavid du Colombier close(pfd[1]); 10739a747e4fSDavid du Colombier dup(pfd[0], 0); 10749a747e4fSDavid du Colombier close(pfd[0]); 10759a747e4fSDavid du Colombier 10769a747e4fSDavid du Colombier if(rcvr != nil){ 10779a747e4fSDavid du Colombier if(pipe(pfd) < 0) 10789a747e4fSDavid du Colombier fatal("%r"); 10799a747e4fSDavid du Colombier switch(fork()){ 10809a747e4fSDavid du Colombier case -1: 10819a747e4fSDavid du Colombier fatal("%r"); 10829a747e4fSDavid du Colombier break; 10839a747e4fSDavid du Colombier case 0: 10849a747e4fSDavid du Colombier close(pfd[0]); 10859a747e4fSDavid du Colombier seek(fd, 0, 2); 10869a747e4fSDavid du Colombier printunixfrom(fd); 10879a747e4fSDavid du Colombier tee(0, pfd[1], fd); 10889a747e4fSDavid du Colombier write(fd, "\n", 1); 10899a747e4fSDavid du Colombier exits(0); 10909a747e4fSDavid du Colombier default: 10919a747e4fSDavid du Colombier close(fd); 10929a747e4fSDavid du Colombier close(pfd[1]); 10939a747e4fSDavid du Colombier dup(pfd[0], 0); 10949a747e4fSDavid du Colombier break; 10959a747e4fSDavid du Colombier } 10969a747e4fSDavid du Colombier } 10979a747e4fSDavid du Colombier 1098106486e8SDavid du Colombier if(replymsg != nil) 1099106486e8SDavid du Colombier putenv("replymsg", replymsg); 1100106486e8SDavid du Colombier 1101dc5a79c1SDavid du Colombier cmd = mboxpath("pipefrom", login, s_new(), 0); 1102f121929fSDavid du Colombier exec(s_to_c(cmd), av); 11039a747e4fSDavid du Colombier exec("/bin/myupassend", av); 11049a747e4fSDavid du Colombier exec("/bin/upas/send", av); 11059a747e4fSDavid du Colombier fatal("execing: %r"); 11069a747e4fSDavid du Colombier break; 11079a747e4fSDavid du Colombier default: 11089a747e4fSDavid du Colombier if(rcvr != nil) 11099a747e4fSDavid du Colombier close(fd); 11109a747e4fSDavid du Colombier close(pfd[0]); 11119a747e4fSDavid du Colombier break; 11129a747e4fSDavid du Colombier } 11139a747e4fSDavid du Colombier return pfd[1]; 11149a747e4fSDavid du Colombier } 11159a747e4fSDavid du Colombier 11163ff48bf5SDavid du Colombier // start up pgp process and return an fd to talk to it with. 11173ff48bf5SDavid du Colombier // its standard output will be the original fd, which goes to sendmail. 11183ff48bf5SDavid du Colombier int 11193ff48bf5SDavid du Colombier pgpfilter(int *pid, int fd, int pgpflag) 11209a747e4fSDavid du Colombier { 11213ff48bf5SDavid du Colombier char **av, **v; 11223ff48bf5SDavid du Colombier int ac; 11233ff48bf5SDavid du Colombier int pfd[2]; 11249a747e4fSDavid du Colombier 1125d9306527SDavid du Colombier v = av = emalloc(sizeof(char*)*8); 11263ff48bf5SDavid du Colombier ac = 0; 11273ff48bf5SDavid du Colombier v[ac++] = "pgp"; 1128*90e65e0fSDavid du Colombier v[ac++] = "-fat"; /* operate as a filter, generate text */ 11293ff48bf5SDavid du Colombier if(pgpflag & PGPsign) 11303ff48bf5SDavid du Colombier v[ac++] = "-s"; 11313ff48bf5SDavid du Colombier if(pgpflag & PGPencrypt) 11323ff48bf5SDavid du Colombier v[ac++] = "-e"; 11333ff48bf5SDavid du Colombier v[ac] = 0; 11343ff48bf5SDavid du Colombier 11353ff48bf5SDavid du Colombier if(pipe(pfd) < 0) 11363ff48bf5SDavid du Colombier fatal("%r"); 11373ff48bf5SDavid du Colombier switch(*pid = fork()){ 11383ff48bf5SDavid du Colombier case -1: 11393ff48bf5SDavid du Colombier fatal("%r"); 11403ff48bf5SDavid du Colombier break; 11413ff48bf5SDavid du Colombier case 0: 11423ff48bf5SDavid du Colombier close(pfd[1]); 11433ff48bf5SDavid du Colombier dup(pfd[0], 0); 11443ff48bf5SDavid du Colombier close(pfd[0]); 11453ff48bf5SDavid du Colombier dup(fd, 1); 11463ff48bf5SDavid du Colombier close(fd); 11473ff48bf5SDavid du Colombier 1148*90e65e0fSDavid du Colombier /* add newline to avoid confusing pgp output with 822 headers */ 1149*90e65e0fSDavid du Colombier write(1, "\n", 1); 1150*90e65e0fSDavid du Colombier exec("/bin/pgp", av); 11513ff48bf5SDavid du Colombier fatal("execing: %r"); 11523ff48bf5SDavid du Colombier break; 11533ff48bf5SDavid du Colombier default: 11543ff48bf5SDavid du Colombier close(pfd[0]); 11559a747e4fSDavid du Colombier break; 11569a747e4fSDavid du Colombier } 11573ff48bf5SDavid du Colombier close(fd); 11583ff48bf5SDavid du Colombier return pfd[1]; 11593ff48bf5SDavid du Colombier } 11603ff48bf5SDavid du Colombier 11613ff48bf5SDavid du Colombier // wait for sendmail and pgp to exit; exit here if either failed 11623ff48bf5SDavid du Colombier char* 11633ff48bf5SDavid du Colombier waitforsubprocs(void) 11643ff48bf5SDavid du Colombier { 11653ff48bf5SDavid du Colombier Waitmsg *w; 11663ff48bf5SDavid du Colombier char *err; 11673ff48bf5SDavid du Colombier 11683ff48bf5SDavid du Colombier err = nil; 11693ff48bf5SDavid du Colombier while((w = wait()) != nil){ 11703ff48bf5SDavid du Colombier if(w->pid == pid || w->pid == pgppid){ 11713ff48bf5SDavid du Colombier if(w->msg[0] != 0) 1172d9306527SDavid du Colombier err = estrdup(w->msg); 11733ff48bf5SDavid du Colombier } 11749a747e4fSDavid du Colombier free(w); 11759a747e4fSDavid du Colombier } 11763ff48bf5SDavid du Colombier if(err) 11773ff48bf5SDavid du Colombier exits(err); 11789a747e4fSDavid du Colombier return nil; 11799a747e4fSDavid du Colombier } 11809a747e4fSDavid du Colombier 11819a747e4fSDavid du Colombier int 11829a747e4fSDavid du Colombier cistrncmp(char *a, char *b, int n) 11839a747e4fSDavid du Colombier { 11849a747e4fSDavid du Colombier while(n-- > 0){ 11859a747e4fSDavid du Colombier if(tolower(*a++) != tolower(*b++)) 11869a747e4fSDavid du Colombier return -1; 11879a747e4fSDavid du Colombier } 11889a747e4fSDavid du Colombier return 0; 11899a747e4fSDavid du Colombier } 11909a747e4fSDavid du Colombier 11919a747e4fSDavid du Colombier int 11929a747e4fSDavid du Colombier cistrcmp(char *a, char *b) 11939a747e4fSDavid du Colombier { 11949a747e4fSDavid du Colombier for(;;){ 11959a747e4fSDavid du Colombier if(tolower(*a) != tolower(*b++)) 11969a747e4fSDavid du Colombier return -1; 11979a747e4fSDavid du Colombier if(*a++ == 0) 11989a747e4fSDavid du Colombier break; 11999a747e4fSDavid du Colombier } 12009a747e4fSDavid du Colombier return 0; 12019a747e4fSDavid du Colombier } 12029a747e4fSDavid du Colombier 12039a747e4fSDavid du Colombier static uchar t64d[256]; 12049a747e4fSDavid du Colombier static char t64e[64]; 12059a747e4fSDavid du Colombier 12069a747e4fSDavid du Colombier static void 12079a747e4fSDavid du Colombier init64(void) 12089a747e4fSDavid du Colombier { 12099a747e4fSDavid du Colombier int c, i; 12109a747e4fSDavid du Colombier 12119a747e4fSDavid du Colombier memset(t64d, 255, 256); 12129a747e4fSDavid du Colombier memset(t64e, '=', 64); 12139a747e4fSDavid du Colombier i = 0; 12149a747e4fSDavid du Colombier for(c = 'A'; c <= 'Z'; c++){ 12159a747e4fSDavid du Colombier t64e[i] = c; 12169a747e4fSDavid du Colombier t64d[c] = i++; 12179a747e4fSDavid du Colombier } 12189a747e4fSDavid du Colombier for(c = 'a'; c <= 'z'; c++){ 12199a747e4fSDavid du Colombier t64e[i] = c; 12209a747e4fSDavid du Colombier t64d[c] = i++; 12219a747e4fSDavid du Colombier } 12229a747e4fSDavid du Colombier for(c = '0'; c <= '9'; c++){ 12239a747e4fSDavid du Colombier t64e[i] = c; 12249a747e4fSDavid du Colombier t64d[c] = i++; 12259a747e4fSDavid du Colombier } 12269a747e4fSDavid du Colombier t64e[i] = '+'; 12279a747e4fSDavid du Colombier t64d['+'] = i++; 12289a747e4fSDavid du Colombier t64e[i] = '/'; 12299a747e4fSDavid du Colombier t64d['/'] = i; 12309a747e4fSDavid du Colombier } 12319a747e4fSDavid du Colombier 12329a747e4fSDavid du Colombier int 12339a747e4fSDavid du Colombier enc64(char *out, int lim, uchar *in, int n) 12349a747e4fSDavid du Colombier { 12359a747e4fSDavid du Colombier int i; 12369a747e4fSDavid du Colombier ulong b24; 12379a747e4fSDavid du Colombier char *start = out; 12389a747e4fSDavid du Colombier char *e = out + lim; 12399a747e4fSDavid du Colombier 12409a747e4fSDavid du Colombier if(t64e[0] == 0) 12419a747e4fSDavid du Colombier init64(); 12429a747e4fSDavid du Colombier for(i = 0; i < n/3; i++){ 12439a747e4fSDavid du Colombier b24 = (*in++)<<16; 12449a747e4fSDavid du Colombier b24 |= (*in++)<<8; 12459a747e4fSDavid du Colombier b24 |= *in++; 12469a747e4fSDavid du Colombier if(out + 5 >= e) 12479a747e4fSDavid du Colombier goto exhausted; 12489a747e4fSDavid du Colombier *out++ = t64e[(b24>>18)]; 12499a747e4fSDavid du Colombier *out++ = t64e[(b24>>12)&0x3f]; 12509a747e4fSDavid du Colombier *out++ = t64e[(b24>>6)&0x3f]; 12519a747e4fSDavid du Colombier *out++ = t64e[(b24)&0x3f]; 12529a747e4fSDavid du Colombier if((i%18) == 17) 12539a747e4fSDavid du Colombier *out++ = '\n'; 12549a747e4fSDavid du Colombier } 12559a747e4fSDavid du Colombier 12569a747e4fSDavid du Colombier switch(n%3){ 12579a747e4fSDavid du Colombier case 2: 12589a747e4fSDavid du Colombier b24 = (*in++)<<16; 12599a747e4fSDavid du Colombier b24 |= (*in)<<8; 12609a747e4fSDavid du Colombier if(out + 4 >= e) 12619a747e4fSDavid du Colombier goto exhausted; 12629a747e4fSDavid du Colombier *out++ = t64e[(b24>>18)]; 12639a747e4fSDavid du Colombier *out++ = t64e[(b24>>12)&0x3f]; 12649a747e4fSDavid du Colombier *out++ = t64e[(b24>>6)&0x3f]; 12659a747e4fSDavid du Colombier break; 12669a747e4fSDavid du Colombier case 1: 12679a747e4fSDavid du Colombier b24 = (*in)<<16; 12689a747e4fSDavid du Colombier if(out + 4 >= e) 12699a747e4fSDavid du Colombier goto exhausted; 12709a747e4fSDavid du Colombier *out++ = t64e[(b24>>18)]; 12719a747e4fSDavid du Colombier *out++ = t64e[(b24>>12)&0x3f]; 12729a747e4fSDavid du Colombier *out++ = '='; 12739a747e4fSDavid du Colombier break; 12749a747e4fSDavid du Colombier case 0: 12759a747e4fSDavid du Colombier if((i%18) != 0) 12769a747e4fSDavid du Colombier *out++ = '\n'; 12779a747e4fSDavid du Colombier *out = 0; 12789a747e4fSDavid du Colombier return out - start; 12799a747e4fSDavid du Colombier } 12809a747e4fSDavid du Colombier exhausted: 12819a747e4fSDavid du Colombier *out++ = '='; 12829a747e4fSDavid du Colombier *out++ = '\n'; 12839a747e4fSDavid du Colombier *out = 0; 12849a747e4fSDavid du Colombier return out - start; 12859a747e4fSDavid du Colombier } 12869a747e4fSDavid du Colombier 1287d9306527SDavid du Colombier void 1288d9306527SDavid du Colombier freealias(Alias *a) 1289d9306527SDavid du Colombier { 1290d9306527SDavid du Colombier freeaddrs(a->addr); 1291d9306527SDavid du Colombier free(a); 1292d9306527SDavid du Colombier } 1293d9306527SDavid du Colombier 1294d9306527SDavid du Colombier void 1295d9306527SDavid du Colombier freealiases(Alias *a) 1296d9306527SDavid du Colombier { 1297d9306527SDavid du Colombier Alias *next; 1298d9306527SDavid du Colombier 1299d9306527SDavid du Colombier while(a != nil){ 1300d9306527SDavid du Colombier next = a->next; 1301d9306527SDavid du Colombier freealias(a); 1302d9306527SDavid du Colombier a = next; 1303d9306527SDavid du Colombier } 1304d9306527SDavid du Colombier } 1305d9306527SDavid du Colombier 13069a747e4fSDavid du Colombier // 13079a747e4fSDavid du Colombier // read alias file 13089a747e4fSDavid du Colombier // 13099a747e4fSDavid du Colombier Alias* 13109a747e4fSDavid du Colombier readaliases(void) 13119a747e4fSDavid du Colombier { 13129a747e4fSDavid du Colombier Alias *a, **l, *first; 1313d9306527SDavid du Colombier Addr *addr, **al; 1314d9306527SDavid du Colombier String *file, *line, *token; 13159a747e4fSDavid du Colombier static int already; 1316dc5a79c1SDavid du Colombier Sinstack *sp; 13179a747e4fSDavid du Colombier 13189a747e4fSDavid du Colombier first = nil; 1319d9306527SDavid du Colombier file = s_new(); 1320d9306527SDavid du Colombier line = s_new(); 1321d9306527SDavid du Colombier token = s_new(); 1322d9306527SDavid du Colombier 1323d9306527SDavid du Colombier // open and get length 1324d9306527SDavid du Colombier mboxpath("names", login, file, 0); 1325dc5a79c1SDavid du Colombier sp = s_allocinstack(s_to_c(file)); 1326dc5a79c1SDavid du Colombier if(sp == nil) 1327d9306527SDavid du Colombier goto out; 1328d9306527SDavid du Colombier 13299a747e4fSDavid du Colombier l = &first; 1330d9306527SDavid du Colombier 1331d9306527SDavid du Colombier // read a line at a time. 1332dc5a79c1SDavid du Colombier while(s_rdinstack(sp, s_restart(line))!=nil) { 1333d9306527SDavid du Colombier s_restart(line); 1334d9306527SDavid du Colombier a = emalloc(sizeof(Alias)); 1335d9306527SDavid du Colombier al = &a->addr; 13369a747e4fSDavid du Colombier for(;;){ 1337d9306527SDavid du Colombier if(s_parse(line, s_restart(token))==0) 13389a747e4fSDavid du Colombier break; 1339d9306527SDavid du Colombier addr = emalloc(sizeof(Addr)); 1340d9306527SDavid du Colombier addr->v = strdup(s_to_c(token)); 1341d9306527SDavid du Colombier addr->next = 0; 1342d9306527SDavid du Colombier *al = addr; 1343d9306527SDavid du Colombier al = &addr->next; 13449a747e4fSDavid du Colombier } 1345d9306527SDavid du Colombier if(a->addr == nil || a->addr->next == nil){ 1346d9306527SDavid du Colombier freealias(a); 1347d9306527SDavid du Colombier continue; 13489a747e4fSDavid du Colombier } 13499a747e4fSDavid du Colombier a->next = nil; 13509a747e4fSDavid du Colombier *l = a; 13519a747e4fSDavid du Colombier l = &a->next; 13529a747e4fSDavid du Colombier } 1353dc5a79c1SDavid du Colombier s_freeinstack(sp); 13549a747e4fSDavid du Colombier 1355d9306527SDavid du Colombier out: 1356d9306527SDavid du Colombier s_free(file); 1357d9306527SDavid du Colombier s_free(line); 1358d9306527SDavid du Colombier s_free(token); 13599a747e4fSDavid du Colombier return first; 13609a747e4fSDavid du Colombier } 13619a747e4fSDavid du Colombier 13629a747e4fSDavid du Colombier Addr* 13639a747e4fSDavid du Colombier newaddr(char *name) 13649a747e4fSDavid du Colombier { 13659a747e4fSDavid du Colombier Addr *a; 13669a747e4fSDavid du Colombier 1367d9306527SDavid du Colombier a = emalloc(sizeof(*a)); 13689a747e4fSDavid du Colombier a->next = nil; 1369d9306527SDavid du Colombier a->v = estrdup(name); 13709a747e4fSDavid du Colombier if(a->v == nil) 13719a747e4fSDavid du Colombier sysfatal("%r"); 13729a747e4fSDavid du Colombier return a; 13739a747e4fSDavid du Colombier } 13749a747e4fSDavid du Colombier 13759a747e4fSDavid du Colombier // 13769a747e4fSDavid du Colombier // expand personal aliases since the names are meaningless in 13779a747e4fSDavid du Colombier // other contexts 13789a747e4fSDavid du Colombier // 13799a747e4fSDavid du Colombier Addr* 13809a747e4fSDavid du Colombier _expand(Addr *old, int *changedp) 13819a747e4fSDavid du Colombier { 13829a747e4fSDavid du Colombier Alias *al; 1383d9306527SDavid du Colombier Addr *first, *next, **l, *a; 13849a747e4fSDavid du Colombier 13859a747e4fSDavid du Colombier *changedp = 0; 13869a747e4fSDavid du Colombier first = nil; 13879a747e4fSDavid du Colombier l = &first; 13889a747e4fSDavid du Colombier for(;old != nil; old = next){ 13899a747e4fSDavid du Colombier next = old->next; 13909a747e4fSDavid du Colombier for(al = aliases; al != nil; al = al->next){ 1391d9306527SDavid du Colombier if(strcmp(al->addr->v, old->v) == 0){ 1392d9306527SDavid du Colombier for(a = al->addr->next; a != nil; a = a->next){ 1393d9306527SDavid du Colombier *l = newaddr(a->v); 13949a747e4fSDavid du Colombier if(*l == nil) 13959a747e4fSDavid du Colombier sysfatal("%r"); 13969a747e4fSDavid du Colombier l = &(*l)->next; 13979a747e4fSDavid du Colombier *changedp = 1; 13989a747e4fSDavid du Colombier } 13999a747e4fSDavid du Colombier break; 14009a747e4fSDavid du Colombier } 14019a747e4fSDavid du Colombier } 14029a747e4fSDavid du Colombier if(al != nil){ 14039a747e4fSDavid du Colombier freeaddr(old); 14049a747e4fSDavid du Colombier continue; 14059a747e4fSDavid du Colombier } 14069a747e4fSDavid du Colombier *l = old; 14079a747e4fSDavid du Colombier old->next = nil; 14089a747e4fSDavid du Colombier l = &(*l)->next; 14099a747e4fSDavid du Colombier } 14109a747e4fSDavid du Colombier return first; 14119a747e4fSDavid du Colombier } 14129a747e4fSDavid du Colombier 14139a747e4fSDavid du Colombier Addr* 14149a747e4fSDavid du Colombier rexpand(Addr *old) 14159a747e4fSDavid du Colombier { 14169a747e4fSDavid du Colombier int i, changed; 14179a747e4fSDavid du Colombier 14189a747e4fSDavid du Colombier changed = 0; 14199a747e4fSDavid du Colombier for(i=0; i<32; i++){ 14209a747e4fSDavid du Colombier old = _expand(old, &changed); 14219a747e4fSDavid du Colombier if(changed == 0) 14229a747e4fSDavid du Colombier break; 14239a747e4fSDavid du Colombier } 14249a747e4fSDavid du Colombier return old; 14259a747e4fSDavid du Colombier } 14269a747e4fSDavid du Colombier 14279a747e4fSDavid du Colombier Addr* 14289a747e4fSDavid du Colombier unique(Addr *first) 14299a747e4fSDavid du Colombier { 14309a747e4fSDavid du Colombier Addr *a, **l, *x; 14319a747e4fSDavid du Colombier 14329a747e4fSDavid du Colombier for(a = first; a != nil; a = a->next){ 14339a747e4fSDavid du Colombier for(l = &a->next; *l != nil;){ 14349a747e4fSDavid du Colombier if(strcmp(a->v, (*l)->v) == 0){ 14359a747e4fSDavid du Colombier x = *l; 14369a747e4fSDavid du Colombier *l = x->next; 14379a747e4fSDavid du Colombier freeaddr(x); 14389a747e4fSDavid du Colombier } else 14399a747e4fSDavid du Colombier l = &(*l)->next; 14409a747e4fSDavid du Colombier } 14419a747e4fSDavid du Colombier } 14429a747e4fSDavid du Colombier return first; 14439a747e4fSDavid du Colombier } 14449a747e4fSDavid du Colombier 14459a747e4fSDavid du Colombier Addr* 14469a747e4fSDavid du Colombier expand(int ac, char **av) 14479a747e4fSDavid du Colombier { 14489a747e4fSDavid du Colombier Addr *first, **l; 14499a747e4fSDavid du Colombier int i; 14509a747e4fSDavid du Colombier 14513ff48bf5SDavid du Colombier first = nil; 14523ff48bf5SDavid du Colombier 14539a747e4fSDavid du Colombier // make a list of the starting addresses 14549a747e4fSDavid du Colombier l = &first; 14559a747e4fSDavid du Colombier for(i = 0; i < ac; i++){ 14569a747e4fSDavid du Colombier *l = newaddr(av[i]); 14579a747e4fSDavid du Colombier if(*l == nil) 14589a747e4fSDavid du Colombier sysfatal("%r"); 14599a747e4fSDavid du Colombier l = &(*l)->next; 14609a747e4fSDavid du Colombier } 14619a747e4fSDavid du Colombier 14629a747e4fSDavid du Colombier // recurse till we don't change any more 14639a747e4fSDavid du Colombier return unique(rexpand(first)); 14649a747e4fSDavid du Colombier } 14659a747e4fSDavid du Colombier 14669a747e4fSDavid du Colombier Addr* 14679a747e4fSDavid du Colombier concataddr(Addr *a, Addr *b) 14689a747e4fSDavid du Colombier { 14699a747e4fSDavid du Colombier Addr *oa; 14709a747e4fSDavid du Colombier 14719a747e4fSDavid du Colombier if(a == nil) 14729a747e4fSDavid du Colombier return b; 14739a747e4fSDavid du Colombier 14749a747e4fSDavid du Colombier oa = a; 14759a747e4fSDavid du Colombier for(; a->next; a=a->next) 14769a747e4fSDavid du Colombier ; 14779a747e4fSDavid du Colombier a->next = b; 14789a747e4fSDavid du Colombier return oa; 14799a747e4fSDavid du Colombier } 14809a747e4fSDavid du Colombier 14819a747e4fSDavid du Colombier void 14829a747e4fSDavid du Colombier freeaddr(Addr *ap) 14839a747e4fSDavid du Colombier { 14849a747e4fSDavid du Colombier free(ap->v); 14859a747e4fSDavid du Colombier free(ap); 14869a747e4fSDavid du Colombier } 14879a747e4fSDavid du Colombier 14889a747e4fSDavid du Colombier void 14899a747e4fSDavid du Colombier freeaddrs(Addr *ap) 14909a747e4fSDavid du Colombier { 14919a747e4fSDavid du Colombier Addr *next; 14929a747e4fSDavid du Colombier 14939a747e4fSDavid du Colombier for(; ap; ap=next) { 14949a747e4fSDavid du Colombier next = ap->next; 14959a747e4fSDavid du Colombier freeaddr(ap); 14969a747e4fSDavid du Colombier } 14979a747e4fSDavid du Colombier } 14989a747e4fSDavid du Colombier 14999a747e4fSDavid du Colombier String* 15009a747e4fSDavid du Colombier s_copyn(char *s, int n) 15019a747e4fSDavid du Colombier { 15029a747e4fSDavid du Colombier return s_nappend(s_reset(nil), s, n); 15039a747e4fSDavid du Colombier } 15049a747e4fSDavid du Colombier 15059a747e4fSDavid du Colombier // fetch the next token from an RFC822 address string 15069a747e4fSDavid du Colombier // we assume the header is RFC822-conformant in that 15079a747e4fSDavid du Colombier // we recognize escaping anywhere even though it is only 15089a747e4fSDavid du Colombier // supposed to be in quoted-strings, domain-literals, and comments. 15099a747e4fSDavid du Colombier // 15109a747e4fSDavid du Colombier // i'd use yylex or yyparse here, but we need to preserve 15119a747e4fSDavid du Colombier // things like comments, which i think it tosses away. 15129a747e4fSDavid du Colombier // 15139a747e4fSDavid du Colombier // we're not strictly RFC822 compliant. we misparse such nonsense as 15149a747e4fSDavid du Colombier // 15159a747e4fSDavid du Colombier // To: gre @ (Grace) plan9 . (Emlin) bell-labs.com 15169a747e4fSDavid du Colombier // 15179a747e4fSDavid du Colombier // make sure there's no whitespace in your addresses and 15189a747e4fSDavid du Colombier // you'll be fine. 15199a747e4fSDavid du Colombier // 15209a747e4fSDavid du Colombier enum { 15219a747e4fSDavid du Colombier Twhite, 15229a747e4fSDavid du Colombier Tcomment, 15239a747e4fSDavid du Colombier Twords, 15249a747e4fSDavid du Colombier Tcomma, 15259a747e4fSDavid du Colombier Tleftangle, 15269a747e4fSDavid du Colombier Trightangle, 15279a747e4fSDavid du Colombier Terror, 15289a747e4fSDavid du Colombier Tend, 15299a747e4fSDavid du Colombier }; 15309a747e4fSDavid du Colombier //char *ty82[] = {"white", "comment", "words", "comma", "<", ">", "err", "end"}; 15319a747e4fSDavid du Colombier #define ISWHITE(p) ((p)==' ' || (p)=='\t' || (p)=='\n' || (p)=='\r') 15329a747e4fSDavid du Colombier int 15339a747e4fSDavid du Colombier get822token(String **tok, char *p, char **pp) 15349a747e4fSDavid du Colombier { 15359a747e4fSDavid du Colombier char *op; 15369a747e4fSDavid du Colombier int type; 15379a747e4fSDavid du Colombier int quoting; 15389a747e4fSDavid du Colombier 15399a747e4fSDavid du Colombier op = p; 15409a747e4fSDavid du Colombier switch(*p){ 15419a747e4fSDavid du Colombier case '\0': 15429a747e4fSDavid du Colombier *tok = nil; 15439a747e4fSDavid du Colombier *pp = nil; 15449a747e4fSDavid du Colombier return Tend; 15459a747e4fSDavid du Colombier 15469a747e4fSDavid du Colombier case ' ': // get whitespace 15479a747e4fSDavid du Colombier case '\t': 15489a747e4fSDavid du Colombier case '\n': 15499a747e4fSDavid du Colombier case '\r': 15509a747e4fSDavid du Colombier type = Twhite; 15519a747e4fSDavid du Colombier while(ISWHITE(*p)) 15529a747e4fSDavid du Colombier p++; 15539a747e4fSDavid du Colombier break; 15549a747e4fSDavid du Colombier 15559a747e4fSDavid du Colombier case '(': // get comment 15569a747e4fSDavid du Colombier type = Tcomment; 15579a747e4fSDavid du Colombier for(p++; *p && *p != ')'; p++) 15589a747e4fSDavid du Colombier if(*p == '\\') { 15599a747e4fSDavid du Colombier if(*(p+1) == '\0') { 15609a747e4fSDavid du Colombier *tok = nil; 15619a747e4fSDavid du Colombier return Terror; 15629a747e4fSDavid du Colombier } 15639a747e4fSDavid du Colombier p++; 15649a747e4fSDavid du Colombier } 15659a747e4fSDavid du Colombier 15669a747e4fSDavid du Colombier if(*p != ')') { 15679a747e4fSDavid du Colombier *tok = nil; 15689a747e4fSDavid du Colombier return Terror; 15699a747e4fSDavid du Colombier } 15709a747e4fSDavid du Colombier p++; 15719a747e4fSDavid du Colombier break; 15729a747e4fSDavid du Colombier case ',': 15739a747e4fSDavid du Colombier type = Tcomma; 15749a747e4fSDavid du Colombier p++; 15759a747e4fSDavid du Colombier break; 15769a747e4fSDavid du Colombier case '<': 15779a747e4fSDavid du Colombier type = Tleftangle; 15789a747e4fSDavid du Colombier p++; 15799a747e4fSDavid du Colombier break; 15809a747e4fSDavid du Colombier case '>': 15819a747e4fSDavid du Colombier type = Trightangle; 15829a747e4fSDavid du Colombier p++; 15839a747e4fSDavid du Colombier break; 15849a747e4fSDavid du Colombier default: // bunch of letters, perhaps quoted strings tossed in 15859a747e4fSDavid du Colombier type = Twords; 15869a747e4fSDavid du Colombier quoting = 0; 15879a747e4fSDavid du Colombier for(; *p && (quoting || (!ISWHITE(*p) && *p != '>' && *p != '<' && *p != ',')); p++) { 15889a747e4fSDavid du Colombier if(*p == '"') 15899a747e4fSDavid du Colombier quoting = !quoting; 15909a747e4fSDavid du Colombier if(*p == '\\') { 15919a747e4fSDavid du Colombier if(*(p+1) == '\0') { 15929a747e4fSDavid du Colombier *tok = nil; 15939a747e4fSDavid du Colombier return Terror; 15949a747e4fSDavid du Colombier } 15959a747e4fSDavid du Colombier p++; 15969a747e4fSDavid du Colombier } 15979a747e4fSDavid du Colombier } 15989a747e4fSDavid du Colombier break; 15999a747e4fSDavid du Colombier } 16009a747e4fSDavid du Colombier 16019a747e4fSDavid du Colombier if(pp) 16029a747e4fSDavid du Colombier *pp = p; 16039a747e4fSDavid du Colombier *tok = s_copyn(op, p-op); 16049a747e4fSDavid du Colombier return type; 16059a747e4fSDavid du Colombier } 16069a747e4fSDavid du Colombier 16079a747e4fSDavid du Colombier // expand local aliases in an RFC822 mail line 16089a747e4fSDavid du Colombier // add list of expanded addresses to to. 16099a747e4fSDavid du Colombier Addr* 16109a747e4fSDavid du Colombier expandline(String **s, Addr *to) 16119a747e4fSDavid du Colombier { 16129a747e4fSDavid du Colombier Addr *na, *nto, *ap; 16139a747e4fSDavid du Colombier char *p; 16149a747e4fSDavid du Colombier int tok, inangle, hadangle, nword; 16159a747e4fSDavid du Colombier String *os, *ns, *stok, *lastword, *sinceword; 16169a747e4fSDavid du Colombier 16179a747e4fSDavid du Colombier os = s_copy(s_to_c(*s)); 16189a747e4fSDavid du Colombier p = strchr(s_to_c(*s), ':'); 16199a747e4fSDavid du Colombier assert(p != nil); 16209a747e4fSDavid du Colombier p++; 16219a747e4fSDavid du Colombier 16229a747e4fSDavid du Colombier ns = s_copyn(s_to_c(*s), p-s_to_c(*s)); 16239a747e4fSDavid du Colombier stok = nil; 16249a747e4fSDavid du Colombier nto = nil; 16259a747e4fSDavid du Colombier // 16269a747e4fSDavid du Colombier // the only valid mailbox namings are word 16279a747e4fSDavid du Colombier // and word* < addr > 16289a747e4fSDavid du Colombier // without comments this would be simple. 16299a747e4fSDavid du Colombier // we keep the following: 16309a747e4fSDavid du Colombier // lastword - current guess at the address 16319a747e4fSDavid du Colombier // sinceword - whitespace and comment seen since lastword 16329a747e4fSDavid du Colombier // 16339a747e4fSDavid du Colombier lastword = s_new(); 16349a747e4fSDavid du Colombier sinceword = s_new(); 16359a747e4fSDavid du Colombier inangle = 0; 16369a747e4fSDavid du Colombier nword = 0; 16379a747e4fSDavid du Colombier hadangle = 0; 16389a747e4fSDavid du Colombier for(;;) { 16399a747e4fSDavid du Colombier stok = nil; 16409a747e4fSDavid du Colombier switch(tok = get822token(&stok, p, &p)){ 16419a747e4fSDavid du Colombier default: 16429a747e4fSDavid du Colombier abort(); 16439a747e4fSDavid du Colombier case Tcomma: 16449a747e4fSDavid du Colombier case Tend: 16459a747e4fSDavid du Colombier if(inangle) 16469a747e4fSDavid du Colombier goto Error; 16479a747e4fSDavid du Colombier if(nword != 1) 16489a747e4fSDavid du Colombier goto Error; 16499a747e4fSDavid du Colombier na = rexpand(newaddr(s_to_c(lastword))); 16509a747e4fSDavid du Colombier s_append(ns, na->v); 16519a747e4fSDavid du Colombier s_append(ns, s_to_c(sinceword)); 16529a747e4fSDavid du Colombier for(ap=na->next; ap; ap=ap->next) { 16539a747e4fSDavid du Colombier s_append(ns, ", "); 16549a747e4fSDavid du Colombier s_append(ns, ap->v); 16559a747e4fSDavid du Colombier } 16569a747e4fSDavid du Colombier nto = concataddr(na, nto); 16579a747e4fSDavid du Colombier if(tok == Tcomma){ 16589a747e4fSDavid du Colombier s_append(ns, ","); 16599a747e4fSDavid du Colombier s_free(stok); 16609a747e4fSDavid du Colombier } 16619a747e4fSDavid du Colombier if(tok == Tend) 16629a747e4fSDavid du Colombier goto Break2; 16639a747e4fSDavid du Colombier inangle = 0; 16649a747e4fSDavid du Colombier nword = 0; 16659a747e4fSDavid du Colombier hadangle = 0; 16669a747e4fSDavid du Colombier s_reset(sinceword); 16679a747e4fSDavid du Colombier s_reset(lastword); 16689a747e4fSDavid du Colombier break; 16699a747e4fSDavid du Colombier case Twhite: 16709a747e4fSDavid du Colombier case Tcomment: 16719a747e4fSDavid du Colombier s_append(sinceword, s_to_c(stok)); 16729a747e4fSDavid du Colombier s_free(stok); 16739a747e4fSDavid du Colombier break; 16749a747e4fSDavid du Colombier case Trightangle: 16759a747e4fSDavid du Colombier if(!inangle) 16769a747e4fSDavid du Colombier goto Error; 16779a747e4fSDavid du Colombier inangle = 0; 16789a747e4fSDavid du Colombier hadangle = 1; 16799a747e4fSDavid du Colombier s_append(sinceword, s_to_c(stok)); 16809a747e4fSDavid du Colombier s_free(stok); 16819a747e4fSDavid du Colombier break; 16829a747e4fSDavid du Colombier case Twords: 16839a747e4fSDavid du Colombier case Tleftangle: 16849a747e4fSDavid du Colombier if(hadangle) 16859a747e4fSDavid du Colombier goto Error; 16869a747e4fSDavid du Colombier if(tok != Tleftangle && inangle && s_len(lastword)) 16879a747e4fSDavid du Colombier goto Error; 16889a747e4fSDavid du Colombier if(tok == Tleftangle) { 16899a747e4fSDavid du Colombier inangle = 1; 16909a747e4fSDavid du Colombier nword = 1; 16919a747e4fSDavid du Colombier } 16929a747e4fSDavid du Colombier s_append(ns, s_to_c(lastword)); 16939a747e4fSDavid du Colombier s_append(ns, s_to_c(sinceword)); 16949a747e4fSDavid du Colombier s_reset(sinceword); 16959a747e4fSDavid du Colombier if(tok == Tleftangle) { 16969a747e4fSDavid du Colombier s_append(ns, "<"); 16979a747e4fSDavid du Colombier s_reset(lastword); 16989a747e4fSDavid du Colombier } else { 16999a747e4fSDavid du Colombier s_free(lastword); 17009a747e4fSDavid du Colombier lastword = stok; 17019a747e4fSDavid du Colombier } 17029a747e4fSDavid du Colombier if(!inangle) 17039a747e4fSDavid du Colombier nword++; 17049a747e4fSDavid du Colombier break; 17059a747e4fSDavid du Colombier case Terror: // give up, use old string, addrs 17069a747e4fSDavid du Colombier Error: 17079a747e4fSDavid du Colombier ns = os; 17089a747e4fSDavid du Colombier os = nil; 17099a747e4fSDavid du Colombier freeaddrs(nto); 17109a747e4fSDavid du Colombier nto = nil; 17119a747e4fSDavid du Colombier werrstr("rfc822 syntax error"); 17129a747e4fSDavid du Colombier rfc822syntaxerror = 1; 17139a747e4fSDavid du Colombier goto Break2; 17149a747e4fSDavid du Colombier } 17159a747e4fSDavid du Colombier } 17169a747e4fSDavid du Colombier Break2: 17179a747e4fSDavid du Colombier s_free(*s); 17189a747e4fSDavid du Colombier s_free(os); 17199a747e4fSDavid du Colombier *s = ns; 17209a747e4fSDavid du Colombier nto = concataddr(nto, to); 17219a747e4fSDavid du Colombier return nto; 17229a747e4fSDavid du Colombier } 17239a747e4fSDavid du Colombier 17249a747e4fSDavid du Colombier void 17259a747e4fSDavid du Colombier Bdrain(Biobuf *b) 17269a747e4fSDavid du Colombier { 17279a747e4fSDavid du Colombier char buf[8192]; 17289a747e4fSDavid du Colombier 17299a747e4fSDavid du Colombier while(Bread(b, buf, sizeof buf) > 0) 17309a747e4fSDavid du Colombier ; 17319a747e4fSDavid du Colombier } 1732d9306527SDavid du Colombier 1733d9306527SDavid du Colombier void 1734d9306527SDavid du Colombier readmimetypes(void) 1735d9306527SDavid du Colombier { 1736d9306527SDavid du Colombier Biobuf *b; 1737d9306527SDavid du Colombier char *p; 1738d9306527SDavid du Colombier char *f[6]; 1739d9306527SDavid du Colombier char type[256]; 1740d9306527SDavid du Colombier static int alloced, inuse; 1741d9306527SDavid du Colombier 1742d9306527SDavid du Colombier if(mimetypes == 0){ 1743d9306527SDavid du Colombier alloced = 256; 1744d9306527SDavid du Colombier mimetypes = emalloc(alloced*sizeof(Ctype)); 1745d9306527SDavid du Colombier mimetypes[0].ext = ""; 1746d9306527SDavid du Colombier } 1747d9306527SDavid du Colombier 1748d9306527SDavid du Colombier b = Bopen("/sys/lib/mimetype", OREAD); 1749d9306527SDavid du Colombier if(b == nil) 1750d9306527SDavid du Colombier return; 1751d9306527SDavid du Colombier for(;;){ 1752d9306527SDavid du Colombier p = Brdline(b, '\n'); 1753d9306527SDavid du Colombier if(p == nil) 1754d9306527SDavid du Colombier break; 1755d9306527SDavid du Colombier p[Blinelen(b)-1] = 0; 1756d9306527SDavid du Colombier if(tokenize(p, f, 6) < 4) 1757d9306527SDavid du Colombier continue; 1758d9306527SDavid du Colombier if(strcmp(f[0], "-") == 0 || strcmp(f[1], "-") == 0 || strcmp(f[2], "-") == 0) 1759d9306527SDavid du Colombier continue; 1760d9306527SDavid du Colombier if(inuse + 1 >= alloced){ 1761d9306527SDavid du Colombier alloced += 256; 1762d9306527SDavid du Colombier mimetypes = erealloc(mimetypes, alloced*sizeof(Ctype)); 1763d9306527SDavid du Colombier } 1764d9306527SDavid du Colombier snprint(type, sizeof(type), "%s/%s", f[1], f[2]); 1765d9306527SDavid du Colombier mimetypes[inuse].type = estrdup(type); 1766d9306527SDavid du Colombier mimetypes[inuse].ext = estrdup(f[0]+1); 1767d9306527SDavid du Colombier mimetypes[inuse].display = !strcmp(type, "text/plain"); 1768d9306527SDavid du Colombier inuse++; 1769d9306527SDavid du Colombier 1770d9306527SDavid du Colombier // always make sure there's a terminator 1771d9306527SDavid du Colombier mimetypes[inuse].ext = 0; 1772d9306527SDavid du Colombier } 1773d9306527SDavid du Colombier Bterm(b); 1774d9306527SDavid du Colombier } 1775d9306527SDavid du Colombier 1776d9306527SDavid du Colombier char* 1777d9306527SDavid du Colombier estrdup(char *x) 1778d9306527SDavid du Colombier { 1779d9306527SDavid du Colombier x = strdup(x); 1780d9306527SDavid du Colombier if(x == nil) 1781d9306527SDavid du Colombier fatal("memory"); 1782d9306527SDavid du Colombier return x; 1783d9306527SDavid du Colombier } 1784d9306527SDavid du Colombier 1785d9306527SDavid du Colombier void* 1786d9306527SDavid du Colombier emalloc(int n) 1787d9306527SDavid du Colombier { 1788d9306527SDavid du Colombier void *x; 1789d9306527SDavid du Colombier 1790d9306527SDavid du Colombier x = malloc(n); 1791d9306527SDavid du Colombier if(x == nil) 1792d9306527SDavid du Colombier fatal("%r"); 1793d9306527SDavid du Colombier return x; 1794d9306527SDavid du Colombier } 1795d9306527SDavid du Colombier 1796d9306527SDavid du Colombier void* 1797d9306527SDavid du Colombier erealloc(void *x, int n) 1798d9306527SDavid du Colombier { 1799d9306527SDavid du Colombier x = realloc(x, n); 1800d9306527SDavid du Colombier if(x == nil) 1801d9306527SDavid du Colombier fatal("%r"); 1802d9306527SDavid du Colombier return x; 1803d9306527SDavid du Colombier } 1804d9306527SDavid du Colombier 1805d9306527SDavid du Colombier // 1806d9306527SDavid du Colombier // Formatter for %" 1807d9306527SDavid du Colombier // Use double quotes to protect white space, frogs, \ and " 1808d9306527SDavid du Colombier // 1809d9306527SDavid du Colombier enum 1810d9306527SDavid du Colombier { 1811d9306527SDavid du Colombier Qok = 0, 1812d9306527SDavid du Colombier Qquote, 1813d9306527SDavid du Colombier Qbackslash, 1814d9306527SDavid du Colombier }; 1815d9306527SDavid du Colombier 1816d9306527SDavid du Colombier static int 1817d9306527SDavid du Colombier needtoquote(Rune r) 1818d9306527SDavid du Colombier { 1819d9306527SDavid du Colombier if(r >= Runeself) 1820d9306527SDavid du Colombier return Qquote; 1821d9306527SDavid du Colombier if(r <= ' ') 1822d9306527SDavid du Colombier return Qquote; 1823d9306527SDavid du Colombier if(r=='\\' || r=='"') 1824d9306527SDavid du Colombier return Qbackslash; 1825d9306527SDavid du Colombier return Qok; 1826d9306527SDavid du Colombier } 1827d9306527SDavid du Colombier 1828d9306527SDavid du Colombier int 1829d9306527SDavid du Colombier doublequote(Fmt *f) 1830d9306527SDavid du Colombier { 1831d9306527SDavid du Colombier char *s, *t; 1832d9306527SDavid du Colombier int w, quotes; 1833d9306527SDavid du Colombier Rune r; 1834d9306527SDavid du Colombier 1835d9306527SDavid du Colombier s = va_arg(f->args, char*); 1836d9306527SDavid du Colombier if(s == nil || *s == '\0') 1837d9306527SDavid du Colombier return fmtstrcpy(f, "\"\""); 1838d9306527SDavid du Colombier 1839d9306527SDavid du Colombier quotes = 0; 1840d9306527SDavid du Colombier for(t=s; *t; t+=w){ 1841d9306527SDavid du Colombier w = chartorune(&r, t); 1842d9306527SDavid du Colombier quotes |= needtoquote(r); 1843d9306527SDavid du Colombier } 1844d9306527SDavid du Colombier if(quotes == 0) 1845d9306527SDavid du Colombier return fmtstrcpy(f, s); 1846d9306527SDavid du Colombier 1847d9306527SDavid du Colombier fmtrune(f, '"'); 1848d9306527SDavid du Colombier for(t=s; *t; t+=w){ 1849d9306527SDavid du Colombier w = chartorune(&r, t); 1850d9306527SDavid du Colombier if(needtoquote(r) == Qbackslash) 1851d9306527SDavid du Colombier fmtrune(f, '\\'); 1852d9306527SDavid du Colombier fmtrune(f, r); 1853d9306527SDavid du Colombier } 1854d9306527SDavid du Colombier return fmtrune(f, '"'); 1855d9306527SDavid du Colombier } 1856