xref: /plan9/sys/src/cmd/upas/smtp/smtp.c (revision 21887c0b3fa083db468c32f3247dbe932cf708df)
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*);
11db7ae703SDavid du Colombier 
12219b2ee8SDavid du Colombier void	addhostdom(String*, char*);
13219b2ee8SDavid du Colombier String*	bangtoat(char*);
147dd7cddfSDavid du Colombier String*	convertheader(String*);
153e12c5d1SDavid du Colombier int	dBprint(char*, ...);
167dd7cddfSDavid du Colombier int	dBputc(int);
17db7ae703SDavid du Colombier char*	data(String*, Biobuf*);
18db7ae703SDavid du Colombier char*	domainify(char*, char*);
197dd7cddfSDavid du Colombier String*	fixrouteaddr(String*, Node*, Node*);
20db7ae703SDavid du Colombier char*	getcrnl(String*);
21db7ae703SDavid du Colombier int	getreply(void);
22db7ae703SDavid du Colombier char*	hello(char*, int);
23db7ae703SDavid du Colombier char*	mailfrom(char*);
24db7ae703SDavid du Colombier int	printdate(Node*);
25db7ae703SDavid du Colombier int	printheader(void);
26db7ae703SDavid du Colombier void	putcrnl(char*, int);
27db7ae703SDavid du Colombier void	quit(char*);
28db7ae703SDavid du Colombier char*	rcptto(char*);
29db7ae703SDavid du Colombier char	*rewritezone(char *);
303e12c5d1SDavid du Colombier 
317dd7cddfSDavid du Colombier #define Retry	"Retry, Temporary Failure"
323e12c5d1SDavid du Colombier #define Giveup	"Permanent Failure"
333e12c5d1SDavid du Colombier 
343e12c5d1SDavid du Colombier String	*reply;		/* last reply */
35219b2ee8SDavid du Colombier String	*toline;
36db7ae703SDavid du Colombier 
37a8453668SDavid du Colombier int	alarmscale;
38db7ae703SDavid du Colombier int	autistic;
39db7ae703SDavid du Colombier int	debug;		/* true if we're debugging */
403e12c5d1SDavid du Colombier int	filter;
41db7ae703SDavid du Colombier int	insecure;
42db7ae703SDavid du Colombier int	last = 'n';	/* last character sent by putcrnl() */
43db7ae703SDavid du Colombier int	ping;
447dd7cddfSDavid du Colombier int	quitting;	/* when error occurs in quit */
45db7ae703SDavid du Colombier int	tryauth;	/* Try to authenticate, if supported */
46db7ae703SDavid du Colombier int	trysecure;	/* Try to use TLS if the other side supports it */
47db7ae703SDavid du Colombier 
487dd7cddfSDavid du Colombier char	*quitrv;	/* deferred return value when in quit */
493e12c5d1SDavid du Colombier char	ddomain[1024];	/* domain name of destination machine */
503e12c5d1SDavid du Colombier char	*gdomain;	/* domain name of gateway */
51bd389b36SDavid du Colombier char	*uneaten;	/* first character after rfc822 headers */
527dd7cddfSDavid du Colombier char	*farend;	/* system we are trying to send to */
53d9306527SDavid du Colombier char	*user;		/* user we are authenticating as, if authenticating */
54219b2ee8SDavid du Colombier char	hostdomain[256];
55db7ae703SDavid du Colombier 
563e12c5d1SDavid du Colombier Biobuf	bin;
573e12c5d1SDavid du Colombier Biobuf	bout;
583e12c5d1SDavid du Colombier Biobuf	berr;
597dd7cddfSDavid du Colombier Biobuf	bfile;
603e12c5d1SDavid du Colombier 
61617c0e1eSDavid du Colombier static int bustedmx;
62617c0e1eSDavid du Colombier 
633e12c5d1SDavid du Colombier void
643e12c5d1SDavid du Colombier usage(void)
653e12c5d1SDavid du Colombier {
66617c0e1eSDavid du Colombier 	fprint(2, "usage: smtp [-aAdfips] [-b busted-mx] [-g gw] [-h host] "
67617c0e1eSDavid du Colombier 		"[-u user] [.domain] net!host[!service] sender rcpt-list\n");
68219b2ee8SDavid du Colombier 	exits(Giveup);
69219b2ee8SDavid du Colombier }
70219b2ee8SDavid du Colombier 
717dd7cddfSDavid du Colombier int
72219b2ee8SDavid du Colombier timeout(void *x, char *msg)
73219b2ee8SDavid du Colombier {
74219b2ee8SDavid du Colombier 	USED(x);
757dd7cddfSDavid du Colombier 	syslog(0, "smtp.fail", "interrupt: %s: %s", farend,  msg);
76219b2ee8SDavid du Colombier 	if(strstr(msg, "alarm")){
777dd7cddfSDavid du Colombier 		fprint(2, "smtp timeout: connection to %s timed out\n", farend);
787dd7cddfSDavid du Colombier 		if(quitting)
797dd7cddfSDavid du Colombier 			exits(quitrv);
807dd7cddfSDavid du Colombier 		exits(Retry);
81219b2ee8SDavid du Colombier 	}
827dd7cddfSDavid du Colombier 	if(strstr(msg, "closed pipe")){
837dd7cddfSDavid du Colombier 			/* call _exits() to prevent Bio from trying to flush closed pipe */
847dd7cddfSDavid du Colombier 		fprint(2, "smtp timeout: connection closed to %s\n", farend);
857dd7cddfSDavid du Colombier 		if(quitting){
867dd7cddfSDavid du Colombier 			syslog(0, "smtp.fail", "closed pipe to %s", farend);
877dd7cddfSDavid du Colombier 			_exits(quitrv);
887dd7cddfSDavid du Colombier 		}
897dd7cddfSDavid du Colombier 		_exits(Retry);
907dd7cddfSDavid du Colombier 	}
917dd7cddfSDavid du Colombier 	return 0;
923e12c5d1SDavid du Colombier }
933e12c5d1SDavid du Colombier 
943e12c5d1SDavid du Colombier void
95a8453668SDavid du Colombier removenewline(char *p)
96a8453668SDavid du Colombier {
97a8453668SDavid du Colombier 	int n = strlen(p)-1;
98a8453668SDavid du Colombier 
99a8453668SDavid du Colombier 	if(n < 0)
100a8453668SDavid du Colombier 		return;
101a8453668SDavid du Colombier 	if(p[n] == '\n')
102a8453668SDavid du Colombier 		p[n] = 0;
103a8453668SDavid du Colombier }
104a8453668SDavid du Colombier 
105a8453668SDavid du Colombier void
1063e12c5d1SDavid du Colombier main(int argc, char **argv)
1073e12c5d1SDavid du Colombier {
1087dd7cddfSDavid du Colombier 	int i, ok, rcvrs;
109db7ae703SDavid du Colombier 	char *addr, *rv, *trv, *host, *domain;
1107dd7cddfSDavid du Colombier 	char **errs;
111db7ae703SDavid du Colombier 	char hellodomain[256];
112db7ae703SDavid du Colombier 	String *from, *fromm, *sender;
1133e12c5d1SDavid du Colombier 
114a8453668SDavid du Colombier 	alarmscale = 60*1000;	/* minutes */
115fe096d1aSDavid du Colombier 	quotefmtinstall();
1167dd7cddfSDavid du Colombier 	errs = malloc(argc*sizeof(char*));
1173e12c5d1SDavid du Colombier 	reply = s_new();
118219b2ee8SDavid du Colombier 	host = 0;
1193e12c5d1SDavid du Colombier 	ARGBEGIN{
120d9306527SDavid du Colombier 	case 'a':
121d9306527SDavid du Colombier 		tryauth = 1;
122d9306527SDavid du Colombier 		trysecure = 1;
123d9306527SDavid du Colombier 		break;
124db7ae703SDavid du Colombier 	case 'A':	/* autistic: won't talk to us until we talk (Verizon) */
125db7ae703SDavid du Colombier 		autistic = 1;
1263e12c5d1SDavid du Colombier 		break;
127617c0e1eSDavid du Colombier 	case 'b':
128617c0e1eSDavid du Colombier 		if (bustedmx >= Maxbustedmx)
129617c0e1eSDavid du Colombier 			sysfatal("more than %d busted mxs given", Maxbustedmx);
130617c0e1eSDavid du Colombier 		bustedmxs[bustedmx++] = EARGF(usage());
131617c0e1eSDavid du Colombier 		break;
1323e12c5d1SDavid du Colombier 	case 'd':
1333e12c5d1SDavid du Colombier 		debug = 1;
1343e12c5d1SDavid du Colombier 		break;
135db7ae703SDavid du Colombier 	case 'f':
136db7ae703SDavid du Colombier 		filter = 1;
137db7ae703SDavid du Colombier 		break;
1383e12c5d1SDavid du Colombier 	case 'g':
139db7ae703SDavid du Colombier 		gdomain = EARGF(usage());
1403e12c5d1SDavid du Colombier 		break;
1413e12c5d1SDavid du Colombier 	case 'h':
142db7ae703SDavid du Colombier 		host = EARGF(usage());
1433e12c5d1SDavid du Colombier 		break;
144a8453668SDavid du Colombier 	case 'i':
145a8453668SDavid du Colombier 		insecure = 1;
146a8453668SDavid du Colombier 		break;
147a8453668SDavid du Colombier 	case 'p':
148a8453668SDavid du Colombier 		alarmscale = 10*1000;	/* tens of seconds */
149a8453668SDavid du Colombier 		ping = 1;
150a8453668SDavid du Colombier 		break;
151d9306527SDavid du Colombier 	case 's':
152d9306527SDavid du Colombier 		trysecure = 1;
153d9306527SDavid du Colombier 		break;
154d9306527SDavid du Colombier 	case 'u':
155db7ae703SDavid du Colombier 		user = EARGF(usage());
156d9306527SDavid du Colombier 		break;
1573e12c5d1SDavid du Colombier 	default:
1583e12c5d1SDavid du Colombier 		usage();
1593e12c5d1SDavid du Colombier 		break;
1603e12c5d1SDavid du Colombier 	}ARGEND;
1613e12c5d1SDavid du Colombier 
1623e12c5d1SDavid du Colombier 	Binit(&berr, 2, OWRITE);
1637dd7cddfSDavid du Colombier 	Binit(&bfile, 0, OREAD);
1643e12c5d1SDavid du Colombier 
1653e12c5d1SDavid du Colombier 	/*
1663e12c5d1SDavid du Colombier 	 *  get domain and add to host name
1673e12c5d1SDavid du Colombier 	 */
1687dd7cddfSDavid du Colombier 	if(*argv && **argv=='.') {
1697dd7cddfSDavid du Colombier 		domain = *argv;
1707dd7cddfSDavid du Colombier 		argv++; argc--;
1717dd7cddfSDavid du Colombier 	} else
1727dd7cddfSDavid du Colombier 		domain = domainname_read();
1733e12c5d1SDavid du Colombier 	if(host == 0)
1743e12c5d1SDavid du Colombier 		host = sysname_read();
1753e12c5d1SDavid du Colombier 	strcpy(hostdomain, domainify(host, domain));
1767dd7cddfSDavid du Colombier 	strcpy(hellodomain, domainify(sysname_read(), domain));
1773e12c5d1SDavid du Colombier 
1783e12c5d1SDavid du Colombier 	/*
1793e12c5d1SDavid du Colombier 	 *  get destination address
1803e12c5d1SDavid du Colombier 	 */
1813e12c5d1SDavid du Colombier 	if(*argv == 0)
1823e12c5d1SDavid du Colombier 		usage();
1837dd7cddfSDavid du Colombier 	addr = *argv++; argc--;
1847dd7cddfSDavid du Colombier 	farend = addr;
1853e12c5d1SDavid du Colombier 
1863e12c5d1SDavid du Colombier 	/*
1873e12c5d1SDavid du Colombier 	 *  get sender's machine.
1883e12c5d1SDavid du Colombier 	 *  get sender in internet style.  domainify if necessary.
1893e12c5d1SDavid du Colombier 	 */
190219b2ee8SDavid du Colombier 	if(*argv == 0)
191219b2ee8SDavid du Colombier 		usage();
1927dd7cddfSDavid du Colombier 	sender = unescapespecial(s_copy(*argv++));
1937dd7cddfSDavid du Colombier 	argc--;
1947dd7cddfSDavid du Colombier 	fromm = s_clone(sender);
1953e12c5d1SDavid du Colombier 	rv = strrchr(s_to_c(fromm), '!');
1963e12c5d1SDavid du Colombier 	if(rv)
1973e12c5d1SDavid du Colombier 		*rv = 0;
1983e12c5d1SDavid du Colombier 	else
1993e12c5d1SDavid du Colombier 		*s_to_c(fromm) = 0;
2007dd7cddfSDavid du Colombier 	from = bangtoat(s_to_c(sender));
2013e12c5d1SDavid du Colombier 
2023e12c5d1SDavid du Colombier 	/*
2033e12c5d1SDavid du Colombier 	 *  send the mail
2043e12c5d1SDavid du Colombier 	 */
2053e12c5d1SDavid du Colombier 	if(filter){
2063e12c5d1SDavid du Colombier 		Binit(&bout, 1, OWRITE);
2077dd7cddfSDavid du Colombier 		rv = data(from, &bfile);
2083e12c5d1SDavid du Colombier 		if(rv != 0)
2093e12c5d1SDavid du Colombier 			goto error;
2107dd7cddfSDavid du Colombier 		exits(0);
2117dd7cddfSDavid du Colombier 	}
2127dd7cddfSDavid du Colombier 
213ab3dc52fSDavid du Colombier 	/* mxdial uses its own timeout handler */
2147dd7cddfSDavid du Colombier 	if((rv = connect(addr)) != 0)
2157dd7cddfSDavid du Colombier 		exits(rv);
216ab3dc52fSDavid du Colombier 
217ab3dc52fSDavid du Colombier 	/* 10 minutes to get through the initial handshake */
218ab3dc52fSDavid du Colombier 	atnotify(timeout, 1);
219a8453668SDavid du Colombier 	alarm(10*alarmscale);
220d9306527SDavid du Colombier 	if((rv = hello(hellodomain, 0)) != 0)
2217dd7cddfSDavid du Colombier 		goto error;
222a8453668SDavid du Colombier 	alarm(10*alarmscale);
2237dd7cddfSDavid du Colombier 	if((rv = mailfrom(s_to_c(from))) != 0)
2247dd7cddfSDavid du Colombier 		goto error;
2257dd7cddfSDavid du Colombier 
2267dd7cddfSDavid du Colombier 	ok = 0;
2277dd7cddfSDavid du Colombier 	rcvrs = 0;
2287dd7cddfSDavid du Colombier 	/* if any rcvrs are ok, we try to send the message */
2297dd7cddfSDavid du Colombier 	for(i = 0; i < argc; i++){
2307dd7cddfSDavid du Colombier 		if((trv = rcptto(argv[i])) != 0){
2317dd7cddfSDavid du Colombier 			/* remember worst error */
2327dd7cddfSDavid du Colombier 			if(rv != Giveup)
2337dd7cddfSDavid du Colombier 				rv = trv;
2347dd7cddfSDavid du Colombier 			errs[rcvrs] = strdup(s_to_c(reply));
235a8453668SDavid du Colombier 			removenewline(errs[rcvrs]);
2367dd7cddfSDavid du Colombier 		} else {
2377dd7cddfSDavid du Colombier 			ok++;
2387dd7cddfSDavid du Colombier 			errs[rcvrs] = 0;
2397dd7cddfSDavid du Colombier 		}
2407dd7cddfSDavid du Colombier 		rcvrs++;
2417dd7cddfSDavid du Colombier 	}
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 	/* if no ok rcvrs or worst error is retry, give up */
2447dd7cddfSDavid du Colombier 	if(ok == 0 || rv == Retry)
2457dd7cddfSDavid du Colombier 		goto error;
2467dd7cddfSDavid du Colombier 
247a8453668SDavid du Colombier 	if(ping){
248a8453668SDavid du Colombier 		quit(0);
249a8453668SDavid du Colombier 		exits(0);
250a8453668SDavid du Colombier 	}
251a8453668SDavid du Colombier 
2527dd7cddfSDavid du Colombier 	rv = data(from, &bfile);
2537dd7cddfSDavid du Colombier 	if(rv != 0)
2547dd7cddfSDavid du Colombier 		goto error;
2557dd7cddfSDavid du Colombier 	quit(0);
2567dd7cddfSDavid du Colombier 	if(rcvrs == ok)
2577dd7cddfSDavid du Colombier 		exits(0);
2587dd7cddfSDavid du Colombier 
2597dd7cddfSDavid du Colombier 	/*
2607dd7cddfSDavid du Colombier 	 *  here when some but not all rcvrs failed
2617dd7cddfSDavid du Colombier 	 */
2627dd7cddfSDavid du Colombier 	fprint(2, "%s connect to %s:\n", thedate(), addr);
2637dd7cddfSDavid du Colombier 	for(i = 0; i < rcvrs; i++){
2647dd7cddfSDavid du Colombier 		if(errs[i]){
265a8453668SDavid du Colombier 			syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]);
2667dd7cddfSDavid du Colombier 			fprint(2, "  mail to %s failed: %s", argv[i], errs[i]);
2677dd7cddfSDavid du Colombier 		}
2687dd7cddfSDavid du Colombier 	}
2697dd7cddfSDavid du Colombier 	exits(Giveup);
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 	/*
2727dd7cddfSDavid du Colombier 	 *  here when all rcvrs failed
2737dd7cddfSDavid du Colombier 	 */
2743e12c5d1SDavid du Colombier error:
275a8453668SDavid du Colombier 	removenewline(s_to_c(reply));
276a8453668SDavid du Colombier 	syslog(0, "smtp.fail", "%s to %s failed: %s",
277a8453668SDavid du Colombier 		ping ? "ping" : "delivery",
278a8453668SDavid du Colombier 		addr, s_to_c(reply));
2797dd7cddfSDavid du Colombier 	fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));
2807dd7cddfSDavid du Colombier 	if(!filter)
2817dd7cddfSDavid du Colombier 		quit(rv);
2823e12c5d1SDavid du Colombier 	exits(rv);
2833e12c5d1SDavid du Colombier }
2843e12c5d1SDavid du Colombier 
2853e12c5d1SDavid du Colombier /*
2863e12c5d1SDavid du Colombier  *  connect to the remote host
2873e12c5d1SDavid du Colombier  */
2887dd7cddfSDavid du Colombier static char *
2893e12c5d1SDavid du Colombier connect(char* net)
2903e12c5d1SDavid du Colombier {
291*21887c0bSDavid du Colombier 	char buf[Errlen];
2923e12c5d1SDavid du Colombier 	int fd;
2933e12c5d1SDavid du Colombier 
2947dd7cddfSDavid du Colombier 	fd = mxdial(net, ddomain, gdomain);
2953e12c5d1SDavid du Colombier 
2963e12c5d1SDavid du Colombier 	if(fd < 0){
2979a747e4fSDavid du Colombier 		rerrstr(buf, sizeof(buf));
2983ff48bf5SDavid du Colombier 		Bprint(&berr, "smtp: %s (%s)\n", buf, net);
2993ff48bf5SDavid du Colombier 		syslog(0, "smtp.fail", "%s (%s)", buf, net);
3007dd7cddfSDavid du Colombier 		if(strstr(buf, "illegal")
3017dd7cddfSDavid du Colombier 		|| strstr(buf, "unknown")
302271b8d73SDavid du Colombier 		|| strstr(buf, "can't translate"))
3033e12c5d1SDavid du Colombier 			return Giveup;
3043e12c5d1SDavid du Colombier 		else
3053e12c5d1SDavid du Colombier 			return Retry;
3063e12c5d1SDavid du Colombier 	}
3073e12c5d1SDavid du Colombier 	Binit(&bin, fd, OREAD);
3083e12c5d1SDavid du Colombier 	fd = dup(fd, -1);
3093e12c5d1SDavid du Colombier 	Binit(&bout, fd, OWRITE);
3103e12c5d1SDavid du Colombier 	return 0;
3113e12c5d1SDavid du Colombier }
3123e12c5d1SDavid du Colombier 
313fe096d1aSDavid du Colombier static char smtpthumbs[] =	"/sys/lib/tls/smtp";
314fe096d1aSDavid du Colombier static char smtpexclthumbs[] =	"/sys/lib/tls/smtp.exclude";
315fe096d1aSDavid du Colombier 
3163e12c5d1SDavid du Colombier /*
317fe096d1aSDavid du Colombier  *  exchange names with remote host, attempt to
318fe096d1aSDavid du Colombier  *  enable encryption and optionally authenticate.
319fe096d1aSDavid du Colombier  *  not fatal if we can't.
3203e12c5d1SDavid du Colombier  */
321d9306527SDavid du Colombier static char *
322d9306527SDavid du Colombier dotls(char *me)
3233e12c5d1SDavid du Colombier {
324d9306527SDavid du Colombier 	TLSconn *c;
325d9306527SDavid du Colombier 	Thumbprint *goodcerts;
326d9306527SDavid du Colombier 	char *h;
327d9306527SDavid du Colombier 	int fd;
328d9306527SDavid du Colombier 	uchar hash[SHA1dlen];
329d9306527SDavid du Colombier 
330d9306527SDavid du Colombier 	c = mallocz(sizeof(*c), 1);	/* Note: not freed on success */
331d9306527SDavid du Colombier 	if (c == nil)
332d9306527SDavid du Colombier 		return Giveup;
333fe096d1aSDavid du Colombier 
334d9306527SDavid du Colombier 	dBprint("STARTTLS\r\n");
335fe096d1aSDavid du Colombier 	if (getreply() != 2)
336fe096d1aSDavid du Colombier 		return Giveup;
337fe096d1aSDavid du Colombier 
338d9306527SDavid du Colombier 	fd = tlsClient(Bfildes(&bout), c);
339fe096d1aSDavid du Colombier 	if (fd < 0) {
340fe096d1aSDavid du Colombier 		syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
341d9306527SDavid du Colombier 		return Giveup;
342dddc47c2SDavid du Colombier 	}
343fe096d1aSDavid du Colombier 	goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
344d9306527SDavid du Colombier 	if (goodcerts == nil) {
345d9306527SDavid du Colombier 		free(c);
346fe096d1aSDavid du Colombier 		close(fd);
347fe096d1aSDavid du Colombier 		syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
348fe096d1aSDavid du Colombier 		return Giveup;		/* how to recover? TLS is started */
349d9306527SDavid du Colombier 	}
350fe096d1aSDavid du Colombier 
351fe096d1aSDavid du Colombier 	/* compute sha1 hash of remote's certificate, see if we know it */
352d9306527SDavid du Colombier 	sha1(c->cert, c->certlen, hash, nil);
353d9306527SDavid du Colombier 	if (!okThumbprint(hash, goodcerts)) {
354fe096d1aSDavid du Colombier 		/* TODO? if not excluded, add hash to thumb list */
355fe096d1aSDavid du Colombier 		free(c);
356fe096d1aSDavid du Colombier 		close(fd);
357d9306527SDavid du Colombier 		h = malloc(2*sizeof hash + 1);
358d9306527SDavid du Colombier 		if (h != nil) {
359d9306527SDavid du Colombier 			enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
360c6569576SDavid du Colombier 			// fprint(2, "x509 sha1=%s", h);
361fe096d1aSDavid du Colombier 			syslog(0, "smtp",
362fe096d1aSDavid du Colombier 		"remote cert. has bad thumbprint: x509 sha1=%s server=%q",
363fe096d1aSDavid du Colombier 				h, ddomain);
364d9306527SDavid du Colombier 			free(h);
365d9306527SDavid du Colombier 		}
366fe096d1aSDavid du Colombier 		return Giveup;		/* how to recover? TLS is started */
367d9306527SDavid du Colombier 	}
368d9306527SDavid du Colombier 	freeThumbprints(goodcerts);
369d9306527SDavid du Colombier 	Bterm(&bin);
370d9306527SDavid du Colombier 	Bterm(&bout);
371fe096d1aSDavid du Colombier 
372fe096d1aSDavid du Colombier 	/*
373fe096d1aSDavid du Colombier 	 * set up bin & bout to use the TLS fd, i/o upon which generates
374fe096d1aSDavid du Colombier 	 * i/o on the original, underlying fd.
375fe096d1aSDavid du Colombier 	 */
376d9306527SDavid du Colombier 	Binit(&bin, fd, OREAD);
377d9306527SDavid du Colombier 	fd = dup(fd, -1);
378d9306527SDavid du Colombier 	Binit(&bout, fd, OWRITE);
379fe096d1aSDavid du Colombier 
380fe096d1aSDavid du Colombier 	syslog(0, "smtp", "started TLS to %q", ddomain);
381d9306527SDavid du Colombier 	return(hello(me, 1));
382d9306527SDavid du Colombier }
383d9306527SDavid du Colombier 
384d9306527SDavid du Colombier static char *
3853cf081f0SDavid du Colombier doauth(char *methods)
386d9306527SDavid du Colombier {
3879863c128SDavid du Colombier 	char *buf, *base64;
388d9306527SDavid du Colombier 	int n;
3899863c128SDavid du Colombier 	DS ds;
3909863c128SDavid du Colombier 	UserPasswd *p;
391d9306527SDavid du Colombier 
3929863c128SDavid du Colombier 	dial_string_parse(ddomain, &ds);
3933cf081f0SDavid du Colombier 
394d9306527SDavid du Colombier 	if(user != nil)
395d9306527SDavid du Colombier 		p = auth_getuserpasswd(nil,
3969863c128SDavid du Colombier 	  	  "proto=pass service=smtp server=%q user=%q", ds.host, user);
397d9306527SDavid du Colombier 	else
398d9306527SDavid du Colombier 		p = auth_getuserpasswd(nil,
3999863c128SDavid du Colombier 	  	  "proto=pass service=smtp server=%q", ds.host);
400d9306527SDavid du Colombier 	if (p == nil)
401d9306527SDavid du Colombier 		return Giveup;
4023cf081f0SDavid du Colombier 
4033cf081f0SDavid du Colombier 	if (strstr(methods, "LOGIN")){
4043cf081f0SDavid du Colombier 		dBprint("AUTH LOGIN\r\n");
4053cf081f0SDavid du Colombier 		if (getreply() != 3)
4063cf081f0SDavid du Colombier 			return Retry;
4073cf081f0SDavid du Colombier 
4083cf081f0SDavid du Colombier 		n = strlen(p->user);
4093cf081f0SDavid du Colombier 		base64 = malloc(2*n);
4103cf081f0SDavid du Colombier 		if (base64 == nil)
4113cf081f0SDavid du Colombier 			return Retry;	/* Out of memory */
4123cf081f0SDavid du Colombier 		enc64(base64, 2*n, (uchar *)p->user, n);
4133cf081f0SDavid du Colombier 		dBprint("%s\r\n", base64);
4143cf081f0SDavid du Colombier 		if (getreply() != 3)
4153cf081f0SDavid du Colombier 			return Retry;
4163cf081f0SDavid du Colombier 
4173cf081f0SDavid du Colombier 		n = strlen(p->passwd);
4183cf081f0SDavid du Colombier 		base64 = malloc(2*n);
4193cf081f0SDavid du Colombier 		if (base64 == nil)
4203cf081f0SDavid du Colombier 			return Retry;	/* Out of memory */
4213cf081f0SDavid du Colombier 		enc64(base64, 2*n, (uchar *)p->passwd, n);
4223cf081f0SDavid du Colombier 		dBprint("%s\r\n", base64);
4233cf081f0SDavid du Colombier 		if (getreply() != 2)
4243cf081f0SDavid du Colombier 			return Retry;
4253cf081f0SDavid du Colombier 
4263cf081f0SDavid du Colombier 		free(base64);
4273cf081f0SDavid du Colombier 	}
4283cf081f0SDavid du Colombier 	else
4293cf081f0SDavid du Colombier 	if (strstr(methods, "PLAIN")){
430d9306527SDavid du Colombier 		n = strlen(p->user) + strlen(p->passwd) + 3;
431d9306527SDavid du Colombier 		buf = malloc(n);
432d9306527SDavid du Colombier 		base64 = malloc(2 * n);
433fe096d1aSDavid du Colombier 		if (buf == nil || base64 == nil) {
434d9306527SDavid du Colombier 			free(buf);
435d9306527SDavid du Colombier 			return Retry;	/* Out of memory */
436d9306527SDavid du Colombier 		}
437d9306527SDavid du Colombier 		snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd);
438d9306527SDavid du Colombier 		enc64(base64, 2 * n, (uchar *)buf, n - 1);
439d9306527SDavid du Colombier 		free(buf);
440d9306527SDavid du Colombier 		dBprint("AUTH PLAIN %s\r\n", base64);
441d9306527SDavid du Colombier 		free(base64);
442d9306527SDavid du Colombier 		if (getreply() != 2)
443d9306527SDavid du Colombier 			return Retry;
4443cf081f0SDavid du Colombier 	}
4453cf081f0SDavid du Colombier 	else
4463cf081f0SDavid du Colombier 		return "No supported AUTH method";
447d9306527SDavid du Colombier 	return(0);
448d9306527SDavid du Colombier }
449d9306527SDavid du Colombier 
450d9306527SDavid du Colombier char *
451d9306527SDavid du Colombier hello(char *me, int encrypted)
452d9306527SDavid du Colombier {
453d9306527SDavid du Colombier 	int ehlo;
454d9306527SDavid du Colombier 	String *r;
4553cf081f0SDavid du Colombier 	char *ret, *s, *t;
456d9306527SDavid du Colombier 
457db7ae703SDavid du Colombier 	if (!encrypted) {
458db7ae703SDavid du Colombier 		/*
459db7ae703SDavid du Colombier 		 * Verizon fails to print the smtp greeting banner when it
460db7ae703SDavid du Colombier 		 * answers a call.  Send a no-op in the hope of making it
461db7ae703SDavid du Colombier 		 * talk.
462db7ae703SDavid du Colombier 		 */
463ea813757SDavid du Colombier 		if (autistic) {
464db7ae703SDavid du Colombier 			dBprint("NOOP\r\n");
465ea813757SDavid du Colombier 			getreply();	/* consume the smtp greeting */
466ea813757SDavid du Colombier 			/* next reply will be response to noop */
467ea813757SDavid du Colombier 		}
4683e12c5d1SDavid du Colombier 		switch(getreply()){
4693e12c5d1SDavid du Colombier 		case 2:
4703e12c5d1SDavid du Colombier 			break;
4713e12c5d1SDavid du Colombier 		case 5:
4723e12c5d1SDavid du Colombier 			return Giveup;
4733e12c5d1SDavid du Colombier 		default:
4743e12c5d1SDavid du Colombier 			return Retry;
4753e12c5d1SDavid du Colombier 		}
476db7ae703SDavid du Colombier 	}
477d9306527SDavid du Colombier 
478d9306527SDavid du Colombier 	ehlo = 1;
479d9306527SDavid du Colombier   Again:
480d9306527SDavid du Colombier 	if(ehlo)
481d9306527SDavid du Colombier 		dBprint("EHLO %s\r\n", me);
482d9306527SDavid du Colombier 	else
4833e12c5d1SDavid du Colombier 		dBprint("HELO %s\r\n", me);
4843e12c5d1SDavid du Colombier 	switch (getreply()) {
4853e12c5d1SDavid du Colombier 	case 2:
4863e12c5d1SDavid du Colombier 		break;
4873e12c5d1SDavid du Colombier 	case 5:
488d9306527SDavid du Colombier 		if(ehlo){
489d9306527SDavid du Colombier 			ehlo = 0;
490d9306527SDavid du Colombier 			goto Again;
491d9306527SDavid du Colombier 		}
4923e12c5d1SDavid du Colombier 		return Giveup;
4933e12c5d1SDavid du Colombier 	default:
4943e12c5d1SDavid du Colombier 		return Retry;
4953e12c5d1SDavid du Colombier 	}
496d9306527SDavid du Colombier 	r = s_clone(reply);
497d9306527SDavid du Colombier 	if(r == nil)
498d9306527SDavid du Colombier 		return Retry;	/* Out of memory or couldn't get string */
499d9306527SDavid du Colombier 
500d9306527SDavid du Colombier 	/* Invariant: every line has a newline, a result of getcrlf() */
501d9306527SDavid du Colombier 	for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
502d9306527SDavid du Colombier 		*t = '\0';
503d9306527SDavid du Colombier 		for (t = s; *t != '\0'; t++)
504d9306527SDavid du Colombier 			*t = toupper(*t);
505d9306527SDavid du Colombier 		if(!encrypted && trysecure &&
506d9306527SDavid du Colombier 		    (strcmp(s, "250-STARTTLS") == 0 ||
507d9306527SDavid du Colombier 		     strcmp(s, "250 STARTTLS") == 0)){
508d9306527SDavid du Colombier 			s_free(r);
509db7ae703SDavid du Colombier 			return dotls(me);
510d9306527SDavid du Colombier 		}
511a8453668SDavid du Colombier 		if(tryauth && (encrypted || insecure) &&
512d9306527SDavid du Colombier 		    (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
5133cf081f0SDavid du Colombier 		     strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
5143cf081f0SDavid du Colombier 			ret = doauth(s + strlen("250 AUTH "));
515d9306527SDavid du Colombier 			s_free(r);
5163cf081f0SDavid du Colombier 			return ret;
517d9306527SDavid du Colombier 		}
518d9306527SDavid du Colombier 	}
519d9306527SDavid du Colombier 	s_free(r);
5203e12c5d1SDavid du Colombier 	return 0;
5213e12c5d1SDavid du Colombier }
5223e12c5d1SDavid du Colombier 
5233e12c5d1SDavid du Colombier /*
5243e12c5d1SDavid du Colombier  *  report sender to remote
5253e12c5d1SDavid du Colombier  */
5263e12c5d1SDavid du Colombier char *
5273e12c5d1SDavid du Colombier mailfrom(char *from)
5283e12c5d1SDavid du Colombier {
5297dd7cddfSDavid du Colombier 	if(!returnable(from))
5307dd7cddfSDavid du Colombier 		dBprint("MAIL FROM:<>\r\n");
5317dd7cddfSDavid du Colombier 	else
532219b2ee8SDavid du Colombier 	if(strchr(from, '@'))
5333e12c5d1SDavid du Colombier 		dBprint("MAIL FROM:<%s>\r\n", from);
534219b2ee8SDavid du Colombier 	else
535219b2ee8SDavid du Colombier 		dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
5363e12c5d1SDavid du Colombier 	switch(getreply()){
5373e12c5d1SDavid du Colombier 	case 2:
5383e12c5d1SDavid du Colombier 		break;
5393e12c5d1SDavid du Colombier 	case 5:
5403e12c5d1SDavid du Colombier 		return Giveup;
5413e12c5d1SDavid du Colombier 	default:
5423e12c5d1SDavid du Colombier 		return Retry;
5433e12c5d1SDavid du Colombier 	}
5443e12c5d1SDavid du Colombier 	return 0;
5453e12c5d1SDavid du Colombier }
5463e12c5d1SDavid du Colombier 
5473e12c5d1SDavid du Colombier /*
5483e12c5d1SDavid du Colombier  *  report a recipient to remote
5493e12c5d1SDavid du Colombier  */
5503e12c5d1SDavid du Colombier char *
5513e12c5d1SDavid du Colombier rcptto(char *to)
5523e12c5d1SDavid du Colombier {
553219b2ee8SDavid du Colombier 	String *s;
554219b2ee8SDavid du Colombier 
5557dd7cddfSDavid du Colombier 	s = unescapespecial(bangtoat(to));
5567dd7cddfSDavid du Colombier 	if(toline == 0)
557219b2ee8SDavid du Colombier 		toline = s_new();
5587dd7cddfSDavid du Colombier 	else
559219b2ee8SDavid du Colombier 		s_append(toline, ", ");
560219b2ee8SDavid du Colombier 	s_append(toline, s_to_c(s));
561219b2ee8SDavid du Colombier 	if(strchr(s_to_c(s), '@'))
562219b2ee8SDavid du Colombier 		dBprint("RCPT TO:<%s>\r\n", s_to_c(s));
563219b2ee8SDavid du Colombier 	else {
564219b2ee8SDavid du Colombier 		s_append(toline, "@");
565219b2ee8SDavid du Colombier 		s_append(toline, ddomain);
566219b2ee8SDavid du Colombier 		dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);
567219b2ee8SDavid du Colombier 	}
568a8453668SDavid du Colombier 	alarm(10*alarmscale);
5693e12c5d1SDavid du Colombier 	switch(getreply()){
5703e12c5d1SDavid du Colombier 	case 2:
5713e12c5d1SDavid du Colombier 		break;
5723e12c5d1SDavid du Colombier 	case 5:
5733e12c5d1SDavid du Colombier 		return Giveup;
5743e12c5d1SDavid du Colombier 	default:
5753e12c5d1SDavid du Colombier 		return Retry;
5763e12c5d1SDavid du Colombier 	}
5773e12c5d1SDavid du Colombier 	return 0;
5783e12c5d1SDavid du Colombier }
5793e12c5d1SDavid du Colombier 
5809a747e4fSDavid du Colombier static char hex[] = "0123456789abcdef";
5819a747e4fSDavid du Colombier 
5823e12c5d1SDavid du Colombier /*
5833e12c5d1SDavid du Colombier  *  send the damn thing
5843e12c5d1SDavid du Colombier  */
5853e12c5d1SDavid du Colombier char *
5867dd7cddfSDavid du Colombier data(String *from, Biobuf *b)
5873e12c5d1SDavid du Colombier {
5887dd7cddfSDavid du Colombier 	char *buf, *cp;
5899a747e4fSDavid du Colombier 	int i, n, nbytes, bufsize, eof, r;
5907dd7cddfSDavid du Colombier 	String *fromline;
5919a747e4fSDavid du Colombier 	char errmsg[Errlen];
5929a747e4fSDavid du Colombier 	char id[40];
5933e12c5d1SDavid du Colombier 
5943e12c5d1SDavid du Colombier 	/*
5957dd7cddfSDavid du Colombier 	 *  input the header.
5963e12c5d1SDavid du Colombier 	 */
5977dd7cddfSDavid du Colombier 
5987dd7cddfSDavid du Colombier 	buf = malloc(1);
5997dd7cddfSDavid du Colombier 	if(buf == 0){
6007dd7cddfSDavid du Colombier 		s_append(s_restart(reply), "out of memory");
6017dd7cddfSDavid du Colombier 		return Retry;
6027dd7cddfSDavid du Colombier 	}
6037dd7cddfSDavid du Colombier 	n = 0;
6043e12c5d1SDavid du Colombier 	eof = 0;
6057dd7cddfSDavid du Colombier 	for(;;){
6067dd7cddfSDavid du Colombier 		cp = Brdline(b, '\n');
6077dd7cddfSDavid du Colombier 		if(cp == nil){
6083e12c5d1SDavid du Colombier 			eof = 1;
6093e12c5d1SDavid du Colombier 			break;
6103e12c5d1SDavid du Colombier 		}
6117dd7cddfSDavid du Colombier 		nbytes = Blinelen(b);
6127dd7cddfSDavid du Colombier 		buf = realloc(buf, n+nbytes+1);
6137dd7cddfSDavid du Colombier 		if(buf == 0){
6147dd7cddfSDavid du Colombier 			s_append(s_restart(reply), "out of memory");
6157dd7cddfSDavid du Colombier 			return Retry;
6167dd7cddfSDavid du Colombier 		}
6177dd7cddfSDavid du Colombier 		strncpy(buf+n, cp, nbytes);
6187dd7cddfSDavid du Colombier 		n += nbytes;
6197dd7cddfSDavid du Colombier 		if(nbytes == 1)		/* end of header */
6207dd7cddfSDavid du Colombier 			break;
6213e12c5d1SDavid du Colombier 	}
6223e12c5d1SDavid du Colombier 	buf[n] = 0;
6237dd7cddfSDavid du Colombier 	bufsize = n;
6243e12c5d1SDavid du Colombier 
6253e12c5d1SDavid du Colombier 	/*
6263e12c5d1SDavid du Colombier 	 *  parse the header, turn all addresses into @ format
6273e12c5d1SDavid du Colombier 	 */
6289a747e4fSDavid du Colombier 	yyinit(buf, n);
6293e12c5d1SDavid du Colombier 	yyparse();
6303e12c5d1SDavid du Colombier 
6313e12c5d1SDavid du Colombier 	/*
6323e12c5d1SDavid du Colombier 	 *  print message observing '.' escapes and using \r\n for \n
6333e12c5d1SDavid du Colombier 	 */
634a8453668SDavid du Colombier 	alarm(20*alarmscale);
6353e12c5d1SDavid du Colombier 	if(!filter){
6363e12c5d1SDavid du Colombier 		dBprint("DATA\r\n");
6373e12c5d1SDavid du Colombier 		switch(getreply()){
6383e12c5d1SDavid du Colombier 		case 3:
6393e12c5d1SDavid du Colombier 			break;
6403e12c5d1SDavid du Colombier 		case 5:
6417dd7cddfSDavid du Colombier 			free(buf);
6423e12c5d1SDavid du Colombier 			return Giveup;
6433e12c5d1SDavid du Colombier 		default:
6447dd7cddfSDavid du Colombier 			free(buf);
6453e12c5d1SDavid du Colombier 			return Retry;
6463e12c5d1SDavid du Colombier 		}
6473e12c5d1SDavid du Colombier 	}
6483e12c5d1SDavid du Colombier 	/*
6499a747e4fSDavid du Colombier 	 *  send header.  add a message-id, a sender, and a date if there
6503e12c5d1SDavid du Colombier 	 *  isn't one
6513e12c5d1SDavid du Colombier 	 */
6527dd7cddfSDavid du Colombier 	nbytes = 0;
6537dd7cddfSDavid du Colombier 	fromline = convertheader(from);
654bd389b36SDavid du Colombier 	uneaten = buf;
6559a747e4fSDavid du Colombier 
656d9306527SDavid du Colombier 	srand(truerand());
6579a747e4fSDavid du Colombier 	if(messageid == 0){
6589a747e4fSDavid du Colombier 		for(i=0; i<16; i++){
659d9306527SDavid du Colombier 			r = rand()&0xFF;
6609a747e4fSDavid du Colombier 			id[2*i] = hex[r&0xF];
6619a747e4fSDavid du Colombier 			id[2*i+1] = hex[(r>>4)&0xF];
6629a747e4fSDavid du Colombier 		}
6639a747e4fSDavid du Colombier 		id[2*i] = '\0';
6649a747e4fSDavid du Colombier 		nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain);
6659a747e4fSDavid du Colombier 		if(debug)
6669a747e4fSDavid du Colombier 			Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain);
6679a747e4fSDavid du Colombier 	}
6689a747e4fSDavid du Colombier 
6697dd7cddfSDavid du Colombier 	if(originator==0){
6707dd7cddfSDavid du Colombier 		nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline));
6717dd7cddfSDavid du Colombier 		if(debug)
6727dd7cddfSDavid du Colombier 			Bprint(&berr, "From: %s\r\n", s_to_c(fromline));
6737dd7cddfSDavid du Colombier 	}
6747dd7cddfSDavid du Colombier 	s_free(fromline);
6757dd7cddfSDavid du Colombier 
676219b2ee8SDavid du Colombier 	if(destination == 0 && toline)
6777dd7cddfSDavid du Colombier 		if(*s_to_c(toline) == '@'){	/* route addr */
6787dd7cddfSDavid du Colombier 			nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline));
6797dd7cddfSDavid du Colombier 			if(debug)
6807dd7cddfSDavid du Colombier 				Bprint(&berr, "To: <%s>\r\n", s_to_c(toline));
6817dd7cddfSDavid du Colombier 		} else {
6827dd7cddfSDavid du Colombier 			nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline));
6837dd7cddfSDavid du Colombier 			if(debug)
6847dd7cddfSDavid du Colombier 				Bprint(&berr, "To: %s\r\n", s_to_c(toline));
6857dd7cddfSDavid du Colombier 		}
6867dd7cddfSDavid du Colombier 
6873e12c5d1SDavid du Colombier 	if(date==0 && udate)
6887dd7cddfSDavid du Colombier 		nbytes += printdate(udate);
689bd389b36SDavid du Colombier 	if (usys)
690bd389b36SDavid du Colombier 		uneaten = usys->end + 1;
6917dd7cddfSDavid du Colombier 	nbytes += printheader();
692bd389b36SDavid du Colombier 	if (*uneaten != '\n')
693bd389b36SDavid du Colombier 		putcrnl("\n", 1);
6943e12c5d1SDavid du Colombier 
6953e12c5d1SDavid du Colombier 	/*
6963e12c5d1SDavid du Colombier 	 *  send body
6973e12c5d1SDavid du Colombier 	 */
6987dd7cddfSDavid du Colombier 
699bd389b36SDavid du Colombier 	putcrnl(uneaten, buf+n - uneaten);
7007dd7cddfSDavid du Colombier 	nbytes += buf+n - uneaten;
7017dd7cddfSDavid du Colombier 	if(eof == 0){
702219b2ee8SDavid du Colombier 		for(;;){
7037dd7cddfSDavid du Colombier 			n = Bread(b, buf, bufsize);
704219b2ee8SDavid du Colombier 			if(n < 0){
7059a747e4fSDavid du Colombier 				rerrstr(errmsg, sizeof(errmsg));
7067dd7cddfSDavid du Colombier 				s_append(s_restart(reply), errmsg);
7077dd7cddfSDavid du Colombier 				free(buf);
7087dd7cddfSDavid du Colombier 				return Retry;
709219b2ee8SDavid du Colombier 			}
710219b2ee8SDavid du Colombier 			if(n == 0)
711219b2ee8SDavid du Colombier 				break;
712a8453668SDavid du Colombier 			alarm(10*alarmscale);
7133e12c5d1SDavid du Colombier 			putcrnl(buf, n);
7147dd7cddfSDavid du Colombier 			nbytes += n;
715219b2ee8SDavid du Colombier 		}
7167dd7cddfSDavid du Colombier 	}
7177dd7cddfSDavid du Colombier 	free(buf);
7183e12c5d1SDavid du Colombier 	if(!filter){
7193e12c5d1SDavid du Colombier 		if(last != '\n')
7203e12c5d1SDavid du Colombier 			dBprint("\r\n.\r\n");
7213e12c5d1SDavid du Colombier 		else
7223e12c5d1SDavid du Colombier 			dBprint(".\r\n");
723a8453668SDavid du Colombier 		alarm(10*alarmscale);
7243e12c5d1SDavid du Colombier 		switch(getreply()){
7253e12c5d1SDavid du Colombier 		case 2:
7263e12c5d1SDavid du Colombier 			break;
7273e12c5d1SDavid du Colombier 		case 5:
7283e12c5d1SDavid du Colombier 			return Giveup;
7297dd7cddfSDavid du Colombier 		default:
7307dd7cddfSDavid du Colombier 			return Retry;
7313e12c5d1SDavid du Colombier 		}
7327dd7cddfSDavid du Colombier 		syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from),
7337dd7cddfSDavid du Colombier 				nbytes, s_to_c(toline));/**/
7343e12c5d1SDavid du Colombier 	}
7353e12c5d1SDavid du Colombier 	return 0;
7363e12c5d1SDavid du Colombier }
7373e12c5d1SDavid du Colombier 
7383e12c5d1SDavid du Colombier /*
7393e12c5d1SDavid du Colombier  *  we're leaving
7403e12c5d1SDavid du Colombier  */
7413e12c5d1SDavid du Colombier void
7427dd7cddfSDavid du Colombier quit(char *rv)
7433e12c5d1SDavid du Colombier {
7447dd7cddfSDavid du Colombier 		/* 60 minutes to quit */
7457dd7cddfSDavid du Colombier 	quitting = 1;
7467dd7cddfSDavid du Colombier 	quitrv = rv;
747a8453668SDavid du Colombier 	alarm(60*alarmscale);
7483e12c5d1SDavid du Colombier 	dBprint("QUIT\r\n");
7493e12c5d1SDavid du Colombier 	getreply();
7507dd7cddfSDavid du Colombier 	Bterm(&bout);
7517dd7cddfSDavid du Colombier 	Bterm(&bfile);
7523e12c5d1SDavid du Colombier }
7533e12c5d1SDavid du Colombier 
7543e12c5d1SDavid du Colombier /*
7553e12c5d1SDavid du Colombier  *  read a reply into a string, return the reply code
7563e12c5d1SDavid du Colombier  */
7573e12c5d1SDavid du Colombier int
7583e12c5d1SDavid du Colombier getreply(void)
7593e12c5d1SDavid du Colombier {
7603e12c5d1SDavid du Colombier 	char *line;
7613e12c5d1SDavid du Colombier 	int rv;
7623e12c5d1SDavid du Colombier 
7633e12c5d1SDavid du Colombier 	reply = s_reset(reply);
7643e12c5d1SDavid du Colombier 	for(;;){
7653e12c5d1SDavid du Colombier 		line = getcrnl(reply);
766499069deSDavid du Colombier 		if(debug)
767499069deSDavid du Colombier 			Bflush(&berr);
7683e12c5d1SDavid du Colombier 		if(line == 0)
7693e12c5d1SDavid du Colombier 			return -1;
7703e12c5d1SDavid du Colombier 		if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]))
7713e12c5d1SDavid du Colombier 			return -1;
7723e12c5d1SDavid du Colombier 		if(line[3] != '-')
7733e12c5d1SDavid du Colombier 			break;
7743e12c5d1SDavid du Colombier 	}
7757dd7cddfSDavid du Colombier 	if(debug)
7767dd7cddfSDavid du Colombier 		Bflush(&berr);
7773e12c5d1SDavid du Colombier 	rv = atoi(line)/100;
7783e12c5d1SDavid du Colombier 	return rv;
7793e12c5d1SDavid du Colombier }
7807dd7cddfSDavid du Colombier void
7817dd7cddfSDavid du Colombier addhostdom(String *buf, char *host)
7827dd7cddfSDavid du Colombier {
7837dd7cddfSDavid du Colombier 	s_append(buf, "@");
7847dd7cddfSDavid du Colombier 	s_append(buf, host);
7857dd7cddfSDavid du Colombier }
7863e12c5d1SDavid du Colombier 
7873e12c5d1SDavid du Colombier /*
7883e12c5d1SDavid du Colombier  *	Convert from `bang' to `source routing' format.
7893e12c5d1SDavid du Colombier  *
7903e12c5d1SDavid du Colombier  *	   a.x.y!b.p.o!c!d ->	@a.x.y:c!d@b.p.o
7913e12c5d1SDavid du Colombier  */
7923e12c5d1SDavid du Colombier String *
793219b2ee8SDavid du Colombier bangtoat(char *addr)
7943e12c5d1SDavid du Colombier {
7953e12c5d1SDavid du Colombier 	String *buf;
7963e12c5d1SDavid du Colombier 	register int i;
7973e12c5d1SDavid du Colombier 	int j, d;
7983e12c5d1SDavid du Colombier 	char *field[128];
7993e12c5d1SDavid du Colombier 
8003e12c5d1SDavid du Colombier 	/* parse the '!' format address */
8013e12c5d1SDavid du Colombier 	buf = s_new();
8023e12c5d1SDavid du Colombier 	for(i = 0; addr; i++){
8033e12c5d1SDavid du Colombier 		field[i] = addr;
8043e12c5d1SDavid du Colombier 		addr = strchr(addr, '!');
8053e12c5d1SDavid du Colombier 		if(addr)
8063e12c5d1SDavid du Colombier 			*addr++ = 0;
8073e12c5d1SDavid du Colombier 	}
8083e12c5d1SDavid du Colombier 	if (i==1) {
8093e12c5d1SDavid du Colombier 		s_append(buf, field[0]);
8103e12c5d1SDavid du Colombier 		return buf;
8113e12c5d1SDavid du Colombier 	}
8123e12c5d1SDavid du Colombier 
8133e12c5d1SDavid du Colombier 	/*
8143e12c5d1SDavid du Colombier 	 *  count leading domain fields (non-domains don't count)
8153e12c5d1SDavid du Colombier 	 */
8167dd7cddfSDavid du Colombier 	for(d = 0; d<i-1; d++)
8173e12c5d1SDavid du Colombier 		if(strchr(field[d], '.')==0)
8183e12c5d1SDavid du Colombier 			break;
8193e12c5d1SDavid du Colombier 	/*
8203e12c5d1SDavid du Colombier 	 *  if there are more than 1 leading domain elements,
8213e12c5d1SDavid du Colombier 	 *  put them in as source routing
8223e12c5d1SDavid du Colombier 	 */
8233e12c5d1SDavid du Colombier 	if(d > 1){
824219b2ee8SDavid du Colombier 		addhostdom(buf, field[0]);
8253e12c5d1SDavid du Colombier 		for(j=1; j<d-1; j++){
8263e12c5d1SDavid du Colombier 			s_append(buf, ",");
8273e12c5d1SDavid du Colombier 			s_append(buf, "@");
8283e12c5d1SDavid du Colombier 			s_append(buf, field[j]);
8293e12c5d1SDavid du Colombier 		}
8303e12c5d1SDavid du Colombier 		s_append(buf, ":");
8313e12c5d1SDavid du Colombier 	}
8323e12c5d1SDavid du Colombier 
8333e12c5d1SDavid du Colombier 	/*
8343e12c5d1SDavid du Colombier 	 *  throw in the non-domain elements separated by '!'s
8353e12c5d1SDavid du Colombier 	 */
8363e12c5d1SDavid du Colombier 	s_append(buf, field[d]);
8373e12c5d1SDavid du Colombier 	for(j=d+1; j<=i-1; j++) {
8383e12c5d1SDavid du Colombier 		s_append(buf, "!");
8393e12c5d1SDavid du Colombier 		s_append(buf, field[j]);
8403e12c5d1SDavid du Colombier 	}
8413e12c5d1SDavid du Colombier 	if(d)
842219b2ee8SDavid du Colombier 		addhostdom(buf, field[d-1]);
8433e12c5d1SDavid du Colombier 	return buf;
8443e12c5d1SDavid du Colombier }
8453e12c5d1SDavid du Colombier 
8463e12c5d1SDavid du Colombier /*
8473e12c5d1SDavid du Colombier  *  convert header addresses to @ format.
8483e12c5d1SDavid du Colombier  *  if the address is a source address, and a domain is specified,
8493e12c5d1SDavid du Colombier  *  make sure it falls in the domain.
8503e12c5d1SDavid du Colombier  */
8517dd7cddfSDavid du Colombier String*
852219b2ee8SDavid du Colombier convertheader(String *from)
8533e12c5d1SDavid du Colombier {
8543e12c5d1SDavid du Colombier 	Field *f;
8557dd7cddfSDavid du Colombier 	Node *p, *lastp;
8563e12c5d1SDavid du Colombier 	String *a;
8573e12c5d1SDavid du Colombier 
8587dd7cddfSDavid du Colombier 	if(!returnable(s_to_c(from))){
8597dd7cddfSDavid du Colombier 		from = s_new();
8607dd7cddfSDavid du Colombier 		s_append(from, "Postmaster");
8617dd7cddfSDavid du Colombier 		addhostdom(from, hostdomain);
8627dd7cddfSDavid du Colombier 	} else
8637dd7cddfSDavid du Colombier 	if(strchr(s_to_c(from), '@') == 0){
8647dd7cddfSDavid du Colombier 		a = username(from);
8657dd7cddfSDavid du Colombier 		if(a) {
8667dd7cddfSDavid du Colombier 			s_append(a, " <");
8677dd7cddfSDavid du Colombier 			s_append(a, s_to_c(from));
8687dd7cddfSDavid du Colombier 			addhostdom(a, hostdomain);
8697dd7cddfSDavid du Colombier 			s_append(a, ">");
8707dd7cddfSDavid du Colombier 			from = a;
8717dd7cddfSDavid du Colombier 		} else {
8727dd7cddfSDavid du Colombier 			from = s_copy(s_to_c(from));
8737dd7cddfSDavid du Colombier 			addhostdom(from, hostdomain);
874219b2ee8SDavid du Colombier 		}
8757dd7cddfSDavid du Colombier 	} else
8767dd7cddfSDavid du Colombier 		from = s_copy(s_to_c(from));
8773e12c5d1SDavid du Colombier 	for(f = firstfield; f; f = f->next){
8787dd7cddfSDavid du Colombier 		lastp = 0;
8797dd7cddfSDavid du Colombier 		for(p = f->node; p; lastp = p, p = p->next){
8803e12c5d1SDavid du Colombier 			if(!p->addr)
8813e12c5d1SDavid du Colombier 				continue;
882219b2ee8SDavid du Colombier 			a = bangtoat(s_to_c(p->s));
8833e12c5d1SDavid du Colombier 			s_free(p->s);
8847dd7cddfSDavid du Colombier 			if(strchr(s_to_c(a), '@') == 0)
8857dd7cddfSDavid du Colombier 				addhostdom(a, hostdomain);
8867dd7cddfSDavid du Colombier 			else if(*s_to_c(a) == '@')
8877dd7cddfSDavid du Colombier 				a = fixrouteaddr(a, p->next, lastp);
8883e12c5d1SDavid du Colombier 			p->s = a;
8893e12c5d1SDavid du Colombier 		}
8903e12c5d1SDavid du Colombier 	}
8917dd7cddfSDavid du Colombier 	return from;
8927dd7cddfSDavid du Colombier }
8937dd7cddfSDavid du Colombier /*
8947dd7cddfSDavid du Colombier  *	ensure route addr has brackets around it
8957dd7cddfSDavid du Colombier  */
8967dd7cddfSDavid du Colombier String*
8977dd7cddfSDavid du Colombier fixrouteaddr(String *raddr, Node *next, Node *last)
8987dd7cddfSDavid du Colombier {
8997dd7cddfSDavid du Colombier 	String *a;
9007dd7cddfSDavid du Colombier 
9017dd7cddfSDavid du Colombier 	if(last && last->c == '<' && next && next->c == '>')
9027dd7cddfSDavid du Colombier 		return raddr;			/* properly formed already */
9037dd7cddfSDavid du Colombier 
9047dd7cddfSDavid du Colombier 	a = s_new();
9057dd7cddfSDavid du Colombier 	s_append(a, "<");
9067dd7cddfSDavid du Colombier 	s_append(a, s_to_c(raddr));
9077dd7cddfSDavid du Colombier 	s_append(a, ">");
9087dd7cddfSDavid du Colombier 	s_free(raddr);
9097dd7cddfSDavid du Colombier 	return a;
9103e12c5d1SDavid du Colombier }
9113e12c5d1SDavid du Colombier 
9123e12c5d1SDavid du Colombier /*
9133e12c5d1SDavid du Colombier  *  print out the parsed header
9143e12c5d1SDavid du Colombier  */
9157dd7cddfSDavid du Colombier int
9163e12c5d1SDavid du Colombier printheader(void)
9173e12c5d1SDavid du Colombier {
9187dd7cddfSDavid du Colombier 	int n, len;
9193e12c5d1SDavid du Colombier 	Field *f;
9203e12c5d1SDavid du Colombier 	Node *p;
9213e12c5d1SDavid du Colombier 	char *cp;
9223e12c5d1SDavid du Colombier 	char c[1];
9233e12c5d1SDavid du Colombier 
9247dd7cddfSDavid du Colombier 	n = 0;
9253e12c5d1SDavid du Colombier 	for(f = firstfield; f; f = f->next){
9263e12c5d1SDavid du Colombier 		for(p = f->node; p; p = p->next){
9273e12c5d1SDavid du Colombier 			if(p->s)
9287dd7cddfSDavid du Colombier 				n += dBprint("%s", s_to_c(p->s));
9293e12c5d1SDavid du Colombier 			else {
9303e12c5d1SDavid du Colombier 				c[0] = p->c;
9313e12c5d1SDavid du Colombier 				putcrnl(c, 1);
9327dd7cddfSDavid du Colombier 				n++;
9333e12c5d1SDavid du Colombier 			}
9343e12c5d1SDavid du Colombier 			if(p->white){
9353e12c5d1SDavid du Colombier 				cp = s_to_c(p->white);
9367dd7cddfSDavid du Colombier 				len = strlen(cp);
9377dd7cddfSDavid du Colombier 				putcrnl(cp, len);
9387dd7cddfSDavid du Colombier 				n += len;
9393e12c5d1SDavid du Colombier 			}
940bd389b36SDavid du Colombier 			uneaten = p->end;
9413e12c5d1SDavid du Colombier 		}
9423e12c5d1SDavid du Colombier 		putcrnl("\n", 1);
9437dd7cddfSDavid du Colombier 		n++;
944bd389b36SDavid du Colombier 		uneaten++;		/* skip newline */
9453e12c5d1SDavid du Colombier 	}
9467dd7cddfSDavid du Colombier 	return n;
9473e12c5d1SDavid du Colombier }
9483e12c5d1SDavid du Colombier 
9493e12c5d1SDavid du Colombier /*
9503e12c5d1SDavid du Colombier  *  add a domain onto an name, return the new name
9513e12c5d1SDavid du Colombier  */
9523e12c5d1SDavid du Colombier char *
9533e12c5d1SDavid du Colombier domainify(char *name, char *domain)
9543e12c5d1SDavid du Colombier {
9553e12c5d1SDavid du Colombier 	static String *s;
9567dd7cddfSDavid du Colombier 	char *p;
9573e12c5d1SDavid du Colombier 
9583e12c5d1SDavid du Colombier 	if(domain==0 || strchr(name, '.')!=0)
9593e12c5d1SDavid du Colombier 		return name;
9603e12c5d1SDavid du Colombier 
9613e12c5d1SDavid du Colombier 	s = s_reset(s);
9623e12c5d1SDavid du Colombier 	s_append(s, name);
9637dd7cddfSDavid du Colombier 	p = strchr(domain, '.');
9647dd7cddfSDavid du Colombier 	if(p == 0){
965219b2ee8SDavid du Colombier 		s_append(s, ".");
9667dd7cddfSDavid du Colombier 		p = domain;
9677dd7cddfSDavid du Colombier 	}
9687dd7cddfSDavid du Colombier 	s_append(s, p);
9693e12c5d1SDavid du Colombier 	return s_to_c(s);
9703e12c5d1SDavid du Colombier }
9713e12c5d1SDavid du Colombier 
9723e12c5d1SDavid du Colombier /*
9733e12c5d1SDavid du Colombier  *  print message observing '.' escapes and using \r\n for \n
9743e12c5d1SDavid du Colombier  */
9753e12c5d1SDavid du Colombier void
9763e12c5d1SDavid du Colombier putcrnl(char *cp, int n)
9773e12c5d1SDavid du Colombier {
9783e12c5d1SDavid du Colombier 	int c;
9793e12c5d1SDavid du Colombier 
9803e12c5d1SDavid du Colombier 	for(; n; n--, cp++){
9813e12c5d1SDavid du Colombier 		c = *cp;
9823e12c5d1SDavid du Colombier 		if(c == '\n')
9837dd7cddfSDavid du Colombier 			dBputc('\r');
9843e12c5d1SDavid du Colombier 		else if(c == '.' && last=='\n')
9857dd7cddfSDavid du Colombier 			dBputc('.');
9867dd7cddfSDavid du Colombier 		dBputc(c);
9873e12c5d1SDavid du Colombier 		last = c;
9883e12c5d1SDavid du Colombier 	}
9893e12c5d1SDavid du Colombier }
9903e12c5d1SDavid du Colombier 
9913e12c5d1SDavid du Colombier /*
9923e12c5d1SDavid du Colombier  *  Get a line including a crnl into a string.  Convert crnl into nl.
9933e12c5d1SDavid du Colombier  */
9943e12c5d1SDavid du Colombier char *
9953e12c5d1SDavid du Colombier getcrnl(String *s)
9963e12c5d1SDavid du Colombier {
9973e12c5d1SDavid du Colombier 	int c;
9983e12c5d1SDavid du Colombier 	int count;
9993e12c5d1SDavid du Colombier 
10003e12c5d1SDavid du Colombier 	count = 0;
10013e12c5d1SDavid du Colombier 	for(;;){
10023e12c5d1SDavid du Colombier 		c = Bgetc(&bin);
10033e12c5d1SDavid du Colombier 		if(debug)
10043e12c5d1SDavid du Colombier 			Bputc(&berr, c);
10053e12c5d1SDavid du Colombier 		switch(c){
10063e12c5d1SDavid du Colombier 		case -1:
10077dd7cddfSDavid du Colombier 			s_append(s, "connection closed unexpectedly by remote system");
10083e12c5d1SDavid du Colombier 			s_terminate(s);
10093e12c5d1SDavid du Colombier 			return 0;
10103e12c5d1SDavid du Colombier 		case '\r':
10113e12c5d1SDavid du Colombier 			c = Bgetc(&bin);
10123e12c5d1SDavid du Colombier 			if(c == '\n'){
1013499069deSDavid du Colombier 		case '\n':
10143e12c5d1SDavid du Colombier 				s_putc(s, c);
10153e12c5d1SDavid du Colombier 				if(debug)
10163e12c5d1SDavid du Colombier 					Bputc(&berr, c);
10173e12c5d1SDavid du Colombier 				count++;
10183e12c5d1SDavid du Colombier 				s_terminate(s);
10193e12c5d1SDavid du Colombier 				return s->ptr - count;
10203e12c5d1SDavid du Colombier 			}
10213e12c5d1SDavid du Colombier 			Bungetc(&bin);
10223e12c5d1SDavid du Colombier 			s_putc(s, '\r');
10233e12c5d1SDavid du Colombier 			if(debug)
10243e12c5d1SDavid du Colombier 				Bputc(&berr, '\r');
10253e12c5d1SDavid du Colombier 			count++;
10263e12c5d1SDavid du Colombier 			break;
10273e12c5d1SDavid du Colombier 		default:
10283e12c5d1SDavid du Colombier 			s_putc(s, c);
10293e12c5d1SDavid du Colombier 			count++;
10303e12c5d1SDavid du Colombier 			break;
10313e12c5d1SDavid du Colombier 		}
10323e12c5d1SDavid du Colombier 	}
10333e12c5d1SDavid du Colombier }
10343e12c5d1SDavid du Colombier 
10353e12c5d1SDavid du Colombier /*
10363e12c5d1SDavid du Colombier  *  print out a parsed date
10373e12c5d1SDavid du Colombier  */
10387dd7cddfSDavid du Colombier int
10393e12c5d1SDavid du Colombier printdate(Node *p)
10403e12c5d1SDavid du Colombier {
10417dd7cddfSDavid du Colombier 	int n, sep = 0;
10423e12c5d1SDavid du Colombier 
10437dd7cddfSDavid du Colombier 	n = dBprint("Date: %s,", s_to_c(p->s));
10443e12c5d1SDavid du Colombier 	for(p = p->next; p; p = p->next){
10453e12c5d1SDavid du Colombier 		if(p->s){
10467dd7cddfSDavid du Colombier 			if(sep == 0) {
10477dd7cddfSDavid du Colombier 				dBputc(' ');
10487dd7cddfSDavid du Colombier 				n++;
10497dd7cddfSDavid du Colombier 			}
1050bd389b36SDavid du Colombier 			if (p->next)
10517dd7cddfSDavid du Colombier 				n += dBprint("%s", s_to_c(p->s));
1052bd389b36SDavid du Colombier 			else
10537dd7cddfSDavid du Colombier 				n += dBprint("%s", rewritezone(s_to_c(p->s)));
10543e12c5d1SDavid du Colombier 			sep = 0;
10553e12c5d1SDavid du Colombier 		} else {
10567dd7cddfSDavid du Colombier 			dBputc(p->c);
10577dd7cddfSDavid du Colombier 			n++;
10583e12c5d1SDavid du Colombier 			sep = 1;
10593e12c5d1SDavid du Colombier 		}
10603e12c5d1SDavid du Colombier 	}
10617dd7cddfSDavid du Colombier 	n += dBprint("\r\n");
10627dd7cddfSDavid du Colombier 	return n;
10633e12c5d1SDavid du Colombier }
10643e12c5d1SDavid du Colombier 
1065bd389b36SDavid du Colombier char *
1066bd389b36SDavid du Colombier rewritezone(char *z)
1067bd389b36SDavid du Colombier {
10687dd7cddfSDavid du Colombier 	int mindiff;
10697dd7cddfSDavid du Colombier 	char s;
10707dd7cddfSDavid du Colombier 	Tm *tm;
10717dd7cddfSDavid du Colombier 	static char x[7];
1072bd389b36SDavid du Colombier 
10737dd7cddfSDavid du Colombier 	tm = localtime(time(0));
10747dd7cddfSDavid du Colombier 	mindiff = tm->tzoff/60;
1075bd389b36SDavid du Colombier 
10767dd7cddfSDavid du Colombier 	/* if not in my timezone, don't change anything */
10777dd7cddfSDavid du Colombier 	if(strcmp(tm->zone, z) != 0)
1078bd389b36SDavid du Colombier 		return z;
10797dd7cddfSDavid du Colombier 
10807dd7cddfSDavid du Colombier 	if(mindiff < 0){
10817dd7cddfSDavid du Colombier 		s = '-';
10827dd7cddfSDavid du Colombier 		mindiff = -mindiff;
10837dd7cddfSDavid du Colombier 	} else
10847dd7cddfSDavid du Colombier 		s = '+';
10857dd7cddfSDavid du Colombier 
10867dd7cddfSDavid du Colombier 	sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
10877dd7cddfSDavid du Colombier 	return x;
1088bd389b36SDavid du Colombier }
1089bd389b36SDavid du Colombier 
10903e12c5d1SDavid du Colombier /*
10913e12c5d1SDavid du Colombier  *  stolen from libc/port/print.c
10923e12c5d1SDavid du Colombier  */
10933e12c5d1SDavid du Colombier #define	SIZE	4096
10943e12c5d1SDavid du Colombier int
10953e12c5d1SDavid du Colombier dBprint(char *fmt, ...)
10963e12c5d1SDavid du Colombier {
10973e12c5d1SDavid du Colombier 	char buf[SIZE], *out;
10987dd7cddfSDavid du Colombier 	va_list arg;
10993e12c5d1SDavid du Colombier 	int n;
11003e12c5d1SDavid du Colombier 
11017dd7cddfSDavid du Colombier 	va_start(arg, fmt);
11029a747e4fSDavid du Colombier 	out = vseprint(buf, buf+SIZE, fmt, arg);
11037dd7cddfSDavid du Colombier 	va_end(arg);
11043e12c5d1SDavid du Colombier 	if(debug){
11053e12c5d1SDavid du Colombier 		Bwrite(&berr, buf, (long)(out-buf));
11063e12c5d1SDavid du Colombier 		Bflush(&berr);
11073e12c5d1SDavid du Colombier 	}
11083e12c5d1SDavid du Colombier 	n = Bwrite(&bout, buf, (long)(out-buf));
11093e12c5d1SDavid du Colombier 	Bflush(&bout);
11103e12c5d1SDavid du Colombier 	return n;
11113e12c5d1SDavid du Colombier }
11127dd7cddfSDavid du Colombier 
11137dd7cddfSDavid du Colombier int
11147dd7cddfSDavid du Colombier dBputc(int x)
11157dd7cddfSDavid du Colombier {
11167dd7cddfSDavid du Colombier 	if(debug)
11177dd7cddfSDavid du Colombier 		Bputc(&berr, x);
11187dd7cddfSDavid du Colombier 	return Bputc(&bout, x);
11197dd7cddfSDavid du Colombier }
1120