/*
 * This code is part of Screws project.
 * Copylefted by pancake@phreaker.net at 2003
 */

#include "Accept.h"
#include "Request.h"
#include "Modules.h"
#include "Signal.h"
#include "Engine.h"
#include "Logs.h"
#include "Env.h"

int tcpwin;

/* memmem */
#ifndef __linux__
char *
memmem(buf,len1,str,len2)
	char *buf;
	int len1;
	char *str;
	int len2;
{
	int i=0;
	int len=len2;
	/* TODO BINARY MEMMEM */
	return strstr(buf,str);

	while(i<len)
	{
		if ( memcmp(buf+i,str,len) == 0)
			return (char *)(buf+i);
		i++;
	}
	return (char *)0; // not found
}
#endif

/* Main Accept function, here we handle connections */
int
Accept(n)
	int n;
{
	int s   =0,
	    l   =Svr.Lis[n].s, 
	    sas =sizeof(struct sockaddr); /* sockaddrsize */
	int keep=0;
	bool keep_enabled=false;
	int err,i;
	int frk;
	char *b=0;
	struct sockaddr sa;
	unsigned int nheads=Svr.Lis[n].V[_HEADS];
	char **head=0; /* Headers */
#if USESSL
	SSL *ssl;
#endif
	int sock; // Generic Socket

	/* son is finished */
	if (waitpid(-1,0,WNOHANG)>0) 
			forks--;

	memset(&sa,0,sizeof(sa));

	if (forks>Svr.Lis[n].V[_MAXTHR]) 
	{
		screws_logs_error(n,ERROR_MAXTHR,"");
		//usleep(Svr.usleep);
		forks--;
		return 0;
	}

	s=accept(l,(struct sockaddr *)&sa,&sas);
	if (s==-1) {
		// logerr ACCEPT
		return (0);
	}
	err=0;
	setsockopt(s,SOL_SOCKET, SO_KEEPALIVE,&err,sizeof(err));

	forks++;
	//fcntl(s,F_SETFL,O_NONBLOCK); /* non-blocking for dead child ripping */

	if (waitpid(-1,0,WNOHANG|WUNTRACED)>0) 
	{
			forks--;
	}

	/* Fork for Feed */
	switch((frk=fork()))
	{
	  case -1: return ERROR_FORK;     /* err */
	  case  0: setsid(); break;       /* son */
	  default: close(s); return frk;  /* +ok */
	}

	/* SSL stuff */
#if USESSL
	if (Svr.Lis[n].usessl)
	{
		ssl = SSL_new(Svr.Lis[n].ctx);
		SSL_set_fd(ssl, s);
		SSL_accept(ssl);
		if (Svr.v>=V_SHOW)
		{
		  SslShowCertificates(ssl);
		  printf("[i] SSL: enabled\n");
		}
	} else {
		ssl=0;
		if (Svr.v>=V_SHOW)
		{
		  printf("[i] SSL: disabled\n");
		}
	}
#endif
	/* Do-While :: Keep-Alive */
	if (CONNECTION) { free(CONNECTION); CONNECTION=0; }
	do {
	  nheads=Svr.Lis[n].V[_HEADS]; // XXX twice declaration

	  /* Enables timeout */
	  if (!Svr.notimeout)
		catchTimeout(s,Svr.Lis[n].V[_TIMEOUT],ALARM_TIMEOUT);

	#if USESSL
	if (Svr.Lis[n].usessl)
		sock=(int)ssl;
	else
	#endif
		sock=s;

	  /* Get input */
	  if (b) free(b); 
	  b = readRequest(sock, &nheads);

	  if ((int)b>ERROR_NULL && (int)b<ERROR_LASTONE)
		{
			//screws_logs_error(n,ERROR_REQUEST,""); // XXX log remote IP
			return;
		}

	  /* keep-alive only for HTTP/1.1 */
	  if ( HTTP[2]!='1' ) keep=Svr.keep;
	  else keep=0;

	  /* Check vars */
	  if (nheads>NCARGS)
	  {
		printf("maxheaders overflows his value,"
			"resetted to NCARGS(%d)\n",NCARGS);
		nheads=Svr.Lis[n].V[_HEADS]=NCARGS;
	  }

	  /* Check readRequest */
	  if ( ((int)b>0) && ((int)b<ERROR_LASTONE) )
		return (int)b;

	  /* Disable Timeout */
	  if (!Svr.notimeout)
		catchTimeout(s,0,ALARM_NULL); 

#if DEBUG
	  if (METHOD[2]=='\1')
		fprintf(stderr,"SSL DETECTED\n");
#define SSLDETLEN 3
	/* SSL-HACK DETECTION */
	#if USESSL
	if (Svr.v>=V_FULL)
	{
	loop_t i;
	struct ssldetect 
	{	char *navigator;
		unsigned char id[4];
	} ssldetect[SSLDETLEN]=
	{ { "lynx (gnutls)" , { 22,  3,1,0} },
	  { "mozilla (?)"   , {128,103,1,3} },
	  { "links (libssl)", {128,124,1,3} } };
	
	for(i=0;i<SSLDETLEN;i++)
	   {
		if (!memcmp(ssldetect[i].id,METHOD,4))
			printf("A '%s' trying to connect to HTTP using HTTPS\n",
					ssldetect[i].navigator);
	   }
	}
	#endif
#endif

	if ( ((int)b<ERROR_LASTONE) && ((int)b>=0)) 
	{
		screws_logs_error(n,b,"");
		screws_socket_close(s);
		exit(1);
	}

	/* Work with heads Oo)) */
	if (!nheads) LIMIT=true;
	else         LIMIT=false;

	if (head)  // free all heads
	{
		while(head[i]) free(head[i++]);
		free(head);
	}

	head=(char **)malloc((nheads+1)*sizeof(char *));
	if (head==0) 
	{
		screws_logs_error(n,ERROR_MALLOC,"");
		exit(1);
	}

	/* Parse Headers */
	head[0]=strdup("screws-run");
	nheads=Svr.Lis[n].V[_HEADS]-nheads;
	#if USESSL
	if (Svr.Lis[n].usessl) // XXX TMP
		screws_heads_parse((int)ssl,b,head,nheads);
	else
	#endif
	screws_heads_parse(s,b,head,nheads);

	/* TCP WINDOW */
	sas=sizeof(tcpwin);
	getsockopt(s,SOL_SOCKET, SO_SNDBUF, &tcpwin,&sas);
	if (Svr.v>=V_SHOW)
	  printf("[i] TCPwindow : %d\n",tcpwin);
	setEnv(n,&sa,tcpwin);


	/* TODO Cache */

	screws_IO(sock,n,head);

	//	keep++; /* OneMoreKeep */
	if (!strcasecmp(CONNECTION,"keep-alive"))
		{
		if (keep_enabled) keep++;
		else keep_enabled=true;
		}
	keep=Svr.keep; // XXX IT ONLY WORKS WHEN CLIENT REPLIES Content-Length
	} while( keep<Svr.keep );


	/* Socket close ( aka c-U sock! ) */
	shutdown(s,SHUT_RDWR);
	screws_socket_close(s);

	/* Close pipes */ // Must be in screws_IO_close or something like that? XXX
	//close(f_in [0]); close(f_in [1]);
	//close(f_out[0]); close(f_out[1]);
	//close(f_err[0]); close(f_err[1]);

	/* End Of Children :: Sad End */
	if (frk<0) 
	{
		free(b);
		Free();
		#if USESSL
		SSL_free(ssl);
		#endif
		return ERROR_NULL;
	}

	exit(0);
	return 1;
}
