|
|
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 q
Length: 29884 (0x74bc)
Types: TextFile
Names: »queue.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/queue.c«
/* @(#)queue.c 3.28 2/22/89 01:31:23 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* queue.c:
* operations on the queue, such as reading them and writing to
* the queue through the spooling functions, and scanning for work.
*
* external functions: queue_message, read_message, write_body,
* log_incoming, check_grade, scan_queue,
* swallow_smtp
*/
#include <stdio.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include "defs.h"
#include "smail.h"
#include "spool.h"
#include "addr.h"
#include "main.h"
#include "log.h"
#include "field.h"
#include "dys.h"
#include "exitcodes.h"
#ifndef DEPEND
# include "debug.h"
# include "extern.h"
#endif
/* variables local to this file */
static long start_msg; /* where message body begins */
#define MAXUSER 16 /* maximum length of user name */
/* variables exported from this file */
int msg_grade; /* grade level for this message */
char login_user[MAXUSER+1]; /* login name from spool file */
/* functions local to this file */
static int queue_envelope();
static int queue_message_internal();
static int queue_message_with_dots();
static int queue_message_simple();
static int fixup_login_user();
static int put_line();
static int read_from_lines();
static char *forward_token();
static char *backward_token();
static void finish_sender();
static int queue_compare();
\f
/*
* queue_message - spool a message.
*
* spool a message from a stdio file, possibly obeying the hidden-dot
* protocol from the input. It will always turn cr/lf into just a
* newline, while never requiring this to be at the end of lines.
*
* If a caller wishes to log any errors that occured during spooling,
* it must call log_spool_errors.
*
* return SUCCEED or FAIL.
*/
int
queue_message(f, dusage, list, error)
register FILE *f; /* input file */
enum dot_usage dusage; /* how to treat dots */
struct addr *list; /* list of recipient addr structures */
char **error; /* return error message here */
{
DEBUG(DBG_QUEUE_HI, "queue_message called\n");
/* create the spool file */
if (creat_spool() == FAIL) {
*error = "spool file creat error";
if (dusage == SMTP_DOTS) {
(void) swallow_smtp(f);
}
return FAIL;
}
errno = 0;
*error = NULL;
/* fill it with stuff */
if (queue_envelope(list) == FAIL ||
queue_message_internal(f, dusage, error) == FAIL ||
write_spool() == FAIL ||
fixup_login_user() == FAIL)
{
if (*error == NULL) {
*error = "spool file write error";
}
unlink_spool();
return FAIL;
}
return SUCCEED;
}
/*
* swallow_smtp - read and throw away SMTP message contents
*
* Read lines up to a line containing a single '.'. Do not do
* anything with these lines. Return SUCCEED if we successfully read
* up to a line containing only a single '.'. Return FAIL otherwise.
*
* This is used to read in a complete message from an SMTP transaction
* which will not be using because of some error.
*/
int
swallow_smtp(f)
register FILE *f; /* input file */
{
register int c;
for (;;) {
if ((c = getc(f)) == EOF) return FAIL;
if (c == '.') {
if ((c = getc(f)) == EOF) return FAIL;
if (c == '\n') {
return SUCCEED;
}
if (c == '\r') {
if ((c = getc(f)) == EOF) return FAIL;
if (c == '\n') {
return SUCCEED;
}
}
}
while (c != '\n') {
if ((c = getc(f)) == EOF) return FAIL;
}
}
}
/*
* queue_envelope - write out header filler and arguments for spool file
*
* save some space for the login username, which is computed last, and
* write out any arguments, to the spool file. The form used is one smail
* command argument per line, allowing the use of process_args() in main
* to decode the envelope information.
*/
static int
queue_envelope(list)
struct addr *list; /* input recipient list */
{
register struct addr *cur; /* current recipient addr to process */
char *p; /* temp */
char buf[50]; /* temp storage */
/*
* first write out some spaces which will be filled in later with the
* login name of the sender
*/
if (put_line(" ") == EOF) {
return FAIL;
}
/*
* write out the real uid and effective gid, as numbers. Validation
* of trusted_users will be done from the ids stored here.
*/
(void) sprintf(buf, "%d %d", real_uid, prog_egid);
if (put_line(buf) == EOF) {
return FAIL;
}
/*
* write out important state information in the form of command arguments.
*/
if (extract_addresses) {
/* signal that addresses are to be taken from the header */
if (put_line("-t") == EOF) {
return FAIL;
}
}
/* tell which form of error recover is being used */
switch (error_processing) {
case MAIL_BACK:
p = "-oem";
break;
case WRITE_BACK:
p = "-oew";
break;
case TERMINAL:
p = "-oep";
break;
default:
p = "-oeq";
break;
}
if (put_line(p) == EOF) {
return FAIL;
}
if (dont_deliver) {
put_line("-N");
}
if (hop_count >= 0) {
(void) sprintf(buf, "-h%d", hop_count);
if (put_line(buf) == EOF) {
return FAIL;
}
}
if (! do_aliasing) {
if (put_line("-n") == EOF) {
return FAIL;
}
}
if (me_too) {
if (put_line("-om") == EOF) {
return FAIL;
}
}
/*
* write the sender's full name, if one was given explicitly. If it was
* not given explicitly, we would not yet know what it is.
*/
if (sender_name) {
if (put_line("-F") == FAIL ||
put_line(sender_name) == FAIL)
{
return FAIL;
}
}
/*
* write the sender address, if one was given explicitly. If it was
* not given explicitly, we would not yet know what it is.
*/
if (sender) {
if (put_line("-f") == FAIL ||
put_line(sender) == FAIL)
{
return FAIL;
}
}
/*
* write out all of the recipient addresses. If the -t flag was given,
* this is the list of addresses that are explicitly not recipients.
*/
for (cur = list; cur; cur = cur->succ) {
if (put_line(cur->in_addr) == FAIL) {
return FAIL;
}
}
/* envelope ends in one blank line */
if (put_line("") == FAIL) {
return FAIL;
}
return SUCCEED;
}
\f
/*
* queue_message_internal - read the message into a spool file.
*
* Call either queue_message_with_dots() or queue_message_simple() to
* spool a message to a spool file.
*/
static int
queue_message_internal(f, dusage, error)
FILE *f;
enum dot_usage dusage;
char **error; /* optionally store error here */
{
if (dusage == NO_DOT_PROTOCOL) {
return queue_message_simple(f);
}
return queue_message_with_dots(f, dusage, error);
}
/*
* queue_message_with_dots - read in a message and write it to the
* spool file, with a dot protocol.
*
* Read in a message using one of the dot protocols (HIDDEN_DOTS,
* DOT_ENDS_MESSAGE or SMTP_DOTS). All of these protocols end a
* message when a dot is found on a line by itself. The HIDDEN_DOTS
* protocol drops an initial dot from a line that contains other data.
* The SMTP_DOTS protocol is like HIDDEN_DOTS except that a message
* must be terminated by a '.', with an encouter of EOF being an
* error. Also, with SMTP_DOTS, the entire message is read from the
* input even if a write error occured to the spool file.
*
* All of these protocols treat a CR/LF sequence as equivalent to a
* single newline.
*
* return SUCCEED or FAIL.
*/
static int
queue_message_with_dots(f, dusage, error)
register FILE *f; /* input file */
enum dot_usage dusage; /* how to treat dots */
char **error; /* optionally store error here */
{
register int c;
register int put_success = SUCCEED; /* FAIL if PUTSPOOL fails */
for (;;) {
if ((c = getc(f)) == EOF) break;
if (c == '.') {
if ((c = getc(f)) == EOF) break;
if (c == '\n') {
return put_success;
}
if (c == '\r') {
if ((c = getc(f)) == EOF) break;
if (c == '\n') {
return put_success;
}
if (put_success == SUCCEED &&
(dusage == DOT_ENDS_MESSAGE && PUTSPOOL('.') == FAIL) ||
PUTSPOOL('\r') == FAIL)
{
if (dusage != SMTP_DOTS) {
return FAIL;
}
put_success = FAIL;
}
} else {
if (put_success == SUCCEED &&
(dusage == DOT_ENDS_MESSAGE && PUTSPOOL('.') == FAIL))
{
put_success = FAIL;
}
}
}
for (;;) {
while (c != '\n' && c != '\r') {
if (put_success == SUCCEED && PUTSPOOL(c) == FAIL) {
put_success = FAIL;
}
if ((c = getc(f)) == EOF) goto read_eof;
}
if (c == '\r') {
if ((c = getc(f)) == EOF) goto read_eof;
if (c != '\n') {
if (put_success == SUCCEED && PUTSPOOL('\r') == FAIL) {
put_success = FAIL;
}
continue;
}
}
if (put_success == SUCCEED && PUTSPOOL(c) == FAIL) {
put_success = FAIL;
}
break;
}
}
read_eof:
if (dusage != SMTP_DOTS) {
return put_success;
}
*error = "Unexpected end of file";
return FAIL;
}
/*
* queue_message_simple - read in a message with no dot protocol.
*
* Read in a message without any specific protocol. No CR/LF mapping
* is done and the end of message is given only with an EOF.
*
* Return SUCCEED or FAIL.
*/
static int
queue_message_simple(f)
register FILE *f;
{
register int c;
while ((c = getc(f)) != EOF) {
if (PUTSPOOL(c) == FAIL) {
return FAIL;
}
}
return SUCCEED;
}
\f
/*
* fixup_login_user - insert the login username at the start of the spoolfile
*
* to get the message into the spool file as fast as possible, the login
* name of the user that ran smail is computed last and filled after
* everything else is through.
*
* This is ugly, but it can take a significant amount of time to compute
* the real user, and it is better to get the spool file written quickly,
* thus narrowing the window of system crash vulnerability.
*/
static int
fixup_login_user()
{
extern char *getlogin();
register char *p = getlogin();
register int i;
extern long lseek();
struct passwd *pw;
/* make sure the login name exists and is a real user in /etc/passwd */
if (p == NULL || getpwbyname(p) == NULL) {
pw = getpwbyuid(real_uid);
if (pw == NULL) {
p = "Postmaster";
} else {
p = pw->pw_name;
}
}
/*
* truncate to 16 chars, if for some reason we got a
* larger user name
*/
if (strlen(p) >= MAXUSER) {
p[MAXUSER] = '\0';
}
/*
* write the name to disk
*/
(void) lseek(spoolfile, 0L, 0);
i = strlen(p);
if (write(spoolfile, p, i) < i) {
return FAIL;
}
if (msg_foffset == 0) {
/*
* if the current incore spool file region is at the
* beginning of the file, make a copy there too.
*/
register char *q;
q = msg_buf;
while (*p) {
*q++ = *p++;
}
}
return SUCCEED;
}
/*
* put_line - write a complete line to the spool file
*
* the string passed is assumed to be an atomic argument for smail, which
* should end in a newline. To guard against newlines in the text of the
* string to end an argument, newline is encoded as \n and \ is encoded as
* \\.
*/
static int
put_line(s)
register char *s;
{
while (*s) {
if (*s == '\\') {
/* encode \ as \\ and newline as \n */
if (PUTSPOOL('\\') == EOF || PUTSPOOL('\\') == EOF) {
return FAIL;
}
} else if (*s == '\n') {
if (PUTSPOOL('\\') == EOF || PUTSPOOL('n') == EOF) {
return FAIL;
}
} else {
if (PUTSPOOL(*s) == EOF) {
return FAIL;
}
}
s++;
}
if (PUTSPOOL('\n') == EOF) {
return FAIL;
}
return SUCCEED;
}
\f
/*
* read_message - scan through an open spool file
*
* read through the current open spool file to grab From_ and Header:
* information. Putting the sender address in the sender variable,
* and storing the header information in the headers variable as
* side effects.
*
* return the argument vector in *argv
*
* return NULL on failure. The reason for failure will be logged
* in the system log file and the spool file should be closed and
* ignored for now.
*/
char **
read_message()
{
int a = 32; /* vectors allocated thus far */
int i; /* temp */
register int c; /* character from spool file */
register char **argv; /* computed argument vector */
char *p;
int just_trust = TRUE; /* true if no "trusted" variables */
DEBUG(DBG_QUEUE_HI, "read_message called\n");
/* seek to the beginning of the region */
if (seek_spool(0L) == FAIL) {
return NULL;
}
/* get the login username from the spool file */
i = 0;
while ((c = GETSPOOL()) != EOF && c != READ_FAIL &&
c != '\n' && c != ' ' && i < MAXUSER)
{
login_user[i++] = c;
}
/* read to the end of the first line */
while (c != '\n' && c != EOF && c != READ_FAIL) {
c = GETSPOOL();
}
login_user[i] = '\0';
/* read the real uid and effective gid under which the mailer was exec'd */
if (c != READ_FAIL && c != EOF) {
char buf[50]; /* temp input buffer */
char *p = buf; /* temp for scanning buf */
while ((c = GETSPOOL()) != '\n' && c != EOF && c != READ_FAIL) {
*p++ = c;
}
*p = '\0';
(void) sscanf(buf, "%d %d", &real_uid, &prog_egid);
}
if (c == READ_FAIL) {
write_log(LOG_SYS|LOG_PANIC, "read failed: %s/%s: %s",
spool_dir, input_spool_fn, strerrno());
return NULL;
}
if (c == EOF) {
write_log(LOG_SYS, "unexpected end of file: %s/%s",
spool_dir, input_spool_fn);
}
/*
* determine if the login user is a trusted user, using the real uid.
*/
p = NULL;
if (trusted && trusted[0]) {
just_trust = FALSE;
/* trusted string a colon separated list */
for (p = strcolon(trusted); p; p = strcolon((char *)NULL)) {
struct passwd *pw = getpwbyname(p);
if (pw && real_uid == pw->pw_uid) {
break;
}
}
}
/*
* trusted group string can also flag invoker as trusted
*/
if (p == NULL && trusted_groups && trusted_groups[0]) {
just_trust = FALSE;
for (p = strcolon(trusted_groups); p; p = strcolon((char *)NULL)) {
struct group *gr = getgrbyname(p);
if (gr && prog_egid == gr->gr_gid) {
break;
}
}
}
if (p == NULL && !just_trust) {
/* user is not verified, sender is the login user */
DEBUG1(DBG_QUEUE_MID, "sender is not trusted\n", real_uid);
sender = login_user;
islocal = TRUE;
sender_is_trusted = FALSE;
}
/*
* if sender was already specified and the originator of the
* message is trusted to supply a sender, see if the sender name
* is in local form.
*/
if (sender && sender_is_trusted) {
char *error; /* temp to store error message */
islocal = (parse_address(sender, (char **)NULL, &error) == LOCAL);
}
/*
* read the argument vectors stored in the spool file
*/
DEBUG1(DBG_QUEUE_HI, "read arg vectors from spool file (at %d)\n",
tell_spool());
argv = (char **)xmalloc(a * sizeof(char *));
i = 0;
while ((c = GETSPOOL()) != EOF && c != '\n') {
struct str str;
register struct str *sp = &str; /* temp string */
/* read one vector */
STR_INIT(sp);
do {
if (c == '\\') {
c = GETSPOOL();
if (c == EOF) {
STR_FREE(sp);
write_log(LOG_SYS, "unexpected end of file: %s/%s",
spool_dir, input_spool_fn);
return NULL; /* no message */
} else if (c == 'n') {
c = '\n';
} else if (c != '\\') {
write_log(LOG_SYS, "format error in: %s/%s",
spool_dir, input_spool_fn);
return NULL; /* format error */
}
}
STR_NEXT(sp, c);
} while ((c = GETSPOOL()) != EOF && c != '\n');
/*
* finish off the argument string and store it in the vector
*/
STR_NEXT(sp, '\0');
STR_DONE(sp);
DEBUG1(DBG_QUEUE_HI, "read vector <%s> from spool file\n", sp->p);
argv[i++] = sp->p;
/* do we need a larger vector? */
if (i >= a) {
a += 32;
argv = (char **)xrealloc((char *)argv, a*sizeof(char *));
}
}
/* finish off the argument vector */
argv[i] = NULL;
/*
* login name and arguments processed, scan for From_ lines, if
* any exist. As a side effect, this may set the sender if it
* has not been set already.
*/
if (read_from_lines() == FAIL) {
/* read_from_lines logged the error */
return NULL;
}
/*
* if the sender isn't known by now, then use the login user, or
* postmaster if nothing else is available. This may be overridden
* later with a -f or -r flag.
*/
if (sender == NULL) {
if (login_user[0]) {
sender = login_user;
} else {
/* love to gang up on that postmaster */
DEBUG(DBG_QUEUE_LO, "no login user, set sender to postmaster\n");
sender = "Postmaster";
}
islocal = TRUE; /* sender appears to be local */
}
/*
* now grab the header lines.
*/
DEBUG1(DBG_QUEUE_HI, "calling read_header (offset=%d)\n", tell_spool());
if (read_header() == FAIL) {
/* read_header logged the error */
return NULL;
}
/* save the start position for the message */
start_msg = tell_spool();
DEBUG1(DBG_SPOOL_HI, "body starts at %d\n", start_msg);
/* all done, return the arguments */
return argv;
}
/*
* read_from_lines - scan From_ lines and build up a sender
*
* Scan through to the end of the From_ lines (if any exist).
* As a side effect, build up a path to the sender, if the
* sender is not already known.
*
* return SUCCESS or FAIL, and log reason for failure.
*/
static int
read_from_lines()
{
struct str pstr;
register struct str *path = &pstr; /* build up sender path here */
struct str lstr;
register struct str *line = &lstr; /* input line */
int c; /* input character */
register char *lp; /* temp for processing line */
struct str ustr;
register struct str *user = &ustr; /* user name from From_ line */
char *mark; /* mark point in line */
long line_mark; /* seek position for start of line */
DEBUG1(DBG_SPOOL_HI, "read_from_lines called (offset=%d)\n", tell_spool());
/* if we already know the sender, then don't bother computing it */
if (sender == NULL) {
islocal = TRUE; /* set FALSE if we find otherwise */
STR_INIT(user);
STR_INIT(path);
}
STR_INIT(line);
/*
* loop until we have read all of the From_ lines
*/
for (;;) {
/* read one complete line */
line->i = 0;
line_mark = tell_spool();
while ((c = GETSPOOL()) != EOF && c != READ_FAIL && c != '\n') {
STR_NEXT(line, c);
}
if (c == READ_FAIL) {
write_log(LOG_SYS, "read failed: %s/%s: %s",
spool_dir, input_spool_fn, strerrno());
}
if (c != EOF) {
STR_NEXT(line, c);
}
lp = line->p;
if (line->i < 20) {
/* less than 20 chars? couldn't possibly be a From_ line */
break;
}
/* skip beginning > character */
if (*lp == '>') {
lp++;
}
/* do we have a From line? */
if (strncmp(lp, "From", 4) != 0 || !isspace(lp[4])) {
/* no, end of From_ lines */
break;
}
if (sender) {
continue; /* we know sender scan next line */
}
/* extract the user */
user->i = 0;
lp += 5;
while (isspace(*lp)) { /* scan for start of user name */
lp++;
}
mark = lp; /* mark the start of the user name */
/* skip over the user name token */
lp = forward_token(lp);
*lp = '\0';
STR_CAT(user, mark);
STR_NEXT(user, '\0');
/* search for a remote from host */
lp = line->p + line->i - 2;
while (isspace(*lp)) {
lp--;
}
lp[1] = '\0'; /* terminate end of potential host */
lp = backward_token(lp); /* back to start of host (?) */
mark = lp; /* host may be here */
lp = backward_token(lp-1); /* back to start of from (?) */
if (strncmp(lp, "from", 4) != 0 || !isspace(lp[4])) {
path->i = 0; /* no remote from host, toss path */
DEBUG1(DBG_SPOOL_HI, "no remote from host in <%s>, toss path\n",
line->p);
continue; /* next From_ line */
}
lp = backward_token(lp-1); /* back to start of remote (?) */
if (strncmp(lp, "remote", 6) != 0 || !isspace(lp[6])) {
path->i = 0; /* no remote from host, toss path */
DEBUG1(DBG_SPOOL_HI, "no remote from host in <%s>, toss path\n",
line->p);
continue; /* next From_ line */
}
/*
* found a remote_from host, add it to the sender path
*/
if (path->i > 0) {
STR_NEXT(path, '!'); /* ! is separator */
}
STR_CAT(path, mark);
islocal = FALSE; /* not a local message */
}
STR_FREE(line); /* finished processing input line */
/*
* if we are building up a sender, then finish doing so
*/
if (sender == NULL) {
STR_NEXT(path, '\0'); /* terminate path and user */
STR_NEXT(user, '\0');
finish_sender(path->p, user->p);
STR_FREE(path); /* free the storage used */
STR_FREE(user);
}
/*
* seek back to the beginning of the last line read, which was not
* a From_ line
*/
if (seek_spool(line_mark) == FAIL) {
write_log(LOG_SYS, "seek failed on: %s/%s: %s",
spool_dir, input_spool_fn, strerrno());
return FAIL;
}
return SUCCEED;
}
/*
* forward_token - scan past the following token
*
* return the position of the character after the first token after p.
* a token ends in a space, but can include any chars in quotes, or
* after a \.
*/
static char *
forward_token(p)
register char *p; /* start search here */
{
while (isspace(*p)) p++; /* scan to start of token */
/* loop exits by return when done */
for (;;) {
register int inquote = FALSE; /* not in a quote */
switch (*p++) {
case '\\':
p++;
break;
case ' ':
case '\t':
case '\n':
if (!inquote) {
return p-1; /* past end of token */
}
break;
case '\0':
return p-1; /* past end of token */
case '"': /* start or end of a quote */
inquote = !inquote;
break;
}
}
}
/*
* backward_token - scan to beginning of previous token
*
* return the position of the first character of a group
* of non white-space characters before p.
*
* NOTE: we don't take into account text in quotes in scanning
* backwards.
*/
static char *
backward_token(p)
register char *p; /* start search here */
{
while (isspace(*p)) p--; /* scan before end of token */
while (*p && !isspace(*p)) p--; /* scan to start of token */
return p+1;
}
/*
* finish_sender - build the sender address
*
* Build the sender address out of the path and user computed in
* read_from_lines.
*/
static void
finish_sender(path, user)
register char *path; /* path to sender */
register char *user; /* user name of sender */
{
char *target; /* parsed target */
char *remainder; /* parsed remainder */
register int form; /* form from parse_address */
if (user[0] == '\0') {
return; /* didn't find anything */
}
/* need room for at most the path, the user and a '!' */
sender = xmalloc((unsigned)(strlen(path) + strlen(user) + 2));
/* parse the user address, which may yield more path information */
form = parse_address(user, &target, &remainder);
if (form == FAIL) {
/*
* failed to parse user, store an error for logging later
*/
write_log(LOG_MLOG|LOG_SYS, "Error reading sender: %s", remainder);
sender = "Postmaster"; /* guess who gets it */
} else if (form == LOCAL && path[0] == '\0' && !islocal) {
/*
* message was flagged as non-local, but we no longer have any
* host information, send it to the postmaster
*/
write_log(LOG_MLOG|LOG_SYS, "Path to sender unknown: sender = %s",
user);
sender = "Postmaster"; /* don't know the sender */
} else if (path[0] == '\0' && form == LOCAL) {
/*
* simple case: no path, simple user, just make a copy of user
*/
(void) strcpy(sender, remainder);
} else if (path[0] == '\0' && form == UUCP_ROUTE) {
/*
* user is in !-route form, so put the user back together
*/
(void) sprintf(sender, "%s!%s", target, remainder);
} else if (form == LOCAL) {
/*
* simple user form, add it to path
*/
(void) sprintf(sender, "%s!%s", path, remainder);
} else if (form == UUCP_ROUTE) {
/*
* user is in !-route form, put it back together and add to path
*/
(void) sprintf(sender, "%s!%s!%s", path, target, remainder);
} else {
/*
* the difficult case, need to construct a !-route for user
*/
char *error; /* store error here */
char *route = build_uucp_route(remainder, &error);
if (route == NULL) {
/* found an error building the route */
write_log(LOG_MLOG|LOG_SYS, "Error building sender: %s", error);
} else if (path[0] == '\0') {
/*
* no path, just put the user back together
*/
(void) sprintf(sender, "%s!%s", target, route);
} else {
/*
* put user back together and add to path
*/
(void) sprintf(sender, "%s!%s!%s", path, target, route);
}
}
}
\f
/*
* write_body - write the message body to a stdio FILE pointer.
*
* the body of the message will be sent to the given file. Pass
* the the transport flags to send_spool in writing.
*
* return SUCCEED, WRITE_FAIL or READ_FAIL.
*/
int
write_body(f, flags)
FILE *f; /* write to this file */
long flags; /* transport flags */
{
if (seek_spool(start_msg) == FAIL) {
write_log(LOG_SYS, "seek failed: %s/%s: %s",
spool_dir, input_spool_fn, strerrno());
return READ_FAIL;
}
return send_spool(f, flags);
}
\f
/*
* check_grade - set grade for message to value from Precedence: field
*
* If a Precedence: field is specified for a message and this grade
* is not the message's current grade, then change the grade for the
* message, which will involve moving the file to a new name.
*/
void
check_grade()
{
struct queue *hq; /* temp for scanning headers */
/* grab the current grade */
msg_grade = message_id[strlen(message_id) - 1];
/* find the precedence field (or not) */
for (hq = header; hq; hq = hq->succ) {
if (HDREQ("precedence", hq->text)) {
/* found the precedence header */
int grade = parse_precedence(index(hq->text, ':') + 1);
if (grade && msg_grade != grade) {
/*
* change the grade to the new grade, if we fail to
* change the grade, don't sweat it too badly.
*/
(void) new_grade(grade);
msg_grade = grade;
}
break;
}
}
}
/*
* log_incoming - put a message in the log file about the incoming message
*
* look for a message-id header field and log it, if it exists, otherwise
* announce new mail without a previous message-id.
*/
void
log_incoming()
{
struct queue *hq; /* temp for scanning headers */
char *old_id = NULL; /* previous message-id */
char *resent_mid = NULL; /* point to Resent-Message-Id field */
char *mid = NULL; /* point to Message-Id field */
for (hq = header; hq; hq = hq->succ) {
if (HDREQ("resent-message-id", hq->text)) {
resent_mid = hq->text;
break;
}
if (HDREQ("message-id", hq->text)) {
mid = hq->text;
}
}
if (resent_mid) {
old_id = index(resent_mid, ':');
} else if (mid) {
old_id = index(mid, ':');
}
if (old_id) {
char *trim_old_id = xmalloc(strlen(old_id));
register char *p;
for (p = trim_old_id, old_id++; *old_id; old_id++) {
if (!isspace(*old_id)) {
*p++ = *old_id;
}
}
*p = '\0';
write_log(LOG_SYS, "new msg: from %s, old id=%s", sender, trim_old_id);
xfree(trim_old_id);
} else {
write_log(LOG_SYS, "new msg: from %s", sender);
}
}
/*
* scan_spool_dirs - scan for work and return spool files in sorted order
*
* look through all of the spool directories, and sort by precedence and
* by creation date.
*
* the returned filename vector contains data which may be reused on
* subsequent calls to scan_spool_dirs().
*/
char **
scan_spool_dirs()
{
static char **mv = NULL; /* vector of messages */
int mc = 0; /* count of messages */
static int mv_size = 32; /* allocated vector entries */
char *dn; /* current directory to scan */
static struct str str; /* storage for filenames */
register int i; /* temp */
/* get the initial vector and string storage allocation */
if (mv == NULL) {
mv = (char **)xmalloc(mv_size * sizeof(*mv));
STR_INIT(&str);
}
str.i = 0; /* clear out filename storage */
/* loop through all spool directories looking for work */
for (dn = strcolon(spool_dirs); dn; dn = strcolon((char *)NULL)) {
char *fn; /* filename from directory */
char *in_dn = xprintf("%s/input", dn);
for (fn = scan_dir(in_dn); fn; fn = scan_dir((char *)NULL)) {
/* see if the file matches the form of a spool file */
if (! isdigit(fn[0]) || strlen(fn) != SPOOL_FN_LEN) {
continue; /* nope */
}
/* add to the list of files */
if (mc >= mv_size - 1) {
/* note: we allow, in advance, for the last NULL entry */
mv_size += 32; /* get more entries */
mv = (char **)xrealloc((char *)mv, mv_size * sizeof(*mv));
}
/* use offsets for now, to be converted to (char *) later */
set_ptr(mv[mc++], str.i);
str_printf(&str, "%s/%s%N", in_dn, fn); /* %N is nul byte */
}
xfree(in_dn);
}
/* close off the vectors */
mv[mc] = NULL;
/* convert the offsets in mv to (char *)'s */
for (i = 0; i < mc; i++) {
mv[i] = str.p + get_ptr(mv[i]);
}
/* sort it */
(void) qsort((char *)mv, mc, sizeof(*mv), queue_compare);
return mv;
}
/*
* queue_compare - compare two filenames which represent queue files
*/
static int
queue_compare(a, b)
char **a;
char **b;
{
int a_grade = (*a)[strlen(*a) - 1];
int b_grade = (*b)[strlen(*b) - 1];
if (a_grade != b_grade) {
return a_grade - b_grade;
}
return strcmpic(rindex(*a, '/'), rindex(*b, '/'));
}