usersmtp.c revision 71345
1/*
2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16#ifndef lint
17# if SMTP
18static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.18 2000/12/20 16:36:11 ca Exp $ (with SMTP)";
19# else /* SMTP */
20static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.18 2000/12/20 16:36:11 ca Exp $ (without SMTP)";
21# endif /* SMTP */
22#endif /* ! lint */
23
24#include <sysexits.h>
25
26#if SMTP
27
28
29static void	datatimeout __P((void));
30static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
31static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
32
33/*
34**  USERSMTP -- run SMTP protocol from the user end.
35**
36**	This protocol is described in RFC821.
37*/
38
39# define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
40# define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
41# define SMTPCLOSING	421			/* "Service Shutting Down" */
42
43#define ENHSCN(e, d)	(e) == NULL ? (d) : newstr(e)
44
45static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
46static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
47static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
48/*
49**  SMTPINIT -- initialize SMTP.
50**
51**	Opens the connection and sends the initial protocol.
52**
53**	Parameters:
54**		m -- mailer to create connection to.
55**		mci -- the mailer connection info.
56**		e -- the envelope.
57**		onlyhelo -- send only helo command?
58**
59**	Returns:
60**		none.
61**
62**	Side Effects:
63**		creates connection and sends initial protocol.
64*/
65
66void
67smtpinit(m, mci, e, onlyhelo)
68	MAILER *m;
69	register MCI *mci;
70	ENVELOPE *e;
71	bool onlyhelo;
72{
73	register int r;
74	register char *p;
75	register char *hn;
76	char *enhsc;
77
78	enhsc = NULL;
79	if (tTd(18, 1))
80	{
81		dprintf("smtpinit ");
82		mci_dump(mci, FALSE);
83	}
84
85	/*
86	**  Open the connection to the mailer.
87	*/
88
89	SmtpError[0] = '\0';
90	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
91	if (CurHostName == NULL)
92		CurHostName = MyHostName;
93	SmtpNeedIntro = TRUE;
94	switch (mci->mci_state)
95	{
96	  case MCIS_ACTIVE:
97		/* need to clear old information */
98		smtprset(m, mci, e);
99		/* FALLTHROUGH */
100
101	  case MCIS_OPEN:
102		if (!onlyhelo)
103			return;
104		break;
105
106	  case MCIS_ERROR:
107	  case MCIS_QUITING:
108	  case MCIS_SSD:
109		/* shouldn't happen */
110		smtpquit(m, mci, e);
111		/* FALLTHROUGH */
112
113	  case MCIS_CLOSED:
114		syserr("451 4.4.0 smtpinit: state CLOSED");
115		return;
116
117	  case MCIS_OPENING:
118		break;
119	}
120	if (onlyhelo)
121		goto helo;
122
123	mci->mci_state = MCIS_OPENING;
124
125	/*
126	**  Get the greeting message.
127	**	This should appear spontaneously.  Give it five minutes to
128	**	happen.
129	*/
130
131	SmtpPhase = mci->mci_phase = "client greeting";
132	sm_setproctitle(TRUE, e, "%s %s: %s",
133			qid_printname(e), CurHostName, mci->mci_phase);
134	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
135	if (r < 0)
136		goto tempfail1;
137	if (REPLYTYPE(r) == 4)
138		goto tempfail2;
139	if (REPLYTYPE(r) != 2)
140		goto unavailable;
141
142	/*
143	**  Send the HELO command.
144	**	My mother taught me to always introduce myself.
145	*/
146
147helo:
148	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
149		mci->mci_flags |= MCIF_ESMTP;
150	hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
151
152tryhelo:
153	if (bitnset(M_LMTP, m->m_flags))
154	{
155		smtpmessage("LHLO %s", m, mci, hn);
156		SmtpPhase = mci->mci_phase = "client LHLO";
157	}
158	else if (bitset(MCIF_ESMTP, mci->mci_flags))
159	{
160		smtpmessage("EHLO %s", m, mci, hn);
161		SmtpPhase = mci->mci_phase = "client EHLO";
162	}
163	else
164	{
165		smtpmessage("HELO %s", m, mci, hn);
166		SmtpPhase = mci->mci_phase = "client HELO";
167	}
168	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
169			CurHostName, mci->mci_phase);
170	r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL);
171	if (r < 0)
172		goto tempfail1;
173	else if (REPLYTYPE(r) == 5)
174	{
175		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
176		    !bitnset(M_LMTP, m->m_flags))
177		{
178			/* try old SMTP instead */
179			mci->mci_flags &= ~MCIF_ESMTP;
180			goto tryhelo;
181		}
182		goto unavailable;
183	}
184	else if (REPLYTYPE(r) != 2)
185		goto tempfail2;
186
187	/*
188	**  Check to see if we actually ended up talking to ourself.
189	**  This means we didn't know about an alias or MX, or we managed
190	**  to connect to an echo server.
191	*/
192
193	p = strchr(&SmtpReplyBuffer[4], ' ');
194	if (p != NULL)
195		*p = '\0';
196	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
197	    !bitnset(M_LMTP, m->m_flags) &&
198	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
199	{
200		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
201			CurHostName);
202		mci_setstat(mci, EX_CONFIG, "5.3.5", "system config error");
203		mci->mci_errno = 0;
204		smtpquit(m, mci, e);
205		return;
206	}
207
208	/*
209	**  If this is expected to be another sendmail, send some internal
210	**  commands.
211	*/
212
213	if (bitnset(M_INTERNAL, m->m_flags))
214	{
215		/* tell it to be verbose */
216		smtpmessage("VERB", m, mci);
217		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc);
218		if (r < 0)
219			goto tempfail1;
220	}
221
222	if (mci->mci_state != MCIS_CLOSED)
223	{
224		mci->mci_state = MCIS_OPEN;
225		return;
226	}
227
228	/* got a 421 error code during startup */
229
230  tempfail1:
231	if (mci->mci_errno == 0)
232		mci->mci_errno = errno;
233	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
234	if (mci->mci_state != MCIS_CLOSED)
235		smtpquit(m, mci, e);
236	return;
237
238  tempfail2:
239	if (mci->mci_errno == 0)
240		mci->mci_errno = errno;
241	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
242	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
243		    SmtpReplyBuffer);
244	if (mci->mci_state != MCIS_CLOSED)
245		smtpquit(m, mci, e);
246	return;
247
248  unavailable:
249	mci->mci_errno = errno;
250	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
251	smtpquit(m, mci, e);
252	return;
253}
254/*
255**  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
256**
257**	Parameters:
258**		line -- the response line.
259**		firstline -- set if this is the first line of the reply.
260**		m -- the mailer.
261**		mci -- the mailer connection info.
262**		e -- the envelope.
263**
264**	Returns:
265**		none.
266*/
267
268static void
269esmtp_check(line, firstline, m, mci, e)
270	char *line;
271	bool firstline;
272	MAILER *m;
273	register MCI *mci;
274	ENVELOPE *e;
275{
276	if (strstr(line, "ESMTP") != NULL)
277		mci->mci_flags |= MCIF_ESMTP;
278	if (strstr(line, "8BIT-OK") != NULL)
279		mci->mci_flags |= MCIF_8BITOK;
280}
281# if SASL
282/*
283**  STR_UNION -- create the union of two lists
284**
285**	Parameters:
286**		s1, s2 -- lists of items (separated by single blanks).
287**
288**	Returns:
289**		the union of both lists.
290*/
291
292static char *
293str_union(s1, s2)
294	char *s1, *s2;
295{
296	char *hr, *h1, *h, *res;
297	int l1, l2, rl;
298
299	if (s1 == NULL || *s1 == '\0')
300		return s2;
301	if (s2 == NULL || *s2 == '\0')
302		return s1;
303	l1 = strlen(s1);
304	l2 = strlen(s2);
305	rl = l1 + l2;
306	res = (char *)malloc(rl + 2);
307	if (res == NULL)
308	{
309		if (l1 > l2)
310			return s1;
311		return s2;
312	}
313	(void) strlcpy(res, s1, rl);
314	hr = res;
315	h1 = s2;
316	h = s2;
317
318	/* walk through s2 */
319	while (h != NULL && *h1 != '\0')
320	{
321		/* is there something after the current word? */
322		if ((h = strchr(h1, ' ')) != NULL)
323			*h = '\0';
324		l1 = strlen(h1);
325
326		/* does the current word appear in s1 ? */
327		if (iteminlist(h1, s1, " ") == NULL)
328		{
329			/* add space as delimiter */
330			*hr++ = ' ';
331
332			/* copy the item */
333			memcpy(hr, h1, l1);
334
335			/* advance pointer in result list */
336			hr += l1;
337			*hr = '\0';
338		}
339		if (h != NULL)
340		{
341			/* there are more items */
342			*h = ' ';
343			h1 = h + 1;
344		}
345	}
346	return res;
347}
348# endif /* SASL */
349/*
350**  HELO_OPTIONS -- process the options on a HELO line.
351**
352**	Parameters:
353**		line -- the response line.
354**		firstline -- set if this is the first line of the reply.
355**		m -- the mailer.
356**		mci -- the mailer connection info.
357**		e -- the envelope.
358**
359**	Returns:
360**		none.
361*/
362
363static void
364helo_options(line, firstline, m, mci, e)
365	char *line;
366	bool firstline;
367	MAILER *m;
368	register MCI *mci;
369	ENVELOPE *e;
370{
371	register char *p;
372
373	if (firstline)
374	{
375# if SASL
376		if (mci->mci_saslcap != NULL)
377			free(mci->mci_saslcap);
378		mci->mci_saslcap = NULL;
379# endif /* SASL */
380		return;
381	}
382
383	if (strlen(line) < (SIZE_T) 5)
384		return;
385	line += 4;
386	p = strpbrk(line, " =");
387	if (p != NULL)
388		*p++ = '\0';
389	if (strcasecmp(line, "size") == 0)
390	{
391		mci->mci_flags |= MCIF_SIZE;
392		if (p != NULL)
393			mci->mci_maxsize = atol(p);
394	}
395	else if (strcasecmp(line, "8bitmime") == 0)
396	{
397		mci->mci_flags |= MCIF_8BITMIME;
398		mci->mci_flags &= ~MCIF_7BIT;
399	}
400	else if (strcasecmp(line, "expn") == 0)
401		mci->mci_flags |= MCIF_EXPN;
402	else if (strcasecmp(line, "dsn") == 0)
403		mci->mci_flags |= MCIF_DSN;
404	else if (strcasecmp(line, "enhancedstatuscodes") == 0)
405		mci->mci_flags |= MCIF_ENHSTAT;
406# if STARTTLS
407	else if (strcasecmp(line, "starttls") == 0)
408		mci->mci_flags |= MCIF_TLS;
409# endif /* STARTTLS */
410# if SASL
411	else if (strcasecmp(line, "auth") == 0)
412	{
413		if (p != NULL && *p != '\0')
414		{
415			if (mci->mci_saslcap != NULL)
416			{
417				char *h;
418
419				/*
420				**  create the union with previous auth
421				**  offerings because we recognize "auth "
422				**  and "auth=" (old format).
423				*/
424				h = mci->mci_saslcap;
425				mci->mci_saslcap = str_union(h, p);
426				if (h != mci->mci_saslcap)
427					free(h);
428				mci->mci_flags |= MCIF_AUTH;
429			}
430			else
431			{
432				int l;
433
434				l = strlen(p) + 1;
435				mci->mci_saslcap = (char *)malloc(l);
436
437				/* XXX this may be leaked */
438				if (mci->mci_saslcap != NULL)
439				{
440					(void) strlcpy(mci->mci_saslcap, p, l);
441					mci->mci_flags |= MCIF_AUTH;
442				}
443			}
444		}
445	}
446# endif /* SASL */
447}
448# if SASL
449
450/*
451**  GETSASLDATA -- process the challenges from the SASL protocol
452**
453**	This gets the relevant sasl response data out of the reply
454**	from the server
455**
456**	Parameters:
457**		line -- the response line.
458**		firstline -- set if this is the first line of the reply.
459**		m -- the mailer.
460**		mci -- the mailer connection info.
461**		e -- the envelope.
462**
463**	Returns:
464**		none.
465*/
466
467void
468getsasldata(line, firstline, m, mci, e)
469	char *line;
470	bool firstline;
471	MAILER *m;
472	register MCI *mci;
473	ENVELOPE *e;
474{
475	int len;
476	char *out;
477	int result;
478
479	/* if not a continue we don't care about it */
480	if ((strlen(line) <= 4) ||
481	    (line[0] != '3') ||
482	    (line[1] != '3') ||
483	    (line[2] != '4'))
484	{
485		mci->mci_sasl_string = NULL;
486		return;
487	}
488
489	/* forget about "334 " */
490	line += 4;
491	len = strlen(line);
492
493	out = xalloc(len + 1);
494	result = sasl_decode64(line, len, out, (u_int *)&len);
495	if (result != SASL_OK)
496	{
497		len = 0;
498		*out = '\0';
499	}
500	if (mci->mci_sasl_string != NULL)
501	{
502		if (mci->mci_sasl_string_len <= len)
503		{
504			free(mci->mci_sasl_string);
505			mci->mci_sasl_string = xalloc(len + 1);
506		}
507	}
508	else
509		mci->mci_sasl_string = xalloc(len + 1);
510	/* XXX this is probably leaked */
511	memcpy(mci->mci_sasl_string, out, len);
512	mci->mci_sasl_string[len] = '\0';
513	mci->mci_sasl_string_len = len;
514	free(out);
515	return;
516}
517
518/*
519**  READAUTH -- read auth value from a file
520**
521**	Parameters:
522**		l -- line to define.
523**		filename -- name of file to read.
524**		safe -- if set, this is a safe read.
525**
526**	Returns:
527**		line from file
528**
529**	Side Effects:
530**		overwrites local static buffer. The caller should copy
531**		the result.
532**
533*/
534
535/* lines in authinfo file */
536# define SASL_USER	1
537# define SASL_AUTHID	2
538# define SASL_PASSWORD	3
539# define SASL_DEFREALM	4
540# define SASL_MECH	5
541
542static char *sasl_info_name[] =
543{
544	"",
545	"user id",
546	"authorization id",
547	"password",
548	"realm",
549	"mechanism"
550};
551
552static char *
553readauth(l, filename, safe)
554	int l;
555	char *filename;
556	bool safe;
557{
558	FILE *f;
559	long sff;
560	pid_t pid;
561	int lc;
562	static char buf[MAXLINE];
563
564	if (filename == NULL || filename[0] == '\0')
565		return "";
566#if !_FFR_ALLOW_SASLINFO
567	/*
568	**  make sure we don't use a program that is not
569	**  accesible to the user who specified a different authinfo file.
570	**  However, currently we don't pass this info (authinfo file
571	**  specified by user) around, so we just turn off program access.
572	*/
573	if (filename[0] == '|')
574	{
575		auto int fd;
576		int i;
577		char *p;
578		char *argv[MAXPV + 1];
579
580		i = 0;
581		for (p = strtok(&filename[1], " \t"); p != NULL;
582		     p = strtok(NULL, " \t"))
583		{
584			if (i >= MAXPV)
585				break;
586			argv[i++] = p;
587		}
588		argv[i] = NULL;
589		pid = prog_open(argv, &fd, CurEnv);
590		if (pid < 0)
591			f = NULL;
592		else
593			f = fdopen(fd, "r");
594	}
595	else
596#endif /* !_FFR_ALLOW_SASLINFO */
597	{
598		pid = -1;
599		sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
600		      | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
601		if (DontLockReadFiles)
602			sff |= SFF_NOLOCK;
603#if _FFR_ALLOW_SASLINFO
604		/*
605		**  XXX: make sure we don't read or open files that are not
606		**  accesible to the user who specified a different authinfo
607		**  file.
608		*/
609		sff |= SFF_MUSTOWN;
610#else /* _FFR_ALLOW_SASLINFO */
611		if (safe)
612			sff |= SFF_OPENASROOT;
613#endif /* _FFR_ALLOW_SASLINFO */
614
615		f = safefopen(filename, O_RDONLY, 0, sff);
616	}
617	if (f == NULL)
618	{
619		syserr("readauth: cannot open %s", filename);
620		return "";
621	}
622
623	lc = 0;
624	while (lc < l && fgets(buf, sizeof buf, f) != NULL)
625	{
626		if (buf[0] != '#')
627			lc++;
628	}
629
630	(void) fclose(f);
631	if (pid > 0)
632		(void) waitfor(pid);
633	if (lc < l)
634	{
635		if (LogLevel >= 9)
636			sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s",
637			  sasl_info_name[l], filename);
638		return "";
639	}
640	lc = strlen(buf) - 1;
641	if (lc >= 0)
642		buf[lc] = '\0';
643	if (tTd(95, 6))
644		dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf);
645	return buf;
646}
647
648#  ifndef __attribute__
649#   define __attribute__(x)
650#  endif /* ! __attribute__ */
651
652static int getsimple	__P((void *, int, const char **, unsigned *));
653static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
654static int saslgetrealm	__P((void *, int, const char **, const char **));
655
656static sasl_callback_t callbacks[] =
657{
658	{	SASL_CB_GETREALM,	&saslgetrealm,	NULL	},
659# define CB_GETREALM_IDX	0
660	{	SASL_CB_PASS,		&getsecret,	NULL	},
661# define CB_PASS_IDX	1
662	{	SASL_CB_USER,		&getsimple,	NULL	},
663# define CB_USER_IDX	2
664	{	SASL_CB_AUTHNAME,	&getsimple,	NULL	},
665# define CB_AUTHNAME_IDX	3
666	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
667	{	SASL_CB_LIST_END,	NULL,		NULL	}
668};
669
670/*
671**  GETSIMPLE -- callback to get userid or authid
672**
673**	Parameters:
674**		context -- unused
675**		id -- what to do
676**		result -- (pointer to) result
677**		len -- (pointer to) length of result
678**
679**	Returns:
680**		OK/failure values
681*/
682
683static int
684getsimple(context, id, result, len)
685	void *context __attribute__((unused));
686	int id;
687	const char **result;
688	unsigned *len;
689{
690	char *h;
691#  if SASL > 10509
692	int addrealm;
693	static int addedrealm = FALSE;
694#  endif /* SASL > 10509 */
695	static char *user = NULL;
696	static char *authid = NULL;
697
698	if (result == NULL)
699		return SASL_BADPARAM;
700
701	switch (id)
702	{
703	  case SASL_CB_USER:
704		if (user == NULL)
705		{
706			h = readauth(SASL_USER, SASLInfo, TRUE);
707			user = newstr(h);
708		}
709		*result = user;
710		if (tTd(95, 5))
711			dprintf("AUTH username '%s'\n", *result);
712		if (len != NULL)
713			*len = user ? strlen(user) : 0;
714		break;
715
716	  case SASL_CB_AUTHNAME:
717#  if SASL > 10509
718		/* XXX maybe other mechanisms too?! */
719		addrealm = context != NULL &&
720			   strcasecmp(context, "CRAM-MD5") == 0;
721		if (addedrealm != addrealm && authid != NULL)
722		{
723#  if SASL > 10522
724			/*
725			**  digest-md5 prior to 1.5.23 doesn't copy the
726			**  value it gets from the callback, but free()s
727			**  it later on
728			**  workaround: don't free() it here
729			**  this can cause a memory leak!
730			*/
731			free(authid);
732#  endif /* SASL > 10522 */
733			authid = NULL;
734			addedrealm = addrealm;
735		}
736#  endif /* SASL > 10509 */
737		if (authid == NULL)
738		{
739			h = readauth(SASL_AUTHID, SASLInfo, TRUE);
740#  if SASL > 10509
741			if (addrealm && strchr(h, '@') == NULL)
742			{
743				size_t l;
744				char *realm;
745
746				realm = callbacks[CB_GETREALM_IDX].context;
747				l = strlen(h) + strlen(realm) + 2;
748				authid = xalloc(l);
749				snprintf(authid, l, "%s@%s", h, realm);
750			}
751			else
752#  endif /* SASL > 10509 */
753				authid = newstr(h);
754		}
755		*result = authid;
756		if (tTd(95, 5))
757			dprintf("AUTH authid '%s'\n", *result);
758		if (len != NULL)
759			*len = authid ? strlen(authid) : 0;
760		break;
761
762	  case SASL_CB_LANGUAGE:
763		*result = NULL;
764		if (len != NULL)
765			*len = 0;
766		break;
767
768	  default:
769		return SASL_BADPARAM;
770	}
771	return SASL_OK;
772}
773
774/*
775**  GETSECRET -- callback to get password
776**
777**	Parameters:
778**		conn -- connection information
779**		context -- unused
780**		id -- what to do
781**		psecret -- (pointer to) result
782**
783**	Returns:
784**		OK/failure values
785*/
786
787static int
788getsecret(conn, context, id, psecret)
789	sasl_conn_t *conn;
790	void *context __attribute__((unused));
791	int id;
792	sasl_secret_t **psecret;
793{
794	char *h;
795	int len;
796	static char *authpass = NULL;
797
798	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
799		return SASL_BADPARAM;
800
801	if (authpass == NULL)
802	{
803		h = readauth(SASL_PASSWORD, SASLInfo, TRUE);
804		authpass = newstr(h);
805	}
806	len = strlen(authpass);
807	*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len + 1);
808	if (*psecret == NULL)
809		return SASL_FAIL;
810	(void) strlcpy((*psecret)->data, authpass, len + 1);
811	(*psecret)->len = len;
812	return SASL_OK;
813}
814
815/*
816**  SAFESASLFILE -- callback for sasl: is file safe?
817**
818**	Parameters:
819**		context -- pointer to context between invocations (unused)
820**		file -- name of file to check
821**		type -- type of file to check
822**
823**	Returns:
824**		SASL_OK: file can be used
825**		SASL_CONTINUE: don't use file
826**		SASL_FAIL: failure (not used here)
827**
828*/
829int
830# if SASL > 10515
831safesaslfile(context, file, type)
832# else /* SASL > 10515 */
833safesaslfile(context, file)
834# endif /* SASL > 10515 */
835	void *context;
836	char *file;
837# if SASL > 10515
838	int type;
839# endif /* SASL > 10515 */
840{
841	long sff;
842	int r;
843	char *p;
844
845	if (file == NULL || *file == '\0')
846		return SASL_OK;
847	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
848	if ((p = strrchr(file, '/')) == NULL)
849		p = file;
850	else
851		++p;
852
853# if SASL <= 10515
854	/* everything beside libs and .conf files must not be readable */
855	r = strlen(p);
856	if ((r <= 3 || strncmp(p, "lib", 3) != 0) &&
857	    (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0)
858#  if _FFR_UNSAFE_SASL
859	    && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)
860#  endif /* _FFR_UNSAFE_SASL */
861	   )
862		sff |= SFF_NORFILES;
863# else /* SASL > 10515 */
864	/* files containing passwords should be not readable */
865	if (type == SASL_VRFY_PASSWD)
866	{
867#  if _FFR_UNSAFE_SASL
868		if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
869			sff |= SFF_NOWRFILES;
870		else
871#  endif /* _FFR_UNSAFE_SASL */
872			sff |= SFF_NORFILES;
873	}
874# endif /* SASL <= 10515 */
875
876	if ((r = safefile(file, RunAsUid, RunAsGid, RunAsUserName, sff,
877			  S_IRUSR, NULL)) == 0)
878		return SASL_OK;
879	if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
880		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
881			  file, errstring(r));
882	return SASL_CONTINUE;
883}
884
885/*
886**  SASLGETREALM -- return the realm for SASL
887**
888**	return the realm for the client
889**
890**	Parameters:
891**		context -- context shared between invocations
892**			here: realm to return
893**		availrealms -- list of available realms
894**			{realm, realm, ...}
895**		result -- pointer to result
896**
897**	Returns:
898**		failure/success
899*/
900static int
901saslgetrealm(context, id, availrealms, result)
902	void *context;
903	int id;
904	const char **availrealms;
905	const char **result;
906{
907	if (LogLevel > 12)
908		sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
909			  context == NULL ? "<No Context>" : (char *) context,
910			  (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
911	if (context == NULL)
912		return SASL_FAIL;
913
914	/* check whether context is in list? */
915	if (availrealms != NULL && *availrealms != NULL)
916	{
917		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
918		    NULL)
919		{
920			if (LogLevel > 8)
921				sm_syslog(LOG_ERR, NOQID,
922					  "saslgetrealm: realm %s not in list %s",
923					  context, *availrealms);
924			return SASL_FAIL;
925		}
926	}
927	*result = (char *)context;
928	return SASL_OK;
929}
930/*
931**  ITEMINLIST -- does item appear in list?
932**
933**	Check whether item appears in list (which must be separated by a
934**	character in delim) as a "word", i.e. it must appear at the begin
935**	of the list or after a space, and it must end with a space or the
936**	end of the list.
937**
938**	Parameters:
939**		item -- item to search.
940**		list -- list of items.
941**		delim -- list of delimiters.
942**
943**	Returns:
944**		pointer to occurrence (NULL if not found).
945*/
946
947char *
948iteminlist(item, list, delim)
949	char *item;
950	char *list;
951	char *delim;
952{
953	char *s;
954	int len;
955
956	if (list == NULL || *list == '\0')
957		return NULL;
958	if (item == NULL || *item == '\0')
959		return NULL;
960	s = list;
961	len = strlen(item);
962	while (s != NULL && *s != '\0')
963	{
964		if (strncasecmp(s, item, len) == 0 &&
965		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
966			return s;
967		s = strpbrk(s, delim);
968		if (s != NULL)
969			while (*++s == ' ')
970				continue;
971	}
972	return NULL;
973}
974/*
975**  REMOVEMECH -- remove item [rem] from list [list]
976**
977**	Parameters:
978**		rem -- item to remove
979**		list -- list of items
980**
981**	Returns:
982**		pointer to new list (NULL in case of error).
983*/
984
985char *
986removemech(rem, list)
987	char *rem;
988	char *list;
989{
990	char *ret;
991	char *needle;
992	int len;
993
994	if (list == NULL)
995		return NULL;
996	if (rem == NULL || *rem == '\0')
997	{
998		/* take out what? */
999		return NULL;
1000	}
1001
1002	/* find the item in the list */
1003	if ((needle = iteminlist(rem, list, " ")) == NULL)
1004	{
1005		/* not in there: return original */
1006		return list;
1007	}
1008
1009	/* length of string without rem */
1010	len = strlen(list) - strlen(rem);
1011	if (len == 0)
1012	{
1013		ret = xalloc(1);  /* XXX leaked */
1014		*ret = '\0';
1015		return ret;
1016	}
1017	ret = xalloc(len);  /* XXX leaked */
1018	memset(ret, '\0', len);
1019
1020	/* copy from start to removed item */
1021	memcpy(ret, list, needle - list);
1022
1023	/* length of rest of string past removed item */
1024	len = strlen(needle) - strlen(rem) - 1;
1025	if (len > 0)
1026	{
1027		/* not last item -- copy into string */
1028		memcpy(ret + (needle - list),
1029		       list + (needle - list) + strlen(rem) + 1,
1030		       len);
1031	}
1032	else
1033		ret[(needle - list) - 1] = '\0';
1034	return ret;
1035}
1036/*
1037**  INTERSECT -- create the intersection between two lists
1038**
1039**	Parameters:
1040**		s1, s2 -- lists of items (separated by single blanks).
1041**
1042**	Returns:
1043**		the intersection of both lists.
1044*/
1045
1046char *
1047intersect(s1, s2)
1048	char *s1, *s2;
1049{
1050	char *hr, *h1, *h, *res;
1051	int l1, l2, rl;
1052
1053	if (s1 == NULL || s2 == NULL)	/* NULL string(s) -> NULL result */
1054		return NULL;
1055	l1 = strlen(s1);
1056	l2 = strlen(s2);
1057	rl = min(l1, l2);
1058	res = (char *)malloc(rl + 1);
1059	if (res == NULL)
1060		return NULL;
1061	*res = '\0';
1062	if (rl == 0)	/* at least one string empty? */
1063		return res;
1064	hr = res;
1065	h1 = s1;
1066	h = s1;
1067
1068	/* walk through s1 */
1069	while (h != NULL && *h1 != '\0')
1070	{
1071		/* is there something after the current word? */
1072		if ((h = strchr(h1, ' ')) != NULL)
1073			*h = '\0';
1074		l1 = strlen(h1);
1075
1076		/* does the current word appear in s2 ? */
1077		if (iteminlist(h1, s2, " ") != NULL)
1078		{
1079			/* add a blank if not first item */
1080			if (hr != res)
1081				*hr++ = ' ';
1082
1083			/* copy the item */
1084			memcpy(hr, h1, l1);
1085
1086			/* advance pointer in result list */
1087			hr += l1;
1088			*hr = '\0';
1089		}
1090		if (h != NULL)
1091		{
1092			/* there are more items */
1093			*h = ' ';
1094			h1 = h + 1;
1095		}
1096	}
1097	return res;
1098}
1099/*
1100**  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1101**
1102**	Parameters:
1103**		m -- the mailer.
1104**		mci -- the mailer connection structure.
1105**		e -- the envelope (including the sender to specify).
1106**		mechused - filled in with mechanism used
1107**
1108**	Returns:
1109**		EX_OK/EX_TEMPFAIL
1110*/
1111
1112int
1113attemptauth(m, mci, e, mechused)
1114	MAILER *m;
1115	MCI *mci;
1116	ENVELOPE *e;
1117	char **mechused;
1118{
1119	int saslresult, smtpresult;
1120	sasl_external_properties_t ssf;
1121	sasl_interact_t *client_interact = NULL;
1122	char *out;
1123	unsigned int outlen;
1124	static char *mechusing;
1125	sasl_security_properties_t ssp;
1126	char in64[MAXOUTLEN];
1127# if NETINET
1128	extern SOCKADDR CurHostAddr;
1129# endif /* NETINET */
1130
1131	*mechused = NULL;
1132	if (mci->mci_conn != NULL)
1133	{
1134		sasl_dispose(&(mci->mci_conn));
1135
1136		/* just in case, sasl_dispose() should take care of it */
1137		mci->mci_conn = NULL;
1138	}
1139
1140	/* make a new client sasl connection */
1141	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1142								 : "smtp",
1143				     CurHostName, NULL, 0, &mci->mci_conn);
1144
1145	/* set properties */
1146	(void) memset(&ssp, '\0', sizeof ssp);
1147#  if SFIO
1148	/* XXX should these be options settable via .cf ? */
1149	/* ssp.min_ssf = 0; is default due to memset() */
1150	{
1151		ssp.max_ssf = INT_MAX;
1152		ssp.maxbufsize = MAXOUTLEN;
1153#   if 0
1154		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1155#   endif /* 0 */
1156	}
1157#  endif /* SFIO */
1158	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1159	if (saslresult != SASL_OK)
1160		return EX_TEMPFAIL;
1161
1162	/* external security strength factor, authentication id */
1163	ssf.ssf = 0;
1164	ssf.auth_id = NULL;
1165# if _FFR_EXT_MECH
1166	out = macvalue(macid("{cert_subject}", NULL), e);
1167	if (out != NULL && *out != '\0')
1168		ssf.auth_id = out;
1169	out = macvalue(macid("{cipher_bits}", NULL), e);
1170	if (out != NULL && *out != '\0')
1171		ssf.ssf = atoi(out);
1172# endif /* _FFR_EXT_MECH */
1173	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1174	if (saslresult != SASL_OK)
1175		return EX_TEMPFAIL;
1176
1177# if NETINET
1178	/* set local/remote ipv4 addresses */
1179	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1180	{
1181		SOCKADDR_LEN_T addrsize;
1182		struct sockaddr_in saddr_l;
1183
1184		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1185				 (struct sockaddr_in *) &CurHostAddr)
1186		    != SASL_OK)
1187			return EX_TEMPFAIL;
1188		addrsize = sizeof(struct sockaddr_in);
1189		if (getsockname(fileno(mci->mci_out),
1190				(struct sockaddr *) &saddr_l, &addrsize) == 0)
1191		{
1192			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1193					 &saddr_l) != SASL_OK)
1194				return EX_TEMPFAIL;
1195		}
1196	}
1197# endif /* NETINET */
1198
1199	/* start client side of sasl */
1200	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1201				       NULL, &client_interact,
1202				       &out, &outlen,
1203				       (const char **)&mechusing);
1204	callbacks[CB_AUTHNAME_IDX].context = mechusing;
1205
1206	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1207	{
1208#  if SFIO
1209		if (saslresult == SASL_NOMECH && LogLevel > 8)
1210		{
1211			sm_syslog(LOG_NOTICE, e->e_id,
1212				  "available AUTH mechanisms do not fulfill requirements");
1213		}
1214#  endif /* SFIO */
1215		return EX_TEMPFAIL;
1216	}
1217
1218	*mechused = mechusing;
1219
1220	/* send the info across the wire */
1221	if (outlen > 0)
1222	{
1223		saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1224		if (saslresult != SASL_OK) /* internal error */
1225		{
1226			if (LogLevel > 8)
1227				sm_syslog(LOG_ERR, e->e_id,
1228					"encode64 for AUTH failed");
1229			return EX_TEMPFAIL;
1230		}
1231		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1232	}
1233	else
1234	{
1235		smtpmessage("AUTH %s", m, mci, mechusing);
1236	}
1237
1238	/* get the reply */
1239	smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL);
1240	/* which timeout? XXX */
1241
1242	for (;;)
1243	{
1244		/* check return code from server */
1245		if (smtpresult == 235)
1246		{
1247			define(macid("{auth_type}", NULL),
1248			       newstr(mechusing), e);
1249#  if !SFIO
1250			if (LogLevel > 9)
1251				sm_syslog(LOG_INFO, NOQID,
1252					  "SASL: outgoing connection to %.64s: mech=%.16s",
1253					  mci->mci_host, mechusing);
1254#  endif /* !SFIO */
1255			return EX_OK;
1256		}
1257		if (smtpresult == -1)
1258			return EX_IOERR;
1259		if (smtpresult != 334)
1260			return EX_TEMPFAIL;
1261
1262		saslresult = sasl_client_step(mci->mci_conn,
1263					      mci->mci_sasl_string,
1264					      mci->mci_sasl_string_len,
1265					      &client_interact,
1266					      &out, &outlen);
1267
1268		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1269		{
1270			if (tTd(95, 5))
1271				dprintf("AUTH FAIL: %s (%d)\n",
1272					sasl_errstring(saslresult, NULL, NULL),
1273					saslresult);
1274
1275			/* fail deliberately, see RFC 2254 4. */
1276			smtpmessage("*", m, mci);
1277
1278			/*
1279			**  but we should only fail for this authentication
1280			**  mechanism; how to do that?
1281			*/
1282
1283			smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1284					   getsasldata, NULL);
1285			return EX_TEMPFAIL;
1286		}
1287
1288		if (outlen > 0)
1289		{
1290			saslresult = sasl_encode64(out, outlen, in64,
1291						   MAXOUTLEN, NULL);
1292			if (saslresult != SASL_OK)
1293			{
1294				/* give an error reply to the other side! */
1295				smtpmessage("*", m, mci);
1296				return EX_TEMPFAIL;
1297			}
1298		}
1299		else
1300			in64[0] = '\0';
1301		smtpmessage("%s", m, mci, in64);
1302		smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1303				   getsasldata, NULL);
1304		/* which timeout? XXX */
1305	}
1306	/* NOTREACHED */
1307}
1308
1309/*
1310**  SMTPAUTH -- try to AUTHenticate
1311**
1312**	This will try mechanisms in the order the sasl library decided until:
1313**	- there are no more mechanisms
1314**	- a mechanism succeeds
1315**	- the sasl library fails initializing
1316**
1317**	Parameters:
1318**		m -- the mailer.
1319**		mci -- the mailer connection info.
1320**		e -- the envelope.
1321**
1322**	Returns:
1323**		EX_OK/EX_TEMPFAIL
1324*/
1325
1326int
1327smtpauth(m, mci, e)
1328	MAILER *m;
1329	MCI *mci;
1330	ENVELOPE *e;
1331{
1332	int result;
1333	char *mechused;
1334	char *h;
1335	static char *defrealm = NULL;
1336	static char *mechs = NULL;
1337
1338	mci->mci_sasl_auth = FALSE;
1339	if (defrealm == NULL)
1340	{
1341		h = readauth(SASL_DEFREALM, SASLInfo, TRUE);
1342		if (h != NULL && *h != '\0')
1343			defrealm = newstr(h);
1344	}
1345	if (defrealm == NULL || *defrealm == '\0')
1346		defrealm = newstr(macvalue('j', CurEnv));
1347	callbacks[CB_GETREALM_IDX].context = defrealm;
1348
1349# if _FFR_DEFAUTHINFO_MECHS
1350	if (mechs == NULL)
1351	{
1352		h = readauth(SASL_MECH, SASLInfo, TRUE);
1353		if (h != NULL && *h != '\0')
1354			mechs = newstr(h);
1355	}
1356# endif /* _FFR_DEFAUTHINFO_MECHS */
1357	if (mechs == NULL || *mechs == '\0')
1358		mechs = AuthMechanisms;
1359	mci->mci_saslcap = intersect(mechs, mci->mci_saslcap);
1360
1361	/* initialize sasl client library */
1362	result = sasl_client_init(callbacks);
1363	if (result != SASL_OK)
1364		return EX_TEMPFAIL;
1365	do
1366	{
1367		result = attemptauth(m, mci, e, &mechused);
1368		if (result == EX_OK)
1369			mci->mci_sasl_auth = TRUE;
1370		else if (result == EX_TEMPFAIL)
1371		{
1372			mci->mci_saslcap = removemech(mechused,
1373						      mci->mci_saslcap);
1374			if (mci->mci_saslcap == NULL ||
1375			    *(mci->mci_saslcap) == '\0')
1376				return EX_TEMPFAIL;
1377		}
1378		else	/* all others for now */
1379			return EX_TEMPFAIL;
1380	} while (result != EX_OK);
1381	return result;
1382}
1383# endif /* SASL */
1384
1385/*
1386**  SMTPMAILFROM -- send MAIL command
1387**
1388**	Parameters:
1389**		m -- the mailer.
1390**		mci -- the mailer connection structure.
1391**		e -- the envelope (including the sender to specify).
1392*/
1393
1394int
1395smtpmailfrom(m, mci, e)
1396	MAILER *m;
1397	MCI *mci;
1398	ENVELOPE *e;
1399{
1400	int r;
1401	char *bufp;
1402	char *bodytype;
1403	char buf[MAXNAME + 1];
1404	char optbuf[MAXLINE];
1405	char *enhsc;
1406
1407	if (tTd(18, 2))
1408		dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
1409	enhsc = NULL;
1410
1411	/* set up appropriate options to include */
1412	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
1413	{
1414		snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
1415		bufp = &optbuf[strlen(optbuf)];
1416	}
1417	else
1418	{
1419		optbuf[0] = '\0';
1420		bufp = optbuf;
1421	}
1422
1423	bodytype = e->e_bodytype;
1424	if (bitset(MCIF_8BITMIME, mci->mci_flags))
1425	{
1426		if (bodytype == NULL &&
1427		    bitset(MM_MIME8BIT, MimeMode) &&
1428		    bitset(EF_HAS8BIT, e->e_flags) &&
1429		    !bitset(EF_DONT_MIME, e->e_flags) &&
1430		    !bitnset(M_8BITS, m->m_flags))
1431			bodytype = "8BITMIME";
1432		if (bodytype != NULL &&
1433		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
1434		{
1435			snprintf(bufp, SPACELEFT(optbuf, bufp),
1436				 " BODY=%s", bodytype);
1437			bufp += strlen(bufp);
1438		}
1439	}
1440	else if (bitnset(M_8BITS, m->m_flags) ||
1441		 !bitset(EF_HAS8BIT, e->e_flags) ||
1442		 bitset(MCIF_8BITOK, mci->mci_flags))
1443	{
1444		/* EMPTY */
1445		/* just pass it through */
1446	}
1447# if MIME8TO7
1448	else if (bitset(MM_CVTMIME, MimeMode) &&
1449		 !bitset(EF_DONT_MIME, e->e_flags) &&
1450		 (!bitset(MM_PASS8BIT, MimeMode) ||
1451		  bitset(EF_IS_MIME, e->e_flags)))
1452	{
1453		/* must convert from 8bit MIME format to 7bit encoded */
1454		mci->mci_flags |= MCIF_CVT8TO7;
1455	}
1456# endif /* MIME8TO7 */
1457	else if (!bitset(MM_PASS8BIT, MimeMode))
1458	{
1459		/* cannot just send a 8-bit version */
1460		extern char MsgBuf[];
1461
1462		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
1463		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
1464		return EX_DATAERR;
1465	}
1466
1467	if (bitset(MCIF_DSN, mci->mci_flags))
1468	{
1469		if (e->e_envid != NULL &&
1470		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
1471		{
1472			snprintf(bufp, SPACELEFT(optbuf, bufp),
1473				 " ENVID=%s", e->e_envid);
1474			bufp += strlen(bufp);
1475		}
1476
1477		/* RET= parameter */
1478		if (bitset(EF_RET_PARAM, e->e_flags) &&
1479		    SPACELEFT(optbuf, bufp) > 9)
1480		{
1481			snprintf(bufp, SPACELEFT(optbuf, bufp),
1482				 " RET=%s",
1483				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
1484					"HDRS" : "FULL");
1485			bufp += strlen(bufp);
1486		}
1487	}
1488
1489	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
1490	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
1491# if SASL
1492	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
1493# endif /* SASL */
1494	    )
1495	{
1496		snprintf(bufp, SPACELEFT(optbuf, bufp),
1497			 " AUTH=%s", e->e_auth_param);
1498		bufp += strlen(bufp);
1499	}
1500
1501	/*
1502	**  Send the MAIL command.
1503	**	Designates the sender.
1504	*/
1505
1506	mci->mci_state = MCIS_ACTIVE;
1507
1508	if (bitset(EF_RESPONSE, e->e_flags) &&
1509	    !bitnset(M_NO_NULL_FROM, m->m_flags))
1510		buf[0] = '\0';
1511	else
1512		expand("\201g", buf, sizeof buf, e);
1513	if (buf[0] == '<')
1514	{
1515		/* strip off <angle brackets> (put back on below) */
1516		bufp = &buf[strlen(buf) - 1];
1517		if (*bufp == '>')
1518			*bufp = '\0';
1519		bufp = &buf[1];
1520	}
1521	else
1522		bufp = buf;
1523	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
1524	    !bitnset(M_FROMPATH, m->m_flags))
1525	{
1526		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
1527	}
1528	else
1529	{
1530		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
1531			*bufp == '@' ? ',' : ':', bufp, optbuf);
1532	}
1533	SmtpPhase = mci->mci_phase = "client MAIL";
1534	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1535			CurHostName, mci->mci_phase);
1536	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc);
1537	if (r < 0)
1538	{
1539		/* communications failure */
1540		mci->mci_errno = errno;
1541		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1542		smtpquit(m, mci, e);
1543		return EX_TEMPFAIL;
1544	}
1545	else if (r == SMTPCLOSING)
1546	{
1547		/* service shutting down */
1548		mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
1549			    SmtpReplyBuffer);
1550		smtpquit(m, mci, e);
1551		return EX_TEMPFAIL;
1552	}
1553	else if (REPLYTYPE(r) == 4)
1554	{
1555		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
1556			    SmtpReplyBuffer);
1557		return EX_TEMPFAIL;
1558	}
1559	else if (REPLYTYPE(r) == 2)
1560	{
1561		return EX_OK;
1562	}
1563	else if (r == 501)
1564	{
1565		/* syntax error in arguments */
1566		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
1567			    SmtpReplyBuffer);
1568		return EX_DATAERR;
1569	}
1570	else if (r == 553)
1571	{
1572		/* mailbox name not allowed */
1573		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
1574			    SmtpReplyBuffer);
1575		return EX_DATAERR;
1576	}
1577	else if (r == 552)
1578	{
1579		/* exceeded storage allocation */
1580		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
1581			    SmtpReplyBuffer);
1582		if (bitset(MCIF_SIZE, mci->mci_flags))
1583			e->e_flags |= EF_NO_BODY_RETN;
1584		return EX_UNAVAILABLE;
1585	}
1586	else if (REPLYTYPE(r) == 5)
1587	{
1588		/* unknown error */
1589		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
1590			    SmtpReplyBuffer);
1591		return EX_UNAVAILABLE;
1592	}
1593
1594	if (LogLevel > 1)
1595	{
1596		sm_syslog(LOG_CRIT, e->e_id,
1597			  "%.100s: SMTP MAIL protocol error: %s",
1598			  CurHostName,
1599			  shortenstring(SmtpReplyBuffer, 403));
1600	}
1601
1602	/* protocol error -- close up */
1603	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1604		    SmtpReplyBuffer);
1605	smtpquit(m, mci, e);
1606	return EX_PROTOCOL;
1607}
1608/*
1609**  SMTPRCPT -- designate recipient.
1610**
1611**	Parameters:
1612**		to -- address of recipient.
1613**		m -- the mailer we are sending to.
1614**		mci -- the connection info for this transaction.
1615**		e -- the envelope for this transaction.
1616**
1617**	Returns:
1618**		exit status corresponding to recipient status.
1619**
1620**	Side Effects:
1621**		Sends the mail via SMTP.
1622*/
1623
1624int
1625smtprcpt(to, m, mci, e)
1626	ADDRESS *to;
1627	register MAILER *m;
1628	MCI *mci;
1629	ENVELOPE *e;
1630{
1631	register int r;
1632	char *bufp;
1633	char optbuf[MAXLINE];
1634	char *enhsc;
1635
1636	enhsc = NULL;
1637	optbuf[0] = '\0';
1638	bufp = optbuf;
1639
1640	/*
1641	**  warning: in the following it is assumed that the free space
1642	**  in bufp is sizeof optbuf
1643	*/
1644	if (bitset(MCIF_DSN, mci->mci_flags))
1645	{
1646		/* NOTIFY= parameter */
1647		if (bitset(QHASNOTIFY, to->q_flags) &&
1648		    bitset(QPRIMARY, to->q_flags) &&
1649		    !bitnset(M_LOCALMAILER, m->m_flags))
1650		{
1651			bool firstone = TRUE;
1652
1653			(void) strlcat(bufp, " NOTIFY=", sizeof optbuf);
1654			if (bitset(QPINGONSUCCESS, to->q_flags))
1655			{
1656				(void) strlcat(bufp, "SUCCESS", sizeof optbuf);
1657				firstone = FALSE;
1658			}
1659			if (bitset(QPINGONFAILURE, to->q_flags))
1660			{
1661				if (!firstone)
1662					(void) strlcat(bufp, ",",
1663						       sizeof optbuf);
1664				(void) strlcat(bufp, "FAILURE", sizeof optbuf);
1665				firstone = FALSE;
1666			}
1667			if (bitset(QPINGONDELAY, to->q_flags))
1668			{
1669				if (!firstone)
1670					(void) strlcat(bufp, ",",
1671						       sizeof optbuf);
1672				(void) strlcat(bufp, "DELAY", sizeof optbuf);
1673				firstone = FALSE;
1674			}
1675			if (firstone)
1676				(void) strlcat(bufp, "NEVER", sizeof optbuf);
1677			bufp += strlen(bufp);
1678		}
1679
1680		/* ORCPT= parameter */
1681		if (to->q_orcpt != NULL &&
1682		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
1683		{
1684			snprintf(bufp, SPACELEFT(optbuf, bufp),
1685				 " ORCPT=%s", to->q_orcpt);
1686			bufp += strlen(bufp);
1687		}
1688	}
1689
1690	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
1691
1692	SmtpPhase = mci->mci_phase = "client RCPT";
1693	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1694			CurHostName, mci->mci_phase);
1695	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
1696	to->q_rstatus = newstr(SmtpReplyBuffer);
1697	to->q_status = ENHSCN(enhsc, smtptodsn(r));
1698	if (!bitnset(M_LMTP, m->m_flags))
1699		to->q_statmta = mci->mci_host;
1700	if (r < 0 || REPLYTYPE(r) == 4)
1701		return EX_TEMPFAIL;
1702	else if (REPLYTYPE(r) == 2)
1703		return EX_OK;
1704	else if (r == 550)
1705	{
1706		to->q_status = ENHSCN(enhsc, "5.1.1");
1707		return EX_NOUSER;
1708	}
1709	else if (r == 551)
1710	{
1711		to->q_status = ENHSCN(enhsc, "5.1.6");
1712		return EX_NOUSER;
1713	}
1714	else if (r == 553)
1715	{
1716		to->q_status = ENHSCN(enhsc, "5.1.3");
1717		return EX_NOUSER;
1718	}
1719	else if (REPLYTYPE(r) == 5)
1720	{
1721		return EX_UNAVAILABLE;
1722	}
1723
1724	if (LogLevel > 1)
1725	{
1726		sm_syslog(LOG_CRIT, e->e_id,
1727			  "%.100s: SMTP RCPT protocol error: %s",
1728			  CurHostName,
1729			  shortenstring(SmtpReplyBuffer, 403));
1730	}
1731
1732	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1733		    SmtpReplyBuffer);
1734	return EX_PROTOCOL;
1735}
1736/*
1737**  SMTPDATA -- send the data and clean up the transaction.
1738**
1739**	Parameters:
1740**		m -- mailer being sent to.
1741**		mci -- the mailer connection information.
1742**		e -- the envelope for this message.
1743**
1744**	Returns:
1745**		exit status corresponding to DATA command.
1746**
1747**	Side Effects:
1748**		none.
1749*/
1750
1751static jmp_buf	CtxDataTimeout;
1752
1753int
1754smtpdata(m, mci, e)
1755	MAILER *m;
1756	register MCI *mci;
1757	register ENVELOPE *e;
1758{
1759	register int r;
1760	register EVENT *ev;
1761	int rstat;
1762	int xstat;
1763	time_t timeout;
1764	char *enhsc;
1765
1766	enhsc = NULL;
1767	/*
1768	**  Send the data.
1769	**	First send the command and check that it is ok.
1770	**	Then send the data.
1771	**	Follow it up with a dot to terminate.
1772	**	Finally get the results of the transaction.
1773	*/
1774
1775	/* send the command and check ok to proceed */
1776	smtpmessage("DATA", m, mci);
1777	SmtpPhase = mci->mci_phase = "client DATA 354";
1778	sm_setproctitle(TRUE, e, "%s %s: %s",
1779			qid_printname(e), CurHostName, mci->mci_phase);
1780	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc);
1781	if (r < 0 || REPLYTYPE(r) == 4)
1782	{
1783		smtpquit(m, mci, e);
1784		return EX_TEMPFAIL;
1785	}
1786	else if (REPLYTYPE(r) == 5)
1787	{
1788		smtprset(m, mci, e);
1789		return EX_UNAVAILABLE;
1790	}
1791	else if (REPLYTYPE(r) != 3)
1792	{
1793		if (LogLevel > 1)
1794		{
1795			sm_syslog(LOG_CRIT, e->e_id,
1796				  "%.100s: SMTP DATA-1 protocol error: %s",
1797				  CurHostName,
1798				  shortenstring(SmtpReplyBuffer, 403));
1799		}
1800		smtprset(m, mci, e);
1801		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1802			    SmtpReplyBuffer);
1803		return EX_PROTOCOL;
1804	}
1805
1806	/*
1807	**  Set timeout around data writes.  Make it at least large
1808	**  enough for DNS timeouts on all recipients plus some fudge
1809	**  factor.  The main thing is that it should not be infinite.
1810	*/
1811
1812	if (setjmp(CtxDataTimeout) != 0)
1813	{
1814		mci->mci_errno = errno;
1815		mci->mci_state = MCIS_ERROR;
1816		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1817
1818		/*
1819		**  If putbody() couldn't finish due to a timeout,
1820		**  rewind it here in the timeout handler.  See
1821		**  comments at the end of putbody() for reasoning.
1822		*/
1823
1824		if (e->e_dfp != NULL)
1825			(void) bfrewind(e->e_dfp);
1826
1827		errno = mci->mci_errno;
1828		syserr("451 4.4.1 timeout writing message to %s", CurHostName);
1829		smtpquit(m, mci, e);
1830		return EX_TEMPFAIL;
1831	}
1832
1833	if (tTd(18, 101))
1834	{
1835		/* simulate a DATA timeout */
1836		timeout = 1;
1837	}
1838	else
1839		timeout = DATA_PROGRESS_TIMEOUT;
1840
1841	ev = setevent(timeout, datatimeout, 0);
1842
1843
1844	if (tTd(18, 101))
1845	{
1846		/* simulate a DATA timeout */
1847		(void) sleep(1);
1848	}
1849
1850	/*
1851	**  Output the actual message.
1852	*/
1853
1854	(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
1855	(*e->e_putbody)(mci, e, NULL);
1856
1857	/*
1858	**  Cleanup after sending message.
1859	*/
1860
1861	clrevent(ev);
1862
1863# if _FFR_CATCH_BROKEN_MTAS
1864	{
1865		fd_set readfds;
1866		struct timeval timeout;
1867
1868		FD_ZERO(&readfds);
1869		FD_SET(fileno(mci->mci_in), &readfds);
1870		timeout.tv_sec = 0;
1871		timeout.tv_usec = 0;
1872		if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds,
1873			   NULL, NULL, &timeout) > 0 &&
1874		    FD_ISSET(fileno(mci->mci_in), &readfds))
1875		{
1876			/* terminate the message */
1877			fprintf(mci->mci_out, ".%s", m->m_eol);
1878			if (TrafficLogFile != NULL)
1879				fprintf(TrafficLogFile, "%05d >>> .\n",
1880					(int) getpid());
1881			if (Verbose)
1882				nmessage(">>> .");
1883
1884			mci->mci_errno = EIO;
1885			mci->mci_state = MCIS_ERROR;
1886			mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
1887			smtpquit(m, mci, e);
1888			return EX_PROTOCOL;
1889		}
1890	}
1891# endif /* _FFR_CATCH_BROKEN_MTAS */
1892
1893	if (ferror(mci->mci_out))
1894	{
1895		/* error during processing -- don't send the dot */
1896		mci->mci_errno = EIO;
1897		mci->mci_state = MCIS_ERROR;
1898		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
1899		smtpquit(m, mci, e);
1900		return EX_IOERR;
1901	}
1902
1903	/* terminate the message */
1904	fprintf(mci->mci_out, ".%s", m->m_eol);
1905	if (TrafficLogFile != NULL)
1906		fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
1907	if (Verbose)
1908		nmessage(">>> .");
1909
1910	/* check for the results of the transaction */
1911	SmtpPhase = mci->mci_phase = "client DATA status";
1912	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1913			CurHostName, mci->mci_phase);
1914	if (bitnset(M_LMTP, m->m_flags))
1915		return EX_OK;
1916	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
1917	if (r < 0)
1918	{
1919		smtpquit(m, mci, e);
1920		return EX_TEMPFAIL;
1921	}
1922	mci->mci_state = MCIS_OPEN;
1923	xstat = EX_NOTSTICKY;
1924	if (r == 452)
1925		rstat = EX_TEMPFAIL;
1926	else if (REPLYTYPE(r) == 4)
1927		rstat = xstat = EX_TEMPFAIL;
1928	else if (REPLYCLASS(r) != 5)
1929		rstat = xstat = EX_PROTOCOL;
1930	else if (REPLYTYPE(r) == 2)
1931		rstat = xstat = EX_OK;
1932	else if (REPLYTYPE(r) == 5)
1933		rstat = EX_UNAVAILABLE;
1934	else
1935		rstat = EX_PROTOCOL;
1936	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
1937		    SmtpReplyBuffer);
1938	if (e->e_statmsg != NULL)
1939		free(e->e_statmsg);
1940	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
1941	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
1942		r += 5;
1943	else
1944		r = 4;
1945	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
1946	if (rstat != EX_PROTOCOL)
1947		return rstat;
1948	if (LogLevel > 1)
1949	{
1950		sm_syslog(LOG_CRIT, e->e_id,
1951			  "%.100s: SMTP DATA-2 protocol error: %s",
1952			  CurHostName,
1953			  shortenstring(SmtpReplyBuffer, 403));
1954	}
1955	return rstat;
1956}
1957
1958
1959static void
1960datatimeout()
1961{
1962	if (DataProgress)
1963	{
1964		time_t timeout;
1965		register EVENT *ev;
1966
1967		/* check back again later */
1968		if (tTd(18, 101))
1969		{
1970			/* simulate a DATA timeout */
1971			timeout = 1;
1972		}
1973		else
1974			timeout = DATA_PROGRESS_TIMEOUT;
1975
1976		DataProgress = FALSE;
1977		ev = setevent(timeout, datatimeout, 0);
1978	}
1979	else
1980	{
1981		/* no progress, give up */
1982		longjmp(CtxDataTimeout, 1);
1983	}
1984}
1985/*
1986**  SMTPGETSTAT -- get status code from DATA in LMTP
1987**
1988**	Parameters:
1989**		m -- the mailer to which we are sending the message.
1990**		mci -- the mailer connection structure.
1991**		e -- the current envelope.
1992**
1993**	Returns:
1994**		The exit status corresponding to the reply code.
1995*/
1996
1997int
1998smtpgetstat(m, mci, e)
1999	MAILER *m;
2000	MCI *mci;
2001	ENVELOPE *e;
2002{
2003	int r;
2004	int status;
2005	char *enhsc;
2006
2007	enhsc = NULL;
2008	/* check for the results of the transaction */
2009	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
2010	if (r < 0)
2011	{
2012		smtpquit(m, mci, e);
2013		return EX_TEMPFAIL;
2014	}
2015	if (REPLYTYPE(r) == 4)
2016		status = EX_TEMPFAIL;
2017	else if (REPLYCLASS(r) != 5)
2018		status = EX_PROTOCOL;
2019	else if (REPLYTYPE(r) == 2)
2020		status = EX_OK;
2021	else if (REPLYTYPE(r) == 5)
2022		status = EX_UNAVAILABLE;
2023	else
2024		status = EX_PROTOCOL;
2025	if (e->e_statmsg != NULL)
2026		free(e->e_statmsg);
2027	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2028	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2029		r += 5;
2030	else
2031		r = 4;
2032	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
2033	mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)),
2034		    SmtpReplyBuffer);
2035	if (LogLevel > 1 && status == EX_PROTOCOL)
2036	{
2037		sm_syslog(LOG_CRIT, e->e_id,
2038			  "%.100s: SMTP DATA-3 protocol error: %s",
2039			  CurHostName,
2040			  shortenstring(SmtpReplyBuffer, 403));
2041	}
2042	return status;
2043}
2044/*
2045**  SMTPQUIT -- close the SMTP connection.
2046**
2047**	Parameters:
2048**		m -- a pointer to the mailer.
2049**		mci -- the mailer connection information.
2050**		e -- the current envelope.
2051**
2052**	Returns:
2053**		none.
2054**
2055**	Side Effects:
2056**		sends the final protocol and closes the connection.
2057*/
2058
2059void
2060smtpquit(m, mci, e)
2061	register MAILER *m;
2062	register MCI *mci;
2063	ENVELOPE *e;
2064{
2065	bool oldSuprErrs = SuprErrs;
2066	int rcode;
2067
2068	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2069	if (CurHostName == NULL)
2070		CurHostName = MyHostName;
2071
2072	/*
2073	**	Suppress errors here -- we may be processing a different
2074	**	job when we do the quit connection, and we don't want the
2075	**	new job to be penalized for something that isn't it's
2076	**	problem.
2077	*/
2078
2079	SuprErrs = TRUE;
2080
2081	/* send the quit message if we haven't gotten I/O error */
2082	if (mci->mci_state != MCIS_ERROR &&
2083	    mci->mci_state != MCIS_QUITING)
2084	{
2085		int origstate = mci->mci_state;
2086
2087		SmtpPhase = "client QUIT";
2088		mci->mci_state = MCIS_QUITING;
2089		smtpmessage("QUIT", m, mci);
2090		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL);
2091		SuprErrs = oldSuprErrs;
2092		if (mci->mci_state == MCIS_CLOSED ||
2093		    origstate == MCIS_CLOSED)
2094			return;
2095	}
2096
2097	/* now actually close the connection and pick up the zombie */
2098	rcode = endmailer(mci, e, NULL);
2099	if (rcode != EX_OK)
2100	{
2101		char *mailer = NULL;
2102
2103		if (mci->mci_mailer != NULL &&
2104		    mci->mci_mailer->m_name != NULL)
2105			mailer = mci->mci_mailer->m_name;
2106
2107		/* look for naughty mailers */
2108		sm_syslog(LOG_ERR, e->e_id,
2109			  "smtpquit: mailer%s%s exited with exit value %d",
2110			  mailer == NULL ? "" : " ",
2111			  mailer == NULL ? "" : mailer,
2112			  rcode);
2113	}
2114
2115	SuprErrs = oldSuprErrs;
2116}
2117/*
2118**  SMTPRSET -- send a RSET (reset) command
2119*/
2120
2121void
2122smtprset(m, mci, e)
2123	register MAILER *m;
2124	register MCI *mci;
2125	ENVELOPE *e;
2126{
2127	int r;
2128
2129	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2130	if (CurHostName == NULL)
2131		CurHostName = MyHostName;
2132
2133	SmtpPhase = "client RSET";
2134	smtpmessage("RSET", m, mci);
2135	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
2136	if (r < 0)
2137		mci->mci_state = MCIS_ERROR;
2138	else
2139	{
2140		/*
2141		**  Any response is deemed to be acceptable.
2142		**  The standard does not state the proper action
2143		**  to take when a value other than 250 is received.
2144		**
2145		**  However, if 421 is returned for the RSET, leave
2146		**  mci_state as MCIS_SSD (set in reply()).
2147		*/
2148
2149		if (mci->mci_state != MCIS_SSD)
2150			mci->mci_state = MCIS_OPEN;
2151		return;
2152	}
2153	smtpquit(m, mci, e);
2154}
2155/*
2156**  SMTPPROBE -- check the connection state
2157*/
2158
2159int
2160smtpprobe(mci)
2161	register MCI *mci;
2162{
2163	int r;
2164	MAILER *m = mci->mci_mailer;
2165	ENVELOPE *e;
2166	extern ENVELOPE BlankEnvelope;
2167
2168	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2169	if (CurHostName == NULL)
2170		CurHostName = MyHostName;
2171
2172	e = &BlankEnvelope;
2173	SmtpPhase = "client probe";
2174	smtpmessage("RSET", m, mci);
2175	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL);
2176	if (r < 0 || REPLYTYPE(r) != 2)
2177		smtpquit(m, mci, e);
2178	return r;
2179}
2180/*
2181**  REPLY -- read arpanet reply
2182**
2183**	Parameters:
2184**		m -- the mailer we are reading the reply from.
2185**		mci -- the mailer connection info structure.
2186**		e -- the current envelope.
2187**		timeout -- the timeout for reads.
2188**		pfunc -- processing function called on each line of response.
2189**			If null, no special processing is done.
2190**
2191**	Returns:
2192**		reply code it reads.
2193**
2194**	Side Effects:
2195**		flushes the mail file.
2196*/
2197
2198int
2199reply(m, mci, e, timeout, pfunc, enhstat)
2200	MAILER *m;
2201	MCI *mci;
2202	ENVELOPE *e;
2203	time_t timeout;
2204	void (*pfunc)();
2205	char **enhstat;
2206{
2207	register char *bufp;
2208	register int r;
2209	bool firstline = TRUE;
2210	char junkbuf[MAXLINE];
2211	static char enhstatcode[ENHSCLEN];
2212	int save_errno;
2213
2214	if (mci->mci_out != NULL)
2215		(void) fflush(mci->mci_out);
2216
2217	if (tTd(18, 1))
2218		dprintf("reply\n");
2219
2220	/*
2221	**  Read the input line, being careful not to hang.
2222	*/
2223
2224	bufp = SmtpReplyBuffer;
2225	for (;;)
2226	{
2227		register char *p;
2228
2229		/* actually do the read */
2230		if (e->e_xfp != NULL)
2231			(void) fflush(e->e_xfp);	/* for debugging */
2232
2233		/* if we are in the process of closing just give the code */
2234		if (mci->mci_state == MCIS_CLOSED)
2235			return SMTPCLOSING;
2236
2237		if (mci->mci_out != NULL)
2238			(void) fflush(mci->mci_out);
2239
2240		/* get the line from the other side */
2241		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
2242		mci->mci_lastuse = curtime();
2243
2244		if (p == NULL)
2245		{
2246			bool oldholderrs;
2247			extern char MsgBuf[];
2248
2249			/* if the remote end closed early, fake an error */
2250			if (errno == 0)
2251# ifdef ECONNRESET
2252				errno = ECONNRESET;
2253# else /* ECONNRESET */
2254				errno = EPIPE;
2255# endif /* ECONNRESET */
2256
2257			mci->mci_errno = errno;
2258			oldholderrs = HoldErrs;
2259			HoldErrs = TRUE;
2260			usrerr("451 4.4.1 reply: read error from %s",
2261			       CurHostName == NULL ? "NO_HOST" : CurHostName);
2262
2263			/* errors on QUIT should not be persistent */
2264			if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
2265				mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
2266
2267			/* if debugging, pause so we can see state */
2268			if (tTd(18, 100))
2269				(void) pause();
2270			mci->mci_state = MCIS_ERROR;
2271			save_errno = errno;
2272			smtpquit(m, mci, e);
2273# if XDEBUG
2274			{
2275				char wbuf[MAXLINE];
2276				int wbufleft = sizeof wbuf;
2277
2278				p = wbuf;
2279				if (e->e_to != NULL)
2280				{
2281					int plen;
2282
2283					snprintf(p, wbufleft, "%s... ",
2284						shortenstring(e->e_to, MAXSHORTSTR));
2285					plen = strlen(p);
2286					p += plen;
2287					wbufleft -= plen;
2288				}
2289				snprintf(p, wbufleft, "reply(%.100s) during %s",
2290					 CurHostName == NULL ? "NO_HOST" : CurHostName,
2291					 SmtpPhase);
2292				checkfd012(wbuf);
2293			}
2294# endif /* XDEBUG */
2295			errno = save_errno;
2296			HoldErrs = oldholderrs;
2297			return -1;
2298		}
2299		fixcrlf(bufp, TRUE);
2300
2301		/* EHLO failure is not a real error */
2302		if (e->e_xfp != NULL && (bufp[0] == '4' ||
2303		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
2304		{
2305			/* serious error -- log the previous command */
2306			if (SmtpNeedIntro)
2307			{
2308				/* inform user who we are chatting with */
2309				fprintf(CurEnv->e_xfp,
2310					"... while talking to %s:\n",
2311					CurHostName == NULL ? "NO_HOST" : CurHostName);
2312				SmtpNeedIntro = FALSE;
2313			}
2314			if (SmtpMsgBuffer[0] != '\0')
2315				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
2316			SmtpMsgBuffer[0] = '\0';
2317
2318			/* now log the message as from the other side */
2319			fprintf(e->e_xfp, "<<< %s\n", bufp);
2320		}
2321
2322		/* display the input for verbose mode */
2323		if (Verbose)
2324			nmessage("050 %s", bufp);
2325
2326		/* ignore improperly formatted input */
2327		if (!ISSMTPREPLY(bufp))
2328			continue;
2329
2330		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2331		    enhstat != NULL &&
2332		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
2333			*enhstat = enhstatcode;
2334
2335		/* process the line */
2336		if (pfunc != NULL)
2337			(*pfunc)(bufp, firstline, m, mci, e);
2338
2339		firstline = FALSE;
2340
2341		/* decode the reply code */
2342		r = atoi(bufp);
2343
2344		/* extra semantics: 0xx codes are "informational" */
2345		if (r < 100)
2346			continue;
2347
2348		/* if no continuation lines, return this line */
2349		if (bufp[3] != '-')
2350			break;
2351
2352		/* first line of real reply -- ignore rest */
2353		bufp = junkbuf;
2354	}
2355
2356	/*
2357	**  Now look at SmtpReplyBuffer -- only care about the first
2358	**  line of the response from here on out.
2359	*/
2360
2361	/* save temporary failure messages for posterity */
2362	if (SmtpReplyBuffer[0] == '4' &&
2363	    (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
2364		snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
2365
2366	/* reply code 421 is "Service Shutting Down" */
2367	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
2368	{
2369		/* send the quit protocol */
2370		mci->mci_state = MCIS_SSD;
2371		smtpquit(m, mci, e);
2372	}
2373
2374	return r;
2375}
2376/*
2377**  SMTPMESSAGE -- send message to server
2378**
2379**	Parameters:
2380**		f -- format
2381**		m -- the mailer to control formatting.
2382**		a, b, c -- parameters
2383**
2384**	Returns:
2385**		none.
2386**
2387**	Side Effects:
2388**		writes message to mci->mci_out.
2389*/
2390
2391/*VARARGS1*/
2392void
2393# ifdef __STDC__
2394smtpmessage(char *f, MAILER *m, MCI *mci, ...)
2395# else /* __STDC__ */
2396smtpmessage(f, m, mci, va_alist)
2397	char *f;
2398	MAILER *m;
2399	MCI *mci;
2400	va_dcl
2401# endif /* __STDC__ */
2402{
2403	VA_LOCAL_DECL
2404
2405	VA_START(mci);
2406	(void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
2407	VA_END;
2408
2409	if (tTd(18, 1) || Verbose)
2410		nmessage(">>> %s", SmtpMsgBuffer);
2411	if (TrafficLogFile != NULL)
2412		fprintf(TrafficLogFile, "%05d >>> %s\n",
2413			(int) getpid(), SmtpMsgBuffer);
2414	if (mci->mci_out != NULL)
2415	{
2416		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
2417			m == NULL ? "\r\n" : m->m_eol);
2418	}
2419	else if (tTd(18, 1))
2420	{
2421		dprintf("smtpmessage: NULL mci_out\n");
2422	}
2423}
2424
2425#endif /* SMTP */
2426