[comp.sources.unix] v18i004: Fido/Usenet gateway, Part03/05

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

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

#!/bin/sh
# this is part 3 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file fpack.c continued
#
CurArch=3
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 fpack.c"
sed 's/^X//' << 'SHAR_EOF' >> fpack.c
X       mail is public and default is private).
X
X   All letters having arguments have only on space after them, and all
X   rest of the line is considered to be that argument. */
X
Xbool
Xwrite_hdr(source, packet)
X     FILE *source, *packet;
X{
X  char buffer[BUFSIZ];
X  char from[36], to[36], subject[72], date[20];
X  Node msg_node;
X  int attr = 0;
X  struct stat stbuf;
X
X  msg_node.zone = msg_node.net = msg_node.node = msg_node.point = -1;
X
X  /* clean up from, to and subject */
X  *from = 0;
X  *to = 0;
X  *subject = 0;
X  *date = 0;
X
X  while (fgets(buffer, BUFSIZ, source) && *buffer != '\n')
X    {
X      buffer[strlen(buffer) - 1] = 0; /* strip newline */
X      switch (*buffer)
X        {
X        case 'N':
X          if (parsefnetaddress(buffer, &msg_node))
X            {
X              log("Invalid destination: %s", buffer);
X              return False;
X            }
X          break;
X        case 'F':
X          (void) strncpy(from, buffer + 2, 35);
X          from[35] = 0;
X          break;
X        case 'T':
X          (void) strncpy(to, buffer + 2, 35);
X          to[35] = 0;
X          break;
X        case 'S':
X          (void) strncpy(subject, buffer + 2, 71);
X          subject[71] = 0;
X          break;
X        case 'D':
X          (void) strncpy(date, buffer + 2, 19);
X          date[19] = 0;
X          break;
X        case 'P':
X          attr |= ATTR_PRIVATE;
X          break;
X        }
X    }
X
X  /* check if net/node was missing */
X  if (msg_node.net == -1 || msg_node.node == -1)
X    {
X      log("Message with no destination");
X      return False;
X    }
X
X  /* we must have also receiver */
X  if (!*to)
X    {
X      log("Missing receiver in mail header");
X      return False;
X    }
X
X  /* if from is missing, we'll improvise...*/
X  if (!*from)
X    {
X      log("Missing from is header");
X      (void) strcpy(from, "Usenet");
X    }
X
X  /* if subject is missing, let's put there something */
X  if (!*subject)
X    {
X      log("Missing subject in header");
X      (void) strcpy(subject, "Mail from Usenet");
X    }
X
X  /* if date is missing, put there current date */
X  if (!*date)
X    {
X      log("Missing date in header");
X      (void) strcpy(date, fido_date(time((long *) 0)));
X    }
X
X  /* save all header values */
X  (void) write_int(packet, MSGTYPE); /* save msg type */
X  (void) write_int(packet, MY_NODE); /* save our node */
X  (void) write_int(packet, msg_node.node); /* save messages node */
X  (void) write_int(packet, MY_NET); /* save our net */
X  (void) write_int(packet, msg_node.net); /* save messages net */
X  (void) write_int(packet, attr); /* save attributes */
X  (void) write_int(packet, 0); /* cost, not used by us */
X  put_string(packet, date); /* save time of mail */
X  put_string(packet, to); /* save receiver */
X  put_string(packet, from); /* save sender */
X  put_string(packet, subject); /* save subject */
X
X  /* get status of mailfile and log this message */
X  if (fstat(fileno(source), &stbuf) == -1)
X    log("Unable to get stat of msg");
X
X  log("Msg from %s to %s at %s packetized (%ld chars)", from, to,
X      ascnode(msg_node), stbuf.st_size - ftell(source));
X
X  /* done with this header */
X  return True;
X}
X
X/* Write packet header for new packet.
X
X   Tue Oct 11 04:01:49 1988
X   Changed fwrite to write-ints and so on, as there is too many problems
X   with int and specially long alignment on different architectures. This
X   should be more portable!
X   (two extra bytes added for long alignment caused confmail to find
X   message type to be 0 and so it just forgot about that packet thinking
X   that it was in the end!)
X */
X
Xbool
Xwrite_pkthdr(packet)
X     FILE *packet;
X{
X  Packet header;
X  int count;
X  struct tm *tm;
X  time_t clock = time((long *) 0);
X
X  tm = localtime(&clock);
X
X  /* create packet structure */
X  header.orig_node = int16(MY_NODE);
X  header.dest_node = int16(node.node);
X  header.orig_net = int16(MY_NET);
X  header.dest_net = int16(node.net);
X
X  /* save time for header (why all these fields?) */
X  header.year = int16(tm->tm_year + 1900);
X  header.month = int16(tm->tm_mon);
X  header.day = int16(tm->tm_mday);
X  header.hour = int16(tm->tm_hour + 1);
X  header.minute = int16(tm->tm_min);
X  header.second = int16(tm->tm_sec);
X
X  header.rate = int16(MAXBAUD);
X  header.ver = int16(HDRVER);
X  header.product = 0;
X  header.x1 = 0;
X#ifdef FIDO_V11w
X  for (count = 0; count < 16; count++) header.fill[count] = 0;
X#else
X  for (count = 0; count < 8; count++) header.pwd_kludge[count] = 0;
X  header.orig_zone = int16(MY_ZONE);
X  header.dest_zone = int16(node.zone);
X  for (count = 0; count < 16; count++) header.B_fill2[count] = 0;
X  header.B_fill3 = 0;
X#endif
X  /* write header to file */
X  FPUTINT16(header.orig_node, packet);
X  FPUTINT16(header.dest_node, packet);
X  FPUTINT16(header.year, packet);
X  FPUTINT16(header.month, packet);
X  FPUTINT16(header.day, packet);
X  FPUTINT16(header.hour, packet);
X  FPUTINT16(header.minute, packet);
X  FPUTINT16(header.second, packet);
X  FPUTINT16(header.rate, packet);
X  FPUTINT16(header.ver, packet);
X  FPUTINT16(header.orig_net, packet);
X  FPUTINT16(header.dest_net, packet);
X  putc(header.product, packet);
X  putc(header.x1, packet);
X  for (count = 0; count < 8; count++) putc(header.pwd_kludge[count], packet);
X  FPUTINT16(header.orig_zone, packet);
X  FPUTINT16(header.dest_zone, packet);
X  for (count = 0; count < 16; count++) putc(header.B_fill2[count], packet);
X  for (count = 0; count < 4; count++)
X    putc(header.B_fill3 << (8 * count), packet); /* pc long = 4 bytes! */
X
X  if (ferr(packet))
X    {
X      log("$Write error on packet header");
X      return False;
X    }
X
X  debug(1, "New packet created");
X
X  return True;
X}
X
X/*ARGSUSED*/
Xint
Xmain(argc, argv, envp)
X     int argc;
X     char **argv, **envp;
X{
X  DIR *dp;
X  struct dirent *dir;
X  FILE *msg, *packet;
X  char packet_name[16];
X  int c;
X  Node np;
X  char *error;
X
X  /* get options */
X  while ((c = getopt(argc, argv, "vf:")) != EOF)
X    switch (c)
X      {
X      case 'f':
X    if (parsefnetaddress(optarg, &np)) exit(1);
X    node = np;
X        break;
X      case 'v':
X        verbose++;
X        break;
X      default:
X        (void) fprintf(stderr, "Usage: %s [-v] -f fidonet_address\n", *argv);
X        exit(1);
X      }
X
X  /* make sure that we got net/node */
X  if (node.net == -1 || node.node == -1)
X    {
X      log("Missing fidonet node in command line");
X      exit(1);
X    }
X
X  /* try to update nodelist-index */
X  if (error = update_index())
X    {
X      if (*error == '$')
X        log("$Cannot update nodelist-index: %s", error + 1);
X      else
X        log("Cannot update nodelist-index: %s", error);
X      exit(EX_OSERR);
X    }
X
X  /* goto spool directory, everything exiting happens there... */
X  if (chdir(SPOOL) == -1)
X    {
X      log("$Can not chdir to %s", SPOOL);
X      exit(1);
X    }
X
X  /* create packet name */
X  sprintpacketname(packet_name, node);
X
X  if (access(sprintfs("out/%s", packet_name), 0) == 0) {
X    debug(1, "Packet out/%s exists, append to it", packet_name);
X    if ((packet = fopen(sprintfs("out/%s", packet_name), "r+")) == NULL)
X      {
X        log("$Can not open out/%s for update", packet_name);
X        exit(1);
X      }
X    (void) fseek(packet, -2l, 2);
X  }
X  else {
X    debug(1, "New packet out/%s", packet_name);
X    if ((packet = fopen(sprintfs("out/%s", packet_name), "w")) == NULL)
X      {
X        log("$Can not open packet out/%s for writing", packet_name);
X        exit(1);
X      }
X
X    /* protect packet from users...*/
X    (void) chmod(sprintfs("out/%s", packet_name), 0600);
X
X    /* write packet-header, if it fails, exit */
X    if (!write_pkthdr(packet))
X      {
X        (void) unlink(sprintfs("out/%s", packet_name));
X        exit(1);
X      }
X  }
X
X  /* lock packet, wait if it's alredy locked */
X  while (lock(fileno(packet)) == -1 && errno == EAGAIN)
X    (void) sleep((unsigned) 1);
X
X  /* open spool directory */
X  if (dp = opendir("."))
X    {
X      while (dir = readdir(dp))
X        {
X          /* check that file is for us */
X          if (*dir->d_name == 'M' && dir->d_name[1] == '.')
X            {
X              if (msg = fopen(dir->d_name, "r"))
X                {
X                  debug(1, "Adding mailfile %s", dir->d_name);
X
X                  /* save header */
X                  if (write_hdr(msg, packet))
X                    {
X                      /* copy mail text, replace newlines with <cr> <lf> */
X                      while ((c = getc(msg)) && c != EOF)
X                        {
X                          if (c == '\n')
X                            (void) putc('\r', packet);
X                          (void) putc(c, packet);
X                        }
X                      /* null-terminate msg text */
X                      (void) putc(0, packet);
X                    }
X                  (void) fclose(msg);
X                  if (unlink(dir->d_name) == -1)
X                    log("$Unable to unlink %s", dir->d_name);
X                }
X              else
X                log("$Can not open mail %s for reading", dir->d_name);
X            }
X        }
X      (void) closedir(dp);
X    }
X  else
X    {
X      log("$Can not open spool directory %s", SPOOL);
X      exit(1);
X    }
X
X  /* msg type 0 indicates end of packet */
X  (void) write_int(packet, 0);
X
X  (void) fclose(packet);
X
X  exit(0);
X  /* NOTREACHED */
X}
X
X
SHAR_EOF
echo "File fpack.c is complete"
chmod 0644 fpack.c || echo "restore of fpack.c fails"
echo "x - extracting funpack.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > funpack.c &&
X#ifndef lint
Xstatic char *sccsid = "@(#)%M%  %I%  Teemu Torma %H%";
X#endif
X
X/* Unpaketize fido-mail packets and send mail to reciver or send it to
X   news-server.
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 <varargs.h>
X#include <time.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#include "sysexits.h"
X#include "fnews.h"
X
X#define MAXARGS (32) /* maximum number of arguments */
X
Xextern time_t time();
Xextern char *tzname[];
Xextern int getopt();
Xextern int optind;
Xextern char *optarg;
Xextern void exit(), perror(), free();
Xextern char *malloc();
Xextern time_t dateconv();
Xextern void swab();
X
Xint verbose = INIT_VERBOSE;
Xint newsmode = FALSE;
Xint acceptprivate = FALSE;
Xint trashprivate = FALSE;
X
X#ifdef SCCS
Xchar *version = "%I%";
X#else
Xchar *version = PROGRAMNAME;
X#endif
X
X/* Test if string is totally 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 msdos interger as machine integer */
X
Xint
Xint16(msint)
X     INT16 msint;
X{
X  static INT16 value;
X
X#ifdef SWAB_BYTES
X  swab((char *) &msint, (char *) &value, 2);
X#else
X  value = msint;
X#endif
X  return (int) value;
X}
X
Xstatic FILE *badtemp = NULL;
X
X/*VARARGS2*/
Xtprintf(fp, fmt, va_alist)
X     FILE *fp;
X     char *fmt;
X{
X  char buffer[BUFSIZ];
X  va_list pvar;
X
X  va_start(pvar);
X  vsprintf(buffer, fmt, pvar);
X  fputs(buffer, fp);
X  if (badtemp) fputs(buffer, badtemp);
X  va_end(pvar);
X}
X
Xtputs(buffer, fp)
X     char *buffer;
X     FILE *fp;
X{
X  fputs(buffer, fp);
X  if (badtemp) fputs(buffer, badtemp);
X}
X
X#define tputc(c, fp) putc(c, fp); if (badtemp) putc(c, badtemp);
X
X/* Replacement for fgets(3S) to understand cr's generated by Fido
X   and 'soft' cr's generated by SEAdog. It return EOF if text is over,
X   ie. 0 is got from file.
X
X   Sat Oct  1 10:15:46 1988
X   Must notice cr without linefeed, as confmail seems to send
X   strange ids without lf.
X
X   Mon Oct  3 01:51:01 1988
X   Notice * Origin header and extract node from it. Useless time consumer
X   when looking for mail.
X   */
X
Xstatic char *
Xffgets(buffer, maxlen, fp)
X     char *buffer;
X     int maxlen;
X     FILE *fp;
X{
X  register int c, ch, index;
X  register char *cp;
X  char buf[BUFSIZ];
X  static char wordsave[BUFSIZ];
X  /* TRUE if last line was origin line without valid node */
X  static int last_line_was_origin = FALSE;
X  /* TRUE if last character caused line wrap */
X  static int last_char_wrapped = FALSE;
X  Node node;
X
X  /* There might be wrapped word lying around */
X  if (*wordsave)
X    {
X      strcpy(buffer, wordsave);
X      strsclean(buffer);
X      debug(20, "Picked up word '%s'", buffer);
X      *wordsave = 0;
X    }
X  else
X    *buffer = 0;
X
X  cp = buffer + strlen(buffer);
X
X  while (--maxlen > 0 && (c = getc(fp)) != EOF && c)
X    {
X      /* Hard carriage return */
X
X      if (c == '\r')
X    c = '\n';
X      else if (c == '\n' || c == 0x8d || c == '\215')
X    {
X      /* Forget about these ! */
X      continue;
X    }
X      else
X    if (c == '\001')
X      {
X        /* Kludge. Stupid idea to put control chars to messages,
X           but thats the way it is, replace with something which
X           doesn't make programs expecting ascii mad */
X        strncpy(cp, "FSC-Control:", 12);
X        cp += 12;
X        c = ' ';
X      }
X
X      /* If last character caused line wrap, and we now got another
X     linefeed, skip this linefeed to avoid unneeded empty lines. */
X
X      if (last_char_wrapped)
X    {
X      if (c == '\n')
X        {
X          last_char_wrapped = FALSE;
X          continue;
X        }
X
X      if (isspace(c) && strempty(buffer)) continue;
X    }
X
X      *cp++ = c;
X
X      if (c == '\n')
X        break;
X
X      *cp = 0;
X
X      /* Try to wrap if line is too long and it is not a seen-by, origin
X     or path line. */
X      if (strlen(buffer) >= MAX_LINELEN - 1 &&
X      strncmp(" * Origin:", buffer, 10) &&
X      strncmp("SEEN-BY:", buffer, 8) &&
X      strncmp("FSC-Control:", buffer, 12)) /* - 1 for \n */
X    {
X      last_char_wrapped = TRUE;
X
X      /* Search for place to cut */
X      for (index = strlen(buffer) - 1; index >= 0; index--)
X        {
X          c = buffer[index];
X          if (index <= MAX_LINELEN / 3)
X        {
X          /* Too long, cut. */
X          *cp++ = c = '\n';
X          goto collected;
X        }
X
X          /* Note: [\]{|}@` are not considered punctuation because
X         they are used in some countries for national characters. */
X          if (isspace(c) || (ispunct(c) && !strchr("[\\]{|}@`", c)))
X        {
X          /* Wrap here! */
X          cp = buffer + index + 1; /* Punctuation left on this line */
X          strcpy(wordsave, cp);
X          debug(20, "saving word '%s'", wordsave);
X          *cp++ = c = '\n';
X          goto collected;
X        }
X        }
X    }
X
X      last_char_wrapped = FALSE;
X    }
X
X collected:
X
X  /* if we got nul, put it back if occurred in the middle of line */
X  if (!c && cp != buffer)
X    (void) ungetc(0, fp);
X
X  *cp = 0; /* Cut it here */
X
X  /* This is really nasty part: Try to work out reply-to path from
X     origin for those people who don't have node in nodelist.
X     This should handle both situations where origin line is split by
X     some stupid mailer, or because origin line was too long (sysops,
X     please don't use long system identifiers, it causes lots of trouble!).
X     It checks that line doesn't start with SEEN-BY: or FSC-Control as
X     those things generally follow origin lines. */
X
X  if (!strncmp(" * Origin:", buffer, 10) || last_line_was_origin) {
X    char *left, *right;
X
X    if (last_line_was_origin)
X      {
X    last_line_was_origin = FALSE;
X    /* If its seen-by or control, it probably isn't continuation! */
X    if (!strncmp("SEEN-BY:", buffer, 8) ||
X        !strncmp("FSC-Control:", buffer, 12))
X      goto out;
X      }
X    else
X      last_line_was_origin = TRUE;
X
X    strcpy(buf, buffer);
X    right = buf;
X
X    for (;;)
X      {
X    if ((left = strchr(right, '(')) && (right = strchr(left + 1, ')')))
X      {
X        *right = 0;
X        if (parsefnetaddress(++left, &node))
X          {
X        debug(1, "Could not parse %s", left);
X          }
X        else
X          {
X        memcpy( (char *) &originnode, (char *) &node, sizeof(Node));
X        debug(1, "Parsed %s to %d:%d/%d.%d", left, originnode.zone,
X              originnode.net, originnode.node, originnode.point);
X          }
X        left = ++right;
X      }
X    else
X      break;
X      }
X  }
X
X out:
X  return ((!c || c == EOF) && cp == buffer) ? (char *) 0 : buffer;
X}
X
X/* Execute sender of fido-message. Argument will be program name, anrgument
X   for that and pid to return. If command is not empty, use it instead
X   of given program and build parameter table. */
X
Xstatic char command[64];
X
XFILE *
Xopen_sender(program, args, pid)
X     char *program, **args;
X     int *pid;
X{
X  FILE *fp;
X  int fd[2], count = 0;
X  char *p, *realargs[MAXARGS];
X  char *cmd, *realprogram;
X
X  cmd = alloca(strlen(command) + 1);
X  strcpy(cmd, command);
X
X  if (*cmd)
X    {
X      while (p = strtok(cmd, SEPARATORS))
X    {
X      realargs[count++] = p;
X      cmd = NULL; /* For strtok */
X    }
X
X      realargs[count++] = NULL;
X      realprogram = *realargs;
X
X      if (realprogram)
X    {
X      program = realprogram;
X      args = realargs;
X    }
X    }
X
X  if (pipe(fd) == -1)
X    {
X      perror("funpack: pipe");
X      return (FILE *) 0;
X    }
X
X  switch (*pid = fork())
X    {
X    case -1:
X      perror("funpack: fork failed");
X      return (FILE *) 0;
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          if (args)
X            (void) execvp(program, args);
X          else
X            (void) execlp(program, basename(program), (char *) 0);
X          perror(program);
X        }
X      else
X        perror("funpack: dup");
X      exit(EX_OSERR);
X    default:
X      (void) close(fd[0]);
X      if ((fp = fdopen(fd[1], "w")) == NULL)
X        {
X          perror("funpack: fdopen");
X          return (FILE *) 0;
X        }
X    }
X  return fp;
X}
X
Xstatic Packet header;
X
Xread_header(fp)
X     FILE *fp;
X{
X  FGETINT16(header.orig_node, fp);
X  FGETINT16(header.dest_node, fp);
X  FGETINT16(header.year, fp);
X  FGETINT16(header.month, fp);
X  FGETINT16(header.day, fp);
X  FGETINT16(header.hour, fp);
X  FGETINT16(header.minute, fp);
X  FGETINT16(header.second, fp);
X  FGETINT16(header.rate, fp);
X  FGETINT16(header.ver, fp);
X  FGETINT16(header.orig_net, fp);
X  FGETINT16(header.dest_net, fp);
X  header.product = getc(fp);
X  header.x1 = getc(fp);
X  FREAD(header.pwd_kludge, 8, 1, fp);
X  FGETINT16(header.orig_zone, fp);
X  if (header.orig_zone == 0) header.orig_zone = int16(MY_ZONE);
X  FGETINT16(header.dest_zone, fp);
X  if (header.dest_zone == 0) header.dest_zone = int16(MY_ZONE);
X  FREAD(header.B_fill2, 16, 1, fp);
X  FREAD( (char *) &header.B_fill3, 4, 1, fp);
X  return ferr(fp);
X}
X
X/* Return int from file. Int in file is supposed to be 16 bit ms-dos
X   integer. */
X
Xint
Xread_int(fp)
X     FILE *fp;
X{
X  static INT16 value;
X
X  if (fread((char *) &value, 2, 1, fp) != 1)
X    {
X      if (ferr(fp))
X    log("Cannot read file, errno %d : %s", errno, sys_errlist[errno]);
X
X      value = 0;
X    }
X
X  debug(8, "< %02x(%ld)", int16(value), ftell(fp));
X  return int16(value);
X}
X
X/* Read null-terminated string from file. Ensure that buffer is also
X   null-terminated. Remove possible \n:s from end, they are generated by
X   some buggy mailers. */
X
Xvoid
Xget_string(buffer, fp, nbytes)
X     char *buffer;
X     FILE *fp;
X     int nbytes;
X{
X  register int n;
X  char *p;
X
X  debug(8, "get string start %ld", ftell(fp));
X
X  for (n = 0, *buffer = 0; n < nbytes; n++)
X    if ((buffer[n] = getc(fp)) == 0)
X      break;
X    else
X      debug(8, "<%d %c>", buffer[n], buffer[n]);
X
X  /* If still more chars in buffer, skip them until null char found */
X  if (n >= nbytes)
X    {
X      debug(8, "Skipping rest");
X      while(getc(fp));
X    }
X
X  buffer[nbytes] = 0;
X
X  /* Remove \n from end if its there, its a bug */
X  if (p = strchr(buffer, '\n')) *p = 0;
X
X  debug(8, "Getstring at %ld %s", ftell(fp), buffer);
X}
X
X#define NGFLAG_ACCEPT_PRIVATE 0
X#define NGFLAG_COMMAND 1
X#define NGFLAG_TRASH_PRIVATE 2
X
Xstatic char distribution[64];
Xstatic char *ngflags[] = { "accept-private", "command", "trash-private", "" };
X
X/* Like strtok but returns empty string instead of null if no more thigns
X   found */
X
Xchar *estrtok(s, sep)
X     char *s, *sep;
X{
X  char *p;
X
X  if (p = strtok(s, sep)) return p;
X  return "";
X}
X
Xchar *
Xget_ng(config, echo, distrib)
X     FILE *config;
X     char *echo, *distrib;
X{
X  static char conv[BUFLEN];
X  char *gr, *flag;
X
X  debug(2, "Checking echolist '%s'", echo);
X
X  *command = 0;
X  section(SECT_AREA_NG, config);
X  trashprivate = FALSE;
X  acceptprivate = FALSE;
X  while (getcl(conv, BUFLEN, config))
X    {
X      debug(3, "Config line '%s'", conv);
X
X      gr = estrtok(conv, SEPARATORS);
X      if (!strcmp(gr, echo))
X    {
X      /* Matched, take distribution and return newsgroup */
X
X      gr = estrtok(NULL, SEPARATORS);
X      strcpy(distrib, estrtok(NULL, SEPARATORS));
X      while (flag = strtok(NULL, SEPARATORS))
X        switch(listscan(ngflags, flag))
X          {
X           case NGFLAG_ACCEPT_PRIVATE:
X        acceptprivate = TRUE;
X        break;
X
X           case NGFLAG_COMMAND:
X        /* Fails if fnews.cf has DEL char in it, but thats
X           mostly a philosophical question. */
X        strcpy(command, estrtok(NULL, "\377"));
X        break;
X
X           case NGFLAG_TRASH_PRIVATE:
X        trashprivate = TRUE;
X        break;
X
X           case -1:
X           default:
X        log("Bad flag '%s' for newsgroup %s", flag, gr);
X        break;
X          }
X
X      debug(3, "Match, return newsgroup '%s', distribution %s",
X        gr, distrib);
X      return gr;
X    }
X    }
X  log("No newsgroup for '%s' found, return junk, distribution local", echo);
X  strcpy(distrib, "local");
X  return "junk";
X}
X
X/* Check if message is news-message (currenlty: if first line begins
X   with AREA:). If area is found, we'll return name for that area,
X   otherwise NULL. */
X
Xchar *
Xnews_msg(fp)
X     FILE *fp;
X{
X  FILE *config;
X  long offset = ftell(fp);
X  char *cp;
X  static char area[64];
X
X  if (ffgets(area, 64, fp) && !strncmp(area, "AREA:", 5))
X    {
X      /* this is echomail-message */
X      area[strlen(area) - 1] = 0;
X
X      /* strip possible spaces */
X      for (cp = area + 5; *cp && isspace(*cp); cp++);
X
X      if ((config = pfopen(LIBDIR, "fnews.cf", "r")) == NULL)
X    {
X      log("$Unable to open config file");
X      exit(1);
X    }
X
X      strcpy(cp, get_ng(config, cp, distribution));
X      fclose(config);
X
X      /* return converted area-name */
X      return cp;
X    }
X  else
X    {
X      /* this is not echomail message, seek back */
X      (void) fseek(fp, offset, 0);
X      return (char *) 0;
X    }
X  /* NOTREACHED */
X}
X
X/* Return date of message. Date will be read and converted to one
X   long-interger as normal Unix-time will be.
X
X   Both SEAdog and Fido date formats are understood.
X   SEAgod: Mon 1 Jan 86 02:34
X   Fido:   01 Jan 86 02:34:56 */
X
Xtime_t
Xlgetdate(packet)
X     FILE *packet;
X{
X  char buffer[20];
X  int c;
X  time_t timevar;
X  int n;
X
X  /* read date from packet */
X  for (n = 0; n < 20; n++)
X    if ((buffer[n] = getc(packet)) == NULL) break;
X
X  /* Some message-packers do mistakes! Date should be 20 bytes but
X     they start name directly after null terminating 18-char date
X     used in some systems. Check if following char is null or not,
X     and if not, put it back there as it probably is first char in
X     recipent name. This seems to be problem in OMMM, but I'm not
X     sure yet.
X
X     Wed Nov 16 21:11:34 1988 Seems that the bug is in fsc001, as
X     I constantly keep receiving messages which 19 byte date?
X     */
X
X#ifdef FSC_IS_REALLY_CORRECT
X  for (n++; n < 20; n++)
X    if (c = getc(packet))
X      {
X    ungetc(c, packet);
X      }
X#endif
X
X  buffer[19] = 0;
X  debug(8, "Getdate %s at %ld", buffer, ftell(packet));
X
X  /* try to get date */
X  timevar = getdate(buffer, (struct timeb *) NULL);
X  return timevar;
X}
X
X/* Remove 'of net/node' from name. Number will be saved
X   for later use and used as reply-to address, as I think most users use
X   it as their home system or if they are sysops, as their node. */
X
Xsearch_node(from, node)
X     char *from;
X     Node *node;
X{
X  char *s, *p, *of;
X  Node tempnode;
X
X  s = alloca(strlen(from) + 1);
X  strcpy(s, from);
X
X  p = strtok(s, WHITESPACE);
X  while (p)
X    {
X      if (!stricmp(p, "of"))
X    {
X      of = p;
X      /* Skip possible multiple 'of's */
X      for (; (p = strtok(NULL, WHITESPACE)) && !stricmp(p, "of"););
X      /* Of and something else follows, try to parse */
X      if (p)
X        {
X          if (!parsefnetaddress(p, &tempnode))
X        {
X          if (node) *node = tempnode;
X          /* Of net/node should be the last string */
X          if ((p = strtok(NULL, WHITESPACE)) == NULL)
X            {
X              /* Remove 'of ...' from name */
X              *(from + (int) (of - s)) = 0;
X              strclean(from);
X              return; /* Found it */
X            }
X          else
X            continue; /* Something follows, try again */
X        }
X        }
X      else
X        return; /* no more stuff */
X    }
X      p = strtok(NULL, WHITESPACE);
X    }
X  return;
X}
X
X/* Remove userids from names, like "James Smith (SMITH)" will become
X   "James Smith". Simple algorithm now, but its enough. Scandinvian
X   chars are translated to a and o, and other special chars are
X   removed. */
X
Xremove_id(name)
X     char *name;
X{
X  char *p, *cp;
X
X  if (p = strchr(name, '(')) {
X    while (*p != ')' && *p)
X      for (cp = p; *cp = *(cp + 1); cp++); /* Remove one char */
X
X    if (*p == ')')
X      for (cp = p; *cp = *(cp + 1); cp++); /* Remove last ) */
X  }
X
X  /* Remove blanks from the end */
X  while (strlen(name) && isspace(name[strlen(name) - 1]))
X    name[strlen(name) - 1] = 0;
X  (void) ascii_convert(name);
X  fine_convert(name);
X  stripbad(name);
X}
X
X/* Search alias name which matches with fidonet name, return NULL
X   if no alias specified. */
X
Xchar *get_alias(name)
X     char *name;
X{
X  char buffer[BUFSIZ];
X  char *cp;
X  FILE *fp;
X  static char unixname[BUFSIZ], fidoname[BUFSIZ];
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        {
X          if ((cp = strchr(buffer, ' ')) ?
X          cp : (cp = strchr(buffer, '\t')))
X        {
X          *cp = 0; /* Break unixname out */
X          strcpy(unixname, buffer); /* And save it */
X          debug(8, "Unix name %s", unixname);
X        }
X          else
X        {
X          /* No space or tab found, probably bad line */
X          debug(1, "Bad alias line %s", buffer);
X          continue;
X        }
X
X          /* Search for name start, there may be space between */
X          cp++;
X          while (*cp && isspace(*cp)) cp++;
X          if (!*cp)
X        {
X          debug(1, "Bad alias line %s", buffer);
X          continue;
X        }
X
X          strcpy(fidoname, cp); /* Save fidonet name */
X          debug(8, "Fidoname %s", fidoname);
X
X          if (!stricmp(fidoname, name))
X        {
X          fclose(fp);
X
X          /* There may be node specs after name, null them out */
X          if (cp = strchr(unixname, ',')) *cp = 0;
X
X          debug(8, "Fidoname %s matched with %s, return %s",
X            fidoname, name, unixname);
X          return unixname;
X        }
X        }
X    }
X    }
X
X  fclose(fp);
X  return NULL;
X}
X
X/* Save bad article from file given to bad directory */
X
Xsavebad(fp)
X     FILE *fp;
X{
X  char *fname, buffer[BUFSIZ];
X  FILE *badfile;
X
X  log("Saving bad article/mail to %s",
X      fname = sprintfs("%s/bad%ld", BADARTICLES, sequencer(BADSEQ)));
X  if (badfile = fopen(fname, "w+"))
X    {
X      rewind(fp);
X      while (fgets(buffer, BUFSIZ, fp))
X    fputs(buffer, badfile);
X    }
X  else
X    {
X      log("$Could not open bad article file");
X      return -1;
X    }
X  fclose(badfile);
X  return 0;
X}
X
X/* Unpack packet and all files in it. Packet's header will be stripped
X   off. For each message check if it is news-msg (First line begins
X   with AREA:). If it is, send it to news-sender sfnews, otherwise
X   send it to rmail. Address is given in to-field, or if it wont
X   fit to it, in the first line of message. In later case to-field
X   must be "Usenet". */
X
Xvoid
Xunpack(packet, packetnode)
X     FILE *packet;
X     Node packetnode;
X{
X  int count, attributes, messagetype;
X  char to[36], from[36], realto[40], realfrom[40], subject[72];
X  char badname[BUFSIZ];
X  char *args[MAXARGS];
X  char buffer[BUFSIZ], *cp, *p;
X  FILE *sender, *newssender;
X  int n, pid, stat_loc, c;
X  Node *entry, mynode, orignode, destnode, fromnode;
X  char hostname[10];
X  time_t msg_date;
X  char *area;
X
X  /* get node-entry fo packet-sender */
X  if (node_entry(packetnode) == NULL)
X    {
X      log("Unknown packet sender: %s", ascnode(packetnode));
X      return;
X    }
X
X  mynode.zone = MY_ZONE;
X  mynode.net = MY_NET;
X  mynode.node = MY_NODE;
X  mynode.point = MY_POINT;
X
X  if ((entry = node_entry(mynode)) == NULL)
X    {
X      log("Unable to find this net/node from nodelist");
X      return;
X    }
X
X  /* get our uucp-nodename */
X  if (gethostname(hostname, 10) == -1)
X    {
X      log("$Unable to get hostname");
X      return;
X    }
X
X  while ((messagetype = read_int(packet)) == MSGTYPE)
X    {
X      *args = NULL;
X      originnode.zone = -1;
X      fromnode.zone = -1;
X      orignode.zone = packetnode.zone; /* Orignode zone and point are not */
X      orignode.point = packetnode.point; /* included in messages. */
X      destnode.zone = packetnode.zone;
X      destnode.point = packetnode.point;
X
X      /* get nodes information about message */
X      orignode.node = read_int(packet);
X      destnode.node = read_int(packet);
X
X      /* get nodes from message */
X      orignode.net = read_int(packet);
X      destnode.net = read_int(packet);
X
X      debug(2, "Original net/node = %s", ascnode(orignode));
X      debug(2, "Destination net/node = %s", ascnode(destnode));
X
X      /* we're not interested about attributes currenty */
X      attributes = read_int(packet);
X
X      /* through away cost */
X      (void) read_int(packet);
X
X      /* get date */
X      msg_date = lgetdate(packet);
X
X      /* get receiver of message */
X      get_string(to, packet, 35);
X      search_node(to, (Node *) NULL);
X      remove_id(to);
X      debug(2, "Msg is to '%s'", to);
X
X      /* get sender of message */
X      get_string(from, packet, 35);
X      search_node(from, &fromnode);
X      remove_id(from);
X      debug(2, "Msg is from '%s'", from);
X
X      /* get subject */
X      get_string(subject, packet, 71);
X
X      /* Remove trailing blanks */
X      for (count = strlen(subject) - 1; count >= 0 && isspace(subject[count]);
X       count--) subject[count] = 0;
X
X      /* News expects some kind of subject and ignores the message
X     without one? */
X      if (!*subject) strcpy(subject, "No subject");
X
X      debug(2, "Subject is '%s'", subject);
X
X      /* check that message is to this node */
X      if (!samenode(destnode, mynode))
X        {
X          log("Msg from %s to %s at %s: wrong node address",
X          from, to, ascnode(destnode));
X          goto error;
X        }
X
X      /* check if this is news-message. Private messages will not
X         be forwarded to readnews, they go to mail, if received is
X     known, otherwise they will be thrown away. Exception are
X     conferences which are marked accept-private. */
X      newsmode = FALSE;
X      if ((area = news_msg(packet)) &&
X      (acceptprivate || !(attributes & ATTR_PRIVATE)))
X    {
X      /* This is news-artcile. Open sendfidonews and give area name as
X         argument to it. Input of sfnews will be just like input
X         of mail. */
X
X      debug(1, "Message is news-article");
X      newsmode = TRUE;
X
X      /* create args for sender */
X      *args = strsave(RNEWS);
X#ifdef NEEDED
X      args[1] = strsave(area);
X      args[2] = NULL;
X#else
X      args[1] = NULL;
X#endif
X      /* open sender */
X      if (!(sender = tmpfile()))
X        {
X          log("Can not create temp file, errno %d: %s",
X          errno, sys_errlist[errno]);
X          goto error;
X        }
X
X      if ((newssender = open_sender(*args, args, &pid)) == NULL)
X        {
X          log("Can not execute %s", *args);
X          goto error;
X        }
X
X      badtemp = NULL; /* tmp file for news messages will be
X                 created later. */
X        }
X      else
X        {
X          debug(1, "Message is for mail");
X      if (area)
X        {
X          debug(1, "Private message for area %s", area);
X          if (trashprivate) goto error;
X        }
X
X          /* If message receiver is Usenet, then receiver of mail will
X             be at first-line of message separated by commands/spaces.
X             If receiver is not usenet, mail will be sent to normal
X             receiver, but in this case all charactes will be converted
X             to lower case (Fido changes them, you know). */
X
X          if (!strcmp(to, "Usenet"))
X            {
X              if (ffgets(buffer, BUFSIZ, packet) == NULL)
X                {
X                  log("Missing receiver in msg from %s at %s", from,
X                      ascnode(orignode));
X                  goto error;
X                }
X              buffer[strlen(buffer) - 1] = 0;
X            }
X          else
X            {
X          debug(8, "Searching alias for %s", to);
X          if (p = get_alias(to))
X        {
X          (void) strcpy(buffer, p);
X          debug(8, "Got alias %s", buffer);
X        }
X          else
X        {
X          if (area)
X            {
X              log("Skipping private echo for %s", to);
X              goto error; /* If private echo message, skip */
X            }
X          (void) strcpy(buffer, to);
X          debug(8, "No alias, using %s", buffer);
X        }
X
X              for (n = 0; buffer[n]; n++)
X                buffer[n] = tolower(buffer[n]);
X            }
X
X          *args = strsave(basename(RMAIL));
X          for (n = 1, cp = strtok(buffer, ", \t"); cp && n < MAXARGS;
X               cp = strtok((char *) 0, ", \t"), n++)
X            args[n] = strsave(cp);
X          args[n] = NULL;
X
X          for (n = 1; args[n]; n++)
X            log("Sending mail from %s at %s to %s", from, ascnode(orignode),
X        args[n]);
X
X          /* open rmail for sending message */
X          if ((sender = open_sender(RMAIL, args, &pid)) == NULL)
X            {
X              log("Can not execute %s", RMAIL);
X              goto error;
X            }
X
X      if (badtemp)
X        fclose(badtemp);
X
X      if ((badtemp =
X           fopen(strcpy(badname, mktemp("/tmp/funpXXXXXX")), "w+"))
X          == NULL)
X        {
X          log("$Cannot open tmp file %s", badname);
X          goto error;
X        }
X
X      area = NULL;
X
X        }
X
X      /* Save real names for further use. Blanks included, */
X      if (strchr(from, ' '))
X    {
X      strcpy(realfrom, " (");
X      strcat(realfrom, from);
X      strcat(realfrom, ")");
X    }
X      else
X    *realfrom = 0;
X
X      if (strchr(to, ' '))
X    {
X      strcpy(realto, " (");
X      strcat(realto, to);
X      strcat(realto, ")");
X    }
X      else
X    *realto = 0;
X
X      /* change all spaces in name to _'s */
X      for (n = 0; from[n]; n++)
X        if (from[n] == ' ') from[n] = '_';
X      /* Same for to field */
X      for (n = 0; to[n]; n++)
X    if (to[n] == ' ') to[n] = '_';
X
X      /* set From_ line */
X      (void) tprintf(sender, "From %d!%s %s remote from %d\n", orignode.node,
X                     from, date("%a %h %d %T 19%y", (long *) 0), orignode.net);
X
X      /* Now generate valid RFC 822 header for mail. Some fields may
X         be different, in all places there may be comments between (
X         and )-charactes. */
X
X      /* This is imporant for news articles, as without this inews
X     tries to post it back. This is a bit complicated, as we cannot
X     put fidonet path here? Hmm... why not, I have to check this out! */
X      (void) tprintf(sender, "Path: %s!%s\n", RECEIVE_PATH, from);
X
X      /* print Received: field */
X      (void) tprintf(sender, "Received: by %s (funpack%s/%s)\n",
X             internode(*entry), version, entry->name);
X      (void) tprintf(sender, "\tid AA%05d; %s\n",
X                     getpid(), date("%a, %d %h %y %T %o (%z)", (long *) 0));
X
X      /* print Date: field */
X      (void) tprintf(sender, "Date: %s\n", date("%a, %d %h %y %T %o",
X                                                &msg_date));
X
X      /* print From: field */
X      (void) tprintf(sender, "From: %s@%s%s\n", from, internode(orignode),
X                     realfrom);
X
X      /* print Subject: field */
X      (void) tprintf(sender, "Subject: %s\n", subject);
X
X      /* print Message-Id: field */
X      (void) tprintf(sender, "Message-Id: <%s.AA%ld@%s>\n",
X                     date("%y%m%q%H%M", (long *) 0), sequencer(IDSEQUENCE),
X             internode(*entry));
X
X      if (area)
X    {
X      (void) tprintf(sender, "Newsgroups: %s\n", area);
X      if (strlen(distribution))
X        (void) tprintf(sender, "Distribution: %s\n", distribution);
X
X      /* print Reply-To: field */
X      /* Note: This is not accurate yet, as * Origin can change
X         address to other node */
X      (void) tprintf(sender, "Reply-To: %s@%s%s\n",
X             from, internode(destnode), realfrom);
X    }
X      else
X    {
X      /* print To: field */
X      (void) tprintf(sender, "To: ");
X      for (n = 1; args[n]; n++)
X        {
X          (void) tprintf(sender, "%s", args[n]);
X          if (args[n + 1])
X        (void) tprintf(sender, ", ");
X        }
X      tprintf(sender, "\n");
X
X      /* print Reply-To: field */
X      (void) tprintf(sender, "Reply-To: %s@%s%s\n",
X             from, internode(orignode), realfrom);
X    }
X
X      /* done with header, now send message text */
X
X      tprintf(sender, "\n");
X      while (ffgets(buffer, BUFSIZ, packet))
X        (void) tputs(buffer, sender);
X
X      /* News messages are a little bit more compilicated. News
X     article has been saved in temp file, and now we need to
X     read it through, replace Reply-To with correct return address
X     taken from origin row, nodelist, or if not found, echo feed.
X     Then really send mail to newssender. */
X
X      if (area)
X    {
X      debug(1, "Reading back news article");
X      rewind(sender);
X
X      if (badtemp)
X        fclose(badtemp);
X
X      if ((badtemp =
X           fopen(strcpy(badname, mktemp("/tmp/funpXXXXXX")), "w+"))
X          == NULL)
X        {
X          log("$Cannot open tmp file %s", badname);
X          goto error;
X        }
X
X      while (fgets(buffer, BUFSIZ, sender))
X        {
X          if (!strncmp(buffer, "Reply-To:", 9))
X        {
X          if (search_name(from))
X            {
X              debug(1, "%s is not a sysop, try his name", from);
X              if (fromnode.zone != -1)
X            originnode = fromnode;
X              else
X            {
X              debug(1, "No node in his name, try * Origin", from);
X              if (originnode.zone == -1)
X                {
X                  debug(1, "No * Origin, replace with echo feed");
X                  originnode = orignode;
X                }
X              else
X                debug(2, "Origin %s", ascnode(originnode));
X            }
X            }
X
X          (void) tprintf(newssender, "Reply-To: %s@%s%s\n",
X                 from, internode(originnode), realfrom);
X
X          /* Set Comment-To field */
X          if (search_name(to))
X            {
X              debug(1, "%s is not a sysop, try * Origin", to);
X              if (originnode.zone == -1)
X            {
X              debug(1, "No * Origin, replace with echo feed");
X              originnode = orignode;
X            }
X              else
X            debug(2, "Origin %s", ascnode(originnode));
X            }
X
X          (void) tprintf(newssender, "Comment-To: %s@%s%s\n", to,
X                 internode(originnode), realto);
X        }
X          else
X        (void) tputs(buffer, newssender);
X        }
X      fclose(newssender); /* sender will vanish when its closed! */
X    }
X
X      /* done with this msg, wait process to finish */
X      (void) fclose(sender);
X      while ((n = wait(&stat_loc)) != pid && n != -1);
X
X      debug(2, "Wait status: %o", stat_loc);
X      if (stat_loc)
X    savebad(badtemp);
X
X      fclose(badtemp);
X      unlink(badname);
X
X      debug(1, "Done with message");
X
X      /* free argument-list */
X      for (n = 0; args[n]; n++)
X        free(args[n]);
X      continue;
X
X      /* in the case of error skip text of message */
X    error:
X      while ((c = getc(packet)) && c != EOF);
X
X      /* free argument-list */
X      for (n = 0; args[n]; n++)
X        free(args[n]);
X    }
X
X  if (messagetype != MSGTYPE && messagetype != EOF && messagetype != 0)
X    log("Strange ending: %d", messagetype);
X
X  debug(1, "Done with packet");
X}
X
X/* ARGSUSED */
Xint
Xmain(argc, argv, envp)
X     int argc;
X     char **argv, **envp;
X{
X  struct dirent *dir;
X  DIR *dp;
X  int c;
X  FILE *packet;
X  char files[BUFLEN];
X  Node node;
X  bool nocheck = False;
X  char *error, *p;
X  Node packetnode;
X
X  node.zone = -1;
X  while ((c = getopt(argc, argv, "if:vV:")) != EOF)
X    switch (c)
X      {
X      case 'V':
X        version = optarg;
X        break;
X      case 'i':
X        nocheck = True;
X        break;
X      case 'v':
X        verbose++;
X        break;
X      case 'f':
X    if (parsefnetaddress(optarg, &node)) exit(1);
X        break;
X      default:
X        fprintf(stderr,
X        "Usage: %s [-v] [-f [[<zone>:]<net>/]<node>[.<point>]]\n",
X        *argv);
X        exit(EX_USAGE);
X      }
X
X  /* create name for unpacking */
X  if (node.zone == -1)
X    (void) strcpy(files, "");
X  else
X    {
X      sprintipacketname(files, node);
X      /* Cut sequence number off */
X      if (p = strrchr(files, ".")) *p = 0;
X    }
X
X  debug(2, "Unpacking packets beginning with %s", files);
X
X  /* try to update nodelist-index */
X  if (error = update_index())
X    {
X      if (*error == '$')
X        log("$Cannot update nodelist-index: %s", error + 1);
X      else
X        log("Cannot update nodelist-index: %s", error);
X      exit(EX_OSERR);
X    }
X
X  if (chdir(sprintfs("%s/in", SPOOL)) == -1)
X    {
X      log("$Cannot chdir to %s/in", SPOOL);
X      exit(EX_OSERR);
X    };
X
X  if (dp = opendir("."))
X    {
X      while (dir = readdir(dp))
X        if (!strncmp(dir->d_name, files, strlen(files)) && *dir->d_name != '.')
X          {
X
X            /* this packet is right */
X            debug(1, "Unpacking %s", dir->d_name);
X
X            /* open packet */
X            if (packet = fopen(dir->d_name, "r"))
X              {
X                if (read_header(packet))
X          {
X            if (feof(packet))
X              log("Missing packet header");
X            else
X              log("Error reading header, errno %d : %s",
X              errno, sys_errlist[errno]);
X          }
X                else
X          {
X            packetnode.zone = int16(header.orig_zone);
X            packetnode.net = int16(header.orig_net);
X            packetnode.node = int16(header.orig_node);
X            packetnode.point = 0; /* no points in fidonet header? */
X            debug(1, "Packet from %s", ascnode(packetnode));
X            debug(1, "Time %02d:%02d:%02d %d.%d.%d",
X              int16(header.hour),
X              int16(header.minute), int16(header.second),
X              int16(header.day), int16(header.month),
X              int16(header.year));
X            debug(1, "Max baud rate %d, version %d, product %d, x %d",
X              int16(header.rate), int16(header.ver),
X              header.product, header.x1);
X            debug(1, "Pwd \"%s\"", header.pwd_kludge);
X          }
X
X                  if (nocheck || ((int16(header.dest_zone) == MY_ZONE ||
X                   int16(header.dest_zone) == 0) &&
X                  int16(header.dest_node) == MY_NODE &&
X                  int16(header.dest_net) == MY_NET))
X                    unpack(packet, packetnode);
X                  else
X                    log("Packet is to %d:%d/%d",
X            int16(header.dest_zone),
X                        int16(header.dest_net),
X                        int16(header.dest_node));
X                (void) fclose(packet);
X
X        /* Move packet to unpacked directory. */
X        if (link(dir->d_name,
X             sprintfs("%s/%s", UNPACKED_DIR, dir->d_name)))
X          log("$Could not link packet %s", dir->d_name);
X        else
X          if (unlink(dir->d_name))
X            log("$Could not unlink packet %s", dir->d_name);
X              }
X            else
X              log("$Unable open packet %s", dir->d_name);
X          }
X      (void) closedir(dp);
X    }
X  else
X    {
X      log("$Unable to open spool directory");
X      exit(EX_OSERR);
X    }
X  exit(EX_OK);
X  /* NOTREACHED */
X}
SHAR_EOF
chmod 0644 funpack.c || echo "restore of funpack.c fails"
echo "x - extracting fcall.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > fcall.c &&
X#ifndef lint
Xstatic char *sccsid = "@(#)%M%  %I%  Teemu Torma %H%";
X#endif
X
X/* Dial to host-fido for 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 <string.h>
X#include <ctype.h>
X#include <signal.h>
X#include <termio.h>
X#include <dial.h>
X#include <sys/types.h>
X#include <dirent.h>
X#include <time.h>
X#include "hsu.h"
X#include "config.h"
X#include "fnet.h"
X#include "fio.h"
X#include "nodelist.h"
X
Xextern unsigned sleep();
Xextern void exit();
Xextern int getopt();
Xextern int optind;
Xextern char *optarg;
X
Xint line = -1;
Xint verbose = INIT_VERBOSE;
X
X/* Macros to check timeout */
X
X#define SetStart() (stime = time((long *) 0))
X#define Timeout(t) (time((long *) 0) - stime > (t))
X
X/* States for session sender. */
X
X#define SendInit        (0)
X#define WaitCxD         (1)
X#define WhackCRs        (2)
X#define WaitClear       (3)
X#define SendMail        (4)
X#define CheckMail       (5)
X#define SendFiles       (6)
X#define CheckFiles      (7)
X#define TryPickup       (8)
X
X/* States for session receiver. */
X
X#define WaitTsync       (9)
X#define RecMail         (10)
X#define XRecEnd         (11)
X#define RecFiles        (12)
X#define ChkFiles        (13)
X#define AllowPickup     (14)
X
X/* States to break up the loops */
X
X#define Error           (-1)
X#define Done            (-2)
X
X/* For debugging */
X
Xstatic char devicebuffer[100];
XCALL call; /* call structure for dial(3) */
X
X/* Show dialer's error code as message */
X
Xvoid
Xlog_dialerr(code)
X     int code;
X{
X  switch (code)
X    {
X    case INTRPT:
X      log("Interrupt occured during dialing");
X      break;
X    case D_HUNG:
X      log("Dialer hung (no return from write)");
X      break;
X    case NO_ANS:
X      log("No answer");
X      break;
X    case ILL_BD:
X      log("Illegal baud-rate");
X      break;
X    case A_PROB:
X      log("Acu problem (open() failure)");
X      break;
X    case L_PROB:
X      log("Line problem (open() failure)");
X      break;
X    case NO_Ldv:
X      log("Can't open LDEVS file");
X      break;
X    case DV_NT_A:
X      log("Requested device not available");
X      break;
X    case DV_NT_K:
X      log("Requested device %s, line %s telno %s speed %d baud %d, not known",
X      call.device, call.line, call.telno, call.speed, call.baud);
X      break;
X    case NO_BD_A:
X      log("No device available at requested baud");
X      break;
X    case NO_BD_K:
X      log("No device known at requested baud");
X      break;
X    case DV_NT_E:
X      log("Requested speed does not match");
X      break;
X    default:
X      log("Dial error %d", code);
X    }
X}
X
X/* Trapper for signals to quit gracefully. Be must do undial to get lock-
X   file removed */
X
Xint
Xquit(sig)
X     int sig;
X{
X  (void) signal(SIGINT, SIG_IGN);
X  (void) signal(SIGQUIT, SIG_IGN);
X  (void) signal(SIGTERM, SIG_IGN);
X  (void) signal(SIGHUP, SIG_IGN);
X
X  if (line >= 0)
X    undial(line);
X  if (sig >= 0)
X    log("Caught signal %d", sig);
X  exit(1);
X  /* NOTREACHED */
X}
X
X/* Send files in batch protocol */
X
Xbool
Xsendfiles()
X{
X/*   if (batchsend("fio.h")) */
X  return batchsend((char *) 0);
X/*   else return 0; */
X}
X
Xbool recfiles()
X{
X  return batchrec((char *) 0);
X}
X
X/* ARGSUSED */
Xint
Xmain(argc, argv, envp)
X     int argc;
X     char **argv, **envp;
X{
X  struct termio tio; /* terminal state */
X  int c;
X  time_t stime;
X  int retry_time = 0, no_retrys = 0, wait_line = 0;
X  int state = SendInit, receive_retrys = MAX_SEND_RETRIES;
X  bool ok, pickup = False;
X  Node *node, tnode;
X  char phonenumber[100];
X  char packetname[100], ipacketname[100];
X  char *error, *p;
X
X  /* try to update nodelist-index */
X  if (error = update_index())
X    {
X      if (*error == '$')
X        log("$Cannot update nodelist-index: %s", error + 1);
X      else
X        log("Cannot update nodelist-index: %s", error);
X      exit(EX_OSERR);
X    }
X
X  if (chdir(SPOOL) < 0)
X    {
X      log("$Can not chdir to %s", SPOOL);
X      exit(1);
X    }
X
X  strcpy(packetname, "packet.out");
X  strcpy(ipacketname, "packet.junk");
X
X  call.attr = &tio;
X  call.baud = 0;
X  call.speed = 0;
X  call.line = NULL;
X  call.telno = NULL;
X  call.modem = 0;
X  call.device = devicebuffer;
X  call.dev_len = sizeof(devicebuffer);
X
X  tio.c_iflag = IGNPAR | IGNBRK;
X  tio.c_oflag = 0;
X  tio.c_cflag = CS8 | CREAD | HUPCL | CLOCAL;
X  tio.c_lflag = NOFLSH;
X  tio.c_cc[VMIN] = 1;
X  tio.c_cc[VTIME] = 0;
X
X  if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X    (void) signal(SIGINT, quit);
X  if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
X    (void) signal(SIGTERM, quit);
X  if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X    (void) signal(SIGQUIT, quit);
X  if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
X    (void) signal(SIGHUP, quit);
X
X  while ((c = getopt(argc, argv, "vmdp:l:b:s:r:n:w:f:")) != EOF)
X    switch (c)
X      {
X       case 'd':
X        pickup = True;
X        break;
X       case 'p':
X    call.telno = optarg;
X    break;
X      case 'v':
X        verbose++;
X        break;
X      case 'l':
X        call.line = optarg;
X        break;
X      case 'b':
X        call.baud = atoi(optarg);
X        break;
X      case 's':
X        call.speed = atoi(optarg);
X        break;
X      case 'r':
X        retry_time = atoi(optarg);
X        break;
X      case 'n':
X        no_retrys = atoi(optarg);
X        break;
X      case 'w':
X        wait_line = atoi(optarg);
X        break;
X       case 'm':
X    call.modem = 1;
X    break;
X       case 'f':
X
X    /* Fidonet address to call */
X
X    if (parsefnetaddress(optarg, &tnode)) exit(1);
X    if (!(node = node_entry(tnode))) {
X      log("Could not get node information");
X      exit(1);
X    }
X
X    /* Use either maximum speed set or maximum speed of fido system */
X
X    if (node->speed > MAXBAUD) {
X      if (!call.baud) {
X        call.baud = call.speed = MAXBAUD;
X      }
X    } else {
X      call.baud = call.speed = node->speed;
X    }
X    if (call.baud < MINBAUD) call.baud = call.speed = MINBAUD;
X
X    /* Translate phone number from nodelist, if not set */
X
X    if (!call.telno) {
X      dial_translation(phonenumber, node->phone);
X      call.telno = phonenumber;
X    }
X
X    sprintpacketname(packetname, *node);
X    sprintipacketname(ipacketname, *node);
X    break;
X
X       default:
X        log("Illegal option for fcall");
X        exit(1);
X      }
X
X  while (state >= SendInit && state < AllowPickup)
X    switch (state)
X      {
X      case SendInit:
X        if (call.telno)
X          log("Dialing to %s", call.telno);
X        line = dial(call);
X        state = WaitCxD;
X        break;
X      case WaitCxD:
X        if (line == NO_ANS && retry_time)
X          if (--no_retrys < 0)
X            {
X              log("Too many retries in call");
X              state = Error;
X            }
X          else
X            {
X              log("No answer, redial in %d seconds", retry_time);
X              (void) sleep((unsigned) retry_time);
X              state = SendInit;
X            }
X        else
X          if ((line == DV_NT_A || line == NO_BD_A) && wait_line)
X            {
X              log("No free lines, waiting %d seconds", wait_line);
X              (void) sleep((unsigned) wait_line);
X            }
X          else {
X            if (line >= 0)
X              {
X                log("Call succeeded");
X                state = WhackCRs;
X                debug(1, "Wait %d seconds before start", PREWAIT);
X                (void) sleep((unsigned) PREWAIT);
X              }
X            else
X              {
X                log_dialerr(line);
X                log("Dial failed");
X                state = Error;
X              }
X          }
X        break;
X      case WhackCRs:
X        SetStart();
X        while (state == WhackCRs)
X          if (Timeout(30))
X            {
X              log("No response");
X              state = Error;
X            }
X          else if (readline(1) == '\r')
X            {
X              debug(1, "Got CR, wait 1 second");
X              (void) sleep((unsigned) 1);
X              state = WaitClear;
X            }
X          else
X            {
X              debug(2, "Sending <sp><cr>");
X              sendline(' ');
X          sendline('\r');
X            }
X        break;
X       case WaitTsync:
X    SetStart();
X    while (state == WaitTsync)
X      if (Timeout(60))
X        {
X          log("Garbage, no Tsync, Call ok (?)");
X          state = AllowPickup;
X        }
X      else if ((c = readline(1)) == TSYNCH)
X        {
X          debug(1, "Received TSYNCH");
X          state = RecMail;
X        }
X      else if (c == ENQ)
X        {
X          debug(1, "Got ENQ, nothing to pick up");
X          state = AllowPickup;
X        }
X    break;
X       case RecMail:
X    ok = xtrec(sprintfs("in/%s", ipacketname), False);
X    state = XRecEnd;
X    break;
X       case XRecEnd:
X    if (ok)
X      {
X        log("Mail received successfully");
X
X        /* Flush input */
X
X        (void) sleep((unsigned) 1);
X
X        SetStart();
X        while (state == XRecEnd)
X          if (Timeout(60))
X        {
X          log("Garbage on line");
X          state = Error;
X          break;
X        }
X          else if (readline(1) == TIMEOUT)
X        {
X          debug(1, "Line is clear, rec files");
X          state = RecFiles;
X        }
X      }
X    else
X      {
X        log("Receiving mail failed or nothing to receive");
X        log("Moving received packet in/%s to %s", ipacketname,
X        p = sprintfs("%s/%s", BADARTICLES, ipacketname));
X        if (link(sprintfs("in/%s", ipacketname), p))
X          log("$Could not link packet in/%s", ipacketname);
X        else
X          if (unlink(sprintfs("in/%s", ipacketname)))
X        log("$Could not unlink packet in/%s", ipacketname);
X
X        state = Error;
X      }
X    break;
X       case RecFiles:
X    ok = recfiles();
X    state = ChkFiles;
X    break;
X       case ChkFiles:
X    if (ok)
X      {
X        log("Files received succesfully");
X        (void) sleep((unsigned) 2);
X        state = AllowPickup;
X      }
X    else
X      {
X        log("Files not received ok");
X        state = Error;
X      }
X    break;
X      case WaitClear:
X        SetStart();
X        while (state == WaitClear)
X          if (Timeout(60))
X            {
X              log("Garbage on line");
X              state = Error;
X              break;
X            }
X          else if (readline(WAITCLEAR) == TIMEOUT)
X            {
X              debug(1, "Line is clear, send TSYNCH");
X              sendline(TSYNCH);
X          clear_input();
X              state = SendMail;
X            }
X        break;
X      case SendMail:
X        ok = xtsend(sprintfs("out/%s", packetname), False);
X    if (!ok && receive_retrys--)
X      state = WaitClear;
X    else
X      state = CheckMail;
X
X        break;
X      case CheckMail:
X        if (ok)
X          {
X            log("Mail sent successfully");
X        log("Moving mail packet out/%s to %s", packetname,
X        p = sprintfs("%s/%s.%s", SENTBUNDLE_DIR, packetname,
X        baseit(sequencer(OPACKETSEQUENCE))));
X        if (link(sprintfs("out/%s", packetname), p))
X          log("$Could not link packet out/%s", packetname);
X        else
X          if (unlink(sprintfs("out/%s", packetname)))
X        log("$Could not unlink packet out/%s",packetname);
X
X        sequencer(OPACKETSEQUENCE);
X            state = SendFiles;
X          }
X        else
X          {
X            log("Mail send failed");
X            state = Error;
X          }
X        break;
X      case SendFiles:
X        ok = sendfiles();
X        state = CheckFiles;
X        break;
X      case CheckFiles:
X        if (ok)
X          {
X            log("Files sent successfully");
X            state = TryPickup;
X          }
X        else
X          {
X            log("Files not send ok");
X            state = Error;
X          }
X        break;
X      case TryPickup:
X        log("Send successful");
X        if (pickup)
X          {
X            log("Starting mail pickup");
X            state = WaitTsync;
X          }
X        else
X          state = Done;
X        break;
X      }
X
X  if (state == Error) {
X    log("Conversation failed");
X  } else {
X    log("Conversation complete");
X  }
X
X  if (line >= 0) {
X    (void) sleep((unsigned) 5);
X    undial(line);
X  }
X  exit(0);
X  /* NOTREACHED */
X}
SHAR_EOF
chmod 0644 fcall.c || echo "restore of fcall.c fails"
echo "x - extracting fio.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > fio.c &&
X#ifndef lint
Xstatic char *sccsid = "@(#)%M%  %I%  Teemu Torma %H%";
X#endif
X
X/* Routines for io for fidonet software.
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 <signal.h>
X#include <termio.h>
X#include <sys/types.h>
X#include "fnet.h"
X#include "fio.h"
X
X/* number of characters in input buffer */
Xint nchars = 0;
X
X#ifdef DEBUG
X/* This is for protocol debugging logs */
X#define DEBUG_STATE_READ 1
X#define DEBUG_STATE_WRITE 2
X#endif
X
X/* Signal trapper for readline. */
X
Xstatic int
Xtrap(sig)
X     int sig;
X{
X  (void) signal(sig, trap);
X  return 0;
X}
X
X/* Read one character from line. We will do buffering up to BUFBIZ
X   characters. TIMEOUT will be returned it readline timeouts */
X
Xint
Xreadline(timeout)
X     int timeout;
X{
X  int c;
X  static unsigned char buffer[BUFSIZ];
X  static int pos = 0;
X  void (*alrm)();
X
X  if (pos < nchars)
X    /* there are characters in buffer */
X    c = buffer[pos++];
X  else
X    {
X      /* trap alarm signals and set timeout value */
X      alrm = signal(SIGALRM, trap);
X      (void) alarm((unsigned) timeout);
X
X      /* read characters */
X      nchars = read(line, (char *) buffer, BUFSIZ);
X#ifdef NEEDED
X      if (!receiving_data) debug(10, "Read returned %d", nchars);
X#endif
X
X      /* turn off alarm and return function value */
X      (void) alarm((unsigned) 0);
X      (void) signal(SIGALRM, alrm);
X
X      /* if we got characters return first of them, otherwise return
X         TIMEOUT */
X
X      if (nchars <= 0)
X        c = TIMEOUT;
X      else
X    {
X      c = buffer[pos = 0];
X      pos++;
X    }
X    }
X
X  if (!receiving_data) debug(8, "< %02x", c);
X  return c;
X}
X
X/* Flush line. Both terminal buffers and input buffer of readline will
X   be flushed */
X
Xvoid
Xflush()
X{
X  (void) ioctl(line, TCFLSH, 0);
X  nchars = 0;
X}
X
X/* Send character c to line. This also clears readline's input buffer
X   but not terminal buffers because it might takes too much time.
X   Tue Nov  8 13:49:27 1988
X   No more clearing buffer, as I try to make it sealink compatible.
X   */
X
Xvoid
Xsendline(c)
X     int c;
X{
X  unsigned char cc = (c & 0377);
X
X  debug(8, "> %02x", cc);
X  (void) write(line, (char *) &cc, 1);
X#ifdef NO_SEALINK
X  nchars = 0;
X#endif
X}
X
Xclear_input()
SHAR_EOF
echo "End of part 3"
echo "File fio.c is continued in part 4"
echo "4" > s2_seq_.tmp
exit 0

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