#include "captive.h"

#include <limits.h>



char *tedit( time_t *tp ) {

	static char str[100];
	strcpy( str, ctime(tp) );
	str[strlen(str)-1] = '\0';
	return str;
}

int parse_log_entry( char *buffer, char *ip, char *timestring, char *url, long *objectsize, char *cachestatus ) {
	char *ptr;
	char *endofbuffer;
	int i;

	endofbuffer = (char *)((long)buffer + strlen(buffer) );

	ptr = buffer;
	i = 0;
	while( (i < 30) && (ptr < endofbuffer) && (*ptr != ' ') ) {
		ip[i] = *ptr;
		ptr++;
		i++;
	}
	ip[i] = '\0';
	if( ptr == endofbuffer ) return -1;

	while( (ptr < endofbuffer) && (*ptr != '[') ) ptr++;
	if( ptr == endofbuffer ) return -2;
	ptr++;
	i = 0;
	while( (i < 30) && (ptr < endofbuffer) && (*ptr != ' ') ) {
		timestring[i] = *ptr;
		ptr++;
		i++;
	}
	timestring[i] = '\0';

	ptr = strstr( buffer, "http" );
	if( ptr == NULL ) return -3;
	i = 0; 
	while( (i < 500) && (ptr < endofbuffer) && (*ptr != ' ') ) {
		url[i] = *ptr;
		ptr++;
		i++;
	}
	url[i] = '\0';

	ptr++;
	while( (ptr < endofbuffer) && (*ptr != ' ') ) ptr++;
	if( ptr == endofbuffer ) return -4;
	ptr++;
	while( (ptr < endofbuffer) && (*ptr != ' ') ) ptr++;
	if( ptr == endofbuffer ) return -5;

	ptr++;
	*objectsize = strtol(ptr,NULL,10);

	ptr++;
	while( (ptr < endofbuffer) && (*ptr != ' ') ) ptr++;
	if( ptr == endofbuffer ) return -6;

	ptr++;
	strncpy( cachestatus, ptr, 50 );
	cachestatus[strlen(cachestatus)-1] = '\0';

	return 1;
}


time_t convert_timestring( char *timestring ) {
	time_t tp;
	char str[10];

	struct tm timestruct;

	/* Get day of month. */
	str[0] = timestring[0]; str[1] = timestring[1]; str[2] = '\0';
	timestruct.tm_mday = atoi(str);
	if( (timestruct.tm_mday < 1) || (timestruct.tm_mday > 31) ) return -1;

	/* Get month of year. */
	str[0] = timestring[3]; str[1] = timestring[4]; str[2] = timestring[5]; str[3] = '\0';
	if( strncmp(str,"Jan",3) == 0 ) timestruct.tm_mon = 0;
	else if( strncmp(str,"Feb",3) == 0 ) timestruct.tm_mon = 1;
	else if( strncmp(str,"Mar",3) == 0 ) timestruct.tm_mon = 2;
	else if( strncmp(str,"Apr",3) == 0 ) timestruct.tm_mon = 3;
	else if( strncmp(str,"May",3) == 0 ) timestruct.tm_mon = 4;
	else if( strncmp(str,"Jun",3) == 0 ) timestruct.tm_mon = 5;
	else if( strncmp(str,"Jul",3) == 0 ) timestruct.tm_mon = 6;
	else if( strncmp(str,"Aug",3) == 0 ) timestruct.tm_mon = 7;
	else if( strncmp(str,"Sep",3) == 0 ) timestruct.tm_mon = 8;
	else if( strncmp(str,"Oct",3) == 0 ) timestruct.tm_mon = 9;
	else if( strncmp(str,"Nov",3) == 0 ) timestruct.tm_mon = 10;
	else if( strncmp(str,"Dec",3) == 0 ) timestruct.tm_mon = 11;
	else return -2;
	if( (timestruct.tm_mon < 0) || (timestruct.tm_mon > 11) ) return -2;

	/* Get year. */
	str[0] = timestring[7]; str[1] = timestring[8]; str[2] = timestring[9]; str[3] = timestring[10]; str[4] = '\0';
	timestruct.tm_year = atoi(str) - 1900;
	if( (timestruct.tm_year < 105) || (timestruct.tm_mday > 130) ) return -3;

	/* Get hour. */
	str[0] = timestring[12]; str[1] = timestring[13]; str[2] = '\0';
	timestruct.tm_hour = atoi(str);
	if( (timestruct.tm_hour < 0) || (timestruct.tm_hour > 23) ) return -4;
	
	/* Get minute. */
	str[0] = timestring[15]; str[1] = timestring[16]; str[2] = '\0';
	timestruct.tm_min = atoi(str);
	if( (timestruct.tm_min < 0) || (timestruct.tm_min > 59) ) return -5;

	/* Get second */
	str[0] = timestring[18]; str[1] = timestring[19]; str[2] = '\0';
	timestruct.tm_sec = atoi(str);
	if( (timestruct.tm_sec < 0) || (timestruct.tm_sec > 61) ) return -6; /* Leap seconds! */

	tp = mktime( &timestruct );

	return tp;
}


int get_file_type( char *url, char *filetype) {
	int i;
	char *ptr, *ptr2;
	char *endofurl;
	int isadot;

	filetype[0] = '\0';
	ptr = (char *)((long)url + strlen(url));
	endofurl = ptr;
	ptr--;
	if( *ptr == '?' ) {
		filetype[0] = '?'; filetype[1] = '\0';
		return 1;
	}
	if( *ptr == '/' ) {
		filetype[0] = '/'; filetype[1] = '\0';
		return 1;
	}
	ptr2 = ptr;
	while( *ptr2 != '/' ) {
		if( *ptr2 == '=' ) {
			filetype[0] = '='; filetype[1] = '\0';
			return 1;
		}
		ptr2--;
	}
	ptr2 = ptr;
	isadot = 0;
	while( *ptr2 != '/' ) {
		if( *ptr2 == '.' ) {
			isadot = 1;
			break;
		}
		ptr2--;
	}
	if( !isadot ) {
		filetype[0] = '/'; filetype[1] = '\0';
		return 1;
	}


	while( (ptr > url) && isalnum(*ptr) ) ptr--;
	ptr++;
	i = 0;
	while( (ptr < endofurl) && (*ptr != '\0') && (i < 19) ) {
		filetype[i] = *ptr;
		ptr++;
		i++;
	}
	if( i > 19 ) return -2;
	filetype[i] = '\0';

	return 1;
}


int get_domain( char *url, char *domain ) {
	int i;
	char *ptr;
	char *endofurl;
	char temp[50];
	int num_dots = 0;
	int cnt = 0;

	endofurl = (char *)((long)url + strlen(url));
	ptr = url + 7; /*    7 = strlen("http://")     */
	i = 0;
	while( (ptr < endofurl) && (*ptr != '/') && (i < 49) ) {
		domain[i] = *ptr;
		ptr++;
		i++;
	}
	if( ptr == endofurl ) return -1;
	if( i > 49 ) return -2;
	domain[i] = '\0';

	/* Convert to tld, if necessary. */
	/* If it's a two-letter tld, like .au or .tw, then leave ???.com.au instead of just .com.au */
	ptr = domain + strlen(domain);
	ptr--;

	if( isdigit(*ptr) ) return 1;

	while( ptr > domain ) {
		if( *ptr == '.' ) {
			num_dots++;
			if( cnt == 2 ) num_dots--;  /* exception for two-letter TLDs */
		}
		if( num_dots == 2 ) break;
		cnt++;
		ptr--;
	}
	if( ptr == domain ) return 1;
	ptr++;

	strcpy( temp, ptr );
	strcpy( domain, temp );

	return 1;
}



void get_window_data( char *username, time_t login_window[500][2], char login_ip[500][IP_LEN] ) {

	FILE *f;
	char buffer[1000];
	int data_event_code;
	long data_time;
	char data_username[USERNAME_LEN], data_ip[IP_LEN], data_mac[MAC_LEN];
	int i;
	int looking_for_login = 1;


	f = fopen( "/var/smoothwall/mods/captive/log.txt", "r" );

	for( i = 0; i < 500; i++ ) {
		login_window[i][0] = 0;
		login_window[i][1] = LONG_MAX;
		login_ip[i][0] = '\0';
	}
		
	i = 0;
	fgets( buffer, 1000, f );
	while( !feof(f) ) {

		sscanf( buffer, "%d\t%ld\t%s\t%s\t%s\n", &data_event_code, &data_time, data_username, data_ip, data_mac );

		if( (strcmp(data_username,username) == 0) || (data_event_code == EVENT_SYSTEM_FLUSH) ) {
			if( looking_for_login ) {
				if( data_event_code == EVENT_LOGIN_SUCCESS ) {
					login_window[i][0] = (time_t)data_time;
					strncpy( login_ip[i], data_ip, IP_LEN );
					looking_for_login = 0;
				}
			} else { /* looking for logoff */
				if( (data_event_code == EVENT_LOGOFF_TIMED) ||
				    (data_event_code == EVENT_LOGOFF_FORCED) ||
				    (data_event_code == EVENT_LOGOFF_ELECTIVE) ||
				    (data_event_code == EVENT_SYSTEM_FLUSH) ) {
					login_window[i][1] = (time_t)data_time;
					looking_for_login = 1;
					i++;
					if( i == 500 ) break;
				}
			}
		}
		fgets( buffer, 1000, f );
	}

	fclose( f );

}


void add_to_domain_list( char domain_str[1000][50], long domain_cnt[1000], char *domain ) {

	int i;

	/* Look for a match in the list already, or put this domain in the first empty spot. */

	for( i = 0; i < 1000; i++ ) {
		if( domain_cnt[i] == 0 ) {
			strncpy( domain_str[i], domain, 50 );
			domain_cnt[i]++;
			break;
		}
		if( strncmp(domain_str[i],domain,50) == 0 ) {
			domain_cnt[i]++;
			break;
		}
	}

}


void sort_domain_list( char domain_str[1000][50], long domain_cnt[1000] ) {
	int i;
	int last;
	int done;
	int tmpint;
	char tmpstr[50];

	for( i = 0; i < 1000; i++ ) if( domain_cnt[i] == 0 ) { last = i; break; }

	done = 0;
	while( !done ) {
		done = 1;
		for( i = 0; i < last-1; i++ ) {
			if( domain_cnt[i] < domain_cnt[i+1] ) {
				done = 0;
				tmpint = domain_cnt[i];
				domain_cnt[i] = domain_cnt[i+1];
				domain_cnt[i+1] = tmpint;
				strncpy( tmpstr, domain_str[i], 50 );
				strncpy( domain_str[i], domain_str[i+1], 50 );
				strncpy( domain_str[i+1], tmpstr, 50 );
			}
		}

	}

}



void printstatline( FILE *f, long d1, double divisor, char *filetype, long d2, double divisor2, char *domain ) {

	fprintf( f, "  <tr><td align=\"right\"><font face=\"arial, sans-serif\" size=\"-1\"><li><br></font></td><td width=\"10\"></td><td align=\"right\"><font face=\"arial, sans-serif\" size=\"-1\">%ld<br></font></td><td width=\"10\"></td><td align=\"right\"><font face=\"arial, sans-serif\" size=\"-1\">%.2f%%<br></font></td><td width=\"10\"></td><td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">%s<br></font></td><td width=\"20\"></td>\n", d1, 100.0*(double)d1/divisor, filetype );
	if( d2 > 0 ) {
		fprintf( f, "<td align=\"right\"><font face=\"arial, sans-serif\" size=\"-1\">%ld<br></font></td><td width=\"10\"></td><td align=\"right\"><font face=\"arial, sans-serif\" size=\"-1\">%.2f%%<br></font></td><td width=\"10\"></td><td align=\"left\"><font face=\"arial, sans-serif\" size=\"-1\">%s<br></font></td></tr>\n", d2, 100.0*(double)d2/divisor2, domain );
	} else {
		fprintf( f, "<td colspan=\"4\"></td></tr>\n" );
	}
}




	



void generate_summary( char *username, char *filename ) {

	long count = 0;
	FILE *f;
	FILE *f2;
	char fn[100];
	char buffer[1000];
	int i;

	char ip[50], timestring[50], url[500], cachestatus[50];
	char domain[50];
	char domain_str[1000][50];
	long domain_cnt[1000];
	long total_domain_count = 0;
	long objectsize;
	time_t time_log_entry;
	long total_bytes = 0;
	long total_files = 0;
	char filetype[20];
	double divisor, divisor2;

	long type_image = 0;
	long bytes_image = 0;
	long type_doc = 0;
	long bytes_doc = 0;
	long type_html = 0;
	long bytes_html = 0;
	long type_cgi = 0;
	long bytes_cgi = 0;
	long type_zip = 0;
	long bytes_zip = 0;
	long type_exe = 0;
	long bytes_exe = 0;
	long type_mm = 0;
	long bytes_mm = 0;
	long type_other = 0;
	long bytes_other = 0;


	long cache_hit = 0, cache_miss = 0;

	int retval;




	for( i = 0; i < 1000; i++ ) {
		domain_str[i][0] = '\0';
		domain_cnt[i] = 0;
	}

	f = fopen( filename, "r" );
	if( !f ) { printf( "Unable to open %s\n", filename ); exit(0); }

	fgets( buffer, 1000, f );

	while( !feof(f) ) {

		/* Turn data in this log entry into useful pieces ... */
		retval = parse_log_entry( buffer, ip, timestring, url, &objectsize, cachestatus );
		time_log_entry = convert_timestring( timestring );

		get_domain( url, domain );

		get_file_type( url, filetype );


		if( strncasecmp(filetype,"htm",3) == 0 ) { type_html++; bytes_html+=objectsize; }
		else if( strncasecmp(filetype,"xml",3) == 0 ) { type_html++; bytes_html+=objectsize; }
		else if( strncasecmp(filetype,"xht",3) == 0 ) { type_html++; bytes_html+=objectsize; }
		else if( strncasecmp(filetype,"sht",3) == 0 ) { type_html++; bytes_html+=objectsize; }
		else if( strncasecmp(filetype,"stm",3) == 0 ) { type_html++; bytes_html+=objectsize; }
		else if( strncasecmp(filetype,"css",3) == 0 ) { type_html++; bytes_html+=objectsize; }
		else if( filetype[0] == '/' ) { type_html++; bytes_html+=objectsize; }

		else if( strncasecmp(domain,"yimg.com",8) == 0 ) { type_image++; bytes_image+=objectsize; }
		else if( strncasecmp(filetype,"jpg",3) == 0 ) { type_image++; bytes_image+=objectsize; }
		else if( strncasecmp(filetype,"jpe",3) == 0 ) { type_image++; bytes_image+=objectsize; }
		else if( strncasecmp(filetype,"gif",3) == 0 ) { type_image++; bytes_image+=objectsize; }
		else if( strncasecmp(filetype,"png",3) == 0 ) { type_image++; bytes_image+=objectsize; }
		else if( strncasecmp(filetype,"bmp",3) == 0 ) { type_image++; bytes_image+=objectsize; }
		else if( strncasecmp(filetype,"tif",3) == 0 ) { type_image++; bytes_image+=objectsize; }
		else if( strncasecmp(filetype,"ico",3) == 0 ) { type_image++; bytes_image+=objectsize; }

		else if( strncasecmp(filetype,"zip",3) == 0 ) { type_zip++; bytes_zip+=objectsize; }
		else if( strncasecmp(filetype,"tar",3) == 0 ) { type_zip++; bytes_zip+=objectsize; }
		else if( strncasecmp(filetype,"tgz",3) == 0 ) { type_zip++; bytes_zip+=objectsize; }
		else if( strncasecmp(filetype,"bz2",3) == 0 ) { type_zip++; bytes_zip+=objectsize; }
		else if( strncasecmp(filetype,"gz",2) == 0 ) { type_zip++; bytes_zip+=objectsize; }
		else if( strncasecmp(filetype,"iso",3) == 0 ) { type_zip++; bytes_zip+=objectsize; }
		else if( strncasecmp(filetype,"cab",3) == 0 ) { type_zip++; bytes_zip+=objectsize; }

		else if( strncasecmp(filetype,"exe",3) == 0 ) { type_exe++; bytes_exe+=objectsize; }
		else if( strncasecmp(filetype,"jar",3) == 0 ) { type_exe++; bytes_exe+=objectsize; }
		else if( strncasecmp(filetype,"bin",3) == 0 ) { type_exe++; bytes_exe+=objectsize; }

		else if( strncasecmp(filetype,"cgi",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"php",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"psf",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"asp",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"js",2) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"xpi",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"pl",2) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"dll",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"cfm",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"ctf",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"mspx",4) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"src",3) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"class",5) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( strncasecmp(filetype,"crumb",5) == 0 ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( filetype[0] == '?' ) { type_cgi++; bytes_cgi+=objectsize; }
		else if( filetype[0] == '=' ) { type_cgi++; bytes_cgi+=objectsize; }

		else if( strncasecmp(filetype,"avi",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"mpg",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"mpe",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"wmv",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"wma",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"mov",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"mp2",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"mp3",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"mpa",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"wav",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"aif",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"asf",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"asx",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"au",2) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"cda",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"div",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"ram",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"rm",2) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"mid",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"snd",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }
		else if( strncasecmp(filetype,"swf",3) == 0 ) { type_mm++; bytes_mm+=objectsize; }

		else if( strncasecmp(filetype,"pdf",3) == 0 ) { type_doc++; bytes_doc+=objectsize; }
		else if( strncasecmp(filetype,"doc",3) == 0 ) { type_doc++; bytes_doc+=objectsize; }
		else if( strncasecmp(filetype,"xls",3) == 0 ) { type_doc++; bytes_doc+=objectsize; }
		else if( strncasecmp(filetype,"txt",3) == 0 ) { type_doc++; bytes_doc+=objectsize; }
		else if( strncasecmp(filetype,"rtf",3) == 0 ) { type_doc++; bytes_doc+=objectsize; }
		else if( strncasecmp(filetype,"ppt",3) == 0 ) { type_doc++; bytes_doc+=objectsize; }

		else { type_other++; bytes_other+=objectsize;
			f2 = fopen("/tmp/log.txt", "a" ); fprintf( f2, "%s - %s\n", url, filetype ); fclose( f2 );
		}

		if( strstr(cachestatus,"HIT") == NULL ) cache_miss++;
		else cache_hit++;

		add_to_domain_list( domain_str, domain_cnt, domain );
		total_domain_count++;

		total_bytes += objectsize;
		total_files ++;

		count++;
		fgets( buffer, 1000, f );
/*
		if( (count % 100) == 0 ) fprintf( stderr, "." );
		if( (count % 10000) == 0 ) fprintf( stderr, "\n" );
*/
	}

	fclose( f );

	sort_domain_list( domain_str, domain_cnt );

	if( total_files == 0 ) divisor = 1.0;
	else divisor = (double)total_files;
	if( total_domain_count == 0 ) divisor2 = 1.0;
	else divisor2 = (double)total_domain_count;

	snprintf( fn, 100, "/var/smoothwall/mods/captive/squid/%s-stats", username );
	f = fopen( fn, "w" );
	if( !f ) { printf( "Unable to open %s\n", fn ); exit(0); }

	fprintf( f, "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n" );

	i = 0;
	printstatline( f, type_html, divisor, "static web pages", domain_cnt[i], divisor2, domain_str[i] ); i++;
	printstatline( f, type_cgi, divisor, "dynamic web pages", domain_cnt[i], divisor2, domain_str[i] ); i++;
	printstatline( f, type_image, divisor, "images", domain_cnt[i], divisor2, domain_str[i] ); i++;
	printstatline( f, type_mm, divisor, "multimedia", domain_cnt[i], divisor2, domain_str[i] ); i++;
	printstatline( f, type_zip, divisor, "compressed", domain_cnt[i], divisor2, domain_str[i] ); i++;
	printstatline( f, type_exe, divisor, "executable", domain_cnt[i], divisor2, domain_str[i] ); i++;
	printstatline( f, type_doc, divisor, "documents", domain_cnt[i], divisor2, domain_str[i] ); i++;
	printstatline( f, type_other, divisor, "other", domain_cnt[i], divisor2, domain_str[i] ); i++;

	fprintf( f, "</table>\n" );

/*
 (%.2f) files were images (%.2f%% of bytes transferred)\n", type_image, total_files, 100.0*(double)type_image/(double)total_files, 100.0*(double)bytes_image/(double)total_bytes );
	printf( "\t%ld of %ld (%.2f) files were static web pages (%.2f%% of bytes transferred)\n", type_html, total_files, 100.0*(double)type_html/(double)total_files, 100.0*(double)bytes_html/(double)total_bytes );
	printf( "\t%ld of %ld (%.2f) files were dynamic web pages (%.2f%% of bytes transferred)\n", type_cgi, total_files, 100.0*(double)type_cgi/(double)total_files, 100.0*(double)bytes_cgi/(double)total_bytes );
	printf( "\t%ld of %ld (%.2f) files were documents (%.2f%% of bytes transferred)\n", type_doc, total_files, 100.0*(double)type_doc/(double)total_files, 100.0*(double)bytes_doc/(double)total_bytes );
	printf( "\t%ld of %ld (%.2f) files were compressed archives (%.2f%% of bytes transferred)\n", type_zip, total_files, 100.0*(double)type_zip/(double)total_files, 100.0*(double)bytes_zip/(double)total_bytes );
	printf( "\t%ld of %ld (%.2f) files were executables (%.2f%% of bytes transferred)\n", type_exe, total_files, 100.0*(double)type_exe/(double)total_files, 100.0*(double)bytes_exe/(double)total_bytes );
	printf( "\t%ld of %ld (%.2f) files were multimedia (%.2f%% of bytes transferred)\n", type_mm, total_files, 100.0*(double)type_mm/(double)total_files, 100.0*(double)bytes_mm/(double)total_bytes );
	printf( "\t%ld of %ld (%.2f) files were other (%.2f%% of bytes transferred)\n", type_other, total_files, 100.0*(double)type_other/(double)total_files, 100.0*(double)bytes_other/(double)total_bytes );


	printf( "\n\tDomain List\n\n" );
	for( i = 0; i < 1000; i++ )
		if( domain_cnt[i] == 0 ) break;
		else printf( "\t%d - %s\n", domain_cnt[i], domain_str[i] );
*/




}















typedef struct session_list_element {
	char username[USERNAME_LEN];
	time_t login, logoff;
	char ip[IP_LEN];
	struct session_list_element *next;
	struct user_list_element *user;
} session_list_element;


typedef struct user_list_element {
	char username[USERNAME_LEN];

	char log_filename[200];
        char domain_str[1000][50];
        int domain_cnt[1000];
        long total_bytes, total_files;

	int num_sessions;
	struct user_list_element *next;
	struct session_list_element *session;
} user_list_element;



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

	struct user_list_element *ul_head;
	struct user_list_element *ul_curr;
	struct session_list_element *sl_head;
	struct session_list_element *sl_curr;
	struct session_list_element *sl_last;

	char username[USERNAME_LEN];
        char ip[50], timestring[50], url[500], cachestatus[50];
        long objectsize;
        time_t time_log_entry;
	time_t tp;
	int retval;

	FILE *f, *f2;
        char buffer[1000];
        char buffer2[1000];

        int logdata_event_code;
        time_t logdata_time;
        char logdata_username[USERNAME_LEN], logdata_ip[IP_LEN], logdata_mac[MAC_LEN];

        int i;
	int done;
	int found;
	int num_users;

	ul_head = NULL;
	num_users = 0;


        f = fopen( "/var/smoothwall/mods/captive/log.txt", "r" );

        i = 0;
        fgets( buffer, 1000, f );
        while( !feof(f) ) {

		done = 0;
		found = 0;
                sscanf( buffer, "%d\t%ld\t%s\t%s\t%s\n", &logdata_event_code, &logdata_time, logdata_username, logdata_ip, logdata_mac );

/*
printf("---> %s", buffer );
*/

		switch( logdata_event_code ) {
			case EVENT_LOGIN_SUCCESS:
				if( ul_head == NULL ) { /* first entry */
					ul_head = malloc( sizeof(user_list_element) );
					ul_curr = ul_head;
					ul_curr->next = NULL;
					ul_curr->num_sessions = 1;
					strncpy( ul_curr->username, logdata_username, USERNAME_LEN );

					/* create first session list node */
					ul_curr->session = malloc( sizeof(session_list_element) );
					sl_curr = ul_curr->session;
					sl_curr->next = NULL;
					sl_curr->user = ul_curr;
					strncpy( sl_curr->ip, logdata_ip, IP_LEN );
					sl_curr->login = logdata_time;
					sl_curr->logoff = LONG_MAX;
					strncpy( sl_curr->username, ul_curr->username, USERNAME_LEN );
					found = 1;
					done = 1;
				} else {
					/* Go through our linked list to see if this username already has an entry. */
					for( ul_curr = ul_head; ul_curr != NULL; ul_curr = ul_curr->next )
						if( strncmp(ul_curr->username,logdata_username,USERNAME_LEN) == 0 ) {
							found = 1;
							break;
						}
				}

				if( !found ) {
					/* Then ul_curr is now NULL, off the end of the list. Go through the userlist again,
					   stopping at the end */
					for( ul_curr = ul_head; ul_curr->next != NULL; ul_curr = ul_curr->next )
						/* do nothing, we're just moving down the list */ ;
					ul_curr->next = malloc( sizeof(user_list_element) );
					ul_curr = ul_curr->next;
					strncpy( ul_curr->username, logdata_username, USERNAME_LEN );
					ul_curr->num_sessions = 1;
					ul_curr->next = NULL;
					ul_curr->session = malloc( sizeof(session_list_element) );
					sl_curr = ul_curr->session;
					sl_curr->login = logdata_time;
					strncpy( sl_curr->username, ul_curr->username, USERNAME_LEN );
					sl_curr->logoff = LONG_MAX;
					strncpy( sl_curr->ip, logdata_ip, IP_LEN );
					sl_curr->next = NULL;
					sl_curr->user = ul_curr;
				} else if( !done ) {
					/* Then ul_curr is pointing to the user now. Go down that user's session list
					   until we get to the end.  */
					for( sl_curr = ul_curr->session; sl_curr->next != NULL; sl_curr = sl_curr->next )
						/* do nothing, we're just moving the pointer */;
					/* sl_curr is now pointing at the last item in this user's session list */
					if( sl_curr->logoff != LONG_MAX ) {
						/* this is a new session */
						ul_curr->num_sessions++;
						sl_curr->next = malloc( sizeof(session_list_element) );
						sl_curr = sl_curr->next;
						sl_curr->logoff = LONG_MAX;
						sl_curr->login = logdata_time;
						strncpy( sl_curr->username, ul_curr->username, USERNAME_LEN );
						strncpy( sl_curr->ip, logdata_ip, IP_LEN );
						sl_curr->next = NULL;
						sl_curr->user = ul_curr;
					} else {
						/* do nothing; he re-logged in at the same IP/MAC before the last session expired */
					}
				}

				break;

			case EVENT_LOGOFF_TIMED:
			case EVENT_LOGOFF_FORCED:
			case EVENT_LOGOFF_ELECTIVE:
				/* Go through our linked list to find this username's an entry. */
				found = 0;
				for( ul_curr = ul_head; ul_curr != NULL; ul_curr = ul_curr->next ) {
					if( strncmp(ul_curr->username,logdata_username,USERNAME_LEN) == 0 ) {
						found = 1;
						break;
					}
				}
				if( found ) {
					/* move to the end of the session list for this user */
					for( sl_curr = ul_curr->session; sl_curr->next != NULL; sl_curr = sl_curr->next )
						;
					sl_curr->logoff = logdata_time;

				}

				break;

			case EVENT_SYSTEM_FLUSH:
				/* Go through the list of users and log off every one that is currently marked as logged in.  */
				for( ul_curr = ul_head; ul_curr != NULL; ul_curr = ul_curr->next )
					for( sl_curr = ul_curr->session; sl_curr->next != NULL; sl_curr = sl_curr->next )
						/* do nothing, we're just moving to the last entry in the session list */ ;

						if( sl_curr->logoff == LONG_MAX )
							sl_curr->logoff = logdata_time;


				break;

			default:
				; /* do nothing */
		};

                fgets( buffer, 1000, f );
        }

        fclose( f );



	/* connecting the list of sessions ... */
	if( ul_head == NULL ) {
		printf( "No users / sessions.\n" );
		exit( 0 );
	}

	sl_head = ul_head->session;
	for( ul_curr = ul_head; ul_curr != NULL; ul_curr = ul_curr->next ) {
		for( sl_curr = ul_curr->session; sl_curr->next != NULL; sl_curr = sl_curr->next )
			;
		if( ul_curr->next != NULL ) sl_curr->next = ul_curr->next->session;

	}

	/* sorting list by login time (increasing) ... should make the access.log parsing more efficient */
	done = 0;
	while( !done ) {
		done = 1;

		sl_curr = sl_head;
		for( sl_curr = sl_head; sl_curr->next != NULL; sl_curr = sl_curr->next ) {
			if( sl_curr->login > sl_curr->next->login ) {
				done = 0;
				if( sl_curr == sl_head ) {
					sl_head = sl_curr->next;
					sl_curr->next = sl_curr->next->next;
					sl_head->next = sl_curr;
				} else {
					sl_last->next = sl_curr->next;
					sl_curr->next = sl_curr->next->next;
					sl_last->next->next = sl_curr;
					sl_curr = sl_last->next;
				}
			}
			sl_last = sl_curr;
		}
	}


/*
	for( sl_curr = sl_head; sl_curr != NULL; sl_curr = sl_curr->next ) {
		printf( "\t%s\t\t%s ... %s [%ld]-> ", sl_curr->username, sl_curr->ip, tedit(&(sl_curr->login)), sl_curr->login );
		printf( "%s [%ld]\n", tedit(&(sl_curr->logoff)), sl_curr->logoff );
	}
*/



	/* initialize the userlist data structures */
	for( ul_curr = ul_head; ul_curr != NULL; ul_curr = ul_curr->next ) {
		snprintf( ul_curr->log_filename, 200, "/var/smoothwall/mods/captive/squid/%s", ul_curr->username );

		/* zero the log files ... */
		f2 = fopen( ul_curr->log_filename, "w" );
		if( !f2 ) { printf( "Unable to open %s\n", ul_curr->log_filename ); exit(0); }
		fclose( f2 );
	}




/*
	fprintf( stderr, "Processing access.log ... " );
*/

	f = fopen( "/var/log/squid/access.log", "r" );
	if( !f ) { printf( "Unable to open access.log\n" ); exit(0); }
	fgets( buffer, 1000, f );
	while( !feof(f) ) {

		/* Turn data in this log entry into useful pieces ... */
		retval = parse_log_entry( buffer, ip, timestring, url, &objectsize, cachestatus );
		time_log_entry = convert_timestring( timestring );


		strncpy( username, "unknown", USERNAME_LEN );
		/* Find the user responsible ... */
		for( sl_curr = sl_head; sl_curr != NULL; sl_curr = sl_curr->next ) {
			if( sl_curr->logoff >= time_log_entry ) {
				if( sl_curr->login <= time_log_entry ) {
					if( strncmp(sl_curr->ip,ip,IP_LEN) == 0 ) {
						f2 = fopen( sl_curr->user->log_filename, "a" );
						if( !f2 ) { printf( "Unable to open %s\n", sl_curr->user->log_filename ); exit(0); }
						fprintf( f2, "%s", buffer );
						fclose( f2 );
					}
				}
			}

		}

		fgets( buffer, 1000, f );

	}

	fclose( f );

/*
	fprintf( stderr, "done.\n" );
*/


	for( ul_curr = ul_head; ul_curr != NULL; ul_curr = ul_curr->next ) {
		generate_summary( ul_curr->username, ul_curr->log_filename );
	}

	for( sl_curr = sl_head; sl_curr != NULL; sl_curr = sl_curr->next )
		free( sl_curr );

	for( ul_curr = ul_head; ul_curr != NULL; ul_curr = ul_curr->next )
		free( ul_curr );

	time( &tp );

	strcpy( buffer, ctime(&tp) );
	buffer[strlen(buffer)-1] = '\0';
	for( i = 11; i < 16; i++ ) buffer2[i-11] = buffer[i];
	buffer2[5] = ' ';
	buffer2[6] = 'o';
	buffer2[7] = 'n';
	buffer2[8] = ' ';
	for( i = 0; i < 10; i++ ) buffer2[i+9] = buffer[i];
	buffer2[19] = ',';
	buffer2[20] = ' ';
	for( i = 20; i < 24; i++ ) buffer2[i+1] = buffer[i];
	buffer2[25] = '\0';

	f = fopen( "/var/smoothwall/mods/captive/squid/_time", "w" );
	if( !f ) { printf( "Could not open /var/smoothwall/mods/captive/squid/_time\n" ); exit(0); }
	fprintf( f, "%s", buffer2 );
	fclose( f );

	return 0;
}

