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