/*
 * This code is part of Screws project.
 * Copylefted by pancake@phreaker.net at 2003
 *
 * 20040503 - sepharad - Correct DATE string
 * 
 */

#include "Env.h"
#include "Modules.h"

char *CONTENT=0;
char  HTTP[4];
char *METHOD=0;
char *FILER=0;
char *ARGS=0;
char DATE[32];
char *HOST=0;
char *USERAGENT=0;
char *REFERER=0;
char *CONNECTION=0;
char *CLENGTH=0;
char HOSTNAME[32];
bool LIMIT=false; /* Arrive at server limits? */

#if __solaris__
int setenv(var,val,force)
        char *var,*val;
        int force;
{
        char *buf;
        buf=(char *)malloc(strlen(var)+strlen(val)+3);
        sprintf(buf,"%s=%s",var,val);
        putenv(buf);
        free(buf);
        return 0;
}
#endif

bool _code=false;
void setHTTPCode(char *b, int len)
{
	char httpret[32];
	if (_code) return; /* Is yet defined? */

	b[len]=0; /* fix len */
	sscanf(b,"%10s %10s\n",httpret,httpret);
	if (atoi(httpret))
	{
		httpret[3]=0;
		setenv("CODE",httpret,1);
		httpret[3]=' ';
	}
	else setenv("CODE","",1);
	_code=true;
}

void
getIOstring(char *IO)
{
	switch(Svr.io)
	{
	case IO_DIRECT:
		strcpy(IO,"direct"); break;
	case IO_POLL:
		strcpy(IO,"poll"); break;
	case IO_BUFFER:
		strcpy(IO,"buffer"); break;
	case IO_SELECT:
		strcpy(IO,"select"); break;
	}
}

/* Set Parent Level Environ */
void
setParentEnv(int n)
{
	struct utsname sysname;
	char sysname_str[256];
	char b[1024];

	/* System environs (UNAME) */
	uname(&sysname);
	sprintf(sysname_str,"%s %s/%s",
				sysname.sysname,
				sysname.release,
				sysname.machine);
	setenv("UNAME",sysname_str,1);
	setenv("SYSNAME",sysname.sysname,1);
	setenv("SYSVERSION",sysname.version,1);
	setenv("RELEASE",sysname.release,1);
	setenv("MACHINE",sysname.machine,1);

	/* Screws stuff */
	setenv("VERSION",VERSION,1);
	#if IPV6
	setenv("IPV6","1",1);
	#else
	setenv("IPV6","0",1);
	#endif
	#if POLL
	setenv("POLL","1",1);
	#else
	setenv("POLL","0",1);
	#endif
	#if LOGS
	setenv("LOGS","1",1);
	#else
	setenv("LOGS","0",1);
	#endif

	/* Thread stuff */
	sprintf(b,"%d",Svr.Lis[n].port);
	setenv("LISTEN_PORT",b,1);

	sprintf(b,"%s",Svr.Lis[n].ip);
	setenv("LISTEN_ADDR",b,1);

	sprintf(b,"%d",(Svr.Lis[n].family==AF_INET)?4:6);
	setenv("FAMILY",b,1);

	setenv("THREAD_ID",Svr.Lis[n].name,1);

	getIOstring(b);
	setenv("IO",b,1);
}

/* Set Environment Variables for this process */
void
setEnv(n,sa,win)
	int n;
	struct sockaddr *sa;
	int win;
{
	char *b,*chunk=0;
	int blen; // XXX BLEN IS UGLY
	register int argc=0, i=0;
	struct sockaddr_in *sa4=(struct sockaddr_in*)sa;
	struct sockaddr_in6 *sa6=(struct sockaddr_in6*)sa;

	/* clear variables */
	/* XXX not here! */
	if (FILER[0]=='\n') FILER[0]='/';

	/* Malloc and Blen */
	blen=strlen(FILER);
	if (blen<256) blen=256;
	b=(char *)malloc(blen+1);
	
	/* env directive */
	for(i=0;i<Svr.Lis[n].nenv;i++)
	{
		setenv( Svr.Lis[n].envkey[i],
			Svr.Lis[n].envval[i],1);
	}

	/* TCP window */
	snprintf(b,10,"%d",win);
	setenv("TCPWINSIZE",b,1);
	
	/* set args */
	if (!Svr.noargs)
	{
		char *var=0, *value=0, *v=0;
		char *argv=0;
		int len=strlen(ARGS);

		setenv("ARGS_ENABLED","1",1);
		argv=(char *)malloc(1); argv[0]=0; /* basic argv[] */
		setenv("ARGS",ARGS,1);     /* set Argument string */

		var=ARGS;
		for(i=0;i<=len;i++)
		{
		   switch( ARGS[i] )
		   {
			case '&':  /* ampersand */
			case '\0': /* end of string */
				ARGS[i]=0;
				if (v) { free(v); v=0; }
				if (!var||!strlen(var)) break;
				if (!value||!strlen(value)) break;
				v=malloc(strlen(var)+10);
				sprintf(v,"ARG_%s",var);
				setenv(v,value,1);
				if (Svr.v>3) fprintf(stderr,"[a] ARG: %s=%s\n",v,value);
				var=ARGS+i+1;
				argc++;
				argv=(char *)realloc(argv,strlen(argv)
					+strlen(v)+3); /* 3 ...good num ;) */
				strcat(argv," ");
				strcat(argv,v);
				break;
			case '=':
				ARGS[i]=0;
				if (ARGS[i+1]=='&'||
					ARGS[i+1]=='\0') /* null arg ?moo=& */
				{
					setenv(var,"",1);
					i++;
					var=ARGS+i+1;
				}
				value=ARGS+i+1;
				break;
		   }
		}
		/* Safer snprintf */
		while(snprintf(b,blen,"%d",argc)<0)
			b=(char *)realloc(b,blen+=256);
		setenv("ARGC",b,1);
		setenv("ARGV",argv,1);
		//if (v) free(v); free(argv);
		//free(ARGS);
	} else setenv("ARGS_ENABLED","0",1);

	/* Set process environ variables */
	if (CLENGTH)	setenv("CLENGTH",CLENGTH,1); /* Content Length */
	else 		setenv("CLENGTH","0",1);
	if (CONTENT) setenv("CONTENT",CONTENT,1);
	else 	 	setenv("CONTENT","",1);
	setenv("HTTP",HTTP,1);
	setenv("METHOD",METHOD,1);
	setenv("FILE",FILER,1);
	setenv("ORIG_FILE",FILER,1);
	for (i=strlen(FILER);i;i--)   /* Directory */
		if (FILER[i]=='/')
		{
			FILER[i+1]='\0';
			setenv("DIR",FILER,1);
			FILER[i+1]='/';
			break;
		}

	if (!HOST) 	
	{
		setenv("HOST","",1);
		setenv("LISTEN_HOST","",1); 
	} else {
		setenv("HOST",HOST,1);
		setenv("LISTEN_HOST",HOST,1); 
	}
	if (!USERAGENT) setenv("USERAGENT","",1);
	else setenv("USERAGENT",USERAGENT,1);
	if (!REFERER) setenv("REFERER","",1);
	else setenv("REFERER",REFERER,1);

	/* XXX hostname on each query? */
	gethostname(HOSTNAME,30);
	HOSTNAME[31]=0;
	setenv("HOSTNAME",HOSTNAME,1);
#if IPV6
	if (Svr.Lis[n].family==AF_INET6)
	{
		char addr6[INET6_ADDRSTRLEN],port[64];
		if (getnameinfo ( (struct sockaddr *)
				  &sa,     sizeof(sa),
				  addr6,   sizeof(addr6),
				  port,    sizeof(port),
				  NI_NUMERICHOST | NI_NUMERICSERV))
		{
			setenv("REMOTE_ADDR",addr6,1);
	  		setenv("REMOTE_HOST",addr6,1);
			setenv("REMOTE_PORT",port,1);
		} else {
			fprintf(stderr,"Error getting remote ipv6 addr.\n");
		}
	} else { /* IPV4 */
#endif
		while(snprintf(b,blen,"%s",inet_ntoa(sa4->sin_addr))<0)
			b=(char *)realloc(b,blen+=256);
		setenv("REMOTE_ADDR",b,1); // TODO INET_NTOA IN BUFFER NOT ALLOC!
		setenv("REMOTE_HOST",b,1);
		while(snprintf(b,blen,"%d",sa4->sin_port)<0)
			b=(char *)realloc(b,blen+=256);
		setenv("REMOTE_PORT",b,1);
#if IPV6
	}
#endif

	/* XXX - Use getaddrinfo ( IPv6 compliant ) */
	if (Svr.resolv)
	{
	  struct hostent *he;
	  //he=gethostbyaddr(sa,sizeof(sa),sa->sa_family);
	  switch(sa->sa_family)
	  {
	  case AF_INET:
		  he=gethostbyaddr((const char *)&sa4->sin_addr,
				  sizeof(sa4->sin_addr),AF_INET);
		  break;
	  case AF_INET6:
		  he=gethostbyaddr((const char *)&sa6->sin6_addr,
				  sizeof(sa6->sin6_addr),AF_INET6);
		  break;
	  }
	
	  if (he!=(struct hostent *)0)
	  {
	    setenv("REMOTE_HOST",he->h_name,1);
	  } // else cannot resolve hostname

	} /* else { (.. in REMOTE_ADDR code..) } */

	/* Date..useful for scripts :) */
	getHTTPDate(DATE);
	setenv("DATE",DATE,1);

	if (CONNECTION)
		setenv("CONNECTION",CONNECTION,1);
	else 	setenv("CONNECTION","",1);

	/* filetype */
	for(i=strlen(FILER);i;i--)
		if (FILER[i]=='.') break;
	if (i)  strncpy(b,FILER+i+1,blen);
	else    strncpy(b,"",blen);
	setenv("FILETYPE",b,1);

	/* limits? */
	if (LIMIT) setenv("HTTP_LIMIT","TRUE",1);
	else       setenv("HTTP_LIMIT","",1);

	#if DLOPEN
	if ( execModule(MOD_ENV) ) return;
	#endif

}

/* Free al known chunks */
void
Free()
{
	free(METHOD);
	free(FILER);
}

void getHTTPDate(char *DATE)
{
	struct tm curt; /* current time */
	time_t l;
	char week_day[4], month[4];

	l=time(0);
	localtime_r(&l,&curt);

	DATE[0]=0;
	switch(curt.tm_wday)
	{
		case 0: strcpy(week_day, "Sun");
			break;
		case 1: strcpy(week_day, "Mon");
			break;
		case 2:	strcpy(week_day, "Tue");
			break;
		case 3:	strcpy(week_day, "Wed");
			break;
		case 4:	strcpy(week_day, "Thu");
			break;
		case 5:	strcpy(week_day, "Fri");
			break;
		case 6:	strcpy(week_day, "Sat");
			break;
		default: return;
	}
	
	switch(curt.tm_mon)
	{
		case 0: strcpy(month, "Jan");
			break;
		case 1: strcpy(month, "Feb");
			break;
		case 2: strcpy(month, "Mar");
			break;
		case 3: strcpy(month, "Apr");
			break;
		case 4: strcpy(month, "May");
			break;
		case 5: strcpy(month, "Jun");
			break;
		case 6: strcpy(month, "Jul");
			break;
		case 7: strcpy(month, "Aug");
			break;
		case 8: strcpy(month, "Sep");
			break;
		case 9: strcpy(month, "Oct");
			break;
		case 10: strcpy(month, "Nov");
			break;
		case 11: strcpy(month, "Dec");
			break;
		default: return;
	}
	
	sprintf(DATE, "%s, %02d %s %d %02d:%02d:%02d GMT", 
			week_day, curt.tm_mday, month, 
			curt.tm_year + 1900, curt.tm_hour, 
			curt.tm_min, curt.tm_sec);
}
