int die(char *s)
{
	printf("dmie %s \n",s);
}
#ifdef VMS

#ifdef MULTINET
	#define NEED_DECLARE XXX
	#include "multinet_root:[multinet.include]errno.h"
	#include "multinet_root:[multinet.include.sys]types.h"
	#include "multinet_root:[multinet.include.sys]socket.h"
	#include "multinet_root:[multinet.include.sys]time.h"
	#include "multinet_root:[multinet.include.sys]ioctl.h"
	#include "multinet_root:[multinet.include.netinet]in.h"
	#include "multinet_root:[multinet.include]netdb.h"
#else
	#define UCX TRUE
	#include "fdset.h"
	#include <unistd.h>
	#include  <errno.h>
	#include  <types.h>
	#include  <stdio.h>
	#include  <socket.h>
	#include  <in.h>
	#include  <netdb.h>		/* change hostent to comply with BSD 4.3 */
	#include  <inet.h>
	#include  <ucx$inetdef.h>	/* INET symbol definitions */
	#define SOCKET_DEF TRUE
#endif
#endif

#ifdef unix
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <sys/time.h>
#include <unistd.h>
#ifdef LINUX
#include <linux/in.h>
#else /* This one works for solaris , why the hell isn't it standardized */ 
#include <netinet/in.h>
#endif
#define SOCKET_DEF TRUE
#endif

#ifdef SOCKET_DEF
#define socket_perror perror
#define socket_close close
#define socket_open open
#define socket_read read
#define socket_ioctl ioctl
#define socket_write write
#define bcopy(src,dst,len) memmove(dst,src,len)

#endif

#include <stdio.h>
#include "dnews.h"
#include "str.h"
#include "lib.h"
#include "emsg.h"
#define EOL "\015\012"

#ifdef NEED_DECLARE
int socket(unsigned int address,unsigned int type,unsigned int protocol);
void socket_perror(char *s);
int socket_read(int channel, char *buffer, int size);
int socket_write(int channel, char *buffer, int size);
int connect();
int select();
int socket_close();
int setsockopt();
int bind();
int listen();
int accept();
char *bcopy();
char *bset();
char *bzero();
int htons();
int sleep();
int inet_ntoa();
int getdtablesize();
#endif

#include <stdlib.h>
#include <string.h>
#include "nntp.h"

void channel_read(int c, int gotio);
void event_wait(void);
void suck_do(void);
void lib_sleep(int x);
void chan_init(int c);

static int read_again[MAX_CHAN];

static short s_listen,chan[MAX_CHAN]; /*Should keep list of inuse ones.*/
static struct sockaddr_in chan_sin[MAX_CHAN];
static char *chan_outbf[MAX_CHAN];
static String chan_write[MAX_CHAN];
static int chan_busy[MAX_CHAN];
static int chan_close[MAX_CHAN];
int nchan;

static	struct sockaddr_in sin, sin2;
static	struct hostent *hp;
static	struct servent *sp;
#define BFSZ 4000

#define emsg printf
#define dmsg printf
#define imsg printf
#define filemsg printf


int nntp_chan_init(int i);
void nntp_doclose(int c);
int nntp_chan_init(int i)
{
	int noblock=1;
	chan_close[i] = FALSE;
	socket_ioctl(chan[i],FIONBIO,&noblock);
	str_setlen(&chan_write[i],0); /* No more to write */
	return TRUE;
}
int nntp_chan(int i)
{
	return (int) chan[i];
}
void nntp_init(void)
{
	int n;
#ifdef UCX
	char on=1;
#else
	int on=1;
#endif
	char buf[256];
	int i;

	for (i=0; i<MAX_CHAN; i++) {
		chan[i] = -1;	
		str_init(&chan_write[i]);
		chan_outbf[i] = malloc(BFSZ+2);
	}
	/*
	 *  Create an IP-family socket on which to listen for connections
	 */

	s_listen = socket(AF_INET, SOCK_STREAM, 0);
	if (s_listen < 0) {
		socket_perror("tcpechoserver: socket");
		filemsg("nntp: Can't open socket\n");
		exit(0x10000000);
	}

	sp = getservbyname("nntp", "tcp");
	if (sp == NULL) {
		emsg("nntp: Can't getservbyname \n");
		fprintf(stderr, "tcpechoserver: nntp/tcp: unknown service\n");
		exit(0x10000000);
	}

	if (setsockopt(s_listen, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
		filemsg("nntp: Can't setsockopt \n");
		socket_perror("tcpnntpserver: setsockopt");
		exit(0x10000000);
	}

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = sp->s_port;

	if (bind(s_listen,(struct sockaddr *)  &sin, sizeof (sin)) < 0) {
		filemsg("nntp: Can't bind \n");
		socket_perror("tcpnntpserver: bind");
		exit(0x10000000);
	}

	if (listen(s_listen, 5) < 0) {
		filemsg("nntp: Can't set listen \n");
		socket_perror("tcpnntpserver: listen");
		exit(0x10000000);
	}
	dmsg("nntp: init done\n");
}
static struct timeval timeout;
static struct timeval timecopy;
void nntp_setsleep(int x)  /* Used when background job wants to be called alot*/
{
	timeout.tv_sec = x;
}
void nntp_wait(void)
{
	int i,nc;
	fd_set readfd,writefd,exceptfd;	

	dmsg("nntp: Wait start\n");

	timeout.tv_sec = 3; /*Wake up once a second*/
	timeout.tv_usec = 0;
again:
	FD_ZERO(&readfd);
	FD_ZERO(&writefd);
	FD_ZERO(&exceptfd);
	FD_SET(s_listen,&readfd);
	for (i=0; i<nchan; i++) if (chan[i]>=0) {
		   if (chan_close[i]) if (!chan_busy[i]) nntp_doclose(i);
	}
	for (i=0; i<nchan; i++) if (chan[i]>=0) {
		if (chan[i]>=0) {
		   FD_SET(chan[i],&readfd);
		   FD_SET(chan[i],&exceptfd); 
		   if (chan_busy[i]) FD_SET(chan[i],&writefd);
		}	   
	}
   	timecopy = timeout; /* Because linux clears the value each call */
   	nc = select(getdtablesize(),&readfd,&writefd,&exceptfd,&timecopy);
   if (nc<0) {

      for (i=0; i<nchan; i++) if (chan[i]>=0)  {
	 FD_ZERO(&readfd);
	 FD_ZERO(&writefd); FD_ZERO(&exceptfd);
	 FD_SET(chan[i],&readfd);
   	 timecopy.tv_sec = 0;
	 nc = select(getdtablesize(), &readfd, &writefd, &exceptfd, &timecopy);
	 if (nc<0) {
	   filemsg("nntp: which bad Status [%d] %d =%d \n",i,chan[i],nc);
	   nntp_doclose(i);
	 }
      }
   } else {
        if (FD_ISSET(s_listen,&readfd)) {emsg("nntp: Newchan\n"); nntp_newchan();}
	for (i=0; i<nchan; i++) if (chan[i]>=0) {
		if (FD_ISSET(chan[i],&exceptfd)) {
			dmsg("nntp: Except condition on channel %d %d \n",i,chan[i]);
		}
		if (chan_busy[i]) if (FD_ISSET(chan[i],&writefd)) {
			nntp_write_now(i);
		}
		if (FD_ISSET(chan[i],&readfd)) {
		 /* dmsg("nntp: chan_read %d\n",i); */
		  channel_read(i,TRUE); 
		  /* dmsg("nntp: chan_read_done \n"); */
		}
	}
	suck_do(); /*do back ground jobs */
   }
      goto again;
}
int nntp_getchan(void)
{
	/* shoould search thru list for unallocated one.*/
	int i;
	for (i=0; i<nchan ; i++) {
		if (chan[i]<0) {
			return i;
		}
	}	
	return nchan++;
}
void nntp_close(int c)
{	
	wmsg("chan: que close  %d %d \n",c,chan[c]);
	chan_close[c] = TRUE;
}
void nntp_doclose(int c)
{	
	wmsg("chan: real close  %d %d \n",c,chan[c]);
 	if (chan[c]<0) emsg("nntp: Channel is not open, PANIC, %d %d \n",c,chan[c]); 
	if (socket_close(chan[c])<0) imsg("nntp: Channel close failed\n");
	chan[c] = -1;
	chan_close[c] = FALSE;
	if (c==nchan-1) nchan--; /* should search backwards too, but doesn' matter much*/
}
void nntp_newchan(void)
{
	char buf[1000];
	int n=nntp_getchan(); 
	int sizesin=sizeof(chan_sin[0]);
	int noblock=1;
	int status;
	char *canpost;
   	
	dmsg("nntp: Newchan step 1 [%d]\n",n);
	chan[n] = accept(s_listen,(struct sockaddr *)  &chan_sin[n], &sizesin);
	dmsg("nntp: Newchan step 2 %d \n",chan[n]);
	if (chan[n] < 0) { 
	  socket_perror("tcpnntpserver: accept"); 
	  filemsg("Socket newchan failed\n");
	  return;
	}
	hp = gethostbyaddr(&chan_sin[n].sin_addr, sizeof(chan_sin[n].sin_addr), AF_INET);
	dmsg("nntp: newchan step ipnum %s \n",inet_ntoa(chan_sin[n].sin_addr));
	if (hp) {
		emsg("name %s\n", hp->h_name);
	}
 	wmsg("nntp: open channel %d %d \n",n,chan[n]);

	status = 201;
	canpost = "posting disabled";
	canpost = "posting OK"; status=200;
	sprintf(buf, "%d %s Dynamic-NEWS Version %s, %s \015\012",
			status,"myname",DNEWS_VERSION,canpost);
	nntp_chan_init(n);
	nntp_write(n, buf, strlen(buf));
}
int nntp_open(char *dst_name)
{
	int dst_port=119;
	struct hostent *hp;
	struct sockaddr_in sin;
	int noblock=1;
	int st;
	int n=nntp_getchan(); 
	char bf[200],*s;

	if (strchr(dst_name,':')!=NULL) {
		strcpy(bf,dst_name);
		s = strchr(bf,':');
		*s = 0;
		dst_name = bf;
		if (s==NULL) emsg("nntp: Thats very strange\n");
		dst_port = atoi(s+1);
		if (dst_port==0) dst_port = 119;
	}

	hp = gethostbyname(dst_name);
	if (hp==NULL) {
		emsg("nntp: Hostname not found {%s} \n",dst_name);
		return -1;
	}

	chan[n] = socket(hp->h_addrtype,SOCK_STREAM, 0);
	if (chan[n]<0) { socket_perror("Socket"); return -1;}
	sin.sin_family = hp->h_addrtype;
	bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
	sin.sin_port = htons(dst_port);
	st = connect(chan[n],(struct sockaddr *) &sin, sizeof(sin));
        if (st<0) { socket_perror("connect"); return -1;}
	nntp_chan_init(n);
	return n;
}
int nntp_write(int c, char *bf, int sz)
{
	str_adds(&chan_write[c],bf,sz);
	chan_busy[c] = TRUE;
	return TRUE;
}
int nntp_write_now(int c)
{
	int sz;
	int r;
	int blen=str_len(&chan_write[c]);

	sz = BFSZ-1; 
	if (sz > blen) sz = blen;
	dmsg("nntp: write %d bytes \n",sz);
	ncpy(chan_outbf[c],str_s(&chan_write[c]),sz);
	if (blen>sz) {
		str_right(&chan_write[c],sz);	
	} else {
		str_setlen(&chan_write[c],0); /* No more to write */
		chan_busy[c] = FALSE;
	}
	printf("Writenow {%s} \n",chan_outbf[c]);
	r = socket_write(chan[c],chan_outbf[c],sz);
	if (r<0) {
		filemsg("nntp: socket_write error\n");
		nntp_close(c);
	}
	dmsg("nntp: write_done\n");
	return r;
}
int nntp_read(int c, char *bf, int sz)
{
  int r;
  int nbytes;
 
  if (chan[c]<0) {
	emsg("nntp: Channel is not open, PANIC, %d %d \n",c,chan[c]);
	return -1;
  }
#ifdef unix
   socket_ioctl(chan[c],FIONREAD,&nbytes);
  if (nbytes<sz) sz = nbytes;
#else
	nbytes = sz;
#endif
  r = 0; 
  if (nbytes>0) {
	/* dmsg("nntp: Socket read %d bytes \n",sz); */
	r =  socket_read(chan[c],bf,sz);
  }
	if (r<500) dmsg("nntp: Socket read_done, got %d bytes\n",r); 
  return r;
}
void hexdump(char *x)
{
   int i;
   printf("Hex: ");
   for (i=0; i<8; i++,x++)  {
      printf("%2x ",*x);
   }
  printf("\n");    
}
int nntp_isopen(int c)
{
	if (chan[c]<0) return FALSE;
	return TRUE;
}
char *nntp_hostname(void)
{
   static char host[200],dom[200],both[300];
   if (gethostname(host,190)!=0) emsg("nntp: Can't get host name\n");
   return host;
}
#ifdef NEED_DTABLE
int getdtablesize(void)
{
  return 30;
}
#endif




main()
{
	int c;

	nntp_init();
	nntp_wait();
}

enum {S_OPEN,S_MODE,S_LIST,S_GROUP,S_NEXT,S_LOOP,S_WAIT};
static char *state_name[]={ 
	"S_OPEN","S_MODE","S_LIST","S_GROUP","S_NEXT","S_LOOP","S_WAIT",NULL
};

int nntp_put(int chan, char *s)
{
	printf("Write {%s} \n",s);
	return nntp_write(chan,s,strlen(s));
}
static int state,newstate;
static char *wait_str="startxx";
#define wait_for(xx) newstate=state+1; state=S_WAIT; wait_str = xx;
void suck_do(void)
{
	static int c;
	if (newstate!=S_GROUP) printf("State %d {%s} wait {%s} \n",state,state_name[state],wait_str);
	switch (state) {
	  case S_OPEN:
		c = nntp_open("news.palm.cri.nz");
		wait_for("200");
		break;
	  case S_MODE:
		nntp_put(c,"mode reader\015\012");
		wait_for("200");
		break;
	  case S_LIST:
		nntp_put(c,"list\015\012");
		wait_for(".\015\012");
		break;
	  case S_GROUP:
		nntp_put(c,"group comp.os.vms\015\012");
		wait_for("211");
		break;
	  case S_NEXT:
		nntp_put(c,"next\015\012");
		state++;
		break;
	  case S_LOOP:
		state = S_LIST;
		break;
	  case S_WAIT:
		break;
	}
}
#define MAXREC 30000
#define INSIZE MAXREC
#define LF "\012"
void channel_read(int c, int xxx)
{
	int nn,rlen;
	char *s1,*s2;
	char tmp[200],rec[MAXREC];
	static int n[MAX_CHAN],r[MAX_CHAN];
	static char *buf[MAX_CHAN]; /* Increase to 8001 when debuging finished*/ 
	static int want;

	if (buf[c]==NULL) buf[c] = (char *) malloc(INSIZE);

	want = INSIZE-1-n[c];
	if (want>2000) want = 2000; /* Limit the size of chunks we process */
	if (want<=0) {
		emsg("chan: Record too big in item, throwing it away, tough\n");
		n[c] = 0;
	}
	nn = nntp_read(c, buf[c]+n[c],want);
	if (nn==0) emsg("fatal: Read zero bytes, asked for %d bytes \n",want);
	if (nn>0) n[c] +=nn;
	if (nn<=0) {nntp_close(c); n[c] = 0;}
	else if (n[c]>0)  {
		s1 = buf[c];
		s1[n[c]] = 0;	
		s2 = strstr(s1,LF);
		if (s2==NULL) return;
		for (;s2!=NULL;) {
			rlen = s2-s1+1;
			if (rlen>=(MAXREC-100)) {
				emsg("Record too big %d \n",rec);
				rlen = MAXREC-100;
			}
			ncpy(rec,s1,rlen); 
			/*dmsg("chanrec: Got rec n[%d]=%d (%d) \n",c,n[c],rlen);*/
			n[c] -= rlen;
			if (rec[rlen-2]!='\r') { /* add CR if missing */
				strcpy(rec+rlen-1,EOL); rlen++;
			}
			chan_read(c,rec,rlen);
			s1 = s2 + 1;
			s2 = strstr(s1,LF);		
		}
		memmove(buf[c],s1,strlen(s1)+1); 
	}
}
int chan_read(int c, char *ss, int sz)
{
	int r=0;
	
	if (newstate!=S_GROUP) printf("Chan_read, {%s} \n",ss);
	if (strncmp(ss,wait_str,strlen(wait_str))==0) {
		printf("Matched {%s} \n",wait_str);
		state=newstate;
		wait_str = "tennis.rackque.asdf";
	}
	return TRUE;
}

char *ncpy(char *dst, char *src, int len)
{
	strncpy(dst,src,len);
	dst[len] = 0;
	return dst;
}

#undef emsg
#undef dmsg
#undef wmsg
void emsg(va_list arg_list, ...)
{
	printf("emsg  \n");
}
void dmsg(va_list arg_list, ...)
{
	printf("dmsg  \n");
}
void wmsg(va_list arg_list, ...)
{
	printf("dmsg  \n");
}
