#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>

#include "unp.h"

#define MAX_LOGIN_PAGE_LENGTH 10000l


/* Ripped off and slightly modified from the sample source code that
   accompanies Stevens' _Unix Network Programming Vol 1_.

   The included libunp.a library was compiled on a Fedora Core 1 system.

   Program does the following:
	Opens /var/smoothwall/mods/captive/loginpage.html and reads it

	Listens for connections on port 82.  The IP address is specified
	as the first command line argument.

	Regardless of what web page the client says it wants, always serves
	the html page referenced above.  Closes connection afterwards.

  This program forks a process for every client that connects.  This is
  certainly overkill, but what the hell.  It works.

*/

void
sig_chld(int signo)
{
	pid_t	pid;
	int		stat;

	pid = wait(&stat);
	/*printf("child %d terminated\n", pid);*/
	return;
}


int
parse_request( char *readbuffer, long rblength ) {

	if( (rblength < 3) || (rblength > 10000) ) return 0;

	if( strncmp(readbuffer,"GET ",4) == 0 ) return 1;
	else return 0;

}


long
load_loginpage( char *buffer ) {

	int fd;
	long len;

	fd = open( "/var/smoothwall/mods/captive/loginpage.html", O_RDONLY );
	if( fd < 0 ) return -1;
	len = lseek( fd, 0, SEEK_END );
	if( len > MAX_LOGIN_PAGE_LENGTH ) {
		fprintf( stderr, "captive_httpd: loginpage.html is too long (%ld bytes; max is %ld bytes)\n", len, MAX_LOGIN_PAGE_LENGTH );
		return -1;
	}
	lseek( fd, 0, SEEK_SET );
	read( fd, buffer, len );
	close( fd );
	return len;

}


char *twodig( int n, char *buffer ) {
	if( n < 10 ) snprintf( buffer, 3, "0%d", n );
	else snprintf( buffer, 3, "%d", n );
	return buffer;
}


/* This tedious function turns the current time into a string
   in a format that will fit in a http header.
*/
void
get_current_time( char *str ) {
	time_t tp;
	struct tm *tmptr;
	char weekday[4], month[4];
	char b1[3], b2[3], b3[3];

	time( &tp );
        tmptr = gmtime( &tp );

	switch( tmptr->tm_wday ) {
		case 0: strcpy(weekday,"Sun"); break;
		case 1: strcpy(weekday,"Mon"); break;
		case 2: strcpy(weekday,"Tue"); break;
		case 3: strcpy(weekday,"Wed"); break;
		case 4: strcpy(weekday,"Thu"); break;
		case 5: strcpy(weekday,"Fri"); break;
		case 6: strcpy(weekday,"Sat");
	}; 
	switch( tmptr->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");
	};

	snprintf( str, 50, "%s, %d %s %d %s:%s:%s GMT", weekday, tmptr->tm_mday, month, tmptr->tm_year+1900, twodig(tmptr->tm_hour, b1), twodig(tmptr->tm_min, b2), twodig(tmptr->tm_sec, b3) );  


}





int
main(int argc, char **argv)
{
	int					listenfd, connfd;
	pid_t				childpid;
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;
	void				sig_chld(int);

	char timestr[50];
        char loginpage[MAX_LOGIN_PAGE_LENGTH];
	long loginpagelength;
	char httpheader[500];
	long httpheaderlength;
	char readbuffer[10000];
	long rblength;


	loginpagelength = load_loginpage( loginpage );
	if( (loginpagelength <= 0) || (loginpage[0] == '\0') ) {
		fprintf( stderr, "captive_httpd failed to start: unable to open loginpage.html\n" );
		exit(0);
	}

	get_current_time( timestr );
	snprintf( httpheader, 500, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: Apache\r\nConnection: close\r\nContent-Length: %ld\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n", timestr, loginpagelength );
	httpheaderlength = strlen(httpheader);

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;

	/* Listen on port 82 of whatever IP address was specified on the command line */
	servaddr.sin_addr.s_addr = htonl(inet_network(argv[1]));
	servaddr.sin_port        = htons(82);

	/* These first-letter-capitalized functions are just wrappers around the
	   functions of the same name.  Nothing to see here.  */

	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	Listen(listenfd, LISTENQ);

	Signal(SIGCHLD, sig_chld);

	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
			if (errno == EINTR)
				continue;		/* back to for() */
			else
				err_sys("accept error");
		}

		if ( (childpid = Fork()) == 0) {	/* child process */
			Close(listenfd);	/* close listening socket */

			rblength = read( connfd, readbuffer, 10000 );

			if( parse_request(readbuffer,rblength) == 1 ) {
				/* this is a HTTP request, so serve login page */
				write(connfd, httpheader, httpheaderlength );
				write(connfd, loginpage, loginpagelength );
			} else {
				/* this is NOT a HTTP request, so close the connection */
				write(connfd, "Protocol mismatch.  Disconnecting.\n", 35 );
			}

			exit(0);
		}
		Close(connfd);			/* parent closes connected socket */
	}

	free( loginpage );
	return 0;
}
