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