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