1 /*
2 **  Sendmail
3 **  Copyright (c) 1983  Eric P. Allman
4 **  Berkeley, California
5 **
6 **  Copyright (c) 1983 Regents of the University of California.
7 **  All rights reserved.  The Berkeley software License Agreement
8 **  specifies the terms and conditions for redistribution.
9 */
10 
11 #ifndef lint
12 static char	SccsId[] = "@(#)headers.c	5.4 (Berkeley) 06/08/85";
13 #endif not lint
14 
15 # include <errno.h>
16 # include "sendmail.h"
17 
18 /*
19 **  CHOMPHEADER -- process and save a header line.
20 **
21 **	Called by collect and by readcf to deal with header lines.
22 **
23 **	Parameters:
24 **		line -- header as a text line.
25 **		def -- if set, this is a default value.
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)
36 	char *line;
37 	bool def;
38 {
39 	register char *p;
40 	register HDR *h;
41 	HDR **hp;
42 	char *fname;
43 	char *fvalue;
44 	struct hdrinfo *hi;
45 	bool cond = FALSE;
46 	BITMAP mopts;
47 	extern char *crackaddr();
48 
49 # ifdef DEBUG
50 	if (tTd(31, 6))
51 		printf("chompheader: %s\n", line);
52 # endif DEBUG
53 
54 	/* strip off options */
55 	clrbitmap(mopts);
56 	p = line;
57 	if (*p == '?')
58 	{
59 		/* have some */
60 		register char *q = index(p + 1, *p);
61 
62 		if (q != NULL)
63 		{
64 			*q++ = '\0';
65 			while (*++p != '\0')
66 				setbitn(*p, mopts);
67 			p = q;
68 		}
69 		else
70 			syserr("chompheader: syntax error, line \"%s\"", line);
71 		cond = TRUE;
72 	}
73 
74 	/* find canonical name */
75 	fname = p;
76 	p = index(p, ':');
77 	if (p == NULL)
78 	{
79 		syserr("chompheader: syntax error, line \"%s\"", line);
80 		return (0);
81 	}
82 	fvalue = &p[1];
83 	while (isspace(*--p))
84 		continue;
85 	*++p = '\0';
86 	makelower(fname);
87 
88 	/* strip field value on front */
89 	if (*fvalue == ' ')
90 		fvalue++;
91 
92 	/* see if it is a known type */
93 	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
94 	{
95 		if (strcmp(hi->hi_field, fname) == 0)
96 			break;
97 	}
98 
99 	/* see if this is a resent message */
100 	if (!def && bitset(H_RESENT, hi->hi_flags))
101 		CurEnv->e_flags |= EF_RESENT;
102 
103 	/* if this means "end of header" quit now */
104 	if (bitset(H_EOH, hi->hi_flags))
105 		return (hi->hi_flags);
106 
107 	/* drop explicit From: if same as what we would generate -- for MH */
108 	p = "resent-from";
109 	if (!bitset(EF_RESENT, CurEnv->e_flags))
110 		p += 7;
111 	if (!def && !QueueRun && strcmp(fname, p) == 0)
112 	{
113 		if (strcmp(fvalue, CurEnv->e_from.q_paddr) == 0)
114 			return (hi->hi_flags);
115 	}
116 
117 	/* delete default value for this header */
118 	for (hp = &CurEnv->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 		CurEnv->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 **
210 **	Returns:
211 **		pointer to the value part.
212 **		NULL if not found.
213 **
214 **	Side Effects:
215 **		none.
216 */
217 
218 char *
219 hvalue(field)
220 	char *field;
221 {
222 	register HDR *h;
223 
224 	for (h = CurEnv->e_header; h != NULL; h = h->h_link)
225 	{
226 		if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
227 			return (h->h_value);
228 	}
229 	return (NULL);
230 }
231 /*
232 **  ISHEADER -- predicate telling if argument is a header.
233 **
234 **	A line is a header if it has a single word followed by
235 **	optional white space followed by a colon.
236 **
237 **	Parameters:
238 **		s -- string to check for possible headerness.
239 **
240 **	Returns:
241 **		TRUE if s is a header.
242 **		FALSE otherwise.
243 **
244 **	Side Effects:
245 **		none.
246 */
247 
248 bool
249 isheader(s)
250 	register char *s;
251 {
252 	while (*s > ' ' && *s != ':' && *s != '\0')
253 		s++;
254 
255 	/* following technically violates RFC822 */
256 	while (isspace(*s))
257 		s++;
258 
259 	return (*s == ':');
260 }
261 /*
262 **  EATHEADER -- run through the stored header and extract info.
263 **
264 **	Parameters:
265 **		e -- the envelope to process.
266 **
267 **	Returns:
268 **		none.
269 **
270 **	Side Effects:
271 **		Sets a bunch of global variables from information
272 **			in the collected header.
273 **		Aborts the message if the hop count is exceeded.
274 */
275 
276 eatheader(e)
277 	register ENVELOPE *e;
278 {
279 	register HDR *h;
280 	register char *p;
281 	int hopcnt = 0;
282 
283 #ifdef DEBUG
284 	if (tTd(32, 1))
285 		printf("----- collected header -----\n");
286 #endif DEBUG
287 	for (h = e->e_header; h != NULL; h = h->h_link)
288 	{
289 #ifdef DEBUG
290 		extern char *capitalize();
291 
292 		if (tTd(32, 1))
293 			printf("%s: %s\n", capitalize(h->h_field), h->h_value);
294 #endif DEBUG
295 		/* count the number of times it has been processed */
296 		if (bitset(H_TRACE, h->h_flags))
297 			hopcnt++;
298 
299 		/* send to this person if we so desire */
300 		if (GrabTo && bitset(H_RCPT, h->h_flags) &&
301 		    !bitset(H_DEFAULT, h->h_flags) &&
302 		    (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags)))
303 		{
304 			sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
305 		}
306 
307 		/* log the message-id */
308 #ifdef LOG
309 		if (!QueueRun && LogLevel > 8 && h->h_value != NULL &&
310 		    strcmp(h->h_field, "message-id") == 0)
311 		{
312 			char buf[MAXNAME];
313 
314 			p = h->h_value;
315 			if (bitset(H_DEFAULT, h->h_flags))
316 			{
317 				expand(p, buf, &buf[sizeof buf], e);
318 				p = buf;
319 			}
320 			syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p);
321 		}
322 #endif LOG
323 	}
324 #ifdef DEBUG
325 	if (tTd(32, 1))
326 		printf("----------------------------\n");
327 #endif DEBUG
328 
329 	/* store hop count */
330 	if (hopcnt > e->e_hopcount)
331 		e->e_hopcount = hopcnt;
332 
333 	/* message priority */
334 	p = hvalue("precedence");
335 	if (p != NULL)
336 		e->e_class = priencode(p);
337 	if (!QueueRun)
338 		e->e_msgpriority = e->e_msgsize + e->e_ctime - e->e_class * WKPRIFACT;
339 
340 	/* return receipt to */
341 	p = hvalue("return-receipt-to");
342 	if (p != NULL)
343 		e->e_receiptto = p;
344 
345 	/* errors to */
346 	p = hvalue("errors-to");
347 	if (p != NULL)
348 		sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue);
349 
350 	/* from person */
351 	if (OpMode == MD_ARPAFTP)
352 	{
353 		register struct hdrinfo *hi = HdrInfo;
354 
355 		for (p = NULL; p == NULL && hi->hi_field != NULL; hi++)
356 		{
357 			if (bitset(H_FROM, hi->hi_flags))
358 				p = hvalue(hi->hi_field);
359 		}
360 		if (p != NULL)
361 			setsender(p);
362 	}
363 
364 	/* full name of from person */
365 	p = hvalue("full-name");
366 	if (p != NULL)
367 		define('x', p, e);
368 
369 	/* date message originated */
370 	p = hvalue("posted-date");
371 	if (p == NULL)
372 		p = hvalue("date");
373 	if (p != NULL)
374 	{
375 		define('a', p, e);
376 		/* we don't have a good way to do canonical conversion ....
377 		define('d', newstr(arpatounix(p)), e);
378 		.... so we will ignore the problem for the time being */
379 	}
380 
381 	/*
382 	**  Log collection information.
383 	*/
384 
385 # ifdef LOG
386 	if (!QueueRun && LogLevel > 1)
387 	{
388 		syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n",
389 		       CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
390 		       CurEnv->e_class);
391 	}
392 # endif LOG
393 }
394 /*
395 **  PRIENCODE -- encode external priority names into internal values.
396 **
397 **	Parameters:
398 **		p -- priority in ascii.
399 **
400 **	Returns:
401 **		priority as a numeric level.
402 **
403 **	Side Effects:
404 **		none.
405 */
406 
407 priencode(p)
408 	char *p;
409 {
410 	register int i;
411 	extern bool sameword();
412 
413 	for (i = 0; i < NumPriorities; i++)
414 	{
415 		if (sameword(p, Priorities[i].pri_name))
416 			return (Priorities[i].pri_val);
417 	}
418 
419 	/* unknown priority */
420 	return (0);
421 }
422 /*
423 **  CRACKADDR -- parse an address and turn it into a macro
424 **
425 **	This doesn't actually parse the address -- it just extracts
426 **	it and replaces it with "$g".  The parse is totally ad hoc
427 **	and isn't even guaranteed to leave something syntactically
428 **	identical to what it started with.  However, it does leave
429 **	something semantically identical.
430 **
431 **	The process is kind of strange.  There are a number of
432 **	interesting cases:
433 **		1.  comment <address> comment	==> comment <$g> comment
434 **		2.  address			==> address
435 **		3.  address (comment)		==> $g (comment)
436 **		4.  (comment) address		==> (comment) $g
437 **	And then there are the hard cases....
438 **		5.  add (comment) ress		==> $g (comment)
439 **		6.  comment <address (comment)>	==> comment <$g (comment)>
440 **		7.    .... etc ....
441 **
442 **	Parameters:
443 **		addr -- the address to be cracked.
444 **
445 **	Returns:
446 **		a pointer to the new version.
447 **
448 **	Side Effects:
449 **		none.
450 **
451 **	Warning:
452 **		The return value is saved in local storage and should
453 **		be copied if it is to be reused.
454 */
455 
456 char *
457 crackaddr(addr)
458 	register char *addr;
459 {
460 	register char *p;
461 	register int i;
462 	static char buf[MAXNAME];
463 	char *rhs;
464 	bool gotaddr;
465 	register char *bp;
466 
467 # ifdef DEBUG
468 	if (tTd(33, 1))
469 		printf("crackaddr(%s)\n", addr);
470 # endif DEBUG
471 
472 	(void) strcpy(buf, "");
473 	rhs = NULL;
474 
475 	/* strip leading spaces */
476 	while (*addr != '\0' && isspace(*addr))
477 		addr++;
478 
479 	/*
480 	**  See if we have anything in angle brackets.  If so, that is
481 	**  the address part, and the rest is the comment.
482 	*/
483 
484 	p = index(addr, '<');
485 	if (p != NULL)
486 	{
487 		/* copy the beginning of the addr field to the buffer */
488 		*p = '\0';
489 		(void) strcpy(buf, addr);
490 		(void) strcat(buf, "<");
491 		*p++ = '<';
492 
493 		/* skip spaces */
494 		while (isspace(*p))
495 			p++;
496 
497 		/* find the matching right angle bracket */
498 		addr = p;
499 		for (i = 0; *p != '\0'; p++)
500 		{
501 			switch (*p)
502 			{
503 			  case '<':
504 				i++;
505 				break;
506 
507 			  case '>':
508 				i--;
509 				break;
510 			}
511 			if (i < 0)
512 				break;
513 		}
514 
515 		/* p now points to the closing quote (or a null byte) */
516 		if (*p != '\0')
517 		{
518 			/* make rhs point to the extra stuff at the end */
519 			rhs = p;
520 			*p++ = '\0';
521 		}
522 	}
523 
524 	/*
525 	**  Now parse the real address part.  "addr" points to the (null
526 	**  terminated) version of what we are inerested in; rhs points
527 	**  to the extra stuff at the end of the line, if any.
528 	*/
529 
530 	p = addr;
531 
532 	/* now strip out comments */
533 	bp = &buf[strlen(buf)];
534 	gotaddr = FALSE;
535 	for (; *p != '\0'; p++)
536 	{
537 		if (*p == '(')
538 		{
539 			/* copy to matching close paren */
540 			*bp++ = *p++;
541 			for (i = 0; *p != '\0'; p++)
542 			{
543 				*bp++ = *p;
544 				switch (*p)
545 				{
546 				  case '(':
547 					i++;
548 					break;
549 
550 				  case ')':
551 					i--;
552 					break;
553 				}
554 				if (i < 0)
555 					break;
556 			}
557 			continue;
558 		}
559 
560 		/*
561 		**  If this is the first "real" character we have seen,
562 		**  then we put the "$g" in the buffer now.
563 		*/
564 
565 		if (isspace(*p))
566 			*bp++ = *p;
567 		else if (!gotaddr)
568 		{
569 			(void) strcpy(bp, "\001g");
570 			bp += 2;
571 			gotaddr = TRUE;
572 		}
573 	}
574 
575 	/* hack, hack.... strip trailing blanks */
576 	do
577 	{
578 		*bp-- = '\0';
579 	} while (isspace(*bp));
580 	bp++;
581 
582 	/* put any right hand side back on */
583 	if (rhs != NULL)
584 	{
585 		*rhs = '>';
586 		(void) strcpy(bp, rhs);
587 	}
588 
589 # ifdef DEBUG
590 	if (tTd(33, 1))
591 		printf("crackaddr=>`%s'\n", buf);
592 # endif DEBUG
593 
594 	return (buf);
595 }
596 /*
597 **  PUTHEADER -- put the header part of a message from the in-core copy
598 **
599 **	Parameters:
600 **		fp -- file to put it on.
601 **		m -- mailer to use.
602 **		e -- envelope to use.
603 **
604 **	Returns:
605 **		none.
606 **
607 **	Side Effects:
608 **		none.
609 */
610 
611 putheader(fp, m, e)
612 	register FILE *fp;
613 	register MAILER *m;
614 	register ENVELOPE *e;
615 {
616 	char buf[BUFSIZ];
617 	register HDR *h;
618 	extern char *arpadate();
619 	extern char *capitalize();
620 	char obuf[MAXLINE];
621 
622 	for (h = e->e_header; h != NULL; h = h->h_link)
623 	{
624 		register char *p;
625 		extern bool bitintersect();
626 
627 		if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
628 		    !bitintersect(h->h_mflags, m->m_flags))
629 			continue;
630 
631 		/* handle Resent-... headers specially */
632 		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
633 			continue;
634 
635 		p = h->h_value;
636 		if (bitset(H_DEFAULT, h->h_flags))
637 		{
638 			/* macro expand value if generated internally */
639 			expand(p, buf, &buf[sizeof buf], e);
640 			p = buf;
641 			if (p == NULL || *p == '\0')
642 				continue;
643 		}
644 
645 		if (bitset(H_FROM|H_RCPT, h->h_flags))
646 		{
647 			/* address field */
648 			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
649 
650 			if (bitset(H_FROM, h->h_flags))
651 				oldstyle = FALSE;
652 			commaize(h, p, fp, oldstyle, m);
653 		}
654 		else
655 		{
656 			/* vanilla header line */
657 			register char *nlp;
658 
659 			(void) sprintf(obuf, "%s: ", capitalize(h->h_field));
660 			while ((nlp = index(p, '\n')) != NULL)
661 			{
662 				*nlp = '\0';
663 				(void) strcat(obuf, p);
664 				*nlp = '\n';
665 				putline(obuf, fp, m);
666 				p = ++nlp;
667 				obuf[0] = '\0';
668 			}
669 			(void) strcat(obuf, p);
670 			putline(obuf, fp, m);
671 		}
672 	}
673 }
674 /*
675 **  COMMAIZE -- output a header field, making a comma-translated list.
676 **
677 **	Parameters:
678 **		h -- the header field to output.
679 **		p -- the value to put in it.
680 **		fp -- file to put it to.
681 **		oldstyle -- TRUE if this is an old style header.
682 **		m -- a pointer to the mailer descriptor.  If NULL,
683 **			don't transform the name at all.
684 **
685 **	Returns:
686 **		none.
687 **
688 **	Side Effects:
689 **		outputs "p" to file "fp".
690 */
691 
692 commaize(h, p, fp, oldstyle, m)
693 	register HDR *h;
694 	register char *p;
695 	FILE *fp;
696 	bool oldstyle;
697 	register MAILER *m;
698 {
699 	register char *obp;
700 	int opos;
701 	bool firstone = TRUE;
702 	char obuf[MAXLINE + 3];
703 
704 	/*
705 	**  Output the address list translated by the
706 	**  mailer and with commas.
707 	*/
708 
709 # ifdef DEBUG
710 	if (tTd(14, 2))
711 		printf("commaize(%s: %s)\n", h->h_field, p);
712 # endif DEBUG
713 
714 	obp = obuf;
715 	(void) sprintf(obp, "%s: ", capitalize(h->h_field));
716 	opos = strlen(h->h_field) + 2;
717 	obp += opos;
718 
719 	/*
720 	**  Run through the list of values.
721 	*/
722 
723 	while (*p != '\0')
724 	{
725 		register char *name;
726 		char savechar;
727 		extern char *remotename();
728 		extern char *DelimChar;		/* defined in prescan */
729 
730 		/*
731 		**  Find the end of the name.  New style names
732 		**  end with a comma, old style names end with
733 		**  a space character.  However, spaces do not
734 		**  necessarily delimit an old-style name -- at
735 		**  signs mean keep going.
736 		*/
737 
738 		/* find end of name */
739 		while (isspace(*p) || *p == ',')
740 			p++;
741 		name = p;
742 		for (;;)
743 		{
744 			char *oldp;
745 			char pvpbuf[PSBUFSIZE];
746 			extern bool isatword();
747 			extern char **prescan();
748 
749 			(void) prescan(p, oldstyle ? ' ' : ',', pvpbuf);
750 			p = DelimChar;
751 
752 			/* look to see if we have an at sign */
753 			oldp = p;
754 			while (*p != '\0' && isspace(*p))
755 				p++;
756 
757 			if (*p != '@' && !isatword(p))
758 			{
759 				p = oldp;
760 				break;
761 			}
762 			p += *p == '@' ? 1 : 2;
763 			while (*p != '\0' && isspace(*p))
764 				p++;
765 		}
766 		/* at the end of one complete name */
767 
768 		/* strip off trailing white space */
769 		while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
770 			p--;
771 		if (++p == name)
772 			continue;
773 		savechar = *p;
774 		*p = '\0';
775 
776 		/* translate the name to be relative */
777 		name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE);
778 		if (*name == '\0')
779 		{
780 			*p = savechar;
781 			continue;
782 		}
783 
784 		/* output the name with nice formatting */
785 		opos += qstrlen(name);
786 		if (!firstone)
787 			opos += 2;
788 		if (opos > 78 && !firstone)
789 		{
790 			(void) strcpy(obp, ",\n");
791 			putline(obuf, fp, m);
792 			obp = obuf;
793 			(void) sprintf(obp, "        ");
794 			opos = strlen(obp);
795 			obp += opos;
796 			opos += qstrlen(name);
797 		}
798 		else if (!firstone)
799 		{
800 			(void) sprintf(obp, ", ");
801 			obp += 2;
802 		}
803 
804 		/* strip off quote bits as we output */
805 		while (*name != '\0' && obp < &obuf[MAXLINE])
806 		{
807 			if (bitset(0200, *name))
808 				*obp++ = '\\';
809 			*obp++ = *name++ & ~0200;
810 		}
811 		firstone = FALSE;
812 		*p = savechar;
813 	}
814 	(void) strcpy(obp, "\n");
815 	putline(obuf, fp, m);
816 }
817 /*
818 **  ISATWORD -- tell if the word we are pointing to is "at".
819 **
820 **	Parameters:
821 **		p -- word to check.
822 **
823 **	Returns:
824 **		TRUE -- if p is the word at.
825 **		FALSE -- otherwise.
826 **
827 **	Side Effects:
828 **		none.
829 */
830 
831 bool
832 isatword(p)
833 	register char *p;
834 {
835 	extern char lower();
836 
837 	if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
838 	    p[2] != '\0' && isspace(p[2]))
839 		return (TRUE);
840 	return (FALSE);
841 }
842