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