/*
 *
 * 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
 *
 * Mods by PA0GRI, WG7J and KO4KS
 */

#include <stdio.h>
#include <time.h>
#include <stdarg.h>
#include <ctype.h>
#include <setjmp.h>
#ifdef MSDOS
#include <dos.h>
#include <dir.h>
#endif
#ifdef UNIX
#include <sys/types.h>
#endif
#include <sys/stat.h>
#ifdef MSDOS
#include <io.h>
#endif
#include <string.h>
#include "global.h"
#include "config.h"
#ifdef NNTPS
#include "mbuf.h"
#include "cmdparse.h"
#include "socket.h"
#include "iface.h"
#include "proc.h"
#include "smtp.h"
#include "commands.h"
#include "dirutil.h"
#include "ftp.h"
#include "netuser.h"
#include "nntp.h"
#include "session.h"
#include "files.h"
#include "smtp.h"
#include "bm.h"
#ifdef LESS_WHINING
#include "pc.h"
#include "usock.h"
#include "nr4.h"
#include "netrom.h"
#include "udp.h"
#include "tcp.h"
#include "ip.h"
#endif

int setintrc __ARGS((int16 *var,char *label,int argc, char *argv[],int minval,int16 maxval));
extern int subcmd __ARGS((struct cmds tab[],int argc,char *argv[],void *p));
FILE *open_file __ARGS((char *name,char *mode,int s,int t));
FILE *temp_file __ARGS((int s,int t));     /*                            NMB */

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


#undef CONTROL				/* reverse NNTP function (not implemented yet) */
#define	LINE	80

static int Snntp = -1;  	/* prototype socket for service */
static int16 Nntpquiet = 0;
int Filecheck = 0;			/* flag if file system has been checked */

struct post *Post = NULLPOST;
struct Servers *Nntpserver = NULLSERVER;

static char *Host	= NULLCHAR;
static char NEol[] 	= ".\n";

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

/* Command table */
static char *commands[] = {
	"quit",
#define QUIT_CMD	0
	"help",
#define HELP_CMD	1
	"list",
#define LIST_CMD	2
	"group",
#define GROUP_CMD	3
	"debug",
#define DEBUG_CMD	4
	"article",
#define ARTICLE_CMD	5
	"next",
#define NEXT_CMD	6
	"xinfo",
#define XINFO_CMD	7
	"ihave",
#define IHAVE_CMD	8
	"newnews",
#define NEWNEWS_CMD	9
	"head",
#define HEAD_CMD	10
	"body",
#define BODY_CMD	11
	"stat",
#define STAT_CMD	12
	"last",
#define LAST_CMD	13
	"slave",
#define SLAVE_CMD	14
	NULLCHAR
};

static char artmsg[] 	= " Article retrieved - ";

static char info[]	= "100 %s Info:\n";
static char xinfo[]	= "100 No info available\n";
static char debug[]	= "100 DEBUG %s\n";

static char nnversion[]	= "20%s %s NNTP version %s ready at %s\n";
static char slave[]    	= "202 SLAVE %s\n";
static char closing[]	= "203 Closing\n";
static char listarticle[]= "211 %u %u %u%s\n";
static char retrieve[] 	= "220 %u%s%shead and body follow\n";
static char head[]	= "221 %u%s%sHead\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 newnews_t[]	= "230 New news by message id follows\n";
static char transok[]	= "235 Thanks\n";

static char sendart[]	= "335 Send article, 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 badsyntax[]	= "501 Syntax error\n";
static char error[]	= "503 Command not performed\n";
static char nospace[]	= "503 Not enough disk space left\n";
static char lowmem[]	= "503 System overloaded\n";
static char fatal[]	= "503 Fatal error FILE %s\n";

static char quitcmd[]	= "QUIT\n";
static char help[]	= "NNTP Server 1992,\n\n100 ARTICLE  BODY  GROUP   HEAD  HELP IHAVE\n100 LAST     LIST  NEWNEWS NEXT  QUIT STAT\n";

#ifdef MSDOS
static void near
#else
static void
#endif
rip2(s)
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
 */
#ifdef MSDOS
static int near
#else
static int
#endif
make_dir(path,s)
char *path;
int s;
{
	register char *cp;

	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)) {
			tprintf("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(mp,f)
struct nntpsv *mp;
FILE *f;
{
	char line[LineLen];

	/* mp already checked */

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

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

	if ((f = open_file(line,READ_TEXT,0,0)) == NULLFILE)
		usputs(mp->s,noart);
	return f;
}

/* file-receiving routine
 * returncode: -1 if error or 'recvline' faults; 0 success; 1 if blank line */
static int
recv_file(fp,s)
FILE *fp;
int s;
{
	char line[LineLen];
	int check = 0;

	for (;;) {
		if (recvline(s,line,LineLen) == -1)
			return -1;
		rip2(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 */
static int
check_article(id)
char *id;
{
	char *p, line[LineLen];
	FILE *f;

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

	for(;;) {
		if (fgets(line,LineLen,f) == NULL) {
			fclose(f);
			return 0;
		}
		if (strstr(line,p) != NULL) {
			fclose(f);
			return 1;
		}
	}
}

/* checks for not valid chars in a line
 * returncode: 0 if valid; 1 if invalid */
static int
check_blank(bp)
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(path)
char *path;
{
	if(path == NULLCHAR)
		return -1;
	if(access(path,0))
		return close(creat(path,S_IWRITE));
	return 0;
}

/* checks if two spaces exists in given string
 * returncode: -1 if error; 1 success */
static int
check_one(str)
register char *str;
{
	register char *cp;

	if(str == NULLCHAR || (cp = strchr(str,' ')) == NULLCHAR)
		return -1;
	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()
{
	FILE *f;
	char line[LineLen];
	int error = 0;

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

	if(Filecheck)
		return 0;

	error  = make_dir(Forward,0);
	error |= check_file(Pointer);
	error |= check_file(History);
	error |= check_file(Active);
	error |= check_file(Poll);

	if(error)
		goto quit;

	sprintf(line,"%s/fwd.seq",Newsdir);
	if (access(line,0)) {
		if((f = open_file(line,"w+",0,1)) == NULLFILE) {
			goto quit;
		}
		fputs("1\n",f);
		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);
		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,cb->buf,LineLen) != -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(group,mp)
char *group;
struct nntpsv *mp;
{
	FILE *f;
	char line[LineLen], *cp;

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

	group++;

	for (;;) {
		if (fgets(line,LineLen,f) == NULL)
			break;
		if (strcspn(line," ") != strlen(group))
			continue;
		if (strnicmp(group,line,strlen(group)) == 0) {
			cp = (strchr(line,' ')) + 1;
			if (mp->path != NULLCHAR)
				free(mp->path);
			mp->path = strdup(cp);
			rip2(mp->path);
			fclose(f);
			return 1;
		}
	}
	fclose(f);
	return 0;
}

/* checkes if path to given article exists
 * returncode: -1 error; 1 success; 0 no path */
static int
get_path2(art)
struct article *art;
{
	FILE *f;
	char line[LineLen], *p;

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

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

	for (;;) {
		if (fgets(line,LineLen,f) == NULL)
			break;
		if (strcspn(line," ") != strlen(p))
			continue;
		if (strnicmp(p,line,strlen(p)) == 0) {
			p = (strchr(line,' ')) + 1;
			if(art->path != NULLCHAR)
				free(art->path);
			art->path = strdup(p);
			rip2(art->path);
			fclose(f);
			return 1;
		}
	}
	fclose(f);
	return 0;
}

/* returncode: -1 if error; 1 success; 0 no pointer */
static int
get_pointer(group,mp)
char *group;
struct nntpsv *mp;
{
	FILE *f;
	char line[LineLen], *p;

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

	group++;

	for (;;) {
		if (fgets(line,LineLen,f) == NULL)
			break;
		if (strcspn(line," ") != strlen(group))
			continue;
		if (strnicmp(group,line,strlen(group))==0) {
			p = strchr(line,' ');
			mp->last = (unsigned)atoi(p);
			p++;
			mp->first = (unsigned)atoi(strchr(p,' '));
			mp->pointer = (mp->first > mp->last ) ? 0 : mp->first;
			fclose(f);
			return 1;
		}
	}
	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(group)
char *group;
{
	FILE *f;
	char cp[LineLen], cp1[LineLen], *cp2;
	int got_it;

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

	got_it = 0;
	strcpy(cp1,group);

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

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

		if(make_dir(cp,0)) {
			fclose(f);
			return -1;
		}
		if(got_it) {
			fprintf(f,"%s %s\n",group,cp);
			fclose(f);
			return 0;
		}
		*cp2 = '/';
	}
}

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

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

	if ((t = temp_file(0,1)) == NULLFILE) {
		fclose(f);
		return -1;
	}
	p1 = a->ap->group;
	a->ap->number = 0;
	for (;;) {
		if (fgets(a->buf,LineLen,f) == NULL)
			break;
		a->ap->tmpu = strcspn(a->buf," ");
		strncpy(l2,a->buf,a->ap->tmpu);
		l2[a->ap->tmpu] = '\0';
		if (strcmp(p1,l2) == 0) {
			p = strchr(a->buf,' ') + 1;
			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);
	}
	fclose(f);
	rewind(t);
	if ((f = open_file(Active,WRITE_TEXT,a->s,0)) == NULLFILE) {
		fclose(t);
		return -1;
	}
	for(;;) {
		if (fgets(a->buf,LineLen,t) == NULL)
			break;
		fputs(a->buf,f);
	}
	fclose(t);
	if (a->ap->number == 0) {
		make_path(a->ap->group);
		a->ap->number = 1;
		fprintf(f,"%s 00001 00001 y\n",a->ap->group);
	}
	fclose(f);
	return (a->ap->number);
}

/* returncode: -1 error; 0 success */
static int
dup_f(in,out,mp)
FILE *in;
FILE *out;
struct nntpsv *mp;
/* Path: bug in col 1 of article body bug fixed by G4JEC 901019....*/
{
	char *p;
	int blank_line_flag = 1;

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

		if (blank_line_flag)
            if (strnicmp(mp->buf,Hdrs[PATH],5) == 0) {
				 p = strchr(mp->buf,' ') + 1;
                 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);
		if(!check_blank(mp->buf))
			blank_line_flag = 0;
	}
}

/* returncode: < 1 if error; 1 success */
static int
dofwd(mp,f,history)
struct nntpsv *mp;
FILE *f;
FILE *history;
{
	FILE *fwd;

	if(mp == NULLNNTPSV)
		return -1;

	sprintf(mp->buf,"%s/fwd.seq",Newsdir);
	if ((fwd = open_file(mp->buf,"r+",mp->s,0)) == NULLFILE)
		return -1;

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

	sprintf(mp->buf,"%s/%u",Forward,mp->hold_i);
	if ((fwd = open_file(mp->buf,WRITE_TEXT,mp->s,0)) == NULLFILE)
		return -2;

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

#ifdef CONTROL
static int
docontrol(f,mp)
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(;;) {
		if ((fgets(mp->buf,LineLen,f)) == NULL)
			break;
		if (check_blank(mp->buf))
			break;
		rip2(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(f,mp)
FILE *f;
struct nntpsv *mp;
{
	char line[LineLen], *p, *group = NULLCHAR, *p1, *from = NULLCHAR;
	FILE *fptr, *history;
	struct tm *stm;
	int x;
#ifdef CONTROL
	int control = 0;
#endif
	long currtime;

	if(f == NULLFILE || mp == NULLNNTPSV ||
	  (history = open_file(History,APPEND_TEXT,mp->s,0)) == NULLFILE)
		return -1;

	for (;;) {
		if (fgets(line,LineLen,f) == NULL)
			break;
		rip2(line);
        if (strnicmp(line,Hdrs[FROM],6) == 0) {
			p = strchr(line,' ') + 1;
			from = strdup(p);
			continue;
		}
        if (strnicmp(line,Hdrs[NEWSGROUPS],12) == 0) {
			p = strchr(line,' ') + 1;
			group = strdup(p);
			break;
		}
	}
	time(&currtime);
	stm = localtime(&currtime);
	fprintf(history,"%s %2.2d%2.2d%2.2d %2.2d%2.2d%2.2d",
		mp->id,
		stm->tm_year,stm->tm_mon+1,stm->tm_mday,
		stm->tm_hour,stm->tm_min,stm->tm_sec);

	mp->hold_i = 0;
	p = group;
	if((mp->ap = (struct article *)callocw(1,sizeof(struct article))) == NULLARTICLE)
		goto quit;
	x = 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;
		}
		update_list(mp);
		if (mp->ap->number > 0) {
			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) {
				fclose(history);
				free(mp->ap->group);
				goto quit;
			}
			dup_f(f,fptr,mp);
			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) {
		if ((x = dofwd(mp,f,history)) < 1)
	    mp->hold_i = 0;
	}
	fputc('\n',history);
	fclose(history);
	time(&currtime);
	if(Nntpquiet < 2)
		tprintf("New mail in newsgroup %s\n  from <%s> at %s%s",
			group,from,ctime(&currtime),
			(Nntpquiet < 1)  ? "\007" : "");

#ifdef CONTROL
	if (control == 1)
		docontrol(f,mp);
#endif
quit:
	free(from);
	free(group);
	free((char *)mp->ap);
	return 1;
}

static void
nntppoll(unused,cb1,p)
int unused;
void *cb1;
void *p;
{
	char *cp, line[LineLen];
	struct sockaddr_in fsocket;
	struct nntpsv *cb;
	struct tm *stm, *ltm;
	FILE *f, *f1, *pf;
	long t;
	int r, now;
	struct Servers *sp = (struct Servers *) cb1;
	long currtime;

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

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

	if(availmem() < Memthresh) {
		start_timer(&sp->nntpt);
		return;
	}

	/* check connection window */
	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_timer(&sp->nntpt);
			return;
		}
	} else {
		if (now < sp->lowtime && now >= sp->hightime) {
			start_timer(&sp->nntpt);
			return;
		}
	}
	if((pf = open_file(Poll,"r+",0,1)) == NULLFILE ||
	  (cb = (struct nntpsv *)callocw(1,sizeof(struct nntpsv))) == NULLNNTPSV) {
		start_timer(&sp->nntpt);
		return;
	}
	stop_timer(&sp->nntpt);

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

	for(t = 0L; fgets(line,LineLen,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) {
			rip2(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)
		 cb->newnews = strdup("800101 000000");

	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)
		goto quit;
	sockmode(cb->s,SOCK_ASCII);

	if(connect(cb->s,(char *)&fsocket,SOCKSIZE) == -1)
		goto quit;
	log(cb->s,"Connect NNTP");

	time(&currtime);
	stm = localtime(&currtime);

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

#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 quit;

	usprintf(cb->s,"NEWNEWS %s %s\n",sp->newsgroups,cb->newnews);
	if(getreply(cb) != 230) {
		fclose(f);
		goto quit;
	}
	if(recv_file(f,cb->s) == -1) {
		usprintf(cb->s,quitcmd);
		fclose(f);
		goto quit;
	}
	if ((f1 = temp_file(cb->s,1)) == NULLFILE) {
		fclose(f);
		goto quit;
	}
	rewind(f);

	for(;;) {
		if (fgets(cb->buf,LineLen,f) == NULL)
			break;
	    rip2(cb->buf);
		if (strcmp(cb->buf,".") == 0)
			break;
		if (check_article(cb->buf) == 1)
			continue;
	    usprintf(cb->s,"ARTICLE %s\n",cb->buf);
		for (;;) {
			if ((r = recvline(cb->s,cb->buf,LineLen)) == -1)
				break;
			rip2(cb->buf);
			if(!isdigit(cb->buf[0])) {
				r = -1;
				continue;
			} else {
				r = atoi(cb->buf);
				break;
			}
		}
		if(r == -1)
			break;
		if (r == 220) {
			recv_file(f1,cb->s);
			rewind(f1);
			for ( ;; ) {
				if (fgets(cb->buf,LineLen,f1) == NULL)
					break;
				rip2(cb->buf);
                if (strnicmp(cb->buf,Hdrs[MSGID],12) == 0) {
					cb->id = strdup((strchr(cb->buf,' ')) + 1);
					break;
				}
			}
			rewind(f1);
			xfer_article2(f1,cb);
			free(cb->id);
		}
		fclose(f1);
		if ((f1 = temp_file(cb->s,1)) == NULLFILE)
			goto quit;
	}
	fclose(f);
	fclose(f1);
	usprintf(cb->s,quitcmd);
	if (recvline(cb->s,cb->buf,LineLen) == -1)
		goto quit;
quit:
	if(errno == 0) {
		/* Now write back the opening date and time */
		fprintf(pf,"%s %02d%02d%02d %02d%02d%02d\n",
			sp->name,
			stm->tm_year,
			stm->tm_mon + 1,
			stm->tm_mday,
			stm->tm_hour,
			stm->tm_min,
			stm->tm_sec);
	}
	fclose(pf);

	close_s(cb->s);
	free(cb->newnews);
	free((char *)cb);
	start_timer(&sp->nntpt);
}

static int
ngmatcha(func,dflt,ngspec,matchlist)
int	(*func)();
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(w, s)
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
 * returncode: 0 (!!) if error; > 0 success */
static int32
make_nntime(d,t,str)
struct date *d;
struct time *t;
char *str;
{
	register char *cp;
	char tmp[3];

	if(str == NULLCHAR)
		return 0;

	tmp[2] = '\0';
	cp = str;

	strncpy(tmp,cp,2);

	d->da_year = atoi(tmp)+1900;

	if (d->da_year < 1980)
		d->da_year = 1980;

	if (d->da_year > 2099)
		goto quit;

	cp+=2;

	strncpy(tmp,cp,2);
	d->da_mon = atoi(tmp);
	if (d->da_mon < 1 || d->da_mon > 12)
		goto quit;

	cp+=2;

	strncpy(tmp,cp,2);
	d->da_day = atoi(tmp);
	if (d->da_day < 1 || d->da_day > 32)
		goto quit;

	cp+=3;

	strncpy(tmp,cp,2);
	t->ti_hour = atoi(tmp);
	if (t->ti_hour < 0 || t->ti_hour > 24)
		goto quit;

	cp+=2;

	strncpy(tmp,cp,2);
	t->ti_min = atoi(tmp);
	if (t->ti_min < 0 || t->ti_min > 60)
		goto quit;

	cp+=2;

	strncpy(tmp,cp,2);
	t->ti_sec = atoi(tmp);
	if (t->ti_sec < 0 || t->ti_sec > 60)
		goto quit;

	t->ti_hund = 0;
	return (dostounix(d,t));
quit :
	return 0L;
}

/* returncode: -1 if error; 0 if no new news; 1 new news available */
static int
newnews(string,mp,f)
char *string;
struct nntpsv *mp;
FILE *f;
{
	register int i,j;
	char *cp, *cp1, line[LineLen], groups[LineLen];
	struct g_list *ng, *hist, *ngp, *histp, *ptr;
	FILE *f1;
	int all = 1;

	if (check_one(string) == -1)
		return -1;

	cp = string;
	i = 1;

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

	strncpy(groups,string,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);
	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 = strcspn(cp,",");
			ng->str = (char *)callocw(1,j+1);
			strncpy(ng->str,cp,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(History,READ_TEXT,mp->s,0)) == NULLFILE)
		goto quit;

	for (;;) {
		if (fgets(line,LineLen,f1) == NULL)
			break;
		rip2(line);

		if (!all) {
			for(i = 3, cp = line; i; --i)
				cp = strchr(cp,' ') + 1;
			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 = strcspn(cp," ");
				hist->str = (char *)callocw(1,j+1);
				strncpy(hist->str,cp,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,' ') + 1;
		if ((mp->ftime = make_nntime(mp->datest,mp->timest,cp)) == 0) {
			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 (;;) {
				ptr = histp->next;
				free(histp->str);
				free(histp);
				histp = ptr;
				if (histp == NULLG)
					break;
			}
		}
	}
	fclose(f1);
	if (!all) {
		ptr = ngp;
		for (;;) {
			ptr = ngp->next;
			free(ngp->str);
			free(ngp);
			ngp = ptr;
			if (ngp == NULLG)
				break;
		}
	}
	free(mp->datest);
	free(mp->timest);
	return 1;
quit:
	return 0;
}

/* handles incoming newnews-cmd
 * returncode: -1 if error; 0 no groups or bad syntax; 1 success */
static int
donewnews(string,mp)
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);
		sendfile(f,mp->s,ASCII_TYPE,0);
		usputs(mp->s,NEol);
		ret = 1;
		break;
	}
	fclose(f);
	return ret;
}

/* change current newsgroup
 * returncode: -1 if error; 0 success; 1 no newsgroup */
static int
dogroup(mp,buf)
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;
		}
	case -1:
		usputs(mp->s,error);
		return -1;
	}
}


/* checks if newsgroup is selected
 * returncode: -1 no group selected; 0 success */
static int
check_grp(mp)
struct nntpsv *mp;
{
	if(mp == NULLNNTPSV || mp->path == NULLCHAR) {
		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(bp,mp)
char *bp;
struct nntpsv *mp;
{
	FILE *f;
	char tmp[LineLen];

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

	for (;;) {
		if (fgets(tmp,LineLen,f) == NULL)
			break;
		if (check_blank(tmp))
			break;
        if (strnicmp(tmp,Hdrs[MSGID],12)==0) {
			fclose(f);
			strcpy(bp,strchr(tmp,' '));
			rip2(bp);
			return 1;
		}
	}
	fclose(f);
	strcpy(bp,"\0");
	return 0;
}

/* gets next news of newsgroup
 * returncode: -1 if error; 0 no news; 1 success */
static int
get_next(mp)
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) {
			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) {
			fclose(f);
			return 1;
		}
	}
}

static int
art_ret(mp)
struct nntpsv *mp;
{
	if(get_id(mp->buf,mp) == -1)
		return -1;
	return usprintf(mp->s,retrieve,mp->pointer,mp->buf,artmsg);
}

static int
doarticle(buf,mp)   /* Sends article <message-id> to the client */
char *buf;
struct nntpsv *mp;
{
	FILE *f;
	char *p, *p2, *holds, line[LineLen];

	if((p = strchr(buf,'<')) == NULLCHAR
	 || (f = open_file(History,READ_TEXT,mp->s,0)) == NULLFILE)
		return 0;

	for(;;) {
		if (fgets(line,LineLen,f) == NULL)
			break;
		if (strstr(line,p) != NULL) {
			fclose(f);

			p = (strchr(line,' ')) + 14;  	/* point to the first newsgroup */
			p2 = strchr(p,'/');	  			/* point to article number */
			mp->hold_i = mp->pointer;   	/* save current pointer */
			holds = strdup(mp->path);      /* save path of current article */
			mp->pointer = atoi((p2 + 1)); 	/* get the article number */

			if (mp->path != NULLCHAR) {
				free(mp->path);
				mp->path='\0';
			}

			*p2 = '\0';
			rip2(p);

			if (strncmp(p+1,"JUNK",6) == 0) {
				sprintf(mp->buf,"%s",Forward);
				mp->path = strdup(mp->buf);
			} else
				get_path(p,mp);

			if ((f = open_message(mp,f)) == NULLFILE) {
				usputs(mp->s,error);
				return 0;
			}
			art_ret(mp);
			sendfile(f, mp->s, ASCII_TYPE,0);
			fclose(f);
			usputs(mp->s,NEol);
			free(mp->path);
			mp->path=strdup(holds);
			mp->pointer=mp->hold_i;
			free(holds);
			return 1;
	    }

	}
	fclose(f);
	usputs(mp->s,noart);
	return 0;
}

/* checks for a minimum header
 * returncode: -1 if error; 0 complete; 1 not complete */
static int
garbled(f)
FILE *f;
{
/*
 * The minimum requirement for an incoming article is that it must have
 * a newsgroups: line, a message-id: line, and a blank line.  This is
 * pretty forgiving.
 */
	char line[LineLen];
	int ok = 0;

	rewind(f);
	for(;;) {
		if (fgets(line,LineLen,f) == NULL)
			break;
        if (strnicmp(line,Hdrs[NEWSGROUPS],12) == 0)
			ok |= 1;
        if (strnicmp(line,Hdrs[MSGID],12) == 0)
			ok |= 2;
		if (ok == 3)
			break;
		if (check_blank(line))
			break;
	}
	rewind(f);
	return (ok == 3) ? 0 : 1;
}

/* returncode: -1 if error; 0 success */
static int
get_article2(mp, id)
struct nntpsv *mp;
char *id;
{
	FILE *f;
	int ret = -1;

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

	mp->ap = (struct article *)callocw(1,sizeof(struct article));
	mp->id = strdup(id);
	usputs(mp->s,sendart);

	if(recv_file(f,mp->s) == (-1)) {
		free((struct article *)mp->ap);	/* yc1dav */
		goto quit;
		}
	if ((ret = garbled(f)) != 0) {
		usputs(mp->s,transnotok);
		free((struct article *)mp->ap);	/* yc1dav */
		goto quit;
		}
	if(xfer_article2(f,mp) == 0)
	    goto quit;
	usputs(mp->s,transok);

/* 	free_ap(mp);	     	<------------ */
quit:
/*  free((char *)mp->ap);  <------------ */
/*	free(mp->id);      <----- yc1dav */
	fclose(f);
	return ret;
}

void
poll(p)
void *p;
{
	newproc("NNTP Client", 2048, nntppoll, 0, p, NULL, 0);
}

static void
nntpserv(s,unused,p)
int s;
void *unused;
void *p;
{
	struct nntpsv *mp;
	FILE *fp;
	long t;
	int cnt;
	char **cmdp, *arg, *cp, *cmd, buf[LineLen];

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

	if (!Filecheck)
		if(check_system()) {
			usprintf(s,fatal,"STRUCTURE");
			log(s,"NNTP error - FILE STRU");
			close_s(s);
			return;
		}
	if((mp = (struct nntpsv *)callocw(1,sizeof(struct nntpsv))) == NULLNNTPSV) {
		usprintf(s,Nospace);
		close_s(s);
		return;
	}
	mp->s = s;
	mp->debug = 0;
	mp->path = NULLCHAR;

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

	usprintf(s,nnversion,(Filecheck) ? "0" : "1",Host,Version,cp);

loop:
	if ((cnt = recvline(s,buf,LineLen)) == -1) {
		/* He closed on us */
		goto quit;
	}
	rip2(buf);
	cmd = buf;

	/* Translate entire buffer to lower case */
	for(cp = cmd; *cp != '\0' ;cp++) {
		if ( *cp == ' ' ) break;
		*cp = tolower(*cp);
	}
	/* Find command in table; if not present, return syntax error */
	for(cmdp = commands; *cmdp != NULLCHAR; cmdp++)
		if(strnicmp(*cmdp,cmd,min(strlen(*cmdp),strlen(cmd))) == 0)
			break;

	if(*cmdp == NULLCHAR){
		usputs(mp->s,badsyntax);
		goto loop;
	}
	arg = &cmd[strlen(*cmdp)];

	/* Skip spaces after command */
	while(*arg == ' ')
		arg++;

	/* Execute specific command */
	switch(cmdp - commands) {
	case QUIT_CMD:
		goto quit;
	case NEWNEWS_CMD:
		if ((cp = strchr(buf,' ')) == NULLCHAR) {
			usputs(mp->s,badsyntax);
			break;
		}
		cp++;
		donewnews(cp,mp);
		break;
	case IHAVE_CMD:
		if ((cp = strchr(buf,'<')) == NULLCHAR) {
			usputs(mp->s,badsyntax);
			break;
		}
		if (check_article(cp) == 1) {
			usputs(mp->s,notwanted);
			break;
		}
		if(get_article2(mp,cp))
			goto quit;
		break;
	case HELP_CMD:
		usprintf(mp->s,"100 %s - help follows:\n",Host);
		if ((fp = open_file(Nhelp,READ_TEXT,0,0)) != NULLFILE ) {
			sendfile(fp,mp->s,ASCII_TYPE,0);
			fclose(fp);
		} else {
			usputs(mp->s,help);
		}
		usputs(mp->s,NEol);
		break;
	case XINFO_CMD:
		if ((fp = open_file(NInfo,READ_TEXT,0,0)) != NULLFILE ) {
			usprintf(mp->s,"100 %s - xinfo follows:\n",Host);
		    sendfile(fp,mp->s,ASCII_TYPE,0);
			fclose(fp);
		} else {
			usputs(mp->s,xinfo);
		}
		usputs(mp->s,NEol);
	    break;
	case LIST_CMD:
		if ((fp = open_file(Active,READ_TEXT,mp->s,0)) != NULLFILE) {
			sendfile(fp,mp->s,ASCII_TYPE,0);
			fclose(fp);
			usputs(mp->s,NEol);
		}
		break;
	case GROUP_CMD :
		dogroup(mp,&buf[0]);
		break;
	case HEAD_CMD : {
		if (check_grp(mp))
			break;
		if (get_id(mp->buf,mp) == 0)
			break;
		usprintf(mp->s,head,mp->pointer,mp->buf,artmsg);
		if ((fp = open_message(mp,fp)) == NULLFILE)
			break;
		for (;;) {
			if ((fgets(mp->buf,LineLen,fp)) == NULL)
				break;
			if (check_blank(mp->buf))
				break;
			usprintf(mp->s,"%s",mp->buf);
		}
		fclose(fp);
		usputs(mp->s,NEol);
		break;
	}
	case BODY_CMD : {
		if (check_grp(mp))
			break;
		if (get_id(mp->buf,mp) == 0)
			break;
		usprintf(mp->s,body,mp->pointer,mp->buf,artmsg);
		if ((fp = open_message(mp,fp)) == NULLFILE)
			break;
		mp->hold_i = 0;
		for (;;) {
			if ((fgets(mp->buf,LineLen,fp)) == NULL)
				break;
			if (mp->hold_i)
				usputs(mp->s,mp->buf);
			if (check_blank(mp->buf))
				mp->hold_i = 1;
		}
		fclose(fp);
		usputs(mp->s,NEol);
		break;
	}
	case STAT_CMD : {
		if (check_grp(mp))
			break;
		if (get_id(mp->buf,mp) == 0)
			break;
		usprintf(mp->s,statistics,mp->pointer,mp->buf,artmsg);
		break;
	}
	case ARTICLE_CMD : {
		if (strchr(buf,'<') != NULLCHAR) {
	      doarticle(buf,mp);
	      break;
	    }
		if (check_grp(mp))
			break;
		cp = strchr(buf,'e');
	    cp++;
		if ((cnt < 9 ) || (check_blank(cp))) {
			if ((fp = open_message(mp,fp)) == NULLFILE)
				break;
			art_ret(mp);
			sendfile(fp,mp->s,ASCII_TYPE,0);
			fclose(fp);
			usputs(mp->s,NEol);
			break;
		}
		if ((cp = strpbrk(buf,"0123456789")) != NULLCHAR) {
			cnt = atoi(cp);
			if ((cnt > mp->last) || ( cnt < mp->first)) {
				usputs(mp->s,noart);
				break;
			}
			mp->pointer = cnt;
			if ((fp = open_message(mp,fp)) == NULLFILE)
				break;
			art_ret(mp);
			sendfile(fp,mp->s,ASCII_TYPE,0);
			fclose(fp);
			usputs(mp->s,NEol);
			break;
		}
	    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;
		}
	    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;
	/* This two following cmds currently are not used for much */
	case DEBUG_CMD:
		mp->debug = (mp->debug == 0) ? 1 : 0;
		usprintf(mp->s,debug,(mp->debug == 0) ? "OFF" : "ON");
		break;
	case SLAVE_CMD :
		mp->slave = (mp->slave == 0) ? 1 : 0;
		usprintf(mp->s,slave,(mp->slave == 0) ? "OFF" : "ON");
		break;
	}
	goto loop;

quit:
	usputs(mp->s,closing);
	log(mp->s,"close NNTP");
	close_s(mp->s);
	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 --------------------------- */

#ifdef MSDOS
static char * near
#else
static char *
#endif
mkreplypath(data)
FILE *data;
{
	char buf[LINELEN], *cp, *cp1, *path;

    sprintf(buf,"%sNNTP_GATE@%s",Hdrs[PATH],Hostname);
	path = strdup(buf);
	rewind(data);
	while((fgets(buf,LINELEN,data)) != 0) {
		if(htype(buf) == DATE)
			break;
		if(htype(buf) == RECEIVED) {
			cp = strchr(buf,' ');
			cp++;
			cp = strchr(cp, ' ');
			cp++;
			cp1 = strpbrk(cp, ". \0");
			*cp1 = '\0';
			cp1 = mallocw(strlen(path)+strlen(cp)+2);  /* */
			sprintf(cp1,"%s!%s",path,cp);
			free(path);
			path = cp1;
		}
	}
	return(path);
}

int
nnGpost(data, from, le)
FILE *data;
char *from;
struct list *le;
{
	struct nntpsv *mp;
	char buf[LINELEN], *cp;
	FILE *f, *idf;
	int32 id;
	int strl; /* yc1dav */
	long 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';
	strcpy(cp,le->val);
	strl = 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
	*-----------------------------------------------------------------*/
	rewind (data);
	while((fgets(buf,sizeof(buf),data))!=0)   {
		if (htype(buf)== SUBJECT)
			break;
		continue;
	}
	fputs((*buf == 0) ? "Subject: (none)\n" : buf,f);
	/*-------------------------------------------------------------------*
	* use msgid of original message
	*--------------------------------------------------------------------*/
	rewind(data);
	while((fgets(buf,sizeof(buf),data))!=0)   {
		if (htype(buf)== MSGID)
			break;
		continue;
	}
	if (*buf == 0)   {
		sprintf(buf,"%s/sequence.seq",Mailqdir);
		idf = open_file(buf,"r+",0,1);
		id = atol(fgets(buf,LineLen,idf));
		rewind(idf);
		fprintf(idf,"%ld",id+2);
		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,'<');
		strcpy(mp->buf,cp);
		if (check_article(mp->buf))
			goto quit;
	}
	rewind (data);
	time(&currtime);
	fprintf(f,"Sender: NNTP@%s\n",Hostname);
	/*-------------------------------------------------------------------*
	* message follows                                                    *
	*--------------------------------------------------------------------*/
	fputs("Comments: Message transferred from SMTP\n",f);
	while((fgets(buf,sizeof(buf),data))!=0)   {
		switch(htype(buf))   {
			case FROM:
			case MSGID:
			case RECEIVED:
				fgets(buf,sizeof(buf),data); /*eat the id*/
				continue;
		}
		fputs(buf,f);
	}
	rewind(f);
	mp->id = strdup(mp->buf);
	xfer_article2(f,mp);
/*
	log(-1,"NNGT: transfer: Msg %s",mp->id);
*/
quit:
	fclose(f);
	free(mp);
	return 0;
}

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

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

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

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

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

	Snntp = socket(AF_INET,SOCK_STREAM,0);
	bind(Snntp,(char *)&lsocket,sizeof(lsocket));
	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 */
        sockmode(s,SOCK_ASCII);
        /* Spawn a server */
        newproc("NNTP server",2048,nntpserv,s,NULL,NULL,0);
	}
	return 0;
}

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

/* ---------------------------- Subcmds --------------------------- */

/* lists active newsgroups
 * returncode: -1 if error; 0 success */
static int
donnactive(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	FILE *fp;
	char line[80], *cp;

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

	tputs("Msg#  next  mod newsgroup\n");

	for(;;) {
		if (fgets(line,sizeof(line),fp) == NULL)
			break;
		if((cp = strchr(line,' ')) == NULLCHAR)
			break;
		*cp = '\0';
		rip(++cp);
		tprintf("%s   %s\n",cp,line);
	}
	fclose(fp);
	return 0;
}

/* add nntp servers to list */
static int
donnadds(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	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;
	}
	if (argc > 3) {
		int i;
		if (np->newsgroups == NULLCHAR) {
			np->newsgroups = callocw(1,LineLen);
			*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;
			} else if ((strlen(np->newsgroups)+strlen(argv[i])+2) >= LineLen)
				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_timer(&np->nntpt);		/* and fire it up */
	return 0;
}

/* drops nntp servers from list */
static int
donndrops(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	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(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	FILE *t, *f, *o;
	char path[LineLen], line[LineLen], *cp, newsname[25], *cp1;
	struct ffblk blk;

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

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

	path[0] = '\0';
	line[0] = '\0';

	for(;;) {
		if (fgets(line,LineLen,f) == NULL)
			break;
		if (!strncmp(line,argv[1],strlen(argv[1]))) {
			cp = strchr(line,' ') + 1;
			strcpy(path,cp);
			break;
		}
	}
	fclose(f);

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

	rip2(path);
	cp = strrchr(path,'/');
	cp1 = &newsname[0];

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

	strcpy(line,path);
	strcat(line,"/*.*");
	if(findfirst(line,&blk,0)) {
		tputs("No news 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);
		for (;;) {
			if((t = temp_file(0,1)) == NULLFILE) {
				fclose(o);
				goto error;
			}
			sprintf(line,"%s/%s",path,blk.ff_name);
			/* Open the article */
			if ((f = open_file(line,READ_TEXT,0,1)) == NULLFILE) {
				fclose(t);
				fclose(o);
				goto error;
			}
			pwait(NULL);
			tputc('.'); /* One dot/article processed */
			tflush();

			for (;;) {
				if (fgets(line,LineLen,f) == NULL)
					break;
				fputs(line,t);
                if (!strnicmp(line,Hdrs[FROM],6)) {
					cp = strchr(line,' ')+1;
					fprintf(o,"From %s",cp);
				}
			}
			rewind(t);
			for(;;) {
				if (fgets(line,LineLen,t) == NULL)
					break;
				fputs(line,o);
			}
			fputc('\n',o);
			fclose(t);
			fclose(f);
			if (findnext(&blk))
				break;
		}
		rmlock(Mailspool,newsname);
	} else
		tputs("Mailfile is busy, try later");
	fclose(o);
	tputs("\n");

error:
	return 0;
}

static int
donnkick(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	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;
}

/* list nntp servers */
static int
donnlists(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	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 : "");
	}
	return 0;
}

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

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

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

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

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

		sprintf(buf,"%s/sequence.seq",Mailqdir);
		if ((idf = open_file(buf,"r+",0,1)) == NULLFILE) {
			tprintf("Please create %s dir ...\n",Mailqdir);
			fclose(f);
			goto done;
		}

		id = atoi(fgets(buf,LineLen,idf));
		rewind(idf);
		fprintf(idf,"%d",id+2);
		fclose(idf);

		if (Post->user == NULLCHAR) {
			tputs("User name? ");
			recvline(sp->input,buf,LineLen);
			rip2(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? ");
		recvline(sp->input,buf,LineLen);
		rip2(buf);
		if (check_blank(buf))
			goto done;
        fprintf(f,"%s%s\n",Hdrs[NEWSGROUPS],buf);

		tputs("Subject? ");
		recvline(sp->input,buf,LineLen);
        fprintf(f,"%s%s",Hdrs[SUBJECT],buf);
        fprintf(f,"%s<%d@%s>\n",Hdrs[MSGID],id,Hostname);
		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 (;;) {
			recvline(sp->input,buf,LineLen);
			if(strcmp(buf,".u\n") == 0
			  || strcmp(buf,".r\n") == 0) {
				tputs("Filename? ");
				recvline(sp->input,buf,LineLen);
				rip2(buf);
				if((ufp = open_file(buf,READ_TEXT,0,1)) != NULLFILE) {
					while(fgets(buf,LineLen,ufp) != NULL)
						fputs(buf,f);
					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,LineLen,idf) != NULL)
					fputs(buf,f);
				fclose(idf);
			}
		}

loop:   tputs("\n[Send, Abort, Exit, List] ");
		recvline(sp->input,buf,LineLen);
		switch(tolower(buf[0])) {
		case 's':
			rewind(f);
			sprintf(mp->buf,"<%d@%s>",id,Hostname);
			mp->id = strdup(mp->buf);
			xfer_article2(f,mp);
			break;
		case 'l':
			rewind(f);
			for(;;) {
				if (fgets(buf,LineLen,f) == NULL)
					break;
				tputs(buf);
			}
			rewind(f);
			goto loop;
		case 'e':
			fclose(f);
			goto done;
		case 'a':
			break;
		default:
			goto loop;
		}
	    fclose(f);
		tputs("Post another? ");
		recvline(sp->input,buf,LineLen);
		if (tolower(buf[0]) == 'n')
			goto done;
	}

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

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

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

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

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

static int
donnreply(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	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,
	};

/* subcmd parser */
static int
donnprofile(argc,argv,p)
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[] = {
		"active",	donnactive,		0, 	  0, NULLCHAR,
		"add",		donnadds,		0, 	  3, "nntp add <nntpserv> <interval> [<groups>]",
		"drop",		donndrops,		0, 	  2, "nntp drop <nntpserv>",
		"dump", 	donndump, 		0, 	  2, "nntp dump <newsgroup> [<mailbox>]",
		"kick", 	donnkick, 		0, 	  2, "nntp kick <server>",
		"list",		donnlists,		0, 	  0, NULLCHAR,
		"post", 	donnpost,     	2024, 0, NULLCHAR,
		"profile",	donnprofile,	0, 	  0, NULLCHAR,
		"quiet",	donnquiet,		0,	  0, NULLCHAR,
		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
 */
FILE *
open_file(name,mode,s,t)
char *name;
char *mode;
int s;
int t;
{
	FILE *f;
	register char *cp;

	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)
			tprintf("Can't open %s: %s\n",name,sys_errlist[errno]);
	}
	return f;
}

/* main tempfile-opening routine
 * returncode: NULLFILE if error; filepointer success
 */
FILE *
temp_file(s,t)
int s;
int t;
{
	FILE *f;

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

/* Subroutine for setting and displaying int variables (with range check) */
int
setintrc(var, label, argc, argv, minval, maxval)
int16 *var;
char *label;
int argc;
char *argv[];
int minval;
int16 maxval;
{
	int tmp;

	if (argc < 2)
		tprintf("%s: %u\n", label, *var);
	else {
		tmp = atoi(argv[1]);
		if (isalpha(*argv[1]) || tmp < minval || tmp > maxval) {
			tprintf("%s must be %i..%i\n", label, minval, maxval);
			return 1;
		}
		*var = (int16)tmp;
	}
	return 0;
}
#endif /* NNTPS */

