xref: /freebsd/contrib/sendmail/src/usersmtp.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /*
2  * Copyright (c) 1998-2000 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.13 2000/09/26 00:46:21 gshapiro Exp $ (with SMTP)";
19 # else /* SMTP */
20 static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.13 2000/09/26 00:46:21 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 	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
848 	if ((p = strrchr(file, '/')) == NULL)
849 		p = file;
850 	else
851 		++p;
852 
853 # if SASL <= 10515
854 	/* everything beside libs and .conf files must not be readable */
855 	r = strlen(p);
856 	if ((r <= 3 || strncmp(p, "lib", 3) != 0) &&
857 	    (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0)
858 #  if _FFR_UNSAFE_SASL
859 	    && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)
860 #  endif /* _FFR_UNSAFE_SASL */
861 	   )
862 		sff |= SFF_NORFILES;
863 # else /* SASL > 10515 */
864 	/* files containing passwords should be not readable */
865 	if (type == SASL_VRFY_PASSWD)
866 	{
867 #  if _FFR_UNSAFE_SASL
868 		if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
869 			sff |= SFF_NOWRFILES;
870 		else
871 #  endif /* _FFR_UNSAFE_SASL */
872 			sff |= SFF_NORFILES;
873 	}
874 # endif /* SASL <= 10515 */
875 
876 	if ((r = safefile(file, RunAsUid, RunAsGid, RunAsUserName, sff,
877 			  S_IRUSR, NULL)) == 0)
878 		return SASL_OK;
879 	if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
880 		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
881 			  file, errstring(r));
882 	return SASL_CONTINUE;
883 }
884 
885 /*
886 **  SASLGETREALM -- return the realm for SASL
887 **
888 **	return the realm for the client
889 **
890 **	Parameters:
891 **		context -- context shared between invocations
892 **			here: realm to return
893 **		availrealms -- list of available realms
894 **			{realm, realm, ...}
895 **		result -- pointer to result
896 **
897 **	Returns:
898 **		failure/success
899 */
900 static int
901 saslgetrealm(context, id, availrealms, result)
902 	void *context;
903 	int id;
904 	const char **availrealms;
905 	const char **result;
906 {
907 	if (LogLevel > 12)
908 		sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
909 			  context == NULL ? "<No Context>" : (char *) context,
910 			  (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
911 	if (context == NULL)
912 		return SASL_FAIL;
913 
914 	/* check whether context is in list? */
915 	if (availrealms != NULL && *availrealms != NULL)
916 	{
917 		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
918 		    NULL)
919 		{
920 			if (LogLevel > 8)
921 				sm_syslog(LOG_ERR, NOQID,
922 					  "saslgetrealm: realm %s not in list %s",
923 					  context, *availrealms);
924 			return SASL_FAIL;
925 		}
926 	}
927 	*result = (char *)context;
928 	return SASL_OK;
929 }
930 /*
931 **  ITEMINLIST -- does item appear in list?
932 **
933 **	Check whether item appears in list (which must be separated by a
934 **	character in delim) as a "word", i.e. it must appear at the begin
935 **	of the list or after a space, and it must end with a space or the
936 **	end of the list.
937 **
938 **	Parameters:
939 **		item -- item to search.
940 **		list -- list of items.
941 **		delim -- list of delimiters.
942 **
943 **	Returns:
944 **		pointer to occurrence (NULL if not found).
945 */
946 
947 char *
948 iteminlist(item, list, delim)
949 	char *item;
950 	char *list;
951 	char *delim;
952 {
953 	char *s;
954 	int len;
955 
956 	if (list == NULL || *list == '\0')
957 		return NULL;
958 	if (item == NULL || *item == '\0')
959 		return NULL;
960 	s = list;
961 	len = strlen(item);
962 	while (s != NULL && *s != '\0')
963 	{
964 		if (strncasecmp(s, item, len) == 0 &&
965 		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
966 			return s;
967 		s = strpbrk(s, delim);
968 		if (s != NULL)
969 			while (*++s == ' ')
970 				continue;
971 	}
972 	return NULL;
973 }
974 /*
975 **  REMOVEMECH -- remove item [rem] from list [list]
976 **
977 **	Parameters:
978 **		rem -- item to remove
979 **		list -- list of items
980 **
981 **	Returns:
982 **		pointer to new list (NULL in case of error).
983 */
984 
985 char *
986 removemech(rem, list)
987 	char *rem;
988 	char *list;
989 {
990 	char *ret;
991 	char *needle;
992 	int len;
993 
994 	if (list == NULL)
995 		return NULL;
996 	if (rem == NULL || *rem == '\0')
997 	{
998 		/* take out what? */
999 		return NULL;
1000 	}
1001 
1002 	/* find the item in the list */
1003 	if ((needle = iteminlist(rem, list, " ")) == NULL)
1004 	{
1005 		/* not in there: return original */
1006 		return list;
1007 	}
1008 
1009 	/* length of string without rem */
1010 	len = strlen(list) - strlen(rem);
1011 	if (len == 0)
1012 	{
1013 		ret = xalloc(1);  /* XXX leaked */
1014 		*ret = '\0';
1015 		return ret;
1016 	}
1017 	ret = xalloc(len);  /* XXX leaked */
1018 	memset(ret, '\0', len);
1019 
1020 	/* copy from start to removed item */
1021 	memcpy(ret, list, needle - list);
1022 
1023 	/* length of rest of string past removed item */
1024 	len = strlen(needle) - strlen(rem) - 1;
1025 	if (len > 0)
1026 	{
1027 		/* not last item -- copy into string */
1028 		memcpy(ret + (needle - list),
1029 		       list + (needle - list) + strlen(rem) + 1,
1030 		       len);
1031 	}
1032 	else
1033 		ret[(needle - list) - 1] = '\0';
1034 	return ret;
1035 }
1036 /*
1037 **  INTERSECT -- create the intersection between two lists
1038 **
1039 **	Parameters:
1040 **		s1, s2 -- lists of items (separated by single blanks).
1041 **
1042 **	Returns:
1043 **		the intersection of both lists.
1044 */
1045 
1046 char *
1047 intersect(s1, s2)
1048 	char *s1, *s2;
1049 {
1050 	char *hr, *h1, *h, *res;
1051 	int l1, l2, rl;
1052 
1053 	if (s1 == NULL || s2 == NULL)	/* NULL string(s) -> NULL result */
1054 		return NULL;
1055 	l1 = strlen(s1);
1056 	l2 = strlen(s2);
1057 	rl = min(l1, l2);
1058 	res = (char *)malloc(rl + 1);
1059 	if (res == NULL)
1060 		return NULL;
1061 	*res = '\0';
1062 	if (rl == 0)	/* at least one string empty? */
1063 		return res;
1064 	hr = res;
1065 	h1 = s1;
1066 	h = s1;
1067 
1068 	/* walk through s1 */
1069 	while (h != NULL && *h1 != '\0')
1070 	{
1071 		/* is there something after the current word? */
1072 		if ((h = strchr(h1, ' ')) != NULL)
1073 			*h = '\0';
1074 		l1 = strlen(h1);
1075 
1076 		/* does the current word appear in s2 ? */
1077 		if (iteminlist(h1, s2, " ") != NULL)
1078 		{
1079 			/* add a blank if not first item */
1080 			if (hr != res)
1081 				*hr++ = ' ';
1082 
1083 			/* copy the item */
1084 			memcpy(hr, h1, l1);
1085 
1086 			/* advance pointer in result list */
1087 			hr += l1;
1088 			*hr = '\0';
1089 		}
1090 		if (h != NULL)
1091 		{
1092 			/* there are more items */
1093 			*h = ' ';
1094 			h1 = h + 1;
1095 		}
1096 	}
1097 	return res;
1098 }
1099 /*
1100 **  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1101 **
1102 **	Parameters:
1103 **		m -- the mailer.
1104 **		mci -- the mailer connection structure.
1105 **		e -- the envelope (including the sender to specify).
1106 **		mechused - filled in with mechanism used
1107 **
1108 **	Returns:
1109 **		EX_OK/EX_TEMPFAIL
1110 */
1111 
1112 int
1113 attemptauth(m, mci, e, mechused)
1114 	MAILER *m;
1115 	MCI *mci;
1116 	ENVELOPE *e;
1117 	char **mechused;
1118 {
1119 	int saslresult, smtpresult;
1120 	sasl_external_properties_t ssf;
1121 	sasl_interact_t *client_interact = NULL;
1122 	char *out;
1123 	unsigned int outlen;
1124 	static char *mechusing;
1125 	sasl_security_properties_t ssp;
1126 	char in64[MAXOUTLEN];
1127 # if NETINET
1128 	extern SOCKADDR CurHostAddr;
1129 # endif /* NETINET */
1130 
1131 	*mechused = NULL;
1132 	if (mci->mci_conn != NULL)
1133 	{
1134 		sasl_dispose(&(mci->mci_conn));
1135 
1136 		/* just in case, sasl_dispose() should take care of it */
1137 		mci->mci_conn = NULL;
1138 	}
1139 
1140 	/* make a new client sasl connection */
1141 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1142 								 : "smtp",
1143 				     CurHostName, NULL, 0, &mci->mci_conn);
1144 
1145 	/* set properties */
1146 	(void) memset(&ssp, '\0', sizeof ssp);
1147 #  if SFIO
1148 	/* XXX should these be options settable via .cf ? */
1149 	/* ssp.min_ssf = 0; is default due to memset() */
1150 	{
1151 		ssp.max_ssf = INT_MAX;
1152 		ssp.maxbufsize = MAXOUTLEN;
1153 #   if 0
1154 		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1155 #   endif /* 0 */
1156 	}
1157 #  endif /* SFIO */
1158 	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1159 	if (saslresult != SASL_OK)
1160 		return EX_TEMPFAIL;
1161 
1162 	/* external security strength factor, authentication id */
1163 	ssf.ssf = 0;
1164 	ssf.auth_id = NULL;
1165 # if _FFR_EXT_MECH
1166 	out = macvalue(macid("{cert_subject}", NULL), e);
1167 	if (out != NULL && *out != '\0')
1168 		ssf.auth_id = out;
1169 	out = macvalue(macid("{cipher_bits}", NULL), e);
1170 	if (out != NULL && *out != '\0')
1171 		ssf.ssf = atoi(out);
1172 # endif /* _FFR_EXT_MECH */
1173 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1174 	if (saslresult != SASL_OK)
1175 		return EX_TEMPFAIL;
1176 
1177 # if NETINET
1178 	/* set local/remote ipv4 addresses */
1179 	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1180 	{
1181 		SOCKADDR_LEN_T addrsize;
1182 		struct sockaddr_in saddr_l;
1183 
1184 		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1185 				 (struct sockaddr_in *) &CurHostAddr)
1186 		    != SASL_OK)
1187 			return EX_TEMPFAIL;
1188 		addrsize = sizeof(struct sockaddr_in);
1189 		if (getsockname(fileno(mci->mci_out),
1190 				(struct sockaddr *) &saddr_l, &addrsize) != 0)
1191 		{
1192 			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1193 					 &saddr_l) != SASL_OK)
1194 				return EX_TEMPFAIL;
1195 		}
1196 	}
1197 # endif /* NETINET */
1198 
1199 	/* start client side of sasl */
1200 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1201 				       NULL, &client_interact,
1202 				       &out, &outlen,
1203 				       (const char **)&mechusing);
1204 	callbacks[CB_AUTHNAME_IDX].context = mechusing;
1205 
1206 	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1207 	{
1208 #  if SFIO
1209 		if (saslresult == SASL_NOMECH && LogLevel > 8)
1210 		{
1211 			sm_syslog(LOG_NOTICE, e->e_id,
1212 				  "available AUTH mechanisms do not fulfill requirements");
1213 		}
1214 #  endif /* SFIO */
1215 		return EX_TEMPFAIL;
1216 	}
1217 
1218 	*mechused = mechusing;
1219 
1220 	/* send the info across the wire */
1221 	if (outlen > 0)
1222 	{
1223 		saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1224 		if (saslresult != SASL_OK) /* internal error */
1225 		{
1226 			if (LogLevel > 8)
1227 				sm_syslog(LOG_ERR, e->e_id,
1228 					"encode64 for AUTH failed");
1229 			return EX_TEMPFAIL;
1230 		}
1231 		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1232 	}
1233 	else
1234 	{
1235 		smtpmessage("AUTH %s", m, mci, mechusing);
1236 	}
1237 
1238 	/* get the reply */
1239 	smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL);
1240 	/* which timeout? XXX */
1241 
1242 	for (;;)
1243 	{
1244 		/* check return code from server */
1245 		if (smtpresult == 235)
1246 		{
1247 			define(macid("{auth_type}", NULL),
1248 			       newstr(mechusing), e);
1249 #  if !SFIO
1250 			if (LogLevel > 9)
1251 				sm_syslog(LOG_INFO, NOQID,
1252 					  "SASL: outgoing connection to %.64s: mech=%.16s",
1253 					  mci->mci_host, mechusing);
1254 #  endif /* !SFIO */
1255 			return EX_OK;
1256 		}
1257 		if (smtpresult == -1)
1258 			return EX_IOERR;
1259 		if (smtpresult != 334)
1260 			return EX_TEMPFAIL;
1261 
1262 		saslresult = sasl_client_step(mci->mci_conn,
1263 					      mci->mci_sasl_string,
1264 					      mci->mci_sasl_string_len,
1265 					      &client_interact,
1266 					      &out, &outlen);
1267 
1268 		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1269 		{
1270 			if (tTd(95, 5))
1271 				dprintf("AUTH FAIL: %s (%d)\n",
1272 					sasl_errstring(saslresult, NULL, NULL),
1273 					saslresult);
1274 
1275 			/* fail deliberately, see RFC 2254 4. */
1276 			smtpmessage("*", m, mci);
1277 
1278 			/*
1279 			**  but we should only fail for this authentication
1280 			**  mechanism; how to do that?
1281 			*/
1282 
1283 			smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1284 					   getsasldata, NULL);
1285 			return EX_TEMPFAIL;
1286 		}
1287 
1288 		if (outlen > 0)
1289 		{
1290 			saslresult = sasl_encode64(out, outlen, in64,
1291 						   MAXOUTLEN, NULL);
1292 			if (saslresult != SASL_OK)
1293 			{
1294 				/* give an error reply to the other side! */
1295 				smtpmessage("*", m, mci);
1296 				return EX_TEMPFAIL;
1297 			}
1298 		}
1299 		else
1300 			in64[0] = '\0';
1301 		smtpmessage(in64, m, mci);
1302 		smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1303 				   getsasldata, NULL);
1304 		/* which timeout? XXX */
1305 	}
1306 	/* NOTREACHED */
1307 }
1308 
1309 /*
1310 **  SMTPAUTH -- try to AUTHenticate
1311 **
1312 **	This will try mechanisms in the order the sasl library decided until:
1313 **	- there are no more mechanisms
1314 **	- a mechanism succeeds
1315 **	- the sasl library fails initializing
1316 **
1317 **	Parameters:
1318 **		m -- the mailer.
1319 **		mci -- the mailer connection info.
1320 **		e -- the envelope.
1321 **
1322 **	Returns:
1323 **		EX_OK/EX_TEMPFAIL
1324 */
1325 
1326 int
1327 smtpauth(m, mci, e)
1328 	MAILER *m;
1329 	MCI *mci;
1330 	ENVELOPE *e;
1331 {
1332 	int result;
1333 	char *mechused;
1334 	char *h;
1335 	static char *defrealm = NULL;
1336 	static char *mechs = NULL;
1337 
1338 	mci->mci_sasl_auth = FALSE;
1339 	if (defrealm == NULL)
1340 	{
1341 		h = readauth(SASL_DEFREALM, SASLInfo, TRUE);
1342 		if (h != NULL && *h != '\0')
1343 			defrealm = newstr(h);
1344 	}
1345 	if (defrealm == NULL || *defrealm == '\0')
1346 		defrealm = newstr(macvalue('j', CurEnv));
1347 	callbacks[CB_GETREALM_IDX].context = defrealm;
1348 
1349 # if _FFR_DEFAUTHINFO_MECHS
1350 	if (mechs == NULL)
1351 	{
1352 		h = readauth(SASL_MECH, SASLInfo, TRUE);
1353 		if (h != NULL && *h != '\0')
1354 			mechs = newstr(h);
1355 	}
1356 # endif /* _FFR_DEFAUTHINFO_MECHS */
1357 	if (mechs == NULL || *mechs == '\0')
1358 		mechs = AuthMechanisms;
1359 	mci->mci_saslcap = intersect(mechs, mci->mci_saslcap);
1360 
1361 	/* initialize sasl client library */
1362 	result = sasl_client_init(callbacks);
1363 	if (result != SASL_OK)
1364 		return EX_TEMPFAIL;
1365 	do
1366 	{
1367 		result = attemptauth(m, mci, e, &mechused);
1368 		if (result == EX_OK)
1369 			mci->mci_sasl_auth = TRUE;
1370 		else if (result == EX_TEMPFAIL)
1371 		{
1372 			mci->mci_saslcap = removemech(mechused,
1373 						      mci->mci_saslcap);
1374 			if (mci->mci_saslcap == NULL ||
1375 			    *(mci->mci_saslcap) == '\0')
1376 				return EX_TEMPFAIL;
1377 		}
1378 		else	/* all others for now */
1379 			return EX_TEMPFAIL;
1380 	} while (result != EX_OK);
1381 	return result;
1382 }
1383 # endif /* SASL */
1384 
1385 /*
1386 **  SMTPMAILFROM -- send MAIL command
1387 **
1388 **	Parameters:
1389 **		m -- the mailer.
1390 **		mci -- the mailer connection structure.
1391 **		e -- the envelope (including the sender to specify).
1392 */
1393 
1394 int
1395 smtpmailfrom(m, mci, e)
1396 	MAILER *m;
1397 	MCI *mci;
1398 	ENVELOPE *e;
1399 {
1400 	int r;
1401 	char *bufp;
1402 	char *bodytype;
1403 	char buf[MAXNAME + 1];
1404 	char optbuf[MAXLINE];
1405 	char *enhsc;
1406 
1407 	if (tTd(18, 2))
1408 		dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
1409 	enhsc = NULL;
1410 
1411 	/* set up appropriate options to include */
1412 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
1413 	{
1414 		snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
1415 		bufp = &optbuf[strlen(optbuf)];
1416 	}
1417 	else
1418 	{
1419 		optbuf[0] = '\0';
1420 		bufp = optbuf;
1421 	}
1422 
1423 	bodytype = e->e_bodytype;
1424 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
1425 	{
1426 		if (bodytype == NULL &&
1427 		    bitset(MM_MIME8BIT, MimeMode) &&
1428 		    bitset(EF_HAS8BIT, e->e_flags) &&
1429 		    !bitset(EF_DONT_MIME, e->e_flags) &&
1430 		    !bitnset(M_8BITS, m->m_flags))
1431 			bodytype = "8BITMIME";
1432 		if (bodytype != NULL &&
1433 		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
1434 		{
1435 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1436 				 " BODY=%s", bodytype);
1437 			bufp += strlen(bufp);
1438 		}
1439 	}
1440 	else if (bitnset(M_8BITS, m->m_flags) ||
1441 		 !bitset(EF_HAS8BIT, e->e_flags) ||
1442 		 bitset(MCIF_8BITOK, mci->mci_flags))
1443 	{
1444 		/* EMPTY */
1445 		/* just pass it through */
1446 	}
1447 # if MIME8TO7
1448 	else if (bitset(MM_CVTMIME, MimeMode) &&
1449 		 !bitset(EF_DONT_MIME, e->e_flags) &&
1450 		 (!bitset(MM_PASS8BIT, MimeMode) ||
1451 		  bitset(EF_IS_MIME, e->e_flags)))
1452 	{
1453 		/* must convert from 8bit MIME format to 7bit encoded */
1454 		mci->mci_flags |= MCIF_CVT8TO7;
1455 	}
1456 # endif /* MIME8TO7 */
1457 	else if (!bitset(MM_PASS8BIT, MimeMode))
1458 	{
1459 		/* cannot just send a 8-bit version */
1460 		extern char MsgBuf[];
1461 
1462 		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
1463 		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
1464 		return EX_DATAERR;
1465 	}
1466 
1467 	if (bitset(MCIF_DSN, mci->mci_flags))
1468 	{
1469 		if (e->e_envid != NULL &&
1470 		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
1471 		{
1472 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1473 				 " ENVID=%s", e->e_envid);
1474 			bufp += strlen(bufp);
1475 		}
1476 
1477 		/* RET= parameter */
1478 		if (bitset(EF_RET_PARAM, e->e_flags) &&
1479 		    SPACELEFT(optbuf, bufp) > 9)
1480 		{
1481 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1482 				 " RET=%s",
1483 				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
1484 					"HDRS" : "FULL");
1485 			bufp += strlen(bufp);
1486 		}
1487 	}
1488 
1489 	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
1490 	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
1491 # if SASL
1492 	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
1493 # endif /* SASL */
1494 	    )
1495 	{
1496 		snprintf(bufp, SPACELEFT(optbuf, bufp),
1497 			 " AUTH=%s", e->e_auth_param);
1498 		bufp += strlen(bufp);
1499 	}
1500 
1501 	/*
1502 	**  Send the MAIL command.
1503 	**	Designates the sender.
1504 	*/
1505 
1506 	mci->mci_state = MCIS_ACTIVE;
1507 
1508 	if (bitset(EF_RESPONSE, e->e_flags) &&
1509 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
1510 		buf[0] = '\0';
1511 	else
1512 		expand("\201g", buf, sizeof buf, e);
1513 	if (buf[0] == '<')
1514 	{
1515 		/* strip off <angle brackets> (put back on below) */
1516 		bufp = &buf[strlen(buf) - 1];
1517 		if (*bufp == '>')
1518 			*bufp = '\0';
1519 		bufp = &buf[1];
1520 	}
1521 	else
1522 		bufp = buf;
1523 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
1524 	    !bitnset(M_FROMPATH, m->m_flags))
1525 	{
1526 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
1527 	}
1528 	else
1529 	{
1530 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
1531 			*bufp == '@' ? ',' : ':', bufp, optbuf);
1532 	}
1533 	SmtpPhase = mci->mci_phase = "client MAIL";
1534 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1535 			CurHostName, mci->mci_phase);
1536 	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc);
1537 	if (r < 0)
1538 	{
1539 		/* communications failure */
1540 		mci->mci_errno = errno;
1541 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1542 		smtpquit(m, mci, e);
1543 		return EX_TEMPFAIL;
1544 	}
1545 	else if (r == 421)
1546 	{
1547 		/* service shutting down */
1548 		mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
1549 			    SmtpReplyBuffer);
1550 		smtpquit(m, mci, e);
1551 		return EX_TEMPFAIL;
1552 	}
1553 	else if (REPLYTYPE(r) == 4)
1554 	{
1555 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
1556 			    SmtpReplyBuffer);
1557 		return EX_TEMPFAIL;
1558 	}
1559 	else if (REPLYTYPE(r) == 2)
1560 	{
1561 		return EX_OK;
1562 	}
1563 	else if (r == 501)
1564 	{
1565 		/* syntax error in arguments */
1566 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
1567 			    SmtpReplyBuffer);
1568 		return EX_DATAERR;
1569 	}
1570 	else if (r == 553)
1571 	{
1572 		/* mailbox name not allowed */
1573 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
1574 			    SmtpReplyBuffer);
1575 		return EX_DATAERR;
1576 	}
1577 	else if (r == 552)
1578 	{
1579 		/* exceeded storage allocation */
1580 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
1581 			    SmtpReplyBuffer);
1582 		if (bitset(MCIF_SIZE, mci->mci_flags))
1583 			e->e_flags |= EF_NO_BODY_RETN;
1584 		return EX_UNAVAILABLE;
1585 	}
1586 	else if (REPLYTYPE(r) == 5)
1587 	{
1588 		/* unknown error */
1589 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
1590 			    SmtpReplyBuffer);
1591 		return EX_UNAVAILABLE;
1592 	}
1593 
1594 	if (LogLevel > 1)
1595 	{
1596 		sm_syslog(LOG_CRIT, e->e_id,
1597 			  "%.100s: SMTP MAIL protocol error: %s",
1598 			  CurHostName,
1599 			  shortenstring(SmtpReplyBuffer, 403));
1600 	}
1601 
1602 	/* protocol error -- close up */
1603 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1604 		    SmtpReplyBuffer);
1605 	smtpquit(m, mci, e);
1606 	return EX_PROTOCOL;
1607 }
1608 /*
1609 **  SMTPRCPT -- designate recipient.
1610 **
1611 **	Parameters:
1612 **		to -- address of recipient.
1613 **		m -- the mailer we are sending to.
1614 **		mci -- the connection info for this transaction.
1615 **		e -- the envelope for this transaction.
1616 **
1617 **	Returns:
1618 **		exit status corresponding to recipient status.
1619 **
1620 **	Side Effects:
1621 **		Sends the mail via SMTP.
1622 */
1623 
1624 int
1625 smtprcpt(to, m, mci, e)
1626 	ADDRESS *to;
1627 	register MAILER *m;
1628 	MCI *mci;
1629 	ENVELOPE *e;
1630 {
1631 	register int r;
1632 	char *bufp;
1633 	char optbuf[MAXLINE];
1634 	char *enhsc;
1635 
1636 	enhsc = NULL;
1637 	optbuf[0] = '\0';
1638 	bufp = optbuf;
1639 
1640 	/*
1641 	**  warning: in the following it is assumed that the free space
1642 	**  in bufp is sizeof optbuf
1643 	*/
1644 	if (bitset(MCIF_DSN, mci->mci_flags))
1645 	{
1646 		/* NOTIFY= parameter */
1647 		if (bitset(QHASNOTIFY, to->q_flags) &&
1648 		    bitset(QPRIMARY, to->q_flags) &&
1649 		    !bitnset(M_LOCALMAILER, m->m_flags))
1650 		{
1651 			bool firstone = TRUE;
1652 
1653 			(void) strlcat(bufp, " NOTIFY=", sizeof optbuf);
1654 			if (bitset(QPINGONSUCCESS, to->q_flags))
1655 			{
1656 				(void) strlcat(bufp, "SUCCESS", sizeof optbuf);
1657 				firstone = FALSE;
1658 			}
1659 			if (bitset(QPINGONFAILURE, to->q_flags))
1660 			{
1661 				if (!firstone)
1662 					(void) strlcat(bufp, ",",
1663 						       sizeof optbuf);
1664 				(void) strlcat(bufp, "FAILURE", sizeof optbuf);
1665 				firstone = FALSE;
1666 			}
1667 			if (bitset(QPINGONDELAY, to->q_flags))
1668 			{
1669 				if (!firstone)
1670 					(void) strlcat(bufp, ",",
1671 						       sizeof optbuf);
1672 				(void) strlcat(bufp, "DELAY", sizeof optbuf);
1673 				firstone = FALSE;
1674 			}
1675 			if (firstone)
1676 				(void) strlcat(bufp, "NEVER", sizeof optbuf);
1677 			bufp += strlen(bufp);
1678 		}
1679 
1680 		/* ORCPT= parameter */
1681 		if (to->q_orcpt != NULL &&
1682 		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
1683 		{
1684 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1685 				 " ORCPT=%s", to->q_orcpt);
1686 			bufp += strlen(bufp);
1687 		}
1688 	}
1689 
1690 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
1691 
1692 	SmtpPhase = mci->mci_phase = "client RCPT";
1693 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1694 			CurHostName, mci->mci_phase);
1695 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
1696 	to->q_rstatus = newstr(SmtpReplyBuffer);
1697 	to->q_status = ENHSCN(enhsc, smtptodsn(r));
1698 	if (!bitnset(M_LMTP, m->m_flags))
1699 		to->q_statmta = mci->mci_host;
1700 	if (r < 0 || REPLYTYPE(r) == 4)
1701 		return EX_TEMPFAIL;
1702 	else if (REPLYTYPE(r) == 2)
1703 		return EX_OK;
1704 	else if (r == 550)
1705 	{
1706 		to->q_status = ENHSCN(enhsc, "5.1.1");
1707 		return EX_NOUSER;
1708 	}
1709 	else if (r == 551)
1710 	{
1711 		to->q_status = ENHSCN(enhsc, "5.1.6");
1712 		return EX_NOUSER;
1713 	}
1714 	else if (r == 553)
1715 	{
1716 		to->q_status = ENHSCN(enhsc, "5.1.3");
1717 		return EX_NOUSER;
1718 	}
1719 	else if (REPLYTYPE(r) == 5)
1720 	{
1721 		return EX_UNAVAILABLE;
1722 	}
1723 
1724 	if (LogLevel > 1)
1725 	{
1726 		sm_syslog(LOG_CRIT, e->e_id,
1727 			  "%.100s: SMTP RCPT protocol error: %s",
1728 			  CurHostName,
1729 			  shortenstring(SmtpReplyBuffer, 403));
1730 	}
1731 
1732 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1733 		    SmtpReplyBuffer);
1734 	return EX_PROTOCOL;
1735 }
1736 /*
1737 **  SMTPDATA -- send the data and clean up the transaction.
1738 **
1739 **	Parameters:
1740 **		m -- mailer being sent to.
1741 **		mci -- the mailer connection information.
1742 **		e -- the envelope for this message.
1743 **
1744 **	Returns:
1745 **		exit status corresponding to DATA command.
1746 **
1747 **	Side Effects:
1748 **		none.
1749 */
1750 
1751 static jmp_buf	CtxDataTimeout;
1752 
1753 int
1754 smtpdata(m, mci, e)
1755 	MAILER *m;
1756 	register MCI *mci;
1757 	register ENVELOPE *e;
1758 {
1759 	register int r;
1760 	register EVENT *ev;
1761 	int rstat;
1762 	int xstat;
1763 	time_t timeout;
1764 	char *enhsc;
1765 
1766 	enhsc = NULL;
1767 	/*
1768 	**  Send the data.
1769 	**	First send the command and check that it is ok.
1770 	**	Then send the data.
1771 	**	Follow it up with a dot to terminate.
1772 	**	Finally get the results of the transaction.
1773 	*/
1774 
1775 	/* send the command and check ok to proceed */
1776 	smtpmessage("DATA", m, mci);
1777 	SmtpPhase = mci->mci_phase = "client DATA 354";
1778 	sm_setproctitle(TRUE, e, "%s %s: %s",
1779 			qid_printname(e), CurHostName, mci->mci_phase);
1780 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc);
1781 	if (r < 0 || REPLYTYPE(r) == 4)
1782 	{
1783 		smtpquit(m, mci, e);
1784 		return EX_TEMPFAIL;
1785 	}
1786 	else if (REPLYTYPE(r) == 5)
1787 	{
1788 		smtprset(m, mci, e);
1789 		return EX_UNAVAILABLE;
1790 	}
1791 	else if (REPLYTYPE(r) != 3)
1792 	{
1793 		if (LogLevel > 1)
1794 		{
1795 			sm_syslog(LOG_CRIT, e->e_id,
1796 				  "%.100s: SMTP DATA-1 protocol error: %s",
1797 				  CurHostName,
1798 				  shortenstring(SmtpReplyBuffer, 403));
1799 		}
1800 		smtprset(m, mci, e);
1801 		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1802 			    SmtpReplyBuffer);
1803 		return EX_PROTOCOL;
1804 	}
1805 
1806 	/*
1807 	**  Set timeout around data writes.  Make it at least large
1808 	**  enough for DNS timeouts on all recipients plus some fudge
1809 	**  factor.  The main thing is that it should not be infinite.
1810 	*/
1811 
1812 	if (setjmp(CtxDataTimeout) != 0)
1813 	{
1814 		mci->mci_errno = errno;
1815 		mci->mci_state = MCIS_ERROR;
1816 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1817 
1818 		/*
1819 		**  If putbody() couldn't finish due to a timeout,
1820 		**  rewind it here in the timeout handler.  See
1821 		**  comments at the end of putbody() for reasoning.
1822 		*/
1823 
1824 		if (e->e_dfp != NULL)
1825 			(void) bfrewind(e->e_dfp);
1826 
1827 		errno = mci->mci_errno;
1828 		syserr("451 4.4.1 timeout writing message to %s", CurHostName);
1829 		smtpquit(m, mci, e);
1830 		return EX_TEMPFAIL;
1831 	}
1832 
1833 	if (tTd(18, 101))
1834 	{
1835 		/* simulate a DATA timeout */
1836 		timeout = 1;
1837 	}
1838 	else
1839 		timeout = DATA_PROGRESS_TIMEOUT;
1840 
1841 	ev = setevent(timeout, datatimeout, 0);
1842 
1843 
1844 	if (tTd(18, 101))
1845 	{
1846 		/* simulate a DATA timeout */
1847 		(void) sleep(1);
1848 	}
1849 
1850 	/*
1851 	**  Output the actual message.
1852 	*/
1853 
1854 	(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
1855 	(*e->e_putbody)(mci, e, NULL);
1856 
1857 	/*
1858 	**  Cleanup after sending message.
1859 	*/
1860 
1861 	clrevent(ev);
1862 
1863 # if _FFR_CATCH_BROKEN_MTAS
1864 	{
1865 		fd_set readfds;
1866 		struct timeval timeout;
1867 
1868 		FD_ZERO(&readfds);
1869 		FD_SET(fileno(mci->mci_in), &readfds);
1870 		timeout.tv_sec = 0;
1871 		timeout.tv_usec = 0;
1872 		if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds,
1873 			   NULL, NULL, &timeout) > 0 &&
1874 		    FD_ISSET(fileno(mci->mci_in), &readfds))
1875 		{
1876 			/* terminate the message */
1877 			fprintf(mci->mci_out, ".%s", m->m_eol);
1878 			if (TrafficLogFile != NULL)
1879 				fprintf(TrafficLogFile, "%05d >>> .\n",
1880 					(int) getpid());
1881 			if (Verbose)
1882 				nmessage(">>> .");
1883 
1884 			mci->mci_errno = EIO;
1885 			mci->mci_state = MCIS_ERROR;
1886 			mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
1887 			smtpquit(m, mci, e);
1888 			return EX_PROTOCOL;
1889 		}
1890 	}
1891 # endif /* _FFR_CATCH_BROKEN_MTAS */
1892 
1893 	if (ferror(mci->mci_out))
1894 	{
1895 		/* error during processing -- don't send the dot */
1896 		mci->mci_errno = EIO;
1897 		mci->mci_state = MCIS_ERROR;
1898 		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
1899 		smtpquit(m, mci, e);
1900 		return EX_IOERR;
1901 	}
1902 
1903 	/* terminate the message */
1904 	fprintf(mci->mci_out, ".%s", m->m_eol);
1905 	if (TrafficLogFile != NULL)
1906 		fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
1907 	if (Verbose)
1908 		nmessage(">>> .");
1909 
1910 	/* check for the results of the transaction */
1911 	SmtpPhase = mci->mci_phase = "client DATA status";
1912 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1913 			CurHostName, mci->mci_phase);
1914 	if (bitnset(M_LMTP, m->m_flags))
1915 		return EX_OK;
1916 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
1917 	if (r < 0)
1918 	{
1919 		smtpquit(m, mci, e);
1920 		return EX_TEMPFAIL;
1921 	}
1922 	mci->mci_state = MCIS_OPEN;
1923 	xstat = EX_NOTSTICKY;
1924 	if (r == 452)
1925 		rstat = EX_TEMPFAIL;
1926 	else if (REPLYTYPE(r) == 4)
1927 		rstat = xstat = EX_TEMPFAIL;
1928 	else if (REPLYCLASS(r) != 5)
1929 		rstat = xstat = EX_PROTOCOL;
1930 	else if (REPLYTYPE(r) == 2)
1931 		rstat = xstat = EX_OK;
1932 	else if (REPLYTYPE(r) == 5)
1933 		rstat = EX_UNAVAILABLE;
1934 	else
1935 		rstat = EX_PROTOCOL;
1936 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
1937 		    SmtpReplyBuffer);
1938 	if (e->e_statmsg != NULL)
1939 		free(e->e_statmsg);
1940 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
1941 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
1942 		r += 5;
1943 	else
1944 		r = 4;
1945 	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
1946 	if (rstat != EX_PROTOCOL)
1947 		return rstat;
1948 	if (LogLevel > 1)
1949 	{
1950 		sm_syslog(LOG_CRIT, e->e_id,
1951 			  "%.100s: SMTP DATA-2 protocol error: %s",
1952 			  CurHostName,
1953 			  shortenstring(SmtpReplyBuffer, 403));
1954 	}
1955 	return rstat;
1956 }
1957 
1958 
1959 static void
1960 datatimeout()
1961 {
1962 	if (DataProgress)
1963 	{
1964 		time_t timeout;
1965 		register EVENT *ev;
1966 
1967 		/* check back again later */
1968 		if (tTd(18, 101))
1969 		{
1970 			/* simulate a DATA timeout */
1971 			timeout = 1;
1972 		}
1973 		else
1974 			timeout = DATA_PROGRESS_TIMEOUT;
1975 
1976 		DataProgress = FALSE;
1977 		ev = setevent(timeout, datatimeout, 0);
1978 	}
1979 	else
1980 	{
1981 		/* no progress, give up */
1982 		longjmp(CtxDataTimeout, 1);
1983 	}
1984 }
1985 /*
1986 **  SMTPGETSTAT -- get status code from DATA in LMTP
1987 **
1988 **	Parameters:
1989 **		m -- the mailer to which we are sending the message.
1990 **		mci -- the mailer connection structure.
1991 **		e -- the current envelope.
1992 **
1993 **	Returns:
1994 **		The exit status corresponding to the reply code.
1995 */
1996 
1997 int
1998 smtpgetstat(m, mci, e)
1999 	MAILER *m;
2000 	MCI *mci;
2001 	ENVELOPE *e;
2002 {
2003 	int r;
2004 	int status;
2005 	char *enhsc;
2006 
2007 	enhsc = NULL;
2008 	/* check for the results of the transaction */
2009 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
2010 	if (r < 0)
2011 	{
2012 		smtpquit(m, mci, e);
2013 		return EX_TEMPFAIL;
2014 	}
2015 	if (REPLYTYPE(r) == 4)
2016 		status = EX_TEMPFAIL;
2017 	else if (REPLYCLASS(r) != 5)
2018 		status = EX_PROTOCOL;
2019 	else if (REPLYTYPE(r) == 2)
2020 		status = EX_OK;
2021 	else if (REPLYTYPE(r) == 5)
2022 		status = EX_UNAVAILABLE;
2023 	else
2024 		status = EX_PROTOCOL;
2025 	if (e->e_statmsg != NULL)
2026 		free(e->e_statmsg);
2027 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2028 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2029 		r += 5;
2030 	else
2031 		r = 4;
2032 	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
2033 	mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)),
2034 		    SmtpReplyBuffer);
2035 	if (LogLevel > 1 && status == EX_PROTOCOL)
2036 	{
2037 		sm_syslog(LOG_CRIT, e->e_id,
2038 			  "%.100s: SMTP DATA-3 protocol error: %s",
2039 			  CurHostName,
2040 			  shortenstring(SmtpReplyBuffer, 403));
2041 	}
2042 	return status;
2043 }
2044 /*
2045 **  SMTPQUIT -- close the SMTP connection.
2046 **
2047 **	Parameters:
2048 **		m -- a pointer to the mailer.
2049 **		mci -- the mailer connection information.
2050 **		e -- the current envelope.
2051 **
2052 **	Returns:
2053 **		none.
2054 **
2055 **	Side Effects:
2056 **		sends the final protocol and closes the connection.
2057 */
2058 
2059 void
2060 smtpquit(m, mci, e)
2061 	register MAILER *m;
2062 	register MCI *mci;
2063 	ENVELOPE *e;
2064 {
2065 	bool oldSuprErrs = SuprErrs;
2066 	int rcode;
2067 
2068 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2069 	if (CurHostName == NULL)
2070 		CurHostName = MyHostName;
2071 
2072 	/*
2073 	**	Suppress errors here -- we may be processing a different
2074 	**	job when we do the quit connection, and we don't want the
2075 	**	new job to be penalized for something that isn't it's
2076 	**	problem.
2077 	*/
2078 
2079 	SuprErrs = TRUE;
2080 
2081 	/* send the quit message if we haven't gotten I/O error */
2082 	if (mci->mci_state != MCIS_ERROR &&
2083 	    mci->mci_state != MCIS_QUITING)
2084 	{
2085 		int origstate = mci->mci_state;
2086 
2087 		SmtpPhase = "client QUIT";
2088 		mci->mci_state = MCIS_QUITING;
2089 		smtpmessage("QUIT", m, mci);
2090 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL);
2091 		SuprErrs = oldSuprErrs;
2092 		if (mci->mci_state == MCIS_CLOSED ||
2093 		    origstate == MCIS_CLOSED)
2094 			return;
2095 	}
2096 
2097 	/* now actually close the connection and pick up the zombie */
2098 	rcode = endmailer(mci, e, NULL);
2099 	if (rcode != EX_OK)
2100 	{
2101 		char *mailer = NULL;
2102 
2103 		if (mci->mci_mailer != NULL &&
2104 		    mci->mci_mailer->m_name != NULL)
2105 			mailer = mci->mci_mailer->m_name;
2106 
2107 		/* look for naughty mailers */
2108 		sm_syslog(LOG_ERR, e->e_id,
2109 			  "smtpquit: mailer%s%s exited with exit value %d\n",
2110 			  mailer == NULL ? "" : " ",
2111 			  mailer == NULL ? "" : mailer,
2112 			  rcode);
2113 	}
2114 
2115 	SuprErrs = oldSuprErrs;
2116 }
2117 /*
2118 **  SMTPRSET -- send a RSET (reset) command
2119 */
2120 
2121 void
2122 smtprset(m, mci, e)
2123 	register MAILER *m;
2124 	register MCI *mci;
2125 	ENVELOPE *e;
2126 {
2127 	int r;
2128 
2129 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2130 	if (CurHostName == NULL)
2131 		CurHostName = MyHostName;
2132 
2133 	SmtpPhase = "client RSET";
2134 	smtpmessage("RSET", m, mci);
2135 	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
2136 	if (r < 0)
2137 		mci->mci_state = MCIS_ERROR;
2138 	else
2139 	{
2140 		/*
2141 		**  Any response is deemed to be acceptable.
2142 		**  The standard does not state the proper action
2143 		**  to take when a value other than 250 is received.
2144 		*/
2145 
2146 		mci->mci_state = MCIS_OPEN;
2147 		return;
2148 	}
2149 	smtpquit(m, mci, e);
2150 }
2151 /*
2152 **  SMTPPROBE -- check the connection state
2153 */
2154 
2155 int
2156 smtpprobe(mci)
2157 	register MCI *mci;
2158 {
2159 	int r;
2160 	MAILER *m = mci->mci_mailer;
2161 	ENVELOPE *e;
2162 	extern ENVELOPE BlankEnvelope;
2163 
2164 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2165 	if (CurHostName == NULL)
2166 		CurHostName = MyHostName;
2167 
2168 	e = &BlankEnvelope;
2169 	SmtpPhase = "client probe";
2170 	smtpmessage("RSET", m, mci);
2171 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL);
2172 	if (r < 0 || REPLYTYPE(r) != 2)
2173 		smtpquit(m, mci, e);
2174 	return r;
2175 }
2176 /*
2177 **  REPLY -- read arpanet reply
2178 **
2179 **	Parameters:
2180 **		m -- the mailer we are reading the reply from.
2181 **		mci -- the mailer connection info structure.
2182 **		e -- the current envelope.
2183 **		timeout -- the timeout for reads.
2184 **		pfunc -- processing function called on each line of response.
2185 **			If null, no special processing is done.
2186 **
2187 **	Returns:
2188 **		reply code it reads.
2189 **
2190 **	Side Effects:
2191 **		flushes the mail file.
2192 */
2193 
2194 int
2195 reply(m, mci, e, timeout, pfunc, enhstat)
2196 	MAILER *m;
2197 	MCI *mci;
2198 	ENVELOPE *e;
2199 	time_t timeout;
2200 	void (*pfunc)();
2201 	char **enhstat;
2202 {
2203 	register char *bufp;
2204 	register int r;
2205 	bool firstline = TRUE;
2206 	char junkbuf[MAXLINE];
2207 	static char enhstatcode[ENHSCLEN];
2208 	int save_errno;
2209 
2210 	if (mci->mci_out != NULL)
2211 		(void) fflush(mci->mci_out);
2212 
2213 	if (tTd(18, 1))
2214 		dprintf("reply\n");
2215 
2216 	/*
2217 	**  Read the input line, being careful not to hang.
2218 	*/
2219 
2220 	bufp = SmtpReplyBuffer;
2221 	for (;;)
2222 	{
2223 		register char *p;
2224 
2225 		/* actually do the read */
2226 		if (e->e_xfp != NULL)
2227 			(void) fflush(e->e_xfp);	/* for debugging */
2228 
2229 		/* if we are in the process of closing just give the code */
2230 		if (mci->mci_state == MCIS_CLOSED)
2231 			return SMTPCLOSING;
2232 
2233 		if (mci->mci_out != NULL)
2234 			(void) fflush(mci->mci_out);
2235 
2236 		/* get the line from the other side */
2237 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
2238 		mci->mci_lastuse = curtime();
2239 
2240 		if (p == NULL)
2241 		{
2242 			bool oldholderrs;
2243 			extern char MsgBuf[];
2244 
2245 			/* if the remote end closed early, fake an error */
2246 			if (errno == 0)
2247 # ifdef ECONNRESET
2248 				errno = ECONNRESET;
2249 # else /* ECONNRESET */
2250 				errno = EPIPE;
2251 # endif /* ECONNRESET */
2252 
2253 			mci->mci_errno = errno;
2254 			oldholderrs = HoldErrs;
2255 			HoldErrs = TRUE;
2256 			usrerr("451 4.4.1 reply: read error from %s",
2257 			       CurHostName == NULL ? "NO_HOST" : CurHostName);
2258 
2259 			/* errors on QUIT should not be persistent */
2260 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
2261 				mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
2262 
2263 			/* if debugging, pause so we can see state */
2264 			if (tTd(18, 100))
2265 				(void) pause();
2266 			mci->mci_state = MCIS_ERROR;
2267 			save_errno = errno;
2268 			smtpquit(m, mci, e);
2269 # if XDEBUG
2270 			{
2271 				char wbuf[MAXLINE];
2272 				int wbufleft = sizeof wbuf;
2273 
2274 				p = wbuf;
2275 				if (e->e_to != NULL)
2276 				{
2277 					int plen;
2278 
2279 					snprintf(p, wbufleft, "%s... ",
2280 						shortenstring(e->e_to, MAXSHORTSTR));
2281 					plen = strlen(p);
2282 					p += plen;
2283 					wbufleft -= plen;
2284 				}
2285 				snprintf(p, wbufleft, "reply(%.100s) during %s",
2286 					 CurHostName == NULL ? "NO_HOST" : CurHostName,
2287 					 SmtpPhase);
2288 				checkfd012(wbuf);
2289 			}
2290 # endif /* XDEBUG */
2291 			errno = save_errno;
2292 			HoldErrs = oldholderrs;
2293 			return -1;
2294 		}
2295 		fixcrlf(bufp, TRUE);
2296 
2297 		/* EHLO failure is not a real error */
2298 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
2299 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
2300 		{
2301 			/* serious error -- log the previous command */
2302 			if (SmtpNeedIntro)
2303 			{
2304 				/* inform user who we are chatting with */
2305 				fprintf(CurEnv->e_xfp,
2306 					"... while talking to %s:\n",
2307 					CurHostName == NULL ? "NO_HOST" : CurHostName);
2308 				SmtpNeedIntro = FALSE;
2309 			}
2310 			if (SmtpMsgBuffer[0] != '\0')
2311 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
2312 			SmtpMsgBuffer[0] = '\0';
2313 
2314 			/* now log the message as from the other side */
2315 			fprintf(e->e_xfp, "<<< %s\n", bufp);
2316 		}
2317 
2318 		/* display the input for verbose mode */
2319 		if (Verbose)
2320 			nmessage("050 %s", bufp);
2321 
2322 		/* ignore improperly formatted input */
2323 		if (!ISSMTPREPLY(bufp))
2324 			continue;
2325 
2326 		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2327 		    enhstat != NULL &&
2328 		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
2329 			*enhstat = enhstatcode;
2330 
2331 		/* process the line */
2332 		if (pfunc != NULL)
2333 			(*pfunc)(bufp, firstline, m, mci, e);
2334 
2335 		firstline = FALSE;
2336 
2337 		/* decode the reply code */
2338 		r = atoi(bufp);
2339 
2340 		/* extra semantics: 0xx codes are "informational" */
2341 		if (r < 100)
2342 			continue;
2343 
2344 		/* if no continuation lines, return this line */
2345 		if (bufp[3] != '-')
2346 			break;
2347 
2348 		/* first line of real reply -- ignore rest */
2349 		bufp = junkbuf;
2350 	}
2351 
2352 	/*
2353 	**  Now look at SmtpReplyBuffer -- only care about the first
2354 	**  line of the response from here on out.
2355 	*/
2356 
2357 	/* save temporary failure messages for posterity */
2358 	if (SmtpReplyBuffer[0] == '4' &&
2359 	    (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
2360 		snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
2361 
2362 	/* reply code 421 is "Service Shutting Down" */
2363 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
2364 	{
2365 		/* send the quit protocol */
2366 		mci->mci_state = MCIS_SSD;
2367 		smtpquit(m, mci, e);
2368 	}
2369 
2370 	return r;
2371 }
2372 /*
2373 **  SMTPMESSAGE -- send message to server
2374 **
2375 **	Parameters:
2376 **		f -- format
2377 **		m -- the mailer to control formatting.
2378 **		a, b, c -- parameters
2379 **
2380 **	Returns:
2381 **		none.
2382 **
2383 **	Side Effects:
2384 **		writes message to mci->mci_out.
2385 */
2386 
2387 /*VARARGS1*/
2388 void
2389 # ifdef __STDC__
2390 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
2391 # else /* __STDC__ */
2392 smtpmessage(f, m, mci, va_alist)
2393 	char *f;
2394 	MAILER *m;
2395 	MCI *mci;
2396 	va_dcl
2397 # endif /* __STDC__ */
2398 {
2399 	VA_LOCAL_DECL
2400 
2401 	VA_START(mci);
2402 	(void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
2403 	VA_END;
2404 
2405 	if (tTd(18, 1) || Verbose)
2406 		nmessage(">>> %s", SmtpMsgBuffer);
2407 	if (TrafficLogFile != NULL)
2408 		fprintf(TrafficLogFile, "%05d >>> %s\n",
2409 			(int) getpid(), SmtpMsgBuffer);
2410 	if (mci->mci_out != NULL)
2411 	{
2412 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
2413 			m == NULL ? "\r\n" : m->m_eol);
2414 	}
2415 	else if (tTd(18, 1))
2416 	{
2417 		dprintf("smtpmessage: NULL mci_out\n");
2418 	}
2419 }
2420 
2421 #endif /* SMTP */
2422