[alt.sources] FIDOGATE Part 3/6

mj@dfv.rwth-aachen.de (Martin Junius) (01/28/91)

---- 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@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@hippo.uucp (Martin Junius)
X * Use:
X *   get_header("To") -> "mj@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@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@dfv.rwth-aachen.de
|_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931