			/* ------------------------------------- */
			//										 //
			//  +-+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+  //
			//  |S|i|m|p|l|e| |P|r|o|x|y| |2|0|0|4|  //
			//  +-+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+  //
			//										 //
			//										 //
			//  AUTOR : Aymeric  //
			//  DATE  : May 2004					 //
			//  FILE  : proxy.c						 //
			//  DESC  : main program				 //
			//  									 //
			/* ------------------------------------- */


/************************************************************************/
// a voir http://forums.devshed.com/archive/t-97618

#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "proxy.h"

volatile sig_atomic_t keep_going = 1; /* controls program termination */
int sock; /* main socket */


/************************************************************************/
// FunctionName : mySIG
//
// Description  : called when CtrlC is pressed in debug mode.
//				  Close main socket and stream.
//
// Parameters
// [in]
//			int signum : signal number
//
// [out]
//			nothing
//
// Returns
//			exit(1)
//
// Calls
//			nothing
//
// Globals
//			int sock	     : the main socket id.
//			FILE *access_log : file id for the access log
//			enum bool debug	 : if debug is true, we are in debug mode
//
/************************************************************************/

void mySIG(int signum)
{
	shutdown(sock,2);
	close(sock);
	if (debug) printf("[mySIGINT] End ... Close connection -> OK !\n");
	fclose(access_log);
	exit(1);
}


/************************************************************************/
// FunctionName : main
//
// Description  : main function of the program. This function is used
//				  to open sockets and listen of the main port.
//
// Parameters
// [in]
//			int argc   : represent the number of arguments when the
//					     program is call
//			char* argv : represent arguments
//
// [out]
//			nothing
//
// Returns
//			int : 1 if sucess
//				  0 if fail
//
// Calls
//			readConfig() : Pass config file name and read it.
//						   Return true if valid
//			mySIG()		 : Pass the sig number. Is called when CtrlC
//						   is pushed. Close streams. Return void.
//			logtime()	 : Return the current time for timestamps in log
//			execute()	 : Pass received msg, current socket, config params.
//						   Return true if valid
//			getPage()	 : Pass received msg, current socket, config params,
//						   and the url. Return true if valid.
//
// Globals
//			int sock	     : the main socket id.
//			FILE *log	 	 : file id for the general log
//			FILE *access_log : file id for the access log
//			enum bool debug	 : if debug is true, we are in debug mode
//			volatile keep_going : controls program termination
//
/************************************************************************/

int main(int argc, char * argv[])
{

	int csock;
	struct sockaddr_in sin;
	struct sockaddr_in csin;
	struct sConfig config;
	struct hostent *host_ent;

	char buf[BUFFERSIZE];

	int sinsize;
	int lg,pid;
	char c;

	enum bool configok=false;
	FILE *F;

	debug=false;

	if (geteuid() == 0)
	{
		printf("It's not safe to run this program with root privileges !\n");
		exit(0);
	}

	/* we check if there is parameters
	   only c, C, d and h are valids
	   c : we read the config file given
	   C : we clear the cache
	   d : debug mode (debug = true)
	   h : print the help message */
	if (argc > 0)
	{
		while ((c = getopt(argc, argv, "c:Cdh")) != EOF)
		{
		    switch (c)
		    {
				case 'c':
					configok=readConfig(optarg,&config);
					if (configok == false) exit(0);
					break;
				case 'C':
					#ifdef __CYGWIN__
						system("rm cache/*");
					#elif __WIN32__
						system("del /Q cache/*");
					#else
						system("rm cache/*");
					#endif
					printf("[warning] Cache has been cleared.\n");
					break;
				case 'd':
					printf("[debug] Debug mode ok (UNSTABLE VERSION)\n");
					debug=true;
					break;
				default:
					printf("Simple Proxy 2004 (c)\n");
					printf("Usage: \n");
					printf("%s [-c file] [-C] [-d] [-h]\n",argv[0]);
					printf("parameters: -c use the specified file as the config file (default: proxy.cfg)\n");
					printf("            -C clear the cache\n");
					printf("            -d debug mode\n");
					printf("            -h print this message\n");
					printf("\n");
					exit(0);
			}
		}
	}

	/* we read the defaulf config file */
	if (configok == false)
		if (readConfig("proxy.cfg",&config) == false) exit(0);

	/* we can't use the debug mode with the daemon mode */
	if ( (debug) && (config.daemon) )
	{
		printf("[debug] Debug mode and Daemon mode -> IMPOSSIBLE !\n");
		printf("[debug] Daemon mode -> OFF !\n");
		printf("[debug] Debug mode  -> ON  !\n");
		config.daemon=0;
	}

	/* if we are in daemon mode, we open the stream to general logs */
	if (config.daemon) log=fopen("proxy.log","a+");

	if (!debug && !config.daemon) log=stdout;

	if (debug) printf("[debug] Simple Proxy 2004 - (hostname = %s | port = %d)\n",config.host,config.port);
	else
	{
		fprintf(log,"[%s] Simple Proxy 2004 - (hostname = %s | port = %d)\n",logtime(),config.host,config.port);
		fflush(log);
	}

	/* we open the stream to the access.log file */
	access_log=fopen("access.log","a+");

	if ( config.cache )
	{
		/* we check if cache index file exists */
		F=fopen(INDEXFILE,"r");

		if ( F == NULL )
		{
			if (debug) printf("[debug] %s not Found. I create it.\n",INDEXFILE);
			else
			{
				fprintf(log,"[%s] %s not Found. I create it.\n",logtime(),INDEXFILE);
				fflush(log);
			}
			F=fopen(INDEXFILE,"w");
			if ( F == NULL )
			{
				if (debug) printf("[debug] Unable to create %s\n",INDEXFILE);
				else
				{
					fprintf(log,"[%s] Unable to create %s\n",logtime(),INDEXFILE);
					fflush(log);
				}
				perror("INDEXFILE");
				exit(-1);
			}
			fclose(F);
		}
	}

	/* we open the main socket */
	if ( (sin.sin_addr.s_addr = inet_addr(config.host)) == -1 )
	{
		host_ent = gethostbyname(config.host);
		if (host_ent == NULL)
		{
			if (debug) printf("[debug] Unresolve hostname %s\n",config.host);
			else
			{
				fprintf(log,"[%s] Unresolve hostname %s\n",logtime(),config.host);
				fflush(log);
			}
			perror(NULL);
			exit(0);
		}
		memcpy ((char *)&sin.sin_addr,host_ent->h_addr_list[0],host_ent->h_length);
	}

	sin.sin_family				= AF_INET;
	sin.sin_port				= htons(config.port);
	memset(&(sin.sin_zero), '\0', 8);

	sock = socket(AF_INET, SOCK_STREAM, 0);

	if (sock == -1)
    {
		if (debug) printf("[debug] Bad Socket (main function)\n");
		else
		{
			fprintf(log,"[%s] Bad Socket (main function)\n",logtime());
			fflush(log);
		}
		perror("SOCKET");
		exit(0);
    }

	if (debug) printf("[debug] Main Socket -> OK !\n");

	if ( bind(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1 )
	{
		if (debug) printf("[debug] Bad Bind (main function)\n");
		else
		{
			fprintf(log,"[%s] Bad Bind (main function)\n",logtime());
			fflush(log);
		}
		perror("BIND");
		close(sock);
		exit(0);
	}

	if ( listen(sock, BUFFERSIZE) == -1 )
	{
		if (debug) printf("[debug] Bad Listen (main function)\n");
		else
		{
			fprintf(log,"[%s] Bad Listen (main function)\n",logtime());
			fflush(log);
		}
		perror("LISTEN");
		close(sock);
		exit(0);
	}

	/* if it's the daemon mode, we create the daemon */
	if ( config.daemon )
	{
		fprintf(log,"[%s] Simple Proxy is now a daemon.\n",logtime());
		if (!config.cache) fprintf(log,"[%s] No cache.\n",logtime());
		fflush(log);
		fprintf(stdout,"Launched into the background (daemon mode)\n");
		fflush(stdout);

		/* Become a daemon: */
		switch (fork ())
		{
			case -1:                    /* can't fork */
				fprintf(log,"[%s] Can't fork.\n",logtime());
				fflush(log);
				close(sock);
				perror ("fork()");
				exit (3);
			case 0:                     /* child, process becomes a daemon: */
				close (STDIN_FILENO);
				close (STDOUT_FILENO);
				close (STDERR_FILENO);
				if (setsid () == -1)      /* request a new session (job control) */
				{
					exit (4);
				}
				break;
			default:                    /* parent returns to calling process: */
				return 0;
		}

		/* Establish signal handler to clean up before termination: */
		if (signal(SIGTERM, termination_handler) == SIG_IGN)
			signal(SIGTERM, SIG_IGN);
		signal(SIGINT, SIG_IGN);
		signal(SIGHUP, SIG_IGN);
		signal(SIGCHLD, SIG_IGN);
		fprintf(log,"[%s] New pid (%d)\n",logtime(),getpid());
		fflush(log);
	}
	else signal(SIGINT,mySIG);

	sinsize = sizeof(sin);

	csock = accept(sock, (struct sockaddr *)&csin, &sinsize); // csock unique pour cette connexion
	if ( csock == -1 )
	{
		if (debug) printf("[debug] Bad Accept (main function)\n");
		else
		{
			fprintf(log,"[%s] Bad Accept (main function)\n",logtime());
			fflush(log);
		}
		close(sock);
		perror("ACCEPT");
		exit(0);
	}

	if (debug) printf("[debug] KEEP GOING\n");

	/* Main program loop */
   	while (keep_going)
	{

		/* we create the child process */
		if ((pid = fork ()) < 0)
		{
			perror ("erreur fork () ");
			exit (0);
		}
		else if (pid  > 0)
		{
			/* FATHER PROCESS */
			if (debug) printf("\n[debug] Processus DAD (pid = %d)\n",pid);

			/* we can close csock because the socket has been pasted to the child */
			close (csock);
			if (debug) printf("[debug] Close socket -> OK !\n\n");

			/* Waiting for a new connection */
			csock = accept(sock, (struct sockaddr *)&csin, &sinsize);
			if ( csock == -1 )
			{
				if (debug) printf("[debug] Bad Accept (main function)\n");
				else
				{
					fprintf(log,"[%s] Bad Accept (main function)\n",logtime());
					fflush(log);
				}

				close(sock);
				perror("ACCEPT");
				exit(0);
			}
		}

		else if (pid == 0)
		{
			/* CHILD PROCESS */
			/* Used the socket pasted by the father */
			char *url,*hostname;
			int n=0;
			struct sLog alog;

			/* we close the main socket */
			close(sock);

			if (debug) printf("\n[debug] Processus SUN\n");

			url=(char *)calloc(sizeof(char),2550);

			/* reading the request */
			lg = read(csock,buf,BUFFERSIZE-1);

			if ( lg == -1 )
			{
				if (debug) printf("[debug] Bad Read (main function)\n");
				else
				{
					fprintf(log,"[%s] Bad Read (main function)\n",logtime());
					fflush(log);
				}
				close(csock);
				close(sock);
				perror("READ");
				exit(0);
			}

			buf[lg]='\0';

			if (strcmp(buf,"") == 0) break;

			hostname=(char *)inet_ntoa(csin.sin_addr);
			for (lg=0; lg < 255; lg++)
			{
				alog.hostname[lg]=*hostname++;
				if ( alog.hostname[lg] == '\0' ) break;
			}

			if (debug)
			{
				printf("[debug] New connection from %s\n",alog.hostname);
				if (!config.cache) printf("[debug] No cache.\n");
			}

			if ( execute(buf,csock,url,n,config,&alog) == false ) break;
			if ( getPage(buf,config,csock,url,alog) == false ) break;

			free(url);
			exit(0);
		}
	}
	/* we close the main socket and streams */
	shutdown(sock,2);
	close(sock);
	if (config.daemon) fclose(stdout);

	fclose(access_log);
	if (debug) printf("[debug] Close main socket -> OK !\n");
 	return (1);
}


/************************************************************************/
// FunctionName : init
//
// Description  : initialise the headerHTTP structure
//
// Parameters
// [in]
//			struct headerHTTP *Headers : the struct to initialise
//
// [out]
//			the param passed in parameter
//
// Returns
//			nothing
//
// Calls
//			nothing
//
// Globals
//			none
//
/************************************************************************/

void init(struct headerHTTP *Headers)
{
	Headers->HTTPversion = NULL;
	Headers->StatusCode = 0;
	Headers->URIproto = NULL;
	Headers->URIserv = NULL;
	Headers->URIpath = NULL;
	Headers->URIport = 0;
	Headers->Host = NULL;
	Headers->CacheControl = NULL;
	Headers->MaxAge = 0;
	Headers->Pragma = NULL;
	Headers->Date = 0;
	Headers->Expires = 0;
	Headers->ContentLength = 0;
	Headers->Age = 0;
}

void termination_handler (int signum)
{
  keep_going = 0;
  fprintf(log,"[%s] Process stopped.\n",logtime());
  fflush(log);
  if (signum == SIGCHLD)
     while( waitpid(0, NULL, WNOHANG) > 0 );
  shutdown(sock,2);
  close(sock);
  signal (signum, termination_handler);
}

/************************************************************************/
// FunctionName : logtime
//
// Description  : print a log format of time
//
// Parameters
// [in]
//			nothing
//
// [out]
//			nothing
//
// Returns
//			the current time in the right format
//
// Calls
//			nothing
//
// Globals
//			none
//
/************************************************************************/

char *logtime()
{
	time_t time_of_day;
	char *timeday;

	timeday=(char *)calloc(sizeof(char),150);

	/* time_of_Day = time from the 1st January 1970 */
	time_of_day = time( NULL );

	strftime( timeday, 150, "%d/%m/%y %H:%M:%S",localtime( &time_of_day ) );

	return(timeday);
}
