/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <descrip.h>
#include "netlibdef.h" 
#include "dnews.h"
#include "lib.h"
#include "emsg.h"
#include "nntp.h"

typedef struct 
	{unsigned short len; unsigned char type, class; char *ptr;} Str;
#define DEFSTR(NAME,BUFF,LEN) Str NAME = {LEN, DSC$K_DTYPE_T, DSC$K_CLASS_S, BUFF}
#define DEFSTR1(NAME) Str NAME
#define DEFSTR2(NAME,BUFF,LEN) NAME.len = LEN, NAME.type = DSC$K_DTYPE_T,NAME.class = DSC$K_CLASS_S, NAME.ptr = BUFF
typedef struct Iosb {short status,len,j1,j2;} IOSB;
#define EOL "\015\012"
int nntp_ast(int x);

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);
int vstatus(int r);

static int read_again[MAX_CHAN];
int nchan;

static void *l_new,*chan[MAX_CHAN]; /*Should keep list of inuse ones.*/
static IOSB chan_iosb[MAX_CHAN];
static char *chan_bf[MAX_CHAN];
static Str chan_str[MAX_CHAN];
static int chan_got[MAX_CHAN];
static int chan_block[MAX_CHAN];
static struct SINDEF l_sin;
static unsigned int l_sin_len=sizeof(l_sin);
static unsigned int l_len;
#define BFSZ 2000
static struct SINDEF ssin;
int vstatus2(char *s, int r);

void nntp_setblock(int c, int t)
{
	chan_block[c] = t;
}
void nntp_init(void)
{
	int n, on=1;
	char buf[256];
	int i,r;
	static IOSB iosb;
	unsigned int sin_len=sizeof(struct SINDEF);
	int backlog=5;
	int type = NETLIB_K_TYPE_STREAM;

	for (i=0; i<MAX_CHAN; i++) chan[i] = NULL;       
	for (i=0; i<MAX_CHAN; i++) {
		chan_bf[i] = malloc(BFSZ+2);
		chan_str[i].len = BFSZ-1;
		chan_str[i].type = DSC$K_DTYPE_T;
		chan_str[i].class = DSC$K_CLASS_S;
		chan_str[i].ptr = chan_bf[i];
		chan_got[i] = FALSE;
	}
	nchan = 1; /* chan 0 is the listener channel */

	r = netlib_socket(&chan[0],&type);  vstatus2("netlib_socket",r);
	memset(&ssin,0,sizeof(ssin));
	ssin.sin_w_family = NETLIB_K_AF_INET;
	ssin.sin_w_port = 119;
	ssin.sin_w_port = netlib_hton_word(&ssin.sin_w_port);
	ssin.sin_x_mbz[0] = 0;
	/* sin.sin_addr.s_addr = INADDR_ANY;*/
	r = netlib_bind(&chan[0],&ssin,&sin_len,&iosb);  vstatus2("bind",r);
	vstatus2("bind status",iosb.status);
	r = netlib_listen(&chan[0],&backlog,&iosb); vstatus2("listen",r);
	vstatus(iosb.status);
	
	nntp_accept();
}
void vmessage(int r)
{
	char bf[256];
	short rlen;
	DEFSTR1(msgstr);
	DEFSTR2(msgstr,bf,255);
	
	sys$getmsg(r,&rlen,&msgstr,NULL,NULL);
	bf[rlen] = 0;
	emsg("nntp: %s \n",bf);
}
int vstatus2(char *s, int r)
{
	if ((r&1)!=1) {emsg("nntp: Vstatus {%s} \n",s); vmessage(r);}
	return TRUE;
}
int vstatus(int r)
{
	if ((r&1)!=1) vmessage(r);
	return TRUE;
}
int nntp_accept(void)
{
	int r;
	memset(&l_sin,0,sizeof(l_sin));
	l_new = 0; 
	memset(&chan_iosb[0],0,sizeof(chan_iosb[0]));
/*	r = netlib_accept(&chan[0],&l_new,&l_sin,&l_sin_len,&l_len,&chan_iosb[0],nntp_ast,0); */
	r = netlib_accept(&chan[0],&l_new,NULL,NULL,NULL
		,&chan_iosb[0],nntp_ast,0);
	vstatus2("Accept",r);
	return TRUE;
}
static int timeout[2];
void nntp_setsleep(int x)  /* Used when background job wants to be called alot*/
{
	timeout[0] = -x*10000000;
	timeout[1] = -1;
}
void nntp_wait(void)
{
	int i;
	nntp_setsleep(2);
	for (;;) {
		sys$schdwk(NULL,NULL,&timeout,NULL); 
		sys$hiber(); /* Woken up by ast from timer or listen or other */
		if (chan_got[0]) {chan_got[0] = FALSE; nntp_newchan();}
		for (i=1; i<nchan; i++) if (chan_got[i]) {
			chan_got[i] = FALSE;
			channel_read(i,TRUE);
		}
		suck_do();
	}
}
int nntp_ast(int x)
{
	if ((x>MAX_CHAN) || (x<0)) return 1;
	chan_got[x] = TRUE;
	sys$wake(NULL,NULL);
	return 1;
}
int nntp_getchan(void)
{
	int i;
	for (i=0; i<nchan ; i++) {
		if (chan[i]==NULL) {
			chan_init(i); 
			return i;
		}
	}       
	chan_init(nchan); 
	return nchan++;
}
void nntp_close(int c)
{       
	int r;
	static int type=NETLIB_K_SHUTDOWN_RECEIVER;
	wmsg("chan: close  %d %d \n",c,chan[c]);
	sleep(2);
	r = netlib_shutdown(&chan[c],&type); vstatus2("shutdown",r);
	r = netlib_close(&chan[c]); vstatus2("close",r);
	chan[c] = NULL;
	if (c==nchan-1) nchan--; /* should search backwards too, but doesn' matter much*/
}
void nntp_newchan(void)
{
	char buf[1000];
	int n=nntp_getchan(); 
	struct INADDRDEF addr;
	struct SINDEF sin;
	int r;
	char *canpost;
	unsigned int l_sin_len=sizeof(addr);
	char num[300],nam[300];
	int num_len=0,nam_len=0;
	unsigned int sin_len=sizeof(sin);
	int status;
	unsigned int which = NETLIB_K_LOOKUP_DNS;
	DEFSTR1(numdes);
	DEFSTR1(namdes);

	DEFSTR2(numdes,num,200);
	DEFSTR2(namdes,nam,200);
	chan[n] = l_new;        
	strcpy(nam,"");   

	vstatus2("Chan_new status",chan_iosb[0].status);
	if ((chan_iosb[0].status & 1)!=1) goto do_return;
	r = netlib_getpeername(&l_new,&sin,&sin_len,&sin_len); vstatus2("peer",r);
	dmsg("nntp: get name\n");
	nam[0] = 0;
	if (strlen(config_get(C_NODNS))==0) {
		r = netlib_address_to_name(&l_new,&which,&sin.sin_x_addr,&l_sin_len,&namdes,&nam_len); vstatus2("address_to_name",r);
		nam[nam_len] = 0;
	}
	dmsg("nntp: got name \n");
	r = netlib_addrtostr(&sin.sin_x_addr,&numdes,&num_len); vstatus2("addr_to_str",r);
	dmsg("nntp: got ADDr len %d  \n",num_len);
	num[num_len] = 0;
	chan_setipnum(n,num);
	if (strlen(nam)==0) strcpy(nam,num);
	chan_setipname(n,nam);  /* Should use num if address lookup failed*/
	wmsg("nntp: open channel %d %d %s \n",n,chan[n],chan_getipname(n));

	status = 201;
	canpost = "posting disabled";
	nntp_chan_init(n);
	if (chan_canpost(n)) canpost = "posting OK"; status=200;
	sprintf(buf, "%d %s Dynamic-NEWS Version %s, %s \015\012",
			status,config_get(C_MYNAME),DNEWS_VERSION,canpost);
	nntp_write(n, buf, strlen(buf));
	nntp_queread(n); /* Que a read for this channel */
do_return: ;
	nntp_accept(); 
	/*Que the next accept call */
}
int nntp_open(char *dst_name)
{
	static unsigned short dst_port=119;
	int st;
	int v,r;
	int n=nntp_getchan(); 
	char bf[200],*s;
	IOSB iosb;
	static DEFSTR1(hostdes);
	static unsigned int dnslookup=NETLIB_K_LOOKUP_DNS;
	int nlist;
	unsigned int nlistmax=10;
	struct INADDRDEF addrlist[11];
	struct SINDEF sin;
	unsigned int sin_len=sizeof(sin);
	int type = NETLIB_K_TYPE_STREAM;
	DEFSTR2(hostdes,dst_name,strlen(dst_name));

	imsg("Open {%s} %d %p\n",dst_name,n,chan[n]);
	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;
	}
	r = netlib_socket(&chan[n],&type);  vstatus2("open_socket",r);
	v = netlib_name_to_address(&chan[n],&dnslookup,&hostdes,addrlist,
		&nlistmax,&nlist);
	vstatus2("nametoaddr",v);
	memset(&sin,0,sizeof(sin));
	sin.sin_w_family = NETLIB_K_AF_INET;
	sin.sin_w_port = netlib_hton_word(&dst_port);
	sin.sin_x_addr = addrlist[0];
	r = netlib_connect(&chan[n],&sin,&sin_len,&iosb); vstatus2("open_connect",r);
	vstatus2("open_connectstat",iosb.status);

	chan_setipnum(n,dst_name);
	chan_setipname(n,dst_name);
	nntp_chan_init(n);
	nntp_queread(n);
	return n;
}
int nntp_write(int c, char *bf, int sz)
{
	int r;
	DEFSTR1(bfdes);
	DEFSTR2(bfdes,bf,sz);
	r = netlib_write(&chan[c],&bfdes); vstatus2("write",r);
	return 0;
}
int nntp_queread(int c)
{
	int r;
	static int xtimeout[2];
	xtimeout[0] = -100;
	xtimeout[1] = -100;
	chan_iosb[c].status = 1;
	chan_iosb[c].len = 0;
	dmsg("nntp: que_read.\n");
	r = netlib_read(&chan[c],&chan_str[c],NULL,NULL,NULL,&xtimeout
		,&chan_iosb[c],nntp_ast,c); vstatus2("que read",r);
	return TRUE;
}
int nntp_read(int c, char *bf, int sz)
{
	int r;
	int got;
	int rlen;
	char *cbf = chan_bf[c];

	if (chan_block[c]) {
		for (;!chan_got[c];) sys$hiber();
	}
	got = chan_iosb[c].len;
	cbf[got] = 0;
	vstatus2("nntp_read",chan_iosb[c].status);
	if ((chan_iosb[c].status & 1) !=1) return -1;
	dmsg("nntp: nntp_read, got %d bytes \n",got);
	rlen = got;
	if (sz<rlen) rlen = sz;
	ncpy(bf,chan_bf[c],rlen);

	if (rlen<got) {
		memmove(cbf,cbf+rlen,got-rlen);
		chan_iosb[c].len = got-rlen;
		chan_got[c] = TRUE;
		return rlen;
	}
	dmsg("nntp: nntp_read, ask for more \n");
	chan_got[c] = FALSE;
	nntp_queread(c);
	return rlen;
}
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]==NULL) return FALSE;
	return TRUE;
}
char *nntp_hostname(void)
{
   return "UNKNOWN-HOST.PUT.NAME.IN.NEWS.CONF";
}

int nntp_chan(int i)
{
	return (int) chan[i];
}
int nntp_outsz(int c)
{
	return 0;
}
int nntp_isbusy(int c)
{
	return 0;
}
int nntp_chan_init(int i)
{
	chan_got[i] = FALSE;
	nntp_setblock(i,FALSE);
	if (chan_bf[i] == NULL) {
		chan_bf[i] = malloc(BFSZ+2);
		chan_str[i].len = BFSZ-1;
		chan_str[i].type = DSC$K_DTYPE_T;
		chan_str[i].class = DSC$K_CLASS_S;
		chan_str[i].ptr = chan_bf[i];
		chan_got[i] = FALSE;
	}
	return TRUE;
}
