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