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