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