[alt.sources] FIDOGATE 05/06

mj@dfv.rwth-aachen.de (Martin Junius) (05/24/91)

---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is part 05 of a multipart archive
# ============= funpack.c ==============
if test -f 'funpack.c' -a X"$1" != X"-c"; then
    echo 'x - skipping funpack.c (File already exists)'
else
echo 'x - extracting funpack.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'funpack.c' &&
/*:ts=4*/
/*****************************************************************************
X * FIDOGATE --- Gateway software UNIX <-> FIDO
X *
X * $Id: funpack.c,v 2.18 91/05/21 09:40:54 mj Exp $
X *
X * Unpack fido mail packets
X *
X * $Log:   funpack.c,v $
X * Revision 2.18  91/05/21  09:40:54  mj
X * Changed log entry for NetMail messages.
X * 
X * Revision 2.17  91/05/21  08:48:20  mj
X * Fixed a small bug in bouncing mail.
X * 
X * Revision 2.16  91/05/19  17:21:58  mj
X * More clean-up of the source code. Mail to unknown local user is bounced.
X * 
X * Revision 2.15  91/04/28  21:39:03  mj
X * Changed address in `From_' line from packet origin node to message
X * origin node. Less verbose `Received:' header.
X * 
X * Revision 2.14  91/03/29  18:11:13  mj
X * Names `UUCP' and `GATEWAY' aren't added to output To: adress. Log of
X * personal mail improved. RFC headers at start of FIDO message are not
X * included in output.
X * 
X * Revision 2.13  91/01/28  19:33:07  mj
X * Generated `Errors-To:' header for mail addressed from FIDO.
X * `SEEN-BY' and tear lines are removed from output message.
X * 
X * Revision 2.12  91/01/26  13:27:37  mj
X * Changed generation of `Path' header to include FIDO address of gateway.
X * 
X * Revision 2.11  90/12/09  17:34:26  mj
X * Recognize `To:' header at start of message.
X * 
X * Revision 2.10  90/12/02  21:22:02  mj
X * Changed program header to mention both authors of the original
X * software posted to alt.sources.
X * 
X * Revision 2.9  90/11/23  20:41:10  mj
X * Changed locally generated message ids to <funpackNNN@fN.nN.zN.fidonet.org>,
X * corrected line breaking in ffgets().
X * 
X * Revision 2.8  90/11/20  21:08:44  mj
X * Added support for ^AINTL kludge.
X * 
X * Revision 2.7  90/11/05  20:50:14  mj
X * Changed my signature in all program headers.
X * 
X * Revision 2.6  90/11/04  14:14:48  mj
X * A small change in line wrapping: no more line wrapping at punctuation
X * characters.
X * 
X * Revision 2.5  90/11/01  14:33:56  mj
X * Convert FIDO ^AMSGID and ^AREPLY kludges to RFC822 Message-ID and
X * References headers.
X * 
X * Revision 2.4  90/10/29  21:19:37  mj
X * Ignore case when checking for name alias.
X * 
X * Revision 2.3  90/09/16  17:35:49  mj
X * Also look for "UUCPFROM:" in first lines of message body.
X * 
X * Revision 2.2  90/09/09  10:54:37  mj
X * Fixed a bug in handling of `ReplyTo:' in FIDO message body. Spaces
X * in such an address are now striped off.
X * 
X * Revision 2.1  90/09/08  18:46:32  mj
X * Changed some code for getting real names out of FIDO from and to header
X * fields. (Ignore things after "%" or "Of")
X * Now we look for RFC822 like header lines at the beginning of the
X * message body and use them for the `From:' and `To:' field.
X * 
X * Revision 2.0  90/09/03  17:54:23  mj
X * Rewrote much of the code inside this module.
X * There are now hooks, where support for most of
X * the FIDO ^A kludges (e.g. ^AMSGID) can be installed.
X * Function stripbad() from nodelist.c moved to
X * funpack.c
X * 
X * Revision 1.7  90/08/12  11:56:54  mj
X * Some changes. (what?)
X * 
X * Revision 1.6  90/07/23  12:58:26  mj
X * The FIDO `^AFMPT x' kludge is now recognized by funpack and
X * inserted as the point address in the `From: ...' line.
X * 
X * Revision 1.5  90/07/15  10:26:11  mj
X * Messages for mail: M.xxxx.TO is not used any more. frecv now
X * looks for `To: ...' line is mail file M.xxxx.
X * Messages for news: Address from origin line is only substituted
X * in header `From: ...' line, all others are left unchanged.
X * 
X * Revision 1.4  90/07/01  15:20:18  mj
X * Fixed some bugs in funpack caused by the removal of alloca().
X * No more core dumps, but heaven knows, why it works now. Strange.
X * 
X * Revision 1.3  90/07/01  13:45:50  mj
X * Removed all calls to alloca(). All unsave malloc()'s without
X * checking the returned pointer are now done via xmalloc().
X * Fixed a malloc() error in rmail.
X * 
X * Revision 1.2  90/06/28  22:04:19  mj
X * Much rework of the sources, no more hsu.h and other clean up.
X * rmail improved, now handles special XENIX quirks.
X * 
X * Revision 1.1  90/06/21  21:09:19  mj
X * Everything seems to work, so this delta was made.
X * 
X * Revision 1.0  90/06/19  18:32:29  mj
X * Initial revision
X * 
X *
X *****************************************************************************
X * This version hacked and maintained by:
X *    _____ _____
X *   |     |___  |   Martin Junius     FIDO:    2:242/6.1   2:242/6.0
X *   | | | |   | |   Republikplatz 3   DOMAIN:  mju@dfv.rwth-aachen.de
X *   |_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931
X *
X * Original version of these programs and files:
X *
X *   Teemu Torma
X *   Heikki Suonsivu   FIDO: 2:504/1   UUCP: ...!mcsun!santra!hsu
X *
X *****************************************************************************/
X
/*
X * Fido mail packets in SPOOL/in are processed and splitted into
X * single messages for either news
X *     N.xxxx
X * or mail
X *     M.xxxx
X * in directory SPOOL/unpacked.
X *
X */
X
#include "fidogate.h"
X
#include <pwd.h>
X
X
X
#define PROGRAMNAME "funpack $Revision: 2.18 $"
X
X
X
#define SEPARATORS " ,;\015\012\011"
#define WHITESPACE " \015\012\011"
X
X
X
extern time_t time();
extern char *tzname[];
extern int getopt();
extern int optind;
extern char *optarg;
extern void exit(), perror();
extern time_t dateconv();
extern void swab();
X
X
int verbose = INIT_VERBOSE;
X
/* Our net/node information */
Node this;
X
/* News distribution for FIDO area */
char distribution[64];
X
/* FIDO mail packet header */
Packet header;
X
X
/*
X * For recognizing `^AINTL', `^AFMPT x', `^AMSGID ...' and `^AREPLY ...'
X */
#define SAVEBUFSIZ 256
X
static int  msgbody_fmpt = 0;
static char msgbody_msgid[SAVEBUFSIZ];
static char msgbody_reply[SAVEBUFSIZ];
static char msgbody_intl[SAVEBUFSIZ];
X
X
/*
X * For grabbing addresses from RFC822 header lines at start of message
X */
X
static char *msgbody_rfc_from;
static char *msgbody_rfc_to;
X
X
X
/*
X * check_origin() --- Analyse ` * Origin: ...' line in FIDO message
X *                    body and parse address in ()s into Node structure
X *
X * Origin line is checked for the rightmost occurence of
X * ([text] z:n/n.p). If origin line get splitted across
X * two lines you're out of luck.
X */
X
check_origin(buffer, node, text)
char *buffer;
Node *node;
char *text;
{
char *left, *right;
char *buf;
X
X   if(!strncmp(" * Origin:", buffer, 10)) {
X       debug(3, "Checking origin line for FIDO address");
X       buf   = strsave(buffer);
X       right = strrchr(buf, ')');
X       if(!right) {
X           free(buf);
X           return(FALSE);
X       }
X       left  = strrchr(buf, '(');
X       if(!left) {
X           free(buf);
X           return(FALSE);
X       }
X       *right = 0;
X       *left++ = 0;
X       /*
X        * Copy info text for Organization: header
X        */
X       strcpy(text, buf + strlen(" * Origin: "));
X       /*
X        * Parse node info
X        */
X       if(parsefnetaddress(left, node)) {
X           /* Not a valid FIDO address */
X           debug(3, "Could not parse %s", left);
X           node->zone = node->net = node->node = node->point = -1;
X           free(buf);
X           return(FALSE);
X       }
X       else {
X           /* Valid FIDO address */
X           debug(3, "Parsed %s to %s", left, ascnode(*node));
X       }
X       free(buf);
X       return(TRUE);
X   }
X   return(FALSE);
}
X
X
X
/*
X * check_ctrl_a() --- Check for various ^A kludges
X */
X
check_ctrl_a(buffer)
char *buffer;
{
register char *p;
X
X   /*
X    * Look for `^AINTL ...'
X    */
X   if(!strncmp(buffer, "^AINTL", 6)) {
X       buffer += 6;
X       while(*buffer==':' || *buffer==' ')
X           buffer++;
X       strcpy(msgbody_intl, buffer);
X   }
X
X   /*
X    * Look for `^AFMPT x'
X    */
X   if(!strncmp(buffer, "^AFMPT", 6)) {
X       for(p=buffer+6; *p && !isdigit(*p); p++);
X       if(*p)
X           msgbody_fmpt = atoi(p);
X   }
X   
X   /*
X    * Look for `^AMSGID ...'
X    */
X   if(!strncmp(buffer, "^AMSGID: ", 9))
X       strcpy(msgbody_msgid, buffer+9);
X   
X   /*
X    * Look for `^AREPLY ...'
X    */
X   if(!strncmp(buffer, "^AREPLY: ", 9))
X       strcpy(msgbody_reply, buffer+9);
X
}
X
X
X
/*
X * check_rfc_header() --- Check for RFC822 like header lines at
X *                        the beginning of the FIDO message body
X */
X
int check_rfc_header(buffer)
char *buffer;
{
X   if(!strnicmp(buffer, "From: ", 6)) {
X       msgbody_rfc_from = strsaveline(buffer + 6);
/*      compress_spaces(msgbody_rfc_from); */
X       return(1);
X   }
X   if(!strnicmp(buffer, "Reply-To: ", 10)) {
X       msgbody_rfc_from = strsaveline(buffer + 10);
X       compress_spaces(msgbody_rfc_from);
X       return(1);
X   }
X   if(!strncmp(buffer, "UUCPFROM:", 9)) {
X       msgbody_rfc_from = strsaveline(buffer + 9);
/*      compress_spaces(msgbody_rfc_from); */
X       return(1);
X   }
X   if(!strnicmp(buffer, "To: ", 4)) {
X       msgbody_rfc_to = strsaveline(buffer + 4);
X       return(1);
X   }
X   if(!strnicmp(buffer, "To:", 3)) {
X       msgbody_rfc_to = strsaveline(buffer + 3);
X       return(1);
X   }
X   return(0);
}
X
X
X
/*
X * compress_spaces()  ---  Strip spaces out of string
X */
X
compress_spaces(string)
char *string;
{
char *p;
X
X   for(p=string; *p; p++)
X       if(*p != ' ')
X           *string++ = *p;
X   *string = 0;
}
X
X
X
/*
X * Generate RFC822 message-id from FIDO ^AMSGID/REPLY kludge
X */
X
int generate_msgid(fp, header, msgid)
FILE *fp;
char *header, *msgid;
{
char *system;
char *id;
Node idnode;
long idnumber, xtol();
X
X   /*
X    * Seperate system string
X    */
X   for(system=msgid; *msgid && !isspace(*msgid); msgid++);
X   if(!*msgid)
X       return(-1);
X   *msgid++ = 0;
X   /*
X    * Seperate id string
X    */
X   for(; *msgid && isspace(*msgid); msgid++);
X   if(!*msgid)
X       return(-1);
X   for(id=msgid; *msgid && isxdigit(*msgid); msgid++);
X   *msgid = 0;
X
X   /*
X    * Try to interprete system as FIDO node
X    */
X   if(!parsefnetaddress(system, &idnode))
X       system = internode(idnode);
X   else
X       stripbad(system);
X   /*
X    * Convert hex id to decimal
X    */
X   idnumber = xtol(id);
X   
X   /*
X    * Output RFC822 style message id
X    */
X   fprintf(fp, "%s <%lu@%s>\n", header, idnumber, system);
X   
X   return(0);
}
X
X
X
/*
X * Replacement for fgets(3S) to understand cr's generated by Fido
X * and 'soft' cr's generated by SEAdog.
X * ie. 0 is got from file.
X */
X
static char *
ffgets(buffer, maxlen, fp)
char *buffer;
int maxlen;
FILE *fp;
{
X   register int c, ch, index;
X   register char *cp;
X   static char wordsave[BUFSIZ];
X
X   /* TRUE if last line was origin line without valid node */
X   static int last_line_was_origin = FALSE;
X
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       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       /* Hard carriage return */
X
X       if (c == '\r')
X           c = '\n';
X       else if (c == '\n' || c == 0x8d) {
X           /* Forget about these ! */
X           continue;
X       }
X       else if(c < ' ') {
X           /*
X            * Substitute control chars with '^X'
X            */
X           if(c != '\t') {
X               *cp++ = '^';
X               c = c + '@';
X           }
X       }
X       else if(c & 0x80) {
X           /*
X            * Convert IBM umlaut chars and others above 0x80
X            */
X           switch(c) {
X               case 132:
X                   *cp++ = 'a';    c = 'e';    break;
X               case 148:
X                   *cp++ = 'o';    c = 'e';    break;
X               case 129:
X                   *cp++ = 'u';    c = 'e';    break;
X               case 142:
X                   *cp++ = 'A';    c = 'e';    break;
X               case 153:
X                   *cp++ = 'O';    c = 'e';    break;
X               case 154:
X                   *cp++ = 'U';    c = 'e';    break;
X               case 225:  case 158:
X                   *cp++ = 's';    c = 's';    break;
X               default:
X                   c = '*';                    break;
X           }
X       }
X
X       /*
X        * If last character caused line wrap, and we now got another linefeed,
X        * skip this linefeed to avoid unneeded empty lines.
X        */
X       if (last_char_wrapped) {
X           if (c == '\n') {
X               last_char_wrapped = FALSE;
X               continue;
X           }
X
X           if (isspace(c) && strempty(buffer))
X               continue;
X       }
X
X       *cp++ = c;
X
X       if (c == '\n')
X           break;
X
X       *cp = 0;
X
X       /*
X        * Try to wrap if line is too long and it is not a seen-by, origin or
X        * path line.
X        */
X       if (strlen(buffer) >= MAX_LINELEN &&
X           strncmp(" * Origin:", buffer, 10) &&
X           strncmp("SEEN-BY:", buffer, 8) &&
X           strncmp("^A", buffer, 2) &&
X           strncmp("FSC-Control:", buffer, 12)) {      /* - 1 for \n */
X           last_char_wrapped = TRUE;
X
X           /* Search for place to cut */
X           for (index = strlen(buffer) - 1; index >= 0; index--) {
X               c = buffer[index];
X               if (index <= MAX_LINELEN / 3) {
X                   /* Too long, cut. */
X                   *cp++ = c = '\n';
X                   goto collected;
X               }
X
X               if (isspace(c)) {
X                   /* Wrap here! */
X                   cp = buffer + index + 1;    /* Punctuation left on this
X                                                * 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
X  out:
X   return ((!c || c == EOF) && cp == buffer) ? (char *) 0 : buffer;
}
X
X
X
/*
X * Read FIDO message header from input file
X */
X
read_header(fp)
FILE *fp;
{
X   header.orig_node = read_int(fp);
X   header.dest_node = read_int(fp);
X   header.year      = read_int(fp);
X   header.month     = read_int(fp);
X   header.day       = read_int(fp);
X   header.hour      = read_int(fp);
X   header.minute    = read_int(fp);
X   header.second    = read_int(fp);
X   header.rate      = read_int(fp);
X   header.ver       = read_int(fp);
X   header.orig_net  = read_int(fp);
X   header.dest_net  = read_int(fp);
X   header.product   = getc(fp);
X   header.x1        = getc(fp);
X   fread(header.pwd_kludge, 8, 1, fp);
X   header.orig_zone = read_int(fp);
X   if (header.orig_zone == 0)
X       header.orig_zone = MY_ZONE;
X   header.dest_zone = read_int(fp);
X   if (header.dest_zone == 0)
X       header.dest_zone = MY_ZONE;
X   fread(header.B_fill2, 16, 1, fp);
X   fread((char *) &header.B_fill3, 4, 1, fp);
X   return(ferror(fp) || feof(fp));
}
X
X
X
/*
X * Read 16-bit integer in 80x86 format, i.e. low byte first,
X * then high byte. Machine independent function.
X */
X
int read_int(fp)
FILE *fp;
{
register int c;
register int val;
X
X   if((c = getc(fp)) == EOF) {
X       log("$Can't read file (EOF)");
X       return(0);
X   }
X   val  = c;
X   if((c = getc(fp)) == EOF) {
X       log("$Can't read file (EOF)");
X       return(0);
X   }
X   val |= c << 8;
X   return(val);
}
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
void
get_string(buffer, fp, nbytes)
char *buffer;
FILE *fp;
int nbytes;
{
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       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'))
X       *p = 0;
X
X   debug(8, "Getstring at %ld %s", ftell(fp), buffer);
}
X
X
X
/*
X * Like strtok but returns empty string instead of null if no more thigns
X * found
X */
X
char *
estrtok(s, sep)
char *s, *sep;
{
X   char *p;
X
X   if (p = strtok(s, sep))
X       return p;
X   return "";
}
X
X
X
/*
X * Get matching newsgroup for FIDO area from `AREAS' file
X */
X
char *get_ng(config, echo, distrib)
FILE *config;
char *echo, *distrib;
{
static char conv[BUFLEN];
char *gr, *flag;
X
X   debug(2, "Checking echolist '%s'", echo);
X
X   while (getcl(conv, BUFLEN, config)) {
X       debug(3, "Config line '%s'", conv);
X
X       gr = estrtok(conv, SEPARATORS);
X       if (!strcmp(gr, echo)) {
X           /* Matched, take distribution and return newsgroup */
X
X           gr = estrtok(NULL, SEPARATORS);
X           strcpy(distrib, estrtok(NULL, SEPARATORS));
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
/*
X * Test for news article by reading first line. If it starts with `AREA:'
X * this is FIDO EchoMail. Returns matching newsgroup name or NULL.
X */
X
char *news_msg(fp)
FILE *fp;
{
FILE *config;
long offset = ftell(fp);
char *cp;
static char area[128];
X
X   if (ffgets(area, 128, fp) && !strncmp(area, "AREA:", 5)) {
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, "Areas", "r")) == NULL) {
X           log("$Unable to open areas 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       /* this is not echomail message, seek back */
X       (void) fseek(fp, offset, 0);
X       return (char *) 0;
X   }
X   /* NOTREACHED */
}
X
X
X
/*
X * Return date of message in UNIX format (secs after the epoche)
X *
X * Understood formats: see getdate.y
X */
X
time_t lgetdate(packet)
FILE *packet;
{
char buffer[20];
int c;
time_t timevar;
int n;
X
X   /*
X    * Some FIDO message packers do it wrong! Regarding FTS-0001, the
X    * message date is a fixed 20 bytes long string. But some programs,
X    * e.g. QMail under certain circumstances, produce 19 bytes dates.
X    */
X   /* read date from packet */
X   for (n = 0; n < 20; n++)
X       if ((buffer[n] = getc(packet)) == 0)
X           break;
X   buffer[19] = 0;
X
X   debug(8, "Getdate %s at %ld", buffer, ftell(packet));
X
X   /* try to get date */
X   timevar = getdate(buffer, NULL);
X   return timevar;
}
X
X
X
/* Search alias name which matches with fidonet name, return NULL
X   if no alias specified. */
X
char *
get_alias(name)
char *name;
{
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       while (fgets(buffer, BUFSIZ, fp)) {
X           buffer[strlen(buffer) - 1] = 0;
X           if (*buffer != '#') {
X               if ((cp = strchr(buffer, ' ')) ?
X                   cp : (cp = strchr(buffer, '\t'))) {
X                   *cp = 0;    /* Break unixname out */
X                   strcpy(unixname, buffer);   /* And save it */
X                   debug(8, "Unix name %s", unixname);
X               }
X               else {
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))
X                   cp++;
X               if (!*cp) {
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                   fclose(fp);
X
X                   /* There may be node specs after name, null them out */
X                   if (cp = strchr(unixname, ','))
X                       *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
/*
X * Strip bad chars from FIDO names, i.e. chars not allowed in
X * RFC822 `atoms'. One execption is ' ' (space), which is converted
X * to '_' later on.
X */
X
stripbad(name)
char *name;
{
char *d;
X
X   for(d=name; *d; d++)
X       if( !iscntrl(*d) && !strchr("()<>@,;:\\\"[]", *d) )
X           *name++ = *d;
X   *name = 0;
}
X
X
X
/*
X * Capitalize string
X */
X
char *strcap(s)
char *s;
{
X   if(!s || !*s)
X       return(s);
X   if(islower(*s))
X       *s = toupper(*s);
X   for(s++; *s; s++)
X       if(isupper(*s))
X           *s = tolower(*s);
X   return(s);
}
X
X
X
/*
X * Convert name in FIDO from or to field to real name
X * for use in () in RFC822 header. Everything after
X * `%' or `Of' is thrown away. The result is capitalized.
X */
X
realname_convert(name, realname)
char *name;
char *realname;
{
char *p, *string;
int cat_flag = 0;
X
X   string = strsave(name);
X   *realname = 0;
X   for(p=strtok(string, " \t"); p; p=strtok(NULL, " \t")) {
X       if(!strcmp(p, "%") || !strcmp(p, "of") || !strcmp(p, "Of") ||
X          !strcmp(p, "-")                                              )
X           break;
X       strcap(p);
X       if(cat_flag)
X           strcat(realname, " ");
X       strcat(realname, p);
X       cat_flag = 1;
X   }
X   free(string);
}
X
X
X
/*
X * Unpack FIDO mail packet. Each message header und some information in
X * the message body (^A kludges and some RFC like lines) ist converted
X * to an RFC compatible message header. If the first line of the message
X * starts with `AREA:' then message is processed as a news article, else
X * as a mail message.
X */
X
void unpack(packet, packetnode)
FILE *packet;
Node packetnode;
{
/*
X * Variables for header info
X */
Node msg_orignode, msg_destnode;        /* Origin/destination address */
int msg_attributes;                     /* Attributes of message (priv. etc.) */
time_t msg_date;                        /* Date of message */
char msg_to[36];                        /* To name */
char msg_from[36];                      /* From name */
char msg_subject[72];                   /* Subject */
/*
X * Variables for info from FIDO kludges
X */
Node origin_node;                       /* Address in origin line */
char origin_text[128];                  /* Organization taken from * Origin */
X
int lines;                              /* Lines in message body */
X
char *area;                             /* Area / newsgroup (NULL = mail) */
char *p;
int count, messagetype;
char realto[40], realfrom[40];
char searchto[36];
char buffer[BUFSIZ];
int c;
Node *entry, mynode;
Node node;
char hostname[10];
long msgid;                             /* Msg id from sequence file */
char out_name[128];                     /* Name for output msg */
char mail_to[128];                      /* Addressee of mail */
FILE *outtmp, *out;
int rfc_header_flag;
X
X
#ifdef NODELIST_SUPPORT
X   /* get node-entry fo packet-sender */
X   if (node_entry(packetnode) == NULL) {
X       log("Unknown packet sender: %s", ascnode(packetnode));
X       return;
X   }
#endif
X
X   mynode.zone = MY_ZONE;
X   mynode.net = MY_NET;
X   mynode.node = MY_NODE;
X   mynode.point = MY_POINT;
X   strcpy(mynode.name, MY_NAME);
X
#ifdef NODELIST_SUPPORT
X   if ((entry = node_entry(mynode)) == NULL) {
X       log("Unable to find this net/node from nodelist");
X       return;
X   }
#else
X   entry = &mynode;
#endif
X
X   /* get our uucp-nodename */
X   if (gethostname(hostname, 10) == -1) {
X       log("Unable to get hostname");
X       return;
X   }
X
X   while ((messagetype = read_int(packet)) == MSGTYPE) {
X       /*
X        * Clear stuff set by parsing message body
X        */
X       origin_node.zone = -1;
X       origin_text[0] = 0;
X       msgbody_fmpt = 0;
X       msgbody_msgid[0] = 0;
X       msgbody_reply[0] = 0;
X       msgbody_intl[0]  = 0;
X       if(msgbody_rfc_from) {
X           free(msgbody_rfc_from);
X           msgbody_rfc_from = NULL;
X       }
X       if(msgbody_rfc_to) {
X           free(msgbody_rfc_to);
X           msgbody_rfc_to = NULL;
X       }
X       lines = 1;
X
X       /*
X        * Initialize some stuff
X        */
X       msg_orignode.zone  = packetnode.zone;
X       msg_orignode.point = packetnode.point;
X       msg_destnode.zone  = packetnode.zone;
X       msg_destnode.point = packetnode.point;
X
X       /*
X        * Read FIDO message header and save information
X        */
X       /***** Origin/destination node *****/
X       msg_orignode.node = read_int(packet);
X       msg_destnode.node = read_int(packet);
X       /***** Origin/destination net *****/
X       msg_orignode.net = read_int(packet);
X       msg_destnode.net = read_int(packet);
X       debug(2, "Origin:      %s", ascnode(msg_orignode));
X       debug(2, "Destination: %s", ascnode(msg_destnode));
X       /***** Message attributes *****/
X       msg_attributes = read_int(packet);
X       /***** Cost (thrown away) *****/
X       read_int(packet);
X       /***** Date *****/
X       msg_date = lgetdate(packet);
X       /***** To name *****/
X       get_string(msg_to, packet, 35);
X       debug(2, "To:      %s", msg_to);
X       /***** From name *****/
X       get_string(msg_from, packet, 35);
X       debug(2, "From:    %s", msg_from);
X       /***** Subject *****/
X       get_string(msg_subject, packet, 71);
X       /* Remove trailing blanks */
X       for(count = strlen(msg_subject) - 1;
X           count >= 0 && isspace(msg_subject[count]);
X           count--                                 )
X           msg_subject[count] = 0;
X       if (!*msg_subject)
X           strcpy(msg_subject, "(no subject)");
X       debug(2, "Subject: %s", msg_subject);
X
X       /*
X        * Check that message is addressed to this node
X        */
X       if(!samenode(msg_destnode, mynode)) {
X           log("Msg from %s to %s at %s: wrong node address",
X               msg_from, msg_to, ascnode(msg_destnode));
X           goto error;
X       }
X       
X       /*
X        * Strip bad chars from header fields
X        */
X       stripbad(msg_from);
X       stripbad(msg_to);
X       strcpy(searchto, msg_to);
X       /*
X        * Convert to real names for use in ()
X        */
X       realname_convert(msg_from, realfrom);
X       realname_convert(msg_to, realto);
X       /*
X        * We don't want `UUCP' and `GATEWAY' as names
X        */
X       if(!stricmp(realto, "UUCP"))
X           *realto = 0;
X       if(!stricmp(realto, "GATEWAY"))
X           *realto = 0;
X       /*
X        * Convert spaces to `_'
X        */
X       for(p=msg_from; *p; p++)
X           *p = *p==' ' ? '_' : *p;
X       for(p=msg_to; *p; p++)
X           *p = *p==' ' ? '_' : *p;
X
X       /*
X        * Get next message-id
X        */
X       msgid = sequencer(IDSEQUENCE);
X
X       /*
X        * Check for mail or news.
X        * Read first line from FIDO message body. If it starts with
X        * `AREA:...' then it is news, else it is mail. area is newsgroup
X        * name or NULL for mail.
X        */
X       if(area = news_msg(packet)) {
X           /*
X            * This is a news article.
X            */
X           debug(1, "Message is news-article");
X           sprintf(out_name, "%s/unpacked/N.%ld", SPOOL, msgid);
X       }
X       else {
X           /*
X            * This is personal mail
X            */
X           debug(1, "Message is for mail");
X           sprintf(out_name, "%s/unpacked/M.%ld", SPOOL, msgid);
X
X           debug(8, "Searching alias for %s", searchto);
X           if (p = get_alias(searchto)) {
X               (void) strcpy(buffer, p);
X               debug(8, "Got alias %s", buffer);
X           }
X           else {
X               (void) strcpy(buffer, msg_to);
X               debug(8, "No alias, using %s", buffer);
X           }
X
X           for(p=buffer; *p; p++)
X               *p = isupper(*p) ? tolower(*p) : *p;
X
X           strcpy(mail_to, buffer);
X
X           area = NULL;
X       }
X
X       /*
X        * Create files
X        */
X       outtmp = tmpfile();
X       if(!outtmp) {
X           log("$Can't create temp file");
X           goto error;
X       }
X       out = fopen(out_name, "w");
X       if(!out) {
X           log("$Can't create output file %s", out_name);
X           fclose(outtmp);
X           goto error;
X       }
X
X       /*
X        * Read entire FIDO message body and write it
X        * to temporary file for later use. (Must read
X        * message body first `cause we need some information
X        * from it.)
X        * Some special treatment for origin line and ^A
X        * kludges is done.
X        */
X       rfc_header_flag = 1;
X       while(ffgets(buffer, BUFSIZ, packet)) {
X           if(!strncmp(buffer, "^A", 2)) {
X               check_ctrl_a(buffer);
X               continue;
X           }
X           else if(rfc_header_flag) {
X               if(rfc_header_flag = check_rfc_header(buffer))
X                   continue;
X           }
X           if(!strncmp(" * Origin:", buffer, 10)) {
X               check_origin(buffer, &origin_node, origin_text);
X               continue;
X           }
X           if(!strncmp("SEEN-BY:", buffer, 8) ||
X              !strncmp("--- ", buffer, 4)          )
X               continue;
X           lines++;
X           fputs(buffer, outtmp);
X       }
X
X       /*
X        * Construct real from address, using information from
X        * header, origin line and ^A kludges.
X        */
X       if(area && origin_node.zone != -1) {
X           debug(1, "Using address from ` * Origin: ...'");
X           msg_orignode = origin_node;
X       }
X       else {
X           debug(1, "Using address in message header");
X       }
X       if(*msgbody_intl) {
X           p = strtok(msgbody_intl, " \n");        /* Destination */
X           p = strtok(NULL        , " \n");        /* Source */
X           if(p)
X               if(!parsefnetaddress(p, &node)) {
X                   debug(1, "Using address from ^AINTL");
X                   msg_orignode = node;
X               }
X       }
X       if(msgbody_fmpt) {
X           debug(1, "Point address %d", msgbody_fmpt);
X           msg_orignode.point = msgbody_fmpt;
X       }
X       debug(1, "New from address %s", ascnode(msg_orignode));
X
X       /*
X        * Check mail messages' user name
X        */
X       if(!area && !msgbody_rfc_to) {
X           /*
X            * If mail_to is a local user name, check for existence.
X            * If user does not exist, bounce message.
X            */
X           struct passwd *pw, *getpwnam();
X           
X           pw = getpwnam(mail_to);
X           if(!pw) {                           /* Doesn't exist -> bounce */
X               fprintf(out, "From funpack %s\n",
X                             date("%a %h %d %T 19%y", NULL) );
X               fprintf(out, "Date: %s\n", date("%a, %d %h %y %T %o", NULL));
X               fprintf(out, "From: Gateway to FIDONet <%s@%s>\n",
X                            "rfmail", internode(this)              );
X               fprintf(out, "Subject: Returned mail: user unknown\n");
X               fprintf(out, "Message-Id: <%s.AA%05d@%s>\n",
X                            date("%y%m%q%H%M", (long *) 0), getpid(),
X                            internode(this)                            );
X               fprintf(out, "To: %s@%s\n", msg_from, internode(msg_orignode));
X               fprintf(out, "\n  ----- Transcript of session follows -----\n");
X               fprintf(out, "User %s is unknown at %s%s.\n\n",
X                            mail_to, MY_HOSTNAME, MY_DOMAIN    );
X               fprintf(out, "If you want to send mail to a user on the Internet/Usenet/DNet/EUNET\n");
X               fprintf(out, "via this gateway, your message must start with a `To:' line containing\n");
X               fprintf(out, "the address, e.g.\n");
X               fprintf(out, "    To: mj@dfv.rwth-aachen.de\n\n");
X               fprintf(out, "  ----- Unsent message follows -----\n");
X
X               log("Mail from %s at %s to %s bounced", msg_from,
X                   ascnode(msg_orignode), mail_to                  );
X
X               goto output_msgbody;
X           }
X       }
X           
X       /*
X        * Output `From user ...' for mail
X        */
X       if(!area) {
X           fprintf(out, "From %s %s remote from %s\n",
X                       msg_from,  date("%a %h %d %T 19%y", NULL),
X                       internode(msg_orignode)                     );
X           /* Log it */
X           log("%s @ %s -> %s", msg_from, ascnode(msg_orignode),
X               msgbody_rfc_to ? msgbody_rfc_to : mail_to                   );
X       }
X
X       /*
X        * Output RFC822 header for mail/news
X        */
X       if(area) 
X           fprintf(out, "Path: %s!%s!%s\n",
X                        internode(this), internode(msg_orignode), msg_from);
X       else {
X           fprintf(out, "Received: by %s (%s)\n",
X                   internode(*entry), PROGRAMNAME);
X           fprintf(out, "\tid AA%05d; %s\n",
X                   getpid(), date("%a, %d %h %y %T %o (%z)", (long *) 0));
X       }
X       fprintf(out, "Date: %s\n", date("%a, %d %h %y %T %o", &msg_date));
X       if(msgbody_rfc_from) {
X           if(strchr(msgbody_rfc_from, '('))
X               fprintf(out, "From: %s\n", msgbody_rfc_from);
X           else
X               fprintf(out, "From: %s (%s)\n", msgbody_rfc_from, realfrom);
X       }
X       else
X           fprintf(out, "From: %s@%s (%s)\n", msg_from, internode(msg_orignode),
X                   realfrom);
#ifdef LOCAL_REPLY_TO
X       fprintf(out, "Reply-To: %s%%%s@%s%s (%s)\n", msg_from,
X               internodex(msg_orignode), MY_HOSTNAME, MY_DOMAIN, realfrom );
#endif
X       fprintf(out, "Subject: %s\n", msg_subject);
X       if(!*msgbody_msgid ||
X          generate_msgid(out, "Message-ID:", msgbody_msgid) )
X           fprintf(out, "Message-ID: <funpack%lu@%s>\n", msgid, internode(*entry));
X       if(*msgbody_reply)
X           generate_msgid(out, "References:", msgbody_reply);
X
X       if (area) {
X           /*
X            * News special
X            */
X           fprintf(out, "Newsgroups: %s\n", area);
X           if(*distribution)
X               fprintf(out, "Distribution: %s\n", distribution);
X           /***** This is a *USER DEFINED* header, not in RFC822 !!! *****/
X           fprintf(out, "Comment-To: %s@%s (%s)\n", msg_to,
X                   internode(msg_destnode), realto);
X       }
X       else {
X           /*
X            * Mail special
X            */
X           if(msgbody_rfc_to) {
X               if(strchr(msgbody_rfc_to, '(') || !*realto)
X                   fprintf(out, "To: %s\n", msgbody_rfc_to);
X               else
X                   fprintf(out, "To: %s (%s)\n", msgbody_rfc_to, realto);
X               /* Bounced messages will be sent to ... */
#ifdef ERRORS_TO
X               fprintf(out, "Errors-To: %s\n", ERRORS_TO);
#endif
X           }
X           else
X               fprintf(out, "To: %s\n", mail_to);
X       }
X       /*
X        * Some more headers ...
X        */
X       if(*origin_text)
X           fprintf(out, "Organization: %s\n", origin_text);
X       fprintf(out, "Lines: %d\n", lines);
X
X       /*
X        * Append message body in temporary file to message
X        */
X       fprintf(out, "\n");
X
output_msgbody:     
X       rewind(outtmp);
X       while(fgets(buffer, BUFSIZ, outtmp))
X           fputs(buffer, out);
X
X       /*
X        * Dome with this message.
X        */
X       fclose(outtmp);
X       fclose(out);
X       debug(1, "Done with message");
X       continue;
X
X       /*
X        * In case of error skip text of message
X        */
error:
X       while((c = getc(packet)) && c != EOF);
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
main(argc, argv)
int argc;
char *argv[];
{
struct dirent *dir;
DIR *dp;
int c;
FILE *packet;
char files[BUFLEN];
Node node;
bool nocheck = FALSE;
char *error, *p;
Node packetnode;
X
X   node.zone = -1;
X   while ((c = getopt(argc, argv, "if:vV:")) != EOF)
X       switch (c) {
X           case 'i':
X               nocheck = TRUE;
X               break;
X           case 'v':
X               verbose++;
X               break;
X           case 'V':
X               verbose = atoi(optarg);
X               break;
X           case 'f':
X               if (parsefnetaddress(optarg, &node))
X                   exit(1);
X               break;
X           default:
X               fprintf(stderr, "%s\n\n", PROGRAMNAME);
X               fprintf(stderr, "usage: funpack [-iv] [-V verbose_level] [-f Z:N/F.P]\n\n");
X               exit(EX_USAGE);
X               break;
X       }
X
X   this.zone  = MY_ZONE;
X   this.net   = MY_NET;
X   this.node  = MY_NODE;
X   this.point = MY_POINT;
X   strcpy(this.name, MY_NAME);
X
X   /* create name for unpacking */
X   if (node.zone == -1)
X       (void) strcpy(files, "");
X   else {
X       sprintipacketname(files, node);
X       /* Cut sequence number off */
X       if (p = strrchr(files, "."))
X           *p = 0;
X   }
X
X   debug(2, "Unpacking packets beginning with %s", files);
X
X   /* try to update nodelist-index */
#ifdef NODELIST_SUPPORT
X   if (error = update_index()) {
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   }
#endif
X   if (chdir(sprintfs("%s/in", SPOOL)) == -1) {
X       log("$Cannot chdir to %s/in", SPOOL);
X       exit(EX_OSERR);
X   };
X   if (dp = opendir(".")) {
X       while (dir = readdir(dp))
X           if (!strncmp(dir->d_name, files, strlen(files)) && *dir->d_name != '.') {
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                   if (read_header(packet)) {
X                       if (feof(packet))
X                           log("Missing packet header");
X                       else
X                           log("$Error reading header");
X                   }
X                   else {
X                       packetnode.zone  = header.orig_zone;
X                       packetnode.net   = header.orig_net;
X                       packetnode.node  = header.orig_node;
X                       packetnode.point = 0;
X                       debug(1, "Packet from %s", ascnode(packetnode));
X                       debug(1, "Time %02d:%02d:%02d %d.%d.%d",
X                               header.hour, header.minute, header.second,
X                               header.day, header.month+1, header.year   );
X                       debug(1, "Max baud rate %d, version %d, product %d, x %d",
X                               header.rate, header.ver, header.product, header.x1);
X                       debug(1, "Pwd \"%s\"", header.pwd_kludge);
X                   }
X
X                   if (nocheck || ((header.dest_zone == MY_ZONE ||
X                                    header.dest_zone == 0) &&
X                                   header.dest_node == MY_NODE &&
X                                   header.dest_net == MY_NET))
X                       unpack(packet, packetnode);
X                   else
X                       log("Packet is to %d:%d/%d",
X                           header.dest_zone,
X                           header.dest_net,
X                           header.dest_node);
X                   (void) fclose(packet);
X
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       log("$Unable to open spool directory");
X       exit(EX_OSERR);
X   }
X   exit(EX_OK);
X   /* NOTREACHED */
}
SHAR_EOF
chmod 0644 funpack.c ||
echo 'restore of funpack.c failed'
Wc_c="`wc -c < 'funpack.c'`"
test 33958 -eq "$Wc_c" ||
    echo 'funpack.c: original size 33958, current size' "$Wc_c"
fi
true || echo 'restore of nodelist.c failed'
echo End of part 5, continue with part 6
exit 0

--
 _____ _____
|     |___  |   Martin Junius     FIDO:    2:242/6.1   2:242/6.0
| | | |   | |   Republikplatz 3   DOMAIN:  mj@dfv.rwth-aachen.de
|_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931