xref: /plan9/sys/src/cmd/upas/smtp/smtp.c (revision ab3dc52f122c98aa4bc2bd64bd2292bb7b80fba2)
13e12c5d1SDavid du Colombier #include "common.h"
23e12c5d1SDavid du Colombier #include "smtp.h"
33e12c5d1SDavid du Colombier #include <ctype.h>
4d9306527SDavid du Colombier #include <mp.h>
5d9306527SDavid du Colombier #include <libsec.h>
6d9306527SDavid du Colombier #include <auth.h>
73e12c5d1SDavid du Colombier 
87dd7cddfSDavid du Colombier static	char*	connect(char*);
9d9306527SDavid du Colombier static	char*	dotls(char*);
103cf081f0SDavid du Colombier static	char*	doauth(char*);
11d9306527SDavid du Colombier char*	hello(char*, int);
123e12c5d1SDavid du Colombier char*	mailfrom(char*);
133e12c5d1SDavid du Colombier char*	rcptto(char*);
147dd7cddfSDavid du Colombier char*	data(String*, Biobuf*);
157dd7cddfSDavid du Colombier void	quit(char*);
163e12c5d1SDavid du Colombier int	getreply(void);
17219b2ee8SDavid du Colombier void	addhostdom(String*, char*);
18219b2ee8SDavid du Colombier String*	bangtoat(char*);
197dd7cddfSDavid du Colombier String*	convertheader(String*);
207dd7cddfSDavid du Colombier int	printheader(void);
213e12c5d1SDavid du Colombier char*	domainify(char*, char*);
223e12c5d1SDavid du Colombier void	putcrnl(char*, int);
233e12c5d1SDavid du Colombier char*	getcrnl(String*);
247dd7cddfSDavid du Colombier int	printdate(Node*);
25bd389b36SDavid du Colombier char	*rewritezone(char *);
263e12c5d1SDavid du Colombier int	dBprint(char*, ...);
277dd7cddfSDavid du Colombier int	dBputc(int);
287dd7cddfSDavid du Colombier String*	fixrouteaddr(String*, Node*, Node*);
29a8453668SDavid du Colombier int	ping;
30a8453668SDavid du Colombier int	insecure;
313e12c5d1SDavid du Colombier 
327dd7cddfSDavid du Colombier #define Retry	"Retry, Temporary Failure"
333e12c5d1SDavid du Colombier #define Giveup	"Permanent Failure"
343e12c5d1SDavid du Colombier 
353e12c5d1SDavid du Colombier int	debug;		/* true if we're debugging */
363e12c5d1SDavid du Colombier String	*reply;		/* last reply */
37219b2ee8SDavid du Colombier String	*toline;
38a8453668SDavid du Colombier int	alarmscale;
393e12c5d1SDavid du Colombier int	last = 'n';	/* last character sent by putcrnl() */
403e12c5d1SDavid du Colombier int	filter;
41d9306527SDavid du Colombier int	trysecure;	/* Try to use TLS if the other side supports it */
42d9306527SDavid du Colombier int	tryauth;	/* Try to authenticate, if supported */
437dd7cddfSDavid du Colombier int	quitting;	/* when error occurs in quit */
447dd7cddfSDavid du Colombier char	*quitrv;	/* deferred return value when in quit */
453e12c5d1SDavid du Colombier char	ddomain[1024];	/* domain name of destination machine */
463e12c5d1SDavid du Colombier char	*gdomain;	/* domain name of gateway */
47bd389b36SDavid du Colombier char	*uneaten;	/* first character after rfc822 headers */
487dd7cddfSDavid du Colombier char	*farend;	/* system we are trying to send to */
49d9306527SDavid du Colombier char	*user;		/* user we are authenticating as, if authenticating */
50219b2ee8SDavid du Colombier char	hostdomain[256];
513e12c5d1SDavid du Colombier Biobuf	bin;
523e12c5d1SDavid du Colombier Biobuf	bout;
533e12c5d1SDavid du Colombier Biobuf	berr;
547dd7cddfSDavid du Colombier Biobuf	bfile;
553e12c5d1SDavid du Colombier 
563e12c5d1SDavid du Colombier void
573e12c5d1SDavid du Colombier usage(void)
583e12c5d1SDavid du Colombier {
59a8453668SDavid du Colombier 	fprint(2, "usage: smtp [-adips] [-uuser] [-hhost] [.domain] net!host[!service] sender rcpt-list\n");
60219b2ee8SDavid du Colombier 	exits(Giveup);
61219b2ee8SDavid du Colombier }
62219b2ee8SDavid du Colombier 
637dd7cddfSDavid du Colombier int
64219b2ee8SDavid du Colombier timeout(void *x, char *msg)
65219b2ee8SDavid du Colombier {
66219b2ee8SDavid du Colombier 	USED(x);
677dd7cddfSDavid du Colombier 	syslog(0, "smtp.fail", "interrupt: %s: %s", farend,  msg);
68219b2ee8SDavid du Colombier 	if(strstr(msg, "alarm")){
697dd7cddfSDavid du Colombier 		fprint(2, "smtp timeout: connection to %s timed out\n", farend);
707dd7cddfSDavid du Colombier 		if(quitting)
717dd7cddfSDavid du Colombier 			exits(quitrv);
727dd7cddfSDavid du Colombier 		exits(Retry);
73219b2ee8SDavid du Colombier 	}
747dd7cddfSDavid du Colombier 	if(strstr(msg, "closed pipe")){
757dd7cddfSDavid du Colombier 			/* call _exits() to prevent Bio from trying to flush closed pipe */
767dd7cddfSDavid du Colombier 		fprint(2, "smtp timeout: connection closed to %s\n", farend);
777dd7cddfSDavid du Colombier 		if(quitting){
787dd7cddfSDavid du Colombier 			syslog(0, "smtp.fail", "closed pipe to %s", farend);
797dd7cddfSDavid du Colombier 			_exits(quitrv);
807dd7cddfSDavid du Colombier 		}
817dd7cddfSDavid du Colombier 		_exits(Retry);
827dd7cddfSDavid du Colombier 	}
837dd7cddfSDavid du Colombier 	return 0;
843e12c5d1SDavid du Colombier }
853e12c5d1SDavid du Colombier 
863e12c5d1SDavid du Colombier void
87a8453668SDavid du Colombier removenewline(char *p)
88a8453668SDavid du Colombier {
89a8453668SDavid du Colombier 	int n = strlen(p)-1;
90a8453668SDavid du Colombier 
91a8453668SDavid du Colombier 	if(n < 0)
92a8453668SDavid du Colombier 		return;
93a8453668SDavid du Colombier 	if(p[n] == '\n')
94a8453668SDavid du Colombier 		p[n] = 0;
95a8453668SDavid du Colombier }
96a8453668SDavid du Colombier 
97a8453668SDavid du Colombier void
983e12c5d1SDavid du Colombier main(int argc, char **argv)
993e12c5d1SDavid du Colombier {
1007dd7cddfSDavid du Colombier 	char hellodomain[256];
1017dd7cddfSDavid du Colombier 	char *host, *domain;
1023e12c5d1SDavid du Colombier 	String *from;
1033e12c5d1SDavid du Colombier 	String *fromm;
1047dd7cddfSDavid du Colombier 	String *sender;
1053e12c5d1SDavid du Colombier 	char *addr;
1067dd7cddfSDavid du Colombier 	char *rv, *trv;
1077dd7cddfSDavid du Colombier 	int i, ok, rcvrs;
1087dd7cddfSDavid du Colombier 	char **errs;
1093e12c5d1SDavid du Colombier 
110a8453668SDavid du Colombier 	alarmscale = 60*1000;	/* minutes */
111fe096d1aSDavid du Colombier 	quotefmtinstall();
1127dd7cddfSDavid du Colombier 	errs = malloc(argc*sizeof(char*));
1133e12c5d1SDavid du Colombier 	reply = s_new();
114219b2ee8SDavid du Colombier 	host = 0;
1153e12c5d1SDavid du Colombier 	ARGBEGIN{
116d9306527SDavid du Colombier 	case 'a':
117d9306527SDavid du Colombier 		tryauth = 1;
118d9306527SDavid du Colombier 		trysecure = 1;
119d9306527SDavid du Colombier 		break;
1203e12c5d1SDavid du Colombier 	case 'f':
1213e12c5d1SDavid du Colombier 		filter = 1;
1223e12c5d1SDavid du Colombier 		break;
1233e12c5d1SDavid du Colombier 	case 'd':
1243e12c5d1SDavid du Colombier 		debug = 1;
1253e12c5d1SDavid du Colombier 		break;
1263e12c5d1SDavid du Colombier 	case 'g':
1273e12c5d1SDavid du Colombier 		gdomain = ARGF();
1283e12c5d1SDavid du Colombier 		break;
1293e12c5d1SDavid du Colombier 	case 'h':
1303e12c5d1SDavid du Colombier 		host = ARGF();
1313e12c5d1SDavid du Colombier 		break;
132a8453668SDavid du Colombier 	case 'i':
133a8453668SDavid du Colombier 		insecure = 1;
134a8453668SDavid du Colombier 		break;
135a8453668SDavid du Colombier 	case 'p':
136a8453668SDavid du Colombier 		alarmscale = 10*1000;	/* tens of seconds */
137a8453668SDavid du Colombier 		ping = 1;
138a8453668SDavid du Colombier 		break;
139d9306527SDavid du Colombier 	case 's':
140d9306527SDavid du Colombier 		trysecure = 1;
141d9306527SDavid du Colombier 		break;
142d9306527SDavid du Colombier 	case 'u':
143d9306527SDavid du Colombier 		user = ARGF();
144d9306527SDavid du Colombier 		break;
1453e12c5d1SDavid du Colombier 	default:
1463e12c5d1SDavid du Colombier 		usage();
1473e12c5d1SDavid du Colombier 		break;
1483e12c5d1SDavid du Colombier 	}ARGEND;
1493e12c5d1SDavid du Colombier 
1503e12c5d1SDavid du Colombier 	Binit(&berr, 2, OWRITE);
1517dd7cddfSDavid du Colombier 	Binit(&bfile, 0, OREAD);
1523e12c5d1SDavid du Colombier 
1533e12c5d1SDavid du Colombier 	/*
1543e12c5d1SDavid du Colombier 	 *  get domain and add to host name
1553e12c5d1SDavid du Colombier 	 */
1567dd7cddfSDavid du Colombier 	if(*argv && **argv=='.') {
1577dd7cddfSDavid du Colombier 		domain = *argv;
1587dd7cddfSDavid du Colombier 		argv++; argc--;
1597dd7cddfSDavid du Colombier 	} else
1607dd7cddfSDavid du Colombier 		domain = domainname_read();
1613e12c5d1SDavid du Colombier 	if(host == 0)
1623e12c5d1SDavid du Colombier 		host = sysname_read();
1633e12c5d1SDavid du Colombier 	strcpy(hostdomain, domainify(host, domain));
1647dd7cddfSDavid du Colombier 	strcpy(hellodomain, domainify(sysname_read(), domain));
1653e12c5d1SDavid du Colombier 
1663e12c5d1SDavid du Colombier 	/*
1673e12c5d1SDavid du Colombier 	 *  get destination address
1683e12c5d1SDavid du Colombier 	 */
1693e12c5d1SDavid du Colombier 	if(*argv == 0)
1703e12c5d1SDavid du Colombier 		usage();
1717dd7cddfSDavid du Colombier 	addr = *argv++; argc--;
1727dd7cddfSDavid du Colombier 	farend = addr;
1733e12c5d1SDavid du Colombier 
1743e12c5d1SDavid du Colombier 	/*
1753e12c5d1SDavid du Colombier 	 *  get sender's machine.
1763e12c5d1SDavid du Colombier 	 *  get sender in internet style.  domainify if necessary.
1773e12c5d1SDavid du Colombier 	 */
178219b2ee8SDavid du Colombier 	if(*argv == 0)
179219b2ee8SDavid du Colombier 		usage();
1807dd7cddfSDavid du Colombier 	sender = unescapespecial(s_copy(*argv++));
1817dd7cddfSDavid du Colombier 	argc--;
1827dd7cddfSDavid du Colombier 	fromm = s_clone(sender);
1833e12c5d1SDavid du Colombier 	rv = strrchr(s_to_c(fromm), '!');
1843e12c5d1SDavid du Colombier 	if(rv)
1853e12c5d1SDavid du Colombier 		*rv = 0;
1863e12c5d1SDavid du Colombier 	else
1873e12c5d1SDavid du Colombier 		*s_to_c(fromm) = 0;
1887dd7cddfSDavid du Colombier 	from = bangtoat(s_to_c(sender));
1893e12c5d1SDavid du Colombier 
1903e12c5d1SDavid du Colombier 	/*
1913e12c5d1SDavid du Colombier 	 *  send the mail
1923e12c5d1SDavid du Colombier 	 */
1933e12c5d1SDavid du Colombier 	if(filter){
1943e12c5d1SDavid du Colombier 		Binit(&bout, 1, OWRITE);
1957dd7cddfSDavid du Colombier 		rv = data(from, &bfile);
1963e12c5d1SDavid du Colombier 		if(rv != 0)
1973e12c5d1SDavid du Colombier 			goto error;
1987dd7cddfSDavid du Colombier 		exits(0);
1997dd7cddfSDavid du Colombier 	}
2007dd7cddfSDavid du Colombier 
201*ab3dc52fSDavid du Colombier 	/* mxdial uses its own timeout handler */
2027dd7cddfSDavid du Colombier 	if((rv = connect(addr)) != 0)
2037dd7cddfSDavid du Colombier 		exits(rv);
204*ab3dc52fSDavid du Colombier 
205*ab3dc52fSDavid du Colombier 	/* 10 minutes to get through the initial handshake */
206*ab3dc52fSDavid du Colombier 	atnotify(timeout, 1);
207a8453668SDavid du Colombier 	alarm(10*alarmscale);
208d9306527SDavid du Colombier 	if((rv = hello(hellodomain, 0)) != 0)
2097dd7cddfSDavid du Colombier 		goto error;
210a8453668SDavid du Colombier 	alarm(10*alarmscale);
2117dd7cddfSDavid du Colombier 	if((rv = mailfrom(s_to_c(from))) != 0)
2127dd7cddfSDavid du Colombier 		goto error;
2137dd7cddfSDavid du Colombier 
2147dd7cddfSDavid du Colombier 	ok = 0;
2157dd7cddfSDavid du Colombier 	rcvrs = 0;
2167dd7cddfSDavid du Colombier 	/* if any rcvrs are ok, we try to send the message */
2177dd7cddfSDavid du Colombier 	for(i = 0; i < argc; i++){
2187dd7cddfSDavid du Colombier 		if((trv = rcptto(argv[i])) != 0){
2197dd7cddfSDavid du Colombier 			/* remember worst error */
2207dd7cddfSDavid du Colombier 			if(rv != Giveup)
2217dd7cddfSDavid du Colombier 				rv = trv;
2227dd7cddfSDavid du Colombier 			errs[rcvrs] = strdup(s_to_c(reply));
223a8453668SDavid du Colombier 			removenewline(errs[rcvrs]);
2247dd7cddfSDavid du Colombier 		} else {
2257dd7cddfSDavid du Colombier 			ok++;
2267dd7cddfSDavid du Colombier 			errs[rcvrs] = 0;
2277dd7cddfSDavid du Colombier 		}
2287dd7cddfSDavid du Colombier 		rcvrs++;
2297dd7cddfSDavid du Colombier 	}
2307dd7cddfSDavid du Colombier 
2317dd7cddfSDavid du Colombier 	/* if no ok rcvrs or worst error is retry, give up */
2327dd7cddfSDavid du Colombier 	if(ok == 0 || rv == Retry)
2337dd7cddfSDavid du Colombier 		goto error;
2347dd7cddfSDavid du Colombier 
235a8453668SDavid du Colombier 	if(ping){
236a8453668SDavid du Colombier 		quit(0);
237a8453668SDavid du Colombier 		exits(0);
238a8453668SDavid du Colombier 	}
239a8453668SDavid du Colombier 
2407dd7cddfSDavid du Colombier 	rv = data(from, &bfile);
2417dd7cddfSDavid du Colombier 	if(rv != 0)
2427dd7cddfSDavid du Colombier 		goto error;
2437dd7cddfSDavid du Colombier 	quit(0);
2447dd7cddfSDavid du Colombier 	if(rcvrs == ok)
2457dd7cddfSDavid du Colombier 		exits(0);
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier 	/*
2487dd7cddfSDavid du Colombier 	 *  here when some but not all rcvrs failed
2497dd7cddfSDavid du Colombier 	 */
2507dd7cddfSDavid du Colombier 	fprint(2, "%s connect to %s:\n", thedate(), addr);
2517dd7cddfSDavid du Colombier 	for(i = 0; i < rcvrs; i++){
2527dd7cddfSDavid du Colombier 		if(errs[i]){
253a8453668SDavid du Colombier 			syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]);
2547dd7cddfSDavid du Colombier 			fprint(2, "  mail to %s failed: %s", argv[i], errs[i]);
2557dd7cddfSDavid du Colombier 		}
2567dd7cddfSDavid du Colombier 	}
2577dd7cddfSDavid du Colombier 	exits(Giveup);
2587dd7cddfSDavid du Colombier 
2597dd7cddfSDavid du Colombier 	/*
2607dd7cddfSDavid du Colombier 	 *  here when all rcvrs failed
2617dd7cddfSDavid du Colombier 	 */
2623e12c5d1SDavid du Colombier error:
263a8453668SDavid du Colombier 	removenewline(s_to_c(reply));
264a8453668SDavid du Colombier 	syslog(0, "smtp.fail", "%s to %s failed: %s",
265a8453668SDavid du Colombier 		ping ? "ping" : "delivery",
266a8453668SDavid du Colombier 		addr, s_to_c(reply));
2677dd7cddfSDavid du Colombier 	fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));
2687dd7cddfSDavid du Colombier 	if(!filter)
2697dd7cddfSDavid du Colombier 		quit(rv);
2703e12c5d1SDavid du Colombier 	exits(rv);
2713e12c5d1SDavid du Colombier }
2723e12c5d1SDavid du Colombier 
2733e12c5d1SDavid du Colombier /*
2743e12c5d1SDavid du Colombier  *  connect to the remote host
2753e12c5d1SDavid du Colombier  */
2767dd7cddfSDavid du Colombier static char *
2773e12c5d1SDavid du Colombier connect(char* net)
2783e12c5d1SDavid du Colombier {
2793e12c5d1SDavid du Colombier 	char buf[256];
2803e12c5d1SDavid du Colombier 	int fd;
2813e12c5d1SDavid du Colombier 
2827dd7cddfSDavid du Colombier 	fd = mxdial(net, ddomain, gdomain);
2833e12c5d1SDavid du Colombier 
2843e12c5d1SDavid du Colombier 	if(fd < 0){
2859a747e4fSDavid du Colombier 		rerrstr(buf, sizeof(buf));
2863ff48bf5SDavid du Colombier 		Bprint(&berr, "smtp: %s (%s)\n", buf, net);
2873ff48bf5SDavid du Colombier 		syslog(0, "smtp.fail", "%s (%s)", buf, net);
2887dd7cddfSDavid du Colombier 		if(strstr(buf, "illegal")
2897dd7cddfSDavid du Colombier 		|| strstr(buf, "unknown")
290271b8d73SDavid du Colombier 		|| strstr(buf, "can't translate"))
2913e12c5d1SDavid du Colombier 			return Giveup;
2923e12c5d1SDavid du Colombier 		else
2933e12c5d1SDavid du Colombier 			return Retry;
2943e12c5d1SDavid du Colombier 	}
2953e12c5d1SDavid du Colombier 	Binit(&bin, fd, OREAD);
2963e12c5d1SDavid du Colombier 	fd = dup(fd, -1);
2973e12c5d1SDavid du Colombier 	Binit(&bout, fd, OWRITE);
2983e12c5d1SDavid du Colombier 	return 0;
2993e12c5d1SDavid du Colombier }
3003e12c5d1SDavid du Colombier 
301fe096d1aSDavid du Colombier static char smtpthumbs[] =	"/sys/lib/tls/smtp";
302fe096d1aSDavid du Colombier static char smtpexclthumbs[] =	"/sys/lib/tls/smtp.exclude";
303fe096d1aSDavid du Colombier 
3043e12c5d1SDavid du Colombier /*
305fe096d1aSDavid du Colombier  *  exchange names with remote host, attempt to
306fe096d1aSDavid du Colombier  *  enable encryption and optionally authenticate.
307fe096d1aSDavid du Colombier  *  not fatal if we can't.
3083e12c5d1SDavid du Colombier  */
309d9306527SDavid du Colombier static char *
310d9306527SDavid du Colombier dotls(char *me)
3113e12c5d1SDavid du Colombier {
312d9306527SDavid du Colombier 	TLSconn *c;
313d9306527SDavid du Colombier 	Thumbprint *goodcerts;
314d9306527SDavid du Colombier 	char *h;
315d9306527SDavid du Colombier 	int fd;
316d9306527SDavid du Colombier 	uchar hash[SHA1dlen];
317d9306527SDavid du Colombier 
318d9306527SDavid du Colombier 	c = mallocz(sizeof(*c), 1);	/* Note: not freed on success */
319d9306527SDavid du Colombier 	if (c == nil)
320d9306527SDavid du Colombier 		return Giveup;
321fe096d1aSDavid du Colombier 
322d9306527SDavid du Colombier 	dBprint("STARTTLS\r\n");
323fe096d1aSDavid du Colombier 	if (getreply() != 2)
324fe096d1aSDavid du Colombier 		return Giveup;
325fe096d1aSDavid du Colombier 
326d9306527SDavid du Colombier 	fd = tlsClient(Bfildes(&bout), c);
327fe096d1aSDavid du Colombier 	if (fd < 0) {
328fe096d1aSDavid du Colombier 		syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
329d9306527SDavid du Colombier 		return Giveup;
330dddc47c2SDavid du Colombier 	}
331fe096d1aSDavid du Colombier 	goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
332d9306527SDavid du Colombier 	if (goodcerts == nil) {
333d9306527SDavid du Colombier 		free(c);
334fe096d1aSDavid du Colombier 		close(fd);
335fe096d1aSDavid du Colombier 		syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
336fe096d1aSDavid du Colombier 		return Giveup;		/* how to recover? TLS is started */
337d9306527SDavid du Colombier 	}
338fe096d1aSDavid du Colombier 
339fe096d1aSDavid du Colombier 	/* compute sha1 hash of remote's certificate, see if we know it */
340d9306527SDavid du Colombier 	sha1(c->cert, c->certlen, hash, nil);
341d9306527SDavid du Colombier 	if (!okThumbprint(hash, goodcerts)) {
342fe096d1aSDavid du Colombier 		/* TODO? if not excluded, add hash to thumb list */
343fe096d1aSDavid du Colombier 		free(c);
344fe096d1aSDavid du Colombier 		close(fd);
345d9306527SDavid du Colombier 		h = malloc(2*sizeof hash + 1);
346d9306527SDavid du Colombier 		if (h != nil) {
347d9306527SDavid du Colombier 			enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
348fe096d1aSDavid du Colombier 			// print("x509 sha1=%s", h);
349fe096d1aSDavid du Colombier 			syslog(0, "smtp",
350fe096d1aSDavid du Colombier 		"remote cert. has bad thumbprint: x509 sha1=%s server=%q",
351fe096d1aSDavid du Colombier 				h, ddomain);
352d9306527SDavid du Colombier 			free(h);
353d9306527SDavid du Colombier 		}
354fe096d1aSDavid du Colombier 		return Giveup;		/* how to recover? TLS is started */
355d9306527SDavid du Colombier 	}
356d9306527SDavid du Colombier 	freeThumbprints(goodcerts);
357d9306527SDavid du Colombier 	Bterm(&bin);
358d9306527SDavid du Colombier 	Bterm(&bout);
359fe096d1aSDavid du Colombier 
360fe096d1aSDavid du Colombier 	/*
361fe096d1aSDavid du Colombier 	 * set up bin & bout to use the TLS fd, i/o upon which generates
362fe096d1aSDavid du Colombier 	 * i/o on the original, underlying fd.
363fe096d1aSDavid du Colombier 	 */
364d9306527SDavid du Colombier 	Binit(&bin, fd, OREAD);
365d9306527SDavid du Colombier 	fd = dup(fd, -1);
366d9306527SDavid du Colombier 	Binit(&bout, fd, OWRITE);
367fe096d1aSDavid du Colombier 
368fe096d1aSDavid du Colombier 	syslog(0, "smtp", "started TLS to %q", ddomain);
369d9306527SDavid du Colombier 	return(hello(me, 1));
370d9306527SDavid du Colombier }
371d9306527SDavid du Colombier 
372d9306527SDavid du Colombier static char *
3733cf081f0SDavid du Colombier doauth(char *methods)
374d9306527SDavid du Colombier {
3759863c128SDavid du Colombier 	char *buf, *base64;
376d9306527SDavid du Colombier 	int n;
3779863c128SDavid du Colombier 	DS ds;
3789863c128SDavid du Colombier 	UserPasswd *p;
379d9306527SDavid du Colombier 
3809863c128SDavid du Colombier 	dial_string_parse(ddomain, &ds);
3813cf081f0SDavid du Colombier 
382d9306527SDavid du Colombier 	if(user != nil)
383d9306527SDavid du Colombier 		p = auth_getuserpasswd(nil,
3849863c128SDavid du Colombier 	  	  "proto=pass service=smtp server=%q user=%q", ds.host, user);
385d9306527SDavid du Colombier 	else
386d9306527SDavid du Colombier 		p = auth_getuserpasswd(nil,
3879863c128SDavid du Colombier 	  	  "proto=pass service=smtp server=%q", ds.host);
388d9306527SDavid du Colombier 	if (p == nil)
389d9306527SDavid du Colombier 		return Giveup;
3903cf081f0SDavid du Colombier 
3913cf081f0SDavid du Colombier 	if (strstr(methods, "LOGIN")){
3923cf081f0SDavid du Colombier 		dBprint("AUTH LOGIN\r\n");
3933cf081f0SDavid du Colombier 		if (getreply() != 3)
3943cf081f0SDavid du Colombier 			return Retry;
3953cf081f0SDavid du Colombier 
3963cf081f0SDavid du Colombier 		n = strlen(p->user);
3973cf081f0SDavid du Colombier 		base64 = malloc(2*n);
3983cf081f0SDavid du Colombier 		if (base64 == nil)
3993cf081f0SDavid du Colombier 			return Retry;	/* Out of memory */
4003cf081f0SDavid du Colombier 		enc64(base64, 2*n, (uchar *)p->user, n);
4013cf081f0SDavid du Colombier 		dBprint("%s\r\n", base64);
4023cf081f0SDavid du Colombier 		if (getreply() != 3)
4033cf081f0SDavid du Colombier 			return Retry;
4043cf081f0SDavid du Colombier 
4053cf081f0SDavid du Colombier 		n = strlen(p->passwd);
4063cf081f0SDavid du Colombier 		base64 = malloc(2*n);
4073cf081f0SDavid du Colombier 		if (base64 == nil)
4083cf081f0SDavid du Colombier 			return Retry;	/* Out of memory */
4093cf081f0SDavid du Colombier 		enc64(base64, 2*n, (uchar *)p->passwd, n);
4103cf081f0SDavid du Colombier 		dBprint("%s\r\n", base64);
4113cf081f0SDavid du Colombier 		if (getreply() != 2)
4123cf081f0SDavid du Colombier 			return Retry;
4133cf081f0SDavid du Colombier 
4143cf081f0SDavid du Colombier 		free(base64);
4153cf081f0SDavid du Colombier 	}
4163cf081f0SDavid du Colombier 	else
4173cf081f0SDavid du Colombier 	if (strstr(methods, "PLAIN")){
418d9306527SDavid du Colombier 		n = strlen(p->user) + strlen(p->passwd) + 3;
419d9306527SDavid du Colombier 		buf = malloc(n);
420d9306527SDavid du Colombier 		base64 = malloc(2 * n);
421fe096d1aSDavid du Colombier 		if (buf == nil || base64 == nil) {
422d9306527SDavid du Colombier 			free(buf);
423d9306527SDavid du Colombier 			return Retry;	/* Out of memory */
424d9306527SDavid du Colombier 		}
425d9306527SDavid du Colombier 		snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd);
426d9306527SDavid du Colombier 		enc64(base64, 2 * n, (uchar *)buf, n - 1);
427d9306527SDavid du Colombier 		free(buf);
428d9306527SDavid du Colombier 		dBprint("AUTH PLAIN %s\r\n", base64);
429d9306527SDavid du Colombier 		free(base64);
430d9306527SDavid du Colombier 		if (getreply() != 2)
431d9306527SDavid du Colombier 			return Retry;
4323cf081f0SDavid du Colombier 	}
4333cf081f0SDavid du Colombier 	else
4343cf081f0SDavid du Colombier 		return "No supported AUTH method";
435d9306527SDavid du Colombier 	return(0);
436d9306527SDavid du Colombier }
437d9306527SDavid du Colombier 
438d9306527SDavid du Colombier char *
439d9306527SDavid du Colombier hello(char *me, int encrypted)
440d9306527SDavid du Colombier {
441d9306527SDavid du Colombier 	int ehlo;
442d9306527SDavid du Colombier 	String *r;
4433cf081f0SDavid du Colombier 	char *ret, *s, *t;
444d9306527SDavid du Colombier 
445d9306527SDavid du Colombier 	if (!encrypted)
4463e12c5d1SDavid du Colombier 		switch(getreply()){
4473e12c5d1SDavid du Colombier 		case 2:
4483e12c5d1SDavid du Colombier 			break;
4493e12c5d1SDavid du Colombier 		case 5:
4503e12c5d1SDavid du Colombier 			return Giveup;
4513e12c5d1SDavid du Colombier 		default:
4523e12c5d1SDavid du Colombier 			return Retry;
4533e12c5d1SDavid du Colombier 		}
454d9306527SDavid du Colombier 
455d9306527SDavid du Colombier 	ehlo = 1;
456d9306527SDavid du Colombier   Again:
457d9306527SDavid du Colombier 	if(ehlo)
458d9306527SDavid du Colombier 		dBprint("EHLO %s\r\n", me);
459d9306527SDavid du Colombier 	else
4603e12c5d1SDavid du Colombier 		dBprint("HELO %s\r\n", me);
4613e12c5d1SDavid du Colombier 	switch (getreply()) {
4623e12c5d1SDavid du Colombier 	case 2:
4633e12c5d1SDavid du Colombier 		break;
4643e12c5d1SDavid du Colombier 	case 5:
465d9306527SDavid du Colombier 		if(ehlo){
466d9306527SDavid du Colombier 			ehlo = 0;
467d9306527SDavid du Colombier 			goto Again;
468d9306527SDavid du Colombier 		}
4693e12c5d1SDavid du Colombier 		return Giveup;
4703e12c5d1SDavid du Colombier 	default:
4713e12c5d1SDavid du Colombier 		return Retry;
4723e12c5d1SDavid du Colombier 	}
473d9306527SDavid du Colombier 	r = s_clone(reply);
474d9306527SDavid du Colombier 	if(r == nil)
475d9306527SDavid du Colombier 		return Retry;	/* Out of memory or couldn't get string */
476d9306527SDavid du Colombier 
477d9306527SDavid du Colombier 	/* Invariant: every line has a newline, a result of getcrlf() */
478d9306527SDavid du Colombier 	for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
479d9306527SDavid du Colombier 		*t = '\0';
480d9306527SDavid du Colombier 		for (t = s; *t != '\0'; t++)
481d9306527SDavid du Colombier 			*t = toupper(*t);
482d9306527SDavid du Colombier 		if(!encrypted && trysecure &&
483d9306527SDavid du Colombier 		    (strcmp(s, "250-STARTTLS") == 0 ||
484d9306527SDavid du Colombier 		     strcmp(s, "250 STARTTLS") == 0)){
485d9306527SDavid du Colombier 			s_free(r);
486d9306527SDavid du Colombier 			return(dotls(me));
487d9306527SDavid du Colombier 		}
488a8453668SDavid du Colombier 		if(tryauth && (encrypted || insecure) &&
489d9306527SDavid du Colombier 		    (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
4903cf081f0SDavid du Colombier 		     strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
4913cf081f0SDavid du Colombier 			ret = doauth(s + strlen("250 AUTH "));
492d9306527SDavid du Colombier 			s_free(r);
4933cf081f0SDavid du Colombier 			return ret;
494d9306527SDavid du Colombier 		}
495d9306527SDavid du Colombier 	}
496d9306527SDavid du Colombier 	s_free(r);
4973e12c5d1SDavid du Colombier 	return 0;
4983e12c5d1SDavid du Colombier }
4993e12c5d1SDavid du Colombier 
5003e12c5d1SDavid du Colombier /*
5013e12c5d1SDavid du Colombier  *  report sender to remote
5023e12c5d1SDavid du Colombier  */
5033e12c5d1SDavid du Colombier char *
5043e12c5d1SDavid du Colombier mailfrom(char *from)
5053e12c5d1SDavid du Colombier {
5067dd7cddfSDavid du Colombier 	if(!returnable(from))
5077dd7cddfSDavid du Colombier 		dBprint("MAIL FROM:<>\r\n");
5087dd7cddfSDavid du Colombier 	else
509219b2ee8SDavid du Colombier 	if(strchr(from, '@'))
5103e12c5d1SDavid du Colombier 		dBprint("MAIL FROM:<%s>\r\n", from);
511219b2ee8SDavid du Colombier 	else
512219b2ee8SDavid du Colombier 		dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
5133e12c5d1SDavid du Colombier 	switch(getreply()){
5143e12c5d1SDavid du Colombier 	case 2:
5153e12c5d1SDavid du Colombier 		break;
5163e12c5d1SDavid du Colombier 	case 5:
5173e12c5d1SDavid du Colombier 		return Giveup;
5183e12c5d1SDavid du Colombier 	default:
5193e12c5d1SDavid du Colombier 		return Retry;
5203e12c5d1SDavid du Colombier 	}
5213e12c5d1SDavid du Colombier 	return 0;
5223e12c5d1SDavid du Colombier }
5233e12c5d1SDavid du Colombier 
5243e12c5d1SDavid du Colombier /*
5253e12c5d1SDavid du Colombier  *  report a recipient to remote
5263e12c5d1SDavid du Colombier  */
5273e12c5d1SDavid du Colombier char *
5283e12c5d1SDavid du Colombier rcptto(char *to)
5293e12c5d1SDavid du Colombier {
530219b2ee8SDavid du Colombier 	String *s;
531219b2ee8SDavid du Colombier 
5327dd7cddfSDavid du Colombier 	s = unescapespecial(bangtoat(to));
5337dd7cddfSDavid du Colombier 	if(toline == 0)
534219b2ee8SDavid du Colombier 		toline = s_new();
5357dd7cddfSDavid du Colombier 	else
536219b2ee8SDavid du Colombier 		s_append(toline, ", ");
537219b2ee8SDavid du Colombier 	s_append(toline, s_to_c(s));
538219b2ee8SDavid du Colombier 	if(strchr(s_to_c(s), '@'))
539219b2ee8SDavid du Colombier 		dBprint("RCPT TO:<%s>\r\n", s_to_c(s));
540219b2ee8SDavid du Colombier 	else {
541219b2ee8SDavid du Colombier 		s_append(toline, "@");
542219b2ee8SDavid du Colombier 		s_append(toline, ddomain);
543219b2ee8SDavid du Colombier 		dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);
544219b2ee8SDavid du Colombier 	}
545a8453668SDavid du Colombier 	alarm(10*alarmscale);
5463e12c5d1SDavid du Colombier 	switch(getreply()){
5473e12c5d1SDavid du Colombier 	case 2:
5483e12c5d1SDavid du Colombier 		break;
5493e12c5d1SDavid du Colombier 	case 5:
5503e12c5d1SDavid du Colombier 		return Giveup;
5513e12c5d1SDavid du Colombier 	default:
5523e12c5d1SDavid du Colombier 		return Retry;
5533e12c5d1SDavid du Colombier 	}
5543e12c5d1SDavid du Colombier 	return 0;
5553e12c5d1SDavid du Colombier }
5563e12c5d1SDavid du Colombier 
5579a747e4fSDavid du Colombier static char hex[] = "0123456789abcdef";
5589a747e4fSDavid du Colombier 
5593e12c5d1SDavid du Colombier /*
5603e12c5d1SDavid du Colombier  *  send the damn thing
5613e12c5d1SDavid du Colombier  */
5623e12c5d1SDavid du Colombier char *
5637dd7cddfSDavid du Colombier data(String *from, Biobuf *b)
5643e12c5d1SDavid du Colombier {
5657dd7cddfSDavid du Colombier 	char *buf, *cp;
5669a747e4fSDavid du Colombier 	int i, n, nbytes, bufsize, eof, r;
5677dd7cddfSDavid du Colombier 	String *fromline;
5689a747e4fSDavid du Colombier 	char errmsg[Errlen];
5699a747e4fSDavid du Colombier 	char id[40];
5703e12c5d1SDavid du Colombier 
5713e12c5d1SDavid du Colombier 	/*
5727dd7cddfSDavid du Colombier 	 *  input the header.
5733e12c5d1SDavid du Colombier 	 */
5747dd7cddfSDavid du Colombier 
5757dd7cddfSDavid du Colombier 	buf = malloc(1);
5767dd7cddfSDavid du Colombier 	if(buf == 0){
5777dd7cddfSDavid du Colombier 		s_append(s_restart(reply), "out of memory");
5787dd7cddfSDavid du Colombier 		return Retry;
5797dd7cddfSDavid du Colombier 	}
5807dd7cddfSDavid du Colombier 	n = 0;
5813e12c5d1SDavid du Colombier 	eof = 0;
5827dd7cddfSDavid du Colombier 	for(;;){
5837dd7cddfSDavid du Colombier 		cp = Brdline(b, '\n');
5847dd7cddfSDavid du Colombier 		if(cp == nil){
5853e12c5d1SDavid du Colombier 			eof = 1;
5863e12c5d1SDavid du Colombier 			break;
5873e12c5d1SDavid du Colombier 		}
5887dd7cddfSDavid du Colombier 		nbytes = Blinelen(b);
5897dd7cddfSDavid du Colombier 		buf = realloc(buf, n+nbytes+1);
5907dd7cddfSDavid du Colombier 		if(buf == 0){
5917dd7cddfSDavid du Colombier 			s_append(s_restart(reply), "out of memory");
5927dd7cddfSDavid du Colombier 			return Retry;
5937dd7cddfSDavid du Colombier 		}
5947dd7cddfSDavid du Colombier 		strncpy(buf+n, cp, nbytes);
5957dd7cddfSDavid du Colombier 		n += nbytes;
5967dd7cddfSDavid du Colombier 		if(nbytes == 1)		/* end of header */
5977dd7cddfSDavid du Colombier 			break;
5983e12c5d1SDavid du Colombier 	}
5993e12c5d1SDavid du Colombier 	buf[n] = 0;
6007dd7cddfSDavid du Colombier 	bufsize = n;
6013e12c5d1SDavid du Colombier 
6023e12c5d1SDavid du Colombier 	/*
6033e12c5d1SDavid du Colombier 	 *  parse the header, turn all addresses into @ format
6043e12c5d1SDavid du Colombier 	 */
6059a747e4fSDavid du Colombier 	yyinit(buf, n);
6063e12c5d1SDavid du Colombier 	yyparse();
6073e12c5d1SDavid du Colombier 
6083e12c5d1SDavid du Colombier 	/*
6093e12c5d1SDavid du Colombier 	 *  print message observing '.' escapes and using \r\n for \n
6103e12c5d1SDavid du Colombier 	 */
611a8453668SDavid du Colombier 	alarm(20*alarmscale);
6123e12c5d1SDavid du Colombier 	if(!filter){
6133e12c5d1SDavid du Colombier 		dBprint("DATA\r\n");
6143e12c5d1SDavid du Colombier 		switch(getreply()){
6153e12c5d1SDavid du Colombier 		case 3:
6163e12c5d1SDavid du Colombier 			break;
6173e12c5d1SDavid du Colombier 		case 5:
6187dd7cddfSDavid du Colombier 			free(buf);
6193e12c5d1SDavid du Colombier 			return Giveup;
6203e12c5d1SDavid du Colombier 		default:
6217dd7cddfSDavid du Colombier 			free(buf);
6223e12c5d1SDavid du Colombier 			return Retry;
6233e12c5d1SDavid du Colombier 		}
6243e12c5d1SDavid du Colombier 	}
6253e12c5d1SDavid du Colombier 	/*
6269a747e4fSDavid du Colombier 	 *  send header.  add a message-id, a sender, and a date if there
6273e12c5d1SDavid du Colombier 	 *  isn't one
6283e12c5d1SDavid du Colombier 	 */
6297dd7cddfSDavid du Colombier 	nbytes = 0;
6307dd7cddfSDavid du Colombier 	fromline = convertheader(from);
631bd389b36SDavid du Colombier 	uneaten = buf;
6329a747e4fSDavid du Colombier 
633d9306527SDavid du Colombier 	srand(truerand());
6349a747e4fSDavid du Colombier 	if(messageid == 0){
6359a747e4fSDavid du Colombier 		for(i=0; i<16; i++){
636d9306527SDavid du Colombier 			r = rand()&0xFF;
6379a747e4fSDavid du Colombier 			id[2*i] = hex[r&0xF];
6389a747e4fSDavid du Colombier 			id[2*i+1] = hex[(r>>4)&0xF];
6399a747e4fSDavid du Colombier 		}
6409a747e4fSDavid du Colombier 		id[2*i] = '\0';
6419a747e4fSDavid du Colombier 		nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain);
6429a747e4fSDavid du Colombier 		if(debug)
6439a747e4fSDavid du Colombier 			Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain);
6449a747e4fSDavid du Colombier 	}
6459a747e4fSDavid du Colombier 
6467dd7cddfSDavid du Colombier 	if(originator==0){
6477dd7cddfSDavid du Colombier 		nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline));
6487dd7cddfSDavid du Colombier 		if(debug)
6497dd7cddfSDavid du Colombier 			Bprint(&berr, "From: %s\r\n", s_to_c(fromline));
6507dd7cddfSDavid du Colombier 	}
6517dd7cddfSDavid du Colombier 	s_free(fromline);
6527dd7cddfSDavid du Colombier 
653219b2ee8SDavid du Colombier 	if(destination == 0 && toline)
6547dd7cddfSDavid du Colombier 		if(*s_to_c(toline) == '@'){	/* route addr */
6557dd7cddfSDavid du Colombier 			nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline));
6567dd7cddfSDavid du Colombier 			if(debug)
6577dd7cddfSDavid du Colombier 				Bprint(&berr, "To: <%s>\r\n", s_to_c(toline));
6587dd7cddfSDavid du Colombier 		} else {
6597dd7cddfSDavid du Colombier 			nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline));
6607dd7cddfSDavid du Colombier 			if(debug)
6617dd7cddfSDavid du Colombier 				Bprint(&berr, "To: %s\r\n", s_to_c(toline));
6627dd7cddfSDavid du Colombier 		}
6637dd7cddfSDavid du Colombier 
6643e12c5d1SDavid du Colombier 	if(date==0 && udate)
6657dd7cddfSDavid du Colombier 		nbytes += printdate(udate);
666bd389b36SDavid du Colombier 	if (usys)
667bd389b36SDavid du Colombier 		uneaten = usys->end + 1;
6687dd7cddfSDavid du Colombier 	nbytes += printheader();
669bd389b36SDavid du Colombier 	if (*uneaten != '\n')
670bd389b36SDavid du Colombier 		putcrnl("\n", 1);
6713e12c5d1SDavid du Colombier 
6723e12c5d1SDavid du Colombier 	/*
6733e12c5d1SDavid du Colombier 	 *  send body
6743e12c5d1SDavid du Colombier 	 */
6757dd7cddfSDavid du Colombier 
676bd389b36SDavid du Colombier 	putcrnl(uneaten, buf+n - uneaten);
6777dd7cddfSDavid du Colombier 	nbytes += buf+n - uneaten;
6787dd7cddfSDavid du Colombier 	if(eof == 0){
679219b2ee8SDavid du Colombier 		for(;;){
6807dd7cddfSDavid du Colombier 			n = Bread(b, buf, bufsize);
681219b2ee8SDavid du Colombier 			if(n < 0){
6829a747e4fSDavid du Colombier 				rerrstr(errmsg, sizeof(errmsg));
6837dd7cddfSDavid du Colombier 				s_append(s_restart(reply), errmsg);
6847dd7cddfSDavid du Colombier 				free(buf);
6857dd7cddfSDavid du Colombier 				return Retry;
686219b2ee8SDavid du Colombier 			}
687219b2ee8SDavid du Colombier 			if(n == 0)
688219b2ee8SDavid du Colombier 				break;
689a8453668SDavid du Colombier 			alarm(10*alarmscale);
6903e12c5d1SDavid du Colombier 			putcrnl(buf, n);
6917dd7cddfSDavid du Colombier 			nbytes += n;
692219b2ee8SDavid du Colombier 		}
6937dd7cddfSDavid du Colombier 	}
6947dd7cddfSDavid du Colombier 	free(buf);
6953e12c5d1SDavid du Colombier 	if(!filter){
6963e12c5d1SDavid du Colombier 		if(last != '\n')
6973e12c5d1SDavid du Colombier 			dBprint("\r\n.\r\n");
6983e12c5d1SDavid du Colombier 		else
6993e12c5d1SDavid du Colombier 			dBprint(".\r\n");
700a8453668SDavid du Colombier 		alarm(10*alarmscale);
7013e12c5d1SDavid du Colombier 		switch(getreply()){
7023e12c5d1SDavid du Colombier 		case 2:
7033e12c5d1SDavid du Colombier 			break;
7043e12c5d1SDavid du Colombier 		case 5:
7053e12c5d1SDavid du Colombier 			return Giveup;
7067dd7cddfSDavid du Colombier 		default:
7077dd7cddfSDavid du Colombier 			return Retry;
7083e12c5d1SDavid du Colombier 		}
7097dd7cddfSDavid du Colombier 		syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from),
7107dd7cddfSDavid du Colombier 				nbytes, s_to_c(toline));/**/
7113e12c5d1SDavid du Colombier 	}
7123e12c5d1SDavid du Colombier 	return 0;
7133e12c5d1SDavid du Colombier }
7143e12c5d1SDavid du Colombier 
7153e12c5d1SDavid du Colombier /*
7163e12c5d1SDavid du Colombier  *  we're leaving
7173e12c5d1SDavid du Colombier  */
7183e12c5d1SDavid du Colombier void
7197dd7cddfSDavid du Colombier quit(char *rv)
7203e12c5d1SDavid du Colombier {
7217dd7cddfSDavid du Colombier 		/* 60 minutes to quit */
7227dd7cddfSDavid du Colombier 	quitting = 1;
7237dd7cddfSDavid du Colombier 	quitrv = rv;
724a8453668SDavid du Colombier 	alarm(60*alarmscale);
7253e12c5d1SDavid du Colombier 	dBprint("QUIT\r\n");
7263e12c5d1SDavid du Colombier 	getreply();
7277dd7cddfSDavid du Colombier 	Bterm(&bout);
7287dd7cddfSDavid du Colombier 	Bterm(&bfile);
7293e12c5d1SDavid du Colombier }
7303e12c5d1SDavid du Colombier 
7313e12c5d1SDavid du Colombier /*
7323e12c5d1SDavid du Colombier  *  read a reply into a string, return the reply code
7333e12c5d1SDavid du Colombier  */
7343e12c5d1SDavid du Colombier int
7353e12c5d1SDavid du Colombier getreply(void)
7363e12c5d1SDavid du Colombier {
7373e12c5d1SDavid du Colombier 	char *line;
7383e12c5d1SDavid du Colombier 	int rv;
7393e12c5d1SDavid du Colombier 
7403e12c5d1SDavid du Colombier 	reply = s_reset(reply);
7413e12c5d1SDavid du Colombier 	for(;;){
7423e12c5d1SDavid du Colombier 		line = getcrnl(reply);
7433e12c5d1SDavid du Colombier 		if(line == 0)
7443e12c5d1SDavid du Colombier 			return -1;
7453e12c5d1SDavid du Colombier 		if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]))
7463e12c5d1SDavid du Colombier 			return -1;
7473e12c5d1SDavid du Colombier 		if(line[3] != '-')
7483e12c5d1SDavid du Colombier 			break;
7493e12c5d1SDavid du Colombier 	}
7507dd7cddfSDavid du Colombier 	if(debug)
7517dd7cddfSDavid du Colombier 		Bflush(&berr);
7523e12c5d1SDavid du Colombier 	rv = atoi(line)/100;
7533e12c5d1SDavid du Colombier 	return rv;
7543e12c5d1SDavid du Colombier }
7557dd7cddfSDavid du Colombier void
7567dd7cddfSDavid du Colombier addhostdom(String *buf, char *host)
7577dd7cddfSDavid du Colombier {
7587dd7cddfSDavid du Colombier 	s_append(buf, "@");
7597dd7cddfSDavid du Colombier 	s_append(buf, host);
7607dd7cddfSDavid du Colombier }
7613e12c5d1SDavid du Colombier 
7623e12c5d1SDavid du Colombier /*
7633e12c5d1SDavid du Colombier  *	Convert from `bang' to `source routing' format.
7643e12c5d1SDavid du Colombier  *
7653e12c5d1SDavid du Colombier  *	   a.x.y!b.p.o!c!d ->	@a.x.y:c!d@b.p.o
7663e12c5d1SDavid du Colombier  */
7673e12c5d1SDavid du Colombier String *
768219b2ee8SDavid du Colombier bangtoat(char *addr)
7693e12c5d1SDavid du Colombier {
7703e12c5d1SDavid du Colombier 	String *buf;
7713e12c5d1SDavid du Colombier 	register int i;
7723e12c5d1SDavid du Colombier 	int j, d;
7733e12c5d1SDavid du Colombier 	char *field[128];
7743e12c5d1SDavid du Colombier 
7753e12c5d1SDavid du Colombier 	/* parse the '!' format address */
7763e12c5d1SDavid du Colombier 	buf = s_new();
7773e12c5d1SDavid du Colombier 	for(i = 0; addr; i++){
7783e12c5d1SDavid du Colombier 		field[i] = addr;
7793e12c5d1SDavid du Colombier 		addr = strchr(addr, '!');
7803e12c5d1SDavid du Colombier 		if(addr)
7813e12c5d1SDavid du Colombier 			*addr++ = 0;
7823e12c5d1SDavid du Colombier 	}
7833e12c5d1SDavid du Colombier 	if (i==1) {
7843e12c5d1SDavid du Colombier 		s_append(buf, field[0]);
7853e12c5d1SDavid du Colombier 		return buf;
7863e12c5d1SDavid du Colombier 	}
7873e12c5d1SDavid du Colombier 
7883e12c5d1SDavid du Colombier 	/*
7893e12c5d1SDavid du Colombier 	 *  count leading domain fields (non-domains don't count)
7903e12c5d1SDavid du Colombier 	 */
7917dd7cddfSDavid du Colombier 	for(d = 0; d<i-1; d++)
7923e12c5d1SDavid du Colombier 		if(strchr(field[d], '.')==0)
7933e12c5d1SDavid du Colombier 			break;
7943e12c5d1SDavid du Colombier 	/*
7953e12c5d1SDavid du Colombier 	 *  if there are more than 1 leading domain elements,
7963e12c5d1SDavid du Colombier 	 *  put them in as source routing
7973e12c5d1SDavid du Colombier 	 */
7983e12c5d1SDavid du Colombier 	if(d > 1){
799219b2ee8SDavid du Colombier 		addhostdom(buf, field[0]);
8003e12c5d1SDavid du Colombier 		for(j=1; j<d-1; j++){
8013e12c5d1SDavid du Colombier 			s_append(buf, ",");
8023e12c5d1SDavid du Colombier 			s_append(buf, "@");
8033e12c5d1SDavid du Colombier 			s_append(buf, field[j]);
8043e12c5d1SDavid du Colombier 		}
8053e12c5d1SDavid du Colombier 		s_append(buf, ":");
8063e12c5d1SDavid du Colombier 	}
8073e12c5d1SDavid du Colombier 
8083e12c5d1SDavid du Colombier 	/*
8093e12c5d1SDavid du Colombier 	 *  throw in the non-domain elements separated by '!'s
8103e12c5d1SDavid du Colombier 	 */
8113e12c5d1SDavid du Colombier 	s_append(buf, field[d]);
8123e12c5d1SDavid du Colombier 	for(j=d+1; j<=i-1; j++) {
8133e12c5d1SDavid du Colombier 		s_append(buf, "!");
8143e12c5d1SDavid du Colombier 		s_append(buf, field[j]);
8153e12c5d1SDavid du Colombier 	}
8163e12c5d1SDavid du Colombier 	if(d)
817219b2ee8SDavid du Colombier 		addhostdom(buf, field[d-1]);
8183e12c5d1SDavid du Colombier 	return buf;
8193e12c5d1SDavid du Colombier }
8203e12c5d1SDavid du Colombier 
8213e12c5d1SDavid du Colombier /*
8223e12c5d1SDavid du Colombier  *  convert header addresses to @ format.
8233e12c5d1SDavid du Colombier  *  if the address is a source address, and a domain is specified,
8243e12c5d1SDavid du Colombier  *  make sure it falls in the domain.
8253e12c5d1SDavid du Colombier  */
8267dd7cddfSDavid du Colombier String*
827219b2ee8SDavid du Colombier convertheader(String *from)
8283e12c5d1SDavid du Colombier {
8293e12c5d1SDavid du Colombier 	Field *f;
8307dd7cddfSDavid du Colombier 	Node *p, *lastp;
8313e12c5d1SDavid du Colombier 	String *a;
8323e12c5d1SDavid du Colombier 
8337dd7cddfSDavid du Colombier 	if(!returnable(s_to_c(from))){
8347dd7cddfSDavid du Colombier 		from = s_new();
8357dd7cddfSDavid du Colombier 		s_append(from, "Postmaster");
8367dd7cddfSDavid du Colombier 		addhostdom(from, hostdomain);
8377dd7cddfSDavid du Colombier 	} else
8387dd7cddfSDavid du Colombier 	if(strchr(s_to_c(from), '@') == 0){
8397dd7cddfSDavid du Colombier 		a = username(from);
8407dd7cddfSDavid du Colombier 		if(a) {
8417dd7cddfSDavid du Colombier 			s_append(a, " <");
8427dd7cddfSDavid du Colombier 			s_append(a, s_to_c(from));
8437dd7cddfSDavid du Colombier 			addhostdom(a, hostdomain);
8447dd7cddfSDavid du Colombier 			s_append(a, ">");
8457dd7cddfSDavid du Colombier 			from = a;
8467dd7cddfSDavid du Colombier 		} else {
8477dd7cddfSDavid du Colombier 			from = s_copy(s_to_c(from));
8487dd7cddfSDavid du Colombier 			addhostdom(from, hostdomain);
849219b2ee8SDavid du Colombier 		}
8507dd7cddfSDavid du Colombier 	} else
8517dd7cddfSDavid du Colombier 		from = s_copy(s_to_c(from));
8523e12c5d1SDavid du Colombier 	for(f = firstfield; f; f = f->next){
8537dd7cddfSDavid du Colombier 		lastp = 0;
8547dd7cddfSDavid du Colombier 		for(p = f->node; p; lastp = p, p = p->next){
8553e12c5d1SDavid du Colombier 			if(!p->addr)
8563e12c5d1SDavid du Colombier 				continue;
857219b2ee8SDavid du Colombier 			a = bangtoat(s_to_c(p->s));
8583e12c5d1SDavid du Colombier 			s_free(p->s);
8597dd7cddfSDavid du Colombier 			if(strchr(s_to_c(a), '@') == 0)
8607dd7cddfSDavid du Colombier 				addhostdom(a, hostdomain);
8617dd7cddfSDavid du Colombier 			else if(*s_to_c(a) == '@')
8627dd7cddfSDavid du Colombier 				a = fixrouteaddr(a, p->next, lastp);
8633e12c5d1SDavid du Colombier 			p->s = a;
8643e12c5d1SDavid du Colombier 		}
8653e12c5d1SDavid du Colombier 	}
8667dd7cddfSDavid du Colombier 	return from;
8677dd7cddfSDavid du Colombier }
8687dd7cddfSDavid du Colombier /*
8697dd7cddfSDavid du Colombier  *	ensure route addr has brackets around it
8707dd7cddfSDavid du Colombier  */
8717dd7cddfSDavid du Colombier String*
8727dd7cddfSDavid du Colombier fixrouteaddr(String *raddr, Node *next, Node *last)
8737dd7cddfSDavid du Colombier {
8747dd7cddfSDavid du Colombier 	String *a;
8757dd7cddfSDavid du Colombier 
8767dd7cddfSDavid du Colombier 	if(last && last->c == '<' && next && next->c == '>')
8777dd7cddfSDavid du Colombier 		return raddr;			/* properly formed already */
8787dd7cddfSDavid du Colombier 
8797dd7cddfSDavid du Colombier 	a = s_new();
8807dd7cddfSDavid du Colombier 	s_append(a, "<");
8817dd7cddfSDavid du Colombier 	s_append(a, s_to_c(raddr));
8827dd7cddfSDavid du Colombier 	s_append(a, ">");
8837dd7cddfSDavid du Colombier 	s_free(raddr);
8847dd7cddfSDavid du Colombier 	return a;
8853e12c5d1SDavid du Colombier }
8863e12c5d1SDavid du Colombier 
8873e12c5d1SDavid du Colombier /*
8883e12c5d1SDavid du Colombier  *  print out the parsed header
8893e12c5d1SDavid du Colombier  */
8907dd7cddfSDavid du Colombier int
8913e12c5d1SDavid du Colombier printheader(void)
8923e12c5d1SDavid du Colombier {
8937dd7cddfSDavid du Colombier 	int n, len;
8943e12c5d1SDavid du Colombier 	Field *f;
8953e12c5d1SDavid du Colombier 	Node *p;
8963e12c5d1SDavid du Colombier 	char *cp;
8973e12c5d1SDavid du Colombier 	char c[1];
8983e12c5d1SDavid du Colombier 
8997dd7cddfSDavid du Colombier 	n = 0;
9003e12c5d1SDavid du Colombier 	for(f = firstfield; f; f = f->next){
9013e12c5d1SDavid du Colombier 		for(p = f->node; p; p = p->next){
9023e12c5d1SDavid du Colombier 			if(p->s)
9037dd7cddfSDavid du Colombier 				n += dBprint("%s", s_to_c(p->s));
9043e12c5d1SDavid du Colombier 			else {
9053e12c5d1SDavid du Colombier 				c[0] = p->c;
9063e12c5d1SDavid du Colombier 				putcrnl(c, 1);
9077dd7cddfSDavid du Colombier 				n++;
9083e12c5d1SDavid du Colombier 			}
9093e12c5d1SDavid du Colombier 			if(p->white){
9103e12c5d1SDavid du Colombier 				cp = s_to_c(p->white);
9117dd7cddfSDavid du Colombier 				len = strlen(cp);
9127dd7cddfSDavid du Colombier 				putcrnl(cp, len);
9137dd7cddfSDavid du Colombier 				n += len;
9143e12c5d1SDavid du Colombier 			}
915bd389b36SDavid du Colombier 			uneaten = p->end;
9163e12c5d1SDavid du Colombier 		}
9173e12c5d1SDavid du Colombier 		putcrnl("\n", 1);
9187dd7cddfSDavid du Colombier 		n++;
919bd389b36SDavid du Colombier 		uneaten++;		/* skip newline */
9203e12c5d1SDavid du Colombier 	}
9217dd7cddfSDavid du Colombier 	return n;
9223e12c5d1SDavid du Colombier }
9233e12c5d1SDavid du Colombier 
9243e12c5d1SDavid du Colombier /*
9253e12c5d1SDavid du Colombier  *  add a domain onto an name, return the new name
9263e12c5d1SDavid du Colombier  */
9273e12c5d1SDavid du Colombier char *
9283e12c5d1SDavid du Colombier domainify(char *name, char *domain)
9293e12c5d1SDavid du Colombier {
9303e12c5d1SDavid du Colombier 	static String *s;
9317dd7cddfSDavid du Colombier 	char *p;
9323e12c5d1SDavid du Colombier 
9333e12c5d1SDavid du Colombier 	if(domain==0 || strchr(name, '.')!=0)
9343e12c5d1SDavid du Colombier 		return name;
9353e12c5d1SDavid du Colombier 
9363e12c5d1SDavid du Colombier 	s = s_reset(s);
9373e12c5d1SDavid du Colombier 	s_append(s, name);
9387dd7cddfSDavid du Colombier 	p = strchr(domain, '.');
9397dd7cddfSDavid du Colombier 	if(p == 0){
940219b2ee8SDavid du Colombier 		s_append(s, ".");
9417dd7cddfSDavid du Colombier 		p = domain;
9427dd7cddfSDavid du Colombier 	}
9437dd7cddfSDavid du Colombier 	s_append(s, p);
9443e12c5d1SDavid du Colombier 	return s_to_c(s);
9453e12c5d1SDavid du Colombier }
9463e12c5d1SDavid du Colombier 
9473e12c5d1SDavid du Colombier /*
9483e12c5d1SDavid du Colombier  *  print message observing '.' escapes and using \r\n for \n
9493e12c5d1SDavid du Colombier  */
9503e12c5d1SDavid du Colombier void
9513e12c5d1SDavid du Colombier putcrnl(char *cp, int n)
9523e12c5d1SDavid du Colombier {
9533e12c5d1SDavid du Colombier 	int c;
9543e12c5d1SDavid du Colombier 
9553e12c5d1SDavid du Colombier 	for(; n; n--, cp++){
9563e12c5d1SDavid du Colombier 		c = *cp;
9573e12c5d1SDavid du Colombier 		if(c == '\n')
9587dd7cddfSDavid du Colombier 			dBputc('\r');
9593e12c5d1SDavid du Colombier 		else if(c == '.' && last=='\n')
9607dd7cddfSDavid du Colombier 			dBputc('.');
9617dd7cddfSDavid du Colombier 		dBputc(c);
9623e12c5d1SDavid du Colombier 		last = c;
9633e12c5d1SDavid du Colombier 	}
9643e12c5d1SDavid du Colombier }
9653e12c5d1SDavid du Colombier 
9663e12c5d1SDavid du Colombier /*
9673e12c5d1SDavid du Colombier  *  Get a line including a crnl into a string.  Convert crnl into nl.
9683e12c5d1SDavid du Colombier  */
9693e12c5d1SDavid du Colombier char *
9703e12c5d1SDavid du Colombier getcrnl(String *s)
9713e12c5d1SDavid du Colombier {
9723e12c5d1SDavid du Colombier 	int c;
9733e12c5d1SDavid du Colombier 	int count;
9743e12c5d1SDavid du Colombier 
9753e12c5d1SDavid du Colombier 	count = 0;
9763e12c5d1SDavid du Colombier 	for(;;){
9773e12c5d1SDavid du Colombier 		c = Bgetc(&bin);
9783e12c5d1SDavid du Colombier 		if(debug)
9793e12c5d1SDavid du Colombier 			Bputc(&berr, c);
9803e12c5d1SDavid du Colombier 		switch(c){
9813e12c5d1SDavid du Colombier 		case -1:
9827dd7cddfSDavid du Colombier 			s_append(s, "connection closed unexpectedly by remote system");
9833e12c5d1SDavid du Colombier 			s_terminate(s);
9843e12c5d1SDavid du Colombier 			return 0;
9853e12c5d1SDavid du Colombier 		case '\r':
9863e12c5d1SDavid du Colombier 			c = Bgetc(&bin);
9873e12c5d1SDavid du Colombier 			if(c == '\n'){
9883e12c5d1SDavid du Colombier 				s_putc(s, c);
9893e12c5d1SDavid du Colombier 				if(debug)
9903e12c5d1SDavid du Colombier 					Bputc(&berr, c);
9913e12c5d1SDavid du Colombier 				count++;
9923e12c5d1SDavid du Colombier 				s_terminate(s);
9933e12c5d1SDavid du Colombier 				return s->ptr - count;
9943e12c5d1SDavid du Colombier 			}
9953e12c5d1SDavid du Colombier 			Bungetc(&bin);
9963e12c5d1SDavid du Colombier 			s_putc(s, '\r');
9973e12c5d1SDavid du Colombier 			if(debug)
9983e12c5d1SDavid du Colombier 				Bputc(&berr, '\r');
9993e12c5d1SDavid du Colombier 			count++;
10003e12c5d1SDavid du Colombier 			break;
10013e12c5d1SDavid du Colombier 		default:
10023e12c5d1SDavid du Colombier 			s_putc(s, c);
10033e12c5d1SDavid du Colombier 			count++;
10043e12c5d1SDavid du Colombier 			break;
10053e12c5d1SDavid du Colombier 		}
10063e12c5d1SDavid du Colombier 	}
10073e12c5d1SDavid du Colombier 	return 0;
10083e12c5d1SDavid du Colombier }
10093e12c5d1SDavid du Colombier 
10103e12c5d1SDavid du Colombier /*
10113e12c5d1SDavid du Colombier  *  print out a parsed date
10123e12c5d1SDavid du Colombier  */
10137dd7cddfSDavid du Colombier int
10143e12c5d1SDavid du Colombier printdate(Node *p)
10153e12c5d1SDavid du Colombier {
10167dd7cddfSDavid du Colombier 	int n, sep = 0;
10173e12c5d1SDavid du Colombier 
10187dd7cddfSDavid du Colombier 	n = dBprint("Date: %s,", s_to_c(p->s));
10193e12c5d1SDavid du Colombier 	for(p = p->next; p; p = p->next){
10203e12c5d1SDavid du Colombier 		if(p->s){
10217dd7cddfSDavid du Colombier 			if(sep == 0) {
10227dd7cddfSDavid du Colombier 				dBputc(' ');
10237dd7cddfSDavid du Colombier 				n++;
10247dd7cddfSDavid du Colombier 			}
1025bd389b36SDavid du Colombier 			if (p->next)
10267dd7cddfSDavid du Colombier 				n += dBprint("%s", s_to_c(p->s));
1027bd389b36SDavid du Colombier 			else
10287dd7cddfSDavid du Colombier 				n += dBprint("%s", rewritezone(s_to_c(p->s)));
10293e12c5d1SDavid du Colombier 			sep = 0;
10303e12c5d1SDavid du Colombier 		} else {
10317dd7cddfSDavid du Colombier 			dBputc(p->c);
10327dd7cddfSDavid du Colombier 			n++;
10333e12c5d1SDavid du Colombier 			sep = 1;
10343e12c5d1SDavid du Colombier 		}
10353e12c5d1SDavid du Colombier 	}
10367dd7cddfSDavid du Colombier 	n += dBprint("\r\n");
10377dd7cddfSDavid du Colombier 	return n;
10383e12c5d1SDavid du Colombier }
10393e12c5d1SDavid du Colombier 
1040bd389b36SDavid du Colombier char *
1041bd389b36SDavid du Colombier rewritezone(char *z)
1042bd389b36SDavid du Colombier {
10437dd7cddfSDavid du Colombier 	int mindiff;
10447dd7cddfSDavid du Colombier 	char s;
10457dd7cddfSDavid du Colombier 	Tm *tm;
10467dd7cddfSDavid du Colombier 	static char x[7];
1047bd389b36SDavid du Colombier 
10487dd7cddfSDavid du Colombier 	tm = localtime(time(0));
10497dd7cddfSDavid du Colombier 	mindiff = tm->tzoff/60;
1050bd389b36SDavid du Colombier 
10517dd7cddfSDavid du Colombier 	/* if not in my timezone, don't change anything */
10527dd7cddfSDavid du Colombier 	if(strcmp(tm->zone, z) != 0)
1053bd389b36SDavid du Colombier 		return z;
10547dd7cddfSDavid du Colombier 
10557dd7cddfSDavid du Colombier 	if(mindiff < 0){
10567dd7cddfSDavid du Colombier 		s = '-';
10577dd7cddfSDavid du Colombier 		mindiff = -mindiff;
10587dd7cddfSDavid du Colombier 	} else
10597dd7cddfSDavid du Colombier 		s = '+';
10607dd7cddfSDavid du Colombier 
10617dd7cddfSDavid du Colombier 	sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
10627dd7cddfSDavid du Colombier 	return x;
1063bd389b36SDavid du Colombier }
1064bd389b36SDavid du Colombier 
10653e12c5d1SDavid du Colombier /*
10663e12c5d1SDavid du Colombier  *  stolen from libc/port/print.c
10673e12c5d1SDavid du Colombier  */
10683e12c5d1SDavid du Colombier #define	SIZE	4096
10693e12c5d1SDavid du Colombier int
10703e12c5d1SDavid du Colombier dBprint(char *fmt, ...)
10713e12c5d1SDavid du Colombier {
10723e12c5d1SDavid du Colombier 	char buf[SIZE], *out;
10737dd7cddfSDavid du Colombier 	va_list arg;
10743e12c5d1SDavid du Colombier 	int n;
10753e12c5d1SDavid du Colombier 
10767dd7cddfSDavid du Colombier 	va_start(arg, fmt);
10779a747e4fSDavid du Colombier 	out = vseprint(buf, buf+SIZE, fmt, arg);
10787dd7cddfSDavid du Colombier 	va_end(arg);
10793e12c5d1SDavid du Colombier 	if(debug){
10803e12c5d1SDavid du Colombier 		Bwrite(&berr, buf, (long)(out-buf));
10813e12c5d1SDavid du Colombier 		Bflush(&berr);
10823e12c5d1SDavid du Colombier 	}
10833e12c5d1SDavid du Colombier 	n = Bwrite(&bout, buf, (long)(out-buf));
10843e12c5d1SDavid du Colombier 	Bflush(&bout);
10853e12c5d1SDavid du Colombier 	return n;
10863e12c5d1SDavid du Colombier }
10877dd7cddfSDavid du Colombier 
10887dd7cddfSDavid du Colombier int
10897dd7cddfSDavid du Colombier dBputc(int x)
10907dd7cddfSDavid du Colombier {
10917dd7cddfSDavid du Colombier 	if(debug)
10927dd7cddfSDavid du Colombier 		Bputc(&berr, x);
10937dd7cddfSDavid du Colombier 	return Bputc(&bout, x);
10947dd7cddfSDavid du Colombier }
1095