/*
 *	Client routines for Network News Tranfer Protocol ala RFC977
 *
 *	Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
 *	Permission granted for non-commercial copying and use, provided
 *	this notice is retained.
 *
 *	Changes copyright 1990 Bernie Roehl, All Rights Reserved.
 *	Permission granted for non-commercial copying and use, provided
 *	this notice is retained.
 *
 *  Revision history:
 *
 *     May 11, 1990 - br checked for invalid chars in news filenames
 *
 *     May 10, 1990 - br changed date stamp in 'From ' lines to
 *            seconds since GMT (to make parsing and expiry easier)
 *
 *     May 9, 1990 - br added locking of nntp.dat and history files,
 *            second parameter to NNTP DIR, fixed bug in updating of
 *            nntp.dat
 *
 *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
 *            server-specific newsgroups and connection windows,
 *            locking of newsgroup files using mlock() and rmlock(),
 *            date stamping of 'From ' lines, increased stack space,
 *            updating of nntp.dat only on successful sessions.
 *
 *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
 *
 */
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include <sys/timeb.h>
#include <ctype.h>
#include <string.h>  /* for strchr() */
#ifdef	__TURBOC__
#include <dir.h>
#endif
#include "global.h"
#include "timer.h"
#include "cmdparse.h"
#include "commands.h"
#include "socket.h"
#include "usock.h"
#include "netuser.h"
#include "proc.h"
#include "smtp.h"
#include "files.h"

#define NNTPMAXLEN	512

static struct nntpservers {
	struct timer nntpcli_t;
	char *name;
	char *groups;
	int lowtime, hightime;  /* for connect window */
	struct nntpservers *next;
};

#define	NULLNNTP	(struct nntpservers *)NULL

#define MAXGROUPDIRS 10

static struct grouploc {
	char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
	char *directory;     /* directory where these groups should be */
	} groupdirs[MAXGROUPDIRS] = { NULL, NULL };

struct nntpservers *Nntpservers = NULLNNTP;
static char *Nntpgroups = NULLCHAR;
static unsigned short nntptrace = 1;
static int nntpquiet = 0;
static char *News_spool = NULL;
static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */

static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";

static void nntptick __ARGS((void *tp));
static void nntp_job __ARGS((int i1,void *tp,void *v1));
static int gettxt __ARGS((int s,FILE *fp));
static int getreply __ARGS((int s));
static int getarticle __ARGS((int s,char *msgid));
static int dogroups __ARGS((int argc,char *argv[],void *p));
static int doadds __ARGS((int argc,char *argv[],void *p));
static int dodrops __ARGS((int argc,char *argv[],void *p));
static int dokicks __ARGS((int argc,char *argv[],void *p));
static int dolists __ARGS((int argc,char *argv[],void *p));
static int donntrace __ARGS((int argc,char *argv[],void *p));
static int donnquiet __ARGS((int argc,char *argv[],void *p));
static int dondir __ARGS((int argc,char *argv[],void *p));

/* Tracing levels:
	0 - no tracing
	1 - serious errors reported
	2 - transient errors reported
	3 - session progress reported
	4 - actual received articles displayed
 */

static struct cmds Nntpcmds[] = {
	"addserver",	doadds,	0,	3,
	"nntp addserver <nntpserver> <interval>",
	"directory",	dondir,	0,	0,	NULLCHAR,
	"dropserver",	dodrops,	0,	2,
	"nntp dropserver <nntpserver>",
	"groups",	dogroups,	0,	0,	NULLCHAR,
	"kick",		dokicks,	0,	2,
	"nntp kick <nntpserver>",
	"listservers",	dolists,	0,	0,	NULLCHAR,
	"quiet",	donnquiet,	0,	0,	NULLCHAR,
	"trace",	donntrace,	0,	0,	NULLCHAR,
	NULLCHAR,
};

int
donntp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(Nntpcmds,argc,argv,p);
}

static int
doadds(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct nntpservers *np;
	for(np = Nntpservers; np != NULLNNTP; np = np->next)
		if(stricmp(np->name,argv[1]) == 0)
			break;
	if (np == NULLNNTP) {
		np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
		np->name = strdup(argv[1]);
		np->next = Nntpservers;
		Nntpservers = np;
		np->groups = NULLCHAR;
		np->lowtime = np->hightime = -1;
		np->nntpcli_t.func = nntptick;	/* what to call on timeout */
		np->nntpcli_t.arg = (void *)np;
	}
	if (argc > 3) {
		int i;
		if (np->groups == NULLCHAR) {
			np->groups = mallocw(NNTPMAXLEN);
			*np->groups = '\0';
		}
		for (i = 3; i < argc; ++i) {
			if (isdigit(*argv[i])) {
				int lh, ll, hh, hl;
				sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
				np->lowtime = lh * 100 + ll;
				np->hightime = hh * 100 + hl;
			} else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
				tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
			else {  /* it's a group, and it fits... add it to list */
				if (*np->groups != '\0')
					strcat(np->groups, ",");
				strcat(np->groups, argv[i]);
			}
		}
		if (*np->groups == '\0') {	/* No groups specified? */
			free(np->groups);
			np->groups = NULLCHAR;
		}
	}
	/* set timer duration */
	set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
	start_timer(&np->nntpcli_t);		/* and fire it up */
	return 0;
}

static int
dodrops(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct nntpservers *np, *npprev = NULLNNTP;
	for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
		if(stricmp(np->name,argv[1]) == 0) {
			stop_timer(&np->nntpcli_t);
			free(np->name);
			if (np->groups)
				free(np->groups);
			if(npprev != NULLNNTP)
				npprev->next = np->next;
			else
				Nntpservers = np->next;
			free((char *)np);
			return 0;
	}
	tprintf("No such server enabled.\n");
	return 0;
}

static int
dolists(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct nntpservers *np;
	for(np = Nntpservers; np != NULLNNTP; np = np->next) {
		char tbuf[80];
		if (np->lowtime != -1 && np->hightime != -1)
			sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
		else
			tbuf[0] = '\0';
		tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
			read_timer(&np->nntpcli_t) /1000L,
			dur_timer(&np->nntpcli_t) /1000L,
			tbuf, np->groups ? np->groups : "");
	}
	return 0;
}

static int donntrace(argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setshort(&nntptrace,"NNTP tracing",argc,argv);
}
	
static int donnquiet(argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setbool(&nntpquiet,"NNTP quiet",argc,argv);
}
	
static int dondir(argc, argv, p)
int argc;
char *argv[];
void *p;
{
	if (argc < 2) {
		int i;
		tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
		tprintf("control: %s\n", Newsdir);
		for (i = 0; i < MAXGROUPDIRS; ++i)
			if (groupdirs[i].prefix)
				tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
	} else {
		char *p;
		if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
			int i;
			*p++ = '\0';
			for (i = 0; i < MAXGROUPDIRS; ++i)
				if (groupdirs[i].prefix)
					if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
						if (groupdirs[i].directory) {
							free(groupdirs[i].directory);
							groupdirs[i].directory = NULLCHAR;
							}
						if (*p == '\0') {
							free(groupdirs[i].prefix);
							groupdirs[i].prefix = NULLCHAR;
						} else
							groupdirs[i].directory = strdup(p);
						return 0;
					}
			if (*p == '\0')  /* trashing a group that's not there */
				return 0;
			for (i = 0; i < MAXGROUPDIRS; ++i){
				if (groupdirs[i].prefix == NULLCHAR) {
					groupdirs[i].prefix = strdup(argv[1]);
					if (groupdirs[i].directory) {
						free(groupdirs[i].directory);
						groupdirs[i].directory = NULL;
					}
					groupdirs[i].directory = strdup(p);
					return 0;
				}
			}
			tprintf("Directory table full\n");
		}
		else {  /* no '=', so just set default */
			if (News_spool)
				free(News_spool);
			News_spool = strdup(argv[1]);
		}
		if (argc > 2) {  /* they specified a newsdir as well */
			if (np_all)
				free(Newsdir);
			Newsdir = strdup(argv[2]);
			np_all = 1;
		}
	}
	return 0;
}
	
static int
dokicks(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct nntpservers *np;
	for(np = Nntpservers; np != NULLNNTP; np = np->next)
		if(stricmp(np->name,argv[1]) == 0) {
			/* If the timer is not running, the timeout function has
			* already been called and we don't want to call it again.
			*/
			if(run_timer(&np->nntpcli_t)) {
				stop_timer(&np->nntpcli_t);
			}
			nntptick((void *)np);
			return 0;
	}
	tprintf("No such server enabled.\n");
	return 1;
}

static int
dogroups(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int i;
	if(argc < 2) {
		if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
			tprintf("All groups are currently enabled.\n");
		else
			tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
		return 0;
	}
	if(Nntpgroups == NULLCHAR)
		Nntpgroups = mallocw(NNTPMAXLEN);
	*Nntpgroups = '\0';
	for(i=1; i < argc; ++i) {
		if(i > 1)
			strcat(Nntpgroups,",");
		strcat(Nntpgroups,argv[i]);
	}
	return 0;
}

/* This is the routine that gets called every so often to connect to
 * NNTP servers.
 */
static void
nntptick(tp)
void *tp;
{
	newproc("NNTP client", 3072, nntp_job, 0, tp, NULL,0);
}

static void
nntp_job(i1,tp,v1)
int i1;
void *tp, *v1;
{
	FILE *fp, *tmpf;
	int s = -1, i;
/*	long pos; */
	struct tm *ltm;
	time_t t;
	int now;
	struct nntpservers *np = (struct nntpservers *) tp;
	struct sockaddr_in fsocket;
	char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *lastdate = NULLCHAR;
	if (nntptrace >= 3)
		tprintf("NNTP daemon entered, target = %s\n",np->name);
	if(availmem() < Memthresh){
		if (nntptrace >= 2)
			tprintf("NNTP daemon quit -- low memory\n");
		/* Memory is tight, don't do anything */
		start_timer(&np->nntpcli_t);
		return;
	}

	time(&t);	/* more portable than gettime() */
	ltm = localtime(&t);
	now = ltm->tm_hour * 100 + ltm->tm_min;
	if (np->lowtime < np->hightime) {  /* doesn't cross midnight */
		if (now < np->lowtime || now >= np->hightime) {
			if (nntptrace >= 3)
				tprintf("NNTP window to '%s' not open\n", np->name);
			start_timer(&np->nntpcli_t);
			return;
		}
	} else {
		if (now < np->lowtime && now >= np->hightime) {
			if (nntptrace >= 3)
				tprintf("NNTP window to '%s' not open\n", np->name);
			start_timer(&np->nntpcli_t);
			return;
		}
	}

	fsocket.sin_addr.s_addr = resolve(np->name);
	if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
		if (nntptrace >= 2)
			tprintf("NNTP can't resolve host '%s'\n", np->name);
		/* Try again later */
		start_timer(&np->nntpcli_t);
		return;
	}
	fsocket.sin_family = AF_INET;
	fsocket.sin_port = IPPORT_NNTP;

	s = socket(AF_INET,SOCK_STREAM,0);
	sockmode(s,SOCK_ASCII);
	if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
		cp = sockerr(s);
		log(s,"NNTP %s Connect failed: %s",psocket(&fsocket),
			cp != NULLCHAR ? cp : "");
		if (nntptrace >= 2)
			tprintf("NNTP %s Connect failed: %s\n",psocket(&fsocket),
		cp != NULLCHAR ? cp : "");
		goto quit;
	}
	/* Eat the banner */
	i = getreply(s);
	if(i == -1 || i >= 400) {
		log(s,"NNTP %s bad reply on banner (response was %d)",psocket(&fsocket),i);
		if (nntptrace >= 1)
			tprintf("NNTP %s bad reply on banner (response was %d)\n",psocket(&fsocket),i);
		goto quit;
	}

	if (mlock(Newsdir, "nntp")) {
		if (nntptrace >= 2)
			tprintf("NNTP %s Connect failed: cannot lock nntp.dat\n", psocket(&fsocket));
		goto quit;
	}
	sprintf(buf,"%s/nntp.dat",Newsdir);
	if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
		log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket),
			buf);
		if (nntptrace >= 1)
			tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
		rmlock(Newsdir, "nntp");
		goto quit;
	}
	rewind(fp);
/*	for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
	for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
		if((cp = strchr(buf,' ')) == NULLCHAR)
			continue;	/* something wrong with this line, skip it */
		*cp = '\0';
		if(stricmp(buf,np->name) == 0) {
			rip(cp+1);
			lastdate = strdup(cp+1);
			break;
		}
	}
	fclose(fp);
	rmlock(Newsdir, "nntp");

	if(lastdate == NULLCHAR)
		lastdate = strdup("700101 000000");
	/* snapshot the time for use later in re-writing nntp.dat */
	time(&t);
	ltm = localtime(&t);
				
	/* Get a list of new message-id's */
	if (np->groups) {
		if (nntptrace >= 3)
			tprintf("==>NEWNEWS %s %s\n", np->groups, lastdate);
		usprintf(s,"NEWNEWS %s %s\n", np->groups, lastdate);
	} else {
		if (nntptrace >= 3)
			tprintf("==>NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
		usprintf(s,"NEWNEWS %s %s\n",Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
	}
	free(lastdate);
	/* Get the response */
	if((i = getreply(s)) != 230) { /* protocol error */
		log(s,"NNTP %s protocol error (response was %d)",psocket(&fsocket),i);
		if (nntptrace >= 1)
			tprintf("NNTP %s protocol error (response was %d)\n",psocket(&fsocket),i);
		goto quit;
	}
	if((tmpf = tmpfile()) == NULLFILE) {
		if (nntptrace >= 1)
			tprintf("NNTP %s Cannot open temp file\n", psocket(&fsocket));
		goto quit;
	}
	if(gettxt(s,tmpf) == -1) {
		log(s, "NNTP %s giving up: gettxt() failure",psocket(&fsocket));
		if (nntptrace >= 1)
			tprintf("NNTP %s giving up: gettxt() failure\n",psocket(&fsocket));
		fclose(tmpf);
		goto quit;
	}

	/* Open the history file */
	if (mlock(Newsdir, "history")) {
		if (nntptrace >= 1)
			tprintf("NNTP %s giving up: couldn't lock history file\n", psocket(&fsocket));
		fclose(tmpf);
		goto quit;
	}
	sprintf(buf,"%s/history",Newsdir);
	if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
		log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket), buf);
		if (nntptrace >= 1)
			tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
		fclose(tmpf);
		goto quit;
	}
	/* search through the history file for matching message id's */
	rewind(tmpf);
	while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
		i = 0;
		rewind(fp);
		while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
			if(stricmp(buf,tbuf) == 0) {
				i = 1;
				break;
			}
			pwait(NULL);
		}
		if(i == 0) {		/* not found, get the article */
			if(getarticle(s,tbuf) == -1) {
				log(s,"NNTP %s Giving up: could not get article",psocket(&fsocket));
				if (nntptrace >= 2)
					tprintf("NNTP %s Giving up: could not get article\n",psocket(&fsocket));
				fclose(fp);
				rmlock(Newsdir, "history");
				fclose(tmpf);
				goto quit;
			}
			fprintf(fp,"%s",tbuf); /* add the new message id */
		}
	}
	fclose(fp);
	rmlock(Newsdir, "history");
	fclose(tmpf);
	if (nntptrace >= 3)
		tprintf("==>QUIT\n");
	usprintf(s,"QUIT\n");
	/* Eat the response */
	getreply(s);
	/* NOW, update the nntp.dat file */
	if (mlock(Newsdir, "nntp")) {
		if (nntptrace >= 2)
			tprintf("NNTP %s Could not lock nntp.dat for update\n", psocket(&fsocket));
		goto quit;
	}
	sprintf(buf,"%s/nntp.dat",Newsdir);
	fp = fopen(buf,READ_TEXT);
	sprintf(buf, "%s/nntp.tmp",Newsdir);
	if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
		if (nntptrace >= 1)
			tprintf("NNTP %s Cannot create temp file '%s'\n", psocket(&fsocket), buf);
	if (fp == NULLFILE || tmpf == NULLFILE) {
		log(s,"NNTP %s Could not update %s", psocket(&fsocket), buf);
		if (nntptrace >= 2)
			tprintf("NNTP %s Could not update %s\n",psocket(&fsocket), buf);
		if (fp)
			fclose(fp);
		if (tmpf)
			fclose(tmpf);
		rmlock(Newsdir, "nntp");
		goto quit;
	}
	while (fgets(tbuf, sizeof(tbuf), fp))
		if (strnicmp(tbuf, np->name, strlen(np->name)))
			fputs(tbuf, tmpf);
	fprintf(tmpf,"%s %02d%02d%02d %02d%02d%02d\n",np->name,ltm->tm_year%100,ltm->tm_mon+1,
		ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
	fclose(fp);
	fclose(tmpf);
	sprintf(buf, "%s/nntp.dat", Newsdir);
	sprintf(tbuf, "%s/nntp.tmp", Newsdir);
	unlink(buf);
	rename(tbuf, buf);
	rmlock(Newsdir, "nntp");
quit:
	if (nntptrace >= 3)
		tprintf("NNTP daemon exiting\n");
	close_s(s);
	/* Restart timer */
	start_timer(&np->nntpcli_t);
	return;
}

static int
gettxt(s,fp)
int s;
FILE *fp;
{
	char buf[NNTPMAXLEN];
	int nlines;
	for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
		if (nntptrace >= 4)
			tprintf("<==%s", buf);
		if(strcmp(buf,".\n") == 0) {
			if (nntptrace >= 3)
				tprintf("NNTP received %d lines\n", nlines);
			return 0;
			}
		/* check for escaped '.' characters */
		if(strcmp(buf,"..\n") == 0)
			fputs(".\n",fp);
		else
			fputs(buf,fp);
	}
	if (nntptrace >= 1)
		tprintf("NNTP receive error after %d lines\n", nlines);
	return -1;
}

static int
getreply(s)
int s;
{
	char buf[NNTPMAXLEN];
	int response;
	while(recvline(s,buf,NNTPMAXLEN) != -1) {
		/* skip informative messages and blank lines */
		if(buf[0] == '\0' || buf[0] == '1')
			continue;
		sscanf(buf,"%d",&response);
		if (nntptrace >= 3)
			tprintf("<==%s\n", buf);
		return response;
	}
	if (nntptrace >= 3)
		tprintf("==No response\n");
	return -1;
}

static int
getarticle(s,msgid)
int s;
char *msgid;
{
	char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
	FILE *fp, *tmpf;
	int r;
	char *cp;
	extern int Smtpquiet;

	if (nntptrace >= 3)
		tprintf("==>ARTICLE %s", msgid);
	usprintf(s,"ARTICLE %s", msgid);
	r = getreply(s);
	if(r == -1 || r >= 500)
		return -1;
	if(r >= 400)
		return 0;
	if((tmpf = tmpfile()) == NULLFILE) {
		if (nntptrace >= 1)
			tprintf("NNTP Cannot open temp file for article\n");
		return -1;
	}
	if(gettxt(s,tmpf) == -1) {
		fclose(tmpf);
		return -1;
	}
	/* convert the article into mail format */
	rewind(tmpf);
	froml[0] = '\0';
	newgl[0] = '\0';
	while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
		if(strncmp(buf,"From: ",6) == 0) {
			struct timeb t;
			ftime(&t);
			rip(&buf[6]);
			sprintf(froml,"From %s %ld\n",&buf[6], t.time);
			if(newgl[0] != '\0')
				break;
		}
		if(strncmp(buf,"Newsgroups: ",12) == 0) {
			strcpy(newgl,&buf[12]);
			if(froml[0] != '\0')
				break;
		}
		/* invalid article - missing 'From:' line or 'Newsgroups:' line */
		if(strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
/*			fclose(fp); */
			fclose(tmpf);
			return 0;
		}
	}
	sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
	for(cp=newgl;;++cp) {
		if(*cp == '.') {
#ifdef __TURBOC__
			mkdir(buf); /* create a subdirectory, if necessary */
#else
			mkdir(buf,0755); /* create a subdirectory, if necessary */
#endif
			strcat(buf,"/");
			continue;
		}
		if(*cp == ',' || *cp == '\n') {
			char tempdir[80], prefix[20], *p;
			strcpy(tempdir, buf);
			if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
				*p++ = '\0';
				strcpy(prefix, p);
			}
			if (mlock(tempdir, prefix)) {
				if (nntptrace >= 2)
					tprintf("NNTP group '%s' is locked\n", buf);
				return -1;
			}
			strcat(buf,".txt");
			/* open the mail file */
			if (nntptrace >= 3)
				tprintf("Writing article to '%s'\n", buf);
			if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
				fputs(froml,fp);
				rewind(tmpf);
				while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
					/* for UNIX mail compatiblity */
					if(strncmp(buf,"From ",5) == 0)
						putc('>',fp);
					fputs(buf,fp);
				}
				putc('\n',fp);
				fclose(fp);
			}
			rmlock(tempdir, prefix);
			if (*cp == '\n') 
				break;
			else
				sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
			continue;
		}
		buf[strlen(buf)+1] = '\0';
		buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
	}
	fclose(tmpf);
	strcpy(buf,msgid);		/* Get a copy we can munge */
	rip(buf);			/* remove trailing new-line */
	rip(newgl);			/* ditto */
	if(!nntpquiet)
		tprintf("New news arrived: %s, article %s%c\n",newgl,buf,Smtpquiet?' ':'\007');
	return 0;
}
