[alt.sources] FIDOGATE 03/06

mj@dfv.rwth-aachen.de (Martin Junius) (05/24/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.24 91/05/22 20:13:26 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.24  91/05/22  20:13:26  mj
X * Removed additional '\n' in log file entry.
X * 
X * Revision 2.23  91/05/21  09:41:23  mj
X * rfmail now split messages larger than MAXMSGSIZE into several parts.
X * 
X * Revision 2.22  91/05/21  08:48:52  mj
X * Moved log entry for NetMail to here.
X * 
X * Revision 2.21  91/05/19  17:23:16  mj
X * Some changes. (what?)
X * 
X * Revision 2.20  91/05/07  23:58:59  mj
X * Changed usage of function parse_address() due to new return type.
X * 
X * Revision 2.19  91/03/29  18:13:07  mj
X * No more chmod(600) for news articles.
X * 
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 RFC `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.24 $"
X
#define ORIGIN          "Link to news/mail at hippo.uucp"
X
/*
X * Max. message size for FIDO. Due to some more brain damage in FIDONET
X * programs we have to split larger messages into several smaller ones.
X * QMail moved messages larger than 16K to the BADMSG directory. We use
X * 15000 bytes as the max. message size, this leaves enough room for
X * header and other things.
X */
#define MAXMSGSIZE      15000
X
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(*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(parse_address(address, name, node)) {
X           log("Parse of address %s failed", address);
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, split)
FILE *fp;                               /* Temporary file message body */
char *to;                               /* Adress to send to (news = NULL) */
int split;                              /* Split message into several parts */
{
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           }
#if 0
X           if(!area) {
X               log("No EchoMail area found for newsgroup '%s'", p);
/*              area = "JUNK"; /**/
X           }
#endif
X           if(area) {
X               debug(3, "Sending message to area '%s'", area);
X               status = send_message(fp, node, fido_header, area, split);
X               if(status)
X                   return(status);
X           }
X       }
X   }
X   else {
X       /*
X        * NetMail message
X        */
X       p = get_header("From");
X       log("%s -> %s @ %s", p ? p : fido_header[FIDO_FROM],
X           fido_header[FIDO_TO], fido_header[FIDO_NODE]        );
X       return(send_message(fp, node, fido_header, NULL, split));
X   }
X   
X   return EX_OK;
}
X
X
int send_message(fp, node, fido_header, area, split)
FILE *fp;
Node node;
char *fido_header[];
char *area;
int split;
{
char buffer[BUFSIZ];
FILE *sf;
char *sfile;
char *header;
int part = 1;
long size;
X
X   rewind(fp);
X
X   /*
X    * Open spool file for output
X    */
again:
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   if(private)
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   if(split)
X       fprintf(sf, "S %c: %s\n", part + '@', fido_header[FIDO_SUBJECT]);
X   else
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    * Add line indicating splitted message
X    */
X   if(split)
X       fprintf(sf, " * Large message splitted by rfmail: part %02d/%02d\n\n",
X                   part, split                                             );
X
X   /*
X    * Copy mail file
X    */
X   size = 0;
X   debug(3, "Copying mail");
X   while(fgets(buffer, BUFSIZ, fp)) {
X       fputs(buffer, sf);
X       size += strlen(buffer) + 1;
X       if(size > MAXMSGSIZE) {
X           if(newsmode)
X               print_origin(sf);
X           fclose(sf);
X           part++;
X           goto again;
X       }
X   }
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       print_origin(sf);
X
X   /*
X    * Done
X    */
X   fclose(sf);
X   return EX_OK;
}
X
X
X
/*
X * Generate origin, seen-by and path line
X */
X
int print_origin(fp)
FILE *fp;
{
X   fprintf(fp, "\n--- %s\n", PROGRAMNAME);
X   fprintf(fp, " * Origin: %s (%d:%d/%d.%d)\n", ORIGIN,
X                 REAL_ZONE, REAL_NET, REAL_NODE, REAL_POINT);
X   fprintf(fp, "SEEN-BY: %d/%d ", MY_NET, MY_NODE);
X   if(ECHOFEED_NET != MY_NET)
X       fprintf(fp, "%d/", ECHOFEED_NET);
X   fprintf(fp,"%d\n", ECHOFEED_NODE);
X   fprintf(fp, "\001PATH: %d/%d\n", MY_NET, MY_NODE);
}
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;
long size;
int split;
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 and count size
X    */
X   size = 0;
X   while(fgets(buffer, BUFSIZ, stdin)) {
X       fputs(buffer, mail);
X       size += strlen(buffer) + 1;             /* `+1' for additional CR */
X   }
X   split = size>MAXMSGSIZE ? size/MAXMSGSIZE + 1 : 0;
X   debug(3, "Message body size %ld", size);
X   if(split)
X       debug(3, "Must split message, %d parts", split);
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, split);
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], split)) != 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 36748 -eq "$Wc_c" ||
    echo 'rfmail.c: original size 36748, 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:  mj@dfv.rwth-aachen.de
|_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931