* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)headers.c 8.2 (Berkeley) 7/11/93";
** CHOMPHEADER -- process and save a header line.
** Called by collect and by readcf to deal with header lines.
** line -- header as a text line.
** def -- if set, this is a default value.
** e -- the envelope including this header.
** flags for this header.
** The header is saved on the header list.
** Contents of 'line' are destroyed.
chompheader(line
, def
, e
)
printf("chompheader: %s\n", line
);
register char *q
= strchr(p
+ 1, *p
);
usrerr("553 header syntax error, line \"%s\"", line
);
/* find canonical name */
syserr("553 header syntax error, line \"%s\"", line
);
while (isascii(*--p
) && isspace(*p
))
/* strip field value on front */
/* see if it is a known type */
for (hi
= HdrInfo
; hi
->hi_field
!= NULL
; hi
++)
if (strcasecmp(hi
->hi_field
, fname
) == 0)
/* see if this is a resent message */
if (!def
&& bitset(H_RESENT
, hi
->hi_flags
))
/* if this means "end of header" quit now */
if (bitset(H_EOH
, hi
->hi_flags
))
/* drop explicit From: if same as what we would generate -- for MH */
if (!bitset(EF_RESENT
, e
->e_flags
))
if (!def
&& !bitset(EF_QUEUERUN
, e
->e_flags
) && strcasecmp(fname
, p
) == 0)
printf("comparing header from (%s) against default (%s or %s)\n",
fvalue
, e
->e_from
.q_paddr
, e
->e_from
.q_user
);
if (e
->e_from
.q_paddr
!= NULL
&&
(strcmp(fvalue
, e
->e_from
.q_paddr
) == 0 ||
strcmp(fvalue
, e
->e_from
.q_user
) == 0))
/* delete default value for this header */
for (hp
= &e
->e_header
; (h
= *hp
) != NULL
; hp
= &h
->h_link
)
if (strcasecmp(fname
, h
->h_field
) == 0 &&
bitset(H_DEFAULT
, h
->h_flags
) &&
!bitset(H_FORCE
, h
->h_flags
))
h
= (HDR
*) xalloc(sizeof *h
);
h
->h_field
= newstr(fname
);
bcopy((char *) mopts
, (char *) h
->h_mflags
, sizeof mopts
);
h
->h_flags
= hi
->hi_flags
;
free((char *) h
->h_value
);
h
->h_value
= newstr(fvalue
);
/* hack to see if this is a new format message */
if (!def
&& bitset(H_RCPT
|H_FROM
, h
->h_flags
) &&
(strchr(fvalue
, ',') != NULL
|| strchr(fvalue
, '(') != NULL
||
strchr(fvalue
, '<') != NULL
|| strchr(fvalue
, ';') != NULL
))
e
->e_flags
&= ~EF_OLDSTYLE
;
** ADDHEADER -- add a header entry to the end of the queue.
** This bypasses the special checking of chompheader.
** field -- the name of the header field.
** value -- the value of the field.
** e -- the envelope to add them to.
** adds the field on the list of headers for this envelope.
addheader(field
, value
, e
)
register struct hdrinfo
*hi
;
for (hi
= HdrInfo
; hi
->hi_field
!= NULL
; hi
++)
if (strcasecmp(field
, hi
->hi_field
) == 0)
/* find current place in list -- keep back pointer? */
for (hp
= &e
->e_header
; (h
= *hp
) != NULL
; hp
= &h
->h_link
)
if (strcasecmp(field
, h
->h_field
) == 0)
/* allocate space for new header */
h
= (HDR
*) xalloc(sizeof *h
);
h
->h_value
= newstr(value
);
h
->h_flags
= hi
->hi_flags
| H_DEFAULT
;
** HVALUE -- return value of a header.
** Only "real" fields (i.e., ones that have not been supplied
** as a default) are used.
** field -- the field name.
** e -- the envelope containing the header.
** pointer to the value part.
for (h
= e
->e_header
; h
!= NULL
; h
= h
->h_link
)
if (!bitset(H_DEFAULT
, h
->h_flags
) &&
strcasecmp(h
->h_field
, field
) == 0)
** ISHEADER -- predicate telling if argument is a header.
** A line is a header if it has a single word followed by
** optional white space followed by a colon.
** s -- string to check for possible headerness.
** TRUE if s is a header.
while (*s
> ' ' && *s
!= ':' && *s
!= '\0')
/* following technically violates RFC822 */
while (isascii(*s
) && isspace(*s
))
** EATHEADER -- run through the stored header and extract info.
** e -- the envelope to process.
** full -- if set, do full processing (e.g., compute
** Sets a bunch of global variables from information
** in the collected header.
** Aborts the message if the hop count is exceeded.
** Set up macros for possible expansion in headers.
define('f', e
->e_sender
, e
);
define('g', e
->e_sender
, e
);
printf("----- collected header -----\n");
for (h
= e
->e_header
; h
!= NULL
; h
= h
->h_link
)
if (bitset(H_DEFAULT
, h
->h_flags
) && h
->h_value
!= NULL
)
expand(h
->h_value
, buf
, &buf
[sizeof buf
], e
);
h
->h_value
= newstr(buf
);
h
->h_flags
&= ~H_DEFAULT
;
printf("%s: %s\n", h
->h_field
, h
->h_value
);
/* count the number of times it has been processed */
if (bitset(H_TRACE
, h
->h_flags
))
/* send to this person if we so desire */
if (GrabTo
&& bitset(H_RCPT
, h
->h_flags
) &&
!bitset(H_DEFAULT
, h
->h_flags
) &&
(!bitset(EF_RESENT
, e
->e_flags
) || bitset(H_RESENT
, h
->h_flags
)))
(void) sendtolist(h
->h_value
, (ADDRESS
*) NULL
,
/* save the message-id for logging */
if (full
&& h
->h_value
!= NULL
&&
strcasecmp(h
->h_field
, "message-id") == 0)
while (isascii(*msgid
) && isspace(*msgid
))
/* see if this is a return-receipt header */
if (bitset(H_RECEIPTTO
, h
->h_flags
))
e
->e_receiptto
= h
->h_value
;
/* see if this is an errors-to header */
if (UseErrorsTo
&& bitset(H_ERRORSTO
, h
->h_flags
))
(void) sendtolist(h
->h_value
, (ADDRESS
*) NULL
,
printf("----------------------------\n");
/* if we are just verifying (that is, sendmail -t -bv), drop out now */
if (hopcnt
> e
->e_hopcount
)
p
= hvalue("precedence", e
);
e
->e_class
= priencode(p
);
e
->e_msgpriority
= e
->e_msgsize
- e
->e_class
* WkClassFact
+ e
->e_nrcpts
* WkRecipFact
;
/* full name of from person */
p
= hvalue("full-name", e
);
/* date message originated */
p
= hvalue("posted-date", e
);
** Log collection information.
if (full
&& LogLevel
> 4)
if (bitset(EF_RESPONSE
, e
->e_flags
))
else if ((name
= macvalue('_', e
)) != NULL
)
else if (RealHostName
[0] == '[')
(void) sprintf(hbuf
, "%.80s", RealHostName
);
if (RealHostAddr
.sa
.sa_family
!= 0)
(void) sprintf(p
, " (%s)",
anynet_ntoa(&RealHostAddr
));
/* some versions of syslog only take 5 printf args */
sprintf(sbp
, "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d, msgid=%.100s",
e
->e_from
.q_paddr
, e
->e_msgsize
, e
->e_class
,
e
->e_msgpriority
, e
->e_nrcpts
, msgid
);
if (e
->e_bodytype
!= NULL
)
(void) sprintf(sbp
, ", bodytype=%.20s", e
->e_bodytype
);
(void) sprintf(sbp
, ", proto=%.20s", p
);
syslog(LOG_INFO
, "%s: %s, relay=%s",
** PRIENCODE -- encode external priority names into internal values.
** p -- priority in ascii.
** priority as a numeric level.
for (i
= 0; i
< NumPriorities
; i
++)
if (!strcasecmp(p
, Priorities
[i
].pri_name
))
return (Priorities
[i
].pri_val
);
** CRACKADDR -- parse an address and turn it into a macro
** This doesn't actually parse the address -- it just extracts
** it and replaces it with "$g". The parse is totally ad hoc
** and isn't even guaranteed to leave something syntactically
** identical to what it started with. However, it does leave
** something semantically identical.
** This algorithm has been cleaned up to handle a wider range
** of cases -- notably quoted and backslash escaped strings.
** This modification makes it substantially better at preserving
** addr -- the address to be cracked.
** a pointer to the new version.
** The return value is saved in local storage and should
** be copied if it is to be reused.
int anglelev
, realanglelev
;
static char buf
[MAXNAME
];
printf("crackaddr(%s)\n", addr
);
/* strip leading spaces */
while (*addr
!= '\0' && isascii(*addr
) && isspace(*addr
))
** Start by assuming we have no angle brackets. This will be
** adjusted later if we find them.
buflim
= &buf
[sizeof buf
- 5];
copylev
= anglelev
= realanglelev
= cmtlev
= realcmtlev
= 0;
qmode
= realqmode
= FALSE
;
while ((c
= *p
++) != '\0')
** If the buffer is overful, go into a special "skipping"
** mode that tries to keep legal syntax but doesn't actually
if (copylev
> 0 && !skipping
)
/* check for backslash escapes */
/* arrange to quote the address */
if (cmtlev
<= 0 && !qmode
)
if (copylev
> 0 && !skipping
)
/* check for quoted strings */
if (copylev
> 0 && !skipping
)
/* allow space for closing paren */
/* syntax error: unmatched ) */
/* check for characters that may have to be quoted */
if (strchr(".'@,;:\\()", c
) != NULL
)
** If these occur as the phrase part of a <>
** construct, but are not inside of () or already
** quoted, they will have to be quoted. Note that
** now (but don't actually do the quoting).
if (cmtlev
<= 0 && !qmode
)
/* check for angle brackets */
/* oops -- have to change our mind */
/* back up over the '<' and any spaces */
while (isascii(*--p
) && isspace(*p
))
while ((c
= *p
++) != '<')
putgmac
= quoteit
= FALSE
;
/* syntax error: unmatched > */
/* must be a real address character */
if (copylev
<= 0 && !putgmac
)
/* repair any syntactic damage */
while (realanglelev
-- > 0)
printf("crackaddr=>`%s'\n", buf
);
** PUTHEADER -- put the header part of a message from the in-core copy
** fp -- file to put it on.
* Macro for fast max (not available in e.g. DG/UX, 386/ix).
# define MAX(a,b) (((a)>(b))?(a):(b))
char buf
[MAX(MAXLINE
,BUFSIZ
)];
printf("--- putheader, mailer = %s ---\n", m
->m_name
);
for (h
= e
->e_header
; h
!= NULL
; h
= h
->h_link
)
extern bool bitintersect();
printf(" %s: ", h
->h_field
);
if (bitset(H_CHECK
|H_ACHECK
, h
->h_flags
) &&
!bitintersect(h
->h_mflags
, m
->m_flags
))
/* handle Resent-... headers specially */
if (bitset(H_RESENT
, h
->h_flags
) && !bitset(EF_RESENT
, e
->e_flags
))
printf(" (skipped (resent))\n");
if (bitset(H_DEFAULT
, h
->h_flags
))
/* macro expand value if generated internally */
expand(p
, buf
, &buf
[sizeof buf
], e
);
if (p
== NULL
|| *p
== '\0')
if (bitset(H_FROM
|H_RCPT
, h
->h_flags
))
bool oldstyle
= bitset(EF_OLDSTYLE
, e
->e_flags
);
if (bitset(H_FROM
, h
->h_flags
))
commaize(h
, p
, fp
, oldstyle
, m
, e
);
/* vanilla header line */
(void) sprintf(obuf
, "%s: ", h
->h_field
);
while ((nlp
= strchr(p
, '\n')) != NULL
)
** COMMAIZE -- output a header field, making a comma-translated list.
** h -- the header field to output.
** p -- the value to put in it.
** fp -- file to put it to.
** oldstyle -- TRUE if this is an old style header.
** m -- a pointer to the mailer descriptor. If NULL,
** don't transform the name at all.
** e -- the envelope containing the message.
** outputs "p" to file "fp".
commaize(h
, p
, fp
, oldstyle
, m
, e
)
** Output the address list translated by the
** mailer and with commas.
printf("commaize(%s: %s)\n", h
->h_field
, p
);
(void) sprintf(obp
, "%s: ", h
->h_field
);
opos
= strlen(h
->h_field
) + 2;
** Run through the list of values.
** Find the end of the name. New style names
** end with a comma, old style names end with
** a space character. However, spaces do not
** necessarily delimit an old-style name -- at
** signs mean keep going.
while ((isascii(*p
) && isspace(*p
)) || *p
== ',')
(void) prescan(p
, oldstyle
? ' ' : ',', pvpbuf
, &oldp
);
/* look to see if we have an at sign */
while (*p
!= '\0' && isascii(*p
) && isspace(*p
))
while (*p
!= '\0' && isascii(*p
) && isspace(*p
))
/* at the end of one complete name */
/* strip off trailing white space */
((isascii(*p
) && isspace(*p
)) || *p
== ',' || *p
== '\0'))
/* translate the name to be relative */
flags
= RF_HEADERADDR
|RF_ADDDOMAIN
;
if (bitset(H_FROM
, h
->h_flags
))
name
= remotename(name
, m
, flags
, &stat
, e
);
/* output the name with nice formatting */
if (opos
> 78 && !firstone
)
(void) strcpy(obp
, ",\n");
(void) sprintf(obp
, " ");
(void) sprintf(obp
, ", ");
while ((c
= *name
++) != '\0' && obp
< &obuf
[MAXLINE
])
(void) strcpy(obp
, "\n");
** COPYHEADER -- copy header list
** This routine is the equivalent of newstr for header lists
** header -- list of header structures to copy.
register HDR
**tail
= &ret
;
newhdr
= (HDR
*) xalloc(sizeof(HDR
));
STRUCTCOPY(*header
, *newhdr
);