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