/*
 *
 * NNTP Server/Client - See RFC977
 * Jeffrey R. Comstock. - NR0D - Bloomington, Minnesota USA
 * Copyright 1990 Jeffrey R. Comstock, All Rights Reserved.
 * Permission granted for non-commercial copying and use, provided
 * this notice is retained.
 *
 * DB3FL 9107xx: heavily rewritten and bug fixing in file-handling
 * ported to NOS by PE1NMB - 920120
 *
 * DB3FL: ihave, read & lzw ported to NOS by YC1DAV 920804
 *
 * Mods by PA0GRI
 *
 * OH2BNS 9311xx: Fixed LIST, STAT, HEAD and BODY. Implemented POST.
 *                Added 'nntp create'. Small fixes here and there...
 *
 * OH2BNS 9401xx: Rewrote STAT, HEAD, BODY, ARTICLE. Implemented
 *                XHDR, XOVER, 'nntp access'. Rewrote garbled().
 *
 * N5KNX  950626: NEWGROUPS and DATE added, from G8FSL uknos.
 *
 * G4HIP  960209: This is the JNOS 1.10l nntpserv.c transferred to
 *                TNOS.  Includes 'nntp autocreate' option.
 *
 * G4HIP  960720: rline stripper added, nntp expire added.
 * 
 * G4HIP  970826: "MODE READER" and LISTGROUP added.
 *
 * G4HIP  980824: UKIP Distributions and default newsgroup added.
 *                Enhanced 'ihave'.
 *
 * G4HIP  990311: Added K5KNX's mods to help prevent crashes when going
 *                smtp -> newsgroup.
 */
#include "global.h"
#include "ctype.h"
#include "commands.h"
#include <stdarg.h>
#ifdef MSDOS
#include <dir.h>
#else
#include <setjmp.h>
#include <time.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#ifdef NNTPS
#include "domain.h"
#include "socket.h"
#include "iface.h"
#include "smtp.h"
#include "dirutil.h"
#include "ftp.h"
#include "nntp.h"
#include "session.h"
#include "bm.h"
#ifdef LESS_WHINING
#include "pc.h"
#include "nr4.h"
#include "netrom.h"
#include "udp.h"
#include "tcp.h"
#include "ip.h"
#include "usock.h"
#endif
#ifdef LZW
#include "lzw.h"
#endif
#include "mailutil.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: nntpserv.c,v 1.35 2001/05/08 12:26:45 brian Exp $";
#endif

static FILE *open_file (const char *name, const char *mode, int s, int t);
static FILE *temp_file (int s, int t);	/* NMB */

/* static int make_dir (const char *path,int s); */
static int check_file (const char *path);
static int make_path (char *group);
static int check_blank (char *bp);
static int dup_f (FILE * in, FILE * out, struct nntpsv * mp);
static int update_list (struct nntpsv * a);
static int get_path2 (struct article * art);
static int dofwd (struct nntpsv * mp, FILE * f, FILE * history);
static int getreply (struct nntpsv * cb);
static int newnews (char *string, struct nntpsv * mp, FILE * f);
static int recv_file (FILE * fp, int s);
static int check_article (char *id);
static int xfer_article2 (FILE * f, struct nntpsv * mp);
static int retr_by_mid (char *mid, struct nntpsv * mp);
static int retr_by_num (char *buf, struct nntpsv * mp);
static int doarticlecmd (char *buf, struct nntpsv * mp, int retcode);
static int doheadcmd (char *buf, struct nntpsv * mp);
static int dobodycmd (char *buf, struct nntpsv * mp);
static int dostatcmd (char *buf, struct nntpsv * mp);
static int doxhdrcmd (char *buf, struct nntpsv * mp);
static int doxovercmd (char *buf, struct nntpsv * mp);
static int restreql (register char *w, register char *s);
static int check_spaces (register char *str, int count);
static int32 make_nntime (struct date * d, struct time * t, char *str);
static int ngmatcha (int (*func) (char *, char *), int dflt, struct g_list * ngspec, struct g_list * matchlist);
static int get_path (char *group, struct nntpsv * mp);
static int get_pointer (char *group, struct nntpsv * mp);
static FILE *open_message (struct nntpsv * mp, FILE * f);
static int get_id (char *bp, struct nntpsv * mp);
static int garbled (FILE * inf, FILE * outf);
static int donewnews (char *string, struct nntpsv * mp);
static int donewgroups (char *string, struct nntpsv * mp);
static int dogroup (struct nntpsv * mp, char *buf);
static int dolistgroup (struct nntpsv * mp, char *buf);
static int check_grp (struct nntpsv * mp);
static int get_next (struct nntpsv * mp);
static int get_last (struct nntpsv * mp);
static void poll (void *p);
static int get_article2 (struct nntpsv * mp, char *id);
static int post_article (struct nntpsv * mp);
static int donnprofile (int argc, char *argv[], void *p);
static int donnmax (int argc, char *argv[], void *p);
static int chk_access (int s);
static int donnfirstpoll (int argc, char *argv[], void *p);
static int donnp2n (int argc, char *argv[], void *p);
static int donnn2p (int argc, char *argv[], void *p);
static void make_time_string (char *string, time_t * timep);
static int look_for_header_line (char *buf, int bufsize, FILE *data, int type);
static int donnexpire (int argc,char *argv[],void *p);
static int donnexpart (int argc,char *argv[],void *p);
int donnexbids (int argc,char *argv[],void *p);
static int donnexpnow (int argc,char *argv[],void *p);
static int check_expdate (char *idate, char *wdate);
static void nndel_article (char *buf, struct nntpsv *mp, FILE *fq);
extern void updateCtl (const char *who, struct let * info);
void mail2news (FILE *data, const char *from, const char *to, const char *area);
static void news2mail (FILE *f, char *group);
int holdscan (char *buf, char *badbuf, char *badwords[]);


/***************************************************************************/

#undef CONTROL			/* reverse NNTP function (not implemented yet) */
static int Snntp = -1;		/* prototype socket for service */

#ifdef LZW
static int LzwActive = 1;
#endif

int NNpbbs2nntp = 0;
static int NNnntp2pbbs = 0;
static int AutoCreate = 0;
static int nnRlines = 1;
static char *NNdefdist = NULLCHAR;
static char *NNdefgroup = NULLCHAR;
static const char usedefaultdist[] = "bbs.www";

#ifdef UNIX
extern int Numrows;
#else
extern unsigned char SCREENlength;
#endif
extern long UtcOffset;		/*lint !e15 *  UTC - localtime, in secs */
static int16 Nntpquiet = 0;
static int16 NnIhave = 0;
static int Nntpaccess = 0;
static int Nntpfirstpoll = 5;
static int NNTPTrace = 0;
static int nnexptime_art = 28;
static int nnexptime_bid = 56;
static int Filecheck = 0;		/* flag if file system has been checked */
int NntpUsers = 0;			/* count of active connections */
static int Nntpmax = 10;

static struct post *Post = NULLPOST;
static struct Servers *Nntpserver = NULLSERVER;
static struct let lt;
static long startedat;
static char *Host = NULLCHAR;
static char NEol[] = ".\n";



/*############################ NNTP COMMANDS #################################*/

/* Command table */
static const char *commands[] =
{
	"article",
#define ARTICLE_CMD     0
	"body",
#define BODY_CMD        1
	"debug",
#define DEBUG_CMD       2
	"date",
#define DATE_CMD	3
	"group",
#define GROUP_CMD       4
	"head",
#define HEAD_CMD        5
	"help",
#define HELP_CMD        6
	"ihave",
#define IHAVE_CMD       7
	"last",
#define LAST_CMD        8
        "list extensions",
#define LIST_EXT_CMD    9
        "listgroup",
#define LISTGROUP_CMD   10
        "list",
#define LIST_CMD        11
        "mode reader",     
#define MODE_READER_CMD 12     
	"newgroups",
#define NEWGROUPS_CMD   13
	"newnews",
#define NEWNEWS_CMD     14
	"next",
#define NEXT_CMD        15
	"post",
#define POST_CMD        16
	"quit",
#define QUIT_CMD        17
	"slave",
#define SLAVE_CMD       18
	"stat",
#define STAT_CMD        19
	"xhdr",
#define XHDR_CMD        20
	"xinfo",
#define XINFO_CMD       21
	"xlzw",
#define XLZW_CMD        22
	"xover",
#define XOVER_CMD       23
	NULLCHAR
};



#ifdef CATALOG
#include "catalog.h"

#define CAT nntpserv_catalog

#define quitcmd		__STR(0)
#define xinfo		__STR(1)
#define debugstr	__STR(2)
#define nnversion	__STR(3)
#define slavestr	__STR(4)
#define closing		__STR(5)
#define listarticle	__STR(6)
#define listgroups	__STR(7)
#define newgroups	__STR(8)
#define retrieve	__STR(9)
#define nntphead	__STR(10)
#define xheader1	__STR(11)
#define xheader2	__STR(12)
#define body		__STR(13)
#define statistics	__STR(14)
#define sepcmd		__STR(15)
#define xover		__STR(16)
#define newnews_t	__STR(17)
#define transok		__STR(18)
#define postok		__STR(19)
#define sendart		__STR(20)
#define sendpost	__STR(21)
#define nogroup		__STR(22)
#define noselect	__STR(23)
#define nonext		__STR(24)
#define noprev		__STR(25)
#define noart		__STR(26)
#define notwanted	__STR(27)
#define transnotok	__STR(28)
#define notallowed	__STR(29)
#define postfailed	__STR(30)
#define notrecognd	__STR(31)
#define badsyntax	__STR(32)
#define noaccess	__STR(33)
#define errorstr	__STR(34)
#define fatal		__STR(35)
#define listofarticles	__STR(36)
#define readergreet	__STR(37)
#define listex		__STR(38)

#else /* CATALOG */

static char quitcmd[] = "QUIT\n";
static char xinfo[] = "100 No info available\n";
static char debugstr[] = "100 Debug %s\n";
static char nnversion[] = "20%s %s NNTP version %s ready at %s %s\n";
static char slavestr[] = "202 Slave %s\n";
static char closing[] = "205 Closing\n";
static char listarticle[] = "211 %u %u %u%s\n";
static char listgroups[] = "215 List of newsgroups follows\n";
static char newgroups[] = "231 List of new newsgroups follows\n";
static char retrieve[] = "220 %u%s%shead and body follow\n";
static char nntphead[] = "221 %u%s%sHead\n";
static char xheader1[] = "221 %s fields follow\n";
static char xheader2[] = "221 %u %s header of article %s\n";
static char body[] = "222 %u%s%sBody\n";
static char statistics[] = "223 %u%s%sStatistics\n";
static char sepcmd[] = "223 %u %s%srequest text separately\n";
static char xover[] = "224 data follows\n";
static char newnews_t[] = "230 New news by message id follows\n";
static char transok[] = "235 Thanks\n";
static char postok[] = "240 Article posted ok\n";
static char sendart[] = "335 Send article, end with .\n";
static char sendpost[] = "340 Send article to be posted, end with .\n";
static char nogroup[] = "411 No such newsgroup\n";
static char noselect[] = "412 No newsgroup selected\n";
static char nonext[] = "421 No next article\n";
static char noprev[] = "422 No previous article\n";
static char noart[] = "430 No such article\n";
static char notwanted[] = "435 Article not wanted - do not send it\n";
static char transnotok[] = "437 Article rejected - header garbled - do not try again\n";
static char notallowed[] = "440 Posting not allowed\n";
static char postfailed[] = "441 Posting failed";
static char notrecognd[] = "500 Command not recognized\n";
static char badsyntax[] = "501 Syntax error\n";
static char noaccess[] = "502 I'm not allowed to talk to you\n";
static char errorstr[] = "503 Command not performed\n";
static char fatal[] = "503 Fatal error FILE %s\n";
static char listofarticles[] = "211 list of article numbers follow\n";
static char readergreet [] = "20%s Hello, you %s post\n";
static char listex [] = "205 List of extensions follows\n%s";

#endif /* CATALOG */
 
/*static char nospace[]   = "503 Not enough disk space left\n"; */
/*static char lowmem[]    = "503 System overloaded\n"; */
static char artmsg[] = " Article retrieved - ";



static void
rip2 (register char *s)
{
register char *cp;

#ifndef TNOS_68K
	if ((cp = strpbrk (s, "\r\n")) != NULLCHAR)
#else
	if ((cp = strpbrk (s, "\r\l")) != NULLCHAR)
#endif
		*cp = '\0';
}



/* main directory-creating routine
 * handles special chars in pathname - especially for MSDOS
 * returncode: -1 error; 0 success
 */
static int
make_dir (const char *path, int s)
{
#ifdef MSDOS
register char *cp;
#endif

	if (path == NULLCHAR)
		return -1;

#ifdef MSDOS
	while ((cp = strchr (path, '\\')) != NULLCHAR)
		*cp = '/';
#endif

#ifndef TNOS_68K
	if (access (path, 0)) {
#else
	if (access (path, 0x80)) {
#endif

		if (mkdir (path, 0755)) {	/* 0755 is drwxr-xr-x */
			tcmdprintf ("Can't create %s: %s\n", path, SYS_ERRLIST(errno));
			if (s)
				usprintf (s, fatal, path);
			return -1;
		}
	}
	return 0;
}



/* main message-opening routine
 * returncode: NULLFILE if error; filepointer success */

static FILE *
open_message (struct nntpsv *mp, FILE *f)
{
char line[NNLINELEN];

	/* mp already checked */

	if (f != NULLFILE)
		(void) fclose (f);

	sprintf (line, "%s/%u", mp->path, mp->pointer);

	if ((f = open_file (line, READ_TEXT, 0, 0)) == NULLFILE)
		usputs (mp->s, "430 Error opening message - no such article.\n");

	return f;
}



/* file-receiving routine
 * returncode: -1 if error or 'recvline' faults; 0 success; 1 if blank line */

static int
recv_file (FILE *fp, int s)
{
char line[NNLINELEN];
int check = 0;

	for ( ; ; ) {
		if (recvline (s, (unsigned char *) line, NNLINELEN) == -1)
			return -1;

		rip (line);

		if (strcmp (line, ".") == 0)
			return 0;

		if (!check) {	/* only enabled on first line! */
			check = 1;
			if (*line == '\0')	/* check for blank line */
				return 1;
		}
		fprintf (fp, "%s\n", line);
	}
}



/* checks incoming article-id against existing articles
 * returncode: -1 if error; 1 if article exists; 0 no article found */

/* Now also makes a crude format check - OH2BNS */

static int
check_article (char *id)
{
char *p, *p1, line[NNLINELEN];
FILE *f;

	if (id == NULLCHAR || (p = strchr (id, '<')) == NULLCHAR
	    || (f = open_file (NNTPhistory, READ_TEXT, 0, 1)) == NULLFILE)
		return -1;

	p1 = p;
	while (*p1 != '\0' && *p1 != '>' && !isspace (*p1))
		p1++;
	if (*p1 != '>' || *++p1 != '\0')
		return -1;

	for ( ; ; ) {
		if (fgets (line, NNLINELEN, f) == NULL) {
			(void) fclose (f);
			return 0;
		}

		if (!strncasecmp (line, p, strlen (p))) {
			(void) fclose (f);
			return 1;
		}
		kwait (NULL);	/* let other processes run */
	}
}



/* checks for not valid chars in a line
 * returncode: 0 if valid; 1 if invalid */
static int
check_blank (char *bp)
{
	if (strpbrk (bp, "!@#$%^&*()_+=<>,./?~`[]{}\\|0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") == NULL)
		return 1;
	return 0;
}



/* main file-checking routine
 * returncode: -1 if error; 0 success */
static int
check_file (const char *path)
{
	if (path == NULLCHAR)
		return -1;
	if (access (path, 0))
#ifdef UNIX
		return close (creat (path, 0644));	/* 0644 is -rw-r--r-- */
#else
		return close (creat (path, S_IWRITE));
#endif
	return 0;
}



/* checks if sufficient spaces exist in given string
 * returncode: -1 if error; 1 success */
static int
check_spaces (register char *str, int count)
{
register char *cp;

	if (str == NULLCHAR || (cp = strchr (str, ' ')) == NULLCHAR)
		return -1;

	for (; count > 1; count--) {
		cp++;
		if (strchr (cp, ' ') == NULLCHAR)
			return -1;
	}
	return 1;
}



/* checks the file-system used for NNTP
 * returncode: -1 if error; 0 success - and "Filecheck" is set to 1 */
static int
check_system (void)
{
FILE *f;
char line[NNLINELEN];
int err = 0;

	if (Post == NULLPOST)
		(void) donnprofile (10, NULL, NULL);

	if (Filecheck)
		return 0;

	err = make_dir (NNTPforward, 0);
	err |= check_file (NNTPpointer);
	err |= check_file (NNTPhistory);
	err |= check_file (NNTPactive);
	err |= check_file (NNTPpoll);
	err |= check_file (NNTPaccess);

	if (err)
		goto quit;

	sprintf (line, "%s/fwd.seq", NNTPdir);
	if (access (line, 0)) {
		if ((f = open_file (line, "w+", 0, 1)) == NULLFILE)
			goto quit;

		fputs ("1\n", f);
		(void) fclose (f);
	}

	sprintf (line, "%s/sequence.seq", Mailqdir);
	if (access (line, 0)) {
		if ((f = open_file (line, "w+", 0, 1)) == NULLFILE)
			goto quit;

		fputs ("1\n", f);
		(void) fclose (f);
	}

	Filecheck = 1;
	return 0;

quit:
	Filecheck = 0;
	tputs ("Error in NNTP file system\n");
	return -1;
}



/* handles the response code of an incoming msg
 * returncode: -1 error; 0 no code; value of response code on success */

static int
getreply (cb)
struct nntpsv *cb;
{
int response;

	while (recvline (cb->s, (unsigned char *) cb->buf, NNLINELEN) != -1) {
		/* skip informative messages and blank lines */
		if (cb->buf[0] == '\0' || cb->buf[0] == '1')
			continue;

		sscanf (cb->buf, "%d", &response);
		return (response < 500) ? response : -1;
	}
	return -1;
}



/* returncode: -1 error; 1 success; 0 no entry */

static int
get_path (char *group, struct nntpsv *mp)
{
FILE *f;
char line[NNLINELEN], *cp, *cp1;

	if (group == NULLCHAR || mp == NULLNNTPSV
	    || (f = open_file (NNTPpointer, READ_TEXT, mp->s, 0)) == NULLFILE)
		return -1;

	group++;
	for ( ; ; ) {
		if (fgets (line, NNLINELEN, f) == NULL)
			break;

		if (strcspn (line, " ") != strlen (group))
			continue;

		if (strnicmp (group, line, strlen (group)) == 0) {
			cp = strchr (line, ' ');
			if (!cp)
				continue;
			if ((cp1 = strchr (++cp, ' ')) != NULLCHAR)
				*cp1 = '\0';	/* drop date created */

			if (mp->path != NULLCHAR)
				free (mp->path);

			mp->path = strdup (cp);
			rip (mp->path);
			(void) fclose (f);
			return 1;
		}
	}
	(void) fclose (f);
	return 0;
}



/* checks if path to given article exists
 * returncode: -1 error; 1 success; 0 no path */

static int
get_path2 (struct article *art)
{
FILE *f;
char line[NNLINELEN], *p, *cp1;

	if (art->group == NULLCHAR
	    || (f = open_file (NNTPpointer, READ_TEXT, 0, 1)) == NULLFILE)
		return -1;

	p = art->group;
	art->path = NULLCHAR;

	for ( ; ; ) {
		if (fgets (line, NNLINELEN, f) == NULL)
			break;

		if (strcspn (line, " ") != strlen (p))
			continue;

		if (strnicmp (p, line, strlen (p)) == 0) {
			p = strchr (line, ' ');
			if (!p)
				continue;
			if ((cp1 = strchr (++p, ' ')) != NULLCHAR)
				*cp1 = '\0';	/* drop date created */

			if (art->path != NULLCHAR)
				free (art->path);

			art->path = strdup (p);
			rip (art->path);
			(void) fclose (f);
			return 1;
		}
	}
	(void) fclose (f);
	return 0;
}



/* returncode: -1 if error; 1 success; 0 no pointer */

static int
get_pointer (char *group, struct nntpsv *mp)
{
FILE *f;
char line[NNLINELEN], *p, *p2;

	if (group == NULLCHAR || mp == NULLNNTPSV
	    || (f = open_file (NNTPactive, READ_TEXT, mp->s, 0)) == NULLFILE)
		return -1;

	group++;

	for ( ; ; ) {
		if (fgets (line, NNLINELEN, f) == NULL)
			break;

		if (strcspn (line, " ") != strlen (group))
			continue;

		if (strnicmp (group, line, strlen (group)) == 0) {
			p = strchr (line, ' ');
			if (!p)
				continue;
			mp->last = (unsigned) atoi (p);
			p++;
			p2 = strchr (p, ' ');
			if (!p2)
				continue;
			mp->first = (unsigned) atoi (p2);
			mp->pointer = (mp->first > mp->last) ? 0 : mp->first;
			(void) fclose (f);
			return 1;
		}
	}
	(void) fclose (f);
	return 0;
}



/* creating path to a new newsgroup
 * handling of "." and "\" in pathnames especially MSDOS */
/* returncode: -1 error; 0 success */

static int
make_path (char *group)
{
FILE *f;
char *cp, *cp1, *cp2;
time_t currtime;
char time_string[20];
int got_it;

	if (group == NULLCHAR || (f = open_file (NNTPpointer, APPEND_TEXT, 0, 1)) == NULLFILE)
		return -1;

	got_it = 0;
	cp = mallocw (strlen (NNTPdir) + strlen (group) + 2);	/* '/' + '\0' */
	cp1 = strdup (group);

	for ( ; ; ) {
		if ((cp2 = strchr (cp1, '.')) != NULLCHAR)
			*cp2 = '\0';
		else
			got_it = 1;

		sprintf (cp, "%s/%s", NNTPdir, cp1);

		if (make_dir (cp, 0)) {
			(void) fclose (f);
			free (cp);
			free (cp1);
			return -1;
		}

		if (got_it) {
			(void) time (&currtime);
			make_time_string (time_string, &currtime);	/* uses localtime() */
			fprintf (f, "%s %s %s\n", group, cp, time_string);
			(void) fclose (f);
			free (cp);
			free (cp1);
			return 0;
		}
		if (cp2)
			*cp2 = '/';
	}
}



static int
update_list (struct nntpsv *a)
{
FILE *f, *t;
char *p, *p1, l2[NNLINELEN];

	if ((f = open_file (NNTPactive, READ_TEXT, a->s, 0)) == NULLFILE)
		return -1;

	if ((t = temp_file (0, 1)) == NULLFILE) {
		(void) fclose (f);
		return -1;
	}

	p1 = a->ap->group;
	a->ap->number = 0;

	for ( ; ; ) {
		if (fgets (a->buf, NNLINELEN, f) == NULL)
			break;

		a->ap->tmpu = strcspn (a->buf, " ");
		strncpy (l2, a->buf, a->ap->tmpu);
		l2[a->ap->tmpu] = '\0';

		p = strrchr (a->buf, ' ');
		if (!p)
			continue;
		p++;			/* the char after the last space */
		if (*p == 'n')	{	/* posting to this group not allowed */
			fputs (a->buf, t);
			continue;
		}

		if (strcmp (p1, l2) == 0) {
			p = strchr (a->buf, ' ');
			if (!p)
				continue;
			a->ap->number = (unsigned) atoi (++p);
			(a->ap->number)++;
			p = strchr (p, ' ');
			fprintf (t, "%s %5.5u%s", p1, a->ap->number, p);
		}
		else
			fputs (a->buf, t);

	}

	(void) fclose (f);
	rewind (t);

	if ((f = open_file (NNTPactive, WRITE_TEXT, a->s, 0)) == NULLFILE) {
		(void) fclose (t);
		return -1;
	}

	for ( ; ; ) {
		kwait (NULL);
		if (fgets (a->buf, NNLINELEN, t) == NULL)
			break;

		fputs (a->buf, f);
	}

	(void) fclose (t);

	/* Creating newsgroups by posting is permitted if autocreate is on */

	if ((a->ap->number == 0) && AutoCreate) {
		(void) make_path (a->ap->group);
		a->ap->number = 1;
		fprintf (f, "%s 00001 00001 y\n", a->ap->group);
	}

	(void) fclose (f);
	return (int) (a->ap->number);
}



/* returncode: -1 error; 0 success */
/* Path: bug in col 1 of article body bug fixed by G4JEC 901019....*/

static int
dup_f (FILE *in, FILE *out, struct nntpsv *mp)
{
char *p;
int blank_line_flag = 1;

	for ( ; ; ) {
		kwait (NULL);
		if (fgets (mp->buf, NNLINELEN, in) == NULL)
			return 0;

		if (blank_line_flag)
			if (strnicmp (mp->buf, Hdrs[PATH], 5) == 0) {
				p = strchr (mp->buf, ' ');
				if (p)
					fprintf (out, "%s%s!%s", Hdrs[PATH], Host, ++p);
				continue;
			}

		/* oh oh - nntpserv is modifying articles....*/

		if (strlen (mp->buf) == 1 && mp->buf[0] == '.')
			continue;

		fputs (mp->buf, out);

		/* ! removed. Now dup_f searches the whole header for "Path: ",
		 * not only the first line - OH2BNS */

		if (check_blank (mp->buf))
			blank_line_flag = 0;
	}
}



/* returncode: < 1 if error; 1 success */

static int
dofwd (struct nntpsv *mp, FILE *f, FILE *history)
{
FILE *fwd;

	if (mp == NULLNNTPSV)
		return -1;

	sprintf (mp->buf, "%s/fwd.seq", NNTPdir);

	if ((fwd = open_file (mp->buf, "r+", mp->s, 0)) == NULLFILE)
		return -1;

	(void) fgets (mp->buf, NNLINELEN, fwd);
	mp->hold_i = (unsigned ) (atoi (mp->buf) + 1);
	fprintf (history, " JUNK/%u", mp->hold_i);
	rewind (fwd);
	fprintf (fwd, "%u", mp->hold_i);
	(void) fclose (fwd);

	sprintf (mp->buf, "%s/%u", NNTPforward, mp->hold_i);

	if ((fwd = open_file (mp->buf, WRITE_TEXT, mp->s, 0)) == NULLFILE)
		return -2;

	rewind (f);
	(void) dup_f (f, fwd, mp);
	(void) fclose (fwd);
	return 1;
}



#ifdef CONTROL

static int
docontrol (FILE *f, struct nntpsv *mp)
{
struct head *h;

	if (f == NULLFILE || mp == NULLNNTPSV
	    || (h = (struct head *) callocw (1, sizeof (struct head))) == NULLHEAD)
		     return -1;

	rewind (f);
	h->subject = h->from = h->reply_to = h->id = NULLCHAR;

	for ( ; ; ) {
		kwait (NULL);
		if ((fgets (mp->buf, NNLINELEN, f)) == NULL)
			break;

		if (check_blank (mp->buf))
			break;

		rip (mp->buf);

		if (strncmp (mp->buf, Hdrs[SUBJECT], 9) == 0)
			h->subject = strdup (mp->buf);

		if (strncmp (mp->buf, Hdrs[FROM], 6) == 0)
			h->from = strdup (mp->buf);

		if (strnicmp (mp->buf, Hdrs[REPLYTO], 10) == 0)
			h->reply_to = strdup (mp->buf);

		if (strnicmp (mp->buf, Hdrs[MSGID], 12) == 0)
			h->id = strdup (strchr (mp->buf, '<'));
	}

	if (h->subject != NULLCHAR)
		if (strncmp (h->subject, "Subject: sendme ", 16) == 0)
			dosendme (h);

	if (h->subject != NULLCHAR)
		free (h->subject);

	if (h->from != NULLCHAR)
		free (h->from);

	if (h->reply_to != NULLCHAR)
		free (h->reply_to);

	if (h->id != NULLCHAR)
		free (h->id);

	free ((char *) h);
	return 0;
}
#endif



static int
xfer_article2 (FILE *f, struct nntpsv *mp)
{
char line[NNLINELEN], *p, *group = NULLCHAR, *p1, *from = NULLCHAR;
FILE *fptr, *history;
#if 0
struct tm *stm;
#endif
int x = 0, got = 0;
time_t currtime;
int retval;
#ifdef CONTROL
int control = 0;
#endif
#ifdef NNTPFILTER
char *badwords[100];
char badbuf[NNLINELEN];
int theindex = 0, k;
#endif

	if (f == NULLFILE || mp == NULLNNTPSV)
		return -1;

	while (mlock (NNTPhistory, NULLCHAR)) {
		kpause (1000L);
		if (++x == 60) {
			log (-1, "NNTP: NEWS REJECTED due to history lock");
			return -1;	/* can't lock, so reject (what else? n5knx) */
		}

	}

	/* one last time check the message-id for duplicates */
	if (check_article (mp->id) ||
	    (history = open_file (NNTPhistory, APPEND_TEXT, mp->s, 0)) == NULLFILE) {
		rmlock(NNTPhistory, NULLCHAR);
		return -1;
	}

#ifdef NNTPFILTER
	x = load_badwords (badbuf, badwords, sizeof (badbuf));
#endif
	for ( ; ; ) {		/* obtain From: and Newsgroups: fields from article */
		if (fgets (line, NNLINELEN, f) == NULL)
			break;

		rip (line);

		if (check_blank (line))  /* End of headers */
			got = TRUE;
	   
		if (!got) {

			if (strnicmp (line, Hdrs[FROM], 6) == 0) {
				p = strchr (line, ' ');
				if (!p)
					continue;
				from = strdup (++p);
#if 0
				if (group != NULLCHAR)
					break;
#endif
			} else	if (strnicmp (line, Hdrs[NEWSGROUPS], 12) == 0) {
				p = strchr (line, ' ');
				if (!p)
					continue;
				group = strdup (++p);
#if 0
				if (from != NULLCHAR)
					break;
#endif
		        }
		}
#ifdef NNTPFILTER
		if (holdscan (line, badbuf, badwords))	{
			if (group && strlen (group) > 50)
				strcpy (&group[50], "...");
			log (-1, "NNTP: Rejected mail for %s from <%s>\n",
				(group) ? group : "???", (from) ? from : "???");
			if (NNTPTrace)
				tcmdprintf ("NNTP: Rejected mail for %s from <%s>\n",
					(group) ? group : "???", (from) ? from : "???");
			retval = -1;
			goto quit1;
		}
#endif
	}

	(void) time (&currtime);
	make_time_string (line, &currtime);
	fprintf (history, "%s %s", mp->id, line);
	mp->hold_i = 0;
	p = group;

	if ((mp->ap = (struct article *) callocw (1, sizeof (struct article))) == NULLARTICLE)
		goto quit;

	x = 1;

	if (!p)	{
		rmlock(NNTPhistory, NULLCHAR);
		return -1;
	}

	while (x) {
		if ((p1 = strchr (p, ',')) != NULLCHAR) {
			p1 = line;

			while (*p != ',')
				*(p1++) = *(p++);

			*p1 = '\0';
			p++;
			mp->ap->group = strdup (line);
		} else {
			mp->ap->group = strdup (p);
			x = 0;
		}

		(void) update_list (mp);

		if (mp->ap->number > 0) {
			(void) get_path2 (mp->ap);
			mp->hold_i = 1;
			sprintf (line, "%s/%u", mp->ap->path, mp->ap->number);
			rewind (f);

			if ((fptr = open_file (line, WRITE_TEXT, mp->s, 0)) == NULLFILE) {
				free (mp->ap->group);
				goto quit;
			}

			(void) dup_f (f, fptr, mp);
			(void) fclose (fptr);
			fprintf (history, " %s/%d", mp->ap->group, mp->ap->number);

#ifdef CONTROL
			if (strcmp (mp->ap->group, "control") == 0)
				control = 1;
#endif

			free (mp->ap->path);
		}
		free (mp->ap->group);
	}

	if (mp->hold_i == 0) {
		(void) dofwd (mp, f, history);
		mp->hold_i = 0;
	}

	(void) time (&currtime);

	if (Nntpquiet < 2)
		tcmdprintf ("New mail in newsgroup %s\n  from <%s> at %s%s",
		     (mp->hold_i) ? group : "JUNK", from, ctime (&currtime),
			 (Nntpquiet < 1) ? "\007" : "");

	if (NNnntp2pbbs)
		news2mail (f, group);

#ifdef CONTROL
	if (control == 1)
		docontrol (f, mp);
#endif
quit:
	fputc ('\n', history);
	free ((char *) mp->ap);
	retval = (int) (mp->hold_i);

#ifdef NNTPFILTER
quit1:
#endif
	(void) fclose (history);
	rmlock(NNTPhistory, NULLCHAR);

#ifdef NNTPFILTER
	for (k = 0; k < theindex; k++)
		free (badwords[k]);
#endif

	free (from);
	free (group);
	return retval;
}



static void
nntppoll (int unused OPTIONAL, void *cb1, void *p OPTIONAL)
{
char *cp, line[NNLINELEN];
struct sockaddr_in fsocket;
struct nntpsv *cb;
struct tm *ltm;
FILE *f, *f1, *pf = NULLFILE;
long t;
int r, now, ret, useIhave;
struct Servers *sp = (struct Servers *) cb1;
time_t currtime;
char open_time[20];

	if (!run_timer (&sp->nntpt))
		/* if timer had stopped there is an active session */
		if (!Filecheck)
			if (check_system ())
				return;

	/* check connection window */

	(void) time (&currtime);

	ltm = localtime (&currtime);
	now = ltm->tm_hour * 100 + ltm->tm_min;

	if (sp->lowtime < sp->hightime) {
		/* doesn't cross midnight */
		if (now < sp->lowtime || now >= sp->hightime) {
			start_detached_timer (&sp->nntpt);
			return;
		}
	} else {
		if (now < sp->lowtime && now >= sp->hightime) {
			start_detached_timer (&sp->nntpt);
			return;
		}
	}

	if ((pf = open_file (NNTPpoll, "r+", 0, 1)) == NULLFILE ||
	    (cb = (struct nntpsv *) callocw (1, sizeof (struct nntpsv))) == NULLNNTPSV) {
		start_detached_timer (&sp->nntpt);
		return;
	}

	stop_timer (&sp->nntpt);

	cb->fname = NULLCHAR;
	cb->newnews = NULLCHAR;
	rewind (pf);

	for (t = 0L; fgets (line, NNLINELEN, pf) != NULLCHAR; t = ftell (pf)) {
		if ((cp = strchr (line, ' ')) == NULLCHAR)
			continue;	/* something wrong with this line, skip it */

		*cp = '\0';

		if (strnicmp (line, sp->name, strcspn (line, " ") - 1) == 0) {
			rip (cp + 1);
			cb->newnews = strdup (cp + 1);
			/* Prepare to write back the current host and date */
			fseek (pf, t, 0);
			break;
		}
	}

	if (cb->newnews == NULLCHAR) {
		if (Nntpfirstpoll < 0)
			cb->newnews = strdup ("900101 000000");
		else {		/* from G8FSL */
			cb->newnews = mallocw (20);
			(void) time (&currtime);
			currtime -= Nntpfirstpoll * 86400L;
			make_time_string (cb->newnews, &currtime);
		}
	}

	fsocket.sin_family = AF_INET;
	fsocket.sin_addr.s_addr = cb->dest = sp->dest;
	fsocket.sin_port = IPPORT_NNTP;

	if ((cb->s = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
		NntpUsers++;	/* Because we decrement on quit - KA1NNN 4/95 */
		goto quit;
	}

	(void) sockmode (cb->s, SOCK_ASCII);

	if (connect (cb->s, (char *) &fsocket, SOCKSIZE) == -1) {
		NntpUsers++;	/* Because we decrement on quit - KA1NNN 4/95 */
		goto quit;
	}

	log (cb->s, "NNTP(%s): Connect", sp->name);
	if (NNTPTrace)
		tcmdprintf ("NNTP(%s): Connect\n", sp->name);

	if (Nntpmax <= NntpUsers)	{
		usprintf (cb->s, "Too many users - try later!\n");
		log (cb->s, "Too many NNTP users - connection refused!\n");
	        NntpUsers++;     /* Because we decrement on quit */
		goto quit;
	}
	NntpUsers++;
#ifdef STATS_USE
	STATS_adduse (1);
#endif

	(void) time (&currtime);
	currtime -= 300L;	/* 5 mins rewind bodge against clock errs, G8FSL */
	make_time_string (open_time, &currtime);

	if (getreply (cb) == -1)/* throw away any hello msg */
		goto quit;
   

	if (NNTPTrace)	{
		rip (cb->buf);
		tcmdprintf ("NNTP(%s): Greeting - (%s)\n", sp->name, cb->buf);
	}
   
        /* Check whether 'MODE READER' needs to be sent */
   
	if (sp->mode_reader) { 
		usprintf (cb->s, "%s\n", "MODE READER");
		ret = getreply (cb);
	
		if (NNTPTrace)	{
			rip (cb->buf);
			tcmdprintf ("NNTP(%s): Mode Reader Reply - (%s)\n", sp->name, cb->buf);
		}
	}
		
	/* IHAVE preparation */
   
	useIhave = NnIhave;
	if (sp->ihave == TRUE)
		useIhave = 2;
     
	if ((useIhave == 2) && (cb->ihave = tmpfile ()) != NULLFILE) {
		sprintf (line, "%s %s", useIhave == 2 ? "*" : sp->newsgroups, cb->newnews);

		if (newnews (line, cb, cb->ihave) < 1) {
			(void) fclose (cb->ihave);
			cb->ihave = NULLFILE;
		}
	}

#ifdef LZW
	if (LzwActive) {
		usprintf (cb->s, "XLZW %d %d\n", Lzwbits, Lzwmode);
		if ((ret = getreply (cb)) == 235) {	/* eat negative response */
			if (NNTPTrace)
				tcmdprintf ("NNTP(%s): Using LZW\n", sp->name); 
			lzwinit (cb->s, Lzwbits, Lzwmode);
		}
	}
#endif

#ifdef XXX
	usputs (cb->s, "SLAVE\n");
	if (getreply (cb) != 202)
		goto quit;
#endif

	cb->slave = 1;

	if ((f = temp_file (0, 1)) == NULLFILE)
		goto quit0;

	sprintf (cb->buf, "NEWNEWS %s %s\n",
		(sp->newsgroups == NULLCHAR ? "*" : sp->newsgroups), cb->newnews);

	if (NNTPTrace)
		tcmdprintf ("NNTP(%s): Sending cmd %s", sp->name, cb->buf); 

	usprintf (cb->s, cb->buf);

	if (getreply (cb) != 230)
		goto doihave;

	if (recv_file (f, cb->s) == -1)
		goto doihave;

	if ((f1 = temp_file (cb->s, 1)) == NULLFILE)
		goto doihave;

	rewind (f);

	for ( ; ; ) {
		if (fgets (cb->buf, NNLINELEN, f) == NULL)
			break;

		rip (cb->buf);

		if (strcmp (cb->buf, ".") == 0)
			break;

		if (check_article (cb->buf) != 0)
			continue;

		sprintf (line, "ARTICLE %s\n", cb->buf);
		if (NNTPTrace)
			tcmdprintf ("NNTP(%s): Sending cmd %s", sp->name, line);
		usprintf (cb->s, line);
		

		for ( ; ; ) {
			if ((r = recvline (cb->s, (unsigned char *) cb->buf, NNLINELEN)) == -1)
				break;

			rip (cb->buf);

			if (!isdigit (cb->buf[0])) {
				r = -1;
				continue;
			} else {
				r = atoi (cb->buf);
				break;
			}
		}

		if (r == -1)
			break;

		if (r == 220) {
			(void) recv_file (f1, cb->s);
			rewind (f1);
			for ( ; ; ) {
				if (fgets (cb->buf, NNLINELEN, f1) == NULL)
					break;

				rip (cb->buf);

				if (strnicmp (cb->buf, Hdrs[MSGID], 12) == 0) {
					char *cp2;
					cp2 = strchr (cb->buf, ' ');
					if (!cp2)
						continue;
					cb->id = strdup (++cp2);
					break;
				}
			}

			rewind (f1);
			(void) xfer_article2 (f1, cb);
			free (cb->id);
		}

		(void) fclose (f1);

		if ((f1 = temp_file (cb->s, 1)) == NULLFILE)
			goto doihave;
		kwait (NULL);
	}

	(void) fclose (f1);

doihave:			/* IHAVE offer */

	if (NNTPTrace && !useIhave)
		tcmdprintf ("NNTP(%s): Skipping IHAVE\n", sp->name);
	if (useIhave && cb->ihave != NULLFILE) {
		rewind (cb->ihave);

		while ((void) fgets (line, sizeof (line), cb->ihave), !feof (cb->ihave)) {
			kwait (NULL);
			sprintf (cb->buf, "IHAVE %s", line);
			if (NNTPTrace)
				tcmdprintf ("NNTP(%s): Sending cmd %s", sp->name, cb->buf);
			usprintf (cb->s, cb->buf);

			if ((ret = getreply (cb)) == -1)
				break;

			if (ret != 335)
				continue;

			if (NNTPTrace)
				tcmdprintf ("NNTP(%s): Sending article %s", sp->name, line);

			rip (line);

			if (doarticlecmd (line, cb, 0) == 0) {
				if ((ret = getreply (cb)) == -1)
					break;
				continue;
			}

			if ((ret = getreply (cb)) != 235)
				continue;
		}

		(void) fclose (cb->ihave);
	}

	(void) fclose (f);
	errno = 0;

	/* Now write back the opening date and time */
	fprintf (pf, "%s %s\n", sp->name, open_time);

quit0:
	usputs (cb->s, quitcmd);
quit:
	if (pf != NULLFILE)
		(void) fclose (pf);
	log (cb->s, "NNTP(%s): Disconnect", sp->name);	/* KA1NNN 4/95 */
	if (NNTPTrace)
		tcmdprintf ("NNTP(%s): Disconnect\n", sp->name);
	close_s (cb->s);
	NntpUsers--;		/* KA1NNN 4/95 */
	free (cb->newnews);
	free ((char *) cb);
	start_detached_timer (&sp->nntpt);
}



static int
ngmatcha (
int (*func) (char *, char *),
int dflt,
struct g_list *ngspec,
struct g_list *matchlist
) {
register int match;
char *cp;
struct g_list *m, *n;

	match = dflt;
	m = matchlist;
	for ( ; ; ) {
		if ((cp = strchr (m->str, '/')) != NULLCHAR)
			*cp = '\0';
		n = ngspec;
		for ( ; ; ) {
			if (n->str[0] == '!') {	/* Handle negation */
				if ((*func) (n->str + 1, m->str))
					match = 0;
			} else {
				if ((*func) (n->str, m->str))
					match = 1;
			}
			if (n->next == NULLG)
				break;
			else
				n = n->next;
		}
		if (m->next == NULLG)
			break;
		else
			m = m->next;
	}
	return (match);
}



static int
restreql (register char *w, register char *s)
{
	while (*s && *w) {
		switch (*w) {
		case '*':
			for (w++; *s; s++)
				if (restreql (w, s))
					return 1;
			break;
		default:
			if (*w != *s)
				return 0;
			w++, s++;
			break;
		}
	}
	if (*s)
		return 0;

	while (*w)
		if (*w++ != '*')
			return 0;

	return 1;
}



/* converts timestring to unix-compatible structure
 * str -> YYMMDD HHMMSS [GMT]
 * returncode: 0 (!!) if error; > 0 success */

static int32
make_nntime (struct date *d, struct time *t, char *str)
{
register char *cp;
char tmp[3];
int32 tim;

	if (str == NULLCHAR)
		return 0L;
	tmp[2] = '\0';
	cp = str;
	strncpy (tmp, cp, 2);
	d->da_year = atoi (tmp) + 1900;
	if (d->da_year < 1950)
		d->da_year += 100;	/* next century */
	if (d->da_year < 1980)
		d->da_year = 1980;
	cp += 2;
	strncpy (tmp, cp, 2);
	d->da_mon = atoi (tmp);
	if (d->da_mon < 1 || d->da_mon > 12)
		return 0L;
	cp += 2;
	strncpy (tmp, cp, 2);
	d->da_day = atoi (tmp);
	if (d->da_day < 1 || d->da_day > 32)
		return 0L;
	cp += 3;
	strncpy (tmp, cp, 2);
	t->ti_hour = atoi (tmp);
	if ((t->ti_hour >= 1 && t->ti_hour <= 23) || t->ti_hour == 0) {
		cp += 2;
		strncpy (tmp, cp, 2);
		t->ti_min = atoi (tmp);
	} else
		return 0L;

	if ((t->ti_min >= 1 && t->ti_min <= 59) || t->ti_min == 0) {
		cp += 2;
		strncpy (tmp, cp, 2);
		t->ti_sec = atoi (tmp);
	} else
		return 0L;

	if ((t->ti_sec >= 1 && t->ti_sec <= 59) || t->ti_sec == 0) {
		t->ti_hund = 0;
		tim = dostounix (d, t);
		if (*(cp + 3) == 'G')
			tim -= UtcOffset;	/* remove bias applied by dostounix */
		return tim;
	} else
		return 0L;
}



/* returncode: -1 if error; 0 if no new news; 1 new news available */

static int
newnews (char *string, struct nntpsv *mp, FILE *f)
{
register int i, j;
char *cp, *cp1, line[NNLINELEN], groups[NNLINELEN];
struct g_list *ng, *hist, *ngp, *histp, *ptr;
FILE *f1;
int all = 1;

	if (check_spaces (string, 2) == -1)
		return -1;

	cp = string;
	i = 1;
	ngp = histp = NULLG;

	while (*(cp++) > 32)
		i++;
	if (strlen (cp) < 13)
		return -1;

	strncpy (groups, string, (unsigned) (i - 1));
	groups[i - 1] = '\0';
	if (strcmp (groups, "*") != 0)
		all = 0;

	mp->datest = (struct date *) callocw (1, sizeof (struct date));
	mp->timest = (struct time *) callocw (1, sizeof (struct time));

	gettime (mp->timest);
	tnos_getdate (mp->datest);

	if ((mp->unixtime = make_nntime (mp->datest, mp->timest, cp)) == 0)
		goto quit;
	if (mp->unixtime == -1L)
		return 0;

	if (!all) {
		ng = ngp = (struct g_list *) callocw (1, (sizeof (struct g_list)));

		cp = groups;

		for ( ; ; ) {
			if ((cp1 = strchr (cp, ',')) == NULLCHAR) {
				ng->str = strdup (cp);
				ng->next = NULLG;
				break;
			}
			j = (int) strcspn (cp, ",");
			ng->str = (char *) callocw (1, (unsigned) (j + 1));
			strncpy (ng->str, cp, (unsigned) j);
			ng->str[j] = '\0';
			ng->next = (struct g_list *) callocw (1, sizeof (struct g_list));

			ng = ng->next;
			cp1 = strchr (cp, ',');
			if (cp1 != NULLCHAR)
				cp = cp1 + 1;
			else
				break;
		}
	}

	if ((f1 = open_file (NNTPhistory, READ_TEXT, mp->s, 0)) == NULLFILE)
		goto quit;

	for ( ; ; ) {
		kwait (NULL);
		if (fgets (line, NNLINELEN, f1) == NULL)
			break;
		if ((cp = strchr (line,'/')) == NULLCHAR)	/* This article has been expired */
			continue;
		rip (line);

		if (!all) {
			for (i = 3, cp = line; i; --i)	{
				cp = strchr (cp, ' ');
				if (!cp)
					break;
				cp++;
			}
			if (!cp)
				continue;
			histp = (struct g_list *) callocw (1, sizeof (struct g_list));

			hist = histp;

			for ( ; ; ) {
				if ((cp1 = strchr (cp, ' ')) == NULLCHAR) {
					hist->str = strdup (cp);
					hist->next = NULLG;
					break;
				}

				j = (int) strcspn (cp, " ");
				hist->str = (char *) callocw (1, (unsigned) (j + 1));
				strncpy (hist->str, cp, (unsigned) j);
				hist->str[j] = '\0';
				hist->next = (struct g_list *) callocw (1, sizeof (struct g_list));

				hist = hist->next;
				cp1 = strchr (cp, ' ');
				if (cp1 != NULLCHAR)
					cp = cp1 + 1;
				else
					break;
			}
			if (!ngmatcha (restreql, 0, ngp, histp)) {
				ptr = histp;
				for ( ; ; ) {
					ptr = histp->next;
					free (histp->str);
					free (histp);
					histp = ptr;
					if (histp == NULLG)
						break;
				}
				continue;
			}
		}

		cp = strchr (line, ' ');
		if (!cp)
			continue;

		if ((mp->ftime = make_nntime (mp->datest, mp->timest, ++cp)) == 0) {
			(void) fclose (f1);
			goto quit;
		}

		if ((mp->ftime - mp->unixtime) > 0) {
			cp = line;
			while (*cp > 32)
				fputc (*(cp++), f);
			fputc ('\n', f);
		}

		if (!all) {
			ptr = histp;
			for ( ; ; ) {
				if (histp == NULLG)
					break;
				ptr = histp->next;
				free (histp->str);
				free (histp);
				histp = ptr;
			}
		}
	}
	(void) fclose (f1);
	if (!all) {
		ptr = ngp;
		for ( ; ; ) {
			if (ngp == NULLG)
				break;
			ptr = ngp->next;
			free (ngp->str);
			free (ngp);
			ngp = ptr;
		}
	}
	free (mp->datest);
	free (mp->timest);
	return 1;
quit:
	return 0;
}



/* handles incoming newgroups-cmd (n5knx, adapted from g8fsl) */

static int
donewgroups (char *string, struct nntpsv *mp)
{
char *cp, line[NNLINELEN];
FILE *f;

	cp = string;

	if (check_spaces (cp, 1) == -1 || strlen (cp) < 13) {
		usputs (mp->s, badsyntax);
		return -1;
	}

	mp->datest = (struct date *) callocw (1, sizeof (struct date));
	mp->timest = (struct time *) callocw (1, sizeof (struct time));

	if ((mp->unixtime = make_nntime (mp->datest, mp->timest, cp)) == 0 || mp->unixtime == -1L) {
		usputs (mp->s, badsyntax);
		return -1;
	}
	/* mp->unixtime is in UTC       adsb*/

	usputs (mp->s, newgroups);	/* list to follow */

	if ((f = open_file (NNTPpointer, READ_TEXT, 0, 1)) != NULLFILE) {
		for ( ; ; ) {
			if (fgets (line, NNLINELEN, f) == NULL)
				break;

			rip (line);
			/* skip past ng name & path to time-created */
			if ((cp = strchr (line, ' ')) == NULLCHAR)
				continue;
			if ((cp = strchr (++cp, ' ')) == NULLCHAR)
				continue;
			cp++;

			/* Get time newsgroup created. Entries in file are in local time,
			   but make_nntime() compensates */

			if ((mp->ftime = make_nntime (mp->datest, mp->timest, cp)) == 0)
				continue;

			if ((mp->ftime - mp->unixtime) > 0) {
				cp = line;
				while (*cp > ' ')
					usputc (mp->s, uchar(*(cp++)));
				usputc (mp->s, '\n');
			}
		}
		(void) fclose (f);
		free (mp->datest);
		free (mp->timest);
	}

	usputs (mp->s, NEol);	/* end of (possibly-empty) list */
	return 0;
}



/* handles incoming newnews-cmd
 * returncode: -1 if error; 0 no groups or bad syntax; 1 success */

static int
donewnews (char *string, struct nntpsv *mp)
{
FILE *f;
int ret;

	if ((f = temp_file (mp->s, 1)) == NULLFILE)
		return -1;

	ret = newnews (string, mp, f);

	switch (ret) {
		case -1:		/* error in "newnews" routine */
			ret = 0;
			usputs (mp->s, badsyntax);
			break;
		case 0:			/* no new news */
			usputs (mp->s, newnews_t);
			usputs (mp->s, NEol);
			break;
		default:		/* new news available, send news file */
			rewind (f);
			usputs (mp->s, newnews_t);
			(void) sendfile (f, mp->s, ASCII_TYPE, 0);
			usputs (mp->s, NEol);
			ret = 1;
			break;
	}
	(void) fclose (f);
	return ret;
}



/* change current newsgroup
 * returncode: -1 if error; 0 success; 1 no newsgroup */

static int
dogroup (struct nntpsv *mp, char *buf)
{
char *p;
int er;

	if ((p = strchr (buf, ' ')) == NULLCHAR)
		return -1;

	er = get_path (p, mp);

	switch (er) {
		case 0:
			usputs (mp->s, nogroup);
			return 1;

		default:
			p = strchr (buf, ' ');
			if (get_pointer (p, mp) == 1) {
				usprintf (mp->s, listarticle,
					  mp->last - mp->first + 1, mp->first, mp->last, strchr (buf, ' '));
				return 0;
			}
			/*lint !e616 */
		case -1:
			usputs (mp->s, errorstr);
			return -1;
	}
}



/* Perform listgroup command
 * returncode: -1 if error; 0 success; 1 no newsgroup selected */

static int
dolistgroup (struct nntpsv *mp, char *buf)
{
char *p;
unsigned int ct;

	if ((p = strchr (buf, ' ')) == NULLCHAR) {
		/* use group already selected */
                if (check_grp (mp))
		        return 1;
	} else {
		switch (get_path (p, mp))	{
			case 0:		usputs (mp->s, nogroup);
					return 1;

 
			case -1:	usputs (mp->s, errorstr);
					return -1;

		        default:	p = strchr (buf, ' ');
					if (get_pointer (p, mp) != 1) {
						usputs (mp->s, errorstr);
						return -1;
					}
		}
	}

        usprintf (mp->s, listofarticles);
        for (ct = (mp->first); ct <= (mp->last); ct++)
	       usprintf (mp->s, "%u\n", ct);

        usprintf (mp->s, ".\n");
        return 0;
}



/* checks if newsgroup is selected
 * returncode: -1 no group selected; 0 success */

static int
check_grp (struct nntpsv *mp)
{
	if (mp == NULLNNTPSV || mp->path == NULLCHAR) {
		/* can't write to mp->s if mp == 0!	*/
		if (mp)
			usputs (mp->s, noselect);
		return -1;
	}
	return 0;
}



/* get id-number of message
 * returncode: -1 if error; 0 if no message; 1 success */

static int
get_id (char *bp, struct nntpsv *mp)
{
FILE *f;
char tmp[NNLINELEN];
char *cp;

	if ((f = open_message (mp, NULLFILE)) == NULLFILE)
		return 0;

	for ( ; ; ) {
		if (fgets (tmp, NNLINELEN, f) == NULL)
			break;

		if (check_blank (tmp))
			break;

		if (strnicmp (tmp, Hdrs[MSGID], 12) == 0) {
			(void) fclose (f);
			cp = strchr (tmp, ' ');
			if (!cp)
				continue;
			strcpy (bp, cp);
			rip (bp);
			return 1;
		}
	}
	(void) fclose (f);
	*bp = '\0';
	return 0;
}



/* gets next news of newsgroup

 * returncode: -1 if error; 0 no news; 1 success */

static int
get_next (struct nntpsv *mp)
{
FILE *f;

	for ( ; ; ) {
		if (mp->pointer == 0) {
			usputs (mp->s, nonext);
			return 0;
		}

		if (++(mp->pointer) > mp->last) {
			mp->pointer--;
			usputs (mp->s, nonext);
			return 0;
		}

		sprintf (mp->buf, "%s/%u", mp->path, mp->pointer);
		if ((f = open_file (mp->buf, READ_TEXT, mp->s, 0)) != NULLFILE) {
			(void) fclose (f);
			return 1;
		}
	}
}



/* gets last news of newsgroup

 * returncode: -1 if error; 0 no news; 1 success */

static int
get_last (mp)
struct nntpsv *mp;
{
FILE *f;

	for ( ; ; ) {
		if (mp->pointer == 0) {
			usputs (mp->s, noprev);
			return 0;
		}

		if (--(mp->pointer) < mp->first) {
			mp->pointer++;
			usputs (mp->s, noprev);
			return 0;
		}

		sprintf (mp->buf, "%s/%u", mp->path, mp->pointer);
		if ((f = open_file (mp->buf, READ_TEXT, mp->s, 0)) != NULLFILE) {
			(void) fclose (f);
			return 1;
		}
	}
}



/*
 * Retrieves article by message-id. Sets mp->pointer and mp->path.
 * Return: success 0, no article 1, error -1.
 */

static int
retr_by_mid (char *mid, struct nntpsv *mp)
{
FILE *f;
char *p, *p1, line[NNLINELEN];

	if ((f = open_file (NNTPhistory, READ_TEXT, mp->s, 0)) == NULLFILE)
		return -1;

	for ( ; ; ) {
		if (fgets (line, NNLINELEN - 1, f) == NULLCHAR) {
			usputs (mp->s, noart);
			(void) fclose (f);
			return 1;
		}
		if (strncmp (line, mid, strlen (mid)) == 0)
			break;
	}

	p = strchr (line, ' ');		/* point to the space before first newsgroup */
	if (!p)
		return -1;
	p += 14;
	if (!strnicmp (p, " GMT", 4))
		p += 4;
	p1 = strchr (p, '/');
	if (!p1)
		return -1;
	*p1 = '\0';
	p1++;
	mp->pointer = (unsigned) atoi (p1);	/* get the article number */

	if (mp->path != NULLCHAR)
		free (mp->path);

	if (strcmp (p, "JUNK") == 0)
		mp->path = strdup (NNTPforward);
	else
		(void) get_path (p, mp);

	(void) fclose (f);
	return 0;
}



/*
 * Retrieves article by article number. Sets mp->pointer.
 * Return: success 0, no article 1, error -1.
 */

static int
retr_by_num (char *buf, struct nntpsv *mp)
{
unsigned n;
char *p;

	if (check_grp (mp))
		return 1;

	if ((p = strchr (buf, ' ')) == NULLCHAR || check_blank (p))
		return 0;

	p++;
	n = (unsigned) atoi (p);
	if (!n || n > mp->last || n < mp->first) {
		usputs (mp->s, noart);
		return 1;
	}
	mp->pointer = n;
	return 0;
}



/*
 * Do ARTICLE command. Return: success 0, no article 1, error -1.
 */

static int
doarticlecmd (
char *buf,
struct nntpsv *mp,
int retcode			/* OH2BNS: make "220 ... Article retrieved..." msg optional */
) {
FILE *f;
char *p, *hold_s = NULLCHAR;
int ret, hold_i = 0, hold = 0;

	if ((p = strchr (buf, '<')) != NULLCHAR) {
		hold = 1;
		hold_i = (int) mp->pointer;
		hold_s = mp->path;
		mp->path = NULLCHAR;
		if ((ret = retr_by_mid (p, mp)) != 0)
			return ret;
	} else if ((ret = retr_by_num (buf, mp)) != 0)
		return ret;

	if (get_id (mp->buf, mp) != 1)
		return -1;

	f = NULL;		/* WG7J */
	if ((f = open_message (mp, f)) == NULLFILE)
		return -1;

	if (retcode)
		usprintf (mp->s, retrieve, mp->pointer, mp->buf, artmsg);
	(void) sendfile (f, mp->s, ASCII_TYPE, 0);
	usputs (mp->s, NEol);

	if (hold) {
		mp->pointer = (unsigned) hold_i;
		free (mp->path);
		mp->path = hold_s;
	}
	(void) fclose (f);
	return 0;
}



/*
 * Do HEAD command. Return: success 0, no article 1, error -1.
 */

static int
doheadcmd (char *buf, struct nntpsv *mp)
{
FILE *f;
char *p, *hold_s = NULLCHAR;
int ret, hold_i = 0, hold = 0;

	if ((p = strchr (buf, '<')) != NULLCHAR) {
		hold = 1;
		hold_i = (int) mp->pointer;
		hold_s = mp->path;
		mp->path = NULLCHAR;
		if ((ret = retr_by_mid (p, mp)) != 0)
			return ret;
	} else if ((ret = retr_by_num (buf, mp)) != 0)
		return ret;

	if (get_id (mp->buf, mp) != 1)
		return 1;

	f = NULL;		/* WG7J */
	if ((f = open_message (mp, f)) == NULLFILE)
		return -1;

	usprintf (mp->s, nntphead, mp->pointer, mp->buf, artmsg);
	while (fgets (mp->buf, NNLINELEN - 1, f) != NULLCHAR) {
		if (check_blank (mp->buf))
			break;
		usputs (mp->s, mp->buf);
	}
	usputs (mp->s, NEol);

	if (hold) {
		mp->pointer = (unsigned) hold_i;
		free (mp->path);
		mp->path = hold_s;
	}
	(void) fclose (f);
	return 0;
}



/*
 * Do BODY command. Return: success 0, no article 1, error -1.
 */

static int
dobodycmd (char *buf, struct nntpsv *mp)
{
FILE *f;
char *p, *hold_s = NULLCHAR;
int ret, hold_i = 0, hold = 0, got;

	if ((p = strchr (buf, '<')) != NULLCHAR) {
		hold = 1;
		hold_i = (int) mp->pointer;
		hold_s = mp->path;
		mp->path = NULLCHAR;
		if ((ret = retr_by_mid (p, mp)) != 0)
			return ret;
	} else if ((ret = retr_by_num (buf, mp)) != 0)
		return ret;

	if (get_id (mp->buf, mp) != 1)
		return 1;

	f = NULL;		/* WG7J */
	if ((f = open_message (mp, f)) == NULLFILE)
		return -1;

	usprintf (mp->s, body, mp->pointer, mp->buf, artmsg);
	got = 0;
	while (fgets (mp->buf, NNLINELEN - 1, f) != NULLCHAR) {
		if (got)
			usputs (mp->s, mp->buf);
		if (check_blank (mp->buf))
			got = 1;
	}
	usputs (mp->s, NEol);

	if (hold) {
		mp->pointer = (unsigned) hold_i;
		free (mp->path);
		mp->path = hold_s;
	}
	(void) fclose (f);
	return 0;
}



/*
 * Do STAT command. Return: success 0, no article 1, error -1.
 */

static int
dostatcmd (char *buf, struct nntpsv *mp)
{
char *p, *hold_s = NULLCHAR;
int ret, hold_i = 0, hold = 0;

	if ((p = strchr (buf, '<')) != NULLCHAR) {
		hold = 1;
		hold_i = (int) mp->pointer;
		hold_s = mp->path;
		mp->path = NULLCHAR;
		if ((ret = retr_by_mid (p, mp)) != 0)
			return ret;
	} else if ((ret = retr_by_num (buf, mp)) != 0)
		return ret;

	if (get_id (mp->buf, mp) != 1)
		return 1;

	usprintf (mp->s, statistics, mp->pointer, mp->buf, artmsg);

	if (hold) {
		mp->pointer = (unsigned) hold_i;
		free (mp->path);
		mp->path = hold_s;
	}
	return 0;
}



/*
 * Do XHDR command. Return: success 0, no article 1, error -1.
 */

static int
doxhdrcmd (char *buf, struct nntpsv *mp)
{
FILE *f;
char *path, *fld, *p, *mid, line[NNLINELEN];
int pointer, start, stop, ret = 0;

	if ((fld = strchr (buf, ' ')) == NULLCHAR || check_blank (fld)) {
		usputs (mp->s, badsyntax);
		return -1;
	}
	while (isspace (*++fld))
		;

	pointer = start = stop = (int) mp->pointer;
	path = mp->path;
	mid = mp->path = NULLCHAR;

	if ((p = strchr (fld, ' ')) != NULLCHAR && !check_blank (p)) {
		*p = '\0';
		while (isspace (*++p))
			;

		if (*p == '<') {
			if ((ret = retr_by_mid (p, mp)) != 0)
				goto quit;
			start = stop = (int) mp->pointer;
			mid = p;
		} else {
			start = stop = atoi (p);
			if ((p = strchr (p, '-')) != NULLCHAR)	{
				while (isspace (*++p))
					;
				if (*p)
					stop = atoi (p);
				else
					stop = (int) mp->last;
			}
		}
	}

	if (!mid) {
		mp->path = strdup (path);
		if (check_grp (mp)) {
			ret = 1;
			goto quit;
		}
		usprintf (mp->s, xheader1, fld);
	} else
		usprintf (mp->s, xheader2, 0, fld, p);

	while (start <= stop) {
		if (mid || ((unsigned)start >= mp->first && (unsigned)start <= mp->last)) {
			sprintf (line, "%s/%u", mp->path, start);

			if ((f = open_file (line, READ_TEXT, 0, 0)) != NULLFILE) {
				for ( ; ; ) {
					if (fgets (line, NNLINELEN - 1, f) == NULLCHAR
					    || check_blank (line)) {	/* no match was found */
						if (mid)
							usprintf (mp->s, "%s (none)\n", mid);
						else
							usprintf (mp->s, "%d (none)\n", start);
						break;
					}

					if ((p = strchr (line, ':')) == NULLCHAR)
						continue;
					*p = '\0';
					if (stricmp (line, fld) == 0) {
						if (mid)
							usprintf (mp->s, "%s %s", mid, p + 2);
						else
							usprintf (mp->s, "%d %s", start, p + 2);
						break;
					}
				}
				(void) fclose (f);
			}
		}
		start++;
	}
	usputs (mp->s, NEol);

quit:
	mp->pointer = (unsigned) pointer;
	free (mp->path);
	mp->path = path;

	return ret;
}



/*
 * Do XOVER command. Return: success 0, error -1.
 */

static int
doxovercmd (char *buf, struct nntpsv *mp)
{
FILE *f;
char *subject, *from, *date, *mid, *ref, *p, line[NNLINELEN];
int lines, size, start, stop, got;
char *cp;

	if ((p = strchr (buf, ' ')) != NULLCHAR) {
		start = stop = atoi (p);
		if ((p = strchr (buf, '-')) != NULLCHAR)	{
			while (isspace (*++p))
				;
			if (*p)
				stop = atoi (p);
			else
				stop = (int) mp->last;
		}
	} else
		start = stop = (int) mp->pointer;

	usputs (mp->s, xover);

	while (start <= stop) {
		if ((unsigned)start >= mp->first && (unsigned)start <= mp->last) {
			sprintf (line, "%s/%u", mp->path, start);
			if ((f = open_file (line, READ_TEXT, 0, 0)) != NULLFILE) {
				subject = from = date = mid = ref = NULLCHAR;
				lines = size = got = 0;
				while (fgets (line, NNLINELEN - 1, f) != NULLCHAR) {
					rip (line);
					if (got == 0) {
						cp = strchr (line, ' ');
						if (!cp)
							continue;
						cp++;
						if (strnicmp (line, Hdrs[SUBJECT], 9) == 0)
							subject = strdup (cp);
						else if (strnicmp (line, Hdrs[FROM], 6) == 0)
							from = strdup (cp);
						else if (strnicmp (line, Hdrs[DATE], 6) == 0)
							date = strdup (cp);
						else if (strnicmp (line, Hdrs[MSGID], 12) == 0)
							mid = strdup (cp);
						else if (strnicmp (line, Hdrs[LINECNT], 7) == 0)
							lines = atoi (cp);
						else if (strnicmp (line, Hdrs[REFERENCES], 12) == 0)
							ref = strdup (cp);
					} else
						size += (int) strlen (line);
					if (*line == '\0')
						got = 1;
				}
				usprintf (mp->s, "%u\t%s\t%s\t%s\t%s\t",
					  start, subject, from, date, mid);	/* these must be present */
				if (ref)
					usputs (mp->s, ref);
				usprintf (mp->s, "\t%u\t%u\t%s\n", size, lines, "");	/* last field not (yet) implemented */

				free (subject);
				free (from);
				free (date);
				free (mid);
				free (ref);
				(void) fclose (f);
			}
		}
		start++;
	}
	usputs (mp->s, NEol);
	return 0;
}



/* Checks and rewrites message headers if needed. Returns -1 if error,
 * 0 if all RFC1036 headers are present and 1 if a subset of RFC1036
 * headers is present. (1 means that IHAVE should be rejected but
 * POST is ok.)
 */

static int
garbled (FILE *inf, FILE *outf)
{
char line[NNLINELEN], *cp;
int got, ok, lines, ret;
time_t currtime;

	got = ok = lines = 0;

	/* first scan the headers */
	rewind (inf);
	while (fgets (line, NNLINELEN, inf) != NULLCHAR) {
		if (got) {
			lines++;
			continue;
		}
		rip (line);
		if (check_blank (line)) {	/* end of headers */
			got = 1;
			continue;
		}

		if (line[0] == ' ' || line[0] == '\t')	/* continuation header */
			continue;

		cp = line;
		while (*cp != '\0' && *cp != ' ' && *cp != ':')
			cp++;
		if (*cp != ':' || *++cp != ' ')
			return -1;	/* bad header; reject article */

		cp++;
		if (check_blank (cp))	/* empty header */
			continue;

		if (!strnicmp (line, Hdrs[FROM], 6))
			ok |= 1;
		if (!strnicmp (line, Hdrs[SUBJECT], 9))
			ok |= 2;
		if (!strnicmp (line, Hdrs[NEWSGROUPS], 12))
			ok |= 4;
		if (!strnicmp (line, Hdrs[DATE], 6))
			ok |= 8;
		if (!strnicmp (line, Hdrs[MSGID], 12))
			if (check_article (cp) == 0)
				ok |= 16;
		if (!strnicmp (line, Hdrs[PATH], 6))
			ok |= 32;
		if (!strnicmp (line, Hdrs[LINECNT], 7))
			ok |= 64;
	}

	if ((ok & 7) != 7)	/* From, Subject and Newsgroups not found */
		return -1;

	if ((ok & 63) == 63)
		ret = 0;	/* everything ok */
	else
		ret = 1;

	/* then rewrite if necessary */
	got = 0;
	rewind (inf);
	while (fgets (line, NNLINELEN, inf) != NULLCHAR) {
		kwait (NULL);
		if (got) {
			fputs (line, outf);
			continue;
		}
		if (check_blank (line)) {
			got = 1;
			if (!(ok & 64))
				fprintf (outf, "Lines: %d\n", lines);
			fputc ('\n', outf);
			continue;
		}

		if (line[0] != ' ' && line[0] != '\t') {
			cp = strchr (line, ':');
			if (!cp)
				continue;
			cp++;
			if (check_blank (cp))	/* empty header; do not copy */
				continue;
		}
		if (!strnicmp (line, "Xref: ", 6) ||
		    !strnicmp (line, Hdrs[DATERXD], 15) ||
		    !strnicmp (line, "Posted: ", 8) ||
		    !strnicmp (line, "Posting-Version: ", 17) ||
		    !strnicmp (line, Hdrs[RECEIVED], 10) ||
		    !strnicmp (line, "Relay-Version: ", 15))
			continue;	/* do not copy these headers */

		fputs (line, outf);
		rip (line);

		/* no path? */
		if (!(ok & 32) && strnicmp (line, Hdrs[FROM], 6) == 0) {
			fprintf (outf, "%snot-for-mail\n", Hdrs[PATH]);
			ok |= 32;
		}

		if (strnicmp (line, Hdrs[SUBJECT], 9) == 0) {
			/* no msgid? */
			if (!(ok & 16)) {
				fprintf (outf, "%s<%ld@%s>\n", Hdrs[MSGID], get_msgid (0), Hostname);
				ok |= 16;
			}
			/* no date? */
			if (!(ok & 8)) {
				(void) time (&currtime);
				fprintf (outf, "%s%s", Hdrs[DATE], ptime (&currtime));
				ok |= 8;
			}
		}
	}

	rewind (outf);
	return ret;
}



/* returncode: -1 if error; 0 success */

static int
get_article2 (struct nntpsv *mp, char *id)
{
FILE *f, *f1;
int ret = 0;

	if ((f = temp_file (0, 1)) == NULLFILE)
		return -1;
	if ((f1 = temp_file (0, 1)) == NULLFILE) {
		(void) fclose (f);
		return -1;
	}

	mp->ap = (struct article *) callocw (1, sizeof (struct article));

	mp->id = (*id) ? strdup (id) : strdup (">");
	usputs (mp->s, sendart);

	if (recv_file (f, mp->s) == (-1)) {
		free ((struct article *) mp->ap);
		goto quit;
	}

	if (garbled (f, f1) != 0) {
		usputs (mp->s, transnotok);
		free ((struct article *) mp->ap);
		goto quit;
	}

	if (xfer_article2 (f1, mp) < 1)
		usputs (mp->s, transnotok);
	else {
		/* ret = 0; */
		usputs (mp->s, transok);
	}

quit:

	(void) fclose (f);
	(void) fclose (f1);
	return ret;
}



/*
 * Do a POST -command. Returns 0 if success.
 */

static int
post_article (struct nntpsv *mp)
{
FILE *f, *f1;
#if 0
int ret = -1;
char *p,*p1;
#endif

	if ((f = temp_file (0, 1)) == NULLFILE)
		return -1;
	if ((f1 = temp_file (0, 1)) == NULLFILE) {
		(void) fclose (f);
		return -1;
	}

	usputs (mp->s, sendpost);

	if (recv_file (f, mp->s) == (-1))
		goto quit;

	if (garbled (f, f1) < 0) {
		usprintf (mp->s, "%s - header garbled\n", postfailed);
		goto quit;
	}

	rewind (f1);
	while (fgets (mp->buf, NNLINELEN, f1) != NULL) {
		rip (mp->buf);
		if (strnicmp (mp->buf, Hdrs[MSGID], 12) == 0) {
			char *cp;
			cp = strchr (mp->buf, ' ');
			if (!cp)
				continue;
			mp->id = strdup (++cp);
			break;
		}
	}

	rewind (f1);
	if (xfer_article2 (f1, mp) < 1)
		usprintf (mp->s, "%s\n", postfailed);
	else
		usputs (mp->s, postok);

quit:

	(void) fclose (f1);
	(void) fclose (f);
	return 0;
}



static void
poll (void *p)
{
	if (newproc ("NNTP Client", 2048, nntppoll, 0, p, NULL, 0) == NULLPROC)
		start_detached_timer (&((struct Servers *) p)->nntpt);	/* N5KNX: retry later */

}



static int
chk_access (int s)
{
struct sockaddr fsocket;
FILE *f;
int trans, verb, i;
int caccess = -1;	/* default is no access */
char *cp, *cp1, name[80], line[80];

	trans = DTranslate;	/* save the old state */
	verb = DVerbose;
	DTranslate = 1;		/* force the output of psocket() to be literal */
	DVerbose = 1;
	i = SOCKSIZE;

	name[0] = 0;
	if (getpeername (s, (char *) &fsocket, &i) != -1) {
		strcpy (name, psocket (&fsocket));
		if ((cp = strchr (name, ':')) != NULLCHAR)
			*cp = '\0';
	}

	DTranslate = trans;	/* restore the old state */

	DVerbose = verb;

	if (!*name)
		return caccess;

	if ((f = open_file (NNTPaccess, READ_TEXT, s, 0)) != NULLFILE) {
		while (fgets (line, 79, f) != NULLCHAR) {
			if (*line == '#')
				continue;
			if ((cp = strchr (line, ':')) == NULLCHAR)
				continue;
			*cp = '\0';
			if (wildmat (name, line, NULLCHARP)) {
				if ((cp1 = strchr (++cp, ':')) != NULLCHAR)
					*cp1 = '\0';
				if (strchr (cp, 'R'))	/* read only */
					caccess = 0;
				if (strchr (cp, 'P'))	/* read and post */
					caccess = 1;
				if (strchr (cp, 'I'))	/* permit incoming ihave */
					caccess = 2;
				break;
			}
		}
		(void) fclose (f);
	}
	return caccess;
}



static void
nntpserv (int s, void *unused OPTIONAL, void *p OPTIONAL)
{
struct nntpsv *mp;
FILE *fp;
time_t t;
struct tm *server_time;
int cnt, postallowed = 1, ihaveallowed = 0;
const char **cmdp;
char *arg, *cp, *cmd, buf[NNLINELEN];
#ifdef LZW
int lbits, lmode;
#endif

	(void) sockmode (s, SOCK_ASCII);
	(void) sockowner (s, Curproc);		/* We own it now */
	log (s, "open NNTP");

	if (Nntpmax <= NntpUsers)	{
		usprintf (s, "Too many users - try later!\n");
		log (s, "Too many NNTP users - connection refused!\n");
#if 0
		NntpUsers--;   /* But we haven't incremented the counter ! */
#else
	        close_s (s);   /* Close the connection before returning */
#endif
		return;
	}
	NntpUsers++;
#ifdef STATS_USE
	STATS_adduse (1);
#endif

	if (!Filecheck)
		if  (check_system ()) {
			usprintf (s, fatal, "STRUCTURE");
			log (s, "NNTP error - FILE STRU");
			close_s (s);
			NntpUsers--;
			return;
		}

	if (Nntpaccess)
		if   ((postallowed = chk_access (s)) < 0) {
			usputs (s, noaccess);
			close_s (s);
			NntpUsers--;
			return;
		}
   
        if (postallowed == 2) {
	       postallowed = 1;
	       ihaveallowed = 1;
	}

	if ((mp = (struct nntpsv *) callocw (1, sizeof (struct nntpsv))) == NULLNNTPSV) {
		usputs (s, Nospace);
		close_s (s);
		NntpUsers--;
		return;
	}

	mp->s = s;
	mp->debug = 0;
	mp->path = NULLCHAR;

	(void) time (&t);
	cp = ctime (&t);
	cp[24] = '\0';

	usprintf (s, nnversion, postallowed ? "0" : "1", Host, Version, cp,
		  postallowed ? "(posting ok)" : "(no posting)");

loop:

	if ((cnt = recvline (s, (unsigned char *) buf, NNLINELEN)) == -1) {
		/* He closed on us */
		goto quit;
	}

	if (check_blank (buf))
		goto loop;	/* empty line, do nothing */

	rip (buf);
	cmd = buf;

	/* Translate entire buffer to lower case */

	for (cp = cmd; *cp != '\0'; cp++) {
		if (*cp == ' ')
			break;
		*cp = (char) tolower (*cp);
	}

	/* Find command in table; if not present, return syntax error */
	/* If not present, return 500 command unrecognized */

	for (cmdp = commands; *cmdp != NULLCHAR; cmdp++)	{
		unsigned len, len1, len2;
		len1 = strlen (*cmdp);
		len2 = strlen (cmd);
	        if (!(len2 < len1)) {
		       len = min (len1, len2);
		       if (strnicmp (*cmdp, cmd, len) == 0)
			      break;
		}
	}

	if (*cmdp == NULLCHAR) {
		usputs (mp->s, notrecognd);
		goto loop;
	}

	arg = &cmd[strlen (*cmdp)];

	/* Skip spaces after command */

	while (*arg == ' ')
		arg++;

	/* Execute specific command */

	switch (cmdp - commands) {

		case XLZW_CMD:
#ifdef LZW
			if (LzwActive && (cp = strchr (arg, ' ')) != NULLCHAR) {
				usputs (mp->s, transok);
				*cp++ = '\0';
				lbits = atoi (arg);	/*get lzwbits*/
				lmode = atoi (cp);
				lzwinit (mp->s, lbits, lmode);
			} else
#endif
				usputs (mp->s, errorstr);

			break;

		case QUIT_CMD:
			goto quit;

		case NEWNEWS_CMD:
			if ((cp = strchr (buf, ' ')) == NULLCHAR) {
				usputs (mp->s, badsyntax);
				break;
			}

			cp++;
			(void) donewnews (cp, mp);
			break;

		case IHAVE_CMD:
			if ((cp = strchr (buf, '<')) == NULLCHAR) {
				usputs (mp->s, badsyntax);
				break;
			}

			if ((NnIhave == 0 && ihaveallowed == 0) || (check_article (cp) != 0)) {
				usputs (mp->s, notwanted);
				break;
			}

			if (get_article2 (mp, cp))
				goto quit;

			break;

		case POST_CMD:
			if (!postallowed) {
				usputs (mp->s, notallowed);
				break;
			}

			(void) post_article (mp);
			break;

		case HELP_CMD:
			usprintf (mp->s, "100 %s - help follows:\n", Host);

			if ((fp = open_file (NNTPhelp, READ_TEXT, 0, 0)) != NULLFILE) {
				(void) sendfile (fp, mp->s, ASCII_TYPE, 0);
				(void) fclose (fp);
			} else {
				usprintf (mp->s, "\nKA9Q NOS NNTP Server, version %s\n\n", Version);
				for (cnt = 1; commands[cnt] != NULLCHAR; cnt++)
					usprintf (mp->s, "%-16s%c", commands[cnt], (cnt % 5) ? ' ' : '\n');
				usputs (mp->s, "\n");
			}

			usputs (mp->s, NEol);
			break;

		case XINFO_CMD:
			if ((fp = open_file (NNTPinfo, READ_TEXT, 0, 0)) != NULLFILE) {
				usprintf (mp->s, "100 %s - xinfo follows:\n", Host);
				(void) sendfile (fp, mp->s, ASCII_TYPE, 0);
				(void) fclose (fp);
			} else
				usputs (mp->s, xinfo);

			usputs (mp->s, NEol);
			break;
	        case LISTGROUP_CMD:
	                (void) dolistgroup (mp,buf);
	                break;

		case LIST_CMD:
			if ((fp = open_file (NNTPactive, READ_TEXT, mp->s, 0)) != NULLFILE) {
				usputs (mp->s, listgroups);
				(void) sendfile (fp, mp->s, ASCII_TYPE, 0);
				(void) fclose (fp);
				usputs (mp->s, NEol);
			}
			break;

		case NEWGROUPS_CMD:
			if ((cp = strchr (buf, ' ')) == NULLCHAR) {
				usputs (mp->s, badsyntax);
				break;
			}
			(void) donewgroups (++cp, mp);
			break;

		case GROUP_CMD:
			(void) dogroup (mp, buf);
			break;

		case HEAD_CMD:
			(void) doheadcmd (buf, mp);
			break;

		case BODY_CMD:
			(void) dobodycmd (buf, mp);
			break;

		case STAT_CMD:
			(void) dostatcmd (buf, mp);
			break;

		case ARTICLE_CMD:
			(void) doarticlecmd (buf, mp, 1);
			break;

		case XHDR_CMD:
			(void) doxhdrcmd (buf, mp);
			break;

		case XOVER_CMD:
			if (check_grp (mp))
				break;

			(void) doxovercmd (buf, mp);
			break;

		case NEXT_CMD:
			if (check_grp (mp))
				break;

			if (get_next (mp) == 1) {
				if (get_id (buf, mp) == 1)
					usprintf (mp->s, sepcmd, mp->pointer, &buf[1], artmsg);
			}
			break;

		case DATE_CMD:
			(void) time (&t);
			server_time = gmtime (&t);
			usprintf (mp->s, "111 %04d%02d%02d%02d%02d%02d\n",
				(server_time->tm_year) + 1900, (server_time->tm_mon) + 1, server_time->tm_mday,
				server_time->tm_hour, server_time->tm_min, server_time->tm_sec);
			break;

		case LAST_CMD:
			if (check_grp (mp))
				break;

			if (get_last (mp) == 1) {
				if (get_id (buf, mp) == 1)
					usprintf (mp->s, sepcmd, mp->pointer, &buf[1], artmsg);
				break;
			}

			break;
	   
	        case MODE_READER_CMD:
	                usprintf(mp->s, readergreet, postallowed ? "0" : "1", postallowed ? "can" : "can't");

	                break;

	        case LIST_EXT_CMD:
	                usprintf(mp->s, listex, "LISTGROUP\n.\n");
	     
	                break;
	   

	/* This two following cmds currently are not used for much */

		case DEBUG_CMD:
			mp->debug = (mp->debug == 0) ? 1 : 0;
			usprintf (mp->s, debugstr, (mp->debug == 0) ? "OFF" : "ON");
			break;

		case SLAVE_CMD:
			mp->slave = (mp->slave == 0) ? 1 : 0;
			usprintf (mp->s, slavestr, (mp->slave == 0) ? "OFF" : "ON");
			break;

		default:
			break;
	}

	goto loop;

quit:
	usputs (mp->s, closing);
	log (mp->s, "close NNTP");
	close_s (mp->s);

	NntpUsers--;

	if (mp->path != NULLCHAR)
		free (mp->path);

	if (mp->newnews != NULLCHAR)
		free (mp->newnews);

	if (mp->fname != NULLCHAR)
		free (mp->fname);

	if (mp->id != NULLCHAR)
		free (mp->id);

	free ((char *) mp);
}



/* ---------------------------- NNTP-GATE --------------------------- */



static char *
mkreplypath (FILE * data)
{
char buf[NNLINELEN], *cp, *cp1, *path;
int type;		/*prevtype=NOHEADER */
int wefoundr = 0;
int wefoundd = 0;
unsigned pathlen = 0;

	sprintf (buf, "%sNNTP_GATE@%s", Hdrs[PATH], Hostname);
	path = strdup (buf);
	rewind (data);

	while ((fgets (buf, NNLINELEN, data)) != 0) {
		type = htype (buf);
		if (type == DATE) {
			wefoundd = 1;
			if (nnRlines !=0)
				break;
		}

		if (((wefoundd != 0) && (wefoundr ==2)) || ((wefoundd >=25) && (wefoundr == 0)))
			break;
		/* Increment a counter so if we dont find any r lines */
		/* Within 25 loops after the date we're outta here */
		if (wefoundd != 0)
			wefoundd ++;

		if ((type == RECEIVED) && (wefoundd == 0)) {
			cp = strpbrk (buf, " \t");
			if (!cp)
				continue;
			while (cp && *cp != '\0') {
				cp++;
				if (!strnicmp (cp, "by", 2)) {
					cp = strchr (cp, ' ');
					if (!cp)
						continue;
					cp++;
					cp1 = strpbrk (cp, ". ;\n");
					if (cp1)
						*cp1 = '\0';
					pathlen = (strlen (path) + strlen (cp) + 2);
					if (pathlen < NNPATHLEN)	{
						cp1 = mallocw (pathlen);
						sprintf (cp1, "%s!%s", path, cp);
						free (path);
						path = cp1;
					}
					break;
				}
			}
		}

		/* Add the rlines to the path if needed */
		if ((nnRlines == 0) && (wefoundr != 2))	{
			if (strnicmp (buf, "R:", 2) == 0) {
				if (wefoundr == 0)
					wefoundr = 1;
				cp = strchr (buf, ' ');
				if (cp)	{
					cp++;
					if (*cp == '@') {
						cp++;
						if (*cp == ':')
							cp++;
						cp1 = strpbrk(cp, ". \0");
						if (cp1)	{
							*cp1 = '\0';
							pathlen = (strlen (path) + strlen (cp) + 2);
							if (pathlen < NNPATHLEN)	{	/* How long is the path line ? */
								cp1 = mallocw (pathlen);
								sprintf (cp1, "%s!%s", path, cp);
								free (path);
								path = cp1;
							} else 		/* Too long - bail out ! */
								wefoundr = 2;
						}
					}
				}

			} else if (wefoundr == 1)
				wefoundr = 2;
		}
	}
	return (path);
}



int
nnGpost (FILE * data, const char *from, struct list *le)
{
struct nntpsv *mp;
char buf[NNLINELEN], *cp;
FILE *f, *idf;
int32 id;
int strl;
time_t currtime;

	if (!Filecheck)
		if (check_system ())
			return -1;

	if ((f = temp_file (0, 1)) == NULLFILE)
		return -1;

	mp = (struct nntpsv *) callocw (1, sizeof (struct nntpsv));

	/* build postuser */

	cp = mkreplypath (data);
	fprintf (f, "%s\n", cp);
	free ((char *) cp);
	fprintf (f, "%s%s\n", Hdrs[FROM], from);

	/*-----------------------------------------------------------------*
	 * build newsgroup                                                 *
	 *-----------------------------------------------------------------*/

	if ((cp = strchr (le->val, '@')) != NULLCHAR)
		*cp = '\0';

	strncpy (buf, le->val, NNLINELEN);
	cp = buf;
	strl = (int) strlen (cp);

	while (*cp != '\0') {
		if (*cp == '!')
			*cp = '.';	/* change ! into . - yc1dav */
		cp++;
	}

	fprintf (f, "%s%s\n", Hdrs[NEWSGROUPS], (cp - strl + 1));	/* skip the first "." */

	/*-----------------------------------------------------------------*
	 * find the subject
	 *-----------------------------------------------------------------*/

	if (look_for_header_line (buf, sizeof (buf), data, SUBJECT) == 0)
		fputs (buf, f);
	else
		fputs ("Subject: (none)\n", f);

	/*--------------------------------------------------------------------*
	 * use msgid of original message
	 *--------------------------------------------------------------------*/

	if (look_for_header_line (buf, sizeof (buf), data, MSGID) == 1)	{
		char *cp2;
		sprintf (buf, "%s/sequence.seq", Mailqdir);
		idf = open_file (buf, "r+", 0, 1);
		cp2 = fgets (buf, NNLINELEN, idf);
		if (cp2)	{
			id = atol (cp2);
			rewind (idf);
			fprintf (idf, "%ld", id + 1);
			(void) fclose (idf);
			fprintf (f, "%s<%ld@%s>\n", Hdrs[MSGID], id, Hostname);
			sprintf (mp->buf, "<%ld@%s>", id, Hostname);
		}
	} else {
		fputs (buf, f);
		rip (buf);
		cp = strchr (buf, '<');
		if (!cp)
			goto quit;

		strncpy (mp->buf, cp, 512);

		if (check_article (mp->buf))
			goto quit;
	}

	rewind (data);
	(void) time (&currtime);
	fprintf (f, "Sender: NNTP@%s\n", Hostname);

	/*--------------------------------------------------------------------*
	 * message follows                                                    *
	 *--------------------------------------------------------------------*/

	fputs ("Comments: Message transferred from TNOS SMTP\n", f);
	while ((fgets (buf, sizeof (buf), data)) != 0) {
		switch (htype (buf)) {

			case FROM:
			case MSGID:
			case RECEIVED:
			case SUBJECT:
			case XFORWARD:
			case SENDER:
				continue;	/* don't copy this header line */
			default:
				/* Check for R: Lines and stop them if needed */
				if ((nnRlines != 0) || (strnicmp (buf, "R:", 2) != 0))
					fputs (buf, f);
		}
	}

	rewind (f);
	mp->id = strdup (mp->buf);
	(void) xfer_article2 (f, mp);

#if 0
	log(-1,"NNGT: transfer: Msg %s",mp->id);
#endif

quit:
	(void) fclose (f);
	free (mp);
	return 0;
}



/* ---------------------------- Servercmd --------------------------- */


/* Start up NNTP receiver service */
int
nntp1 (int argc, char **argv, void *p OPTIONAL)
{
struct sockaddr_in lsocket;
int s;

	if (Snntp != -1)
		return -1;
	if (check_system () == -1)
		return -1;

	ksignal (Curproc, 0);	/* Don't keep the parser waiting */
	server_disconnect_io ();
	chname (Curproc, "NNTP listener");

	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	lsocket.sin_port = (int16) ((argc < 2) ? IPPORT_NNTP : atoi (argv[1]));

	Snntp = socket (AF_INET, SOCK_STREAM, 0);
	(void) bind (Snntp, (char *) &lsocket, sizeof (lsocket));
	(void) listen (Snntp, 1);

	for ( ; ; ) {
		if ((s = accept (Snntp, NULLCHAR, (int *) NULL)) == -1)
			break;	/* Service is shutting down */

		/* Low mem check now done in tcpin.c - WG7J */
		(void) sockmode (s, SOCK_ASCII);
		/* Spawn a server */
		(void) newproc ("NNTP server", 2048, nntpserv, s, NULL, NULL, 0);
	}
	return 0;
}



/* Shutdown NNTP service (existing connections are allowed to finish) */
int
nntp0 (int argc OPTIONAL, char **argv OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Snntp));
}



/* ---------------------------- Subcmds --------------------------- */
/* lists active newsgroups
 * returncode: -1 if error; 0 success */

static int
donnactive (int argc OPTIONAL, char **argv OPTIONAL, void *p OPTIONAL)
{
FILE *fp;
char line[256], *cp;
int evenodd = 0;
const char *header = "Msg#  next  mod newsgroup";

	if ((fp = open_file (NNTPactive, READ_TEXT, 0, 1)) == NULLFILE)
		return -1;

	tprintf ("%-39.39s%-39.39s\n", header, header);

	for  ( ; ; ) {
		if (fgets (line, sizeof (line), fp) == NULL)
			break;
		if ((cp = strchr (line, ' ')) == NULLCHAR)
			break;
		*cp = '\0';
		rip (++cp);
		tprintf ("%s   %-22.22s ", cp, line);
		evenodd ^= 1;
		if (!evenodd)	{
			tputs ("\n");
			kwait (NULL);
		}
	}
	if (evenodd)
		tputs ("\n");
	(void) fclose (fp);

	return 0;
}



static int
donnaccess (int argc, char **argv, void *p OPTIONAL)
{
	return setbool (&Nntpaccess, "NNTP access", argc, argv);
}



static int
donnn2p (int argc, char **argv, void *p OPTIONAL)
{
	return setbool (&NNnntp2pbbs, "NNTP nntp-to-pbbs gateway active", argc, argv);
}



static int
donnp2n (int argc, char **argv, void *p OPTIONAL)
{
	return setbool (&NNpbbs2nntp, "NNTP pbbs-to-nntp gateway active", argc, argv);
}



/* add nntp servers to list */
static int
donnadds (int argc, char **argv, void *p OPTIONAL)
{
struct Servers *np;

	for (np = Nntpserver; np != NULLSERVER; np = np->next)
		if (stricmp (np->name, argv[1]) == 0)
			break;
	if (np == NULLSERVER) {
		np = (struct Servers *) callocw (1, sizeof (struct Servers));
		if ((np->dest = resolve (argv[1])) == 0) {
			tprintf (Badhost, argv[1]);
			free ((char *) np);
			return -1;
		}
		np-> name = strdup (argv[1]);

		np->next = Nntpserver;
		Nntpserver = np;
		np->newsgroups = NULLCHAR;
		np->lowtime = np->hightime = -1;
		np->nntpt.func = poll;	/* what to call on timeout */
		np->nntpt.arg = (void *) np;
		np->ihave = FALSE;
		np->mode_reader = FALSE;
	}

	if (argc > 3) {
		int i;

		if (np->newsgroups == NULLCHAR) {
			np->newsgroups = callocw (1, NNLINELEN);
			*np->newsgroups = '\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;
				continue;
			}
		     
			/* Check for ihave pusher*/
		     
			if (strcmpi ("ihave", argv[i]) == 0) {
				np->ihave = TRUE;
				continue;
			}
		     
			/* Check if 'mode reader' is required*/
		     
			if (strcmpi ("modereader", argv[i]) == 0) {
				np->mode_reader = TRUE;
				continue;
			}
		   
			if ((strlen (np->newsgroups) + strlen (argv[i]) + 2) >= NNLINELEN)
				tprintf ("To many groups, '%s' ignored\n", argv[i]);
			else {	/* it's a group, and it fits... add it to list */
				if (*np->newsgroups != '\0')
					strcat (np->newsgroups, ",");
				strcat (np->newsgroups, argv[i]);
			}
		}

		if (*np->newsgroups == '\0') {	/* No groups specified? */
			free (np->newsgroups);
			np->newsgroups = NULLCHAR;
		}
	}
	/* set timer duration */
	set_timer (&np->nntpt, atol (argv[2]) * 1000L);
	start_detached_timer (&np->nntpt);	/* and fire it up */
	return 0;
}



static int
donnfirstpoll (int argc, char **argv, void *p OPTIONAL)	/* from G8FSL */
{
	return setint (&Nntpfirstpoll, "NNTP polls new server for news over last <n> days", argc, argv);
}



/* create a new newsgroup */
static int
donncreate (int argc, char **argv, void *p OPTIONAL)
{
FILE *f, *t;
char line[NNLINELEN];
int n;

	if ((f = open_file (NNTPactive, READ_TEXT, 0, 1)) == NULL)
		return -1;

	if  ((t = temp_file (0, 1)) == NULL) {
		(void) fclose (f);
		return -1;
	}

	while (fgets (line, NNLINELEN, f) != NULL) {
		fputs (line, t);
		n = (int) strcspn (line, " ");
		line[n] = '\0';

		if (strcmp (line, argv[1]) == 0) {
			tprintf ("Newsgroup %s already exists.\n", argv[1]);
			goto quit;
		}
	}

	rewind (t);
	(void) fclose (f);

	if (argc > 2 && *argv[2] == 'n')
		n = 1;
	else
		n = 0;

	f = NULLFILE;
	if (!make_path (argv[1])) {
		if ((f = open_file (NNTPactive, WRITE_TEXT, 0, 1)) == NULLFILE) {
			(void) fclose (t);
			return -1;
		}

		while (fgets (line, NNLINELEN, t) != NULL)
			fputs (line, f);

		fprintf (f, "%s 00000 00001 %c\n", argv[1], (n ? 'n' : 'y'));
	}

quit:
	if (f != NULLFILE)
		(void) fclose (f);
	(void) fclose (t);
	return 0;
}



/* drops nntp servers from list */

static int
donndrops (int argc OPTIONAL, char **argv, void *p OPTIONAL)
{
struct Servers *np, *npprev = NULLSERVER;

	for (np = Nntpserver; np != NULLSERVER; npprev = np, np = np->next)
		if (stricmp (np->name, argv[1]) == 0) {
			stop_timer (&np->nntpt);
			free (np->name);
			if (np->newsgroups)
				free (np->newsgroups);
			if (npprev != NULLSERVER)
				npprev->next = np->next;
			else
				Nntpserver = np->next;
			free ((char *) np);
			return 0;
		}
	tputs ("No such server enabled.\n");

	return -1;
}



/* copies a news from given newsgroup to the mailbox */

static int
donndump (int argc, char **argv, void *p OPTIONAL)
{
FILE *t, *f, *o;
char *path, *line, *cp, newsname[25], *cp1;
struct ffblk blk;

	if (!Filecheck)
		if (check_system ())
			return -1;

	if ((f = open_file (NNTPpointer, READ_TEXT, 0, 1)) == NULLFILE)
		return 0;

	path = mallocw (NNLINELEN);
	line = mallocw (NNLINELEN + 6);
	*path = *line = '\0';

	for ( ; ; ) {
		if (fgets (line, NNLINELEN, f) == NULL)
			break;
		if (!strncmp (line, argv[1], strlen (argv[1]))) {
			cp = strchr (line, ' ');
			if (!cp)
				continue;
			if ((cp1 = strchr (++cp, ' ')) != NULLCHAR)
				*cp1 = '\0';	/* drop date created */
			strncpy (path, cp, NNLINELEN);
			break;
		}
		kwait (NULL);
	}
	(void) fclose (f);

	if (*path == '\0') {
		tprintf ("No newsgroup %s\n", argv[1]);
		goto error;
	}

	rip2 (path);
	cp = strrchr (path, '/');
	if (!cp)	{
		tprintf ("Parsing error\n");
		goto error;
	}

	cp1 = &newsname[0];

	while (*cp)
		*(cp1++) = *(cp++);
	*cp1 = '\0';

	strncpy (line, path, NNLINELEN);
#ifdef UNIX
	strcat (line, "/*");
#else
	strcat (line, "/*.*");
#endif
	if (findfirst (line, &blk, 0)) {
		tputs ("No files in newsgroup\n");
		goto error;
	}

	if (argc > 2)
		sprintf (newsname, "/%s", argv[2]);

	sprintf (line, "%s%s.txt", Mailspool, newsname);
	if ((o = open_file (line, "a+", 0, 1)) == NULLFILE)
		goto error;
	if (!(mlock (Mailspool, newsname))) {
		tprintf ("Newsgroup dump to %s\n", line);

		/* Start the big loop */
		for ( ; ; ) {
			if ((t = temp_file (0, 1)) == NULLFILE) {
				(void) fclose (o);
				rmlock (Mailspool, newsname);
				goto error;
			}
			sprintf (line, "%s/%s", path, blk.ff_name);
			if (strcmp (blk.ff_name, "news.rc") == 0) {
				(void) fclose (t);
				goto skipnewsrc;	/* Horrible goto I know - Not up to Lantz Standards */
			}

			/* Open the article */
			if ((f = open_file (line, READ_TEXT, 0, 1)) == NULLFILE) {
				(void) fclose (t);
				(void) fclose (o);
				goto error;
			}
			kwait (NULL);
			tputc ('.');	/* One dot/article processed */
			tflush ();

			/* Get the start position for indexing */
			startedat = ftell (o);

			for ( ; ; ) {
				if (fgets (line, NNLINELEN, f) == NULL)
					break;
				fputs (line, t);
				if (!strnicmp (line, Hdrs[FROM], 6)) {
					cp = strchr (line, ' ');
					if (!cp)
						continue;
					fprintf (o, "From %s", ++cp);
					tcmdprintf ("From %s", cp);
				}
			}
			rewind (t);
			for ( ; ; ) {
				if (fgets (line, NNLINELEN, t) == NULL)
					break;
				fputs (line, o);
			}
			fputc ('\n', o);

			/* Now get the length of the item, and update the control file */
			lt.size = (ftell (o) - startedat);
			lt.start = startedat;
			lt.status = 0;
			lt.bid = get_msgid (0);
			updateCtl (argv[2], &lt);

			(void) fclose (t);
			(void) fclose (f);
skipnewsrc:
			if (findnext (&blk))
				break;
		}
		rmlock (Mailspool, newsname);
	}
	else
		tputs ("Mailfile is busy, try later");
	(void) fclose (o);
	tputs ("\n");

error:
	free (line);
	free (path);
	return 0;
}



static int
donnkick (int argc OPTIONAL, char **argv, void *p OPTIONAL)
{
struct Servers *np;

	for (np = Nntpserver; np != NULLSERVER; np = np->next)
		if (!stricmp (np->name, argv[1])) {
			/* 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->nntpt) || dur_timer (&np->nntpt) == 0) {
				stop_timer (&np->nntpt);
				poll ((void *) np);
			}
			return 0;
		}
	tputs ("No server enabled\n");
	return 0;
}



#ifdef LZW
/* sets LzwActive flag */
static int
donnlzw (int argc, char **argv, void *p OPTIONAL)
{
	return setbool (&LzwActive, "NNTP LZW", argc, argv);
}
#endif



/* sets autocreate flag */
static int
donnautocr (int argc, char **argv, void *p OPTIONAL)
{
	return setbool (&AutoCreate, "NNTP autocreate", argc, argv);
}



/* sets rline flag */
static int
donnrline (int argc,char ** argv, void *p OPTIONAL)
{
	return setbool (&nnRlines, "NNTP rlines preserved", argc, argv);
}


  
/* sets trace mode */
static int
donntrace (int argc,char ** argv, void *p OPTIONAL)
{
	return setbool (&NNTPTrace, "NNTP tracing", argc, argv);
}


  
/* list nntp servers */
static int
donnlists (int argc OPTIONAL, char **argv OPTIONAL, void *p OPTIONAL)
{
struct Servers *np;
char tbuf[80];

	for  (np = Nntpserver; np != NULLSERVER; np = np->next) {
		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)\n   Groups: %s\n", np->name,
				 read_timer (&np->nntpt) / 1000L,
				 dur_timer (&np->nntpt) / 1000L,
				 tbuf, np->newsgroups ? np->newsgroups : "");
		if (np->ihave)
			tprintf ("IHAVE Pusher Enabled");
		if (np->mode_reader)
			tprintf ("Mode Reader sent on connect");
		tprintf ("\n");
	}
	return 0;
}



/* manually entering new news
 * returncode: -1 if error; 0 success */
static int
donnpost (int argc OPTIONAL, char **argv OPTIONAL, void *p OPTIONAL)
{
struct session *sp;
struct nntpsv *mp;
char buf[NNLINELEN];
long id;
FILE *f = NULLFILE, *idf, *ufp;
time_t currtime;

	if (!Filecheck)
		if (check_system ())
			return -1;

	if ((sp = newsession ("Post", TELNET, 0)) == NULLSESSION)
		return -1;

	mp = (struct nntpsv *) callocw (1, sizeof (struct nntpsv));

	for ( ; ; ) {
		if ((f = temp_file (0, 1)) == NULLFILE)
			goto done;

		id = get_msgid (0);
		if (Post->user == NULLCHAR) {
			tputs ("User name? ");
			(void) recvline (sp->input, (unsigned char *) buf, NNLINELEN);
			rip (buf);
			Post->user = strdup (buf);
		}
		fprintf (f, "%s%s\n", Hdrs[PATH], Post->user);

		fprintf (f, "%s%s@%s", Hdrs[FROM], Post->user, Hostname);
		if (Post->fullname != NULLCHAR)
			fprintf (f, " (%s )", Post->fullname);
		fputc ('\n', f);

		tputs ("Newsgroup? ");
		(void) recvline (sp->input, (unsigned char *) buf, NNLINELEN);
		rip (buf);
		if (check_blank (buf))
			goto done;
		fprintf (f, "%s%s\n", Hdrs[NEWSGROUPS], buf);

		tputs ("Subject? ");
		(void) recvline (sp->input, (unsigned char *) buf, NNLINELEN);
		fprintf (f, "%s%s", Hdrs[SUBJECT], buf);
		fprintf (f, "%s<%ld@%s>\n", Hdrs[MSGID], id, Hostname);
		(void) time (&currtime);
		fprintf (f, "Date: %s", ptime (&currtime));
		fprintf (f, "Sender: NNTP@%s\n", Hostname);

		if (Post->reply != NULLCHAR)
			fprintf (f, "%s%s\n", Hdrs[REPLYTO], Post->reply);

		if (Post->organ != NULLCHAR)
			fprintf (f, "Organization: %s\n", Post->organ);

		fputc ('\n', f);
		tputs ("Enter message - end with .\n");

		for ( ; ; ) {
			(void) recvline (sp->input, (unsigned char *) buf, NNLINELEN);
			if (strcmp (buf, ".u\n") == 0
			    || strcmp (buf, ".r\n") == 0) {
				tputs ("Filename? ");
				(void) recvline (sp->input, (unsigned char *) buf, NNLINELEN);
				rip (buf);
				if ((ufp = open_file (buf, READ_TEXT, 0, 1)) != NULLFILE) {
					while (fgets (buf, NNLINELEN, ufp) != NULL)
						fputs (buf, f);
					(void) fclose (ufp);
				}
				tputs ("(continue)\n");
			}
			if (strcmp (buf, ".\n") == 0
			    || strcmpi (buf, "***END\n") == 0
			    || strcmpi (buf, "/EX\n") == 0)
				break;
			fputs (buf, f);
		}

		if (Post->sig != NULLCHAR) {
			sprintf (buf, "%s", Post->sig);
			if ((idf = open_file (buf, READ_TEXT, 0, 1)) != NULLFILE) {
				while (fgets (buf, NNLINELEN, idf) != NULL)
					fputs (buf, f);
				(void) fclose (idf);
			}
		}

loop:		tputs ("\n[Send, Abort, Exit, List] ");
		(void) recvline (sp->input, (unsigned char *) buf, NNLINELEN);
		switch (tolower (buf[0])) {
			case 's':
				rewind (f);
				sprintf (mp->buf, "<%ld@%s>", id, Hostname);
				mp->id = strdup (mp->buf);
				if (xfer_article2 (f, mp) < 1)
					tputs ("\007Posting failed\n");
				break;

			case 'l':
				rewind (f);
				for ( ; ; ) {
					if (fgets (buf, NNLINELEN, f) == NULL)
						break;
					tputs (buf);
				}
				rewind (f);
				goto loop;

			case 'e':
				(void) fclose (f);
				f = NULLFILE;
				goto done;

			case 'a':
				break;

			default:
				goto loop;
		}
		(void) fclose (f);
		f = NULLFILE;
		tputs ("Post another? ");
		(void) recvline (sp->input, (unsigned char *) buf, NNLINELEN);
		if (tolower (buf[0]) == 'n')
			goto done;
	}

done:
	if (f != NULLFILE)
		(void) fclose (f);
	(void) keywait (NULLCHAR, 1);
	free ((char *) mp);
	freesession (sp);
	return 0;
}



static int
donnquiet (int argc, char **argv, void *p OPTIONAL)
{
	return setintrc (&Nntpquiet, "NNTP quiet", argc, argv, 0, 2);
}



/* -------------------- Profile subcmds -------------------- */

static int
donnuser (int argc, char **argv, void *p OPTIONAL)
{
	if (argc < 2 && Post->user != NULLCHAR)
		tprintf ("%s\n", Post->user);
	else {
		free (Post->user);
		Post->user = strdup (argv[1]);
	}
	return 0;
}



static int
donnsig (int argc, char **argv, void *p OPTIONAL)
{
	if (argc < 2 && Post->sig != NULLCHAR)
		tprintf ("%s\n", Post->sig);
	else {
		if (access (argv[1], 0) == 0) {
			free (Post->sig);
			Post->sig = strdup (argv[1]);
		} else {
			tputs ("No such signature file\n");
			return -1;
		}
	}
	return 0;
}



static int
donnfull (int argc, char **argv, void *p OPTIONAL)
{
	if (argc < 2 && Post->fullname != NULLCHAR)
		tprintf ("%s\n", Post->fullname);
	else {
		free (Post->fullname);
		Post->fullname = strdup (argv[1]);
	}
	return 0;
}



static int
donnhost (int argc, char **argv, void *p OPTIONAL)
{
	if (argc < 2 && Host != NULLCHAR)
		tprintf ("%s\n", Host);
	else {
		free (Host);
		Host = strdup (argv[1]);
	}
	return 0;
}



static int
donnihave (int argc, char **argv, void *p OPTIONAL)
{
	return setintrc (&NnIhave, "NNTP Ihave", argc, argv, 0, 2);
}



static int
donnorgan (int argc, char **argv, void *p OPTIONAL)
{
	if (argc < 2 && Post->organ != NULLCHAR)
		tprintf ("%s\n", Post->organ);
	else {
		free (Post->organ);
		Post->organ = strdup (argv[1]);
	}
	return 0;
}



static int
donndefdist (int argc, char **argv, void *p OPTIONAL)
{
	if (argc < 2)
		tprintf ("%s\n", (NNdefdist) ? NNdefdist : usedefaultdist);
	else {
		free (NNdefdist);
		NNdefdist = strdup (argv[1]);
	}
	return 0;
}

static int
donndefgroup (int argc, char **argv, void *p OPTIONAL)
{
	if (argc < 2)
		tprintf ("%s\n", (NNdefgroup) ? NNdefgroup : "Not set: Using ampr.bbs.*");
	else {
		free (NNdefgroup);
		NNdefgroup = strdup (argv[1]);
	}
	return 0;
}


static int
donnread (int argc, char **argv, void *p OPTIONAL)
{
FILE *f;
struct session *sp;
struct article *art;
char cp[NNLINELEN], buf[81];
int number, row, Nrows, flag = argc;

#ifdef UNIX
	Nrows = Numrows - 1;
#else
	Nrows = SCREENlength;
#endif
	if  ((art = (struct article *) mallocw (sizeof (struct article))) == NULLARTICLE)
		        return -1;

	art->group = strdup (argv[1]);
	if (get_path2 (art) == 1) {
		if (argc > 2)
			number = atoi (argv[2]);
		else
			number = 1;

		sprintf (cp, "%s/news.rc", art->path);
		if (flag < 3 && (f = fopen (cp, READ_TEXT)) != NULLFILE) {
			if ((fgets (buf, sizeof (buf), f)) != 0) {
				number = atoi (buf);
				number++;
			}
			(void) fclose (f);
		}
		if ((sp = newsession ("NNTP read", MORE, 0)) != NULLSESSION) {
			for ( ; ; ) {
				if (number < 1)
					number = 1;
				sp->ttystate.echo = sp->ttystate.edit = 0;
				row = Nrows - 4;
				sprintf (cp, "%s/%d", art->path, number);

				if ((f = fopen (cp, READ_TEXT)) != NULLFILE) {
					tprintf ("Msg #%d\n", number);
					while ((void) fgets (buf, sizeof (buf), f), !feof (f)) {
						tputs (buf);
						if (--row == 0) {
							row = keywait ("--More--", 0);
							switch (row) {
								case -1:
								case 'q':
									(void) fclose (f);
									goto done;
								case '\n':
								case '\r':
									row = 2;
									break;
								default:
									row = Nrows - 3;
							}
						}
					}
					(void) fclose (f);
				} else {
					number--;
					tputs ("No more news");
				}
done:
				row = keywait ("\nRead next/previous? (n/p/q)", 0);
				switch (row) {
					case -1:
					case 'q':
						goto done2;
					case 'p':
						flag = 3;
						if (--number < 1)
							goto done2;
						continue;
					default:
						number++;
						continue;
				}
			}
done2:
			if (flag < 3) {
				sprintf (cp, "%s/news.rc", art->path);
				if ((f = fopen (cp, WRITE_TEXT)) != NULLFILE) {
					sprintf (cp, "%d\n", number);
					fputs (cp, f);
					(void) fclose (f);
				}
			}
			(void) keywait (NULLCHAR, 1);
			freesession (sp);
		}
	} else {
		tprintf ("No such newsgroup %s\n", art->group);
	}
	free (art->path);
	free (art->group);
	free (art);
	return 0;
}



static int
donnreply (int argc, char **argv, void *p OPTIONAL)
{
	if (argc < 2 && Post->reply != NULLCHAR)
		tprintf ("%s\n", Post->reply);
	else {
		free (Post->reply);
		Post->reply = strdup (argv[1]);
	}
	return 0;
}



static struct cmds Prof[] = {
	{ "fullname",	donnfull,		    0, 0, NULLCHAR },
	{ "host",	donnhost,		    0, 0, NULLCHAR },
	{ "organ",	donnorgan,		    0, 0, NULLCHAR },
	{ "reply",	donnreply,		    0, 0, NULLCHAR },
	{ "sig",	donnsig,		    0, 0, NULLCHAR },
	{ "user",	donnuser,		    0, 0, NULLCHAR },
	{ NULLCHAR,	NULLFP((int,char**,void*)), 0, 0, NULLCHAR }
};


/* subcmd parser */

static int
donnprofile (int argc, char **argv, void *p)
{
	if (Post == NULLPOST) {
		Post = (struct post *) callocw (1, sizeof (struct post));
		Post->user = Post->reply = Post->sig = Post->organ = Post->fullname = NULLCHAR;
	}

	if (Host == NULLCHAR)
		Host = strdup (Hostname);

	return (argc == 10) ? 0 : (subcmd (Prof, argc, argv, p));
}



static struct cmds Nntp[] = {
	{ "access",	donnaccess,		    0, 0, NULLCHAR },
	{ "active",	donnactive,		    0, 0, NULLCHAR },
	{ "add",	donnadds,		    0, 3, "nntp add <nntpserv> <interval> [ihave] [<groups>]" },
	{ "autocreate",	donnautocr,		    0, 0, NULLCHAR },
	{ "create",	donncreate,		    0, 2, "nntp create <newsgroup> [y|n]" },
	{ "defaultdist",donndefdist,		    0, 0, NULLCHAR },
	{ "defaultgrp", donndefgroup,		    0, 0, NULLCHAR },
        { "drop",	donndrops,		    0, 2, "nntp drop <nntpserv>" },
	{ "dump",	donndump,		    0, 2, "nntp dump <newsgroup> [<mailbox>]" },
	{ "expire",	donnexpire,		    0, 0, NULLCHAR },
	{ "firstpoll",	donnfirstpoll,		    0, 0, NULLCHAR },
	{ "ihave",	donnihave,		    0, 0, NULLCHAR },
	{ "kick",	donnkick,		    0, 2, "nntp kick <server>" },
	{ "list",	donnlists,		    0, 0, NULLCHAR },
#ifdef LZW
	{ "lzw",	donnlzw,		    0, 0, NULLCHAR },
#endif
	{ "maxusers",	donnmax,		    0, 0, NULLCHAR },
	{ "nntp2pbbs",	donnn2p,		    0, 0, NULLCHAR },
	{ "pbbs2nntp",	donnp2n,		    0, 0, NULLCHAR },
	{ "post",	donnpost,		 2024, 0, NULLCHAR },
	{ "profile",	donnprofile,		    0, 0, NULLCHAR },
	{ "quiet",	donnquiet,		    0, 0, NULLCHAR },
	{ "read",	donnread,		 1024, 2, "nntp read <newsgroup> [number]" },
	{ "rline",	donnrline,		    0, 0, NULLCHAR },
	{ "trace",	donntrace,		    0, 0, NULLCHAR },
	{ NULLCHAR,	NULLFP((int,char**,void*)), 0, 0, NULLCHAR }
};



/* cmd parser */

int
donntp (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return (subcmd (Nntp, argc, argv, p));
}



/* main file-opening routine
 * options: s = socketnumber, if given an error msg is printed to the socket
 * returncode: NULLFILE if error; filepointer success
 */
static FILE *
open_file (const char *name, const char *mode, int s, int t)
{
FILE *f;
#ifdef MSDOS
register char *cp;
#endif

	if (name == NULLCHAR || mode == NULLCHAR)
		return NULLFILE;
#ifdef MSDOS
	while ((cp = strchr (name, '\\')) != NULLCHAR)
		*cp = '/';
#endif
	if ((f = fopen (name, mode)) == NULLFILE) {
		if (s)
			usprintf (s, fatal, name);
		if (t)
			tcmdprintf ("Can't open %s: %s\n", name, SYS_ERRLIST(errno));
	}
	return f;
}



/* main tempfile-opening routine
 * returncode: NULLFILE if error; filepointer success
 */

static FILE *
temp_file (int s, int t)
{
FILE *f;

	if ((f = tmpfile ()) == NULLFILE) {
		if (t)
			tcmdprintf ("Can't open TMP: %s\n", SYS_ERRLIST(errno));
		if (s)
			usprintf (s, fatal, "TMP");
	}
	return f;
}



static void
make_time_string (char *string, time_t * timep)
{
struct tm *stm;

	stm = gmtime (timep);
	sprintf (string, "%02d%02d%02d %02d%02d%02d GMT",
		stm->tm_year % 100, stm->tm_mon + 1, stm->tm_mday, stm->tm_hour,
		stm->tm_min, stm->tm_sec);
}



/* NNTP Expiry Code */

static struct cmds Nnexpirecmds[] = {
	{ "articles",	donnexpart,	0, 0, NULLCHAR },
	{ "bids",	donnexbids,	0, 0, NULLCHAR },
	{ "now",	donnexpnow,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR },
	
};



static int
donnmax (int argc, char *argv[], void *p OPTIONAL)
{
	return setint (&Nntpmax, "Max. NNTP connects", argc, argv);
}


static int
donnexpire (int argc, char *argv[], void *p)
{
	return subcmd (Nnexpirecmds, argc, argv, p);
}



static int
donnexpart (int argc, char *argv[], void *p OPTIONAL)
{
	return setint(&nnexptime_art, "NNTP article expiry time (days)", argc, argv );
}



int
donnexbids (int argc, char *argv[], void *p OPTIONAL)
{
	return setint(&nnexptime_bid, "NNTP BID expiry time (days)", argc, argv );
}



static int
donnexpnow(int argc OPTIONAL, char **argv OPTIONAL, void *p OPTIONAL)
{
int x=0;
char buf [NNLINELEN];
char *cp;
char nHistory [FILE_PATH_SIZE];
char nfilepath [FILE_PATH_SIZE];
char artdate [15], artexpt[15], bidexpt[15], tempexpt[17];
struct nntpsv *mp;   

FILE *fp;
FILE *fpn;
FILE *fq;

time_t now;
time_t tarts;
time_t tbids;
struct tm *tblock;

	/* Always make a note in the log just in case something nasty happens */
	log (-1, "NNTP : Expiry commenced");

	/* Don't let bids expire before the articles ! */
	if (nnexptime_bid < nnexptime_art)
		nnexptime_bid = nnexptime_art;

	/* Second: check that the history file is not locked, if it is, */
	/* then wait a little while before retrying.                    */

	while(mlock (NNTPhistory, NULLCHAR)) {
		kpause (500L);
		if (++x == 4) {
			log (-1, "NNTP: Cannot expire due to history lock");
			return -1;
		}
	}
   
	/* Get the time now in seconds since Jan 1970 */
	now = time (NULL);

	/* Knock off the seconds to get string times for bids and articles */
	tarts = now - (nnexptime_art * 86400);
	tbids = now - (nnexptime_bid * 86400);
	tblock = localtime (&tarts);
	(void) strftime (tempexpt, 16, "%Y%m%d %H%M%S", tblock);
	strncpy (artexpt, &tempexpt[2], 14);
	tblock = localtime (&tbids);
	(void) strftime (tempexpt, 16, "%Y%m%d %H%M%S", tblock);
	strncpy (bidexpt, &tempexpt[2], 14);
	artexpt [14] = '\0';
	bidexpt [14] = '\0';

	/* artexpt and bidexpt are now strings in the form "951131 235959" */
	/* Open History File, and file for New History */
	if ((fp = fopen (NNTPhistory, "r")) == NULLFILE || (mp = (struct nntpsv *) callocw (1, sizeof (struct nntpsv))) == NULLNNTPSV) {
		log (-1, "NNTP: Unable to expire - Duff history file or low memory");
		rmlock (NNTPhistory, NULLCHAR);
		return -1;
	}
   
	sprintf (nHistory, "%s.new", NNTPhistory);
	if ((fpn = fopen (nHistory, "w")) == NULLFILE)  {
		(void) fclose (fp);
		free ((char *) mp);
		log (-1, "NNTP: Unable to Expire - Can't open new history file"); 
		rmlock (NNTPhistory, NULLCHAR);
		return -1;
	}
   
	/* Open up the Active file for READ & WRITE */
	if ((fq = fopen (NNTPactive, "r+")) == NULLFILE)  {
		(void) fclose (fp);
		(void) fclose (fpn);
		free ((char *) mp);
		log (-1, "NNTP: Unable to Expire - Can't open Active file");
		rmlock (NNTPhistory, NULLCHAR);
		return -1;
	}
	
	/* Start the main loop, pull off the lines from the History file */   
	for ( ; ; )  {
		if (fgets (buf, NNLINELEN, fp) == NULL)
			break;

		kwait (NULL); /* Give the rest of TNOS a turn */
      
		/* Get the date and time of the article */
		cp = strchr (buf,' ');
		if (!cp)
			continue;
		cp++;
		strncpy (artdate, cp, 14);

		if (artdate [13] == ' ') {  /* This article hasn't been expired */ 
			artdate [13] = '\0';

			if (check_expdate (artdate, artexpt)) {
				/* Article to old - gather rest of line and expire */
				cp += 14;
				if (!strnicmp (cp, "GMT ", 4))
					cp += 4;
				strncpy (nfilepath, cp, FILE_PATH_SIZE);
				nndel_article (nfilepath, mp, fq);
				/* Form Article number and date for o/p to new history */
				*cp = '\0';
				cp--;
				*cp = '\n';
			}
		}

		/* Check BID expiry and zot if needed */
		if (!check_expdate (artdate, bidexpt))
			fprintf (fpn, "%s", buf);
		}
	(void) fclose (fpn);
	(void) fclose (fp);
	(void) fclose (fq);
	unlink (NNTPhistory);
	(void) merge (NNTPhistory);
	free ((char *) mp);
	rmlock (NNTPhistory, NULLCHAR);
	log (-1, "NNTP: Expiry Finished");
	return 0;
}

/* Compares two expiry dates */
static int
check_expdate (char *idate, char *wdate)
{
	if (idate[0] == '0' && wdate[0] == '9')
		return 0;
	if (idate[0] == '9' && wdate[0] == '0')
		return 1;
	if (strcmp (wdate, idate) > 0)
		return 1;
       
	return 0;
}

/* Finds and zots articles */
static void
nndel_article (char *buf, struct nntpsv *mp, FILE *fq)
{
int err= 0;
unsigned int low, low1;
char *cp, *cp1;
char path [FILE_PATH_SIZE];
char path2 [FILE_PATH_SIZE];
char line [LINELEN];
char line2 [LINELEN];
char number [5]; /* Change this if going to > 5 on article numbers */
long int pointer;   
   
	/* Break down into newsgroup name and article number */
	cp = strchr (buf, '/');
	if (!cp)
		return;
	*cp = '\0';
	*path = ' ';               /* get_path 'discards' first char */
	strncpy ((path+1), buf, FILE_PATH_SIZE - 1);
	cp++;
	strncpy (number, cp, 5);
	if ((cp1 = strchr (number, LF)) != NULLCHAR)
		*cp1 = '\0';
	if (strcmp ((path+1), "JUNK") == 0)  {
		mp->path = strdup (NNTPforward);
		err = 1;
	}
	else
		err = get_path (path, mp); /* get true path from pointer file */
	if (err == 1) {
		sprintf (path2, "%s/%s", mp->path, number); /* combine true path and article number */ 
		unlink (path2);

		*cp = '\0';                                /* put a space on the end to give an exact match */
		cp --;                                     /* when we scan the active file                  */
		*cp = ' ';
		rewind (fq);

		/* update the active list */ 
		for ( ; ; )  {
			pointer = ftell (fq);
			if (fgets (line, LINELEN, fq) == NULL)
				break;
#if 0
			kwait (NULL);        /* Give the rest of TNOS a turn */
#endif

			/* Find the appropriate line and get the "lowest" number*/
			if (strnicmp ((buf), line, strlen (buf)) == 0) {
				cp = strchr (line, ' ');
				if (!cp)
					continue;
				cp1 = strchr (++cp, ' ');   /* pointer to lowest article*/
				if (!cp1)
					continue;
				cp = (strchr (++cp1, ' '));     /* gives length */
				if (!cp)
					continue;
				strncpy (line2, cp1, (unsigned) (cp - cp1));
				low = (unsigned) atoi (number);
				low1 = (unsigned) atoi (line2);
				low ++;
				if (low > low1) {
					(void) itoa ((int) low, line2, 10);
					low = strlen (line2);
					strncpy ((cp-low), line2, low);
					fseek (fq, pointer, 0);
					fputs (line, fq);
					break;
				 }
			}
		}
	}
	return;
}



static int
look_for_header_line (char *buf, int bufsize, FILE *data, int type)
{
	rewind (data);
	while ((fgets (buf, bufsize, data)) != 0) {
		if (check_blank (buf))
			return 1;
		if (htype (buf) == type)
			return 0;
	}
	return 1;
}

void
mail2news (FILE *data, const char *from, const char *to, const char *area)
{
struct nntpsv *mp;
char buf[NNLINELEN], *cp = NULLCHAR;
FILE *f, *fp;
time_t currtime;
int gotto = 0;		/* only pass-thru first 'To:' header */
int eatnext = 0, clobberblank = 0;
char *newsgroup = NULLCHAR, *distribution;
char *toaddr, *hostaddr = NULLCHAR;


	/* first check to see if this just came in from our NNTP. If so,
	 *  then, of course, we don't need to send it back */
	if (look_for_header_line (buf, sizeof(buf), data, XNNTPGATE) == 0)    {
		rip (buf);
		if (stricmp (&buf[13], Hostname))	{
#if 1
			if (NNTPTrace)
				tcmdprintf ("PBBS->NNTP: '%s' - skipped (came from NNTP already)\n", to);
#endif
			return;
		}
	}

	if (!Filecheck)
		if (check_system ())
			return;

	if ((f = temp_file (0, 1)) == NULLFILE)
		return;

	mp = (struct nntpsv *) callocw (1, sizeof (struct nntpsv));

	/* build postuser */

	cp = mkreplypath (data);
	fprintf (f, "%s\n", cp);
	free ((char *) cp);

#if 1
	if (look_for_header_line (buf, sizeof (buf), data, FROM) == 0)
		fputs (buf, f);
	else
#endif
		fprintf (f, "%s%s\n", Hdrs[FROM], from);

	
	distribution = strdup ((NNdefdist) ? NNdefdist : usedefaultdist);
	toaddr = strdup (to);
	if ((cp = strpbrk (toaddr, "%@")) != NULLCHAR)
		*cp++ = 0;
	if (cp)	{
#if 0
		if (stricmp (cp, Hostname))	/* if xxx@Hostname, skip it */
			return;
#endif
		hostaddr = strdup (cp);
		if ((cp = strpbrk (hostaddr, "@%")) != NULLCHAR)
			*cp = 0;
	}

        /* First check for a UKIP gateway.txt file - if present, use that*/
        if ((fp = fopen (UKIPNEWS2Mail, READ_TEXT)) != NULLFILE) {
	        if (NNTPTrace)
				tcmdprintf ("PBBS->NNTP: Using UKIP gateway file\n");
	        
			while (fgets (buf, sizeof (buf), fp) != NULLCHAR) {
				char *mail, *tmp, *news; /* , *dist; */
				rip (buf);
				mail = skipwhite (buf);
				if (*mail == '#' || !*mail)
					continue;		/* skip comments and blank lines */

				tmp = skipnonwhite (mail);
				*tmp = 0;
		   
				if (!wildmat (to, mail, NULLCHARP))
					continue;
		   
				tmp = skipwhite (++tmp);
				tmp = skipnonwhite (tmp);
		     
				news = skipwhite (tmp);
				tmp  = skipnonwhite (news);
		        *tmp = 0;
				newsgroup = strdup (news);
#if 0
				tmp = skipwhite (++tmp);
				tmp = skipnonwhite (++tmp);
				tmp = skipwhite (++tmp);
				tmp = skipnonwhite (tmp);
				tmp = skipwhite (++tmp); /* OK - Produces Dist G4HIP */		   
				if (*tmp)	{
					free (distribution);
					distribution = NULLCHAR;
					if (hostaddr == NULLCHAR || *tmp != '-')	{
						dist = tmp;
						tmp = skipnonwhite (tmp);
						*tmp = 0;
						distribution = strdup (dist);
					}
				}
#endif
				break;
			}
			(void) fclose (fp);
			if (NNTPTrace)
				tcmdprintf ("PBBS->NNTP: UKIP gateway returns Newsgroup: %s Distribution: %s\n", newsgroup, distribution);
		}
   
	else
	   
	/* check the NEWS2Mail file, regardless of public or personal area */
	if ((fp = fopen (NEWS2Mail, READ_TEXT)) != NULLFILE)	{
		if (NNTPTrace)
			tcmdprintf ("PBBS->NNTP: Using news2mail file\n");
	   
		while (fgets (buf, sizeof (buf), fp) != NULLCHAR) {
			char *news, *mail, *tmp, *dist;
			rip (buf);
			news = skipwhite (buf);
			if (*news == '#' || !*news)
				continue;		/* skip comments and blank lines */
			mail = skipnonwhite (news);
			*mail = 0;
			mail = skipwhite (++mail);
			tmp = skipnonwhite (mail);
			*tmp++ = 0;
			if (stricmp (toaddr, mail))
				continue;
			newsgroup = strdup (news);
			tmp = skipwhite (tmp);
			if (*tmp)	{
				free (distribution);
				distribution = NULLCHAR;
				if (hostaddr == NULLCHAR || *tmp != '-')	{
					dist = tmp;
					tmp = skipnonwhite (tmp);
					*tmp = 0;
					distribution = strdup (dist);
				}
			}
			break;
		}
		(void) fclose (fp);
	}
#ifdef UKIP   
	/* If a T: Line is present, get newsgroup from that */
	rewind (data);
	if (look_for_t_line (buf, sizeof (buf), data, "Newsgroups:") ==0) {
		if (buf [13] == ' ') {
			cp = (buf + 14);
			rip (cp);
			free (newsgroup);
			newsgroup = strdup (cp);
		}
	}
#endif
	/* if newsgroup is == NULLCHAR, then we didn't find it in the file */
	if (newsgroup == NULLCHAR)	{

        /* Determine whether this is a personal message or a bulletin */
	if (look_for_header_line (buf, sizeof (buf), data, BBSTYPE) == 0) {
		if ( buf[16] != 'B') {
			free (distribution);
			if (NNTPTrace)
				tcmdprintf ("PBBS->NNTP: Message is not a bulletin - rejecting\n");
			return;        /* Not a bulletin */           
		}
	}
	   
		else {
	      
			/* Old method - must be in 'areas' file to be a bull. */
	   
			/* now we will make up a default newsgroup, if it is NOT a personal message */
			if (!isarea((const char *)area))	{
				free (distribution);
				if (NNTPTrace)
					tcmdprintf ("PBBS->NNTP: Not for a public message area - rejecting\n");

				return;		/* personal area */
			}
		}
	   
		if (!NNdefgroup) {
			newsgroup = mallocw (strlen (toaddr) + 10);
			sprintf (newsgroup, "ampr.bbs.%s", toaddr);
		}
		else {
			newsgroup = mallocw (strlen (NNdefgroup) + 10);
		        strcpy (newsgroup, NNdefgroup);
		}

		if (hostaddr != NULLCHAR)	{
			free (distribution);
			distribution = NULLCHAR;
		}
	}

	if (distribution && *distribution == '-' && strlen (distribution) == 1)	{
		/* reset it to default if no hostaddr */
		free (distribution);
		distribution = strdup ((NNdefdist) ? NNdefdist : usedefaultdist);
	}

#if 0
        if (distribution == NULLCHAR)	{
#endif
	        if (hostaddr == NULLCHAR) {
		        if (NNTPTrace)
	                tcmdprintf ("PBBS->NNTP: No 'Hostname' - Rejecting\n");
			return;
		}
	/*	distribution = mallocw (strlen (hostaddr) + 5); */
	/*	sprintf (distribution, "bbs.%s", hostaddr);   */

	/*   If we have a @host can we map this from distribution file ? */
	/*                  Following based on UKNOS                     */
	   
	        if ((fp = fopen (NNTPdist, READ_TEXT)) != NULLFILE) {
		        while (fgets (buf, sizeof (buf), fp) != NULL) {
			        rip (buf);
			        if (*buf == '#')
			                continue;
			        if ((cp = strchr (buf, ':')) == NULLCHAR)
			                continue;
			        cp++;
			        if (stricmp (hostaddr, cp) == 0) {
				         cp--;
				         *cp = '\0';
				         if (distribution != NULLCHAR)
				                 free (distribution);
				         distribution = mallocw (strlen (buf) +5);
				         strcpy (distribution, buf);
				}
			}
		} 
#if 0			   	     
	}
#endif
	free (hostaddr);
	free (toaddr);

	
	/*-----------------------------------------------------------------*
	 * build newsgroup                                                 *
	 *-----------------------------------------------------------------*/

	if (NNTPTrace)
		tcmdprintf ("PBBS->NNTP: '%s'->%s\n", to, newsgroup);
	fprintf (f, "%s%s\n", Hdrs[NEWSGROUPS], newsgroup);
	free (newsgroup);

	/*-----------------------------------------------------------------*
	 * build distribution                                              *
	 *-----------------------------------------------------------------*/

	fprintf (f, "%s%s\n", Hdrs[DISTRIBUTION], distribution);
	free (distribution);

	/*-----------------------------------------------------------------*
	 * find the subject
	 *-----------------------------------------------------------------*/

	if (look_for_header_line (buf, sizeof (buf), data, SUBJECT) == 0)
		fputs (buf, f);
	else
		fputs ("Subject: (none)\n", f);
	
	/*-----------------------------------------------------------------*
	 * find the sender
	 *-----------------------------------------------------------------*/

	if (look_for_header_line (buf, sizeof (buf), data, SENDER) == 0)
		fputs (buf, f);
	else
		fprintf (f, "Sender: NNTP@%s\n", Hostname);

	/*--------------------------------------------------------------------*
	 * use msgid of original message
	 *--------------------------------------------------------------------*/

	if (look_for_header_line (buf, sizeof (buf), data, MSGID) == 1)
		goto quit;	/* should never happen */

	rip (buf);
	cp = strchr (buf, '<');
	if (!cp)		/* again, should never happen */
		goto quit;

	strncpy (mp->buf, cp, 512);
#if 0   
	(void) strupr (mp->buf); /* Convert message ID to upper case */
#endif   
	cp = strchr (mp->buf, '>');
	if (!cp)		/* also, shouldn't happen */
		goto quit;
	*++cp = 0;

	/* convert the message-id, if needed */
        if (!stricmp (&mp->buf[strlen(mp->buf) - 5], ".bbs>"))	{
		cp = strrchr (mp->buf, '@');
		if (cp)
			strcpy (++cp, "hamradio>");
	        }
#ifdef UKIP	   
        /* If a T: Line is present, get message-id from that */
	rewind (data);
        if (look_for_t_line (buf, sizeof (buf), data, "Message-ID:") ==0) {
	        if (buf [13] == ' ') {
		        cp = (buf + 14);
		        rip (cp);
		        strcpy (mp->buf, cp);
			}
		}
#endif
	if (check_article (mp->buf))
		goto quit;

	fprintf (f, "%s%s\n", Hdrs[MSGID], mp->buf);
	rewind (data);
	(void) time (&currtime);

	/*--------------------------------------------------------------------*
	 * message follows                                                    *
	 *--------------------------------------------------------------------*/

	fputs ("Comments: Message transferred from TNOS SMTP Gateway\n", f);
	while ((fgets (buf, sizeof (buf), data)) != 0) {
		if (eatnext)	{
			eatnext = 0;   /* eat this line */
			continue;
		   }
		if (clobberblank)
			if (check_blank (buf)) {
				clobberblank = 0;
				continue;
			}
		switch (htype (buf)) {

			case FROM:
			case MSGID:
			case SUBJECT:
			case XBBSHOLD:
			case XFORWARD:
			case SENDER:
			    continue;	/* don't copy this header line */
			case RECEIVED:		/* eat the 'id AA date' line, also */
				eatnext = 1;
				continue;
			case TO:
				if (gotto)
					continue;
				gotto = 1;	/*lint !e616 * and fall thru */
			default:
#if 0
				if ((nnRlines != 0) || (strnicmp (buf, "R:", 2) != 0))
#endif
		                if (nnRlines !=0)
			                fputs (buf, f);
		                else {
			                if ((strncmp (buf, "R:", 2)) == 0)
				                clobberblank = 1;
				        else
					        fputs (buf, f);
				}
		}
	}

	rewind (f);
	mp->id = strdup (mp->buf);
	(void) xfer_article2 (f, mp);

quit:
	(void) fclose (f);
	free (mp);
	return;
}



/* do the NNTP > mail gateway processing */
static void
news2mail (FILE *f, char *group)
{
FILE *fp;
char buf[NNLINELEN], buf1[LINELEN], *cp, *cp1;
char *distribution = NULLCHAR;
char *smtp_to = NULLCHAR, *smtp_from = NULLCHAR;
int foundblank = 0;
long id;
char *msg_id = NULLCHAR;


	/* first check to see if this just came in from our PBBS. If so, then, of course, we don't need to send it back */
	if (look_for_header_line (buf, sizeof(buf), f, PATH) != 0)
		return;
	cp = strstr (buf, "NNTP_GATE@");
	if (cp && !strnicmp (&cp[10], Hostname, strlen(Hostname)))	{
#if 0
		if (NNTPTrace)
			tcmdprintf ("NNTP->PBBS: '%s' - skipped (came from PBBS already)\n", group);
#endif
		return;
	}

#if 0
        /* Search for an "X-BBS-To:" line - if present preserve the original to@host */
	if (look_for_header_line (buf, sizeof(buf), f, XBBSTO) == 0) {
	        rip (buf);
 
	        cp = strchr (buf, '@');
                if (cp) {
		        *cp='\0';
		        cp++;
	                distribution = strdup (cp);
		        cp = strchr (buf, ' ');
		        cp++;
		        smtp_to = strdup (cp);
		        if (NNTPTrace)
		                tcmdprintf ("NNTP->PBBS: X-BBS-To: %s@%s\n", smtp_to, distribution);
		}
	}
#endif
        if (smtp_to == NULLCHAR) {
	   
                /* First check for a UKIP gateway.txt file - if present, use that*/
                if ((fp = fopen (UKIPNEWS2Mail, READ_TEXT)) != NULLFILE) {
	                if (NNTPTrace)
	                        tcmdprintf ("NNTP->PBBS: Using UKIP gateway file\n");

	        while (fgets (buf, sizeof (buf), fp) != NULLCHAR) {
		        char *tmp, *mail, *news;
			        rip (buf);
			        tmp = skipwhite (buf);
			        if (*tmp == '#' || !*tmp)
				        continue;		/* skip comments and blank lines */
			        tmp = skipnonwhite (tmp);
			        mail = skipwhite (++tmp);
			        tmp = skipnonwhite (mail);
			        *tmp = '\0';
	                        news = skipwhite (++tmp);
	                        tmp = skipnonwhite (news);
	                        *tmp ='\0';
			        if (stricmp (group, news))
				        continue;
			        smtp_to = strdup (mail);
			        break;
		        }
		        (void) fclose (fp);
	        }
   
			else   

				/* check the NEWS2Mail file, regardless of public or personal area */
				if ((fp = fopen (NEWS2Mail, READ_TEXT)) != NULLFILE)	{
					if (NNTPTrace)
						tcmdprintf ("NNTP->PBBS: Using news2mail file\n");

					while (fgets (buf, sizeof (buf), fp) != NULLCHAR) {
						char *news, *mail, *tmp;
						rip (buf);
						news = skipwhite (buf);
						if (*news == '#' || !*news)
							continue;		/* skip comments and blank lines */
						mail = skipnonwhite (news);
						*mail = 0;
						mail = skipwhite (++mail);
						tmp = skipnonwhite (mail);
						*tmp++ = 0;
						if (stricmp (group, news))
							continue;
						smtp_to = strdup (mail);
						break;
					}
					(void) fclose (fp);
				}

			if (smtp_to == NULLCHAR)	{
				if (strncmp (group, "ampr.bbs.", 9))
			        return;
		        smtp_to = strdup (&group[9]);
	        }
	}


	/* See if this article needs to be forwarded */
	if ((look_for_header_line (buf, sizeof(buf), f, XMSGID) == 0)
	  || (look_for_header_line (buf, sizeof(buf), f, MSGID) == 0))	{
		rip (buf);
		cp = strchr (buf,' ');
		if (cp)	{
			/* Find the '<' at the start of the MID */
			while (*cp && *cp != '<')
				cp++;
			if (*cp) {
				msg_id = strdup (cp);
#if 0
				(void) strlwr (msg_id);
#endif
				if (!stricmp (&msg_id[strlen(msg_id) - 10], "@hamradio>"))	{
					cp = strrchr (msg_id, '@');
					if (cp)
						strcpy (++cp, "hamradio>");
				}
			}
		}
	}

	if (msg_id == NULLCHAR) {
		free (smtp_to);
		return;
	}

	/* Make the distribution from reading the Distribution: header */
	if ((distribution == NULLCHAR) && (look_for_header_line (buf, sizeof(buf), f, DISTRIBUTION) == 0))	{
		rip (buf);
		cp = strchr (buf,' ');
		if (cp)	{
			while (*cp && *cp == ' ')
				cp++;
			if (cp && *cp) {
				if (strrchr (cp, '.'))	{
					cp = strrchr (cp, '.');
					if (cp)
						cp++;
				}  else {

					/* No . in the distribution lets check distfile */
				   
					if ((fp = fopen (NNTPdist, READ_TEXT)) != NULLFILE ) {   
						while (fgets (buf1, sizeof (buf1), fp) != NULL) {
							rip (buf1);
							if  (*buf1 == '#')
								continue;
							if ((cp1 = strchr (buf1, ':')) == NULLCHAR)
								continue;
							*cp1 = '\0';
							if (stricmp (cp, buf1) == 0) {
								cp1 ++;
								cp = cp1;
								break;
							}
						}
					}
				}
							  
				distribution = strdup (cp);
			}
		}
	}

	/* Now must have a distribution */
	if (distribution == NULLCHAR) {
		const char *ccp;
		ccp = (NNdefdist) ? NNdefdist : usedefaultdist;
		if (strrchr (ccp, '.'))	{
			ccp = strrchr (ccp, '.');
			if (ccp)
				ccp++;
		}
		distribution = strdup (ccp);
	}

	/* Need to find either the Reply-To: or the From: header details */
	if ((look_for_header_line (buf, sizeof(buf), f, REPLYTO) == 0)
	  || (look_for_header_line (buf, sizeof(buf), f, FROM) == 0)) {
		rip (buf);
		cp = strchr (buf, ' ');
		if (cp)	{
			while (*cp && *cp == ' ')
				cp++;
			if (*cp)
				smtp_from = strdup (cp);
		}
	}

	if (smtp_from == NULLCHAR)	{
	  	free (distribution);
	  	free (smtp_to);
	  	free (msg_id);
	  	return;
	}


	/* Now ready to do make a mail message to "<smtp_to>@<distribution>",
	 * storing it in the mail queue directory */

	id = get_msgid (0);

	sprintf (buf, "%s/%ld.txt", Mailqdir, id);
	if ((fp = fopen (buf, WRITE_TEXT)) == NULL) {
		free (smtp_from);
		free (smtp_to);
		free (distribution);
		free (msg_id);
		return;
	}

	rewind (f);
	while ((fgets (buf, sizeof(buf), f)) != 0) {
		if (!foundblank) {
			switch (htype(buf)) {
				/* Headers to drop */
				case TO:	/* going to rebuild the To: header */
				case ORGANIZATION:
				case RRECEIPT:
				case DATERXD:
				case DISTRIBUTION:
				case NEWSGROUPS:
				case POSTHOST:
				case SENDER:
				case MSGID:
				case PATH:
				case XBBSHOLD:
					continue;
				default:
					break;
			}
			if (check_blank (buf)) {
				foundblank = 1;
				if (NNTPTrace)
					tcmdprintf ("NNTP->PBBS: %s->'%s@%s'\n", group, smtp_to, distribution);
#if 1
				/* All messages gated from NNTP are bulletins */
				fprintf (fp, "%s%c\n", Hdrs[BBSTYPE], 'B');
#endif
				fprintf (fp, "%s%s\n", Hdrs[XNNTPGATE], Hostname);
				fprintf (fp, "%s%s\n", Hdrs[XNNTPNEWSGROUPS], group);
				fprintf (fp, "%s%s\n", Hdrs[MSGID], msg_id);
				fprintf (fp, "%s%s@%s\n\n", Hdrs[TO], smtp_to, distribution);
				continue;
			}
		}
		if (strncmp (buf, "From ", 5) == 0)
			fputc ('>', fp);
	        if (strlen (buf) > LINELEN) {  /* It's greater than the smtp line length */
				cp = (buf + LINELEN -1);
		        *cp = '\n';            /* play safe - truncate the line */
		        cp++;
		        *cp = '\0';
			}
		fputs (buf, fp);
	}
	fputc ('\n',fp);
	(void) fclose (fp);

	sprintf (buf, "%s/%ld.wrk", Mailqdir, id);
	if ((fp = fopen (buf, WRITE_TEXT)) != NULL) {
		fputs (Hostname, fp);
		fprintf (fp, "\n%s\n", smtp_from);
		fprintf (fp, "%s@%s\n", smtp_to, distribution);
		(void) fclose (fp);
	} else {
		sprintf (buf,"%s/%ld.txt", Mailqdir, id);
		(void) remove (buf);
	}
	free (smtp_from);
	free (smtp_to);
	free (distribution);
	free (msg_id);
	return;
}

#endif /* NNTPS */
