1/*
2 * Copyright (c) 1998-2004, 2006, 2007 Proofpoint, 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#include <sm/sendmail.h>
16
17SM_RCSID("@(#)$Id: headers.c,v 8.320 2013-11-22 20:51:55 ca Exp $")
18
19static HDR	*allocheader __P((char *, char *, int, SM_RPOOL_T *, bool));
20static size_t	fix_mime_header __P((HDR *, ENVELOPE *));
21static int	priencode __P((char *));
22static bool	put_vanilla_header __P((HDR *, char *, MCI *));
23
24/*
25**  SETUPHEADERS -- initialize headers in symbol table
26**
27**	Parameters:
28**		none
29**
30**	Returns:
31**		none
32*/
33
34void
35setupheaders()
36{
37	struct hdrinfo *hi;
38	STAB *s;
39
40	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
41	{
42		s = stab(hi->hi_field, ST_HEADER, ST_ENTER);
43		s->s_header.hi_flags = hi->hi_flags;
44		s->s_header.hi_ruleset = NULL;
45	}
46}
47
48/*
49**  DOCHOMPHEADER -- process and save a header line.
50**
51**	Called by chompheader.
52**
53**	Parameters:
54**		line -- header as a text line.
55**		pflag -- flags for chompheader() (from sendmail.h)
56**		hdrp -- a pointer to the place to save the header.
57**		e -- the envelope including this header.
58**
59**	Returns:
60**		flags for this header.
61**
62**	Side Effects:
63**		The header is saved on the header list.
64**		Contents of 'line' are destroyed.
65*/
66
67static struct hdrinfo	NormalHeader =	{ NULL, 0, NULL };
68static unsigned long	dochompheader __P((char *, int, HDR **, ENVELOPE *));
69
70static unsigned long
71dochompheader(line, pflag, hdrp, e)
72	char *line;
73	int pflag;
74	HDR **hdrp;
75	ENVELOPE *e;
76{
77	unsigned char mid = '\0';
78	register char *p;
79	register HDR *h;
80	HDR **hp;
81	char *fname;
82	char *fvalue;
83	bool cond = false;
84	bool dropfrom;
85	bool headeronly;
86	STAB *s;
87	struct hdrinfo *hi;
88	bool nullheader = false;
89	BITMAP256 mopts;
90
91	headeronly = hdrp != NULL;
92	if (!headeronly)
93		hdrp = &e->e_header;
94
95	/* strip off options */
96	clrbitmap(mopts);
97	p = line;
98	if (!bitset(pflag, CHHDR_USER) && *p == '?')
99	{
100		int c;
101		register char *q;
102
103		q = strchr(++p, '?');
104		if (q == NULL)
105			goto hse;
106
107		*q = '\0';
108		c = *p & 0377;
109
110		/* possibly macro conditional */
111		if (c == MACROEXPAND)
112		{
113			/* catch ?$? */
114			if (*++p == '\0')
115			{
116				*q = '?';
117				goto hse;
118			}
119
120			mid = (unsigned char) *p++;
121
122			/* catch ?$abc? */
123			if (*p != '\0')
124			{
125				*q = '?';
126				goto hse;
127			}
128		}
129		else if (*p == '$')
130		{
131			/* catch ?$? */
132			if (*++p == '\0')
133			{
134				*q = '?';
135				goto hse;
136			}
137
138			mid = (unsigned char) macid(p);
139			if (bitset(0200, mid))
140			{
141				p += strlen(macname(mid)) + 2;
142				SM_ASSERT(p <= q);
143			}
144			else
145				p++;
146
147			/* catch ?$abc? */
148			if (*p != '\0')
149			{
150				*q = '?';
151				goto hse;
152			}
153		}
154		else
155		{
156			while (*p != '\0')
157			{
158				if (!isascii(*p))
159				{
160					*q = '?';
161					goto hse;
162				}
163
164				setbitn(bitidx(*p), mopts);
165				cond = true;
166				p++;
167			}
168		}
169		p = q + 1;
170	}
171
172	/* find canonical name */
173	fname = p;
174	while (isascii(*p) && isgraph(*p) && *p != ':')
175		p++;
176	fvalue = p;
177	while (SM_ISSPACE(*p))
178		p++;
179	if (*p++ != ':' || fname == fvalue)
180	{
181hse:
182		syserr("553 5.3.0 header syntax error, line \"%s\"", line);
183		return 0;
184	}
185	*fvalue = '\0';
186	fvalue = p;
187
188	/* if the field is null, go ahead and use the default */
189	while (SM_ISSPACE(*p))
190		p++;
191	if (*p == '\0')
192		nullheader = true;
193
194	/* security scan: long field names are end-of-header */
195	if (strlen(fname) > 100)
196		return H_EOH;
197
198	/* check to see if it represents a ruleset call */
199	if (bitset(pflag, CHHDR_DEF))
200	{
201		char hbuf[50];
202
203		(void) expand(fvalue, hbuf, sizeof(hbuf), e);
204		for (p = hbuf; SM_ISSPACE(*p); )
205			p++;
206		if ((*p++ & 0377) == CALLSUBR)
207		{
208			auto char *endp;
209			bool strc;
210
211			strc = *p == '+';	/* strip comments? */
212			if (strc)
213				++p;
214			if (strtorwset(p, &endp, ST_ENTER) > 0)
215			{
216				*endp = '\0';
217				s = stab(fname, ST_HEADER, ST_ENTER);
218				if (LogLevel > 9 &&
219				    s->s_header.hi_ruleset != NULL)
220					sm_syslog(LOG_WARNING, NOQID,
221						  "Warning: redefined ruleset for header=%s, old=%s, new=%s",
222						  fname,
223						  s->s_header.hi_ruleset, p);
224				s->s_header.hi_ruleset = newstr(p);
225				if (!strc)
226					s->s_header.hi_flags |= H_STRIPCOMM;
227			}
228			return 0;
229		}
230	}
231
232	/* see if it is a known type */
233	s = stab(fname, ST_HEADER, ST_FIND);
234	if (s != NULL)
235		hi = &s->s_header;
236	else
237		hi = &NormalHeader;
238
239	if (tTd(31, 9))
240	{
241		if (s == NULL)
242			sm_dprintf("no header flags match\n");
243		else
244			sm_dprintf("header match, flags=%lx, ruleset=%s\n",
245				   hi->hi_flags,
246				   hi->hi_ruleset == NULL ? "<NULL>"
247							  : hi->hi_ruleset);
248	}
249
250	/* see if this is a resent message */
251	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
252	    bitset(H_RESENT, hi->hi_flags))
253		e->e_flags |= EF_RESENT;
254
255	/* if this is an Errors-To: header keep track of it now */
256	if (UseErrorsTo && !bitset(pflag, CHHDR_DEF) && !headeronly &&
257	    bitset(H_ERRORSTO, hi->hi_flags))
258		(void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e);
259
260	/* if this means "end of header" quit now */
261	if (!headeronly && bitset(H_EOH, hi->hi_flags))
262		return hi->hi_flags;
263
264	/*
265	**  Horrible hack to work around problem with Lotus Notes SMTP
266	**  mail gateway, which generates From: headers with newlines in
267	**  them and the <address> on the second line.  Although this is
268	**  legal RFC 822, many MUAs don't handle this properly and thus
269	**  never find the actual address.
270	*/
271
272	if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader)
273	{
274		while ((p = strchr(fvalue, '\n')) != NULL)
275			*p = ' ';
276	}
277
278	/*
279	**  If there is a check ruleset, verify it against the header.
280	*/
281
282	if (bitset(pflag, CHHDR_CHECK))
283	{
284		int rscheckflags;
285		char *rs;
286
287		rscheckflags = RSF_COUNT;
288		if (!bitset(hi->hi_flags, H_FROM|H_RCPT))
289			rscheckflags |= RSF_UNSTRUCTURED;
290
291		/* no ruleset? look for default */
292		rs = hi->hi_ruleset;
293		if (rs == NULL)
294		{
295			s = stab("*", ST_HEADER, ST_FIND);
296			if (s != NULL)
297			{
298				rs = (&s->s_header)->hi_ruleset;
299				if (bitset((&s->s_header)->hi_flags,
300					   H_STRIPCOMM))
301					rscheckflags |= RSF_RMCOMM;
302			}
303		}
304		else if (bitset(hi->hi_flags, H_STRIPCOMM))
305			rscheckflags |= RSF_RMCOMM;
306		if (rs != NULL)
307		{
308			int l, k;
309			char qval[MAXNAME];
310
311			l = 0;
312			qval[l++] = '"';
313
314			/* - 3 to avoid problems with " at the end */
315			/* should be sizeof(qval), not MAXNAME */
316			for (k = 0; fvalue[k] != '\0' && l < MAXNAME - 3; k++)
317			{
318				switch (fvalue[k])
319				{
320				  /* XXX other control chars? */
321				  case '\011': /* ht */
322				  case '\012': /* nl */
323				  case '\013': /* vt */
324				  case '\014': /* np */
325				  case '\015': /* cr */
326					qval[l++] = ' ';
327					break;
328				  case '"':
329					qval[l++] = '\\';
330					/* FALLTHROUGH */
331				  default:
332					qval[l++] = fvalue[k];
333					break;
334				}
335			}
336			qval[l++] = '"';
337			qval[l] = '\0';
338			k += strlen(fvalue + k);
339			if (k >= MAXNAME)
340			{
341				if (LogLevel > 9)
342					sm_syslog(LOG_WARNING, e->e_id,
343						  "Warning: truncated header '%s' before check with '%s' len=%d max=%d",
344						  fname, rs, k, MAXNAME - 1);
345			}
346			macdefine(&e->e_macro, A_TEMP,
347				macid("{currHeader}"), qval);
348			macdefine(&e->e_macro, A_TEMP,
349				macid("{hdr_name}"), fname);
350
351			(void) sm_snprintf(qval, sizeof(qval), "%d", k);
352			macdefine(&e->e_macro, A_TEMP, macid("{hdrlen}"), qval);
353			if (bitset(H_FROM, hi->hi_flags))
354				macdefine(&e->e_macro, A_PERM,
355					macid("{addr_type}"), "h s");
356			else if (bitset(H_RCPT, hi->hi_flags))
357				macdefine(&e->e_macro, A_PERM,
358					macid("{addr_type}"), "h r");
359			else
360				macdefine(&e->e_macro, A_PERM,
361					macid("{addr_type}"), "h");
362			(void) rscheck(rs, fvalue, NULL, e, rscheckflags, 3,
363				       NULL, e->e_id, NULL, NULL);
364		}
365	}
366
367	/*
368	**  Drop explicit From: if same as what we would generate.
369	**  This is to make MH (which doesn't always give a full name)
370	**  insert the full name information in all circumstances.
371	*/
372
373	dropfrom = false;
374	p = "resent-from";
375	if (!bitset(EF_RESENT, e->e_flags))
376		p += 7;
377	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
378	    !bitset(EF_QUEUERUN, e->e_flags) && sm_strcasecmp(fname, p) == 0)
379	{
380		if (e->e_from.q_paddr != NULL &&
381		    e->e_from.q_mailer != NULL &&
382		    bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
383		    (strcmp(fvalue, e->e_from.q_paddr) == 0 ||
384		     strcmp(fvalue, e->e_from.q_user) == 0))
385			dropfrom = true;
386		if (tTd(31, 2))
387		{
388			sm_dprintf("comparing header from (%s) against default (%s or %s), drop=%d\n",
389				fvalue, e->e_from.q_paddr, e->e_from.q_user,
390				dropfrom);
391		}
392	}
393
394	/* delete default value for this header */
395	for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link)
396	{
397		if (sm_strcasecmp(fname, h->h_field) == 0 &&
398		    !bitset(H_USER, h->h_flags) &&
399		    !bitset(H_FORCE, h->h_flags))
400		{
401			if (nullheader)
402			{
403				/* user-supplied value was null */
404				return 0;
405			}
406			if (dropfrom)
407			{
408				/* make this look like the user entered it */
409				h->h_flags |= H_USER;
410
411				/*
412				**  If the MH hack is selected, allow to turn
413				**  it off via a mailer flag to avoid problems
414				**  with setups that remove the F flag from
415				**  the RCPT mailer.
416				*/
417
418				if (bitnset(M_NOMHHACK,
419					    e->e_from.q_mailer->m_flags))
420				{
421					h->h_flags &= ~H_CHECK;
422				}
423				return hi->hi_flags;
424			}
425			h->h_value = NULL;
426			if (!cond)
427			{
428				/* copy conditions from default case */
429				memmove((char *) mopts, (char *) h->h_mflags,
430					sizeof(mopts));
431			}
432			h->h_macro = mid;
433		}
434	}
435
436	/* create a new node */
437	h = (HDR *) sm_rpool_malloc_x(e->e_rpool, sizeof(*h));
438	h->h_field = sm_rpool_strdup_x(e->e_rpool, fname);
439	h->h_value = sm_rpool_strdup_x(e->e_rpool, fvalue);
440	h->h_link = NULL;
441	memmove((char *) h->h_mflags, (char *) mopts, sizeof(mopts));
442	h->h_macro = mid;
443	*hp = h;
444	h->h_flags = hi->hi_flags;
445	if (bitset(pflag, CHHDR_USER) || bitset(pflag, CHHDR_QUEUE))
446		h->h_flags |= H_USER;
447
448	/* strip EOH flag if parsing MIME headers */
449	if (headeronly)
450		h->h_flags &= ~H_EOH;
451	if (bitset(pflag, CHHDR_DEF))
452		h->h_flags |= H_DEFAULT;
453	if (cond || mid != '\0')
454		h->h_flags |= H_CHECK;
455
456	/* hack to see if this is a new format message */
457	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
458	    bitset(H_RCPT|H_FROM, h->h_flags) &&
459	    (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
460	     strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
461	{
462		e->e_flags &= ~EF_OLDSTYLE;
463	}
464
465	return h->h_flags;
466}
467
468/*
469**  CHOMPHEADER -- process and save a header line.
470**
471**	Called by collect, readcf, and readqf to deal with header lines.
472**	This is just a wrapper for dochompheader().
473**
474**	Parameters:
475**		line -- header as a text line.
476**		pflag -- flags for chompheader() (from sendmail.h)
477**		hdrp -- a pointer to the place to save the header.
478**		e -- the envelope including this header.
479**
480**	Returns:
481**		flags for this header.
482**
483**	Side Effects:
484**		The header is saved on the header list.
485**		Contents of 'line' are destroyed.
486*/
487
488
489unsigned long
490chompheader(line, pflag, hdrp, e)
491	char *line;
492	int pflag;
493	HDR **hdrp;
494	register ENVELOPE *e;
495{
496	unsigned long rval;
497
498	if (tTd(31, 6))
499	{
500		sm_dprintf("chompheader: ");
501		xputs(sm_debug_file(), line);
502		sm_dprintf("\n");
503	}
504
505	/* quote this if user (not config file) input */
506	if (bitset(pflag, CHHDR_USER))
507	{
508		char xbuf[MAXLINE];
509		char *xbp = NULL;
510		int xbufs;
511
512		xbufs = sizeof(xbuf);
513		xbp = quote_internal_chars(line, xbuf, &xbufs);
514		if (tTd(31, 7))
515		{
516			sm_dprintf("chompheader: quoted: ");
517			xputs(sm_debug_file(), xbp);
518			sm_dprintf("\n");
519		}
520		rval = dochompheader(xbp, pflag, hdrp, e);
521		if (xbp != xbuf)
522			sm_free(xbp);
523	}
524	else
525		rval = dochompheader(line, pflag, hdrp, e);
526
527	return rval;
528}
529
530/*
531**  ALLOCHEADER -- allocate a header entry
532**
533**	Parameters:
534**		field -- the name of the header field (will not be copied).
535**		value -- the value of the field (will be copied).
536**		flags -- flags to add to h_flags.
537**		rp -- resource pool for allocations
538**		space -- add leading space?
539**
540**	Returns:
541**		Pointer to a newly allocated and populated HDR.
542**
543**	Notes:
544**		o field and value must be in internal format, i.e.,
545**		metacharacters must be "quoted", see quote_internal_chars().
546**		o maybe add more flags to decide:
547**		  - what to copy (field/value)
548**		  - whether to convert value to an internal format
549*/
550
551static HDR *
552allocheader(field, value, flags, rp, space)
553	char *field;
554	char *value;
555	int flags;
556	SM_RPOOL_T *rp;
557	bool space;
558{
559	HDR *h;
560	STAB *s;
561
562	/* find info struct */
563	s = stab(field, ST_HEADER, ST_FIND);
564
565	/* allocate space for new header */
566	h = (HDR *) sm_rpool_malloc_x(rp, sizeof(*h));
567	h->h_field = field;
568	if (space)
569	{
570		size_t l;
571		char *n;
572
573		l = strlen(value);
574		SM_ASSERT(l + 2 > l);
575		n = sm_rpool_malloc_x(rp, l + 2);
576		n[0] = ' ';
577		n[1] = '\0';
578		sm_strlcpy(n + 1, value, l + 1);
579		h->h_value = n;
580	}
581	else
582		h->h_value = sm_rpool_strdup_x(rp, value);
583	h->h_flags = flags;
584	if (s != NULL)
585		h->h_flags |= s->s_header.hi_flags;
586	clrbitmap(h->h_mflags);
587	h->h_macro = '\0';
588
589	return h;
590}
591
592/*
593**  ADDHEADER -- add a header entry to the end of the queue.
594**
595**	This bypasses the special checking of chompheader.
596**
597**	Parameters:
598**		field -- the name of the header field (will not be copied).
599**		value -- the value of the field (will be copied).
600**		flags -- flags to add to h_flags.
601**		e -- envelope.
602**		space -- add leading space?
603**
604**	Returns:
605**		none.
606**
607**	Side Effects:
608**		adds the field on the list of headers for this envelope.
609**
610**	Notes: field and value must be in internal format, i.e.,
611**		metacharacters must be "quoted", see quote_internal_chars().
612*/
613
614void
615addheader(field, value, flags, e, space)
616	char *field;
617	char *value;
618	int flags;
619	ENVELOPE *e;
620	bool space;
621{
622	register HDR *h;
623	HDR **hp;
624	HDR **hdrlist = &e->e_header;
625
626	/* find current place in list -- keep back pointer? */
627	for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link)
628	{
629		if (sm_strcasecmp(field, h->h_field) == 0)
630			break;
631	}
632
633	/* allocate space for new header */
634	h = allocheader(field, value, flags, e->e_rpool, space);
635	h->h_link = *hp;
636	*hp = h;
637}
638
639/*
640**  INSHEADER -- insert a header entry at the specified index
641**	This bypasses the special checking of chompheader.
642**
643**	Parameters:
644**		idx -- index into the header list at which to insert
645**		field -- the name of the header field (will be copied).
646**		value -- the value of the field (will be copied).
647**		flags -- flags to add to h_flags.
648**		e -- envelope.
649**		space -- add leading space?
650**
651**	Returns:
652**		none.
653**
654**	Side Effects:
655**		inserts the field on the list of headers for this envelope.
656**
657**	Notes:
658**		- field and value must be in internal format, i.e.,
659**		metacharacters must be "quoted", see quote_internal_chars().
660**		- the header list contains headers that might not be
661**		sent "out" (see putheader(): "skip"), hence there is no
662**		reliable way to insert a header at an exact position
663**		(except at the front or end).
664*/
665
666void
667insheader(idx, field, value, flags, e, space)
668	int idx;
669	char *field;
670	char *value;
671	int flags;
672	ENVELOPE *e;
673	bool space;
674{
675	HDR *h, *srch, *last = NULL;
676
677	/* allocate space for new header */
678	h = allocheader(field, value, flags, e->e_rpool, space);
679
680	/* find insertion position */
681	for (srch = e->e_header; srch != NULL && idx > 0;
682	     srch = srch->h_link, idx--)
683		last = srch;
684
685	if (e->e_header == NULL)
686	{
687		e->e_header = h;
688		h->h_link = NULL;
689	}
690	else if (srch == NULL)
691	{
692		SM_ASSERT(last != NULL);
693		last->h_link = h;
694		h->h_link = NULL;
695	}
696	else
697	{
698		h->h_link = srch->h_link;
699		srch->h_link = h;
700	}
701}
702
703/*
704**  HVALUE -- return value of a header.
705**
706**	Only "real" fields (i.e., ones that have not been supplied
707**	as a default) are used.
708**
709**	Parameters:
710**		field -- the field name.
711**		header -- the header list.
712**
713**	Returns:
714**		pointer to the value part (internal format).
715**		NULL if not found.
716**
717**	Side Effects:
718**		none.
719*/
720
721char *
722hvalue(field, header)
723	char *field;
724	HDR *header;
725{
726	register HDR *h;
727
728	for (h = header; h != NULL; h = h->h_link)
729	{
730		if (!bitset(H_DEFAULT, h->h_flags) &&
731		    sm_strcasecmp(h->h_field, field) == 0)
732		{
733			char *s;
734
735			s = h->h_value;
736			if (s == NULL)
737				return NULL;
738			while (SM_ISSPACE(*s))
739				s++;
740			return s;
741		}
742	}
743	return NULL;
744}
745
746/*
747**  ISHEADER -- predicate telling if argument is a header.
748**
749**	A line is a header if it has a single word followed by
750**	optional white space followed by a colon.
751**
752**	Header fields beginning with two dashes, although technically
753**	permitted by RFC822, are automatically rejected in order
754**	to make MIME work out.  Without this we could have a technically
755**	legal header such as ``--"foo:bar"'' that would also be a legal
756**	MIME separator.
757**
758**	Parameters:
759**		h -- string to check for possible headerness.
760**
761**	Returns:
762**		true if h is a header.
763**		false otherwise.
764**
765**	Side Effects:
766**		none.
767*/
768
769bool
770isheader(h)
771	char *h;
772{
773	char *s;
774
775	s = h;
776	if (s[0] == '-' && s[1] == '-')
777		return false;
778
779	while (*s > ' ' && *s != ':' && *s != '\0')
780		s++;
781
782	if (h == s)
783		return false;
784
785	/* following technically violates RFC822 */
786	while (SM_ISSPACE(*s))
787		s++;
788
789	return (*s == ':');
790}
791
792/*
793**  EATHEADER -- run through the stored header and extract info.
794**
795**	Parameters:
796**		e -- the envelope to process.
797**		full -- if set, do full processing (e.g., compute
798**			message priority).  This should not be set
799**			when reading a queue file because some info
800**			needed to compute the priority is wrong.
801**		log -- call logsender()?
802**
803**	Returns:
804**		none.
805**
806**	Side Effects:
807**		Sets a bunch of global variables from information
808**			in the collected header.
809*/
810
811void
812eatheader(e, full, log)
813	register ENVELOPE *e;
814	bool full;
815	bool log;
816{
817	register HDR *h;
818	register char *p;
819	int hopcnt = 0;
820	char buf[MAXLINE];
821
822	/*
823	**  Set up macros for possible expansion in headers.
824	*/
825
826	macdefine(&e->e_macro, A_PERM, 'f', e->e_sender);
827	macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
828	if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0')
829		macdefine(&e->e_macro, A_PERM, 'u', e->e_origrcpt);
830	else
831		macdefine(&e->e_macro, A_PERM, 'u', NULL);
832
833	/* full name of from person */
834	p = hvalue("full-name", e->e_header);
835	if (p != NULL)
836	{
837		if (!rfc822_string(p))
838		{
839			/*
840			**  Quote a full name with special characters
841			**  as a comment so crackaddr() doesn't destroy
842			**  the name portion of the address.
843			*/
844
845			p = addquotes(p, e->e_rpool);
846		}
847		macdefine(&e->e_macro, A_PERM, 'x', p);
848	}
849
850	if (tTd(32, 1))
851		sm_dprintf("----- collected header -----\n");
852	e->e_msgid = NULL;
853	for (h = e->e_header; h != NULL; h = h->h_link)
854	{
855		if (tTd(32, 1))
856			sm_dprintf("%s:", h->h_field);
857		if (h->h_value == NULL)
858		{
859			if (tTd(32, 1))
860				sm_dprintf("<NULL>\n");
861			continue;
862		}
863
864		/* do early binding */
865		if (bitset(H_DEFAULT, h->h_flags) &&
866		    !bitset(H_BINDLATE, h->h_flags))
867		{
868			if (tTd(32, 1))
869			{
870				sm_dprintf("(");
871				xputs(sm_debug_file(), h->h_value);
872				sm_dprintf(") ");
873			}
874			expand(h->h_value, buf, sizeof(buf), e);
875			if (buf[0] != '\0' &&
876			    (buf[0] != ' ' || buf[1] != '\0'))
877			{
878				if (bitset(H_FROM, h->h_flags))
879					expand(crackaddr(buf, e),
880					       buf, sizeof(buf), e);
881				h->h_value = sm_rpool_strdup_x(e->e_rpool, buf);
882				h->h_flags &= ~H_DEFAULT;
883			}
884		}
885		if (tTd(32, 1))
886		{
887			xputs(sm_debug_file(), h->h_value);
888			sm_dprintf("\n");
889		}
890
891		/* count the number of times it has been processed */
892		if (bitset(H_TRACE, h->h_flags))
893			hopcnt++;
894
895		/* send to this person if we so desire */
896		if (GrabTo && bitset(H_RCPT, h->h_flags) &&
897		    !bitset(H_DEFAULT, h->h_flags) &&
898		    (!bitset(EF_RESENT, e->e_flags) ||
899		     bitset(H_RESENT, h->h_flags)))
900		{
901#if 0
902			int saveflags = e->e_flags;
903#endif
904
905			(void) sendtolist(denlstring(h->h_value, true, false),
906					  NULLADDR, &e->e_sendqueue, 0, e);
907
908#if 0
909			/*
910			**  Change functionality so a fatal error on an
911			**  address doesn't affect the entire envelope.
912			*/
913
914			/* delete fatal errors generated by this address */
915			if (!bitset(EF_FATALERRS, saveflags))
916				e->e_flags &= ~EF_FATALERRS;
917#endif /* 0 */
918		}
919
920		/* save the message-id for logging */
921		p = "resent-message-id";
922		if (!bitset(EF_RESENT, e->e_flags))
923			p += 7;
924		if (sm_strcasecmp(h->h_field, p) == 0)
925		{
926			e->e_msgid = h->h_value;
927			while (isascii(*e->e_msgid) && isspace(*e->e_msgid))
928				e->e_msgid++;
929			macdefine(&e->e_macro, A_PERM, macid("{msg_id}"),
930				  e->e_msgid);
931		}
932	}
933	if (tTd(32, 1))
934		sm_dprintf("----------------------------\n");
935
936	/* if we are just verifying (that is, sendmail -t -bv), drop out now */
937	if (OpMode == MD_VERIFY)
938		return;
939
940	/* store hop count */
941	if (hopcnt > e->e_hopcount)
942	{
943		e->e_hopcount = hopcnt;
944		(void) sm_snprintf(buf, sizeof(buf), "%d", e->e_hopcount);
945		macdefine(&e->e_macro, A_TEMP, 'c', buf);
946	}
947
948	/* message priority */
949	p = hvalue("precedence", e->e_header);
950	if (p != NULL)
951		e->e_class = priencode(p);
952	if (e->e_class < 0)
953		e->e_timeoutclass = TOC_NONURGENT;
954	else if (e->e_class > 0)
955		e->e_timeoutclass = TOC_URGENT;
956	if (full)
957	{
958		e->e_msgpriority = e->e_msgsize
959				 - e->e_class * WkClassFact
960				 + e->e_nrcpts * WkRecipFact;
961	}
962
963	/* check for DSN to properly set e_timeoutclass */
964	p = hvalue("content-type", e->e_header);
965	if (p != NULL)
966	{
967		bool oldsupr;
968		char **pvp;
969		char pvpbuf[MAXLINE];
970		extern unsigned char MimeTokenTab[256];
971
972		/* tokenize header */
973		oldsupr = SuprErrs;
974		SuprErrs = true;
975		pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL,
976			      MimeTokenTab, false);
977		SuprErrs = oldsupr;
978
979		/* Check if multipart/report */
980		if (pvp != NULL && pvp[0] != NULL &&
981		    pvp[1] != NULL && pvp[2] != NULL &&
982		    sm_strcasecmp(*pvp++, "multipart") == 0 &&
983		    strcmp(*pvp++, "/") == 0 &&
984		    sm_strcasecmp(*pvp++, "report") == 0)
985		{
986			/* Look for report-type=delivery-status */
987			while (*pvp != NULL)
988			{
989				/* skip to semicolon separator */
990				while (*pvp != NULL && strcmp(*pvp, ";") != 0)
991					pvp++;
992
993				/* skip semicolon */
994				if (*pvp++ == NULL || *pvp == NULL)
995					break;
996
997				/* look for report-type */
998				if (sm_strcasecmp(*pvp++, "report-type") != 0)
999					continue;
1000
1001				/* skip equal */
1002				if (*pvp == NULL || strcmp(*pvp, "=") != 0)
1003					continue;
1004
1005				/* check value */
1006				if (*++pvp != NULL &&
1007				    sm_strcasecmp(*pvp,
1008						  "delivery-status") == 0)
1009					e->e_timeoutclass = TOC_DSN;
1010
1011				/* found report-type, no need to continue */
1012				break;
1013			}
1014		}
1015	}
1016
1017	/* message timeout priority */
1018	p = hvalue("priority", e->e_header);
1019	if (p != NULL)
1020	{
1021		/* (this should be in the configuration file) */
1022		if (sm_strcasecmp(p, "urgent") == 0)
1023			e->e_timeoutclass = TOC_URGENT;
1024		else if (sm_strcasecmp(p, "normal") == 0)
1025			e->e_timeoutclass = TOC_NORMAL;
1026		else if (sm_strcasecmp(p, "non-urgent") == 0)
1027			e->e_timeoutclass = TOC_NONURGENT;
1028		else if (bitset(EF_RESPONSE, e->e_flags))
1029			e->e_timeoutclass = TOC_DSN;
1030	}
1031	else if (bitset(EF_RESPONSE, e->e_flags))
1032		e->e_timeoutclass = TOC_DSN;
1033
1034	/* date message originated */
1035	p = hvalue("posted-date", e->e_header);
1036	if (p == NULL)
1037		p = hvalue("date", e->e_header);
1038	if (p != NULL)
1039		macdefine(&e->e_macro, A_PERM, 'a', p);
1040
1041	/* check to see if this is a MIME message */
1042	if ((e->e_bodytype != NULL &&
1043	     sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) ||
1044	    hvalue("MIME-Version", e->e_header) != NULL)
1045	{
1046		e->e_flags |= EF_IS_MIME;
1047		if (HasEightBits)
1048			e->e_bodytype = "8BITMIME";
1049	}
1050	else if ((p = hvalue("Content-Type", e->e_header)) != NULL)
1051	{
1052		/* this may be an RFC 1049 message */
1053		p = strpbrk(p, ";/");
1054		if (p == NULL || *p == ';')
1055		{
1056			/* yep, it is */
1057			e->e_flags |= EF_DONT_MIME;
1058		}
1059	}
1060
1061	/*
1062	**  From person in antiquated ARPANET mode
1063	**	required by UK Grey Book e-mail gateways (sigh)
1064	*/
1065
1066	if (OpMode == MD_ARPAFTP)
1067	{
1068		register struct hdrinfo *hi;
1069
1070		for (hi = HdrInfo; hi->hi_field != NULL; hi++)
1071		{
1072			if (bitset(H_FROM, hi->hi_flags) &&
1073			    (!bitset(H_RESENT, hi->hi_flags) ||
1074			     bitset(EF_RESENT, e->e_flags)) &&
1075			    (p = hvalue(hi->hi_field, e->e_header)) != NULL)
1076				break;
1077		}
1078		if (hi->hi_field != NULL)
1079		{
1080			if (tTd(32, 2))
1081				sm_dprintf("eatheader: setsender(*%s == %s)\n",
1082					hi->hi_field, p);
1083			setsender(p, e, NULL, '\0', true);
1084		}
1085	}
1086
1087	/*
1088	**  Log collection information.
1089	*/
1090
1091	if (tTd(92, 2))
1092		sm_dprintf("eatheader: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d, log=%d\n",
1093			e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel,
1094			log);
1095	if (log && bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
1096	{
1097		logsender(e, e->e_msgid);
1098		e->e_flags &= ~EF_LOGSENDER;
1099	}
1100}
1101
1102/*
1103**  LOGSENDER -- log sender information
1104**
1105**	Parameters:
1106**		e -- the envelope to log
1107**		msgid -- the message id
1108**
1109**	Returns:
1110**		none
1111*/
1112
1113void
1114logsender(e, msgid)
1115	register ENVELOPE *e;
1116	char *msgid;
1117{
1118	char *name;
1119	register char *sbp;
1120	register char *p;
1121	char hbuf[MAXNAME + 1];
1122	char sbuf[MAXLINE + 1]; /* XREF: see below MSGIDLOGLEN */
1123
1124	if (bitset(EF_RESPONSE, e->e_flags))
1125		name = "[RESPONSE]";
1126	else if ((name = macvalue('_', e)) != NULL)
1127		/* EMPTY */
1128		;
1129	else if (RealHostName == NULL)
1130		name = "localhost";
1131	else if (RealHostName[0] == '[')
1132		name = RealHostName;
1133	else
1134	{
1135		name = hbuf;
1136		(void) sm_snprintf(hbuf, sizeof(hbuf), "%.80s", RealHostName);
1137		if (RealHostAddr.sa.sa_family != 0)
1138		{
1139			p = &hbuf[strlen(hbuf)];
1140			(void) sm_snprintf(p, SPACELEFT(hbuf, p),
1141					   " (%.100s)",
1142					   anynet_ntoa(&RealHostAddr));
1143		}
1144	}
1145
1146#if (SYSLOG_BUFSIZE) >= 256
1147	sbp = sbuf;
1148	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1149		"from=%.200s, size=%ld, class=%d, nrcpts=%d",
1150		e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr,
1151		PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
1152	sbp += strlen(sbp);
1153	if (msgid != NULL)
1154	{
1155
1156#ifndef MSGIDLOGLEN
1157# define MSGIDLOGLEN 100
1158# define FIRSTLOGLEN 850
1159# else
1160#  if MSGIDLOGLEN < 100
1161    ERROR MSGIDLOGLEN too short
1162#  endif
1163/* XREF: this is "sizeof(sbuf)", see above */
1164#  if MSGIDLOGLEN >= MAXLINE / 2
1165    ERROR MSGIDLOGLEN too long
1166#  endif
1167
1168/* 850 - 100 for original MSGIDLOGLEN */
1169#  define FIRSTLOGLEN (750 + MSGIDLOGLEN)
1170
1171/* check that total length is ok */
1172#  if FIRSTLOGLEN + 200 >= MAXLINE
1173    ERROR MSGIDLOGLEN too long
1174#  endif
1175#endif
1176		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1177				", msgid=%.*s", MSGIDLOGLEN, msgid);
1178		sbp += strlen(sbp);
1179	}
1180	if (e->e_bodytype != NULL)
1181	{
1182		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1183				", bodytype=%.20s", e->e_bodytype);
1184		sbp += strlen(sbp);
1185	}
1186	p = macvalue('r', e);
1187	if (p != NULL)
1188	{
1189		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1190				", proto=%.20s", p);
1191		sbp += strlen(sbp);
1192	}
1193	p = macvalue(macid("{daemon_name}"), e);
1194	if (p != NULL)
1195	{
1196		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1197				", daemon=%.20s", p);
1198		sbp += strlen(sbp);
1199	}
1200# if _FFR_LOG_MORE1
1201	LOG_MORE(sbuf, sbp);
1202#  if SASL
1203	p = macvalue(macid("{auth_type}"), e);
1204	if (p == NULL || *p == '\0')
1205		p = "NONE";
1206	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", auth=%.20s", p);
1207	sbp += strlen(sbp);
1208#  endif /* SASL */
1209# endif /* _FFR_LOG_MORE1 */
1210	sm_syslog(LOG_INFO, e->e_id, "%.*s, relay=%s", FIRSTLOGLEN, sbuf, name);
1211
1212#else /* (SYSLOG_BUFSIZE) >= 256 */
1213
1214	sm_syslog(LOG_INFO, e->e_id,
1215		  "from=%s",
1216		  e->e_from.q_paddr == NULL ? "<NONE>"
1217					    : shortenstring(e->e_from.q_paddr,
1218							    83));
1219	sm_syslog(LOG_INFO, e->e_id,
1220		  "size=%ld, class=%ld, nrcpts=%d",
1221		  PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
1222	if (msgid != NULL)
1223		sm_syslog(LOG_INFO, e->e_id,
1224			  "msgid=%s",
1225			  shortenstring(msgid, 83));
1226	sbp = sbuf;
1227	*sbp = '\0';
1228	if (e->e_bodytype != NULL)
1229	{
1230		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1231				"bodytype=%.20s, ", e->e_bodytype);
1232		sbp += strlen(sbp);
1233	}
1234	p = macvalue('r', e);
1235	if (p != NULL)
1236	{
1237		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1238				"proto=%.20s, ", p);
1239		sbp += strlen(sbp);
1240	}
1241	sm_syslog(LOG_INFO, e->e_id,
1242		  "%.400srelay=%s", sbuf, name);
1243#endif /* (SYSLOG_BUFSIZE) >= 256 */
1244}
1245
1246/*
1247**  PRIENCODE -- encode external priority names into internal values.
1248**
1249**	Parameters:
1250**		p -- priority in ascii.
1251**
1252**	Returns:
1253**		priority as a numeric level.
1254**
1255**	Side Effects:
1256**		none.
1257*/
1258
1259static int
1260priencode(p)
1261	char *p;
1262{
1263	register int i;
1264
1265	for (i = 0; i < NumPriorities; i++)
1266	{
1267		if (sm_strcasecmp(p, Priorities[i].pri_name) == 0)
1268			return Priorities[i].pri_val;
1269	}
1270
1271	/* unknown priority */
1272	return 0;
1273}
1274
1275/*
1276**  CRACKADDR -- parse an address and turn it into a macro
1277**
1278**	This doesn't actually parse the address -- it just extracts
1279**	it and replaces it with "$g".  The parse is totally ad hoc
1280**	and isn't even guaranteed to leave something syntactically
1281**	identical to what it started with.  However, it does leave
1282**	something semantically identical if possible, else at least
1283**	syntactically correct.
1284**
1285**	For example, it changes "Real Name <real@example.com> (Comment)"
1286**	to "Real Name <$g> (Comment)".
1287**
1288**	This algorithm has been cleaned up to handle a wider range
1289**	of cases -- notably quoted and backslash escaped strings.
1290**	This modification makes it substantially better at preserving
1291**	the original syntax.
1292**
1293**	Parameters:
1294**		addr -- the address to be cracked.
1295**		e -- the current envelope.
1296**
1297**	Returns:
1298**		a pointer to the new version.
1299**
1300**	Side Effects:
1301**		none.
1302**
1303**	Warning:
1304**		The return value is saved in local storage and should
1305**		be copied if it is to be reused.
1306*/
1307
1308#define SM_HAVE_ROOM		((bp < buflim) && (buflim <= bufend))
1309
1310/*
1311**  Append a character to bp if we have room.
1312**  If not, punt and return $g.
1313*/
1314
1315#define SM_APPEND_CHAR(c)					\
1316	do							\
1317	{							\
1318		if (SM_HAVE_ROOM)				\
1319			*bp++ = (c);				\
1320		else						\
1321			goto returng;				\
1322	} while (0)
1323
1324#if MAXNAME < 10
1325ERROR MAXNAME must be at least 10
1326#endif
1327
1328char *
1329crackaddr(addr, e)
1330	register char *addr;
1331	ENVELOPE *e;
1332{
1333	register char *p;
1334	register char c;
1335	int cmtlev;			/* comment level in input string */
1336	int realcmtlev;			/* comment level in output string */
1337	int anglelev;			/* angle level in input string */
1338	int copylev;			/* 0 == in address, >0 copying */
1339	int bracklev;			/* bracket level for IPv6 addr check */
1340	bool addangle;			/* put closing angle in output */
1341	bool qmode;			/* quoting in original string? */
1342	bool realqmode;			/* quoting in output string? */
1343	bool putgmac = false;		/* already wrote $g */
1344	bool quoteit = false;		/* need to quote next character */
1345	bool gotangle = false;		/* found first '<' */
1346	bool gotcolon = false;		/* found a ':' */
1347	register char *bp;
1348	char *buflim;
1349	char *bufhead;
1350	char *addrhead;
1351	char *bufend;
1352	static char buf[MAXNAME + 1];
1353
1354	if (tTd(33, 1))
1355		sm_dprintf("crackaddr(%s)\n", addr);
1356
1357	buflim = bufend = &buf[sizeof(buf) - 1];
1358	bp = bufhead = buf;
1359
1360	/* skip over leading spaces but preserve them */
1361	while (*addr != '\0' && SM_ISSPACE(*addr))
1362	{
1363		SM_APPEND_CHAR(*addr);
1364		addr++;
1365	}
1366	bufhead = bp;
1367
1368	/*
1369	**  Start by assuming we have no angle brackets.  This will be
1370	**  adjusted later if we find them.
1371	*/
1372
1373	p = addrhead = addr;
1374	copylev = anglelev = cmtlev = realcmtlev = 0;
1375	bracklev = 0;
1376	qmode = realqmode = addangle = false;
1377
1378	while ((c = *p++) != '\0')
1379	{
1380		/*
1381		**  Try to keep legal syntax using spare buffer space
1382		**  (maintained by buflim).
1383		*/
1384
1385		if (copylev > 0)
1386			SM_APPEND_CHAR(c);
1387
1388		/* check for backslash escapes */
1389		if (c == '\\')
1390		{
1391			/* arrange to quote the address */
1392			if (cmtlev <= 0 && !qmode)
1393				quoteit = true;
1394
1395			if ((c = *p++) == '\0')
1396			{
1397				/* too far */
1398				p--;
1399				goto putg;
1400			}
1401			if (copylev > 0)
1402				SM_APPEND_CHAR(c);
1403			goto putg;
1404		}
1405
1406		/* check for quoted strings */
1407		if (c == '"' && cmtlev <= 0)
1408		{
1409			qmode = !qmode;
1410			if (copylev > 0 && SM_HAVE_ROOM)
1411			{
1412				if (realqmode)
1413					buflim--;
1414				else
1415					buflim++;
1416				realqmode = !realqmode;
1417			}
1418			continue;
1419		}
1420		if (qmode)
1421			goto putg;
1422
1423		/* check for comments */
1424		if (c == '(')
1425		{
1426			cmtlev++;
1427
1428			/* allow space for closing paren */
1429			if (SM_HAVE_ROOM)
1430			{
1431				buflim--;
1432				realcmtlev++;
1433				if (copylev++ <= 0)
1434				{
1435					if (bp != bufhead)
1436						SM_APPEND_CHAR(' ');
1437					SM_APPEND_CHAR(c);
1438				}
1439			}
1440		}
1441		if (cmtlev > 0)
1442		{
1443			if (c == ')')
1444			{
1445				cmtlev--;
1446				copylev--;
1447				if (SM_HAVE_ROOM)
1448				{
1449					realcmtlev--;
1450					buflim++;
1451				}
1452			}
1453			continue;
1454		}
1455		else if (c == ')')
1456		{
1457			/* syntax error: unmatched ) */
1458			if (copylev > 0 && SM_HAVE_ROOM && bp > bufhead)
1459				bp--;
1460		}
1461
1462		/* count nesting on [ ... ] (for IPv6 domain literals) */
1463		if (c == '[')
1464			bracklev++;
1465		else if (c == ']')
1466			bracklev--;
1467
1468		/* check for group: list; syntax */
1469		if (c == ':' && anglelev <= 0 && bracklev <= 0 &&
1470		    !gotcolon && !ColonOkInAddr)
1471		{
1472			register char *q;
1473
1474			/*
1475			**  Check for DECnet phase IV ``::'' (host::user)
1476			**  or DECnet phase V ``:.'' syntaxes.  The latter
1477			**  covers ``user@DEC:.tay.myhost'' and
1478			**  ``DEC:.tay.myhost::user'' syntaxes (bletch).
1479			*/
1480
1481			if (*p == ':' || *p == '.')
1482			{
1483				if (cmtlev <= 0 && !qmode)
1484					quoteit = true;
1485				if (copylev > 0)
1486				{
1487					SM_APPEND_CHAR(c);
1488					SM_APPEND_CHAR(*p);
1489				}
1490				p++;
1491				goto putg;
1492			}
1493
1494			gotcolon = true;
1495
1496			bp = bufhead;
1497			if (quoteit)
1498			{
1499				SM_APPEND_CHAR('"');
1500
1501				/* back up over the ':' and any spaces */
1502				--p;
1503				while (p > addr &&
1504				       isascii(*--p) && isspace(*p))
1505					continue;
1506				p++;
1507			}
1508			for (q = addrhead; q < p; )
1509			{
1510				c = *q++;
1511				if (quoteit && c == '"')
1512					SM_APPEND_CHAR('\\');
1513				SM_APPEND_CHAR(c);
1514			}
1515			if (quoteit)
1516			{
1517				if (bp == &bufhead[1])
1518					bp--;
1519				else
1520					SM_APPEND_CHAR('"');
1521				while ((c = *p++) != ':')
1522					SM_APPEND_CHAR(c);
1523				SM_APPEND_CHAR(c);
1524			}
1525
1526			/* any trailing white space is part of group: */
1527			while (SM_ISSPACE(*p))
1528			{
1529				SM_APPEND_CHAR(*p);
1530				p++;
1531			}
1532			copylev = 0;
1533			putgmac = quoteit = false;
1534			bufhead = bp;
1535			addrhead = p;
1536			continue;
1537		}
1538
1539		if (c == ';' && copylev <= 0 && !ColonOkInAddr)
1540			SM_APPEND_CHAR(c);
1541
1542		/* check for characters that may have to be quoted */
1543		if (strchr(MustQuoteChars, c) != NULL)
1544		{
1545			/*
1546			**  If these occur as the phrase part of a <>
1547			**  construct, but are not inside of () or already
1548			**  quoted, they will have to be quoted.  Note that
1549			**  now (but don't actually do the quoting).
1550			*/
1551
1552			if (cmtlev <= 0 && !qmode)
1553				quoteit = true;
1554		}
1555
1556		/* check for angle brackets */
1557		if (c == '<')
1558		{
1559			register char *q;
1560
1561			/* assume first of two angles is bogus */
1562			if (gotangle)
1563				quoteit = true;
1564			gotangle = true;
1565
1566			/* oops -- have to change our mind */
1567			anglelev = 1;
1568			if (SM_HAVE_ROOM)
1569			{
1570				if (!addangle)
1571					buflim--;
1572				addangle = true;
1573			}
1574
1575			bp = bufhead;
1576			if (quoteit)
1577			{
1578				SM_APPEND_CHAR('"');
1579
1580				/* back up over the '<' and any spaces */
1581				--p;
1582				while (p > addr &&
1583				       isascii(*--p) && isspace(*p))
1584					continue;
1585				p++;
1586			}
1587			for (q = addrhead; q < p; )
1588			{
1589				c = *q++;
1590				if (quoteit && c == '"')
1591				{
1592					SM_APPEND_CHAR('\\');
1593					SM_APPEND_CHAR(c);
1594				}
1595				else
1596					SM_APPEND_CHAR(c);
1597			}
1598			if (quoteit)
1599			{
1600				if (bp == &buf[1])
1601					bp--;
1602				else
1603					SM_APPEND_CHAR('"');
1604				while ((c = *p++) != '<')
1605					SM_APPEND_CHAR(c);
1606				SM_APPEND_CHAR(c);
1607			}
1608			copylev = 0;
1609			putgmac = quoteit = false;
1610			continue;
1611		}
1612
1613		if (c == '>')
1614		{
1615			if (anglelev > 0)
1616			{
1617				anglelev--;
1618				if (SM_HAVE_ROOM)
1619				{
1620					if (addangle)
1621						buflim++;
1622					addangle = false;
1623				}
1624			}
1625			else if (SM_HAVE_ROOM)
1626			{
1627				/* syntax error: unmatched > */
1628				if (copylev > 0 && bp > bufhead)
1629					bp--;
1630				quoteit = true;
1631				continue;
1632			}
1633			if (copylev++ <= 0)
1634				SM_APPEND_CHAR(c);
1635			continue;
1636		}
1637
1638		/* must be a real address character */
1639	putg:
1640		if (copylev <= 0 && !putgmac)
1641		{
1642			if (bp > buf && bp[-1] == ')')
1643				SM_APPEND_CHAR(' ');
1644			SM_APPEND_CHAR(MACROEXPAND);
1645			SM_APPEND_CHAR('g');
1646			putgmac = true;
1647		}
1648	}
1649
1650	/* repair any syntactic damage */
1651	if (realqmode && bp < bufend)
1652		*bp++ = '"';
1653	while (realcmtlev-- > 0 && bp < bufend)
1654		*bp++ = ')';
1655	if (addangle && bp < bufend)
1656		*bp++ = '>';
1657	*bp = '\0';
1658	if (bp < bufend)
1659		goto success;
1660
1661 returng:
1662	/* String too long, punt */
1663	buf[0] = '<';
1664	buf[1] = MACROEXPAND;
1665	buf[2]= 'g';
1666	buf[3] = '>';
1667	buf[4]= '\0';
1668	sm_syslog(LOG_ALERT, e->e_id,
1669		  "Dropped invalid comments from header address");
1670
1671 success:
1672	if (tTd(33, 1))
1673	{
1674		sm_dprintf("crackaddr=>`");
1675		xputs(sm_debug_file(), buf);
1676		sm_dprintf("'\n");
1677	}
1678	return buf;
1679}
1680
1681/*
1682**  PUTHEADER -- put the header part of a message from the in-core copy
1683**
1684**	Parameters:
1685**		mci -- the connection information.
1686**		hdr -- the header to put.
1687**		e -- envelope to use.
1688**		flags -- MIME conversion flags.
1689**
1690**	Returns:
1691**		true iff header part was written successfully
1692**
1693**	Side Effects:
1694**		none.
1695*/
1696
1697bool
1698putheader(mci, hdr, e, flags)
1699	register MCI *mci;
1700	HDR *hdr;
1701	register ENVELOPE *e;
1702	int flags;
1703{
1704	register HDR *h;
1705	char buf[SM_MAX(MAXLINE,BUFSIZ)];
1706	char obuf[MAXLINE];
1707
1708	if (tTd(34, 1))
1709		sm_dprintf("--- putheader, mailer = %s ---\n",
1710			mci->mci_mailer->m_name);
1711
1712	/*
1713	**  If we're in MIME mode, we're not really in the header of the
1714	**  message, just the header of one of the parts of the body of
1715	**  the message.  Therefore MCIF_INHEADER should not be turned on.
1716	*/
1717
1718	if (!bitset(MCIF_INMIME, mci->mci_flags))
1719		mci->mci_flags |= MCIF_INHEADER;
1720
1721	for (h = hdr; h != NULL; h = h->h_link)
1722	{
1723		register char *p = h->h_value;
1724		char *q;
1725
1726		if (tTd(34, 11))
1727		{
1728			sm_dprintf("  %s:", h->h_field);
1729			xputs(sm_debug_file(), p);
1730		}
1731
1732		/* Skip empty headers */
1733		if (h->h_value == NULL)
1734			continue;
1735
1736		/* heuristic shortening of MIME fields to avoid MUA overflows */
1737		if (MaxMimeFieldLength > 0 &&
1738		    wordinclass(h->h_field,
1739				macid("{checkMIMEFieldHeaders}")))
1740		{
1741			size_t len;
1742
1743			len = fix_mime_header(h, e);
1744			if (len > 0)
1745			{
1746				sm_syslog(LOG_ALERT, e->e_id,
1747					  "Truncated MIME %s header due to field size (length = %ld) (possible attack)",
1748					  h->h_field, (unsigned long) len);
1749				if (tTd(34, 11))
1750					sm_dprintf("  truncated MIME %s header due to field size  (length = %ld) (possible attack)\n",
1751						   h->h_field,
1752						   (unsigned long) len);
1753			}
1754		}
1755
1756		if (MaxMimeHeaderLength > 0 &&
1757		    wordinclass(h->h_field,
1758				macid("{checkMIMETextHeaders}")))
1759		{
1760			size_t len;
1761
1762			len = strlen(h->h_value);
1763			if (len > (size_t) MaxMimeHeaderLength)
1764			{
1765				h->h_value[MaxMimeHeaderLength - 1] = '\0';
1766				sm_syslog(LOG_ALERT, e->e_id,
1767					  "Truncated long MIME %s header (length = %ld) (possible attack)",
1768					  h->h_field, (unsigned long) len);
1769				if (tTd(34, 11))
1770					sm_dprintf("  truncated long MIME %s header (length = %ld) (possible attack)\n",
1771						   h->h_field,
1772						   (unsigned long) len);
1773			}
1774		}
1775
1776		if (MaxMimeHeaderLength > 0 &&
1777		    wordinclass(h->h_field,
1778				macid("{checkMIMEHeaders}")))
1779		{
1780			size_t len;
1781
1782			len = strlen(h->h_value);
1783			if (shorten_rfc822_string(h->h_value,
1784						  MaxMimeHeaderLength))
1785			{
1786				if (len < MaxMimeHeaderLength)
1787				{
1788					/* we only rebalanced a bogus header */
1789					sm_syslog(LOG_ALERT, e->e_id,
1790						  "Fixed MIME %s header (possible attack)",
1791						  h->h_field);
1792					if (tTd(34, 11))
1793						sm_dprintf("  fixed MIME %s header (possible attack)\n",
1794							   h->h_field);
1795				}
1796				else
1797				{
1798					/* we actually shortened header */
1799					sm_syslog(LOG_ALERT, e->e_id,
1800						  "Truncated long MIME %s header (length = %ld) (possible attack)",
1801						  h->h_field,
1802						  (unsigned long) len);
1803					if (tTd(34, 11))
1804						sm_dprintf("  truncated long MIME %s header (length = %ld) (possible attack)\n",
1805							   h->h_field,
1806							   (unsigned long) len);
1807				}
1808			}
1809		}
1810
1811		/*
1812		**  Suppress Content-Transfer-Encoding: if we are MIMEing
1813		**  and we are potentially converting from 8 bit to 7 bit
1814		**  MIME.  If converting, add a new CTE header in
1815		**  mime8to7().
1816		*/
1817
1818		if (bitset(H_CTE, h->h_flags) &&
1819		    bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME,
1820			   mci->mci_flags) &&
1821		    !bitset(M87F_NO8TO7, flags))
1822		{
1823			if (tTd(34, 11))
1824				sm_dprintf(" (skipped (content-transfer-encoding))\n");
1825			continue;
1826		}
1827
1828		if (bitset(MCIF_INMIME, mci->mci_flags))
1829		{
1830			if (tTd(34, 11))
1831				sm_dprintf("\n");
1832			if (!put_vanilla_header(h, p, mci))
1833				goto writeerr;
1834			continue;
1835		}
1836
1837		if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
1838		    !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) &&
1839		    (h->h_macro == '\0' ||
1840		     (q = macvalue(bitidx(h->h_macro), e)) == NULL ||
1841		     *q == '\0'))
1842		{
1843			if (tTd(34, 11))
1844				sm_dprintf(" (skipped)\n");
1845			continue;
1846		}
1847
1848		/* handle Resent-... headers specially */
1849		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
1850		{
1851			if (tTd(34, 11))
1852				sm_dprintf(" (skipped (resent))\n");
1853			continue;
1854		}
1855
1856		/* suppress return receipts if requested */
1857		if (bitset(H_RECEIPTTO, h->h_flags) &&
1858		    (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags)))
1859		{
1860			if (tTd(34, 11))
1861				sm_dprintf(" (skipped (receipt))\n");
1862			continue;
1863		}
1864
1865		/* macro expand value if generated internally */
1866		if (bitset(H_DEFAULT, h->h_flags) ||
1867		    bitset(H_BINDLATE, h->h_flags))
1868		{
1869			expand(p, buf, sizeof(buf), e);
1870			p = buf;
1871			if (*p == '\0')
1872			{
1873				if (tTd(34, 11))
1874					sm_dprintf(" (skipped -- null value)\n");
1875				continue;
1876			}
1877		}
1878
1879		if (bitset(H_BCC, h->h_flags) && !KeepBcc)
1880		{
1881			/* Bcc: field -- either truncate or delete */
1882			if (bitset(EF_DELETE_BCC, e->e_flags))
1883			{
1884				if (tTd(34, 11))
1885					sm_dprintf(" (skipped -- bcc)\n");
1886			}
1887			else
1888			{
1889				/* no other recipient headers: truncate value */
1890				(void) sm_strlcpyn(obuf, sizeof(obuf), 2,
1891						   h->h_field, ":");
1892				if (!putline(obuf, mci))
1893					goto writeerr;
1894			}
1895			continue;
1896		}
1897
1898		if (tTd(34, 11))
1899			sm_dprintf("\n");
1900
1901		if (bitset(H_FROM|H_RCPT, h->h_flags))
1902		{
1903			/* address field */
1904			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
1905
1906			if (bitset(H_FROM, h->h_flags))
1907				oldstyle = false;
1908			if (!commaize(h, p, oldstyle, mci, e,
1909				      PXLF_HEADER | PXLF_STRIPMQUOTE)
1910			    && bitnset(M_xSMTP, mci->mci_mailer->m_flags))
1911				goto writeerr;
1912		}
1913		else
1914		{
1915			if (!put_vanilla_header(h, p, mci))
1916				goto writeerr;
1917		}
1918	}
1919
1920	/*
1921	**  If we are converting this to a MIME message, add the
1922	**  MIME headers (but not in MIME mode!).
1923	*/
1924
1925#if MIME8TO7
1926	if (bitset(MM_MIME8BIT, MimeMode) &&
1927	    bitset(EF_HAS8BIT, e->e_flags) &&
1928	    !bitset(EF_DONT_MIME, e->e_flags) &&
1929	    !bitnset(M_8BITS, mci->mci_mailer->m_flags) &&
1930	    !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) &&
1931	    hvalue("MIME-Version", e->e_header) == NULL)
1932	{
1933		if (!putline("MIME-Version: 1.0", mci))
1934			goto writeerr;
1935		if (hvalue("Content-Type", e->e_header) == NULL)
1936		{
1937			(void) sm_snprintf(obuf, sizeof(obuf),
1938					"Content-Type: text/plain; charset=%s",
1939					defcharset(e));
1940			if (!putline(obuf, mci))
1941				goto writeerr;
1942		}
1943		if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL
1944		    && !putline("Content-Transfer-Encoding: 8bit", mci))
1945			goto writeerr;
1946	}
1947#endif /* MIME8TO7 */
1948	return true;
1949
1950  writeerr:
1951	return false;
1952}
1953
1954/*
1955**  PUT_VANILLA_HEADER -- output a fairly ordinary header
1956**
1957**	Parameters:
1958**		h -- the structure describing this header
1959**		v -- the value of this header
1960**		mci -- the connection info for output
1961**
1962**	Returns:
1963**		true iff header was written successfully
1964*/
1965
1966static bool
1967put_vanilla_header(h, v, mci)
1968	HDR *h;
1969	char *v;
1970	MCI *mci;
1971{
1972	register char *nlp;
1973	register char *obp;
1974	int putflags;
1975	char obuf[MAXLINE + 256];	/* additional length for h_field */
1976
1977	putflags = PXLF_HEADER | PXLF_STRIPMQUOTE;
1978	if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
1979		putflags |= PXLF_STRIP8BIT;
1980	(void) sm_snprintf(obuf, sizeof(obuf), "%.200s:", h->h_field);
1981	obp = obuf + strlen(obuf);
1982	while ((nlp = strchr(v, '\n')) != NULL)
1983	{
1984		int l;
1985
1986		l = nlp - v;
1987
1988		/*
1989		**  XXX This is broken for SPACELEFT()==0
1990		**  However, SPACELEFT() is always > 0 unless MAXLINE==1.
1991		*/
1992
1993		if (SPACELEFT(obuf, obp) - 1 < (size_t) l)
1994			l = SPACELEFT(obuf, obp) - 1;
1995
1996		(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
1997		if (!putxline(obuf, strlen(obuf), mci, putflags))
1998			goto writeerr;
1999		v += l + 1;
2000		obp = obuf;
2001		if (*v != ' ' && *v != '\t')
2002			*obp++ = ' ';
2003	}
2004
2005	/* XXX This is broken for SPACELEFT()==0 */
2006	(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s",
2007			   (int) (SPACELEFT(obuf, obp) - 1), v);
2008	return putxline(obuf, strlen(obuf), mci, putflags);
2009
2010  writeerr:
2011	return false;
2012}
2013
2014/*
2015**  COMMAIZE -- output a header field, making a comma-translated list.
2016**
2017**	Parameters:
2018**		h -- the header field to output.
2019**		p -- the value to put in it.
2020**		oldstyle -- true if this is an old style header.
2021**		mci -- the connection information.
2022**		e -- the envelope containing the message.
2023**		putflags -- flags for putxline()
2024**
2025**	Returns:
2026**		true iff header field was written successfully
2027**
2028**	Side Effects:
2029**		outputs "p" to "mci".
2030*/
2031
2032bool
2033commaize(h, p, oldstyle, mci, e, putflags)
2034	register HDR *h;
2035	register char *p;
2036	bool oldstyle;
2037	register MCI *mci;
2038	register ENVELOPE *e;
2039	int putflags;
2040{
2041	register char *obp;
2042	int opos, omax, spaces;
2043	bool firstone = true;
2044	char **res;
2045	char obuf[MAXLINE + 3];
2046
2047	/*
2048	**  Output the address list translated by the
2049	**  mailer and with commas.
2050	*/
2051
2052	if (tTd(14, 2))
2053		sm_dprintf("commaize(%s:%s)\n", h->h_field, p);
2054
2055	if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
2056		putflags |= PXLF_STRIP8BIT;
2057
2058	obp = obuf;
2059	(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s:", h->h_field);
2060	/* opos = strlen(obp); instead of the next 3 lines? */
2061	opos = strlen(h->h_field) + 1;
2062	if (opos > 201)
2063		opos = 201;
2064	obp += opos;
2065
2066	spaces = 0;
2067	while (*p != '\0' && SM_ISSPACE(*p))
2068	{
2069		++spaces;
2070		++p;
2071	}
2072	if (spaces > 0)
2073	{
2074		SM_ASSERT(sizeof(obuf) > opos  * 2);
2075
2076		/*
2077		**  Restrict number of spaces to half the length of buffer
2078		**  so the header field body can be put in here too.
2079		**  Note: this is a hack...
2080		*/
2081
2082		if (spaces > sizeof(obuf) / 2)
2083			spaces = sizeof(obuf) / 2;
2084		(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%*s", spaces,
2085				"");
2086		opos += spaces;
2087		obp += spaces;
2088		SM_ASSERT(obp < &obuf[MAXLINE]);
2089	}
2090
2091	omax = mci->mci_mailer->m_linelimit - 2;
2092	if (omax < 0 || omax > 78)
2093		omax = 78;
2094
2095	/*
2096	**  Run through the list of values.
2097	*/
2098
2099	while (*p != '\0')
2100	{
2101		register char *name;
2102		register int c;
2103		char savechar;
2104		int flags;
2105		auto int status;
2106
2107		/*
2108		**  Find the end of the name.  New style names
2109		**  end with a comma, old style names end with
2110		**  a space character.  However, spaces do not
2111		**  necessarily delimit an old-style name -- at
2112		**  signs mean keep going.
2113		*/
2114
2115		/* find end of name */
2116		while ((SM_ISSPACE(*p)) || *p == ',')
2117			p++;
2118		name = p;
2119		res = NULL;
2120		for (;;)
2121		{
2122			auto char *oldp;
2123			char pvpbuf[PSBUFSIZE];
2124
2125			res = prescan(p, oldstyle ? ' ' : ',', pvpbuf,
2126				      sizeof(pvpbuf), &oldp, ExtTokenTab, false);
2127			p = oldp;
2128#if _FFR_IGNORE_BOGUS_ADDR
2129			/* ignore addresses that can't be parsed */
2130			if (res == NULL)
2131			{
2132				name = p;
2133				continue;
2134			}
2135#endif /* _FFR_IGNORE_BOGUS_ADDR */
2136
2137			/* look to see if we have an at sign */
2138			while (*p != '\0' && SM_ISSPACE(*p))
2139				p++;
2140
2141			if (*p != '@')
2142			{
2143				p = oldp;
2144				break;
2145			}
2146			++p;
2147			while (*p != '\0' && SM_ISSPACE(*p))
2148				p++;
2149		}
2150		/* at the end of one complete name */
2151
2152		/* strip off trailing white space */
2153		while (p >= name &&
2154		       ((SM_ISSPACE(*p)) || *p == ',' || *p == '\0'))
2155			p--;
2156		if (++p == name)
2157			continue;
2158
2159		/*
2160		**  if prescan() failed go a bit backwards; this is a hack,
2161		**  there should be some better error recovery.
2162		*/
2163
2164		if (res == NULL && p > name &&
2165		    !((SM_ISSPACE(*p)) || *p == ',' || *p == '\0'))
2166			--p;
2167		savechar = *p;
2168		*p = '\0';
2169
2170		/* translate the name to be relative */
2171		flags = RF_HEADERADDR|RF_ADDDOMAIN;
2172		if (bitset(H_FROM, h->h_flags))
2173			flags |= RF_SENDERADDR;
2174#if USERDB
2175		else if (e->e_from.q_mailer != NULL &&
2176			 bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags))
2177		{
2178			char *q;
2179
2180			q = udbsender(name, e->e_rpool);
2181			if (q != NULL)
2182				name = q;
2183		}
2184#endif /* USERDB */
2185		status = EX_OK;
2186		name = remotename(name, mci->mci_mailer, flags, &status, e);
2187		if (status != EX_OK && bitnset(M_xSMTP, mci->mci_mailer->m_flags))
2188		{
2189			if (status == EX_TEMPFAIL)
2190				mci->mci_flags |= MCIF_NOTSTICKY;
2191			goto writeerr;
2192		}
2193		if (*name == '\0')
2194		{
2195			*p = savechar;
2196			continue;
2197		}
2198		name = denlstring(name, false, true);
2199
2200		/* output the name with nice formatting */
2201		opos += strlen(name);
2202		if (!firstone)
2203			opos += 2;
2204		if (opos > omax && !firstone)
2205		{
2206			(void) sm_strlcpy(obp, ",\n", SPACELEFT(obuf, obp));
2207			if (!putxline(obuf, strlen(obuf), mci, putflags))
2208				goto writeerr;
2209			obp = obuf;
2210			(void) sm_strlcpy(obp, "        ", sizeof(obuf));
2211			opos = strlen(obp);
2212			obp += opos;
2213			opos += strlen(name);
2214		}
2215		else if (!firstone)
2216		{
2217			(void) sm_strlcpy(obp, ", ", SPACELEFT(obuf, obp));
2218			obp += 2;
2219		}
2220
2221		while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
2222			*obp++ = c;
2223		firstone = false;
2224		*p = savechar;
2225	}
2226	if (obp < &obuf[sizeof(obuf)])
2227		*obp = '\0';
2228	else
2229		obuf[sizeof(obuf) - 1] = '\0';
2230	return putxline(obuf, strlen(obuf), mci, putflags);
2231
2232  writeerr:
2233	return false;
2234}
2235
2236/*
2237**  COPYHEADER -- copy header list
2238**
2239**	This routine is the equivalent of newstr for header lists
2240**
2241**	Parameters:
2242**		header -- list of header structures to copy.
2243**		rpool -- resource pool, or NULL
2244**
2245**	Returns:
2246**		a copy of 'header'.
2247**
2248**	Side Effects:
2249**		none.
2250*/
2251
2252HDR *
2253copyheader(header, rpool)
2254	register HDR *header;
2255	SM_RPOOL_T *rpool;
2256{
2257	register HDR *newhdr;
2258	HDR *ret;
2259	register HDR **tail = &ret;
2260
2261	while (header != NULL)
2262	{
2263		newhdr = (HDR *) sm_rpool_malloc_x(rpool, sizeof(*newhdr));
2264		STRUCTCOPY(*header, *newhdr);
2265		*tail = newhdr;
2266		tail = &newhdr->h_link;
2267		header = header->h_link;
2268	}
2269	*tail = NULL;
2270
2271	return ret;
2272}
2273
2274/*
2275**  FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header
2276**
2277**	Run through all of the parameters of a MIME header and
2278**	possibly truncate and rebalance the parameter according
2279**	to MaxMimeFieldLength.
2280**
2281**	Parameters:
2282**		h -- the header to truncate/rebalance
2283**		e -- the current envelope
2284**
2285**	Returns:
2286**		length of last offending field, 0 if all ok.
2287**
2288**	Side Effects:
2289**		string modified in place
2290*/
2291
2292static size_t
2293fix_mime_header(h, e)
2294	HDR *h;
2295	ENVELOPE *e;
2296{
2297	char *begin = h->h_value;
2298	char *end;
2299	size_t len = 0;
2300	size_t retlen = 0;
2301
2302	if (begin == NULL || *begin == '\0')
2303		return 0;
2304
2305	/* Split on each ';' */
2306	/* find_character() never returns NULL */
2307	while ((end = find_character(begin, ';')) != NULL)
2308	{
2309		char save = *end;
2310		char *bp;
2311
2312		*end = '\0';
2313
2314		len = strlen(begin);
2315
2316		/* Shorten individual parameter */
2317		if (shorten_rfc822_string(begin, MaxMimeFieldLength))
2318		{
2319			if (len < MaxMimeFieldLength)
2320			{
2321				/* we only rebalanced a bogus field */
2322				sm_syslog(LOG_ALERT, e->e_id,
2323					  "Fixed MIME %s header field (possible attack)",
2324					  h->h_field);
2325				if (tTd(34, 11))
2326					sm_dprintf("  fixed MIME %s header field (possible attack)\n",
2327						   h->h_field);
2328			}
2329			else
2330			{
2331				/* we actually shortened the header */
2332				retlen = len;
2333			}
2334		}
2335
2336		/* Collapse the possibly shortened string with rest */
2337		bp = begin + strlen(begin);
2338		if (bp != end)
2339		{
2340			char *ep = end;
2341
2342			*end = save;
2343			end = bp;
2344
2345			/* copy character by character due to overlap */
2346			while (*ep != '\0')
2347				*bp++ = *ep++;
2348			*bp = '\0';
2349		}
2350		else
2351			*end = save;
2352		if (*end == '\0')
2353			break;
2354
2355		/* Move past ';' */
2356		begin = end + 1;
2357	}
2358	return retlen;
2359}
2360