headers.c revision 302408
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 (isascii(*p) && 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 (isascii(*p) && 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; isascii(*p) && 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 (isascii(*s) && 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 (isascii(*s) && 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 /* 0 */
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];
1123	char mbuf[MAXNAME + 1];
1124
1125	/* don't allow newlines in the message-id */
1126	/* XXX do we still need this? sm_syslog() replaces control chars */
1127	if (msgid != NULL)
1128	{
1129		size_t l;
1130
1131		l = strlen(msgid);
1132		if (l > sizeof(mbuf) - 1)
1133			l = sizeof(mbuf) - 1;
1134		memmove(mbuf, msgid, l);
1135		mbuf[l] = '\0';
1136		p = mbuf;
1137		while ((p = strchr(p, '\n')) != NULL)
1138			*p++ = ' ';
1139	}
1140
1141	if (bitset(EF_RESPONSE, e->e_flags))
1142		name = "[RESPONSE]";
1143	else if ((name = macvalue('_', e)) != NULL)
1144		/* EMPTY */
1145		;
1146	else if (RealHostName == NULL)
1147		name = "localhost";
1148	else if (RealHostName[0] == '[')
1149		name = RealHostName;
1150	else
1151	{
1152		name = hbuf;
1153		(void) sm_snprintf(hbuf, sizeof(hbuf), "%.80s", RealHostName);
1154		if (RealHostAddr.sa.sa_family != 0)
1155		{
1156			p = &hbuf[strlen(hbuf)];
1157			(void) sm_snprintf(p, SPACELEFT(hbuf, p),
1158					   " (%.100s)",
1159					   anynet_ntoa(&RealHostAddr));
1160		}
1161	}
1162
1163	/* some versions of syslog only take 5 printf args */
1164#if (SYSLOG_BUFSIZE) >= 256
1165	sbp = sbuf;
1166	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1167		"from=%.200s, size=%ld, class=%d, nrcpts=%d",
1168		e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr,
1169		PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
1170	sbp += strlen(sbp);
1171	if (msgid != NULL)
1172	{
1173		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1174				", msgid=%.100s", mbuf);
1175		sbp += strlen(sbp);
1176	}
1177	if (e->e_bodytype != NULL)
1178	{
1179		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1180				", bodytype=%.20s", e->e_bodytype);
1181		sbp += strlen(sbp);
1182	}
1183	p = macvalue('r', e);
1184	if (p != NULL)
1185	{
1186		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1187				", proto=%.20s", p);
1188		sbp += strlen(sbp);
1189	}
1190	p = macvalue(macid("{daemon_name}"), e);
1191	if (p != NULL)
1192	{
1193		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1194				", daemon=%.20s", p);
1195		sbp += strlen(sbp);
1196	}
1197# if _FFR_LOG_MORE1
1198#  if STARTTLS
1199	p = macvalue(macid("{verify}"), e);
1200	if (p == NULL || *p == '\0')
1201		p = "NONE";
1202	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", tls_verify=%.20s", p);
1203	sbp += strlen(sbp);
1204#  endif /* STARTTLS */
1205#  if SASL
1206	p = macvalue(macid("{auth_type}"), e);
1207	if (p == NULL || *p == '\0')
1208		p = "NONE";
1209	(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", auth=%.20s", p);
1210	sbp += strlen(sbp);
1211#  endif /* SASL */
1212# endif /* _FFR_LOG_MORE1 */
1213	sm_syslog(LOG_INFO, e->e_id, "%.850s, relay=%s", sbuf, name);
1214
1215#else /* (SYSLOG_BUFSIZE) >= 256 */
1216
1217	sm_syslog(LOG_INFO, e->e_id,
1218		  "from=%s",
1219		  e->e_from.q_paddr == NULL ? "<NONE>"
1220					    : shortenstring(e->e_from.q_paddr,
1221							    83));
1222	sm_syslog(LOG_INFO, e->e_id,
1223		  "size=%ld, class=%ld, nrcpts=%d",
1224		  PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts);
1225	if (msgid != NULL)
1226		sm_syslog(LOG_INFO, e->e_id,
1227			  "msgid=%s",
1228			  shortenstring(mbuf, 83));
1229	sbp = sbuf;
1230	*sbp = '\0';
1231	if (e->e_bodytype != NULL)
1232	{
1233		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1234				"bodytype=%.20s, ", e->e_bodytype);
1235		sbp += strlen(sbp);
1236	}
1237	p = macvalue('r', e);
1238	if (p != NULL)
1239	{
1240		(void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
1241				"proto=%.20s, ", p);
1242		sbp += strlen(sbp);
1243	}
1244	sm_syslog(LOG_INFO, e->e_id,
1245		  "%.400srelay=%s", sbuf, name);
1246#endif /* (SYSLOG_BUFSIZE) >= 256 */
1247}
1248
1249/*
1250**  PRIENCODE -- encode external priority names into internal values.
1251**
1252**	Parameters:
1253**		p -- priority in ascii.
1254**
1255**	Returns:
1256**		priority as a numeric level.
1257**
1258**	Side Effects:
1259**		none.
1260*/
1261
1262static int
1263priencode(p)
1264	char *p;
1265{
1266	register int i;
1267
1268	for (i = 0; i < NumPriorities; i++)
1269	{
1270		if (sm_strcasecmp(p, Priorities[i].pri_name) == 0)
1271			return Priorities[i].pri_val;
1272	}
1273
1274	/* unknown priority */
1275	return 0;
1276}
1277
1278/*
1279**  CRACKADDR -- parse an address and turn it into a macro
1280**
1281**	This doesn't actually parse the address -- it just extracts
1282**	it and replaces it with "$g".  The parse is totally ad hoc
1283**	and isn't even guaranteed to leave something syntactically
1284**	identical to what it started with.  However, it does leave
1285**	something semantically identical if possible, else at least
1286**	syntactically correct.
1287**
1288**	For example, it changes "Real Name <real@example.com> (Comment)"
1289**	to "Real Name <$g> (Comment)".
1290**
1291**	This algorithm has been cleaned up to handle a wider range
1292**	of cases -- notably quoted and backslash escaped strings.
1293**	This modification makes it substantially better at preserving
1294**	the original syntax.
1295**
1296**	Parameters:
1297**		addr -- the address to be cracked.
1298**		e -- the current envelope.
1299**
1300**	Returns:
1301**		a pointer to the new version.
1302**
1303**	Side Effects:
1304**		none.
1305**
1306**	Warning:
1307**		The return value is saved in local storage and should
1308**		be copied if it is to be reused.
1309*/
1310
1311#define SM_HAVE_ROOM		((bp < buflim) && (buflim <= bufend))
1312
1313/*
1314**  Append a character to bp if we have room.
1315**  If not, punt and return $g.
1316*/
1317
1318#define SM_APPEND_CHAR(c)					\
1319	do							\
1320	{							\
1321		if (SM_HAVE_ROOM)				\
1322			*bp++ = (c);				\
1323		else						\
1324			goto returng;				\
1325	} while (0)
1326
1327#if MAXNAME < 10
1328ERROR MAXNAME must be at least 10
1329#endif /* MAXNAME < 10 */
1330
1331char *
1332crackaddr(addr, e)
1333	register char *addr;
1334	ENVELOPE *e;
1335{
1336	register char *p;
1337	register char c;
1338	int cmtlev;			/* comment level in input string */
1339	int realcmtlev;			/* comment level in output string */
1340	int anglelev;			/* angle level in input string */
1341	int copylev;			/* 0 == in address, >0 copying */
1342	int bracklev;			/* bracket level for IPv6 addr check */
1343	bool addangle;			/* put closing angle in output */
1344	bool qmode;			/* quoting in original string? */
1345	bool realqmode;			/* quoting in output string? */
1346	bool putgmac = false;		/* already wrote $g */
1347	bool quoteit = false;		/* need to quote next character */
1348	bool gotangle = false;		/* found first '<' */
1349	bool gotcolon = false;		/* found a ':' */
1350	register char *bp;
1351	char *buflim;
1352	char *bufhead;
1353	char *addrhead;
1354	char *bufend;
1355	static char buf[MAXNAME + 1];
1356
1357	if (tTd(33, 1))
1358		sm_dprintf("crackaddr(%s)\n", addr);
1359
1360	buflim = bufend = &buf[sizeof(buf) - 1];
1361	bp = bufhead = buf;
1362
1363	/* skip over leading spaces but preserve them */
1364	while (*addr != '\0' && isascii(*addr) && isspace(*addr))
1365	{
1366		SM_APPEND_CHAR(*addr);
1367		addr++;
1368	}
1369	bufhead = bp;
1370
1371	/*
1372	**  Start by assuming we have no angle brackets.  This will be
1373	**  adjusted later if we find them.
1374	*/
1375
1376	p = addrhead = addr;
1377	copylev = anglelev = cmtlev = realcmtlev = 0;
1378	bracklev = 0;
1379	qmode = realqmode = addangle = false;
1380
1381	while ((c = *p++) != '\0')
1382	{
1383		/*
1384		**  Try to keep legal syntax using spare buffer space
1385		**  (maintained by buflim).
1386		*/
1387
1388		if (copylev > 0)
1389			SM_APPEND_CHAR(c);
1390
1391		/* check for backslash escapes */
1392		if (c == '\\')
1393		{
1394			/* arrange to quote the address */
1395			if (cmtlev <= 0 && !qmode)
1396				quoteit = true;
1397
1398			if ((c = *p++) == '\0')
1399			{
1400				/* too far */
1401				p--;
1402				goto putg;
1403			}
1404			if (copylev > 0)
1405				SM_APPEND_CHAR(c);
1406			goto putg;
1407		}
1408
1409		/* check for quoted strings */
1410		if (c == '"' && cmtlev <= 0)
1411		{
1412			qmode = !qmode;
1413			if (copylev > 0 && SM_HAVE_ROOM)
1414			{
1415				if (realqmode)
1416					buflim--;
1417				else
1418					buflim++;
1419				realqmode = !realqmode;
1420			}
1421			continue;
1422		}
1423		if (qmode)
1424			goto putg;
1425
1426		/* check for comments */
1427		if (c == '(')
1428		{
1429			cmtlev++;
1430
1431			/* allow space for closing paren */
1432			if (SM_HAVE_ROOM)
1433			{
1434				buflim--;
1435				realcmtlev++;
1436				if (copylev++ <= 0)
1437				{
1438					if (bp != bufhead)
1439						SM_APPEND_CHAR(' ');
1440					SM_APPEND_CHAR(c);
1441				}
1442			}
1443		}
1444		if (cmtlev > 0)
1445		{
1446			if (c == ')')
1447			{
1448				cmtlev--;
1449				copylev--;
1450				if (SM_HAVE_ROOM)
1451				{
1452					realcmtlev--;
1453					buflim++;
1454				}
1455			}
1456			continue;
1457		}
1458		else if (c == ')')
1459		{
1460			/* syntax error: unmatched ) */
1461			if (copylev > 0 && SM_HAVE_ROOM && bp > bufhead)
1462				bp--;
1463		}
1464
1465		/* count nesting on [ ... ] (for IPv6 domain literals) */
1466		if (c == '[')
1467			bracklev++;
1468		else if (c == ']')
1469			bracklev--;
1470
1471		/* check for group: list; syntax */
1472		if (c == ':' && anglelev <= 0 && bracklev <= 0 &&
1473		    !gotcolon && !ColonOkInAddr)
1474		{
1475			register char *q;
1476
1477			/*
1478			**  Check for DECnet phase IV ``::'' (host::user)
1479			**  or DECnet phase V ``:.'' syntaxes.  The latter
1480			**  covers ``user@DEC:.tay.myhost'' and
1481			**  ``DEC:.tay.myhost::user'' syntaxes (bletch).
1482			*/
1483
1484			if (*p == ':' || *p == '.')
1485			{
1486				if (cmtlev <= 0 && !qmode)
1487					quoteit = true;
1488				if (copylev > 0)
1489				{
1490					SM_APPEND_CHAR(c);
1491					SM_APPEND_CHAR(*p);
1492				}
1493				p++;
1494				goto putg;
1495			}
1496
1497			gotcolon = true;
1498
1499			bp = bufhead;
1500			if (quoteit)
1501			{
1502				SM_APPEND_CHAR('"');
1503
1504				/* back up over the ':' and any spaces */
1505				--p;
1506				while (p > addr &&
1507				       isascii(*--p) && isspace(*p))
1508					continue;
1509				p++;
1510			}
1511			for (q = addrhead; q < p; )
1512			{
1513				c = *q++;
1514				if (quoteit && c == '"')
1515					SM_APPEND_CHAR('\\');
1516				SM_APPEND_CHAR(c);
1517			}
1518			if (quoteit)
1519			{
1520				if (bp == &bufhead[1])
1521					bp--;
1522				else
1523					SM_APPEND_CHAR('"');
1524				while ((c = *p++) != ':')
1525					SM_APPEND_CHAR(c);
1526				SM_APPEND_CHAR(c);
1527			}
1528
1529			/* any trailing white space is part of group: */
1530			while (isascii(*p) && isspace(*p))
1531			{
1532				SM_APPEND_CHAR(*p);
1533				p++;
1534			}
1535			copylev = 0;
1536			putgmac = quoteit = false;
1537			bufhead = bp;
1538			addrhead = p;
1539			continue;
1540		}
1541
1542		if (c == ';' && copylev <= 0 && !ColonOkInAddr)
1543			SM_APPEND_CHAR(c);
1544
1545		/* check for characters that may have to be quoted */
1546		if (strchr(MustQuoteChars, c) != NULL)
1547		{
1548			/*
1549			**  If these occur as the phrase part of a <>
1550			**  construct, but are not inside of () or already
1551			**  quoted, they will have to be quoted.  Note that
1552			**  now (but don't actually do the quoting).
1553			*/
1554
1555			if (cmtlev <= 0 && !qmode)
1556				quoteit = true;
1557		}
1558
1559		/* check for angle brackets */
1560		if (c == '<')
1561		{
1562			register char *q;
1563
1564			/* assume first of two angles is bogus */
1565			if (gotangle)
1566				quoteit = true;
1567			gotangle = true;
1568
1569			/* oops -- have to change our mind */
1570			anglelev = 1;
1571			if (SM_HAVE_ROOM)
1572			{
1573				if (!addangle)
1574					buflim--;
1575				addangle = true;
1576			}
1577
1578			bp = bufhead;
1579			if (quoteit)
1580			{
1581				SM_APPEND_CHAR('"');
1582
1583				/* back up over the '<' and any spaces */
1584				--p;
1585				while (p > addr &&
1586				       isascii(*--p) && isspace(*p))
1587					continue;
1588				p++;
1589			}
1590			for (q = addrhead; q < p; )
1591			{
1592				c = *q++;
1593				if (quoteit && c == '"')
1594				{
1595					SM_APPEND_CHAR('\\');
1596					SM_APPEND_CHAR(c);
1597				}
1598				else
1599					SM_APPEND_CHAR(c);
1600			}
1601			if (quoteit)
1602			{
1603				if (bp == &buf[1])
1604					bp--;
1605				else
1606					SM_APPEND_CHAR('"');
1607				while ((c = *p++) != '<')
1608					SM_APPEND_CHAR(c);
1609				SM_APPEND_CHAR(c);
1610			}
1611			copylev = 0;
1612			putgmac = quoteit = false;
1613			continue;
1614		}
1615
1616		if (c == '>')
1617		{
1618			if (anglelev > 0)
1619			{
1620				anglelev--;
1621				if (SM_HAVE_ROOM)
1622				{
1623					if (addangle)
1624						buflim++;
1625					addangle = false;
1626				}
1627			}
1628			else if (SM_HAVE_ROOM)
1629			{
1630				/* syntax error: unmatched > */
1631				if (copylev > 0 && bp > bufhead)
1632					bp--;
1633				quoteit = true;
1634				continue;
1635			}
1636			if (copylev++ <= 0)
1637				SM_APPEND_CHAR(c);
1638			continue;
1639		}
1640
1641		/* must be a real address character */
1642	putg:
1643		if (copylev <= 0 && !putgmac)
1644		{
1645			if (bp > buf && bp[-1] == ')')
1646				SM_APPEND_CHAR(' ');
1647			SM_APPEND_CHAR(MACROEXPAND);
1648			SM_APPEND_CHAR('g');
1649			putgmac = true;
1650		}
1651	}
1652
1653	/* repair any syntactic damage */
1654	if (realqmode && bp < bufend)
1655		*bp++ = '"';
1656	while (realcmtlev-- > 0 && bp < bufend)
1657		*bp++ = ')';
1658	if (addangle && bp < bufend)
1659		*bp++ = '>';
1660	*bp = '\0';
1661	if (bp < bufend)
1662		goto success;
1663
1664 returng:
1665	/* String too long, punt */
1666	buf[0] = '<';
1667	buf[1] = MACROEXPAND;
1668	buf[2]= 'g';
1669	buf[3] = '>';
1670	buf[4]= '\0';
1671	sm_syslog(LOG_ALERT, e->e_id,
1672		  "Dropped invalid comments from header address");
1673
1674 success:
1675	if (tTd(33, 1))
1676	{
1677		sm_dprintf("crackaddr=>`");
1678		xputs(sm_debug_file(), buf);
1679		sm_dprintf("'\n");
1680	}
1681	return buf;
1682}
1683
1684/*
1685**  PUTHEADER -- put the header part of a message from the in-core copy
1686**
1687**	Parameters:
1688**		mci -- the connection information.
1689**		hdr -- the header to put.
1690**		e -- envelope to use.
1691**		flags -- MIME conversion flags.
1692**
1693**	Returns:
1694**		true iff header part was written successfully
1695**
1696**	Side Effects:
1697**		none.
1698*/
1699
1700bool
1701putheader(mci, hdr, e, flags)
1702	register MCI *mci;
1703	HDR *hdr;
1704	register ENVELOPE *e;
1705	int flags;
1706{
1707	register HDR *h;
1708	char buf[SM_MAX(MAXLINE,BUFSIZ)];
1709	char obuf[MAXLINE];
1710
1711	if (tTd(34, 1))
1712		sm_dprintf("--- putheader, mailer = %s ---\n",
1713			mci->mci_mailer->m_name);
1714
1715	/*
1716	**  If we're in MIME mode, we're not really in the header of the
1717	**  message, just the header of one of the parts of the body of
1718	**  the message.  Therefore MCIF_INHEADER should not be turned on.
1719	*/
1720
1721	if (!bitset(MCIF_INMIME, mci->mci_flags))
1722		mci->mci_flags |= MCIF_INHEADER;
1723
1724	for (h = hdr; h != NULL; h = h->h_link)
1725	{
1726		register char *p = h->h_value;
1727		char *q;
1728
1729		if (tTd(34, 11))
1730		{
1731			sm_dprintf("  %s:", h->h_field);
1732			xputs(sm_debug_file(), p);
1733		}
1734
1735		/* Skip empty headers */
1736		if (h->h_value == NULL)
1737			continue;
1738
1739		/* heuristic shortening of MIME fields to avoid MUA overflows */
1740		if (MaxMimeFieldLength > 0 &&
1741		    wordinclass(h->h_field,
1742				macid("{checkMIMEFieldHeaders}")))
1743		{
1744			size_t len;
1745
1746			len = fix_mime_header(h, e);
1747			if (len > 0)
1748			{
1749				sm_syslog(LOG_ALERT, e->e_id,
1750					  "Truncated MIME %s header due to field size (length = %ld) (possible attack)",
1751					  h->h_field, (unsigned long) len);
1752				if (tTd(34, 11))
1753					sm_dprintf("  truncated MIME %s header due to field size  (length = %ld) (possible attack)\n",
1754						   h->h_field,
1755						   (unsigned long) len);
1756			}
1757		}
1758
1759		if (MaxMimeHeaderLength > 0 &&
1760		    wordinclass(h->h_field,
1761				macid("{checkMIMETextHeaders}")))
1762		{
1763			size_t len;
1764
1765			len = strlen(h->h_value);
1766			if (len > (size_t) MaxMimeHeaderLength)
1767			{
1768				h->h_value[MaxMimeHeaderLength - 1] = '\0';
1769				sm_syslog(LOG_ALERT, e->e_id,
1770					  "Truncated long MIME %s header (length = %ld) (possible attack)",
1771					  h->h_field, (unsigned long) len);
1772				if (tTd(34, 11))
1773					sm_dprintf("  truncated long MIME %s header (length = %ld) (possible attack)\n",
1774						   h->h_field,
1775						   (unsigned long) len);
1776			}
1777		}
1778
1779		if (MaxMimeHeaderLength > 0 &&
1780		    wordinclass(h->h_field,
1781				macid("{checkMIMEHeaders}")))
1782		{
1783			size_t len;
1784
1785			len = strlen(h->h_value);
1786			if (shorten_rfc822_string(h->h_value,
1787						  MaxMimeHeaderLength))
1788			{
1789				if (len < MaxMimeHeaderLength)
1790				{
1791					/* we only rebalanced a bogus header */
1792					sm_syslog(LOG_ALERT, e->e_id,
1793						  "Fixed MIME %s header (possible attack)",
1794						  h->h_field);
1795					if (tTd(34, 11))
1796						sm_dprintf("  fixed MIME %s header (possible attack)\n",
1797							   h->h_field);
1798				}
1799				else
1800				{
1801					/* we actually shortened header */
1802					sm_syslog(LOG_ALERT, e->e_id,
1803						  "Truncated long MIME %s header (length = %ld) (possible attack)",
1804						  h->h_field,
1805						  (unsigned long) len);
1806					if (tTd(34, 11))
1807						sm_dprintf("  truncated long MIME %s header (length = %ld) (possible attack)\n",
1808							   h->h_field,
1809							   (unsigned long) len);
1810				}
1811			}
1812		}
1813
1814		/*
1815		**  Suppress Content-Transfer-Encoding: if we are MIMEing
1816		**  and we are potentially converting from 8 bit to 7 bit
1817		**  MIME.  If converting, add a new CTE header in
1818		**  mime8to7().
1819		*/
1820
1821		if (bitset(H_CTE, h->h_flags) &&
1822		    bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME,
1823			   mci->mci_flags) &&
1824		    !bitset(M87F_NO8TO7, flags))
1825		{
1826			if (tTd(34, 11))
1827				sm_dprintf(" (skipped (content-transfer-encoding))\n");
1828			continue;
1829		}
1830
1831		if (bitset(MCIF_INMIME, mci->mci_flags))
1832		{
1833			if (tTd(34, 11))
1834				sm_dprintf("\n");
1835			if (!put_vanilla_header(h, p, mci))
1836				goto writeerr;
1837			continue;
1838		}
1839
1840		if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
1841		    !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) &&
1842		    (h->h_macro == '\0' ||
1843		     (q = macvalue(bitidx(h->h_macro), e)) == NULL ||
1844		     *q == '\0'))
1845		{
1846			if (tTd(34, 11))
1847				sm_dprintf(" (skipped)\n");
1848			continue;
1849		}
1850
1851		/* handle Resent-... headers specially */
1852		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
1853		{
1854			if (tTd(34, 11))
1855				sm_dprintf(" (skipped (resent))\n");
1856			continue;
1857		}
1858
1859		/* suppress return receipts if requested */
1860		if (bitset(H_RECEIPTTO, h->h_flags) &&
1861		    (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags)))
1862		{
1863			if (tTd(34, 11))
1864				sm_dprintf(" (skipped (receipt))\n");
1865			continue;
1866		}
1867
1868		/* macro expand value if generated internally */
1869		if (bitset(H_DEFAULT, h->h_flags) ||
1870		    bitset(H_BINDLATE, h->h_flags))
1871		{
1872			expand(p, buf, sizeof(buf), e);
1873			p = buf;
1874			if (*p == '\0')
1875			{
1876				if (tTd(34, 11))
1877					sm_dprintf(" (skipped -- null value)\n");
1878				continue;
1879			}
1880		}
1881
1882		if (bitset(H_BCC, h->h_flags))
1883		{
1884			/* Bcc: field -- either truncate or delete */
1885			if (bitset(EF_DELETE_BCC, e->e_flags))
1886			{
1887				if (tTd(34, 11))
1888					sm_dprintf(" (skipped -- bcc)\n");
1889			}
1890			else
1891			{
1892				/* no other recipient headers: truncate value */
1893				(void) sm_strlcpyn(obuf, sizeof(obuf), 2,
1894						   h->h_field, ":");
1895				if (!putline(obuf, mci))
1896					goto writeerr;
1897			}
1898			continue;
1899		}
1900
1901		if (tTd(34, 11))
1902			sm_dprintf("\n");
1903
1904		if (bitset(H_FROM|H_RCPT, h->h_flags))
1905		{
1906			/* address field */
1907			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
1908
1909			if (bitset(H_FROM, h->h_flags))
1910				oldstyle = false;
1911			if (!commaize(h, p, oldstyle, mci, e,
1912				      PXLF_HEADER | PXLF_STRIPMQUOTE)
1913			    && bitnset(M_xSMTP, mci->mci_mailer->m_flags))
1914				goto writeerr;
1915		}
1916		else
1917		{
1918			if (!put_vanilla_header(h, p, mci))
1919				goto writeerr;
1920		}
1921	}
1922
1923	/*
1924	**  If we are converting this to a MIME message, add the
1925	**  MIME headers (but not in MIME mode!).
1926	*/
1927
1928#if MIME8TO7
1929	if (bitset(MM_MIME8BIT, MimeMode) &&
1930	    bitset(EF_HAS8BIT, e->e_flags) &&
1931	    !bitset(EF_DONT_MIME, e->e_flags) &&
1932	    !bitnset(M_8BITS, mci->mci_mailer->m_flags) &&
1933	    !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) &&
1934	    hvalue("MIME-Version", e->e_header) == NULL)
1935	{
1936		if (!putline("MIME-Version: 1.0", mci))
1937			goto writeerr;
1938		if (hvalue("Content-Type", e->e_header) == NULL)
1939		{
1940			(void) sm_snprintf(obuf, sizeof(obuf),
1941					"Content-Type: text/plain; charset=%s",
1942					defcharset(e));
1943			if (!putline(obuf, mci))
1944				goto writeerr;
1945		}
1946		if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL
1947		    && !putline("Content-Transfer-Encoding: 8bit", mci))
1948			goto writeerr;
1949	}
1950#endif /* MIME8TO7 */
1951	return true;
1952
1953  writeerr:
1954	return false;
1955}
1956
1957/*
1958**  PUT_VANILLA_HEADER -- output a fairly ordinary header
1959**
1960**	Parameters:
1961**		h -- the structure describing this header
1962**		v -- the value of this header
1963**		mci -- the connection info for output
1964**
1965**	Returns:
1966**		true iff header was written successfully
1967*/
1968
1969static bool
1970put_vanilla_header(h, v, mci)
1971	HDR *h;
1972	char *v;
1973	MCI *mci;
1974{
1975	register char *nlp;
1976	register char *obp;
1977	int putflags;
1978	char obuf[MAXLINE + 256];	/* additional length for h_field */
1979
1980	putflags = PXLF_HEADER | PXLF_STRIPMQUOTE;
1981	if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
1982		putflags |= PXLF_STRIP8BIT;
1983	(void) sm_snprintf(obuf, sizeof(obuf), "%.200s:", h->h_field);
1984	obp = obuf + strlen(obuf);
1985	while ((nlp = strchr(v, '\n')) != NULL)
1986	{
1987		int l;
1988
1989		l = nlp - v;
1990
1991		/*
1992		**  XXX This is broken for SPACELEFT()==0
1993		**  However, SPACELEFT() is always > 0 unless MAXLINE==1.
1994		*/
1995
1996		if (SPACELEFT(obuf, obp) - 1 < (size_t) l)
1997			l = SPACELEFT(obuf, obp) - 1;
1998
1999		(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
2000		if (!putxline(obuf, strlen(obuf), mci, putflags))
2001			goto writeerr;
2002		v += l + 1;
2003		obp = obuf;
2004		if (*v != ' ' && *v != '\t')
2005			*obp++ = ' ';
2006	}
2007
2008	/* XXX This is broken for SPACELEFT()==0 */
2009	(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s",
2010			   (int) (SPACELEFT(obuf, obp) - 1), v);
2011	return putxline(obuf, strlen(obuf), mci, putflags);
2012
2013  writeerr:
2014	return false;
2015}
2016
2017/*
2018**  COMMAIZE -- output a header field, making a comma-translated list.
2019**
2020**	Parameters:
2021**		h -- the header field to output.
2022**		p -- the value to put in it.
2023**		oldstyle -- true if this is an old style header.
2024**		mci -- the connection information.
2025**		e -- the envelope containing the message.
2026**		putflags -- flags for putxline()
2027**
2028**	Returns:
2029**		true iff header field was written successfully
2030**
2031**	Side Effects:
2032**		outputs "p" to "mci".
2033*/
2034
2035bool
2036commaize(h, p, oldstyle, mci, e, putflags)
2037	register HDR *h;
2038	register char *p;
2039	bool oldstyle;
2040	register MCI *mci;
2041	register ENVELOPE *e;
2042	int putflags;
2043{
2044	register char *obp;
2045	int opos, omax, spaces;
2046	bool firstone = true;
2047	char **res;
2048	char obuf[MAXLINE + 3];
2049
2050	/*
2051	**  Output the address list translated by the
2052	**  mailer and with commas.
2053	*/
2054
2055	if (tTd(14, 2))
2056		sm_dprintf("commaize(%s:%s)\n", h->h_field, p);
2057
2058	if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
2059		putflags |= PXLF_STRIP8BIT;
2060
2061	obp = obuf;
2062	(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s:", h->h_field);
2063	/* opos = strlen(obp); instead of the next 3 lines? */
2064	opos = strlen(h->h_field) + 1;
2065	if (opos > 201)
2066		opos = 201;
2067	obp += opos;
2068
2069	spaces = 0;
2070	while (*p != '\0' && isascii(*p) && isspace(*p))
2071	{
2072		++spaces;
2073		++p;
2074	}
2075	if (spaces > 0)
2076	{
2077		SM_ASSERT(sizeof(obuf) > opos  * 2);
2078
2079		/*
2080		**  Restrict number of spaces to half the length of buffer
2081		**  so the header field body can be put in here too.
2082		**  Note: this is a hack...
2083		*/
2084
2085		if (spaces > sizeof(obuf) / 2)
2086			spaces = sizeof(obuf) / 2;
2087		(void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%*s", spaces,
2088				"");
2089		opos += spaces;
2090		obp += spaces;
2091		SM_ASSERT(obp < &obuf[MAXLINE]);
2092	}
2093
2094	omax = mci->mci_mailer->m_linelimit - 2;
2095	if (omax < 0 || omax > 78)
2096		omax = 78;
2097
2098	/*
2099	**  Run through the list of values.
2100	*/
2101
2102	while (*p != '\0')
2103	{
2104		register char *name;
2105		register int c;
2106		char savechar;
2107		int flags;
2108		auto int status;
2109
2110		/*
2111		**  Find the end of the name.  New style names
2112		**  end with a comma, old style names end with
2113		**  a space character.  However, spaces do not
2114		**  necessarily delimit an old-style name -- at
2115		**  signs mean keep going.
2116		*/
2117
2118		/* find end of name */
2119		while ((isascii(*p) && isspace(*p)) || *p == ',')
2120			p++;
2121		name = p;
2122		res = NULL;
2123		for (;;)
2124		{
2125			auto char *oldp;
2126			char pvpbuf[PSBUFSIZE];
2127
2128			res = prescan(p, oldstyle ? ' ' : ',', pvpbuf,
2129				      sizeof(pvpbuf), &oldp, ExtTokenTab, false);
2130			p = oldp;
2131#if _FFR_IGNORE_BOGUS_ADDR
2132			/* ignore addresses that can't be parsed */
2133			if (res == NULL)
2134			{
2135				name = p;
2136				continue;
2137			}
2138#endif /* _FFR_IGNORE_BOGUS_ADDR */
2139
2140			/* look to see if we have an at sign */
2141			while (*p != '\0' && isascii(*p) && isspace(*p))
2142				p++;
2143
2144			if (*p != '@')
2145			{
2146				p = oldp;
2147				break;
2148			}
2149			++p;
2150			while (*p != '\0' && isascii(*p) && isspace(*p))
2151				p++;
2152		}
2153		/* at the end of one complete name */
2154
2155		/* strip off trailing white space */
2156		while (p >= name &&
2157		       ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
2158			p--;
2159		if (++p == name)
2160			continue;
2161
2162		/*
2163		**  if prescan() failed go a bit backwards; this is a hack,
2164		**  there should be some better error recovery.
2165		*/
2166
2167		if (res == NULL && p > name &&
2168		    !((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
2169			--p;
2170		savechar = *p;
2171		*p = '\0';
2172
2173		/* translate the name to be relative */
2174		flags = RF_HEADERADDR|RF_ADDDOMAIN;
2175		if (bitset(H_FROM, h->h_flags))
2176			flags |= RF_SENDERADDR;
2177#if USERDB
2178		else if (e->e_from.q_mailer != NULL &&
2179			 bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags))
2180		{
2181			char *q;
2182
2183			q = udbsender(name, e->e_rpool);
2184			if (q != NULL)
2185				name = q;
2186		}
2187#endif /* USERDB */
2188		status = EX_OK;
2189		name = remotename(name, mci->mci_mailer, flags, &status, e);
2190		if (status != EX_OK && bitnset(M_xSMTP, mci->mci_mailer->m_flags))
2191		{
2192			if (status == EX_TEMPFAIL)
2193				mci->mci_flags |= MCIF_NOTSTICKY;
2194			goto writeerr;
2195		}
2196		if (*name == '\0')
2197		{
2198			*p = savechar;
2199			continue;
2200		}
2201		name = denlstring(name, false, true);
2202
2203		/* output the name with nice formatting */
2204		opos += strlen(name);
2205		if (!firstone)
2206			opos += 2;
2207		if (opos > omax && !firstone)
2208		{
2209			(void) sm_strlcpy(obp, ",\n", SPACELEFT(obuf, obp));
2210			if (!putxline(obuf, strlen(obuf), mci, putflags))
2211				goto writeerr;
2212			obp = obuf;
2213			(void) sm_strlcpy(obp, "        ", sizeof(obuf));
2214			opos = strlen(obp);
2215			obp += opos;
2216			opos += strlen(name);
2217		}
2218		else if (!firstone)
2219		{
2220			(void) sm_strlcpy(obp, ", ", SPACELEFT(obuf, obp));
2221			obp += 2;
2222		}
2223
2224		while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
2225			*obp++ = c;
2226		firstone = false;
2227		*p = savechar;
2228	}
2229	if (obp < &obuf[sizeof(obuf)])
2230		*obp = '\0';
2231	else
2232		obuf[sizeof(obuf) - 1] = '\0';
2233	return putxline(obuf, strlen(obuf), mci, putflags);
2234
2235  writeerr:
2236	return false;
2237}
2238
2239/*
2240**  COPYHEADER -- copy header list
2241**
2242**	This routine is the equivalent of newstr for header lists
2243**
2244**	Parameters:
2245**		header -- list of header structures to copy.
2246**		rpool -- resource pool, or NULL
2247**
2248**	Returns:
2249**		a copy of 'header'.
2250**
2251**	Side Effects:
2252**		none.
2253*/
2254
2255HDR *
2256copyheader(header, rpool)
2257	register HDR *header;
2258	SM_RPOOL_T *rpool;
2259{
2260	register HDR *newhdr;
2261	HDR *ret;
2262	register HDR **tail = &ret;
2263
2264	while (header != NULL)
2265	{
2266		newhdr = (HDR *) sm_rpool_malloc_x(rpool, sizeof(*newhdr));
2267		STRUCTCOPY(*header, *newhdr);
2268		*tail = newhdr;
2269		tail = &newhdr->h_link;
2270		header = header->h_link;
2271	}
2272	*tail = NULL;
2273
2274	return ret;
2275}
2276
2277/*
2278**  FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header
2279**
2280**	Run through all of the parameters of a MIME header and
2281**	possibly truncate and rebalance the parameter according
2282**	to MaxMimeFieldLength.
2283**
2284**	Parameters:
2285**		h -- the header to truncate/rebalance
2286**		e -- the current envelope
2287**
2288**	Returns:
2289**		length of last offending field, 0 if all ok.
2290**
2291**	Side Effects:
2292**		string modified in place
2293*/
2294
2295static size_t
2296fix_mime_header(h, e)
2297	HDR *h;
2298	ENVELOPE *e;
2299{
2300	char *begin = h->h_value;
2301	char *end;
2302	size_t len = 0;
2303	size_t retlen = 0;
2304
2305	if (begin == NULL || *begin == '\0')
2306		return 0;
2307
2308	/* Split on each ';' */
2309	/* find_character() never returns NULL */
2310	while ((end = find_character(begin, ';')) != NULL)
2311	{
2312		char save = *end;
2313		char *bp;
2314
2315		*end = '\0';
2316
2317		len = strlen(begin);
2318
2319		/* Shorten individual parameter */
2320		if (shorten_rfc822_string(begin, MaxMimeFieldLength))
2321		{
2322			if (len < MaxMimeFieldLength)
2323			{
2324				/* we only rebalanced a bogus field */
2325				sm_syslog(LOG_ALERT, e->e_id,
2326					  "Fixed MIME %s header field (possible attack)",
2327					  h->h_field);
2328				if (tTd(34, 11))
2329					sm_dprintf("  fixed MIME %s header field (possible attack)\n",
2330						   h->h_field);
2331			}
2332			else
2333			{
2334				/* we actually shortened the header */
2335				retlen = len;
2336			}
2337		}
2338
2339		/* Collapse the possibly shortened string with rest */
2340		bp = begin + strlen(begin);
2341		if (bp != end)
2342		{
2343			char *ep = end;
2344
2345			*end = save;
2346			end = bp;
2347
2348			/* copy character by character due to overlap */
2349			while (*ep != '\0')
2350				*bp++ = *ep++;
2351			*bp = '\0';
2352		}
2353		else
2354			*end = save;
2355		if (*end == '\0')
2356			break;
2357
2358		/* Move past ';' */
2359		begin = end + 1;
2360	}
2361	return retlen;
2362}
2363