|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T h
Length: 24474 (0x5f9a)
Types: TextFile
Names: »header.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/header.c«
/* @(#)header.c 3.16 9/2/88 05:16:35 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*
* namei master id: @(#)header.c 3.16 9/2/88 05:16:35
*/
/*
* header.c:
* routines to process message headers.
*
* external functions: process_header, read_header, write_header
*/
#include <stdio.h>
#include <ctype.h>
#include "defs.h"
#include "smail.h"
#include "field.h"
#include "addr.h"
#include "transport.h"
#include "main.h"
#include "spool.h"
#include "log.h"
#include "exitcodes.h"
#include "dys.h"
#ifndef DEPEND
# include "extern.h"
# include "debug.h"
#endif
/* variables defined in this file */
struct queue *header; /* list of header fields */
/* variables local to this file */
static int use_resent; /* TRUE if Resent- fields exist */
static int remove_bcc_fields; /* TRUE to remove Bcc: header fields */
static char *to_fn; /* name of To: field */
static char *from_fn; /* name of From: field */
static char *sender_fn; /* name of the Sender: field */
static char *cc_fn; /* name of the Cc: field */
static char *bcc_fn; /* name of the Bcc: field */
static char *message_id_fn; /* name of the Message-Id: field */
static int found_to; /* TRUE if To: field was found */
static int found_message_id; /* TRUE if Message-Id: field found */
static int found_from; /* TRUE if From: field was found */
static int found_cc; /* TRUE if Cc: field was found */
static int found_bcc; /* TRUE if Bcc: field was found */
static int found_date; /* TRUE if Date: field was found */
static int found_sender; /* TRUE if Sender: field was found */
static int count_received; /* count of Received: fields found */
static struct addr *header_from; /* list of From: addrs */
static struct addr *header_sender; /* list of Sender: addrs */
static struct queue *remote_header; /* simple remote-form header */
static struct queue *strict_header; /* strict RFC822 header */
/* functions local to this file */
static void check_for_resent();
static void scan_header();
static void new_field();
static char *build_to_field();
static char *build_date_field();
static char *build_message_id_field();
static char *build_from_field();
static char *build_received_field();
static char *build_return_path();
static void build_remote_header();
static void build_strict_header();
\f
/*
* process_header - first pass processing of a header.
*
* Note the existence of certain headers and, if input rq is non-nil
* fill it with a list of recipient addresses taken from either the
* Resent-To: Resent-Cc: and Resent-Bcc: fields or from the To: Cc:
* and Bcc: fields.
*/
char *
process_header(rq)
struct addr **rq; /* put list of recipients here */
{
char *error;
/*
* initialize the data to be searched for in the header
*/
use_resent = FALSE;
found_to = FALSE; /* nothing found yet */
found_message_id = FALSE;
found_from = FALSE;
found_date = FALSE;
found_sender = FALSE;
found_cc = FALSE;
found_bcc = FALSE;
count_received = 0;
header_from = NULL; /* initialize From: addr list */
header_sender = NULL; /* initialize Sender: addr list */
strict_header = NULL; /* no strict RFC822 header yet */
/*
* Bcc: fields are removed only if we are processing the header
* for addresses, otherwise we assume that the user agent would
* have removed them if it didn't want them passed along
*/
remove_bcc_fields = (rq != NULL);
check_for_resent(); /* setup use_resent */
error = NULL;
scan_header(rq, &error); /* scan for fields and recipients */
if (rq && error) {
/* got an error message while extracting recipients, done */
return error;
}
/*
* if no recipient fields given, create a To: field containing
* the known recipients
*/
if (! (found_to || found_cc || found_bcc) && rq == NULL) {
new_field(build_to_field());
}
/*
* if no From: field exists, and no Sender: field exists, create
* a From: field.
*
* otherwise, if no Sender: field exists and From: is not verified
* to be the actual sender, create a Sender: field.
*/
if (! (found_from || found_sender)) {
if (!islocal) {
from_fn = use_resent?
"Apparently-Resent-From":
"Apparently-From";
}
new_field(build_from_field(from_fn));
} else if (islocal && found_from &&
(!sender_is_trusted || !header_from || header_from->succ))
{
new_field(build_from_field(sender_fn));
}
/*
* if no date field given, create one
*/
if (!found_date) {
new_field(build_date_field());
}
/*
* if no message-id field given, create one
*/
if (!found_message_id) {
new_field(build_message_id_field());
}
/*
* note count of received lines if count is not already known
*/
if (hop_count < 0) {
hop_count = count_received;
}
return error; /* return any error messages */
}
/*
* set use_resent if there exist any standard Resent- fields in
* the header
*/
static void
check_for_resent()
{
register struct queue *q; /* index to headers */
/* on the first pass check for Resent- headers */
for (q = header; q; q = q->succ) {
if (HDREQ("resent-to", q->text) ||
HDREQ("resent-from", q->text) ||
HDREQ("resent-cc", q->text) ||
HDREQ("resent-bcc", q->text))
{
use_resent = TRUE;
break;
}
}
/* set the address field names based on this information */
if (use_resent) {
to_fn = "Resent-To";
from_fn = "Resent-From";
sender_fn = "Resent-Sender";
cc_fn = "Resent-Cc";
bcc_fn = "Resent-Bcc";
message_id_fn = "Resent-Message-Id";
} else {
to_fn = "To";
from_fn = "From";
sender_fn = "Sender";
cc_fn = "Cc";
bcc_fn = "Bcc";
message_id_fn = "Message-Id";
}
}
/*
* scan_header - pass through searching for required headers.
*
* If we are extracting recipient addresses from the header, do
* so here.
*/
static void
scan_header(rq, error)
struct addr **rq; /* put recipients here, if non-NULL */
char **error; /* store error message here */
{
register struct queue *q; /* temp for scanning header */
for (q = header; q; q = q->succ) {
if (hop_count < 0 && HDREQ("Received", q->text)) {
count_received++; /* count Received: fields */
} else if (HDREQ(to_fn, q->text)) {
found_to = TRUE;
if (islocal) {
q->text = process_field(q->text,
index(q->text,':')+1,
(char *)NULL,
(char *)NULL,
rq,
islocal?F_LOCAL:0,
error);
}
} else if (HDREQ("apparently-to", q->text) ||
HDREQ("to", q->text) ||
HDREQ("resent-to", q->text))
{
found_to = TRUE;
} else if (HDREQ(message_id_fn, q->text)) {
found_message_id = TRUE;
} else if (HDREQ(from_fn, q->text)) {
found_from = TRUE;
if (islocal) {
q->text = process_field(q->text,
index(q->text, ':')+1,
(char *)NULL,
(char *)NULL,
&header_from,
islocal?F_LOCAL:0,
error);
}
} else if (HDREQ("date", q->text)) {
found_date = TRUE;
} else if (HDREQ(sender_fn, q->text)) {
if (islocal) {
struct queue **pq;
/*
* local user's can't their own supply Sender: fields.
* so delete this field.
*/
for (pq = &header; *pq && *pq != q; pq = &(*pq)->succ) ;
if (*pq) {
*pq = q->succ;
}
} else {
found_sender = TRUE;
}
} else if (HDREQ(cc_fn, q->text)) {
found_cc = TRUE;
if (islocal) {
q->text = process_field(q->text,
index(q->text, ':') + 1,
(char *)NULL,
(char *)NULL,
rq,
islocal?F_LOCAL:0,
error);
}
} else if (HDREQ(bcc_fn, q->text)) {
found_bcc = TRUE;
if (islocal) {
q->text = process_field(q->text,
index(q->text, ':') + 1,
(char *)NULL,
(char *)NULL,
rq,
islocal?F_LOCAL:0,
error);
}
} else if (HDREQ("precedence", q->text)) {
/*
* a precedence field is specified, use it to set the message
* priority
*/
int new_grade = parse_precedence(index(q->text, ':'));
if (new_grade) {
msg_grade = new_grade;
}
}
}
}
\f
/*
* Create a To: header field
*/
static char *
build_to_field()
{
register struct addr *rq; /* recipients to put in To: field */
struct str str; /* region for string macros */
int col; /* current output line column */
rq = recipients;
/*
* Sendmail inserts Apparently-To: fields because RFC822 people
* complained about it being against the rules to add a To:
* line. We cheat. If the letter is not originated locally,
* use the Apparently-To: convention. Otherwise do it the
* right way.
*
* We'll see if anybody complains
*/
if (!islocal) {
if (use_resent) {
to_fn = "Apparently-Resent-To";
} else {
to_fn = "Apparently-To";
}
}
/*
* now we actually form the To: line from the known recipient
* addresses
*/
STR_INIT(&str); /* initialize dynamic string region */
/* form = To: recipient(, recipient)* */
rq = recipients;
/* special case, insert the first address */
if (rq) {
if (rq->in_addr[0] == '@') {
/* route-addr, put it in angle brackets */
str_printf(&str, "%s: <%s>", to_fn, rq->in_addr);
} else {
/* otherwise just use the address */
str_printf(&str, "%s: %s", to_fn, rq->in_addr);
}
rq = rq->succ;
}
col = str.i;
while (rq) {
int len = strlen(rq->in_addr);
STR_NEXT(&str, ',');
col++;
/*
* put at least 27-chars on a line, try to keep down below 72
* NOTE: we aren't counting TAB characters hidden in the
* addresses.
*/
if (col > 27 && col + len > 72) {
STR_NEXT(&str, '\n');
STR_NEXT(&str, '\t');
col = 8;
} else {
STR_NEXT(&str, ' ');
col++;
}
if (rq->in_addr[0] == '@') {
str_printf(&str, "<%s>", rq->in_addr);
len += 2; /* 2 chars for the angel brackets */
} else {
STR_CAT(&str, rq->in_addr);
}
col += len;
rq = rq->succ;
}
STR_NEXT(&str, '\n');
STR_NEXT(&str, '\0');
STR_DONE(&str);
return str.p; /* return finished To: field */
}
/*
* build a Date: field
*/
static char *
build_date_field()
{
register char *s; /* returned header field */
if (date_field &&
(s = expand_string(date_field, (struct addr *)NULL,
(char *)NULL, (char *)NULL)))
{
return COPY_STRING(s);
}
return "";
}
/*
* Form a Message-Id: header field.
*/
static char *
build_message_id_field()
{
register char *s; /* returned header field */
if (message_id_field &&
(s = expand_string(message_id_field, (struct addr *)NULL,
(char *)NULL, (char *)NULL)))
{
if (use_resent) {
return xprintf("Resent-%s", s);
} else {
return COPY_STRING(s);
}
}
return "";
}
/*
* form a From: or Sender: header field
*/
static char *
build_from_field(fn)
char *fn; /* field name */
{
struct str str; /* build header field here */
STR_INIT(&str);
/* format example: From: user (Name Of User) */
/* format example: From: hostc!hostb!hosta!user */
str_printf(&str, "%s: %s", fn, sender);
if (sender_name == NULL && islocal) {
getfullname();
}
if (sender_name) {
str_printf(&str, " (%s)", sender_name);
}
STR_NEXT(&str, '\n');
STR_NEXT(&str, '\0');
STR_DONE(&str);
return str.p;
}
/*
* link in a brand new field with the given text
*/
static void
new_field(field)
char *field; /* new field to add */
{
register struct queue *f; /* header field entry */
f = (struct queue *)xmalloc(sizeof(struct queue)); /* create it */
f->text = field;
f->succ = header; /* link it in */
header = f;
}
/*
* parse_precedence - parse a Precedence: field and return it's grade value
*
* use tokenize to parse for an atom (the first atom is used, the rest are
* ignored). Then lookup the atom in the grades string and return the
* associated grade, if found, 0 otherwise.
*/
int
parse_precedence(field)
char *field;
{
int len; /* length of token */
struct token *tok; /* list of tokens from tokenize */
register char *p = grades; /* temp for scanning grades */
char *error; /* error message from tokenize */
DEBUG(DBG_HEADER_HI, "parse_precedence called\n");
error = tokenize(field, &tok, FALSE, TRUE);
if (error) {
/* tokenize didn't like the field for some reason */
write_log(LOG_MLOG, "Error in precedence field: %s", error);
return '\0'; /* no precedence value */
}
if (tok->form != T_TEXT) {
return '\0'; /* not a text (atom) token */
}
len = strlen(tok->text);
DEBUG1(DBG_HEADER_MID, "precedence is %s\n", tok->text);
while (p) {
if (strncmpic(tok->text, p, len) == 0 && p[len] == ':') {
/* we have a match, grab the character */
return p[len+1];
}
/* skip to end of precedence string */
p = index(p, ':');
if (p) {
/* skip past grade character */
p = index(p, ':');
if (p) {
p++; /* now at next precedence string */
}
}
}
DEBUG(DBG_QUEUE_MID, "unknown precedence\n");
return '\0'; /* unknown precedence */
}
\f
/*
* read_header - initialize header structures, reading from spool file
*
* Read a header on the spool file. The header data structure is
* initialized to contain an entry for each field of the header.
*
* returns SUCCEED or FAIL, the actual failure is logged.
*/
int
read_header()
{
struct str field; /* input field */
register int c; /* current input char */
int last_c = EOF; /* last input char */
struct queue **link_addr; /* forward link for next field */
long line_mark; /* marks beginning of a line */
link_addr = &header;
/*
* loop until done reading the header
*/
for (;;) {
STR_INIT(&field);
/*
* field name is some non-space chars followed by
* optional white space up to a ':'
*/
if (last_c == ':') {
/* we had a read ahead character past the last newline */
c = EOF; /* don't allow null fieldnames */
} else {
if (last_c != EOF) {
STR_NEXT(&field, last_c);
line_mark = tell_spool() - 1;
last_c = EOF;
} else {
/*
* save the beginning of the line, so we can go back to
* here if this is not a header
*/
line_mark = tell_spool();
}
while ((c = GETSPOOL()) != EOF && c != READ_FAIL &&
!isspace(c) && c != ':')
{
STR_NEXT(&field, c);
last_c = c;
}
while (c != EOF && c != READ_FAIL &&
isspace(c))
{
STR_NEXT(&field, c);
c = GETSPOOL();
}
if (c == READ_FAIL) {
write_log(LOG_SYS, "read error in spool file <%s/%s>",
spool_dir, spool_fn);
return FAIL;
}
}
/* not a valid field name, must be past the header */
if (c != ':') {
break;
}
/*
* looks like we are in a header field, so scan to the end of it
*/
STR_NEXT(&field, c);
last_c = c;
/*
* header field terminated by new line which does not start
* with a SPACE or TAB char, or by EOF.
*/
while ((c = GETSPOOL()) != EOF && c != READ_FAIL &&
!(last_c == '\n' && c != ' ' && c != '\t'))
{
STR_NEXT(&field, c);
last_c = c;
}
if (c == READ_FAIL) {
write_log(LOG_SYS, "read error in spool file <%s/%s>",
spool_dir, spool_fn);
}
last_c = c; /* preserve this for the next field */
STR_NEXT(&field, '\0');
STR_DONE(&field);
*link_addr = (struct queue *)xmalloc(sizeof(struct queue));
(*link_addr)->text = field.p;
link_addr = &(*link_addr)->succ;
if (c == EOF || c == '\n') {
line_mark = -1; /* don't seek back to previous line */
field.p = NULL;
field.i = 0;
break;
}
}
*link_addr = NULL; /* terminate header queue */
/*
* all done reading the field, cleanup, seek to beginning of
* message body (after last valid field line)
*/
if (line_mark >= 0 && seek_spool(line_mark) == FAIL) {
write_log(LOG_SYS, "seek error on spool file <%s/%s>",
spool_dir, spool_fn);
return FAIL;
}
if (field.p) {
STR_DONE(&field); /* free the field that wasn't */
STR_FREE(&field);
}
return SUCCEED;
}
/*
* write_header - write out the header to a stdio FILE
*
* inputs:
* file - file to write to.
* flags - flags from the transport file entry. The following bits
* are used:
* STRICT_TPORT - generate a strict RFC822 header
* LOCAL_TPORT - do not make a remote-form header
* PUT_RECEIVED - build a "Received:" field
* PUT_RETURNPATH - build a "Return-Path:" field
* PUT_CRLF - put \r\n at the end of each line
*
* outputs:
* SUCCEED or FAIL
*/
int
write_header(f, flags)
FILE *f; /* file to send header to */
long flags; /* transport flags */
{
struct queue *q; /* temp for processing header */
int crlf = (flags & PUT_CRLF)?1:0; /* put \r\n at end of each line? */
/* output a Return-Path: header if required */
if (flags & PUT_RETURNPATH) {
if (write_field(build_return_path(), f, crlf) == FAIL) {
return FAIL;
}
}
/* output a Received: header if required */
if (flags & PUT_RECEIVED) {
if (write_field(build_received_field(), f, crlf) == FAIL) {
return FAIL;
}
}
if (use_resent) {
/*
* the situation gets complex if resent- headers are involved,
* so ignore the issue by not changing anything.
*/
q = header;
} else if (flags & STRICT_TPORT) {
/* output a strict header, building one if none exists */
if (strict_header == NULL) {
build_strict_header();
}
q = strict_header;
} else if (flags & LOCAL_TPORT) {
/* output header as is */
q = header;
} else {
/* output a normal remote header */
if (remote_header == NULL) {
build_remote_header();
}
q = remote_header;
}
/*
* output the appropriate header, as selected above
*/
while (q) {
/*
* remote bcc fields if addresses were extracted from the
* header. Otherwise, we expect that the UA would have
* removed them if they were not wanted.
*
* TODO: should we always remove bcc: fields from locally
* generated mail?
*/
if (!remove_bcc_fields || !HDREQ(bcc_fn, q->text)) {
if (write_field(q->text, f, crlf) == FAIL) {
return FAIL;
}
}
q = q->succ;
}
return SUCCEED;
}
/*
* write_field - output a header field, possibly changing \n to \r\n.
*
* return SUCCEED or FAIL.
*/
int
write_field(field, f, crlf)
register char *field; /* field to be written */
FILE *f; /* file to send to */
int crlf; /* TRUE to write \r\n for \n */
{
register int c;
if (field[0] == '\0') {
/* empty field, don't write out anything */
return SUCCEED;
}
while (c = *field++) {
/* write out c, possibly with a '\r' first */
if ((c == '\n' && crlf && putc('\r', f) == EOF) || putc(c, f) == EOF) {
return FAIL;
}
}
/* if field did not end with \n, supply it */
if (field[-2] != '\n') {
if (crlf) {
if (putc('\r', f) == EOF) {
return FAIL;
}
}
if (putc('\n', f) == EOF) {
return FAIL;
}
}
return SUCCEED;
}
/*
* form a Received: header field
*/
static char *
build_received_field()
{
register char *s; /* returned header field */
if (received_field &&
(s = expand_string(received_field, (struct addr *)NULL,
(char *)NULL, (char *)NULL)))
{
return s;
}
return "";
}
/*
* build a Return-Path: header field.
*/
static char *
build_return_path()
{
register char *s; /* returned header field */
if (return_path_field &&
(s = expand_string(return_path_field, (struct addr *)NULL,
(char *)NULL, (char *)NULL)))
{
return s;
}
return "";
}
\f
/*
* build_remote_header - build a simple remote-form header
*
* For From: and Sender: fields prepend localhost! on addresses which
* are pure !-routes, if mail is originated local then convert user to
* user@localdomain and qualify known domain abbreviations.
*/
static void
build_remote_header()
{
struct queue **rhq = &remote_header; /* where to put next field */
struct queue *hq; /* temp for scanning header */
char *error; /* (ignored) error message */
/*
* copy the standard header to the remote header, transforming
* sender and recipient fields as appropriate.
*/
for (hq = header; hq; hq = hq->succ) {
*rhq = (struct queue *)xmalloc(sizeof(**rhq));
(*rhq)->succ = NULL;
if (HDREQ(from_fn, hq->text) || HDREQ(sender_fn, hq->text)) {
/*
* field containing a sender address:
* prepend uucp_host! in !-routes
* append @visiblename after local-form addresses
* qualify known domain abbreviations, if local
*/
(*rhq)->text = process_field(hq->text,
index(hq->text, ':') + 1,
visible_name,
uucp_name,
(struct addr **)NULL,
(islocal? F_LOCAL: 0),
&error);
} else if (islocal && (HDREQ(to_fn, hq->text) ||
HDREQ(cc_fn, hq->text) ||
HDREQ(bcc_fn, hq->text)))
{
/*
* locally generated field containing recipient addresses:
* append @visible_name after local-form addresses
* qualify known domain abbreviations
*/
(*rhq)->text = process_field(hq->text,
index(hq->text, ':') + 1,
visible_name,
(char *)NULL,
(struct addr **)NULL,
F_LOCAL,
&error);
} else {
(*rhq)->text = hq->text;
}
rhq = &(*rhq)->succ;
}
}
/*
* build_strict_header - build a somewhat RFC822 conformant header
*
* For addresses which are in local form, in !-route form or in
* remainder%target form, append @visiblename and prepend a !-route
* path back to the sender. This essentially qualifies the address
* within the context of the current host, which will then be
* routed through on replies to handle the !-routes.
*/
static void
build_strict_header()
{
char *error; /* error messages */
struct queue **shq = &strict_header; /* where to put next field */
struct queue *hq; /* temp for scanning header */
char *sender_path; /* path to sender's machine */
/*
* build a path back to the sender's machine, which is a !-route
* back to the sender with the last component (!username) removed
* this will be passed to process_field as the uucp_name parameter
* for recipient fields.
*/
sender_path = build_uucp_route(sender, &error);
if (sender_path == NULL) {
/*
* errors will generate a NULL sender path, so nothing will
* be prepended to addresses in recipient fields
*/
write_log(LOG_MLOG, "error building path to sender <%s>: %s",
sender, error);
} else {
char *p = sender_path + strlen(sender_path);
/*
* back up over the trailing "!token" and terminate the string
*/
p = back_address_token(sender_path, p);
if (p == NULL) {
write_log(LOG_MLOG,
"error building path to sender <%s>: unexpected end of token",
sender);
sender_path = NULL;
} else if (p == sender_path) {
sender_path = NULL;
} else {
p[-1] = '\0'; /* terminate the path */
}
}
/*
* copy the standard header to a new strict header, performing
* the necessary transformations on sender and recipient fields
*/
for (hq = header; hq; hq = hq->succ) {
*shq = (struct queue *)xmalloc(sizeof(**shq));
(*shq)->succ = NULL;
if (HDREQ(from_fn, hq->text) || HDREQ(sender_fn, hq->text)) {
/*
* field containing a sender address:
* append @visiblename after local-form addresses
* qualify known domain abbreviations, if local
*/
(*shq)->text = process_field(hq->text,
index(hq->text, ':') + 1,
visible_name,
(char *)NULL,
(struct addr **)NULL,
(islocal? F_LOCAL: 0)|F_STRICT,
&error);
} else if (HDREQ(to_fn, hq->text) || HDREQ(cc_fn, hq->text) ||
HDREQ(bcc_fn, hq->text))
{
/*
* field containing a recipient address:
* append @visiblename after !-route, local-form or u%d form
* prepend sender_path! before the same set of addresses
* qualify known domain abbreviations, if local
*/
(*shq)->text = process_field(hq->text,
index(hq->text, ':') + 1,
visible_name,
sender_path,
(struct addr **)NULL,
(islocal? F_LOCAL: 0)|F_STRICT,
&error);
} else {
(*shq)->text = hq->text;
}
shq = &(*shq)->succ;
}
}