xref: /freebsd/contrib/sendmail/src/usersmtp.c (revision 1ed4a948e1e90356ea79bab18495d577540c7165)
1 /*
2  * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13 
14 #include <sendmail.h>
15 
16 #ifndef lint
17 # if SMTP
18 static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.24 2001/02/21 00:59:09 gshapiro Exp $ (with SMTP)";
19 # else /* SMTP */
20 static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.24 2001/02/21 00:59:09 gshapiro Exp $ (without SMTP)";
21 # endif /* SMTP */
22 #endif /* ! lint */
23 
24 #include <sysexits.h>
25 
26 #if SMTP
27 
28 
29 static void	datatimeout __P((void));
30 static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
31 static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
32 
33 /*
34 **  USERSMTP -- run SMTP protocol from the user end.
35 **
36 **	This protocol is described in RFC821.
37 */
38 
39 # define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
40 # define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
41 # define SMTPCLOSING	421			/* "Service Shutting Down" */
42 
43 #define ENHSCN(e, d)	(e) == NULL ? (d) : newstr(e)
44 
45 static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
46 static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
47 static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
48 /*
49 **  SMTPINIT -- initialize SMTP.
50 **
51 **	Opens the connection and sends the initial protocol.
52 **
53 **	Parameters:
54 **		m -- mailer to create connection to.
55 **		mci -- the mailer connection info.
56 **		e -- the envelope.
57 **		onlyhelo -- send only helo command?
58 **
59 **	Returns:
60 **		none.
61 **
62 **	Side Effects:
63 **		creates connection and sends initial protocol.
64 */
65 
66 void
67 smtpinit(m, mci, e, onlyhelo)
68 	MAILER *m;
69 	register MCI *mci;
70 	ENVELOPE *e;
71 	bool onlyhelo;
72 {
73 	register int r;
74 	register char *p;
75 	register char *hn;
76 	char *enhsc;
77 
78 	enhsc = NULL;
79 	if (tTd(18, 1))
80 	{
81 		dprintf("smtpinit ");
82 		mci_dump(mci, FALSE);
83 	}
84 
85 	/*
86 	**  Open the connection to the mailer.
87 	*/
88 
89 	SmtpError[0] = '\0';
90 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
91 	if (CurHostName == NULL)
92 		CurHostName = MyHostName;
93 	SmtpNeedIntro = TRUE;
94 	switch (mci->mci_state)
95 	{
96 	  case MCIS_ACTIVE:
97 		/* need to clear old information */
98 		smtprset(m, mci, e);
99 		/* FALLTHROUGH */
100 
101 	  case MCIS_OPEN:
102 		if (!onlyhelo)
103 			return;
104 		break;
105 
106 	  case MCIS_ERROR:
107 	  case MCIS_QUITING:
108 	  case MCIS_SSD:
109 		/* shouldn't happen */
110 		smtpquit(m, mci, e);
111 		/* FALLTHROUGH */
112 
113 	  case MCIS_CLOSED:
114 		syserr("451 4.4.0 smtpinit: state CLOSED");
115 		return;
116 
117 	  case MCIS_OPENING:
118 		break;
119 	}
120 	if (onlyhelo)
121 		goto helo;
122 
123 	mci->mci_state = MCIS_OPENING;
124 
125 	/*
126 	**  Get the greeting message.
127 	**	This should appear spontaneously.  Give it five minutes to
128 	**	happen.
129 	*/
130 
131 	SmtpPhase = mci->mci_phase = "client greeting";
132 	sm_setproctitle(TRUE, e, "%s %s: %s",
133 			qid_printname(e), CurHostName, mci->mci_phase);
134 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
135 	if (r < 0)
136 		goto tempfail1;
137 	if (REPLYTYPE(r) == 4)
138 		goto tempfail2;
139 	if (REPLYTYPE(r) != 2)
140 		goto unavailable;
141 
142 	/*
143 	**  Send the HELO command.
144 	**	My mother taught me to always introduce myself.
145 	*/
146 
147 helo:
148 	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
149 		mci->mci_flags |= MCIF_ESMTP;
150 	hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
151 
152 tryhelo:
153 	if (bitnset(M_LMTP, m->m_flags))
154 	{
155 		smtpmessage("LHLO %s", m, mci, hn);
156 		SmtpPhase = mci->mci_phase = "client LHLO";
157 	}
158 	else if (bitset(MCIF_ESMTP, mci->mci_flags))
159 	{
160 		smtpmessage("EHLO %s", m, mci, hn);
161 		SmtpPhase = mci->mci_phase = "client EHLO";
162 	}
163 	else
164 	{
165 		smtpmessage("HELO %s", m, mci, hn);
166 		SmtpPhase = mci->mci_phase = "client HELO";
167 	}
168 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
169 			CurHostName, mci->mci_phase);
170 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL);
171 	if (r < 0)
172 		goto tempfail1;
173 	else if (REPLYTYPE(r) == 5)
174 	{
175 		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
176 		    !bitnset(M_LMTP, m->m_flags))
177 		{
178 			/* try old SMTP instead */
179 			mci->mci_flags &= ~MCIF_ESMTP;
180 			goto tryhelo;
181 		}
182 		goto unavailable;
183 	}
184 	else if (REPLYTYPE(r) != 2)
185 		goto tempfail2;
186 
187 	/*
188 	**  Check to see if we actually ended up talking to ourself.
189 	**  This means we didn't know about an alias or MX, or we managed
190 	**  to connect to an echo server.
191 	*/
192 
193 	p = strchr(&SmtpReplyBuffer[4], ' ');
194 	if (p != NULL)
195 		*p = '\0';
196 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
197 	    !bitnset(M_LMTP, m->m_flags) &&
198 	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
199 	{
200 		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
201 			CurHostName);
202 		mci_setstat(mci, EX_CONFIG, "5.3.5", "system config error");
203 		mci->mci_errno = 0;
204 		smtpquit(m, mci, e);
205 		return;
206 	}
207 
208 	/*
209 	**  If this is expected to be another sendmail, send some internal
210 	**  commands.
211 	*/
212 
213 	if (bitnset(M_INTERNAL, m->m_flags))
214 	{
215 		/* tell it to be verbose */
216 		smtpmessage("VERB", m, mci);
217 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc);
218 		if (r < 0)
219 			goto tempfail1;
220 	}
221 
222 	if (mci->mci_state != MCIS_CLOSED)
223 	{
224 		mci->mci_state = MCIS_OPEN;
225 		return;
226 	}
227 
228 	/* got a 421 error code during startup */
229 
230   tempfail1:
231 	if (mci->mci_errno == 0)
232 		mci->mci_errno = errno;
233 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
234 	if (mci->mci_state != MCIS_CLOSED)
235 		smtpquit(m, mci, e);
236 	return;
237 
238   tempfail2:
239 	if (mci->mci_errno == 0)
240 		mci->mci_errno = errno;
241 	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
242 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
243 		    SmtpReplyBuffer);
244 	if (mci->mci_state != MCIS_CLOSED)
245 		smtpquit(m, mci, e);
246 	return;
247 
248   unavailable:
249 	mci->mci_errno = errno;
250 	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
251 	smtpquit(m, mci, e);
252 	return;
253 }
254 /*
255 **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
256 **
257 **	Parameters:
258 **		line -- the response line.
259 **		firstline -- set if this is the first line of the reply.
260 **		m -- the mailer.
261 **		mci -- the mailer connection info.
262 **		e -- the envelope.
263 **
264 **	Returns:
265 **		none.
266 */
267 
268 static void
269 esmtp_check(line, firstline, m, mci, e)
270 	char *line;
271 	bool firstline;
272 	MAILER *m;
273 	register MCI *mci;
274 	ENVELOPE *e;
275 {
276 	if (strstr(line, "ESMTP") != NULL)
277 		mci->mci_flags |= MCIF_ESMTP;
278 	if (strstr(line, "8BIT-OK") != NULL)
279 		mci->mci_flags |= MCIF_8BITOK;
280 }
281 # if SASL
282 /*
283 **  STR_UNION -- create the union of two lists
284 **
285 **	Parameters:
286 **		s1, s2 -- lists of items (separated by single blanks).
287 **
288 **	Returns:
289 **		the union of both lists.
290 */
291 
292 static char *
293 str_union(s1, s2)
294 	char *s1, *s2;
295 {
296 	char *hr, *h1, *h, *res;
297 	int l1, l2, rl;
298 
299 	if (s1 == NULL || *s1 == '\0')
300 		return s2;
301 	if (s2 == NULL || *s2 == '\0')
302 		return s1;
303 	l1 = strlen(s1);
304 	l2 = strlen(s2);
305 	rl = l1 + l2;
306 	res = (char *)malloc(rl + 2);
307 	if (res == NULL)
308 	{
309 		if (l1 > l2)
310 			return s1;
311 		return s2;
312 	}
313 	(void) strlcpy(res, s1, rl);
314 	hr = res;
315 	h1 = s2;
316 	h = s2;
317 
318 	/* walk through s2 */
319 	while (h != NULL && *h1 != '\0')
320 	{
321 		/* is there something after the current word? */
322 		if ((h = strchr(h1, ' ')) != NULL)
323 			*h = '\0';
324 		l1 = strlen(h1);
325 
326 		/* does the current word appear in s1 ? */
327 		if (iteminlist(h1, s1, " ") == NULL)
328 		{
329 			/* add space as delimiter */
330 			*hr++ = ' ';
331 
332 			/* copy the item */
333 			memcpy(hr, h1, l1);
334 
335 			/* advance pointer in result list */
336 			hr += l1;
337 			*hr = '\0';
338 		}
339 		if (h != NULL)
340 		{
341 			/* there are more items */
342 			*h = ' ';
343 			h1 = h + 1;
344 		}
345 	}
346 	return res;
347 }
348 # endif /* SASL */
349 /*
350 **  HELO_OPTIONS -- process the options on a HELO line.
351 **
352 **	Parameters:
353 **		line -- the response line.
354 **		firstline -- set if this is the first line of the reply.
355 **		m -- the mailer.
356 **		mci -- the mailer connection info.
357 **		e -- the envelope.
358 **
359 **	Returns:
360 **		none.
361 */
362 
363 static void
364 helo_options(line, firstline, m, mci, e)
365 	char *line;
366 	bool firstline;
367 	MAILER *m;
368 	register MCI *mci;
369 	ENVELOPE *e;
370 {
371 	register char *p;
372 
373 	if (firstline)
374 	{
375 # if SASL
376 		if (mci->mci_saslcap != NULL)
377 			free(mci->mci_saslcap);
378 		mci->mci_saslcap = NULL;
379 # endif /* SASL */
380 		return;
381 	}
382 
383 	if (strlen(line) < (SIZE_T) 5)
384 		return;
385 	line += 4;
386 	p = strpbrk(line, " =");
387 	if (p != NULL)
388 		*p++ = '\0';
389 	if (strcasecmp(line, "size") == 0)
390 	{
391 		mci->mci_flags |= MCIF_SIZE;
392 		if (p != NULL)
393 			mci->mci_maxsize = atol(p);
394 	}
395 	else if (strcasecmp(line, "8bitmime") == 0)
396 	{
397 		mci->mci_flags |= MCIF_8BITMIME;
398 		mci->mci_flags &= ~MCIF_7BIT;
399 	}
400 	else if (strcasecmp(line, "expn") == 0)
401 		mci->mci_flags |= MCIF_EXPN;
402 	else if (strcasecmp(line, "dsn") == 0)
403 		mci->mci_flags |= MCIF_DSN;
404 	else if (strcasecmp(line, "enhancedstatuscodes") == 0)
405 		mci->mci_flags |= MCIF_ENHSTAT;
406 # if STARTTLS
407 	else if (strcasecmp(line, "starttls") == 0)
408 		mci->mci_flags |= MCIF_TLS;
409 # endif /* STARTTLS */
410 # if SASL
411 	else if (strcasecmp(line, "auth") == 0)
412 	{
413 		if (p != NULL && *p != '\0')
414 		{
415 			if (mci->mci_saslcap != NULL)
416 			{
417 				char *h;
418 
419 				/*
420 				**  create the union with previous auth
421 				**  offerings because we recognize "auth "
422 				**  and "auth=" (old format).
423 				*/
424 				h = mci->mci_saslcap;
425 				mci->mci_saslcap = str_union(h, p);
426 				if (h != mci->mci_saslcap)
427 					free(h);
428 				mci->mci_flags |= MCIF_AUTH;
429 			}
430 			else
431 			{
432 				int l;
433 
434 				l = strlen(p) + 1;
435 				mci->mci_saslcap = (char *)malloc(l);
436 
437 				/* XXX this may be leaked */
438 				if (mci->mci_saslcap != NULL)
439 				{
440 					(void) strlcpy(mci->mci_saslcap, p, l);
441 					mci->mci_flags |= MCIF_AUTH;
442 				}
443 			}
444 		}
445 	}
446 # endif /* SASL */
447 }
448 # if SASL
449 
450 /*
451 **  GETSASLDATA -- process the challenges from the SASL protocol
452 **
453 **	This gets the relevant sasl response data out of the reply
454 **	from the server
455 **
456 **	Parameters:
457 **		line -- the response line.
458 **		firstline -- set if this is the first line of the reply.
459 **		m -- the mailer.
460 **		mci -- the mailer connection info.
461 **		e -- the envelope.
462 **
463 **	Returns:
464 **		none.
465 */
466 
467 void
468 getsasldata(line, firstline, m, mci, e)
469 	char *line;
470 	bool firstline;
471 	MAILER *m;
472 	register MCI *mci;
473 	ENVELOPE *e;
474 {
475 	int len;
476 	char *out;
477 	int result;
478 
479 	/* if not a continue we don't care about it */
480 	if ((strlen(line) <= 4) ||
481 	    (line[0] != '3') ||
482 	    (line[1] != '3') ||
483 	    (line[2] != '4'))
484 	{
485 		mci->mci_sasl_string = NULL;
486 		return;
487 	}
488 
489 	/* forget about "334 " */
490 	line += 4;
491 	len = strlen(line);
492 
493 	out = xalloc(len + 1);
494 	result = sasl_decode64(line, len, out, (u_int *)&len);
495 	if (result != SASL_OK)
496 	{
497 		len = 0;
498 		*out = '\0';
499 	}
500 	if (mci->mci_sasl_string != NULL)
501 	{
502 		if (mci->mci_sasl_string_len <= len)
503 		{
504 			free(mci->mci_sasl_string);
505 			mci->mci_sasl_string = xalloc(len + 1);
506 		}
507 	}
508 	else
509 		mci->mci_sasl_string = xalloc(len + 1);
510 	/* XXX this is probably leaked */
511 	memcpy(mci->mci_sasl_string, out, len);
512 	mci->mci_sasl_string[len] = '\0';
513 	mci->mci_sasl_string_len = len;
514 	free(out);
515 	return;
516 }
517 
518 /*
519 **  READAUTH -- read auth value from a file
520 **
521 **	Parameters:
522 **		l -- line to define.
523 **		filename -- name of file to read.
524 **		safe -- if set, this is a safe read.
525 **
526 **	Returns:
527 **		line from file
528 **
529 **	Side Effects:
530 **		overwrites local static buffer. The caller should copy
531 **		the result.
532 **
533 */
534 
535 /* lines in authinfo file */
536 # define SASL_USER	1
537 # define SASL_AUTHID	2
538 # define SASL_PASSWORD	3
539 # define SASL_DEFREALM	4
540 # define SASL_MECH	5
541 
542 static char *sasl_info_name[] =
543 {
544 	"",
545 	"user id",
546 	"authorization id",
547 	"password",
548 	"realm",
549 	"mechanism"
550 };
551 
552 static char *
553 readauth(l, filename, safe)
554 	int l;
555 	char *filename;
556 	bool safe;
557 {
558 	FILE *f;
559 	long sff;
560 	pid_t pid;
561 	int lc;
562 	static char buf[MAXLINE];
563 
564 	if (filename == NULL || filename[0] == '\0')
565 		return "";
566 #if !_FFR_ALLOW_SASLINFO
567 	/*
568 	**  make sure we don't use a program that is not
569 	**  accesible to the user who specified a different authinfo file.
570 	**  However, currently we don't pass this info (authinfo file
571 	**  specified by user) around, so we just turn off program access.
572 	*/
573 	if (filename[0] == '|')
574 	{
575 		auto int fd;
576 		int i;
577 		char *p;
578 		char *argv[MAXPV + 1];
579 
580 		i = 0;
581 		for (p = strtok(&filename[1], " \t"); p != NULL;
582 		     p = strtok(NULL, " \t"))
583 		{
584 			if (i >= MAXPV)
585 				break;
586 			argv[i++] = p;
587 		}
588 		argv[i] = NULL;
589 		pid = prog_open(argv, &fd, CurEnv);
590 		if (pid < 0)
591 			f = NULL;
592 		else
593 			f = fdopen(fd, "r");
594 	}
595 	else
596 #endif /* !_FFR_ALLOW_SASLINFO */
597 	{
598 		pid = -1;
599 		sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
600 		      | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
601 		if (DontLockReadFiles)
602 			sff |= SFF_NOLOCK;
603 #if _FFR_ALLOW_SASLINFO
604 		/*
605 		**  XXX: make sure we don't read or open files that are not
606 		**  accesible to the user who specified a different authinfo
607 		**  file.
608 		*/
609 		sff |= SFF_MUSTOWN;
610 #else /* _FFR_ALLOW_SASLINFO */
611 		if (safe)
612 			sff |= SFF_OPENASROOT;
613 #endif /* _FFR_ALLOW_SASLINFO */
614 
615 		f = safefopen(filename, O_RDONLY, 0, sff);
616 	}
617 	if (f == NULL)
618 	{
619 		syserr("readauth: cannot open %s", filename);
620 		return "";
621 	}
622 
623 	lc = 0;
624 	while (lc < l && fgets(buf, sizeof buf, f) != NULL)
625 	{
626 		if (buf[0] != '#')
627 			lc++;
628 	}
629 
630 	(void) fclose(f);
631 	if (pid > 0)
632 		(void) waitfor(pid);
633 	if (lc < l)
634 	{
635 		if (LogLevel >= 9)
636 			sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s",
637 			  sasl_info_name[l], filename);
638 		return "";
639 	}
640 	lc = strlen(buf) - 1;
641 	if (lc >= 0)
642 		buf[lc] = '\0';
643 	if (tTd(95, 6))
644 		dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf);
645 	return buf;
646 }
647 
648 #  ifndef __attribute__
649 #   define __attribute__(x)
650 #  endif /* ! __attribute__ */
651 
652 static int getsimple	__P((void *, int, const char **, unsigned *));
653 static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
654 static int saslgetrealm	__P((void *, int, const char **, const char **));
655 
656 static sasl_callback_t callbacks[] =
657 {
658 	{	SASL_CB_GETREALM,	&saslgetrealm,	NULL	},
659 # define CB_GETREALM_IDX	0
660 	{	SASL_CB_PASS,		&getsecret,	NULL	},
661 # define CB_PASS_IDX	1
662 	{	SASL_CB_USER,		&getsimple,	NULL	},
663 # define CB_USER_IDX	2
664 	{	SASL_CB_AUTHNAME,	&getsimple,	NULL	},
665 # define CB_AUTHNAME_IDX	3
666 	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
667 	{	SASL_CB_LIST_END,	NULL,		NULL	}
668 };
669 
670 /*
671 **  GETSIMPLE -- callback to get userid or authid
672 **
673 **	Parameters:
674 **		context -- unused
675 **		id -- what to do
676 **		result -- (pointer to) result
677 **		len -- (pointer to) length of result
678 **
679 **	Returns:
680 **		OK/failure values
681 */
682 
683 static int
684 getsimple(context, id, result, len)
685 	void *context __attribute__((unused));
686 	int id;
687 	const char **result;
688 	unsigned *len;
689 {
690 	char *h;
691 #  if SASL > 10509
692 	int addrealm;
693 	static int addedrealm = FALSE;
694 #  endif /* SASL > 10509 */
695 	static char *user = NULL;
696 	static char *authid = NULL;
697 
698 	if (result == NULL)
699 		return SASL_BADPARAM;
700 
701 	switch (id)
702 	{
703 	  case SASL_CB_USER:
704 		if (user == NULL)
705 		{
706 			h = readauth(SASL_USER, SASLInfo, TRUE);
707 			user = newstr(h);
708 		}
709 		*result = user;
710 		if (tTd(95, 5))
711 			dprintf("AUTH username '%s'\n", *result);
712 		if (len != NULL)
713 			*len = user ? strlen(user) : 0;
714 		break;
715 
716 	  case SASL_CB_AUTHNAME:
717 #  if SASL > 10509
718 		/* XXX maybe other mechanisms too?! */
719 		addrealm = context != NULL &&
720 			   strcasecmp(context, "CRAM-MD5") == 0;
721 		if (addedrealm != addrealm && authid != NULL)
722 		{
723 #  if SASL > 10522
724 			/*
725 			**  digest-md5 prior to 1.5.23 doesn't copy the
726 			**  value it gets from the callback, but free()s
727 			**  it later on
728 			**  workaround: don't free() it here
729 			**  this can cause a memory leak!
730 			*/
731 			free(authid);
732 #  endif /* SASL > 10522 */
733 			authid = NULL;
734 			addedrealm = addrealm;
735 		}
736 #  endif /* SASL > 10509 */
737 		if (authid == NULL)
738 		{
739 			h = readauth(SASL_AUTHID, SASLInfo, TRUE);
740 #  if SASL > 10509
741 			if (addrealm && strchr(h, '@') == NULL)
742 			{
743 				size_t l;
744 				char *realm;
745 
746 				realm = callbacks[CB_GETREALM_IDX].context;
747 				l = strlen(h) + strlen(realm) + 2;
748 				authid = xalloc(l);
749 				snprintf(authid, l, "%s@%s", h, realm);
750 			}
751 			else
752 #  endif /* SASL > 10509 */
753 				authid = newstr(h);
754 		}
755 		*result = authid;
756 		if (tTd(95, 5))
757 			dprintf("AUTH authid '%s'\n", *result);
758 		if (len != NULL)
759 			*len = authid ? strlen(authid) : 0;
760 		break;
761 
762 	  case SASL_CB_LANGUAGE:
763 		*result = NULL;
764 		if (len != NULL)
765 			*len = 0;
766 		break;
767 
768 	  default:
769 		return SASL_BADPARAM;
770 	}
771 	return SASL_OK;
772 }
773 
774 /*
775 **  GETSECRET -- callback to get password
776 **
777 **	Parameters:
778 **		conn -- connection information
779 **		context -- unused
780 **		id -- what to do
781 **		psecret -- (pointer to) result
782 **
783 **	Returns:
784 **		OK/failure values
785 */
786 
787 static int
788 getsecret(conn, context, id, psecret)
789 	sasl_conn_t *conn;
790 	void *context __attribute__((unused));
791 	int id;
792 	sasl_secret_t **psecret;
793 {
794 	char *h;
795 	int len;
796 	static char *authpass = NULL;
797 
798 	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
799 		return SASL_BADPARAM;
800 
801 	if (authpass == NULL)
802 	{
803 		h = readauth(SASL_PASSWORD, SASLInfo, TRUE);
804 		authpass = newstr(h);
805 	}
806 	len = strlen(authpass);
807 	*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len + 1);
808 	if (*psecret == NULL)
809 		return SASL_FAIL;
810 	(void) strlcpy((*psecret)->data, authpass, len + 1);
811 	(*psecret)->len = len;
812 	return SASL_OK;
813 }
814 
815 /*
816 **  SAFESASLFILE -- callback for sasl: is file safe?
817 **
818 **	Parameters:
819 **		context -- pointer to context between invocations (unused)
820 **		file -- name of file to check
821 **		type -- type of file to check
822 **
823 **	Returns:
824 **		SASL_OK: file can be used
825 **		SASL_CONTINUE: don't use file
826 **		SASL_FAIL: failure (not used here)
827 **
828 */
829 int
830 # if SASL > 10515
831 safesaslfile(context, file, type)
832 # else /* SASL > 10515 */
833 safesaslfile(context, file)
834 # endif /* SASL > 10515 */
835 	void *context;
836 	char *file;
837 # if SASL > 10515
838 	int type;
839 # endif /* SASL > 10515 */
840 {
841 	long sff;
842 	int r;
843 	char *p;
844 
845 	if (file == NULL || *file == '\0')
846 		return SASL_OK;
847 
848 	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
849 	if ((p = strrchr(file, '/')) == NULL)
850 		p = file;
851 	else
852 		++p;
853 
854 # if SASL <= 10515
855 	/* everything beside libs and .conf files must not be readable */
856 	r = strlen(p);
857 	if ((r <= 3 || strncmp(p, "lib", 3) != 0) &&
858 	    (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0)
859 #  if _FFR_UNSAFE_SASL
860 	    && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)
861 #  endif /* _FFR_UNSAFE_SASL */
862 	   )
863 		sff |= SFF_NORFILES;
864 # else /* SASL > 10515 */
865 	/* files containing passwords should be not readable */
866 	if (type == SASL_VRFY_PASSWD)
867 	{
868 #  if _FFR_UNSAFE_SASL
869 		if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
870 			sff |= SFF_NOWRFILES;
871 		else
872 #  endif /* _FFR_UNSAFE_SASL */
873 			sff |= SFF_NORFILES;
874 	}
875 # endif /* SASL <= 10515 */
876 
877 	p = file;
878 	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
879 			  S_IRUSR, NULL)) == 0)
880 		return SASL_OK;
881 	if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
882 		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
883 			  p, errstring(r));
884 	return SASL_CONTINUE;
885 }
886 
887 /*
888 **  SASLGETREALM -- return the realm for SASL
889 **
890 **	return the realm for the client
891 **
892 **	Parameters:
893 **		context -- context shared between invocations
894 **			here: realm to return
895 **		availrealms -- list of available realms
896 **			{realm, realm, ...}
897 **		result -- pointer to result
898 **
899 **	Returns:
900 **		failure/success
901 */
902 static int
903 saslgetrealm(context, id, availrealms, result)
904 	void *context;
905 	int id;
906 	const char **availrealms;
907 	const char **result;
908 {
909 	if (LogLevel > 12)
910 		sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
911 			  context == NULL ? "<No Context>" : (char *) context,
912 			  (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
913 	if (context == NULL)
914 		return SASL_FAIL;
915 
916 	/* check whether context is in list? */
917 	if (availrealms != NULL && *availrealms != NULL)
918 	{
919 		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
920 		    NULL)
921 		{
922 			if (LogLevel > 8)
923 				sm_syslog(LOG_ERR, NOQID,
924 					  "saslgetrealm: realm %s not in list %s",
925 					  context, *availrealms);
926 			return SASL_FAIL;
927 		}
928 	}
929 	*result = (char *)context;
930 	return SASL_OK;
931 }
932 /*
933 **  ITEMINLIST -- does item appear in list?
934 **
935 **	Check whether item appears in list (which must be separated by a
936 **	character in delim) as a "word", i.e. it must appear at the begin
937 **	of the list or after a space, and it must end with a space or the
938 **	end of the list.
939 **
940 **	Parameters:
941 **		item -- item to search.
942 **		list -- list of items.
943 **		delim -- list of delimiters.
944 **
945 **	Returns:
946 **		pointer to occurrence (NULL if not found).
947 */
948 
949 char *
950 iteminlist(item, list, delim)
951 	char *item;
952 	char *list;
953 	char *delim;
954 {
955 	char *s;
956 	int len;
957 
958 	if (list == NULL || *list == '\0')
959 		return NULL;
960 	if (item == NULL || *item == '\0')
961 		return NULL;
962 	s = list;
963 	len = strlen(item);
964 	while (s != NULL && *s != '\0')
965 	{
966 		if (strncasecmp(s, item, len) == 0 &&
967 		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
968 			return s;
969 		s = strpbrk(s, delim);
970 		if (s != NULL)
971 			while (*++s == ' ')
972 				continue;
973 	}
974 	return NULL;
975 }
976 /*
977 **  REMOVEMECH -- remove item [rem] from list [list]
978 **
979 **	Parameters:
980 **		rem -- item to remove
981 **		list -- list of items
982 **
983 **	Returns:
984 **		pointer to new list (NULL in case of error).
985 */
986 
987 char *
988 removemech(rem, list)
989 	char *rem;
990 	char *list;
991 {
992 	char *ret;
993 	char *needle;
994 	int len;
995 
996 	if (list == NULL)
997 		return NULL;
998 	if (rem == NULL || *rem == '\0')
999 	{
1000 		/* take out what? */
1001 		return NULL;
1002 	}
1003 
1004 	/* find the item in the list */
1005 	if ((needle = iteminlist(rem, list, " ")) == NULL)
1006 	{
1007 		/* not in there: return original */
1008 		return list;
1009 	}
1010 
1011 	/* length of string without rem */
1012 	len = strlen(list) - strlen(rem);
1013 	if (len == 0)
1014 	{
1015 		ret = xalloc(1);  /* XXX leaked */
1016 		*ret = '\0';
1017 		return ret;
1018 	}
1019 	ret = xalloc(len);  /* XXX leaked */
1020 	memset(ret, '\0', len);
1021 
1022 	/* copy from start to removed item */
1023 	memcpy(ret, list, needle - list);
1024 
1025 	/* length of rest of string past removed item */
1026 	len = strlen(needle) - strlen(rem) - 1;
1027 	if (len > 0)
1028 	{
1029 		/* not last item -- copy into string */
1030 		memcpy(ret + (needle - list),
1031 		       list + (needle - list) + strlen(rem) + 1,
1032 		       len);
1033 	}
1034 	else
1035 		ret[(needle - list) - 1] = '\0';
1036 	return ret;
1037 }
1038 /*
1039 **  INTERSECT -- create the intersection between two lists
1040 **
1041 **	Parameters:
1042 **		s1, s2 -- lists of items (separated by single blanks).
1043 **
1044 **	Returns:
1045 **		the intersection of both lists.
1046 */
1047 
1048 char *
1049 intersect(s1, s2)
1050 	char *s1, *s2;
1051 {
1052 	char *hr, *h1, *h, *res;
1053 	int l1, l2, rl;
1054 
1055 	if (s1 == NULL || s2 == NULL)	/* NULL string(s) -> NULL result */
1056 		return NULL;
1057 	l1 = strlen(s1);
1058 	l2 = strlen(s2);
1059 	rl = min(l1, l2);
1060 	res = (char *)malloc(rl + 1);
1061 	if (res == NULL)
1062 		return NULL;
1063 	*res = '\0';
1064 	if (rl == 0)	/* at least one string empty? */
1065 		return res;
1066 	hr = res;
1067 	h1 = s1;
1068 	h = s1;
1069 
1070 	/* walk through s1 */
1071 	while (h != NULL && *h1 != '\0')
1072 	{
1073 		/* is there something after the current word? */
1074 		if ((h = strchr(h1, ' ')) != NULL)
1075 			*h = '\0';
1076 		l1 = strlen(h1);
1077 
1078 		/* does the current word appear in s2 ? */
1079 		if (iteminlist(h1, s2, " ") != NULL)
1080 		{
1081 			/* add a blank if not first item */
1082 			if (hr != res)
1083 				*hr++ = ' ';
1084 
1085 			/* copy the item */
1086 			memcpy(hr, h1, l1);
1087 
1088 			/* advance pointer in result list */
1089 			hr += l1;
1090 			*hr = '\0';
1091 		}
1092 		if (h != NULL)
1093 		{
1094 			/* there are more items */
1095 			*h = ' ';
1096 			h1 = h + 1;
1097 		}
1098 	}
1099 	return res;
1100 }
1101 /*
1102 **  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1103 **
1104 **	Parameters:
1105 **		m -- the mailer.
1106 **		mci -- the mailer connection structure.
1107 **		e -- the envelope (including the sender to specify).
1108 **		mechused - filled in with mechanism used
1109 **
1110 **	Returns:
1111 **		EX_OK/EX_TEMPFAIL
1112 */
1113 
1114 int
1115 attemptauth(m, mci, e, mechused)
1116 	MAILER *m;
1117 	MCI *mci;
1118 	ENVELOPE *e;
1119 	char **mechused;
1120 {
1121 	int saslresult, smtpresult;
1122 	sasl_external_properties_t ssf;
1123 	sasl_interact_t *client_interact = NULL;
1124 	char *out;
1125 	unsigned int outlen;
1126 	static char *mechusing;
1127 	sasl_security_properties_t ssp;
1128 	char in64[MAXOUTLEN];
1129 # if NETINET
1130 	extern SOCKADDR CurHostAddr;
1131 # endif /* NETINET */
1132 
1133 	*mechused = NULL;
1134 	if (mci->mci_conn != NULL)
1135 	{
1136 		sasl_dispose(&(mci->mci_conn));
1137 
1138 		/* just in case, sasl_dispose() should take care of it */
1139 		mci->mci_conn = NULL;
1140 	}
1141 
1142 	/* make a new client sasl connection */
1143 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1144 								 : "smtp",
1145 				     CurHostName, NULL, 0, &mci->mci_conn);
1146 
1147 	/* set properties */
1148 	(void) memset(&ssp, '\0', sizeof ssp);
1149 #  if SFIO
1150 	/* XXX should these be options settable via .cf ? */
1151 	/* ssp.min_ssf = 0; is default due to memset() */
1152 	{
1153 		ssp.max_ssf = INT_MAX;
1154 		ssp.maxbufsize = MAXOUTLEN;
1155 #   if 0
1156 		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1157 #   endif /* 0 */
1158 	}
1159 #  endif /* SFIO */
1160 	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1161 	if (saslresult != SASL_OK)
1162 		return EX_TEMPFAIL;
1163 
1164 	/* external security strength factor, authentication id */
1165 	ssf.ssf = 0;
1166 	ssf.auth_id = NULL;
1167 # if _FFR_EXT_MECH
1168 	out = macvalue(macid("{cert_subject}", NULL), e);
1169 	if (out != NULL && *out != '\0')
1170 		ssf.auth_id = out;
1171 	out = macvalue(macid("{cipher_bits}", NULL), e);
1172 	if (out != NULL && *out != '\0')
1173 		ssf.ssf = atoi(out);
1174 # endif /* _FFR_EXT_MECH */
1175 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1176 	if (saslresult != SASL_OK)
1177 		return EX_TEMPFAIL;
1178 
1179 # if NETINET
1180 	/* set local/remote ipv4 addresses */
1181 	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1182 	{
1183 		SOCKADDR_LEN_T addrsize;
1184 		struct sockaddr_in saddr_l;
1185 
1186 		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1187 				 (struct sockaddr_in *) &CurHostAddr)
1188 		    != SASL_OK)
1189 			return EX_TEMPFAIL;
1190 		addrsize = sizeof(struct sockaddr_in);
1191 		if (getsockname(fileno(mci->mci_out),
1192 				(struct sockaddr *) &saddr_l, &addrsize) == 0)
1193 		{
1194 			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1195 					 &saddr_l) != SASL_OK)
1196 				return EX_TEMPFAIL;
1197 		}
1198 	}
1199 # endif /* NETINET */
1200 
1201 	/* start client side of sasl */
1202 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1203 				       NULL, &client_interact,
1204 				       &out, &outlen,
1205 				       (const char **)&mechusing);
1206 	callbacks[CB_AUTHNAME_IDX].context = mechusing;
1207 
1208 	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1209 	{
1210 #  if SFIO
1211 		if (saslresult == SASL_NOMECH && LogLevel > 8)
1212 		{
1213 			sm_syslog(LOG_NOTICE, e->e_id,
1214 				  "available AUTH mechanisms do not fulfill requirements");
1215 		}
1216 #  endif /* SFIO */
1217 		return EX_TEMPFAIL;
1218 	}
1219 
1220 	*mechused = mechusing;
1221 
1222 	/* send the info across the wire */
1223 	if (outlen > 0)
1224 	{
1225 		saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1226 		if (saslresult != SASL_OK) /* internal error */
1227 		{
1228 			if (LogLevel > 8)
1229 				sm_syslog(LOG_ERR, e->e_id,
1230 					"encode64 for AUTH failed");
1231 			return EX_TEMPFAIL;
1232 		}
1233 		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1234 	}
1235 	else
1236 	{
1237 		smtpmessage("AUTH %s", m, mci, mechusing);
1238 	}
1239 
1240 	/* get the reply */
1241 	smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL);
1242 	/* which timeout? XXX */
1243 
1244 	for (;;)
1245 	{
1246 		/* check return code from server */
1247 		if (smtpresult == 235)
1248 		{
1249 			define(macid("{auth_type}", NULL),
1250 			       newstr(mechusing), e);
1251 #  if !SFIO
1252 			if (LogLevel > 9)
1253 				sm_syslog(LOG_INFO, NOQID,
1254 					  "SASL: outgoing connection to %.64s: mech=%.16s",
1255 					  mci->mci_host, mechusing);
1256 #  endif /* !SFIO */
1257 			return EX_OK;
1258 		}
1259 		if (smtpresult == -1)
1260 			return EX_IOERR;
1261 		if (smtpresult != 334)
1262 			return EX_TEMPFAIL;
1263 
1264 		saslresult = sasl_client_step(mci->mci_conn,
1265 					      mci->mci_sasl_string,
1266 					      mci->mci_sasl_string_len,
1267 					      &client_interact,
1268 					      &out, &outlen);
1269 
1270 		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1271 		{
1272 			if (tTd(95, 5))
1273 				dprintf("AUTH FAIL: %s (%d)\n",
1274 					sasl_errstring(saslresult, NULL, NULL),
1275 					saslresult);
1276 
1277 			/* fail deliberately, see RFC 2254 4. */
1278 			smtpmessage("*", m, mci);
1279 
1280 			/*
1281 			**  but we should only fail for this authentication
1282 			**  mechanism; how to do that?
1283 			*/
1284 
1285 			smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1286 					   getsasldata, NULL);
1287 			return EX_TEMPFAIL;
1288 		}
1289 
1290 		if (outlen > 0)
1291 		{
1292 			saslresult = sasl_encode64(out, outlen, in64,
1293 						   MAXOUTLEN, NULL);
1294 			if (saslresult != SASL_OK)
1295 			{
1296 				/* give an error reply to the other side! */
1297 				smtpmessage("*", m, mci);
1298 				return EX_TEMPFAIL;
1299 			}
1300 		}
1301 		else
1302 			in64[0] = '\0';
1303 		smtpmessage("%s", m, mci, in64);
1304 		smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1305 				   getsasldata, NULL);
1306 		/* which timeout? XXX */
1307 	}
1308 	/* NOTREACHED */
1309 }
1310 
1311 /*
1312 **  SMTPAUTH -- try to AUTHenticate
1313 **
1314 **	This will try mechanisms in the order the sasl library decided until:
1315 **	- there are no more mechanisms
1316 **	- a mechanism succeeds
1317 **	- the sasl library fails initializing
1318 **
1319 **	Parameters:
1320 **		m -- the mailer.
1321 **		mci -- the mailer connection info.
1322 **		e -- the envelope.
1323 **
1324 **	Returns:
1325 **		EX_OK/EX_TEMPFAIL
1326 */
1327 
1328 int
1329 smtpauth(m, mci, e)
1330 	MAILER *m;
1331 	MCI *mci;
1332 	ENVELOPE *e;
1333 {
1334 	int result;
1335 	char *mechused;
1336 	char *h;
1337 	static char *defrealm = NULL;
1338 	static char *mechs = NULL;
1339 
1340 	mci->mci_sasl_auth = FALSE;
1341 	if (defrealm == NULL)
1342 	{
1343 		h = readauth(SASL_DEFREALM, SASLInfo, TRUE);
1344 		if (h != NULL && *h != '\0')
1345 			defrealm = newstr(h);
1346 	}
1347 	if (defrealm == NULL || *defrealm == '\0')
1348 		defrealm = newstr(macvalue('j', CurEnv));
1349 	callbacks[CB_GETREALM_IDX].context = defrealm;
1350 
1351 # if _FFR_DEFAUTHINFO_MECHS
1352 	if (mechs == NULL)
1353 	{
1354 		h = readauth(SASL_MECH, SASLInfo, TRUE);
1355 		if (h != NULL && *h != '\0')
1356 			mechs = newstr(h);
1357 	}
1358 # endif /* _FFR_DEFAUTHINFO_MECHS */
1359 	if (mechs == NULL || *mechs == '\0')
1360 		mechs = AuthMechanisms;
1361 	mci->mci_saslcap = intersect(mechs, mci->mci_saslcap);
1362 
1363 	/* initialize sasl client library */
1364 	result = sasl_client_init(callbacks);
1365 	if (result != SASL_OK)
1366 		return EX_TEMPFAIL;
1367 	do
1368 	{
1369 		result = attemptauth(m, mci, e, &mechused);
1370 		if (result == EX_OK)
1371 			mci->mci_sasl_auth = TRUE;
1372 		else if (result == EX_TEMPFAIL)
1373 		{
1374 			mci->mci_saslcap = removemech(mechused,
1375 						      mci->mci_saslcap);
1376 			if (mci->mci_saslcap == NULL ||
1377 			    *(mci->mci_saslcap) == '\0')
1378 				return EX_TEMPFAIL;
1379 		}
1380 		else	/* all others for now */
1381 			return EX_TEMPFAIL;
1382 	} while (result != EX_OK);
1383 	return result;
1384 }
1385 # endif /* SASL */
1386 
1387 /*
1388 **  SMTPMAILFROM -- send MAIL command
1389 **
1390 **	Parameters:
1391 **		m -- the mailer.
1392 **		mci -- the mailer connection structure.
1393 **		e -- the envelope (including the sender to specify).
1394 */
1395 
1396 int
1397 smtpmailfrom(m, mci, e)
1398 	MAILER *m;
1399 	MCI *mci;
1400 	ENVELOPE *e;
1401 {
1402 	int r;
1403 	char *bufp;
1404 	char *bodytype;
1405 	char buf[MAXNAME + 1];
1406 	char optbuf[MAXLINE];
1407 	char *enhsc;
1408 
1409 	if (tTd(18, 2))
1410 		dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
1411 	enhsc = NULL;
1412 
1413 	/* set up appropriate options to include */
1414 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
1415 	{
1416 		snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
1417 		bufp = &optbuf[strlen(optbuf)];
1418 	}
1419 	else
1420 	{
1421 		optbuf[0] = '\0';
1422 		bufp = optbuf;
1423 	}
1424 
1425 	bodytype = e->e_bodytype;
1426 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
1427 	{
1428 		if (bodytype == NULL &&
1429 		    bitset(MM_MIME8BIT, MimeMode) &&
1430 		    bitset(EF_HAS8BIT, e->e_flags) &&
1431 		    !bitset(EF_DONT_MIME, e->e_flags) &&
1432 		    !bitnset(M_8BITS, m->m_flags))
1433 			bodytype = "8BITMIME";
1434 		if (bodytype != NULL &&
1435 		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
1436 		{
1437 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1438 				 " BODY=%s", bodytype);
1439 			bufp += strlen(bufp);
1440 		}
1441 	}
1442 	else if (bitnset(M_8BITS, m->m_flags) ||
1443 		 !bitset(EF_HAS8BIT, e->e_flags) ||
1444 		 bitset(MCIF_8BITOK, mci->mci_flags))
1445 	{
1446 		/* EMPTY */
1447 		/* just pass it through */
1448 	}
1449 # if MIME8TO7
1450 	else if (bitset(MM_CVTMIME, MimeMode) &&
1451 		 !bitset(EF_DONT_MIME, e->e_flags) &&
1452 		 (!bitset(MM_PASS8BIT, MimeMode) ||
1453 		  bitset(EF_IS_MIME, e->e_flags)))
1454 	{
1455 		/* must convert from 8bit MIME format to 7bit encoded */
1456 		mci->mci_flags |= MCIF_CVT8TO7;
1457 	}
1458 # endif /* MIME8TO7 */
1459 	else if (!bitset(MM_PASS8BIT, MimeMode))
1460 	{
1461 		/* cannot just send a 8-bit version */
1462 		extern char MsgBuf[];
1463 
1464 		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
1465 		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
1466 		return EX_DATAERR;
1467 	}
1468 
1469 	if (bitset(MCIF_DSN, mci->mci_flags))
1470 	{
1471 		if (e->e_envid != NULL &&
1472 		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
1473 		{
1474 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1475 				 " ENVID=%s", e->e_envid);
1476 			bufp += strlen(bufp);
1477 		}
1478 
1479 		/* RET= parameter */
1480 		if (bitset(EF_RET_PARAM, e->e_flags) &&
1481 		    SPACELEFT(optbuf, bufp) > 9)
1482 		{
1483 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1484 				 " RET=%s",
1485 				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
1486 					"HDRS" : "FULL");
1487 			bufp += strlen(bufp);
1488 		}
1489 	}
1490 
1491 	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
1492 	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
1493 # if SASL
1494 	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
1495 # endif /* SASL */
1496 	    )
1497 	{
1498 		snprintf(bufp, SPACELEFT(optbuf, bufp),
1499 			 " AUTH=%s", e->e_auth_param);
1500 		bufp += strlen(bufp);
1501 	}
1502 
1503 	/*
1504 	**  Send the MAIL command.
1505 	**	Designates the sender.
1506 	*/
1507 
1508 	mci->mci_state = MCIS_ACTIVE;
1509 
1510 	if (bitset(EF_RESPONSE, e->e_flags) &&
1511 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
1512 		buf[0] = '\0';
1513 	else
1514 		expand("\201g", buf, sizeof buf, e);
1515 	if (buf[0] == '<')
1516 	{
1517 		/* strip off <angle brackets> (put back on below) */
1518 		bufp = &buf[strlen(buf) - 1];
1519 		if (*bufp == '>')
1520 			*bufp = '\0';
1521 		bufp = &buf[1];
1522 	}
1523 	else
1524 		bufp = buf;
1525 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
1526 	    !bitnset(M_FROMPATH, m->m_flags))
1527 	{
1528 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
1529 	}
1530 	else
1531 	{
1532 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
1533 			    *bufp == '@' ? ',' : ':', bufp, optbuf);
1534 	}
1535 	SmtpPhase = mci->mci_phase = "client MAIL";
1536 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1537 			CurHostName, mci->mci_phase);
1538 	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc);
1539 	if (r < 0)
1540 	{
1541 		/* communications failure */
1542 		mci->mci_errno = errno;
1543 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1544 		smtpquit(m, mci, e);
1545 		return EX_TEMPFAIL;
1546 	}
1547 	else if (r == SMTPCLOSING)
1548 	{
1549 		/* service shutting down */
1550 		mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
1551 			    SmtpReplyBuffer);
1552 		smtpquit(m, mci, e);
1553 		return EX_TEMPFAIL;
1554 	}
1555 	else if (REPLYTYPE(r) == 4)
1556 	{
1557 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
1558 			    SmtpReplyBuffer);
1559 		return EX_TEMPFAIL;
1560 	}
1561 	else if (REPLYTYPE(r) == 2)
1562 	{
1563 		return EX_OK;
1564 	}
1565 	else if (r == 501)
1566 	{
1567 		/* syntax error in arguments */
1568 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
1569 			    SmtpReplyBuffer);
1570 		return EX_DATAERR;
1571 	}
1572 	else if (r == 553)
1573 	{
1574 		/* mailbox name not allowed */
1575 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
1576 			    SmtpReplyBuffer);
1577 		return EX_DATAERR;
1578 	}
1579 	else if (r == 552)
1580 	{
1581 		/* exceeded storage allocation */
1582 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
1583 			    SmtpReplyBuffer);
1584 		if (bitset(MCIF_SIZE, mci->mci_flags))
1585 			e->e_flags |= EF_NO_BODY_RETN;
1586 		return EX_UNAVAILABLE;
1587 	}
1588 	else if (REPLYTYPE(r) == 5)
1589 	{
1590 		/* unknown error */
1591 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
1592 			    SmtpReplyBuffer);
1593 		return EX_UNAVAILABLE;
1594 	}
1595 
1596 	if (LogLevel > 1)
1597 	{
1598 		sm_syslog(LOG_CRIT, e->e_id,
1599 			  "%.100s: SMTP MAIL protocol error: %s",
1600 			  CurHostName,
1601 			  shortenstring(SmtpReplyBuffer, 403));
1602 	}
1603 
1604 	/* protocol error -- close up */
1605 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1606 		    SmtpReplyBuffer);
1607 	smtpquit(m, mci, e);
1608 	return EX_PROTOCOL;
1609 }
1610 /*
1611 **  SMTPRCPT -- designate recipient.
1612 **
1613 **	Parameters:
1614 **		to -- address of recipient.
1615 **		m -- the mailer we are sending to.
1616 **		mci -- the connection info for this transaction.
1617 **		e -- the envelope for this transaction.
1618 **
1619 **	Returns:
1620 **		exit status corresponding to recipient status.
1621 **
1622 **	Side Effects:
1623 **		Sends the mail via SMTP.
1624 */
1625 
1626 int
1627 smtprcpt(to, m, mci, e)
1628 	ADDRESS *to;
1629 	register MAILER *m;
1630 	MCI *mci;
1631 	ENVELOPE *e;
1632 {
1633 	register int r;
1634 	char *bufp;
1635 	char optbuf[MAXLINE];
1636 	char *enhsc;
1637 
1638 	enhsc = NULL;
1639 	optbuf[0] = '\0';
1640 	bufp = optbuf;
1641 
1642 	/*
1643 	**  warning: in the following it is assumed that the free space
1644 	**  in bufp is sizeof optbuf
1645 	*/
1646 	if (bitset(MCIF_DSN, mci->mci_flags))
1647 	{
1648 		/* NOTIFY= parameter */
1649 		if (bitset(QHASNOTIFY, to->q_flags) &&
1650 		    bitset(QPRIMARY, to->q_flags) &&
1651 		    !bitnset(M_LOCALMAILER, m->m_flags))
1652 		{
1653 			bool firstone = TRUE;
1654 
1655 			(void) strlcat(bufp, " NOTIFY=", sizeof optbuf);
1656 			if (bitset(QPINGONSUCCESS, to->q_flags))
1657 			{
1658 				(void) strlcat(bufp, "SUCCESS", sizeof optbuf);
1659 				firstone = FALSE;
1660 			}
1661 			if (bitset(QPINGONFAILURE, to->q_flags))
1662 			{
1663 				if (!firstone)
1664 					(void) strlcat(bufp, ",",
1665 						       sizeof optbuf);
1666 				(void) strlcat(bufp, "FAILURE", sizeof optbuf);
1667 				firstone = FALSE;
1668 			}
1669 			if (bitset(QPINGONDELAY, to->q_flags))
1670 			{
1671 				if (!firstone)
1672 					(void) strlcat(bufp, ",",
1673 						       sizeof optbuf);
1674 				(void) strlcat(bufp, "DELAY", sizeof optbuf);
1675 				firstone = FALSE;
1676 			}
1677 			if (firstone)
1678 				(void) strlcat(bufp, "NEVER", sizeof optbuf);
1679 			bufp += strlen(bufp);
1680 		}
1681 
1682 		/* ORCPT= parameter */
1683 		if (to->q_orcpt != NULL &&
1684 		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
1685 		{
1686 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1687 				 " ORCPT=%s", to->q_orcpt);
1688 			bufp += strlen(bufp);
1689 		}
1690 	}
1691 
1692 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
1693 
1694 	SmtpPhase = mci->mci_phase = "client RCPT";
1695 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1696 			CurHostName, mci->mci_phase);
1697 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
1698 	to->q_rstatus = newstr(SmtpReplyBuffer);
1699 	to->q_status = ENHSCN(enhsc, smtptodsn(r));
1700 	if (!bitnset(M_LMTP, m->m_flags))
1701 		to->q_statmta = mci->mci_host;
1702 	if (r < 0 || REPLYTYPE(r) == 4)
1703 		return EX_TEMPFAIL;
1704 	else if (REPLYTYPE(r) == 2)
1705 		return EX_OK;
1706 	else if (r == 550)
1707 	{
1708 		to->q_status = ENHSCN(enhsc, "5.1.1");
1709 		return EX_NOUSER;
1710 	}
1711 	else if (r == 551)
1712 	{
1713 		to->q_status = ENHSCN(enhsc, "5.1.6");
1714 		return EX_NOUSER;
1715 	}
1716 	else if (r == 553)
1717 	{
1718 		to->q_status = ENHSCN(enhsc, "5.1.3");
1719 		return EX_NOUSER;
1720 	}
1721 	else if (REPLYTYPE(r) == 5)
1722 	{
1723 		return EX_UNAVAILABLE;
1724 	}
1725 
1726 	if (LogLevel > 1)
1727 	{
1728 		sm_syslog(LOG_CRIT, e->e_id,
1729 			  "%.100s: SMTP RCPT protocol error: %s",
1730 			  CurHostName,
1731 			  shortenstring(SmtpReplyBuffer, 403));
1732 	}
1733 
1734 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1735 		    SmtpReplyBuffer);
1736 	return EX_PROTOCOL;
1737 }
1738 /*
1739 **  SMTPDATA -- send the data and clean up the transaction.
1740 **
1741 **	Parameters:
1742 **		m -- mailer being sent to.
1743 **		mci -- the mailer connection information.
1744 **		e -- the envelope for this message.
1745 **
1746 **	Returns:
1747 **		exit status corresponding to DATA command.
1748 **
1749 **	Side Effects:
1750 **		none.
1751 */
1752 
1753 static jmp_buf	CtxDataTimeout;
1754 
1755 int
1756 smtpdata(m, mci, e)
1757 	MAILER *m;
1758 	register MCI *mci;
1759 	register ENVELOPE *e;
1760 {
1761 	register int r;
1762 	register EVENT *ev;
1763 	int rstat;
1764 	int xstat;
1765 	time_t timeout;
1766 	char *enhsc;
1767 
1768 	enhsc = NULL;
1769 	/*
1770 	**  Send the data.
1771 	**	First send the command and check that it is ok.
1772 	**	Then send the data.
1773 	**	Follow it up with a dot to terminate.
1774 	**	Finally get the results of the transaction.
1775 	*/
1776 
1777 	/* send the command and check ok to proceed */
1778 	smtpmessage("DATA", m, mci);
1779 	SmtpPhase = mci->mci_phase = "client DATA 354";
1780 	sm_setproctitle(TRUE, e, "%s %s: %s",
1781 			qid_printname(e), CurHostName, mci->mci_phase);
1782 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc);
1783 	if (r < 0 || REPLYTYPE(r) == 4)
1784 	{
1785 		smtpquit(m, mci, e);
1786 		return EX_TEMPFAIL;
1787 	}
1788 	else if (REPLYTYPE(r) == 5)
1789 	{
1790 		smtprset(m, mci, e);
1791 		return EX_UNAVAILABLE;
1792 	}
1793 	else if (REPLYTYPE(r) != 3)
1794 	{
1795 		if (LogLevel > 1)
1796 		{
1797 			sm_syslog(LOG_CRIT, e->e_id,
1798 				  "%.100s: SMTP DATA-1 protocol error: %s",
1799 				  CurHostName,
1800 				  shortenstring(SmtpReplyBuffer, 403));
1801 		}
1802 		smtprset(m, mci, e);
1803 		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1804 			    SmtpReplyBuffer);
1805 		return EX_PROTOCOL;
1806 	}
1807 
1808 	/*
1809 	**  Set timeout around data writes.  Make it at least large
1810 	**  enough for DNS timeouts on all recipients plus some fudge
1811 	**  factor.  The main thing is that it should not be infinite.
1812 	*/
1813 
1814 	if (setjmp(CtxDataTimeout) != 0)
1815 	{
1816 		mci->mci_errno = errno;
1817 		mci->mci_state = MCIS_ERROR;
1818 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1819 
1820 		/*
1821 		**  If putbody() couldn't finish due to a timeout,
1822 		**  rewind it here in the timeout handler.  See
1823 		**  comments at the end of putbody() for reasoning.
1824 		*/
1825 
1826 		if (e->e_dfp != NULL)
1827 			(void) bfrewind(e->e_dfp);
1828 
1829 		errno = mci->mci_errno;
1830 		syserr("451 4.4.1 timeout writing message to %s", CurHostName);
1831 		smtpquit(m, mci, e);
1832 		return EX_TEMPFAIL;
1833 	}
1834 
1835 	if (tTd(18, 101))
1836 	{
1837 		/* simulate a DATA timeout */
1838 		timeout = 1;
1839 	}
1840 	else
1841 		timeout = DATA_PROGRESS_TIMEOUT;
1842 
1843 	ev = setevent(timeout, datatimeout, 0);
1844 
1845 
1846 	if (tTd(18, 101))
1847 	{
1848 		/* simulate a DATA timeout */
1849 		(void) sleep(1);
1850 	}
1851 
1852 	/*
1853 	**  Output the actual message.
1854 	*/
1855 
1856 	(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
1857 	(*e->e_putbody)(mci, e, NULL);
1858 
1859 	/*
1860 	**  Cleanup after sending message.
1861 	*/
1862 
1863 	clrevent(ev);
1864 
1865 # if _FFR_CATCH_BROKEN_MTAS
1866 	{
1867 		fd_set readfds;
1868 		struct timeval timeout;
1869 
1870 		FD_ZERO(&readfds);
1871 		FD_SET(fileno(mci->mci_in), &readfds);
1872 		timeout.tv_sec = 0;
1873 		timeout.tv_usec = 0;
1874 		if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds,
1875 			   NULL, NULL, &timeout) > 0 &&
1876 		    FD_ISSET(fileno(mci->mci_in), &readfds))
1877 		{
1878 			/* terminate the message */
1879 			fprintf(mci->mci_out, ".%s", m->m_eol);
1880 			if (TrafficLogFile != NULL)
1881 				fprintf(TrafficLogFile, "%05d >>> .\n",
1882 					(int) getpid());
1883 			if (Verbose)
1884 				nmessage(">>> .");
1885 
1886 			mci->mci_errno = EIO;
1887 			mci->mci_state = MCIS_ERROR;
1888 			mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
1889 			smtpquit(m, mci, e);
1890 			return EX_PROTOCOL;
1891 		}
1892 	}
1893 # endif /* _FFR_CATCH_BROKEN_MTAS */
1894 
1895 	if (ferror(mci->mci_out))
1896 	{
1897 		/* error during processing -- don't send the dot */
1898 		mci->mci_errno = EIO;
1899 		mci->mci_state = MCIS_ERROR;
1900 		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
1901 		smtpquit(m, mci, e);
1902 		return EX_IOERR;
1903 	}
1904 
1905 	/* terminate the message */
1906 	fprintf(mci->mci_out, ".%s", m->m_eol);
1907 	if (TrafficLogFile != NULL)
1908 		fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
1909 	if (Verbose)
1910 		nmessage(">>> .");
1911 
1912 	/* check for the results of the transaction */
1913 	SmtpPhase = mci->mci_phase = "client DATA status";
1914 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1915 			CurHostName, mci->mci_phase);
1916 	if (bitnset(M_LMTP, m->m_flags))
1917 		return EX_OK;
1918 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
1919 	if (r < 0)
1920 	{
1921 		smtpquit(m, mci, e);
1922 		return EX_TEMPFAIL;
1923 	}
1924 	mci->mci_state = MCIS_OPEN;
1925 	xstat = EX_NOTSTICKY;
1926 	if (r == 452)
1927 		rstat = EX_TEMPFAIL;
1928 	else if (REPLYTYPE(r) == 4)
1929 		rstat = xstat = EX_TEMPFAIL;
1930 	else if (REPLYCLASS(r) != 5)
1931 		rstat = xstat = EX_PROTOCOL;
1932 	else if (REPLYTYPE(r) == 2)
1933 		rstat = xstat = EX_OK;
1934 	else if (REPLYTYPE(r) == 5)
1935 		rstat = EX_UNAVAILABLE;
1936 	else
1937 		rstat = EX_PROTOCOL;
1938 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
1939 		    SmtpReplyBuffer);
1940 	if (e->e_statmsg != NULL)
1941 		free(e->e_statmsg);
1942 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
1943 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
1944 		r += 5;
1945 	else
1946 		r = 4;
1947 	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
1948 	SmtpPhase = mci->mci_phase = "idle";
1949 	sm_setproctitle(TRUE, e, "%s: %s", CurHostName, mci->mci_phase);
1950 	if (rstat != EX_PROTOCOL)
1951 		return rstat;
1952 	if (LogLevel > 1)
1953 	{
1954 		sm_syslog(LOG_CRIT, e->e_id,
1955 			  "%.100s: SMTP DATA-2 protocol error: %s",
1956 			  CurHostName,
1957 			  shortenstring(SmtpReplyBuffer, 403));
1958 	}
1959 	return rstat;
1960 }
1961 
1962 
1963 static void
1964 datatimeout()
1965 {
1966 	if (DataProgress)
1967 	{
1968 		time_t timeout;
1969 		register EVENT *ev;
1970 
1971 		/* check back again later */
1972 		if (tTd(18, 101))
1973 		{
1974 			/* simulate a DATA timeout */
1975 			timeout = 1;
1976 		}
1977 		else
1978 			timeout = DATA_PROGRESS_TIMEOUT;
1979 
1980 		DataProgress = FALSE;
1981 		ev = setevent(timeout, datatimeout, 0);
1982 	}
1983 	else
1984 	{
1985 		/* no progress, give up */
1986 		longjmp(CtxDataTimeout, 1);
1987 	}
1988 }
1989 /*
1990 **  SMTPGETSTAT -- get status code from DATA in LMTP
1991 **
1992 **	Parameters:
1993 **		m -- the mailer to which we are sending the message.
1994 **		mci -- the mailer connection structure.
1995 **		e -- the current envelope.
1996 **
1997 **	Returns:
1998 **		The exit status corresponding to the reply code.
1999 */
2000 
2001 int
2002 smtpgetstat(m, mci, e)
2003 	MAILER *m;
2004 	MCI *mci;
2005 	ENVELOPE *e;
2006 {
2007 	int r;
2008 	int status;
2009 	char *enhsc;
2010 
2011 	enhsc = NULL;
2012 	/* check for the results of the transaction */
2013 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
2014 	if (r < 0)
2015 	{
2016 		smtpquit(m, mci, e);
2017 		return EX_TEMPFAIL;
2018 	}
2019 	if (REPLYTYPE(r) == 4)
2020 		status = EX_TEMPFAIL;
2021 	else if (REPLYCLASS(r) != 5)
2022 		status = EX_PROTOCOL;
2023 	else if (REPLYTYPE(r) == 2)
2024 		status = EX_OK;
2025 	else if (REPLYTYPE(r) == 5)
2026 		status = EX_UNAVAILABLE;
2027 	else
2028 		status = EX_PROTOCOL;
2029 	if (e->e_statmsg != NULL)
2030 		free(e->e_statmsg);
2031 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2032 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2033 		r += 5;
2034 	else
2035 		r = 4;
2036 	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
2037 	mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)),
2038 		    SmtpReplyBuffer);
2039 	if (LogLevel > 1 && status == EX_PROTOCOL)
2040 	{
2041 		sm_syslog(LOG_CRIT, e->e_id,
2042 			  "%.100s: SMTP DATA-3 protocol error: %s",
2043 			  CurHostName,
2044 			  shortenstring(SmtpReplyBuffer, 403));
2045 	}
2046 	return status;
2047 }
2048 /*
2049 **  SMTPQUIT -- close the SMTP connection.
2050 **
2051 **	Parameters:
2052 **		m -- a pointer to the mailer.
2053 **		mci -- the mailer connection information.
2054 **		e -- the current envelope.
2055 **
2056 **	Returns:
2057 **		none.
2058 **
2059 **	Side Effects:
2060 **		sends the final protocol and closes the connection.
2061 */
2062 
2063 void
2064 smtpquit(m, mci, e)
2065 	register MAILER *m;
2066 	register MCI *mci;
2067 	ENVELOPE *e;
2068 {
2069 	bool oldSuprErrs = SuprErrs;
2070 	int rcode;
2071 
2072 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2073 	if (CurHostName == NULL)
2074 		CurHostName = MyHostName;
2075 
2076 	/*
2077 	**	Suppress errors here -- we may be processing a different
2078 	**	job when we do the quit connection, and we don't want the
2079 	**	new job to be penalized for something that isn't it's
2080 	**	problem.
2081 	*/
2082 
2083 	SuprErrs = TRUE;
2084 
2085 	/* send the quit message if we haven't gotten I/O error */
2086 	if (mci->mci_state != MCIS_ERROR &&
2087 	    mci->mci_state != MCIS_QUITING)
2088 	{
2089 		int origstate = mci->mci_state;
2090 
2091 		SmtpPhase = "client QUIT";
2092 		mci->mci_state = MCIS_QUITING;
2093 		smtpmessage("QUIT", m, mci);
2094 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL);
2095 		SuprErrs = oldSuprErrs;
2096 		if (mci->mci_state == MCIS_CLOSED ||
2097 		    origstate == MCIS_CLOSED)
2098 			return;
2099 	}
2100 
2101 	/* now actually close the connection and pick up the zombie */
2102 	rcode = endmailer(mci, e, NULL);
2103 	if (rcode != EX_OK)
2104 	{
2105 		char *mailer = NULL;
2106 
2107 		if (mci->mci_mailer != NULL &&
2108 		    mci->mci_mailer->m_name != NULL)
2109 			mailer = mci->mci_mailer->m_name;
2110 
2111 		/* look for naughty mailers */
2112 		sm_syslog(LOG_ERR, e->e_id,
2113 			  "smtpquit: mailer%s%s exited with exit value %d",
2114 			  mailer == NULL ? "" : " ",
2115 			  mailer == NULL ? "" : mailer,
2116 			  rcode);
2117 	}
2118 
2119 	SuprErrs = oldSuprErrs;
2120 }
2121 /*
2122 **  SMTPRSET -- send a RSET (reset) command
2123 **
2124 **	Parameters:
2125 **		m -- a pointer to the mailer.
2126 **		mci -- the mailer connection information.
2127 **		e -- the current envelope.
2128 **
2129 **	Returns:
2130 **		none.
2131 **
2132 **	Side Effects:
2133 **		closes the connection if there is no reply to RSET.
2134 */
2135 
2136 void
2137 smtprset(m, mci, e)
2138 	register MAILER *m;
2139 	register MCI *mci;
2140 	ENVELOPE *e;
2141 {
2142 	int r;
2143 
2144 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2145 	if (CurHostName == NULL)
2146 		CurHostName = MyHostName;
2147 
2148 	SmtpPhase = "client RSET";
2149 	smtpmessage("RSET", m, mci);
2150 	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
2151 	if (r < 0)
2152 		mci->mci_state = MCIS_ERROR;
2153 	else
2154 	{
2155 		/*
2156 		**  Any response is deemed to be acceptable.
2157 		**  The standard does not state the proper action
2158 		**  to take when a value other than 250 is received.
2159 		**
2160 		**  However, if 421 is returned for the RSET, leave
2161 		**  mci_state as MCIS_SSD (set in reply()).
2162 		*/
2163 
2164 		if (mci->mci_state != MCIS_SSD)
2165 			mci->mci_state = MCIS_OPEN;
2166 		return;
2167 	}
2168 	smtpquit(m, mci, e);
2169 }
2170 /*
2171 **  SMTPPROBE -- check the connection state
2172 **
2173 **	Parameters:
2174 **		mci -- the mailer connection information.
2175 **
2176 **	Returns:
2177 **		none.
2178 **
2179 **	Side Effects:
2180 **		closes the connection if there is no reply to RSET.
2181 */
2182 
2183 int
2184 smtpprobe(mci)
2185 	register MCI *mci;
2186 {
2187 	int r;
2188 	MAILER *m = mci->mci_mailer;
2189 	ENVELOPE *e;
2190 	extern ENVELOPE BlankEnvelope;
2191 
2192 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2193 	if (CurHostName == NULL)
2194 		CurHostName = MyHostName;
2195 
2196 	e = &BlankEnvelope;
2197 	SmtpPhase = "client probe";
2198 	smtpmessage("RSET", m, mci);
2199 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL);
2200 	if (r < 0 || REPLYTYPE(r) != 2)
2201 		smtpquit(m, mci, e);
2202 	return r;
2203 }
2204 /*
2205 **  REPLY -- read arpanet reply
2206 **
2207 **	Parameters:
2208 **		m -- the mailer we are reading the reply from.
2209 **		mci -- the mailer connection info structure.
2210 **		e -- the current envelope.
2211 **		timeout -- the timeout for reads.
2212 **		pfunc -- processing function called on each line of response.
2213 **			If null, no special processing is done.
2214 **
2215 **	Returns:
2216 **		reply code it reads.
2217 **
2218 **	Side Effects:
2219 **		flushes the mail file.
2220 */
2221 
2222 int
2223 reply(m, mci, e, timeout, pfunc, enhstat)
2224 	MAILER *m;
2225 	MCI *mci;
2226 	ENVELOPE *e;
2227 	time_t timeout;
2228 	void (*pfunc)();
2229 	char **enhstat;
2230 {
2231 	register char *bufp;
2232 	register int r;
2233 	bool firstline = TRUE;
2234 	char junkbuf[MAXLINE];
2235 	static char enhstatcode[ENHSCLEN];
2236 	int save_errno;
2237 
2238 	if (mci->mci_out != NULL)
2239 		(void) fflush(mci->mci_out);
2240 
2241 	if (tTd(18, 1))
2242 		dprintf("reply\n");
2243 
2244 	/*
2245 	**  Read the input line, being careful not to hang.
2246 	*/
2247 
2248 	bufp = SmtpReplyBuffer;
2249 	for (;;)
2250 	{
2251 		register char *p;
2252 
2253 		/* actually do the read */
2254 		if (e->e_xfp != NULL)
2255 			(void) fflush(e->e_xfp);	/* for debugging */
2256 
2257 		/* if we are in the process of closing just give the code */
2258 		if (mci->mci_state == MCIS_CLOSED)
2259 			return SMTPCLOSING;
2260 
2261 		if (mci->mci_out != NULL)
2262 			(void) fflush(mci->mci_out);
2263 
2264 		/* get the line from the other side */
2265 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
2266 		mci->mci_lastuse = curtime();
2267 
2268 		if (p == NULL)
2269 		{
2270 			bool oldholderrs;
2271 			extern char MsgBuf[];
2272 
2273 			/* if the remote end closed early, fake an error */
2274 			if (errno == 0)
2275 # ifdef ECONNRESET
2276 				errno = ECONNRESET;
2277 # else /* ECONNRESET */
2278 				errno = EPIPE;
2279 # endif /* ECONNRESET */
2280 
2281 			mci->mci_errno = errno;
2282 			oldholderrs = HoldErrs;
2283 			HoldErrs = TRUE;
2284 			usrerr("451 4.4.1 reply: read error from %s",
2285 			       CurHostName == NULL ? "NO_HOST" : CurHostName);
2286 
2287 			/* errors on QUIT should not be persistent */
2288 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
2289 				mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
2290 
2291 			/* if debugging, pause so we can see state */
2292 			if (tTd(18, 100))
2293 				(void) pause();
2294 			mci->mci_state = MCIS_ERROR;
2295 			save_errno = errno;
2296 			smtpquit(m, mci, e);
2297 # if XDEBUG
2298 			{
2299 				char wbuf[MAXLINE];
2300 				int wbufleft = sizeof wbuf;
2301 
2302 				p = wbuf;
2303 				if (e->e_to != NULL)
2304 				{
2305 					int plen;
2306 
2307 					snprintf(p, wbufleft, "%s... ",
2308 						shortenstring(e->e_to, MAXSHORTSTR));
2309 					plen = strlen(p);
2310 					p += plen;
2311 					wbufleft -= plen;
2312 				}
2313 				snprintf(p, wbufleft, "reply(%.100s) during %s",
2314 					 CurHostName == NULL ? "NO_HOST" : CurHostName,
2315 					 SmtpPhase);
2316 				checkfd012(wbuf);
2317 			}
2318 # endif /* XDEBUG */
2319 			errno = save_errno;
2320 			HoldErrs = oldholderrs;
2321 			return -1;
2322 		}
2323 		fixcrlf(bufp, TRUE);
2324 
2325 		/* EHLO failure is not a real error */
2326 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
2327 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
2328 		{
2329 			/* serious error -- log the previous command */
2330 			if (SmtpNeedIntro)
2331 			{
2332 				/* inform user who we are chatting with */
2333 				fprintf(CurEnv->e_xfp,
2334 					"... while talking to %s:\n",
2335 					CurHostName == NULL ? "NO_HOST" : CurHostName);
2336 				SmtpNeedIntro = FALSE;
2337 			}
2338 			if (SmtpMsgBuffer[0] != '\0')
2339 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
2340 			SmtpMsgBuffer[0] = '\0';
2341 
2342 			/* now log the message as from the other side */
2343 			fprintf(e->e_xfp, "<<< %s\n", bufp);
2344 		}
2345 
2346 		/* display the input for verbose mode */
2347 		if (Verbose)
2348 			nmessage("050 %s", bufp);
2349 
2350 		/* ignore improperly formatted input */
2351 		if (!ISSMTPREPLY(bufp))
2352 			continue;
2353 
2354 		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2355 		    enhstat != NULL &&
2356 		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
2357 			*enhstat = enhstatcode;
2358 
2359 		/* process the line */
2360 		if (pfunc != NULL)
2361 			(*pfunc)(bufp, firstline, m, mci, e);
2362 
2363 		firstline = FALSE;
2364 
2365 		/* decode the reply code */
2366 		r = atoi(bufp);
2367 
2368 		/* extra semantics: 0xx codes are "informational" */
2369 		if (r < 100)
2370 			continue;
2371 
2372 		/* if no continuation lines, return this line */
2373 		if (bufp[3] != '-')
2374 			break;
2375 
2376 		/* first line of real reply -- ignore rest */
2377 		bufp = junkbuf;
2378 	}
2379 
2380 	/*
2381 	**  Now look at SmtpReplyBuffer -- only care about the first
2382 	**  line of the response from here on out.
2383 	*/
2384 
2385 	/* save temporary failure messages for posterity */
2386 	if (SmtpReplyBuffer[0] == '4' &&
2387 	    (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
2388 		snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
2389 
2390 	/* reply code 421 is "Service Shutting Down" */
2391 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
2392 	{
2393 		/* send the quit protocol */
2394 		mci->mci_state = MCIS_SSD;
2395 		smtpquit(m, mci, e);
2396 	}
2397 
2398 	return r;
2399 }
2400 /*
2401 **  SMTPMESSAGE -- send message to server
2402 **
2403 **	Parameters:
2404 **		f -- format
2405 **		m -- the mailer to control formatting.
2406 **		a, b, c -- parameters
2407 **
2408 **	Returns:
2409 **		none.
2410 **
2411 **	Side Effects:
2412 **		writes message to mci->mci_out.
2413 */
2414 
2415 /*VARARGS1*/
2416 void
2417 # ifdef __STDC__
2418 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
2419 # else /* __STDC__ */
2420 smtpmessage(f, m, mci, va_alist)
2421 	char *f;
2422 	MAILER *m;
2423 	MCI *mci;
2424 	va_dcl
2425 # endif /* __STDC__ */
2426 {
2427 	VA_LOCAL_DECL
2428 
2429 	VA_START(mci);
2430 	(void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
2431 	VA_END;
2432 
2433 	if (tTd(18, 1) || Verbose)
2434 		nmessage(">>> %s", SmtpMsgBuffer);
2435 	if (TrafficLogFile != NULL)
2436 		fprintf(TrafficLogFile, "%05d >>> %s\n",
2437 			(int) getpid(), SmtpMsgBuffer);
2438 	if (mci->mci_out != NULL)
2439 	{
2440 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
2441 			m == NULL ? "\r\n" : m->m_eol);
2442 	}
2443 	else if (tTd(18, 1))
2444 	{
2445 		dprintf("smtpmessage: NULL mci_out\n");
2446 	}
2447 }
2448 
2449 #endif /* SMTP */
2450