1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)headers.c	5.20 (Berkeley) 07/12/92";
11 #endif /* not lint */
12 
13 # include <sys/param.h>
14 # include <errno.h>
15 # include "sendmail.h"
16 
17 /*
18 **  CHOMPHEADER -- process and save a header line.
19 **
20 **	Called by collect and by readcf to deal with header lines.
21 **
22 **	Parameters:
23 **		line -- header as a text line.
24 **		def -- if set, this is a default value.
25 **		e -- the envelope including this header.
26 **
27 **	Returns:
28 **		flags for this header.
29 **
30 **	Side Effects:
31 **		The header is saved on the header list.
32 **		Contents of 'line' are destroyed.
33 */
34 
35 chompheader(line, def, e)
36 	char *line;
37 	bool def;
38 	register ENVELOPE *e;
39 {
40 	register char *p;
41 	register HDR *h;
42 	HDR **hp;
43 	char *fname;
44 	char *fvalue;
45 	struct hdrinfo *hi;
46 	bool cond = FALSE;
47 	BITMAP mopts;
48 	extern char *crackaddr();
49 
50 	if (tTd(31, 6))
51 		printf("chompheader: %s\n", line);
52 
53 	/* strip off options */
54 	clrbitmap(mopts);
55 	p = line;
56 	if (*p == '?')
57 	{
58 		/* have some */
59 		register char *q = index(p + 1, *p);
60 
61 		if (q != NULL)
62 		{
63 			*q++ = '\0';
64 			while (*++p != '\0')
65 				setbitn(*p, mopts);
66 			p = q;
67 		}
68 		else
69 			usrerr("chompheader: syntax error, line \"%s\"", line);
70 		cond = TRUE;
71 	}
72 
73 	/* find canonical name */
74 	fname = p;
75 	p = index(p, ':');
76 	if (p == NULL)
77 	{
78 		syserr("chompheader: syntax error, line \"%s\"", line);
79 		return (0);
80 	}
81 	fvalue = &p[1];
82 	while (isspace(*--p))
83 		continue;
84 	*++p = '\0';
85 	makelower(fname);
86 
87 	/* strip field value on front */
88 	if (*fvalue == ' ')
89 		fvalue++;
90 
91 	/* see if it is a known type */
92 	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
93 	{
94 		if (strcmp(hi->hi_field, fname) == 0)
95 			break;
96 	}
97 
98 	/* see if this is a resent message */
99 	if (!def && bitset(H_RESENT, hi->hi_flags))
100 		e->e_flags |= EF_RESENT;
101 
102 	/* if this means "end of header" quit now */
103 	if (bitset(H_EOH, hi->hi_flags))
104 		return (hi->hi_flags);
105 
106 	/* drop explicit From: if same as what we would generate -- for MH */
107 	p = "resent-from";
108 	if (!bitset(EF_RESENT, e->e_flags))
109 		p += 7;
110 	if (!def && !QueueRun && strcmp(fname, p) == 0)
111 	{
112 		if (e->e_from.q_paddr != NULL &&
113 		    strcmp(fvalue, e->e_from.q_paddr) == 0)
114 			return (hi->hi_flags);
115 	}
116 
117 	/* delete default value for this header */
118 	for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
119 	{
120 		if (strcmp(fname, h->h_field) == 0 &&
121 		    bitset(H_DEFAULT, h->h_flags) &&
122 		    !bitset(H_FORCE, h->h_flags))
123 			h->h_value = NULL;
124 	}
125 
126 	/* create a new node */
127 	h = (HDR *) xalloc(sizeof *h);
128 	h->h_field = newstr(fname);
129 	h->h_value = NULL;
130 	h->h_link = NULL;
131 	bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts);
132 	*hp = h;
133 	h->h_flags = hi->hi_flags;
134 	if (def)
135 		h->h_flags |= H_DEFAULT;
136 	if (cond)
137 		h->h_flags |= H_CHECK;
138 	if (h->h_value != NULL)
139 		free((char *) h->h_value);
140 	h->h_value = newstr(fvalue);
141 
142 	/* hack to see if this is a new format message */
143 	if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
144 	    (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
145 	     index(fvalue, '<') != NULL || index(fvalue, ';') != NULL))
146 	{
147 		e->e_flags &= ~EF_OLDSTYLE;
148 	}
149 
150 	return (h->h_flags);
151 }
152 /*
153 **  ADDHEADER -- add a header entry to the end of the queue.
154 **
155 **	This bypasses the special checking of chompheader.
156 **
157 **	Parameters:
158 **		field -- the name of the header field.
159 **		value -- the value of the field.  It must be lower-cased.
160 **		e -- the envelope to add them to.
161 **
162 **	Returns:
163 **		none.
164 **
165 **	Side Effects:
166 **		adds the field on the list of headers for this envelope.
167 */
168 
169 addheader(field, value, e)
170 	char *field;
171 	char *value;
172 	ENVELOPE *e;
173 {
174 	register HDR *h;
175 	register struct hdrinfo *hi;
176 	HDR **hp;
177 
178 	/* find info struct */
179 	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
180 	{
181 		if (strcmp(field, hi->hi_field) == 0)
182 			break;
183 	}
184 
185 	/* find current place in list -- keep back pointer? */
186 	for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
187 	{
188 		if (strcmp(field, h->h_field) == 0)
189 			break;
190 	}
191 
192 	/* allocate space for new header */
193 	h = (HDR *) xalloc(sizeof *h);
194 	h->h_field = field;
195 	h->h_value = newstr(value);
196 	h->h_link = *hp;
197 	h->h_flags = hi->hi_flags | H_DEFAULT;
198 	clrbitmap(h->h_mflags);
199 	*hp = h;
200 }
201 /*
202 **  HVALUE -- return value of a header.
203 **
204 **	Only "real" fields (i.e., ones that have not been supplied
205 **	as a default) are used.
206 **
207 **	Parameters:
208 **		field -- the field name.
209 **		e -- the envelope containing the header.
210 **
211 **	Returns:
212 **		pointer to the value part.
213 **		NULL if not found.
214 **
215 **	Side Effects:
216 **		none.
217 */
218 
219 char *
220 hvalue(field, e)
221 	char *field;
222 	register ENVELOPE *e;
223 {
224 	register HDR *h;
225 
226 	for (h = e->e_header; h != NULL; h = h->h_link)
227 	{
228 		if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
229 			return (h->h_value);
230 	}
231 	return (NULL);
232 }
233 /*
234 **  ISHEADER -- predicate telling if argument is a header.
235 **
236 **	A line is a header if it has a single word followed by
237 **	optional white space followed by a colon.
238 **
239 **	Parameters:
240 **		s -- string to check for possible headerness.
241 **
242 **	Returns:
243 **		TRUE if s is a header.
244 **		FALSE otherwise.
245 **
246 **	Side Effects:
247 **		none.
248 */
249 
250 bool
251 isheader(s)
252 	register char *s;
253 {
254 	while (*s > ' ' && *s != ':' && *s != '\0')
255 		s++;
256 
257 	/* following technically violates RFC822 */
258 	while (isspace(*s))
259 		s++;
260 
261 	return (*s == ':');
262 }
263 /*
264 **  EATHEADER -- run through the stored header and extract info.
265 **
266 **	Parameters:
267 **		e -- the envelope to process.
268 **
269 **	Returns:
270 **		none.
271 **
272 **	Side Effects:
273 **		Sets a bunch of global variables from information
274 **			in the collected header.
275 **		Aborts the message if the hop count is exceeded.
276 */
277 
278 eatheader(e)
279 	register ENVELOPE *e;
280 {
281 	register HDR *h;
282 	register char *p;
283 	int hopcnt = 0;
284 
285 	if (tTd(32, 1))
286 		printf("----- collected header -----\n");
287 	for (h = e->e_header; h != NULL; h = h->h_link)
288 	{
289 		extern char *capitalize();
290 
291 		if (tTd(32, 1))
292 			printf("%s: %s\n", capitalize(h->h_field), h->h_value);
293 		/* count the number of times it has been processed */
294 		if (bitset(H_TRACE, h->h_flags))
295 			hopcnt++;
296 
297 		/* send to this person if we so desire */
298 		if (GrabTo && bitset(H_RCPT, h->h_flags) &&
299 		    !bitset(H_DEFAULT, h->h_flags) &&
300 		    (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags)))
301 		{
302 			sendtolist(h->h_value, (ADDRESS *) NULL,
303 				   &e->e_sendqueue, e);
304 		}
305 
306 		/* log the message-id */
307 #ifdef LOG
308 		if (!QueueRun && LogLevel > 8 && h->h_value != NULL &&
309 		    strcmp(h->h_field, "message-id") == 0)
310 		{
311 			char buf[MAXNAME];
312 
313 			p = h->h_value;
314 			if (bitset(H_DEFAULT, h->h_flags))
315 			{
316 				expand(p, buf, &buf[sizeof buf], e);
317 				p = buf;
318 			}
319 			syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p);
320 		}
321 #endif LOG
322 	}
323 	if (tTd(32, 1))
324 		printf("----------------------------\n");
325 
326 	/* store hop count */
327 	if (hopcnt > e->e_hopcount)
328 		e->e_hopcount = hopcnt;
329 
330 	/* message priority */
331 	p = hvalue("precedence", e);
332 	if (p != NULL)
333 		e->e_class = priencode(p);
334 	if (!QueueRun)
335 		e->e_msgpriority = e->e_msgsize
336 				 - e->e_class * WkClassFact
337 				 + e->e_nrcpts * WkRecipFact;
338 
339 	/* return receipt to */
340 	p = hvalue("return-receipt-to", e);
341 	if (p != NULL)
342 		e->e_receiptto = p;
343 
344 	/* errors to */
345 	p = hvalue("errors-to", e);
346 	if (p != NULL)
347 		sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue, e);
348 
349 	/* full name of from person */
350 	p = hvalue("full-name", e);
351 	if (p != NULL)
352 		define('x', p, e);
353 
354 	/* date message originated */
355 	p = hvalue("posted-date", e);
356 	if (p == NULL)
357 		p = hvalue("date", e);
358 	if (p != NULL)
359 	{
360 		define('a', p, e);
361 		/* we don't have a good way to do canonical conversion ....
362 		define('d', newstr(arpatounix(p)), e);
363 		.... so we will ignore the problem for the time being */
364 	}
365 
366 	/*
367 	**  Log collection information.
368 	*/
369 
370 # ifdef LOG
371 	if (!QueueRun && LogLevel > 1)
372 	{
373 		char hbuf[100], *name = hbuf;
374 
375 		if (RealHostName == NULL)
376 			name = "local";
377 		else if (RealHostName[0] == '[')
378 			name = RealHostName;
379 		else
380 			(void)sprintf(hbuf, "%.80s (%s)",
381 			    RealHostName, inet_ntoa(RealHostAddr.sin_addr));
382 		syslog(LOG_INFO,
383 		    "%s: from=%s, size=%ld, class=%d, received from %s\n",
384 		    e->e_id, e->e_from.q_paddr, e->e_msgsize,
385 		    e->e_class, name);
386 	}
387 # endif LOG
388 }
389 /*
390 **  PRIENCODE -- encode external priority names into internal values.
391 **
392 **	Parameters:
393 **		p -- priority in ascii.
394 **
395 **	Returns:
396 **		priority as a numeric level.
397 **
398 **	Side Effects:
399 **		none.
400 */
401 
402 priencode(p)
403 	char *p;
404 {
405 	register int i;
406 
407 	for (i = 0; i < NumPriorities; i++)
408 	{
409 		if (!strcasecmp(p, Priorities[i].pri_name))
410 			return (Priorities[i].pri_val);
411 	}
412 
413 	/* unknown priority */
414 	return (0);
415 }
416 /*
417 **  CRACKADDR -- parse an address and turn it into a macro
418 **
419 **	This doesn't actually parse the address -- it just extracts
420 **	it and replaces it with "$g".  The parse is totally ad hoc
421 **	and isn't even guaranteed to leave something syntactically
422 **	identical to what it started with.  However, it does leave
423 **	something semantically identical.
424 **
425 **	This algorithm has been cleaned up to handle a wider range
426 **	of cases -- notably quoted and backslash escaped strings.
427 **	This modification makes it substantially better at preserving
428 **	the original syntax.
429 **
430 **	Parameters:
431 **		addr -- the address to be cracked.
432 **
433 **	Returns:
434 **		a pointer to the new version.
435 **
436 **	Side Effects:
437 **		none.
438 **
439 **	Warning:
440 **		The return value is saved in local storage and should
441 **		be copied if it is to be reused.
442 */
443 
444 char *
445 crackaddr(addr)
446 	register char *addr;
447 {
448 	register char *p;
449 	register char c;
450 	int cmtlev;
451 	int copylev;
452 	bool qmode;
453 	bool putgmac = FALSE;
454 	register char *bp;
455 	static char buf[MAXNAME];
456 
457 	if (tTd(33, 1))
458 		printf("crackaddr(%s)\n", addr);
459 
460 	/* strip leading spaces */
461 	while (*addr != '\0' && isspace(*addr))
462 		addr++;
463 
464 	/*
465 	**  Start by assuming we have no angle brackets.  This will be
466 	**  adjusted later if we find them.
467 	*/
468 
469 	bp = buf;
470 	p = addr;
471 	copylev = cmtlev = 0;
472 	qmode = FALSE;
473 
474 	while ((c = *p++) != '\0')
475 	{
476 		if (copylev > 0 || c == ' ')
477 			*bp++ = c;
478 
479 		/* check for backslash escapes */
480 		if (c == '\\')
481 		{
482 			if ((c = *p++) == '\0')
483 			{
484 				/* too far */
485 				p--;
486 				goto putg;
487 			}
488 			if (copylev > 0)
489 				*bp++ = c;
490 			goto putg;
491 		}
492 
493 		/* check for quoted strings */
494 		if (c == '"')
495 		{
496 			qmode = !qmode;
497 			continue;
498 		}
499 		if (qmode)
500 			goto putg;
501 
502 		/* check for comments */
503 		if (c == '(')
504 		{
505 			cmtlev++;
506 			if (copylev++ <= 0)
507 				*bp++ = c;
508 		}
509 		if (cmtlev > 0)
510 		{
511 			if (c == ')')
512 			{
513 				cmtlev--;
514 				copylev--;
515 			}
516 			continue;
517 		}
518 
519 		/* check for angle brackets */
520 		if (c == '<')
521 		{
522 			/* oops -- have to change our mind */
523 			bcopy(addr, buf, p - addr);
524 			bp = &buf[p - addr];
525 			copylev = 0;
526 			putgmac = FALSE;
527 			continue;
528 		}
529 
530 		if (c == '>')
531 		{
532 			if (copylev++ <= 0)
533 				*bp++ = c;
534 			continue;
535 		}
536 
537 		/* must be a real address character */
538 	putg:
539 		if (copylev <= 0 && !putgmac)
540 		{
541 			*bp++ = '\001';
542 			*bp++ = 'g';
543 			putgmac = TRUE;
544 		}
545 	}
546 
547 	*bp++ = '\0';
548 
549 	if (tTd(33, 1))
550 		printf("crackaddr=>`%s'\n", buf);
551 
552 	return (buf);
553 }
554 /*
555 **  PUTHEADER -- put the header part of a message from the in-core copy
556 **
557 **	Parameters:
558 **		fp -- file to put it on.
559 **		m -- mailer to use.
560 **		e -- envelope to use.
561 **
562 **	Returns:
563 **		none.
564 **
565 **	Side Effects:
566 **		none.
567 */
568 
569 putheader(fp, m, e)
570 	register FILE *fp;
571 	register MAILER *m;
572 	register ENVELOPE *e;
573 {
574 	char buf[MAX(MAXFIELD,BUFSIZ)];
575 	register HDR *h;
576 	extern char *arpadate();
577 	extern char *capitalize();
578 	char obuf[MAX(MAXFIELD,MAXLINE)];
579 
580 	for (h = e->e_header; h != NULL; h = h->h_link)
581 	{
582 		register char *p;
583 		extern bool bitintersect();
584 
585 		if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
586 		    !bitintersect(h->h_mflags, m->m_flags))
587 			continue;
588 
589 		/* handle Resent-... headers specially */
590 		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
591 			continue;
592 
593 		p = h->h_value;
594 		if (bitset(H_DEFAULT, h->h_flags))
595 		{
596 			/* macro expand value if generated internally */
597 			expand(p, buf, &buf[sizeof buf], e);
598 			p = buf;
599 			if (p == NULL || *p == '\0')
600 				continue;
601 		}
602 
603 		if (bitset(H_FROM|H_RCPT, h->h_flags))
604 		{
605 			/* address field */
606 			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
607 
608 			if (bitset(H_FROM, h->h_flags))
609 				oldstyle = FALSE;
610 			commaize(h, p, fp, oldstyle, m, e);
611 		}
612 		else
613 		{
614 			/* vanilla header line */
615 			register char *nlp;
616 
617 			(void) sprintf(obuf, "%s: ", capitalize(h->h_field));
618 			while ((nlp = index(p, '\n')) != NULL)
619 			{
620 				*nlp = '\0';
621 				(void) strcat(obuf, p);
622 				*nlp = '\n';
623 				putline(obuf, fp, m);
624 				p = ++nlp;
625 				obuf[0] = '\0';
626 			}
627 			(void) strcat(obuf, p);
628 			putline(obuf, fp, m);
629 		}
630 	}
631 }
632 /*
633 **  COMMAIZE -- output a header field, making a comma-translated list.
634 **
635 **	Parameters:
636 **		h -- the header field to output.
637 **		p -- the value to put in it.
638 **		fp -- file to put it to.
639 **		oldstyle -- TRUE if this is an old style header.
640 **		m -- a pointer to the mailer descriptor.  If NULL,
641 **			don't transform the name at all.
642 **		e -- the envelope containing the message.
643 **
644 **	Returns:
645 **		none.
646 **
647 **	Side Effects:
648 **		outputs "p" to file "fp".
649 */
650 
651 commaize(h, p, fp, oldstyle, m, e)
652 	register HDR *h;
653 	register char *p;
654 	FILE *fp;
655 	bool oldstyle;
656 	register MAILER *m;
657 	register ENVELOPE *e;
658 {
659 	register char *obp;
660 	int opos;
661 	bool firstone = TRUE;
662 	char obuf[MAXLINE + 3];
663 
664 	/*
665 	**  Output the address list translated by the
666 	**  mailer and with commas.
667 	*/
668 
669 	if (tTd(14, 2))
670 		printf("commaize(%s: %s)\n", h->h_field, p);
671 
672 	obp = obuf;
673 	(void) sprintf(obp, "%s: ", capitalize(h->h_field));
674 	opos = strlen(h->h_field) + 2;
675 	obp += opos;
676 
677 	/*
678 	**  Run through the list of values.
679 	*/
680 
681 	while (*p != '\0')
682 	{
683 		register char *name;
684 		register int c;
685 		char savechar;
686 		extern char *remotename();
687 		extern char *DelimChar;		/* defined in prescan */
688 
689 		/*
690 		**  Find the end of the name.  New style names
691 		**  end with a comma, old style names end with
692 		**  a space character.  However, spaces do not
693 		**  necessarily delimit an old-style name -- at
694 		**  signs mean keep going.
695 		*/
696 
697 		/* find end of name */
698 		while (isspace(*p) || *p == ',')
699 			p++;
700 		name = p;
701 		for (;;)
702 		{
703 			char *oldp;
704 			char pvpbuf[PSBUFSIZE];
705 			extern bool isatword();
706 			extern char **prescan();
707 
708 			(void) prescan(p, oldstyle ? ' ' : ',', pvpbuf);
709 			p = DelimChar;
710 
711 			/* look to see if we have an at sign */
712 			oldp = p;
713 			while (*p != '\0' && isspace(*p))
714 				p++;
715 
716 			if (*p != '@' && !isatword(p))
717 			{
718 				p = oldp;
719 				break;
720 			}
721 			p += *p == '@' ? 1 : 2;
722 			while (*p != '\0' && isspace(*p))
723 				p++;
724 		}
725 		/* at the end of one complete name */
726 
727 		/* strip off trailing white space */
728 		while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
729 			p--;
730 		if (++p == name)
731 			continue;
732 		savechar = *p;
733 		*p = '\0';
734 
735 		/* translate the name to be relative */
736 		name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE, e);
737 		if (*name == '\0')
738 		{
739 			*p = savechar;
740 			continue;
741 		}
742 
743 		/* output the name with nice formatting */
744 		opos += strlen(name);
745 		if (!firstone)
746 			opos += 2;
747 		if (opos > 78 && !firstone)
748 		{
749 			(void) strcpy(obp, ",\n");
750 			putline(obuf, fp, m);
751 			obp = obuf;
752 			(void) sprintf(obp, "        ");
753 			opos = strlen(obp);
754 			obp += opos;
755 			opos += strlen(name);
756 		}
757 		else if (!firstone)
758 		{
759 			(void) sprintf(obp, ", ");
760 			obp += 2;
761 		}
762 
763 		/* strip off quote bits as we output */
764 		while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
765 		{
766 			if (bitnset(M_7BITS, m->m_flags))
767 				c &= 0177;
768 			*obp++ = c;
769 		}
770 		firstone = FALSE;
771 		*p = savechar;
772 	}
773 	(void) strcpy(obp, "\n");
774 	putline(obuf, fp, m);
775 }
776 /*
777 **  ISATWORD -- tell if the word we are pointing to is "at".
778 **
779 **	Parameters:
780 **		p -- word to check.
781 **
782 **	Returns:
783 **		TRUE -- if p is the word at.
784 **		FALSE -- otherwise.
785 **
786 **	Side Effects:
787 **		none.
788 */
789 
790 bool
791 isatword(p)
792 	register char *p;
793 {
794 	extern char lower();
795 
796 	if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
797 	    p[2] != '\0' && isspace(p[2]))
798 		return (TRUE);
799 	return (FALSE);
800 }
801