FIDOGATE Part 3/6
Martin Junius
mj at dfv.rwth-aachen.de
Mon Jan 28 20:34:28 AEST 1991
---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is part 03 of a multipart archive
# ============= rfmail.c ==============
if test -f 'rfmail.c' -a X"$1" != X"-c"; then
echo 'x - skipping rfmail.c (File already exists)'
else
echo 'x - extracting rfmail.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'rfmail.c' &&
/*:ts=4*/
/*****************************************************************************
X * FIDOGATE --- Gateway software UNIX <-> FIDO
X *
X * $Id: rfmail.c,v 2.18 91/01/05 13:07:34 mj Exp $
X *
X * Read mail or news from standard input and put it into spool-directory.
X * rfmail is called by the included version of rmail (mail receiver)
X * and by relaynews from the CNews software.
X *
X * $Log: rfmail.c,v $
X * Revision 2.18 91/01/05 13:07:34 mj
X * Improved generation of FIDO `^AREPLY', use `References:' or `In-Reply-To:'
X * even if no `Message-ID:' present. Fixed function get_header().
X *
X * Revision 2.17 90/12/09 18:36:48 mj
X * Support for new `X' header in intermediate file taken from X-F[D[D[DRFC `X-Flags:'.
X * Crash mail is now possible via `X-Flags: C'.
X *
X * Revision 2.16 90/12/09 17:35:13 mj
X * Fixed a small bug in subject handling.
X *
X * Revision 2.15 90/12/02 21:22:25 mj
X * Changed program header to mention both authors of the original
X * software posted to alt.sources.
X *
X * Revision 2.14 90/12/01 17:49:49 mj
X * Major new feature: rfmail is now able to handle news articles posted
X * to several newsgroup. For each newsgroup in the Newsgroups header a
X * seperate spool file for the corresponding FIDO EchoMail area is created.
X *
X * Revision 2.13 90/11/23 21:52:14 mj
X * Changed date output to be compliant with FTS-0001: `DD MON YY HH:MM:SS',
X * so QMail hopefully won't mess around with rfmail's dates any more.
X *
X * Revision 2.12 90/11/23 20:42:36 mj
X * Output of ^AREPLY no longer depends on real versus fake net address.
X *
X * Revision 2.11 90/11/20 21:09:35 mj
X * Added support for ^AINTL kludge.
X *
X * Revision 2.10 90/11/05 20:50:55 mj
X * Changed my signature in all program headers.
X *
X * Revision 2.9 90/11/01 14:53:43 mj
X * Take last message id from RFC822 Message-ID/References header.
X *
X * Revision 2.8 90/11/01 14:35:05 mj
X * Enabled generation of ^AREPLY kludge.
X *
X * Revision 2.7 90/10/29 21:20:15 mj
X * Enabled generation of ^AMSGID kludge.
X *
X * Revision 2.6 90/09/16 17:36:25 mj
X * Some changes. (what?)
X *
X * Revision 2.5 90/09/15 14:22:54 mj
X * Bug in newsgroup<->area conversion removed.
X *
X * Revision 2.4 90/09/13 21:58:38 mj
X * Two major changes:
X * Improved handling of user names in addresses and real names.
X * Normally we use real name from header lines, but in case of
X * special addressing via '%', e.g. "... % MAUS AC", we must use
X * name in address, because "% ..." is not in real name.
X * rfmail now handles news messages too. `-n' option implemented for
X * this purpose. Look up newsgroup<->area conversion in Areas file
X * and generate tear, origin, seen-by and path line. No more need
X * for the rfnews program.
X *
X * Revision 2.3 90/09/08 18:49:03 mj
X * Move strsaveline() to xalloc.c
X *
X * Revision 2.2 90/09/03 17:57:44 mj
X * Some changes. Generation of ^AMSGID and ^AREPLY not active
X * at this point.
X *
X * Revision 2.1 90/08/12 14:16:05 mj
X * Added output of FIDO `^AMSGID:' and `^AREPLY:' kludges to the
X * spool file for fpack. These lines are generated from RFC 822
X * header lines `Message-ID:' and `References:'.
X *
X * Revision 2.0 90/08/12 11:58:33 mj
X * Rewrote much of the code of rfmail.c: Parsing of header is now
X * much cleaner, changed the names of some functions, introduced
X * other ones. Sending of mail to more than one user now really
X * works.
X *
X * Revision 1.6 90/08/09 19:18:05 mj
X * `Comment-To:' header line no longer makes it's way into the FIDO
X * message.
X *
X * Revision 1.5 90/07/29 18:12:23 mj
X * Place real net/node in message header for FIDO netmail. Also
X * a `^AFMPT x' kludge is generated in this case. Recipient of
X * our mail now gets real address from message, which should
X * make replying much easier.
X *
X * Revision 1.4 90/07/07 18:23:53 mj
X * Support for point addressing and the IFNA `^ATOPT x' kludge
X * added to rfmail.
X *
X * Revision 1.3 90/07/01 13:46:04 mj
X * Removed all calls to alloca(). All unsave malloc()'s without
X * checking the returned pointer are now done via xmalloc().
X * Fixed a malloc() error in rmail.
X *
X * Revision 1.2 90/06/28 22:04:43 mj
X * Much rework of the sources, no more hsu.h and other clean up.
X * rmail improved, now handles special XENIX quirks.
X *
X * Revision 1.1 90/06/21 21:09:34 mj
X * Everything seems to work, so this delta was made.
X *
X * Revision 1.0 90/06/19 18:33:19 mj
X * Initial revision
X *
X *
X *****************************************************************************
X * This version hacked and maintained by:
X * _____ _____
X * | |___ | Martin Junius FIDO: 2:242/6.1 2:242/6.0
X * | | | | | | Republikplatz 3 DOMAIN: mju at dfv.rwth-aachen.de
X * |_|_|_|_____| D-5100 Aachen Tel. (Voice) 0241-86931
X *
X * Original version of these programs and files:
X *
X * Teemu Torma
X * Heikki Suonsivu FIDO: 2:504/1 UUCP: ...!mcsun!santra!hsu
X *
X *****************************************************************************/
X
#include "fidogate.h"
X
#include <varargs.h>
#include <pwd.h>
X
X
X
#define PROGRAMNAME "rfmail $Revision: 2.18 $"
X
X
X
#define ORIGIN "Link to news/mail at hippo.uucp"
X
X
extern struct passwd *getpwuid();
extern int getopt();
extern char *optarg;
extern int optind;
extern void exit();
extern char *spoolfile(), *basename();
extern char *regex();
extern time_t time();
extern FILE *popen();
X
X
/* Verbosity */
int verbose = INIT_VERBOSE;
X
/* Private mail (default) */
bool private = TRUE;
X
/* News-article */
int newsmode = FALSE;
X
/* Our net/node information */
Node this;
X
/*
X * Global array to save message header
X */
#define MAXHEADERS 50
X
char *headers[MAXHEADERS];
X
X
X
/*
X * get_header() --- Get specific message header from headers[]
X *
X * Example:
X * To: mj at hippo.uucp (Martin Junius)
X * Use:
X * get_header("To") -> "mj at hippo.uucp (Martin Junius)"
X */
X
char *get_header(name)
char *name;
{
register int i, len;
X
X len = strlen(name);
X for(i=0; i<MAXHEADERS && headers[i]; i++)
X if(!strncmp(headers[i], name, len) && headers[i][len]==':')
X return(headers[i] + strlen(name) + 2); /* +2 for ": " */
X return(NULL);
}
X
X
X
/*
X * Extract address/name from `From:', `Reply-To:' or `To:' field.
X * Understood formats:
X * full name <address>
X * address (full name)
X * address
X */
X
void get_address(field, address)
char *field, *address;
{
register char *cp, *np;
X
X if ((cp = strchr(field, '(')) && strchr(cp + 1, ')')) {
X /* format is 'address (full name)' */
X for (np = field; np < cp - 1; np++)
X *address++ = *np;
X *address = 0;
X }
X else if ((cp = strchr(field, '<')) && (np = strchr(cp + 1, '>'))) {
X /* format is 'full name <address>' */
X cp++;
X while (cp < np)
X *address++ = *cp++;
X *address = 0;
X }
X else
X /* line contains only address */
X strcpy(address, field);
}
X
void get_name(field, name)
char *field;
char *name;
{
register char *cp, *np;
X
X if ((cp = strchr(field, '(')) && (np = strchr(cp + 1, ')'))) {
X /* format is 'address (full name)' */
X for(cp++; *cp && cp<np; cp++)
X *name++ = *cp;
X *name= 0;
X }
X else if ((cp = strchr(field, '<')) && (np = strchr(cp + 1, '>'))) {
X /* format is 'full name <address>' */
X for(cp=field; *cp && *cp!='<' && *(cp+1)!='<'; cp++)
X *name++ = *cp;
X *name = 0;
X }
X else
X /* line contains only address */
X *name = 0;
}
X
X
X
/*
X * Get return address for returning undeliverable mail to sender.
X * Extract it from Reply-To: or From: fields.
X */
X
void
get_return_address(address)
char *address;
{
char buffer[BUFSIZ];
char *field;
X
X *address = 0;
X
X /* check is there is Reply-To: field */
X if(field = get_header("Reply-To")) {
X get_address(field, address);
X return;
X }
X
X /* no Reply-To:, check for From: */
X if(field = get_header("From")) {
X get_address(field, address);
X return;
X }
X
X /* not found, send it to root */
X strcpy(address, "root");
}
X
X
X
/*
X * Open stream associated with programs standard input. Program is invoked
X * with given argument list. Popen(3S) would invoke mailer thru sh(1),
X * so this uses less memory and is faster.
X */
X
FILE *open_mailer(program, args, pid)
char *program, **args;
int *pid;
{
FILE *fp;
int fd[2];
X
X /* create pipe */
X if (pipe(fd) == -1) {
X perror("rfmail: pipe");
X exit(EX_OSERR);
X }
X
X switch (*pid = fork()) {
X case -1: /* Error */
X perror("rfmail: fork failed");
X exit(EX_OSERR);
X case 0: /* Child */
X (void) close(0);
X if (dup(fd[0]) == 0) {
X (void) close(fd[0]);
X (void) close(fd[1]);
X (void) execvp(program, args);
X perror(program);
X }
X else
X perror("rfmail: dup");
X exit(EX_OSERR);
X default: /* Parent */
X (void) close(fd[0]);
X if ((fp = fdopen(fd[1], "w")) == NULL) {
X perror("rfmail: fdopen");
X exit(EX_OSERR);
X }
X }
X return fp;
}
X
X
X
/*
X * In case of error send mail back to sender.
X * First argument is file pointer for mail file, the following
X * are format string and args for printf().
X */
X
/**VARARGS**/
void sendback(va_alist)
va_dcl
{
va_list args;
FILE *mail;
char *fmt;
X
#ifdef RETURN_FAILED_MAIL
FILE *mailer;
char to[128];
char *argv[3];
int pid;
char buffer[BUFSIZ];
int i;
#endif /* RETURN_FAILED_MAIL */
X
X va_start(args);
X mail = va_arg(args, FILE *);
X fmt = va_arg(args, char *);
X
#ifdef RETURN_FAILED_MAIL
X get_return_address(to);
X log("Mail failed, return to %s", to);
X
X argv[0] = RMAIL;
X argv[1] = to;
X argv[2] = NULL;
X mailer = open_mailer(RMAIL, argv, &pid);
X if(!mailer) {
X log("$Unable to invoke mailer for returned mail");
X va_end(args);
X return;
X }
X
X /* print correct header for mailer */
X fprintf(mailer, "From rfmail %s\n", date("%a %h %d %T 19%y", (long *) 0));
X fprintf(mailer, "Received: by %s (%s/%s)\n",
X internode(this), PROGRAMNAME, this.name);
X fprintf(mailer, "\tid AA%05d; %s\n", getpid(),
X date("%a, %d %h %y %T %o (%z)", (long *) 0));
X fprintf(mailer, "Date: %s\n", date("%a, %d %h %y %T %o", (long *) 0));
X fprintf(mailer, "From: Gateway to FIDONet <%s@%s>\n",
X "rfmail", internode(this));
X fprintf(mailer, "Subject: Returned mail: Unable to deliver mail to FIDONet\n");
X fprintf(mailer, "Message-Id: <%s.AA%05d@%s>\n",
X date("%y%m%q%H%M", (long *) 0), getpid(),
X internode(this));
X fprintf(mailer, "To: %s\n", to);
X fprintf(mailer, "\n");
X fprintf(mailer, " ----- Transcript of session follows -----\n");
X vfprintf(mailer, fmt, args);
X fprintf(mailer, "\n\n");
X fprintf(mailer, " ----- Unsent message follows -----\n");
X
X /* Copy removed header line */
X for(i=0; i<MAXHEADERS && headers[i]; i++)
X fprintf(mailer, "%s\n", headers[i]);
X fprintf(mailer, "\n");
X
X /* now copy the message to mailer */
X rewind(mail);
X while (fgets(buffer, BUFSIZ, mail))
X fputs(buffer, mailer);
X
X /* mail is now sent, close mailer */
X fclose(mailer);
X
#else /**!RETURN_FAILED_MAIL**/
X mail = va_arg(args, FILE*);
X fmt = va_arg(args, char *);
X
X vfprintf(stderr, fmt, args);
#endif /* not RETURN_FAILED_MAIL */
X
X va_end(args);
}
X
X
X
/* Check that net/node exists and mail can be send to there (ie. it
X is not down or in hold). Also be will replace name with sysop's name,
X if mail is for sysop (note that alias will override this sysop-name).
X Mail will be send back to sender, if this checking fails. */
X
int
valid_netnode(name, node, mail)
X char *name;
X Node node;
X FILE *mail;
{
#ifdef NODELIST_SUPPORT
X Node *entry;
X
X if (entry = node_entry(node))
X switch (entry->type)
X {
X case HOLD:
X /* node is in hold */
X sendback(mail, "Node %s is in hold", ascnode(node));
X return EX_NOHOST;
X case DOWN:
X /* node is down */
X sendback(mail, "Node %s is currently down", ascnode(node));
X return EX_NOHOST;
X default:
X /* everything was fine */
X if (!strcmp(name, "sysop") || !strcmp(name, "Sysop"))
X (void) strcpy(name, entry->sysop);
X return EX_OK;
X }
X
X /* we didn't find node */
X sendback(mail, "Node %s does not exist", ascnode(node));
X return EX_NOHOST;
#else
X return EX_OK;
#endif
}
X
X
X
/*
X * Return sender of mail. Figure it out from From: field or from
X * our uid if it's not administrative uid (e.g. uucp or nuucp).
X * If not there, try $HOME/.fullname, then
X * $HOME/.realname, then
X * environment string NAME.
X * If neither method works, return NULL.
X * If $HOME is not defined, skip both .fullname and .realname
X */
X
char *mail_sender()
{
struct passwd *pwd;
static char name[36];
char buffer[BUFSIZ];
register char *cp, *np;
register int cnt;
Node dummynode;
FILE *fp;
char *from;
X
X name[35] = 0;
X buffer[0] = 0;
X
X if(from = get_header("From"))
X strcpy(buffer, from);
X
X if(*buffer) {
X debug(2, "Checking From: field");
X /*
X * Parse the name out from From: field. there are basically
X * two kinds of formats: 'User Name <address>' or
X * 'address (User Name)'. We'll try to figure it out
X * which format sender uses.
X */
X if ((cp = strchr(buffer, '<')) && (np = strchr(cp, '>'))) {
X /* Format is 'From: Name <address>' */
X for(np = buffer,
X cnt = 0; np < cp - 1 && cnt < 35;
X np++, cnt++)
X name[cnt] = *np;
X name[cnt] = 0;
X debug(2, "Got name %s, fmt name <address>", name);
X }
X else if ((cp = strchr(buffer, '(')) && (np = strchr(cp, ')'))) {
X /* Format is 'From: address (Name)' */
X for (cnt = 0, cp++; cp < np && cnt < 35; cp++, cnt++)
X name[cnt] = *cp;
X name[cnt] = 0;
X debug(2, "Got name %s, fmt address (name)", name);
X }
X else {
X debug(5, "Could no find realname in <> or ()");
X /* Try parse it with parse_address */
X if (parse_address(buffer, name, &dummynode)) {
X (void) strncpy(name, buffer, 35);
X name[35] = 0;
X debug(2, "No format in From: line, name %s", name);
X }
X else {
X name[35] = 0;
X debug(2, "Name %s parsed from address", name);
X }
X }
X return name;
X }
X
X /* hmm.. no From: field in mail. let's try to figure this problem
X out some other way. If our uid is some user's uid, we'll use
X that uid to show user's name, otherwise we'll return NULL. */
X
X if (getuid() >= USERUID && (pwd = getpwuid( (int) getuid())))
X {
X /* There are two commonly used gecos-formats: So called USG
X format used by System III and System V machines and BSD
X format used by BSD machines. In USG format name is
X sandwitched between '-' and '(' characters and in BSD
X format name is first this in gecos-field up to first comma
X in it. In many machines there's only name in gecos. */
X
X if ((cp = strchr(pwd->pw_gecos, '-')) && (np = strchr(cp, '(')))
X {
X /* USG format 'stuff-name(stuff)' */
X for (cnt = 0, cp++; cnt < 35 && cp < np; cp++, cnt++)
X name[cnt] = *cp;
X name[cnt] = 0;
X debug(3, "Got name from USG fmt, name %s", name);
X }
X else
X if (cp = strchr(pwd->pw_gecos, ','))
X {
X /* BSD format 'name,stuff...' */
X for (cp = buffer, cnt = 0; cnt < 35 && *cp != ','; cp++, cnt++)
X name[cnt] = *cp;
X name[cnt] = 0;
X debug(3, "Got name from BSD format, name %s", name);
X }
X else
X if (*pwd->pw_gecos)
X {
X /* non-empty gecos, assume that there's only name */
X (void) strncpy(name, pwd->pw_gecos, 35);
X name[35] = 0;
X debug(3, "No fmt in gecos, name %s", name);
X }
X else
X {
X /* Lazy administrator or user who want's to be anonymous
X (or this is some administrative uid with no explanation
X which)... We'll use only the username. */
X
X (void) strncpy(name, pwd->pw_name, 35);
X /* never heard over 35 char usernames???? I haven't but... */
X name[35] = 0;
X debug(3, "No gecos, name = %s");
X }
X return name;
X }
X
X *buffer = 0;
X if (cp = getenv("HOME"))
X {
X debug(5, "Try .fullname");
X if (fp = fopen(sprintfs("%s/%s", cp, ".fullname"), "r"))
X {
X if (!fgets(buffer, BUFSIZ, fp)) *buffer = 0;
X fclose(fp);
X strncpy(name, buffer, 35);
X if (!strempty(name))
X {
X strclean(name);
X debug(2, "Got name %s from .fullname", name);
X return name;
X }
X else
X debug(1, "Empty name '%s' in .fullname", name);
X }
X debug(5, "Try .realname");
X if (fp = fopen(sprintfs("%s/%s", cp, ".realname"), "r"))
X {
X if (!fgets(buffer, BUFSIZ, fp)) *buffer = 0;
X fclose(fp);
X strncpy(name, buffer, 35);
X if (!strempty(name))
X {
X strclean(name);
X debug(2, "Got name %s from .realname", name);
X return name;
X }
X else
X debug(1, "Empty name '%s' in .realname", name);
X }
X }
X
X if (cp = getenv("NAME"))
X {
X debug(5, "Name defined, use it");
X strncpy(name, buffer, 35);
X if (!strempty(name))
X {
X strclean(name);
X debug(2, "Got name %s from environment NAME", name);
X return name;
X }
X }
X
X debug(2, "No name, uid = %d", getuid());
X
X return (char *) 0;
}
X
X
X
/* Get net/node information from info. If zone, net, or point are missing,
X they will be returned as -1TRUE is returned, if
X everything went fine, otherwise FALSE. */
X
bool
getnode(info, node)
X char *info;
X Node *node;
{
X /* Extract zone information */
X if (strchr(info, ':'))
X {
X for (node->zone = 0; *info != ':'; info++)
X if (isdigit(*info))
X node->zone = node->zone * 10 + *info - '0';
X else
X {
X debug(1, "Invalid character in zone '%c' %02x", *info, *info);
X return FALSE;
X }
X info++;
X }
X else
X node->zone = -1;
X
X /* Extract net information if net is present, otherwise
X set net to -1. */
X
X if (strchr(info, '/'))
X {
X for (node->net = 0; *info != '/'; info++)
X if (isdigit(*info))
X node->net = node->net * 10 + *info - '0';
X else
X {
X debug(1, "Invalid character in net '%c' %02x", *info, *info);
X return FALSE;
X }
X info++;
X }
X else
X node->net = -1;
X
X /* Exract node information, set to -1 if empty. */
X
X if (*info)
X for (node->node = 0; *info && *info != '.'; info++)
X if (isdigit(*info))
X node->node = node->node * 10 + *info - '0';
X else
X {
X debug(1, "Invalid characer in node '%c' %02x", *info, *info);
X return FALSE;
X }
X else
X node->node = -1;
X
X /* Exract point information, set to -1 if empty. */
X
X if (*info)
X for (node->point = 0; *info; info++)
X if (isdigit(*info))
X node->point = node->point * 10 + *info - '0';
X else
X {
X debug(1, "Invalid characer in node '%c' %02x", *info, *info);
X return FALSE;
X }
X else
X node->point = -1;
X
X debug(2, "Got alias %s", ascnode(*node));
X return TRUE;
}
X
/* Compare receiver and user in aliasfile. Each line in aliasfile contains
X alias, optional net/node information separated by commas zero or
X more times, white space(s) and rest of the line literally to whom
X mail should be send. Net and node information is format 'net/node'.
X if net is omitted then every net is counted, if node is omitted,
X then all nodes in that net. If net is omitted, slash may be left off.
X
X E.g following are valid aliases:
X
X tot,504/ Teemu Torma
X foo Foo Bar
X sysop,504/1,504/9 System Operator */
X
bool aliascmp(alias, to, node)
char *alias, *to;
Node *node;
{
char buffer[BUFSIZ];
char *cp;
Node anode;
X
X while (*alias && *alias != ',' && !isspace(*alias) && *to)
X if (*alias++ != *to++)
X return FALSE;
X
X if (isspace(*alias)) /* match */
X return TRUE;
X
X if (*alias == ',') {
X /* copy alias to buffer and terminate the it after first space */
X strcpy(buffer, alias + 1);
X for (cp = buffer; *cp; cp++)
X if (isspace(*cp)) {
X *cp = 0;
X break;
X }
X
X /* get net/node information from buffer one at the time */
X for (cp = strtok(buffer, ","); cp; cp = strtok((char *) 0, ",")) {
X debug(2, "Got node '%s'", cp);
X if (getnode(cp, &anode)) {
X if ((anode.zone == -1 || anode.zone == node->zone ) &&
X (anode.net == -1 || anode.net == node->net ) &&
X (anode.node == -1 || anode.node == node->node ) &&
X (anode.point == -1 || anode.point == node->point) )
X return TRUE;
X }
X else
X return FALSE;
X }
X }
X else
X debug(1, "Invalid alias, %c is not ',' or white space", *alias);
X
X return FALSE;
}
X
X
X
/* Return receiver's name. If name is aliased, return it, otherwise
X return receiver's name. */
X
char *receiver(to, node)
char *to;
Node *node;
{
static char name[36];
char buffer[BUFSIZ];
register int cnt;
register char *cp;
FILE *fp;
int i, c, convert_flag;
X
X debug(3, "Name for alias checking: %s", to);
X
X if (fp = fopen(ALIAS, "r")) {
X while (fgets(buffer, BUFSIZ, fp)) {
X buffer[strlen(buffer) - 1] = 0;
X if (*buffer != '#')
X debug(3, "Checking for alias %s", buffer);
X if (*buffer != '#' && aliascmp(buffer, to, node)) {
X /* match, save the alias. */
X for (cp = buffer; *cp && !isspace(*cp); cp++)
X /* skip alias itself */;
X while (isspace(*cp))
X cp++;
X if (*cp) {
X for (cnt = 0; *cp && cnt < 35; cnt++, cp++)
X name[cnt] = *cp;
X name[cnt] = 0;
X debug(2, "Got alias %s", name);
X fclose(fp);
X return name;
X }
X else
X debug(1, "Missing alias");
X }
X }
X fclose(fp);
X }
X else
X log("$Unable to open aliasfile %s", ALIAS);
X
X /*
X * Alias not found. Return the the original receiver with all
X * '_' characters replaced by space and all words capitalized.
X */
X convert_flag = isupper(*to) ? -1 : 1;
X for(i=0; *to && i<35; i++, to++) {
X c = *to;
X switch(c) {
X case '_':
X name[i] = ' ';
X if(!convert_flag)
X convert_flag = 1;
X break;
X case '%':
X if(convert_flag != -1)
X convert_flag = 2;
X /**Fall thru**/
X default:
X if(convert_flag > 0) {
X name[i] = islower(c) ? toupper(c) : c;
X if(convert_flag == 1)
X convert_flag = 0;
X }
X else
X name[i] = c;
X break;
X }
X }
X name[i] = 0;
X
X debug(2, "No alias, name %s", name);
X return name;
}
X
X
X
/*
X * Return from field for FIDO message.
X * Alias checking is done via receiver().
X */
X
char *mail_receiver(address, node)
char *address;
Node *node;
{
char *cp;
int found = 0;
char name[36];
char realname[36];
char addr[128];
char *to;
Node dummy;
X
X realname[0] = 0;
X
X if(address) {
X /*
X * Address is argument
X */
X debug(2, "Address to parse: %s", address);
X if(cp = parse_address(address, name, node)) {
X log("Parse failed: %s", cp);
X return NULL;
X }
X }
X else {
X /*
X * Address is echo feed
X */
X node->zone = MY_ZONE;
X node->net = ECHOFEED_NET;
X node->node = ECHOFEED_NODE;
X node->point = 0;
X strcpy(name, "All");
X }
X
X /*
X * Try to look up a better real name in header fields
X *
X * Standard RFC822 header line
X */
X if(to = get_header("To")) {
X debug(2, "Checking To: field");
X get_address(to, addr);
X if(!address || !strcmp(address, addr)) {
X get_name(to, realname);
X found = 1;
X }
X }
X
X /*
X * User-defined header line for gateway software
X * (can be patched into news reader)
X */
X if(!found && (to = get_header("Comment-To"))) {
X debug(2, "Checking Comment-To: field");
X get_address(to, addr);
X if(!address || !strcmp(address, addr)) {
X get_name(to, realname);
X found = 1;
X }
X }
X
X /*
X * Header generated by nn's `r' command
X */
X if(!found && (to = get_header("Orig-To"))) {
X debug(2, "Checking Orig-To: field");
X get_address(to, addr);
X if(!address || !strcmp(address, addr)) {
X get_name(to, realname);
X found = 1;
X }
X }
X
X /*
X * News message: get name from address taken out of header line
X */
X if(found && !address)
X parse_address(addr, name, &dummy);
X
X /*
X * Use real name from header line, if no special addressing with '%'
X */
X if(found && !strchr(name, '%') && *realname)
X strcpy(name, realname);
X
X return receiver(name, node);
}
X
X
X
/*
X * Get date field for FIDO message. Look for `Date:' header or use
X * current time.
X */
X
char *fido_date()
{
time_t timevar;
struct tm *localtime();
struct tm *mtime;
static char timebuf[20];
/* literal months */
static char *months[] = {
X "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X (char *) 0,
};
/* Literal weekdays */
static char *wkdays[] = {
X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", (char *) 0,
};
char *header_date;
X
X if(header_date = get_header("Date")) {
X /* try to extract date and other information from it */
X debug(2, "Found date: '%s'", header_date);
X timevar = getdate(header_date, NULL);
X }
X else
X timevar = time(0);
X
X debug(2, "timevar %ld", timevar);
X mtime = localtime(&timevar);
X /* Save date in FTS-0001 format ! */
X sprintf(timebuf, "%02d %3s %02d %02d:%02d:%02d",
X mtime->tm_mday, months[mtime->tm_mon], mtime->tm_year,
X mtime->tm_hour, mtime->tm_min, mtime->tm_sec );
X debug(1, "Returning %s", timebuf);
X return timebuf;
}
X
X
X
char *estrtok(s, sep)
char *s, *sep;
{
X char *p;
X
X if (p = strtok(s, sep))
X return p;
X return "";
}
X
X
X
/*
X * Output mail/news messages into spool file(s) for fpack
X */
X
#define NGROUPS 10
#define NFIDO 6
X
#define FIDO_NODE 0
#define FIDO_TO 1
#define FIDO_FROM 2
#define FIDO_SUBJECT 3
#define FIDO_DATE 4
#define FIDO_FLAGS 5
X
int send_mail(fp, to)
FILE *fp; /* Temporary file message body */
char *to; /* Adress to send to (news = NULL) */
{
char buffer[BUFSIZ];
char groups[BUFSIZ];
char *group[NGROUPS];
Node node;
char *p;
char *fido_header[NFIDO];
char *area;
char *newsgroup;
FILE *fpareas;
int status;
int i;
X
X if(to)
X debug(1, "Sending mail to %s", to);
X else
X debug(1, "Sending news");
X
X /*
X * Get to/from/node/subject/date
X */
X fido_header[FIDO_TO] = mail_receiver(to, &node);
X if(!fido_header[FIDO_TO]) {
X sendback(fp, "Illegal address %s", to);
X return(EX_NOHOST);
X }
X
X fido_header[FIDO_FROM] = mail_sender();
X if(!fido_header[FIDO_FROM])
X fido_header[FIDO_FROM] = "Gateway to FIDONET";
X
X fido_header[FIDO_NODE] = ascnode(node);
X
X fido_header[FIDO_SUBJECT] = get_header("Subject");
X if(!fido_header[FIDO_SUBJECT])
X fido_header[FIDO_SUBJECT] = "Mail from Gateway to FIDONET";
X
X fido_header[FIDO_DATE] = fido_date();
X
X fido_header[FIDO_FLAGS] = get_header("X-Flags");
X if(!fido_header[FIDO_FLAGS])
X fido_header[FIDO_FLAGS] = "";
X
X debug(2, "FIDO Node: %s", fido_header[FIDO_NODE]);
X debug(2, "FIDO To: %s", fido_header[FIDO_TO]);
X debug(2, "FIDO From: %s", fido_header[FIDO_FROM]);
X debug(2, "FIDO Subject: %s", fido_header[FIDO_SUBJECT]);
X debug(2, "FIDO Date: %s", fido_header[FIDO_DATE]);
X debug(2, "FIDO Flags: %s", fido_header[FIDO_DATE]);
X
X if(newsmode) {
X /*
X * News message: get newsgroups and convert to FIDO areas
X */
X p = get_header("Newsgroups");
X if(!p) {
X sendback(fp, "No Newsgroups header in news message");
X return(EX_DATAERR);
X }
X strcpy(groups, p);
X debug(3, "Newsgroups '%s'", groups);
X
X for(i=0, p=strtok(groups, ","); p && i<NGROUPS; p=strtok(NULL, ","), i++)
X group[i] = p;
X for(; i<NGROUPS; i++)
X group[i] = NULL;
X
X fpareas = pfopen(LIBDIR, "Areas", "r");
X if(!fpareas) {
X log("$Can't open areas file");
X sendback(fp, "Internal error: can't open file");
X return(EX_IOERR);
X }
X
X for(i=0; i<NGROUPS && group[i]; i++) {
X p = group[i];
X debug(3, "Look up newsgroup %s", p);
X rewind(fpareas);
X while(getcl(buffer, BUFSIZ, fpareas)) {
X area = estrtok(buffer, " \t");
X newsgroup = estrtok(NULL, " \t");
X debug(4, "Checking newsgroup %s", newsgroup);
X if(!strcmp(newsgroup, p)) { /* Found */
X debug(4, "Found area %s", area);
X break;
X }
X area = NULL;
X }
X if(!area) {
X log("No EchoMail area found for newsgroup '%s'", p);
/* area = "JUNK"; /**/
X }
X if(area) {
X debug(3, "Sending message to area '%s'", area);
X status = send_message(fp, node, fido_header, area);
X if(status)
X return(status);
X }
X }
X }
X else {
X /*
X * NetMail message
X */
X return(send_message(fp, node, fido_header, NULL));
X }
X
X return EX_OK;
}
X
X
int send_message(fp, node, fido_header, area)
FILE *fp;
Node node;
char *fido_header[];
char *area;
{
char buffer[BUFSIZ];
FILE *sf;
char *sfile;
char *header;
X
X /*
X * Open spool file for output
X */
X sf = fopen(sfile = spoolfile("M."), "w");
X if(!sf) {
X log("$Unable to open spoolfile %s", sfile);
X return(EX_CANTCREAT);
X }
X
X /* set correct permissions for spoolfile and lock it */
X chmod(sfile, 0600);
X lock(fileno(sf));
X
X /*
X * Header for fpack
X * N destination node
X * T destination name
X * F from name
X * S subject
X * D date
X * X flags (P=private, C=crash)
X */
X fprintf(sf, "N %s\n", fido_header[FIDO_NODE]);
X fprintf(sf, "T %s\n", fido_header[FIDO_TO]);
X fprintf(sf, "F %s\n", fido_header[FIDO_FROM]);
X fprintf(sf, "S %s\n", fido_header[FIDO_SUBJECT]);
X fprintf(sf, "D %s\n", fido_header[FIDO_DATE]);
X fprintf(sf, "X %s%s\n", private ? "P" : "", fido_header[FIDO_FLAGS]);
X fprintf(sf, "\n");
X
X
X if(newsmode) {
X /*
X * Add AREA:... line for echo mail
X */
X fprintf(sf, "AREA:%s\n", area);
X }
X else {
X /*
X * Add IFNA kludges for zone/point addressing
X */
X if(private && (node.zone != MY_ZONE))
X fprintf(sf, "\001INTL %s %s\n", ascnoden(node), ascnoden(this));
X if(private && REAL_POINT)
X fprintf(sf, "\001FMPT %d\n", REAL_POINT);
X if(node.point)
X fprintf(sf, "\001TOPT %d\n", node.point);
X }
X
X /*
X * Add kludge for MSGID / REPLY
X */
X if(header = get_header("Message-ID")) {
X if(print_msgid(sf, "MSGID", header))
X print_local_msgid(sf);
X if(header = get_header("References"))
X print_msgid(sf, "REPLY", header);
X else if(header = get_header("In-Reply-To"))
X print_msgid(sf, "REPLY", header);
X }
X else {
X print_local_msgid(sf);
X if(header = get_header("In-Reply-To"))
X print_msgid(sf, "REPLY", header);
X }
X
X /*
X * Add some header lines
X */
X if(header = get_header("From"))
X fprintf(sf, "From: %s\n", header);
X if(header = get_header("Reply-To"))
X fprintf(sf, "Reply-To: %s\n", header);
X if(header = get_header("To"))
X fprintf(sf, "To: %s\n", header);
X if(header = get_header("Cc"))
X fprintf(sf, "Cc: %s\n", header);
X if(header = get_header("Newsgroups"))
X if(strchr(header, ',')) /* Posted to multiple groups */
X fprintf(sf, "Newsgroups: %s\n", header);
X fprintf(sf, "\n");
X
X /*
X * Copy mail file
X */
X debug(3, "Copying mail");
X rewind(fp);
X while(fgets(buffer, BUFSIZ, fp))
X fputs(buffer, sf);
X /*
X * Done
X */
X fclose(sf);
X return EX_OK;
}
X
X
X
/*
X * Generate new FIDO kludge: `^AMSGID:' and `^AREPLY:'
X */
X
int print_msgid(fp, name, message_id)
FILE *fp;
char *name;
char *message_id;
{
char *id, *host, *p;
char *savep;
Node node;
long atol();
X
X savep = message_id = strsave(message_id);
X /*
X * Format of message_id is "<identification at host.domain> ..."
X * We want the the last one in the chain, which is the message id
X * of the article replied to.
X */
X message_id = strrchr(message_id, '<');
X if(!message_id)
X goto error;
X id = message_id+1;
X host = strchr(message_id, '@');
X if(!host)
X goto error;
X *host++ = 0;
X p = strchr(host, '>');
X if(!p)
X goto error;
X *p = 0;
X
X /*
X * First let's test id. If it is entirely numeric, we can use
X * it for the 32-bit number in ^AMSGID, else give up.
X */
X for(p=id; *p; p++)
X if(!isdigit(*p))
X goto error;
X
X /*
X * Try to interprete host as a FIDO node. If this succeedes
X * print out host as <zone>:<net>/<node>[.<point>], else
X * print host as we got it from message_id.
X */
X if(!parseinternode(host, &node))
X host = ascnode(node);
X fprintf(fp, "\001%s: %s %08lx\n", name, host, atol(id));
X
X free(savep);
X return(0);
X
error:
X free(savep);
X return(-1);
}
X
X
X
/*
X * Generate local `^AMSGID:' if none is found in message header
X */
X
print_local_msgid(fp)
FILE *fp;
{
long msgid;
X
X msgid = sequencer(MSGIDSEQ);
X fprintf(fp, "\001MSGID: %s %08lx\n", ascnode(this), msgid);
}
X
X
X
main(argc, argv)
int argc;
char *argv[];
{
int cnt, c;
FILE *mail;
char buffer[BUFSIZ], tmpfile[L_tmpnam];
int status = EX_OK;
char *error;
Node *node, mynode;
int header_count;
X
X newsmode = FALSE;
X
X while ((c = getopt(argc, argv, "npvV:")) != EOF)
X switch (c) {
X case 'n':
X /* Set news-mode */
X newsmode = TRUE;
X private = FALSE;
X break;
X case 'v':
X /* set more verbosity */
X verbose++;
X break;
X case 'V':
X verbose = atoi(optarg);
X break;
X case 'p':
X /* this is not private message */
X private = FALSE;
X break;
X default:
X fprintf(stderr, "%s\n\n", PROGRAMNAME);
X fprintf(stderr, "usage: rfmail [-npv] [-V verbose_level] user ...\n\n");
X exit(EX_USAGE);
X break;
X }
X
X /*
X * Create temp file for saving standard input and open it
X */
X tmpnam(tmpfile);
X if((mail = fopen(tmpfile, "w+")) == NULL) {
X log("$Can not open %s for writing", tmpfile);
X exit(EX_CANTCREAT);
X }
X /* protect mail file */
X chmod(tmpfile, 0600);
X
X /*
X * Read headers from stdin
X */
X header_count = 0;
X while(fgets(buffer, BUFSIZ, stdin)) {
X if(*buffer == '\n') /* End of header lines */
X break;
X if(header_count < MAXHEADERS)
X headers[header_count++] = strsaveline(buffer);
X }
X
X /*
X * Copy remainder of stdin to temporary file
X */
X while(fgets(buffer, BUFSIZ, stdin))
X fputs(buffer, mail);
X
X /*
X * If message is for echo mail (-n flag) then add
X * tear, origin, seen-by and path line.
X */
X if(newsmode) {
X fprintf(mail, "\n--- %s\n", PROGRAMNAME);
X fprintf(mail, " * Origin: %s (%d:%d/%d.%d)\n", ORIGIN,
X REAL_ZONE, REAL_NET, REAL_NODE, REAL_POINT);
X fprintf(mail, "SEEN-BY: %d/%d ", MY_NET, MY_NODE);
X if(ECHOFEED_NET != MY_NET)
X fprintf(mail, "%d/", ECHOFEED_NET);
X fprintf(mail,"%d\n", ECHOFEED_NODE);
X fprintf(mail, "\001PATH: %d/%d\n", MY_NET, MY_NODE);
X }
X
#ifdef NODELIST_SUPPORT
X /* update nodelist-index if needed */
X if (error = update_index()) {
X /* there was error while updating nodelist-index */
X if (*error == '$')
X sendback(mail, "%s: %s", error + 1, strerror(errno));
X else
X sendback(mail, "%s", error);
X exit(EX_SOFTWARE);
X }
#endif
X
X mynode.zone = REAL_ZONE;
X mynode.net = REAL_NET;
X mynode.node = REAL_NODE;
X mynode.point = REAL_POINT;
X strcpy(mynode.name, MY_NAME);
X
#ifdef NODELIST_SUPPORT
X if ((node = node_entry(mynode)) == NULL) {
X (void) fprintf(stderr, "Unable to this node from nodelist\n");
X log("No %s in nodelist", ascnode(mynode));
X exit(EX_SOFTWARE);
X }
#else
X node = &mynode;
#endif
X this = *node;
X
X
X rewind(mail);
X
X if(newsmode)
X /*
X * Send mail to echo feed for news messages
X */
X status = send_mail(mail, NULL);
X else
X /*
X * Send mail to addresses from command line args
X */
X for(cnt = optind; cnt < argc; cnt++)
X if((status = send_mail(mail, argv[cnt])) != EX_OK)
X break;
X
X /* remove temporary file */
X fclose(mail);
X unlink(tmpfile);
X
X exit(status);
}
SHAR_EOF
chmod 0644 rfmail.c ||
echo 'restore of rfmail.c failed'
Wc_c="`wc -c < 'rfmail.c'`"
test 34750 -eq "$Wc_c" ||
echo 'rfmail.c: original size 34750, current size' "$Wc_c"
fi
true || echo 'restore of funcs.c failed'
echo End of part 3, continue with part 4
exit 0
--
_____ _____
| |___ | Martin Junius FIDO: 2:242/6.1 2:242/6.0
| | | | | | Republikplatz 3 DOMAIN: mju at dfv.rwth-aachen.de
|_|_|_|_____| D-5100 Aachen Tel. (Voice) 0241-86931
More information about the Alt.sources
mailing list