[alt.sources] FIDOGATE Part 5/6

mj@dfv.rwth-aachen.de (Martin Junius) (01/28/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.12 91/01/26 13:27:37 mj Exp $
X *
X * Unpack fido mail packets
X *
X * $Log:	funpack.c,v $
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
X
X
#define PROGRAMNAME "funpack $Revision: 2.12 $"
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;
int acceptprivate = FALSE;
int trashprivate = FALSE;
X
/* Our net/node information */
Node this;
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(!strncmp(buffer, "From: ", 6)) {
X		msgbody_rfc_from = strsaveline(buffer + 6);
/*		compress_spaces(msgbody_rfc_from); */
X		return(1);
X	}
X	if(!strncmp(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(!strncmp(buffer, "To: ", 4)) {
X		msgbody_rfc_to = strsaveline(buffer + 4);
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
static Packet header;
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
#define NGFLAG_ACCEPT_PRIVATE 0
#define NGFLAG_COMMAND 1
#define NGFLAG_TRASH_PRIVATE 2
X
static char distribution[64];
static char *ngflags[] =
{"accept-private", "command", "trash-private", ""};
X
/* Like strtok but returns empty string instead of null if no more thigns
X   found */
X
char *
estrtok(s, sep)
char *s, *sep;
{
X	char *p;
X
X	if (p = strtok(s, sep))
X		return p;
X	return "";
}
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
/*	section(SECT_AREA_NG, config); /**/
X	trashprivate = FALSE;
X	acceptprivate = FALSE;
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			while (flag = strtok(NULL, SEPARATORS))
X				switch (listscan(ngflags, flag)) {
X					case NGFLAG_ACCEPT_PRIVATE:
X						acceptprivate = TRUE;
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
/* 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
char *
news_msg(fp)
FILE *fp;
{
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		/* 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	/* read date from packet */
X	for (n = 0; n < 20; n++)
X		if ((buffer[n] = getc(packet)) == 0)
X			break;
X
X	/*
X	 * Some message-packers do mistakes! Date should be 20 bytes but they start
X	 * name directly after null terminating 18-char date used in some systems.
X	 * Check if following char is null or not, and if not, put it back there as
X	 * it probably is first char in recipent name. This seems to be problem in
X	 * OMMM, but I'm not sure yet.
X	 * 
X	 * Wed Nov 16 21:11:34 1988 Seems that the bug is in fsc001, as I constantly
X	 * keep receiving messages which 19 byte date?
X	 */
X
#ifdef FSC_IS_REALLY_CORRECT
X	for (n++; n < 20; n++)
X		if (c = getc(packet)) {
X			ungetc(c, packet);
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, 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
/* 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
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		 * 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			(acceptprivate || !(msg_attributes & ATTR_PRIVATE)))
X		{
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			if (area) {
X				debug(1, "Private message for area %s", area);
X				if (trashprivate)
X					goto error;
X			}
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				if (area) {
X					log("Skipping private echo for %s", msg_to);
X					goto error;		/* If private echo message, skip */
X				}
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			log("Sending mail from %s at %s to %s", msg_from,
X				ascnode(msg_orignode), 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				rfc_header_flag = check_rfc_header(buffer);
X			}
X			if(!strncmp(" * Origin:", buffer, 10)) {
X				check_origin(buffer, &origin_node, origin_text);
X			}
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 (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		 * Output `From user ...' for mail
X		 */
X		if(!area) {
X			fprintf(out, "From %s %s remote from %s\n",
X						msg_from, 
X						date("%a %h %d %T 19%y", (long *) 0),
X						internode(packetnode)					);
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/%s)\n",
X					internode(*entry), PROGRAMNAME, entry->name);
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);
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, '('))
X					fprintf(out, "To: %s\n", msgbody_rfc_to);
X				else
X					fprintf(out, "To: %s (%s)\n", msgbody_rfc_to, realto);
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		
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 32138 -eq "$Wc_c" ||
	echo 'funpack.c: original size 32138, 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:  mju@dfv.rwth-aachen.de
|_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931