/*
 * listen.c Copyright 1999 Christopher M Sedore. All Rights Reserved.
 * Please see the "COPYING" file for license details.
 * 
 * Unfortunately, this is a bit of a mish-mash.  It contains routines to
 * asynchronously listen on a socket, accept a connection, and connect.
 * It also contains the signal handling code.
 */
#include "main.h"

extern volatile int timerCallback;

static int curConnected=0,maxConnections=512;

int lsock=-1;

int mypid;

void
my_accept(struct myaiocb *,int);

int
my_listen(int port)
{
	int s;
	struct sockaddr_in sa;
	int val=1;
	struct myaiocb *cb;

	signal_setup();
	
	s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

	setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&val,4);
	
	bzero(&sa,sizeof(struct sockaddr_in));
	sa.sin_port=htons(port);

	bind(s,(struct sockaddr *)&sa,sizeof(sa));

	listen(s,128);
	
	if (ioctl(s,FIOASYNC,&val)) {
		perror("ioctl");
	}
	if (ioctl(s,FIONBIO,&val)) {
		perror("ioctl");
	} 

	lsock=s;
	cb=(struct myaiocb *) malloc(sizeof(struct myaiocb));
	bzero(cb,sizeof(struct myaiocb));
	cb->cb.aio_fildes=s;
	cb->cb.aio_buf=&cb->cb.aio_offset;
	cb->cb.aio_nbytes=2;
	cb->callback=(void *)my_accept;

	if (aio_read((struct aiocb *)cb)<0) {
		perror("listensock");
	}

	return s;
}

void
setsocktimeouts(int s)
{
	struct timeval tv;

	tv.tv_sec=60;
	tv.tv_usec=0;

	if (setsockopt(s,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(struct timeval)) ||
	setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(struct timeval)))
		perror("socktimeouts");

	
}

void
my_accept(struct myaiocb *cb,int unused)
{
	struct sockaddr_in sa;
	int l,s;
	struct context *cc;
	int val=1;
	struct incoming_entry *ie;
	char buf[80];

	l=sizeof(sa);

	if ((s=accept(lsock,(struct sockaddr *)&sa,&l))<0) {
		perror("accept");
		return;
	}

	setsockopt(s,SOL_SOCKET,TCP_NODELAY,&val,4);
	val=0;
	setsockopt(s,SOL_SOCKET,SO_LINGER,&val,4);

	setsocktimeouts(s);
	
	
	if (ioctl(s,FIONBIO,&val)) {
		perror("ioctl");
	} 

	if ((ie=IncomingCheck(sa.sin_addr.s_addr))==NULL) {
		sprintf(buf,"502 %s - %s\r\n",
			GetConfigString("ReportedHostname"),
			GetConfigString("DenyBanner"));
		write(s,buf,strlen(buf));
		close(s);
		aio_read((struct aiocb *)cb);
		return;
	}
/*
	if (ie->connected+1>ie->maxConnections) {
		sprintf(buf,"400 %s - Only allowed %i concurrent connections\r\n",
				GetConfigString("ReportedHostName"),
				ie->maxConnections);
		write(s,buf,strlen(buf));
		close(s);
		aio_read((struct aiocb *)cb);
		return;
	}		

	if (curConnected+1>maxConnections) {
		sprintf(buf,"502 System connection limit at %i.\r\n",
				ie->maxConnections);
		write(s,buf,strlen(buf));
		close(s);
		aio_read((struct aiocb *)cb);
		return;
	}		
*/

	cc=(struct context *)malloc(sizeof(struct context));	

	bzero(cc,sizeof(struct context));

	TAILQ_INSERT_TAIL(&ie->activeConns,cc,clist);
	ie->connected++;

	cc->incoming=ie;
	
	cc->bufsz=8192;
	cc->obufsz=9248;
	
	cc->cb.cb.aio_fildes=s;
	cc->cb.cb.aio_buf=cc->bp=cc->buf;
	cc->cb.cb.aio_nbytes=cc->bufsz;
	cc->flags|=PERM_CONNECT|PERM_FEED|PERM_MAINT;

/*	{ 
 	  char fname[64];
	  sprintf(fname,"/tmp/%u.%u",getpid(),s);
	  cc->cb.logfd=open(fname,O_CREAT|O_TRUNC|O_RDWR);
	} */

	sprintf(cc->obuf,"200 %s - %s - %i - ready\r\n",
		GetConfigString("ReportedHostName"),
		GetConfigString("AcceptBanner"),
		ie->connected);
	cc->obuflen=strlen(cc->obuf);
	cc->callback=NntpMain;
	WriteConn(cc);

	aio_read((struct aiocb *)cb);
		
	return;
}


int
AsyncConnect(unsigned int addr,unsigned int port)
{
	struct sockaddr_in sin,sinl;
	int val=1,error;
	int s;

	sin.sin_addr.s_addr=addr;
	sin.sin_port=htons((short)port);
	sin.sin_family=AF_INET;
	
	s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

	if (s<0)
		return -1;

	if (ioctl(s,FIONBIO,&val)) {
		perror("ioctl");
	} 

	bzero(&sinl,sizeof(struct sockaddr_in));
	
	if (bind(s,(struct sockaddr *)&sinl,sizeof(struct sockaddr_in)))
		return -1;
	
	setsockopt(s,SOL_SOCKET,TCP_NODELAY,&val,4);
	val=0;
	setsockopt(s,SOL_SOCKET,SO_LINGER,&val,4); 

	error=connect(s,(struct sockaddr *)&sin,sizeof(struct sockaddr_in));


	if (ioctl(s,FIONBIO,&val)) {
		perror("ioctl");
	} 

	if ((!error) || ((error) && (errno==EINPROGRESS))) {
		setsocktimeouts(s);
		return s;
	}

	perror("asyncconnect");
	close(s);

	return -1;
}



void
sig_term()
{

	Shutdown();
	
}

void
sig_io()
{

	printf("sigio\n");

}

void
sig_ignore()
{
}

void
sig_alarm()
{
  timerCallback=1;

}

static int b_mask;
static int ub_mask;

int
signal_setup()
{
  struct sigaction nact;

  nact.sa_handler=sig_io;
  sigemptyset(&nact.sa_mask);
  nact.sa_flags=0;

  sigaction(SIGIO,&nact,NULL);

  nact.sa_handler=sig_term;

  sigaction(SIGTERM,&nact,NULL);
  sigaction(SIGINT,&nact,NULL);
  sigaction(SIGQUIT,&nact,NULL);
//  sigaction(SIGSEGV,&nact,NULL);
//  sigaction(SIGBUS,&nact,NULL);

  nact.sa_handler=sig_alarm;

  sigaction(SIGALRM,&nact,NULL);

  ualarm(500000,5000000);

  nact.sa_handler=sig_ignore;

  sigaction(SIGPIPE,&nact,NULL);

  b_mask=0;
  
  ub_mask=sigmask(SIGUSR1)|sigmask(SIGUSR2)|
           sigmask(SIGINFO)|sigmask(SIGINT)|sigmask(SIGCONT);


  mypid=getpid();
}

int
BlockTimer()
{
  sigset_t mask;

  sigaddset(&mask,SIGALRM);

  sigprocmask(SIG_BLOCK,&mask,NULL);

}

int
UnblockTimer()
{

  sigset_t mask;

  sigaddset(&mask,SIGALRM);

  sigprocmask(SIG_UNBLOCK,&mask,NULL);

}
