1 # include <errno.h>
2 # include "sendmail.h"
3 
4 SCCSID(@(#)headers.c	3.26		08/22/82);
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 	extern bool isheader();
31 	char *fname;
32 	char *fvalue;
33 	struct hdrinfo *hi;
34 	u_long mopts;
35 	extern u_long mfencode();
36 	extern char *crackfrom();
37 
38 # ifdef DEBUG
39 	if (tTd(31, 6))
40 		printf("chompheader: %s\n", line);
41 # endif DEBUG
42 
43 	/* strip off trailing newline */
44 	p = rindex(line, '\n');
45 	if (p != NULL)
46 		*p = '\0';
47 
48 	/* strip off options */
49 	mopts = 0;
50 	p = line;
51 	if (*p == '?')
52 	{
53 		/* have some */
54 		register char *q = index(p + 1, *p);
55 
56 		if (q != NULL)
57 		{
58 			*q++ = '\0';
59 			mopts = mfencode(p + 1);
60 			p = q;
61 		}
62 		else
63 			syserr("chompheader: syntax error, line \"%s\"", line);
64 	}
65 
66 	/* find canonical name */
67 	fname = p;
68 	p = index(p, ':');
69 	fvalue = &p[1];
70 	while (isspace(*--p))
71 		continue;
72 	*++p = '\0';
73 	makelower(fname);
74 
75 	/* strip field value on front */
76 	if (*fvalue == ' ')
77 		fvalue++;
78 
79 	/* search header list for this header */
80 	for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; hp = &h->h_link, h = h->h_link)
81 	{
82 		if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags))
83 			break;
84 	}
85 
86 	/* see if it is a known type */
87 	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
88 	{
89 		if (strcmp(hi->hi_field, fname) == 0)
90 			break;
91 	}
92 
93 	/* if this means "end of header" quit now */
94 	if (bitset(H_EOH, hi->hi_flags))
95 		return (hi->hi_flags);
96 
97 	/* count Received: lines to avoid loops (simulate hop counts) */
98 	if (strcmp(fname, "received") == 0)
99 		HopCount++;
100 
101 	/* create/fill in a new node */
102 	if (h == NULL || bitset(H_FORCE, h->h_flags))
103 	{
104 		/* create a new node */
105 		h = (HDR *) xalloc(sizeof *h);
106 		h->h_field = newstr(fname);
107 		h->h_value = NULL;
108 		h->h_link = *hp;
109 		h->h_flags = hi->hi_flags;
110 		h->h_mflags = mopts | hi->hi_mflags;
111 		*hp = h;
112 	}
113 	if (def)
114 		h->h_flags |= H_DEFAULT;
115 	else if (mopts == 0)
116 		h->h_flags &= ~H_CHECK;
117 	if (h->h_value != NULL)
118 		free(h->h_value);
119 	if (!def && strcmp(fname, "from") == 0)
120 	{
121 		/* turn it into a macro -- will be expanded later */
122 		h->h_value = newstr(crackfrom(fvalue));
123 		h->h_flags |= H_DEFAULT;
124 	}
125 	else
126 		h->h_value = newstr(fvalue);
127 	if (!def && GrabTo && bitset(H_RCPT, h->h_flags))
128 		sendto(h->h_value, 0, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
129 
130 	/* hack to see if this is a new format message */
131 	if (bitset(H_RCPT, h->h_flags) &&
132 	    (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
133 	     index(fvalue, '<') != NULL))
134 		CurEnv->e_oldstyle = FALSE;
135 
136 	return (h->h_flags);
137 }
138 /*
139 **  ADDHEADER -- add a header entry to the end of the queue.
140 **
141 **	This bypasses the special checking of chompheader.
142 **
143 **	Parameters:
144 **		field -- the name of the header field.
145 **		value -- the value of the field.  It must be lower-cased.
146 **		e -- the envelope to add them to.
147 **
148 **	Returns:
149 **		none.
150 **
151 **	Side Effects:
152 **		adds the field on the list of headers for this envelope.
153 */
154 
155 addheader(field, value, e)
156 	char *field;
157 	char *value;
158 	ENVELOPE *e;
159 {
160 	register HDR *h;
161 	register struct hdrinfo *hi;
162 	HDR **hp;
163 
164 	/* find info struct */
165 	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
166 	{
167 		if (strcmp(field, hi->hi_field) == 0)
168 			break;
169 	}
170 
171 	/* find current place in list -- keep back pointer? */
172 	for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
173 	{
174 		if (strcmp(field, h->h_field) == 0)
175 			break;
176 	}
177 
178 	/* allocate space for new header */
179 	h = (HDR *) xalloc(sizeof *h);
180 	h->h_field = field;
181 	h->h_value = newstr(value);
182 	h->h_link = *hp;
183 	h->h_flags = hi->hi_flags | H_DEFAULT;
184 	h->h_mflags = hi->hi_mflags;
185 	*hp = h;
186 }
187 /*
188 **  HVALUE -- return value of a header.
189 **
190 **	Only "real" fields (i.e., ones that have not been supplied
191 **	as a default) are used.
192 **
193 **	Parameters:
194 **		field -- the field name.
195 **
196 **	Returns:
197 **		pointer to the value part.
198 **		NULL if not found.
199 **
200 **	Side Effects:
201 **		sets the H_USED bit in the header if found.
202 */
203 
204 char *
205 hvalue(field)
206 	char *field;
207 {
208 	register HDR *h;
209 
210 	for (h = CurEnv->e_header; h != NULL; h = h->h_link)
211 	{
212 		if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
213 		{
214 			h->h_flags |= H_USED;
215 			return (h->h_value);
216 		}
217 	}
218 	return (NULL);
219 }
220 /*
221 **  HRVALUE -- return pointer to header descriptor.
222 **
223 **	Like hvalue except returns header descriptor block and isn't
224 **	picky about "real" headers.
225 **
226 **	Parameters:
227 **		field -- name of field we are interested in.
228 **
229 **	Returns:
230 **		pointer to header descriptor.
231 **
232 **	Side Effects:
233 **		none.
234 */
235 
236 HDR *
237 hrvalue(field)
238 	char *field;
239 {
240 	register HDR *h;
241 
242 	for (h = CurEnv->e_header; h != NULL; h = h->h_link)
243 	{
244 		if (strcmp(h->h_field, field) == 0)
245 			return (h);
246 	}
247 	return (NULL);
248 }
249 /*
250 **  ISHEADER -- predicate telling if argument is a header.
251 **
252 **	A line is a header if it has a single word followed by
253 **	optional white space followed by a colon.
254 **
255 **	Parameters:
256 **		s -- string to check for possible headerness.
257 **
258 **	Returns:
259 **		TRUE if s is a header.
260 **		FALSE otherwise.
261 **
262 **	Side Effects:
263 **		none.
264 **
265 **	Bugs:
266 **		According to RFC733, there should be a newline
267 **		permitted after the word but before the colon.
268 **		We don't seem to support that.....
269 */
270 
271 bool
272 isheader(s)
273 	register char *s;
274 {
275 	if (!isalnum(*s))
276 		return (FALSE);
277 	while (!isspace(*s) && *s != ':')
278 		s++;
279 	while (isspace(*s))
280 		s++;
281 	return (*s == ':');
282 }
283 /*
284 **  GETXPART -- extract the "signature" part of an address line.
285 **
286 **	Try to extract the full name from a general address
287 **	field.  We take anything which is a comment as a
288 **	first choice.  Failing in that, we see if there is
289 **	a "machine readable" name (in <angle brackets>); if
290 **	so we take anything preceeding that clause.
291 **
292 **	If we blow it here it's not all that serious.
293 **
294 **	Parameters:
295 **		p -- line to crack.
296 **
297 **	Returns:
298 **		signature part.
299 **		NULL if no signature part.
300 **
301 **	Side Effects:
302 **		none.
303 */
304 
305 char *
306 getxpart(p)
307 	register char *p;
308 {
309 	register char *q;
310 	register char *rval = NULL;
311 
312 	q = index(p, '(');
313 	if (q != NULL)
314 	{
315 		int parenlev = 0;
316 
317 		for (p = q; *p != '\0'; p++)
318 		{
319 			if (*p == '(')
320 				parenlev++;
321 			else if (*p == ')' && --parenlev <= 0)
322 				break;
323 		}
324 		if (*p == ')')
325 		{
326 			*p = '\0';
327 			if (*++q != '\0')
328 				rval = newstr(q);
329 			*p = ')';
330 		}
331 	}
332 	else if ((q = index(p, '<')) != NULL)
333 	{
334 		char savec;
335 
336 		while (*--q == ' ')
337 			continue;
338 		while (isspace(*p))
339 			p++;
340 		savec = *++q;
341 		*q = '\0';
342 		if (*p != '\0')
343 			rval = newstr(p);
344 		*q = savec;
345 	}
346 
347 	return (rval);
348 }
349 /*
350 **  EATHEADER -- run through the stored header and extract info.
351 **
352 **	Parameters:
353 **		none.
354 **
355 **	Returns:
356 **		none.
357 **
358 **	Side Effects:
359 **		Sets a bunch of global variables from information
360 **		in the collected header.
361 */
362 
363 eatheader()
364 {
365 	register HDR *h;
366 	register char *p;
367 	char buf[MAXLINE];
368 	char *msgid;
369 
370 # ifdef DEBUG
371 	if (tTd(32, 2))
372 	{
373 		extern char *capitalize();
374 
375 		printf("----- collected header -----\n");
376 		for (h = CurEnv->e_header; h != NULL; h = h->h_link)
377 			printf("%s: %s\n", capitalize(h->h_field), h->h_value);
378 		printf("----------------------------\n");
379 	}
380 # endif DEBUG
381 
382 	/* message id */
383 	h = hrvalue("message-id");
384 	if (h == NULL)
385 		msgid = NULL;
386 	else if (bitset(H_DEFAULT, h->h_flags))
387 	{
388 		(void) expand(h->h_value, buf, &buf[sizeof buf - 1], CurEnv);
389 		msgid = buf;
390 	}
391 	else
392 		msgid = h->h_value;
393 	if (msgid != NULL)
394 	{
395 # ifdef DEBUG
396 		if (tTd(32, 1))
397 			printf("Message-Id: %s\n", msgid);
398 # endif DEBUG
399 # ifdef LOG
400 		if (LogLevel > 1)
401 			syslog(LOG_INFO, "%s: messageid=%s", CurEnv->e_id, msgid);
402 # endif LOG
403 	}
404 
405 	/* message priority */
406 	if (!QueueRun)
407 	{
408 		/* adjust total priority by message priority */
409 		CurEnv->e_msgpriority = CurEnv->e_msgsize;
410 		p = hvalue("priority");
411 		if (p != NULL)
412 			CurEnv->e_class = priencode(p);
413 		else
414 			CurEnv->e_class = PRI_NORMAL;
415 		CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT;
416 	}
417 
418 	/* special handling */
419 	p = hvalue("special-handling");
420 	if (p != NULL)
421 		spechandling(p);
422 
423 	/* from person */
424 	p = hvalue("sender");
425 	if (p == NULL)
426 		p = CurEnv->e_origfrom;
427 	if (ArpaMode)
428 		setfrom(p, (char *) NULL);
429 
430 	/* full name of from person */
431 	p = hvalue("full-name");
432 	if (p != NULL)
433 		define('x', p);
434 
435 	/* date message originated */
436 	p = hvalue("posted-date");
437 	if (p == NULL)
438 		p = hvalue("date");
439 	if (p != NULL)
440 	{
441 		define('a', p);
442 		/* we don't have a good way to do canonical conversion ....
443 		define('d', newstr(arpatounix(p)));
444 		.... so we will ignore the problem for the time being */
445 	}
446 }
447 /*
448 **  PRIENCODE -- encode external priority names into internal values.
449 **
450 **	Parameters:
451 **		p -- priority in ascii.
452 **
453 **	Returns:
454 **		priority as a numeric level.
455 **
456 **	Side Effects:
457 **		none.
458 */
459 
460 struct prio
461 {
462 	char	*pri_name;	/* external name of priority */
463 	int	pri_val;	/* internal value for same */
464 };
465 
466 static struct prio	Prio[] =
467 {
468 	"alert",		PRI_ALERT,
469 	"quick",		PRI_QUICK,
470 	"first-class",		PRI_FIRSTCL,
471 	"normal",		PRI_NORMAL,
472 	"second-class",		PRI_SECONDCL,
473 	"third-class",		PRI_THIRDCL,
474 	"junk",			PRI_JUNK,
475 	NULL,			PRI_NORMAL,
476 };
477 
478 priencode(p)
479 	char *p;
480 {
481 	register struct prio *pl;
482 	extern bool sameword();
483 
484 	for (pl = Prio; pl->pri_name != NULL; pl++)
485 	{
486 		if (sameword(p, pl->pri_name))
487 			break;
488 	}
489 	return (pl->pri_val);
490 }
491 /*
492 **  SPECHANDLE -- do special handling
493 **
494 **	Parameters:
495 **		p -- pointer to list of special handling words.
496 **
497 **	Returns:
498 **		none.
499 **
500 **	Side Effects:
501 **		Sets flags as indicated by p.
502 */
503 
504 struct handling
505 {
506 	char	*han_name;		/* word to get this magic */
507 	int	han_what;		/* what to do, see below */
508 };
509 
510 /* modes for han_what */
511 # define	HAN_NONE	0	/* nothing special */
512 # define	HAN_RRECEIPT	1	/* give return receipt */
513 
514 struct handling	Handling[] =
515 {
516 	"return-receipt-requested",	HAN_RRECEIPT,
517 	NULL,				HAN_NONE
518 };
519 
520 spechandling(p)
521 	register char *p;
522 {
523 	register char *w;
524 	register struct handling *h;
525 	extern bool sameword();
526 
527 	while (*p != '\0')
528 	{
529 		/* collect a word to compare to */
530 		while (*p != '\0' && (*p == ',' || isspace(*p)))
531 			p++;
532 		if (*p == '\0')
533 			break;
534 		w = p;
535 		while (*p != '\0' && *p != ',' && !isspace(*p))
536 			p++;
537 		if (*p != '\0')
538 			*p++ = '\0';
539 
540 		/* scan the special handling table */
541 		for (h = Handling; h->han_name != NULL; h++)
542 			if (sameword(h->han_name, w))
543 				break;
544 
545 		/* see if we can do anything interesting */
546 		switch (h->han_what)
547 		{
548 		  case HAN_NONE:	/* nothing to be done */
549 			break;
550 
551 		  case HAN_RRECEIPT:	/* give return receipt */
552 			CurEnv->e_retreceipt = TRUE;
553 # ifdef DEBUG
554 			if (tTd(30, 3))
555 				printf(">>> Return receipt requested\n");
556 # endif DEBUG
557 			break;
558 
559 		  default:
560 			syserr("spechandling: handling %d (%s)", h->han_what, w);
561 		}
562 	}
563 }
564 /*
565 **  CRACKFROM -- parse the from line and turn it into a macro
566 **
567 **	This doesn't actually parse the address -- it just extracts
568 **	it and replaces it with "$g".  The parse is totally ad hoc
569 **	and isn't even guaranteed to leave something syntactically
570 **	identical to what it started with.  However, it does leave
571 **	something semantically identical.
572 **
573 **	The process is kind of strange.  There are a number of
574 **	interesting cases:
575 **		1.  comment <address> comment	==> comment <$g> comment
576 **		2.  address			==> address
577 **		3.  address (comment)		==> $g (comment)
578 **		4.  (comment) address		==> (comment) $g
579 **	And then there are the hard cases....
580 **		5.  add (comment) ress		==> $g (comment)
581 **		6.  comment <address (comment)>	==> comment <$g (comment)>
582 **		7.    .... etc ....
583 **
584 **	Parameters:
585 **		from -- the value part of the from line.
586 **
587 **	Returns:
588 **		a pointer to the new version.
589 **
590 **	Side Effects:
591 **		The $f and $x macros may be defined.
592 **
593 **	Warning:
594 **		The return value is saved in local storage and should
595 **		be copied if it is to be reused.
596 */
597 
598 char *
599 crackfrom(from)
600 	register char *from;
601 {
602 	register char *p;
603 	register int i;
604 	static char buf[MAXNAME];
605 	char *rhs;
606 	bool gotaddr;
607 	register char *bp;
608 
609 # ifdef DEBUG
610 	if (tTd(33, 1))
611 		printf("crackfrom(%s)\n", from);
612 # endif DEBUG
613 
614 	strcpy(buf, "");
615 	rhs = NULL;
616 
617 	/*
618 	**  See if we have anything in angle brackets.  If so, that is
619 	**  the address part, and the rest is the comment.
620 	*/
621 
622 	p = index(from, '<');
623 	if (p != NULL)
624 	{
625 		/* copy the beginning of the from field to the buffer */
626 		*p = '\0';
627 		strcpy(buf, from);
628 		strcat(buf, "<");
629 		*p = '<';
630 
631 		/* find the matching right angle bracket */
632 		from = ++p;
633 		for (i = 0; *p != '\0'; p++)
634 		{
635 			switch (*p)
636 			{
637 			  case '<':
638 				i++;
639 				break;
640 
641 			  case '>':
642 				i--;
643 				break;
644 			}
645 			if (i < 0)
646 				break;
647 		}
648 
649 		/* p now points to the closing quote (or a null byte) */
650 		if (*p != '\0')
651 		{
652 			/* make rhs point to the extra stuff at the end */
653 			rhs = p;
654 			*p++ = '\0';
655 		}
656 	}
657 
658 	/*
659 	**  Now parse the real address part.  from points to the (null
660 	**  terminated) version of what we are inerested in; rhs points
661 	**  to the extra stuff at the end of the line, if any.
662 	*/
663 
664 	p = from;
665 
666 	/* now strip out comments */
667 	bp = &buf[strlen(buf)];
668 	gotaddr = FALSE;
669 	for (; *p != '\0'; p++)
670 	{
671 		if (*p == '(')
672 		{
673 			/* copy to matching close paren */
674 			*bp++ = *p++;
675 			for (i = 0; *p != '\0'; p++)
676 			{
677 				*bp++ = *p;
678 				switch (*p)
679 				{
680 				  case '(':
681 					i++;
682 					break;
683 
684 				  case ')':
685 					i--;
686 					break;
687 				}
688 				if (i < 0)
689 					break;
690 			}
691 			continue;
692 		}
693 
694 		/*
695 		**  If this is the first "real" character we have seen,
696 		**  then we put the "$g" in the buffer now.
697 		*/
698 
699 		if (isspace(*p))
700 			*bp++ = *p;
701 		else if (!gotaddr)
702 		{
703 			strcpy(bp, "$g");
704 			bp += 2;
705 			gotaddr = TRUE;
706 		}
707 	}
708 
709 	/*
710 	**  If there is a tag at the end, insert it.
711 	*/
712 
713 	*bp = '\0';
714 	if (rhs != NULL)
715 	{
716 		*rhs = '>';
717 		strcpy(bp, rhs);
718 	}
719 
720 # ifdef DEBUG
721 	if (tTd(33, 1))
722 		printf("crackfrom=>%s\n", buf);
723 # endif DEBUG
724 
725 	return (buf);
726 }
727