This is header.c in view mode; [Download] [Up]
/* @(#)src/header.c 1.4 02 Dec 1990 03:39:02 */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * 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 list *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 list *remote_header; /* simple remote-form header */ static struct list *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(); /* * 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; } error = NULL; /* * 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 list *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 list *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 list **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; } } } } /* * 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 list *f; /* header field entry */ f = (struct list *)xmalloc(sizeof(struct list)); /* 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 */ } /* * 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 list **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 list *)xmalloc(sizeof(struct list)); (*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 list *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 ""; } /* * 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 list **rhq = &remote_header; /* where to put next field */ struct list *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 list *)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 list **shq = &strict_header; /* where to put next field */ struct list *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 list *)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; } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.