1 # include <errno.h>
2 # include "sendmail.h"
3 
4 SCCSID(@(#)headers.c	3.39		11/24/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 	bool cond = FALSE;
34 	u_long mopts;
35 	extern u_long mfencode();
36 	extern char *crackaddr();
37 
38 # ifdef DEBUG
39 	if (tTd(31, 6))
40 		printf("chompheader: %s\n", line);
41 # endif DEBUG
42 
43 	/* strip off options */
44 	mopts = 0;
45 	p = line;
46 	if (*p == '?')
47 	{
48 		/* have some */
49 		register char *q = index(p + 1, *p);
50 
51 		if (q != NULL)
52 		{
53 			*q++ = '\0';
54 			mopts = mfencode(p + 1);
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 	fvalue = &p[1];
66 	while (isspace(*--p))
67 		continue;
68 	*++p = '\0';
69 	makelower(fname);
70 
71 	/* strip field value on front */
72 	if (*fvalue == ' ')
73 		fvalue++;
74 
75 	/* search header list for this header */
76 	for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; hp = &h->h_link, h = h->h_link)
77 	{
78 		if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags))
79 			break;
80 	}
81 
82 	/* see if it is a known type */
83 	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
84 	{
85 		if (strcmp(hi->hi_field, fname) == 0)
86 			break;
87 	}
88 
89 	/* if this means "end of header" quit now */
90 	if (bitset(H_EOH, hi->hi_flags))
91 		return (hi->hi_flags);
92 
93 	/* count Received: lines to avoid loops (simulate hop counts) */
94 	if (bitset(H_TRACE, hi->hi_flags))
95 		HopCount++;
96 
97 	/* create/fill in a new node */
98 	if (h == NULL || bitset(H_FORCE, h->h_flags))
99 	{
100 		/* create a new node */
101 		h = (HDR *) xalloc(sizeof *h);
102 		h->h_field = newstr(fname);
103 		h->h_value = NULL;
104 		h->h_link = *hp;
105 		h->h_mflags = mopts;
106 		*hp = h;
107 	}
108 	h->h_flags = hi->hi_flags;
109 	if (def)
110 		h->h_flags |= H_DEFAULT;
111 	if (cond)
112 		h->h_flags |= H_CHECK;
113 	if (h->h_value != NULL)
114 		free((char *) h->h_value);
115 	h->h_value = newstr(fvalue);
116 
117 	/* hack to see if this is a new format message */
118 	if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
119 	    (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
120 	     index(fvalue, '<') != NULL || index(fvalue, ';') != NULL))
121 	{
122 		CurEnv->e_flags &= ~EF_OLDSTYLE;
123 	}
124 
125 	/* send to this person if we so desire */
126 	if (!def && GrabTo && bitset(H_RCPT, h->h_flags))
127 		sendto(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
128 
129 	return (h->h_flags);
130 }
131 /*
132 **  ADDHEADER -- add a header entry to the end of the queue.
133 **
134 **	This bypasses the special checking of chompheader.
135 **
136 **	Parameters:
137 **		field -- the name of the header field.
138 **		value -- the value of the field.  It must be lower-cased.
139 **		e -- the envelope to add them to.
140 **
141 **	Returns:
142 **		none.
143 **
144 **	Side Effects:
145 **		adds the field on the list of headers for this envelope.
146 */
147 
148 addheader(field, value, e)
149 	char *field;
150 	char *value;
151 	ENVELOPE *e;
152 {
153 	register HDR *h;
154 	register struct hdrinfo *hi;
155 	HDR **hp;
156 
157 	/* find info struct */
158 	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
159 	{
160 		if (strcmp(field, hi->hi_field) == 0)
161 			break;
162 	}
163 
164 	/* find current place in list -- keep back pointer? */
165 	for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
166 	{
167 		if (strcmp(field, h->h_field) == 0)
168 			break;
169 	}
170 
171 	/* allocate space for new header */
172 	h = (HDR *) xalloc(sizeof *h);
173 	h->h_field = field;
174 	h->h_value = newstr(value);
175 	h->h_link = *hp;
176 	h->h_flags = hi->hi_flags | H_DEFAULT;
177 	h->h_mflags = 0;
178 	*hp = h;
179 }
180 /*
181 **  HVALUE -- return value of a header.
182 **
183 **	Only "real" fields (i.e., ones that have not been supplied
184 **	as a default) are used.
185 **
186 **	Parameters:
187 **		field -- the field name.
188 **
189 **	Returns:
190 **		pointer to the value part.
191 **		NULL if not found.
192 **
193 **	Side Effects:
194 **		sets the H_USED bit in the header if found.
195 */
196 
197 char *
198 hvalue(field)
199 	char *field;
200 {
201 	register HDR *h;
202 
203 	for (h = CurEnv->e_header; h != NULL; h = h->h_link)
204 	{
205 		if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
206 		{
207 			h->h_flags |= H_USED;
208 			return (h->h_value);
209 		}
210 	}
211 	return (NULL);
212 }
213 /*
214 **  ISHEADER -- predicate telling if argument is a header.
215 **
216 **	A line is a header if it has a single word followed by
217 **	optional white space followed by a colon.
218 **
219 **	Parameters:
220 **		s -- string to check for possible headerness.
221 **
222 **	Returns:
223 **		TRUE if s is a header.
224 **		FALSE otherwise.
225 **
226 **	Side Effects:
227 **		none.
228 */
229 
230 bool
231 isheader(s)
232 	register char *s;
233 {
234 	if (!isalnum(*s))
235 		return (FALSE);
236 	while (!isspace(*s) && *s != ':' && *s != '\0')
237 		s++;
238 	while (isspace(*s))
239 		s++;
240 	return (*s == ':');
241 }
242 /*
243 **  EATHEADER -- run through the stored header and extract info.
244 **
245 **	Parameters:
246 **		none.
247 **
248 **	Returns:
249 **		none.
250 **
251 **	Side Effects:
252 **		Sets a bunch of global variables from information
253 **		in the collected header.
254 */
255 
256 eatheader()
257 {
258 	register HDR *h;
259 	register char *p;
260 
261 # ifdef DEBUG
262 	if (tTd(32, 2))
263 	{
264 		extern char *capitalize();
265 
266 		printf("----- collected header -----\n");
267 		for (h = CurEnv->e_header; h != NULL; h = h->h_link)
268 			printf("%s: %s\n", capitalize(h->h_field), h->h_value);
269 		printf("----------------------------\n");
270 	}
271 # endif DEBUG
272 
273 	/* message priority */
274 	if (!QueueRun)
275 	{
276 		/* adjust total priority by message priority */
277 		CurEnv->e_msgpriority = CurEnv->e_msgsize;
278 		p = hvalue("precedence");
279 		if (p != NULL)
280 			CurEnv->e_class = priencode(p);
281 		CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT;
282 	}
283 
284 	/* return receipt to */
285 	p = hvalue("return-receipt-to");
286 	if (p != NULL)
287 		CurEnv->e_receiptto = p;
288 
289 	/* errors to */
290 	p = hvalue("errors-to");
291 	if (p != NULL)
292 		sendto(p, (ADDRESS *) NULL, &CurEnv->e_errorqueue);
293 
294 	/* from person */
295 	if (OpMode == MD_ARPAFTP)
296 	{
297 		register struct hdrinfo *hi = HdrInfo;
298 
299 		for (p = NULL; p == NULL && hi->hi_field != NULL; hi++)
300 		{
301 			if (bitset(H_FROM, hi->hi_flags))
302 				p = hvalue(hi->hi_field);
303 		}
304 		if (p != NULL)
305 			setfrom(p, (char *) NULL);
306 	}
307 
308 	/* full name of from person */
309 	p = hvalue("full-name");
310 	if (p != NULL)
311 		define('x', p);
312 
313 	/* date message originated */
314 	p = hvalue("posted-date");
315 	if (p == NULL)
316 		p = hvalue("date");
317 	if (p != NULL)
318 	{
319 		define('a', p);
320 		/* we don't have a good way to do canonical conversion ....
321 		define('d', newstr(arpatounix(p)));
322 		.... so we will ignore the problem for the time being */
323 	}
324 }
325 /*
326 **  PRIENCODE -- encode external priority names into internal values.
327 **
328 **	Parameters:
329 **		p -- priority in ascii.
330 **
331 **	Returns:
332 **		priority as a numeric level.
333 **
334 **	Side Effects:
335 **		none.
336 */
337 
338 priencode(p)
339 	char *p;
340 {
341 	register int i;
342 	extern bool sameword();
343 
344 	for (i = 0; i < NumPriorities; i++)
345 	{
346 		if (sameword(p, Priorities[i].pri_name))
347 			return (Priorities[i].pri_val);
348 	}
349 
350 	/* unknown priority */
351 	return (0);
352 }
353 /*
354 **  CRACKADDR -- parse an address and turn it into a macro
355 **
356 **	This doesn't actually parse the address -- it just extracts
357 **	it and replaces it with "$g".  The parse is totally ad hoc
358 **	and isn't even guaranteed to leave something syntactically
359 **	identical to what it started with.  However, it does leave
360 **	something semantically identical.
361 **
362 **	The process is kind of strange.  There are a number of
363 **	interesting cases:
364 **		1.  comment <address> comment	==> comment <$g> comment
365 **		2.  address			==> address
366 **		3.  address (comment)		==> $g (comment)
367 **		4.  (comment) address		==> (comment) $g
368 **	And then there are the hard cases....
369 **		5.  add (comment) ress		==> $g (comment)
370 **		6.  comment <address (comment)>	==> comment <$g (comment)>
371 **		7.    .... etc ....
372 **
373 **	Parameters:
374 **		addr -- the address to be cracked.
375 **
376 **	Returns:
377 **		a pointer to the new version.
378 **
379 **	Side Effects:
380 **		none.
381 **
382 **	Warning:
383 **		The return value is saved in local storage and should
384 **		be copied if it is to be reused.
385 */
386 
387 char *
388 crackaddr(addr)
389 	register char *addr;
390 {
391 	register char *p;
392 	register int i;
393 	static char buf[MAXNAME];
394 	char *rhs;
395 	bool gotaddr;
396 	register char *bp;
397 
398 # ifdef DEBUG
399 	if (tTd(33, 1))
400 		printf("crackaddr(%s)\n", addr);
401 # endif DEBUG
402 
403 	strcpy(buf, "");
404 	rhs = NULL;
405 
406 	/* strip leading spaces */
407 	while (*addr != '\0' && isspace(*addr))
408 		addr++;
409 
410 	/*
411 	**  See if we have anything in angle brackets.  If so, that is
412 	**  the address part, and the rest is the comment.
413 	*/
414 
415 	p = index(addr, '<');
416 	if (p != NULL)
417 	{
418 		/* copy the beginning of the addr field to the buffer */
419 		*p = '\0';
420 		strcpy(buf, addr);
421 		strcat(buf, "<");
422 		*p++ = '<';
423 
424 		/* skip spaces */
425 		while (isspace(*p))
426 			p++;
427 
428 		/* find the matching right angle bracket */
429 		addr = p;
430 		for (i = 0; *p != '\0'; p++)
431 		{
432 			switch (*p)
433 			{
434 			  case '<':
435 				i++;
436 				break;
437 
438 			  case '>':
439 				i--;
440 				break;
441 			}
442 			if (i < 0)
443 				break;
444 		}
445 
446 		/* p now points to the closing quote (or a null byte) */
447 		if (*p != '\0')
448 		{
449 			/* make rhs point to the extra stuff at the end */
450 			rhs = p;
451 			*p++ = '\0';
452 		}
453 	}
454 
455 	/*
456 	**  Now parse the real address part.  "addr" points to the (null
457 	**  terminated) version of what we are inerested in; rhs points
458 	**  to the extra stuff at the end of the line, if any.
459 	*/
460 
461 	p = addr;
462 
463 	/* now strip out comments */
464 	bp = &buf[strlen(buf)];
465 	gotaddr = FALSE;
466 	for (; *p != '\0'; p++)
467 	{
468 		if (*p == '(')
469 		{
470 			/* copy to matching close paren */
471 			*bp++ = *p++;
472 			for (i = 0; *p != '\0'; p++)
473 			{
474 				*bp++ = *p;
475 				switch (*p)
476 				{
477 				  case '(':
478 					i++;
479 					break;
480 
481 				  case ')':
482 					i--;
483 					break;
484 				}
485 				if (i < 0)
486 					break;
487 			}
488 			continue;
489 		}
490 
491 		/*
492 		**  If this is the first "real" character we have seen,
493 		**  then we put the "$g" in the buffer now.
494 		*/
495 
496 		if (isspace(*p))
497 			*bp++ = *p;
498 		else if (!gotaddr)
499 		{
500 			strcpy(bp, "$g");
501 			bp += 2;
502 			gotaddr = TRUE;
503 		}
504 	}
505 
506 	/* hack, hack.... strip trailing blanks */
507 	do
508 	{
509 		*bp-- = '\0';
510 	} while (isspace(*bp));
511 	bp++;
512 
513 	/* put any right hand side back on */
514 	if (rhs != NULL)
515 	{
516 		*rhs = '>';
517 		strcpy(bp, rhs);
518 	}
519 
520 # ifdef DEBUG
521 	if (tTd(33, 1))
522 		printf("crackaddr=>`%s'\n", buf);
523 # endif DEBUG
524 
525 	return (buf);
526 }
527