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