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