/*
 * Copyright (c) 2004-2008 Darron M Broad
 * All rights reserved.
 *
 * Licensed under the terms of the GPL3 license, see file COPYING for details.
 */

#include "inspector.h"

/*
 * log Inspector class methods
 */

/*
 * retrieve the size of a file
 */
size_t
Inspector::getsize(char *filename)
{
	struct stat st;

	stat(filename, &st);
	return st.st_size;
}

/*
 * retrieve the new lines of a file
 */
vector<string>
Inspector::getnewlines(char *filename, size_t &offset)
{
	ifstream ifs(filename, ios::in | ios::binary);
	if( !ifs.is_open() )
		throw "Can't open file";

	ifs.seekg(offset);
	
	vector<string> lines;
	char clinebuf[STRLEN];

	do
	{
		ifs.getline(clinebuf,STRLEN);

		if(ifs.eof())
			break;

		offset += strlen(clinebuf) + 1;
		lines.push_back( clinebuf );
	}
	while( true );

	ifs.close();

	return lines;
}

/*
 * retrieve the contents of a config file
 */
vector<string>
Inspector::getconfigfile(char *filename)
{
	ifstream ifs(filename, ios::in | ios::binary);
	if( !ifs.is_open() )
		throw "Can't open config file";

	vector<string> lines;
	char clinebuf[STRLEN];

	do
	{
		ifs.getline(clinebuf,STRLEN);
		
		if(ifs.eof())
			break;

		if( clinebuf[0]!='#' )
			lines.push_back( clinebuf );
	}
	while( true );

	ifs.close();

	return lines;
}

/*
 * regex test string "test_string" against regex pattern "test_pattern"
 *
 * return first substring match else empty string on no match
 */
string
Inspector::regextest(string test_string, string test_regex)
{
	regex_t re;
	regmatch_t rm;

	/* compile expression from pattern */
	if( regcomp(&re, test_regex.c_str(), REG_EXTENDED | REG_ICASE ) != 0 )
		throw "Can't compile expression";

	/* test string against expression */
	int rc=regexec(&re, test_string.c_str(), 1, &rm, 0);
	
    regfree(&re);

    if( rc != 0 || rm.rm_eo <= rm.rm_so )
        return "";

	string match= test_string.substr(rm.rm_so, rm.rm_eo - rm.rm_so);

	return match;
}

/*
 * take an ip address in a string and reverse it
 *
 * Eg. 1.2.3.4 -> 4.3.2.1
 */
string
Inspector::reverseipaddr(string ipaddr)
{
	int quad1,quad2,quad3,quad4;

	int index= 0;
	quad1= atoi( ipaddr.c_str() );

	index= ipaddr.find_first_of('.', index)+1;
	quad2= atoi( ipaddr.substr(index).c_str() );

	index= ipaddr.find_first_of('.', index)+1;
	quad3= atoi( ipaddr.substr(index).c_str() );

	index= ipaddr.find_first_of('.', index)+1;
	quad4= atoi( ipaddr.substr(index).c_str() );

	char revipaddr[STRLEN];
	snprintf( revipaddr, STRLEN, "%d.%d.%d.%d", quad4, quad3, quad2, quad1);

	return revipaddr;
}

/*
 * the thread
 */
int
Inspector::thread(void)
{
	/* get args */
	char *log_file      = *++argv; argc--;
	char *rules_file    = *++argv; argc--;
	char *whitelist_file= *++argv; argc--;

	try
	{
		Conductor *conductor= new Conductor();
		conductor->start(argc, argv);

		vector<string> rules, whitelist, newlines;
		size_t fileoffset= getsize( log_file ), filesize;
		struct event event;
		string reject, ipaddr;
		int ipaddr_left, ipaddr_right, ipaddr_len;
        bool allow;
		
        rules    = getconfigfile(rules_file);
        whitelist= getconfigfile(whitelist_file);

		while( true )
		{
			filesize= getsize(log_file);

			if( filesize < fileoffset )         /* file truncated */
			{
				fileoffset= 0;
			}
			else if( filesize > fileoffset )    /* file grown */
			{
				newlines= getnewlines( log_file, fileoffset );

				for( vector<string>::const_iterator l=newlines.begin(); l!=newlines.end(); ++l)
				{
                    if(Util::stringToInt(*l) < 1970 || l->length() < 21)
                        continue;

					event.datetime= l->substr(0,19);
					reject        = l->substr(20);

					for( vector<string>::const_iterator r=rules.begin(); r!=rules.end(); ++r)
					{
                        event.match = regextest( reject, *r );
						if( event.match.length() )
						{
							ipaddr_left = reject.rfind('[', reject.length()) + 1;
							ipaddr_right= reject.rfind(']', reject.length());
                            if(ipaddr_left != -1 && ipaddr_right > ipaddr_left)
                            {
							    ipaddr_len= ipaddr_right - ipaddr_left;
                                ipaddr    = reject.substr( ipaddr_left, ipaddr_len );

                                allow = false;
					            for( vector<string>::const_iterator w=whitelist.begin(); w!=whitelist.end(); ++w)
                                {
                                    if( *w == ipaddr )
                                    {
                                        allow = true;
                                        break;
                                    }
                                }
                                if(!allow)
					            {
                                    event.ipaddr= reverseipaddr( ipaddr );
                                
                                    cout << event.datetime << " ";
                                    cout << resetiosflags(ios::right) << setiosflags(ios::left) << setw(18) << (string)("N=" + event.ipaddr);
                                    cout << resetiosflags(ios::right) << setiosflags(ios::left) << setw(18) << (string)("["  + ipaddr + "]");
                                    cout << "C=" << event.match << endl;

							        conductor->newevent( event );
                                }
                            }
						}
					}
				}
			}
			sleep( 1 );
		}
	}
	catch (const char *exception)
	{
		cout << "Inspector: exception: " << exception << endl;
		exit( EX_SOFTWARE );
	}
	return 0;
}
