xref: /freebsd/contrib/sendmail/src/usersmtp.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
1 /*
2  * Copyright (c) 1998-2005 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 SM_RCSID("@(#)$Id: usersmtp.c,v 8.463 2005/03/16 00:36:09 ca Exp $")
17 
18 #include <sysexits.h>
19 
20 
21 static void	datatimeout __P((int));
22 static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
23 static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
24 static int	smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
25 
26 #if SASL
27 extern void	*sm_sasl_malloc __P((unsigned long));
28 extern void	sm_sasl_free __P((void *));
29 #endif /* SASL */
30 
31 /*
32 **  USERSMTP -- run SMTP protocol from the user end.
33 **
34 **	This protocol is described in RFC821.
35 */
36 
37 #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
38 #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
39 #define SMTPCLOSING	421			/* "Service Shutting Down" */
40 
41 #define ENHSCN(e, d)	((e) == NULL ? (d) : (e))
42 
43 #define ENHSCN_RPOOL(e, d, rpool) \
44 	((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
45 
46 static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
47 static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
48 static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
49 /*
50 **  SMTPINIT -- initialize SMTP.
51 **
52 **	Opens the connection and sends the initial protocol.
53 **
54 **	Parameters:
55 **		m -- mailer to create connection to.
56 **		mci -- the mailer connection info.
57 **		e -- the envelope.
58 **		onlyhelo -- send only helo command?
59 **
60 **	Returns:
61 **		none.
62 **
63 **	Side Effects:
64 **		creates connection and sends initial protocol.
65 */
66 
67 void
68 smtpinit(m, mci, e, onlyhelo)
69 	MAILER *m;
70 	register MCI *mci;
71 	ENVELOPE *e;
72 	bool onlyhelo;
73 {
74 	register int r;
75 	int state;
76 	register char *p;
77 	register char *hn;
78 	char *enhsc;
79 
80 	enhsc = NULL;
81 	if (tTd(18, 1))
82 	{
83 		sm_dprintf("smtpinit ");
84 		mci_dump(sm_debug_file(), mci, false);
85 	}
86 
87 	/*
88 	**  Open the connection to the mailer.
89 	*/
90 
91 	SmtpError[0] = '\0';
92 	SmtpMsgBuffer[0] = '\0';
93 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
94 	if (CurHostName == NULL)
95 		CurHostName = MyHostName;
96 	SmtpNeedIntro = true;
97 	state = mci->mci_state;
98 	switch (state)
99 	{
100 	  case MCIS_MAIL:
101 	  case MCIS_RCPT:
102 	  case MCIS_DATA:
103 		/* need to clear old information */
104 		smtprset(m, mci, e);
105 		/* FALLTHROUGH */
106 
107 	  case MCIS_OPEN:
108 		if (!onlyhelo)
109 			return;
110 		break;
111 
112 	  case MCIS_ERROR:
113 	  case MCIS_QUITING:
114 	  case MCIS_SSD:
115 		/* shouldn't happen */
116 		smtpquit(m, mci, e);
117 		/* FALLTHROUGH */
118 
119 	  case MCIS_CLOSED:
120 		syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
121 		return;
122 
123 	  case MCIS_OPENING:
124 		break;
125 	}
126 	if (onlyhelo)
127 		goto helo;
128 
129 	mci->mci_state = MCIS_OPENING;
130 	clrsessenvelope(e);
131 
132 	/*
133 	**  Get the greeting message.
134 	**	This should appear spontaneously.  Give it five minutes to
135 	**	happen.
136 	*/
137 
138 	SmtpPhase = mci->mci_phase = "client greeting";
139 	sm_setproctitle(true, e, "%s %s: %s",
140 			qid_printname(e), CurHostName, mci->mci_phase);
141 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL,
142 		XS_DEFAULT);
143 	if (r < 0)
144 		goto tempfail1;
145 	if (REPLYTYPE(r) == 4)
146 		goto tempfail2;
147 	if (REPLYTYPE(r) != 2)
148 		goto unavailable;
149 
150 	/*
151 	**  Send the HELO command.
152 	**	My mother taught me to always introduce myself.
153 	*/
154 
155 helo:
156 	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
157 		mci->mci_flags |= MCIF_ESMTP;
158 	hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
159 
160 tryhelo:
161 #if _FFR_IGNORE_EXT_ON_HELO
162 	mci->mci_flags &= ~MCIF_HELO;
163 #endif /* _FFR_IGNORE_EXT_ON_HELO */
164 	if (bitnset(M_LMTP, m->m_flags))
165 	{
166 		smtpmessage("LHLO %s", m, mci, hn);
167 		SmtpPhase = mci->mci_phase = "client LHLO";
168 	}
169 	else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
170 		 !bitnset(M_FSMTP, m->m_flags))
171 	{
172 		smtpmessage("EHLO %s", m, mci, hn);
173 		SmtpPhase = mci->mci_phase = "client EHLO";
174 	}
175 	else
176 	{
177 		smtpmessage("HELO %s", m, mci, hn);
178 		SmtpPhase = mci->mci_phase = "client HELO";
179 #if _FFR_IGNORE_EXT_ON_HELO
180 		mci->mci_flags |= MCIF_HELO;
181 #endif /* _FFR_IGNORE_EXT_ON_HELO */
182 	}
183 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
184 			CurHostName, mci->mci_phase);
185 	r = reply(m, mci, e,
186 		  bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
187 					      : TimeOuts.to_helo,
188 		  helo_options, NULL, XS_DEFAULT);
189 	if (r < 0)
190 		goto tempfail1;
191 	else if (REPLYTYPE(r) == 5)
192 	{
193 		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
194 		    !bitnset(M_LMTP, m->m_flags))
195 		{
196 			/* try old SMTP instead */
197 			mci->mci_flags &= ~MCIF_ESMTP;
198 			goto tryhelo;
199 		}
200 		goto unavailable;
201 	}
202 	else if (REPLYTYPE(r) != 2)
203 		goto tempfail2;
204 
205 	/*
206 	**  Check to see if we actually ended up talking to ourself.
207 	**  This means we didn't know about an alias or MX, or we managed
208 	**  to connect to an echo server.
209 	*/
210 
211 	p = strchr(&SmtpReplyBuffer[4], ' ');
212 	if (p != NULL)
213 		*p = '\0';
214 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
215 	    !bitnset(M_LMTP, m->m_flags) &&
216 	    sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
217 	{
218 		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
219 			CurHostName);
220 		mci_setstat(mci, EX_CONFIG, "5.3.5",
221 			    "553 5.3.5 system config error");
222 		mci->mci_errno = 0;
223 		smtpquit(m, mci, e);
224 		return;
225 	}
226 
227 	/*
228 	**  If this is expected to be another sendmail, send some internal
229 	**  commands.
230 	**  If we're running as MSP, "propagate" -v flag if possible.
231 	*/
232 
233 	if ((UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
234 # if !_FFR_DEPRECATE_MAILER_FLAG_I
235 	    || bitnset(M_INTERNAL, m->m_flags)
236 # endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
237 	   )
238 	{
239 		/* tell it to be verbose */
240 		smtpmessage("VERB", m, mci);
241 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc,
242 			XS_DEFAULT);
243 		if (r < 0)
244 			goto tempfail1;
245 	}
246 
247 	if (mci->mci_state != MCIS_CLOSED)
248 	{
249 		mci->mci_state = MCIS_OPEN;
250 		return;
251 	}
252 
253 	/* got a 421 error code during startup */
254 
255   tempfail1:
256 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
257 	if (mci->mci_state != MCIS_CLOSED)
258 		smtpquit(m, mci, e);
259 	return;
260 
261   tempfail2:
262 	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
263 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
264 		    SmtpReplyBuffer);
265 	if (mci->mci_state != MCIS_CLOSED)
266 		smtpquit(m, mci, e);
267 	return;
268 
269   unavailable:
270 	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
271 	smtpquit(m, mci, e);
272 	return;
273 }
274 /*
275 **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
276 **
277 **	Parameters:
278 **		line -- the response line.
279 **		firstline -- set if this is the first line of the reply.
280 **		m -- the mailer.
281 **		mci -- the mailer connection info.
282 **		e -- the envelope.
283 **
284 **	Returns:
285 **		none.
286 */
287 
288 static void
289 esmtp_check(line, firstline, m, mci, e)
290 	char *line;
291 	bool firstline;
292 	MAILER *m;
293 	register MCI *mci;
294 	ENVELOPE *e;
295 {
296 	if (strstr(line, "ESMTP") != NULL)
297 		mci->mci_flags |= MCIF_ESMTP;
298 
299 	/*
300 	**  Dirty hack below. Quoting the author:
301 	**  This was a response to people who wanted SMTP transmission to be
302 	**  just-send-8 by default.  Essentially, you could put this tag into
303 	**  your greeting message to behave as though the F=8 flag was set on
304 	**  the mailer.
305 	*/
306 
307 	if (strstr(line, "8BIT-OK") != NULL)
308 		mci->mci_flags |= MCIF_8BITOK;
309 }
310 
311 #if SASL
312 /* specify prototype so compiler can check calls */
313 static char *str_union __P((char *, char *, SM_RPOOL_T *));
314 
315 /*
316 **  STR_UNION -- create the union of two lists
317 **
318 **	Parameters:
319 **		s1, s2 -- lists of items (separated by single blanks).
320 **		rpool -- resource pool from which result is allocated.
321 **
322 **	Returns:
323 **		the union of both lists.
324 */
325 
326 static char *
327 str_union(s1, s2, rpool)
328 	char *s1, *s2;
329 	SM_RPOOL_T *rpool;
330 {
331 	char *hr, *h1, *h, *res;
332 	int l1, l2, rl;
333 
334 	if (s1 == NULL || *s1 == '\0')
335 		return s2;
336 	if (s2 == NULL || *s2 == '\0')
337 		return s1;
338 	l1 = strlen(s1);
339 	l2 = strlen(s2);
340 	rl = l1 + l2;
341 	res = (char *) sm_rpool_malloc(rpool, rl + 2);
342 	if (res == NULL)
343 	{
344 		if (l1 > l2)
345 			return s1;
346 		return s2;
347 	}
348 	(void) sm_strlcpy(res, s1, rl);
349 	hr = res + l1;
350 	h1 = s2;
351 	h = s2;
352 
353 	/* walk through s2 */
354 	while (h != NULL && *h1 != '\0')
355 	{
356 		/* is there something after the current word? */
357 		if ((h = strchr(h1, ' ')) != NULL)
358 			*h = '\0';
359 		l1 = strlen(h1);
360 
361 		/* does the current word appear in s1 ? */
362 		if (iteminlist(h1, s1, " ") == NULL)
363 		{
364 			/* add space as delimiter */
365 			*hr++ = ' ';
366 
367 			/* copy the item */
368 			memcpy(hr, h1, l1);
369 
370 			/* advance pointer in result list */
371 			hr += l1;
372 			*hr = '\0';
373 		}
374 		if (h != NULL)
375 		{
376 			/* there are more items */
377 			*h = ' ';
378 			h1 = h + 1;
379 		}
380 	}
381 	return res;
382 }
383 #endif /* SASL */
384 
385 /*
386 **  HELO_OPTIONS -- process the options on a HELO line.
387 **
388 **	Parameters:
389 **		line -- the response line.
390 **		firstline -- set if this is the first line of the reply.
391 **		m -- the mailer.
392 **		mci -- the mailer connection info.
393 **		e -- the envelope (unused).
394 **
395 **	Returns:
396 **		none.
397 */
398 
399 static void
400 helo_options(line, firstline, m, mci, e)
401 	char *line;
402 	bool firstline;
403 	MAILER *m;
404 	register MCI *mci;
405 	ENVELOPE *e;
406 {
407 	register char *p;
408 #if _FFR_IGNORE_EXT_ON_HELO
409 	static bool logged = false;
410 #endif /* _FFR_IGNORE_EXT_ON_HELO */
411 
412 	if (firstline)
413 	{
414 #if SASL
415 		mci->mci_saslcap = NULL;
416 #endif /* SASL */
417 #if _FFR_IGNORE_EXT_ON_HELO
418 		logged = false;
419 #endif /* _FFR_IGNORE_EXT_ON_HELO */
420 		return;
421 	}
422 #if _FFR_IGNORE_EXT_ON_HELO
423 	else if (bitset(MCIF_HELO, mci->mci_flags))
424 	{
425 		if (LogLevel > 8 && !logged)
426 		{
427 			sm_syslog(LOG_WARNING, NOQID,
428 				  "server=%s [%s] returned extensions despite HELO command",
429 				  macvalue(macid("{server_name}"), e),
430 				  macvalue(macid("{server_addr}"), e));
431 			logged = true;
432 		}
433 		return;
434 	}
435 #endif /* _FFR_IGNORE_EXT_ON_HELO */
436 
437 	if (strlen(line) < 5)
438 		return;
439 	line += 4;
440 	p = strpbrk(line, " =");
441 	if (p != NULL)
442 		*p++ = '\0';
443 	if (sm_strcasecmp(line, "size") == 0)
444 	{
445 		mci->mci_flags |= MCIF_SIZE;
446 		if (p != NULL)
447 			mci->mci_maxsize = atol(p);
448 	}
449 	else if (sm_strcasecmp(line, "8bitmime") == 0)
450 	{
451 		mci->mci_flags |= MCIF_8BITMIME;
452 		mci->mci_flags &= ~MCIF_7BIT;
453 	}
454 	else if (sm_strcasecmp(line, "expn") == 0)
455 		mci->mci_flags |= MCIF_EXPN;
456 	else if (sm_strcasecmp(line, "dsn") == 0)
457 		mci->mci_flags |= MCIF_DSN;
458 	else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
459 		mci->mci_flags |= MCIF_ENHSTAT;
460 	else if (sm_strcasecmp(line, "pipelining") == 0)
461 		mci->mci_flags |= MCIF_PIPELINED;
462 	else if (sm_strcasecmp(line, "verb") == 0)
463 		mci->mci_flags |= MCIF_VERB;
464 #if STARTTLS
465 	else if (sm_strcasecmp(line, "starttls") == 0)
466 		mci->mci_flags |= MCIF_TLS;
467 #endif /* STARTTLS */
468 	else if (sm_strcasecmp(line, "deliverby") == 0)
469 	{
470 		mci->mci_flags |= MCIF_DLVR_BY;
471 		if (p != NULL)
472 			mci->mci_min_by = atol(p);
473 	}
474 #if SASL
475 	else if (sm_strcasecmp(line, "auth") == 0)
476 	{
477 		if (p != NULL && *p != '\0')
478 		{
479 			if (mci->mci_saslcap != NULL)
480 			{
481 				/*
482 				**  Create the union with previous auth
483 				**  offerings because we recognize "auth "
484 				**  and "auth=" (old format).
485 				*/
486 
487 				mci->mci_saslcap = str_union(mci->mci_saslcap,
488 							     p, mci->mci_rpool);
489 				mci->mci_flags |= MCIF_AUTH;
490 			}
491 			else
492 			{
493 				int l;
494 
495 				l = strlen(p) + 1;
496 				mci->mci_saslcap = (char *)
497 					sm_rpool_malloc(mci->mci_rpool, l);
498 				if (mci->mci_saslcap != NULL)
499 				{
500 					(void) sm_strlcpy(mci->mci_saslcap, p,
501 							  l);
502 					mci->mci_flags |= MCIF_AUTH;
503 				}
504 			}
505 		}
506 	}
507 #endif /* SASL */
508 }
509 #if SASL
510 
511 static int getsimple	__P((void *, int, const char **, unsigned *));
512 static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
513 static int saslgetrealm	__P((void *, int, const char **, const char **));
514 static int readauth	__P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
515 static int getauth	__P((MCI *, ENVELOPE *, SASL_AI_T *));
516 static char *removemech	__P((char *, char *, SM_RPOOL_T *));
517 static int attemptauth	__P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
518 
519 static sasl_callback_t callbacks[] =
520 {
521 	{	SASL_CB_GETREALM,	&saslgetrealm,	NULL	},
522 #define CB_GETREALM_IDX	0
523 	{	SASL_CB_PASS,		&getsecret,	NULL	},
524 #define CB_PASS_IDX	1
525 	{	SASL_CB_USER,		&getsimple,	NULL	},
526 #define CB_USER_IDX	2
527 	{	SASL_CB_AUTHNAME,	&getsimple,	NULL	},
528 #define CB_AUTHNAME_IDX	3
529 	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
530 #define CB_SAFESASL_IDX	4
531 	{	SASL_CB_LIST_END,	NULL,		NULL	}
532 };
533 
534 /*
535 **  INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
536 **
537 **	Parameters:
538 **		none.
539 **
540 **	Returns:
541 **		SASL_OK -- if successful.
542 **		SASL error code -- otherwise.
543 **
544 **	Side Effects:
545 **		checks/sets sasl_clt_init.
546 */
547 
548 static bool sasl_clt_init = false;
549 
550 static int
551 init_sasl_client()
552 {
553 	int result;
554 
555 	if (sasl_clt_init)
556 		return SASL_OK;
557 	result = sasl_client_init(callbacks);
558 
559 	/* should we retry later again or just remember that it failed? */
560 	if (result == SASL_OK)
561 		sasl_clt_init = true;
562 	return result;
563 }
564 /*
565 **  STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
566 **
567 **	Parameters:
568 **		none.
569 **
570 **	Returns:
571 **		none.
572 **
573 **	Side Effects:
574 **		checks/sets sasl_clt_init.
575 */
576 
577 void
578 stop_sasl_client()
579 {
580 	if (!sasl_clt_init)
581 		return;
582 	sasl_clt_init = false;
583 	sasl_done();
584 }
585 /*
586 **  GETSASLDATA -- process the challenges from the SASL protocol
587 **
588 **	This gets the relevant sasl response data out of the reply
589 **	from the server.
590 **
591 **	Parameters:
592 **		line -- the response line.
593 **		firstline -- set if this is the first line of the reply.
594 **		m -- the mailer.
595 **		mci -- the mailer connection info.
596 **		e -- the envelope (unused).
597 **
598 **	Returns:
599 **		none.
600 */
601 
602 static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
603 
604 static void
605 getsasldata(line, firstline, m, mci, e)
606 	char *line;
607 	bool firstline;
608 	MAILER *m;
609 	register MCI *mci;
610 	ENVELOPE *e;
611 {
612 	int len;
613 	int result;
614 # if SASL < 20000
615 	char *out;
616 # endif /* SASL < 20000 */
617 
618 	/* if not a continue we don't care about it */
619 	len = strlen(line);
620 	if ((len <= 4) ||
621 	    (line[0] != '3') ||
622 	     !isascii(line[1]) || !isdigit(line[1]) ||
623 	     !isascii(line[2]) || !isdigit(line[2]))
624 	{
625 		SM_FREE_CLR(mci->mci_sasl_string);
626 		return;
627 	}
628 
629 	/* forget about "334 " */
630 	line += 4;
631 	len -= 4;
632 # if SASL >= 20000
633 	/* XXX put this into a macro/function? It's duplicated below */
634 	if (mci->mci_sasl_string != NULL)
635 	{
636 		if (mci->mci_sasl_string_len <= len)
637 		{
638 			sm_free(mci->mci_sasl_string); /* XXX */
639 			mci->mci_sasl_string = xalloc(len + 1);
640 		}
641 	}
642 	else
643 		mci->mci_sasl_string = xalloc(len + 1);
644 
645 	result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
646 			       (unsigned int *) &mci->mci_sasl_string_len);
647 	if (result != SASL_OK)
648 	{
649 		mci->mci_sasl_string_len = 0;
650 		*mci->mci_sasl_string = '\0';
651 	}
652 # else /* SASL >= 20000 */
653 	out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
654 	result = sasl_decode64(line, len, out, (unsigned int *) &len);
655 	if (result != SASL_OK)
656 	{
657 		len = 0;
658 		*out = '\0';
659 	}
660 
661 	/*
662 	**  mci_sasl_string is "shared" with Cyrus-SASL library; hence
663 	**	it can't be in an rpool unless we use the same memory
664 	**	management mechanism (with same rpool!) for Cyrus SASL.
665 	*/
666 
667 	if (mci->mci_sasl_string != NULL)
668 	{
669 		if (mci->mci_sasl_string_len <= len)
670 		{
671 			sm_free(mci->mci_sasl_string); /* XXX */
672 			mci->mci_sasl_string = xalloc(len + 1);
673 		}
674 	}
675 	else
676 		mci->mci_sasl_string = xalloc(len + 1);
677 
678 	memcpy(mci->mci_sasl_string, out, len);
679 	mci->mci_sasl_string[len] = '\0';
680 	mci->mci_sasl_string_len = len;
681 # endif /* SASL >= 20000 */
682 	return;
683 }
684 /*
685 **  READAUTH -- read auth values from a file
686 **
687 **	Parameters:
688 **		filename -- name of file to read.
689 **		safe -- if set, this is a safe read.
690 **		sai -- where to store auth_info.
691 **		rpool -- resource pool for sai.
692 **
693 **	Returns:
694 **		EX_OK -- data succesfully read.
695 **		EX_UNAVAILABLE -- no valid filename.
696 **		EX_TEMPFAIL -- temporary failure.
697 */
698 
699 static char *sasl_info_name[] =
700 {
701 	"user id",
702 	"authentication id",
703 	"password",
704 	"realm",
705 	"mechlist"
706 };
707 static int
708 readauth(filename, safe, sai, rpool)
709 	char *filename;
710 	bool safe;
711 	SASL_AI_T *sai;
712 	SM_RPOOL_T *rpool;
713 {
714 	SM_FILE_T *f;
715 	long sff;
716 	pid_t pid;
717 	int lc;
718 	char *s;
719 	char buf[MAXLINE];
720 
721 	if (filename == NULL || filename[0] == '\0')
722 		return EX_UNAVAILABLE;
723 
724 #if !_FFR_ALLOW_SASLINFO
725 	/*
726 	**  make sure we don't use a program that is not
727 	**  accesible to the user who specified a different authinfo file.
728 	**  However, currently we don't pass this info (authinfo file
729 	**  specified by user) around, so we just turn off program access.
730 	*/
731 
732 	if (filename[0] == '|')
733 	{
734 		auto int fd;
735 		int i;
736 		char *p;
737 		char *argv[MAXPV + 1];
738 
739 		i = 0;
740 		for (p = strtok(&filename[1], " \t"); p != NULL;
741 		     p = strtok(NULL, " \t"))
742 		{
743 			if (i >= MAXPV)
744 				break;
745 			argv[i++] = p;
746 		}
747 		argv[i] = NULL;
748 		pid = prog_open(argv, &fd, CurEnv);
749 		if (pid < 0)
750 			f = NULL;
751 		else
752 			f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
753 				       (void *) &fd, SM_IO_RDONLY, NULL);
754 	}
755 	else
756 #endif /* !_FFR_ALLOW_SASLINFO */
757 	{
758 		pid = -1;
759 		sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
760 		      |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
761 # if _FFR_GROUPREADABLEAUTHINFOFILE
762 		if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
763 # endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
764 			sff |= SFF_NOGRFILES;
765 		if (DontLockReadFiles)
766 			sff |= SFF_NOLOCK;
767 
768 #if _FFR_ALLOW_SASLINFO
769 		/*
770 		**  XXX: make sure we don't read or open files that are not
771 		**  accesible to the user who specified a different authinfo
772 		**  file.
773 		*/
774 
775 		sff |= SFF_MUSTOWN;
776 #else /* _FFR_ALLOW_SASLINFO */
777 		if (safe)
778 			sff |= SFF_OPENASROOT;
779 #endif /* _FFR_ALLOW_SASLINFO */
780 
781 		f = safefopen(filename, O_RDONLY, 0, sff);
782 	}
783 	if (f == NULL)
784 	{
785 		if (LogLevel > 5)
786 			sm_syslog(LOG_ERR, NOQID,
787 				  "AUTH=client, error: can't open %s: %s",
788 				  filename, sm_errstring(errno));
789 		return EX_TEMPFAIL;
790 	}
791 
792 	lc = 0;
793 	while (lc <= SASL_MECHLIST &&
794 		sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
795 	{
796 		if (buf[0] != '#')
797 		{
798 			(*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
799 			if ((s = strchr((*sai)[lc], '\n')) != NULL)
800 				*s = '\0';
801 			lc++;
802 		}
803 	}
804 
805 	(void) sm_io_close(f, SM_TIME_DEFAULT);
806 	if (pid > 0)
807 		(void) waitfor(pid);
808 	if (lc < SASL_PASSWORD)
809 	{
810 		if (LogLevel > 8)
811 			sm_syslog(LOG_ERR, NOQID,
812 				  "AUTH=client, error: can't read %s from %s",
813 				  sasl_info_name[lc + 1], filename);
814 		return EX_TEMPFAIL;
815 	}
816 	return EX_OK;
817 }
818 
819 /*
820 **  GETAUTH -- get authinfo from ruleset call
821 **
822 **	{server_name}, {server_addr} must be set
823 **
824 **	Parameters:
825 **		mci -- the mailer connection structure.
826 **		e -- the envelope (including the sender to specify).
827 **		sai -- pointer to authinfo (result).
828 **
829 **	Returns:
830 **		EX_OK -- ruleset was succesfully called, data may not
831 **			be available, sai must be checked.
832 **		EX_UNAVAILABLE -- ruleset unavailable (or failed).
833 **		EX_TEMPFAIL -- temporary failure (from ruleset).
834 **
835 **	Side Effects:
836 **		Fills in sai if successful.
837 */
838 
839 static int
840 getauth(mci, e, sai)
841 	MCI *mci;
842 	ENVELOPE *e;
843 	SASL_AI_T *sai;
844 {
845 	int i, r, l, got, ret;
846 	char **pvp;
847 	char pvpbuf[PSBUFSIZE];
848 
849 	r = rscap("authinfo", macvalue(macid("{server_name}"), e),
850 		   macvalue(macid("{server_addr}"), e), e,
851 		   &pvp, pvpbuf, sizeof(pvpbuf));
852 
853 	if (r != EX_OK)
854 		return EX_UNAVAILABLE;
855 
856 	/* other than expected return value: ok (i.e., no auth) */
857 	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
858 		return EX_OK;
859 	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
860 		return EX_TEMPFAIL;
861 
862 	/*
863 	**  parse the data, put it into sai
864 	**  format: "TDstring" (including the '"' !)
865 	**  where T is a tag: 'U', ...
866 	**  D is a delimiter: ':' or '='
867 	*/
868 
869 	ret = EX_OK;	/* default return value */
870 	i = 0;
871 	got = 0;
872 	while (i < SASL_ENTRIES)
873 	{
874 		if (pvp[i + 1] == NULL)
875 			break;
876 		if (pvp[i + 1][0] != '"')
877 			break;
878 		switch (pvp[i + 1][1])
879 		{
880 		  case 'U':
881 		  case 'u':
882 			r = SASL_USER;
883 			break;
884 		  case 'I':
885 		  case 'i':
886 			r = SASL_AUTHID;
887 			break;
888 		  case 'P':
889 		  case 'p':
890 			r = SASL_PASSWORD;
891 			break;
892 		  case 'R':
893 		  case 'r':
894 			r = SASL_DEFREALM;
895 			break;
896 		  case 'M':
897 		  case 'm':
898 			r = SASL_MECHLIST;
899 			break;
900 		  default:
901 			goto fail;
902 		}
903 		l = strlen(pvp[i + 1]);
904 
905 		/* check syntax */
906 		if (l <= 3 || pvp[i + 1][l - 1] != '"')
907 			goto fail;
908 
909 		/* remove closing quote */
910 		pvp[i + 1][l - 1] = '\0';
911 
912 		/* remove "TD and " */
913 		l -= 4;
914 		(*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
915 		if ((*sai)[r] == NULL)
916 			goto tempfail;
917 		if (pvp[i + 1][2] == ':')
918 		{
919 			/* ':text' (just copy) */
920 			(void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
921 			got |= 1 << r;
922 		}
923 		else if (pvp[i + 1][2] == '=')
924 		{
925 			unsigned int len;
926 
927 			/* '=base64' (decode) */
928 # if SASL >= 20000
929 			ret = sasl_decode64(pvp[i + 1] + 3,
930 					  (unsigned int) l, (*sai)[r],
931 					  (unsigned int) l + 1, &len);
932 # else /* SASL >= 20000 */
933 			ret = sasl_decode64(pvp[i + 1] + 3,
934 					  (unsigned int) l, (*sai)[r], &len);
935 # endif /* SASL >= 20000 */
936 			if (ret != SASL_OK)
937 				goto fail;
938 			got |= 1 << r;
939 		}
940 		else
941 			goto fail;
942 		if (tTd(95, 5))
943 			sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
944 				  sasl_info_name[r], (*sai)[r]);
945 		++i;
946 	}
947 
948 	/* did we get the expected data? */
949 	/* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
950 	if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
951 	      bitset(SASL_PASSWORD_BIT, got)))
952 		goto fail;
953 
954 	/* no authid? copy uid */
955 	if (!bitset(SASL_AUTHID_BIT, got))
956 	{
957 		l = strlen((*sai)[SASL_USER]) + 1;
958 		(*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
959 							       l + 1);
960 		if ((*sai)[SASL_AUTHID] == NULL)
961 			goto tempfail;
962 		(void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
963 	}
964 
965 	/* no uid? copy authid */
966 	if (!bitset(SASL_USER_BIT, got))
967 	{
968 		l = strlen((*sai)[SASL_AUTHID]) + 1;
969 		(*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
970 							     l + 1);
971 		if ((*sai)[SASL_USER] == NULL)
972 			goto tempfail;
973 		(void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
974 	}
975 	return EX_OK;
976 
977   tempfail:
978 	ret = EX_TEMPFAIL;
979   fail:
980 	if (LogLevel > 8)
981 		sm_syslog(LOG_WARNING, NOQID,
982 			  "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
983 			  macvalue(macid("{server_name}"), e),
984 			  macvalue(macid("{server_addr}"), e),
985 			  ret == EX_TEMPFAIL ? "temp" : "");
986 	for (i = 0; i <= SASL_MECHLIST; i++)
987 		(*sai)[i] = NULL;	/* just clear; rpool */
988 	return ret;
989 }
990 
991 # if SASL >= 20000
992 /*
993 **  GETSIMPLE -- callback to get userid or authid
994 **
995 **	Parameters:
996 **		context -- sai
997 **		id -- what to do
998 **		result -- (pointer to) result
999 **		len -- (pointer to) length of result
1000 **
1001 **	Returns:
1002 **		OK/failure values
1003 */
1004 
1005 static int
1006 getsimple(context, id, result, len)
1007 	void *context;
1008 	int id;
1009 	const char **result;
1010 	unsigned *len;
1011 {
1012 	SASL_AI_T *sai;
1013 
1014 	if (result == NULL || context == NULL)
1015 		return SASL_BADPARAM;
1016 	sai = (SASL_AI_T *) context;
1017 
1018 	switch (id)
1019 	{
1020 	  case SASL_CB_USER:
1021 		*result = (*sai)[SASL_USER];
1022 		if (tTd(95, 5))
1023 			sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1024 				  *result);
1025 		if (len != NULL)
1026 			*len = *result != NULL ? strlen(*result) : 0;
1027 		break;
1028 
1029 	  case SASL_CB_AUTHNAME:
1030 		*result = (*sai)[SASL_AUTHID];
1031 		if (tTd(95, 5))
1032 			sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1033 				  *result);
1034 		if (len != NULL)
1035 			*len = *result != NULL ? strlen(*result) : 0;
1036 		break;
1037 
1038 	  case SASL_CB_LANGUAGE:
1039 		*result = NULL;
1040 		if (len != NULL)
1041 			*len = 0;
1042 		break;
1043 
1044 	  default:
1045 		return SASL_BADPARAM;
1046 	}
1047 	return SASL_OK;
1048 }
1049 /*
1050 **  GETSECRET -- callback to get password
1051 **
1052 **	Parameters:
1053 **		conn -- connection information
1054 **		context -- sai
1055 **		id -- what to do
1056 **		psecret -- (pointer to) result
1057 **
1058 **	Returns:
1059 **		OK/failure values
1060 */
1061 
1062 static int
1063 getsecret(conn, context, id, psecret)
1064 	sasl_conn_t *conn;
1065 	SM_UNUSED(void *context);
1066 	int id;
1067 	sasl_secret_t **psecret;
1068 {
1069 	int len;
1070 	char *authpass;
1071 	MCI *mci;
1072 
1073 	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1074 		return SASL_BADPARAM;
1075 
1076 	mci = (MCI *) context;
1077 	authpass = mci->mci_sai[SASL_PASSWORD];
1078 	len = strlen(authpass);
1079 
1080 	/*
1081 	**  use an rpool because we are responsible for free()ing the secret,
1082 	**  but we can't free() it until after the auth completes
1083 	*/
1084 
1085 	*psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
1086 						     sizeof(sasl_secret_t) +
1087 						     len + 1);
1088 	if (*psecret == NULL)
1089 		return SASL_FAIL;
1090 	(void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
1091 	(*psecret)->len = (unsigned long) len;
1092 	return SASL_OK;
1093 }
1094 # else /* SASL >= 20000 */
1095 /*
1096 **  GETSIMPLE -- callback to get userid or authid
1097 **
1098 **	Parameters:
1099 **		context -- sai
1100 **		id -- what to do
1101 **		result -- (pointer to) result
1102 **		len -- (pointer to) length of result
1103 **
1104 **	Returns:
1105 **		OK/failure values
1106 */
1107 
1108 static int
1109 getsimple(context, id, result, len)
1110 	void *context;
1111 	int id;
1112 	const char **result;
1113 	unsigned *len;
1114 {
1115 	char *h, *s;
1116 # if SASL > 10509
1117 	bool addrealm;
1118 # endif /* SASL > 10509 */
1119 	size_t l;
1120 	SASL_AI_T *sai;
1121 	char *authid = NULL;
1122 
1123 	if (result == NULL || context == NULL)
1124 		return SASL_BADPARAM;
1125 	sai = (SASL_AI_T *) context;
1126 
1127 	/*
1128 	**  Unfortunately it is not clear whether this routine should
1129 	**  return a copy of a string or just a pointer to a string.
1130 	**  The Cyrus-SASL plugins treat these return values differently, e.g.,
1131 	**  plugins/cram.c free()s authid, plugings/digestmd5.c does not.
1132 	**  The best solution to this problem is to fix Cyrus-SASL, but it
1133 	**  seems there is nobody who creates patches... Hello CMU!?
1134 	**  The second best solution is to have flags that tell this routine
1135 	**  whether to return an malloc()ed copy.
1136 	**  The next best solution is to always return an malloc()ed copy,
1137 	**  and suffer from some memory leak, which is ugly for persistent
1138 	**  queue runners.
1139 	**  For now we go with the last solution...
1140 	**  We can't use rpools (which would avoid this particular problem)
1141 	**  as explained in sasl.c.
1142 	*/
1143 
1144 	switch (id)
1145 	{
1146 	  case SASL_CB_USER:
1147 		l = strlen((*sai)[SASL_USER]) + 1;
1148 		s = sm_sasl_malloc(l);
1149 		if (s == NULL)
1150 		{
1151 			if (len != NULL)
1152 				*len = 0;
1153 			*result = NULL;
1154 			return SASL_NOMEM;
1155 		}
1156 		(void) sm_strlcpy(s, (*sai)[SASL_USER], l);
1157 		*result = s;
1158 		if (tTd(95, 5))
1159 			sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1160 				  *result);
1161 		if (len != NULL)
1162 			*len = *result != NULL ? strlen(*result) : 0;
1163 		break;
1164 
1165 	  case SASL_CB_AUTHNAME:
1166 		h = (*sai)[SASL_AUTHID];
1167 # if SASL > 10509
1168 		/* XXX maybe other mechanisms too?! */
1169 		addrealm = (*sai)[SASL_MECH] != NULL &&
1170 			   sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
1171 
1172 		/*
1173 		**  Add realm to authentication id unless authid contains
1174 		**  '@' (i.e., a realm) or the default realm is empty.
1175 		*/
1176 
1177 		if (addrealm && h != NULL && strchr(h, '@') == NULL)
1178 		{
1179 			/* has this been done before? */
1180 			if ((*sai)[SASL_ID_REALM] == NULL)
1181 			{
1182 				char *realm;
1183 
1184 				realm = (*sai)[SASL_DEFREALM];
1185 
1186 				/* do not add an empty realm */
1187 				if (*realm == '\0')
1188 				{
1189 					authid = h;
1190 					(*sai)[SASL_ID_REALM] = NULL;
1191 				}
1192 				else
1193 				{
1194 					l = strlen(h) + strlen(realm) + 2;
1195 
1196 					/* should use rpool, but from where? */
1197 					authid = sm_sasl_malloc(l);
1198 					if (authid != NULL)
1199 					{
1200 						(void) sm_snprintf(authid, l,
1201 								  "%s@%s",
1202 								   h, realm);
1203 						(*sai)[SASL_ID_REALM] = authid;
1204 					}
1205 					else
1206 					{
1207 						authid = h;
1208 						(*sai)[SASL_ID_REALM] = NULL;
1209 					}
1210 				}
1211 			}
1212 			else
1213 				authid = (*sai)[SASL_ID_REALM];
1214 		}
1215 		else
1216 # endif /* SASL > 10509 */
1217 			authid = h;
1218 		l = strlen(authid) + 1;
1219 		s = sm_sasl_malloc(l);
1220 		if (s == NULL)
1221 		{
1222 			if (len != NULL)
1223 				*len = 0;
1224 			*result = NULL;
1225 			return SASL_NOMEM;
1226 		}
1227 		(void) sm_strlcpy(s, authid, l);
1228 		*result = s;
1229 		if (tTd(95, 5))
1230 			sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1231 				  *result);
1232 		if (len != NULL)
1233 			*len = authid ? strlen(authid) : 0;
1234 		break;
1235 
1236 	  case SASL_CB_LANGUAGE:
1237 		*result = NULL;
1238 		if (len != NULL)
1239 			*len = 0;
1240 		break;
1241 
1242 	  default:
1243 		return SASL_BADPARAM;
1244 	}
1245 	return SASL_OK;
1246 }
1247 /*
1248 **  GETSECRET -- callback to get password
1249 **
1250 **	Parameters:
1251 **		conn -- connection information
1252 **		context -- sai
1253 **		id -- what to do
1254 **		psecret -- (pointer to) result
1255 **
1256 **	Returns:
1257 **		OK/failure values
1258 */
1259 
1260 static int
1261 getsecret(conn, context, id, psecret)
1262 	sasl_conn_t *conn;
1263 	SM_UNUSED(void *context);
1264 	int id;
1265 	sasl_secret_t **psecret;
1266 {
1267 	int len;
1268 	char *authpass;
1269 	SASL_AI_T *sai;
1270 
1271 	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1272 		return SASL_BADPARAM;
1273 
1274 	sai = (SASL_AI_T *) context;
1275 	authpass = (*sai)[SASL_PASSWORD];
1276 	len = strlen(authpass);
1277 	*psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
1278 						    len + 1);
1279 	if (*psecret == NULL)
1280 		return SASL_FAIL;
1281 	(void) sm_strlcpy((*psecret)->data, authpass, len + 1);
1282 	(*psecret)->len = (unsigned long) len;
1283 	return SASL_OK;
1284 }
1285 # endif /* SASL >= 20000 */
1286 
1287 /*
1288 **  SAFESASLFILE -- callback for sasl: is file safe?
1289 **
1290 **	Parameters:
1291 **		context -- pointer to context between invocations (unused)
1292 **		file -- name of file to check
1293 **		type -- type of file to check
1294 **
1295 **	Returns:
1296 **		SASL_OK -- file can be used
1297 **		SASL_CONTINUE -- don't use file
1298 **		SASL_FAIL -- failure (not used here)
1299 **
1300 */
1301 
1302 int
1303 #if SASL > 10515
1304 safesaslfile(context, file, type)
1305 #else /* SASL > 10515 */
1306 safesaslfile(context, file)
1307 #endif /* SASL > 10515 */
1308 	void *context;
1309 # if SASL >= 20000
1310 	const char *file;
1311 # else /* SASL >= 20000 */
1312 	char *file;
1313 # endif /* SASL >= 20000 */
1314 #if SASL > 10515
1315 # if SASL >= 20000
1316 	sasl_verify_type_t type;
1317 # else /* SASL >= 20000 */
1318 	int type;
1319 # endif /* SASL >= 20000 */
1320 #endif /* SASL > 10515 */
1321 {
1322 	long sff;
1323 	int r;
1324 #if SASL <= 10515
1325 	size_t len;
1326 #endif /* SASL <= 10515 */
1327 	char *p;
1328 
1329 	if (file == NULL || *file == '\0')
1330 		return SASL_OK;
1331 	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
1332 #if SASL <= 10515
1333 	if ((p = strrchr(file, '/')) == NULL)
1334 		p = file;
1335 	else
1336 		++p;
1337 
1338 	/* everything beside libs and .conf files must not be readable */
1339 	len = strlen(p);
1340 	if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
1341 	    (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
1342 	{
1343 		if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1344 			sff |= SFF_NORFILES;
1345 		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1346 			sff |= SFF_NOGWFILES;
1347 	}
1348 #else /* SASL <= 10515 */
1349 	/* files containing passwords should be not readable */
1350 	if (type == SASL_VRFY_PASSWD)
1351 	{
1352 		if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1353 			sff |= SFF_NOWRFILES;
1354 		else
1355 			sff |= SFF_NORFILES;
1356 		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1357 			sff |= SFF_NOGWFILES;
1358 	}
1359 #endif /* SASL <= 10515 */
1360 
1361 	p = (char *) file;
1362 	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
1363 			  S_IRUSR, NULL)) == 0)
1364 		return SASL_OK;
1365 	if (LogLevel > (r != ENOENT ? 8 : 10))
1366 		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
1367 			  p, sm_errstring(r));
1368 	return SASL_CONTINUE;
1369 }
1370 
1371 /*
1372 **  SASLGETREALM -- return the realm for SASL
1373 **
1374 **	return the realm for the client
1375 **
1376 **	Parameters:
1377 **		context -- context shared between invocations
1378 **		availrealms -- list of available realms
1379 **			{realm, realm, ...}
1380 **		result -- pointer to result
1381 **
1382 **	Returns:
1383 **		failure/success
1384 */
1385 
1386 static int
1387 saslgetrealm(context, id, availrealms, result)
1388 	void *context;
1389 	int id;
1390 	const char **availrealms;
1391 	const char **result;
1392 {
1393 	char *r;
1394 	SASL_AI_T *sai;
1395 
1396 	sai = (SASL_AI_T *) context;
1397 	if (sai == NULL)
1398 		return SASL_FAIL;
1399 	r = (*sai)[SASL_DEFREALM];
1400 
1401 	if (LogLevel > 12)
1402 		sm_syslog(LOG_INFO, NOQID,
1403 			  "AUTH=client, realm=%s, available realms=%s",
1404 			  r == NULL ? "<No Realm>" : r,
1405 			  (availrealms == NULL || *availrealms == NULL)
1406 				? "<No Realms>" : *availrealms);
1407 
1408 	/* check whether context is in list */
1409 	if (availrealms != NULL && *availrealms != NULL)
1410 	{
1411 		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
1412 		    NULL)
1413 		{
1414 			if (LogLevel > 8)
1415 				sm_syslog(LOG_ERR, NOQID,
1416 					  "AUTH=client, realm=%s not in list=%s",
1417 					  r, *availrealms);
1418 			return SASL_FAIL;
1419 		}
1420 	}
1421 	*result = r;
1422 	return SASL_OK;
1423 }
1424 /*
1425 **  ITEMINLIST -- does item appear in list?
1426 **
1427 **	Check whether item appears in list (which must be separated by a
1428 **	character in delim) as a "word", i.e. it must appear at the begin
1429 **	of the list or after a space, and it must end with a space or the
1430 **	end of the list.
1431 **
1432 **	Parameters:
1433 **		item -- item to search.
1434 **		list -- list of items.
1435 **		delim -- list of delimiters.
1436 **
1437 **	Returns:
1438 **		pointer to occurrence (NULL if not found).
1439 */
1440 
1441 char *
1442 iteminlist(item, list, delim)
1443 	char *item;
1444 	char *list;
1445 	char *delim;
1446 {
1447 	char *s;
1448 	int len;
1449 
1450 	if (list == NULL || *list == '\0')
1451 		return NULL;
1452 	if (item == NULL || *item == '\0')
1453 		return NULL;
1454 	s = list;
1455 	len = strlen(item);
1456 	while (s != NULL && *s != '\0')
1457 	{
1458 		if (sm_strncasecmp(s, item, len) == 0 &&
1459 		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
1460 			return s;
1461 		s = strpbrk(s, delim);
1462 		if (s != NULL)
1463 			while (*++s == ' ')
1464 				continue;
1465 	}
1466 	return NULL;
1467 }
1468 /*
1469 **  REMOVEMECH -- remove item [rem] from list [list]
1470 **
1471 **	Parameters:
1472 **		rem -- item to remove
1473 **		list -- list of items
1474 **		rpool -- resource pool from which result is allocated.
1475 **
1476 **	Returns:
1477 **		pointer to new list (NULL in case of error).
1478 */
1479 
1480 static char *
1481 removemech(rem, list, rpool)
1482 	char *rem;
1483 	char *list;
1484 	SM_RPOOL_T *rpool;
1485 {
1486 	char *ret;
1487 	char *needle;
1488 	int len;
1489 
1490 	if (list == NULL)
1491 		return NULL;
1492 	if (rem == NULL || *rem == '\0')
1493 	{
1494 		/* take out what? */
1495 		return NULL;
1496 	}
1497 
1498 	/* find the item in the list */
1499 	if ((needle = iteminlist(rem, list, " ")) == NULL)
1500 	{
1501 		/* not in there: return original */
1502 		return list;
1503 	}
1504 
1505 	/* length of string without rem */
1506 	len = strlen(list) - strlen(rem);
1507 	if (len <= 0)
1508 	{
1509 		ret = (char *) sm_rpool_malloc_x(rpool, 1);
1510 		*ret = '\0';
1511 		return ret;
1512 	}
1513 	ret = (char *) sm_rpool_malloc_x(rpool, len);
1514 	memset(ret, '\0', len);
1515 
1516 	/* copy from start to removed item */
1517 	memcpy(ret, list, needle - list);
1518 
1519 	/* length of rest of string past removed item */
1520 	len = strlen(needle) - strlen(rem) - 1;
1521 	if (len > 0)
1522 	{
1523 		/* not last item -- copy into string */
1524 		memcpy(ret + (needle - list),
1525 		       list + (needle - list) + strlen(rem) + 1,
1526 		       len);
1527 	}
1528 	else
1529 		ret[(needle - list) - 1] = '\0';
1530 	return ret;
1531 }
1532 /*
1533 **  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1534 **
1535 **	Parameters:
1536 **		m -- the mailer.
1537 **		mci -- the mailer connection structure.
1538 **		e -- the envelope (including the sender to specify).
1539 **		sai - sasl authinfo
1540 **
1541 **	Returns:
1542 **		EX_OK -- authentication was successful.
1543 **		EX_NOPERM -- authentication failed.
1544 **		EX_IOERR -- authentication dialogue failed (I/O problem?).
1545 **		EX_TEMPFAIL -- temporary failure.
1546 **
1547 */
1548 
1549 static int
1550 attemptauth(m, mci, e, sai)
1551 	MAILER *m;
1552 	MCI *mci;
1553 	ENVELOPE *e;
1554 	SASL_AI_T *sai;
1555 {
1556 	int saslresult, smtpresult;
1557 # if SASL >= 20000
1558 	sasl_ssf_t ssf;
1559 	const char *auth_id;
1560 	const char *out;
1561 # else /* SASL >= 20000 */
1562 	sasl_external_properties_t ssf;
1563 	char *out;
1564 # endif /* SASL >= 20000 */
1565 	unsigned int outlen;
1566 	sasl_interact_t *client_interact = NULL;
1567 	char *mechusing;
1568 	sasl_security_properties_t ssp;
1569 	char in64[MAXOUTLEN];
1570 #if NETINET || (NETINET6 && SASL >= 20000)
1571 	extern SOCKADDR CurHostAddr;
1572 #endif /* NETINET || (NETINET6 && SASL >= 20000) */
1573 
1574 	/* no mechanism selected (yet) */
1575 	(*sai)[SASL_MECH] = NULL;
1576 
1577 	/* dispose old connection */
1578 	if (mci->mci_conn != NULL)
1579 		sasl_dispose(&(mci->mci_conn));
1580 
1581 	/* make a new client sasl connection */
1582 # if SASL >= 20000
1583 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1584 								 : "smtp",
1585 				     CurHostName, NULL, NULL, NULL, 0,
1586 				     &mci->mci_conn);
1587 # else /* SASL >= 20000 */
1588 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1589 								 : "smtp",
1590 				     CurHostName, NULL, 0, &mci->mci_conn);
1591 # endif /* SASL >= 20000 */
1592 	if (saslresult != SASL_OK)
1593 		return EX_TEMPFAIL;
1594 
1595 	/* set properties */
1596 	(void) memset(&ssp, '\0', sizeof ssp);
1597 
1598 	/* XXX should these be options settable via .cf ? */
1599 	{
1600 		ssp.max_ssf = MaxSLBits;
1601 		ssp.maxbufsize = MAXOUTLEN;
1602 #  if 0
1603 		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1604 #  endif /* 0 */
1605 	}
1606 	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1607 	if (saslresult != SASL_OK)
1608 		return EX_TEMPFAIL;
1609 
1610 # if SASL >= 20000
1611 	/* external security strength factor, authentication id */
1612 	ssf = 0;
1613 	auth_id = NULL;
1614 #  if STARTTLS
1615 	out = macvalue(macid("{cert_subject}"), e);
1616 	if (out != NULL && *out != '\0')
1617 		auth_id = out;
1618 	out = macvalue(macid("{cipher_bits}"), e);
1619 	if (out != NULL && *out != '\0')
1620 		ssf = atoi(out);
1621 #  endif /* STARTTLS */
1622 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1623 	if (saslresult != SASL_OK)
1624 		return EX_TEMPFAIL;
1625 	saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
1626 	if (saslresult != SASL_OK)
1627 		return EX_TEMPFAIL;
1628 
1629 #  if NETINET || NETINET6
1630 	/* set local/remote ipv4 addresses */
1631 	if (mci->mci_out != NULL && (
1632 #   if NETINET6
1633 		CurHostAddr.sa.sa_family == AF_INET6 ||
1634 #   endif /* NETINET6 */
1635 		CurHostAddr.sa.sa_family == AF_INET))
1636 	{
1637 		SOCKADDR_LEN_T addrsize;
1638 		SOCKADDR saddr_l;
1639 		char localip[60], remoteip[60];
1640 
1641 		switch (CurHostAddr.sa.sa_family)
1642 		{
1643 		  case AF_INET:
1644 			addrsize = sizeof(struct sockaddr_in);
1645 			break;
1646 #   if NETINET6
1647 		  case AF_INET6:
1648 			addrsize = sizeof(struct sockaddr_in6);
1649 			break;
1650 #   endif /* NETINET6 */
1651 		  default:
1652 			break;
1653 		}
1654 		if (iptostring(&CurHostAddr, addrsize,
1655 			       remoteip, sizeof remoteip))
1656 		{
1657 			if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
1658 					 remoteip) != SASL_OK)
1659 				return EX_TEMPFAIL;
1660 		}
1661 		addrsize = sizeof(saddr_l);
1662 		if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1663 					      NULL),
1664 				(struct sockaddr *) &saddr_l, &addrsize) == 0)
1665 		{
1666 			if (iptostring(&saddr_l, addrsize,
1667 				       localip, sizeof localip))
1668 			{
1669 				if (sasl_setprop(mci->mci_conn,
1670 						 SASL_IPLOCALPORT,
1671 						 localip) != SASL_OK)
1672 					return EX_TEMPFAIL;
1673 			}
1674 		}
1675 	}
1676 #  endif /* NETINET || NETINET6 */
1677 
1678 	/* start client side of sasl */
1679 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1680 				       &client_interact,
1681 				       &out, &outlen,
1682 				       (const char **) &mechusing);
1683 # else /* SASL >= 20000 */
1684 	/* external security strength factor, authentication id */
1685 	ssf.ssf = 0;
1686 	ssf.auth_id = NULL;
1687 #  if STARTTLS
1688 	out = macvalue(macid("{cert_subject}"), e);
1689 	if (out != NULL && *out != '\0')
1690 		ssf.auth_id = out;
1691 	out = macvalue(macid("{cipher_bits}"), e);
1692 	if (out != NULL && *out != '\0')
1693 		ssf.ssf = atoi(out);
1694 #  endif /* STARTTLS */
1695 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1696 	if (saslresult != SASL_OK)
1697 		return EX_TEMPFAIL;
1698 
1699 #  if NETINET
1700 	/* set local/remote ipv4 addresses */
1701 	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1702 	{
1703 		SOCKADDR_LEN_T addrsize;
1704 		struct sockaddr_in saddr_l;
1705 
1706 		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1707 				 (struct sockaddr_in *) &CurHostAddr)
1708 		    != SASL_OK)
1709 			return EX_TEMPFAIL;
1710 		addrsize = sizeof(struct sockaddr_in);
1711 		if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1712 					      NULL),
1713 				(struct sockaddr *) &saddr_l, &addrsize) == 0)
1714 		{
1715 			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1716 					 &saddr_l) != SASL_OK)
1717 				return EX_TEMPFAIL;
1718 		}
1719 	}
1720 #  endif /* NETINET */
1721 
1722 	/* start client side of sasl */
1723 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1724 				       NULL, &client_interact,
1725 				       &out, &outlen,
1726 				       (const char **) &mechusing);
1727 # endif /* SASL >= 20000 */
1728 
1729 	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1730 	{
1731 		if (saslresult == SASL_NOMECH && LogLevel > 8)
1732 		{
1733 			sm_syslog(LOG_NOTICE, e->e_id,
1734 				  "AUTH=client, available mechanisms do not fulfill requirements");
1735 		}
1736 		return EX_TEMPFAIL;
1737 	}
1738 
1739 	/* just point current mechanism to the data in the sasl library */
1740 	(*sai)[SASL_MECH] = mechusing;
1741 
1742 	/* send the info across the wire */
1743 	if (out == NULL
1744 		/* login and digest-md5 up to 1.5.28 set out="" */
1745 	    || (outlen == 0 &&
1746 		(sm_strcasecmp(mechusing, "LOGIN") == 0 ||
1747 		 sm_strcasecmp(mechusing, "DIGEST-MD5") == 0))
1748 	   )
1749 	{
1750 		/* no initial response */
1751 		smtpmessage("AUTH %s", m, mci, mechusing);
1752 	}
1753 	else if (outlen == 0)
1754 	{
1755 		/*
1756 		**  zero-length initial response, per RFC 2554 4.:
1757 		**  "Unlike a zero-length client answer to a 334 reply, a zero-
1758 		**  length initial response is sent as a single equals sign"
1759 		*/
1760 
1761 		smtpmessage("AUTH %s =", m, mci, mechusing);
1762 	}
1763 	else
1764 	{
1765 		saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1766 		if (saslresult != SASL_OK) /* internal error */
1767 		{
1768 			if (LogLevel > 8)
1769 				sm_syslog(LOG_ERR, e->e_id,
1770 					"encode64 for AUTH failed");
1771 			return EX_TEMPFAIL;
1772 		}
1773 		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1774 	}
1775 # if SASL < 20000
1776 	sm_sasl_free(out); /* XXX only if no rpool is used */
1777 # endif /* SASL < 20000 */
1778 
1779 	/* get the reply */
1780 	smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL,
1781 			XS_AUTH);
1782 
1783 	for (;;)
1784 	{
1785 		/* check return code from server */
1786 		if (smtpresult == 235)
1787 		{
1788 			macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
1789 				  mechusing);
1790 			return EX_OK;
1791 		}
1792 		if (smtpresult == -1)
1793 			return EX_IOERR;
1794 		if (REPLYTYPE(smtpresult) == 5)
1795 			return EX_NOPERM;	/* ugly, but ... */
1796 		if (REPLYTYPE(smtpresult) != 3)
1797 		{
1798 			/* should we fail deliberately, see RFC 2554 4. ? */
1799 			/* smtpmessage("*", m, mci); */
1800 			return EX_TEMPFAIL;
1801 		}
1802 
1803 		saslresult = sasl_client_step(mci->mci_conn,
1804 					      mci->mci_sasl_string,
1805 					      mci->mci_sasl_string_len,
1806 					      &client_interact,
1807 					      &out, &outlen);
1808 
1809 		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1810 		{
1811 			if (tTd(95, 5))
1812 				sm_dprintf("AUTH FAIL=%s (%d)\n",
1813 					sasl_errstring(saslresult, NULL, NULL),
1814 					saslresult);
1815 
1816 			/* fail deliberately, see RFC 2554 4. */
1817 			smtpmessage("*", m, mci);
1818 
1819 			/*
1820 			**  but we should only fail for this authentication
1821 			**  mechanism; how to do that?
1822 			*/
1823 
1824 			smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1825 					   getsasldata, NULL, XS_AUTH);
1826 			return EX_NOPERM;
1827 		}
1828 
1829 		if (outlen > 0)
1830 		{
1831 			saslresult = sasl_encode64(out, outlen, in64,
1832 						   MAXOUTLEN, NULL);
1833 			if (saslresult != SASL_OK)
1834 			{
1835 				/* give an error reply to the other side! */
1836 				smtpmessage("*", m, mci);
1837 				return EX_TEMPFAIL;
1838 			}
1839 		}
1840 		else
1841 			in64[0] = '\0';
1842 # if SASL < 20000
1843 		sm_sasl_free(out); /* XXX only if no rpool is used */
1844 # endif /* SASL < 20000 */
1845 		smtpmessage("%s", m, mci, in64);
1846 		smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1847 				   getsasldata, NULL, XS_AUTH);
1848 	}
1849 	/* NOTREACHED */
1850 }
1851 /*
1852 **  SMTPAUTH -- try to AUTHenticate
1853 **
1854 **	This will try mechanisms in the order the sasl library decided until:
1855 **	- there are no more mechanisms
1856 **	- a mechanism succeeds
1857 **	- the sasl library fails initializing
1858 **
1859 **	Parameters:
1860 **		m -- the mailer.
1861 **		mci -- the mailer connection info.
1862 **		e -- the envelope.
1863 **
1864 **	Returns:
1865 **		EX_OK -- authentication was successful
1866 **		EX_UNAVAILABLE -- authentication not possible, e.g.,
1867 **			no data available.
1868 **		EX_NOPERM -- authentication failed.
1869 **		EX_TEMPFAIL -- temporary failure.
1870 **
1871 **	Notice: AuthInfo is used for all connections, hence we must
1872 **		return EX_TEMPFAIL only if we really want to retry, i.e.,
1873 **		iff getauth() tempfailed or getauth() was used and
1874 **		authentication tempfailed.
1875 */
1876 
1877 int
1878 smtpauth(m, mci, e)
1879 	MAILER *m;
1880 	MCI *mci;
1881 	ENVELOPE *e;
1882 {
1883 	int result;
1884 	int i;
1885 	bool usedgetauth;
1886 
1887 	mci->mci_sasl_auth = false;
1888 	for (i = 0; i < SASL_MECH ; i++)
1889 		mci->mci_sai[i] = NULL;
1890 
1891 	result = getauth(mci, e, &(mci->mci_sai));
1892 	if (result == EX_TEMPFAIL)
1893 		return result;
1894 	usedgetauth = true;
1895 
1896 	/* no data available: don't try to authenticate */
1897 	if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
1898 		return result;
1899 	if (result != EX_OK)
1900 	{
1901 		if (SASLInfo == NULL)
1902 			return EX_UNAVAILABLE;
1903 
1904 		/* read authinfo from file */
1905 		result = readauth(SASLInfo, true, &(mci->mci_sai),
1906 				  mci->mci_rpool);
1907 		if (result != EX_OK)
1908 			return result;
1909 		usedgetauth = false;
1910 	}
1911 
1912 	/* check whether sufficient data is available */
1913 	if (mci->mci_sai[SASL_PASSWORD] == NULL ||
1914 	    *(mci->mci_sai)[SASL_PASSWORD] == '\0')
1915 		return EX_UNAVAILABLE;
1916 	if ((mci->mci_sai[SASL_AUTHID] == NULL ||
1917 	     *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
1918 	    (mci->mci_sai[SASL_USER] == NULL ||
1919 	     *(mci->mci_sai)[SASL_USER] == '\0'))
1920 		return EX_UNAVAILABLE;
1921 
1922 	/* set the context for the callback function to sai */
1923 # if SASL >= 20000
1924 	callbacks[CB_PASS_IDX].context = (void *) mci;
1925 # else /* SASL >= 20000 */
1926 	callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
1927 # endif /* SASL >= 20000 */
1928 	callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
1929 	callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
1930 	callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
1931 #if 0
1932 	callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
1933 #endif /* 0 */
1934 
1935 	/* set default value for realm */
1936 	if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
1937 		(mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
1938 							macvalue('j', CurEnv));
1939 
1940 	/* set default value for list of mechanism to use */
1941 	if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
1942 	    *(mci->mci_sai)[SASL_MECHLIST] == '\0')
1943 		(mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
1944 
1945 	/* create list of mechanisms to try */
1946 	mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
1947 				     mci->mci_saslcap, mci->mci_rpool);
1948 
1949 	/* initialize sasl client library */
1950 	result = init_sasl_client();
1951 	if (result != SASL_OK)
1952 		return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
1953 	do
1954 	{
1955 		result = attemptauth(m, mci, e, &(mci->mci_sai));
1956 		if (result == EX_OK)
1957 			mci->mci_sasl_auth = true;
1958 		else if (result == EX_TEMPFAIL || result == EX_NOPERM)
1959 		{
1960 			mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
1961 						      mci->mci_saslcap,
1962 						      mci->mci_rpool);
1963 			if (mci->mci_saslcap == NULL ||
1964 			    *(mci->mci_saslcap) == '\0')
1965 				return usedgetauth ? result
1966 						   : EX_UNAVAILABLE;
1967 		}
1968 		else
1969 			return result;
1970 	} while (result != EX_OK);
1971 	return result;
1972 }
1973 #endif /* SASL */
1974 
1975 /*
1976 **  SMTPMAILFROM -- send MAIL command
1977 **
1978 **	Parameters:
1979 **		m -- the mailer.
1980 **		mci -- the mailer connection structure.
1981 **		e -- the envelope (including the sender to specify).
1982 */
1983 
1984 int
1985 smtpmailfrom(m, mci, e)
1986 	MAILER *m;
1987 	MCI *mci;
1988 	ENVELOPE *e;
1989 {
1990 	int r;
1991 	char *bufp;
1992 	char *bodytype;
1993 	char *enhsc;
1994 	char buf[MAXNAME + 1];
1995 	char optbuf[MAXLINE];
1996 
1997 	if (tTd(18, 2))
1998 		sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
1999 	enhsc = NULL;
2000 
2001 	/*
2002 	**  Check if connection is gone, if so
2003 	**  it's a tempfail and we use mci_errno
2004 	**  for the reason.
2005 	*/
2006 
2007 	if (mci->mci_state == MCIS_CLOSED)
2008 	{
2009 		errno = mci->mci_errno;
2010 		return EX_TEMPFAIL;
2011 	}
2012 
2013 	/* set up appropriate options to include */
2014 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
2015 	{
2016 		(void) sm_snprintf(optbuf, sizeof optbuf, " SIZE=%ld",
2017 			e->e_msgsize);
2018 		bufp = &optbuf[strlen(optbuf)];
2019 	}
2020 	else
2021 	{
2022 		optbuf[0] = '\0';
2023 		bufp = optbuf;
2024 	}
2025 
2026 	bodytype = e->e_bodytype;
2027 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
2028 	{
2029 		if (bodytype == NULL &&
2030 		    bitset(MM_MIME8BIT, MimeMode) &&
2031 		    bitset(EF_HAS8BIT, e->e_flags) &&
2032 		    !bitset(EF_DONT_MIME, e->e_flags) &&
2033 		    !bitnset(M_8BITS, m->m_flags))
2034 			bodytype = "8BITMIME";
2035 		if (bodytype != NULL &&
2036 		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
2037 		{
2038 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2039 				 " BODY=%s", bodytype);
2040 			bufp += strlen(bufp);
2041 		}
2042 	}
2043 	else if (bitnset(M_8BITS, m->m_flags) ||
2044 		 !bitset(EF_HAS8BIT, e->e_flags) ||
2045 		 bitset(MCIF_8BITOK, mci->mci_flags))
2046 	{
2047 		/* EMPTY */
2048 		/* just pass it through */
2049 	}
2050 #if MIME8TO7
2051 	else if (bitset(MM_CVTMIME, MimeMode) &&
2052 		 !bitset(EF_DONT_MIME, e->e_flags) &&
2053 		 (!bitset(MM_PASS8BIT, MimeMode) ||
2054 		  bitset(EF_IS_MIME, e->e_flags)))
2055 	{
2056 		/* must convert from 8bit MIME format to 7bit encoded */
2057 		mci->mci_flags |= MCIF_CVT8TO7;
2058 	}
2059 #endif /* MIME8TO7 */
2060 	else if (!bitset(MM_PASS8BIT, MimeMode))
2061 	{
2062 		/* cannot just send a 8-bit version */
2063 		extern char MsgBuf[];
2064 
2065 		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
2066 		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
2067 		return EX_DATAERR;
2068 	}
2069 
2070 	if (bitset(MCIF_DSN, mci->mci_flags))
2071 	{
2072 		if (e->e_envid != NULL &&
2073 		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
2074 		{
2075 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2076 				 " ENVID=%s", e->e_envid);
2077 			bufp += strlen(bufp);
2078 		}
2079 
2080 		/* RET= parameter */
2081 		if (bitset(EF_RET_PARAM, e->e_flags) &&
2082 		    SPACELEFT(optbuf, bufp) > 9)
2083 		{
2084 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2085 				 " RET=%s",
2086 				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
2087 					"HDRS" : "FULL");
2088 			bufp += strlen(bufp);
2089 		}
2090 	}
2091 
2092 	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
2093 	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
2094 #if SASL
2095 	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
2096 #endif /* SASL */
2097 	    )
2098 	{
2099 		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2100 			 " AUTH=%s", e->e_auth_param);
2101 		bufp += strlen(bufp);
2102 	}
2103 
2104 	/*
2105 	**  17 is the max length required, we could use log() to compute
2106 	**  the exact length (and check IS_DLVR_TRACE())
2107 	*/
2108 
2109 	if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
2110 	    IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
2111 	{
2112 		long dby;
2113 
2114 		/*
2115 		**  Avoid problems with delays (for R) since the check
2116 		**  in deliver() whether min-deliver-time is sufficient.
2117 		**  Alternatively we could pass the computed time to this
2118 		**  function.
2119 		*/
2120 
2121 		dby = e->e_deliver_by - (curtime() - e->e_ctime);
2122 		if (dby <= 0 && IS_DLVR_RETURN(e))
2123 			dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
2124 		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2125 			" BY=%ld;%c%s",
2126 			dby,
2127 			IS_DLVR_RETURN(e) ? 'R' : 'N',
2128 			IS_DLVR_TRACE(e) ? "T" : "");
2129 		bufp += strlen(bufp);
2130 	}
2131 
2132 	/*
2133 	**  Send the MAIL command.
2134 	**	Designates the sender.
2135 	*/
2136 
2137 	mci->mci_state = MCIS_MAIL;
2138 
2139 	if (bitset(EF_RESPONSE, e->e_flags) &&
2140 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
2141 		buf[0] = '\0';
2142 	else
2143 		expand("\201g", buf, sizeof buf, e);
2144 	if (buf[0] == '<')
2145 	{
2146 		/* strip off <angle brackets> (put back on below) */
2147 		bufp = &buf[strlen(buf) - 1];
2148 		if (*bufp == '>')
2149 			*bufp = '\0';
2150 		bufp = &buf[1];
2151 	}
2152 	else
2153 		bufp = buf;
2154 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
2155 	    !bitnset(M_FROMPATH, m->m_flags))
2156 	{
2157 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
2158 	}
2159 	else
2160 	{
2161 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
2162 			    *bufp == '@' ? ',' : ':', bufp, optbuf);
2163 	}
2164 	SmtpPhase = mci->mci_phase = "client MAIL";
2165 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2166 			CurHostName, mci->mci_phase);
2167 	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc, XS_DEFAULT);
2168 	if (r < 0)
2169 	{
2170 		/* communications failure */
2171 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2172 		return EX_TEMPFAIL;
2173 	}
2174 	else if (r == SMTPCLOSING)
2175 	{
2176 		/* service shutting down: handled by reply() */
2177 		return EX_TEMPFAIL;
2178 	}
2179 	else if (REPLYTYPE(r) == 4)
2180 	{
2181 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
2182 			    SmtpReplyBuffer);
2183 		return EX_TEMPFAIL;
2184 	}
2185 	else if (REPLYTYPE(r) == 2)
2186 	{
2187 		return EX_OK;
2188 	}
2189 	else if (r == 501)
2190 	{
2191 		/* syntax error in arguments */
2192 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
2193 			    SmtpReplyBuffer);
2194 		return EX_DATAERR;
2195 	}
2196 	else if (r == 553)
2197 	{
2198 		/* mailbox name not allowed */
2199 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
2200 			    SmtpReplyBuffer);
2201 		return EX_DATAERR;
2202 	}
2203 	else if (r == 552)
2204 	{
2205 		/* exceeded storage allocation */
2206 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
2207 			    SmtpReplyBuffer);
2208 		if (bitset(MCIF_SIZE, mci->mci_flags))
2209 			e->e_flags |= EF_NO_BODY_RETN;
2210 		return EX_UNAVAILABLE;
2211 	}
2212 	else if (REPLYTYPE(r) == 5)
2213 	{
2214 		/* unknown error */
2215 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
2216 			    SmtpReplyBuffer);
2217 		return EX_UNAVAILABLE;
2218 	}
2219 
2220 	if (LogLevel > 1)
2221 	{
2222 		sm_syslog(LOG_CRIT, e->e_id,
2223 			  "%.100s: SMTP MAIL protocol error: %s",
2224 			  CurHostName,
2225 			  shortenstring(SmtpReplyBuffer, 403));
2226 	}
2227 
2228 	/* protocol error -- close up */
2229 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2230 		    SmtpReplyBuffer);
2231 	smtpquit(m, mci, e);
2232 	return EX_PROTOCOL;
2233 }
2234 /*
2235 **  SMTPRCPT -- designate recipient.
2236 **
2237 **	Parameters:
2238 **		to -- address of recipient.
2239 **		m -- the mailer we are sending to.
2240 **		mci -- the connection info for this transaction.
2241 **		e -- the envelope for this transaction.
2242 **
2243 **	Returns:
2244 **		exit status corresponding to recipient status.
2245 **
2246 **	Side Effects:
2247 **		Sends the mail via SMTP.
2248 */
2249 
2250 int
2251 smtprcpt(to, m, mci, e, ctladdr, xstart)
2252 	ADDRESS *to;
2253 	register MAILER *m;
2254 	MCI *mci;
2255 	ENVELOPE *e;
2256 	ADDRESS *ctladdr;
2257 	time_t xstart;
2258 {
2259 	char *bufp;
2260 	char optbuf[MAXLINE];
2261 
2262 #if PIPELINING
2263 	/*
2264 	**  If there is status waiting from the other end, read it.
2265 	**  This should normally happen because of SMTP pipelining.
2266 	*/
2267 
2268 	while (mci->mci_nextaddr != NULL &&
2269 	       sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2270 	{
2271 		int r;
2272 
2273 		r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2274 		if (r != EX_OK)
2275 		{
2276 			markfailure(e, mci->mci_nextaddr, mci, r, false);
2277 			giveresponse(r, mci->mci_nextaddr->q_status,  m, mci,
2278 				     ctladdr, xstart, e, to);
2279 		}
2280 		mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2281 	}
2282 #endif /* PIPELINING */
2283 
2284 	/*
2285 	**  Check if connection is gone, if so
2286 	**  it's a tempfail and we use mci_errno
2287 	**  for the reason.
2288 	*/
2289 
2290 	if (mci->mci_state == MCIS_CLOSED)
2291 	{
2292 		errno = mci->mci_errno;
2293 		return EX_TEMPFAIL;
2294 	}
2295 
2296 	optbuf[0] = '\0';
2297 	bufp = optbuf;
2298 
2299 	/*
2300 	**  Warning: in the following it is assumed that the free space
2301 	**  in bufp is sizeof optbuf
2302 	*/
2303 
2304 	if (bitset(MCIF_DSN, mci->mci_flags))
2305 	{
2306 		if (IS_DLVR_NOTIFY(e) &&
2307 		    !bitset(MCIF_DLVR_BY, mci->mci_flags))
2308 		{
2309 			/* RFC 2852: 4.1.4.2 */
2310 			if (!bitset(QHASNOTIFY, to->q_flags))
2311 				to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
2312 			else if (bitset(QPINGONSUCCESS, to->q_flags) ||
2313 				 bitset(QPINGONFAILURE, to->q_flags) ||
2314 				 bitset(QPINGONDELAY, to->q_flags))
2315 				to->q_flags |= QPINGONDELAY;
2316 		}
2317 
2318 		/* NOTIFY= parameter */
2319 		if (bitset(QHASNOTIFY, to->q_flags) &&
2320 		    bitset(QPRIMARY, to->q_flags) &&
2321 		    !bitnset(M_LOCALMAILER, m->m_flags))
2322 		{
2323 			bool firstone = true;
2324 
2325 			(void) sm_strlcat(bufp, " NOTIFY=", sizeof optbuf);
2326 			if (bitset(QPINGONSUCCESS, to->q_flags))
2327 			{
2328 				(void) sm_strlcat(bufp, "SUCCESS", sizeof optbuf);
2329 				firstone = false;
2330 			}
2331 			if (bitset(QPINGONFAILURE, to->q_flags))
2332 			{
2333 				if (!firstone)
2334 					(void) sm_strlcat(bufp, ",",
2335 						       sizeof optbuf);
2336 				(void) sm_strlcat(bufp, "FAILURE", sizeof optbuf);
2337 				firstone = false;
2338 			}
2339 			if (bitset(QPINGONDELAY, to->q_flags))
2340 			{
2341 				if (!firstone)
2342 					(void) sm_strlcat(bufp, ",",
2343 						       sizeof optbuf);
2344 				(void) sm_strlcat(bufp, "DELAY", sizeof optbuf);
2345 				firstone = false;
2346 			}
2347 			if (firstone)
2348 				(void) sm_strlcat(bufp, "NEVER", sizeof optbuf);
2349 			bufp += strlen(bufp);
2350 		}
2351 
2352 		/* ORCPT= parameter */
2353 		if (to->q_orcpt != NULL &&
2354 		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
2355 		{
2356 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2357 				 " ORCPT=%s", to->q_orcpt);
2358 			bufp += strlen(bufp);
2359 		}
2360 	}
2361 
2362 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
2363 	mci->mci_state = MCIS_RCPT;
2364 
2365 	SmtpPhase = mci->mci_phase = "client RCPT";
2366 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2367 			CurHostName, mci->mci_phase);
2368 
2369 #if PIPELINING
2370 	/*
2371 	**  If running SMTP pipelining, we will pick up status later
2372 	*/
2373 
2374 	if (bitset(MCIF_PIPELINED, mci->mci_flags))
2375 		return EX_OK;
2376 #endif /* PIPELINING */
2377 
2378 	return smtprcptstat(to, m, mci, e);
2379 }
2380 /*
2381 **  SMTPRCPTSTAT -- get recipient status
2382 **
2383 **	This is only called during SMTP pipelining
2384 **
2385 **	Parameters:
2386 **		to -- address of recipient.
2387 **		m -- mailer being sent to.
2388 **		mci -- the mailer connection information.
2389 **		e -- the envelope for this message.
2390 **
2391 **	Returns:
2392 **		EX_* -- protocol status
2393 */
2394 
2395 static int
2396 smtprcptstat(to, m, mci, e)
2397 	ADDRESS *to;
2398 	MAILER *m;
2399 	register MCI *mci;
2400 	register ENVELOPE *e;
2401 {
2402 	int r;
2403 	int save_errno;
2404 	char *enhsc;
2405 
2406 	/*
2407 	**  Check if connection is gone, if so
2408 	**  it's a tempfail and we use mci_errno
2409 	**  for the reason.
2410 	*/
2411 
2412 	if (mci->mci_state == MCIS_CLOSED)
2413 	{
2414 		errno = mci->mci_errno;
2415 		return EX_TEMPFAIL;
2416 	}
2417 
2418 	enhsc = NULL;
2419 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc, XS_DEFAULT);
2420 	save_errno = errno;
2421 	to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
2422 	to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
2423 	if (!bitnset(M_LMTP, m->m_flags))
2424 		to->q_statmta = mci->mci_host;
2425 	if (r < 0 || REPLYTYPE(r) == 4)
2426 	{
2427 		mci->mci_retryrcpt = true;
2428 		errno = save_errno;
2429 		return EX_TEMPFAIL;
2430 	}
2431 	else if (REPLYTYPE(r) == 2)
2432 	{
2433 		char *t;
2434 
2435 		if ((t = mci->mci_tolist) != NULL)
2436 		{
2437 			char *p;
2438 
2439 			*t++ = ',';
2440 			for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
2441 				continue;
2442 			*t = '\0';
2443 			mci->mci_tolist = t;
2444 		}
2445 #if PIPELINING
2446 		mci->mci_okrcpts++;
2447 #endif /* PIPELINING */
2448 		return EX_OK;
2449 	}
2450 	else if (r == 550)
2451 	{
2452 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
2453 		return EX_NOUSER;
2454 	}
2455 	else if (r == 551)
2456 	{
2457 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
2458 		return EX_NOUSER;
2459 	}
2460 	else if (r == 553)
2461 	{
2462 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
2463 		return EX_NOUSER;
2464 	}
2465 	else if (REPLYTYPE(r) == 5)
2466 	{
2467 		return EX_UNAVAILABLE;
2468 	}
2469 
2470 	if (LogLevel > 1)
2471 	{
2472 		sm_syslog(LOG_CRIT, e->e_id,
2473 			  "%.100s: SMTP RCPT protocol error: %s",
2474 			  CurHostName,
2475 			  shortenstring(SmtpReplyBuffer, 403));
2476 	}
2477 
2478 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2479 		    SmtpReplyBuffer);
2480 	return EX_PROTOCOL;
2481 }
2482 /*
2483 **  SMTPDATA -- send the data and clean up the transaction.
2484 **
2485 **	Parameters:
2486 **		m -- mailer being sent to.
2487 **		mci -- the mailer connection information.
2488 **		e -- the envelope for this message.
2489 **
2490 **	Returns:
2491 **		exit status corresponding to DATA command.
2492 */
2493 
2494 static jmp_buf	CtxDataTimeout;
2495 static SM_EVENT	*volatile DataTimeout = NULL;
2496 
2497 int
2498 smtpdata(m, mci, e, ctladdr, xstart)
2499 	MAILER *m;
2500 	register MCI *mci;
2501 	register ENVELOPE *e;
2502 	ADDRESS *ctladdr;
2503 	time_t xstart;
2504 {
2505 	register int r;
2506 	int rstat;
2507 	int xstat;
2508 	time_t timeout;
2509 	char *enhsc;
2510 
2511 	/*
2512 	**  Check if connection is gone, if so
2513 	**  it's a tempfail and we use mci_errno
2514 	**  for the reason.
2515 	*/
2516 
2517 	if (mci->mci_state == MCIS_CLOSED)
2518 	{
2519 		errno = mci->mci_errno;
2520 		return EX_TEMPFAIL;
2521 	}
2522 
2523 	enhsc = NULL;
2524 
2525 	/*
2526 	**  Send the data.
2527 	**	First send the command and check that it is ok.
2528 	**	Then send the data (if there are valid recipients).
2529 	**	Follow it up with a dot to terminate.
2530 	**	Finally get the results of the transaction.
2531 	*/
2532 
2533 	/* send the command and check ok to proceed */
2534 	smtpmessage("DATA", m, mci);
2535 
2536 #if PIPELINING
2537 	if (mci->mci_nextaddr != NULL)
2538 	{
2539 		char *oldto = e->e_to;
2540 
2541 		/* pick up any pending RCPT responses for SMTP pipelining */
2542 		while (mci->mci_nextaddr != NULL)
2543 		{
2544 			int r;
2545 
2546 			e->e_to = mci->mci_nextaddr->q_paddr;
2547 			r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2548 			if (r != EX_OK)
2549 			{
2550 				markfailure(e, mci->mci_nextaddr, mci, r,
2551 					    false);
2552 				giveresponse(r, mci->mci_nextaddr->q_status, m,
2553 					     mci, ctladdr, xstart, e,
2554 					     mci->mci_nextaddr);
2555 				if (r == EX_TEMPFAIL)
2556 					mci->mci_nextaddr->q_state = QS_RETRY;
2557 			}
2558 			mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2559 		}
2560 		e->e_to = oldto;
2561 
2562 		/*
2563 		**  Connection might be closed in response to a RCPT command,
2564 		**  i.e., the server responded with 421. In that case (at
2565 		**  least) one RCPT has a temporary failure, hence we don't
2566 		**  need to check mci_okrcpts (as it is done below) to figure
2567 		**  out which error to return.
2568 		*/
2569 
2570 		if (mci->mci_state == MCIS_CLOSED)
2571 		{
2572 			errno = mci->mci_errno;
2573 			return EX_TEMPFAIL;
2574 		}
2575 	}
2576 #endif /* PIPELINING */
2577 
2578 	/* now proceed with DATA phase */
2579 	SmtpPhase = mci->mci_phase = "client DATA 354";
2580 	mci->mci_state = MCIS_DATA;
2581 	sm_setproctitle(true, e, "%s %s: %s",
2582 			qid_printname(e), CurHostName, mci->mci_phase);
2583 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc, XS_DEFAULT);
2584 	if (r < 0 || REPLYTYPE(r) == 4)
2585 	{
2586 		if (r >= 0)
2587 			smtpquit(m, mci, e);
2588 		errno = mci->mci_errno;
2589 		return EX_TEMPFAIL;
2590 	}
2591 	else if (REPLYTYPE(r) == 5)
2592 	{
2593 		smtprset(m, mci, e);
2594 #if PIPELINING
2595 		if (mci->mci_okrcpts <= 0)
2596 			return mci->mci_retryrcpt ? EX_TEMPFAIL
2597 						  : EX_UNAVAILABLE;
2598 #endif /* PIPELINING */
2599 		return EX_UNAVAILABLE;
2600 	}
2601 	else if (REPLYTYPE(r) != 3)
2602 	{
2603 		if (LogLevel > 1)
2604 		{
2605 			sm_syslog(LOG_CRIT, e->e_id,
2606 				  "%.100s: SMTP DATA-1 protocol error: %s",
2607 				  CurHostName,
2608 				  shortenstring(SmtpReplyBuffer, 403));
2609 		}
2610 		smtprset(m, mci, e);
2611 		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2612 			    SmtpReplyBuffer);
2613 #if PIPELINING
2614 		if (mci->mci_okrcpts <= 0)
2615 			return mci->mci_retryrcpt ? EX_TEMPFAIL
2616 						  : EX_PROTOCOL;
2617 #endif /* PIPELINING */
2618 		return EX_PROTOCOL;
2619 	}
2620 
2621 #if PIPELINING
2622 	if (mci->mci_okrcpts > 0)
2623 	{
2624 #endif /* PIPELINING */
2625 
2626 	/*
2627 	**  Set timeout around data writes.  Make it at least large
2628 	**  enough for DNS timeouts on all recipients plus some fudge
2629 	**  factor.  The main thing is that it should not be infinite.
2630 	*/
2631 
2632 	if (setjmp(CtxDataTimeout) != 0)
2633 	{
2634 		mci->mci_errno = errno;
2635 		mci->mci_state = MCIS_ERROR;
2636 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2637 
2638 		/*
2639 		**  If putbody() couldn't finish due to a timeout,
2640 		**  rewind it here in the timeout handler.  See
2641 		**  comments at the end of putbody() for reasoning.
2642 		*/
2643 
2644 		if (e->e_dfp != NULL)
2645 			(void) bfrewind(e->e_dfp);
2646 
2647 		errno = mci->mci_errno;
2648 		syserr("451 4.4.1 timeout writing message to %s", CurHostName);
2649 		smtpquit(m, mci, e);
2650 		return EX_TEMPFAIL;
2651 	}
2652 
2653 	if (tTd(18, 101))
2654 	{
2655 		/* simulate a DATA timeout */
2656 		timeout = 1;
2657 	}
2658 	else
2659 		timeout = DATA_PROGRESS_TIMEOUT;
2660 
2661 	DataTimeout = sm_setevent(timeout, datatimeout, 0);
2662 
2663 
2664 	/*
2665 	**  Output the actual message.
2666 	*/
2667 
2668 	(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
2669 
2670 	if (tTd(18, 101))
2671 	{
2672 		/* simulate a DATA timeout */
2673 		(void) sleep(2);
2674 	}
2675 
2676 	(*e->e_putbody)(mci, e, NULL);
2677 
2678 	/*
2679 	**  Cleanup after sending message.
2680 	*/
2681 
2682 	if (DataTimeout != NULL)
2683 		sm_clrevent(DataTimeout);
2684 
2685 #if PIPELINING
2686 	}
2687 #endif /* PIPELINING */
2688 
2689 #if _FFR_CATCH_BROKEN_MTAS
2690 	if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2691 	{
2692 		/* terminate the message */
2693 		(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
2694 				     m->m_eol);
2695 		if (TrafficLogFile != NULL)
2696 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2697 					     "%05d >>> .\n", (int) CurrentPid);
2698 		if (Verbose)
2699 			nmessage(">>> .");
2700 
2701 		sm_syslog(LOG_CRIT, e->e_id,
2702 			  "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
2703 			  CurHostName);
2704 		mci->mci_errno = EIO;
2705 		mci->mci_state = MCIS_ERROR;
2706 		mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
2707 		smtpquit(m, mci, e);
2708 		return EX_PROTOCOL;
2709 	}
2710 #endif /* _FFR_CATCH_BROKEN_MTAS */
2711 
2712 	if (sm_io_error(mci->mci_out))
2713 	{
2714 		/* error during processing -- don't send the dot */
2715 		mci->mci_errno = EIO;
2716 		mci->mci_state = MCIS_ERROR;
2717 		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
2718 		smtpquit(m, mci, e);
2719 		return EX_IOERR;
2720 	}
2721 
2722 	/* terminate the message */
2723 	(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s", m->m_eol);
2724 	if (TrafficLogFile != NULL)
2725 		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2726 				     "%05d >>> .\n", (int) CurrentPid);
2727 	if (Verbose)
2728 		nmessage(">>> .");
2729 
2730 	/* check for the results of the transaction */
2731 	SmtpPhase = mci->mci_phase = "client DATA status";
2732 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2733 			CurHostName, mci->mci_phase);
2734 	if (bitnset(M_LMTP, m->m_flags))
2735 		return EX_OK;
2736 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
2737 	if (r < 0)
2738 		return EX_TEMPFAIL;
2739 	if (mci->mci_state == MCIS_DATA)
2740 		mci->mci_state = MCIS_OPEN;
2741 	xstat = EX_NOTSTICKY;
2742 	if (r == 452)
2743 		rstat = EX_TEMPFAIL;
2744 	else if (REPLYTYPE(r) == 4)
2745 		rstat = xstat = EX_TEMPFAIL;
2746 	else if (REPLYTYPE(r) == 2)
2747 		rstat = xstat = EX_OK;
2748 	else if (REPLYCLASS(r) != 5)
2749 		rstat = xstat = EX_PROTOCOL;
2750 	else if (REPLYTYPE(r) == 5)
2751 		rstat = EX_UNAVAILABLE;
2752 	else
2753 		rstat = EX_PROTOCOL;
2754 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
2755 		    SmtpReplyBuffer);
2756 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2757 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2758 		r += 5;
2759 	else
2760 		r = 4;
2761 	e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
2762 	SmtpPhase = mci->mci_phase = "idle";
2763 	sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
2764 	if (rstat != EX_PROTOCOL)
2765 		return rstat;
2766 	if (LogLevel > 1)
2767 	{
2768 		sm_syslog(LOG_CRIT, e->e_id,
2769 			  "%.100s: SMTP DATA-2 protocol error: %s",
2770 			  CurHostName,
2771 			  shortenstring(SmtpReplyBuffer, 403));
2772 	}
2773 	return rstat;
2774 }
2775 
2776 static void
2777 datatimeout(ignore)
2778 	int ignore;
2779 {
2780 	int save_errno = errno;
2781 
2782 	/*
2783 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2784 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2785 	**	DOING.
2786 	*/
2787 
2788 	if (DataProgress)
2789 	{
2790 		time_t timeout;
2791 
2792 		/* check back again later */
2793 		if (tTd(18, 101))
2794 		{
2795 			/* simulate a DATA timeout */
2796 			timeout = 1;
2797 		}
2798 		else
2799 			timeout = DATA_PROGRESS_TIMEOUT;
2800 
2801 		/* reset the timeout */
2802 		DataTimeout = sm_sigsafe_setevent(timeout, datatimeout, 0);
2803 		DataProgress = false;
2804 	}
2805 	else
2806 	{
2807 		/* event is done */
2808 		DataTimeout = NULL;
2809 	}
2810 
2811 	/* if no progress was made or problem resetting event, die now */
2812 	if (DataTimeout == NULL)
2813 	{
2814 		errno = ETIMEDOUT;
2815 		longjmp(CtxDataTimeout, 1);
2816 	}
2817 	errno = save_errno;
2818 }
2819 /*
2820 **  SMTPGETSTAT -- get status code from DATA in LMTP
2821 **
2822 **	Parameters:
2823 **		m -- the mailer to which we are sending the message.
2824 **		mci -- the mailer connection structure.
2825 **		e -- the current envelope.
2826 **
2827 **	Returns:
2828 **		The exit status corresponding to the reply code.
2829 */
2830 
2831 int
2832 smtpgetstat(m, mci, e)
2833 	MAILER *m;
2834 	MCI *mci;
2835 	ENVELOPE *e;
2836 {
2837 	int r;
2838 	int off;
2839 	int status, xstat;
2840 	char *enhsc;
2841 
2842 	enhsc = NULL;
2843 
2844 	/* check for the results of the transaction */
2845 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
2846 	if (r < 0)
2847 		return EX_TEMPFAIL;
2848 	xstat = EX_NOTSTICKY;
2849 	if (REPLYTYPE(r) == 4)
2850 		status = EX_TEMPFAIL;
2851 	else if (REPLYTYPE(r) == 2)
2852 		status = xstat = EX_OK;
2853 	else if (REPLYCLASS(r) != 5)
2854 		status = xstat = EX_PROTOCOL;
2855 	else if (REPLYTYPE(r) == 5)
2856 		status = EX_UNAVAILABLE;
2857 	else
2858 		status = EX_PROTOCOL;
2859 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2860 	    (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2861 		off += 5;
2862 	else
2863 		off = 4;
2864 	e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
2865 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer);
2866 	if (LogLevel > 1 && status == EX_PROTOCOL)
2867 	{
2868 		sm_syslog(LOG_CRIT, e->e_id,
2869 			  "%.100s: SMTP DATA-3 protocol error: %s",
2870 			  CurHostName,
2871 			  shortenstring(SmtpReplyBuffer, 403));
2872 	}
2873 	return status;
2874 }
2875 /*
2876 **  SMTPQUIT -- close the SMTP connection.
2877 **
2878 **	Parameters:
2879 **		m -- a pointer to the mailer.
2880 **		mci -- the mailer connection information.
2881 **		e -- the current envelope.
2882 **
2883 **	Returns:
2884 **		none.
2885 **
2886 **	Side Effects:
2887 **		sends the final protocol and closes the connection.
2888 */
2889 
2890 void
2891 smtpquit(m, mci, e)
2892 	register MAILER *m;
2893 	register MCI *mci;
2894 	ENVELOPE *e;
2895 {
2896 	bool oldSuprErrs = SuprErrs;
2897 	int rcode;
2898 	char *oldcurhost;
2899 
2900 	if (mci->mci_state == MCIS_CLOSED)
2901 	{
2902 		mci_close(mci, "smtpquit:1");
2903 		return;
2904 	}
2905 
2906 	oldcurhost = CurHostName;
2907 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2908 	if (CurHostName == NULL)
2909 		CurHostName = MyHostName;
2910 
2911 #if PIPELINING
2912 	mci->mci_okrcpts = 0;
2913 #endif /* PIPELINING */
2914 
2915 	/*
2916 	**	Suppress errors here -- we may be processing a different
2917 	**	job when we do the quit connection, and we don't want the
2918 	**	new job to be penalized for something that isn't it's
2919 	**	problem.
2920 	*/
2921 
2922 	SuprErrs = true;
2923 
2924 	/* send the quit message if we haven't gotten I/O error */
2925 	if (mci->mci_state != MCIS_ERROR &&
2926 	    mci->mci_state != MCIS_QUITING)
2927 	{
2928 		SmtpPhase = "client QUIT";
2929 		mci->mci_state = MCIS_QUITING;
2930 		smtpmessage("QUIT", m, mci);
2931 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL,
2932 				XS_DEFAULT);
2933 		SuprErrs = oldSuprErrs;
2934 		if (mci->mci_state == MCIS_CLOSED)
2935 			goto end;
2936 	}
2937 
2938 	/* now actually close the connection and pick up the zombie */
2939 	rcode = endmailer(mci, e, NULL);
2940 	if (rcode != EX_OK)
2941 	{
2942 		char *mailer = NULL;
2943 
2944 		if (mci->mci_mailer != NULL &&
2945 		    mci->mci_mailer->m_name != NULL)
2946 			mailer = mci->mci_mailer->m_name;
2947 
2948 		/* look for naughty mailers */
2949 		sm_syslog(LOG_ERR, e->e_id,
2950 			  "smtpquit: mailer%s%s exited with exit value %d",
2951 			  mailer == NULL ? "" : " ",
2952 			  mailer == NULL ? "" : mailer,
2953 			  rcode);
2954 	}
2955 
2956 	SuprErrs = oldSuprErrs;
2957 
2958   end:
2959 	CurHostName = oldcurhost;
2960 	return;
2961 }
2962 /*
2963 **  SMTPRSET -- send a RSET (reset) command
2964 **
2965 **	Parameters:
2966 **		m -- a pointer to the mailer.
2967 **		mci -- the mailer connection information.
2968 **		e -- the current envelope.
2969 **
2970 **	Returns:
2971 **		none.
2972 **
2973 **	Side Effects:
2974 **		closes the connection if there is no reply to RSET.
2975 */
2976 
2977 void
2978 smtprset(m, mci, e)
2979 	register MAILER *m;
2980 	register MCI *mci;
2981 	ENVELOPE *e;
2982 {
2983 	int r;
2984 
2985 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2986 	if (CurHostName == NULL)
2987 		CurHostName = MyHostName;
2988 
2989 #if PIPELINING
2990 	mci->mci_okrcpts = 0;
2991 #endif /* PIPELINING */
2992 
2993 	/*
2994 	**  Check if connection is gone, if so
2995 	**  it's a tempfail and we use mci_errno
2996 	**  for the reason.
2997 	*/
2998 
2999 	if (mci->mci_state == MCIS_CLOSED)
3000 	{
3001 		errno = mci->mci_errno;
3002 		return;
3003 	}
3004 
3005 	SmtpPhase = "client RSET";
3006 	smtpmessage("RSET", m, mci);
3007 	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL, XS_DEFAULT);
3008 	if (r < 0)
3009 		return;
3010 
3011 	/*
3012 	**  Any response is deemed to be acceptable.
3013 	**  The standard does not state the proper action
3014 	**  to take when a value other than 250 is received.
3015 	**
3016 	**  However, if 421 is returned for the RSET, leave
3017 	**  mci_state alone (MCIS_SSD can be set in reply()
3018 	**  and MCIS_CLOSED can be set in smtpquit() if
3019 	**  reply() gets a 421 and calls smtpquit()).
3020 	*/
3021 
3022 	if (mci->mci_state != MCIS_SSD && mci->mci_state != MCIS_CLOSED)
3023 		mci->mci_state = MCIS_OPEN;
3024 }
3025 /*
3026 **  SMTPPROBE -- check the connection state
3027 **
3028 **	Parameters:
3029 **		mci -- the mailer connection information.
3030 **
3031 **	Returns:
3032 **		none.
3033 **
3034 **	Side Effects:
3035 **		closes the connection if there is no reply to RSET.
3036 */
3037 
3038 int
3039 smtpprobe(mci)
3040 	register MCI *mci;
3041 {
3042 	int r;
3043 	MAILER *m = mci->mci_mailer;
3044 	ENVELOPE *e;
3045 	extern ENVELOPE BlankEnvelope;
3046 
3047 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
3048 	if (CurHostName == NULL)
3049 		CurHostName = MyHostName;
3050 
3051 	e = &BlankEnvelope;
3052 	SmtpPhase = "client probe";
3053 	smtpmessage("RSET", m, mci);
3054 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL, XS_DEFAULT);
3055 	if (REPLYTYPE(r) != 2)
3056 		smtpquit(m, mci, e);
3057 	return r;
3058 }
3059 /*
3060 **  REPLY -- read arpanet reply
3061 **
3062 **	Parameters:
3063 **		m -- the mailer we are reading the reply from.
3064 **		mci -- the mailer connection info structure.
3065 **		e -- the current envelope.
3066 **		timeout -- the timeout for reads.
3067 **		pfunc -- processing function called on each line of response.
3068 **			If null, no special processing is done.
3069 **		enhstat -- optional, returns enhanced error code string (if set)
3070 **		rtype -- type of SmtpMsgBuffer: does it contains secret data?
3071 **
3072 **	Returns:
3073 **		reply code it reads.
3074 **
3075 **	Side Effects:
3076 **		flushes the mail file.
3077 */
3078 
3079 int
3080 reply(m, mci, e, timeout, pfunc, enhstat, rtype)
3081 	MAILER *m;
3082 	MCI *mci;
3083 	ENVELOPE *e;
3084 	time_t timeout;
3085 	void (*pfunc) __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
3086 	char **enhstat;
3087 	int rtype;
3088 {
3089 	register char *bufp;
3090 	register int r;
3091 	bool firstline = true;
3092 	char junkbuf[MAXLINE];
3093 	static char enhstatcode[ENHSCLEN];
3094 	int save_errno;
3095 
3096 	/*
3097 	**  Flush the output before reading response.
3098 	**
3099 	**	For SMTP pipelining, it would be better if we didn't do
3100 	**	this if there was already data waiting to be read.  But
3101 	**	to do it properly means pushing it to the I/O library,
3102 	**	since it really needs to be done below the buffer layer.
3103 	*/
3104 
3105 	if (mci->mci_out != NULL)
3106 		(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3107 
3108 	if (tTd(18, 1))
3109 		sm_dprintf("reply\n");
3110 
3111 	/*
3112 	**  Read the input line, being careful not to hang.
3113 	*/
3114 
3115 	bufp = SmtpReplyBuffer;
3116 	for (;;)
3117 	{
3118 		register char *p;
3119 
3120 		/* actually do the read */
3121 		if (e->e_xfp != NULL)	/* for debugging */
3122 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
3123 
3124 		/* if we are in the process of closing just give the code */
3125 		if (mci->mci_state == MCIS_CLOSED)
3126 			return SMTPCLOSING;
3127 
3128 		/* don't try to read from a non-existent fd */
3129 		if (mci->mci_in == NULL)
3130 		{
3131 			if (mci->mci_errno == 0)
3132 				mci->mci_errno = EBADF;
3133 
3134 			/* errors on QUIT should be ignored */
3135 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3136 			{
3137 				errno = mci->mci_errno;
3138 				mci_close(mci, "reply:1");
3139 				return -1;
3140 			}
3141 			mci->mci_state = MCIS_ERROR;
3142 			smtpquit(m, mci, e);
3143 			errno = mci->mci_errno;
3144 			return -1;
3145 		}
3146 
3147 		if (mci->mci_out != NULL)
3148 			(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3149 
3150 		/* get the line from the other side */
3151 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
3152 		save_errno = errno;
3153 		mci->mci_lastuse = curtime();
3154 
3155 		if (p == NULL)
3156 		{
3157 			bool oldholderrs;
3158 			extern char MsgBuf[];
3159 
3160 			/* errors on QUIT should be ignored */
3161 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3162 			{
3163 				mci_close(mci, "reply:2");
3164 				return -1;
3165 			}
3166 
3167 			/* if the remote end closed early, fake an error */
3168 			errno = save_errno;
3169 			if (errno == 0)
3170 			{
3171 				(void) sm_snprintf(SmtpReplyBuffer,
3172 						   sizeof SmtpReplyBuffer,
3173 						   "421 4.4.1 Connection reset by %s",
3174 						   CURHOSTNAME);
3175 #ifdef ECONNRESET
3176 				errno = ECONNRESET;
3177 #else /* ECONNRESET */
3178 				errno = EPIPE;
3179 #endif /* ECONNRESET */
3180 			}
3181 
3182 			mci->mci_errno = errno;
3183 			oldholderrs = HoldErrs;
3184 			HoldErrs = true;
3185 			usrerr("451 4.4.1 reply: read error from %s",
3186 			       CURHOSTNAME);
3187 			mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
3188 
3189 			/* if debugging, pause so we can see state */
3190 			if (tTd(18, 100))
3191 				(void) pause();
3192 			mci->mci_state = MCIS_ERROR;
3193 			smtpquit(m, mci, e);
3194 #if XDEBUG
3195 			{
3196 				char wbuf[MAXLINE];
3197 
3198 				p = wbuf;
3199 				if (e->e_to != NULL)
3200 				{
3201 					(void) sm_snprintf(p,
3202 							   SPACELEFT(wbuf, p),
3203 							   "%s... ",
3204 							   shortenstring(e->e_to, MAXSHORTSTR));
3205 					p += strlen(p);
3206 				}
3207 				(void) sm_snprintf(p, SPACELEFT(wbuf, p),
3208 						   "reply(%.100s) during %s",
3209 						   CURHOSTNAME, SmtpPhase);
3210 				checkfd012(wbuf);
3211 			}
3212 #endif /* XDEBUG */
3213 			HoldErrs = oldholderrs;
3214 			errno = save_errno;
3215 			return -1;
3216 		}
3217 		fixcrlf(bufp, true);
3218 
3219 		/* EHLO failure is not a real error */
3220 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
3221 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
3222 		{
3223 			/* serious error -- log the previous command */
3224 			if (SmtpNeedIntro)
3225 			{
3226 				/* inform user who we are chatting with */
3227 				(void) sm_io_fprintf(CurEnv->e_xfp,
3228 						     SM_TIME_DEFAULT,
3229 						     "... while talking to %s:\n",
3230 						     CURHOSTNAME);
3231 				SmtpNeedIntro = false;
3232 			}
3233 			if (SmtpMsgBuffer[0] != '\0')
3234 			{
3235 				(void) sm_io_fprintf(e->e_xfp,
3236 					SM_TIME_DEFAULT,
3237 					">>> %s\n",
3238 					(rtype == XS_STARTTLS)
3239 					? "STARTTLS dialogue"
3240 					: ((rtype == XS_AUTH)
3241 					   ? "AUTH dialogue"
3242 					   : SmtpMsgBuffer));
3243 				SmtpMsgBuffer[0] = '\0';
3244 			}
3245 
3246 			/* now log the message as from the other side */
3247 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3248 					     "<<< %s\n", bufp);
3249 		}
3250 
3251 		/* display the input for verbose mode */
3252 		if (Verbose)
3253 			nmessage("050 %s", bufp);
3254 
3255 		/* ignore improperly formatted input */
3256 		if (!ISSMTPREPLY(bufp))
3257 			continue;
3258 
3259 		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
3260 		    enhstat != NULL &&
3261 		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
3262 			*enhstat = enhstatcode;
3263 
3264 		/* process the line */
3265 		if (pfunc != NULL)
3266 			(*pfunc)(bufp, firstline, m, mci, e);
3267 
3268 		firstline = false;
3269 
3270 		/* decode the reply code */
3271 		r = atoi(bufp);
3272 
3273 		/* extra semantics: 0xx codes are "informational" */
3274 		if (r < 100)
3275 			continue;
3276 
3277 		/* if no continuation lines, return this line */
3278 		if (bufp[3] != '-')
3279 			break;
3280 
3281 		/* first line of real reply -- ignore rest */
3282 		bufp = junkbuf;
3283 	}
3284 
3285 	/*
3286 	**  Now look at SmtpReplyBuffer -- only care about the first
3287 	**  line of the response from here on out.
3288 	*/
3289 
3290 	/* save temporary failure messages for posterity */
3291 	if (SmtpReplyBuffer[0] == '4')
3292 		(void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof SmtpError);
3293 
3294 	/* reply code 421 is "Service Shutting Down" */
3295 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
3296 	    mci->mci_state != MCIS_QUITING)
3297 	{
3298 		/* send the quit protocol */
3299 		mci->mci_state = MCIS_SSD;
3300 		smtpquit(m, mci, e);
3301 	}
3302 
3303 	return r;
3304 }
3305 /*
3306 **  SMTPMESSAGE -- send message to server
3307 **
3308 **	Parameters:
3309 **		f -- format
3310 **		m -- the mailer to control formatting.
3311 **		a, b, c -- parameters
3312 **
3313 **	Returns:
3314 **		none.
3315 **
3316 **	Side Effects:
3317 **		writes message to mci->mci_out.
3318 */
3319 
3320 /*VARARGS1*/
3321 void
3322 #ifdef __STDC__
3323 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
3324 #else /* __STDC__ */
3325 smtpmessage(f, m, mci, va_alist)
3326 	char *f;
3327 	MAILER *m;
3328 	MCI *mci;
3329 	va_dcl
3330 #endif /* __STDC__ */
3331 {
3332 	SM_VA_LOCAL_DECL
3333 
3334 	SM_VA_START(ap, mci);
3335 	(void) sm_vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
3336 	SM_VA_END(ap);
3337 
3338 	if (tTd(18, 1) || Verbose)
3339 		nmessage(">>> %s", SmtpMsgBuffer);
3340 	if (TrafficLogFile != NULL)
3341 		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
3342 				     "%05d >>> %s\n", (int) CurrentPid,
3343 				     SmtpMsgBuffer);
3344 	if (mci->mci_out != NULL)
3345 	{
3346 		(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
3347 				     SmtpMsgBuffer, m == NULL ? "\r\n"
3348 							      : m->m_eol);
3349 	}
3350 	else if (tTd(18, 1))
3351 	{
3352 		sm_dprintf("smtpmessage: NULL mci_out\n");
3353 	}
3354 }
3355