xref: /plan9/sys/src/cmd/upas/marshal/marshal.c (revision 41dd6b4775bcffc7275c15aee7294944759a2ea7)
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