[comp.sources.unix] v18i003: Fido/Usenet gateway, Part02/05

rsalz@uunet.uu.net (Rich Salz) (03/07/89)

Submitted-by: Heikki Suonsivu <hsu@santra.hut.fi>
Posting-number: Volume 18, Issue 3
Archive-name: fnet/part02

#!/bin/sh
# this is part 2 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file rfmail.c continued
#
CurArch=2
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file rfmail.c"
sed 's/^X//' << 'SHAR_EOF' >> rfmail.c
X}
X
X/* Get return address from mail. Extract it from Reply-To: or From: fields. */
X
Xvoid
Xget_return_address(mail, address)
X     FILE *mail;
X     char *address;
X{
X  char buffer[BUFSIZ];
X
X  *address = 0;
X
X  /* check is there is Reply-To: field */
X  (void) rewind(mail);
X  while (fgets(buffer, BUFSIZ, mail) && *buffer != '\n')
X    if (!strncmp(buffer, "Reply-To: ", 10))
X      {
X        get_address(buffer + 10, address);
X        return;
X      }
X
X  /* no Reply-To:, check for From: */
X  (void) rewind(mail);
X  while (fgets(buffer, BUFSIZ, mail) && *buffer != '\n')
X    if (!strncmp(buffer, "From: ", 6))
X      {
X        get_address(buffer + 6, address);
X        return;
X      }
X
X  /* not found, send it to uucp */
X  (void) strcpy(address, "uucp");
X}
X
X/* Send mail back to sender. If Reply-To: of From:-field is present,
X   we'll use address specified in that, otherwise we will not return
X   mail (it also should be able to parse From_ fields). First argument
X   is file containing this mail, others are for vprintf(3S) to be used
X   as transcript. */
X
X/* VARARGS */
Xvoid
Xsendback(va_alist)
X     va_dcl
X{
X  va_list args;
X  FILE *mail;
X
X#ifdef RETURN_FAILED_MAIL
X  FILE *mailer;
X  char *fmt, buffer[BUFSIZ];
X  char to[128];
X#endif /* RETURN_FAILED_MAIL */
X
X  va_start(args);
X  mail = va_arg(args, FILE *);
X  fmt = va_arg(args, char *);
X
X#ifdef RETURN_FAILED_MAIL
X  get_return_address(mail, to);
X  log("Mail failed, return to %s", to);
X
X  /* generate shell command and open it */
X  (void) sprintf(buffer, "exec %s %s", RMAIL, to);
X  if (mailer = popen(buffer, "w"))
X    {
X      /* print correct header for mailer */
X      (void) fprintf(mailer, "From %d!MAILER-DAEMON %s remote from %d\n",
X                     this.node, date("%a %h %d %T 19%y", (long *) 0),
X                     this.net);
X      (void) fprintf(mailer, "Received: by %s (%s/%s)\n",
X                     internode(this), PROGRAMNAME, this.name);
X      (void) fprintf(mailer, "\tid AA%05d; %s\n", getpid(),
X                     date("%a, %d %h %y %T %o (%z)", (long *) 0));
X      (void) fprintf(mailer, "Date: %s\n", date("%a, %d %h %y %T %o",
X                                                (long *) 0));
X      (void) fprintf(mailer, "From: FidoNet Mail <%s@%s>\n",
X                     "MAILER-DAEMON", internode(this));
X      (void) fprintf(mailer, "Subject: Returned mail: %s\n",
X                     "Unable to deliver mail to FidoNet");
X      (void) fprintf(mailer, "Message-Id: <%s.AA%05d@%s>\n",
X                     date("%y%m%q%H%M", (long *) 0), getpid(),
X             internode(this));
X      (void) fprintf(mailer, "To: %s\n", to);
X      (void) fprintf(mailer, "\n");
X      (void) fprintf(mailer, "  ----- Transcript of session follows -----\n");
X      (void) vfprintf(mailer, fmt, args);
X      (void) fprintf(mailer, "\n\n");
X      (void) fprintf(mailer, "  ----- Unsent message follows -----\n");
X
X      /* now copy the message to mailer */
X      (void) rewind(mail);
X      while (fgets(buffer, BUFSIZ, mail))
X        (void) fputs(buffer, mailer);
X
X      /* mail is now sent, close mailer */
X      (void) pclose(mailer);
X    }
X  else
X    log("$Unable to invoke mailer for returned mail");
X#else /* not RETURN_FAILED_MAIL */
X  /* get dummy mail-file pointer */
X  mail = va_arg(args, FILE*);
X  fmt = va_arg(args, char *);
X
X  /* just print error to stderr, sendmail will return mail to sender
X     due to non-zero exit code. */
X  (void) vfprintf(stderr, fmt, args);
X#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
Xint
Xvalid_netnode(name, node, mail)
X     char *name;
X     Node node;
X     FILE *mail;
X{
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;
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
Xchar *
Xsender(mail, field)
X     FILE *mail;
X     char *field;
X{
X  struct passwd *pwd;
X  static char name[36];
X  char buffer[BUFSIZ];
X  register char *cp, *np;
X  register int cnt;
X  Node dummynode;
X  FILE *fp;
X
X  name[35] = 0;
X  (void) rewind(mail);
X  while (fgets(buffer, BUFSIZ, mail))
X    {
X      buffer[strlen(buffer) - 1] = 0; /* strip newline */
X      if (!strncmp(buffer, field, strlen(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            {
X              /* Format is 'From: Name <address>' */
X              for (np = buffer + strlen(field),
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
X            if ((cp = strchr(buffer, '(')) && (np = strchr(cp, ')')))
X              {
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              {
X        debug(5, "Could no find realname in <> or ()");
X        /* Try parse it with parse_address */
X        if (parse_address(buffer + strlen(field), name, &dummynode))
X          {
X            (void) strncpy(name, buffer + strlen(field), 35);
X            name[35] = 0;
X            debug(2, "No format in %s, name %s", field, name);
X          }
X        else
X          {
X            name[35] = 0;
X            debug(2, "Name %s parsed from address", name);
X          }
X              }
X          return name;
X        }
X    }
X
X  /* If not searching for sender, don't try to use uid */
X
X  if (strcmp(field, "From: ")) return NULL;
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  /* Found nothing reasonable. we could put there path, but it's quite
X     complicated to parse it and it's quite often more than 35 characters
X     and partial paths are not very informative. Reader can figure
X     it out by himself (or herself) and tell sender to use mailer... */
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
Xbool
Xgetnode(info, node)
X     char *info;
X     Node *node;
X{
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}
X
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
Xbool
Xaliascmp(alias, to, node)
X     char *alias, *to;
X     Node node;
X{
X  char buffer[BUFSIZ];
X  char *cp;
X  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    {
X      /* copy alias to buffer and terminate the it after first space */
X      (void) strcpy(buffer, alias + 1);
X      for (cp = buffer; *cp; cp++)
X        if (isspace(*cp))
X          {
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        {
X          debug(2, "Got node '%s'", cp);
X          if (getnode(cp, &anode))
X            {
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
Xchar *
Xreceiver(to, node)
X     char *to;
X     Node node;
X{
X  static char name[36];
X  char buffer[BUFSIZ];
X  register int cnt;
X  register char *cp;
X  FILE *fp;
X
X  if (fp = fopen(ALIAS, "r"))
X    {
X      while (fgets(buffer, BUFSIZ, fp))
X        {
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            {
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                {
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                  return name;
X                }
X              else
X                debug(1, "Missing alias");
X            }
X        }
X      (void) fclose(fp);
X    }
X  else
X    log("$Unable to open aliasfile %s", ALIAS);
X
X  /* Alias not found. Return the the original receiver with all
X     _-characters replaced by space and all words calitalized. */
X
X  for (cp = NULL, cnt = 0; *to && cnt < 35; cnt++, cp = to++)
X    {
X      if (*to == '_')
X        name[cnt] = ' ';
X      else
X        if (!cp || *cp == '_' || *to == ' ')
X          name[cnt] = toupper(*to);
X        else
X          name[cnt] = tolower(*to);
X    }
X  name[cnt] = 0;
X  debug(2, "No alias, name %s", name);
X  return name;
X}
X
X/* Return receiver's name, took Comment-To: field. If not found,
X   call receiver() and return what it returns */
X
Xchar *newsreceiver(fp, to, node)
X     FILE *fp;
X     char *to;
X     Node node;
X{
X  char *cp;
X
X  debug(2, "Checking To: field");
X  if (cp = sender(fp, "To: "))
X    {
X      debug(1, "Got recipent %s from To: field", cp);
X      strcpy(to, cp);
X    }
X  else
X    {
X      debug(2, "Checking Comment-To: field");
X      if (cp = sender(fp, "Comment-To: "))
X    {
X      debug(1, "Got recipent %s from Comment-To field", cp);
X      strcpy(to, cp);
X    }
X    }
X
X  return receiver(to, node);
X}
X
X/* Get date from mail. Look for Date: field, if present and time is in
X   correct format (same than ARPA-net mail uses), it will be used, otherwise
X   current time will be taken. */
X
Xchar *
Xlgetdate(fp)
X     FILE *fp;
X{
X  time_t timevar;
X  struct tm *localtime();
X  struct tm *mtime;
X  static char timebuf[20];
X  char buffer[BUFSIZ];
X  /* literal months */
X  static char *months[] = {
X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X    (char *) 0,
X  };
X  /* Literal weekdays */
X  static char *wkdays[] = {
X    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", (char *) 0,
X  };
X
X  (void) rewind(fp);
X  while (fgets(buffer, BUFSIZ, fp))
X    /* did we find Date: */
X    if (!strncmp(buffer, "Date: ", 6))
X      {
X    buffer[strlen(buffer) - 1] = '\0';
X        /* try to extract date and other information from it */
X        debug(2, "Found date: '%s'", buffer + 6);
X#ifdef OLD_VERSION
X        if (sscanf(buffer + 6, "%3s, %2d %3s %2d %2d:%2d:%*2d %*s",
X           wday, &mday, month, &year, &hour, &min) == 6)
X          {
X            debug(2, "Correct date format");
X            /* save date in SEAdog format */
X            (void) sprintf(timebuf, "%3s %2d %3s %2d %02d:%02d",
X                           wday, mday, month, year, hour, min);
X            return timebuf;
X          }
X#else
X    timevar = getdate(buffer + 6, (struct timeb *) NULL);
X    debug(2, "timevar %ld", timevar);
X    mtime = localtime(&timevar);
X    /* Save date in SEAdog format */
X    (void) sprintf(timebuf, "%3s %2d %3s %2d %02d:%02d",
X               wkdays[mtime->tm_wday], mtime->tm_mday,
X               months[mtime->tm_mon], mtime->tm_year,
X               mtime->tm_hour, mtime->tm_min);
X    debug(1, "Returning %s", timebuf);
X    return timebuf;
X#endif
X      }
X
X  (void) rewind(fp);
X  debug(2, "No Date: field in mail");
X
X  /* we did not found Date: field, let's use current time */
X  return date("%a %d %h %y %T", (long *) NULL);
X}
X
X/* Send mail from filepoiner fp to user to. To is in format of
X   net!node!receiver, where net and node are destnation and
X   receiver is receivers name in which blankos are replaces by
X   _-characters or alias. */
X
Xint
Xsendmail(fp, to)
X     FILE *fp;
X     char *to;
X{
X  char buffer[BUFSIZ], *cp;
X  FILE *sf;
X  char *sfile;
X  char hostname[10];
X  Node node;
X  char name[36];
X  int status, nodate, nosubject, nofrom;
X
X  (void) gethostname(hostname, 10);
X
X  /* get receiver and net/node */
X  if (cp = parse_address(to, name, &node))
X    {
X      sendback(fp, "Parse failed: %s", cp);
X      return EX_NOHOST;
X    }
X
X  debug(1, "Sending mail to %s at %s", name, ascnode(node));
X
X  if ((status = valid_netnode(name, node, fp)) != EX_OK)
X    return status;
X
X  debug(1, "Netnode ok, opening spool file");
X
X  if (sf = fopen(sfile = spoolfile("M."), "w"))
X    {
X      /* set correct permissions for spoolfile and lock it */
X      (void) chmod(sfile, 0600);
X      (void) lock(fileno(sf));
X
X      debug(1, "chmod and lock ok");
X
X      /* save net and node information */
X      (void) fprintf(sf, "N %s\n", ascnode(node));
X
X      /* save receiver. If using newsreceiver seem to cause
X     trouble, use normal receiver if not in news mode */
X
X      (void) fprintf(sf, "T %s\n", newsreceiver(fp, name, node));
X
X      /* save sender ... */
X      debug(10, "Trying to find sender (From)");
X      (void) fprintf(sf, "F %s\n", (cp = sender(fp, "From: ")) ?
X             cp : "Usenet");
X
X      /* try to find subject ... */
X      (void) rewind(fp);
X      while (fgets(buffer, BUFSIZ, fp))
X        if (!strncmp("Subject: ", buffer, 9))
X          {
X            (void) fprintf(sf, "S %s", buffer + 9);
X            buffer[strlen(buffer) - 1] = 0;
X            debug(2, "Subject: %s", buffer + 9);
X            break;
X          }
X
X      if (feof(fp))
X        /* we didn't find subject.. */
X        (void) fprintf(sf, "S Mail from Usenet\n");
X
X      /* save date */
X      (void) fprintf(sf, "D %s\n", lgetdate(fp));
X
X      /* if not public, mark that message is private */
X      if (!public)
X        (void) fprintf(sf, "P \n");
X
X      /* done with header information, now we'll copy the hole mail
X         after the header */
X
X      (void) putc('\n', sf);
X
X      debug(2, "Copying mail");
X
X      /* Now copy mail, also add Received field to mail */
X      (void) rewind(fp);
X      while (fgets(buffer, BUFSIZ, fp) && (!strncmp(buffer, "From ", 5) ||
X                                           !strncmp(buffer, ">From ", 6) ||
X                       !strncmp(buffer, "AREA:", 5)))
X    (void) fputs(buffer, sf);
X
X      (void) fprintf(sf, "Received: from %s%s by %s (%s%s/%s)\n",
X             hostname, DOMAIN, internode(this), "rfmail",
X             version, this.name);
X      (void) fprintf(sf, "\tid AA%05d; %s\n", getpid(),
X                     date("%a, %d %h %y %T %o (%z)", (long *) NULL));
X
X      /* Copy header. Try to avoid duplicating headers already in
X     fidonet header. Only first occurence of header is processed,
X     others will be ignored. Header is considered to end at first
X     empty line. */
X
X      nodate = TRUE;
X      nosubject = TRUE;
X      nofrom = TRUE;
X      do {
X
X    /* Date is in mail header, so skip header Date: */
X    if (nodate)
X      if (!strncmp(buffer, "Date:", 5))
X        {
X          nodate = FALSE;
X          continue;
X        }
X
X    /* Print subject only if it doesnt fit in fidonet header correctly */
X    if (nosubject)
X      if (!strncmp(buffer, "Subject:", 8))
X        {
X          if (strlen(buffer + 8) > 72)
X        fputs(buffer, sf);
X          nosubject = FALSE;
X          continue;
X        }
X
X    /* Print From field only if it doesn't fit in fidonet header */
X    if (nofrom)
X      if (!strncmp(buffer, "From:", 5))
X        {
X          if (strlen(buffer + 5) > 36)
X        fputs(buffer, sf);
X          nofrom = FALSE;
X          continue;
X        }
X
X    fputs(buffer, sf);
X      } while (fgets(buffer, BUFSIZ, fp));
X
X      /* done. */
X      (void) fclose(sf);
X      return EX_OK;
X    }
X  else
X    {
X      log("$Unable to open spoolfile");
X      return EX_CANTCREAT;
X    }
X  /* NOTREACHED */
X}
X
X/*ARGSUSED*/
Xint
Xmain(argc, argv, envp)
X     int argc;
X     char **argv, **envp;
X{
X  int cnt, c;
X  FILE *mail;
X  char buffer[BUFSIZ], tmpf[L_tmpnam];
X  int status = EX_OK;
X  char *error;
X  Node *node, mynode;
X
X  newsmode = FALSE;
X  while ((c = getopt(argc, argv, "npvV:")) != EOF)
X    switch (c)
X      {
X       case 'n':
X    /* Set news-mode */
X    newsmode = TRUE;
X    break;
X      case 'v':
X        /* set more verbosity */
X        verbose++;
X        break;
X      case 'V':
X        /* allow version on command line only if test version. */
X        if (*version != '%' || version[1] != 'I' || version[2] != '%')
X          version = optarg;
X        /* NOTE: Comparison cannot be with strcmp, because SCCS would also
X           change that comparison string. */
X        break;
X      case 'p':
X        /* this is not private message */
X        public = True;
X        break;
X      default:
X        (void) fprintf(stderr, "See manual page.\n", *argv);
X        exit(EX_USAGE);
X      }
X
X  /* create name of temporary mail-message */
X  (void) strcpy(tmpf, P_tmpdir);
X  (void) strcat(tmpf, mktemp("rfm.XXXXXX"));
X
X  /* open mail-temp */
X  if (mail = fopen(tmpf, "w+"))
X    {
X      /* protect mail file */
X      (void) chmod(tmpf, 0600);
X
X      /* copy mail to temp-file */
X      while (fgets(buffer, BUFSIZ, stdin))
X        (void) fputs(buffer, mail);
X
X      /* update nodelist-index if needed */
X      if (error = update_index())
X        {
X          /* there was error while updating nodelist-index */
X          if (*error == '$')
X            sendback(mail, "%s: %s", error + 1, sys_errlist[errno]);
X          else
X            sendback(mail, "%s", error);
X          exit(EX_SOFTWARE);
X        }
X
X      mynode.zone = MY_ZONE;
X      mynode.net = MY_NET;
X      mynode.node = MY_NODE;
X      mynode.point = MY_POINT;
X      if ((node = node_entry(mynode)) == NULL)
X        {
X          (void) fprintf(stderr, "Unable to this node from nodelist\n");
X          log("No %s in nodelist", ascnode(mynode));
X          exit(EX_SOFTWARE);
X        }
X      this = *node;
X
X
X      /* send mail to all user's */
X      for (rewind(mail), cnt = optind; cnt < argc; rewind(mail), cnt++)
X          /* send mail */
X          if ((status = sendmail(mail, argv[cnt])) != EX_OK)
X            break;
X
X      /* remove temporary file */
X      (void) fclose(mail);
X      (void) unlink(tmpf);
X    }
X  else
X    {
X      /* opening tmp-file failed, we can't even send mail back */
X      log("$Can not open %s for writing", tmpf);
X      exit(EX_CANTCREAT);
X    }
X
X  exit(status);
X  /*NOTREACHED*/
X}
SHAR_EOF
echo "File rfmail.c is complete"
chmod 0644 rfmail.c || echo "restore of rfmail.c fails"
echo "x - extracting funcs.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > funcs.c &&
X#ifndef lint
Xstatic char *sccsid = "@(#)%M%  %I%  Teemu Torma %H%";
X#endif
X
X/* Miscancelleus functions, logging, sequence numberic etc.
X
X   @(#)Copyright (c) 1987 by Teemu Torma
X
X   Permission is given to distribute this program and alter this code as
X   needed to adapt it to forign systems provided that this header is
X   included and that the original author's name is preserved. */
X
X/* LINTLIBRARY */
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include <varargs.h>
X#include <unistd.h>
X#include <sys/types.h>
X#include <time.h>
X#include <sys/utsname.h>
X#include <errno.h>
X#include "hsu.h"
X#include "config.h"
X#include "fnet.h"
X#include "sysexits.h"
X#include "nodelist.h"
X#include "shuffle.h"
X
X#define labs(n) (((n) < 0l) ? (-(n)) : (n))
X
Xextern void exit(), perror();
Xextern long atol();
Xextern time_t time();
Xextern int errno;
Xextern char *sys_errlist[];
X
XFILE *logfp = NULL;
X
X/* Lock file descriptor up to the end. If yur system doesn't have lockf()
X   (also known as locking()), or other region or file locking function
X   or system call, this should be done with lock-files. */
X
Xint
Xlock(fd)
X     int fd;
X{
X#ifdef LOCK_LOCKF
X  return lockf(fd, F_LOCK, 0l);
X#else
X  return locking(fd, F_LOCK, 0l);
X#endif
X}
X
X/* Unlock file descriptor up to the end. Routine which calls this should
X   first seek to original position. */
X
Xint
Xunlock(fd)
X     int fd;
X{
X#ifdef LOCK_LOCKF
X  return lockf(fd, F_ULOCK, 0l);
X#else
X  return locking(fd, F_ULOCK, 0l);
X#endif
X}
X
X/* Return ascii-date in specified format. If format string is null, return
X   date as date(1) returns it. Format is same than date(1) has, in addition
X   %z, which means timezone name. Clock is the time to convert, if NULL,
X   we'll use current time. */
X
Xchar *
Xdate(fmt, clock)
X     char *fmt;
X     time_t *clock;
X{
X  /* names for weekdays */
X  static char *weekdays[] = {
X    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
X  };
X
X  /* names for months */
X  static char *months[] = {
X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X  };
X
X  static char buffer[80];
X  char *bp = buffer;
X  time_t _clock;
X  struct tm *tm;
X
X  if (!clock)
X    _clock = time((long *) 0);
X  tm = localtime(clock ? clock : &_clock);
X
X  /* if no format string, this is default */
X  if (!fmt)
X    fmt = "%a %h %d %T %z 19%y";
X
X  for (*bp = 0; *fmt; fmt++)
X    switch (*fmt)
X      {
X      case '%':
X        switch (*++fmt)
X          {
X            /* newline */
X          case 'n':
X            *bp++ = '\n';
X            break;
X            /* tabulator */
X          case 't':
X            *bp++ = '\t';
X            break;
X            /* month number 1-12 */
X          case 'm':
X            (void) sprintf(bp, "%02d", tm->tm_mon + 1);
X            while (*bp)
X              bp++;
X            break;
X            /* day of month 1-31 */
X          case 'd':
X            (void) sprintf(bp, "%2d", tm->tm_mday);
X            while (*bp)
X              bp++;
X            break;
X          case 'q':
X            (void) sprintf(bp, "%02d", tm->tm_mday);
X            while (*bp)
X              bp++;
X            break;
X            /* year 00-99 */
X          case 'y':
X            (void) sprintf(bp, "%02d", tm->tm_year);
X            while (*bp)
X              bp++;
X            break;
X            /* date in format YY/MM/DD */
X          case 'D':
X            (void) sprintf(bp, "%02d/%02d/%02d", tm->tm_year,
X                           tm->tm_mon + 1, tm->tm_mday);
X            while (*bp)
X              bp++;
X            break;
X            /* hour 0-23 */
X          case 'H':
X            (void) sprintf(bp, "%02d", tm->tm_hour);
X            while (*bp)
X              bp++;
X            break;
X            /* minutes 0-59 */
X          case 'M':
X            (void) sprintf(bp, "%02d", tm->tm_min);
X            while (*bp)
X              bp++;
X            break;
X            /* seconds 0-59 */
X          case 'S':
X            (void) sprintf(bp, "%02d", tm->tm_sec);
X            while (*bp)
X              bp++;
X            break;
X            /* time in format HH:MM:SS */
X          case 'T':
X            (void) sprintf(bp, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
X                           tm->tm_sec);
X            while (*bp)
X              bp++;
X            break;
X            /* day of year 1-356 */
X          case 'j':
X            (void) sprintf(bp, "%03d", tm->tm_yday + 1);
X            while (*bp)
X              bp++;
X            break;
X            /* weekday 0-6 */
X          case 'w':
X            (void) sprintf(bp, "%d", tm->tm_wday);
X            while (*bp)
X              bp++;
X            break;
X            /* name of weekday 'Mon', 'Tue', ... , 'Sun' */
X          case 'a':
X            (void) strcpy(bp, weekdays[tm->tm_wday]);
X            while (*bp)
X              bp++;
X            break;
X            /* name of month 'Jan', 'Feb', ... , 'Dec' */
X          case 'h':
X            (void) strcpy(bp, months[tm->tm_mon]);
X            while (*bp)
X              bp++;
X            break;
X            /* name of timezone, e.g. EST */
X          case 'z':
X            (void) strcpy(bp, *tzname);
X            while (*bp)
X              bp++;
X            break;
X            /* numeric time zone, e.g. +0200 */
X          case 'o':
X            (void) sprintf(bp, "%c%02ld%02ld", (timezone <= 0l) ? '+' : '-',
X                           (labs(timezone) / (60l * 60l)),
X                           (labs(timezone) % (60l * 60l)));
X            while (*bp)
X              bp++;
X            break;
X          case 'l':
X            /* military time zone, Z = UT, A = -1, M = -12, (J not used),
X               N = +1, Y = +12.. */
X            *bp = (timezone == 0l) ? 'Z' : ((int) (labs(timezone) /
X                                                   (60l * 60l)) +
X                                            ((timezone < 0l) ? 'M' : '@'));
X            if (timezone > 0l && *bp >= 'J')
X              (*bp)++;
X            *++bp = 0;
X            break;
X          default:
X            *bp++ = *fmt;
X            break;
X          }
X        break;
X      default:
X        *bp++ = *fmt;
X        break;
X      }
X
X  *bp = 0;
X  return buffer;
X}
X
X/* Log to logfile. If logfile is not open, open it.
X
X   If first character in format string is '$', print also errno. If external
X   variable verbose is set, logging will be done also to stderr. */
X
X/* VARARGS */
Xvoid
Xlog(va_alist)
X     va_dcl
X{
X  va_list args;
X  char *fmt;
X  int eno = errno;
X
X  va_start(args);
X
X  fmt = va_arg(args, char *);
X
X  if (!logfp)
X    if ((logfp = fopen(LOGFILE, "a")) == NULL)
X      {
X    perror("Cannot open log file");
X    return;
X      }
X
X  (void) fprintf(logfp, "%s: ", date("%a, %d %h %y %T %o", (long *) 0));
X  (void) vfprintf(logfp, *fmt == '$' ? fmt + 1 : fmt, args);
X  if (*fmt == '$')
X    (void) fprintf(logfp, " errno = %d (%s)\n", eno, sys_errlist[eno]);
X  else
X    (void) fprintf(logfp, "\n");
X  (void) fflush(logfp);
X
X  /* if verbose is set, print also to stderr */
X  if (verbose)
X    {
X      (void) vfprintf(stderr, fmt + (*fmt == '$'), args);
X      if (*fmt == '$')
X        (void) fprintf(stderr, " errno = %d (%s)\n", eno,
X                       sys_errlist[eno]);
X      else
X        (void) fprintf(stderr, "\n");
X      (void) fflush(stderr);
X    }
X
X  va_end(args);
X}
X
X/* Debug output. First argument should be number, rest are used arguments
X   for vfprintf(3S). If external variable verbose has equal or greater
X   value than first number, vfprintf(3S) will be used to print other
X   arguments to stderr. */
X
X/* VARARGS */
Xvoid
Xdebug(va_alist)
X     va_dcl
X{
X  va_list args;
X  char *fmt;
X  int debug_level;
X
X  va_start(args);
X
X  debug_level = va_arg(args, int);
X  fmt = va_arg(args, char *);
X
X  if (debug_level <= verbose)
X    {
X      if (*fmt != '>' && *fmt != '<')
X    (void) fprintf(stderr, "\n%ld: ", time( (long *) NULL));
X      (void) vfprintf(stderr, fmt, args);
X    }
X
X  va_end(args);
X}
X
X/* Get next job number. New sequemnt number will be taken from file
X   LIBDIR/seq, which is in ascii-format and new number will be saved
X   back there. */
X
Xlong
Xjob_number()
X{
X  char seqfile[128], buffer[14];
X  FILE *fp;
X  long seqn = 0;
X
X  (void) sprintf(seqfile, "%s/seq", LIBDIR);
X  if (fp = fopen(seqfile, "r+"))
X    {
X      (void) lock(fileno(fp));
X      if (fgets(buffer, 14, fp))
X        seqn = atol(buffer);
X      seqn++;
X      (void) rewind(fp);
X      (void) fprintf(fp, "%ld\n", seqn);
X      (void) unlock(fileno(fp));
X      (void) fclose(fp);
X    }
X  else
X    {
X      log("$Can not open seq-file %s", seqfile);
X      exit(EX_OSFILE);
X    }
X  return seqn;
X}
X
X/* General sequencer */
X
Xlong
Xsequencer(filename)
X     char *filename;
X{
X  char seqfile[128], buffer[14];
X  FILE *fp;
X  long seqn = 0;
X
X  (void) sprintf(seqfile, "%s", filename);
X  if ((fp = fopen(seqfile, "r+")) == NULL)
X    {
X      if (errno == ENOENT)
X    {
X      if ((fp = fopen(seqfile, "w+")) == NULL)
X        {
X          log("$Can not create seq-file %s", seqfile);
X          exit(EX_OSFILE);
X        }
X      fputs("1", fp);
X      fclose(fp);
X      if ((fp = fopen(seqfile, "r+")) == NULL)
X        {
X          log("$Can not open new seq-file %s", seqfile);
X          exit(EX_OSFILE);
X        }
X    }
X      else
X    {
X      log("$Can not open seq-file %s", seqfile);
X      exit(EX_OSFILE);
X    }
X    }
X
X  (void) lock(fileno(fp));
X  if (fgets(buffer, 14, fp))
X    seqn = atol(buffer);
X  else
X    seqn = 0; /* This can theoretically fail */
X
X  seqn++;
X  (void) rewind(fp);
X  (void) fprintf(fp, "%ld\n", seqn);
X  (void) unlock(fileno(fp));
X  (void) fclose(fp);
X  return seqn;
X}
X
X/* Returns current last sequence number */
Xlong
Xgetsequencer(filename)
X     char *filename;
X{
X  char seqfile[128], buffer[14];
X  FILE *fp;
X  long seqn = 0;
X
X  (void) sprintf(seqfile, "%s", filename);
X  if ((fp = fopen(seqfile, "r+")) == NULL)
X    {
X      if (errno == ENOENT)
X    {
X      if ((fp = fopen(seqfile, "w+")) == NULL)
X        {
X          log("$Can not create seq-file %s", seqfile);
X          exit(EX_OSFILE);
X        }
X      fputs("1", fp);
X      fclose(fp);
X      if ((fp = fopen(seqfile, "r+")) == NULL)
X        {
X          log("$Can not open new seq-file %s", seqfile);
X          exit(EX_OSFILE);
X        }
X    }
X      else
X    {
X      log("$Can not open seq-file %s", seqfile);
X      exit(EX_OSFILE);
X    }
X    }
X
X  (void) lock(fileno(fp));
X  if (fgets(buffer, 14, fp))
X    seqn = atol(buffer);
X  else
X    seqn = 0; /* This can theoretically fail */
X
X  (void) unlock(fileno(fp));
X  (void) fclose(fp);
X  return seqn;
X}
X
X/* Get full pathname for spoolfile with new job number. File is in
X   spool directory and contains prefix followed by four digit
X   job number. */
X
Xchar *
Xspoolfile(prefix)
X     char *prefix;
X{
X  static char file[BUFLEN];
X
X  (void) sprintf(file, "%s/%s%08ld", SPOOL, prefix, job_number());
X  return file;
X}
X
X/* Return basename of s */
X
Xchar *
Xbasename(s)
X     register char *s;
X{
X  register char *p = s;
X
X  while (*s)
X    if (*s++ == '/')
X      p = s;
X  return p;
X}
X
X/* Open file with directory name and filename. */
X
XFILE *
Xpfopen(dir, name, mode)
X     char *dir, *name, *mode;
X{
X  char filename[128];
X
X  (void) strcpy(filename, dir);
X  (void) strcat(filename, "/");
X  (void) strcat(filename, name);
X
X  return fopen(filename, mode);
X}
X
Xchar *baseit(n)
X     long n;
X{
X  static char tab[] =
X    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
X  int count = 0;
X
X  SHUFFLEBUFFERS;
X
X  while (n)
X    {
X      tcharp[count] = tab[n % strlen(tab)];
X      n = n / strlen(tab);
X      count++;
X    }
X
X  tcharp[count] = 0;
X  return tcharp;
X}
X
X/* Create packet name for node given */
X
Xsprintpacketname(s, node)
X     char *s;
X     Node node;
X{
X  sprintf(s, "%s%s.%s.%s", node.point ?
X      sprintfs("%s.", baseit( (long) node.point)) : "",
X      baseit( (long) node.node), baseit( (long) node.net),
X      baseit( (long) node.zone));
X}
X
X/* Create packet name for inbound xx.xx.xx.xx.num. If point
X   number is 0, don't include it. All numbers are in ~63-base to squeeze
X   them to as small space as possible. It could be more sensible solution
X   to make them directory trees but I would need more time for that. This
X   trick makes finding packets difficult.
X   */
X
Xsprintipacketname(s, node)
X     char *s;
X     Node node;
X{
X  sprintf(s, "%s%s.%s.%s.%s", node.point ?
X      sprintfs("%s.", baseit( (long) node.point)) : "",
X      baseit( (long) node.node), baseit( (long) node.net),
X      baseit( (long) node.zone), baseit(sequencer(IPACKETSEQUENCE)));
X}
X
X/* Get line from config file. If *-character is in first column, report
X   EOF, and otherwise return line with comments stripped. This causes
X   effect, that each section in configuration file looks like it's own
X   file. Arguments and return value are the same than with fgets(3S). */
X
Xchar *
Xgetcl(buffer, len, fp)
X     char *buffer;
X     int len;
X     FILE *fp;
X{
X  char *cp;
X
X  while (fgets(buffer, len, fp))
X    {
X      buffer[strlen(buffer) - 1] = 0;
X      if (*buffer == '*')
X        return (char *) NULL;
X      /* everything after #-sign is comment */
X      if (cp = strchr(buffer, '#'))
X        *cp = 0;
X      /* if there's something left, return it */
X      if (*buffer)
X        return buffer;
X    }
X  return (char *) NULL;
X}
X
X/* Scan config file to specified section. This mechanism is not very
X   effective, but otherwise it would get too complicated. */
X
Xvoid
Xsection(number, config)
X     int number;
X     FILE *config;
X{
X  char buffer[BUFLEN];
X
X  (void) rewind(config);
X  while (--number)
X    while (getcl(buffer, BUFLEN, config))
X      /* skip section */;
X}
X
X/* Get header field from file. */
X
X#define MAX_HEADER_LEN 256
X
Xchar *mheader(fp, headername)
X     FILE *fp;
X     char *headername;
X{
X  static char header[MAX_HEADER_LEN];
X  long position;
X
X  position = ftell(fp);
X
X  rewind(fp);
X
X  /* Blank line terminates also, there shouldn't be any headers
X     after it any more */
X
X  while (fgets(header, MAX_HEADER_LEN, fp) && *header != '\n')
X    if (!strncmp(header, headername, strlen(headername)))
X      {
X    /* Remove \n at end */
X    header[strlen(header) - 1] = 0;
X    fseek(fp, position, 0);
X    return header + strlen(headername);
X      }
X
X  /* Not found, return empty string */
X
X  fseek(fp, position, 0);
X  return "";
X}
X
X
X
SHAR_EOF
chmod 0644 funcs.c || echo "restore of funcs.c fails"
echo "x - extracting rfnews.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > rfnews.c &&
X#ifndef lint
Xstatic char *sccsid = "@(#)%M%  %I%  Teemu Torma %H%";
X#endif
X
X/* Spool Usenet news articles to fidonet. Currently there is no news-system
X   in fido, it's done by software after and before mail-slots. Echomail
X   is most popular one, and this program does about the same thing:
X   it sends news-articles as fido-mail containing suitable information
X   for echomail. Echomail relies on normal fido-mail, we send news thru
X   rfmail, because this mechanism requires nothing different from normal
X   mail.
X
X   @(#)Copyright (c) 1987 by Teemu Torma
X
X   Permission is given to distribute this program and alter this code as
X   needed to adapt it to forign systems provided that this header is
X   included and that the original author's name is preserved. */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include <sys/types.h>
X#include "hsu.h"
X#include "config.h"
X#include "fnet.h"
X#include "fnews.h"
X#include "nodelist.h"
X
Xextern void exit();
Xextern void perror();
X
Xint verbose = INIT_VERBOSE;
X
X/* Get header field. Rewind header file and scan forward until we find
X   desired field. Not effective, but... Return NULL if field not found. */
X
Xchar *
Xget_field(field, hdr)
X     char *field;
X     FILE *hdr;
X{
X  static char buffer[BUFLEN];
X  int conditional = FALSE;
X  char *p;
X
X  if (*field == '?')
X    {
X      conditional = TRUE;
X      for (p = field; *p = *(p + 1); p++);
X    }
X
X  (void) rewind(hdr);
X  while (fgets(buffer, BUFLEN, hdr))
X    if (!strncmp(field, buffer, strlen(field)))
X      {
X    if (conditional)
X      {
X        /* Note: <= is ok, because there is one blank after header which
X           wouldn't be included in Fidonet message header */
X        if (!strncmp(field, "Subject:", 8))
X          if (strlen(buffer + 8) <= 72) return NULL;
X        if (!strncmp(field, "From:", 5))
X          if (strlen(buffer + 5) <= 36) return NULL;
X        if (!strncmp(field, "Reply-To:", 9))
X          if (strlen(buffer + 9) <= 36) return NULL;
X        if (!strncmp(field, "To:", 3))
X          if (strlen(buffer + 3) <= 36) return NULL;
X      }
X
X        buffer[strlen(buffer) - 1] = 0;
X        return buffer + strlen(field);
X      }
X
X  return (char *) NULL;
X}
X
X/* Open stream which is associated with fido-mailers starndard input.
X   We could use also popen(3S), but it invokes also sh(1) which slows
X   down opening and gets more memory. This routine exists if something
X   goes wrong, because this is quite essential part of program and
X   we can not continue without mailer. */
X
XFILE *
Xopen_mailer(to)
X     char *to;
X{
X  FILE *fp;
X  int fd[2];
X  char mailer[128];
X
X  (void) sprintf(mailer, "%s/rfmail", LIBDIR);
X
X  /* create pipe */
X  if (pipe(fd) == -1)
X    {
X      perror("rfnews: pipe");
X      exit(1);
X    }
X
X  switch (fork())
X    {
X    case -1:
X      perror("rfnews: fork failed");
X      exit(1);
X    case 0:
X      (void) close(0);
X      /* duplicate pipe and close now unused fd's */
X      if (dup(fd[0]) == 0)
X        {
X          (void) close(fd[0]);
X          (void) close(fd[1]);
X          (void) execlp(mailer, "rfmail", "-p", to, (char *) NULL);
X          perror(mailer);
X        }
X      else
X        perror("rfnews: dup");
X      exit(0);
X    default:
X      (void) close(fd[0]);
X      /* open the other end of pipe as buffered */
X      if ((fp = fdopen(fd[1], "w")) == NULL)
X        {
X          perror("rfnews: fdopen");
X          exit(1);
X        }
X    }
X  return fp;
X}
X
X/* Save news header for future processing and return file pointer to it. */
X
XFILE *
Xsave_hdr()
X{
X  char buffer[BUFLEN];
X  FILE *hdr = tmpfile();
X
X  while (fgets(buffer, BUFLEN, stdin) && *buffer != '\n')
X    (void) fputs(buffer, hdr);
X  return hdr;
X}
X
X/* Open rfmail. Send mail to net/node in config file, receiver will be
X   All, because Usenet News articles don't have To: field. Net/node
X   exraction is not very reliable of there's something wrong in
X   configuration file. */
X
XFILE *
Xinvoke_rfmail(config)
X     FILE *config;
X{
X  char buffer[BUFLEN], *cp;
X  int net, node;
X
X  section(SECT_NETNODE, config);
X  if (getcl(buffer, BUFLEN, config))
X    {
X      /* get net number */
X      for (cp = buffer, net = 0; *cp && isdigit(*cp) && *cp != '/'; cp++)
X        net = net * 10 + *cp - '0';
X      debug(1, "Got net %d from config file", net);
X      if (*cp)
X        {
X          /* get node number */
X          for (cp++, node = 0; *cp && isdigit(*cp); cp++)
X            node = node * 10 + *cp - '0';
X          debug(1, "Got node %d from config file", node);
X        }
X      else
X        {
X          log("Missing node in config file");
X          exit(1);
X        }
X      (void) sprintf(buffer, "All@%d.%d.FidoNet", node, net);
X      return open_mailer(buffer);
X    }
X  else
X    {
X      log("Missing net/node in config file");
X      exit(1);
X    }
X  /* NOTREACHED */
X}
X
X/* Try to get area for some of the newsgroups in newsgroup list. Method
X   is not very sophisticated, but it should work. */
X
Xchar *
Xget_area(config, groups)
X     FILE *config;
X     char *groups;
X{
X  static char conv[BUFLEN];
X  char buffer[BUFLEN];
X  char *cp, *gr, *area;
X
X  debug(2, "Checking grouplist '%s'", groups);
X
X  section(SECT_NG_AREA, config);
X  while (getcl(conv, BUFLEN, config))
X    {
X      for (cp = conv; !isspace(*cp); cp++)
X        /* find the end of newsgroup name */;
X      if (isspace(*cp))
X        {
X          *cp++ = 0;
X          (void) strcpy(buffer, groups);
X          debug(3, "Config line '%s'", conv);
X          /* get group by group from newsgroup-list */
X          for (gr = strtok(buffer, " ,"); gr; gr = strtok((char *) NULL, " ,"))
X            {
X              debug(3, "Comparing '%s' and '%s'", gr, conv);
X              if (!strcmp(gr, conv))
X                {
X                  while (isspace(*cp))
X                    cp++;
X                  area = cp;
X                  while (*cp && !isspace(*cp))
X                    cp++;
X                  *cp = 0;
X                  debug(3, "Match, return area '%s'", area);
X                  return area;
X                }
X            }
X          debug(3, "No match");
X        }
X      else
X        log("Invalid line in config: '%s'", conv);
X    }
X  log("No area found for any group in '%s'", groups);
X  return (char *) NULL;
X}
X
X/* Do actual processing of article. This will copy article, get newsgoroups
X   and save them and then send all rest of article. */
X
X/* ARGSUSED */
Xint
Xmain(argc, argv, envp)
X     int argc;
X     char **argv, **envp;
X{
X  FILE *config, *rfmail, *hdr;
X  char buffer[BUFLEN], seenbys[BUFLEN];
X  int c;
X  char *cp, *errorstr;
X
X  /* get options */
X  while ((c = getopt(argc, argv, "v")) != EOF)
X    switch (c)
X      {
X      case 'v':
X        verbose++;
X        break;
X      default:
X        (void) fprintf(stderr, "Usage: %s [-v]\n", *argv);
X        exit(1);
X      }
X
X  /* try to update nodelist-index */
X  if (errorstr = update_index())
X    {
X      if (*errorstr == '$')
X        log("$Cannot update nodelist-index: %s", errorstr + 1);
X      else
X        log("Cannot update nodelist-index: %s", errorstr);
X      exit(EX_OSERR);
X    }
X
X  /* try to open config file */
X  if ((config = pfopen(LIBDIR, "fnews.cf", "r")) == NULL)
X    {
X      log("$Unable to open config file");
X      exit(1);
X    }
X
X  /* open rfmail to send news */
X  rfmail = invoke_rfmail(config);
X
X  /* save news header */
X  hdr = save_hdr();
X
X  /* try to get area to which this article is going */
X  if (cp = get_field("Newsgroups: ", hdr))
X    if (cp = get_area(config, cp))
X      {
X    (void) fprintf(rfmail, "AREA:%s\n", cp);
X    debug(2, "send area %s", cp);
X      }
X    else
X      {
X        log("Unable to get area");
X#ifdef JUNK_AREA
X        (void) fprintf(rfmail, "AREA:\n", JUNK_AREA);
X        log("Sending article to area %s", JUNK_AREA);
X#else
X        exit(1);
X#endif
X      }
X  else
X    {
X      log("Missing Newsgroups: line in article");
X      exit(1);
X    }
X
X  /* now send news header */
X  section(SECT_HEADERS, config);
X  while (getcl(buffer, BUFLEN, config))
X    if (cp = get_field(buffer, hdr))
X      (void) fprintf(rfmail,"%s%s\n", buffer, cp);
X  (void) putc('\n', rfmail);
X
X  /* send rest of the article */
X  while (fgets(buffer, BUFLEN, stdin))
X    (void) fputs(buffer, rfmail);
X
X  /* send last information */
X  fprintf(rfmail, "--- %s\n", PROGRAMNAME);
X  section(SECT_ENDMSG, config);
X  if (!getcl(buffer, BUFLEN, config))
X    strcpy(buffer, "No Origin");
X  else if (!getcl(seenbys, BUFLEN, config))
X    *seenbys = 0;
X
X  (void) fprintf(rfmail, " * Origin: %s (%d:%d/%d.%d)\n", buffer,
X         MY_ZONE, MY_NET, MY_NODE, MY_POINT);
X  fprintf(rfmail, "SEEN-BY: %s \n", seenbys);
X  fprintf(rfmail, "\001PATH: %d/%d \n", MY_NET, MY_NODE);
X
X  /* I think that that was all... */
X  (void) fclose(rfmail);
X  (void) wait((int *) NULL);
X
X  exit(0);
X  /* NOTREACHED */
X}
SHAR_EOF
chmod 0644 rfnews.c || echo "restore of rfnews.c fails"
echo "x - extracting rmail.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > rmail.c &&
X#ifndef lint
Xstatic char *sccsid = "@(#)%M%  %I%  Teemu Torma %H%";
X#endif
X
X/* Replacement for rmail. This looks out, if destination system
X   name is numeric, and if it is, feeds mail to fidonet mail
X   server. If systemname is not numeric, this executes real rmail.
X   If this site is not fidonet gateway, define GATEWAY in rmail.h
X   to path to that host.
X
X   This is just a temporary replacement, MUCH MORE better is to use
X   sendmail for gatewaying or smail 2.3 version that also can invoke
X   other mailer.
X
X   @(#)Copyright (c) 1987 by Teemu Torma
X
X   Permission is given to distribute this program and alter this code as
X   needed to adapt it to forign systems provided that this header is
X   included and that the original author's name is preserved. */
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include "hsu.h"
X#include "rmail.h"
X#include "config.h"
X#include "sysexits.h"
X#include "nodelist.h"
X#include "fnet.h"
X
Xextern char *malloc();
Xextern void exit();
Xextern void perror();
Xextern char *regex();
X
X/* verbosity */
Xint verbose = INIT_VERBOSE;
X
X#ifndef GATEWAY
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
XFILE *
Xopen_mailer(program, args, pid)
X     char *program, **args;
X     int *pid;
X{
X  FILE *fp;
X  int fd[2];
X
X  /* create pipe */
X  if (pipe(fd) == -1)
X    {
X      perror("rmail: pipe");
X      exit(EX_OSERR);
X    }
X
X  switch (*pid = fork())
X    {
X    case -1:
X      perror("rmail: fork failed");
X      exit(EX_OSERR);
X    case 0:
X      (void) close(0);
X      if (dup(fd[0]) == 0)
X        {
X          (void) close(fd[0]);
X          (void) close(fd[1]);
X          (void) execvp(program, args);
X          perror(program);
X        }
X      else
X        perror("rmail: dup");
X      exit(EX_OSERR);
X    default:
X      (void) close(fd[0]);
X      if ((fp = fdopen(fd[1], "w")) == NULL)
X        {
X          perror("rmail: fdopen");
X          exit(EX_OSERR);
X        }
X    }
X  return fp;
X}
X
X#endif
X
X
X/* ARGSUSED */
Xint
Xmain(argc, argv, envp)
X     int argc;
X     char **argv, **envp;
X{
X  int cnt;
X
X#ifdef GATEWAY
X
X  char *path;
X
X#else
X
X  char **rargs = marray(argc + 1), **fargs = marray(argc + 1);
X  int rrec = 0, frec = 0, rargc = 1, fargc = 1;
X  int status = EX_OK;
X  char dummyname[100];
X  Node dummynode;
X
X#endif
X
X#ifdef GATEWAY
X
X  /* If GATEWAY is defined, send all mail going to fidonet to there.
X     If domain .FidoNet is known by other mahcines, routing is not
X     really problem and this rmail is not needed elsewhere than in
X     real gateway if there's no sendmail or other intelligent system
X     in use. */
X
X  for (cnt = 1; cnt < argc; cnt++)
X    if (parse_address(argv[cnt], dummyname, &dummynet, &dummynode) == NULL)
X      {
X#ifdef DEBUG
X        (void) printf("Routing %s to %s.\n", argv[cnt], GATEWAY);
X#endif
X        path = malloc((unsigned) (strlen(GATEWAY) +
X                                  strlen(argv[cnt]) + 2));
X        (void) strcpy(path, GATEWAY);
X        (void) strcat(path, "!");
X        argv[cnt] = strcat(path, argv[cnt]);
X      }
X
X  (void) execvp(RMAIL, argv);
X  perror(RMAIL);
X
X#else
X
X  *rargs = RMAIL;
X  *fargs = FIDOMAILER;
X
X  /* Scan thru receiver list and put all receivers in fidonet in fido-
X     mailer's receiver-list and all others in real rmails one. No
X     options can be passed to fidomailer thru this, because it would
X     be too difficult to determine which one goes to which one and
X     there might be same options also. Somehow it's good that fidomailer
X     is well hidden under this... */
X
X  for (cnt = 1; cnt < argc; cnt++)
X    if (*argv[cnt] == '-')
X      rargs[rargc++] = strsave(argv[cnt]);
X    else
X      {
X        if (parse_address(argv[cnt], dummyname, &dummynode) == NULL)
X          {
X#ifdef DEBUG
X            (void) printf("Argument %d (receiver %d) in fidomailer: %s\n",
X                          fargc, frec + 1, argv[cnt]);
X#endif
X            fargs[fargc++] = strsave(argv[cnt]);
X            frec++;
X          }
X        else
X          {
X#ifdef DEBUG
X            (void) printf("Argument %d (receiver %d) in rmail: %s\n",
X                          rargc, rrec + 1, argv[cnt]);
X#endif
X            rargs[rargc++] = strsave(argv[cnt]);
X            rrec++;
X          }
X      }
X
X  /* NULL terminate arument lists */
X  rargs[rargc] = NULL;
X  fargs[fargc] = NULL;
X
X  /* If there is mail only to rmail or fidomail, execute that mailer
X     with our original argument list. */
X
X  if (!frec)
X    {
X#ifdef DEBUG
X      (void) printf("No mail to fidonet, executing %s\n", RMAIL);
X#endif
X      (void) execvp(RMAIL, argv);
X      perror(RMAIL);
X      exit(1);
X    }
X  else
X    if (!rrec)
X      {
X#ifdef DEBUG
X        (void) printf("No mail to UUCP, executing %s\n", FIDOMAILER);
X#endif
X        (void) execvp(FIDOMAILER, argv);
X        perror(FIDOMAILER);
X        exit(1);
X      }
X    else
X      {
X
X        /* There's mail to both fidonet and usenet. We'll have to open
X           both mailers and feed this letter to them at the same time.
X           This is quite risky: If one of the mailer's exits for any
X           reason, we will get SIGPIPE and we'll fall down.... if that
X           happens, the other mailer got incomplete letter. */
X
X        int rmail_pid, fmail_pid;
X        register FILE *rmail = open_mailer(RMAIL, rargs, &rmail_pid);
X        register FILE *fmail = open_mailer(FIDOMAILER, fargs, &fmail_pid);
X        char buffer[BUFSIZ];
X        int stat_loc, pid;
X
X#ifdef DEBUG
X        (void) printf("Mail both to fidonet and UUCP\n");
X#endif
X        while (fgets(buffer, BUFSIZ, stdin))
X          {
X            (void) fputs(buffer, rmail);
X            (void) fputs(buffer, fmail);
X          }
X        (void) fclose(rmail);
X        (void) fclose(fmail);
X
X        /* We should wait for both mailers to exit and check their exit
X           statuses. Then we should decide which one we'll should return,
X           in this case let's return status from rmail, if other than
X           EX_OK, otherwise from fidomail. */
X
X        while ((pid = wait(&stat_loc)) != -1)
X          if (pid == rmail_pid)
X            {
X              if ((stat_loc & 0377) == 0)
X                status = stat_loc >> 8;
X            }
X          else
X            if (pid == fmail_pid)
X              {
X                if ((stat_loc & 0377) == 0 && status == EX_OK)
X                  status = stat_loc >> 8;
X              }
X      }
X#endif
X
X  exit(status);
X  /* NOTREACHED */
X}
SHAR_EOF
chmod 0644 rmail.c || echo "restore of rmail.c fails"
echo "x - extracting fpack.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > fpack.c &&
X#ifndef lint
Xstatic char *sccsid = "@(#)%M%  %I%  Teemu Torma %H%";
X#endif
X
X/* Create and update fidomail packets. Read mail messages from
X   spool directory and append them to packet. If packet doesn't
X   exist already, it will be created. Packet name in P.point.node.net.zone.
X
X   @(#)Copyright (c) 1987 by Teemu Torma
X
X   Permission is given to distribute this program and alter this code as
X   needed to adapt it to forign systems provided that this header is
X   included and that the original author's name is preserved. */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <errno.h>
X#include <string.h>
X#include <fcntl.h>
X#include <sys/types.h>
X#include <time.h>
X#include <sys/stat.h>
X#include <dirent.h>
X#include "hsu.h"
X#include "config.h"
X#include "fnet.h"
X#include "fpack.h"
X#include "nodelist.h"
X
Xextern time_t time();
Xextern int getopt();
Xextern int optind;
Xextern char *optarg;
Xextern uint sleep();
Xextern void exit();
Xextern void swab();
X
XNode node;
Xint verbose = INIT_VERBOSE;
X
X/* Return True if string is numeric. */
X
Xbool
Xnumeric(s)
X     register char *s;
X{
X  while (*s)
X    if (!isdigit(*s++))
X      return False;
X  return True;
X}
X
X/* Return argument as 16 bit interger to msdos */
X
XINT16
Xint16(value)
X     int value;
X{
X  INT16 msint, mval = (INT16) value;
X
X#ifdef SWAB_BYTES
X  swab((char *) &mval, (char *) &msint, 2);
X#else
X  msint = mval;
X#endif
X  return msint;
X}
X
X/* Put string to file in null-terminated format. */
X
Xvoid
Xput_string(fp, s)
X     FILE *fp;
X     char *s;
X{
X  while (*s)
X    {
X      (void) putc(*s, fp);
X      s++;
X    }
X  (void) putc(0, fp);
X}
X
X/* Return date in ascii string that is in fido-format, ie.
X   dd mmm yy hh:mm:ss, like 24 Jan 87 06:24:27 */
X
Xchar
X*fido_date(clock)
X     time_t clock;
X{
X  struct tm *tm;
X  static char date[20];
X
X  /* Literal months */
X  static char *months[] = {
X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X  };
X
X  tm = localtime(&clock);
X  (void) sprintf(date, "%02d %3s %02d %02d:%02d:%02d", tm->tm_mday,
X                 months[tm->tm_mon], tm->tm_year, tm->tm_hour,
X                 tm->tm_min, tm->tm_sec);
X  return date;
X}
X
X/* Write int to file in msdos interger format */
X
Xint
Xwrite_int(fp, value)
X     FILE *fp;
X     int value;
X{
X  INT16 msint = int16(value);
X
X  return fwrite((char *) &msint, sizeof(INT16), 1, fp);
X}
X
X/* Write fido msg header for mailfile. Mailfile contains first headers
X   terminated by empty line, after that comes message text.
X   Header consists of one letter id followed by arguments if any.
X
X   Id-letters:
X   N - fidonet address information of message, e.g. N 2:504/8.0.
X   T - to whom message is to, e.g. T Teemu Torma.
X   F - from whom message is from, e.g. F Foo Bar.
X   S - subject of message, e.g S How are you?.
X   P - indicates that message is private. (Here is little conflict:
X       rfmail uses option -p to indicate that mail to public and here
X       P means that mail is private. Maybe this P should mean that
SHAR_EOF
echo "End of part 2"
echo "File fpack.c is continued in part 3"
echo "3" > s2_seq_.tmp
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.