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