118a458bcSDavid du Colombier /*
218a458bcSDavid du Colombier * marshal - gather mail message for transmission
318a458bcSDavid du Colombier */
49a747e4fSDavid du Colombier #include "common.h"
59a747e4fSDavid du Colombier #include <ctype.h>
69a747e4fSDavid du Colombier
79a747e4fSDavid du Colombier typedef struct Attach Attach;
89a747e4fSDavid du Colombier typedef struct Alias Alias;
99a747e4fSDavid du Colombier typedef struct Addr Addr;
109a747e4fSDavid du Colombier typedef struct Ctype Ctype;
119a747e4fSDavid du Colombier
129a747e4fSDavid du Colombier struct Attach {
139a747e4fSDavid du Colombier Attach *next;
149a747e4fSDavid du Colombier char *path;
159a747e4fSDavid du Colombier char *type;
16824682f6SDavid du Colombier int ainline;
179a747e4fSDavid du Colombier Ctype *ctype;
189a747e4fSDavid du Colombier };
199a747e4fSDavid du Colombier
209a747e4fSDavid du Colombier struct Alias
219a747e4fSDavid du Colombier {
229a747e4fSDavid du Colombier Alias *next;
239a747e4fSDavid du Colombier int n;
24d9306527SDavid du Colombier Addr *addr;
259a747e4fSDavid du Colombier };
269a747e4fSDavid du Colombier
279a747e4fSDavid du Colombier struct Addr
289a747e4fSDavid du Colombier {
299a747e4fSDavid du Colombier Addr *next;
309a747e4fSDavid du Colombier char *v;
319a747e4fSDavid du Colombier };
329a747e4fSDavid du Colombier
339a747e4fSDavid du Colombier enum {
349a747e4fSDavid du Colombier Hfrom,
359a747e4fSDavid du Colombier Hto,
369a747e4fSDavid du Colombier Hcc,
379a747e4fSDavid du Colombier Hbcc,
389a747e4fSDavid du Colombier Hsender,
399a747e4fSDavid du Colombier Hreplyto,
406b6b9ac8SDavid du Colombier Hinreplyto,
419a747e4fSDavid du Colombier Hdate,
429a747e4fSDavid du Colombier Hsubject,
439a747e4fSDavid du Colombier Hmime,
449a747e4fSDavid du Colombier Hpriority,
459a747e4fSDavid du Colombier Hmsgid,
469a747e4fSDavid du Colombier Hcontent,
479a747e4fSDavid du Colombier Hx,
48379e2210SDavid du Colombier Hprecedence,
499a747e4fSDavid du Colombier Nhdr,
509a747e4fSDavid du Colombier };
519a747e4fSDavid du Colombier
523ff48bf5SDavid du Colombier enum {
533ff48bf5SDavid du Colombier PGPsign = 1,
543ff48bf5SDavid du Colombier PGPencrypt = 2,
553ff48bf5SDavid du Colombier };
563ff48bf5SDavid du Colombier
579a747e4fSDavid du Colombier char *hdrs[Nhdr] = {
589a747e4fSDavid du Colombier [Hfrom] "from:",
599a747e4fSDavid du Colombier [Hto] "to:",
609a747e4fSDavid du Colombier [Hcc] "cc:",
619a747e4fSDavid du Colombier [Hbcc] "bcc:",
629a747e4fSDavid du Colombier [Hreplyto] "reply-to:",
636b6b9ac8SDavid du Colombier [Hinreplyto] "in-reply-to:",
649a747e4fSDavid du Colombier [Hsender] "sender:",
659a747e4fSDavid du Colombier [Hdate] "date:",
669a747e4fSDavid du Colombier [Hsubject] "subject:",
679a747e4fSDavid du Colombier [Hpriority] "priority:",
689a747e4fSDavid du Colombier [Hmsgid] "message-id:",
699a747e4fSDavid du Colombier [Hmime] "mime-",
709a747e4fSDavid du Colombier [Hcontent] "content-",
719a747e4fSDavid du Colombier [Hx] "x-",
72379e2210SDavid du Colombier [Hprecedence] "precedence",
739a747e4fSDavid du Colombier };
749a747e4fSDavid du Colombier
759a747e4fSDavid du Colombier struct Ctype {
769a747e4fSDavid du Colombier char *type;
779a747e4fSDavid du Colombier char *ext;
789a747e4fSDavid du Colombier int display;
799a747e4fSDavid du Colombier };
809a747e4fSDavid du Colombier
819a747e4fSDavid du Colombier Ctype ctype[] = {
829a747e4fSDavid du Colombier { "text/plain", "txt", 1, },
839a747e4fSDavid du Colombier { "text/html", "html", 1, },
849a747e4fSDavid du Colombier { "text/html", "htm", 1, },
859a747e4fSDavid du Colombier { "text/tab-separated-values", "tsv", 1, },
869a747e4fSDavid du Colombier { "text/richtext", "rtx", 1, },
879a747e4fSDavid du Colombier { "message/rfc822", "txt", 1, },
889a747e4fSDavid du Colombier { "", 0, 0, },
899a747e4fSDavid du Colombier };
909a747e4fSDavid du Colombier
91d9306527SDavid du Colombier Ctype *mimetypes;
92d9306527SDavid du Colombier
939a747e4fSDavid du Colombier int pid = -1;
943ff48bf5SDavid du Colombier int pgppid = -1;
959a747e4fSDavid du Colombier
969a747e4fSDavid du Colombier void Bdrain(Biobuf*);
9718a458bcSDavid du Colombier void attachment(Attach*, Biobuf*);
9818a458bcSDavid du Colombier void body(Biobuf*, Biobuf*, int);
9918a458bcSDavid du Colombier int cistrcmp(char*, char*);
10018a458bcSDavid du Colombier int cistrncmp(char*, char*, int);
10118a458bcSDavid du Colombier int doublequote(Fmt*);
102d9306527SDavid du Colombier void* emalloc(int);
10318a458bcSDavid du Colombier int enc64(char*, int, uchar*, int);
104d9306527SDavid du Colombier void* erealloc(void*, int);
10518a458bcSDavid du Colombier char* estrdup(char*);
10618a458bcSDavid du Colombier Addr* expand(int, char**);
10718a458bcSDavid du Colombier Addr* expandline(String**, Addr*);
10818a458bcSDavid du Colombier void freeaddr(Addr*);
109d9306527SDavid du Colombier void freeaddr(Addr *);
110d9306527SDavid du Colombier void freeaddrs(Addr*);
111d9306527SDavid du Colombier void freealias(Alias*);
112d9306527SDavid du Colombier void freealiases(Alias*);
11318a458bcSDavid du Colombier Attach* mkattach(char*, char*, int);
11418a458bcSDavid du Colombier char* mkboundary(void);
11517dd33a2SDavid du Colombier char* mksubject(char*);
11618a458bcSDavid du Colombier int pgpfilter(int*, int, int);
11718a458bcSDavid du Colombier int pgpopts(char*);
11818a458bcSDavid du Colombier int printcc(Biobuf*, Addr*);
11918a458bcSDavid du Colombier int printdate(Biobuf*);
12018a458bcSDavid du Colombier int printfrom(Biobuf*);
12118a458bcSDavid du Colombier int printinreplyto(Biobuf*, char*);
12218a458bcSDavid du Colombier int printsubject(Biobuf*, char*);
12318a458bcSDavid du Colombier int printto(Biobuf*, Addr*);
12418a458bcSDavid du Colombier Alias* readaliases(void);
12518a458bcSDavid du Colombier int readheaders(Biobuf*, int*, String**, Addr**, int);
12618a458bcSDavid du Colombier void readmimetypes(void);
12718a458bcSDavid du Colombier int rfc2047fmt(Fmt*);
12818a458bcSDavid du Colombier int sendmail(Addr*, Addr*, int*, char*);
12918a458bcSDavid du Colombier char* waitforsubprocs(void);
1309a747e4fSDavid du Colombier
131e288d156SDavid du Colombier int rflag, lbflag, xflag, holding, nflag, Fflag, eightflag, dflag;
1323ff48bf5SDavid du Colombier int pgpflag = 0;
1339a747e4fSDavid du Colombier char *user;
1349a747e4fSDavid du Colombier char *login;
1359a747e4fSDavid du Colombier Alias *aliases;
1369a747e4fSDavid du Colombier int rfc822syntaxerror;
1379a747e4fSDavid du Colombier char lastchar;
138106486e8SDavid du Colombier char *replymsg;
1399a747e4fSDavid du Colombier
1409a747e4fSDavid du Colombier enum
1419a747e4fSDavid du Colombier {
1429a747e4fSDavid du Colombier Ok = 0,
1439a747e4fSDavid du Colombier Nomessage = 1,
1449a747e4fSDavid du Colombier Nobody = 2,
1459a747e4fSDavid du Colombier Error = -1,
1469a747e4fSDavid du Colombier };
1479a747e4fSDavid du Colombier
148d9306527SDavid du Colombier #pragma varargck type "Z" char*
14917dd33a2SDavid du Colombier #pragma varargck type "U" char*
150d9306527SDavid du Colombier
1519a747e4fSDavid du Colombier void
usage(void)1529a747e4fSDavid du Colombier usage(void)
1539a747e4fSDavid du Colombier {
15418a458bcSDavid du Colombier fprint(2, "usage: %s [-Fr#xn] [-s subject] [-c ccrecipient] [-t type]"
15518a458bcSDavid du Colombier " [-aA attachment] [-p[es]] [-R replymsg] -8 | recipient-list\n",
1569a747e4fSDavid du Colombier argv0);
1579a747e4fSDavid du Colombier exits("usage");
1589a747e4fSDavid du Colombier }
1599a747e4fSDavid du Colombier
1609a747e4fSDavid du Colombier void
fatal(char * fmt,...)1619a747e4fSDavid du Colombier fatal(char *fmt, ...)
1629a747e4fSDavid du Colombier {
1639a747e4fSDavid du Colombier char buf[1024];
1649a747e4fSDavid du Colombier va_list arg;
1659a747e4fSDavid du Colombier
1669a747e4fSDavid du Colombier if(pid >= 0)
1679a747e4fSDavid du Colombier postnote(PNPROC, pid, "die");
1683ff48bf5SDavid du Colombier if(pgppid >= 0)
1693ff48bf5SDavid du Colombier postnote(PNPROC, pgppid, "die");
1709a747e4fSDavid du Colombier
1719a747e4fSDavid du Colombier va_start(arg, fmt);
1729a747e4fSDavid du Colombier vseprint(buf, buf+sizeof(buf), fmt, arg);
1739a747e4fSDavid du Colombier va_end(arg);
1749a747e4fSDavid du Colombier fprint(2, "%s: %s\n", argv0, buf);
1759a747e4fSDavid du Colombier holdoff(holding);
1769a747e4fSDavid du Colombier exits(buf);
1779a747e4fSDavid du Colombier }
1789a747e4fSDavid du Colombier
179*41dd6b47SDavid du Colombier static void
bwritesfree(Biobuf * bp,String ** str)180*41dd6b47SDavid du Colombier bwritesfree(Biobuf *bp, String **str)
181*41dd6b47SDavid du Colombier {
182*41dd6b47SDavid du Colombier if(Bwrite(bp, s_to_c(*str), s_len(*str)) != s_len(*str))
183*41dd6b47SDavid du Colombier fatal("write error");
184*41dd6b47SDavid du Colombier s_free(*str);
185*41dd6b47SDavid du Colombier *str = nil;
186*41dd6b47SDavid du Colombier }
187*41dd6b47SDavid du Colombier
1889a747e4fSDavid du Colombier void
main(int argc,char ** argv)1899a747e4fSDavid du Colombier main(int argc, char **argv)
1909a747e4fSDavid du Colombier {
19118a458bcSDavid du Colombier int ccargc, flags, fd, noinput, headersrv;
1929a747e4fSDavid du Colombier char *subject, *type, *boundary;
1933ff48bf5SDavid du Colombier char *ccargv[32];
19418a458bcSDavid du Colombier Addr *cc, *to;
19518a458bcSDavid du Colombier Attach *first, **l, *a;
19618a458bcSDavid du Colombier Biobuf in, out, *b;
19718a458bcSDavid du Colombier String *file, *hdrstring;
1989a747e4fSDavid du Colombier
1999a747e4fSDavid du Colombier noinput = 0;
2009a747e4fSDavid du Colombier subject = nil;
2019a747e4fSDavid du Colombier first = nil;
2029a747e4fSDavid du Colombier l = &first;
2039a747e4fSDavid du Colombier type = nil;
2049a747e4fSDavid du Colombier hdrstring = nil;
2053ff48bf5SDavid du Colombier ccargc = 0;
2063ff48bf5SDavid du Colombier
207d9306527SDavid du Colombier quotefmtinstall();
208d9306527SDavid du Colombier fmtinstall('Z', doublequote);
20917dd33a2SDavid du Colombier fmtinstall('U', rfc2047fmt);
210d9306527SDavid du Colombier
2119a747e4fSDavid du Colombier ARGBEGIN{
2129a747e4fSDavid du Colombier case 'a':
2139a747e4fSDavid du Colombier flags = 0;
2149a747e4fSDavid du Colombier goto aflag;
2159a747e4fSDavid du Colombier case 'A':
2169a747e4fSDavid du Colombier flags = 1;
2179a747e4fSDavid du Colombier aflag:
21818a458bcSDavid du Colombier a = mkattach(EARGF(usage()), type, flags);
2199a747e4fSDavid du Colombier if(a == nil)
2209a747e4fSDavid du Colombier exits("bad args");
2219a747e4fSDavid du Colombier type = nil;
2229a747e4fSDavid du Colombier *l = a;
2239a747e4fSDavid du Colombier l = &a->next;
2249a747e4fSDavid du Colombier break;
2253ff48bf5SDavid du Colombier case 'C':
2263ff48bf5SDavid du Colombier if(ccargc >= nelem(ccargv)-1)
2273ff48bf5SDavid du Colombier sysfatal("too many cc's");
22818a458bcSDavid du Colombier ccargv[ccargc++] = EARGF(usage());
2299a747e4fSDavid du Colombier break;
230e288d156SDavid du Colombier case 'd':
23118a458bcSDavid du Colombier dflag = 1; /* for sendmail */
232e288d156SDavid du Colombier break;
23318a458bcSDavid du Colombier case 'F':
23418a458bcSDavid du Colombier Fflag = 1; /* file message */
2359a747e4fSDavid du Colombier break;
23618a458bcSDavid du Colombier case 'n': /* no standard input */
2379a747e4fSDavid du Colombier nflag = 1;
2389a747e4fSDavid du Colombier break;
23918a458bcSDavid du Colombier case 'p': /* pgp flag: encrypt, sign, or both */
24018a458bcSDavid du Colombier if(pgpopts(EARGF(usage())) < 0)
24118a458bcSDavid du Colombier sysfatal("bad pgp options");
24218a458bcSDavid du Colombier break;
24318a458bcSDavid du Colombier case 'r':
24418a458bcSDavid du Colombier rflag = 1; /* for sendmail */
24518a458bcSDavid du Colombier break;
24618a458bcSDavid du Colombier case 'R':
24718a458bcSDavid du Colombier replymsg = EARGF(usage());
24818a458bcSDavid du Colombier break;
24918a458bcSDavid du Colombier case 's':
25018a458bcSDavid du Colombier subject = EARGF(usage());
25118a458bcSDavid du Colombier break;
25218a458bcSDavid du Colombier case 't':
25318a458bcSDavid du Colombier type = EARGF(usage());
25418a458bcSDavid du Colombier break;
25518a458bcSDavid du Colombier case 'x':
25618a458bcSDavid du Colombier xflag = 1; /* for sendmail */
25718a458bcSDavid du Colombier break;
25818a458bcSDavid du Colombier case '8': /* read recipients from rfc822 header */
2599a747e4fSDavid du Colombier eightflag = 1;
2609a747e4fSDavid du Colombier break;
26118a458bcSDavid du Colombier case '#':
26218a458bcSDavid du Colombier lbflag = 1; /* for sendmail */
2633ff48bf5SDavid du Colombier break;
2649a747e4fSDavid du Colombier default:
2659a747e4fSDavid du Colombier usage();
2669a747e4fSDavid du Colombier break;
2679a747e4fSDavid du Colombier }ARGEND;
2689a747e4fSDavid du Colombier
2699a747e4fSDavid du Colombier login = getlog();
2709a747e4fSDavid du Colombier user = getenv("upasname");
2719a747e4fSDavid du Colombier if(user == nil || *user == 0)
2729a747e4fSDavid du Colombier user = login;
2739a747e4fSDavid du Colombier if(user == nil || *user == 0)
2749a747e4fSDavid du Colombier sysfatal("can't read user name");
2759a747e4fSDavid du Colombier
2769a747e4fSDavid du Colombier if(Binit(&in, 0, OREAD) < 0)
2779a747e4fSDavid du Colombier sysfatal("can't Binit 0: %r");
2789a747e4fSDavid du Colombier
2799a747e4fSDavid du Colombier if(nflag && eightflag)
2809a747e4fSDavid du Colombier sysfatal("can't use both -n and -8");
2819a747e4fSDavid du Colombier if(eightflag && argc >= 1)
2829a747e4fSDavid du Colombier usage();
2839a747e4fSDavid du Colombier else if(!eightflag && argc < 1)
2849a747e4fSDavid du Colombier usage();
2859a747e4fSDavid du Colombier
2869a747e4fSDavid du Colombier aliases = readaliases();
2873ff48bf5SDavid du Colombier if(!eightflag){
2889a747e4fSDavid du Colombier to = expand(argc, argv);
2893ff48bf5SDavid du Colombier cc = expand(ccargc, ccargv);
290*41dd6b47SDavid du Colombier } else
291*41dd6b47SDavid du Colombier to = cc = nil;
2929a747e4fSDavid du Colombier
2939a747e4fSDavid du Colombier flags = 0;
2949a747e4fSDavid du Colombier headersrv = Nomessage;
295e288d156SDavid du Colombier if(!nflag && !xflag && !lbflag &&!dflag) {
29618a458bcSDavid du Colombier /*
29718a458bcSDavid du Colombier * pass through headers, keeping track of which we've seen,
29818a458bcSDavid du Colombier * perhaps building to list.
29918a458bcSDavid du Colombier */
3009a747e4fSDavid du Colombier holding = holdon();
30118a458bcSDavid du Colombier headersrv = readheaders(&in, &flags, &hdrstring,
30218a458bcSDavid du Colombier eightflag? &to: nil, 1);
3039a747e4fSDavid du Colombier if(rfc822syntaxerror){
3049a747e4fSDavid du Colombier Bdrain(&in);
3059a747e4fSDavid du Colombier fatal("rfc822 syntax error, message not sent");
3069a747e4fSDavid du Colombier }
3079a747e4fSDavid du Colombier if(to == nil){
3089a747e4fSDavid du Colombier Bdrain(&in);
3099a747e4fSDavid du Colombier fatal("no addresses found, message not sent");
3109a747e4fSDavid du Colombier }
3119a747e4fSDavid du Colombier
3129a747e4fSDavid du Colombier switch(headersrv){
31318a458bcSDavid du Colombier case Error: /* error */
3149a747e4fSDavid du Colombier fatal("reading");
3159a747e4fSDavid du Colombier break;
31618a458bcSDavid du Colombier case Nomessage: /* no message, just exit mimicking old behavior */
3179a747e4fSDavid du Colombier noinput = 1;
3189a747e4fSDavid du Colombier if(first == nil)
3199a747e4fSDavid du Colombier exits(0);
3209a747e4fSDavid du Colombier break;
3219a747e4fSDavid du Colombier }
3229a747e4fSDavid du Colombier }
3239a747e4fSDavid du Colombier
3243ff48bf5SDavid du Colombier fd = sendmail(to, cc, &pid, Fflag ? argv[0] : nil);
3259a747e4fSDavid du Colombier if(fd < 0)
3269a747e4fSDavid du Colombier sysfatal("execing sendmail: %r\n:");
327e288d156SDavid du Colombier if(xflag || lbflag || dflag){
3289a747e4fSDavid du Colombier close(fd);
3293ff48bf5SDavid du Colombier exits(waitforsubprocs());
3309a747e4fSDavid du Colombier }
3319a747e4fSDavid du Colombier
3329a747e4fSDavid du Colombier if(Binit(&out, fd, OWRITE) < 0)
3339a747e4fSDavid du Colombier fatal("can't Binit 1: %r");
3349a747e4fSDavid du Colombier
335*41dd6b47SDavid du Colombier if(!nflag)
336*41dd6b47SDavid du Colombier bwritesfree(&out, &hdrstring);
3379a747e4fSDavid du Colombier
33818a458bcSDavid du Colombier /* read user's standard headers */
3399a747e4fSDavid du Colombier file = s_new();
3409a747e4fSDavid du Colombier mboxpath("headers", user, file, 0);
3419a747e4fSDavid du Colombier b = Bopen(s_to_c(file), OREAD);
3429a747e4fSDavid du Colombier if(b != nil){
343*41dd6b47SDavid du Colombier if (readheaders(b, &flags, &hdrstring, nil, 0) == Error)
3449a747e4fSDavid du Colombier fatal("reading");
3459a747e4fSDavid du Colombier Bterm(b);
346*41dd6b47SDavid du Colombier bwritesfree(&out, &hdrstring);
3479a747e4fSDavid du Colombier }
3489a747e4fSDavid du Colombier
34918a458bcSDavid du Colombier /* add any headers we need */
3509a747e4fSDavid du Colombier if((flags & (1<<Hdate)) == 0)
3519a747e4fSDavid du Colombier if(printdate(&out) < 0)
3529a747e4fSDavid du Colombier fatal("writing");
3539a747e4fSDavid du Colombier if((flags & (1<<Hfrom)) == 0)
3549a747e4fSDavid du Colombier if(printfrom(&out) < 0)
3559a747e4fSDavid du Colombier fatal("writing");
3569a747e4fSDavid du Colombier if((flags & (1<<Hto)) == 0)
3579a747e4fSDavid du Colombier if(printto(&out, to) < 0)
3589a747e4fSDavid du Colombier fatal("writing");
3593ff48bf5SDavid du Colombier if((flags & (1<<Hcc)) == 0)
3603ff48bf5SDavid du Colombier if(printcc(&out, cc) < 0)
3613ff48bf5SDavid du Colombier fatal("writing");
3629a747e4fSDavid du Colombier if((flags & (1<<Hsubject)) == 0 && subject != nil)
3639a747e4fSDavid du Colombier if(printsubject(&out, subject) < 0)
3649a747e4fSDavid du Colombier fatal("writing");
365106486e8SDavid du Colombier if(replymsg != nil)
366106486e8SDavid du Colombier if(printinreplyto(&out, replymsg) < 0)
367106486e8SDavid du Colombier fatal("writing");
3689a747e4fSDavid du Colombier Bprint(&out, "MIME-Version: 1.0\n");
3699a747e4fSDavid du Colombier
37018a458bcSDavid du Colombier if(pgpflag){
37118a458bcSDavid du Colombier /* interpose pgp process between us and sendmail to handle body */
3723ff48bf5SDavid du Colombier Bflush(&out);
3733ff48bf5SDavid du Colombier Bterm(&out);
3743ff48bf5SDavid du Colombier fd = pgpfilter(&pgppid, fd, pgpflag);
3753ff48bf5SDavid du Colombier if(Binit(&out, fd, OWRITE) < 0)
3763ff48bf5SDavid du Colombier fatal("can't Binit 1: %r");
3773ff48bf5SDavid du Colombier }
3783ff48bf5SDavid du Colombier
37918a458bcSDavid du Colombier /* if attachments, stick in multipart headers */
3809a747e4fSDavid du Colombier boundary = nil;
3819a747e4fSDavid du Colombier if(first != nil){
3829a747e4fSDavid du Colombier boundary = mkboundary();
3839a747e4fSDavid du Colombier Bprint(&out, "Content-Type: multipart/mixed;\n");
3849a747e4fSDavid du Colombier Bprint(&out, "\tboundary=\"%s\"\n\n", boundary);
3859a747e4fSDavid du Colombier Bprint(&out, "This is a multi-part message in MIME format.\n");
3869a747e4fSDavid du Colombier Bprint(&out, "--%s\n", boundary);
387a189aeebSDavid du Colombier Bprint(&out, "Content-Disposition: inline\n");
3889a747e4fSDavid du Colombier }
3899a747e4fSDavid du Colombier
3909a747e4fSDavid du Colombier if(!nflag){
39118a458bcSDavid du Colombier if(!noinput && headersrv == Ok)
3929a747e4fSDavid du Colombier body(&in, &out, 1);
3939a747e4fSDavid du Colombier } else
3949a747e4fSDavid du Colombier Bprint(&out, "\n");
3959a747e4fSDavid du Colombier holdoff(holding);
3969a747e4fSDavid du Colombier
3979a747e4fSDavid du Colombier Bflush(&out);
3989a747e4fSDavid du Colombier for(a = first; a != nil; a = a->next){
3999a747e4fSDavid du Colombier if(lastchar != '\n')
4009a747e4fSDavid du Colombier Bprint(&out, "\n");
4019a747e4fSDavid du Colombier Bprint(&out, "--%s\n", boundary);
4029a747e4fSDavid du Colombier attachment(a, &out);
4039a747e4fSDavid du Colombier }
4049a747e4fSDavid du Colombier
4057a02f3c0SDavid du Colombier if(first != nil){
4067a02f3c0SDavid du Colombier if(lastchar != '\n')
4077a02f3c0SDavid du Colombier Bprint(&out, "\n");
4089a747e4fSDavid du Colombier Bprint(&out, "--%s--\n", boundary);
4097a02f3c0SDavid du Colombier }
4109a747e4fSDavid du Colombier
4119a747e4fSDavid du Colombier Bterm(&out);
4129a747e4fSDavid du Colombier close(fd);
4133ff48bf5SDavid du Colombier exits(waitforsubprocs());
4143ff48bf5SDavid du Colombier }
4153ff48bf5SDavid du Colombier
41618a458bcSDavid du Colombier /* evaluate pgp option string */
4173ff48bf5SDavid du Colombier int
pgpopts(char * s)4183ff48bf5SDavid du Colombier pgpopts(char *s)
4193ff48bf5SDavid du Colombier {
4203ff48bf5SDavid du Colombier if(s == nil || s[0] == '\0')
4213ff48bf5SDavid du Colombier return -1;
4223ff48bf5SDavid du Colombier while(*s){
4233ff48bf5SDavid du Colombier switch(*s++){
4243ff48bf5SDavid du Colombier case 's': case 'S':
4253ff48bf5SDavid du Colombier pgpflag |= PGPsign;
4263ff48bf5SDavid du Colombier break;
4273ff48bf5SDavid du Colombier case 'e': case 'E':
4283ff48bf5SDavid du Colombier pgpflag |= PGPencrypt;
4293ff48bf5SDavid du Colombier break;
4303ff48bf5SDavid du Colombier default:
4313ff48bf5SDavid du Colombier return -1;
4323ff48bf5SDavid du Colombier }
4333ff48bf5SDavid du Colombier }
4343ff48bf5SDavid du Colombier return 0;
4359a747e4fSDavid du Colombier }
4369a747e4fSDavid du Colombier
43718a458bcSDavid du Colombier /*
43818a458bcSDavid du Colombier * read headers from stdin into a String, expanding local aliases,
43918a458bcSDavid du Colombier * keep track of which headers are there, which addresses we have
44018a458bcSDavid du Colombier * remove Bcc: line.
44118a458bcSDavid du Colombier */
4429a747e4fSDavid du Colombier int
readheaders(Biobuf * in,int * fp,String ** sp,Addr ** top,int strict)443dc5a79c1SDavid du Colombier readheaders(Biobuf *in, int *fp, String **sp, Addr **top, int strict)
4449a747e4fSDavid du Colombier {
44518a458bcSDavid du Colombier int i, seen, hdrtype;
44618a458bcSDavid du Colombier char *p;
4479a747e4fSDavid du Colombier Addr *to;
4489a747e4fSDavid du Colombier String *s, *sline;
4499a747e4fSDavid du Colombier
4509a747e4fSDavid du Colombier s = s_new();
4519a747e4fSDavid du Colombier sline = nil;
4529a747e4fSDavid du Colombier to = nil;
4539a747e4fSDavid du Colombier hdrtype = -1;
4549a747e4fSDavid du Colombier seen = 0;
4559a747e4fSDavid du Colombier for(;;) {
4569a747e4fSDavid du Colombier if((p = Brdline(in, '\n')) != nil) {
4579a747e4fSDavid du Colombier seen = 1;
4589a747e4fSDavid du Colombier p[Blinelen(in)-1] = 0;
4596b6b9ac8SDavid du Colombier
46018a458bcSDavid du Colombier /* coalesce multiline headers */
4619a747e4fSDavid du Colombier if((*p == ' ' || *p == '\t') && sline){
4629a747e4fSDavid du Colombier s_append(sline, "\n");
4639a747e4fSDavid du Colombier s_append(sline, p);
4649a747e4fSDavid du Colombier p[Blinelen(in)-1] = '\n';
4659a747e4fSDavid du Colombier continue;
4669a747e4fSDavid du Colombier }
4679a747e4fSDavid du Colombier }
4689a747e4fSDavid du Colombier
46918a458bcSDavid du Colombier /* process the current header, it's all been read */
4709a747e4fSDavid du Colombier if(sline) {
4719a747e4fSDavid du Colombier assert(hdrtype != -1);
4729a747e4fSDavid du Colombier if(top){
4739a747e4fSDavid du Colombier switch(hdrtype){
4749a747e4fSDavid du Colombier case Hto:
4759a747e4fSDavid du Colombier case Hcc:
4769a747e4fSDavid du Colombier case Hbcc:
4779a747e4fSDavid du Colombier to = expandline(&sline, to);
4789a747e4fSDavid du Colombier break;
4799a747e4fSDavid du Colombier }
4809a747e4fSDavid du Colombier }
48117dd33a2SDavid du Colombier if(hdrtype == Hsubject){
48217dd33a2SDavid du Colombier s_append(s, mksubject(s_to_c(sline)));
48317dd33a2SDavid du Colombier s_append(s, "\n");
48417dd33a2SDavid du Colombier }else if(top==nil || hdrtype!=Hbcc){
4859a747e4fSDavid du Colombier s_append(s, s_to_c(sline));
4869a747e4fSDavid du Colombier s_append(s, "\n");
4879a747e4fSDavid du Colombier }
4889a747e4fSDavid du Colombier s_free(sline);
4899a747e4fSDavid du Colombier sline = nil;
4909a747e4fSDavid du Colombier }
4919a747e4fSDavid du Colombier
4929a747e4fSDavid du Colombier if(p == nil)
4939a747e4fSDavid du Colombier break;
4949a747e4fSDavid du Colombier
49518a458bcSDavid du Colombier /* if no :, it's not a header, seek back and break */
4969a747e4fSDavid du Colombier if(strchr(p, ':') == nil){
4979a747e4fSDavid du Colombier p[Blinelen(in)-1] = '\n';
4989a747e4fSDavid du Colombier Bseek(in, -Blinelen(in), 1);
4999a747e4fSDavid du Colombier break;
5009a747e4fSDavid du Colombier }
5019a747e4fSDavid du Colombier
5029a747e4fSDavid du Colombier sline = s_copy(p);
5039a747e4fSDavid du Colombier
50418a458bcSDavid du Colombier /*
50518a458bcSDavid du Colombier * classify the header. If we don't recognize it, break.
50618a458bcSDavid du Colombier * This is to take care of users who start messages with
50718a458bcSDavid du Colombier * lines that contain ':'s but that aren't headers.
50818a458bcSDavid du Colombier * This is a bit hokey. Since I decided to let users type
50918a458bcSDavid du Colombier * headers, I need some way to distinguish. Therefore,
51018a458bcSDavid du Colombier * marshal tries to know all likely headers and will indeed
51118a458bcSDavid du Colombier * screw up if the user types an unlikely one. -- presotto
51218a458bcSDavid du Colombier */
5139a747e4fSDavid du Colombier hdrtype = -1;
5149a747e4fSDavid du Colombier for(i = 0; i < nelem(hdrs); i++){
5159a747e4fSDavid du Colombier if(cistrncmp(hdrs[i], p, strlen(hdrs[i])) == 0){
5169a747e4fSDavid du Colombier *fp |= 1<<i;
5179a747e4fSDavid du Colombier hdrtype = i;
5189a747e4fSDavid du Colombier break;
5199a747e4fSDavid du Colombier }
5209a747e4fSDavid du Colombier }
521e58cd47aSDavid du Colombier if(strict){
5229a747e4fSDavid du Colombier if(hdrtype == -1){
5239a747e4fSDavid du Colombier p[Blinelen(in)-1] = '\n';
5249a747e4fSDavid du Colombier Bseek(in, -Blinelen(in), 1);
5259a747e4fSDavid du Colombier break;
5269a747e4fSDavid du Colombier }
527dc5a79c1SDavid du Colombier } else
528dc5a79c1SDavid du Colombier hdrtype = 0;
5299a747e4fSDavid du Colombier p[Blinelen(in)-1] = '\n';
5309a747e4fSDavid du Colombier }
5319a747e4fSDavid du Colombier
5329a747e4fSDavid du Colombier *sp = s;
5339a747e4fSDavid du Colombier if(top)
5349a747e4fSDavid du Colombier *top = to;
5359a747e4fSDavid du Colombier
5366b6b9ac8SDavid du Colombier if(seen == 0){
5376b6b9ac8SDavid du Colombier if(Blinelen(in) == 0)
5389a747e4fSDavid du Colombier return Nomessage;
5396b6b9ac8SDavid du Colombier else
5406b6b9ac8SDavid du Colombier return Ok;
5416b6b9ac8SDavid du Colombier }
5429a747e4fSDavid du Colombier if(p == nil)
5439a747e4fSDavid du Colombier return Nobody;
5449a747e4fSDavid du Colombier return Ok;
5459a747e4fSDavid du Colombier }
5469a747e4fSDavid du Colombier
54718a458bcSDavid du Colombier /* pass the body to sendmail, make sure body starts and ends with a newline */
5489a747e4fSDavid du Colombier void
body(Biobuf * in,Biobuf * out,int docontenttype)5499a747e4fSDavid du Colombier body(Biobuf *in, Biobuf *out, int docontenttype)
5509a747e4fSDavid du Colombier {
5519a747e4fSDavid du Colombier char *buf, *p;
5529a747e4fSDavid du Colombier int i, n, len;
5539a747e4fSDavid du Colombier
5549a747e4fSDavid du Colombier n = 0;
5559a747e4fSDavid du Colombier len = 16*1024;
556d9306527SDavid du Colombier buf = emalloc(len);
5579a747e4fSDavid du Colombier
55818a458bcSDavid du Colombier /* first char must be newline */
5599a747e4fSDavid du Colombier i = Bgetc(in);
560d9306527SDavid du Colombier if(i > 0){
5619a747e4fSDavid du Colombier if(i != '\n')
5629a747e4fSDavid du Colombier buf[n++] = '\n';
5639a747e4fSDavid du Colombier buf[n++] = i;
56418a458bcSDavid du Colombier } else
565d9306527SDavid du Colombier buf[n++] = '\n';
5669a747e4fSDavid du Colombier
56718a458bcSDavid du Colombier /* read into memory */
5689a747e4fSDavid du Colombier if(docontenttype){
5699a747e4fSDavid du Colombier while(docontenttype){
5709a747e4fSDavid du Colombier if(n == len){
5719a747e4fSDavid du Colombier len += len >> 2;
5729a747e4fSDavid du Colombier buf = realloc(buf, len);
5739a747e4fSDavid du Colombier if(buf == nil)
5749a747e4fSDavid du Colombier sysfatal("%r");
5759a747e4fSDavid du Colombier }
5769a747e4fSDavid du Colombier p = buf+n;
5779a747e4fSDavid du Colombier i = Bread(in, p, len - n);
5789a747e4fSDavid du Colombier if(i < 0)
5799a747e4fSDavid du Colombier fatal("input error2");
5809a747e4fSDavid du Colombier if(i == 0)
5819a747e4fSDavid du Colombier break;
5829a747e4fSDavid du Colombier n += i;
5839a747e4fSDavid du Colombier for(; i > 0; i--)
5847a02f3c0SDavid du Colombier if((*p++ & 0x80) && docontenttype){
5859a747e4fSDavid du Colombier Bprint(out, "Content-Type: text/plain; charset=\"UTF-8\"\n");
5869a747e4fSDavid du Colombier Bprint(out, "Content-Transfer-Encoding: 8bit\n");
5879a747e4fSDavid du Colombier docontenttype = 0;
5889a747e4fSDavid du Colombier break;
5899a747e4fSDavid du Colombier }
5909a747e4fSDavid du Colombier }
5919a747e4fSDavid du Colombier if(docontenttype){
5929a747e4fSDavid du Colombier Bprint(out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
5939a747e4fSDavid du Colombier Bprint(out, "Content-Transfer-Encoding: 7bit\n");
5949a747e4fSDavid du Colombier }
5959a747e4fSDavid du Colombier }
5969a747e4fSDavid du Colombier
59718a458bcSDavid du Colombier /* write what we already read */
5989a747e4fSDavid du Colombier if(Bwrite(out, buf, n) < 0)
5999a747e4fSDavid du Colombier fatal("output error");
6009a747e4fSDavid du Colombier if(n > 0)
6019a747e4fSDavid du Colombier lastchar = buf[n-1];
6029a747e4fSDavid du Colombier else
6039a747e4fSDavid du Colombier lastchar = '\n';
6049a747e4fSDavid du Colombier
6059a747e4fSDavid du Colombier
60618a458bcSDavid du Colombier /* pass the rest */
6079a747e4fSDavid du Colombier for(;;){
6089a747e4fSDavid du Colombier n = Bread(in, buf, len);
6099a747e4fSDavid du Colombier if(n < 0)
6109a747e4fSDavid du Colombier fatal("input error2");
6119a747e4fSDavid du Colombier if(n == 0)
6129a747e4fSDavid du Colombier break;
6139a747e4fSDavid du Colombier if(Bwrite(out, buf, n) < 0)
6149a747e4fSDavid du Colombier fatal("output error");
6159a747e4fSDavid du Colombier lastchar = buf[n-1];
6169a747e4fSDavid du Colombier }
6179a747e4fSDavid du Colombier }
6189a747e4fSDavid du Colombier
61918a458bcSDavid du Colombier /*
62018a458bcSDavid du Colombier * pass the body to sendmail encoding with base64
62118a458bcSDavid du Colombier *
62218a458bcSDavid du Colombier * the size of buf is very important to enc64. Anything other than
62318a458bcSDavid du Colombier * a multiple of 3 will cause enc64 to output a termination sequence.
62418a458bcSDavid du Colombier * To ensure that a full buf corresponds to a multiple of complete lines,
62518a458bcSDavid du Colombier * we make buf a multiple of 3*18 since that's how many enc64 sticks on
62618a458bcSDavid du Colombier * a single line. This avoids short lines in the output which is pleasing
62718a458bcSDavid du Colombier * but not necessary.
62818a458bcSDavid du Colombier */
6299a747e4fSDavid du Colombier void
body64(Biobuf * in,Biobuf * out)6309a747e4fSDavid du Colombier body64(Biobuf *in, Biobuf *out)
6319a747e4fSDavid du Colombier {
63218a458bcSDavid du Colombier int m, n;
6339a747e4fSDavid du Colombier uchar buf[3*18*54];
6349a747e4fSDavid du Colombier char obuf[3*18*54*2];
6359a747e4fSDavid du Colombier
6369a747e4fSDavid du Colombier Bprint(out, "\n");
6379a747e4fSDavid du Colombier for(;;){
6389a747e4fSDavid du Colombier n = Bread(in, buf, sizeof(buf));
6399a747e4fSDavid du Colombier if(n < 0)
6409a747e4fSDavid du Colombier fatal("input error");
6419a747e4fSDavid du Colombier if(n == 0)
6429a747e4fSDavid du Colombier break;
6439a747e4fSDavid du Colombier m = enc64(obuf, sizeof(obuf), buf, n);
6449a747e4fSDavid du Colombier if(Bwrite(out, obuf, m) < 0)
6459a747e4fSDavid du Colombier fatal("output error");
6469a747e4fSDavid du Colombier }
6479a747e4fSDavid du Colombier lastchar = '\n';
6489a747e4fSDavid du Colombier }
6499a747e4fSDavid du Colombier
65018a458bcSDavid du Colombier /* pass message to sendmail, make sure body starts with a newline */
6519a747e4fSDavid du Colombier void
copy(Biobuf * in,Biobuf * out)6529a747e4fSDavid du Colombier copy(Biobuf *in, Biobuf *out)
6539a747e4fSDavid du Colombier {
6549a747e4fSDavid du Colombier int n;
65518a458bcSDavid du Colombier char buf[4*1024];
6569a747e4fSDavid du Colombier
6579a747e4fSDavid du Colombier for(;;){
6589a747e4fSDavid du Colombier n = Bread(in, buf, sizeof(buf));
6599a747e4fSDavid du Colombier if(n < 0)
6609a747e4fSDavid du Colombier fatal("input error");
6619a747e4fSDavid du Colombier if(n == 0)
6629a747e4fSDavid du Colombier break;
6639a747e4fSDavid du Colombier if(Bwrite(out, buf, n) < 0)
6649a747e4fSDavid du Colombier fatal("output error");
6659a747e4fSDavid du Colombier }
6669a747e4fSDavid du Colombier }
6679a747e4fSDavid du Colombier
6689a747e4fSDavid du Colombier void
attachment(Attach * a,Biobuf * out)6699a747e4fSDavid du Colombier attachment(Attach *a, Biobuf *out)
6709a747e4fSDavid du Colombier {
6719a747e4fSDavid du Colombier Biobuf *f;
6729a747e4fSDavid du Colombier char *p;
6739a747e4fSDavid du Colombier
67418a458bcSDavid du Colombier /* if it's already mime encoded, just copy */
6759a747e4fSDavid du Colombier if(strcmp(a->type, "mime") == 0){
6769a747e4fSDavid du Colombier f = Bopen(a->path, OREAD);
6779a747e4fSDavid du Colombier if(f == nil){
67818a458bcSDavid du Colombier /*
67918a458bcSDavid du Colombier * hack: give marshal time to stdin, before we kill it
68018a458bcSDavid du Colombier * (for dead.letter)
68118a458bcSDavid du Colombier */
6829a747e4fSDavid du Colombier sleep(500);
6839a747e4fSDavid du Colombier postnote(PNPROC, pid, "interrupt");
6849a747e4fSDavid du Colombier sysfatal("opening %s: %r", a->path);
6859a747e4fSDavid du Colombier }
6869a747e4fSDavid du Colombier copy(f, out);
6879a747e4fSDavid du Colombier Bterm(f);
6889a747e4fSDavid du Colombier }
6899a747e4fSDavid du Colombier
69018a458bcSDavid du Colombier /* if it's not already mime encoded ... */
6919a747e4fSDavid du Colombier if(strcmp(a->type, "text/plain") != 0)
6929a747e4fSDavid du Colombier Bprint(out, "Content-Type: %s\n", a->type);
6939a747e4fSDavid du Colombier
69418a458bcSDavid du Colombier if(a->ainline)
695a189aeebSDavid du Colombier Bprint(out, "Content-Disposition: inline\n");
69618a458bcSDavid du Colombier else {
6979a747e4fSDavid du Colombier p = strrchr(a->path, '/');
6989a747e4fSDavid du Colombier if(p == nil)
6999a747e4fSDavid du Colombier p = a->path;
7009a747e4fSDavid du Colombier else
7019a747e4fSDavid du Colombier p++;
702d9306527SDavid du Colombier Bprint(out, "Content-Disposition: attachment; filename=%Z\n", p);
7039a747e4fSDavid du Colombier }
7049a747e4fSDavid du Colombier
7059a747e4fSDavid du Colombier f = Bopen(a->path, OREAD);
7069a747e4fSDavid du Colombier if(f == nil){
70718a458bcSDavid du Colombier /*
70818a458bcSDavid du Colombier * hack: give marshal time to stdin, before we kill it
70918a458bcSDavid du Colombier * (for dead.letter)
71018a458bcSDavid du Colombier */
7119a747e4fSDavid du Colombier sleep(500);
7129a747e4fSDavid du Colombier postnote(PNPROC, pid, "interrupt");
7139a747e4fSDavid du Colombier sysfatal("opening %s: %r", a->path);
7149a747e4fSDavid du Colombier }
715d9306527SDavid du Colombier
716d9306527SDavid du Colombier /* dump our local 'From ' line when passing along mail messages */
717d9306527SDavid du Colombier if(strcmp(a->type, "message/rfc822") == 0){
718d9306527SDavid du Colombier p = Brdline(f, '\n');
719d9306527SDavid du Colombier if(strncmp(p, "From ", 5) != 0)
720d9306527SDavid du Colombier Bseek(f, 0, 0);
721d9306527SDavid du Colombier }
72218a458bcSDavid du Colombier if(a->ctype->display)
7239a747e4fSDavid du Colombier body(f, out, strcmp(a->type, "text/plain") == 0);
72418a458bcSDavid du Colombier else {
7259a747e4fSDavid du Colombier Bprint(out, "Content-Transfer-Encoding: base64\n");
7269a747e4fSDavid du Colombier body64(f, out);
7279a747e4fSDavid du Colombier }
7289a747e4fSDavid du Colombier Bterm(f);
7299a747e4fSDavid du Colombier }
7309a747e4fSDavid du Colombier
7319a747e4fSDavid du Colombier char *ascwday[] =
7329a747e4fSDavid du Colombier {
7339a747e4fSDavid du Colombier "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
7349a747e4fSDavid du Colombier };
7359a747e4fSDavid du Colombier
7369a747e4fSDavid du Colombier char *ascmon[] =
7379a747e4fSDavid du Colombier {
7389a747e4fSDavid du Colombier "Jan", "Feb", "Mar", "Apr", "May", "Jun",
7399a747e4fSDavid du Colombier "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
7409a747e4fSDavid du Colombier };
7419a747e4fSDavid du Colombier
7429a747e4fSDavid du Colombier int
printdate(Biobuf * b)7439a747e4fSDavid du Colombier printdate(Biobuf *b)
7449a747e4fSDavid du Colombier {
7459a747e4fSDavid du Colombier int tz;
74618a458bcSDavid du Colombier Tm *tm;
7479a747e4fSDavid du Colombier
7489a747e4fSDavid du Colombier tm = localtime(time(0));
74918a458bcSDavid du Colombier tz = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
7509a747e4fSDavid du Colombier
7519a747e4fSDavid du Colombier return Bprint(b, "Date: %s, %d %s %d %2.2d:%2.2d:%2.2d %s%.4d\n",
7529a747e4fSDavid du Colombier ascwday[tm->wday], tm->mday, ascmon[tm->mon], 1900 + tm->year,
7537c881178SDavid du Colombier tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz);
7549a747e4fSDavid du Colombier }
7559a747e4fSDavid du Colombier
7569a747e4fSDavid du Colombier int
printfrom(Biobuf * b)7579a747e4fSDavid du Colombier printfrom(Biobuf *b)
7589a747e4fSDavid du Colombier {
7599a747e4fSDavid du Colombier return Bprint(b, "From: %s\n", user);
7609a747e4fSDavid du Colombier }
7619a747e4fSDavid du Colombier
7629a747e4fSDavid du Colombier int
printto(Biobuf * b,Addr * a)7633ff48bf5SDavid du Colombier printto(Biobuf *b, Addr *a)
7649a747e4fSDavid du Colombier {
7657c881178SDavid du Colombier int i;
7667c881178SDavid du Colombier
7673ff48bf5SDavid du Colombier if(Bprint(b, "To: %s", a->v) < 0)
7689a747e4fSDavid du Colombier return -1;
7697c881178SDavid du Colombier i = 0;
7703ff48bf5SDavid du Colombier for(a = a->next; a != nil; a = a->next)
7717c881178SDavid du Colombier if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0)
7729a747e4fSDavid du Colombier return -1;
7739a747e4fSDavid du Colombier if(Bprint(b, "\n") < 0)
7749a747e4fSDavid du Colombier return -1;
7753ff48bf5SDavid du Colombier return 0;
7763ff48bf5SDavid du Colombier }
7773ff48bf5SDavid du Colombier
7783ff48bf5SDavid du Colombier int
printcc(Biobuf * b,Addr * a)7793ff48bf5SDavid du Colombier printcc(Biobuf *b, Addr *a)
7803ff48bf5SDavid du Colombier {
7817c881178SDavid du Colombier int i;
7827c881178SDavid du Colombier
7833ff48bf5SDavid du Colombier if(a == nil)
7843ff48bf5SDavid du Colombier return 0;
7853ff48bf5SDavid du Colombier if(Bprint(b, "CC: %s", a->v) < 0)
7863ff48bf5SDavid du Colombier return -1;
7877c881178SDavid du Colombier i = 0;
7883ff48bf5SDavid du Colombier for(a = a->next; a != nil; a = a->next)
7897c881178SDavid du Colombier if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0)
7903ff48bf5SDavid du Colombier return -1;
7913ff48bf5SDavid du Colombier if(Bprint(b, "\n") < 0)
7923ff48bf5SDavid du Colombier return -1;
7933ff48bf5SDavid du Colombier return 0;
7949a747e4fSDavid du Colombier }
7959a747e4fSDavid du Colombier
7969a747e4fSDavid du Colombier int
printsubject(Biobuf * b,char * subject)7979a747e4fSDavid du Colombier printsubject(Biobuf *b, char *subject)
7989a747e4fSDavid du Colombier {
79917dd33a2SDavid du Colombier return Bprint(b, "Subject: %U\n", subject);
8009a747e4fSDavid du Colombier }
8019a747e4fSDavid du Colombier
802106486e8SDavid du Colombier int
printinreplyto(Biobuf * out,char * dir)803106486e8SDavid du Colombier printinreplyto(Biobuf *out, char *dir)
804106486e8SDavid du Colombier {
80518a458bcSDavid du Colombier int fd, n;
806106486e8SDavid du Colombier char buf[256];
80718a458bcSDavid du Colombier String *s = s_copy(dir);
808106486e8SDavid du Colombier
809106486e8SDavid du Colombier s_append(s, "/messageid");
810106486e8SDavid du Colombier fd = open(s_to_c(s), OREAD);
811106486e8SDavid du Colombier s_free(s);
812106486e8SDavid du Colombier if(fd < 0)
813106486e8SDavid du Colombier return 0;
814106486e8SDavid du Colombier n = read(fd, buf, sizeof(buf)-1);
815106486e8SDavid du Colombier close(fd);
816106486e8SDavid du Colombier if(n <= 0)
817106486e8SDavid du Colombier return 0;
818106486e8SDavid du Colombier buf[n] = 0;
819106486e8SDavid du Colombier return Bprint(out, "In-Reply-To: %s\n", buf);
820106486e8SDavid du Colombier }
821106486e8SDavid du Colombier
8229a747e4fSDavid du Colombier Attach*
mkattach(char * file,char * type,int ainline)823824682f6SDavid du Colombier mkattach(char *file, char *type, int ainline)
8249a747e4fSDavid du Colombier {
8259a747e4fSDavid du Colombier int n, pfd[2];
82618a458bcSDavid du Colombier char *p;
82718a458bcSDavid du Colombier char ftype[64];
82818a458bcSDavid du Colombier Attach *a;
82918a458bcSDavid du Colombier Ctype *c;
8309a747e4fSDavid du Colombier
8319a747e4fSDavid du Colombier if(file == nil)
8329a747e4fSDavid du Colombier return nil;
833375daca8SDavid du Colombier if(access(file, 4) == -1){
834375daca8SDavid du Colombier fprint(2, "%s: %s can't read file\n", argv0, file);
835375daca8SDavid du Colombier return nil;
836375daca8SDavid du Colombier }
837d9306527SDavid du Colombier a = emalloc(sizeof(*a));
8389a747e4fSDavid du Colombier a->path = file;
8399a747e4fSDavid du Colombier a->next = nil;
8409a747e4fSDavid du Colombier a->type = type;
841824682f6SDavid du Colombier a->ainline = ainline;
8429a747e4fSDavid du Colombier a->ctype = nil;
8439a747e4fSDavid du Colombier if(type != nil){
8449a747e4fSDavid du Colombier for(c = ctype; ; c++)
8459a747e4fSDavid du Colombier if(strncmp(type, c->type, strlen(c->type)) == 0){
8469a747e4fSDavid du Colombier a->ctype = c;
8479a747e4fSDavid du Colombier break;
8489a747e4fSDavid du Colombier }
8499a747e4fSDavid du Colombier return a;
8509a747e4fSDavid du Colombier }
8519a747e4fSDavid du Colombier
85218a458bcSDavid du Colombier /* pick a type depending on extension */
8539a747e4fSDavid du Colombier p = strchr(file, '.');
854d9306527SDavid du Colombier if(p != nil)
8559a747e4fSDavid du Colombier p++;
856d9306527SDavid du Colombier
85718a458bcSDavid du Colombier /* check the builtin extensions */
858d9306527SDavid du Colombier if(p != nil){
8599a747e4fSDavid du Colombier for(c = ctype; c->ext != nil; c++)
8609a747e4fSDavid du Colombier if(strcmp(p, c->ext) == 0){
8619a747e4fSDavid du Colombier a->type = c->type;
8629a747e4fSDavid du Colombier a->ctype = c;
8639a747e4fSDavid du Colombier return a;
8649a747e4fSDavid du Colombier }
8659a747e4fSDavid du Colombier }
8669a747e4fSDavid du Colombier
86718a458bcSDavid du Colombier /* try the mime types file */
868d9306527SDavid du Colombier if(p != nil){
869d9306527SDavid du Colombier if(mimetypes == nil)
870d9306527SDavid du Colombier readmimetypes();
871d9306527SDavid du Colombier for(c = mimetypes; c != nil && c->ext != nil; c++)
872d9306527SDavid du Colombier if(strcmp(p, c->ext) == 0){
873d9306527SDavid du Colombier a->type = c->type;
874d9306527SDavid du Colombier a->ctype = c;
875d9306527SDavid du Colombier return a;
876d9306527SDavid du Colombier }
877d9306527SDavid du Colombier }
878d9306527SDavid du Colombier
87918a458bcSDavid du Colombier /* run file to figure out the type */
88018a458bcSDavid du Colombier a->type = "application/octet-stream"; /* safest default */
8819a747e4fSDavid du Colombier if(pipe(pfd) < 0)
8829a747e4fSDavid du Colombier return a;
8839a747e4fSDavid du Colombier switch(fork()){
8849a747e4fSDavid du Colombier case -1:
8859a747e4fSDavid du Colombier break;
8869a747e4fSDavid du Colombier case 0:
8879a747e4fSDavid du Colombier close(pfd[1]);
8889a747e4fSDavid du Colombier close(0);
8899a747e4fSDavid du Colombier dup(pfd[0], 0);
8909a747e4fSDavid du Colombier close(1);
8919a747e4fSDavid du Colombier dup(pfd[0], 1);
892f19e7b74SDavid du Colombier execl("/bin/file", "file", "-m", file, nil);
8939a747e4fSDavid du Colombier exits(0);
8949a747e4fSDavid du Colombier default:
8959a747e4fSDavid du Colombier close(pfd[0]);
8969a747e4fSDavid du Colombier n = read(pfd[1], ftype, sizeof(ftype));
8979a747e4fSDavid du Colombier if(n > 0){
8989a747e4fSDavid du Colombier ftype[n-1] = 0;
899d9306527SDavid du Colombier a->type = estrdup(ftype);
9009a747e4fSDavid du Colombier }
9019a747e4fSDavid du Colombier close(pfd[1]);
9029a747e4fSDavid du Colombier waitpid();
9039a747e4fSDavid du Colombier break;
9049a747e4fSDavid du Colombier }
9059a747e4fSDavid du Colombier
9069a747e4fSDavid du Colombier for(c = ctype; ; c++)
9079a747e4fSDavid du Colombier if(strncmp(a->type, c->type, strlen(c->type)) == 0){
9089a747e4fSDavid du Colombier a->ctype = c;
9099a747e4fSDavid du Colombier break;
9109a747e4fSDavid du Colombier }
9119a747e4fSDavid du Colombier return a;
9129a747e4fSDavid du Colombier }
9139a747e4fSDavid du Colombier
9149a747e4fSDavid du Colombier char*
mkboundary(void)9159a747e4fSDavid du Colombier mkboundary(void)
9169a747e4fSDavid du Colombier {
9179a747e4fSDavid du Colombier int i;
91818a458bcSDavid du Colombier char buf[32];
9199a747e4fSDavid du Colombier
9209a747e4fSDavid du Colombier srand((time(0)<<16)|getpid());
9219a747e4fSDavid du Colombier strcpy(buf, "upas-");
9229a747e4fSDavid du Colombier for(i = 5; i < sizeof(buf)-1; i++)
9239a747e4fSDavid du Colombier buf[i] = 'a' + nrand(26);
9249a747e4fSDavid du Colombier buf[i] = 0;
925d9306527SDavid du Colombier return estrdup(buf);
9269a747e4fSDavid du Colombier }
9279a747e4fSDavid du Colombier
92818a458bcSDavid du Colombier /* copy types to two fd's */
9299a747e4fSDavid du Colombier static void
tee(int in,int out1,int out2)9309a747e4fSDavid du Colombier tee(int in, int out1, int out2)
9319a747e4fSDavid du Colombier {
9329a747e4fSDavid du Colombier int n;
93318a458bcSDavid du Colombier char buf[8*1024];
9349a747e4fSDavid du Colombier
93518a458bcSDavid du Colombier while ((n = read(in, buf, sizeof buf)) > 0)
93618a458bcSDavid du Colombier if (write(out1, buf, n) != n ||
93718a458bcSDavid du Colombier write(out2, buf, n) != n)
9389a747e4fSDavid du Colombier break;
9399a747e4fSDavid du Colombier }
9409a747e4fSDavid du Colombier
94118a458bcSDavid du Colombier /* print the unix from line */
9429a747e4fSDavid du Colombier int
printunixfrom(int fd)9439a747e4fSDavid du Colombier printunixfrom(int fd)
9449a747e4fSDavid du Colombier {
9459a747e4fSDavid du Colombier int tz;
94618a458bcSDavid du Colombier Tm *tm;
9479a747e4fSDavid du Colombier
9489a747e4fSDavid du Colombier tm = localtime(time(0));
94918a458bcSDavid du Colombier tz = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
9509a747e4fSDavid du Colombier
9519a747e4fSDavid du Colombier return fprint(fd, "From %s %s %s %d %2.2d:%2.2d:%2.2d %s%.4d %d\n",
9529a747e4fSDavid du Colombier user,
9539a747e4fSDavid du Colombier ascwday[tm->wday], ascmon[tm->mon], tm->mday,
9547c881178SDavid du Colombier tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz, 1900 + tm->year);
9559a747e4fSDavid du Colombier }
9569a747e4fSDavid du Colombier
9576b6b9ac8SDavid du Colombier char *specialfile[] =
9586b6b9ac8SDavid du Colombier {
9596b6b9ac8SDavid du Colombier "pipeto",
9606b6b9ac8SDavid du Colombier "pipefrom",
9616b6b9ac8SDavid du Colombier "L.mbox",
9626b6b9ac8SDavid du Colombier "forward",
9636b6b9ac8SDavid du Colombier "names"
9646b6b9ac8SDavid du Colombier };
9656b6b9ac8SDavid du Colombier
96618a458bcSDavid du Colombier /* return 1 if this is a special file */
9676b6b9ac8SDavid du Colombier static int
special(String * s)9686b6b9ac8SDavid du Colombier special(String *s)
9699a747e4fSDavid du Colombier {
9706b6b9ac8SDavid du Colombier int i;
97118a458bcSDavid du Colombier char *p;
9726b6b9ac8SDavid du Colombier
9736b6b9ac8SDavid du Colombier p = strrchr(s_to_c(s), '/');
9746b6b9ac8SDavid du Colombier if(p == nil)
9756b6b9ac8SDavid du Colombier p = s_to_c(s);
9766b6b9ac8SDavid du Colombier else
9776b6b9ac8SDavid du Colombier p++;
9786b6b9ac8SDavid du Colombier for(i = 0; i < nelem(specialfile); i++)
9796b6b9ac8SDavid du Colombier if(strcmp(p, specialfile[i]) == 0)
9806b6b9ac8SDavid du Colombier return 1;
9816b6b9ac8SDavid du Colombier return 0;
9826b6b9ac8SDavid du Colombier }
9836b6b9ac8SDavid du Colombier
98418a458bcSDavid du Colombier /* open the folder using the recipients account name */
9856b6b9ac8SDavid du Colombier static int
openfolder(char * rcvr)9866b6b9ac8SDavid du Colombier openfolder(char *rcvr)
9876b6b9ac8SDavid du Colombier {
98818a458bcSDavid du Colombier int c, fd, scarey;
9896b6b9ac8SDavid du Colombier char *p;
9906b6b9ac8SDavid du Colombier Dir *d;
99118a458bcSDavid du Colombier String *file;
9926b6b9ac8SDavid du Colombier
9936b6b9ac8SDavid du Colombier file = s_new();
9946b6b9ac8SDavid du Colombier mboxpath("f", user, file, 0);
9956b6b9ac8SDavid du Colombier
99618a458bcSDavid du Colombier /* if $mail/f exists, store there, otherwise in $mail */
9976b6b9ac8SDavid du Colombier d = dirstat(s_to_c(file));
9986b6b9ac8SDavid du Colombier if(d == nil || d->qid.type != QTDIR){
9996b6b9ac8SDavid du Colombier scarey = 1;
10006b6b9ac8SDavid du Colombier file->ptr -= 1;
10016b6b9ac8SDavid du Colombier } else {
10026b6b9ac8SDavid du Colombier s_putc(file, '/');
10036b6b9ac8SDavid du Colombier scarey = 0;
10046b6b9ac8SDavid du Colombier }
10056b6b9ac8SDavid du Colombier free(d);
10069a747e4fSDavid du Colombier
10079a747e4fSDavid du Colombier p = strrchr(rcvr, '!');
10089a747e4fSDavid du Colombier if(p != nil)
10099a747e4fSDavid du Colombier rcvr = p+1;
10109a747e4fSDavid du Colombier
10116b6b9ac8SDavid du Colombier while(*rcvr && *rcvr != '@'){
10126b6b9ac8SDavid du Colombier c = *rcvr++;
10136b6b9ac8SDavid du Colombier if(c == '/')
10146b6b9ac8SDavid du Colombier c = '_';
10156b6b9ac8SDavid du Colombier s_putc(file, c);
10166b6b9ac8SDavid du Colombier }
10176b6b9ac8SDavid du Colombier s_terminate(file);
10186b6b9ac8SDavid du Colombier
10196b6b9ac8SDavid du Colombier if(scarey && special(file)){
10206b6b9ac8SDavid du Colombier fprint(2, "%s: won't overwrite %s\n", argv0, s_to_c(file));
10216b6b9ac8SDavid du Colombier s_free(file);
10226b6b9ac8SDavid du Colombier return -1;
10236b6b9ac8SDavid du Colombier }
10246b6b9ac8SDavid du Colombier
10256b6b9ac8SDavid du Colombier fd = open(s_to_c(file), OWRITE);
10266b6b9ac8SDavid du Colombier if(fd < 0)
10276b6b9ac8SDavid du Colombier fd = create(s_to_c(file), OWRITE, 0660);
10286b6b9ac8SDavid du Colombier
10296b6b9ac8SDavid du Colombier s_free(file);
10306b6b9ac8SDavid du Colombier return fd;
10319a747e4fSDavid du Colombier }
10329a747e4fSDavid du Colombier
103318a458bcSDavid du Colombier /* start up sendmail and return an fd to talk to it with */
10349a747e4fSDavid du Colombier int
sendmail(Addr * to,Addr * cc,int * pid,char * rcvr)10353ff48bf5SDavid du Colombier sendmail(Addr *to, Addr *cc, int *pid, char *rcvr)
10369a747e4fSDavid du Colombier {
10379a747e4fSDavid du Colombier int ac, fd;
10389a747e4fSDavid du Colombier int pfd[2];
103918a458bcSDavid du Colombier char **av, **v;
10409a747e4fSDavid du Colombier Addr *a;
104118a458bcSDavid du Colombier String *cmd;
10429a747e4fSDavid du Colombier
10439a747e4fSDavid du Colombier fd = -1;
10446b6b9ac8SDavid du Colombier if(rcvr != nil)
10456b6b9ac8SDavid du Colombier fd = openfolder(rcvr);
10469a747e4fSDavid du Colombier
10479a747e4fSDavid du Colombier ac = 0;
10489a747e4fSDavid du Colombier for(a = to; a != nil; a = a->next)
10499a747e4fSDavid du Colombier ac++;
10503ff48bf5SDavid du Colombier for(a = cc; a != nil; a = a->next)
10513ff48bf5SDavid du Colombier ac++;
1052e288d156SDavid du Colombier v = av = emalloc(sizeof(char*)*(ac+20));
10539a747e4fSDavid du Colombier ac = 0;
10549a747e4fSDavid du Colombier v[ac++] = "sendmail";
10559a747e4fSDavid du Colombier if(xflag)
10569a747e4fSDavid du Colombier v[ac++] = "-x";
10579a747e4fSDavid du Colombier if(rflag)
10589a747e4fSDavid du Colombier v[ac++] = "-r";
10599a747e4fSDavid du Colombier if(lbflag)
10609a747e4fSDavid du Colombier v[ac++] = "-#";
1061e288d156SDavid du Colombier if(dflag)
1062e288d156SDavid du Colombier v[ac++] = "-d";
10639a747e4fSDavid du Colombier for(a = to; a != nil; a = a->next)
10649a747e4fSDavid du Colombier v[ac++] = a->v;
10653ff48bf5SDavid du Colombier for(a = cc; a != nil; a = a->next)
10663ff48bf5SDavid du Colombier v[ac++] = a->v;
10679a747e4fSDavid du Colombier v[ac] = 0;
10689a747e4fSDavid du Colombier
10699a747e4fSDavid du Colombier if(pipe(pfd) < 0)
10709a747e4fSDavid du Colombier fatal("%r");
1071106486e8SDavid du Colombier switch(*pid = rfork(RFFDG|RFREND|RFPROC|RFENVG)){
10729a747e4fSDavid du Colombier case -1:
10739a747e4fSDavid du Colombier fatal("%r");
10749a747e4fSDavid du Colombier break;
10759a747e4fSDavid du Colombier case 0:
10769a747e4fSDavid du Colombier if(holding)
10779a747e4fSDavid du Colombier close(holding);
10789a747e4fSDavid du Colombier close(pfd[1]);
10799a747e4fSDavid du Colombier dup(pfd[0], 0);
10809a747e4fSDavid du Colombier close(pfd[0]);
10819a747e4fSDavid du Colombier
10829a747e4fSDavid du Colombier if(rcvr != nil){
10839a747e4fSDavid du Colombier if(pipe(pfd) < 0)
10849a747e4fSDavid du Colombier fatal("%r");
10859a747e4fSDavid du Colombier switch(fork()){
10869a747e4fSDavid du Colombier case -1:
10879a747e4fSDavid du Colombier fatal("%r");
10889a747e4fSDavid du Colombier break;
10899a747e4fSDavid du Colombier case 0:
10909a747e4fSDavid du Colombier close(pfd[0]);
10919a747e4fSDavid du Colombier seek(fd, 0, 2);
10929a747e4fSDavid du Colombier printunixfrom(fd);
10939a747e4fSDavid du Colombier tee(0, pfd[1], fd);
10949a747e4fSDavid du Colombier write(fd, "\n", 1);
10959a747e4fSDavid du Colombier exits(0);
10969a747e4fSDavid du Colombier default:
10979a747e4fSDavid du Colombier close(fd);
10989a747e4fSDavid du Colombier close(pfd[1]);
10999a747e4fSDavid du Colombier dup(pfd[0], 0);
11009a747e4fSDavid du Colombier break;
11019a747e4fSDavid du Colombier }
11029a747e4fSDavid du Colombier }
11039a747e4fSDavid du Colombier
1104106486e8SDavid du Colombier if(replymsg != nil)
1105106486e8SDavid du Colombier putenv("replymsg", replymsg);
1106106486e8SDavid du Colombier
1107dc5a79c1SDavid du Colombier cmd = mboxpath("pipefrom", login, s_new(), 0);
1108f121929fSDavid du Colombier exec(s_to_c(cmd), av);
11099a747e4fSDavid du Colombier exec("/bin/myupassend", av);
11109a747e4fSDavid du Colombier exec("/bin/upas/send", av);
11119a747e4fSDavid du Colombier fatal("execing: %r");
11129a747e4fSDavid du Colombier break;
11139a747e4fSDavid du Colombier default:
11149a747e4fSDavid du Colombier if(rcvr != nil)
11159a747e4fSDavid du Colombier close(fd);
11169a747e4fSDavid du Colombier close(pfd[0]);
11179a747e4fSDavid du Colombier break;
11189a747e4fSDavid du Colombier }
11199a747e4fSDavid du Colombier return pfd[1];
11209a747e4fSDavid du Colombier }
11219a747e4fSDavid du Colombier
112218a458bcSDavid du Colombier /*
112318a458bcSDavid du Colombier * start up pgp process and return an fd to talk to it with.
112418a458bcSDavid du Colombier * its standard output will be the original fd, which goes to sendmail.
112518a458bcSDavid du Colombier */
11263ff48bf5SDavid du Colombier int
pgpfilter(int * pid,int fd,int pgpflag)11273ff48bf5SDavid du Colombier pgpfilter(int *pid, int fd, int pgpflag)
11289a747e4fSDavid du Colombier {
11293ff48bf5SDavid du Colombier int ac;
11303ff48bf5SDavid du Colombier int pfd[2];
113118a458bcSDavid du Colombier char **av, **v;
11329a747e4fSDavid du Colombier
1133d9306527SDavid du Colombier v = av = emalloc(sizeof(char*)*8);
11343ff48bf5SDavid du Colombier ac = 0;
11353ff48bf5SDavid du Colombier v[ac++] = "pgp";
113690e65e0fSDavid du Colombier v[ac++] = "-fat"; /* operate as a filter, generate text */
11373ff48bf5SDavid du Colombier if(pgpflag & PGPsign)
11383ff48bf5SDavid du Colombier v[ac++] = "-s";
11393ff48bf5SDavid du Colombier if(pgpflag & PGPencrypt)
11403ff48bf5SDavid du Colombier v[ac++] = "-e";
11413ff48bf5SDavid du Colombier v[ac] = 0;
11423ff48bf5SDavid du Colombier
11433ff48bf5SDavid du Colombier if(pipe(pfd) < 0)
11443ff48bf5SDavid du Colombier fatal("%r");
11453ff48bf5SDavid du Colombier switch(*pid = fork()){
11463ff48bf5SDavid du Colombier case -1:
11473ff48bf5SDavid du Colombier fatal("%r");
11483ff48bf5SDavid du Colombier break;
11493ff48bf5SDavid du Colombier case 0:
11503ff48bf5SDavid du Colombier close(pfd[1]);
11513ff48bf5SDavid du Colombier dup(pfd[0], 0);
11523ff48bf5SDavid du Colombier close(pfd[0]);
11533ff48bf5SDavid du Colombier dup(fd, 1);
11543ff48bf5SDavid du Colombier close(fd);
11553ff48bf5SDavid du Colombier
115690e65e0fSDavid du Colombier /* add newline to avoid confusing pgp output with 822 headers */
115790e65e0fSDavid du Colombier write(1, "\n", 1);
115890e65e0fSDavid du Colombier exec("/bin/pgp", av);
11593ff48bf5SDavid du Colombier fatal("execing: %r");
11603ff48bf5SDavid du Colombier break;
11613ff48bf5SDavid du Colombier default:
11623ff48bf5SDavid du Colombier close(pfd[0]);
11639a747e4fSDavid du Colombier break;
11649a747e4fSDavid du Colombier }
11653ff48bf5SDavid du Colombier close(fd);
11663ff48bf5SDavid du Colombier return pfd[1];
11673ff48bf5SDavid du Colombier }
11683ff48bf5SDavid du Colombier
116918a458bcSDavid du Colombier /* wait for sendmail and pgp to exit; exit here if either failed */
11703ff48bf5SDavid du Colombier char*
waitforsubprocs(void)11713ff48bf5SDavid du Colombier waitforsubprocs(void)
11723ff48bf5SDavid du Colombier {
11733ff48bf5SDavid du Colombier Waitmsg *w;
11743ff48bf5SDavid du Colombier char *err;
11753ff48bf5SDavid du Colombier
11763ff48bf5SDavid du Colombier err = nil;
11773ff48bf5SDavid du Colombier while((w = wait()) != nil){
117818a458bcSDavid du Colombier if(w->pid == pid || w->pid == pgppid)
11793ff48bf5SDavid du Colombier if(w->msg[0] != 0)
1180d9306527SDavid du Colombier err = estrdup(w->msg);
11819a747e4fSDavid du Colombier free(w);
11829a747e4fSDavid du Colombier }
11833ff48bf5SDavid du Colombier if(err)
11843ff48bf5SDavid du Colombier exits(err);
11859a747e4fSDavid du Colombier return nil;
11869a747e4fSDavid du Colombier }
11879a747e4fSDavid du Colombier
11889a747e4fSDavid du Colombier int
cistrncmp(char * a,char * b,int n)11899a747e4fSDavid du Colombier cistrncmp(char *a, char *b, int n)
11909a747e4fSDavid du Colombier {
119118a458bcSDavid du Colombier while(n-- > 0)
11929a747e4fSDavid du Colombier if(tolower(*a++) != tolower(*b++))
11939a747e4fSDavid du Colombier return -1;
11949a747e4fSDavid du Colombier return 0;
11959a747e4fSDavid du Colombier }
11969a747e4fSDavid du Colombier
11979a747e4fSDavid du Colombier int
cistrcmp(char * a,char * b)11989a747e4fSDavid du Colombier cistrcmp(char *a, char *b)
11999a747e4fSDavid du Colombier {
12009a747e4fSDavid du Colombier for(;;){
12019a747e4fSDavid du Colombier if(tolower(*a) != tolower(*b++))
12029a747e4fSDavid du Colombier return -1;
12039a747e4fSDavid du Colombier if(*a++ == 0)
12049a747e4fSDavid du Colombier break;
12059a747e4fSDavid du Colombier }
12069a747e4fSDavid du Colombier return 0;
12079a747e4fSDavid du Colombier }
12089a747e4fSDavid du Colombier
12099a747e4fSDavid du Colombier static uchar t64d[256];
12109a747e4fSDavid du Colombier static char t64e[64];
12119a747e4fSDavid du Colombier
12129a747e4fSDavid du Colombier static void
init64(void)12139a747e4fSDavid du Colombier init64(void)
12149a747e4fSDavid du Colombier {
12159a747e4fSDavid du Colombier int c, i;
12169a747e4fSDavid du Colombier
12179a747e4fSDavid du Colombier memset(t64d, 255, 256);
12189a747e4fSDavid du Colombier memset(t64e, '=', 64);
12199a747e4fSDavid du Colombier i = 0;
12209a747e4fSDavid du Colombier for(c = 'A'; c <= 'Z'; c++){
12219a747e4fSDavid du Colombier t64e[i] = c;
12229a747e4fSDavid du Colombier t64d[c] = i++;
12239a747e4fSDavid du Colombier }
12249a747e4fSDavid du Colombier for(c = 'a'; c <= 'z'; c++){
12259a747e4fSDavid du Colombier t64e[i] = c;
12269a747e4fSDavid du Colombier t64d[c] = i++;
12279a747e4fSDavid du Colombier }
12289a747e4fSDavid du Colombier for(c = '0'; c <= '9'; c++){
12299a747e4fSDavid du Colombier t64e[i] = c;
12309a747e4fSDavid du Colombier t64d[c] = i++;
12319a747e4fSDavid du Colombier }
12329a747e4fSDavid du Colombier t64e[i] = '+';
12339a747e4fSDavid du Colombier t64d['+'] = i++;
12349a747e4fSDavid du Colombier t64e[i] = '/';
12359a747e4fSDavid du Colombier t64d['/'] = i;
12369a747e4fSDavid du Colombier }
12379a747e4fSDavid du Colombier
12389a747e4fSDavid du Colombier int
enc64(char * out,int lim,uchar * in,int n)12399a747e4fSDavid du Colombier enc64(char *out, int lim, uchar *in, int n)
12409a747e4fSDavid du Colombier {
12419a747e4fSDavid du Colombier int i;
12429a747e4fSDavid du Colombier ulong b24;
12439a747e4fSDavid du Colombier char *start = out;
12449a747e4fSDavid du Colombier char *e = out + lim;
12459a747e4fSDavid du Colombier
12469a747e4fSDavid du Colombier if(t64e[0] == 0)
12479a747e4fSDavid du Colombier init64();
12489a747e4fSDavid du Colombier for(i = 0; i < n/3; i++){
12499a747e4fSDavid du Colombier b24 = (*in++)<<16;
12509a747e4fSDavid du Colombier b24 |= (*in++)<<8;
12519a747e4fSDavid du Colombier b24 |= *in++;
12529a747e4fSDavid du Colombier if(out + 5 >= e)
12539a747e4fSDavid du Colombier goto exhausted;
12549a747e4fSDavid du Colombier *out++ = t64e[(b24>>18)];
12559a747e4fSDavid du Colombier *out++ = t64e[(b24>>12)&0x3f];
12569a747e4fSDavid du Colombier *out++ = t64e[(b24>>6)&0x3f];
12579a747e4fSDavid du Colombier *out++ = t64e[(b24)&0x3f];
12589a747e4fSDavid du Colombier if((i%18) == 17)
12599a747e4fSDavid du Colombier *out++ = '\n';
12609a747e4fSDavid du Colombier }
12619a747e4fSDavid du Colombier
12629a747e4fSDavid du Colombier switch(n%3){
12639a747e4fSDavid du Colombier case 2:
12649a747e4fSDavid du Colombier b24 = (*in++)<<16;
12659a747e4fSDavid du Colombier b24 |= (*in)<<8;
12669a747e4fSDavid du Colombier if(out + 4 >= e)
12679a747e4fSDavid du Colombier goto exhausted;
12689a747e4fSDavid du Colombier *out++ = t64e[(b24>>18)];
12699a747e4fSDavid du Colombier *out++ = t64e[(b24>>12)&0x3f];
12709a747e4fSDavid du Colombier *out++ = t64e[(b24>>6)&0x3f];
12719a747e4fSDavid du Colombier break;
12729a747e4fSDavid du Colombier case 1:
12739a747e4fSDavid du Colombier b24 = (*in)<<16;
12749a747e4fSDavid du Colombier if(out + 4 >= e)
12759a747e4fSDavid du Colombier goto exhausted;
12769a747e4fSDavid du Colombier *out++ = t64e[(b24>>18)];
12779a747e4fSDavid du Colombier *out++ = t64e[(b24>>12)&0x3f];
12789a747e4fSDavid du Colombier *out++ = '=';
12799a747e4fSDavid du Colombier break;
12809a747e4fSDavid du Colombier case 0:
12819a747e4fSDavid du Colombier if((i%18) != 0)
12829a747e4fSDavid du Colombier *out++ = '\n';
12839a747e4fSDavid du Colombier *out = 0;
12849a747e4fSDavid du Colombier return out - start;
12859a747e4fSDavid du Colombier }
12869a747e4fSDavid du Colombier exhausted:
12879a747e4fSDavid du Colombier *out++ = '=';
12889a747e4fSDavid du Colombier *out++ = '\n';
12899a747e4fSDavid du Colombier *out = 0;
12909a747e4fSDavid du Colombier return out - start;
12919a747e4fSDavid du Colombier }
12929a747e4fSDavid du Colombier
1293d9306527SDavid du Colombier void
freealias(Alias * a)1294d9306527SDavid du Colombier freealias(Alias *a)
1295d9306527SDavid du Colombier {
1296d9306527SDavid du Colombier freeaddrs(a->addr);
1297d9306527SDavid du Colombier free(a);
1298d9306527SDavid du Colombier }
1299d9306527SDavid du Colombier
1300d9306527SDavid du Colombier void
freealiases(Alias * a)1301d9306527SDavid du Colombier freealiases(Alias *a)
1302d9306527SDavid du Colombier {
1303d9306527SDavid du Colombier Alias *next;
1304d9306527SDavid du Colombier
1305d9306527SDavid du Colombier while(a != nil){
1306d9306527SDavid du Colombier next = a->next;
1307d9306527SDavid du Colombier freealias(a);
1308d9306527SDavid du Colombier a = next;
1309d9306527SDavid du Colombier }
1310d9306527SDavid du Colombier }
1311d9306527SDavid du Colombier
131218a458bcSDavid du Colombier /*
131318a458bcSDavid du Colombier * read alias file
131418a458bcSDavid du Colombier */
13159a747e4fSDavid du Colombier Alias*
readaliases(void)13169a747e4fSDavid du Colombier readaliases(void)
13179a747e4fSDavid du Colombier {
1318d9306527SDavid du Colombier Addr *addr, **al;
131918a458bcSDavid du Colombier Alias *a, **l, *first;
132018a458bcSDavid du Colombier Sinstack *sp;
1321d9306527SDavid du Colombier String *file, *line, *token;
13229a747e4fSDavid du Colombier static int already;
13239a747e4fSDavid du Colombier
13249a747e4fSDavid du Colombier first = nil;
1325d9306527SDavid du Colombier file = s_new();
1326d9306527SDavid du Colombier line = s_new();
1327d9306527SDavid du Colombier token = s_new();
1328d9306527SDavid du Colombier
132918a458bcSDavid du Colombier /* open and get length */
1330d9306527SDavid du Colombier mboxpath("names", login, file, 0);
1331dc5a79c1SDavid du Colombier sp = s_allocinstack(s_to_c(file));
1332dc5a79c1SDavid du Colombier if(sp == nil)
1333d9306527SDavid du Colombier goto out;
1334d9306527SDavid du Colombier
13359a747e4fSDavid du Colombier l = &first;
1336d9306527SDavid du Colombier
133718a458bcSDavid du Colombier /* read a line at a time. */
1338dc5a79c1SDavid du Colombier while(s_rdinstack(sp, s_restart(line))!=nil) {
1339d9306527SDavid du Colombier s_restart(line);
1340d9306527SDavid du Colombier a = emalloc(sizeof(Alias));
1341d9306527SDavid du Colombier al = &a->addr;
134218a458bcSDavid du Colombier while(s_parse(line, s_restart(token)) != 0) {
1343d9306527SDavid du Colombier addr = emalloc(sizeof(Addr));
1344d9306527SDavid du Colombier addr->v = strdup(s_to_c(token));
1345d9306527SDavid du Colombier addr->next = 0;
1346d9306527SDavid du Colombier *al = addr;
1347d9306527SDavid du Colombier al = &addr->next;
13489a747e4fSDavid du Colombier }
1349d9306527SDavid du Colombier if(a->addr == nil || a->addr->next == nil){
1350d9306527SDavid du Colombier freealias(a);
1351d9306527SDavid du Colombier continue;
13529a747e4fSDavid du Colombier }
13539a747e4fSDavid du Colombier a->next = nil;
13549a747e4fSDavid du Colombier *l = a;
13559a747e4fSDavid du Colombier l = &a->next;
13569a747e4fSDavid du Colombier }
1357dc5a79c1SDavid du Colombier s_freeinstack(sp);
1358d9306527SDavid du Colombier out:
1359d9306527SDavid du Colombier s_free(file);
1360d9306527SDavid du Colombier s_free(line);
1361d9306527SDavid du Colombier s_free(token);
13629a747e4fSDavid du Colombier return first;
13639a747e4fSDavid du Colombier }
13649a747e4fSDavid du Colombier
13659a747e4fSDavid du Colombier Addr*
newaddr(char * name)13669a747e4fSDavid du Colombier newaddr(char *name)
13679a747e4fSDavid du Colombier {
13689a747e4fSDavid du Colombier Addr *a;
13699a747e4fSDavid du Colombier
1370d9306527SDavid du Colombier a = emalloc(sizeof(*a));
13719a747e4fSDavid du Colombier a->next = nil;
1372d9306527SDavid du Colombier a->v = estrdup(name);
13739a747e4fSDavid du Colombier if(a->v == nil)
13749a747e4fSDavid du Colombier sysfatal("%r");
13759a747e4fSDavid du Colombier return a;
13769a747e4fSDavid du Colombier }
13779a747e4fSDavid du Colombier
137818a458bcSDavid du Colombier /*
137918a458bcSDavid du Colombier * expand personal aliases since the names are meaningless in
138018a458bcSDavid du Colombier * other contexts
138118a458bcSDavid du Colombier */
13829a747e4fSDavid du Colombier Addr*
_expand(Addr * old,int * changedp)13839a747e4fSDavid du Colombier _expand(Addr *old, int *changedp)
13849a747e4fSDavid du Colombier {
1385d9306527SDavid du Colombier Addr *first, *next, **l, *a;
138618a458bcSDavid du Colombier Alias *al;
13879a747e4fSDavid du Colombier
13889a747e4fSDavid du Colombier *changedp = 0;
13899a747e4fSDavid du Colombier first = nil;
13909a747e4fSDavid du Colombier l = &first;
13919a747e4fSDavid du Colombier for(;old != nil; old = next){
13929a747e4fSDavid du Colombier next = old->next;
13939a747e4fSDavid du Colombier for(al = aliases; al != nil; al = al->next){
1394d9306527SDavid du Colombier if(strcmp(al->addr->v, old->v) == 0){
1395d9306527SDavid du Colombier for(a = al->addr->next; a != nil; a = a->next){
1396d9306527SDavid du Colombier *l = newaddr(a->v);
13979a747e4fSDavid du Colombier if(*l == nil)
13989a747e4fSDavid du Colombier sysfatal("%r");
13999a747e4fSDavid du Colombier l = &(*l)->next;
14009a747e4fSDavid du Colombier *changedp = 1;
14019a747e4fSDavid du Colombier }
14029a747e4fSDavid du Colombier break;
14039a747e4fSDavid du Colombier }
14049a747e4fSDavid du Colombier }
14059a747e4fSDavid du Colombier if(al != nil){
14069a747e4fSDavid du Colombier freeaddr(old);
14079a747e4fSDavid du Colombier continue;
14089a747e4fSDavid du Colombier }
14099a747e4fSDavid du Colombier *l = old;
14109a747e4fSDavid du Colombier old->next = nil;
14119a747e4fSDavid du Colombier l = &(*l)->next;
14129a747e4fSDavid du Colombier }
14139a747e4fSDavid du Colombier return first;
14149a747e4fSDavid du Colombier }
14159a747e4fSDavid du Colombier
14169a747e4fSDavid du Colombier Addr*
rexpand(Addr * old)14179a747e4fSDavid du Colombier rexpand(Addr *old)
14189a747e4fSDavid du Colombier {
14199a747e4fSDavid du Colombier int i, changed;
14209a747e4fSDavid du Colombier
14219a747e4fSDavid du Colombier changed = 0;
14229a747e4fSDavid du Colombier for(i = 0; i < 32; i++){
14239a747e4fSDavid du Colombier old = _expand(old, &changed);
14249a747e4fSDavid du Colombier if(changed == 0)
14259a747e4fSDavid du Colombier break;
14269a747e4fSDavid du Colombier }
14279a747e4fSDavid du Colombier return old;
14289a747e4fSDavid du Colombier }
14299a747e4fSDavid du Colombier
14309a747e4fSDavid du Colombier Addr*
unique(Addr * first)14319a747e4fSDavid du Colombier unique(Addr *first)
14329a747e4fSDavid du Colombier {
14339a747e4fSDavid du Colombier Addr *a, **l, *x;
14349a747e4fSDavid du Colombier
14359a747e4fSDavid du Colombier for(a = first; a != nil; a = a->next){
14369a747e4fSDavid du Colombier for(l = &a->next; *l != nil;){
14379a747e4fSDavid du Colombier if(strcmp(a->v, (*l)->v) == 0){
14389a747e4fSDavid du Colombier x = *l;
14399a747e4fSDavid du Colombier *l = x->next;
14409a747e4fSDavid du Colombier freeaddr(x);
14419a747e4fSDavid du Colombier } else
14429a747e4fSDavid du Colombier l = &(*l)->next;
14439a747e4fSDavid du Colombier }
14449a747e4fSDavid du Colombier }
14459a747e4fSDavid du Colombier return first;
14469a747e4fSDavid du Colombier }
14479a747e4fSDavid du Colombier
14489a747e4fSDavid du Colombier Addr*
expand(int ac,char ** av)14499a747e4fSDavid du Colombier expand(int ac, char **av)
14509a747e4fSDavid du Colombier {
14519a747e4fSDavid du Colombier int i;
145218a458bcSDavid du Colombier Addr *first, **l;
14539a747e4fSDavid du Colombier
14543ff48bf5SDavid du Colombier first = nil;
14553ff48bf5SDavid du Colombier
145618a458bcSDavid du Colombier /* make a list of the starting addresses */
14579a747e4fSDavid du Colombier l = &first;
14589a747e4fSDavid du Colombier for(i = 0; i < ac; i++){
14599a747e4fSDavid du Colombier *l = newaddr(av[i]);
14609a747e4fSDavid du Colombier if(*l == nil)
14619a747e4fSDavid du Colombier sysfatal("%r");
14629a747e4fSDavid du Colombier l = &(*l)->next;
14639a747e4fSDavid du Colombier }
14649a747e4fSDavid du Colombier
146518a458bcSDavid du Colombier /* recurse till we don't change any more */
14669a747e4fSDavid du Colombier return unique(rexpand(first));
14679a747e4fSDavid du Colombier }
14689a747e4fSDavid du Colombier
14699a747e4fSDavid du Colombier Addr*
concataddr(Addr * a,Addr * b)14709a747e4fSDavid du Colombier concataddr(Addr *a, Addr *b)
14719a747e4fSDavid du Colombier {
14729a747e4fSDavid du Colombier Addr *oa;
14739a747e4fSDavid du Colombier
14749a747e4fSDavid du Colombier if(a == nil)
14759a747e4fSDavid du Colombier return b;
14769a747e4fSDavid du Colombier
14779a747e4fSDavid du Colombier oa = a;
14789a747e4fSDavid du Colombier for(; a->next; a=a->next)
14799a747e4fSDavid du Colombier ;
14809a747e4fSDavid du Colombier a->next = b;
14819a747e4fSDavid du Colombier return oa;
14829a747e4fSDavid du Colombier }
14839a747e4fSDavid du Colombier
14849a747e4fSDavid du Colombier void
freeaddr(Addr * ap)14859a747e4fSDavid du Colombier freeaddr(Addr *ap)
14869a747e4fSDavid du Colombier {
14879a747e4fSDavid du Colombier free(ap->v);
14889a747e4fSDavid du Colombier free(ap);
14899a747e4fSDavid du Colombier }
14909a747e4fSDavid du Colombier
14919a747e4fSDavid du Colombier void
freeaddrs(Addr * ap)14929a747e4fSDavid du Colombier freeaddrs(Addr *ap)
14939a747e4fSDavid du Colombier {
14949a747e4fSDavid du Colombier Addr *next;
14959a747e4fSDavid du Colombier
14969a747e4fSDavid du Colombier for(; ap; ap=next) {
14979a747e4fSDavid du Colombier next = ap->next;
14989a747e4fSDavid du Colombier freeaddr(ap);
14999a747e4fSDavid du Colombier }
15009a747e4fSDavid du Colombier }
15019a747e4fSDavid du Colombier
15029a747e4fSDavid du Colombier String*
s_copyn(char * s,int n)15039a747e4fSDavid du Colombier s_copyn(char *s, int n)
15049a747e4fSDavid du Colombier {
15059a747e4fSDavid du Colombier return s_nappend(s_reset(nil), s, n);
15069a747e4fSDavid du Colombier }
15079a747e4fSDavid du Colombier
150818a458bcSDavid du Colombier /*
150918a458bcSDavid du Colombier * fetch the next token from an RFC822 address string
151018a458bcSDavid du Colombier * we assume the header is RFC822-conformant in that
151118a458bcSDavid du Colombier * we recognize escaping anywhere even though it is only
151218a458bcSDavid du Colombier * supposed to be in quoted-strings, domain-literals, and comments.
151318a458bcSDavid du Colombier *
151418a458bcSDavid du Colombier * i'd use yylex or yyparse here, but we need to preserve
151518a458bcSDavid du Colombier * things like comments, which i think it tosses away.
151618a458bcSDavid du Colombier *
151718a458bcSDavid du Colombier * we're not strictly RFC822 compliant. we misparse such nonsense as
151818a458bcSDavid du Colombier *
151918a458bcSDavid du Colombier * To: gre @ (Grace) plan9 . (Emlin) bell-labs.com
152018a458bcSDavid du Colombier *
152118a458bcSDavid du Colombier * make sure there's no whitespace in your addresses and
152218a458bcSDavid du Colombier * you'll be fine.
152318a458bcSDavid du Colombier */
15249a747e4fSDavid du Colombier enum {
15259a747e4fSDavid du Colombier Twhite,
15269a747e4fSDavid du Colombier Tcomment,
15279a747e4fSDavid du Colombier Twords,
15289a747e4fSDavid du Colombier Tcomma,
15299a747e4fSDavid du Colombier Tleftangle,
15309a747e4fSDavid du Colombier Trightangle,
15319a747e4fSDavid du Colombier Terror,
15329a747e4fSDavid du Colombier Tend,
15339a747e4fSDavid du Colombier };
153418a458bcSDavid du Colombier
15359a747e4fSDavid du Colombier // char *ty82[] = {"white", "comment", "words", "comma", "<", ">", "err", "end"};
153618a458bcSDavid du Colombier
15379a747e4fSDavid du Colombier #define ISWHITE(p) ((p)==' ' || (p)=='\t' || (p)=='\n' || (p)=='\r')
153818a458bcSDavid du Colombier
15399a747e4fSDavid du Colombier int
get822token(String ** tok,char * p,char ** pp)15409a747e4fSDavid du Colombier get822token(String **tok, char *p, char **pp)
15419a747e4fSDavid du Colombier {
154218a458bcSDavid du Colombier int type, quoting;
15439a747e4fSDavid du Colombier char *op;
15449a747e4fSDavid du Colombier
15459a747e4fSDavid du Colombier op = p;
15469a747e4fSDavid du Colombier switch(*p){
15479a747e4fSDavid du Colombier case '\0':
15489a747e4fSDavid du Colombier *tok = nil;
15499a747e4fSDavid du Colombier *pp = nil;
15509a747e4fSDavid du Colombier return Tend;
15519a747e4fSDavid du Colombier
155218a458bcSDavid du Colombier case ' ': /* get whitespace */
15539a747e4fSDavid du Colombier case '\t':
15549a747e4fSDavid du Colombier case '\n':
15559a747e4fSDavid du Colombier case '\r':
15569a747e4fSDavid du Colombier type = Twhite;
15579a747e4fSDavid du Colombier while(ISWHITE(*p))
15589a747e4fSDavid du Colombier p++;
15599a747e4fSDavid du Colombier break;
15609a747e4fSDavid du Colombier
156118a458bcSDavid du Colombier case '(': /* get comment */
15629a747e4fSDavid du Colombier type = Tcomment;
15639a747e4fSDavid du Colombier for(p++; *p && *p != ')'; p++)
15649a747e4fSDavid du Colombier if(*p == '\\') {
15659a747e4fSDavid du Colombier if(*(p+1) == '\0') {
15669a747e4fSDavid du Colombier *tok = nil;
15679a747e4fSDavid du Colombier return Terror;
15689a747e4fSDavid du Colombier }
15699a747e4fSDavid du Colombier p++;
15709a747e4fSDavid du Colombier }
15719a747e4fSDavid du Colombier
15729a747e4fSDavid du Colombier if(*p != ')') {
15739a747e4fSDavid du Colombier *tok = nil;
15749a747e4fSDavid du Colombier return Terror;
15759a747e4fSDavid du Colombier }
15769a747e4fSDavid du Colombier p++;
15779a747e4fSDavid du Colombier break;
15789a747e4fSDavid du Colombier case ',':
15799a747e4fSDavid du Colombier type = Tcomma;
15809a747e4fSDavid du Colombier p++;
15819a747e4fSDavid du Colombier break;
15829a747e4fSDavid du Colombier case '<':
15839a747e4fSDavid du Colombier type = Tleftangle;
15849a747e4fSDavid du Colombier p++;
15859a747e4fSDavid du Colombier break;
15869a747e4fSDavid du Colombier case '>':
15879a747e4fSDavid du Colombier type = Trightangle;
15889a747e4fSDavid du Colombier p++;
15899a747e4fSDavid du Colombier break;
159018a458bcSDavid du Colombier default: /* bunch of letters, perhaps quoted strings tossed in */
15919a747e4fSDavid du Colombier type = Twords;
15929a747e4fSDavid du Colombier quoting = 0;
159318a458bcSDavid du Colombier for (; *p && (quoting ||
159418a458bcSDavid du Colombier (!ISWHITE(*p) && *p != '>' && *p != '<' && *p != ',')); p++) {
15959a747e4fSDavid du Colombier if(*p == '"')
15969a747e4fSDavid du Colombier quoting = !quoting;
15979a747e4fSDavid du Colombier if(*p == '\\') {
15989a747e4fSDavid du Colombier if(*(p+1) == '\0') {
15999a747e4fSDavid du Colombier *tok = nil;
16009a747e4fSDavid du Colombier return Terror;
16019a747e4fSDavid du Colombier }
16029a747e4fSDavid du Colombier p++;
16039a747e4fSDavid du Colombier }
16049a747e4fSDavid du Colombier }
16059a747e4fSDavid du Colombier break;
16069a747e4fSDavid du Colombier }
16079a747e4fSDavid du Colombier
16089a747e4fSDavid du Colombier if(pp)
16099a747e4fSDavid du Colombier *pp = p;
16109a747e4fSDavid du Colombier *tok = s_copyn(op, p-op);
16119a747e4fSDavid du Colombier return type;
16129a747e4fSDavid du Colombier }
16139a747e4fSDavid du Colombier
161418a458bcSDavid du Colombier /*
161518a458bcSDavid du Colombier * expand local aliases in an RFC822 mail line
161618a458bcSDavid du Colombier * add list of expanded addresses to to.
161718a458bcSDavid du Colombier */
16189a747e4fSDavid du Colombier Addr*
expandline(String ** s,Addr * to)16199a747e4fSDavid du Colombier expandline(String **s, Addr *to)
16209a747e4fSDavid du Colombier {
16219a747e4fSDavid du Colombier int tok, inangle, hadangle, nword;
162218a458bcSDavid du Colombier char *p;
162318a458bcSDavid du Colombier Addr *na, *nto, *ap;
16249a747e4fSDavid du Colombier String *os, *ns, *stok, *lastword, *sinceword;
16259a747e4fSDavid du Colombier
16269a747e4fSDavid du Colombier os = s_copy(s_to_c(*s));
16279a747e4fSDavid du Colombier p = strchr(s_to_c(*s), ':');
16289a747e4fSDavid du Colombier assert(p != nil);
16299a747e4fSDavid du Colombier p++;
16309a747e4fSDavid du Colombier
16319a747e4fSDavid du Colombier ns = s_copyn(s_to_c(*s), p-s_to_c(*s));
16329a747e4fSDavid du Colombier stok = nil;
16339a747e4fSDavid du Colombier nto = nil;
163418a458bcSDavid du Colombier /*
163518a458bcSDavid du Colombier * the only valid mailbox namings are word
163618a458bcSDavid du Colombier * and word* < addr >
163718a458bcSDavid du Colombier * without comments this would be simple.
163818a458bcSDavid du Colombier * we keep the following:
163918a458bcSDavid du Colombier * lastword - current guess at the address
164018a458bcSDavid du Colombier * sinceword - whitespace and comment seen since lastword
164118a458bcSDavid du Colombier */
16429a747e4fSDavid du Colombier lastword = s_new();
16439a747e4fSDavid du Colombier sinceword = s_new();
16449a747e4fSDavid du Colombier inangle = 0;
16459a747e4fSDavid du Colombier nword = 0;
16469a747e4fSDavid du Colombier hadangle = 0;
16479a747e4fSDavid du Colombier for(;;) {
16489a747e4fSDavid du Colombier stok = nil;
16499a747e4fSDavid du Colombier switch(tok = get822token(&stok, p, &p)){
16509a747e4fSDavid du Colombier default:
16519a747e4fSDavid du Colombier abort();
16529a747e4fSDavid du Colombier case Tcomma:
16539a747e4fSDavid du Colombier case Tend:
16549a747e4fSDavid du Colombier if(inangle)
16559a747e4fSDavid du Colombier goto Error;
16569a747e4fSDavid du Colombier if(nword != 1)
16579a747e4fSDavid du Colombier goto Error;
16589a747e4fSDavid du Colombier na = rexpand(newaddr(s_to_c(lastword)));
16599a747e4fSDavid du Colombier s_append(ns, na->v);
16609a747e4fSDavid du Colombier s_append(ns, s_to_c(sinceword));
16619a747e4fSDavid du Colombier for(ap=na->next; ap; ap=ap->next) {
16629a747e4fSDavid du Colombier s_append(ns, ", ");
16639a747e4fSDavid du Colombier s_append(ns, ap->v);
16649a747e4fSDavid du Colombier }
16659a747e4fSDavid du Colombier nto = concataddr(na, nto);
16669a747e4fSDavid du Colombier if(tok == Tcomma){
16679a747e4fSDavid du Colombier s_append(ns, ",");
16689a747e4fSDavid du Colombier s_free(stok);
16699a747e4fSDavid du Colombier }
16709a747e4fSDavid du Colombier if(tok == Tend)
16719a747e4fSDavid du Colombier goto Break2;
16729a747e4fSDavid du Colombier inangle = 0;
16739a747e4fSDavid du Colombier nword = 0;
16749a747e4fSDavid du Colombier hadangle = 0;
16759a747e4fSDavid du Colombier s_reset(sinceword);
16769a747e4fSDavid du Colombier s_reset(lastword);
16779a747e4fSDavid du Colombier break;
16789a747e4fSDavid du Colombier case Twhite:
16799a747e4fSDavid du Colombier case Tcomment:
16809a747e4fSDavid du Colombier s_append(sinceword, s_to_c(stok));
16819a747e4fSDavid du Colombier s_free(stok);
16829a747e4fSDavid du Colombier break;
16839a747e4fSDavid du Colombier case Trightangle:
16849a747e4fSDavid du Colombier if(!inangle)
16859a747e4fSDavid du Colombier goto Error;
16869a747e4fSDavid du Colombier inangle = 0;
16879a747e4fSDavid du Colombier hadangle = 1;
16889a747e4fSDavid du Colombier s_append(sinceword, s_to_c(stok));
16899a747e4fSDavid du Colombier s_free(stok);
16909a747e4fSDavid du Colombier break;
16919a747e4fSDavid du Colombier case Twords:
16929a747e4fSDavid du Colombier case Tleftangle:
16939a747e4fSDavid du Colombier if(hadangle)
16949a747e4fSDavid du Colombier goto Error;
16959a747e4fSDavid du Colombier if(tok != Tleftangle && inangle && s_len(lastword))
16969a747e4fSDavid du Colombier goto Error;
16979a747e4fSDavid du Colombier if(tok == Tleftangle) {
16989a747e4fSDavid du Colombier inangle = 1;
16999a747e4fSDavid du Colombier nword = 1;
17009a747e4fSDavid du Colombier }
17019a747e4fSDavid du Colombier s_append(ns, s_to_c(lastword));
17029a747e4fSDavid du Colombier s_append(ns, s_to_c(sinceword));
17039a747e4fSDavid du Colombier s_reset(sinceword);
17049a747e4fSDavid du Colombier if(tok == Tleftangle) {
17059a747e4fSDavid du Colombier s_append(ns, "<");
17069a747e4fSDavid du Colombier s_reset(lastword);
17079a747e4fSDavid du Colombier } else {
17089a747e4fSDavid du Colombier s_free(lastword);
17099a747e4fSDavid du Colombier lastword = stok;
17109a747e4fSDavid du Colombier }
17119a747e4fSDavid du Colombier if(!inangle)
17129a747e4fSDavid du Colombier nword++;
17139a747e4fSDavid du Colombier break;
171418a458bcSDavid du Colombier case Terror: /* give up, use old string, addrs */
17159a747e4fSDavid du Colombier Error:
17169a747e4fSDavid du Colombier ns = os;
17179a747e4fSDavid du Colombier os = nil;
17189a747e4fSDavid du Colombier freeaddrs(nto);
17199a747e4fSDavid du Colombier nto = nil;
17209a747e4fSDavid du Colombier werrstr("rfc822 syntax error");
17219a747e4fSDavid du Colombier rfc822syntaxerror = 1;
17229a747e4fSDavid du Colombier goto Break2;
17239a747e4fSDavid du Colombier }
17249a747e4fSDavid du Colombier }
17259a747e4fSDavid du Colombier Break2:
17269a747e4fSDavid du Colombier s_free(*s);
17279a747e4fSDavid du Colombier s_free(os);
17289a747e4fSDavid du Colombier *s = ns;
17299a747e4fSDavid du Colombier nto = concataddr(nto, to);
17309a747e4fSDavid du Colombier return nto;
17319a747e4fSDavid du Colombier }
17329a747e4fSDavid du Colombier
17339a747e4fSDavid du Colombier void
Bdrain(Biobuf * b)17349a747e4fSDavid du Colombier Bdrain(Biobuf *b)
17359a747e4fSDavid du Colombier {
17369a747e4fSDavid du Colombier char buf[8192];
17379a747e4fSDavid du Colombier
17389a747e4fSDavid du Colombier while(Bread(b, buf, sizeof buf) > 0)
17399a747e4fSDavid du Colombier ;
17409a747e4fSDavid du Colombier }
1741d9306527SDavid du Colombier
1742d9306527SDavid du Colombier void
readmimetypes(void)1743d9306527SDavid du Colombier readmimetypes(void)
1744d9306527SDavid du Colombier {
1745d9306527SDavid du Colombier char *p;
1746d9306527SDavid du Colombier char type[256];
174718a458bcSDavid du Colombier char *f[6];
174818a458bcSDavid du Colombier Biobuf *b;
1749d9306527SDavid du Colombier static int alloced, inuse;
1750d9306527SDavid du Colombier
1751d9306527SDavid du Colombier if(mimetypes == 0){
1752d9306527SDavid du Colombier alloced = 256;
1753d9306527SDavid du Colombier mimetypes = emalloc(alloced*sizeof(Ctype));
1754d9306527SDavid du Colombier mimetypes[0].ext = "";
1755d9306527SDavid du Colombier }
1756d9306527SDavid du Colombier
1757d9306527SDavid du Colombier b = Bopen("/sys/lib/mimetype", OREAD);
1758d9306527SDavid du Colombier if(b == nil)
1759d9306527SDavid du Colombier return;
1760d9306527SDavid du Colombier for(;;){
1761d9306527SDavid du Colombier p = Brdline(b, '\n');
1762d9306527SDavid du Colombier if(p == nil)
1763d9306527SDavid du Colombier break;
1764d9306527SDavid du Colombier p[Blinelen(b)-1] = 0;
1765d9306527SDavid du Colombier if(tokenize(p, f, 6) < 4)
1766d9306527SDavid du Colombier continue;
176718a458bcSDavid du Colombier if (strcmp(f[0], "-") == 0 || strcmp(f[1], "-") == 0 ||
176818a458bcSDavid du Colombier strcmp(f[2], "-") == 0)
1769d9306527SDavid du Colombier continue;
1770d9306527SDavid du Colombier if(inuse + 1 >= alloced){
1771d9306527SDavid du Colombier alloced += 256;
1772d9306527SDavid du Colombier mimetypes = erealloc(mimetypes, alloced*sizeof(Ctype));
1773d9306527SDavid du Colombier }
1774d9306527SDavid du Colombier snprint(type, sizeof(type), "%s/%s", f[1], f[2]);
1775d9306527SDavid du Colombier mimetypes[inuse].type = estrdup(type);
1776d9306527SDavid du Colombier mimetypes[inuse].ext = estrdup(f[0]+1);
1777d9306527SDavid du Colombier mimetypes[inuse].display = !strcmp(type, "text/plain");
1778d9306527SDavid du Colombier inuse++;
1779d9306527SDavid du Colombier
178018a458bcSDavid du Colombier /* always make sure there's a terminator */
1781d9306527SDavid du Colombier mimetypes[inuse].ext = 0;
1782d9306527SDavid du Colombier }
1783d9306527SDavid du Colombier Bterm(b);
1784d9306527SDavid du Colombier }
1785d9306527SDavid du Colombier
1786d9306527SDavid du Colombier char*
estrdup(char * x)1787d9306527SDavid du Colombier estrdup(char *x)
1788d9306527SDavid du Colombier {
1789d9306527SDavid du Colombier x = strdup(x);
1790d9306527SDavid du Colombier if(x == nil)
1791d9306527SDavid du Colombier fatal("memory");
1792d9306527SDavid du Colombier return x;
1793d9306527SDavid du Colombier }
1794d9306527SDavid du Colombier
1795d9306527SDavid du Colombier void*
emalloc(int n)1796d9306527SDavid du Colombier emalloc(int n)
1797d9306527SDavid du Colombier {
1798d9306527SDavid du Colombier void *x;
1799d9306527SDavid du Colombier
1800d9306527SDavid du Colombier x = malloc(n);
1801d9306527SDavid du Colombier if(x == nil)
1802d9306527SDavid du Colombier fatal("%r");
1803d9306527SDavid du Colombier return x;
1804d9306527SDavid du Colombier }
1805d9306527SDavid du Colombier
1806d9306527SDavid du Colombier void*
erealloc(void * x,int n)1807d9306527SDavid du Colombier erealloc(void *x, int n)
1808d9306527SDavid du Colombier {
1809d9306527SDavid du Colombier x = realloc(x, n);
1810d9306527SDavid du Colombier if(x == nil)
1811d9306527SDavid du Colombier fatal("%r");
1812d9306527SDavid du Colombier return x;
1813d9306527SDavid du Colombier }
1814d9306527SDavid du Colombier
181518a458bcSDavid du Colombier /*
181618a458bcSDavid du Colombier * Formatter for %"
181718a458bcSDavid du Colombier * Use double quotes to protect white space, frogs, \ and "
181818a458bcSDavid du Colombier */
1819d9306527SDavid du Colombier enum
1820d9306527SDavid du Colombier {
1821d9306527SDavid du Colombier Qok = 0,
1822d9306527SDavid du Colombier Qquote,
1823d9306527SDavid du Colombier Qbackslash,
1824d9306527SDavid du Colombier };
1825d9306527SDavid du Colombier
1826d9306527SDavid du Colombier static int
needtoquote(Rune r)1827d9306527SDavid du Colombier needtoquote(Rune r)
1828d9306527SDavid du Colombier {
1829d9306527SDavid du Colombier if(r >= Runeself)
1830d9306527SDavid du Colombier return Qquote;
1831d9306527SDavid du Colombier if(r <= ' ')
1832d9306527SDavid du Colombier return Qquote;
1833d9306527SDavid du Colombier if(r=='\\' || r=='"')
1834d9306527SDavid du Colombier return Qbackslash;
1835d9306527SDavid du Colombier return Qok;
1836d9306527SDavid du Colombier }
1837d9306527SDavid du Colombier
1838d9306527SDavid du Colombier int
doublequote(Fmt * f)1839d9306527SDavid du Colombier doublequote(Fmt *f)
1840d9306527SDavid du Colombier {
1841d9306527SDavid du Colombier int w, quotes;
184218a458bcSDavid du Colombier char *s, *t;
1843d9306527SDavid du Colombier Rune r;
1844d9306527SDavid du Colombier
1845d9306527SDavid du Colombier s = va_arg(f->args, char*);
1846d9306527SDavid du Colombier if(s == nil || *s == '\0')
1847d9306527SDavid du Colombier return fmtstrcpy(f, "\"\"");
1848d9306527SDavid du Colombier
1849d9306527SDavid du Colombier quotes = 0;
1850d9306527SDavid du Colombier for(t = s; *t; t += w){
1851d9306527SDavid du Colombier w = chartorune(&r, t);
1852d9306527SDavid du Colombier quotes |= needtoquote(r);
1853d9306527SDavid du Colombier }
1854d9306527SDavid du Colombier if(quotes == 0)
1855d9306527SDavid du Colombier return fmtstrcpy(f, s);
1856d9306527SDavid du Colombier
1857d9306527SDavid du Colombier fmtrune(f, '"');
1858d9306527SDavid du Colombier for(t = s; *t; t += w){
1859d9306527SDavid du Colombier w = chartorune(&r, t);
1860d9306527SDavid du Colombier if(needtoquote(r) == Qbackslash)
1861d9306527SDavid du Colombier fmtrune(f, '\\');
1862d9306527SDavid du Colombier fmtrune(f, r);
1863d9306527SDavid du Colombier }
1864d9306527SDavid du Colombier return fmtrune(f, '"');
1865d9306527SDavid du Colombier }
186617dd33a2SDavid du Colombier
186717dd33a2SDavid du Colombier int
rfc2047fmt(Fmt * fmt)186817dd33a2SDavid du Colombier rfc2047fmt(Fmt *fmt)
186917dd33a2SDavid du Colombier {
187017dd33a2SDavid du Colombier char *s, *p;
187117dd33a2SDavid du Colombier
187217dd33a2SDavid du Colombier s = va_arg(fmt->args, char*);
187317dd33a2SDavid du Colombier if(s == nil)
187417dd33a2SDavid du Colombier return fmtstrcpy(fmt, "");
187517dd33a2SDavid du Colombier for(p=s; *p; p++)
187617dd33a2SDavid du Colombier if((uchar)*p >= 0x80)
187717dd33a2SDavid du Colombier goto hard;
187817dd33a2SDavid du Colombier return fmtstrcpy(fmt, s);
187917dd33a2SDavid du Colombier
188017dd33a2SDavid du Colombier hard:
188117dd33a2SDavid du Colombier fmtprint(fmt, "=?utf-8?q?");
188217dd33a2SDavid du Colombier for(p = s; *p; p++){
188317dd33a2SDavid du Colombier if(*p == ' ')
188417dd33a2SDavid du Colombier fmtrune(fmt, '_');
188518a458bcSDavid du Colombier else if(*p == '_' || *p == '\t' || *p == '=' || *p == '?' ||
188618a458bcSDavid du Colombier (uchar)*p >= 0x80)
188717dd33a2SDavid du Colombier fmtprint(fmt, "=%.2uX", (uchar)*p);
188817dd33a2SDavid du Colombier else
188917dd33a2SDavid du Colombier fmtrune(fmt, (uchar)*p);
189017dd33a2SDavid du Colombier }
189117dd33a2SDavid du Colombier fmtprint(fmt, "?=");
189217dd33a2SDavid du Colombier return 0;
189317dd33a2SDavid du Colombier }
189417dd33a2SDavid du Colombier
189517dd33a2SDavid du Colombier char*
mksubject(char * line)189617dd33a2SDavid du Colombier mksubject(char *line)
189717dd33a2SDavid du Colombier {
189817dd33a2SDavid du Colombier char *p, *q;
189917dd33a2SDavid du Colombier static char buf[1024];
190017dd33a2SDavid du Colombier
190117dd33a2SDavid du Colombier p = strchr(line, ':') + 1;
190217dd33a2SDavid du Colombier while(*p == ' ')
190317dd33a2SDavid du Colombier p++;
190417dd33a2SDavid du Colombier for(q = p; *q; q++)
190517dd33a2SDavid du Colombier if((uchar)*q >= 0x80)
190617dd33a2SDavid du Colombier goto hard;
190717dd33a2SDavid du Colombier return line;
190817dd33a2SDavid du Colombier
190917dd33a2SDavid du Colombier hard:
191017dd33a2SDavid du Colombier snprint(buf, sizeof buf, "Subject: %U", p);
191117dd33a2SDavid du Colombier return buf;
191217dd33a2SDavid du Colombier }
1913