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