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