138032Speter/*
2261363Sgshapiro * Copyright (c) 1998-2004, 2006, 2007 Proofpoint, Inc. and its suppliers.
364565Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1490795Sgshapiro#include <sendmail.h>
15168520Sgshapiro#include <sm/sendmail.h>
1638032Speter
17266692SgshapiroSM_RCSID("@(#)$Id: headers.c,v 8.320 2013-11-22 20:51:55 ca Exp $")
1864565Sgshapiro
19168520Sgshapirostatic HDR	*allocheader __P((char *, char *, int, SM_RPOOL_T *, bool));
20111826Sgshapirostatic size_t	fix_mime_header __P((HDR *, ENVELOPE *));
2164565Sgshapirostatic int	priencode __P((char *));
22157006Sgshapirostatic bool	put_vanilla_header __P((HDR *, char *, MCI *));
2364565Sgshapiro
2438032Speter/*
2538032Speter**  SETUPHEADERS -- initialize headers in symbol table
2638032Speter**
2738032Speter**	Parameters:
2838032Speter**		none
2938032Speter**
3038032Speter**	Returns:
3138032Speter**		none
3238032Speter*/
3338032Speter
3438032Spetervoid
3538032Spetersetupheaders()
3638032Speter{
3738032Speter	struct hdrinfo *hi;
3838032Speter	STAB *s;
3938032Speter
4038032Speter	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
4138032Speter	{
4238032Speter		s = stab(hi->hi_field, ST_HEADER, ST_ENTER);
4338032Speter		s->s_header.hi_flags = hi->hi_flags;
4438032Speter		s->s_header.hi_ruleset = NULL;
4538032Speter	}
4638032Speter}
47168520Sgshapiro
4890795Sgshapiro/*
49168520Sgshapiro**  DOCHOMPHEADER -- process and save a header line.
5038032Speter**
51168520Sgshapiro**	Called by chompheader.
5238032Speter**
5338032Speter**	Parameters:
5438032Speter**		line -- header as a text line.
5566497Sgshapiro**		pflag -- flags for chompheader() (from sendmail.h)
5638032Speter**		hdrp -- a pointer to the place to save the header.
5738032Speter**		e -- the envelope including this header.
5838032Speter**
5938032Speter**	Returns:
6038032Speter**		flags for this header.
6138032Speter**
6238032Speter**	Side Effects:
6338032Speter**		The header is saved on the header list.
6438032Speter**		Contents of 'line' are destroyed.
6538032Speter*/
6638032Speter
6764565Sgshapirostatic struct hdrinfo	NormalHeader =	{ NULL, 0, NULL };
68168520Sgshapirostatic unsigned long	dochompheader __P((char *, int, HDR **, ENVELOPE *));
6938032Speter
70168520Sgshapirostatic unsigned long
71168520Sgshapirodochompheader(line, pflag, hdrp, e)
7238032Speter	char *line;
7364565Sgshapiro	int pflag;
7438032Speter	HDR **hdrp;
75168520Sgshapiro	ENVELOPE *e;
7638032Speter{
7790795Sgshapiro	unsigned char mid = '\0';
7838032Speter	register char *p;
7938032Speter	register HDR *h;
8038032Speter	HDR **hp;
8138032Speter	char *fname;
8238032Speter	char *fvalue;
8390795Sgshapiro	bool cond = false;
8464565Sgshapiro	bool dropfrom;
8538032Speter	bool headeronly;
8638032Speter	STAB *s;
8738032Speter	struct hdrinfo *hi;
8890795Sgshapiro	bool nullheader = false;
8964565Sgshapiro	BITMAP256 mopts;
9038032Speter
9138032Speter	headeronly = hdrp != NULL;
9238032Speter	if (!headeronly)
9338032Speter		hdrp = &e->e_header;
9438032Speter
9538032Speter	/* strip off options */
9638032Speter	clrbitmap(mopts);
9738032Speter	p = line;
9864565Sgshapiro	if (!bitset(pflag, CHHDR_USER) && *p == '?')
9938032Speter	{
10064565Sgshapiro		int c;
10164565Sgshapiro		register char *q;
10264565Sgshapiro
10364565Sgshapiro		q = strchr(++p, '?');
10464565Sgshapiro		if (q == NULL)
10564565Sgshapiro			goto hse;
10664565Sgshapiro
10764565Sgshapiro		*q = '\0';
10864565Sgshapiro		c = *p & 0377;
10964565Sgshapiro
11064565Sgshapiro		/* possibly macro conditional */
11164565Sgshapiro		if (c == MACROEXPAND)
11238032Speter		{
11364565Sgshapiro			/* catch ?$? */
11464565Sgshapiro			if (*++p == '\0')
11564565Sgshapiro			{
11664565Sgshapiro				*q = '?';
11764565Sgshapiro				goto hse;
11864565Sgshapiro			}
11964565Sgshapiro
12090795Sgshapiro			mid = (unsigned char) *p++;
12164565Sgshapiro
12264565Sgshapiro			/* catch ?$abc? */
12364565Sgshapiro			if (*p != '\0')
12464565Sgshapiro			{
12564565Sgshapiro				*q = '?';
12664565Sgshapiro				goto hse;
12764565Sgshapiro			}
12864565Sgshapiro		}
12964565Sgshapiro		else if (*p == '$')
13064565Sgshapiro		{
13164565Sgshapiro			/* catch ?$? */
13264565Sgshapiro			if (*++p == '\0')
13364565Sgshapiro			{
13464565Sgshapiro				*q = '?';
13564565Sgshapiro				goto hse;
13664565Sgshapiro			}
13764565Sgshapiro
13890795Sgshapiro			mid = (unsigned char) macid(p);
13964565Sgshapiro			if (bitset(0200, mid))
140120259Sgshapiro			{
14164565Sgshapiro				p += strlen(macname(mid)) + 2;
142120259Sgshapiro				SM_ASSERT(p <= q);
143120259Sgshapiro			}
14464565Sgshapiro			else
14564565Sgshapiro				p++;
14664565Sgshapiro
14764565Sgshapiro			/* catch ?$abc? */
14864565Sgshapiro			if (*p != '\0')
14964565Sgshapiro			{
15064565Sgshapiro				*q = '?';
15164565Sgshapiro				goto hse;
15264565Sgshapiro			}
15364565Sgshapiro		}
15464565Sgshapiro		else
15564565Sgshapiro		{
15664565Sgshapiro			while (*p != '\0')
15764565Sgshapiro			{
15864565Sgshapiro				if (!isascii(*p))
15964565Sgshapiro				{
16064565Sgshapiro					*q = '?';
16164565Sgshapiro					goto hse;
16264565Sgshapiro				}
16364565Sgshapiro
16471348Sgshapiro				setbitn(bitidx(*p), mopts);
16590795Sgshapiro				cond = true;
16664565Sgshapiro				p++;
16764565Sgshapiro			}
16838032Speter		}
16964565Sgshapiro		p = q + 1;
17038032Speter	}
17138032Speter
17238032Speter	/* find canonical name */
17338032Speter	fname = p;
17438032Speter	while (isascii(*p) && isgraph(*p) && *p != ':')
17538032Speter		p++;
17638032Speter	fvalue = p;
17738032Speter	while (isascii(*p) && isspace(*p))
17838032Speter		p++;
17938032Speter	if (*p++ != ':' || fname == fvalue)
18038032Speter	{
18164565Sgshapirohse:
18264565Sgshapiro		syserr("553 5.3.0 header syntax error, line \"%s\"", line);
18338032Speter		return 0;
18438032Speter	}
18538032Speter	*fvalue = '\0';
18643733Speter	fvalue = p;
18738032Speter
18843733Speter	/* if the field is null, go ahead and use the default */
18943733Speter	while (isascii(*p) && isspace(*p))
19043733Speter		p++;
19143733Speter	if (*p == '\0')
19290795Sgshapiro		nullheader = true;
19343733Speter
19438032Speter	/* security scan: long field names are end-of-header */
19538032Speter	if (strlen(fname) > 100)
19638032Speter		return H_EOH;
19738032Speter
19838032Speter	/* check to see if it represents a ruleset call */
19964565Sgshapiro	if (bitset(pflag, CHHDR_DEF))
20038032Speter	{
20138032Speter		char hbuf[50];
20238032Speter
203168520Sgshapiro		(void) expand(fvalue, hbuf, sizeof(hbuf), e);
20438032Speter		for (p = hbuf; isascii(*p) && isspace(*p); )
20538032Speter			p++;
20638032Speter		if ((*p++ & 0377) == CALLSUBR)
20738032Speter		{
20838032Speter			auto char *endp;
20964565Sgshapiro			bool strc;
21038032Speter
21164565Sgshapiro			strc = *p == '+';	/* strip comments? */
21264565Sgshapiro			if (strc)
21364565Sgshapiro				++p;
21438032Speter			if (strtorwset(p, &endp, ST_ENTER) > 0)
21538032Speter			{
21638032Speter				*endp = '\0';
21738032Speter				s = stab(fname, ST_HEADER, ST_ENTER);
21890795Sgshapiro				if (LogLevel > 9 &&
21990795Sgshapiro				    s->s_header.hi_ruleset != NULL)
22090795Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
22190795Sgshapiro						  "Warning: redefined ruleset for header=%s, old=%s, new=%s",
22290795Sgshapiro						  fname,
22390795Sgshapiro						  s->s_header.hi_ruleset, p);
22438032Speter				s->s_header.hi_ruleset = newstr(p);
22564565Sgshapiro				if (!strc)
22664565Sgshapiro					s->s_header.hi_flags |= H_STRIPCOMM;
22738032Speter			}
22838032Speter			return 0;
22938032Speter		}
23038032Speter	}
23138032Speter
23238032Speter	/* see if it is a known type */
23338032Speter	s = stab(fname, ST_HEADER, ST_FIND);
23438032Speter	if (s != NULL)
23538032Speter		hi = &s->s_header;
23638032Speter	else
23738032Speter		hi = &NormalHeader;
23838032Speter
23938032Speter	if (tTd(31, 9))
24038032Speter	{
24138032Speter		if (s == NULL)
24290795Sgshapiro			sm_dprintf("no header flags match\n");
24338032Speter		else
24490795Sgshapiro			sm_dprintf("header match, flags=%lx, ruleset=%s\n",
24590795Sgshapiro				   hi->hi_flags,
24690795Sgshapiro				   hi->hi_ruleset == NULL ? "<NULL>"
24790795Sgshapiro							  : hi->hi_ruleset);
24838032Speter	}
24938032Speter
25038032Speter	/* see if this is a resent message */
25164565Sgshapiro	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
25264565Sgshapiro	    bitset(H_RESENT, hi->hi_flags))
25338032Speter		e->e_flags |= EF_RESENT;
25438032Speter
25538032Speter	/* if this is an Errors-To: header keep track of it now */
25664565Sgshapiro	if (UseErrorsTo && !bitset(pflag, CHHDR_DEF) && !headeronly &&
25738032Speter	    bitset(H_ERRORSTO, hi->hi_flags))
25838032Speter		(void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e);
25938032Speter
26038032Speter	/* if this means "end of header" quit now */
26138032Speter	if (!headeronly && bitset(H_EOH, hi->hi_flags))
26238032Speter		return hi->hi_flags;
26338032Speter
26438032Speter	/*
26538032Speter	**  Horrible hack to work around problem with Lotus Notes SMTP
26638032Speter	**  mail gateway, which generates From: headers with newlines in
26738032Speter	**  them and the <address> on the second line.  Although this is
26838032Speter	**  legal RFC 822, many MUAs don't handle this properly and thus
26938032Speter	**  never find the actual address.
27038032Speter	*/
27138032Speter
27238032Speter	if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader)
27338032Speter	{
27438032Speter		while ((p = strchr(fvalue, '\n')) != NULL)
27538032Speter			*p = ' ';
27638032Speter	}
27738032Speter
27838032Speter	/*
27938032Speter	**  If there is a check ruleset, verify it against the header.
28038032Speter	*/
28138032Speter
28264565Sgshapiro	if (bitset(pflag, CHHDR_CHECK))
28364565Sgshapiro	{
284102531Sgshapiro		int rscheckflags;
28564565Sgshapiro		char *rs;
28638032Speter
287102531Sgshapiro		rscheckflags = RSF_COUNT;
288102531Sgshapiro		if (!bitset(hi->hi_flags, H_FROM|H_RCPT))
289102531Sgshapiro			rscheckflags |= RSF_UNSTRUCTURED;
290132946Sgshapiro
291132946Sgshapiro		/* no ruleset? look for default */
292132946Sgshapiro		rs = hi->hi_ruleset;
29364565Sgshapiro		if (rs == NULL)
29464565Sgshapiro		{
29564565Sgshapiro			s = stab("*", ST_HEADER, ST_FIND);
29664565Sgshapiro			if (s != NULL)
29764565Sgshapiro			{
29864565Sgshapiro				rs = (&s->s_header)->hi_ruleset;
299102531Sgshapiro				if (bitset((&s->s_header)->hi_flags,
300102531Sgshapiro					   H_STRIPCOMM))
301102531Sgshapiro					rscheckflags |= RSF_RMCOMM;
30264565Sgshapiro			}
30364565Sgshapiro		}
304102531Sgshapiro		else if (bitset(hi->hi_flags, H_STRIPCOMM))
305102531Sgshapiro			rscheckflags |= RSF_RMCOMM;
30664565Sgshapiro		if (rs != NULL)
30764565Sgshapiro		{
30890795Sgshapiro			int l, k;
30964565Sgshapiro			char qval[MAXNAME];
31064565Sgshapiro
31164565Sgshapiro			l = 0;
31290795Sgshapiro			qval[l++] = '"';
31390795Sgshapiro
31490795Sgshapiro			/* - 3 to avoid problems with " at the end */
315120259Sgshapiro			/* should be sizeof(qval), not MAXNAME */
31690795Sgshapiro			for (k = 0; fvalue[k] != '\0' && l < MAXNAME - 3; k++)
31764565Sgshapiro			{
31890795Sgshapiro				switch (fvalue[k])
31964565Sgshapiro				{
32090795Sgshapiro				  /* XXX other control chars? */
32164565Sgshapiro				  case '\011': /* ht */
32264565Sgshapiro				  case '\012': /* nl */
32364565Sgshapiro				  case '\013': /* vt */
32464565Sgshapiro				  case '\014': /* np */
32564565Sgshapiro				  case '\015': /* cr */
32690795Sgshapiro					qval[l++] = ' ';
32764565Sgshapiro					break;
32864565Sgshapiro				  case '"':
32990795Sgshapiro					qval[l++] = '\\';
33064565Sgshapiro					/* FALLTHROUGH */
33164565Sgshapiro				  default:
33290795Sgshapiro					qval[l++] = fvalue[k];
33364565Sgshapiro					break;
33464565Sgshapiro				}
33564565Sgshapiro			}
33690795Sgshapiro			qval[l++] = '"';
33790795Sgshapiro			qval[l] = '\0';
33890795Sgshapiro			k += strlen(fvalue + k);
33990795Sgshapiro			if (k >= MAXNAME)
34064565Sgshapiro			{
34164565Sgshapiro				if (LogLevel > 9)
34264565Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
34364565Sgshapiro						  "Warning: truncated header '%s' before check with '%s' len=%d max=%d",
34490795Sgshapiro						  fname, rs, k, MAXNAME - 1);
34564565Sgshapiro			}
34690795Sgshapiro			macdefine(&e->e_macro, A_TEMP,
34790795Sgshapiro				macid("{currHeader}"), qval);
34890795Sgshapiro			macdefine(&e->e_macro, A_TEMP,
34990795Sgshapiro				macid("{hdr_name}"), fname);
35090795Sgshapiro
351168520Sgshapiro			(void) sm_snprintf(qval, sizeof(qval), "%d", k);
35290795Sgshapiro			macdefine(&e->e_macro, A_TEMP, macid("{hdrlen}"), qval);
353132946Sgshapiro			if (bitset(H_FROM, hi->hi_flags))
35490795Sgshapiro				macdefine(&e->e_macro, A_PERM,
35590795Sgshapiro					macid("{addr_type}"), "h s");
356132946Sgshapiro			else if (bitset(H_RCPT, hi->hi_flags))
35790795Sgshapiro				macdefine(&e->e_macro, A_PERM,
35890795Sgshapiro					macid("{addr_type}"), "h r");
35990795Sgshapiro			else
36090795Sgshapiro				macdefine(&e->e_macro, A_PERM,
36190795Sgshapiro					macid("{addr_type}"), "h");
362102531Sgshapiro			(void) rscheck(rs, fvalue, NULL, e, rscheckflags, 3,
363285303Sgshapiro				       NULL, e->e_id, NULL, NULL);
36464565Sgshapiro		}
36564565Sgshapiro	}
36664565Sgshapiro
36738032Speter	/*
36838032Speter	**  Drop explicit From: if same as what we would generate.
36938032Speter	**  This is to make MH (which doesn't always give a full name)
37038032Speter	**  insert the full name information in all circumstances.
37138032Speter	*/
37238032Speter
37390795Sgshapiro	dropfrom = false;
37438032Speter	p = "resent-from";
37538032Speter	if (!bitset(EF_RESENT, e->e_flags))
37638032Speter		p += 7;
37764565Sgshapiro	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
37890795Sgshapiro	    !bitset(EF_QUEUERUN, e->e_flags) && sm_strcasecmp(fname, p) == 0)
37938032Speter	{
38038032Speter		if (e->e_from.q_paddr != NULL &&
38164565Sgshapiro		    e->e_from.q_mailer != NULL &&
38264565Sgshapiro		    bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
38338032Speter		    (strcmp(fvalue, e->e_from.q_paddr) == 0 ||
38438032Speter		     strcmp(fvalue, e->e_from.q_user) == 0))
38590795Sgshapiro			dropfrom = true;
386261363Sgshapiro		if (tTd(31, 2))
387261363Sgshapiro		{
388261363Sgshapiro			sm_dprintf("comparing header from (%s) against default (%s or %s), drop=%d\n",
389261363Sgshapiro				fvalue, e->e_from.q_paddr, e->e_from.q_user,
390261363Sgshapiro				dropfrom);
391261363Sgshapiro		}
39238032Speter	}
39338032Speter
39438032Speter	/* delete default value for this header */
39538032Speter	for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link)
39638032Speter	{
39790795Sgshapiro		if (sm_strcasecmp(fname, h->h_field) == 0 &&
39864565Sgshapiro		    !bitset(H_USER, h->h_flags) &&
39938032Speter		    !bitset(H_FORCE, h->h_flags))
40038032Speter		{
40143733Speter			if (nullheader)
40243733Speter			{
40343733Speter				/* user-supplied value was null */
40443733Speter				return 0;
40543733Speter			}
40664565Sgshapiro			if (dropfrom)
40764565Sgshapiro			{
40864565Sgshapiro				/* make this look like the user entered it */
40964565Sgshapiro				h->h_flags |= H_USER;
410261363Sgshapiro
411261363Sgshapiro				/*
412261363Sgshapiro				**  If the MH hack is selected, allow to turn
413261363Sgshapiro				**  it off via a mailer flag to avoid problems
414261363Sgshapiro				**  with setups that remove the F flag from
415261363Sgshapiro				**  the RCPT mailer.
416261363Sgshapiro				*/
417261363Sgshapiro
418285303Sgshapiro				if (bitnset(M_NOMHHACK,
419261363Sgshapiro					    e->e_from.q_mailer->m_flags))
420261363Sgshapiro				{
421261363Sgshapiro					h->h_flags &= ~H_CHECK;
422261363Sgshapiro				}
42364565Sgshapiro				return hi->hi_flags;
42464565Sgshapiro			}
42538032Speter			h->h_value = NULL;
42638032Speter			if (!cond)
42738032Speter			{
42838032Speter				/* copy conditions from default case */
42990795Sgshapiro				memmove((char *) mopts, (char *) h->h_mflags,
430168520Sgshapiro					sizeof(mopts));
43138032Speter			}
43264565Sgshapiro			h->h_macro = mid;
43338032Speter		}
43438032Speter	}
43538032Speter
43638032Speter	/* create a new node */
437168520Sgshapiro	h = (HDR *) sm_rpool_malloc_x(e->e_rpool, sizeof(*h));
43890795Sgshapiro	h->h_field = sm_rpool_strdup_x(e->e_rpool, fname);
43990795Sgshapiro	h->h_value = sm_rpool_strdup_x(e->e_rpool, fvalue);
44038032Speter	h->h_link = NULL;
441168520Sgshapiro	memmove((char *) h->h_mflags, (char *) mopts, sizeof(mopts));
44264565Sgshapiro	h->h_macro = mid;
44338032Speter	*hp = h;
44438032Speter	h->h_flags = hi->hi_flags;
44566497Sgshapiro	if (bitset(pflag, CHHDR_USER) || bitset(pflag, CHHDR_QUEUE))
44664565Sgshapiro		h->h_flags |= H_USER;
44738032Speter
44838032Speter	/* strip EOH flag if parsing MIME headers */
44938032Speter	if (headeronly)
45038032Speter		h->h_flags &= ~H_EOH;
45164565Sgshapiro	if (bitset(pflag, CHHDR_DEF))
45238032Speter		h->h_flags |= H_DEFAULT;
45364565Sgshapiro	if (cond || mid != '\0')
45438032Speter		h->h_flags |= H_CHECK;
45538032Speter
45638032Speter	/* hack to see if this is a new format message */
45764565Sgshapiro	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
45864565Sgshapiro	    bitset(H_RCPT|H_FROM, h->h_flags) &&
45938032Speter	    (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
46038032Speter	     strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
46138032Speter	{
46238032Speter		e->e_flags &= ~EF_OLDSTYLE;
46338032Speter	}
46438032Speter
46538032Speter	return h->h_flags;
46638032Speter}
467168520Sgshapiro
46890795Sgshapiro/*
469168520Sgshapiro**  CHOMPHEADER -- process and save a header line.
470168520Sgshapiro**
471168520Sgshapiro**	Called by collect, readcf, and readqf to deal with header lines.
472168520Sgshapiro**	This is just a wrapper for dochompheader().
473168520Sgshapiro**
474168520Sgshapiro**	Parameters:
475168520Sgshapiro**		line -- header as a text line.
476168520Sgshapiro**		pflag -- flags for chompheader() (from sendmail.h)
477168520Sgshapiro**		hdrp -- a pointer to the place to save the header.
478168520Sgshapiro**		e -- the envelope including this header.
479168520Sgshapiro**
480168520Sgshapiro**	Returns:
481168520Sgshapiro**		flags for this header.
482168520Sgshapiro**
483168520Sgshapiro**	Side Effects:
484168520Sgshapiro**		The header is saved on the header list.
485168520Sgshapiro**		Contents of 'line' are destroyed.
486168520Sgshapiro*/
487168520Sgshapiro
488168520Sgshapiro
489168520Sgshapirounsigned long
490168520Sgshapirochompheader(line, pflag, hdrp, e)
491168520Sgshapiro	char *line;
492168520Sgshapiro	int pflag;
493168520Sgshapiro	HDR **hdrp;
494168520Sgshapiro	register ENVELOPE *e;
495168520Sgshapiro{
496168520Sgshapiro	unsigned long rval;
497168520Sgshapiro
498168520Sgshapiro	if (tTd(31, 6))
499168520Sgshapiro	{
500168520Sgshapiro		sm_dprintf("chompheader: ");
501168520Sgshapiro		xputs(sm_debug_file(), line);
502168520Sgshapiro		sm_dprintf("\n");
503168520Sgshapiro	}
504168520Sgshapiro
505168520Sgshapiro	/* quote this if user (not config file) input */
506168520Sgshapiro	if (bitset(pflag, CHHDR_USER))
507168520Sgshapiro	{
508168520Sgshapiro		char xbuf[MAXLINE];
509168520Sgshapiro		char *xbp = NULL;
510168520Sgshapiro		int xbufs;
511168520Sgshapiro
512168520Sgshapiro		xbufs = sizeof(xbuf);
513168520Sgshapiro		xbp = quote_internal_chars(line, xbuf, &xbufs);
514168520Sgshapiro		if (tTd(31, 7))
515168520Sgshapiro		{
516168520Sgshapiro			sm_dprintf("chompheader: quoted: ");
517168520Sgshapiro			xputs(sm_debug_file(), xbp);
518168520Sgshapiro			sm_dprintf("\n");
519168520Sgshapiro		}
520168520Sgshapiro		rval = dochompheader(xbp, pflag, hdrp, e);
521168520Sgshapiro		if (xbp != xbuf)
522168520Sgshapiro			sm_free(xbp);
523168520Sgshapiro	}
524168520Sgshapiro	else
525168520Sgshapiro		rval = dochompheader(line, pflag, hdrp, e);
526168520Sgshapiro
527168520Sgshapiro	return rval;
528168520Sgshapiro}
529168520Sgshapiro
530168520Sgshapiro/*
531132946Sgshapiro**  ALLOCHEADER -- allocate a header entry
532132946Sgshapiro**
533132946Sgshapiro**	Parameters:
534168520Sgshapiro**		field -- the name of the header field (will not be copied).
535168520Sgshapiro**		value -- the value of the field (will be copied).
536132946Sgshapiro**		flags -- flags to add to h_flags.
537132946Sgshapiro**		rp -- resource pool for allocations
538168520Sgshapiro**		space -- add leading space?
539132946Sgshapiro**
540132946Sgshapiro**	Returns:
541132946Sgshapiro**		Pointer to a newly allocated and populated HDR.
542168520Sgshapiro**
543168520Sgshapiro**	Notes:
544168520Sgshapiro**		o field and value must be in internal format, i.e.,
545168520Sgshapiro**		metacharacters must be "quoted", see quote_internal_chars().
546168520Sgshapiro**		o maybe add more flags to decide:
547168520Sgshapiro**		  - what to copy (field/value)
548168520Sgshapiro**		  - whether to convert value to an internal format
549132946Sgshapiro*/
550132946Sgshapiro
551132946Sgshapirostatic HDR *
552168520Sgshapiroallocheader(field, value, flags, rp, space)
553132946Sgshapiro	char *field;
554132946Sgshapiro	char *value;
555132946Sgshapiro	int flags;
556132946Sgshapiro	SM_RPOOL_T *rp;
557168520Sgshapiro	bool space;
558132946Sgshapiro{
559132946Sgshapiro	HDR *h;
560132946Sgshapiro	STAB *s;
561132946Sgshapiro
562132946Sgshapiro	/* find info struct */
563132946Sgshapiro	s = stab(field, ST_HEADER, ST_FIND);
564132946Sgshapiro
565132946Sgshapiro	/* allocate space for new header */
566168520Sgshapiro	h = (HDR *) sm_rpool_malloc_x(rp, sizeof(*h));
567132946Sgshapiro	h->h_field = field;
568168520Sgshapiro	if (space)
569168520Sgshapiro	{
570168520Sgshapiro		size_t l;
571168520Sgshapiro		char *n;
572168520Sgshapiro
573168520Sgshapiro		l = strlen(value);
574168520Sgshapiro		SM_ASSERT(l + 2 > l);
575168520Sgshapiro		n = sm_rpool_malloc_x(rp, l + 2);
576168520Sgshapiro		n[0] = ' ';
577168520Sgshapiro		n[1] = '\0';
578168520Sgshapiro		sm_strlcpy(n + 1, value, l + 1);
579168520Sgshapiro		h->h_value = n;
580168520Sgshapiro	}
581168520Sgshapiro	else
582168520Sgshapiro		h->h_value = sm_rpool_strdup_x(rp, value);
583132946Sgshapiro	h->h_flags = flags;
584132946Sgshapiro	if (s != NULL)
585132946Sgshapiro		h->h_flags |= s->s_header.hi_flags;
586132946Sgshapiro	clrbitmap(h->h_mflags);
587132946Sgshapiro	h->h_macro = '\0';
588132946Sgshapiro
589132946Sgshapiro	return h;
590132946Sgshapiro}
591168520Sgshapiro
592132946Sgshapiro/*
59338032Speter**  ADDHEADER -- add a header entry to the end of the queue.
59438032Speter**
59538032Speter**	This bypasses the special checking of chompheader.
59638032Speter**
59738032Speter**	Parameters:
598168520Sgshapiro**		field -- the name of the header field (will not be copied).
599168520Sgshapiro**		value -- the value of the field (will be copied).
60064565Sgshapiro**		flags -- flags to add to h_flags.
60190795Sgshapiro**		e -- envelope.
602168520Sgshapiro**		space -- add leading space?
60338032Speter**
60438032Speter**	Returns:
60538032Speter**		none.
60638032Speter**
60738032Speter**	Side Effects:
60838032Speter**		adds the field on the list of headers for this envelope.
609168520Sgshapiro**
610168520Sgshapiro**	Notes: field and value must be in internal format, i.e.,
611168520Sgshapiro**		metacharacters must be "quoted", see quote_internal_chars().
61238032Speter*/
61338032Speter
61438032Spetervoid
615168520Sgshapiroaddheader(field, value, flags, e, space)
61638032Speter	char *field;
61738032Speter	char *value;
61864565Sgshapiro	int flags;
61990795Sgshapiro	ENVELOPE *e;
620168520Sgshapiro	bool space;
62138032Speter{
62238032Speter	register HDR *h;
62338032Speter	HDR **hp;
62490795Sgshapiro	HDR **hdrlist = &e->e_header;
62538032Speter
62638032Speter	/* find current place in list -- keep back pointer? */
62738032Speter	for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link)
62838032Speter	{
62990795Sgshapiro		if (sm_strcasecmp(field, h->h_field) == 0)
63038032Speter			break;
63138032Speter	}
63238032Speter
63338032Speter	/* allocate space for new header */
634168520Sgshapiro	h = allocheader(field, value, flags, e->e_rpool, space);
63538032Speter	h->h_link = *hp;
63638032Speter	*hp = h;
63738032Speter}
638168520Sgshapiro
63990795Sgshapiro/*
640132946Sgshapiro**  INSHEADER -- insert a header entry at the specified index
641132946Sgshapiro**	This bypasses the special checking of chompheader.
642132946Sgshapiro**
643132946Sgshapiro**	Parameters:
644132946Sgshapiro**		idx -- index into the header list at which to insert
645168520Sgshapiro**		field -- the name of the header field (will be copied).
646168520Sgshapiro**		value -- the value of the field (will be copied).
647132946Sgshapiro**		flags -- flags to add to h_flags.
648132946Sgshapiro**		e -- envelope.
649168520Sgshapiro**		space -- add leading space?
650132946Sgshapiro**
651132946Sgshapiro**	Returns:
652132946Sgshapiro**		none.
653132946Sgshapiro**
654132946Sgshapiro**	Side Effects:
655132946Sgshapiro**		inserts the field on the list of headers for this envelope.
656168520Sgshapiro**
657168520Sgshapiro**	Notes:
658168520Sgshapiro**		- field and value must be in internal format, i.e.,
659168520Sgshapiro**		metacharacters must be "quoted", see quote_internal_chars().
660168520Sgshapiro**		- the header list contains headers that might not be
661168520Sgshapiro**		sent "out" (see putheader(): "skip"), hence there is no
662168520Sgshapiro**		reliable way to insert a header at an exact position
663168520Sgshapiro**		(except at the front or end).
664132946Sgshapiro*/
665132946Sgshapiro
666132946Sgshapirovoid
667168520Sgshapiroinsheader(idx, field, value, flags, e, space)
668132946Sgshapiro	int idx;
669132946Sgshapiro	char *field;
670132946Sgshapiro	char *value;
671132946Sgshapiro	int flags;
672132946Sgshapiro	ENVELOPE *e;
673168520Sgshapiro	bool space;
674132946Sgshapiro{
675132946Sgshapiro	HDR *h, *srch, *last = NULL;
676132946Sgshapiro
677132946Sgshapiro	/* allocate space for new header */
678168520Sgshapiro	h = allocheader(field, value, flags, e->e_rpool, space);
679132946Sgshapiro
680132946Sgshapiro	/* find insertion position */
681132946Sgshapiro	for (srch = e->e_header; srch != NULL && idx > 0;
682132946Sgshapiro	     srch = srch->h_link, idx--)
683132946Sgshapiro		last = srch;
684132946Sgshapiro
685132946Sgshapiro	if (e->e_header == NULL)
686132946Sgshapiro	{
687132946Sgshapiro		e->e_header = h;
688132946Sgshapiro		h->h_link = NULL;
689132946Sgshapiro	}
690132946Sgshapiro	else if (srch == NULL)
691132946Sgshapiro	{
692132946Sgshapiro		SM_ASSERT(last != NULL);
693132946Sgshapiro		last->h_link = h;
694132946Sgshapiro		h->h_link = NULL;
695132946Sgshapiro	}
696132946Sgshapiro	else
697132946Sgshapiro	{
698132946Sgshapiro		h->h_link = srch->h_link;
699132946Sgshapiro		srch->h_link = h;
700132946Sgshapiro	}
701132946Sgshapiro}
702168520Sgshapiro
703132946Sgshapiro/*
70438032Speter**  HVALUE -- return value of a header.
70538032Speter**
70638032Speter**	Only "real" fields (i.e., ones that have not been supplied
70738032Speter**	as a default) are used.
70838032Speter**
70938032Speter**	Parameters:
71038032Speter**		field -- the field name.
71138032Speter**		header -- the header list.
71238032Speter**
71338032Speter**	Returns:
714168520Sgshapiro**		pointer to the value part (internal format).
71538032Speter**		NULL if not found.
71638032Speter**
71738032Speter**	Side Effects:
71838032Speter**		none.
71938032Speter*/
72038032Speter
72138032Speterchar *
72238032Speterhvalue(field, header)
72338032Speter	char *field;
72438032Speter	HDR *header;
72538032Speter{
72638032Speter	register HDR *h;
72738032Speter
72838032Speter	for (h = header; h != NULL; h = h->h_link)
72938032Speter	{
73038032Speter		if (!bitset(H_DEFAULT, h->h_flags) &&
73190795Sgshapiro		    sm_strcasecmp(h->h_field, field) == 0)
732203004Sgshapiro		{
733203004Sgshapiro			char *s;
734203004Sgshapiro
735203004Sgshapiro			s = h->h_value;
736203004Sgshapiro			if (s == NULL)
737203004Sgshapiro				return NULL;
738203004Sgshapiro			while (isascii(*s) && isspace(*s))
739203004Sgshapiro				s++;
740203004Sgshapiro			return s;
741203004Sgshapiro		}
74238032Speter	}
74364565Sgshapiro	return NULL;
74438032Speter}
745168520Sgshapiro
74690795Sgshapiro/*
74738032Speter**  ISHEADER -- predicate telling if argument is a header.
74838032Speter**
74938032Speter**	A line is a header if it has a single word followed by
75038032Speter**	optional white space followed by a colon.
75138032Speter**
75238032Speter**	Header fields beginning with two dashes, although technically
75338032Speter**	permitted by RFC822, are automatically rejected in order
75438032Speter**	to make MIME work out.  Without this we could have a technically
75538032Speter**	legal header such as ``--"foo:bar"'' that would also be a legal
75638032Speter**	MIME separator.
75738032Speter**
75838032Speter**	Parameters:
75938032Speter**		h -- string to check for possible headerness.
76038032Speter**
76138032Speter**	Returns:
76290795Sgshapiro**		true if h is a header.
76390795Sgshapiro**		false otherwise.
76438032Speter**
76538032Speter**	Side Effects:
76638032Speter**		none.
76738032Speter*/
76838032Speter
76938032Speterbool
77038032Speterisheader(h)
77138032Speter	char *h;
77238032Speter{
773168520Sgshapiro	char *s;
77438032Speter
775168520Sgshapiro	s = h;
77638032Speter	if (s[0] == '-' && s[1] == '-')
77790795Sgshapiro		return false;
77838032Speter
77938032Speter	while (*s > ' ' && *s != ':' && *s != '\0')
78038032Speter		s++;
78138032Speter
78238032Speter	if (h == s)
78390795Sgshapiro		return false;
78438032Speter
78538032Speter	/* following technically violates RFC822 */
78638032Speter	while (isascii(*s) && isspace(*s))
78738032Speter		s++;
78838032Speter
78938032Speter	return (*s == ':');
79038032Speter}
791168520Sgshapiro
79290795Sgshapiro/*
79338032Speter**  EATHEADER -- run through the stored header and extract info.
79438032Speter**
79538032Speter**	Parameters:
79638032Speter**		e -- the envelope to process.
79738032Speter**		full -- if set, do full processing (e.g., compute
79838032Speter**			message priority).  This should not be set
79938032Speter**			when reading a queue file because some info
80038032Speter**			needed to compute the priority is wrong.
80190795Sgshapiro**		log -- call logsender()?
80238032Speter**
80338032Speter**	Returns:
80438032Speter**		none.
80538032Speter**
80638032Speter**	Side Effects:
80738032Speter**		Sets a bunch of global variables from information
80838032Speter**			in the collected header.
80938032Speter*/
81038032Speter
81138032Spetervoid
81290795Sgshapiroeatheader(e, full, log)
81338032Speter	register ENVELOPE *e;
81438032Speter	bool full;
81590795Sgshapiro	bool log;
81638032Speter{
81738032Speter	register HDR *h;
81838032Speter	register char *p;
81938032Speter	int hopcnt = 0;
82038032Speter	char buf[MAXLINE];
82138032Speter
82238032Speter	/*
82338032Speter	**  Set up macros for possible expansion in headers.
82438032Speter	*/
82538032Speter
82690795Sgshapiro	macdefine(&e->e_macro, A_PERM, 'f', e->e_sender);
82790795Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
82838032Speter	if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0')
82990795Sgshapiro		macdefine(&e->e_macro, A_PERM, 'u', e->e_origrcpt);
83038032Speter	else
83190795Sgshapiro		macdefine(&e->e_macro, A_PERM, 'u', NULL);
83238032Speter
83338032Speter	/* full name of from person */
83438032Speter	p = hvalue("full-name", e->e_header);
83538032Speter	if (p != NULL)
83638032Speter	{
83738032Speter		if (!rfc822_string(p))
83838032Speter		{
83938032Speter			/*
84038032Speter			**  Quote a full name with special characters
84138032Speter			**  as a comment so crackaddr() doesn't destroy
84238032Speter			**  the name portion of the address.
84338032Speter			*/
84490795Sgshapiro
84590795Sgshapiro			p = addquotes(p, e->e_rpool);
84638032Speter		}
84790795Sgshapiro		macdefine(&e->e_macro, A_PERM, 'x', p);
84838032Speter	}
84938032Speter
85038032Speter	if (tTd(32, 1))
85190795Sgshapiro		sm_dprintf("----- collected header -----\n");
85290795Sgshapiro	e->e_msgid = NULL;
85338032Speter	for (h = e->e_header; h != NULL; h = h->h_link)
85438032Speter	{
85538032Speter		if (tTd(32, 1))
856168520Sgshapiro			sm_dprintf("%s:", h->h_field);
85738032Speter		if (h->h_value == NULL)
85838032Speter		{
85938032Speter			if (tTd(32, 1))
86090795Sgshapiro				sm_dprintf("<NULL>\n");
86138032Speter			continue;
86238032Speter		}
86338032Speter
86438032Speter		/* do early binding */
86564565Sgshapiro		if (bitset(H_DEFAULT, h->h_flags) &&
86664565Sgshapiro		    !bitset(H_BINDLATE, h->h_flags))
86738032Speter		{
86838032Speter			if (tTd(32, 1))
86938032Speter			{
87090795Sgshapiro				sm_dprintf("(");
871132946Sgshapiro				xputs(sm_debug_file(), h->h_value);
87290795Sgshapiro				sm_dprintf(") ");
87338032Speter			}
874168520Sgshapiro			expand(h->h_value, buf, sizeof(buf), e);
875168520Sgshapiro			if (buf[0] != '\0' &&
876168520Sgshapiro			    (buf[0] != ' ' || buf[1] != '\0'))
87738032Speter			{
87838032Speter				if (bitset(H_FROM, h->h_flags))
879111826Sgshapiro					expand(crackaddr(buf, e),
880168520Sgshapiro					       buf, sizeof(buf), e);
88190795Sgshapiro				h->h_value = sm_rpool_strdup_x(e->e_rpool, buf);
88238032Speter				h->h_flags &= ~H_DEFAULT;
88338032Speter			}
88438032Speter		}
88538032Speter		if (tTd(32, 1))
88638032Speter		{
887132946Sgshapiro			xputs(sm_debug_file(), h->h_value);
88890795Sgshapiro			sm_dprintf("\n");
88938032Speter		}
89038032Speter
89138032Speter		/* count the number of times it has been processed */
89238032Speter		if (bitset(H_TRACE, h->h_flags))
89338032Speter			hopcnt++;
89438032Speter
89538032Speter		/* send to this person if we so desire */
89638032Speter		if (GrabTo && bitset(H_RCPT, h->h_flags) &&
89738032Speter		    !bitset(H_DEFAULT, h->h_flags) &&
89890795Sgshapiro		    (!bitset(EF_RESENT, e->e_flags) ||
89990795Sgshapiro		     bitset(H_RESENT, h->h_flags)))
90038032Speter		{
90138032Speter#if 0
90238032Speter			int saveflags = e->e_flags;
90364565Sgshapiro#endif /* 0 */
90438032Speter
90590795Sgshapiro			(void) sendtolist(denlstring(h->h_value, true, false),
90690795Sgshapiro					  NULLADDR, &e->e_sendqueue, 0, e);
90738032Speter
90838032Speter#if 0
90938032Speter			/*
91042580Speter			**  Change functionality so a fatal error on an
91142580Speter			**  address doesn't affect the entire envelope.
91238032Speter			*/
91364565Sgshapiro
91438032Speter			/* delete fatal errors generated by this address */
91538032Speter			if (!bitset(EF_FATALERRS, saveflags))
91638032Speter				e->e_flags &= ~EF_FATALERRS;
91764565Sgshapiro#endif /* 0 */
91838032Speter		}
91938032Speter
92038032Speter		/* save the message-id for logging */
92138032Speter		p = "resent-message-id";
92238032Speter		if (!bitset(EF_RESENT, e->e_flags))
92338032Speter			p += 7;
92490795Sgshapiro		if (sm_strcasecmp(h->h_field, p) == 0)
92538032Speter		{
92690795Sgshapiro			e->e_msgid = h->h_value;
92790795Sgshapiro			while (isascii(*e->e_msgid) && isspace(*e->e_msgid))
92890795Sgshapiro				e->e_msgid++;
929125823Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{msg_id}"),
930132946Sgshapiro				  e->e_msgid);
93138032Speter		}
93238032Speter	}
93338032Speter	if (tTd(32, 1))
93490795Sgshapiro		sm_dprintf("----------------------------\n");
93538032Speter
93638032Speter	/* if we are just verifying (that is, sendmail -t -bv), drop out now */
93738032Speter	if (OpMode == MD_VERIFY)
93838032Speter		return;
93938032Speter
94038032Speter	/* store hop count */
94138032Speter	if (hopcnt > e->e_hopcount)
94290795Sgshapiro	{
94338032Speter		e->e_hopcount = hopcnt;
944168520Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%d", e->e_hopcount);
94590795Sgshapiro		macdefine(&e->e_macro, A_TEMP, 'c', buf);
94690795Sgshapiro	}
94738032Speter
94838032Speter	/* message priority */
94938032Speter	p = hvalue("precedence", e->e_header);
95038032Speter	if (p != NULL)
95138032Speter		e->e_class = priencode(p);
95238032Speter	if (e->e_class < 0)
95338032Speter		e->e_timeoutclass = TOC_NONURGENT;
95438032Speter	else if (e->e_class > 0)
95538032Speter		e->e_timeoutclass = TOC_URGENT;
95638032Speter	if (full)
95738032Speter	{
95838032Speter		e->e_msgpriority = e->e_msgsize
95938032Speter				 - e->e_class * WkClassFact
96038032Speter				 + e->e_nrcpts * WkRecipFact;
96138032Speter	}
96238032Speter
963132946Sgshapiro	/* check for DSN to properly set e_timeoutclass */
964132946Sgshapiro	p = hvalue("content-type", e->e_header);
965132946Sgshapiro	if (p != NULL)
966132946Sgshapiro	{
967132946Sgshapiro		bool oldsupr;
968132946Sgshapiro		char **pvp;
969132946Sgshapiro		char pvpbuf[MAXLINE];
970132946Sgshapiro		extern unsigned char MimeTokenTab[256];
971132946Sgshapiro
972132946Sgshapiro		/* tokenize header */
973132946Sgshapiro		oldsupr = SuprErrs;
974132946Sgshapiro		SuprErrs = true;
975168520Sgshapiro		pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL,
976132946Sgshapiro			      MimeTokenTab, false);
977132946Sgshapiro		SuprErrs = oldsupr;
978132946Sgshapiro
979132946Sgshapiro		/* Check if multipart/report */
980132946Sgshapiro		if (pvp != NULL && pvp[0] != NULL &&
981132946Sgshapiro		    pvp[1] != NULL && pvp[2] != NULL &&
982132946Sgshapiro		    sm_strcasecmp(*pvp++, "multipart") == 0 &&
983132946Sgshapiro		    strcmp(*pvp++, "/") == 0 &&
984132946Sgshapiro		    sm_strcasecmp(*pvp++, "report") == 0)
985132946Sgshapiro		{
986132946Sgshapiro			/* Look for report-type=delivery-status */
987132946Sgshapiro			while (*pvp != NULL)
988132946Sgshapiro			{
989132946Sgshapiro				/* skip to semicolon separator */
990132946Sgshapiro				while (*pvp != NULL && strcmp(*pvp, ";") != 0)
991132946Sgshapiro					pvp++;
992132946Sgshapiro
993132946Sgshapiro				/* skip semicolon */
994132946Sgshapiro				if (*pvp++ == NULL || *pvp == NULL)
995132946Sgshapiro					break;
996132946Sgshapiro
997132946Sgshapiro				/* look for report-type */
998132946Sgshapiro				if (sm_strcasecmp(*pvp++, "report-type") != 0)
999132946Sgshapiro					continue;
1000132946Sgshapiro
1001132946Sgshapiro				/* skip equal */
1002132946Sgshapiro				if (*pvp == NULL || strcmp(*pvp, "=") != 0)
1003132946Sgshapiro					continue;
1004132946Sgshapiro
1005132946Sgshapiro				/* check value */
1006132946Sgshapiro				if (*++pvp != NULL &&
1007132946Sgshapiro				    sm_strcasecmp(*pvp,
1008132946Sgshapiro						  "delivery-status") == 0)
1009132946Sgshapiro					e->e_timeoutclass = TOC_DSN;
1010132946Sgshapiro
1011132946Sgshapiro				/* found report-type, no need to continue */
1012132946Sgshapiro				break;
1013132946Sgshapiro			}
1014132946Sgshapiro		}
1015132946Sgshapiro	}
1016132946Sgshapiro
101738032Speter	/* message timeout priority */
101838032Speter	p = hvalue("priority", e->e_header);
101938032Speter	if (p != NULL)
102038032Speter	{
102138032Speter		/* (this should be in the configuration file) */
102290795Sgshapiro		if (sm_strcasecmp(p, "urgent") == 0)
102338032Speter			e->e_timeoutclass = TOC_URGENT;
102490795Sgshapiro		else if (sm_strcasecmp(p, "normal") == 0)
102538032Speter			e->e_timeoutclass = TOC_NORMAL;
102690795Sgshapiro		else if (sm_strcasecmp(p, "non-urgent") == 0)
102738032Speter			e->e_timeoutclass = TOC_NONURGENT;
1028125823Sgshapiro		else if (bitset(EF_RESPONSE, e->e_flags))
1029125823Sgshapiro			e->e_timeoutclass = TOC_DSN;
103038032Speter	}
1031125823Sgshapiro	else if (bitset(EF_RESPONSE, e->e_flags))
1032112813Sgshapiro		e->e_timeoutclass = TOC_DSN;
1033112813Sgshapiro
103438032Speter	/* date message originated */
103538032Speter	p = hvalue("posted-date", e->e_header);
103638032Speter	if (p == NULL)
103738032Speter		p = hvalue("date", e->e_header);
103838032Speter	if (p != NULL)
103990795Sgshapiro		macdefine(&e->e_macro, A_PERM, 'a', p);
104038032Speter
104138032Speter	/* check to see if this is a MIME message */
104238032Speter	if ((e->e_bodytype != NULL &&
104390795Sgshapiro	     sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) ||
104438032Speter	    hvalue("MIME-Version", e->e_header) != NULL)
104538032Speter	{
104638032Speter		e->e_flags |= EF_IS_MIME;
104738032Speter		if (HasEightBits)
104838032Speter			e->e_bodytype = "8BITMIME";
104938032Speter	}
105038032Speter	else if ((p = hvalue("Content-Type", e->e_header)) != NULL)
105138032Speter	{
105238032Speter		/* this may be an RFC 1049 message */
105338032Speter		p = strpbrk(p, ";/");
105438032Speter		if (p == NULL || *p == ';')
105538032Speter		{
105638032Speter			/* yep, it is */
105738032Speter			e->e_flags |= EF_DONT_MIME;
105838032Speter		}
105938032Speter	}
106038032Speter
106138032Speter	/*
106238032Speter	**  From person in antiquated ARPANET mode
106338032Speter	**	required by UK Grey Book e-mail gateways (sigh)
106438032Speter	*/
106538032Speter
106638032Speter	if (OpMode == MD_ARPAFTP)
106738032Speter	{
106838032Speter		register struct hdrinfo *hi;
106938032Speter
107038032Speter		for (hi = HdrInfo; hi->hi_field != NULL; hi++)
107138032Speter		{
107238032Speter			if (bitset(H_FROM, hi->hi_flags) &&
107338032Speter			    (!bitset(H_RESENT, hi->hi_flags) ||
107438032Speter			     bitset(EF_RESENT, e->e_flags)) &&
107538032Speter			    (p = hvalue(hi->hi_field, e->e_header)) != NULL)
107638032Speter				break;
107738032Speter		}
107838032Speter		if (hi->hi_field != NULL)
107938032Speter		{
108038032Speter			if (tTd(32, 2))
108190795Sgshapiro				sm_dprintf("eatheader: setsender(*%s == %s)\n",
108238032Speter					hi->hi_field, p);
108390795Sgshapiro			setsender(p, e, NULL, '\0', true);
108438032Speter		}
108538032Speter	}
108638032Speter
108738032Speter	/*
108838032Speter	**  Log collection information.
108938032Speter	*/
109038032Speter
1091203004Sgshapiro	if (tTd(92, 2))
1092203004Sgshapiro		sm_dprintf("eatheader: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d, log=%d\n",
1093203004Sgshapiro			e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel,
1094203004Sgshapiro			log);
109590795Sgshapiro	if (log && bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
109690795Sgshapiro	{
109790795Sgshapiro		logsender(e, e->e_msgid);
109890795Sgshapiro		e->e_flags &= ~EF_LOGSENDER;
109990795Sgshapiro	}
110038032Speter}
1101168520Sgshapiro
110290795Sgshapiro/*
110338032Speter**  LOGSENDER -- log sender information
110438032Speter**
110538032Speter**	Parameters:
110638032Speter**		e -- the envelope to log
110738032Speter**		msgid -- the message id
110838032Speter**
110938032Speter**	Returns:
111038032Speter**		none
111138032Speter*/
111238032Speter
111338032Spetervoid
111438032Speterlogsender(e, msgid)
111538032Speter	register ENVELOPE *e;
111638032Speter	char *msgid;
111738032Speter{
111838032Speter	char *name;
111938032Speter	register char *sbp;
112038032Speter	register char *p;
112138032Speter	char hbuf[MAXNAME + 1];
112238032Speter	char sbuf[MAXLINE + 1];
112338032Speter	char mbuf[MAXNAME + 1];
112438032Speter
112538032Speter	/* don't allow newlines in the message-id */
112690795Sgshapiro	/* XXX do we still need this? sm_syslog() replaces control chars */
112738032Speter	if (msgid != NULL)
112838032Speter	{
1129157006Sgshapiro		size_t l;
1130157006Sgshapiro
113138032Speter		l = strlen(msgid);
1132168520Sgshapiro		if (l > sizeof(mbuf) - 1)
1133168520Sgshapiro			l = sizeof(mbuf) - 1;
113464565Sgshapiro		memmove(mbuf, msgid, l);
113538032Speter		mbuf[l] = '\0';
113638032Speter		p = mbuf;
113738032Speter		while ((p = strchr(p, '\n')) != NULL)
113838032Speter			*p++ = ' ';
113938032Speter	}
114038032Speter
114138032Speter	if (bitset(EF_RESPONSE, e->e_flags))
114238032Speter		name = "[RESPONSE]";
114338032Speter	else if ((name = macvalue('_', e)) != NULL)
114464565Sgshapiro		/* EMPTY */
114538032Speter		;
114638032Speter	else if (RealHostName == NULL)
114738032Speter		name = "localhost";
114838032Speter	else if (RealHostName[0] == '[')
114938032Speter		name = RealHostName;
115038032Speter	else
115138032Speter	{
115238032Speter		name = hbuf;
1153168520Sgshapiro		(void) sm_snprintf(hbuf, sizeof(hbuf), "%.80s", RealHostName);
115438032Speter		if (RealHostAddr.sa.sa_family != 0)
115538032Speter		{
115638032Speter			p = &hbuf[strlen(hbuf)];
115790795Sgshapiro			(void) sm_snprintf(p, SPACELEFT(hbuf, p),
115890795Sgshapiro					   " (%.100s)",
115990795Sgshapiro					   anynet_ntoa(&RealHostAddr));
116038032Speter		}
116138032Speter	}
116238032Speter
116338032Speter	/* some versions of syslog only take 5 printf args */
116464565Sgshapiro#if (SYSLOG_BUFSIZE) >= 256
116538032Speter	sbp = sbuf;
116690795Sgshapiro	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
116790795Sgshapiro		"from=%.200s, size=%ld, class=%d, nrcpts=%d",
116890795Sgshapiro		e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr,
1169244833Sgshapiro		PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
117038032Speter	sbp += strlen(sbp);
117138032Speter	if (msgid != NULL)
117238032Speter	{
117390795Sgshapiro		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
117490795Sgshapiro				", msgid=%.100s", mbuf);
117538032Speter		sbp += strlen(sbp);
117638032Speter	}
117738032Speter	if (e->e_bodytype != NULL)
117838032Speter	{
117990795Sgshapiro		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
118090795Sgshapiro				", bodytype=%.20s", e->e_bodytype);
118138032Speter		sbp += strlen(sbp);
118238032Speter	}
118338032Speter	p = macvalue('r', e);
118438032Speter	if (p != NULL)
118564565Sgshapiro	{
118690795Sgshapiro		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
118790795Sgshapiro				", proto=%.20s", p);
118864565Sgshapiro		sbp += strlen(sbp);
118964565Sgshapiro	}
119090795Sgshapiro	p = macvalue(macid("{daemon_name}"), e);
119164565Sgshapiro	if (p != NULL)
119264565Sgshapiro	{
119390795Sgshapiro		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
119490795Sgshapiro				", daemon=%.20s", p);
119564565Sgshapiro		sbp += strlen(sbp);
119664565Sgshapiro	}
1197285303Sgshapiro# if _FFR_LOG_MORE1
1198285303Sgshapiro#  if STARTTLS
1199285303Sgshapiro	p = macvalue(macid("{verify}"), e);
1200285303Sgshapiro	if (p == NULL || *p == '\0')
1201285303Sgshapiro		p = "NONE";
1202285303Sgshapiro	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", tls_verify=%.20s", p);
1203285303Sgshapiro	sbp += strlen(sbp);
1204285303Sgshapiro#  endif /* STARTTLS */
1205285303Sgshapiro#  if SASL
1206285303Sgshapiro	p = macvalue(macid("{auth_type}"), e);
1207285303Sgshapiro	if (p == NULL || *p == '\0')
1208285303Sgshapiro		p = "NONE";
1209285303Sgshapiro	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", auth=%.20s", p);
1210285303Sgshapiro	sbp += strlen(sbp);
1211285303Sgshapiro#  endif /* SASL */
1212285303Sgshapiro# endif /* _FFR_LOG_MORE1 */
1213110563Sgshapiro	sm_syslog(LOG_INFO, e->e_id, "%.850s, relay=%s", sbuf, name);
121438032Speter
121564565Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */
121638032Speter
121738032Speter	sm_syslog(LOG_INFO, e->e_id,
121864565Sgshapiro		  "from=%s",
121964565Sgshapiro		  e->e_from.q_paddr == NULL ? "<NONE>"
122090795Sgshapiro					    : shortenstring(e->e_from.q_paddr,
122190795Sgshapiro							    83));
122238032Speter	sm_syslog(LOG_INFO, e->e_id,
122364565Sgshapiro		  "size=%ld, class=%ld, nrcpts=%d",
1224244833Sgshapiro		  PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
122538032Speter	if (msgid != NULL)
122638032Speter		sm_syslog(LOG_INFO, e->e_id,
122764565Sgshapiro			  "msgid=%s",
122864565Sgshapiro			  shortenstring(mbuf, 83));
122938032Speter	sbp = sbuf;
123038032Speter	*sbp = '\0';
123138032Speter	if (e->e_bodytype != NULL)
123238032Speter	{
123390795Sgshapiro		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
123490795Sgshapiro				"bodytype=%.20s, ", e->e_bodytype);
123538032Speter		sbp += strlen(sbp);
123638032Speter	}
123738032Speter	p = macvalue('r', e);
123838032Speter	if (p != NULL)
123938032Speter	{
124090795Sgshapiro		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
124190795Sgshapiro				"proto=%.20s, ", p);
124238032Speter		sbp += strlen(sbp);
124338032Speter	}
124438032Speter	sm_syslog(LOG_INFO, e->e_id,
1245110563Sgshapiro		  "%.400srelay=%s", sbuf, name);
124664565Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */
124738032Speter}
1248168520Sgshapiro
124990795Sgshapiro/*
125038032Speter**  PRIENCODE -- encode external priority names into internal values.
125138032Speter**
125238032Speter**	Parameters:
125338032Speter**		p -- priority in ascii.
125438032Speter**
125538032Speter**	Returns:
125638032Speter**		priority as a numeric level.
125738032Speter**
125838032Speter**	Side Effects:
125938032Speter**		none.
126038032Speter*/
126138032Speter
126264565Sgshapirostatic int
126338032Speterpriencode(p)
126438032Speter	char *p;
126538032Speter{
126638032Speter	register int i;
126738032Speter
126838032Speter	for (i = 0; i < NumPriorities; i++)
126938032Speter	{
127090795Sgshapiro		if (sm_strcasecmp(p, Priorities[i].pri_name) == 0)
127164565Sgshapiro			return Priorities[i].pri_val;
127238032Speter	}
127338032Speter
127438032Speter	/* unknown priority */
127564565Sgshapiro	return 0;
127638032Speter}
1277168520Sgshapiro
127890795Sgshapiro/*
127938032Speter**  CRACKADDR -- parse an address and turn it into a macro
128038032Speter**
128138032Speter**	This doesn't actually parse the address -- it just extracts
128238032Speter**	it and replaces it with "$g".  The parse is totally ad hoc
128338032Speter**	and isn't even guaranteed to leave something syntactically
128438032Speter**	identical to what it started with.  However, it does leave
1285111826Sgshapiro**	something semantically identical if possible, else at least
1286111826Sgshapiro**	syntactically correct.
128738032Speter**
1288111826Sgshapiro**	For example, it changes "Real Name <real@example.com> (Comment)"
1289111826Sgshapiro**	to "Real Name <$g> (Comment)".
1290111826Sgshapiro**
129138032Speter**	This algorithm has been cleaned up to handle a wider range
129238032Speter**	of cases -- notably quoted and backslash escaped strings.
129338032Speter**	This modification makes it substantially better at preserving
129438032Speter**	the original syntax.
129538032Speter**
129638032Speter**	Parameters:
129738032Speter**		addr -- the address to be cracked.
1298111826Sgshapiro**		e -- the current envelope.
129938032Speter**
130038032Speter**	Returns:
130138032Speter**		a pointer to the new version.
130238032Speter**
130338032Speter**	Side Effects:
130438032Speter**		none.
130538032Speter**
130638032Speter**	Warning:
130738032Speter**		The return value is saved in local storage and should
130838032Speter**		be copied if it is to be reused.
130938032Speter*/
131038032Speter
1311111826Sgshapiro#define SM_HAVE_ROOM		((bp < buflim) && (buflim <= bufend))
1312111826Sgshapiro
1313111826Sgshapiro/*
1314111826Sgshapiro**  Append a character to bp if we have room.
1315111826Sgshapiro**  If not, punt and return $g.
1316111826Sgshapiro*/
1317111826Sgshapiro
1318111826Sgshapiro#define SM_APPEND_CHAR(c)					\
1319111826Sgshapiro	do							\
1320111826Sgshapiro	{							\
1321111826Sgshapiro		if (SM_HAVE_ROOM)				\
1322111826Sgshapiro			*bp++ = (c);				\
1323111826Sgshapiro		else						\
1324111826Sgshapiro			goto returng;				\
1325111826Sgshapiro	} while (0)
1326111826Sgshapiro
1327111826Sgshapiro#if MAXNAME < 10
1328111826SgshapiroERROR MAXNAME must be at least 10
1329111826Sgshapiro#endif /* MAXNAME < 10 */
1330111826Sgshapiro
133138032Speterchar *
1332111826Sgshapirocrackaddr(addr, e)
133338032Speter	register char *addr;
1334111826Sgshapiro	ENVELOPE *e;
133538032Speter{
133638032Speter	register char *p;
133738032Speter	register char c;
1338111826Sgshapiro	int cmtlev;			/* comment level in input string */
1339111826Sgshapiro	int realcmtlev;			/* comment level in output string */
1340111826Sgshapiro	int anglelev;			/* angle level in input string */
1341111826Sgshapiro	int copylev;			/* 0 == in address, >0 copying */
1342111826Sgshapiro	int bracklev;			/* bracket level for IPv6 addr check */
1343111826Sgshapiro	bool addangle;			/* put closing angle in output */
1344111826Sgshapiro	bool qmode;			/* quoting in original string? */
1345111826Sgshapiro	bool realqmode;			/* quoting in output string? */
1346111826Sgshapiro	bool putgmac = false;		/* already wrote $g */
1347111826Sgshapiro	bool quoteit = false;		/* need to quote next character */
1348111826Sgshapiro	bool gotangle = false;		/* found first '<' */
1349111826Sgshapiro	bool gotcolon = false;		/* found a ':' */
135038032Speter	register char *bp;
135138032Speter	char *buflim;
135238032Speter	char *bufhead;
135338032Speter	char *addrhead;
1354111826Sgshapiro	char *bufend;
135538032Speter	static char buf[MAXNAME + 1];
135638032Speter
135738032Speter	if (tTd(33, 1))
135890795Sgshapiro		sm_dprintf("crackaddr(%s)\n", addr);
135938032Speter
1360168520Sgshapiro	buflim = bufend = &buf[sizeof(buf) - 1];
1361168520Sgshapiro	bp = bufhead = buf;
1362168520Sgshapiro
1363168520Sgshapiro	/* skip over leading spaces but preserve them */
136438032Speter	while (*addr != '\0' && isascii(*addr) && isspace(*addr))
1365168520Sgshapiro	{
1366168520Sgshapiro		SM_APPEND_CHAR(*addr);
136738032Speter		addr++;
1368168520Sgshapiro	}
1369168520Sgshapiro	bufhead = bp;
137038032Speter
137138032Speter	/*
137238032Speter	**  Start by assuming we have no angle brackets.  This will be
137338032Speter	**  adjusted later if we find them.
137438032Speter	*/
137538032Speter
137638032Speter	p = addrhead = addr;
1377111826Sgshapiro	copylev = anglelev = cmtlev = realcmtlev = 0;
137838032Speter	bracklev = 0;
1379111826Sgshapiro	qmode = realqmode = addangle = false;
138038032Speter
138138032Speter	while ((c = *p++) != '\0')
138238032Speter	{
138338032Speter		/*
1384111826Sgshapiro		**  Try to keep legal syntax using spare buffer space
1385111826Sgshapiro		**  (maintained by buflim).
138638032Speter		*/
138738032Speter
1388111826Sgshapiro		if (copylev > 0)
1389111826Sgshapiro			SM_APPEND_CHAR(c);
139038032Speter
139138032Speter		/* check for backslash escapes */
139238032Speter		if (c == '\\')
139338032Speter		{
139438032Speter			/* arrange to quote the address */
139538032Speter			if (cmtlev <= 0 && !qmode)
139690795Sgshapiro				quoteit = true;
139738032Speter
139838032Speter			if ((c = *p++) == '\0')
139938032Speter			{
140038032Speter				/* too far */
140138032Speter				p--;
140238032Speter				goto putg;
140338032Speter			}
1404111826Sgshapiro			if (copylev > 0)
1405111826Sgshapiro				SM_APPEND_CHAR(c);
140638032Speter			goto putg;
140738032Speter		}
140838032Speter
140938032Speter		/* check for quoted strings */
141038032Speter		if (c == '"' && cmtlev <= 0)
141138032Speter		{
141238032Speter			qmode = !qmode;
1413111826Sgshapiro			if (copylev > 0 && SM_HAVE_ROOM)
1414111826Sgshapiro			{
1415111826Sgshapiro				if (realqmode)
1416111826Sgshapiro					buflim--;
1417111826Sgshapiro				else
1418111826Sgshapiro					buflim++;
141938032Speter				realqmode = !realqmode;
1420111826Sgshapiro			}
142138032Speter			continue;
142238032Speter		}
142338032Speter		if (qmode)
142438032Speter			goto putg;
142538032Speter
142638032Speter		/* check for comments */
142738032Speter		if (c == '(')
142838032Speter		{
142938032Speter			cmtlev++;
143038032Speter
143138032Speter			/* allow space for closing paren */
1432111826Sgshapiro			if (SM_HAVE_ROOM)
143338032Speter			{
143438032Speter				buflim--;
143538032Speter				realcmtlev++;
143638032Speter				if (copylev++ <= 0)
143738032Speter				{
143838032Speter					if (bp != bufhead)
1439111826Sgshapiro						SM_APPEND_CHAR(' ');
1440111826Sgshapiro					SM_APPEND_CHAR(c);
144138032Speter				}
144238032Speter			}
144338032Speter		}
144438032Speter		if (cmtlev > 0)
144538032Speter		{
144638032Speter			if (c == ')')
144738032Speter			{
144838032Speter				cmtlev--;
144938032Speter				copylev--;
1450111826Sgshapiro				if (SM_HAVE_ROOM)
145138032Speter				{
145238032Speter					realcmtlev--;
145338032Speter					buflim++;
145438032Speter				}
145538032Speter			}
145638032Speter			continue;
145738032Speter		}
145838032Speter		else if (c == ')')
145938032Speter		{
146038032Speter			/* syntax error: unmatched ) */
1461120259Sgshapiro			if (copylev > 0 && SM_HAVE_ROOM && bp > bufhead)
146238032Speter				bp--;
146338032Speter		}
146438032Speter
146538032Speter		/* count nesting on [ ... ] (for IPv6 domain literals) */
146638032Speter		if (c == '[')
146738032Speter			bracklev++;
146838032Speter		else if (c == ']')
146938032Speter			bracklev--;
147038032Speter
147138032Speter		/* check for group: list; syntax */
147238032Speter		if (c == ':' && anglelev <= 0 && bracklev <= 0 &&
147338032Speter		    !gotcolon && !ColonOkInAddr)
147438032Speter		{
147538032Speter			register char *q;
147638032Speter
147738032Speter			/*
147838032Speter			**  Check for DECnet phase IV ``::'' (host::user)
1479111826Sgshapiro			**  or DECnet phase V ``:.'' syntaxes.  The latter
148038032Speter			**  covers ``user@DEC:.tay.myhost'' and
148138032Speter			**  ``DEC:.tay.myhost::user'' syntaxes (bletch).
148238032Speter			*/
148338032Speter
148438032Speter			if (*p == ':' || *p == '.')
148538032Speter			{
148638032Speter				if (cmtlev <= 0 && !qmode)
148790795Sgshapiro					quoteit = true;
1488111826Sgshapiro				if (copylev > 0)
148938032Speter				{
1490111826Sgshapiro					SM_APPEND_CHAR(c);
1491111826Sgshapiro					SM_APPEND_CHAR(*p);
149238032Speter				}
149338032Speter				p++;
149438032Speter				goto putg;
149538032Speter			}
149638032Speter
149790795Sgshapiro			gotcolon = true;
149838032Speter
149938032Speter			bp = bufhead;
150038032Speter			if (quoteit)
150138032Speter			{
1502111826Sgshapiro				SM_APPEND_CHAR('"');
150338032Speter
150438032Speter				/* back up over the ':' and any spaces */
150538032Speter				--p;
1506111826Sgshapiro				while (p > addr &&
1507111826Sgshapiro				       isascii(*--p) && isspace(*p))
150838032Speter					continue;
150938032Speter				p++;
151038032Speter			}
151138032Speter			for (q = addrhead; q < p; )
151238032Speter			{
151338032Speter				c = *q++;
1514111826Sgshapiro				if (quoteit && c == '"')
1515111826Sgshapiro					SM_APPEND_CHAR('\\');
1516132946Sgshapiro				SM_APPEND_CHAR(c);
151738032Speter			}
151838032Speter			if (quoteit)
151938032Speter			{
152038032Speter				if (bp == &bufhead[1])
152138032Speter					bp--;
152238032Speter				else
1523111826Sgshapiro					SM_APPEND_CHAR('"');
152438032Speter				while ((c = *p++) != ':')
1525111826Sgshapiro					SM_APPEND_CHAR(c);
1526111826Sgshapiro				SM_APPEND_CHAR(c);
152738032Speter			}
152838032Speter
152938032Speter			/* any trailing white space is part of group: */
1530111826Sgshapiro			while (isascii(*p) && isspace(*p))
1531111826Sgshapiro			{
1532111826Sgshapiro				SM_APPEND_CHAR(*p);
1533111826Sgshapiro				p++;
1534111826Sgshapiro			}
153538032Speter			copylev = 0;
153690795Sgshapiro			putgmac = quoteit = false;
153738032Speter			bufhead = bp;
153838032Speter			addrhead = p;
153938032Speter			continue;
154038032Speter		}
154138032Speter
154238032Speter		if (c == ';' && copylev <= 0 && !ColonOkInAddr)
1543111826Sgshapiro			SM_APPEND_CHAR(c);
154438032Speter
154538032Speter		/* check for characters that may have to be quoted */
154638032Speter		if (strchr(MustQuoteChars, c) != NULL)
154738032Speter		{
154838032Speter			/*
154938032Speter			**  If these occur as the phrase part of a <>
155038032Speter			**  construct, but are not inside of () or already
155138032Speter			**  quoted, they will have to be quoted.  Note that
155238032Speter			**  now (but don't actually do the quoting).
155338032Speter			*/
155438032Speter
155538032Speter			if (cmtlev <= 0 && !qmode)
155690795Sgshapiro				quoteit = true;
155738032Speter		}
155838032Speter
155938032Speter		/* check for angle brackets */
156038032Speter		if (c == '<')
156138032Speter		{
156238032Speter			register char *q;
156338032Speter
156438032Speter			/* assume first of two angles is bogus */
156538032Speter			if (gotangle)
156690795Sgshapiro				quoteit = true;
156790795Sgshapiro			gotangle = true;
156838032Speter
156938032Speter			/* oops -- have to change our mind */
157038032Speter			anglelev = 1;
1571111826Sgshapiro			if (SM_HAVE_ROOM)
1572111826Sgshapiro			{
1573111826Sgshapiro				if (!addangle)
1574111826Sgshapiro					buflim--;
1575111826Sgshapiro				addangle = true;
1576111826Sgshapiro			}
157738032Speter
157838032Speter			bp = bufhead;
157938032Speter			if (quoteit)
158038032Speter			{
1581111826Sgshapiro				SM_APPEND_CHAR('"');
158238032Speter
158338032Speter				/* back up over the '<' and any spaces */
158438032Speter				--p;
1585111826Sgshapiro				while (p > addr &&
1586111826Sgshapiro				       isascii(*--p) && isspace(*p))
158738032Speter					continue;
158838032Speter				p++;
158938032Speter			}
159038032Speter			for (q = addrhead; q < p; )
159138032Speter			{
159238032Speter				c = *q++;
1593111826Sgshapiro				if (quoteit && c == '"')
159438032Speter				{
1595111826Sgshapiro					SM_APPEND_CHAR('\\');
1596111826Sgshapiro					SM_APPEND_CHAR(c);
159738032Speter				}
1598111826Sgshapiro				else
1599111826Sgshapiro					SM_APPEND_CHAR(c);
160038032Speter			}
160138032Speter			if (quoteit)
160238032Speter			{
160338032Speter				if (bp == &buf[1])
160438032Speter					bp--;
160538032Speter				else
1606111826Sgshapiro					SM_APPEND_CHAR('"');
160738032Speter				while ((c = *p++) != '<')
1608111826Sgshapiro					SM_APPEND_CHAR(c);
1609111826Sgshapiro				SM_APPEND_CHAR(c);
161038032Speter			}
161138032Speter			copylev = 0;
161290795Sgshapiro			putgmac = quoteit = false;
161338032Speter			continue;
161438032Speter		}
161538032Speter
161638032Speter		if (c == '>')
161738032Speter		{
161838032Speter			if (anglelev > 0)
161938032Speter			{
162038032Speter				anglelev--;
1621111826Sgshapiro				if (SM_HAVE_ROOM)
162238032Speter				{
1623111826Sgshapiro					if (addangle)
1624111826Sgshapiro						buflim++;
1625111826Sgshapiro					addangle = false;
162638032Speter				}
162738032Speter			}
1628111826Sgshapiro			else if (SM_HAVE_ROOM)
162938032Speter			{
163038032Speter				/* syntax error: unmatched > */
1631120259Sgshapiro				if (copylev > 0 && bp > bufhead)
163238032Speter					bp--;
163390795Sgshapiro				quoteit = true;
163438032Speter				continue;
163538032Speter			}
163638032Speter			if (copylev++ <= 0)
1637111826Sgshapiro				SM_APPEND_CHAR(c);
163838032Speter			continue;
163938032Speter		}
164038032Speter
164138032Speter		/* must be a real address character */
164238032Speter	putg:
164338032Speter		if (copylev <= 0 && !putgmac)
164438032Speter		{
1645111826Sgshapiro			if (bp > buf && bp[-1] == ')')
1646111826Sgshapiro				SM_APPEND_CHAR(' ');
1647111826Sgshapiro			SM_APPEND_CHAR(MACROEXPAND);
1648111826Sgshapiro			SM_APPEND_CHAR('g');
164990795Sgshapiro			putgmac = true;
165038032Speter		}
165138032Speter	}
165238032Speter
165338032Speter	/* repair any syntactic damage */
1654111826Sgshapiro	if (realqmode && bp < bufend)
165538032Speter		*bp++ = '"';
1656111826Sgshapiro	while (realcmtlev-- > 0 && bp < bufend)
165738032Speter		*bp++ = ')';
1658111826Sgshapiro	if (addangle && bp < bufend)
165938032Speter		*bp++ = '>';
1660111826Sgshapiro	*bp = '\0';
1661111826Sgshapiro	if (bp < bufend)
1662111826Sgshapiro		goto success;
166338032Speter
1664111826Sgshapiro returng:
1665111826Sgshapiro	/* String too long, punt */
1666111826Sgshapiro	buf[0] = '<';
1667111826Sgshapiro	buf[1] = MACROEXPAND;
1668111826Sgshapiro	buf[2]= 'g';
1669111826Sgshapiro	buf[3] = '>';
1670111826Sgshapiro	buf[4]= '\0';
1671111826Sgshapiro	sm_syslog(LOG_ALERT, e->e_id,
1672111826Sgshapiro		  "Dropped invalid comments from header address");
1673111826Sgshapiro
1674111826Sgshapiro success:
167538032Speter	if (tTd(33, 1))
167638032Speter	{
167790795Sgshapiro		sm_dprintf("crackaddr=>`");
1678132946Sgshapiro		xputs(sm_debug_file(), buf);
167990795Sgshapiro		sm_dprintf("'\n");
168038032Speter	}
168164565Sgshapiro	return buf;
168238032Speter}
1683168520Sgshapiro
168490795Sgshapiro/*
168538032Speter**  PUTHEADER -- put the header part of a message from the in-core copy
168638032Speter**
168738032Speter**	Parameters:
168838032Speter**		mci -- the connection information.
168964565Sgshapiro**		hdr -- the header to put.
169038032Speter**		e -- envelope to use.
169143733Speter**		flags -- MIME conversion flags.
169238032Speter**
169338032Speter**	Returns:
1694159613Sgshapiro**		true iff header part was written successfully
169538032Speter**
169638032Speter**	Side Effects:
169738032Speter**		none.
169838032Speter*/
169938032Speter
1700157006Sgshapirobool
170143733Speterputheader(mci, hdr, e, flags)
170238032Speter	register MCI *mci;
170338032Speter	HDR *hdr;
170438032Speter	register ENVELOPE *e;
170543733Speter	int flags;
170638032Speter{
170738032Speter	register HDR *h;
170890795Sgshapiro	char buf[SM_MAX(MAXLINE,BUFSIZ)];
170938032Speter	char obuf[MAXLINE];
171038032Speter
171138032Speter	if (tTd(34, 1))
171290795Sgshapiro		sm_dprintf("--- putheader, mailer = %s ---\n",
171338032Speter			mci->mci_mailer->m_name);
171438032Speter
171538032Speter	/*
171638032Speter	**  If we're in MIME mode, we're not really in the header of the
171738032Speter	**  message, just the header of one of the parts of the body of
171838032Speter	**  the message.  Therefore MCIF_INHEADER should not be turned on.
171938032Speter	*/
172038032Speter
172138032Speter	if (!bitset(MCIF_INMIME, mci->mci_flags))
172238032Speter		mci->mci_flags |= MCIF_INHEADER;
172338032Speter
172438032Speter	for (h = hdr; h != NULL; h = h->h_link)
172538032Speter	{
172638032Speter		register char *p = h->h_value;
172790795Sgshapiro		char *q;
172838032Speter
172938032Speter		if (tTd(34, 11))
173038032Speter		{
1731168520Sgshapiro			sm_dprintf("  %s:", h->h_field);
1732132946Sgshapiro			xputs(sm_debug_file(), p);
173338032Speter		}
173438032Speter
173564565Sgshapiro		/* Skip empty headers */
173664565Sgshapiro		if (h->h_value == NULL)
173764565Sgshapiro			continue;
173864565Sgshapiro
173942580Speter		/* heuristic shortening of MIME fields to avoid MUA overflows */
174042580Speter		if (MaxMimeFieldLength > 0 &&
174142580Speter		    wordinclass(h->h_field,
174290795Sgshapiro				macid("{checkMIMEFieldHeaders}")))
174342580Speter		{
174471348Sgshapiro			size_t len;
174571348Sgshapiro
1746111826Sgshapiro			len = fix_mime_header(h, e);
174771348Sgshapiro			if (len > 0)
174842580Speter			{
174942580Speter				sm_syslog(LOG_ALERT, e->e_id,
175071348Sgshapiro					  "Truncated MIME %s header due to field size (length = %ld) (possible attack)",
175171348Sgshapiro					  h->h_field, (unsigned long) len);
175242580Speter				if (tTd(34, 11))
175390795Sgshapiro					sm_dprintf("  truncated MIME %s header due to field size  (length = %ld) (possible attack)\n",
175490795Sgshapiro						   h->h_field,
175590795Sgshapiro						   (unsigned long) len);
175642580Speter			}
175742580Speter		}
175842580Speter
175942580Speter		if (MaxMimeHeaderLength > 0 &&
176042580Speter		    wordinclass(h->h_field,
176190795Sgshapiro				macid("{checkMIMETextHeaders}")))
176242580Speter		{
176371348Sgshapiro			size_t len;
176471348Sgshapiro
176571348Sgshapiro			len = strlen(h->h_value);
176671348Sgshapiro			if (len > (size_t) MaxMimeHeaderLength)
176742580Speter			{
176842580Speter				h->h_value[MaxMimeHeaderLength - 1] = '\0';
176942580Speter				sm_syslog(LOG_ALERT, e->e_id,
177071348Sgshapiro					  "Truncated long MIME %s header (length = %ld) (possible attack)",
177171348Sgshapiro					  h->h_field, (unsigned long) len);
177242580Speter				if (tTd(34, 11))
177390795Sgshapiro					sm_dprintf("  truncated long MIME %s header (length = %ld) (possible attack)\n",
177490795Sgshapiro						   h->h_field,
177590795Sgshapiro						   (unsigned long) len);
177642580Speter			}
177742580Speter		}
177842580Speter
177942580Speter		if (MaxMimeHeaderLength > 0 &&
178042580Speter		    wordinclass(h->h_field,
178190795Sgshapiro				macid("{checkMIMEHeaders}")))
178242580Speter		{
178371348Sgshapiro			size_t len;
178471348Sgshapiro
178571348Sgshapiro			len = strlen(h->h_value);
178671348Sgshapiro			if (shorten_rfc822_string(h->h_value,
178771348Sgshapiro						  MaxMimeHeaderLength))
178842580Speter			{
1789111826Sgshapiro				if (len < MaxMimeHeaderLength)
1790111826Sgshapiro				{
1791111826Sgshapiro					/* we only rebalanced a bogus header */
1792111826Sgshapiro					sm_syslog(LOG_ALERT, e->e_id,
1793111826Sgshapiro						  "Fixed MIME %s header (possible attack)",
1794111826Sgshapiro						  h->h_field);
1795111826Sgshapiro					if (tTd(34, 11))
1796111826Sgshapiro						sm_dprintf("  fixed MIME %s header (possible attack)\n",
1797111826Sgshapiro							   h->h_field);
1798111826Sgshapiro				}
1799111826Sgshapiro				else
1800111826Sgshapiro				{
1801111826Sgshapiro					/* we actually shortened header */
1802111826Sgshapiro					sm_syslog(LOG_ALERT, e->e_id,
1803111826Sgshapiro						  "Truncated long MIME %s header (length = %ld) (possible attack)",
1804111826Sgshapiro						  h->h_field,
1805111826Sgshapiro						  (unsigned long) len);
1806111826Sgshapiro					if (tTd(34, 11))
1807111826Sgshapiro						sm_dprintf("  truncated long MIME %s header (length = %ld) (possible attack)\n",
1808111826Sgshapiro							   h->h_field,
1809111826Sgshapiro							   (unsigned long) len);
1810111826Sgshapiro				}
181142580Speter			}
181242580Speter		}
181342580Speter
181443733Speter		/*
181543733Speter		**  Suppress Content-Transfer-Encoding: if we are MIMEing
181643733Speter		**  and we are potentially converting from 8 bit to 7 bit
181743733Speter		**  MIME.  If converting, add a new CTE header in
181843733Speter		**  mime8to7().
181943733Speter		*/
182090795Sgshapiro
182138032Speter		if (bitset(H_CTE, h->h_flags) &&
182243733Speter		    bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME,
182343733Speter			   mci->mci_flags) &&
182443733Speter		    !bitset(M87F_NO8TO7, flags))
182538032Speter		{
182638032Speter			if (tTd(34, 11))
182790795Sgshapiro				sm_dprintf(" (skipped (content-transfer-encoding))\n");
182838032Speter			continue;
182938032Speter		}
183038032Speter
183138032Speter		if (bitset(MCIF_INMIME, mci->mci_flags))
183238032Speter		{
183338032Speter			if (tTd(34, 11))
183490795Sgshapiro				sm_dprintf("\n");
1835157006Sgshapiro			if (!put_vanilla_header(h, p, mci))
1836157006Sgshapiro				goto writeerr;
183738032Speter			continue;
183838032Speter		}
183938032Speter
184038032Speter		if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
184164565Sgshapiro		    !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) &&
184264565Sgshapiro		    (h->h_macro == '\0' ||
184390795Sgshapiro		     (q = macvalue(bitidx(h->h_macro), e)) == NULL ||
184490795Sgshapiro		     *q == '\0'))
184538032Speter		{
184638032Speter			if (tTd(34, 11))
184790795Sgshapiro				sm_dprintf(" (skipped)\n");
184838032Speter			continue;
184938032Speter		}
185038032Speter
185138032Speter		/* handle Resent-... headers specially */
185238032Speter		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
185338032Speter		{
185438032Speter			if (tTd(34, 11))
185590795Sgshapiro				sm_dprintf(" (skipped (resent))\n");
185638032Speter			continue;
185738032Speter		}
185838032Speter
185938032Speter		/* suppress return receipts if requested */
186038032Speter		if (bitset(H_RECEIPTTO, h->h_flags) &&
186138032Speter		    (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags)))
186238032Speter		{
186338032Speter			if (tTd(34, 11))
186490795Sgshapiro				sm_dprintf(" (skipped (receipt))\n");
186538032Speter			continue;
186638032Speter		}
186738032Speter
186838032Speter		/* macro expand value if generated internally */
186964565Sgshapiro		if (bitset(H_DEFAULT, h->h_flags) ||
187064565Sgshapiro		    bitset(H_BINDLATE, h->h_flags))
187138032Speter		{
1872168520Sgshapiro			expand(p, buf, sizeof(buf), e);
187338032Speter			p = buf;
187438032Speter			if (*p == '\0')
187538032Speter			{
187638032Speter				if (tTd(34, 11))
187790795Sgshapiro					sm_dprintf(" (skipped -- null value)\n");
187838032Speter				continue;
187938032Speter			}
188038032Speter		}
188138032Speter
188238032Speter		if (bitset(H_BCC, h->h_flags))
188338032Speter		{
188438032Speter			/* Bcc: field -- either truncate or delete */
188538032Speter			if (bitset(EF_DELETE_BCC, e->e_flags))
188638032Speter			{
188738032Speter				if (tTd(34, 11))
188890795Sgshapiro					sm_dprintf(" (skipped -- bcc)\n");
188938032Speter			}
189038032Speter			else
189138032Speter			{
189238032Speter				/* no other recipient headers: truncate value */
1893168520Sgshapiro				(void) sm_strlcpyn(obuf, sizeof(obuf), 2,
189490795Sgshapiro						   h->h_field, ":");
1895157006Sgshapiro				if (!putline(obuf, mci))
1896157006Sgshapiro					goto writeerr;
189738032Speter			}
189838032Speter			continue;
189938032Speter		}
190038032Speter
190138032Speter		if (tTd(34, 11))
190290795Sgshapiro			sm_dprintf("\n");
190338032Speter
190438032Speter		if (bitset(H_FROM|H_RCPT, h->h_flags))
190538032Speter		{
190638032Speter			/* address field */
190738032Speter			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
190838032Speter
190938032Speter			if (bitset(H_FROM, h->h_flags))
191090795Sgshapiro				oldstyle = false;
1911285303Sgshapiro			if (!commaize(h, p, oldstyle, mci, e,
1912285303Sgshapiro				      PXLF_HEADER | PXLF_STRIPMQUOTE)
1913285303Sgshapiro			    && bitnset(M_xSMTP, mci->mci_mailer->m_flags))
1914285303Sgshapiro				goto writeerr;
191538032Speter		}
191638032Speter		else
191738032Speter		{
1918157006Sgshapiro			if (!put_vanilla_header(h, p, mci))
1919157006Sgshapiro				goto writeerr;
192038032Speter		}
192138032Speter	}
192238032Speter
192338032Speter	/*
192438032Speter	**  If we are converting this to a MIME message, add the
192564565Sgshapiro	**  MIME headers (but not in MIME mode!).
192638032Speter	*/
192738032Speter
192838032Speter#if MIME8TO7
192938032Speter	if (bitset(MM_MIME8BIT, MimeMode) &&
193038032Speter	    bitset(EF_HAS8BIT, e->e_flags) &&
193138032Speter	    !bitset(EF_DONT_MIME, e->e_flags) &&
193238032Speter	    !bitnset(M_8BITS, mci->mci_mailer->m_flags) &&
193364565Sgshapiro	    !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) &&
193464565Sgshapiro	    hvalue("MIME-Version", e->e_header) == NULL)
193538032Speter	{
1936157006Sgshapiro		if (!putline("MIME-Version: 1.0", mci))
1937157006Sgshapiro			goto writeerr;
193838032Speter		if (hvalue("Content-Type", e->e_header) == NULL)
193938032Speter		{
1940168520Sgshapiro			(void) sm_snprintf(obuf, sizeof(obuf),
194190795Sgshapiro					"Content-Type: text/plain; charset=%s",
194290795Sgshapiro					defcharset(e));
1943157006Sgshapiro			if (!putline(obuf, mci))
1944157006Sgshapiro				goto writeerr;
194538032Speter		}
1946157006Sgshapiro		if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL
1947157006Sgshapiro		    && !putline("Content-Transfer-Encoding: 8bit", mci))
1948157006Sgshapiro			goto writeerr;
194938032Speter	}
195064565Sgshapiro#endif /* MIME8TO7 */
1951157006Sgshapiro	return true;
1952157006Sgshapiro
1953157006Sgshapiro  writeerr:
1954157006Sgshapiro	return false;
195538032Speter}
1956168520Sgshapiro
195790795Sgshapiro/*
195838032Speter**  PUT_VANILLA_HEADER -- output a fairly ordinary header
195938032Speter**
196038032Speter**	Parameters:
196138032Speter**		h -- the structure describing this header
196238032Speter**		v -- the value of this header
196338032Speter**		mci -- the connection info for output
196438032Speter**
196538032Speter**	Returns:
1966159613Sgshapiro**		true iff header was written successfully
196738032Speter*/
196838032Speter
1969157006Sgshapirostatic bool
197038032Speterput_vanilla_header(h, v, mci)
197138032Speter	HDR *h;
197238032Speter	char *v;
197338032Speter	MCI *mci;
197438032Speter{
197538032Speter	register char *nlp;
197638032Speter	register char *obp;
197738032Speter	int putflags;
1978141862Sgshapiro	char obuf[MAXLINE + 256];	/* additional length for h_field */
197938032Speter
1980168520Sgshapiro	putflags = PXLF_HEADER | PXLF_STRIPMQUOTE;
198138032Speter	if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
198238032Speter		putflags |= PXLF_STRIP8BIT;
1983168520Sgshapiro	(void) sm_snprintf(obuf, sizeof(obuf), "%.200s:", h->h_field);
198438032Speter	obp = obuf + strlen(obuf);
198538032Speter	while ((nlp = strchr(v, '\n')) != NULL)
198638032Speter	{
198738032Speter		int l;
198838032Speter
198938032Speter		l = nlp - v;
1990120259Sgshapiro
1991120259Sgshapiro		/*
1992120259Sgshapiro		**  XXX This is broken for SPACELEFT()==0
1993120259Sgshapiro		**  However, SPACELEFT() is always > 0 unless MAXLINE==1.
1994120259Sgshapiro		*/
1995120259Sgshapiro
199690795Sgshapiro		if (SPACELEFT(obuf, obp) - 1 < (size_t) l)
199738032Speter			l = SPACELEFT(obuf, obp) - 1;
199838032Speter
199990795Sgshapiro		(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
2000157006Sgshapiro		if (!putxline(obuf, strlen(obuf), mci, putflags))
2001157006Sgshapiro			goto writeerr;
200238032Speter		v += l + 1;
200338032Speter		obp = obuf;
200438032Speter		if (*v != ' ' && *v != '\t')
200538032Speter			*obp++ = ' ';
200638032Speter	}
2007120259Sgshapiro
2008120259Sgshapiro	/* XXX This is broken for SPACELEFT()==0 */
200990795Sgshapiro	(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s",
201090795Sgshapiro			   (int) (SPACELEFT(obuf, obp) - 1), v);
2011157006Sgshapiro	return putxline(obuf, strlen(obuf), mci, putflags);
2012157006Sgshapiro
2013157006Sgshapiro  writeerr:
2014157006Sgshapiro	return false;
201538032Speter}
2016168520Sgshapiro
201790795Sgshapiro/*
201838032Speter**  COMMAIZE -- output a header field, making a comma-translated list.
201938032Speter**
202038032Speter**	Parameters:
202138032Speter**		h -- the header field to output.
202238032Speter**		p -- the value to put in it.
202390795Sgshapiro**		oldstyle -- true if this is an old style header.
202438032Speter**		mci -- the connection information.
202538032Speter**		e -- the envelope containing the message.
2026173343Sgshapiro**		putflags -- flags for putxline()
202738032Speter**
202838032Speter**	Returns:
2029159613Sgshapiro**		true iff header field was written successfully
203038032Speter**
203138032Speter**	Side Effects:
2032168520Sgshapiro**		outputs "p" to "mci".
203338032Speter*/
203438032Speter
2035157006Sgshapirobool
2036173343Sgshapirocommaize(h, p, oldstyle, mci, e, putflags)
203738032Speter	register HDR *h;
203838032Speter	register char *p;
203938032Speter	bool oldstyle;
204038032Speter	register MCI *mci;
204138032Speter	register ENVELOPE *e;
2042173343Sgshapiro	int putflags;
204338032Speter{
204438032Speter	register char *obp;
2045168520Sgshapiro	int opos, omax, spaces;
204690795Sgshapiro	bool firstone = true;
2047120259Sgshapiro	char **res;
204838032Speter	char obuf[MAXLINE + 3];
204938032Speter
205038032Speter	/*
205138032Speter	**  Output the address list translated by the
205238032Speter	**  mailer and with commas.
205338032Speter	*/
205438032Speter
205538032Speter	if (tTd(14, 2))
2056168520Sgshapiro		sm_dprintf("commaize(%s:%s)\n", h->h_field, p);
205738032Speter
205838032Speter	if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
205938032Speter		putflags |= PXLF_STRIP8BIT;
206038032Speter
206138032Speter	obp = obuf;
2062168520Sgshapiro	(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s:", h->h_field);
2063168520Sgshapiro	/* opos = strlen(obp); instead of the next 3 lines? */
2064168520Sgshapiro	opos = strlen(h->h_field) + 1;
2065168520Sgshapiro	if (opos > 201)
2066168520Sgshapiro		opos = 201;
2067168520Sgshapiro	obp += opos;
2068120259Sgshapiro
2069168520Sgshapiro	spaces = 0;
2070168520Sgshapiro	while (*p != '\0' && isascii(*p) && isspace(*p))
2071168520Sgshapiro	{
2072168520Sgshapiro		++spaces;
2073168520Sgshapiro		++p;
2074168520Sgshapiro	}
2075168520Sgshapiro	if (spaces > 0)
2076168520Sgshapiro	{
2077168520Sgshapiro		SM_ASSERT(sizeof(obuf) > opos  * 2);
2078168520Sgshapiro
2079168520Sgshapiro		/*
2080168520Sgshapiro		**  Restrict number of spaces to half the length of buffer
2081168520Sgshapiro		**  so the header field body can be put in here too.
2082168520Sgshapiro		**  Note: this is a hack...
2083168520Sgshapiro		*/
2084168520Sgshapiro
2085168520Sgshapiro		if (spaces > sizeof(obuf) / 2)
2086168520Sgshapiro			spaces = sizeof(obuf) / 2;
2087168520Sgshapiro		(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%*s", spaces,
2088168520Sgshapiro				"");
2089168520Sgshapiro		opos += spaces;
2090168520Sgshapiro		obp += spaces;
2091168520Sgshapiro		SM_ASSERT(obp < &obuf[MAXLINE]);
2092168520Sgshapiro	}
2093168520Sgshapiro
209438032Speter	omax = mci->mci_mailer->m_linelimit - 2;
209538032Speter	if (omax < 0 || omax > 78)
209638032Speter		omax = 78;
209738032Speter
209838032Speter	/*
209938032Speter	**  Run through the list of values.
210038032Speter	*/
210138032Speter
210238032Speter	while (*p != '\0')
210338032Speter	{
210438032Speter		register char *name;
210538032Speter		register int c;
210638032Speter		char savechar;
210738032Speter		int flags;
210864565Sgshapiro		auto int status;
210938032Speter
211038032Speter		/*
211138032Speter		**  Find the end of the name.  New style names
211238032Speter		**  end with a comma, old style names end with
211338032Speter		**  a space character.  However, spaces do not
211438032Speter		**  necessarily delimit an old-style name -- at
211538032Speter		**  signs mean keep going.
211638032Speter		*/
211738032Speter
211838032Speter		/* find end of name */
211938032Speter		while ((isascii(*p) && isspace(*p)) || *p == ',')
212038032Speter			p++;
212138032Speter		name = p;
2122120259Sgshapiro		res = NULL;
212338032Speter		for (;;)
212438032Speter		{
212538032Speter			auto char *oldp;
212638032Speter			char pvpbuf[PSBUFSIZE];
212738032Speter
2128120259Sgshapiro			res = prescan(p, oldstyle ? ' ' : ',', pvpbuf,
2129168520Sgshapiro				      sizeof(pvpbuf), &oldp, ExtTokenTab, false);
213038032Speter			p = oldp;
2131120259Sgshapiro#if _FFR_IGNORE_BOGUS_ADDR
2132120259Sgshapiro			/* ignore addresses that can't be parsed */
2133120259Sgshapiro			if (res == NULL)
2134120259Sgshapiro			{
2135120259Sgshapiro				name = p;
2136120259Sgshapiro				continue;
2137120259Sgshapiro			}
2138120259Sgshapiro#endif /* _FFR_IGNORE_BOGUS_ADDR */
213938032Speter
214038032Speter			/* look to see if we have an at sign */
214138032Speter			while (*p != '\0' && isascii(*p) && isspace(*p))
214238032Speter				p++;
214338032Speter
214438032Speter			if (*p != '@')
214538032Speter			{
214638032Speter				p = oldp;
214738032Speter				break;
214838032Speter			}
214990795Sgshapiro			++p;
215038032Speter			while (*p != '\0' && isascii(*p) && isspace(*p))
215138032Speter				p++;
215238032Speter		}
215338032Speter		/* at the end of one complete name */
215438032Speter
215538032Speter		/* strip off trailing white space */
215638032Speter		while (p >= name &&
215738032Speter		       ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
215838032Speter			p--;
215938032Speter		if (++p == name)
216038032Speter			continue;
2161120259Sgshapiro
2162120259Sgshapiro		/*
2163120259Sgshapiro		**  if prescan() failed go a bit backwards; this is a hack,
2164120259Sgshapiro		**  there should be some better error recovery.
2165120259Sgshapiro		*/
2166120259Sgshapiro
2167120259Sgshapiro		if (res == NULL && p > name &&
2168120259Sgshapiro		    !((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
2169120259Sgshapiro			--p;
217038032Speter		savechar = *p;
217138032Speter		*p = '\0';
217238032Speter
217338032Speter		/* translate the name to be relative */
217438032Speter		flags = RF_HEADERADDR|RF_ADDDOMAIN;
217538032Speter		if (bitset(H_FROM, h->h_flags))
217638032Speter			flags |= RF_SENDERADDR;
217738032Speter#if USERDB
217838032Speter		else if (e->e_from.q_mailer != NULL &&
217938032Speter			 bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags))
218038032Speter		{
218138032Speter			char *q;
218238032Speter
218390795Sgshapiro			q = udbsender(name, e->e_rpool);
218438032Speter			if (q != NULL)
218538032Speter				name = q;
218638032Speter		}
218764565Sgshapiro#endif /* USERDB */
218864565Sgshapiro		status = EX_OK;
218964565Sgshapiro		name = remotename(name, mci->mci_mailer, flags, &status, e);
2190285303Sgshapiro		if (status != EX_OK && bitnset(M_xSMTP, mci->mci_mailer->m_flags))
2191285303Sgshapiro		{
2192285303Sgshapiro			if (status == EX_TEMPFAIL)
2193285303Sgshapiro				mci->mci_flags |= MCIF_NOTSTICKY;
2194285303Sgshapiro			goto writeerr;
2195285303Sgshapiro		}
219638032Speter		if (*name == '\0')
219738032Speter		{
219838032Speter			*p = savechar;
219938032Speter			continue;
220038032Speter		}
220190795Sgshapiro		name = denlstring(name, false, true);
220238032Speter
220338032Speter		/* output the name with nice formatting */
220438032Speter		opos += strlen(name);
220538032Speter		if (!firstone)
220638032Speter			opos += 2;
220738032Speter		if (opos > omax && !firstone)
220838032Speter		{
220990795Sgshapiro			(void) sm_strlcpy(obp, ",\n", SPACELEFT(obuf, obp));
2210157006Sgshapiro			if (!putxline(obuf, strlen(obuf), mci, putflags))
2211157006Sgshapiro				goto writeerr;
221238032Speter			obp = obuf;
2213168520Sgshapiro			(void) sm_strlcpy(obp, "        ", sizeof(obuf));
221438032Speter			opos = strlen(obp);
221538032Speter			obp += opos;
221638032Speter			opos += strlen(name);
221738032Speter		}
221838032Speter		else if (!firstone)
221938032Speter		{
222090795Sgshapiro			(void) sm_strlcpy(obp, ", ", SPACELEFT(obuf, obp));
222138032Speter			obp += 2;
222238032Speter		}
222338032Speter
222438032Speter		while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
222538032Speter			*obp++ = c;
222690795Sgshapiro		firstone = false;
222738032Speter		*p = savechar;
222838032Speter	}
2229168520Sgshapiro	if (obp < &obuf[sizeof(obuf)])
2230120259Sgshapiro		*obp = '\0';
2231120259Sgshapiro	else
2232168520Sgshapiro		obuf[sizeof(obuf) - 1] = '\0';
2233157006Sgshapiro	return putxline(obuf, strlen(obuf), mci, putflags);
2234157006Sgshapiro
2235157006Sgshapiro  writeerr:
2236157006Sgshapiro	return false;
223738032Speter}
2238157006Sgshapiro
223990795Sgshapiro/*
224038032Speter**  COPYHEADER -- copy header list
224138032Speter**
224238032Speter**	This routine is the equivalent of newstr for header lists
224338032Speter**
224438032Speter**	Parameters:
224538032Speter**		header -- list of header structures to copy.
224690795Sgshapiro**		rpool -- resource pool, or NULL
224738032Speter**
224838032Speter**	Returns:
224938032Speter**		a copy of 'header'.
225038032Speter**
225138032Speter**	Side Effects:
225238032Speter**		none.
225338032Speter*/
225438032Speter
225538032SpeterHDR *
225690795Sgshapirocopyheader(header, rpool)
225738032Speter	register HDR *header;
225890795Sgshapiro	SM_RPOOL_T *rpool;
225938032Speter{
226038032Speter	register HDR *newhdr;
226138032Speter	HDR *ret;
226238032Speter	register HDR **tail = &ret;
226338032Speter
226438032Speter	while (header != NULL)
226538032Speter	{
2266168520Sgshapiro		newhdr = (HDR *) sm_rpool_malloc_x(rpool, sizeof(*newhdr));
226738032Speter		STRUCTCOPY(*header, *newhdr);
226838032Speter		*tail = newhdr;
226938032Speter		tail = &newhdr->h_link;
227038032Speter		header = header->h_link;
227138032Speter	}
227238032Speter	*tail = NULL;
227364565Sgshapiro
227438032Speter	return ret;
227538032Speter}
2276168520Sgshapiro
227790795Sgshapiro/*
227842580Speter**  FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header
227942580Speter**
228042580Speter**	Run through all of the parameters of a MIME header and
228142580Speter**	possibly truncate and rebalance the parameter according
228242580Speter**	to MaxMimeFieldLength.
228342580Speter**
228442580Speter**	Parameters:
2285111826Sgshapiro**		h -- the header to truncate/rebalance
2286111826Sgshapiro**		e -- the current envelope
228742580Speter**
228842580Speter**	Returns:
228971348Sgshapiro**		length of last offending field, 0 if all ok.
229042580Speter**
229142580Speter**	Side Effects:
229242580Speter**		string modified in place
229342580Speter*/
229442580Speter
229571348Sgshapirostatic size_t
2296111826Sgshapirofix_mime_header(h, e)
2297111826Sgshapiro	HDR *h;
2298111826Sgshapiro	ENVELOPE *e;
229942580Speter{
2300111826Sgshapiro	char *begin = h->h_value;
230142580Speter	char *end;
230271348Sgshapiro	size_t len = 0;
230371348Sgshapiro	size_t retlen = 0;
230464565Sgshapiro
2305111826Sgshapiro	if (begin == NULL || *begin == '\0')
230671348Sgshapiro		return 0;
230764565Sgshapiro
230842580Speter	/* Split on each ';' */
2309120259Sgshapiro	/* find_character() never returns NULL */
231042580Speter	while ((end = find_character(begin, ';')) != NULL)
231142580Speter	{
231242580Speter		char save = *end;
231342580Speter		char *bp;
231464565Sgshapiro
231542580Speter		*end = '\0';
231664565Sgshapiro
231771348Sgshapiro		len = strlen(begin);
231871348Sgshapiro
231942580Speter		/* Shorten individual parameter */
232042580Speter		if (shorten_rfc822_string(begin, MaxMimeFieldLength))
2321111826Sgshapiro		{
2322111826Sgshapiro			if (len < MaxMimeFieldLength)
2323111826Sgshapiro			{
2324111826Sgshapiro				/* we only rebalanced a bogus field */
2325111826Sgshapiro				sm_syslog(LOG_ALERT, e->e_id,
2326111826Sgshapiro					  "Fixed MIME %s header field (possible attack)",
2327111826Sgshapiro					  h->h_field);
2328111826Sgshapiro				if (tTd(34, 11))
2329111826Sgshapiro					sm_dprintf("  fixed MIME %s header field (possible attack)\n",
2330111826Sgshapiro						   h->h_field);
2331111826Sgshapiro			}
2332111826Sgshapiro			else
2333111826Sgshapiro			{
2334111826Sgshapiro				/* we actually shortened the header */
2335111826Sgshapiro				retlen = len;
2336111826Sgshapiro			}
2337111826Sgshapiro		}
233864565Sgshapiro
233942580Speter		/* Collapse the possibly shortened string with rest */
234042580Speter		bp = begin + strlen(begin);
234142580Speter		if (bp != end)
234242580Speter		{
234342580Speter			char *ep = end;
234464565Sgshapiro
234542580Speter			*end = save;
234642580Speter			end = bp;
234764565Sgshapiro
234842580Speter			/* copy character by character due to overlap */
234942580Speter			while (*ep != '\0')
235042580Speter				*bp++ = *ep++;
235142580Speter			*bp = '\0';
235242580Speter		}
235342580Speter		else
235442580Speter			*end = save;
235542580Speter		if (*end == '\0')
235642580Speter			break;
235764565Sgshapiro
235842580Speter		/* Move past ';' */
235942580Speter		begin = end + 1;
236042580Speter	}
236171348Sgshapiro	return retlen;
236242580Speter}
2363