/*
Priority boost system

  If enabled, then any channel that has sucked more than 10mb of data, drop it's
  priority.

  Only do 'low priority' channels, if 'all high priority' ones are 'done' or if
  more than 1 second has elapsed.  

*/

#include "maxchan.h"
#ifdef INCREASE_FD
#define FD_SETSIZE MAX_CHAN
#endif

/*
** For select(), FD_SETSIZE determines the number of socket descriptors in
** 'fd_array.' This number can be increased for version v4.11 and beyond of
** NLMLib.NLM by calling Set_FD_SETSIZE() and using a dynamic version of
** 'fd_set.'
*/

#ifdef NETWARE
#define FD_SETSIZE 120
#endif

/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
#ifdef VMS
#define GOT_TIME TRUE
#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"
#endif

#ifdef UCX
#define NEED_DTABLE TRUE
#define socket_perror perror
#define FD_SETSIZE 256
#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

#ifndef isunix
#define isunix true
#endif
#ifdef OS2
#undef isunix
#endif
#ifdef NETWARE
#undef isunix
#endif
#ifdef Mac
#undef isunix
#endif
#ifdef WIN32
#undef isunix
#endif
#ifdef VMS
#undef isunix
#endif

#ifdef OS2
#define BSD_SELECT   TRUE
#endif


#ifdef WIN32
#define WIN_SOCK TRUE
#endif

#ifdef __MWERKS__ /*RHP we will take this to mean we are making a Mac version*/
#include "Mac.h"
#define WIN_SOCK TRUE
#endif


#ifdef WIN_SOCK
#define FD_SETSIZE MAX_CHAN /* This should let us have this many connectins ??*/


#include <winsock.h>
#ifdef __MWERKS__
#define socket_close mac_socket_close
#else
#define socket_close closesocket
#endif
#define socket_ioctl ioctlsocket
#define socket_read(s,b,l) recv(s,b,l,0)
#define socket_write(s,b,l) send(s,b,l,0)
#define GOT_SOCKADDR TRUE
#define NOTUNIX TRUE
#endif

#ifdef __MWERKS__
/* use asynchronous DNS lookups on Mac */
#include "macdns.h"
#define gethostbyname macgethostbyname
#define gethostbyaddr macgethostbyaddr
#endif

#ifdef NETWARE
#define NOTUNIX TRUE
#define GOT_SOCKADDR TRUE
#define NEED_DTABLE TRUE
#endif


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

#ifdef OS2
#include <sys/select.h>
#include <nerrno.h>
#include <utils.h>
#undef SOCKET_DEF
#define socket_ioctl ioctl
#define socket_read(s,b,l) recv(s,b,l,0)
#define socket_write(s,b,l) send(s,b,l,0)
#define socket_close(s) soclose(s)
#endif


#include "chan_dmy.h"
#include "chan.h"

#ifdef SOCKET_DEF
#define socket_close close
#define socket_open open
#define socket_read read
#define socket_ioctl ioctl
#define socket_write write
#define NEED_BCOPY TRUE

#endif
#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 *bset();
char *bzero();
int htons();
int sleep();
char *inet_ntoa();
int getdtablesize();
#endif


#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#ifndef WIN32
#include <fcntl.h>
#endif

#ifdef NETWARE
#define NETDB_USE_INTERNET 1
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define socket_read(s,b,l) recv(s,b,l,0)
#define socket_write(s,b,l) send(s,b,l,0)
#define socket_close close
#define socket_ioctl ioctl
NETDB_DEFINE_CONTEXT
NETINET_DEFINE_CONTEXT
#endif


#include "mutex.h"

#include "dnews.h"
#include "str.h"
#include "lib.h"
#include "emsg.h"

#include "nntp.h"

#ifdef __MWERKS__
int mac_socket_close(SOCKET s);
#endif
/* void channel_read(int c, int gotio); */
void nntp_usleep(int x);
int nntp_bind_in(struct sockaddr_in *sin);
int nntp_bind_out(struct sockaddr_in *sin);
void event_wait(void);
void suck_do(void);
void lib_sleep(int x);
void chan_init(int c);
int nntp_fastout(int c);
void nntp_speedout(int c, int sz);
char *slave_port(void);
void close_on_exec(int fd);

#ifdef WIN_SOCK
static SOCKET x_listen = INVALID_SOCKET;
static SOCKET chan[MAX_CHAN]; /*Should keep list of inuse ones.*/
#else
static short x_listen = -1;
static short chan[MAX_CHAN]; /*Should keep list of inuse ones.*/
#define SOCKET short
#endif
void nntp_setnolinger(SOCKET c);
SOCKET nntp_listen(char *host, int port);

void nntp_newchanx(SOCKET xsk,char *ipnum, char *lclip, SOCKET x_listen);
static struct sockaddr_in chan_sin[MAX_CHAN];
/*static char *chan_outbf[MAX_CHAN];*/
static String nntp_chan_write[MAX_CHAN];
static int chan_busy[MAX_CHAN];
static int cmd_busy[MAX_CHAN],cmd_anybusy;
static int cmd_thread[MAX_CHAN];
static int nntp_chan_close[MAX_CHAN];
static int chan_closetime[MAX_CHAN];
static int chan_block[MAX_CHAN];
static int chan_pend[MAX_CHAN];
static int chan_total[MAX_CHAN];
static int chan_ismulti[MAX_CHAN];
int nchan;
void socket_write_debug(char *s, int x);
static SOCKET l_chan[100];
static int l_port[100];
static int nlisten;

static  struct sockaddr_in xsin;
static  struct servent *sp,fakesp;
/* #define BIGSZ 4000  Temporary change for debugging mosaic */
#define BIGSZ 8000
void nntp_perror(char *s);
int nntp_errbusy(void);
char *chan_getipnum(int c);
int chan_isfree(int c);
void chan_newchan(int c);
void nntp_addidle(int x);
int getdtablesize();
int socket_write_small(SOCKET sk, char *data, int len);

#ifdef unixware
#define socket_ioctl fcntl
#endif
void socket_read_debug(char *s, int x);

void log_socket_write(int chan, char *bf, int sz);
int nntp_chan_init(int i);
void nntp_doclose(int c);
void nntp_closeit(int c);
int ihavein_clear(int i);
int tcp_waitfor_reason(SOCKET sk, int msec, char *reason);

int nntp_isslowdown(int c);
void nntp_lasterror(void);
void nntp_perror(char *s);
static int nfailed;
int dns_ageok(int i, int isreverse);
int dns_add(char *name, struct sockaddr_in *sin, int isok);
int dns_find_name(char *name);
int dns_find_addr(struct sockaddr_in *sin);
int nntp_getaddr(char *name, struct sockaddr_in *sin);
int nntp_getname(char *name, struct sockaddr_in *sin);
void nntp_setblockraw(SOCKET c, int t);
void nntp_newchan_all(void);
int nntp_newchan_waiting(void);


#ifndef WIN_SOCK
#define INVALID_SOCKET  -1
#endif
#define check(x) if (chan[x]==INVALID_SOCKET) {emsg("nntp: channel not open %d ",x); dnews_ex("nntp");} if (x<0) {emsg("nntp: bad channel %d ",x); dnews_ex("channel");}


#ifdef NEED_BCOPY
#define bcopy(src,dst,len) memmove(dst,src,len)
#endif

#ifdef HAS_TABLESIZE
#define getdtablesize xgetdtablesize
#endif


static int in_count[MAX_CHAN];
static double in_speed[MAX_CHAN];
static int in_time[MAX_CHAN];
static int out_count[MAX_CHAN];
static double out_speed[MAX_CHAN];
static int out_time[MAX_CHAN];
static int max_out=100000000;
static int max_out_total=900000000;
int nntp_ifnoblock(void);

int nntp_except(int c);
int hard_socket_close(SOCKET s);

int nntp_errclosed(void);
static int werrno;
static int max_xx;
static char *chan_lclip[MAX_CHAN];
#undef mymalloc
#undef myfree
#define mymalloc(x) mymalloc2(x,M_NNTP)
#define myfree(x) myfree2(x,M_NNTP)



#ifdef VMS
#include <stdio.h>
#include <iodef.h>

#ifdef TCPWARE
#include "tcpware_include:ucx$inetdef.h"
#else
#define UCX$C_IOCTL 2
#define FIONBIO -2147195266
#endif /* ucx$inetdef.h is missing in UCX 5.x (DECpaq morons at work) */
#include <errno.h>

#define IOC_OUT (int)0x40000000

extern int vaxc$get_sdc(), sys$qiow();

int ioctl (d, request, argp)
int d, request;
char *argp;
{
  int sdc, status;
  unsigned short fun, iosb[4];
  char *p5, *p6;
  struct comm
    {
      int command;
      char *addr;
    } ioctl_comm;
  struct it2
    {
      unsigned short len;
      unsigned short opt;
      struct comm *addr;
    } ioctl_desc;

  if ((sdc = vaxc$get_sdc(d)) == 0) {
      errno = EBADF;
      return -1;
  }

  ioctl_desc.opt  = UCX$C_IOCTL;
  ioctl_desc.len  = sizeof(struct comm);
  ioctl_desc.addr = &ioctl_comm;

  if (request & IOC_OUT) {
      fun = IO$_SENSEMODE;
      p5 = 0;
      p6 = (char *)&ioctl_desc;
  } else {
      fun = IO$_SETMODE;
      p5 = (char *)&ioctl_desc;
      p6 = 0;
  }
    
  ioctl_comm.command = request;
  ioctl_comm.addr = argp;

  status = sys$qiow (0, sdc, fun, iosb, 0, 0, 0, 0, 0, 0, p5, p6);
  if (!(status & 01)) {
      errno = status;
      return -1;
  }

  if (!(iosb[0] & 01)) {
      errno = iosb[0];
      return -1;
  }

  return 0;
}
#endif


static int dont_dup;
int nntp_setnodup(int x)
{
	dont_dup = x;
	return TRUE;
}

#ifdef WIN32
SOCKET dupaccept(SOCKET s, struct sockaddr FAR *addr, int FAR* addrlen)
{
	SOCKET sock, sock2;
	HANDLE p;
	int r;

	sock=accept(s, addr, addrlen);
	if(sock==INVALID_SOCKET)
		return sock;
	if (!config_true(C_SOCKET_NOINHERIT)) {
		return sock;
	}
	p=GetCurrentProcess();
	r=DuplicateHandle(p, (HANDLE) sock, p,(HANDLE *) &sock2, 0, FALSE, DUPLICATE_SAME_ACCESS);
	socket_close(sock);
	if(r==FALSE)
	{
		int i;
		LPVOID lpMsgBuf;

		i=GetLastError();
		FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
		i, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
		
		emsg("*** Warning *** Couldn't dup accepted socket: error %d (%s)", i, lpMsgBuf);
		LocalFree(lpMsgBuf);
		return INVALID_SOCKET;
	}
	return sock2;
}
SOCKET dupsocket(int af, int type, int protocol)
{
	SOCKET sock, sock2;
	HANDLE p;
	int r;

	sock=socket(af, type, protocol);
	if(sock==INVALID_SOCKET)
		return sock;
	if (dont_dup) return sock;
/*return sock;*/
	p=GetCurrentProcess();
	r=DuplicateHandle(p,(HANDLE ) sock, p,(HANDLE *) &sock2, 0, FALSE, DUPLICATE_SAME_ACCESS);
	CloseHandle((HANDLE) sock);
	if(r==FALSE)
	{
		int i;
		LPVOID lpMsgBuf;

		i=GetLastError();
		FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
		i, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
		
		emsg("*** Warning *** Couldn't dup socket: error %d (%s)", i, lpMsgBuf);
		LocalFree(lpMsgBuf);
		return INVALID_SOCKET;
	}
	return sock2;
}
#else
SOCKET socket_low(SOCKET sk);
SOCKET dupaccept(SOCKET s, struct sockaddr *addr, int *addrlen)
{
	SOCKET sock;

	sock=accept(s, addr, addrlen);
#ifdef SAVE_LOW
	sock = socket_low(sock);
#endif
	return sock;
};

#ifdef SAVE_LOW
static char low_used[2000];
static int nlow;
SOCKET socket_low(SOCKET sk)
{
	int i;
	if (sk==INVALID_SOCKET) return sk;
	if (sk>3000) return sk;
	for (i=300; i<2000; i++) {
		if ((i>=nlow) || (!low_used[i])) {
			low_used[i] = TRUE;
			if (nlow<=i) nlow = i+1;
			if (dup2(sk,i) == -1) {
				filemsg("dup2 failed, add ulimit -n 1024 to dnews_start.sh or dmulti_start.sh\n");
				low_used[i] = FALSE;
				return sk;
/*				socket_close(sk);
				return INVALID_SOCKET;
				*/
			}
			socket_close(sk);
			return (SOCKET) i;
		}
	}
	emsg("No handles free for socket_low\n");
	abort();
}
int socket_low_free(SOCKET xsk)
{
	int sk = (int) xsk;
	if (sk<0) return 0;
	if (sk>=2000) return 0;
	low_used[sk] = FALSE;
}
#endif
SOCKET dupsocket(int af, int type, int protocol)
{
	SOCKET sock;

	sock=socket(af, type, protocol);
	return sock;
}
#endif

int nntp_bigsz(void)
{
        static int bigsz;
        if (bigsz>0) return bigsz;
        bigsz = atoi(config_get(C_OUT_SIZE) );
        if (bigsz==0) bigsz = 64000; /* BIGSZ; */
        if (bigsz<4000) bigsz = 4000;
        if (bigsz>100000) bigsz = 100000;
        return bigsz;
}
int check_sock(int c, char *s);
int nntp_getpending(int c)
{
        return chan_pend[c];
}
void nntp_setpending(int c, int v)
{
        chan_pend[c] = v;
}
int nntp_getsk(int c)
{
	return chan[c];
}
int check_sock(int c, char *s)
{
        if (c<0) {
                emsg("%s: Invalid socket number %d\n",s,c);
                return FALSE;
        } 
        if (chan[c]==INVALID_SOCKET) {
                dmsg("nntp: (%s) Invalid Socket [%d] probably it closed while we were writing to it\n",s,c);
                return FALSE;
        }
        return TRUE;
}
int nntp_setmulti(int chan, int t)
{
	if (chan<0) return FALSE;
	if (chan>=MAX_CHAN) return FALSE;
	chan_ismulti[chan] = t;
	return FALSE;
}
int nntp_ismulti(int chan)
{
	if (chan<0) return FALSE;
	if (chan>=MAX_CHAN) return FALSE;
	return chan_ismulti[chan];
}
void close_on_exec(int fd)
{
#ifndef VMS
#ifndef WIN32
#ifndef Mac
#ifndef NETWARE
#ifndef OS2
        if (fcntl(fd,F_SETFD,1)==-1){
                imsg("Error doing close on exec fcntl %d %s\n",fd,strerror(errno));
        }
#endif
#endif
#endif
#endif
#endif
}


int nntp_isthread(int c)
{
        return cmd_thread[c];
}
static int skthread[12000];
int nntp_getbusy(int c)
{
	if (cmd_thread[c]) return TRUE;
        return cmd_busy[c];
}
void nntp_setthread(int c, int v)
{
	int sk;
	cmd_thread[c] = v;
	sk = chan[c];
	if (sk<0) return;
	if (sk>5000) return;
	skthread[sk] = v;
}
int nntp_isthreadsk(int c)
{
	int sk;
	sk = chan[c];
	if (sk<0) return FALSE;
	if (sk>5000) return FALSE;
	return skthread[sk];
}
void nntp_setbusy(int c, int v)
{
        cmd_busy[c] = v;
        if (v) cmd_anybusy++;
        else cmd_anybusy--;
        if (cmd_anybusy<0) cmd_anybusy = 0;
}
void nntp_setblock(int c, int t)
{
        chan_block[c] = t;
        nntp_setblockraw(chan[c],t);
}
void nntp_setblockraw(SOCKET c, int t)
{
#ifdef SET_LINGER
        struct linger ling;
#endif
#ifdef Mac
        unsigned long w;
#else
        int w;
#endif
        if (t) w=0;
        else w=1;
#ifndef VMSx
#ifdef OS2
        if (socket_ioctl(c,FIONBIO,&w,sizeof(w))<0) filemsg("setblock failed %d\n",c);
#else
        if (socket_ioctl(c,FIONBIO,&w)<0) {
		filemsg("setblock failed %d\n",c);
	}

#endif
#endif

#ifdef SET_LINGER
        ling.l_onoff = 1;
        ling.l_linger = 20; 
        setsockopt(c, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));   
#endif

}
void nntp_setrcvbf_raw(SOCKET c, int sz);
void nntp_setrcvbf_chan(int c, int sz)
{
	nntp_setrcvbf_raw(chan[c],sz);
}
void nntp_setrcvbf_raw(SOCKET c, int sz)
{
	int xsz=sz;
	int was;
	int xlen=sizeof(xsz);
	char *s;
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_RCVBUF, s, &xlen);   
	if (was>=sz) return;
	imsg("tcp receive buffer was %d\n",was);
	s = (char *) &xsz;
        setsockopt(c, SOL_SOCKET, SO_RCVBUF, s, sizeof(xsz));   
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_RCVBUF, s, &xlen);   
	imsg("receive buffer is now %d\n",was);

}
static char poison[BFSZ];
int nntp_set_poison(char *ip)
{
	if (strlen(poison)>200) strcpy(poison,"");
	if (strlen(ip)>100) return TRUE;
	strcat(poison,",");
	strcat(poison,ip);
	strcat(poison,",");
	return TRUE;
}
int nntp_ispoison(char *ip)
{
	char bf[BFSZ];
	lprintf(bf,BFSZ-1,",%.100s,",ip);
	if (strstr(poison,bf)!=NULL) return TRUE;
	return FALSE;
}
void nntp_setsndbf_raw(SOCKET c, int sz);
void nntp_setsndbf(int c, int sz)
{
	if (c<0) return;
	if (chan[c]==INVALID_SOCKET) return;
	nntp_setsndbf_raw(chan[c],sz);
}
void nntp_setsndbf_raw(SOCKET c, int sz)
{
	int xsz=sz;
	int was;
	int xlen=sizeof(xsz);
	char *s;
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_SNDBUF, s, &xlen);   
	if (was>=sz) return;
	imsg("sndbf buffer was %d\n",was);
	s = (char *) &xsz;
        setsockopt(c, SOL_SOCKET, SO_SNDBUF, s, sizeof(xsz));   
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_SNDBUF, s, &xlen);   
	imsg("sndbf buffer is now  %d\n",was);

}
void nntp_setnolinger(SOCKET c)
{
#ifdef SET_LINGER
        struct linger ling;
        ling.l_onoff = 0;
        ling.l_linger = 1; 
        setsockopt(c, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));   
#endif
}
void nntp_linger_hard(SOCKET c)
{
#ifdef SET_LINGER
        struct linger ling;
        ling.l_onoff = 1;
        ling.l_linger = 0; 
        setsockopt(c, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));   
#endif
}


#ifdef WIN32
#define HAS_NODELAY TRUE
#else 
#ifdef TCP_NODELAY
#define HAS_NODELAY
#endif
#endif
int nntp_chan_init(int i)
{
        int on= -1;
/*        if (chan_outbf[i]==NULL) {
                chan_outbf[i] = mymalloc(nntp_bigsz()+2);
        }
*/
        cmd_busy[i] = FALSE;
        chan_pend[i] = FALSE;
        nntp_chan_close[i] = FALSE;
        if (check_sock(i,"setblock")) nntp_setblock(i,FALSE);
        
        close_on_exec(chan[i]);

#ifdef HAS_NODELAY

        if (config_true(C_TCP_NODELAY)) {
                imsg("nntp: Setting TCP_NODELAY for socket [%d]\n",i);
                if (setsockopt(chan[i], IPPROTO_TCP, TCP_NODELAY,(char *) &on, sizeof(int))!=0) {
	                nntp_lasterror();               
                        nntp_perror("nntp: Can't set socket to nodelay");
		}
        }
#endif 
/*
#ifdef WIN32
        int xxx;
        xxx = 1;
        setsockopt(chan[i],SOL_SOCKET,SO_OOBINLINE,&xxx,sizeof(xxx));
#endif
*/
        str_clear(&nntp_chan_write[i]);
        str_expand(&nntp_chan_write[i],8000);
        return TRUE;
}
int nntp_chan(int i)
{
        return (int) chan[i];
}
#ifdef UCX
#define DOCAST
#endif
#ifdef WIN_SOCK
#define DOCAST
#endif
#ifdef NETWARE
#define DOCAST
#endif

#ifdef DOCAST
#define ONCAST (char *)
#else
#define ONCAST
#endif

void CheckEvents(void);

void nntp_startup(void)
{
        int i;
#ifdef OS2
        if (sock_init()!=0) {
                emsg("FATAL: TCPIP sockets are not installed on this machine\n");
                return;
        }
#endif
#ifdef WIN_SOCK
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
        wVersionRequested = MAKEWORD( 1, 1 );

        err = WSAStartup( wVersionRequested, &wsaData );
        if ( err != 0 ) { emsg("No winsock found of correct version\n"); return;}
        if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
                emsg("No winsock found of correct version, trying to continue\n");
                goto ret;
        }
        imsg("Socket library found, %d sockets available\n",wsaData.iMaxSockets);

/* wmsg("Socket library found and is version 1.1, Great!\n"); */
/* The Windows Sockets DLL is acceptable. Proceed. */
ret: ;
#endif
        for (i=0; i<MAX_CHAN; i++) {
                chan[i] = INVALID_SOCKET;
                str_init(&nntp_chan_write[i]);
        }

#ifdef Mac
        WSASetBlockingHook(NewWSABlockingHookProc(CheckEvents));
#endif

}
#ifdef WIN32
int getdtablesize(void);
#endif
void nntp_init(int pchan)
{
        int on=1;
        char *s;
        int i;
        int r;
        int quelen;


#ifdef NETWARE
        Set_FD_SETSIZE(FD_SETSIZE);
#endif
        imsg("nntp: DTableSize = %d \n",getdtablesize());

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

        if (pchan<0) {
                x_listen = dupsocket(AF_INET, SOCK_STREAM, 0);
                nntp_lasterror();               
                if (x_listen == INVALID_SOCKET) {
                        nntp_perror("nntp: Can't open socket");
                        exit(0x10000000);
                }

                sp = getservbyname("nntp", "tcp");
                if (sp == NULL) {
                        emsg("nntp: Can't getservbyname \n");
                        sp = &fakesp;
                        fakesp.s_port = htons(119);
                }

                r = setsockopt(x_listen, SOL_SOCKET, SO_REUSEADDR,ONCAST &on, sizeof(int));
                nntp_lasterror();               

                if (r < 0) {
                        filemsg("nntp: Can't setsockopt  %d sock=%d\n",r,x_listen);
                        nntp_perror("tcpnntpserver: setsockopt");
                        exit(0x10000000);
                }

                xsin.sin_family = AF_INET;
                xsin.sin_addr.s_addr = INADDR_ANY;
                nntp_bind_in(&xsin);
                xsin.sin_port = sp->s_port;
                s = slave_port();
                if (strlen(s)==0) s = config_get(C_PORT);
                if (strlen(s)!=0) imsg("nntp: listening on port %s\n",s);
                if (strlen(s)!=0) xsin.sin_port = htons((unsigned short) atoi(s));
                if (xsin.sin_port == 0) {
                        xsin.sin_port = htons(119);
                        imsg("nntp: listening on port 119\n");
                }
 
                imsg("nntp: listening on port %d\n",(int) ntohs(xsin.sin_port));

#ifdef GOT_SOCKADDR
                if (bind(x_listen,(struct sockaddr *)  &xsin, sizeof (xsin)) < 0) {
#else
                if (bind(x_listen,&xsin, sizeof (xsin)) < 0) {
#endif
                        nntp_lasterror();               
                        filemsg("nntp: Can't bind \n");
                        nntp_perror("tcpnntpserver: bind");
                        exit(0x10000000);
                }
        } else x_listen = pchan;
        if (nntp_ifnoblock()) nntp_setblockraw(x_listen,FALSE);
        nntp_setnolinger(x_listen);
        close_on_exec(x_listen);

        
        quelen = 25;
#ifdef WIN32
        quelen = 200;
#endif        
        if (listen(x_listen, quelen) < 0) {
                nntp_lasterror();               
                filemsg("nntp: Can't set listen \n");
                nntp_perror("tcpnntpserver: listen");
                exit(0x10000000);
        }
        dmsg("nntp: init done\n");
}
static struct timeval timeout;
static struct timeval timecopy;


int tcp_waitfor(SOCKET sk, int msec);
int chan_waitfor(int c, int ms)
{
	if (c<0) return FALSE;
	if (chan[c] <0) return FALSE;
	return tcp_waitfor(chan[c],ms);
}
#ifdef WIN32
#define EAGAIN WSAEWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS
#endif
int nntp_read_timed_old(int c, char *bf, int len, int msec, char *reason)
{
	int r;
	SOCKET sk;
	if (c<0) return -1;
	sk = chan[c];
	nntp_setblockraw(sk,FALSE);
	errno = 0;
	r = socket_read(sk,bf,len);
	if (r>=0) {
		lprintf(reason,BFSZ-1,"c=%d sk=%d r=%d %s",c,sk,r,strerror(errno));
		nntp_setblockraw(sk,TRUE);
		if (errno==EAGAIN) goto again; /* should never happen */
		return r;
	}
	if (!tcp_waitfor_reason(sk,msec,reason)) {
		nntp_setblockraw(sk,TRUE);
		return -3;
	}
again:	errno = 0;
	r = socket_read(sk,bf,len);
	if (r<0) if (nntp_errbusy()) {
		nntp_usleep(1000);
		goto again;
	}
	if (r<=0) {
		lprintf(reason,BFSZ-1,"c=%d sk=%d r=%d %s",c,sk,r,strerror(errno));
	}
	nntp_setblockraw(sk,TRUE);
	if (r==0) if (errno==EAGAIN) return -4;
	return r;
}
char *nntp_error_string(int x, char *tmp)
{
#ifdef WIN32
        int err = x;
	ncpy(tmp,"none",20);
        if (err==WSANOTINITIALISED) ncpy(tmp,"A successful WSAStartup must occur before using this API.",200);
        else if (err==WSAENETDOWN)      ncpy(tmp,"The Windows Sockets implementation has detected that the network subsystem has failed.",200);
        else if (err==WSAEACCES)        ncpy(tmp,"The requested address is a broadcast address, but the appropriate flag was not set.",200);
        else if (err==WSAEINTR)		ncpy(tmp,"The (blocking) call was cancelled via WSACancelBlockingCall.",200);
        else if (err==WSAEINPROGRESS)   ncpy(tmp,"A blocking Windows Sockets operation is in progress.",200);
        else if (err==WSAEFAULT)        ncpy(tmp,"The buf argument is not in a valid part of the user address space.",200);
        else if (err==WSAENETRESET)     ncpy(tmp,"The connection must be reset because the Windows Sockets implementation dropped it.",200);
        else if (err==WSAENOBUFS)       ncpy(tmp,"The Windows Sockets implementation reports a buffer deadlock.",200);
        else if (err==WSAENOTCONN)      ncpy(tmp,"The socket is not connected.",200);
        else if (err==WSAENOTSOCK)      ncpy(tmp,"The descriptor is not a socket.",200);
        else if (err==WSAEOPNOTSUPP)    ncpy(tmp,"MSG_OOB was specified, but the socket is not of type SOCK_STREAM.",200);
        else if (err==WSAESHUTDOWN)     ncpy(tmp,"The socket has been shutdown; it is not possible to send on a socket after shutdown has been invoked with how set to 1 or 2.",200);
        else if (err==WSAEWOULDBLOCK)   ncpy(tmp,"The socket is marked as non-blocking and the requested operation would block.",200);
        else if (err==WSAEMSGSIZE)      ncpy(tmp,"The socket is of type SOCK_DGRAM, and the datagram is larger than the maximum supported by the Windows Sockets implementation.",200);
        else if (err==WSAEINVAL)        ncpy(tmp,"The socket has not been bound with bind.",200);
        else if (err==WSAECONNABORTED)  ncpy(tmp,"The virtual circuit was aborted due to timeout or other failure.",200);
        else if (err==WSAECONNRESET)    ncpy(tmp,"The virtual circuit was reset by the remote side.",200);
        else if (err==WSAETOOMANYREFS)  ncpy(tmp,"Too many refs.",200);
        else if (err==WSAETIMEDOUT)     ncpy(tmp,"Timedout",200);
        else if (err==WSAECONNREFUSED)  ncpy(tmp,"Connection Refused",200);
        else if (err==WSAEDESTADDRREQ)  ncpy(tmp,"DestAddrReq",200);
        else if (err==WSAEMSGSIZE)      ncpy(tmp,"Message Size",200);
        else if (err==WSAEADDRINUSE)    ncpy(tmp,"Address in use",200);
        else if (err==WSAEADDRNOTAVAIL) ncpy(tmp,"Address not avail",200);
        else if (err==WSAENETUNREACH)   ncpy(tmp,"Network Unreachable",200);
        else if (err==WSAENETRESET)     ncpy(tmp,"Network Reset",200);
        else if (err==WSAENOBUFS)       ncpy(tmp,"No bufs",200);
        else if (err==WSAESHUTDOWN)     ncpy(tmp,"Shutdown",200);
        else if (err==WSAENAMETOOLONG)  ncpy(tmp,"Name too long",200);
        else if (err==WSAEHOSTDOWN)     ncpy(tmp,"Host down",200);
        else if (err==WSAEHOSTUNREACH)  ncpy(tmp,"Host Unreachable",200);
        else if (err==WSAENOTEMPTY)     ncpy(tmp,"Not Empty",200);
        else lprintf(tmp,200,"Socket Unknown error code %d ",err);
#else
        strcpy(tmp,"None");
	if (x>=0) ncpy(tmp,strerror(x),200);
#endif
	return tmp;
}
int nntp_read_timed(int c, char *bf, int len, int msec, char *reason)
{
	int r;
	int sl;
	char tmp3[BFSZ];
	int lerrno;
	SOCKET sk;
	int th_off=-10;
	if (c<0) return -1;
	sk = chan[c];
	nntp_setblockraw(sk,FALSE);

	for (;TRUE;th_off++) {
		errno = 0;
		r = socket_read(sk,bf,len);
#ifndef WIN32
		lerrno = errno;
#else
		lerrno = WSAGetLastError();
#endif
		if (r==0) {
			lprintf(reason,BFSZ-1,"closed normally %d",lerrno);
			return r;
		}
		if (r>0) {
			strcpy(reason,"Normal read");
			nntp_setblockraw(sk,TRUE);
			return r;
		}
		if (r<0) {
			if ( (lerrno!=EAGAIN) && (lerrno!=EWOULDBLOCK) && (lerrno!=EINPROGRESS) ) {
				lprintf(reason,BFSZ-1,"c=%d sk=%d r=%d err=%d %s",c,sk,r,lerrno,nntp_error_string(lerrno,tmp3));
				nntp_setblockraw(sk,TRUE);
				return r;
			}
		}

		if (th_off>100) {
			if (!tcp_waitfor_reason(sk,msec,reason)) {
				nntp_setblockraw(sk,TRUE);
				return -3;
			}
			th_off = 10; 
			continue;
		}
		sl = 100*th_off;
		if (sl<0) sl = 0;
		nntp_usleep(sl);
	}
	return r;
}
int tcp_waitfor(SOCKET sk, int msec)
{
	char reason[BFSZ];
	return tcp_waitfor_reason(sk,msec,reason);
}
int tcp_waitfor_reason(SOCKET sk, int msec, char *reason)
{
        fd_set readfd,writefd,exceptfd;
	int nc;
	struct timeval timeout;

	
        timeout.tv_sec = msec/1000; /*Wake up once a second*/
        timeout.tv_usec = (msec%1000) * 1000 ;
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
	FD_SET(sk,&readfd);		
        nc = select(getdtablesize(),&readfd,&writefd,&exceptfd,&timeout);
	if (nc>0) return TRUE;
	lprintf(reason,BFSZ-1,"nc=%d sk=%d %s",nc,sk,strerror(errno));
	return FALSE;
}
int nntp_wait_write(int c, int msec)
{
        fd_set readfd,writefd,exceptfd;
	int nc;
	struct timeval timeout;

	
        timeout.tv_sec = msec/1000; /*Wake up once a second*/
        timeout.tv_usec = (msec%1000) * 1000 ;
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
	FD_SET(chan[c],&writefd);		
        nc = select(getdtablesize(),&readfd,&writefd,&exceptfd,&timeout);
	if (nc>0) return TRUE;
	return FALSE;
}

void nntp_setsleep(int x)  /* Used when background job wants to be called alot*/
{
        if (x>1) x = 1;
        timeout.tv_sec = x;
        dmsg("nntp: setsleep %d \n",x);
}

static int measure_idle = FALSE;
void nntp_usleep(int x);


void nntp_usleep(int x)
{
#ifdef WIN32
        if (config_true(C_DONT_SLEEP)) return;
        Sleep(x/1000);
#endif
#ifdef Mac
#include <OSUtils.h>
        unsigned long d;
        if (config_true(C_DONT_SLEEP)) return;
        Delay(x/16667,&d);
#else
#ifndef NOUSLEEP
#ifdef isunix
        if (config_true(C_DONT_SLEEP)) return;
        lib_usleep(x);
#endif
#endif
#endif
}


#ifdef NEED_USLEEP
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
void uwakeup(int x)
{
        return;
}
int usleep(int x)
{
        struct itimerval tval,oval;
        tval.it_interval.tv_usec = 0;
        tval.it_interval.tv_sec = 0;
        tval.it_value.tv_usec = x;
        tval.it_value.tv_sec = 0;
        signal(SIGALRM,uwakeup);
        setitimer(ITIMER_REAL,&tval,&oval);
        pause();
        tval.it_interval.tv_usec = 0;
        tval.it_interval.tv_sec = 0;
        setitimer(ITIMER_REAL,&oval,NULL);
}
#endif
int nntp_measure_idle(int t)
{
        measure_idle = t;
        return TRUE;
}
int xtime_now;
int cmd_finish(int i);
#define OUT_LIMIT 20000
int nntp_fastout_total(void);

static int master_link=-2;
void nntp_idle(void);
void nntp_setmaster(int i)
{
        master_link = i;
}
static int isaslave = FALSE;
static int nskip,nall;
void nntp_set_isslave(void)
{
	isaslave = TRUE;
}
static int small_fast;
void nntp_wait(void)
{
        static int last_give;
        int i,nc;
	int nnone;
	int skipit;
	static int stoppedat = -1,lastat = -1;
        int xnow;
	int snooze=0;
        int totalout=0;
	int nsmall;
	int last_real;
        fd_set readfd,writefd,exceptfd;
        static int curtime=0;
        int xbusy,xsec;      
        int nvalid=0;
	int skipped = 0;
	int nwrite;
        dmsg("nntp: Wait start\n");

        timeout.tv_sec = 1; /*Wake up once a second*/
        timeout.tv_usec = 0;
        xnow = xtime_now = lib_gmtime();

	mutex_wait(M_MAIN);

again:
        nvalid = 1;
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
        if (x_listen!=INVALID_SOCKET) {FD_SET(x_listen,&readfd); nvalid++;}

	for (i=0; i<nlisten; i++) { /* listening on other ports */
		FD_SET(l_chan[i],&readfd);
	}

        xtime_now = lib_gmtime();
        if (curtime!=xtime_now) {
                curtime = xtime_now;
                for (i=0; i<nchan; i++) {
                        nntp_speedin(i,0);
                        nntp_speedout(i,0);
                }
                nntp_speedout(MAX_CHAN-1,0);
        }
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
                 if (nntp_chan_close[i]) {
                        if (!chan_busy[i]) {
				imsgf("Close later called and now closing chan [%d]\n",i);
				nntp_doclose(i);
			} else {
                                if ((lib_gmtime()-chan_closetime[i]) >20) {
                                        wmsg("nntp: Closing channel with data still in buffer %d\n",i);
                                        nntp_doclose(i);
                                }
                        }
                 }
        }
        timecopy = timeout; /* Because linux clears the value each call */
        totalout =nntp_fastout_total(); 
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
                if (chan[i]!=INVALID_SOCKET) {
                   
                   if (!nntp_getpending(i)) if (nntp_outsz(i)<OUT_LIMIT)  if (!nntp_toofast(i)) if (!cmd_busy[i]) if (!cmd_thread[i]) FD_SET(chan[i],&readfd);              
                   if (i==master_link) FD_SET(chan[i],&readfd);
                   if (cmd_busy[i]) if (nntp_outsz(i)<OUT_LIMIT) timecopy.tv_sec = 0;
                   
                   FD_SET(chan[i],&exceptfd);
                   nvalid++;
                   if (chan_busy[i]) if (!nntp_fastout(i)) if (!totalout) FD_SET(chan[i],&writefd);
                }
        }
        timecopy.tv_usec = 0;
        xbusy = lib_msec();
#ifndef Mac
	snooze++;
	if (snooze % 10 ==4)  nntp_usleep(5000); /* Sleep for 1/200th of a second  .005 .001. 200*10K */
	if (isaslave) nntp_usleep(5000);
#endif
	stat_start(X_SELECT);
	mutex_release(M_MAIN);
        if (nvalid==0) {
                nntp_usleep(1000);
                nc = 0;
        } else {
                nc = select(getdtablesize(),&readfd,&writefd,&exceptfd,&timecopy);
        }
        xnow = lib_gmtime();
	xsec = lib_msec();
	mutex_wait(M_MAIN);
	stat_end(X_SELECT);
        if ((xsec-xbusy)>0) nntp_addidle(xsec-xbusy);
        xtime_now = xnow;
        nntp_lasterror();               
        if (nc<0) nntp_perror("Main Select call failed");
   if (nc<0) {
      for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET)  {
         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 (x_listen!=INVALID_SOCKET) if (FD_ISSET(x_listen,&readfd)) {
                nntp_newchan_all();     
        }
	for (i=0; i<nlisten; i++) { /* listening on other ports */
		if (FD_ISSET(l_chan[i],&readfd)) nntp_newchanx(0,NULL,NULL,l_chan[i]);
	}

	if (!nntp_fastout_total()) {
		stoppedat = -1;
	}
        stat_start(X_LOOP);
	nsmall = 0;
	skipped = 0;
	nwrite = 0;
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
                  echan_set(i); 
		  dom_setdom(i);
		  skipit = nntp_isslowdown(i);
		  if (small_fast==0) skipit = FALSE;
		  if (last_real != xnow) skipit = FALSE;
		  if (skipit) nskip++;
		  if (skipit) skipped++;
		  nall++;
               if (FD_ISSET(chan[i],&exceptfd)) {
                        imsg("nntp: Channel closed or didn't open [%d] %d {%s}\n",i,chan[i],chan_getipname(i));
                        nntp_except(i);
                } else {
		       if (!skipit) {
                        if (nntp_outsz(i)<OUT_LIMIT) if (cmd_busy[i] || chan_ismore(i)) {
				nsmall++;
                                cmd_finish(i); /* Still processing command */
                        }
                        if (chan[i]!=INVALID_SOCKET) if (FD_ISSET(chan[i],&readfd)) {
				dmsg("nntp: chan_read %d\n",i);
				stat_start(X_CHAN_READ);
				channel_read(i);
				nsmall++;
				stat_end(X_CHAN_READ);
				dmsg("nntp: chan_read_done \n");
                        }
		       }
		       if (chan_busy[i]) if (FD_ISSET(chan[i],&writefd)) {
			if (i>=lastat) {
                          echan_set(i); 
                          stat_start(X_NNTP_WRITE);
			  if (!nntp_fastout_total()) {
				  if (i>=lastat) {
					nwrite++;
					  nntp_write_now(i);
				  }
			  } else {
				  if (stoppedat<0) {
					  stoppedat = i;
				  }

			  }
                          stat_end(X_NNTP_WRITE);
                          echan_set(-1); 
			}
		       }
                }
          echan_set(-1); 
        }
	if (last_real != xnow) last_real = lib_gmtime();
	if (small_fast!=0) {
		dmsg("xloop: took %d msec, nsmall %d, nwrite %d, skipped %d \n",lib_msec()-xsec,nsmall,nwrite,skipped);
		if ((lib_msec()-xsec) == 0) nnone++; else nnone=0;
		if (nnone>5) {
			last_real -= 10;
			nnone = 0;		
		}
	}
	if (nsmall==0) last_real -= 10;
	
	lastat = stoppedat;
	if (!nntp_fastout_total()) {
		stoppedat = -1;
	}
        stat_end(X_LOOP);

        dmsg("nntp: background\n");
        {
		stat_start(X_BACKGROUND);
                suck_do(); /*do back ground jobs */
		stat_end(X_BACKGROUND);
		stat_start(X_NNTPIDLE);
		nntp_idle();
		stat_end(X_NNTPIDLE);
        }
        dmsg("nntp: background_returned\n");
#ifdef WIN_SOCK
/*      wsock_do();*/
#endif
   }
      goto again;
}
void nntp_idle(void)
{
	int i;
	if (xtime_now%10!=2) return;
	dmsg("nntp: Free unused output buffers\n");
	for (i=0; i<nchan; i++) {
		if (str_len(&nntp_chan_write[i])==0) {
			str_clear(&nntp_chan_write[i]);
		        /*str_expand(&nntp_chan_write[i],8000);*/
		}
	}
	dmsg("nntp: Free unused output buffers-done\n");
}
int nntp_chanwait(int c, int sec)
{
	int t = time(NULL);
        fd_set readfd,writefd,exceptfd;
        int nc;
        
        timecopy.tv_sec = sec; /*Wake up once a second*/
        timecopy.tv_usec = 0;
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
        if (chan[c]==INVALID_SOCKET) return FALSE;
        FD_SET(chan[c],&readfd);                
        nc = select(getdtablesize(),&readfd,&writefd,&exceptfd,&timecopy);
	if (nc<0) {
	        nntp_lasterror();               
		imsg("nntp_chanwait error %d %s\n",errno,strerror(errno)); 
		imsg("nntp_chanwait [%d] sk=%d/%d timeout=%d\n",c,chan[c],getdtablesize(),sec); 
	        nntp_perror("nntp: chanwait gave error");
		if (((time(NULL)-t)<2)) return TRUE; /* it  couldn't fail this quick */
	}
        if (nc<=0) return FALSE;
        return TRUE;
}

#ifdef unix
#ifndef WIN_SOCK
#define MIGHT_FAIL TRUE
#endif
#endif
#ifdef VMS
#define MIGHT_FAIL TRUE
#endif


int nntp_except(int c)
{
#ifdef WIN_SOCKxx
        int r;
        char bf[BFSZ];
        if (chan[c]==INVALID_SOCKET) { nntp_doclose(c); return TRUE; }
        r =  recv(chan[c],bf,BFSZ-1,MSG_OOB);
        nntp_lasterror();               
        if (r>0) {
                bf[r] = 0;
                emsg("nntp: Except data? {%s}\n",bf);
                return TRUE;
        }
#ifdef MIGHT_FAIL
        if (werrno==EAGAIN) {
                emsg("nntp: Socket reported EAGAIN, trying to continue\n");
                return 0;
        }
        if (werrno!=0) imsg("nntp: Socket closed, errno=%d {%s}\n",werrno,chan_getipname(c));
#endif
        if (r!=0) nntp_perror("socket_read failed");
        else imsg("nntp: Socket closed by remote end [%d] %d %s\n",c,chan[c],chan_getipname(c));
#endif
        nntp_doclose(c);
        return FALSE;
}
int nntp_iswaiting(void)
{
        int i,nc;
        fd_set readfd,writefd,exceptfd;
        static struct timeval timeout;
        
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
             if (cmd_busy[i]) if (nntp_outsz(i)<OUT_LIMIT) return TRUE;
        }

        timeout.tv_sec = 0; /*Wake up once a second*/
        timeout.tv_usec = 0;

        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);

        if (x_listen!=INVALID_SOCKET) FD_SET(x_listen,&readfd);
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) {
                if (chan[i]!=INVALID_SOCKET) {
                   if (nntp_outsz(i)<OUT_LIMIT)  if (!nntp_toofast(i)) 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);
        nntp_lasterror();               
        if (nc<0) nntp_perror("Select2 call failed");
        if (nc==0) return FALSE;
        return TRUE;

}
int nntp_getchan(void)
{
        /* shoould search thru list for unallocated one.*/
        int i;
        for (i=0; i<nchan ; i++) {
                if ((chan[i]==INVALID_SOCKET) && chan_isfree(i) ) {
                        chan_init(i);
                        return i;
                }
        }
        chan_init(nchan);
    
        if ((nchan+4)>MAX_CHAN) emsg("Channels exhausted, panic %d \n",nchan);
/*        if (chan_outbf[nchan]==NULL)  chan_outbf[nchan] = mymalloc(nntp_bigsz()+2);*/
        return nchan++;
}
void nntp_close_later(int c)
{
	nntp_close(c);
        chan_busy[c] = FALSE;
}

void nntp_close(int c)
{
        if (c<0) return;
        if (c>=MAX_CHAN) return;
        if (chan[c]==INVALID_SOCKET) return;
        dmsg("chan: que close  %d %d \n",c,chan[c]);
        chan_closetime[c] = lib_gmtime();
        nntp_chan_close[c] = TRUE;
}
int nntp_isclosing(int c)
{
        return nntp_chan_close[c];
}
void nntp_closeit(int c)
{
	if (c<0) return;
        chan[c] = INVALID_SOCKET;
        if (cmd_busy[c]) {
		imsg("nntp_closeit cmd_setbusy[%d] = %d\n",c,0);
		nntp_setbusy(c,FALSE);
	}
        nntp_chan_close[c] = FALSE;
        chan_busy[c] = FALSE;
        if (chan[c]!=INVALID_SOCKET) 
           if (hard_socket_close(chan[c])!=0) imsg("nntp: closit failed\n");
        str_clear(&nntp_chan_write[c]);
        ihavein_clear(c);
        if (c==nchan-1) nchan--; /* should search backwards too, but doesn' matter much*/
}
void chan_closing(int c);

static int small_speed;

int nntp_isslowdown(int c)
{
	if (c<0) return FALSE;
	if (chan_total[c]<small_fast) return FALSE;
	if (nntp_curspeedout(c)<small_speed) return FALSE;
	return TRUE;

}
int nntp_set_small_fast(int sz)
{
	small_fast = sz/1000;
	return TRUE;
}
int nntp_set_small_speed(int sz)
{
	small_speed = sz;
	return TRUE;
}
int nntp_gettotal(int c)
{
	return chan_total[c];
}
void nntp_doclose(int c)
{
	if (c<0) return;
	chan_total[c] = 0;
        if (cmd_busy[c]) nntp_setbusy(c,FALSE);
        dmsg("chan: real close  %d %d \n",c,chan[c]);
        if (chan[c]==INVALID_SOCKET) emsg("nntp: Channel is not open, %d %d \n",c,chan[c]);
        else if (hard_socket_close(chan[c])!=0) 
        {
           filemsg("nntp: Channel close failed\n");
           imsg("nntp: socket_close failed on channel %d\n",c);
           nntp_lasterror();               
           nntp_perror("nntp: socket_close error information");
        }
        chan_closing(c);
        chan[c] = INVALID_SOCKET;
        nntp_chan_close[c] = FALSE;
        chan_busy[c] = FALSE;
        str_clear(&nntp_chan_write[c]);
        ihavein_clear(c);
        if (chan_isfree(c)) 
          if (c==nchan-1) nchan--; /* should search backwards too, but doesn' matter much*/
}

#ifdef NEED_NTOA
char *myinet_ntoa(struct in_addr in)
{
        union {unsigned char b[4]; long all;} both;
        static char bf[100];
        both.all = (long) in.s_addr;
        lprintf(bf,100-1,"%d.%d.%d.%d",both.b[0],both.b[1],both.b[2],both.b[3]);
        return bf;
}

#define inet_ntoa myinet_ntoa
#endif


static char nntp_status[200];
char *nntp_getstatus(void)
{
        return nntp_status;
}
int nntp_newchan_waiting(void)
{
        int nc;
        fd_set readfd,writefd,exceptfd;
        static struct timeval timeout;
        
        timeout.tv_sec = 0; /*Wake up once a second*/
        timeout.tv_usec = 0;

        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);

        FD_SET(x_listen,&readfd);
        timecopy = timeout; /* Because linux clears the value each call */
        nc = select(getdtablesize(),&readfd,&writefd,&exceptfd,&timecopy);
        nntp_lasterror();               
        if (nc<0) nntp_perror("Select3 call failed");
        if (nc==0) return FALSE;
        return TRUE;
}
static char welcome[BFSZ];
void nntp_set_welcome(char *s)
{
	ncpy(welcome,s,BFSZ-100);
}
void nntp_newchan_all(void)
{
        int r;  
        int n;
        if (x_listen==INVALID_SOCKET) return ;

        r = TRUE;
        for (n=0;r;n++) {
                nntp_newchan();
                r = nntp_newchan_waiting();
		if (n>100) break;
	        if (nchan>(MAX_CHAN-20)) break; /* One at a time near the end */
        }
        if (n>2) wmsg("Processed %d pending new channel requests\n",n);
}
int slave_n(void);
int chan_ihaveonly(int c);
void user_addrej(int c);

void nntp_accept(int hhh, char *ipnum, char *lclip)
{
	nntp_newchanx((SOCKET) hhh, ipnum, lclip,x_listen);
}

void nntp_newchan(void)
{
	nntp_newchanx(0,NULL,NULL,x_listen);
}
static char dns_check_msg[BFSZ];

static int hide_version;
int nntp_set_hide_ver(int t)
{
	hide_version = t;
	return TRUE;
}

char *nntp_getwelcome(int n)
{
	static char buf[BFSZ];
	char ver[BFSZ];
	int status;
	char *canpost;
        status = 201;
        canpost = "posting disabled";
        if (chan_canpost(n)) {canpost = "posting OK"; status=200;}
	if (nntp_isopen(n)) {
		dom_setdom(n);
		nntp_set_welcome(dom_ini_get(C_WELCOME));
	}
	lprintf(ver,100," %s,",DNEWS_VERSION);
	if (hide_version) strcpy(ver,"");
        lprintf(buf,BFSZ-1, "%d %s %s%s S%d, %s %s\015\012",
               status,dom_ini_get(C_MYNAME),DNEWS_NAME,ver,slave_n(),canpost
	       ,chan_lclip[n]);
	if (strlen(welcome)>0) {
	        lprintf(buf,BFSZ-1, "%d %s,%s S%d\015\012",status,welcome,ver,slave_n());
	}
	return buf;
}
void nntp_newchanx(SOCKET xsk,char *ipnum, char *lclip, SOCKET x_listen)
{
        char buf[1000];
        int n=nntp_getchan();
        int sizesin=sizeof(chan_sin[0]);
        int status,lookup,gotname;
        char ipname[200];
        char *canpost;
	static int z;
        char *s;
	char ver[BFSZ];
        char bf[BFSZ];
        strcpy(bf,"");

	if (n<0) {
          filemsg("Socket newchan accept() call failed as n was less than zero\n");
          return;
	}
	strcpy(dns_check_msg,"");
        echan_set(n); 
        dmsg("nntp: Newchan step 1 [%d]\n",n);

	if (ipnum!=NULL) {
		chan[n] = xsk;
	} else {
#ifdef GOT_SOCKADDR
		chan[n] = dupaccept(x_listen,(struct sockaddr *) &chan_sin[n], &sizesin);
#else
		chan[n] = dupaccept(x_listen,&chan_sin[n], &sizesin);
#endif
	}

        nntp_lasterror();               
#ifndef WIN32
        if (((int)chan[n])>getdtablesize()) emsg("FATAL, channel is > than dtablesize %d %d\n",chan[n],getdtablesize());
#endif
        dmsg("nntp: Newchan step 2 %d \n",chan[n]);
        if (chan[n] == INVALID_SOCKET) {
          nntp_perror("Socket newchan accept() call failed");
          filemsg("Socket newchan accept() call failed\n");
          return;
        }

        nntp_chan_init(n);
	

        close_on_exec(chan[n]);
	if (ipnum!=NULL) {
	        chan_sin[n].sin_addr.s_addr = inet_addr(ipnum);
	        chan_sin[n].sin_family = AF_INET;
	}
        chan_setipnum(n,inet_ntoa(chan_sin[n].sin_addr));

        if (config_true(C_ACCESS_TWICE)) {
                chan_setipname(n,inet_ntoa(chan_sin[n].sin_addr));
                if (chan_cantalk(n)) {
                        imsg("nntp: access by number worked for %s\n",chan_getipname(n));
                        goto skip_access;
                }
        }       
        lookup = lib_gmtime();
        gotname = nntp_getname(ipname,&chan_sin[n]);
        lookup = lib_gmtime()-lookup;
        if (lookup>3) imsg("Reverse Name lookup took %d seconds \n",lookup);
        if (lookup>14) {
                emsg("Reverse Name lookup took %d seconds, disable lookups if possible \n",lookup);
                lprintf(nntp_status,200-1,"Reverse lookup took %d seconds, disable if possible (see nodns)\015\012",lookup);
        }

        if (gotname) {
                chan_setipname(n,ipname);
        } else {
                chan_setipname(n,inet_ntoa(chan_sin[n].sin_addr));
                if (!config_true(C_NODNS))
                   imsg("Reverse Name lookup failed for {%s}\n",chan_getipname(n));
        }
        wmsg("nntp: connection [%d:%d] From %s %s\n",n,chan[n],chan_getipname(n),chan_getipnum(n));
	imsg("skip %d all %d\n",nskip,nall);
        nntp_chan_init(n);
	if (config_true(C_SENDBF_GROW)) {
		nntp_setsndbf_raw(chan[n],21000);
	}

        if (!chan_cantalk(n)) {
                if (strlen(chan_elimit())==0) {
                        if (strcmp(chan_getipnum(n),"127.0.0.1")==0) {
                                canpost = "posting OK"; 
                                status=200;
                                goto skip2;
                        }
                }
                if (config_true(C_NODNS)) wmsg("Refusing {%s} (nodns true in dnews.conf so name was not looked up)\n",chan_getipname(n));
                if (strlen(chan_elimit())>0) {
                        wmsg("Refusing connection from {%s}, reason (%s)\n",chan_getipname(n),chan_elimit());
			if (config_true(C_LIMIT_CONNECTOK)) {
				goto skip2;
			}
                } else {
			if (strlen(dns_check_msg)>0) {
				wmsg("Refusing connection from {%s}, dns_check true and ip number didn't match (%s)\n",chan_getipname(n),dns_check_msg);
				wmsg("Either set dns_check false in dnews.conf, or correct dns so forward and reverse lookups match, or it could be a spammer attack!\n");
				status_recent("Refused connection, dns_check true and number didn't match name %s->%s",chan_getipname(n),dns_check_msg);
			} else {
				wmsg("Refusing connection from {%s}, not in access.conf\n",chan_getipname(n));
				status_recent("Refused connection, not in access.conf {%s:read,post:::*}",chan_getipname(n));
			}
                }
                user_addrej(n);
                s = config_get(C_NNTP_LOGOFF);
                if (strlen(s)==0) if (config_true(C_NODNS)) {
                        s = "You have no permission to talk, (reverse dns disabled, see nodns in dnews.conf)";
                }
                if (strlen(s)==0) s = "You have no permission to talk";
                if (strlen(chan_elimit())>0) {
                        lprintf(buf,1000-1,"502 %s {%s}, Goodbye\015\012",chan_elimit(),chan_getipname(n));
                } else {
                        lprintf(buf,1000-1,"502 %s {%s},%s Goodbye\015\012",s,dns_check_msg,chan_getipname(n));
                }
                if (nntp_isopen(n)) {
                  nntp_write_s(n, buf, strlen(buf));
                  nntp_close(n);
                }
		nfailed++;
		if (nfailed<30) access_read(); /* Just incase it is local */
                return;
        }
skip_access:;
        if (!config_true(C_IHAVE_NOCLOSE)) {
         if (chan_ihaveonly(n)) {
          if (!lib_checkspace()) {
                lprintf(buf,1000-1,"400 Sorry, disk full, cannot take ihave feed just now, Goodbye %s\015\012",chan_getipname(n));
                if (nntp_isopen(n)) {
                  nntp_write_s(n, buf, strlen(buf));
                  nntp_close(n);
                }
                return;
          }
         }
        }
            
        status = 201;
        canpost = "posting disabled";
        if (chan_canpost(n)) {canpost = "posting OK"; status=200;}
skip2:
	if (nntp_isopen(n)) {
		if (lclip!=NULL) nntp_setlocalip(n,lclip);
		dom_setdom(n);
		nntp_set_welcome(dom_ini_get(C_WELCOME));
	}

	lprintf(ver,100," %s,",DNEWS_VERSION);
	if (hide_version) strcpy(ver,"");
        lprintf(buf,1000-1, "%d %s %s Version %s, S%d, %s \015\012",
               status,dom_ini_get(C_MYNAME),DNEWS_NAME,ver,slave_n(),canpost);

	if (strlen(welcome)>0) {
	        lprintf(buf,1000-1, "%d %s, %s, S%d\015\012",status,welcome,ver,slave_n());
	}

        if (nntp_isopen(n)) if (nntp_ifnoblock()) nntp_setblock(n,FALSE);
 
        if (nntp_isopen(n)) nntp_write_s(n, buf, strlen(buf));


	if (nntp_ispoison(chan_getipnum(n))) {
                if (nntp_isopen(n)) {
			imsg("nntp: Rejecting user who recently read poison virus group (%s)\n",chan_getipnum(n));
			lprintf(buf,1000-1,"502 You recently read a virus group that doesn't exist so we think you are a robot, try later\015\012");
                        nntp_write_s(n, buf, strlen(buf));
                        user_addrej(n);
                        nntp_close(n);
                }	return;
	}
        
	if (n>(getdtablesize()-2)) {
                if (nntp_isopen(n)) {
                        lprintf(buf,1000-1,"502 Sorry only %d simultaneous users permitted, try later\015\012",getdtablesize());
                        nntp_write_s(n, buf, strlen(buf)); nntp_close(n);
                        wmsg("User rejected because %d users already connected\n",getdtablesize());
                }
        }
        if (n>(MAX_CHAN-20)) {
                if (nntp_isopen(n)) {
                        lprintf(buf,1000-1,"502 Sorry only %d simultaneous users permitted, try later\015\012",MAX_CHAN);
                        nntp_write_s(n, buf, strlen(buf)); nntp_close(n);
                        wmsg("User rejected because %d users already connected\n",MAX_CHAN);
                }
        }
	z++;
        if (!nt_getkey(bf,nchan)) {
                if (n>4) if (z%2==1) if (nntp_isopen(n)) {
                        lprintf(buf,1000-1,"502 DNEWS License problem {%s}, See license.txt, Goodbye\015\012",bf);
                        wmsg("License error (%s) %d users online now\n",bf,nchan);
                        nntp_write_s(n, buf, strlen(buf));
                        user_addrej(n);
                        nntp_close(n);
                }
        }
/*        if (nntp_isopen(n)) {
                if (max_xx>0) if (nchan>max_xx) {
                        lprintf(buf,1000-1,"502 DNEWS license faulty,  Please contact Netwin@netwinsite.com\015\012",bf);
                        nntp_write_s(n, buf, strlen(buf));
                        user_addrej(n);
                        nntp_close(n);
                }
        }
*/
        if (nntp_isopen(n)) {
		chan_newchan(n);
		if (lclip!=NULL) nntp_setlocalip(n,lclip);
	}

}
void nntp_setip(int n, char *ipnum)
{
        char buf[1000];
        int lookup,gotname;
        char *s;
        char bf[BFSZ];
        char ipname[BFSZ];
        struct sockaddr_in xsin;

	if (n<0) return;
	if (n>=MAX_CHAN) return;
        strcpy(bf,"");
        chan_setipnum(n,ipnum);
	chan_total[n] = 0;
        if (config_true(C_ACCESS_TWICE)) {
                chan_setipname(n,ipnum);
                if (chan_cantalk(n)) {
                        imsg("nntp: access by number worked for %s\n",chan_getipname(n));
                        goto skip_access;
                }
        }       
        
        lookup = lib_gmtime();
        xsin.sin_addr.s_addr = inet_addr(ipnum);
        xsin.sin_family = AF_INET;
        gotname = nntp_getname(ipname,&xsin);
        lookup = lib_gmtime()-lookup;
        if (lookup>3) imsg("Reverse Name lookup took %d seconds \n",lookup);
        if (lookup>14) {
                wmsg("Reverse Name lookup took %d seconds, disable lookups if possible \n",lookup);
                lprintf(nntp_status,200-1,"Reverse lookup took %d seconds, disable if possible (see nodns)\015\012",lookup);
        }
        if (gotname) {
                chan_setipname(n,ipname);
        } else {
                chan_setipname(n,ipnum);
                if (!config_true(C_NODNS))
                   imsg("Reverse Name lookup failed for {%s}\n",chan_getipname(n));
        }
skip_access:
        wmsg("nntp: multi connection [%d:%d] From %s %s\n",n,chan[n],chan_getipname(n),chan_getipnum(n));
        if (!chan_cantalk(n)) {
                wmsg("Refusing connection from {%s}, not in access.conf\n",chan_getipname(n));
                s = config_get(C_NNTP_LOGOFF);
                if (strlen(s)==0) s = "You have no permission to talk";
                if (strlen(chan_elimit())>0) {
                        lprintf(buf,1000-1,"502 %s {%s}, Goodbye\015\012",chan_elimit(),chan_getipname(n));
                } else {
                        lprintf(buf,1000-1,"502 %s {%s}, Goodbye\015\012",s,chan_getipname(n));
                }
                if (nntp_isopen(n)) {
                  nntp_write_s(n, buf, strlen(buf));
                  nntp_close(n);
                }
		nfailed++;
		if (nfailed<30) access_read(); /* Just incase it is local */
		
                return;
        }

}
static int nntp_doblock;
void nntp_xxsetup(int x)
{
        max_xx = x;
}
void nntp_alwaysblock(void)
{
        nntp_doblock = TRUE;
}
int nntp_ifnoblock(void)
{
        int dontblock = TRUE;
        if (config_true(C_NNTP_BLOCK)) dontblock = FALSE;
        if (nntp_doblock) dontblock = FALSE;
#ifdef VMSx
        dontblock = FALSE;
#endif
        return dontblock;
}


#ifdef WIN_SOCK
#ifdef CAST_SOCKET
#undef CAST_SOCKET
#endif
#define CAST_SOCKET TRUE
#endif
#ifdef NETWARE
#define CAST_SOCKET  TRUE
#endif

#define NMAXSOCKETS 3000
int nntp_nsockets(void)
{
        SOCKET xx[NMAXSOCKETS];
        int i;
        int n;
        for (i=0; i<NMAXSOCKETS; i++) xx[i] = INVALID_SOCKET;
        for (i=0; i<NMAXSOCKETS; i++) {
                xx[i] = socket(AF_INET,SOCK_STREAM, 0);
                if (xx[i]==INVALID_SOCKET) break;
        }
        n = i;
        for (i=0; i<NMAXSOCKETS; i++) {
                if (xx[i]!=INVALID_SOCKET) socket_close(xx[i]);
        }
        for (i=0; i<nchan; i++) if (chan[i]!=INVALID_SOCKET) n++;
        return n;
}
int nntp_bind_in(struct sockaddr_in *sin)
{
        int gotaddr;
        char xme[BFSZ];
        if (strlen(config_get(C_BIND_IN))==0) return FALSE;
        ncpy(xme,config_get(C_BIND_IN),BFSZ-1);
        gotaddr = nntp_getaddr(xme,sin);
        if (!gotaddr) {
                emsg("Host lookup failed for (%s) use a number\n",xme);
                return FALSE;
        } else {
                imsg("bind_in to address (%s)\n",inet_ntoa(sin->sin_addr));     
        }
        return TRUE;
}
static char bindout[BFSZ];
void nntp_set_bind(char *ipaddr)
{
        ncpy(bindout,ipaddr,200);
}
int nntp_bind_out(struct sockaddr_in *sin)
{
        int gotaddr;
        char xme[BFSZ];
        if (strlen(bindout)>0) {
                ncpy(xme,bindout,200);
        } else {
                if (strlen(config_get(C_BIND_OUT))==0) return FALSE;
                ncpy(xme,config_get(C_BIND_OUT),BFSZ-1);
        }
        gotaddr = nntp_getaddr(xme,sin);
        if (!gotaddr) {
                emsg("Host lookup failed for (%s) use a number\n",xme);
                return FALSE;
        } else {
                imsg("bind_out to address (%s)\n",inet_ntoa(sin->sin_addr));    
        }

        return TRUE;
}
static int input_window;
int nntp_input_window(int x)
{
	input_window = x;
	return TRUE;
}
int nntp_open(char *dst_name)
{
        int dst_port=119;
        struct sockaddr_in xsin,xme;
        int st,lookup;
        int gotaddr;
        int n=nntp_getchan();
        char bf[BFSZ],*s;
        int dontblock = FALSE;

        memset(&xsin,0,sizeof(xsin));
        memset(&xme,0,sizeof(xsin));
        xsin.sin_family = AF_INET;

        if (strchr(dst_name,':')!=NULL) {
                ncpy(bf,dst_name,BFSZ-1);
                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;
        }
        strlwr(dst_name); /* Seems to be required by NIS */
        lookup = lib_gmtime();
        gotaddr = nntp_getaddr(dst_name,&xsin);
        lookup = lib_gmtime() - lookup;
        if (lookup>4) emsg("nntp_open: Lookup took %d seconds \n",lookup);
        if (lookup>20) {
                wmsg("nntp_open: Lookup took %d seconds, use numbers if possible \n",lookup);
                lprintf(nntp_status,200-1,"Host lookup took %d seconds, use numbers if possible\015\012",lookup);
        }
        if (!gotaddr) {
                wmsg("Host Lookup failed {%s} \n",dst_name);
                chan[n] = INVALID_SOCKET;
                nntp_closeit(n);
                return -1;
        }

        chan[n] = dupsocket(xsin.sin_family,SOCK_STREAM, 0);
        nntp_lasterror();               
        if (chan[n]==INVALID_SOCKET) {
                nntp_perror("Socket");
                emsg("Can't connect to {%s} \n",dst_name);
                nntp_closeit(n);
                return -1;
        }
        if (nntp_bind_out(&xme)) {
                if (bind(chan[n],(struct sockaddr *) &xme,sizeof(xme))!=0) {
                        nntp_lasterror();
                        nntp_perror("bind");
                        emsg("Can't bind socket to local address BIND_OUT\n");
                }
        }
                
        xsin.sin_port = htons((unsigned short) dst_port);

        dontblock = TRUE;
        if (config_true(C_OPEN_BLOCK)) dontblock = FALSE;
        if (nntp_doblock) dontblock = FALSE;
#ifdef VMSx
        dontblock = FALSE;
#endif
#ifndef VMS
        if (dontblock) {
                nntp_setblock(n,FALSE);
		dmsg("nntp: Set non blocking\n");
        }
#endif
	/* On 95/nt we believe setting the receive buffer before opening the 
	socket makes the tcpip window larger */
	if (input_window>0) nntp_setrcvbf_raw(chan[n],input_window);

#ifdef CAST_SOCKET
        st = connect(chan[n],(struct sockaddr *)   &xsin, sizeof(xsin));
#else
        st = connect(chan[n], &xsin, sizeof(xsin));
#endif
        nntp_lasterror();               
        chan_busy[n] = FALSE;  /* initialize busy flag */
        if (st!=0) {
                if (!nntp_errbusy()) {
                        nntp_perror(dst_name);
                        nntp_doclose(n);
                        return -1;
                }
                chan_busy[n] = TRUE; /* connection in progress, mark as busy */
        }

dmsg("nntp: open7\n");
        chan_setipnum(n,inet_ntoa(xsin.sin_addr));
dmsg("nntp: open8\n");
        chan_setipname(n,dst_name);
        dmsg("nntp: open feeder channel %d %d %s \n",n,chan[n],chan_getipname(n));
        nntp_chan_init(n);
#ifdef VMS
        if (dontblock) {
                nntp_setblock(n,FALSE);
		imsg("nntp: vms, Set non blocking\n");
        }
#endif
        return n;
}



#define FASTNNTP  TRUE
int nntp_write_s(int c, char *s, int len)
{
        static String *ss;
        if (ss==NULL) ss = str_new();
        str_copysn(ss,s);
        nntp_write(c,ss);
        str_clearbig(ss);
        return 0;
}
int max_strbf(void)
{
        static int i;
        if (i>0) return i;
        i = MAX_STRLEN*2;
        if (i<8000000) i = 8000000;
//        if (i>5000000) i = MAX_STRLEN;
        if (i>10000000) i = 9000000;
	if (i<(MAX_STRLEN+100000)) i = MAX_STRLEN+100000;
        return i;
}
int nntp_write_fast(int c, char *s, int sz)
{
        int wasbusy;
	int r;
        if (!check_sock(c,"nntp_write")) return TRUE; 
        if (sz==0) {imsg("nntp_write, empty string? \n"); return TRUE;}

        wasbusy = chan_busy[c];
	if (wasbusy || (str_len(&nntp_chan_write[c])>0)) {
		str_adds(&nntp_chan_write[c],s,sz);
		return TRUE;
	}

        r = socket_write_small(chan[c],s,sz);
/*if (r>0) socket_write_debug(s,r);*/
        nntp_lasterror();               
        nntp_speedout(c,sz);
        nntp_speedout(MAX_CHAN-1,sz);
 
        if (r<0) {
                if (nntp_errbusy()) goto buf_rest;
                nntp_perror("socket_write error fast");
                imsg("nntp: closing chan[%d] %d\n",c,chan[c]);
                nntp_doclose(c);
                return 0;
        }
	if (r==sz) return r;
buf_rest:
	if (r<0) r=0;
	str_adds(&nntp_chan_write[c],s+r,sz-r);
        chan_busy[c] = TRUE;
	return TRUE;
}
int socket_write_small(SOCKET sk, char *data, int len)
{
    int chunk;
    int r;
    int sent=0;
    for (;len>0;) {
	chunk = 128000;
	if (chunk>len) chunk = len;
	r = socket_write(sk,data+sent,chunk);
//	printf("socket_write %d/%d,  sent=%d len=%d\n",r,chunk,sent,len);
        nntp_lasterror();               
	if (r<=0) {
	    if (r==0) return r;
            if (!nntp_errbusy()) return r;
	    if (sent==0) return r;
	    return sent;
	}
	len -= r;
	sent += r;
	if (r<chunk) break;
    }
    return sent;
}
int chan_outspeed(int c);
int chan_isready(int c);
int direct_isok(int c)
{
#ifdef VMS
	return FALSE;
#endif
#ifdef OS2
	return FALSE;
#endif
	if (!chan_isready(c)) return FALSE;
	if (chan_outspeed(c)>200000) return TRUE;
	if (chan_outspeed(c)==0) return TRUE;
	return FALSE;
}
int nntp_write_direct(int c, char *data, int len)
{
	int r;
#ifdef OS2
	dmsg("nntp: socket_write %d bytes\n",len);
#endif

	r = socket_write_small(chan[c],data,len);
        nntp_lasterror();               
	if (r>0) {
	        nntp_speedout(c,r);
	        nntp_speedout(MAX_CHAN-1,r);
	} 
        if (r<0) {
                if (nntp_errbusy()) {
                        chan_busy[c] = TRUE;
                        return 0;
                }
                nntp_perror("socket_write error");
                if (nntp_errclosed()) {
                        emsg("Beter pretend channel is closed just incase\n");
                        chan[c] = INVALID_SOCKET;
                }
                imsg("nntp: closing chan[%d] %d\n",c,chan[c]);
                nntp_doclose(c);
                r = -1;
        }
	return r;
}
int log_fault(int c, char *s, int len)
{
	static FILE *f;
	if (f==NULL) f = fopen(file_log("thfault.log"),"a");
	if (f==NULL) return FALSE;
	fprintf(f,"%d %d %.500s\n",c,nntp_isthreadsk(c),s);
	fflush(f);
	return TRUE;
}
int nntp_write(int c, String *ss)
{
        int sz = str_len(ss);
        int wasbusy;
	int r;
        if (!check_sock(c,"nntp_write")) return TRUE; 

if (nntp_isthread(c)) log_fault(c, str_s(ss),str_len(ss));
if (nntp_isthreadsk(c)) log_fault(c,str_s(ss),str_len(ss));

        if (sz==0) {imsg("nntp_write, empty string? \n"); return TRUE;}
	chan_total[c] += sz/1000;
        if (direct_isok(c)) {
		r = 0;
		if (str_len(&nntp_chan_write[c])==0) {
			r = nntp_write_direct(c,str_s(ss),str_len(ss));
			if (r<0) return FALSE;
			if (r==str_len(ss)) return TRUE;
		}
		str_add_rest(&nntp_chan_write[c],ss,r);
	} else {
		str_add(&nntp_chan_write[c],ss);
	}
	

/*      str_checknulls(&nntp_chan_write[c],"nntp_write");*/
        if (str_len(&nntp_chan_write[c])>(max_strbf())) {
                emsg("nntp_write: FATAL max_strlen exceeded %d\n",str_len(&nntp_chan_write[c]));
                str_clear(&nntp_chan_write[c]);
                return TRUE;
        }

        wasbusy = chan_busy[c];
        chan_busy[c] = TRUE;
        if (chan_block[c]) nntp_write_now(c);
        else {
                if (chan[c]==INVALID_SOCKET) {emsg("nntp: channel not open %d ",c); return TRUE;}
                if (!config_true(C_NNTP_SLOW)) {
                        if (sz<2000) if (!wasbusy) nntp_write_now(c);
                }
                /* chan_busy[c] = TRUE; */  /* never send more than one packet without checking*/
        }
        return TRUE;
}
int nntp_set_busy(int c)
{
	chan_busy[c] = TRUE;	
	return TRUE;
}

void show_outbf(char *s, int len);
int nntp_write_flush(int c)
{
        nntp_write_now(c);
        if (chan_busy[c]) return FALSE;
        return TRUE;
}

void socket_write_debug(char *s, int x)
{
	FILE *f;

	f = fopen("write.txt","ab");
	if (f==NULL) return;
	fwrite(s,1,x,f);
	fclose(f);
}
void socket_read_debug(char *s, int x)
{
	FILE *f;

	f = fopen("read.txt","ab");
	if (f==NULL) return;
	fwrite(s,1,x,f);
	fclose(f);
}

int nntp_write_now(int c)
{
        int sz;
        int i;
        char *s;
        int r;
        int out_size = atoi(config_get(C_OUT_SIZE));
        int blen=str_len(&nntp_chan_write[c]);
        if (!check_sock(c,"nntp_write_now")) return TRUE; 
#ifdef LIMIT_OUT_SIZE
        if (out_size<10) out_size = 2000;
#endif


        sz = nntp_bigsz()-1;
        if ((out_size>0)  && (out_size<nntp_bigsz())) sz = out_size;
        if (sz > blen) sz = blen;

        if (blen==0) {
                chan_busy[c] = FALSE;
                return 0;
        }

        /* Wrap on crlf records for dumb news readers, doesn't work because socket() adds records back together */

        if (config_true(C_TCP_WRAPLF)) {
                s = str_s(&nntp_chan_write[c]) + sz - 1;
                if (sz>1000) for (i=0; i<900; i++,s--) {
                        if (*s == '\012') {
                                dmsg("nntp: Wrapped at %d/%d\n",sz-i,sz);
                                sz -= i;
                                break;
                        }
                }
        }

/*      if (config_true(C_TCP_NODELAY)) {
                s = str_s(&nntp_chan_write[c]);
                s = strchr(s,'\n');
                if (s!=NULL) {
                        sz = s - str_s(&nntp_chan_write[c]) + 1;
                        dmsg("nntp: tcp_lines %d/%d\n",sz,str_len(&nntp_chan_write[c]));
                }
        }
*/

        dmsg("nntp: [%d:%d] write %d bytes \n",c,chan[c],sz);
/*        //ncpy(chan_outbf[c],str_s(&nntp_chan_write[c]),sz);*/
        r = nntp_write_direct(c,str_s(&nntp_chan_write[c]),sz);
        if (r<0) return 0;
        if (r<sz) sz = r;
        if (blen>sz) {
                str_right(&nntp_chan_write[c],sz);
/*              str_checknulls(&nntp_chan_write[c],"after str_right");*/
        } else {
                str_setlen(&nntp_chan_write[c],0);
                /*
		str_clearbig(&nntp_chan_write[c]);
                str_expand(&nntp_chan_write[c],8000);
		*/
                chan_busy[c] = FALSE;
        }

        return r;
}
int nntp_isbusy(int c)
{
        if (c<0) return FALSE;
        return chan_busy[c];
}
int nntp_outsz(int c)
{
        if (!check_sock(c,"nntp_outsz")) return 0; 
        return str_len(&nntp_chan_write[c]);
}

static int nullsok;
int nntp_read_raw(int c, char *bf, int sz)
{
	int r;
	nullsok = TRUE;
	r = nntp_read(c,bf,sz);
	nullsok = FALSE;
	return r;
}
int nntp_socket_read(int c, char *bf, int sz)
{
	int r;
        r =  socket_read(chan[c],bf,sz);
	return r;
}
int nntp_socket_write(int c, char *bf, int sz)
{
	int r;
        r =  socket_write(chan[c],bf,sz);
	return r;
}

int nntp_read(int c, char *bf, int sz)
{
  int r;
  int nbytes=sz;
  char *s1,*s2;
  int i;
        if (!check_sock(c,"nntp_read")) return -1; 
#ifdef MIGHT_FAIL
        errno = 0;
#endif
#ifdef unixxx_messed_up_solaris
  if (!chan_block[c]) {
#ifdef OS2
         socket_ioctl(chan[c],FIONREAD,&nbytes,sizeof(nbytes));
#else
#ifndef VMS
         socket_ioctl(chan[c],FIONREAD,&nbytes);
#endif
#endif


        if (nbytes<sz) sz = nbytes;
  } else nbytes = sz;
#else
        nbytes = sz;
#endif
  r = 0;
  if (nbytes>0) {
        dmsg("nntp: Socket read %d bytes \n",sz);
#ifdef MIGHT_FAIL
        errno = 0;
#endif
        r =  socket_read(chan[c],bf,sz);
/*if (r>0) socket_read_debug(bf,r);*/
        if (r>0) {
                /* Collapse nulls which screw it up */
		if (!nullsok) if (!config_true(C_NULL_NOSTRIP)) { 
		 bf[r] = 0;
		 for (s1=bf;*s1!=0;s1++) ;
		 if ((s1-bf) < r) {
			imsg("Found NULLs in input %d %d\n",(s1-bf), r);
                 for (i=0,s1=s2=bf; i<r; i++,s2++) {
                        *s1 = *s2;
                        if (*s1 != 0) s1++;
                 }
                 if (r!=(s1-bf)) {
			 imsg("Collapsed %d nulls in input %.80s\n",r-(s1-bf),bf); 
		 }
                 r = s1 - bf;
                 if (r==0) {r = 1; bf[0] = ' ';}
		 }
		}
        }
        nntp_lasterror();               
        dmsg("nntp: Socket read_done, got %d bytes\n",r);
  }
#ifdef MIGHT_FAIL
  if (r<0) {
        if ((werrno==EAGAIN) || nntp_errbusy() || (werrno==0)) {
#ifndef VMS
                filemsg("nntp: Socket reported EAGAIN , trying to continue\n");
                                in_count[c]  = chan_inspeed(c) + 100;
#endif
                return 0;
        }
        if (werrno!=0) fimsg("nntp: Socket closed, errno=%d %s\n",werrno,chan_getipname(c));
  }
#endif
  if (r<=0) {
        if (r!=0) {
                nntp_perror("socket_read failed");
                imsg("nntp: Closing socket chan[%d] %d %s\n",c,chan[c],chan_getipname(c));
        }
        else imsg("nntp: Socket closed by remote end [%d] %d %s\n",c,chan[c],chan_getipname(c));
        nntp_doclose(c);
        return -1;
  }
  nntp_speedin(c,r);
  return r;
}
void nntp_speedin(int c, int sz)
{
        int delay;
        int max_speed;
        int t = xtime_now;
        if (in_time[c]!=t) {
                max_speed = chan_inspeed(c);
                delay = t - in_time[c];
                if (delay>20) delay = 20;
                in_speed[c] = in_speed[c]*(1.0-delay*.05) + in_count[c]*(delay*.05);
                if (in_speed[c] < 0.0001) in_speed[c] = 0;
                in_count[c] = 0;
                in_time[c] = t;
        }
        in_count[c] += sz;
}
int chan_outspeed(int c);
void nntp_speedout(int c, int sz)
{
        int delay;
        int t = xtime_now;
        if (out_time[c]!=t) {
                max_out = chan_outspeed(c);
                max_out_total = atoi(config_get(C_MAX_OUT_TOTAL));
                if (max_out_total==0) max_out_total = 100000000;
                delay = t - out_time[c];
                if (delay>20) delay = 20;
                out_speed[c] = out_speed[c]*(1.0-delay*.05) + out_count[c]*(delay*.05);
                if (out_speed[c] < 0.0001) out_speed[c] = 0;
                out_count[c] = 0;
                out_time[c] = t;
        }
        out_count[c] += sz;
}
int nntp_toofast(int c)
{
        static int done;
        int sp = chan_inspeed(c);
        if (in_count[c]>sp) {
                done = TRUE;
                if (!done) imsg("Limiting speed for channel [%d] %s\n",c,chan_getipname(c));
        }
        if (in_count[c]>sp) return TRUE;
        return FALSE;
}
int nntp_fastout(int c)
{
        static int done;
        if (out_count[c]>chan_outspeed(c)) {
                if (!done) imsg("Limiting outgoing speed for channel [%d] %s\n",c,chan_getipname(c));
                done = TRUE;
        }
        if (out_count[c]>chan_outspeed(c)) return TRUE;
        return FALSE;
}
int nntp_fastout_total(void)
{
        int c = MAX_CHAN-1;
        static int done;
        if (out_count[c]>max_out_total) {
                if (!done)  imsg("max_out_total Limiting speed [%d] %d > %d\n",c,out_count[c],max_out_total);
                done = TRUE;
        } 
        if (out_count[c]>max_out_total) return TRUE;
        return FALSE;
}
int nntp_curspeed(int c)
{
        return (int) in_speed[c];
}
int nntp_curspeedout(int c)
{
        return (int) out_speed[c];
}
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]==INVALID_SOCKET) return FALSE;
        return TRUE;
}
char *nntp_hostname(void)
{
        static int fail;
   static char host[200];
#ifdef NETWARE
        strcpy(host,config_get(C_MYNAME));
#else
        if (fail) return "";
        if (gethostname(host,190)!=0) imsg("nntp: Can't get host name\n");
        if (strlen(host)==0) fail = TRUE;
#endif
   return host;
}

int nntp_isnumber(char *name)
{
    char *c;
    int dot =0;
    /* should strip trailing \r\n and whitespace? */
    for(c=name;*c!=0;c++) {
	if (*c==' ') break;
    	if (!((isdigit(*c))||(*c=='.')||(*c=='\r')||(*c=='\n'))) return FALSE;
	if (*c=='.') dot++;
    }
    if (dot==3) return TRUE;
    return FALSE;
}

char *nntp_hostip(void)
{
#ifdef NETWARE
        return nntp_hostname();
#else
        struct sockaddr_in xsin;
        int gotaddr;
        int dontblock = FALSE;
        static char answer[100];

        if (strlen(answer)>0) return answer;
        strcpy(answer,nntp_hostname());
        if (strlen(answer)==0) return "";
        /*if (isdigit(*answer)) return answer;*/
	if (nntp_isnumber(answer))
	    return answer;
        memset(&xsin,0,sizeof(xsin));
        xsin.sin_family = AF_INET;
        gotaddr = nntp_getaddr(nntp_hostname(),&xsin);
        if (!gotaddr) {
                emsg("Host lookup failed for myself (%s)\n",nntp_hostname());
                return nntp_hostname();
        }
        strcpy(answer,inet_ntoa(xsin.sin_addr));
        imsgf("Looking up host ip address (%s) = %s\n",nntp_hostname(),answer);
        return answer;
#endif
}  
static int set_dtable;
int nntp_set_dtable(int x)
{
	set_dtable = x;
	return TRUE;
}

#ifdef OS2
#define NEED_DTABLE TRUE
#endif

#ifdef WIN_SOCK
#define NEED_DTABLE TRUE
#endif

#ifdef USE_RLIMIT
#include <sys/resource.h>
int getdtablesize(void)
{
        static int min;
        struct rlimit lim;
        static int max;
	if (set_dtable>0) return set_dtable;
        if (min>0) return min;

        getrlimit(RLIMIT_NOFILE,&lim);
        max = lim.rlim_max;

// Next line chjanged as linux can have much more now.
#ifndef LINUX
        if (max>1024) max = 1024;
#endif

#ifdef FD_SETSIZE
        if (max>FD_SETSIZE) max = FD_SETSIZE;
#endif
        lim.rlim_cur = max;
        if (setrlimit(RLIMIT_NOFILE,&lim)!=0) filemsg("Set rlimit failed\n");

        min = 1024;
#ifdef FD_SETSIZE
        imsg("nntp: FD_SETSIZE %d, rlim %d\n",FD_SETSIZE,lim.rlim_cur);
        if (min>FD_SETSIZE) min = FD_SETSIZE;
#endif
        if (min>lim.rlim_cur) min = lim.rlim_cur;
        return min;
}
#endif

#ifdef NEED_DTABLE
int getdtablesize(void)
{
	if (set_dtable>0) return set_dtable;
#ifdef WIN_SOCK
        return FD_SETSIZE;
#else
#ifdef unix
#ifdef FD_SETSIZE
        return FD_SETSIZE;
#else
        return 64;
#endif
#endif
#endif
#ifdef VMS
#ifdef MULTINET
        return 32;
#else
        return FD_SETSIZE;
#endif
#endif
}
#endif

void log_socket_write(int chan, char *bf, int sz)
{
        static FILE *f;
        if (f==NULL) {
                f = fopen("socket.log","w");
        }
        if (f==NULL) {emsg("Can't open socket.log\n"); return;}
        fprintf(f,"WRITE TO Channel %d , size %d \n",chan,sz);
        fwrite(bf,1,sz,f);
}
void wsock_error(char *s);
void nntp_perror(char *s)
{
#ifdef OS2
        wsock_error(s);
/*
        psock_errno(s);
	fimsg("nntp_perror: %d\n",sock_errno());
	*/
#else
#ifdef WIN_SOCK
        wsock_error(s);
#else
#ifdef unix
        fimsg("nntp_perror: %s\n",s);
#else
        fimsg("nntp_perror: %s %d\n",s,errno);
        /*
        socket_perror(s);
        */
#endif
#endif
#endif
}

#ifdef OS2
#include "wsa.h"
#define WIN_SOCK 1
#endif

#ifdef WIN_SOCK
void wsock_error(char *s)
{
        int err = werrno;
        if (err==WSANOTINITIALISED) emsg("wsock: (%s) A successful WSAStartup must occur before using this API.\n",s);
        else if (err==WSAENETDOWN)      emsg("wsock: (%s) The Windows Sockets implementation has detected that the network subsystem has failed.\n",s);
        else if (err==WSAEACCES)        emsg("wsock: (%s) The requested address is a broadcast address, but the appropriate flag was not set.\n",s);
        else if (err==WSAEINTR) emsg("wsock: (%s) The (blocking) call was cancelled via WSACancelBlockingCall.\n",s);
        else if (err==WSAEINPROGRESS)   emsg("wsock: (%s) A blocking Windows Sockets operation is in progress.\n",s);
        else if (err==WSAEFAULT)        emsg("wsock: (%s) The buf argument is not in a valid part of the user address space.\n",s);
        else if (err==WSAENETRESET)     emsg("wsock: (%s) The connection must be reset because the Windows Sockets implementation dropped it.\n",s);
        else if (err==WSAENOBUFS)       emsg("wsock: (%s) The Windows Sockets implementation reports a buffer deadlock.\n",s);
        else if (err==WSAENOTCONN)      emsg("wsock: (%s) The socket is not connected.\n",s);
        else if (err==WSAENOTSOCK)      emsg("wsock: (%s) The descriptor is not a socket.\n",s);
        else if (err==WSAEOPNOTSUPP)    emsg("wsock: (%s) MSG_OOB was specified, but the socket is not of type SOCK_STREAM.\n",s);
        else if (err==WSAESHUTDOWN)     emsg("wsock: (%s) The socket has been shutdown; it is not possible to send on a socket after shutdown has been invoked with how set to 1 or 2.\n",s);
        else if (err==WSAEWOULDBLOCK)   emsg("wsock: (%s) The socket is marked as non-blocking and the requested operation would block.\n",s);
        else if (err==WSAEMSGSIZE)      emsg("wsock: (%s) The socket is of type SOCK_DGRAM, and the datagram is larger than the maximum supported by the Windows Sockets implementation.\n",s);
        else if (err==WSAEINVAL)        emsg("wsock: (%s) The socket has not been bound with bind.\n",s);
        else if (err==WSAECONNABORTED)  emsg("wsock: (%s) The virtual circuit was aborted due to timeout or other failure.\n",s);
        else if (err==WSAECONNRESET)    imsg("wsock: (%s) The virtual circuit was reset by the remote side.\n",s);
        else if (err==WSAETOOMANYREFS)  emsg("wsock: (%s) Too many refs.\n",s);
        else if (err==WSAETIMEDOUT)     emsg("wsock: (%s) Timedout\n",s);
        else if (err==WSAECONNREFUSED)  emsg("wsock: (%s) Connection Refused\n",s);
        else if (err==WSAEDESTADDRREQ)  emsg("wsock: (%s) DestAddrReq\n",s);
        else if (err==WSAEMSGSIZE)      emsg("wsock: (%s) Message Size\n",s);
        else if (err==WSAEADDRINUSE)    emsg("wsock: (%s) Address in use\n",s);
        else if (err==WSAEADDRNOTAVAIL) emsg("wsock: (%s) Address not avail\n",s);
        else if (err==WSAENETUNREACH)   emsg("wsock: (%s) Network Unreachable\n",s);
        else if (err==WSAENETRESET)     emsg("wsock: (%s) Network Reset\n",s);
        else if (err==WSAENOBUFS)       emsg("wsock: (%s) No bufs\n",s);
        else if (err==WSAESHUTDOWN)     emsg("wsock: (%s) Shutdown\n",s);
        else if (err==WSAENAMETOOLONG)  emsg("wsock: (%s) Name too long\n",s);
        else if (err==WSAEHOSTDOWN)     emsg("wsock: (%s) Host down\n",s);
        else if (err==WSAEHOSTUNREACH)  emsg("wsock: (%s) Host Unreachable\n",s);
        else if (err==WSAENOTEMPTY)     emsg("wsock: (%s) Not Empty\n",s);
        else emsg("wsock: (%s) Socket Unknown error code %d \n",s,err);
}
#endif

#ifdef WIN_SOCK
int nntp_errbusy(void)
{
        int err = werrno;
        if (err==WSAEWOULDBLOCK) {
                dmsg("wsock: The socket is marked as non-blocking and the requested operation would block.\n");
                return TRUE;
        }
#ifdef OS2
        if (err==WSAEINPROGRESS) {
                dmsg("wsock: Blocking in progress.\n");
                return TRUE;
        }
#endif
        return FALSE;
}
#else
int nntp_errbusy(void)
{
        if (werrno==EWOULDBLOCK) {
                dmsg("socket: EWOULDBLOCK, The socket is marked as non-blocking and the requested operation would block.\n");
                return TRUE;
        }
        if (werrno==EAGAIN) {
                dmsg("socket: EWOULDBLOCK, The socket is marked as non-blocking and the requested operation would block.\n");
                return TRUE;
        }
        if (werrno==EINPROGRESS) {
                dmsg("socket: EINPROGRESS The open is pending \n");
                return TRUE;
        }
        return FALSE;
}
#endif
int nntp_errclosed(void)
{
#ifdef badidea
#ifdef WIN_SOCK
        int err = werrno;
        if (err==WSAECONNABORTED) return TRUE;
        if (err==WSAECONNRESET) return TRUE;
        if (err==WSAESHUTDOWN) return TRUE;
#else
#ifdef OS2
        if (werrno==SOCEPIPE) return TRUE;
#else
        if (werrno==EPIPE) return TRUE;
#endif
#endif  
#endif
        return FALSE;
}



/*
struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};
*/

/* Forward and reverse name lookup cache */


#define DSIZE 400  /* Increase to 500 when testing done*/
static struct sockaddr_in dns_sin[DSIZE];
static char *dns_name[DSIZE];
static long dns_time[DSIZE];
static int dns_ok[DSIZE];
static int ndns,dns_next;

void dns_clear(void)
{
        int i;
        for (i=0; i<ndns; i++) {
                if (dns_name[i]!=NULL) strcpy(dns_name[i],"");
                dns_sin[i].sin_addr.s_addr = 0;
        }
}
int dns_ageok(int i, int isreverse)
{
        int retry;
        int age = lib_gmtime()-dns_time[i];
        retry = atoi(config_get(C_OPEN_RETRY));
        if (retry==0) retry = 60;
        if (retry<=0) retry = 1;

        if (dns_ok[i]) {
                if (age<24*60*60*7) return TRUE;  /* Less than a week old */
        } else {
                if (isreverse) {
                        if (age<60*60*12) return TRUE;  /* Less than 1/2 days old, reverse  */
                } else {
                        if (age<60*retry) return TRUE;  /* Less than 60 minutes old open */
                }
        }
        return FALSE;
}
int dns_add(char *name, struct sockaddr_in *sin, int isok)
{
        int n;

	if (name==NULL) return 0;
        if (strlen(name)!=0) n = dns_find_name(name);
        else n = dns_find_addr(sin);
        if (n<0) {
                if (ndns<DSIZE) {
                        n = ndns++;
                } else {
                        n = dns_next++;
                        if (dns_next>=DSIZE) dns_next = 0;
                }
        }
        if (dns_name[n]==NULL) dns_name[n] = mymalloc(100);
        ncpy(dns_name[n],name,99);
        dns_sin[n].sin_addr.s_addr = sin->sin_addr.s_addr;
        dns_sin[n].sin_family = sin->sin_family;
        dns_ok[n] = isok;
        dns_time[n] = lib_gmtime();
        return 0;
}
int dns_find_name(char *name)
{
        int i;
        for (i=0; i<ndns; i++) {
                if (dns_name[i]!=NULL) if (strcmp(name,dns_name[i])==0) {
                        return i;
                }
        }
        return -1;
}
int dns_find_addr(struct sockaddr_in *sin)
{
        int i;
        for (i=0; i<ndns; i++) {
                if (dns_sin[i].sin_addr.s_addr == sin->sin_addr.s_addr) {
                        return i;
                }
        }
        return -1;
}
int nntp_lookup(char *ip, char *rslt[], int max)
{
        struct sockaddr_in sin;
        struct hostent *hp;
        int i;
        int xnow;
        imsgf("nntp: Doing forward DNS lookup (%s)\n",ip);
        xnow = lib_gmtime();
        hp = gethostbyname(ip);
        imsgf("nntp: forward DNS completed\n");
        if ((lib_gmtime()-xnow)>4) {
                emsg("forward dns lookup took %d seconds, check access.conf entry (%s) and or set access_noforward true\n",(lib_gmtime()-xnow),ip);
        }
        if (hp==NULL) return 0;
        for (i=0; i<max; i++) {
                if (hp->h_addr_list[i] == NULL) break;
                memcpy(&(sin.sin_addr),hp->h_addr_list[i],  hp->h_length);
                strcpy(rslt[i], inet_ntoa(sin.sin_addr));
        }
        return i;
}

int nntp_getaddr(char *name, struct sockaddr_in *sin)
{
        static int badloop;
        struct hostent *hp;
        int i;
        badloop = 0;

        if (name==NULL) {emsg("nntp_getaddr: Can't lookup NULL string\n"); return FALSE;}
        if (strlen(name)==0) {emsg("nntp_getaddr: Can't lookup empty string\n"); return FALSE;}
again:
        badloop++;
        if (badloop>4) {
                emsg("nntp_getaddr() failed for name {%s} \n",name);
                return FALSE;
        }
        if (!config_true(C_AUTODIAL)) {
        i = dns_find_name(name);
         if (i>=0) if (dns_ageok(i,FALSE)) {
                sin->sin_addr.s_addr = dns_sin[i].sin_addr.s_addr;
                sin->sin_family = dns_sin[i].sin_family;
                return dns_ok[i];
         }
        }
        /*if (isdigit(*name)) */
	if (nntp_isnumber(name)) {
		sin->sin_addr.s_addr = inet_addr(name);
                sin->sin_family = AF_INET;
                if (!config_true(C_AUTODIAL)) {
                        dns_add(name,sin,TRUE);
                        return TRUE;
                }
                goto again;
        } else {
                sin->sin_addr.s_addr = 0;
                imsgf("nntp: Doing forward DNS lookup\n");
                hp = gethostbyname(name);
                imsgf("nntp: forward DNS completed\n");
                dmsg("nntp: done lookup %p {%s}\n",hp,name);
                if (hp==NULL) {
                        if (!config_true(C_AUTODIAL)) {
                                dns_add(name,sin,FALSE); 
                        } else return FALSE;
                        goto again; 
                }
                memcpy(&(sin->sin_addr),hp->h_addr,  hp->h_length);
                dmsg("nntp: lookup worked %d\n",hp->h_length);
                sin->sin_family = hp->h_addrtype;
                if (!config_true(C_AUTODIAL)) {
                        dns_add(name,sin,TRUE);
                } else return TRUE;
                goto again;
        }

}

int dns_forward(char *name,struct sockaddr_in *sin)
{
	int n;
	int i;
	static char *rslt[200];
	char original[BFSZ];

	if (rslt[0]==NULL) {
		for (i=0; i<200; i++) rslt[i] = mymalloc(20);
	}

	strcpy(original,inet_ntoa(sin->sin_addr));
	strcpy(dns_check_msg,"Lookup of name failed");
	if (name == NULL) return FALSE; 
	n = nntp_lookup(name,rslt,190);
	if (n==0) {
		lprintf(dns_check_msg,BFSZ-1,"%s->NOT.IN.DNS",name);
		return FALSE;
	}
	for (i=0; i<n; i++) {
		if (strcmp(original,rslt[i])==0) {
			strcpy(dns_check_msg,"");
			return TRUE;
		}
	}
	lprintf(dns_check_msg,BFSZ-1,"%s->%s",name,rslt[0]);
	return FALSE;
}
int nntp_getname(char *name, struct sockaddr_in *sin)
{
        struct hostent *hp;
        int i;
	char xname[BFSZ];
	int xloop=0;

        if (config_true(C_NODNS)) return FALSE;
	ncpy(name,"xfailed",20);

again:
        xloop++;
        if (xloop>2) {
                ncpy(name,"",99);
                return FALSE;
        }
        i = dns_find_addr(sin);
        if (i>=0) if (dns_ageok(i,TRUE)) {
                ncpy(name,dns_name[i],99);
                return dns_ok[i];
        }

/*RHP cast for mac version */
        imsgf("nntp: Doing reverse DNS lookup (%s)\n",name);
#ifdef MacXX
        hp = gethostbyaddr( (ip_addr *)&(sin->sin_addr), sizeof(sin->sin_addr), AF_INET);
#else
        hp = gethostbyaddr( (char *) &(sin->sin_addr), sizeof(sin->sin_addr), AF_INET);
#endif
        imsgf("nntp: Reverse DNS completed %p\n",hp);
        if (hp==NULL) {
		imsg("nntp: reverse lookup failed\n");
bad:            dns_add("",sin,FALSE);
                goto again;
        }

	if (hp->h_name==NULL) {
	    imsg("nntp: reverse lookup failed - name was blank\n");
	    goto bad;
	}

	ncpy(xname,hp->h_name,BFSZ-1);
	dmsg("nntp: name at this poitn (%s)\n",xname);
	if (config_true(C_DNS_CHECK)) { /* Check it goes forward too */
	  if (!dns_forward(xname,sin)) {
		return FALSE;
	  }
	}
	dmsg("nntp: name at this poitn (%s)\n",xname);

        dns_add(xname,sin,TRUE);
        goto again;
}

String *nntp_show_dns(void)
{
        static String *stat;
        char bf[BFSZ];
        int i;
        char *nn,*s;
        if (stat==NULL) stat = str_new();

        str_copysn(stat,"");
        lprintf(bf,BFSZ-1,"DNS caching, entries %d \015\012",ndns);
        str_addsn(stat,bf);
        for (i=0; i<DSIZE; i++) {
                s = dns_name[i];
                if ((s!=NULL) || (dns_sin[i].sin_addr.s_addr !=0)) {
                        if (s==NULL) s = "(none)";
                        if (strcmp(s,"")==0) s = "(unknown)";
                        nn = inet_ntoa(dns_sin[i].sin_addr);
                        lprintf(bf,BFSZ-1,"[%d] ok=%d age=%d %s = %s \015\012",i,dns_ok[i],lib_gmtime()-dns_time[i],s,nn);
                        str_addsn(stat,bf);
                }
        }
        return stat;
}
void nntp_lasterror(void)
{
        werrno = errno;
#ifdef OS2
        werrno = sock_errno();
#else
#ifdef WIN_SOCK
        werrno = WSAGetLastError();
#endif
#endif
}

void nntp_setlocalip(int c,char *s)
{
        if (chan_lclip[c]!=NULL) mystrfree(chan_lclip[c],SD_NCMD);
        chan_lclip[c] = mystrdup(s,SD_NNTP);
}
char *nntp_localip(int c)
{
        /* get the port on our machine he is connecting to */
        int len;
        struct sockaddr_in  usoc, *us;
        len=sizeof(struct sockaddr_in);

        if (chan_lclip[c]!=NULL) return chan_lclip[c];

        us = &usoc;
        if (getsockname(chan[c],(struct sockaddr *) us,&len) < 0 ){
          /*filemsg("Getports failed for [%d] \n",c);*/
	  imsg("getsockname() failed for channel %d h=%d\n",c,chan[c]);
          return "none";
        }
        return inet_ntoa(usoc.sin_addr);
        
}
static int chan_lport[MAX_CHAN];
static int chan_rport[MAX_CHAN];
void nntp_setports(int c, int local, int remote)
{
        if (c<0) return;
        chan_lport[c] = local;
        chan_rport[c] = remote;
}
int nntp_getports(int c, int *local, int *remote)
{
        int len;
        struct sockaddr_in hsoc, usoc, *him, *us;

        if (chan_lport[c]>0) {
                *local = chan_lport[c];
                *remote = chan_rport[c];
                chan_lport[c] = chan_rport[c] = 0;
                return TRUE;
        }
        
        him = &hsoc;
        len=sizeof(struct sockaddr_in);
        if ( getpeername(chan[c], (struct sockaddr *) him, &len) < 0 ) {
          filemsg("getpeername failed for [%d] h=%d\n",c,chan[c]);
          return FALSE;
        }

        /* get the port on our machine he is connecting to */
        len=sizeof(struct sockaddr_in);
        us = &usoc;
        if (getsockname(chan[c],(struct sockaddr *) us,&len) < 0 ){
          filemsg("getports getsockname() failed for [%d] h=%d\n",c,chan[c]);
          return FALSE;
        }

        *local = (int) ntohs(us->sin_port);
        *remote = (int) ntohs(him->sin_port);
        return TRUE;
}
static int idle_ticks,idle_ticks2;
static int idle_time;
void nntp_addidle(int x)
{
        idle_ticks += x;        
        idle_ticks2 += x;       
        if (idle_time==0) idle_time = lib_msec()-x;
}
int nntp_getidle(int *since)
{
        int x = idle_ticks;
        *since = lib_msec() - idle_time;
        idle_time = lib_msec();
        idle_ticks = 0;
        return x;
}
int nntp_getidle2(int *since)
{
        static int idle_time;
        int x = idle_ticks2;
        if (idle_time==0) {
                *since = 1;
                idle_ticks2 = 0;
                idle_time = lib_msec();
                return 1;
        }
        *since = lib_msec() - idle_time;
        idle_time = lib_msec();
        idle_ticks2 = 0;
        return x;
}
#ifndef INADDR_NONE
#define INADDR_NONE 0
#endif
void nntp_exit(void)
{
#ifdef NETWARE
        int w=TRUE;
        int i;
        for (i=0; i<nchan; i++) {
                if (nntp_isopen(i)) {
                        imsg("Closing socket %d %d\n",i,chan[i]);
                        socket_ioctl(chan[i],O_NDELAY,&w);
                        nntp_closeit(i);
                }
        }
        if (x_listen>=0) socket_close(x_listen);
        x_listen = -1;
#endif
}

#ifdef __MWERKS__
#include "macdns.h"
#define CLOSE_DONE 1002

int mac_socket_close(SOCKET s)
{
        imsg("Mac_socket_close()\n");
        shutdown(s,2);
        lib_sleep(1);
        nntp_linger_hard(s);
        if (closesocket(s)!=0) {
                nntp_lasterror();
                nntp_perror("MacCloseSocket");
        }
        return 0;
}
#endif

int hard_socket_close(SOCKET s)
{
        int r;
#ifdef SAVE_LOW
	socket_low_free(s);
#endif
        if (socket_close(s)!=0) {
                nntp_lasterror();
                nntp_perror("SocketClose failed, trying hard close");
                shutdown(s,2);
                nntp_linger_hard(s);
                r = socket_close(s);
                return r;
        }
        return 0;
}



void nntp_listen_hostport(char *hostport)
{
	char *s;
	char host[BFSZ];
	int port=119;

	if (strlen(hostport)==0) return;
	ncpy(host,hostport,100);
	s = strchr(host,':'); 
	if (s!=NULL) {
		*s = 0;
		port = atoi(s+1);
	}

	l_port[nlisten] = port;
	imsg("Attempting to listen on extra port %s %d\n",host,l_port[nlisten]);
	l_chan[nlisten] = nntp_listen(host,l_port[nlisten]);
	if (l_chan[nlisten]!=INVALID_SOCKET) nlisten++; 
}
SOCKET nntp_listen(char *host, int port)
{
        int on=1;
        int r;
        int quelen;
	SOCKET sk;

	sk = socket(AF_INET, SOCK_STREAM, 0);
        nntp_lasterror();               
        if (sk == INVALID_SOCKET) {  
		nntp_perror("nntp: Can't listen on extra port");
		return INVALID_SOCKET;
	}
        sp = &fakesp;
        fakesp.s_port = htons((short) port);
        r = setsockopt(sk, SOL_SOCKET, SO_REUSEADDR,ONCAST &on, sizeof(int));
        nntp_lasterror();               
        if (r < 0) {
		filemsg("nntp: port: Can't setsockopt  %d \n",r);
                nntp_perror("tcpnntpserver: setsockopt");
                return INVALID_SOCKET;
	}
	xsin.sin_family = AF_INET;
        xsin.sin_addr.s_addr = INADDR_ANY;
        xsin.sin_port = sp->s_port;

	
	if (strlen(host)>0) {
	        if (!nntp_getaddr(host,&xsin)) {
	                emsg("Host lookup failed for (%s) use a number\n",host);
			socket_close(sk);
	                return INVALID_SOCKET;
	        } else {
	                imsg("bind_in to address (%s)\n",inet_ntoa(xsin.sin_addr));     
	        }
	}


#ifdef GOT_SOCKADDR
        if (bind(sk,(struct sockaddr *)  &xsin, sizeof (xsin)) < 0) {
#else
        if (bind(sk,&xsin, sizeof (xsin)) < 0) {
#endif
		nntp_lasterror();               
                filemsg("nntp: Can't bind \n");
                nntp_perror("tcpnntpserver: bind");
		socket_close(sk);
                return INVALID_SOCKET;
        }
        
        nntp_setblockraw(sk,FALSE);
        close_on_exec(sk);
        quelen = 25;
        if (listen(sk, quelen) < 0) {
                nntp_lasterror();               
                filemsg("nntp: Can't set listen for port %d\n",port);
                nntp_perror("tcpnntpserver: listen");
		socket_close(sk);
		return INVALID_SOCKET;
        }
	return sk;
}

