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