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