This is queue.c in view mode; [Download] [Up]
/* @(#)src/queue.c 1.6 18 Feb 1991 16:36:55 */ /* * 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(); char *getenv(); /* * 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 */ login_user[0] = '\0'; if (sender_env_variable && (p = getenv(sender_env_variable)) && p[0]) { strncpy(login_user, p, MAXUSER); login_user[MAXUSER] = '\0'; if (put_line(login_user) == EOF) return FAIL; } else 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; } /* * 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; } /* * 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; if (login_user[0] != '\0') return SUCCEED; /* 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; } /* * 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 = xmalloc(strlen(login_user) + 1); strcpy(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 = xmalloc(strlen(login_user) + 1); strcpy(sender, login_user); } else { /* love to gang up on that postmaster */ DEBUG(DBG_QUEUE_LO, "no login user, set sender to postmaster\n"); sender = xmalloc(sizeof("Postmaster")); strcpy(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 = xmalloc(sizeof("Postmaster")); strcpy(sender, "Postmaster"); } 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 = xmalloc(sizeof("Postmaster")); strcpy(sender, "Postmaster"); } 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); } } } /* * 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); } /* * 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 list *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 list *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, '/')); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.