#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <dirent.h>
#include <time.h>

#include <errno.h>
#include <sys/stat.h>

#ifndef __WINDOWS__

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

#else

#include <winsock2.h>

#endif

#define IPT_VERSION     "1.3.5"
//#define DEBUG_CODE
//#define DEBUG_HOST      "192.168.0.3"

#define USE_SQLITE 1

#ifdef USE_SQLITE

#include "sqlite3.h"

#endif

#ifndef MIN
# define MIN(x,y) ((x)<(y)?(x):(y))
#endif

#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))

void printAppVersion()
{
    printf("ipt-parse v%s [%s %s] Copyright (C) 2009-2011 Mark Vejvoda\n",IPT_VERSION,__DATE__,__TIME__);
}


// Maximum days in date range to store data
enum { MAX_RANGE 		                                                = 91};
// Maximum # of clients to keep track of
enum { MAX_CLIENTCOUNT                                                  = 101};

static const int UNKNOWN_DATA	                                        = -1;
static const int INCOMING_DATA	                                        = 0;
static const int OUTGOING_DATA	                                        = 1;

static const int GENERATE_TYPE_TESTLOG_PARSE                            = -1;
static const int GENERATE_TYPE_LOGFILE_CONSOLE                          = 0;
static const int GENERATE_TYPE_LOGFILE_HTML                             = 1;
static const int GENERATE_TYPE_SQLITE_IMPORT                            = 2;
static const int GENERATE_TYPE_SQLITE_CONSOLE                           = 3;
static const int GENERATE_TYPE_SQLITE_HTML                              = 4;
static const int GENERATE_TYPE_SQLITE_DATACOMPRESS                      = 5;
static const int GENERATE_TYPE_SETUP_IPTABLE_USING_TOMATO_STATIC_DHCP   = 6;
static const int GENERATE_TYPE_SHRINK_DATABASE                          = 7;
static const int GENERATE_TYPE_SQLITE_IMPORT_DNS_USAGE                  = 8;
static const int GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE          = 9;
static const int GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_HTML             = 10;

static uint64_t start_date                                              = 0;
static uint64_t end_date                                                = 0;
static char chBytesMeasurementUnitType                                  = 'M';

// Because stupid C compiler cannot use const int for array definitions
enum { MAX_IPADDRESS_LENGTH                                             = 20};
enum { MAX_WEBSITE_LENGTH                                               = 100};

static const char *SUPPORT_WEBSITE                              = "http://soft-haus.com/wiki/index.php5?title=IPTables_Bandwidth_Monitor";
static const char *BANDWIDTH_USAGE_TABLENAME                    = "bandwidthusage";
static const char *TODAYS_DATE_KEYWORD                          = "today";
static const char *MONTHLY_PERIOD_STARTDATE_KEYWORD             = "bw_startday=";
static const char *DEFAULT_IPTABLE_TRAFFIC_NAME                 = "ipt-parse-traffic-all";

//static const char *APP_FLAG_PREFIX                              = "flags=";
static const char *APP_FLAG_MOREHOSTINFO                        = "flags=morehostinfo";
static const char *APP_FLAG_MOREHOSTINFO_APPEND                 = "flags=morehostinfo_append";
static const char *APP_FLAG_HOSTNAME_ONLY                       = "flags=hostname_only";
static const char *APP_FLAG_IMPORT_DAILY_ONLY                   = "flags=importdailyonly";
static const char *APP_FLAG_IMPORT_LOGFILEDATA                  = "flags=import=";
static const char *APP_FLAG_PURGE_OLD_RECORDS                   = "flags=purgeoldrecords=";

static const char *APP_FLAG_GENERATE_TYPE                       = "flags=commandtype=";
static const char *APP_FLAG_USE_HTML_TEMPLATE                   = "flags=usehtmltemplate=";
static const char *APP_FLAG_DB                                  = "flags=database=";
static const char *APP_FLAG_STARTDATE                           = "flags=startdate=";
static const char *APP_FLAG_ENDDATE                             = "flags=enddate=";
static const char *APP_FLAG_DNSLOGFILE                          = "flags=dnslogfile=";
static const char *APP_FLAG_SORTBY                              = "flags=sortby=";
static const char *APP_FLAG_FILTERBY                            = "flags=filterby=";
static const char *APP_FLAG_DNSIGNOREHOSTLIST                   = "flags=dnsignorehosts=";
static const char *APP_FLAG_DNSIGNOREWEBSITELIST                = "flags=dnsignorewebsites=";
static const char *APP_FLAG_DNSCOMBINEWEBSITELIST               = "flags=dnscombinewebsites=";

static const char *APP_FLAG_GET_STATS_CMD                       = "flags=iptables-get-stats-cmd=";
static const char *APP_FLAG_CLEAR_STATS_CMD                     = "flags=iptables-clear-stats-cmd=";
static const char *APP_FLAG_TRAFFIC_NAME                        = "flags=iptables-trafficrule-name=";
static const char *APP_FLAG_TEST_LOGFILE                        = "flags=test-logfile=";
static const char *APP_FLAG_LOGFILE_PATH                        = "flags=logfilepath=";
static const char *APP_FLAG_LOGFILE_INCOMING                    = "flags=logfile-incomingprefix=";
static const char *APP_FLAG_LOGFILE_OUTGOING                    = "flags=logfile-outgoingprefix=";

static const char *APP_FLAG_VERSION                             = "--version";
static const char *APP_FLAG_HELP                                = "--help";


// DNS query lookup tracking
//Nov 20 22:50:35 wrt daemon.debug dnsmasq[149]: query[A] test.co from 192.168.0.107
//Nov 20 22:50:36 wrt daemon.debug dnsmasq[149]: query[A] www.mytestcom.net from 192.168.0.107
//Nov 20 22:50:38 wrt daemon.debug dnsmasq[149]: query[A] by2.storage.msn.com from 192.168.0.126
//Nov 20 22:50:42 wrt daemon.debug dnsmasq[149]: query[A] view.atdmt.com from 192.168.0.126
//Nov 20 22:50:43 wrt daemon.debug dnsmasq[149]: query[A] ad.doubleclick.net from 192.168.0.126
static const char *DNS_USAGE_TABLENAME                          = "dnsusage";

int TAG_INDEX_IPT_PARSE_VERSION             = 0;
int TAG_INDEX_IPT_PARSE_FILTER_START        = 1;
int TAG_INDEX_IPT_PARSE_FILTER_END          = 2;
int TAG_INDEX_IPT_PARSE_EXECUTE_TIMESTAMP   = 3;
int TAG_INDEX_TOTAL_DL                      = 4;
int TAG_INDEX_TOTAL_UL                      = 5;
int TAG_INDEX_TOTAL_TOTAL                   = 6;

static const char * HTML_SPECIAL_TAGS[] =
{
  "<#IPT_PARSE_VERSION>",
  "<#IPT_PARSE_FILTER_START>",
  "<#IPT_PARSE_FILTER_END>",
  "<#IPT_PARSE_EXECUTE_TIMESTAMP>",
  "<#TOTAL_DL>",
  "<#TOTAL_UL>",
  "<#TOTAL_TOTAL>"
};

int TAG_INDEX_DETAIL_START = 0;
int TAG_INDEX_DETAIL_END   = 1;

static const char * HTML_SPECIAL_TAGS_DETAIL_START_END[] =
{
  "<#START_DETAIL>",
  "<#END_DETAIL>"
};

int TAG_INDEX_HOST_ROW_TYPE = 0;
int TAG_INDEX_HOST_IP       = 1;
int TAG_INDEX_HOST_NAME     = 2;
int TAG_INDEX_HOST_DL       = 3;
int TAG_INDEX_HOST_UL       = 4;
int TAG_INDEX_HOST_TOTAL    = 5;

static const char * HTML_SPECIAL_TAGS_DETAIL[] =
{
  "<#HOST_ROW_TYPE>",
  "<#HOST_IP>",
  "<#HOST_NAME>",
  "<#HOST_DL>",
  "<#HOST_UL>",
  "<#HOST_TOTAL>"
};

typedef struct
{
    time_t datetimestamp;
    time_t datestamp;
    char website[MAX_WEBSITE_LENGTH];
    char sourceip[MAX_IPADDRESS_LENGTH];

} dns_usage;

typedef struct
{
    int bShowAdditionalHostInfo;
    int bShowAdditionalHostInfoAppend;
    int bShowHostnameOnly;

} ipt_display_flags;

typedef struct
{
    int iGenerateType;
    ipt_display_flags *pDisplayFlags;
    time_t tStartProcessing;
    int     iRowNumber;

} dns_select_cbd;

typedef struct
{
    char ip[MAX_IPADDRESS_LENGTH];
    double bytes_in[MAX_RANGE];
    double bytes_out[MAX_RANGE];
    double previous_bytes_in[MAX_RANGE];
    double previous_bytes_out[MAX_RANGE];

#ifdef DEBUG_CODE
    char previous_bytes_in_str[1024];
    char previous_bytes_out_str[1024];
#endif

} data_t;

void reset(data_t data)
{
  memset(&data.ip[0],0,MAX_IPADDRESS_LENGTH);
  int idx=0;
  for(idx=0;idx<MAX_RANGE; idx++)
  {
	data.bytes_in[idx]           = 0.0;
	data.bytes_out[idx]          = 0.0;
	data.previous_bytes_in[idx]  = 0.0;
	data.previous_bytes_out[idx] = 0.0;
  }
#ifdef DEBUG_CODE
  memset(&data.previous_bytes_in_str[0],0,1024);
  memset(&data.previous_bytes_out_str[0],0,1024);
#endif
}

static void resetList(data_t tBandwidthUsage[], int listLength)
{
  int idx=0 ;
  for(idx=0;idx<listLength; idx++)
  {
     reset(tBandwidthUsage[idx]);
  }
}


typedef struct
{
    char sPackets[25];
    char sBytes[25];
    char sProt[5];
    char sOp[10];
    char sIn[10];
    char sOut[10];
    char sSource[MAX_IPADDRESS_LENGTH];
    char sDestination[MAX_IPADDRESS_LENGTH];

} ipt_data_t;

void resetIPT(ipt_data_t *data)
{
    memset(&data->sPackets[0],0,25);
    memset(&data->sBytes[0],0,25);
    memset(&data->sProt[0],0,5);
    memset(&data->sOp[0],0,10);
    memset(&data->sIn[0],0,10);
    memset(&data->sOut[0],0,10);
    memset(&data->sSource[0],0,MAX_IPADDRESS_LENGTH);
    memset(&data->sDestination[0],0,MAX_IPADDRESS_LENGTH);
}

typedef struct
{
    data_t *pBandwidthUsage;
    int     iRowNumber;

} ipt_select_cbd;

// =================================================================================
int IsParameterEnabled(const char *pParam, int argc, char *argv[])
{
    int iParameterEnabled = 0;
    int iLookupLen = strlen(pParam);

    int iIdx = 0;
    for(iIdx = 1;
       iIdx < argc;
       iIdx ++)
    {
        char *pArgParam = argv[iIdx];
        if(strncasecmp(pArgParam,pParam,iLookupLen) == 0)
        {
            iParameterEnabled = 1;
            break;
        }
    }

    return iParameterEnabled;
}

int GetParameterIndex(const char *pParam, int argc, char *argv[])
{
    int iParameterIndex = -1;

    int iLookupLen = strlen(pParam);

    int iIdx = 0;
    for(iIdx = 1;
       iIdx < argc;
       iIdx ++)
    {
        char *pArgParam = argv[iIdx];
        if(strncasecmp(pArgParam,pParam,iLookupLen) == 0)
        {
            iParameterIndex = iIdx;
            break;
        }
    }

    return iParameterIndex;
}

int CheckForParameter(const char *pParameter, int argc, char **argv, char *setValue, int defaultArgvIndexIfNotFound)
{
    if(setValue)
    {
        setValue[0] = '\0';
    }
    int iParamIndex = GetParameterIndex(pParameter, argc, argv);
    if(iParamIndex >= 0)
    {
#ifdef DEBUG_CODE
        printf("detected %s\n",pParameter);
        fflush(stdout);
#endif
        if(setValue)
        {
            char *szSpecialFlags = argv[iParamIndex];
            char szSpecialFlagsValue[4096]="";
            strcpy(szSpecialFlagsValue,&(szSpecialFlags[strlen(pParameter)]));
            if(strlen(szSpecialFlagsValue) > 0)
            {
                strcpy(setValue,szSpecialFlagsValue);
            }
        }
    }
    else if(defaultArgvIndexIfNotFound >= 0 && defaultArgvIndexIfNotFound < argc && setValue)
    {
        strcpy(setValue,argv[defaultArgvIndexIfNotFound]);
    }

#ifdef DEBUG_CODE
    if(setValue && strlen(setValue) > 0)
    {
        printf("value found %s\n",setValue);
        fflush(stdout);
    }
#endif

    return iParamIndex;
}

int CheckForParameterAsInteger(const char *pParameter, int argc, char **argv, int *setValue, int defaultArgvIndexIfNotFound)
{
    char szValue[4096]="";
    int result = CheckForParameter(pParameter, argc, argv, szValue, defaultArgvIndexIfNotFound);
    if(result >= 0 || strlen(szValue) > 0)
    {
        if(setValue)
        {
            *setValue = atoi(szValue);
        }
    }

    return result;
}


int CheckForParameterAs2DString(const char *pParameter, int argc, char **argv, char ***setValue, char listDelimiter, int maxStringLength, int defaultArgvIndexIfNotFound)
{
    if(setValue != NULL)
    {
        *setValue=NULL;
    }
    int listSize = 0;
    char szValue[4096]="";
    CheckForParameter(pParameter, argc, argv, szValue, defaultArgvIndexIfNotFound);
    if(strlen(szValue) > 0)
    {
        listSize=1;
        char *pDelimiter=&szValue[0];
        for(;(pDelimiter=strchr(pDelimiter,listDelimiter)) != NULL;)
        {
            pDelimiter++;
            listSize++;
        }

#ifdef DEBUG_CODE
        printf("listSize = %d\n",listSize);
        fflush(stdout);
#endif
        pDelimiter=&szValue[0];
        // Create a pointer to each row of the matrix.
        *setValue = malloc(listSize * sizeof(char *));

        char **array = *setValue;
#ifdef DEBUG_CODE
        printf("*setValue = [%p]\n",array);
        fflush(stdout);
#endif
        // Create each row of the matrix
        int idx=0;
        for(idx = 0; idx < listSize; idx++)
        {
            array[idx] = malloc(maxStringLength * sizeof(char));
            array[idx][0] = '\0';

            char *itemToAdd=pDelimiter;
            if(strchr(pDelimiter,listDelimiter) != NULL)
            {
                pDelimiter = strchr(pDelimiter,listDelimiter);
                pDelimiter[0] = '\0';
                pDelimiter++;
            }

            strcpy(array[idx],itemToAdd);

#ifdef DEBUG_CODE
            printf("Adding item to list [%p][%s]\n",array[idx],array[idx]);
#endif
        }
    }

    return listSize;
}

void CleanupMemoryFor2DString(char ***arrayPtr, int arrayLength)
{
    if(arrayPtr != NULL && *arrayPtr != NULL)
    {
#ifdef DEBUG_CODE
        printf("listSize = %d [%p]\n",arrayLength,*arrayPtr);
        fflush(stdout);
#endif

        char **array = *arrayPtr;
        int arrayLen = arrayLength;
        int idx = 0;
        for(idx = 0; idx < arrayLen; ++idx)
        {
            char *item = array[idx];

#ifdef DEBUG_CODE
            printf("idx = %d [%p][%s]\n",idx,item,item);
            fflush(stdout);
#endif

            if(item != NULL)
            {
                free(item);

#ifdef DEBUG_CODE
            printf("Item freed idx = %d\n",idx);
            fflush(stdout);
#endif

            }
        }

#ifdef DEBUG_CODE
        printf("A\n");
        fflush(stdout);
#endif

        free(array);
        *arrayPtr=NULL;

#ifdef DEBUG_CODE
        printf("B\n");
        fflush(stdout);
#endif

    }
}

int isSpecialHTMLTagParameterEnabled(const char *pData, int argc, const char *argv[])
{
    int iParameterEnabled = 0;
    //int iLookupLen = strlen(pData);

    int iIdx = 0;
    for(iIdx = 0;
       iIdx < argc;
       iIdx ++)
    {
        const char *pArgParam = argv[iIdx];
        if(strstr(pData,pArgParam))
        {
            iParameterEnabled = 1;
            break;
        }
    }

    return iParameterEnabled;
}

// =================================================================================
double ConvertMBytesToNumber(double bytes, char cConvertToType)
{
    switch(cConvertToType)
    {
        case 'K':
        case 'k':
            bytes *= 1024;
            break;
        case 'G':
        case 'g':
            bytes *= 0.0009765625;
            break;
        case 'T':
        case 't':
            bytes *= 9.5367431640625e-07;
            break;
        default:
            bytes *= 1048576;
            break;
    }

    return bytes;
}

double ConvertBytesStringToNumber(char *pBytes, char cConvertToType)
{
    //double bytes = atoi(pBytes);
    char *pEnd=0;
    double bytes = strtod(pBytes,&pEnd);

    // Convert bytes to a standard value in MB for consistent calculations
    if(bytes > 0)
    {
        if(strstr(pBytes,"K") != NULL || strstr(pBytes,"k") != NULL)
        {
            bytes *= 1024;
        }
        else if(strstr(pBytes,"M") != NULL || strstr(pBytes,"m") != NULL)
        {
            bytes *= 1048576;
        }
        else if(strstr(pBytes,"G") != NULL || strstr(pBytes,"g") != NULL)
        {
            bytes *= 1073741824;
        }
    }

    switch(cConvertToType)
    {
        case 'K':
        case 'k':
            bytes *= 0.0009765625;
            break;
        case 'M':
        case 'm':
            bytes *= 9.5367431640625e-07;
            break;
        case 'G':
        case 'g':
            bytes *= 9.31322574615479e-10;
            break;
        case 'T':
        case 't':
            bytes *= .09494701772928e-13;
            break;
        default:
            break;
    }

    return bytes;
}
// ==========================================================================

char* GetHostNameOrIP (char* sHost)
{
#ifdef __WINDOWS__

    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 0);

    int nResult = WSAStartup(wVersionRequested, &wsaData);
    if (nResult != 0)
    {
      perror("Problem Initializing Winsock!");
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0)
    {
      WSACleanup();
      perror("Problem with Winsock Version!");
    }

#endif

	struct in_addr inaddr;
	inaddr.s_addr = inet_addr(sHost);

    char *pReturnValue = 0;
	if( inaddr.s_addr == INADDR_NONE) //if sHost is name and not IP
	{
		struct hostent* phostent = gethostbyname( sHost);
		if( phostent != 0)
		{
            if( sizeof(inaddr) == phostent->h_length)
            {
                inaddr.s_addr = *((unsigned long*) phostent->h_addr);

                pReturnValue = strdup( (char *)inet_ntoa(inaddr));
            }
		}
	}
	else // sHost is probably a valid IP
	{
		struct hostent* phostent = gethostbyaddr( (const char*)&inaddr,sizeof(struct in_addr), AF_INET);
		if( phostent != NULL)
		{
            pReturnValue = strdup(phostent->h_name);
		}
	}

#ifdef __WINDOWS__

    WSACleanup();

#endif

	return pReturnValue;
}

// ==========================================================================
// Nov 20 22:50:43 wrt daemon.debug dnsmasq[149]: query[A] ad.doubleclick.net from 192.168.0.126
time_t getDateTimeValue(char *pMonth, int dayOfMonth, char *pTime)
{
    if(strlen(pMonth) < 3)
    {
        char szBuf[100]="";
        sprintf(szBuf,"In [%s] Invalid date specified pMonth [%s]",__FUNCTION__,pMonth);
        perror(szBuf);
    }
    if(dayOfMonth < 1 || dayOfMonth > 31)
    {
        char szBuf[100]="";
        sprintf(szBuf,"In [%s] Invalid date specified dayOfMonth [%d]",__FUNCTION__,dayOfMonth);
        perror(szBuf);
    }

    time_t now = time(NULL);
    struct tm *nowTm = localtime ( &now );
    if(!nowTm)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for nowTm : [%ld]",__FUNCTION__,now);
        perror(szBuf);
    }

    char szTemp[10] ="";
    struct tm base_tm = { 0 };
    base_tm.tm_isdst = nowTm->tm_isdst;

    base_tm.tm_year = nowTm->tm_year;
    if(strcasecmp(pMonth,"jan") == 0)
    {
        base_tm.tm_mon  = 0;
    }
    else if(strcasecmp(pMonth,"feb") == 0)
    {
        base_tm.tm_mon  = 1;
    }
    else if(strcasecmp(pMonth,"mar") == 0)
    {
        base_tm.tm_mon  = 2;
    }
    else if(strcasecmp(pMonth,"apr") == 0)
    {
        base_tm.tm_mon  = 3;
    }
    else if(strcasecmp(pMonth,"may") == 0)
    {
        base_tm.tm_mon  = 4;
    }
    else if(strcasecmp(pMonth,"jun") == 0)
    {
        base_tm.tm_mon  = 5;
    }
    else if(strcasecmp(pMonth,"jul") == 0)
    {
        base_tm.tm_mon  = 6;
    }
    else if(strcasecmp(pMonth,"aug") == 0)
    {
        base_tm.tm_mon  = 7;
    }
    else if(strcasecmp(pMonth,"sep") == 0)
    {
        base_tm.tm_mon  = 8;
    }
    else if(strcasecmp(pMonth,"oct") == 0)
    {
        base_tm.tm_mon  = 9;
    }
    else if(strcasecmp(pMonth,"nov") == 0)
    {
        base_tm.tm_mon  = 10;
    }
    else if(strcasecmp(pMonth,"dec") == 0)
    {
        base_tm.tm_mon  = 11;
    }
    else
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for now : [%ld]",__FUNCTION__,now);
        perror(szBuf);
    }

    base_tm.tm_mday = dayOfMonth;

    if(pTime)
    {
        strncpy(szTemp,&(pTime[0]),2);
        base_tm.tm_hour = atoi(szTemp);
        memset(szTemp,0,10);
        strncpy(szTemp,&(pTime[3]),2);
        base_tm.tm_min  = atoi(szTemp);
        memset(szTemp,0,10);
        strncpy(szTemp,&(pTime[6]),2);
        base_tm.tm_sec = atoi(szTemp);
    }

    time_t tNewDateTime = mktime(&base_tm);
    if(tNewDateTime < 0)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date/time conversion value specified, returned: [%ld]",__FUNCTION__,tNewDateTime);
        perror(szBuf);
    }

    return tNewDateTime;
}

time_t getDateTimeValueFromString(char *base_date)
{
    if(strlen(base_date) < 10)
    {
        char szBuf[100]="";
        sprintf(szBuf,"In [%s] Invalid date(s) specified base_date [%s]",__FUNCTION__,base_date);
        perror(szBuf);
    }

    char szTemp[10] ="";
    struct tm base_tm = { 0 };
    base_tm.tm_isdst = -1;

    //memset(&base_tm,0,sizeof(struct tm));
    strncpy(szTemp,&(base_date[0]),4);
    base_tm.tm_year = atoi(szTemp) - 1900;
    memset(szTemp,0,10);
    strncpy(szTemp,&(base_date[5]),2);
    base_tm.tm_mon  = atoi(szTemp) - 1;
    memset(szTemp,0,10);
    strncpy(szTemp,&(base_date[8]),2);
    base_tm.tm_mday = atoi(szTemp);
    memset(szTemp,0,10);

    // Look for time value
    if(strlen(base_date) >= 16)
    {
        memset(szTemp,0,10);
        strncpy(szTemp,&(base_date[11]),2);
        base_tm.tm_hour = atoi(szTemp);

        memset(szTemp,0,10);
        strncpy(szTemp,&(base_date[14]),2);
        base_tm.tm_min = atoi(szTemp);

        if(strlen(base_date) >= 19)
        {
            memset(szTemp,0,10);
            strncpy(szTemp,&(base_date[17]),2);
            base_tm.tm_sec = atoi(szTemp);
        }
    }

    time_t tNewDate     = mktime(&base_tm);
    if(tNewDate < 0)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned: [%ld]",__FUNCTION__,tNewDate);
        perror(szBuf);
    }

    return tNewDate;
}
// ==========================================================================

uint64_t getDateValue(char *base_date)
{
    struct tm base_tm = { 0 };
    base_tm.tm_isdst = -1;
    char szTemp[10] ="";
    time_t tNewDate = getDateTimeValueFromString(base_date);
    struct tm *new_base_tm = localtime ( &tNewDate );
    if(!new_base_tm)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for new_base_tm : [%ld]",__FUNCTION__,tNewDate);
        perror(szBuf);
    }

    base_tm = *new_base_tm;
    strftime(szTemp, 9, "%Y%m%d", &base_tm);
    char *pEnd=0;
    uint64_t return_date = strtod(szTemp,&pEnd);
    return return_date;
}
// ==========================================================================

int GetDayIndex(uint64_t base_date, uint64_t current_date)
{
#ifdef DEBUG_CODE
    if(current_date > base_date)
    {
        int ii = 0;
    }
#endif

    int iday_index      = 0;
    char szBase[50]     ="";
    char szCurrent[50]  ="";
    char szTemp[6]      ="";

    if(base_date != current_date)
    {
		sprintf(szBase,"%llu",(long long unsigned int)base_date);
		if(strlen(szBase) < 8)
		{
		    char szBuf[100]="";
		    sprintf(szBuf,"In [%s] Invalid date(s) specified szBase [%s]",__FUNCTION__,szBase);
		    perror(szBuf);
		}

		struct tm base_tm = { 0 };
		base_tm.tm_isdst = -1;
		strncpy(szTemp,&(szBase[0]),4);
		base_tm.tm_year = atoi(szTemp) - 1900;
		memset(szTemp,0,5);
		strncpy(szTemp,&(szBase[4]),2);
		base_tm.tm_mon  = atoi(szTemp) - 1;
		memset(szTemp,0,5);
		strncpy(szTemp,&(szBase[6]),2);
		base_tm.tm_mday = atoi(szTemp);
		memset(szTemp,0,5);

		time_t tNewBaseDate     = mktime(&base_tm);

		sprintf(szCurrent,"%llu",(long long unsigned int)current_date);
		if(strlen(szCurrent) < 8)
		{
		    char szBuf[100]="";
		    sprintf(szBuf,"In [%s] Invalid date(s) specified szCurrent [%s]",__FUNCTION__,szCurrent);
		    perror(szBuf);
		}

		struct tm current_tm = { 0 };
		current_tm.tm_isdst = -1;
		strncpy(szTemp,&(szCurrent[0]),4);
		current_tm.tm_year = atoi(szTemp) - 1900;
		memset(szTemp,0,5);
		strncpy(szTemp,&(szCurrent[4]),2);
		current_tm.tm_mon  = atoi(szTemp) - 1;
		memset(szTemp,0,5);
		strncpy(szTemp,&(szCurrent[6]),2);
		current_tm.tm_mday = atoi(szTemp);
		memset(szTemp,0,5);

		time_t tNewCurrentDate  = mktime(&current_tm);

	#ifdef DEBUG_CODE
		if(tNewBaseDate < 0 || tNewCurrentDate < 0)
		{
		    char szBuf[100]="";
		    sprintf(szBuf,"In [%s] Invalid date(s) specified tNewBaseDate [%ld] tNewCurrentDate: [%ld]",__FUNCTION__,tNewBaseDate,tNewCurrentDate);
		    perror(szBuf);
		}
	#endif

		if(tNewCurrentDate != tNewBaseDate)
		{
		    iday_index = ((tNewCurrentDate - tNewBaseDate) / 86400);
		}

	#ifdef DEBUG_CODE
		if(iday_index != 0)
		{
		    int ii = 0;
		}
	#endif
    }

    if(iday_index == -1)
    {
        iday_index = 0;
    }

    return iday_index;
}
// ==========================================================================

#ifdef USE_SQLITE

static int stub_callback(void *NotUsed, int argc, char **argv, char **azColName) {
  printf("In [%s] [%d]\n",__FUNCTION__,__LINE__);
  fflush(stdout);
  return 0;
}
// ==========================================================================

static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
  //printf("In [%s] [%d]\n",__FUNCTION__,__LINE__);
  int i=0;
  for(i=0; i<argc; i++)
  {
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
  return 0;
}
// ==========================================================================

static int callbackSELECT(void *pCBD_Param, int argc, char **argv, char **azColName)
{
  //printf("In [%s] [%d]\n",__FUNCTION__,__LINE__);
  const int HOST_COLUMN_INDEX          = 0;
  const int INCOMING_DATA_COLUMN_INDEX = 1;
  const int OUTGOING_DATA_COLUMN_INDEX = 2;
  //const int TOTAL_DATA_COLUMN_INDEX    = 3;

  ipt_select_cbd *pCBD = pCBD_Param;
#ifdef DEBUG_CODE
  int i=0;
  for(i=0; i<argc; i++)
  {
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
#endif

  data_t *pBandwidthUsage = pCBD->pBandwidthUsage;

  char *pHostValue = argv[HOST_COLUMN_INDEX];
  strcpy(pBandwidthUsage[pCBD->iRowNumber].ip,pHostValue);

  char *pIncomingDataValue = argv[INCOMING_DATA_COLUMN_INDEX];
  char *pOutgoingDataValue = argv[OUTGOING_DATA_COLUMN_INDEX];

  *pBandwidthUsage[pCBD->iRowNumber].bytes_in   = ConvertBytesStringToNumber(pIncomingDataValue,'M');
  *pBandwidthUsage[pCBD->iRowNumber].bytes_out  = ConvertBytesStringToNumber(pOutgoingDataValue,'M');

  pCBD->iRowNumber++;

  return 0;
}
// ==========================================================================

static int callbackSELECTRange(void *pCBD_Param, int argc, char **argv, char **azColName)
{
  //printf("In [%s] [%d]\n",__FUNCTION__,__LINE__);

  const int HOST_COLUMN_INDEX          = 0;
  const int INCOMING_DATA_COLUMN_INDEX = 1;
  const int OUTGOING_DATA_COLUMN_INDEX = 2;
  //const int TOTAL_DATA_COLUMN_INDEX    = 3;
  const int DATE_COLUMN_INDEX          = 4;

  ipt_select_cbd *pCBD = pCBD_Param;
#ifdef DEBUG_CODE
  int i=0;
  for(i=0; i<argc; i++)
  {
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
#endif

  char *pLogDateTime = argv[DATE_COLUMN_INDEX];
  if(pLogDateTime != NULL)
  {
      uint64_t log_date = getDateValue(pLogDateTime);

      if(pCBD->iRowNumber == 0 && start_date > log_date)
      {
          start_date = log_date;
      }

      data_t *ppBandwidthUsage = pCBD->pBandwidthUsage;
      char *pHostValue = argv[HOST_COLUMN_INDEX];
      char *pIncomingDataValue = argv[INCOMING_DATA_COLUMN_INDEX];
      char *pOutgoingDataValue = argv[OUTGOING_DATA_COLUMN_INDEX];

      double bytes_in   = ConvertBytesStringToNumber(pIncomingDataValue,'M');
      double bytes_out  = ConvertBytesStringToNumber(pOutgoingDataValue,'M');

      int iFirstEmptyIndex    = -1;
	  int bFoundClient        = 0;

      int iIdx = 0;
      for(iIdx = 0;
          iIdx < MAX_CLIENTCOUNT;
		  iIdx ++)
      {
          data_t *pBandwidthUsage = &(ppBandwidthUsage[iIdx]);

  		  if(strncasecmp(pHostValue,pBandwidthUsage->ip,strlen(pHostValue)) == 0)
	      {
			  bFoundClient = 1;

			  uint64_t uiFileDate = log_date;
			  int iday_index      = GetDayIndex(start_date,uiFileDate);

			  if(iday_index >= MAX_RANGE || iday_index < 0)
			  {
                  char szBuf[100]="";
                  sprintf(szBuf,"In [%s] #1 Invalid iday_index value [%d]",__FUNCTION__,iday_index);
                  perror(szBuf);
			  }

#ifdef DEBUG_CODE
              char *pPreviousStr    = &(pBandwidthUsage->previous_bytes_in_str[0]);
#endif
              pBandwidthUsage->bytes_in[iday_index] = bytes_in;

#ifdef DEBUG_CODE
              double dValue = pBandwidthUsage->bytes_in[iday_index];
			  if(dValue < 0)
              {
                  int ii = 0;
			  }

              strcpy(pBandwidthUsage->previous_bytes_in_str,pPreviousStr);
#endif

#ifdef DEBUG_CODE
		      pPreviousStr    = &(pBandwidthUsage->previous_bytes_out_str[0]);
#endif

              pBandwidthUsage->bytes_out[iday_index] = bytes_out;

#ifdef DEBUG_CODE
              dValue = pBandwidthUsage->bytes_out[iday_index];
              if(dValue < 0)
              {
                  int ii = 0;
              }

              strcpy(pBandwidthUsage->previous_bytes_out_str,pPreviousStr);
#endif

			  break;
		  }
		  else if(iFirstEmptyIndex < 0 && pBandwidthUsage->ip[0] == '\0')
		  {
			  iFirstEmptyIndex = iIdx;
			  break;
		  }
      }

	  if(bFoundClient == 0 && iFirstEmptyIndex >= 0)
	  {
		  uint64_t uiFileDate     = log_date;
		  data_t *pBandwidthUsage = &(ppBandwidthUsage[iFirstEmptyIndex]);

		  int iday_index = 0;
		  if(start_date > 0)
		  {
			  iday_index = GetDayIndex(start_date,uiFileDate);
		  }

          if(iday_index >= MAX_RANGE || iday_index < 0)
          {
              char szBuf[100]="";
              sprintf(szBuf,"In [%s] #2 Invalid iday_index value [%d]",__FUNCTION__,iday_index);
              perror(szBuf);
          }

		  strcpy(pBandwidthUsage->ip,pHostValue);
		  pBandwidthUsage->bytes_in[iday_index] = bytes_in;

#ifdef DEBUG_CODE
          //strcpy(pBandwidthUsage->previous_bytes_in_str,pBytes);
#endif
		  pBandwidthUsage->bytes_out[iday_index] = bytes_out;

#ifdef DEBUG_CODE
		  //strcpy(pBandwidthUsage->previous_bytes_out_str,pBytes);
#endif

	  }
  }

  pCBD->iRowNumber++;
  return 0;
}
// ==========================================================================

static int callbackSELECTDNS(void *pCBD_Param, int argc, char **argv, char **azColName)
{
    //printf("In [%s] [%d]\n",__FUNCTION__,__LINE__);

    const int HOST_COLUMN_INDEX       = 0;
    const int WEBSITE_COLUMN_INDEX    = 1;
    const int TIMESTAMP_COLUMN_INDEX  = 2;

    dns_select_cbd *pCBD = pCBD_Param;
#ifdef DEBUG_CODE
    int i=0;
    for(i=0; i<argc; i++)
    {
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
#endif

    char *pHostValue = argv[HOST_COLUMN_INDEX];
    char *pWebsiteValue = argv[WEBSITE_COLUMN_INDEX];
    char *pDateTime = argv[TIMESTAMP_COLUMN_INDEX];
    pCBD->iRowNumber++;

    if(pHostValue != NULL && pHostValue[0] != '\0')
    {
        char szHostInfo[65] = "";
        if(pCBD->pDisplayFlags != NULL && pCBD->pDisplayFlags->bShowAdditionalHostInfo == 1 &&
                                          pCBD->pDisplayFlags->bShowHostnameOnly       == 0)
        {
            char *pHost = GetHostNameOrIP(pHostValue);

            if( pCBD->iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE)
            {
                sprintf(szHostInfo,"%s - %s",pHostValue,pHost);
            }
            else
            {
                if(pCBD->pDisplayFlags->bShowAdditionalHostInfoAppend == 1)
                {
                    sprintf(szHostInfo,"%s - %s",pHostValue, pHost);
                }
                else
                {
                    sprintf(szHostInfo,"<a href='#' title='%s'>%s</a>",pHost,pHostValue);
                }
            }

            if(pHost) free(pHost);
        }
        else
        {
            if(pCBD->pDisplayFlags != NULL && pCBD->pDisplayFlags->bShowHostnameOnly == 1)
            {
                char *pHost = GetHostNameOrIP(pHostValue);

                sprintf(szHostInfo,"%s",pHost);

                if(pHost) free(pHost);
            }
            else
            {
                sprintf(szHostInfo,"%s",pHostValue);
            }
        }

        time_t datetimestamp = getDateTimeValueFromString(pDateTime);
        struct tm *tm_timestamp = localtime (&datetimestamp);
        if(!tm_timestamp)
        {
            char szBuf[100]="";
            sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,datetimestamp);
            perror(szBuf);
        }

        char szTimeStamp[100]="";
        strftime (szTimeStamp, 1023, "%Y-%m-%d %H:%M:%S", tm_timestamp);

        if( pCBD->iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE)
        {
            printf("Host [%s]\tWebsite [%s]\tTimestamp [%s]\n",
                    szHostInfo,
                    pWebsiteValue,
                    szTimeStamp);
        }
        else
        {
            printf("\
            <tr class=\"%s\">\
            <td class=\"rtitle\">%s</td>\
            <td class=\"ul\">%s</td>\
            <td class=\"total\">%s</td>\
            </tr>",
            (pCBD->iRowNumber % 2 == 0 ? "even" : "odd"),
            szHostInfo,
            pWebsiteValue,
            szTimeStamp);
        }
    }
    return 0;
}
// ==========================================================================

#endif

struct tm modifyDateValueByDays(uint64_t base_date, int iAddDays)
{
    char szBase[50]     ="";
    char szTemp[5]      ="";

    sprintf(szBase,"%llu",(long long unsigned int)base_date);

    struct tm base_tm = { 0 };
    base_tm.tm_isdst = -1;
    strncpy(szTemp,&(szBase[0]),4);
    base_tm.tm_year = atoi(szTemp) - 1900;
    memset(szTemp,0,5);
    strncpy(szTemp,&(szBase[4]),2);
    base_tm.tm_mon  = atoi(szTemp) - 1;
    memset(szTemp,0,5);
    strncpy(szTemp,&(szBase[6]),2);
    base_tm.tm_mday = atoi(szTemp);
    memset(szTemp,0,5);

    base_tm.tm_mday     += iAddDays;
    time_t tNewDate     = mktime(&base_tm);
    if(tNewDate < 0)
    {
        char szBuf[100]="";
        sprintf(szBuf,"In [%s] Invalid date conversion value specified, adding [%d] returned: [%ld]",__FUNCTION__,iAddDays,tNewDate);
        perror(szBuf);
    }

    struct tm *new_base_tm = localtime ( &tNewDate );
    if(!new_base_tm)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for new_base_tm : [%ld]",__FUNCTION__,tNewDate);
        perror(szBuf);
    }

    base_tm = *new_base_tm;

    return base_tm;
}
// ==========================================================================

void SetDailyBandwidthForHost(int iday_index, double bytes, double bytes_array[], double previous_bytes_array[],int iLogFileIsOnANewDate)
{
    double previous_bytes = previous_bytes_array[iday_index];

#ifdef DEBUG_CODE
    double current_bytes  = bytes_array[iday_index];
#endif

    // Get previous date's bandwidth value to carry forward to the date we are working on
    if(iLogFileIsOnANewDate && (iday_index - 1 >= 0))
    {
        previous_bytes = previous_bytes_array[iday_index-1];
    }

    if(bytes > 0 && previous_bytes > 0 && bytes < previous_bytes)
    {
        bytes_array[iday_index] += bytes;
    }
    else if(bytes > 0 && previous_bytes > 0 && previous_bytes < bytes)
    {
        double dAddBytes = (bytes - previous_bytes);
        bytes_array[iday_index] += dAddBytes;
    }
    else if(bytes > 0 && previous_bytes == 0)
    {
        bytes_array[iday_index] += bytes;
    }
#ifdef DEBUG_CODE
    else
    {
        int ii = 0;
        if(bytes != previous_bytes || bytes != current_bytes || current_bytes != previous_bytes)
        {
            int ii2 = 0;
        }
    }
#endif
}
// ==========================================================================

void ProcessIPTableRow( char *pLogFileName,
                        int iDirection,
                        ipt_data_t *pData,
                        data_t ppBandwidthUsage[],
                        int iMaxClientCount,
                        int iLogFileIsOnANewDate)
{
    if(strlen(pData->sDestination) == 0 && strlen(pData->sSource) == 0)
    {
        return;
    }

    char *pHostLookup = NULL;
    if(iDirection == UNKNOWN_DATA)
    {
        if(strncasecmp(pData->sSource,"0.0.0.0",7) == 0 || strncasecmp(pData->sSource,"anywhere",8) == 0)
        {
            iDirection = INCOMING_DATA;
        }
        else if(strncasecmp(pData->sDestination,"0.0.0.0",7) == 0 || strncasecmp(pData->sDestination,"anywhere",8) == 0)
        {
            iDirection = OUTGOING_DATA;
        }

    }

    if(iDirection == INCOMING_DATA)
    {
        pHostLookup = &(pData->sDestination[0]);
    }
    else
    {
        pHostLookup = &(pData->sSource[0]);
    }

    if(strncasecmp(pHostLookup,"0.0.0.0",7) == 0 || strncasecmp(pHostLookup,"anywhere",8) == 0)
    {
        return;
    }


#ifdef DEBUG_HOST
    if(strncasecmp(pHostLookup,DEBUG_HOST,strlen(pHostLookup)) != 0)
    {
        return;
    }
#endif

    char *pBytes = pData->sBytes;
    double bytes = ConvertBytesStringToNumber(pBytes,chBytesMeasurementUnitType);

	int iFirstEmptyIndex    = -1;
	int bFoundClient        = 0;

	int iIdx = 0;
    for(iIdx = 0;
        iIdx < iMaxClientCount;
		iIdx ++)
    {
        data_t *pBandwidthUsage = &(ppBandwidthUsage[iIdx]);

		if(strncasecmp(pHostLookup,pBandwidthUsage->ip,strlen(pHostLookup)) == 0)
	    {
			bFoundClient = 1;

			char szFileDate[9]  = "";
			int iFileNameLen    = strlen(pLogFileName);
			memcpy(szFileDate,&(pLogFileName[iFileNameLen-14]),8);

			uint64_t uiFileDate = atoi(szFileDate);
			int iday_index      = GetDayIndex(start_date,uiFileDate);

			if(iday_index >= MAX_RANGE || iday_index < 0)
			{
                char szBuf[100]="";
                sprintf(szBuf,"In [%s] #1 Invalid iday_index value [%d]",__FUNCTION__,iday_index);
                perror(szBuf);
			}

			if(iDirection == INCOMING_DATA)
			{
#ifdef DEBUG_CODE
                char *pPreviousStr    = &(pBandwidthUsage->previous_bytes_in_str[0]);
#endif
                if(uiFileDate < start_date)
                {
                    pBandwidthUsage->bytes_in[iday_index] = bytes;
                }
                else
                {
                    SetDailyBandwidthForHost(iday_index, bytes, pBandwidthUsage->bytes_in, pBandwidthUsage->previous_bytes_in,iLogFileIsOnANewDate);
                }

				pBandwidthUsage->previous_bytes_in[iday_index] = bytes;

#ifdef DEBUG_CODE
				double dValue = pBandwidthUsage->bytes_in[iday_index];
				if(dValue < 0)
				{
				    int ii = 0;
				}

				strcpy(pBandwidthUsage->previous_bytes_in_str,pBytes);
#endif
			}
			else
			{
#ifdef DEBUG_CODE
			    char *pPreviousStr    = &(pBandwidthUsage->previous_bytes_out_str[0]);
#endif

                if(uiFileDate < start_date)
                {
                    pBandwidthUsage->bytes_out[iday_index] = bytes;
                }
                else
                {
                    SetDailyBandwidthForHost(iday_index, bytes, pBandwidthUsage->bytes_out, pBandwidthUsage->previous_bytes_out,iLogFileIsOnANewDate);
                }

				pBandwidthUsage->previous_bytes_out[iday_index] = bytes;

#ifdef DEBUG_CODE
				double dValue = pBandwidthUsage->bytes_out[iday_index];
				if(dValue < 0)
				{
				    int ii = 0;
				}

				strcpy(pBandwidthUsage->previous_bytes_out_str,pBytes);
#endif
			}

            if(end_date < uiFileDate)
            {
                end_date = uiFileDate;
            }

			break;
		}
		else if(iFirstEmptyIndex < 0 && pBandwidthUsage->ip[0] == '\0')
		{
			iFirstEmptyIndex = iIdx;
			break;
		}
    }

	if(bFoundClient == 0 && iFirstEmptyIndex >= 0)
	{
		char szFileDate[9]  = "";
		int iFileNameLen    = strlen(pLogFileName);
		memcpy(szFileDate,&(pLogFileName[iFileNameLen-14]),8);

		uint64_t uiFileDate     = atoi(szFileDate);
		data_t *pBandwidthUsage = &(ppBandwidthUsage[iFirstEmptyIndex]);

		int iday_index = 0;
		if(start_date > 0)
		{
			iday_index = GetDayIndex(start_date,uiFileDate);
		}

        if(iday_index >= MAX_RANGE || iday_index < 0)
        {
            char szBuf[100]="";
            sprintf(szBuf,"In [%s] #2 Invalid iday_index value [%d]",__FUNCTION__,iday_index);
            perror(szBuf);
        }

		strcpy(pBandwidthUsage->ip,pHostLookup);
		if(iDirection == INCOMING_DATA)
		{
			pBandwidthUsage->bytes_in[iday_index] 	        = bytes;
			pBandwidthUsage->previous_bytes_in[iday_index]  = bytes;

#ifdef DEBUG_CODE
			strcpy(pBandwidthUsage->previous_bytes_in_str,pBytes);
#endif
		}
		else
		{
			pBandwidthUsage->bytes_out[iday_index] 	        = bytes;
			pBandwidthUsage->previous_bytes_out[iday_index] = bytes;

#ifdef DEBUG_CODE
			strcpy(pBandwidthUsage->previous_bytes_out_str,pBytes);
#endif
		}

		if(start_date <= 0)
		{
			start_date = uiFileDate;
		}
		if(end_date < uiFileDate)
		{
			end_date = uiFileDate;
		}
	}
}
// ==========================================================================

void DisplayResults(int iGenerateType, data_t tBandwidthUsage[], data_t tBandwidthUsageInitValue[],
                    time_t tStartProcessing, char *pStartRangeTitle, char *pEndRangeTitle,
                    ipt_display_flags *pDisplayFlags)
{
    char szPageGenerated[1024]="";
    time_t now = time ( NULL );
    struct tm *tm_now = localtime ( &now );
    if(!tm_now)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,now);
        perror(szBuf);
    }

    strftime (szPageGenerated, 1023, "%Y-%m-%d %H:%M:%S", tm_now);

    if( iGenerateType == GENERATE_TYPE_LOGFILE_CONSOLE  ||
        iGenerateType == GENERATE_TYPE_SQLITE_IMPORT    ||
        iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE   ||
        iGenerateType == GENERATE_TYPE_TESTLOG_PARSE)
    {
        printf("=====================================================================================================\n");
        if(pStartRangeTitle != NULL && pEndRangeTitle != NULL)
        {
            printf("Bandwidth usage v%s from: %s to %s - last update: %s\n",IPT_VERSION,pStartRangeTitle,pEndRangeTitle, szPageGenerated);
        }
        else
        {
            printf("Bandwidth usage v%s from: %llu to %llu - last update: %s\n",IPT_VERSION,(long long unsigned int)start_date,(long long unsigned int)end_date, szPageGenerated);
        }
    }
    else if(iGenerateType == GENERATE_TYPE_LOGFILE_HTML || iGenerateType == GENERATE_TYPE_SQLITE_HTML)
    {
        if(pStartRangeTitle != NULL && pEndRangeTitle != NULL)
        {
            printf("\
            <html>\
            <link rel='stylesheet' type='text/css' href='tomato.css'>\
            <link rel='stylesheet' type='text/css' href='red.css'>\
            <body>\
            <b>Bandwidth usage v%s from: %s to %s - last update: %s</b>\
            <table class=\"bwmg\" cellspacing=\"1\">\
            <tbody>\
            <tr class=\"header\">\
            <td class=\"rtitle\">Host</td>\
            <td class=\"dl\">Download</td>\
            <td class=\"ul\">Upload</td>\
            <td class=\"total\">Total</td>\
            </tr>",
            IPT_VERSION,
            pStartRangeTitle,
            pEndRangeTitle,
            szPageGenerated);
        }
        else
        {
            printf("\
            <html>\
            <link rel='stylesheet' type='text/css' href='tomato.css'>\
            <link rel='stylesheet' type='text/css' href='red.css'>\
            <body>\
            <b>Bandwidth usage v%s from: %llu to %llu - last update: %s</b>\
            <table class=\"bwmg\" cellspacing=\"1\">\
            <tbody>\
            <tr class=\"header\">\
            <td class=\"rtitle\">Host</td>\
            <td class=\"dl\">Download</td>\
            <td class=\"ul\">Upload</td>\
            <td class=\"total\">Total</td>\
            </tr>",
            IPT_VERSION,
            (long long unsigned int)start_date,
            (long long unsigned int)end_date,
            szPageGenerated);
        }
    }

    double master_bytes_in_total  = 0;
    double master_bytes_out_total = 0;

	int iIdx = 0;
    for(iIdx = 0;
	    iIdx < MAX_CLIENTCOUNT;
		iIdx ++)
    {
        data_t *pBandwidthUsage     = &(tBandwidthUsage[iIdx]);

        if(pBandwidthUsage->ip[0] == '\0') break;

        double first_bytes_in_total  = pBandwidthUsage->bytes_in[0];
        double first_bytes_out_total = pBandwidthUsage->bytes_out[0];

        double bytes_in_total  = 0;
        double bytes_out_total = 0;

        int iIdx2 = 0;
        for(iIdx2 = 0;
            iIdx2 < MAX_RANGE;
            iIdx2 ++)
        {
            bytes_in_total  += pBandwidthUsage->bytes_in[iIdx2];
            bytes_out_total += pBandwidthUsage->bytes_out[iIdx2];
        }

        int iIdx3 = 0;
        for(iIdx3 = 0;
            iIdx3 < MAX_CLIENTCOUNT;
            iIdx3 ++)
        {
            data_t *pBandwidthUsageInit = &(tBandwidthUsageInitValue[iIdx3]);

            if(strcasecmp(pBandwidthUsageInit->ip,pBandwidthUsage->ip) == 0)
            {
                // Subtract off previous days start values
                double dInitialBytesIn  = pBandwidthUsageInit->bytes_in[0];
                double dInitialBytesOut = pBandwidthUsageInit->bytes_out[0];

                if(dInitialBytesIn <= first_bytes_in_total)
                {
                    bytes_in_total  -= dInitialBytesIn;
                }
                if(dInitialBytesOut <= first_bytes_out_total)
                {
                    bytes_out_total -= dInitialBytesOut;
                }

                break;
            }
        }

        master_bytes_in_total += bytes_in_total;
        master_bytes_out_total += bytes_out_total;

        char szHostInfo[65] = "";
        if(pDisplayFlags != NULL && pDisplayFlags->bShowAdditionalHostInfo == 1 &&
                                    pDisplayFlags->bShowHostnameOnly       == 0)
        {
            char *pHost = GetHostNameOrIP(pBandwidthUsage->ip);

            if( iGenerateType == GENERATE_TYPE_LOGFILE_CONSOLE  ||
                iGenerateType == GENERATE_TYPE_SQLITE_IMPORT    ||
                iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE   ||
                iGenerateType == GENERATE_TYPE_TESTLOG_PARSE)
            {
                sprintf(szHostInfo,"%s - %s",pBandwidthUsage->ip,pHost);
            }
            else
            {
                if(pDisplayFlags->bShowAdditionalHostInfoAppend == 1)
                {
                    sprintf(szHostInfo,"%s - %s",pBandwidthUsage->ip, pHost);
                }
                else
                {
                    sprintf(szHostInfo,"<a href='#' title='%s'>%s</a>",pHost,pBandwidthUsage->ip);
                }
            }

            if(pHost) free(pHost);
        }
        else
        {
            if(pDisplayFlags != NULL && pDisplayFlags->bShowHostnameOnly == 1)
            {
                char *pHost = GetHostNameOrIP(pBandwidthUsage->ip);

                sprintf(szHostInfo,"%s",pHost);

                if(pHost) free(pHost);
            }
            else
            {
                sprintf(szHostInfo,"%s",pBandwidthUsage->ip);
            }
        }

        if( iGenerateType == GENERATE_TYPE_LOGFILE_CONSOLE  ||
            iGenerateType == GENERATE_TYPE_SQLITE_IMPORT    ||
            iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE   ||
            iGenerateType == GENERATE_TYPE_TESTLOG_PARSE)
        {
            printf("Totals for host [%s]\tIncoming [%.2f MB]\tOutgoing [%.2f MB]\tTotal [%.2f MB]\n",
                    szHostInfo,
                    bytes_in_total,
                    bytes_out_total,
                    (bytes_in_total + bytes_out_total));
        }
        else if(iGenerateType == GENERATE_TYPE_LOGFILE_HTML ||
                iGenerateType == GENERATE_TYPE_SQLITE_HTML)
        {
            printf("\
            <tr class=\"%s\">\
            <td class=\"rtitle\">%s</td>\
            <td class=\"dl\">%.2f MB</td>\
            <td class=\"ul\">%.2f MB</td>\
            <td class=\"total\">%.2f MB</td>\
            </tr>",
            (iIdx % 2 == 0 ? "even" : "odd"),
            szHostInfo,
            bytes_in_total,
            bytes_out_total,
            (bytes_in_total + bytes_out_total));
        }
    }

    double dElapsedSeconds = difftime(time(NULL),tStartProcessing);

    if( iGenerateType == GENERATE_TYPE_LOGFILE_CONSOLE  ||
        iGenerateType == GENERATE_TYPE_SQLITE_IMPORT    ||
        iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE   ||
        iGenerateType == GENERATE_TYPE_TESTLOG_PARSE)
    {
        printf("=====================================================================================================\n");
        printf("Totals for all hosts in filter\tIncoming [%.2f MB]\tOutgoing [%.2f MB]\tTotal [%.2f MB]\n",master_bytes_in_total,master_bytes_out_total,(master_bytes_in_total + master_bytes_out_total));
        printf("\nElapsed time to generate stats: %.2f\n",dElapsedSeconds);
    }
    else if(iGenerateType == GENERATE_TYPE_LOGFILE_HTML ||
            iGenerateType == GENERATE_TYPE_SQLITE_HTML)
    {
        printf("\
        <tr class=\"%s\">\
        <td class=\"rtitle\">%s</td>\
        <td class=\"dl\">%.2f MB</td>\
        <td class=\"ul\">%.2f MB</td>\
        <td class=\"total\">%.2f MB</td>\
        </tr>",
        "header", //(iIdx % 2 == 0 ? "even" : "odd"),
        "Total",
        master_bytes_in_total,
        master_bytes_out_total,
        (master_bytes_in_total + master_bytes_out_total));

        printf("\
        </tbody>\
        </table>\
        </html>\
        </body>\
        ");
    }
}

int stringHasSpecialTag(char *str)
{
    int result = 0;


    return result;
}

#ifdef __WINDOWS__
size_t getline(char **lineptr, size_t *n, FILE *stream) {
    char *bufptr = NULL;
    char *p = bufptr;
    size_t size;
    int c;

    if (lineptr == NULL) {
        return -1;
    }
    if (stream == NULL) {
        return -1;
    }
    if (n == NULL) {
        return -1;
    }
    bufptr = *lineptr;
    size = *n;

    c = fgetc(stream);
    if (c == EOF) {
        return -1;
    }
    if (bufptr == NULL) {
        bufptr = malloc(128);
        if (bufptr == NULL) {
                return -1;
        }
        size = 128;
    }
    p = bufptr;
    while(c != EOF) {
        if ((p - bufptr) > (size - 1)) {
                size = size + 128;
                bufptr = realloc(bufptr, size);
                if (bufptr == NULL) {
                        return -1;
                }
        }
        *p++ = c;
        if (c == '\n') {
                break;
        }
        c = fgetc(stream);
    }

    *p++ = '\0';
    *lineptr = bufptr;
    *n = size;

    return p - bufptr - 1;
}
#endif

void DisplayResultsUsingTemplateFile(char *templateFile, data_t tBandwidthUsage[], data_t tBandwidthUsageInitValue[],
                    time_t tStartProcessing, char *pStartRangeTitle, char *pEndRangeTitle,
                    ipt_display_flags *pDisplayFlags)
{
    time_t now = time ( NULL );
    struct tm *tm_now = localtime ( &now );
    if(!tm_now)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,now);
        perror(szBuf);
    }

    char szPageGenerated[1024]="";
    strftime (szPageGenerated, 1023, "%Y-%m-%d %H:%M:%S", tm_now);

    FILE *f=NULL;
    if ((f = fopen(templateFile, "r")) != NULL)
    {
        double master_bytes_in_total  = 0;
        double master_bytes_out_total = 0;

        while(feof(f) == 0)
        {
            const int szBufLen=10000;
            char szBuf[szBufLen];
            memset(szBuf,0,szBufLen);
            size_t data_buflen=szBufLen-1;
            char *buf = &szBuf[0];
            if(getline(&buf,&data_buflen,f))
            {
                // Process Detail TAGS
                if(strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL_START_END[TAG_INDEX_DETAIL_START]))
                {
                    int currentbufindex = data_buflen;
                    while(feof(f) == 0 && !strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL_START_END[TAG_INDEX_DETAIL_END]))
                    {
                        currentbufindex = strlen(szBuf);
                        char *buf = &szBuf[currentbufindex];
                        data_buflen=szBufLen-1;
                        getline(&buf,&data_buflen,f);
                        currentbufindex += data_buflen;
                    }

                    // Strip Detail header and footer as we no longer require it
                    char szBuf2[szBufLen];
                    memset(szBuf2,0,szBufLen);
                    char *find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL_START_END[TAG_INDEX_DETAIL_START])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        strpos += strlen(HTML_SPECIAL_TAGS_DETAIL_START_END[TAG_INDEX_DETAIL_START]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }
                    memset(szBuf2,0,szBufLen);
                    find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL_START_END[TAG_INDEX_DETAIL_END])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        strpos += strlen(HTML_SPECIAL_TAGS_DETAIL_START_END[TAG_INDEX_DETAIL_END]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }
                    // END

                    // Search and replace detail tags
                    int hasDetailParameter = isSpecialHTMLTagParameterEnabled(szBuf, ARRAY_LENGTH(HTML_SPECIAL_TAGS_DETAIL), HTML_SPECIAL_TAGS_DETAIL);
                    if(hasDetailParameter == 1)
                    {
                        char szBufCopy[szBufLen];
                        memset(szBufCopy,0,szBufLen);
                        strcpy(szBufCopy,szBuf);

                        master_bytes_in_total  = 0;
                        master_bytes_out_total = 0;

                        int iIdx = 0;
                        for(iIdx = 0;
                            iIdx < MAX_CLIENTCOUNT;
                            iIdx ++)
                        {
                            data_t *pBandwidthUsage     = &(tBandwidthUsage[iIdx]);

                            if(pBandwidthUsage->ip[0] == '\0') break;

                            double first_bytes_in_total  = pBandwidthUsage->bytes_in[0];
                            double first_bytes_out_total = pBandwidthUsage->bytes_out[0];

                            double bytes_in_total  = 0;
                            double bytes_out_total = 0;

                            int iIdx2 = 0;
                            for(iIdx2 = 0;
                                iIdx2 < MAX_RANGE;
                                iIdx2 ++)
                            {
                                bytes_in_total  += pBandwidthUsage->bytes_in[iIdx2];
                                bytes_out_total += pBandwidthUsage->bytes_out[iIdx2];
                            }

                            int iIdx3 = 0;
                            for(iIdx3 = 0;
                                iIdx3 < MAX_CLIENTCOUNT;
                                iIdx3 ++)
                            {
                                data_t *pBandwidthUsageInit = &(tBandwidthUsageInitValue[iIdx3]);

                                if(strcasecmp(pBandwidthUsageInit->ip,pBandwidthUsage->ip) == 0)
                                {
                                    // Subtract off previous days start values
                                    double dInitialBytesIn  = pBandwidthUsageInit->bytes_in[0];
                                    double dInitialBytesOut = pBandwidthUsageInit->bytes_out[0];

                                    if(dInitialBytesIn <= first_bytes_in_total)
                                    {
                                        bytes_in_total  -= dInitialBytesIn;
                                    }
                                    if(dInitialBytesOut <= first_bytes_out_total)
                                    {
                                        bytes_out_total -= dInitialBytesOut;
                                    }

                                    break;
                                }
                            }

                            master_bytes_in_total += bytes_in_total;
                            master_bytes_out_total += bytes_out_total;

                            // Append the detail template buffer to the string so it is
                            // processed again for this host row
                            if(iIdx > 0)
                            {
                                //strcat(szBuf,szBufCopy);
                                strcpy(szBuf,szBufCopy);
                            }

                            //char szBuf2[szBufLen];
                            memset(szBuf2,0,szBufLen);
                            find_ptr = NULL;
                            while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_ROW_TYPE])))
                            {
                                int strpos = find_ptr - szBuf;
                                memcpy(szBuf2,szBuf,strpos);
                                strcat(szBuf2,(iIdx % 2 == 0 ? "even" : "odd"));
                                strpos += strlen(HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_ROW_TYPE]);
                                strcat(szBuf2,&szBuf[strpos]);
                                strcpy(szBuf,szBuf2);
                            }

                            char *pHost = GetHostNameOrIP(pBandwidthUsage->ip);
                            memset(szBuf2,0,szBufLen);
                            find_ptr = NULL;
                            while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_NAME])))
                            {
                                int strpos = find_ptr - szBuf;
                                memcpy(szBuf2,szBuf,strpos);
                                if(pHost != NULL)
                                {
                                    strcat(szBuf2,pHost);
                                }

                                strpos += strlen(HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_NAME]);
                                strcat(szBuf2,&szBuf[strpos]);
                                strcpy(szBuf,szBuf2);
                            }
                            if(pHost) free(pHost);

                            memset(szBuf2,0,szBufLen);
                            find_ptr = NULL;
                            while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_IP])))
                            {
                                int strpos = find_ptr - szBuf;
                                memcpy(szBuf2,szBuf,strpos);
                                strcat(szBuf2,pBandwidthUsage->ip);
                                strpos += strlen(HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_IP]);
                                strcat(szBuf2,&szBuf[strpos]);
                                strcpy(szBuf,szBuf2);
                            }

                            memset(szBuf2,0,szBufLen);
                            find_ptr = NULL;
                            while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_DL])))
                            {
                                int strpos = find_ptr - szBuf;
                                memcpy(szBuf2,szBuf,strpos);
                                sprintf(&szBuf2[strlen(szBuf2)],"%.2f",bytes_in_total);
                                strpos += strlen(HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_DL]);
                                strcat(szBuf2,&szBuf[strpos]);
                                strcpy(szBuf,szBuf2);
                            }

                            memset(szBuf2,0,szBufLen);
                            find_ptr = NULL;
                            while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_UL])))
                            {
                                int strpos = find_ptr - szBuf;
                                memcpy(szBuf2,szBuf,strpos);
                                sprintf(&szBuf2[strlen(szBuf2)],"%.2f",bytes_out_total);
                                strpos += strlen(HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_UL]);
                                strcat(szBuf2,&szBuf[strpos]);
                                strcpy(szBuf,szBuf2);
                            }

                            memset(szBuf2,0,szBufLen);
                            find_ptr = NULL;
                            while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_TOTAL])))
                            {
                                int strpos = find_ptr - szBuf;
                                memcpy(szBuf2,szBuf,strpos);
                                sprintf(&szBuf2[strlen(szBuf2)],"%.2f",(bytes_in_total + bytes_out_total));
                                strpos += strlen(HTML_SPECIAL_TAGS_DETAIL[TAG_INDEX_HOST_TOTAL]);
                                strcat(szBuf2,&szBuf[strpos]);
                                strcpy(szBuf,szBuf2);
                            }

                            puts(szBuf);
                            memset(szBuf,0,szBufLen);
                        }
                    }
                }
                // END

                // Search and replace non detail TAGS
                int hasParameter = isSpecialHTMLTagParameterEnabled(szBuf, ARRAY_LENGTH(HTML_SPECIAL_TAGS), HTML_SPECIAL_TAGS);
                if(hasParameter == 1)
                {
                    char szBuf2[szBufLen];
                    char *find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS[TAG_INDEX_IPT_PARSE_VERSION])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        strcat(szBuf2,IPT_VERSION);
                        strpos += strlen(HTML_SPECIAL_TAGS[TAG_INDEX_IPT_PARSE_VERSION]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }

                    memset(szBuf2,0,szBufLen);
                    find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS[TAG_INDEX_IPT_PARSE_FILTER_START])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        sprintf(&szBuf2[strlen(szBuf2)],"%s",pStartRangeTitle);
                        strpos += strlen(HTML_SPECIAL_TAGS[TAG_INDEX_IPT_PARSE_FILTER_START]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }

                    memset(szBuf2,0,szBufLen);
                    find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS[TAG_INDEX_IPT_PARSE_FILTER_END])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        sprintf(&szBuf2[strlen(szBuf2)],"%s",pEndRangeTitle);
                        strpos += strlen(HTML_SPECIAL_TAGS[TAG_INDEX_IPT_PARSE_FILTER_END]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }

                    memset(szBuf2,0,szBufLen);
                    find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS[TAG_INDEX_IPT_PARSE_EXECUTE_TIMESTAMP])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        sprintf(&szBuf2[strlen(szBuf2)],"%s",szPageGenerated);
                        strpos += strlen(HTML_SPECIAL_TAGS[TAG_INDEX_IPT_PARSE_EXECUTE_TIMESTAMP]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }

                    memset(szBuf2,0,szBufLen);
                    find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS[TAG_INDEX_TOTAL_DL])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        sprintf(&szBuf2[strlen(szBuf2)],"%.2f",master_bytes_in_total);
                        strpos += strlen(HTML_SPECIAL_TAGS[TAG_INDEX_TOTAL_DL]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }

                    memset(szBuf2,0,szBufLen);
                    find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS[TAG_INDEX_TOTAL_UL])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        sprintf(&szBuf2[strlen(szBuf2)],"%.2f",master_bytes_out_total);
                        strpos += strlen(HTML_SPECIAL_TAGS[TAG_INDEX_TOTAL_UL]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }

                    memset(szBuf2,0,szBufLen);
                    find_ptr = NULL;
                    while((find_ptr = strstr(szBuf,HTML_SPECIAL_TAGS[TAG_INDEX_TOTAL_TOTAL])))
                    {
                        int strpos = find_ptr - szBuf;
                        memcpy(szBuf2,szBuf,strpos);
                        sprintf(&szBuf2[strlen(szBuf2)],"%.2f",(master_bytes_in_total + master_bytes_out_total));
                        strpos += strlen(HTML_SPECIAL_TAGS[TAG_INDEX_TOTAL_TOTAL]);
                        strcat(szBuf2,&szBuf[strpos]);
                        strcpy(szBuf,szBuf2);
                    }
                }
                // END

                puts(szBuf);
            }
        }

        fclose(f);
    }
}

// ==========================================================================

int ProcessIPTablesOutput(FILE *f, char *szLogFile, data_t tBandwidthUsage[],int iDirection, int iLogFileIsOnANewDate)
{
    int iHostsFound = 0;
    // iptables -L always seems to output two lines of header info so we will swallow
    // them and pass information along processing to the row parser
    char szDiscard[1024] = "";
    for(;feof(f) == 0 && strstr(szDiscard,"destination") == NULL;)
    {
        fgets(szDiscard,1023,f);
    }

    while(feof(f) == 0)
    {
        ipt_data_t tData;
        //memset(&tData,0,sizeof(tData));
        resetIPT(&tData);

        int iCount = fscanf(f, "%s %s %s %s %s %s %s %s",
                            tData.sPackets,
                            tData.sBytes,
                            tData.sProt,
                            tData.sOp,
                            tData.sIn,
                            tData.sOut,
                            tData.sSource,
                            tData.sDestination);

        if(iCount == 8)
        {
            ProcessIPTableRow(szLogFile, iDirection, &tData,
                              tBandwidthUsage,MAX_CLIENTCOUNT,iLogFileIsOnANewDate);
            iHostsFound++;
        }
    }

    return iHostsFound;
}
// ==========================================================================

#ifdef USE_SQLITE

void mainSelectStatsByCriteria(char *pDB,
                               char *szLogFileFilterStart,
                               char *szLogFileFilterEnd,
                               data_t tBandwidthUsage[],
                               data_t tBandwidthUsageInitValue[],
                               int iGenerateType,
                               char *customSortOrder,
                               char *customFilter)
{
    sqlite3 *db=0;
    char *zErrMsg = 0;
    int rc= sqlite3_open(pDB, &db);
    if( rc )
    {
        printf("Can't open database: [%s] line: %d\n", sqlite3_errmsg(db),__LINE__);
        fflush(stdout);

        sqlite3_close(db);
        exit(1);
    }

    int bUseDateOnly = 1;

    char szFilterStart[18]="";
    memcpy(&(szFilterStart[0]),&(szLogFileFilterStart[0]),4);
    szFilterStart[4] = '-';
    memcpy(&(szFilterStart[5]),&(szLogFileFilterStart[4]),2);
    szFilterStart[7] = '-';
    memcpy(&(szFilterStart[8]),&(szLogFileFilterStart[6]),2);
    szFilterStart[10] = '\0';

    if(strlen(szLogFileFilterStart) >= 14)
    {
        szFilterStart[10] = ' ';
        memcpy(&(szFilterStart[11]),&(szLogFileFilterStart[9]),5);

        if(strlen(szLogFileFilterStart) >= 17)
        {
            memcpy(&(szFilterStart[16]),&(szLogFileFilterStart[14]),3);
        }
        bUseDateOnly = 0;
    }

    char szFilterEnd[18]="";
    memcpy(&(szFilterEnd[0]),&(szLogFileFilterEnd[0]),4);
    szFilterEnd[4] = '-';
    memcpy(&(szFilterEnd[5]),&(szLogFileFilterEnd[4]),2);
    szFilterEnd[7] = '-';
    memcpy(&(szFilterEnd[8]),&(szLogFileFilterEnd[6]),2);
    szFilterEnd[10] = '\0';

    if(strlen(szLogFileFilterEnd) >= 14)
    {
        szFilterEnd[10] = ' ';
        memcpy(&(szFilterEnd[11]),&(szLogFileFilterEnd[9]),5);

        if(strlen(szLogFileFilterEnd) >= 17)
        {
            memcpy(&(szFilterEnd[16]),&(szLogFileFilterEnd[14]),3);
        }
        bUseDateOnly = 0;
    }

    start_date  = atoi(szLogFileFilterStart);
	end_date    = atoi(szLogFileFilterEnd);

    char szSQL[4096]="";

    if(customSortOrder == NULL)
    {
        customSortOrder = "log_datetime,host";
    }
    char *groupBy=NULL;

    if(bUseDateOnly == 1)
    {
        if(iGenerateType == GENERATE_TYPE_SQLITE_DATACOMPRESS)
        {
            groupBy="date(log_datetime),host";
            sprintf(szSQL,"SELECT host,sum(in_mb) inMB,sum(out_mb) outMB, sum(in_mb) + sum(out_mb) totalMB,date(log_datetime) FROM %s WHERE date(log_datetime) BETWEEN date('%s') AND date('%s')",
                        BANDWIDTH_USAGE_TABLENAME,
                        szFilterStart,
                        szFilterEnd);
        }
        else
        {
            groupBy="host";
            sprintf(szSQL,"SELECT host,sum(in_mb) inMB,sum(out_mb) outMB, sum(in_mb) + sum(out_mb) totalMB FROM %s WHERE date(log_datetime) BETWEEN date('%s') AND date('%s')",
                        BANDWIDTH_USAGE_TABLENAME,
                        szFilterStart,
                        szFilterEnd);
        }
    }
    else
    {
        if(iGenerateType == GENERATE_TYPE_SQLITE_DATACOMPRESS)
        {
            groupBy="date(log_datetime),host";
            sprintf(szSQL,"SELECT host,sum(in_mb) inMB, sum(out_mb) outMB, sum(in_mb) + sum(out_mb) totalMB, date(log_datetime) FROM %s WHERE date(log_datetime) BETWEEN date('%s') AND date('%s')",
                        BANDWIDTH_USAGE_TABLENAME,
                        szFilterStart,
                        szFilterEnd);
        }
        else
        {
            groupBy="host";
            sprintf(szSQL,"SELECT host,sum(in_mb) inMB, sum(out_mb) outMB, sum(in_mb) + sum(out_mb) totalMB FROM %s WHERE datetime(log_datetime) BETWEEN datetime('%s') AND datetime('%s')",
                        BANDWIDTH_USAGE_TABLENAME,
                        szFilterStart,
                        szFilterEnd);
        }
    }

    if(customFilter != NULL)
    {
        strcat(szSQL," AND ");
        strcat(szSQL,customFilter);
    }

    if(groupBy != NULL)
    {
        strcat(szSQL," GROUP BY ");
        strcat(szSQL,groupBy);
    }

    strcat(szSQL," ORDER BY ");
    strcat(szSQL,customSortOrder);
    strcat(szSQL,";");




    if(iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE || iGenerateType == GENERATE_TYPE_SQLITE_DATACOMPRESS)
    {
        printf("Selecting SQL [%s]\n", szSQL);
        fflush(stdout);
    }

    ipt_select_cbd tCBD;
    tCBD.pBandwidthUsage = tBandwidthUsage;
    tCBD.iRowNumber      = 0;

    if(iGenerateType == GENERATE_TYPE_SQLITE_DATACOMPRESS)
    {
        rc = sqlite3_exec(db, szSQL, callbackSELECTRange, (void *)&tCBD, &zErrMsg);
    }
    else
    {
        rc = sqlite3_exec(db, szSQL, callbackSELECT, (void *)&tCBD, &zErrMsg);
    }

    if( rc != SQLITE_OK )
    {
        printf("SQL error: %s, line: %d\n", zErrMsg,__LINE__);
        fflush(stdout);

        sqlite3_free(zErrMsg);
    }

    sqlite3_close(db);
}
// ==========================================================================

int CanShrinkDatabase(char *pDB)
{
    int iReturn = 0;

#ifdef __WINDOWS__

    iReturn = 1;

#else

    char szBuf[1024]="";
    sprintf(szBuf,"df %s",pDB);

    FILE *fp = popen(szBuf, "r");
    if(fp == NULL)
    {
        printf("Can't open pipe for command [%s] line: %d\n", szBuf,__LINE__);
    }
    else
    {
        char szDiscard[1024] = "";
        for(;feof(fp) == 0 && strstr(szDiscard,"Mounted on") == NULL;)
        {
            fgets(szDiscard,1023,fp);
        }

        if(feof(fp) == 0)
        {
            char szFileSystem[300]="";

            char sz1KBlocks[50]="";
            char szUsed[50]="";
            char szAvailable[50]="";
            char szUsedPercent[300]="";
            char szMountedOn[300]="";

            fscanf(fp, "%s %s %s %s %s %s",
                                szFileSystem,
                                sz1KBlocks,
                                szUsed,
                                szAvailable,
                                szUsedPercent,
                                szMountedOn);

            char *pEnd=0;
            double d1KBlocks=strtod(sz1KBlocks,&pEnd);
            double dUsed=strtod(szUsed,&pEnd);
            double dAvailable=strtod(szAvailable,&pEnd);

            double dBytesFree = (dAvailable * d1KBlocks);

            struct stat fBuf;
            if(stat(pDB, &fBuf) < 0)
            {
                printf("ERROR: %s, line: %d\n", strerror(errno),__LINE__);
                fflush(stdout);
            }
            else
            {
                if(dBytesFree >= fBuf.st_size)
                {
                    iReturn = 1;
                }

                printf("Filesystem\t1K-blocks\tUsed\tAvailable\tUse%%\tMounted on\tFILESIZE\tBYTES FREE\n");
                printf("%s\t%.2f\t\t%.2f\t%.2f\t%s\t%s\t%lu\t%.2f\n",
                        szFileSystem,
                        d1KBlocks,
                        dUsed,
                        dAvailable,
                        szUsedPercent,
                        szMountedOn,
                        (size_t)fBuf.st_size,
                        dBytesFree);
                fflush(stdout);
            }
        }

        pclose(fp);
    }

    /*
    struct statvfs fiData;
    struct statvfs *fpData;
    char fnPath[128];

    strcpy(fnPath, pDB);
    if((statvfs(fnPath,&fiData)) < 0 )
    {
        printf("Failed to stat %s:\n", fnPath);
        fflush(stdout);
    }
    else
    {
        printf("Disk %s: \n", fnPath);
        printf("\tblock size: %lu\n", fiData.f_bsize);
        printf("\ttotal no blocks: %u\n", fiData.f_blocks);
        printf("\tfree blocks: %u\n", fiData.f_bfree);
        fflush(stdout);

        iReturn = 0;
    }
    */
#endif

    return iReturn;
}

void vacuumDB(char *pDB)
{
    sqlite3 *db=0;
    char *zErrMsg = 0;

    int rc = sqlite3_open(pDB, &db);
    if( rc )
    {
        printf("Can't open database: [%s] line: %d\n", sqlite3_errmsg(db),__LINE__);
        fflush(stdout);

        sqlite3_close(db);
        exit(1);
    }

    rc = sqlite3_exec(db, "VACUUM;", stub_callback, 0, &zErrMsg);
    if( rc != SQLITE_OK )
    {
        printf("VACUUM SQL error: %s, line: %d\n", zErrMsg,__LINE__);
        fflush(stdout);

        sqlite3_free(zErrMsg);
    }

    printf("Closing after shrink database...\n");
    fflush(stdout);

    sqlite3_close(db);
}

int deleteStatsFromSqlite(char *pDB, uint64_t startDate, uint64_t endDate)
{
    int iTotalRowsDelete = 0;

    char szStartDate[21]="";
    struct tm startDateRange = modifyDateValueByDays(startDate, 0);
    strftime (szStartDate, 20, "%Y-%m-%d", &startDateRange);
    char szEndDate[21]="";
    struct tm endDateRange = modifyDateValueByDays(endDate, 0);
    strftime (szEndDate, 20, "%Y-%m-%d", &endDateRange);

    char szSQL[1024]="";
    sprintf(szSQL,"DELETE FROM %s WHERE date(log_datetime) BETWEEN date('%s') AND date('%s');",
                BANDWIDTH_USAGE_TABLENAME,
                szStartDate,
                szEndDate);

    printf("Delete SQL [%s]\n", szSQL);
    fflush(stdout);

    sqlite3 *db=0;
    char *zErrMsg = 0;

    int rc = sqlite3_open(pDB, &db);
    if( rc )
    {
        printf("Can't open database: [%s] line: %d\n", sqlite3_errmsg(db),__LINE__);
        fflush(stdout);

        sqlite3_close(db);
        exit(1);
    }

    rc = sqlite3_exec(db, szSQL, callback, 0, &zErrMsg);
    if( rc != SQLITE_OK )
    {
        printf("DELETE SQL error: %s\n", zErrMsg);
        fflush(stdout);

        sqlite3_free(zErrMsg);
    }

    int iChangeCount  = sqlite3_changes(db);
    iTotalRowsDelete  = iChangeCount;

    printf("Closing database...\n");
    fflush(stdout);

    sqlite3_close(db);

    int bCanShrink = CanShrinkDatabase(pDB);

    printf("Records Deleted [%d] check to shrink database = [%d]...\n", iTotalRowsDelete,bCanShrink);
    fflush(stdout);

    //if(bCanShrink)
    //{
    //    vacuumDB(pDB);
    //}

    return iTotalRowsDelete;
}
// ==========================================================================

int insertStatsIntoSqlite(  char *pDateTimeStamp, sqlite3 *db, const data_t tBandwidthUsage[], const data_t tBandwidthUsageInitValue[],
                            int iForceDailyInsertOrUpdate)
{
    //printf("#A - Processing host [%s]\n", tBandwidthUsage[0].ip);

    int iTotalRowsInserted  = 0;
    int iMaxDay_index       = GetDayIndex(start_date,end_date);
    int iDaysToProcess      = MIN(iMaxDay_index+1,MAX_RANGE);
    int bImportFromLogfile  = 0;

    // This means we are importing data from logfiles
    if(pDateTimeStamp == NULL)
    {
        bImportFromLogfile = 1;
    }

    //printf("#B - Processing host [%s]\n", tBandwidthUsage[0].ip);

    char *zErrMsg = 0;
    int iIdx = 0;
    for(iIdx = 0;
	    iIdx < MAX_CLIENTCOUNT;
		iIdx ++)
    {
        //printf("#C - Processing host [%s]\n", tBandwidthUsage[0].ip);

        const data_t *pBandwidthUsage     = &(tBandwidthUsage[iIdx]);

        if(pBandwidthUsage->ip[0] == '\0') break;

        printf("Processing host [%s]\n", pBandwidthUsage->ip);

        double first_bytes_in_total  = pBandwidthUsage->bytes_in[0];
        double first_bytes_out_total = pBandwidthUsage->bytes_out[0];

        //double bytes_in_total  = 0;
        //double bytes_out_total = 0;

        int iIdx2 = 0;
        for(iIdx2 = 0;
            iIdx2 < iDaysToProcess;
            iIdx2 ++)
        {
            double bytes_in_total  = pBandwidthUsage->bytes_in[iIdx2];
            double bytes_out_total = pBandwidthUsage->bytes_out[iIdx2];

            int iIdx3 = 0;
            for(iIdx3 = 0;
                iIdx3 < MAX_CLIENTCOUNT;
                iIdx3 ++)
            {
                const data_t *pBandwidthUsageInit = &(tBandwidthUsageInitValue[iIdx3]);

                if(strcasecmp(pBandwidthUsageInit->ip,pBandwidthUsage->ip) == 0)
                {
                    if(iIdx2 == 0)
                    {
                        // Subtract off previous days start values
                        double dInitialBytesIn  = pBandwidthUsageInit->bytes_in[0];
                        double dInitialBytesOut = pBandwidthUsageInit->bytes_out[0];

                        if(dInitialBytesIn <= first_bytes_in_total)
                        {
                            bytes_in_total  -= dInitialBytesIn;
                        }
                        if(dInitialBytesOut <= first_bytes_out_total)
                        {
                            bytes_out_total -= dInitialBytesOut;
                        }
                    }

                    break;
                }
            }

            char szNewStamp[50]="";
            // This means we are importing data from logfiles
            if(bImportFromLogfile == 1)
            {
                struct tm logInfoDate = modifyDateValueByDays(start_date, iIdx2);
                //strftime (szNewStamp, 1023, "%Y-%m-%d %H:%M:%S", &logInfoDate);
                strftime (szNewStamp, 49, "%Y-%m-%d 00:00:00", &logInfoDate);
                pDateTimeStamp = &(szNewStamp[0]);
            }

            char szSQL[1024]="";
            sprintf(szSQL,"INSERT INTO %s (log_datetime,host,in_mb,out_mb,mac) values('%s','%s',%f,%f,'');",
                        BANDWIDTH_USAGE_TABLENAME,
                        pDateTimeStamp,
                        pBandwidthUsage->ip,
                        bytes_in_total,
                        bytes_out_total);

            if(bImportFromLogfile == 1)
            {
                // Logfiles were stored in MB but sqlite DB is stored in bytes
                if(chBytesMeasurementUnitType == 'M')
                {
                    bytes_in_total  = ConvertMBytesToNumber(bytes_in_total,  'B');
                    bytes_out_total = ConvertMBytesToNumber(bytes_out_total, 'B');
                }
                sprintf(szSQL,"INSERT INTO %s (log_datetime,host,in_mb,out_mb,mac) SELECT '%s','%s',%f,%f,'' WHERE (SELECT COUNT(*) FROM %s WHERE host='%s' AND date(log_datetime)=date('%s')) = 0;",
                            BANDWIDTH_USAGE_TABLENAME,
                            pDateTimeStamp,
                            pBandwidthUsage->ip,
                            bytes_in_total,
                            bytes_out_total,
                            BANDWIDTH_USAGE_TABLENAME,
                            pBandwidthUsage->ip,
                            pDateTimeStamp);
            }
            else if(iForceDailyInsertOrUpdate == 1)
            {
                sprintf(szSQL,"INSERT INTO %s (log_datetime,host,in_mb,out_mb,mac) SELECT '%s','%s',%f,%f,'' WHERE (SELECT COUNT(*) FROM %s WHERE host='%s' AND date(log_datetime)=date('%s')) = 0;",
                            BANDWIDTH_USAGE_TABLENAME,
                            pDateTimeStamp,
                            pBandwidthUsage->ip,
                            bytes_in_total,
                            bytes_out_total,
                            BANDWIDTH_USAGE_TABLENAME,
                            pBandwidthUsage->ip,
                            pDateTimeStamp);
            }

            printf("Trying to Insert SQL [%s]\n", szSQL);
            fflush(stdout);
            //fflush(stdout);

            int rc = sqlite3_exec(db, szSQL, callback, 0, &zErrMsg);
            if( rc != SQLITE_OK )
            {
                printf("SQL error: %s, line: %d\n", zErrMsg,__LINE__);
                //fflush(stderr);
                //fflush(stdout);

                sqlite3_free(zErrMsg);

                break;
            }

            int iChangeCount    = sqlite3_changes(db);

            if(iChangeCount == 0 && iForceDailyInsertOrUpdate == 1)
            {
                sprintf(szSQL,"UPDATE %s SET in_mb=in_mb+%f, out_mb=out_mb+%f WHERE host='%s' AND date(log_datetime)=date('%s');",
                            BANDWIDTH_USAGE_TABLENAME,
                            bytes_in_total,
                            bytes_out_total,
                            pBandwidthUsage->ip,
                            pDateTimeStamp);

                printf("Trying to Update SQL [%s]\n", szSQL);
                fflush(stdout);
                //fflush(stdout);

                rc = sqlite3_exec(db, szSQL, callback, 0, &zErrMsg);
                if( rc != SQLITE_OK )
                {
                    printf("SQL error: %s, line: %d\n", zErrMsg,__LINE__);
                    //fflush(stderr);
                    //fflush(stdout);

                    sqlite3_free(zErrMsg);

                    break;
                }

                iChangeCount    = sqlite3_changes(db);
            }

            iTotalRowsInserted += iChangeCount;
        }
    }

    return iTotalRowsInserted;
}
// ==========================================================================

int mainInsertLatestStats(int argc, char **argv)
{
    sqlite3 *db=0;
    int rc=0;

    if( argc < 3 )
    {
        printf("Usage: %s 2 DATABASE GET-STATS-CMD CLEAR-STATS-CMD\n", argv[0]);
        exit(1);
    }

    time_t tStartProcessing = time(NULL);

    char szDB[1024] = "";
    CheckForParameter(APP_FLAG_DB, argc, argv,  szDB, 2);
    rc = sqlite3_open(szDB, &db);
    if( rc )
    {
        printf("Can't open database: [%s] line: %d\n", sqlite3_errmsg(db),__LINE__);
        sqlite3_close(db);
        exit(1);
    }

    char szBuffer[1024]="";
    sprintf(szBuffer,"iptables -L %s -vnx",DEFAULT_IPTABLE_TRAFFIC_NAME);
    char *pGetStatsCmd = szBuffer;

    char szGetStats[1024]="";
    if(CheckForParameter(APP_FLAG_GET_STATS_CMD, argc, argv,  szGetStats, 3) >= 0)
    {
        pGetStatsCmd = szGetStats;
    }

    printf("Executing get stats command [%s]\n", pGetStatsCmd);

    FILE *fp = popen(pGetStatsCmd, "r");
    if(fp == NULL)
    {
        sqlite3_close(db);
        printf("Can't open pipe for get stats command [%s] line: %d\n", pGetStatsCmd,__LINE__);
        exit(1);
    }

    data_t tBandwidthUsage[MAX_CLIENTCOUNT];
    data_t tBandwidthUsageInitValue[MAX_CLIENTCOUNT];
    //memset(&tBandwidthUsage[0],0,sizeof(data_t) * MAX_CLIENTCOUNT);
    //memset(&tBandwidthUsageInitValue[0],0,sizeof(data_t) * MAX_CLIENTCOUNT);
    resetList(tBandwidthUsage,MAX_CLIENTCOUNT);
    resetList(tBandwidthUsageInitValue,MAX_CLIENTCOUNT);

    //printf("#1 - Processing host [%s]\n", tBandwidthUsage[0].ip);

    char szLogFile[50]="";
    time_t now = time ( NULL );
    struct tm *tm_now = localtime ( &now );
    if(!tm_now)
    {
        char szBuf[100]="";
        sprintf(szBuf,"#2 [%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,now);
        perror(szBuf);
    }

    strftime (szLogFile, 49, "%Y%m%d%H%M%S", tm_now);
    ProcessIPTablesOutput(fp, szLogFile, tBandwidthUsage,UNKNOWN_DATA, 0);
    pclose(fp);

    //printf("#2 - Processing host [%s]\n", tBandwidthUsage[0].ip);

    memset(szBuffer,0,1024);
    sprintf(szBuffer,"iptables -Z %s",DEFAULT_IPTABLE_TRAFFIC_NAME);
    char *pClearStatsCmd = szBuffer;

    char szClearStats[1024]="";
    if(CheckForParameter(APP_FLAG_CLEAR_STATS_CMD, argc, argv,  szClearStats, 4) >= 0)
    {
        pClearStatsCmd = szClearStats;
    }

    printf("Executing clear stats command [%s]\n", pClearStatsCmd);

    fp = popen(pClearStatsCmd, "r");
    if(fp == NULL)
    {
        sqlite3_close(db);
        printf("Can't open pipe for clear stats command [%s] line: %d\n", pClearStatsCmd,__LINE__);
        exit(1);
    }
    pclose(fp);

    //printf("#3 - Processing host [%s]\n", tBandwidthUsage[0].ip);

    char szNowStamp[50]="";
    strftime (szNowStamp, 49, "%Y-%m-%d %H:%M:%S", tm_now);

    int bImportStatsDailyOnly = IsParameterEnabled(APP_FLAG_IMPORT_DAILY_ONLY, argc, argv);

    //printf("#4 - Processing host [%s]\n", tBandwidthUsage[0].ip);

	int iTotalRowsInserted = insertStatsIntoSqlite(szNowStamp, db, tBandwidthUsage, tBandwidthUsageInitValue,bImportStatsDailyOnly);
    sqlite3_close(db);

    printf("*NOTE: Total rows inserted = %d\n", iTotalRowsInserted);

    int iGenerateType = GENERATE_TYPE_SQLITE_IMPORT;
    DisplayResults(iGenerateType, tBandwidthUsage, tBandwidthUsageInitValue, tStartProcessing, NULL, NULL, NULL);

    return 0;
}
// ==========================================================================

int importLogfileDataIntoSqlite(char *pDB, data_t tBandwidthUsage[], data_t tBandwidthUsageInitValue[])
{
    sqlite3 *db=0;
    int rc=0;

    rc = sqlite3_open(pDB, &db);
    if( rc )
    {
        printf("Can't open database: [%s] line: %d\n", sqlite3_errmsg(db),__LINE__);
        sqlite3_close(db);
        exit(1);
    }

	int iTotalRowsInserted = insertStatsIntoSqlite(NULL, db, tBandwidthUsage, tBandwidthUsageInitValue,0);
    sqlite3_close(db);

    printf("*NOTE: Total rows inserted = %d\n", iTotalRowsInserted);

    return iTotalRowsInserted;
}

#endif

void ProcessLogFile(char *szLogPath, char *szLogFile, data_t tBandwidthUsage[],int iDirection, int iLogFileIsOnANewDate, char *pDebugFilePath)
{
    FILE *f                     = 0;
    char szFullFilePath[1024]   = "";
    if(pDebugFilePath != NULL)
    {
        sprintf(szFullFilePath,"%s",pDebugFilePath);
    }
    else
    {
        sprintf(szFullFilePath,"%s%s",szLogPath,szLogFile);
    }

#ifdef DEBUG_CODE
    printf("Processing file [%s]\n",szFullFilePath);
#endif

    if ((f = fopen(szFullFilePath, "r")) != NULL)
    {
        ProcessIPTablesOutput(f, szLogFile, tBandwidthUsage,iDirection, iLogFileIsOnANewDate);
        fclose(f);
    }
}
// ==========================================================================

void ProcessTrafficByDirectionFlow( char *szLogPath,
                                    char *szLogFilePrefix,
                                    int iDirection,
                                    char *szLogFileFilterStart,
                                    char *szLogFileFilterEnd,
                                    data_t tBandwidthUsage[],
                                    data_t tBandwidthUsageInitValue[])
{
    DIR *dp = opendir(szLogPath);
    if (dp != NULL)
    {
        char szStartFile[1024]  = "";
        char szEndFile[1024]    = "";

        sprintf(szStartFile,"%s%s",szLogFilePrefix,szLogFileFilterStart);
        sprintf(szEndFile,"%s%s",szLogFilePrefix,szLogFileFilterEnd);

        char szStartFileDate[9]  = "";
        int iStartFileNameLen    = strlen(szLogFileFilterStart);
        memcpy(szStartFileDate,&(szLogFileFilterStart[iStartFileNameLen-8]),8);
        uint64_t uiStartFileDate = atoi(szStartFileDate);

        char szEndFileDate[9]  = "";
        int iEndFileNameLen    = strlen(szLogFileFilterEnd);
        memcpy(szEndFileDate,&(szLogFileFilterEnd[iEndFileNameLen-8]),8);
        uint64_t uiEndFileDate = atoi(szEndFileDate);

        int iMaxDateRangeIndex = GetDayIndex(uiStartFileDate, uiEndFileDate);

        int iLastDateRangeIndex = -1;

        int bFoundAndUsedPreviousFile   = 0;
        char szInitFile[1024]           = "";
        struct dirent *ep               = 0;

        uint64_t uiFirstMatchedFileDate = 0;
        uint64_t uiLastMatchedFileDate  = 0;

    	while ((ep = readdir(dp)))
        {
            // Does the file match the initial filter prefix
            if(strncasecmp(ep->d_name,szLogFilePrefix,strlen(szLogFilePrefix)) == 0)
            {
                char szFileDate[9]  = "";
                int iFileNameLen    = strlen(ep->d_name);
                memcpy(szFileDate,&ep->d_name[iFileNameLen-14],8);
                uint64_t uiFileDate = atoi(szFileDate);

                int iFoundStartFileFilter   = (strncasecmp(ep->d_name,szStartFile,strlen(szStartFile)) == 0);
                int iFoundEndFileFilter     = (strncasecmp(ep->d_name,szEndFile,strlen(szEndFile)) == 0);

                if(uiFirstMatchedFileDate == 0 && iFoundStartFileFilter)
                {
                    uiFirstMatchedFileDate = uiFileDate;
                }
                if(uiLastMatchedFileDate == 0 && iFoundEndFileFilter)
                {
                    uiLastMatchedFileDate = uiFileDate;
                }

                int iTestInDateRange = GetDayIndex(uiStartFileDate, uiFileDate);

                int iIncludeFileInCalcs     = (iFoundStartFileFilter || iFoundEndFileFilter);
                if(!iIncludeFileInCalcs)
                {
                    iIncludeFileInCalcs = (uiFirstMatchedFileDate > 0 && uiFileDate >= uiFirstMatchedFileDate && uiLastMatchedFileDate == 0);

                    if(!iIncludeFileInCalcs)
                    {
                        iIncludeFileInCalcs = (iTestInDateRange >= 0 && iTestInDateRange <= iMaxDateRangeIndex && uiFileDate >= uiStartFileDate);

#ifdef DEBUG_CODE
                        if(iIncludeFileInCalcs)
                        {
                            int ii = 0;
                        }
#endif
                    }
                }

                if(iIncludeFileInCalcs)
                {
                    ProcessLogFile(szLogPath, ep->d_name, tBandwidthUsage,iDirection,(iLastDateRangeIndex != iTestInDateRange),NULL);
                    iLastDateRangeIndex = iTestInDateRange;

                    if(bFoundAndUsedPreviousFile == 0)
                    {
                        bFoundAndUsedPreviousFile = 1;

                        if(strlen(szInitFile) > 0)
                        {
                            ProcessLogFile(szLogPath, szInitFile, tBandwidthUsageInitValue,iDirection,1,NULL);
                        }
                    }
                }
                else if(bFoundAndUsedPreviousFile == 0)
                {
                    strcpy(szInitFile,ep->d_name);
                }
            }
        }

        (void) closedir(dp);
    }
    else
    {
        perror("Couldn't open the directory");
    }
}
// ==========================================================================

void ProcessSpecialDateParameter(char *pLogFileFilter, int bNeedSeparators)
{
    time_t now = time ( NULL );
    struct tm *tm_now = localtime ( &now );
    if(!tm_now)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,now);
        perror(szBuf);
    }

    if(strlen(pLogFileFilter) == 0)
    {
        if(bNeedSeparators)
        {
            strftime (pLogFileFilter, 1023, "%Y-%m-%d", tm_now);
        }
        else
        {
            strftime (pLogFileFilter, 1023, "%Y%m%d", tm_now);
        }
    }
    else if(strstr(pLogFileFilter,TODAYS_DATE_KEYWORD) != NULL)
    {
        char *pNumericValue = strstr(pLogFileFilter,"-");
        if(pNumericValue != NULL)
        {
            pNumericValue++;
            int iDecrementValue = atoi(pNumericValue);
            tm_now->tm_mday     -= iDecrementValue;
            time_t tNewDate     = mktime(tm_now);
            if(tNewDate < 0)
            {
                char szBuf[100]="";
                sprintf(szBuf,"Invalid logfilter specified [%s] returned: [%ld]",pLogFileFilter,tNewDate);
                perror(szBuf);
            }

            tm_now = localtime ( &tNewDate );
			if(!tm_now)
			{
				char szBuf[100]="";
				sprintf(szBuf,"#2 [%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,tNewDate);
				perror(szBuf);
			}

        }

        if(bNeedSeparators)
        {
            strftime (pLogFileFilter, 1023, "%Y-%m-%d", tm_now);
        }
        else
        {
            strftime (pLogFileFilter, 1023, "%Y%m%d", tm_now);
        }
    }
    else if(strncasecmp(pLogFileFilter,MONTHLY_PERIOD_STARTDATE_KEYWORD,strlen(MONTHLY_PERIOD_STARTDATE_KEYWORD)) == 0)
    {
        int iMonthStartDay = atoi(&(pLogFileFilter[strlen(MONTHLY_PERIOD_STARTDATE_KEYWORD)]));

        if(iMonthStartDay > tm_now->tm_mday)
        {
            tm_now->tm_mon--;
            tm_now->tm_mday = iMonthStartDay;
        }
        else if(iMonthStartDay < tm_now->tm_mday)
        {
            tm_now->tm_mday = iMonthStartDay;
        }
        time_t tNewDate     = mktime(tm_now);
        if(tNewDate < 0)
        {
            char szBuf[100]="";
            sprintf(szBuf,"Invalid logfilter specified [%s] returned: [%ld]",pLogFileFilter,tNewDate);
            perror(szBuf);
        }

        tm_now = localtime ( &tNewDate );
		if(!tm_now)
		{
		    char szBuf[100]="";
		    sprintf(szBuf,"#3 [%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,tNewDate);
		    perror(szBuf);
		}

        if(bNeedSeparators)
        {
            strftime (pLogFileFilter, 1023, "%Y-%m-%d", tm_now);
        }
        else
        {
            strftime (pLogFileFilter, 1023, "%Y%m%d", tm_now);
        }
    }
}
// ==========================================================================

int AutoSetupBandwidthMonitoring(data_t tBandwidthUsage[], const char *pIPTableName)
{
    int iHostsDetected = 0;
    char szCmd[1024] = "nvram get dhcpd_static";

    printf("Executing get static hosts command [%s]\n", szCmd);

    FILE *fp = popen(szCmd, "r");
    if(fp == NULL)
    {
        printf("Can't open pipe for get static hosts command [%s] line: %d\n", szCmd,__LINE__);
        exit(1);
    }

    int iBufferIndex = 0;
    char szBuffer[1024]="";
    memset(szBuffer,0,1024);

    while(feof(fp) == 0)
    {

#ifndef __WINDOWS__
        char ch = fgetc_unlocked(fp);
#else
        char ch = fgetc(fp);
#endif

#ifdef DEBUG_CODE
        printf("CHAR [%c]\n", ch);
#endif

        if(ch > 0)
        {
            szBuffer[iBufferIndex] = ch;

            if(ch == '>')
            {
                data_t *pBandwidthUsage = &(tBandwidthUsage[iHostsDetected]);

                char *pFoundDelimiter1 = strchr(szBuffer,'<');
                if(pFoundDelimiter1 != NULL)
                {
                    char *pFoundDelimiter2 = strchr(pFoundDelimiter1+1,'<');
                    if(pFoundDelimiter2 != NULL)
                    {
                        int iLen = (pFoundDelimiter2 - pFoundDelimiter1 - 1);

                        strncpy(pBandwidthUsage->ip,pFoundDelimiter1+1,iLen);
                        pBandwidthUsage->ip[iLen] = '\0';
                        iHostsDetected++;

                        printf("Found host [%s]\n", pBandwidthUsage->ip);
                    }
                }

                iBufferIndex = 0;
                memset(szBuffer,0,1024);
            }
            else
            {
                iBufferIndex++;
            }
        }
    }
    pclose(fp);

    fprintf(stdout, "Total Static DHCP hosts found in firmware [%d]\n", iHostsDetected);

    if(iHostsDetected > 0)
    {
        /*
        iptables -N traffic_all
        iptables -I FORWARD 3 -j traffic_all
        iptables -A traffic_all -d 192.168.0.100
        iptables -A traffic_all -s 192.168.0.100
        */

        memset(szCmd,0,1024);
        sprintf(szCmd,"iptables -L %s -vnx 2>&1",pIPTableName);
        fp = popen(szCmd, "r");
        if(fp == NULL)
        {
            printf("Can't open pipe for get static hosts command [%s] line: %d\n", szCmd,__LINE__);
            exit(1);
        }

        data_t tBandwidthUsageInitValue[MAX_CLIENTCOUNT];
        //memset(&tBandwidthUsageInitValue[0],0,sizeof(data_t) * MAX_CLIENTCOUNT);
        resetList(tBandwidthUsageInitValue,MAX_CLIENTCOUNT);

        char szLogFile[50]="";
        time_t now = time ( NULL );
        struct tm *tm_now = localtime ( &now );
		if(!tm_now)
		{
		    char szBuf[100]="";
		    sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,now);
		    perror(szBuf);
		}

        strftime (szLogFile, 49, "%Y%m%d%H%M%S", tm_now);
        int iExistingHostsFound = ProcessIPTablesOutput(fp, szLogFile, tBandwidthUsageInitValue,UNKNOWN_DATA, 0);
        pclose(fp);

#ifdef DEBUG_CODE
        printf("szCmd = [%s] szBuffer = [%s]\n", szCmd, szBuffer);
#endif

        if(iExistingHostsFound == 0)
        {
            memset(szCmd,0,1024);
            sprintf(szCmd,"iptables -N %s 2>&1",pIPTableName);
            fp = popen(szCmd, "r");
            if(fp == NULL)
            {
                printf("Can't open pipe for get static hosts command [%s] line: %d\n", szCmd,__LINE__);
                exit(1);
            }

            memset(szBuffer,0,1024);
            fgets(szBuffer,1023,fp);
            pclose(fp);

#ifdef DEBUG_CODE
            printf("szCmd = [%s] szBuffer = [%s]\n", szCmd, szBuffer);
#endif

            memset(szCmd,0,1024);
            sprintf(szCmd,"iptables -I FORWARD 1 -j %s 2>&1",pIPTableName);
            fp = popen(szCmd, "r");
            if(fp == NULL)
            {
                printf("Can't open pipe for get static hosts command [%s]\n", szCmd);
                exit(1);
            }
            memset(szBuffer,0,1024);
            fgets(szBuffer,1023,fp);
            pclose(fp);

#ifdef DEBUG_CODE
            printf("szCmd = [%s] szBuffer = [%s]\n", szCmd, szBuffer);
#endif
        }

        int iIdx = 0;
        for(iIdx = 0;
            iIdx < MIN(iHostsDetected,MAX_CLIENTCOUNT);
            iIdx ++)
        {
            data_t *pBandwidthUsage     = &(tBandwidthUsage[iIdx]);
            if(pBandwidthUsage->ip[0] == '\0') break;

#ifdef DEBUG_CODE
            printf("Checking to see if we should add rule for host = [%s]\n", pBandwidthUsage->ip);
#endif

            int bAddNewHostRule = 1;

            int iIdx2 = 0;
            for(iIdx2 = 0;
                iIdx2 < MIN(iExistingHostsFound,MAX_CLIENTCOUNT);
                iIdx2 ++)
            {
                data_t *pBandwidthUsageInit = &(tBandwidthUsageInitValue[iIdx2]);
                if(pBandwidthUsageInit->ip[0] == '\0') break;

#ifdef DEBUG_CODE
                printf("Checking to see if host [%s] = [%s]\n", pBandwidthUsage->ip,pBandwidthUsageInit->ip);
#endif

                if(strcasecmp(pBandwidthUsage->ip,pBandwidthUsageInit->ip) == 0)
                {
                    bAddNewHostRule = 0;
                    break;
                }
            }

            if(bAddNewHostRule == 1)
            {
                fprintf(stdout, "Adding new bandwidth capture rule for host [%s]\n", pBandwidthUsage->ip);

                memset(szCmd,0,1024);
                sprintf(szCmd,"iptables -A %s -d %s 2>&1",pIPTableName,pBandwidthUsage->ip);
                fp = popen(szCmd, "r");
                if(fp == NULL)
                {
                    printf("Can't open pipe for get static hosts command [%s] line: %d\n", szCmd, __LINE__);
                    exit(1);
                }
                pclose(fp);

                memset(szCmd,0,1024);
                sprintf(szCmd,"iptables -A %s -s %s 2>&1",pIPTableName,pBandwidthUsage->ip);
                fp = popen(szCmd, "r");
                if(fp == NULL)
                {
                    printf("Can't open pipe for get static hosts command [%s] line: %d\n", szCmd,__LINE__);
                    exit(1);
                }
                pclose(fp);
            }
        }
    }

    return iHostsDetected;
}

int compressDataIntoDailyStats(char *szLogFileStartFilter,char *szLogFileEndFilter,char *szDatabase,
                                data_t tBandwidthUsage[], data_t tBandwidthUsageInitValue[], int iGenerateType)
{
    printf("Using filter date-range [%s] - [%s]\n",szLogFileStartFilter,szLogFileEndFilter);
    fflush(stdout);

    // Step #1 generate daily stats from existing intra-day records for specified date range
    mainSelectStatsByCriteria(szDatabase,szLogFileStartFilter,szLogFileEndFilter,tBandwidthUsage,tBandwidthUsageInitValue,iGenerateType, NULL,NULL);

    printf("About to delete records for date-range [%llu] - [%llu]\n",(long long unsigned int)start_date,(long long unsigned int)end_date);
    fflush(stdout);

    // Step #2 delete old records for specified date range
    int iDeleteRows = deleteStatsFromSqlite(szDatabase, start_date, end_date);

    printf("About to insert compressed records for date-range [%llu] - [%llu]\n",(long long unsigned int)start_date,(long long unsigned int)end_date);
    fflush(stdout);

    // Step #3 insert new daily stats into database for specified date range
    int iInsertedRows = importLogfileDataIntoSqlite(szDatabase, tBandwidthUsage, tBandwidthUsageInitValue);

    printf("Total old rows deleted: [%d]\n", iDeleteRows);
    printf("Total new rows added:   [%d]\n", iInsertedRows);
    fflush(stdout);

    return iInsertedRows;
}

int insertDnsStatIntoSqlite(  sqlite3 *db, const dns_usage tData, char **pCombineWebsiteList,int pCombineWebsiteListSize)
{
    int iTotalRowsInserted  = 0;

    char *zErrMsg = 0;
    const dns_usage *pData     = &tData;

    if(pData->sourceip[0] != '\0')
    {
        printf("Processing host [%s]\n", pData->sourceip);
        fflush(stdout);

        char szNewDateTimeStamp[50]="";
        struct tm *infoDateTime = localtime ( &pData->datetimestamp);
        strftime (szNewDateTimeStamp, 49, "%Y-%m-%d %T", infoDateTime);

        char szNewDateStamp[50]="";
        struct tm *infoDate = localtime ( &pData->datetimestamp);
        strftime (szNewDateStamp, 49, "%Y-%m-%d 00:00:00", infoDate);

        char *combineWebsiteKey=NULL;
        if(pCombineWebsiteList != NULL)
        {
            if(pCombineWebsiteList != NULL) {
                int arrayLen = pCombineWebsiteListSize;
                int idx = 0;
                for(idx = 0; idx < arrayLen; ++idx)
                {
                    char *combineWebsite = pCombineWebsiteList[idx];
                    // Check for wilcard for the prefix portion of website name
                    // example *facebook.com
                    char *match = strstr(tData.website,combineWebsite);

                    if(match)
                    {
                        printf("\n================= [%d] Looking for [%s] in [%s] result [%s]\n\n",arrayLen,combineWebsite,tData.website,match);
                    }

                    if(match != NULL && strcasecmp(match,combineWebsite) == 0)
                    {
                        combineWebsiteKey = combineWebsite;
                        break;
                    }
                }
            }
        }

        int iChangeCount    = 0;
        char szSQL[1024]="";
        if(combineWebsiteKey != NULL)
        {
            sprintf(szSQL,"UPDATE %s SET timestamp='%s' WHERE website LIKE '%%%s' AND host='%s' AND datestamp='%s' AND datetime(timestamp) < '%s';",
                        DNS_USAGE_TABLENAME,
                        szNewDateTimeStamp,
                        combineWebsiteKey,
                        pData->sourceip,
                        szNewDateStamp,
                        szNewDateTimeStamp);
        }
        else
        {
            sprintf(szSQL,"UPDATE %s SET timestamp='%s' WHERE website='%s' AND host='%s' AND datestamp='%s' AND datetime(timestamp) < '%s';",
                        DNS_USAGE_TABLENAME,
                        szNewDateTimeStamp,
                        pData->website,
                        pData->sourceip,
                        szNewDateStamp,
                        szNewDateTimeStamp);
        }

        printf("Trying to Update SQL [%s]\n", szSQL);
        fflush(stdout);

        int rc = sqlite3_exec(db, szSQL, callback, 0, &zErrMsg);
        if( rc != SQLITE_OK )
        {
            printf("SQL error: %s, line: %d\n", zErrMsg,__LINE__);
            fflush(stdout);

            sqlite3_free(zErrMsg);
        }
        else
        {
            iChangeCount    = sqlite3_changes(db);
        }

        // DO NOT have this combination in the DB, insert it
        if(iChangeCount == 0)
        {
            if(combineWebsiteKey != NULL)
            {
                sprintf(szSQL,"INSERT INTO %s (timestamp,website,host,datestamp) SELECT '%s','%s','%s','%s' WHERE (SELECT COUNT(*) FROM %s WHERE website LIKE '%%%s' AND host='%s' AND datestamp='%s') = 0;",
                            DNS_USAGE_TABLENAME,
                            szNewDateTimeStamp,
                            combineWebsiteKey,
                            pData->sourceip,
                            szNewDateStamp,
                            DNS_USAGE_TABLENAME,
                            combineWebsiteKey,
                            pData->sourceip,
                            szNewDateStamp);

            }
            else
            {
                sprintf(szSQL,"INSERT INTO %s (timestamp,website,host,datestamp) values('%s','%s','%s','%s');",
                            DNS_USAGE_TABLENAME,
                            szNewDateTimeStamp,
                            pData->website,
                            pData->sourceip,
                            szNewDateStamp);
            }

            printf("Trying to Insert SQL [%s]\n", szSQL);
            fflush(stdout);

            rc = sqlite3_exec(db, szSQL, callback, 0, &zErrMsg);
            if( rc != SQLITE_OK )
            {
                printf("SQL error: %s, line: %d\n", zErrMsg,__LINE__);
                fflush(stdout);

                sqlite3_free(zErrMsg);
            }
            else
            {
                iChangeCount    = sqlite3_changes(db);
            }
        }

        iTotalRowsInserted += iChangeCount;
    }

    return iTotalRowsInserted;
}
// ==========================================================================

//Nov 20 22:50:36 wrt daemon.debug dnsmasq[149]: query[A] www.mytestcom.net from 192.168.0.107
int ProcessDNSQueryOutput(FILE *f, char *pDB,
                          char **dnsIgnoreList, int dnsIgnoreListSize,
                          char **dnsIgnoreWebsiteList, int dnsIgnoreWebsiteListSize,
                          char **dnsCombineWebsiteList, int dnsCombineWebsiteListSize)
{
    int iHostsFound = 0;

    sqlite3 *db=0;
    int rc=sqlite3_open(pDB, &db);
    if( rc )
    {
        printf("Can't open database: [%s] line: %d\n", sqlite3_errmsg(db),__LINE__);
        sqlite3_close(db);
        exit(1);
    }

    char szBuf[1024] = "";
    while(feof(f) == 0)
    {
        szBuf[0] = '\0';
        fgets(szBuf,1023,f);

        static const char *QUERY_TAG = "query[";
        char *foundDNSQuery = strstr(szBuf,QUERY_TAG);
        if(foundDNSQuery != NULL)
        {
#ifdef DEBUG_CODE
            printf("Found dns query [%s]\n", szBuf);
            fflush(stdout);
#endif
            dns_usage tData;
            memset(&tData,0,sizeof(dns_usage));

            // Nov 20 22:50:43 wrt daemon.debug dnsmasq[149]: query[A] ad.doubleclick.net from 192.168.0.126
            char szMonth[10]="";
            int dayOfMonth=-1;
            char szTime[10]="";
            int iCount = sscanf(szBuf, "%s %d %s",szMonth,&dayOfMonth,szTime);
            if(iCount == 3)
            {
                tData.datetimestamp = getDateTimeValue(szMonth,dayOfMonth,szTime);
                tData.datestamp = getDateTimeValue(szMonth,dayOfMonth,NULL);

                iCount = sscanf(foundDNSQuery, "%*s %s %*s %s",tData.website,tData.sourceip);
                if(iCount == 2)
                {
#ifdef DEBUG_CODE
                    printf("Looking for ignore hosts for dns query [%s]\n", szBuf);
                    fflush(stdout);
#endif
                    int ignoreHostFlag = 0;
                    // Now check if we should ignore this host
                    if(dnsIgnoreList != NULL) {
                        int arrayLen = dnsIgnoreListSize;
                        int idx = 0;
                        for(idx = 0; idx < arrayLen; ++idx)
                        {
                            char *ignoreHost = dnsIgnoreList[idx];
                            if(strcasecmp(tData.sourceip,ignoreHost) == 0)
                            {
                                ignoreHostFlag = 1;
                                break;
                            }
                        }
                    }
#ifdef DEBUG_CODE
                    printf("Looking for ignore websites for dns query [%s]\n", szBuf);
#endif
                    // Now check if we should ignore this host
                    if(ignoreHostFlag == 0 && dnsIgnoreWebsiteList != NULL) {
                        int arrayLen = dnsIgnoreWebsiteListSize;
                        int idx = 0;
                        for(idx = 0; idx < arrayLen; ++idx)
                        {
                            char *ignoreWebsite = dnsIgnoreWebsiteList[idx];
                            // Check for wilcard for the prefix portion of website name
                            // example *facebook.com
                            if(ignoreWebsite[0] == '*' && strlen(ignoreWebsite) > 1)
                            {
                                char *match = strstr(tData.website,&ignoreWebsite[1]);
                                if(match != NULL && strcasecmp(match,&ignoreWebsite[1]) == 0)
                                {
                                    ignoreHostFlag = 1;
                                    break;
                                }
                            }
                            else if(strcasecmp(tData.website,ignoreWebsite) == 0)
                            {
                                ignoreHostFlag = 1;
                                break;
                            }
                        }
                    }

                    if(ignoreHostFlag == 0)
                    {
#ifdef DEBUG_CODE
                        printf("About to insert dns query [%s]\n", szBuf);
                        fflush(stdout);
#endif
                        iHostsFound += insertDnsStatIntoSqlite(db, tData, dnsCombineWebsiteList,dnsCombineWebsiteListSize);
                    }
                }
            }
        }
    }

    sqlite3_close(db);
    printf("*NOTE: Total rows inserted = %d\n", iHostsFound);
    fflush(stdout);

    return iHostsFound;
}
// ==========================================================================

void mainSelectDnsStatsByCriteria( char *pDB,
                                   char *szLogFileFilterStart,
                                   char *szLogFileFilterEnd,
                                   int iGenerateType,
                                   ipt_display_flags *pDisplayFlags,
                                   time_t tStartProcessing,
                                   char *customSortOrder,
                                   char *customFilter)
{
    sqlite3 *db=0;
    char *zErrMsg = 0;
    int rc=sqlite3_open(pDB, &db);
    if( rc )
    {
        printf("Can't open database: [%s] line: %d\n", sqlite3_errmsg(db),__LINE__);
        fflush(stdout);

        sqlite3_close(db);
        exit(1);
    }

    int bUseDateOnly = 1;

    char szFilterStart[18]="";
    memcpy(&(szFilterStart[0]),&(szLogFileFilterStart[0]),4);
    szFilterStart[4] = '-';
    memcpy(&(szFilterStart[5]),&(szLogFileFilterStart[4]),2);
    szFilterStart[7] = '-';
    memcpy(&(szFilterStart[8]),&(szLogFileFilterStart[6]),2);
    szFilterStart[10] = '\0';

    if(strlen(szLogFileFilterStart) >= 14)
    {
        szFilterStart[10] = ' ';
        memcpy(&(szFilterStart[11]),&(szLogFileFilterStart[9]),5);

        if(strlen(szLogFileFilterStart) >= 17)
        {
            memcpy(&(szFilterStart[16]),&(szLogFileFilterStart[14]),3);
        }
        bUseDateOnly = 0;
    }

    char szFilterEnd[18]="";
    memcpy(&(szFilterEnd[0]),&(szLogFileFilterEnd[0]),4);
    szFilterEnd[4] = '-';
    memcpy(&(szFilterEnd[5]),&(szLogFileFilterEnd[4]),2);
    szFilterEnd[7] = '-';
    memcpy(&(szFilterEnd[8]),&(szLogFileFilterEnd[6]),2);
    szFilterEnd[10] = '\0';

    if(strlen(szLogFileFilterEnd) >= 14)
    {
        szFilterEnd[10] = ' ';
        memcpy(&(szFilterEnd[11]),&(szLogFileFilterEnd[9]),5);

        if(strlen(szLogFileFilterEnd) >= 17)
        {
            memcpy(&(szFilterEnd[16]),&(szLogFileFilterEnd[14]),3);
        }
        bUseDateOnly = 0;
    }

    start_date  = atoi(szLogFileFilterStart);
	end_date    = atoi(szLogFileFilterEnd);

    char szSQL[4096]="";

    if(customSortOrder == NULL)
    {
        customSortOrder = "timestamp";
    }

    if(bUseDateOnly == 1)
    {
        sprintf(szSQL,"SELECT host,website,timestamp FROM %s WHERE date(datestamp) BETWEEN date('%s') AND date('%s')",
                        DNS_USAGE_TABLENAME,
                        szFilterStart,
                        szFilterEnd);
    }
    else
    {
        sprintf(szSQL,"SELECT host,website,timestamp FROM %s WHERE datetime(timestamp) BETWEEN datetime('%s') AND datetime('%s')",
                        DNS_USAGE_TABLENAME,
                        szFilterStart,
                        szFilterEnd);
    }

    if(customFilter != NULL)
    {
        strcat(szSQL," AND ");
        strcat(szSQL,customFilter);
    }

    strcat(szSQL," ORDER BY ");
    strcat(szSQL,customSortOrder);
    strcat(szSQL,";");

    if(iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE)
    {
        printf("Selecting SQL [%s]\n", szSQL);
        fflush(stdout);
    }

    dns_select_cbd tCBD;
    tCBD.iGenerateType = iGenerateType;
    tCBD.pDisplayFlags = pDisplayFlags;
    tCBD.tStartProcessing = tStartProcessing;
    tCBD.iRowNumber      = 0;

    char szPageGenerated[1024]="";
    time_t now = time ( NULL );
    struct tm *tm_now = localtime ( &now );
    if(!tm_now)
    {
        char szBuf[100]="";
        sprintf(szBuf,"[%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,now);
        perror(szBuf);
    }

    strftime (szPageGenerated, 1023, "%Y-%m-%d %H:%M:%S", tm_now);

    if( iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE)
    {
        printf("=====================================================================================================\n");
        printf("Dns usage v%s from: %llu to %llu - last update: %s\n",IPT_VERSION,(long long unsigned int)start_date,(long long unsigned int)end_date, szPageGenerated);
    }
    else if(iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_HTML)
    {
        printf("\
        <html>\
        <link rel='stylesheet' type='text/css' href='tomato.css'>\
        <link rel='stylesheet' type='text/css' href='red.css'>\
        <body>\
        <b>Dns usage v%s from: %llu to %llu - last update: %s</b>\
        <table class=\"bwmg\" cellspacing=\"1\">\
        <tbody>\
        <tr class=\"header\">\
        <td class=\"rtitle\">Host</td>\
        <td class=\"dl\">Download</td>\
        <td class=\"ul\">Upload</td>\
        <td class=\"total\">Total</td>\
        </tr>",
        IPT_VERSION,
        (long long unsigned int)start_date,
        (long long unsigned int)end_date,
        szPageGenerated);
    }

    rc = sqlite3_exec(db, szSQL, callbackSELECTDNS, (void *)&tCBD, &zErrMsg);
    if( rc != SQLITE_OK )
    {
        printf("SQL error: %s, line: %d\n", zErrMsg,__LINE__);
        fflush(stdout);

        sqlite3_free(zErrMsg);
    }

    sqlite3_close(db);

    double dElapsedSeconds = difftime(time(NULL),tStartProcessing);

    if( iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE)
    {
        printf("=====================================================================================================\n");
        printf("\nElapsed time to generate stats: %.2f\n",dElapsedSeconds);
    }
    else
    {
        printf("\
        </tbody>\
        </table>\
        </html>\
        </body>\
        ");
    }
}
// ==========================================================================

int deleteDnsStatsFromSqlite(char *pDB, uint64_t startDate, uint64_t endDate)
{
    int iTotalRowsDelete = 0;

    char szStartDate[21]="";
    struct tm startDateRange = modifyDateValueByDays(startDate, 0);
    strftime (szStartDate, 20, "%Y-%m-%d", &startDateRange);
    char szEndDate[21]="";
    struct tm endDateRange = modifyDateValueByDays(endDate, 0);
    strftime (szEndDate, 20, "%Y-%m-%d", &endDateRange);

    char szSQL[1024]="";
    sprintf(szSQL,"DELETE FROM %s WHERE date(datestamp) BETWEEN date('%s') AND date('%s');",
                DNS_USAGE_TABLENAME,
                szStartDate,
                szEndDate);

    printf("Delete SQL [%s]\n", szSQL);
    fflush(stdout);

    sqlite3 *db=0;
    char *zErrMsg = 0;

    int rc = sqlite3_open(pDB, &db);
    if( rc )
    {
        printf("Can't open database: [%s] line: %d\n", sqlite3_errmsg(db),__LINE__);
        fflush(stdout);

        sqlite3_close(db);
        exit(1);
    }

    zErrMsg = 0;
    rc = sqlite3_exec(db, szSQL, callback, 0, &zErrMsg);
    if( rc != SQLITE_OK )
    {
        printf("DELETE SQL error: %s, line: %d\n", zErrMsg,__LINE__);
        fflush(stdout);

        sqlite3_free(zErrMsg);
    }

    int iChangeCount  = sqlite3_changes(db);
    iTotalRowsDelete  = iChangeCount;

    printf("Closing database...\n");
    fflush(stdout);

    sqlite3_close(db);

    int bCanShrink = CanShrinkDatabase(pDB);

    printf("Records Deleted [%d] check to shrink database = [%d]...\n", iTotalRowsDelete,bCanShrink);
    fflush(stdout);

    //if(bCanShrink)
    //{
    //    vacuumDB(pDB);
    //}

    return iTotalRowsDelete;
}
// ==========================================================================

int mainInsertDnsQueries(int argc, char **argv)
{
    if( argc < 3 )
    {
        printf("Usage: %s %d DATABASE LOGFILE\n", argv[0],GENERATE_TYPE_SQLITE_IMPORT_DNS_USAGE);
        exit(1);
    }

    char szDB[1024]              = "";
    CheckForParameter(APP_FLAG_DB, argc, argv,  szDB, 2);

    char **dNSQueryLogFileList = NULL;
    int dNSQueryLogFileListSize = CheckForParameterAs2DString(APP_FLAG_DNSLOGFILE, argc, argv, &dNSQueryLogFileList, ',', 1024, 3);

    char **dnsIgnoreList = NULL;
    int dnsIgnoreListSize = CheckForParameterAs2DString(APP_FLAG_DNSIGNOREHOSTLIST, argc, argv, &dnsIgnoreList, ',', MAX_IPADDRESS_LENGTH, -1);

    char **dnsIgnoreWebsiteList = NULL;
    int dnsIgnoreWebsiteListSize = CheckForParameterAs2DString(APP_FLAG_DNSIGNOREWEBSITELIST, argc, argv, &dnsIgnoreWebsiteList, ',', MAX_WEBSITE_LENGTH, -1);

    char **dnsCombineWebsiteList = NULL;
    int dnsCombineWebsiteListSize = CheckForParameterAs2DString(APP_FLAG_DNSCOMBINEWEBSITELIST, argc, argv, &dnsCombineWebsiteList, ',', MAX_WEBSITE_LENGTH, -1);

    int idx = 0;
    for(idx = 0; idx < dNSQueryLogFileListSize; ++idx)
    {
        char *pDNSQueryLogFile = dNSQueryLogFileList[idx];

//#ifdef DEBUG_CODE
        printf("Processing file [%s]\n",pDNSQueryLogFile);
//#endif

        FILE *f=NULL;
        if ((f = fopen(pDNSQueryLogFile, "r")) != NULL)
        {
//#ifdef DEBUG_CODE
            printf("file opened [%s]\n",pDNSQueryLogFile);
//#endif

            ProcessDNSQueryOutput(f, szDB, dnsIgnoreList, dnsIgnoreListSize,
                                           dnsIgnoreWebsiteList, dnsIgnoreWebsiteListSize,
                                           dnsCombineWebsiteList, dnsCombineWebsiteListSize);
            fclose(f);
        }
    }

#ifdef DEBUG_CODE
    printf("Cleaning up memory\n");
#endif

    CleanupMemoryFor2DString(&dNSQueryLogFileList, dNSQueryLogFileListSize);
    CleanupMemoryFor2DString(&dnsIgnoreList, dnsIgnoreListSize);
    CleanupMemoryFor2DString(&dnsIgnoreWebsiteList, dnsIgnoreWebsiteListSize);
    CleanupMemoryFor2DString(&dnsCombineWebsiteList, dnsCombineWebsiteListSize);
    return 0;
}
// ==========================================================================

int main(int argc, char *argv[])
{
    //char *pTest1 = GetHostNameOrIP("192.168.0.107");
    //char *pTest2 = GetHostNameOrIP("dlinknas");

	//char *p = js_string(nvram_safe_get("dhcpd_static"));
	//web_printf("dhcpd_static = '%s'.split('>');\n", p ? p : "");
	//free(p);

    int parameterEnabled = IsParameterEnabled(APP_FLAG_HELP, argc, argv);
    if(parameterEnabled == 1 || argc < 2)
    {
        printAppVersion();
        printf("\n");
        printf("For detailed help please visit: %s\n\n",SUPPORT_WEBSITE);
		printf("Usage: %s <required-parameters> <flags=x>\n\n",APP_FLAG_GENERATE_TYPE);

		printf(": %svalue, where value is one of the following command types:\n",APP_FLAG_GENERATE_TYPE);
		printf(":                \t  %d : console report  [based on saved logfiles].\n",GENERATE_TYPE_LOGFILE_CONSOLE);
        printf(":                \t  %d : html    report  [based on saved logfiles].\n",GENERATE_TYPE_LOGFILE_HTML);
		printf(":                \t  %d and %d required parameters: %s %s %s\n",GENERATE_TYPE_LOGFILE_CONSOLE,GENERATE_TYPE_LOGFILE_HTML,APP_FLAG_STARTDATE,APP_FLAG_ENDDATE,APP_FLAG_LOGFILE_PATH);
        printf(":                \t                               %s %s\n\n",APP_FLAG_LOGFILE_INCOMING,APP_FLAG_LOGFILE_OUTGOING);
        printf(":                \t  example: %s %s%d %s20090401 %s20090501 %susagelogs/ %straffic_in_ %straffic_out_\n\n",argv[0],APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_LOGFILE_CONSOLE,APP_FLAG_STARTDATE,APP_FLAG_ENDDATE,APP_FLAG_LOGFILE_PATH,APP_FLAG_LOGFILE_INCOMING,APP_FLAG_LOGFILE_OUTGOING);

		printf(":                \t  %d : import data     [based on sqlite db] inserts records into sqlite DB from iptables output.\n",GENERATE_TYPE_SQLITE_IMPORT);
		printf(":                \t  required parameters: %s %s %s\n",APP_FLAG_DB,APP_FLAG_GET_STATS_CMD,APP_FLAG_CLEAR_STATS_CMD);
		printf(":                \t  *NOTE: the parameters %s and %s are ONLY required if iptables was not\n",APP_FLAG_GET_STATS_CMD,APP_FLAG_CLEAR_STATS_CMD);
		printf(":                \t  previously configured using %s%d\n\n",APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SETUP_IPTABLE_USING_TOMATO_STATIC_DHCP);
		printf(":                \t  example: %s %s%d %sBANDWIDTH %s %s60\n\n",argv[0],APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SQLITE_IMPORT,APP_FLAG_DB,APP_FLAG_IMPORT_DAILY_ONLY,APP_FLAG_PURGE_OLD_RECORDS);

		printf(":                \t  %d : console report  [based on sqlite db].\n",GENERATE_TYPE_SQLITE_CONSOLE);
        printf(":                \t  %d : html    report  [based on sqlite db].\n",GENERATE_TYPE_SQLITE_HTML);
		printf(":                \t  %d : summarize data  [based on sqlite db] compresses intra-day record data into one record per day.\n",GENERATE_TYPE_SQLITE_DATACOMPRESS);
		printf(":                \t  %d, %d and %d required parameters: %s %s\n",GENERATE_TYPE_SQLITE_CONSOLE,GENERATE_TYPE_SQLITE_HTML,GENERATE_TYPE_SQLITE_DATACOMPRESS,APP_FLAG_STARTDATE,APP_FLAG_ENDDATE);
        printf(":                \t                                  %s\n\n",APP_FLAG_DB);
        printf(":                \t  example: %s %s%d ""%s20090511 22:15"" ""%s20090512 01:45"" %sBANDWIDTH\n\n",argv[0],APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SQLITE_CONSOLE,APP_FLAG_STARTDATE,APP_FLAG_ENDDATE,APP_FLAG_DB);

		printf(":                \t  %d : Use this option to auto-discover static DHCP clients when running in the Tomato firmware.\n\n",GENERATE_TYPE_SETUP_IPTABLE_USING_TOMATO_STATIC_DHCP);
		printf(":                \t  example: %s %s%d\n\n",argv[0],APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SETUP_IPTABLE_USING_TOMATO_STATIC_DHCP);

		printf(":                \t  %d : clean data      [based on sqlite db] shrink database by removing empty space.\n",GENERATE_TYPE_SHRINK_DATABASE);
		printf(":                \t  required parameters: %s\n\n",APP_FLAG_DB);
		printf(":                \t  example: %s %s%d %sBANDWIDTH %s60\n\n",argv[0],APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SHRINK_DATABASE,APP_FLAG_DB,APP_FLAG_PURGE_OLD_RECORDS);

		printf(":                \t  %d : import DNS data [based on sqlite db] inserts records into sqlite DB from dns message logfile.\n",GENERATE_TYPE_SQLITE_IMPORT_DNS_USAGE);
		printf(":                \t  required parameters: %s %s\n\n",APP_FLAG_DB,APP_FLAG_DNSLOGFILE);
		printf(":                \t  example: %s %s%d %sBANDWIDTH %s60 %s/var/log/messages,/var/log/messages.0 %s127.0.0.1 %s*facebook.com %scbc.ca\n\n",argv[0],APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SQLITE_IMPORT_DNS_USAGE,APP_FLAG_DB,APP_FLAG_PURGE_OLD_RECORDS,APP_FLAG_DNSLOGFILE,APP_FLAG_DNSIGNOREHOSTLIST,APP_FLAG_DNSIGNOREWEBSITELIST,APP_FLAG_DNSCOMBINEWEBSITELIST);

		printf(":                \t  %d  : console report  [based on sqlite db] shows dns website lookup usage via console.\n",GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE);
		printf(":                \t  %d : html    report  [based on sqlite db] shows dns website lookup usage via html.\n",GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_HTML);
		printf(":                \t  required parameters: %s\n\n",APP_FLAG_DB);
		printf(":                \t  example: %s %s%d %sBANDWIDTH %s %stoday %stoday %shost,website ""%swebsite LIKE '%%ebay%%'""\n\n",argv[0],APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE,APP_FLAG_DB,APP_FLAG_MOREHOSTINFO_APPEND,APP_FLAG_STARTDATE,APP_FLAG_ENDDATE,APP_FLAG_SORTBY,APP_FLAG_FILTERBY);

        printf(": Parameter details:\n");
        printf(": ------------------\n");
		printf(": %s\t: start date range:\n",APP_FLAG_STARTDATE);
		printf(":                    \t  example: %s20090425 and default is current date. You may also use the keyword '%s' like this: %s\n\n",APP_FLAG_STARTDATE,TODAYS_DATE_KEYWORD,TODAYS_DATE_KEYWORD);
		printf(": %s  \t: end date range:\n",APP_FLAG_STARTDATE);
		printf(":                    \t  example: %s20090426 and default is current date. You may also use the keyword '%s' like this: %s-30\n",APP_FLAG_ENDDATE,TODAYS_DATE_KEYWORD,TODAYS_DATE_KEYWORD);
		printf(":                    \t  *NOTE: for %s %d and %d you may specify a date and time value using this format: 20090425 19:45\n\n",APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SQLITE_CONSOLE,GENERATE_TYPE_SQLITE_HTML);
		printf(": %s    \t: optional path to search for the logfiles (default value is ./).\n\n",APP_FLAG_LOGFILE_PATH);
		printf(": %s\t: optional log filename prefix used to describe incoming data logs (example: traffic_in_).\n\n",APP_FLAG_LOGFILE_INCOMING);
		printf(": %s\t: optional log filename prefix used to describe outgoing data logs (example: traffic_out_).\n\n",APP_FLAG_LOGFILE_OUTGOING);
		printf(": <flags=x>     \t: optional flags containing special values depending on the %s, possible values:\n",APP_FLAG_GENERATE_TYPE);
		printf(":               \t  For %s%d or %d you may import logfile data into sqlite using:\n",APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_LOGFILE_CONSOLE,GENERATE_TYPE_LOGFILE_HTML);
		printf(":               \t      %s=BANDWIDTH\n",APP_FLAG_IMPORT_LOGFILEDATA);
		printf(":               \t      where BANDWIDTH = the sqlite database filename to import into (the table must be named %s)\n",BANDWIDTH_USAGE_TABLENAME);
		printf(":               \t      %s=html_template_file\n",APP_FLAG_USE_HTML_TEMPLATE);
		printf(":               \t      where html_template_file = a custom HTML file with special TAGS to define where to substitue values for TAGS\n");
		printf(":               \t  For %s%d, %d, %d, and %d you may output additional host information for each client using:\n",APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_LOGFILE_CONSOLE,GENERATE_TYPE_LOGFILE_HTML,GENERATE_TYPE_SQLITE_CONSOLE,GENERATE_TYPE_SQLITE_HTML);
		printf(":               \t      %s (which displays the Hostname along with the IP Address)\n",APP_FLAG_MOREHOSTINFO);
		printf(":               \t      %s (same as above except when using %s%d or %d the HTML will append Hostname to IP Address)\n",APP_FLAG_MOREHOSTINFO_APPEND,APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_LOGFILE_HTML,GENERATE_TYPE_SQLITE_HTML);
		printf(":               \t      %s (will display ONLY the Hostname without references to the IP Address)\n",APP_FLAG_HOSTNAME_ONLY);
		printf(":               \t  For %s%d you may want to keep your database smaller and import statistic summarized by date:\n",APP_FLAG_GENERATE_TYPE,GENERATE_TYPE_SQLITE_IMPORT);
		printf(":               \t      %s (this will allow you to frequently update your database but only store 1 record per Client per day)\n",APP_FLAG_IMPORT_DAILY_ONLY);
		printf(":               \t      %sx (this flag will delete record older then x days)\n",APP_FLAG_PURGE_OLD_RECORDS);
		printf(":               \t      example: %s60 (would delete records older than 60 days)\n",APP_FLAG_PURGE_OLD_RECORDS);
		printf("\n:               \t Special notes: There are some assumed limitations in this tool namely:\n");
		printf(":               \t Maximum supported client IP's = %d, Maximum supported DateRange length = %d days\n",MAX_CLIENTCOUNT,MAX_RANGE);
		printf(":               \t These limits are hard coded but may be easily changed and a new binary produced.\n");

		return -1;
    }

    parameterEnabled = IsParameterEnabled(APP_FLAG_VERSION, argc, argv);
    if(parameterEnabled == 1)
    {
        printAppVersion();
        return 0;
    }

    time_t tStartProcessing = time(NULL);

    int iGenerateType = 0;
    CheckForParameterAsInteger(APP_FLAG_GENERATE_TYPE, argc, argv, &iGenerateType, 1);

    char szLogFileStartFilter[1024]="";
    char szLogFileEndFilter[1024]="";
    char szDB[1024]="";

    data_t tBandwidthUsage[MAX_CLIENTCOUNT];
    data_t tBandwidthUsageInitValue[MAX_CLIENTCOUNT];
    //memset(&tBandwidthUsage[0],0,sizeof(data_t) * MAX_CLIENTCOUNT);
    //memset(&tBandwidthUsageInitValue[0],0,sizeof(data_t) * MAX_CLIENTCOUNT);
    resetList(tBandwidthUsage,MAX_CLIENTCOUNT);
    resetList(tBandwidthUsageInitValue,MAX_CLIENTCOUNT);

    if(iGenerateType == GENERATE_TYPE_SQLITE_IMPORT_DNS_USAGE)
    {
        printAppVersion();
        int iResult = 0;
#ifdef USE_SQLITE

        printf("calling mainInsertDnsQueries...\n");
        iResult = mainInsertDnsQueries(argc, argv);

        int iRecordsThisOldAndOlder = -1;
        int iPurgeDBIndex = CheckForParameterAsInteger(APP_FLAG_PURGE_OLD_RECORDS, argc, argv, &iRecordsThisOldAndOlder, -1);
        if(iPurgeDBIndex >= 0)
        {
            if(iRecordsThisOldAndOlder > 0)
            {
                sprintf(szLogFileStartFilter,"1970-01-01");

                sprintf(szLogFileEndFilter,"%s-%d",TODAYS_DATE_KEYWORD,iRecordsThisOldAndOlder);
                ProcessSpecialDateParameter(szLogFileEndFilter,1);

                uint64_t delete_start_date  = getDateValue(szLogFileStartFilter);
                uint64_t delete_end_date    = getDateValue(szLogFileEndFilter);

                printf("calling deleteDnsStatsFromSqlite to PURGE old records with date range [%llu] - [%llu]\n",(long long unsigned int)delete_start_date,(long long unsigned int)delete_end_date);
                fflush(stdout);

                // Step #2 delete old records for specified date range
                char szDB[1024] = "";
                CheckForParameter(APP_FLAG_DB, argc, argv, szDB, 2);

                int iDeleteRows = deleteDnsStatsFromSqlite(szDB, delete_start_date, delete_end_date);

                printf("Total old rows deleted: [%d]\n", iDeleteRows);
                fflush(stdout);
            }
        }
#endif
        return iResult;
    }
    else if(iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE ||
            iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_HTML)
    {
        if(iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE)
        {
            printAppVersion();
        }

        int iResult = 0;

        CheckForParameter(APP_FLAG_STARTDATE, argc, argv, szLogFileStartFilter, -1);
        CheckForParameter(APP_FLAG_ENDDATE, argc, argv, szLogFileEndFilter, -1);
        CheckForParameter(APP_FLAG_DB, argc, argv, szDB, -1);

        ProcessSpecialDateParameter(szLogFileStartFilter,0);
        ProcessSpecialDateParameter(szLogFileEndFilter,0);

        if(iGenerateType == GENERATE_TYPE_SQLITE_DNS_USAGE_REPORT_CONSOLE)
        {
            printf("Using filter date-range [%s] - [%s]\n",szLogFileStartFilter,szLogFileEndFilter);
        }

        char *customFilter=NULL;
        char szCustomFilter[1024]="";
        if(CheckForParameter(APP_FLAG_FILTERBY, argc, argv, szCustomFilter, -1) >= 0)
        {
            customFilter = &szCustomFilter[0];
        }

        char *customSortOrder=NULL;
        char szCustomSortOrder[1024]="";
        if(CheckForParameter(APP_FLAG_SORTBY, argc, argv, szCustomSortOrder, -1) >= 0)
        {
            customSortOrder = &szCustomSortOrder[0];
        }

#ifdef USE_SQLITE

        ipt_display_flags tDisplayFlags;
        tDisplayFlags.bShowAdditionalHostInfo         = IsParameterEnabled(APP_FLAG_MOREHOSTINFO, argc, argv);
        tDisplayFlags.bShowAdditionalHostInfoAppend   = IsParameterEnabled(APP_FLAG_MOREHOSTINFO_APPEND, argc, argv);
        tDisplayFlags.bShowHostnameOnly               = IsParameterEnabled(APP_FLAG_HOSTNAME_ONLY, argc, argv);

        mainSelectDnsStatsByCriteria(szDB,szLogFileStartFilter,szLogFileEndFilter,iGenerateType,&tDisplayFlags,tStartProcessing,customSortOrder,customFilter);
#endif
        return iResult;
    }
    else if(iGenerateType == GENERATE_TYPE_SETUP_IPTABLE_USING_TOMATO_STATIC_DHCP)
    {
        const char *pIPTableName = DEFAULT_IPTABLE_TRAFFIC_NAME;

        char szTrafficName[1024]="";
        if(CheckForParameter(APP_FLAG_TRAFFIC_NAME, argc, argv, szTrafficName, 2) >= 0)
        {
            pIPTableName = szTrafficName;
        }

        int iResult = AutoSetupBandwidthMonitoring(tBandwidthUsage,pIPTableName);
        return iResult;
    }
    else if(iGenerateType == GENERATE_TYPE_SQLITE_IMPORT)
    {
        printAppVersion();
        int iResult = 0;
        chBytesMeasurementUnitType = 'B';
#ifdef USE_SQLITE

        printf("calling mainInsertLatestStats...\n");
        iResult = mainInsertLatestStats(argc, argv);

        int bImportStatsDailyOnly = IsParameterEnabled(APP_FLAG_IMPORT_DAILY_ONLY, argc, argv);
        if(bImportStatsDailyOnly == 1)
        {
            start_date = 0;
            end_date   = 0;

            //memset(&tBandwidthUsage[0],0,sizeof(data_t) * MAX_CLIENTCOUNT);
            //memset(&tBandwidthUsageInitValue[0],0,sizeof(data_t) * MAX_CLIENTCOUNT);
            resetList(tBandwidthUsage,MAX_CLIENTCOUNT);
            resetList(tBandwidthUsageInitValue,MAX_CLIENTCOUNT);

            sprintf(szLogFileStartFilter,"%s",TODAYS_DATE_KEYWORD);
            ProcessSpecialDateParameter(szLogFileStartFilter,0);
            sprintf(szLogFileEndFilter,"%s",TODAYS_DATE_KEYWORD);
            ProcessSpecialDateParameter(szLogFileEndFilter,0);

            //printf("calling compressDataIntoDailyStats with date range [%s] - [%s]\n",szLogFileStartFilter,szLogFileEndFilter);
            //fflush(stdout);

            //iResult = compressDataIntoDailyStats(szLogFileStartFilter,szLogFileEndFilter,pDB,
            //                                     tBandwidthUsage, tBandwidthUsageInitValue, GENERATE_TYPE_SQLITE_DATACOMPRESS);
        }

        int iRecordsThisOldAndOlder = -1;
        int iPurgeDBIndex = CheckForParameterAsInteger(APP_FLAG_PURGE_OLD_RECORDS, argc, argv, &iRecordsThisOldAndOlder, -1);
        if(iPurgeDBIndex >= 0)
        {
            if(iRecordsThisOldAndOlder > 0)
            {
                sprintf(szLogFileStartFilter,"1970-01-01");

                sprintf(szLogFileEndFilter,"%s-%d",TODAYS_DATE_KEYWORD,iRecordsThisOldAndOlder);
                ProcessSpecialDateParameter(szLogFileEndFilter,1);

                uint64_t delete_start_date  = getDateValue(szLogFileStartFilter);
                uint64_t delete_end_date    = getDateValue(szLogFileEndFilter);

                printf("calling deleteStatsFromSqlite to PURGE old records with date range [%llu] - [%llu]\n",(long long unsigned int)delete_start_date,(long long unsigned int)delete_end_date);
                fflush(stdout);

                // Step #2 delete old records for specified date range
                char szDB[1024] = "";
                CheckForParameter(APP_FLAG_DB, argc, argv,  szDB, 2);

                int iDeleteRows = deleteStatsFromSqlite(szDB, delete_start_date, delete_end_date);

                printf("Total old rows deleted: [%d]\n", iDeleteRows);
                fflush(stdout);
            }
        }

#endif
        return iResult;
    }
    else if(iGenerateType == GENERATE_TYPE_TESTLOG_PARSE        ||
            iGenerateType == GENERATE_TYPE_LOGFILE_CONSOLE      ||
            iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE       ||
            iGenerateType == GENERATE_TYPE_SQLITE_DATACOMPRESS  ||
            iGenerateType == GENERATE_TYPE_SHRINK_DATABASE)
    {
        printAppVersion();
    }

    char szTestLogFile[1024]="";
    char szLogPath[1024]="./";
    char szIncomingDataLogfilePrefix[1024]="";
    char szOutgoingDataLogfilePrefix[1024]="";
    char szSpecialFlags[1024]="";
    char szSpecialFlagsValue[1024]="";

    if(iGenerateType == GENERATE_TYPE_TESTLOG_PARSE)
    {
        CheckForParameter(APP_FLAG_TEST_LOGFILE, argc, argv, szTestLogFile, 2);

        sprintf(szLogFileStartFilter,"%s",TODAYS_DATE_KEYWORD);
        ProcessSpecialDateParameter(szLogFileStartFilter,0);

        sprintf(szLogFileEndFilter,"%s",TODAYS_DATE_KEYWORD);
        ProcessSpecialDateParameter(szLogFileEndFilter,0);
    }
    else if(iGenerateType == GENERATE_TYPE_SHRINK_DATABASE)
    {
        char szDatabase[1024]="./";
        CheckForParameter(APP_FLAG_DB, argc, argv,  szDatabase, 2);

        int iRecordsThisOldAndOlder = -1;
        int iPurgeDBIndex = CheckForParameterAsInteger(APP_FLAG_PURGE_OLD_RECORDS, argc, argv, &iRecordsThisOldAndOlder, -1);
        if(iPurgeDBIndex >= 0)
        {
            if(iRecordsThisOldAndOlder > 0)
            {
                sprintf(szLogFileStartFilter,"1970-01-01");

                sprintf(szLogFileEndFilter,"%s-%d",TODAYS_DATE_KEYWORD,iRecordsThisOldAndOlder);
                ProcessSpecialDateParameter(szLogFileEndFilter,1);

                uint64_t delete_start_date  = getDateValue(szLogFileStartFilter);
                uint64_t delete_end_date    = getDateValue(szLogFileEndFilter);

                printf("calling deleteStatsFromSqlite to PURGE old records with date range [%llu] - [%llu]\n",(long long unsigned int)delete_start_date,(long long unsigned int)delete_end_date);
                fflush(stdout);

                // Step #2 delete old records for specified date range
                int iDeleteRows = deleteStatsFromSqlite(szDatabase, delete_start_date, delete_end_date);

                printf("Total old rows deleted: [%d]\n", iDeleteRows);
                fflush(stdout);
            }
        }

        vacuumDB(szDatabase);

        return 0;
    }
    else
    {
        CheckForParameter(APP_FLAG_STARTDATE, argc, argv, szLogFileStartFilter, 2);
        CheckForParameter(APP_FLAG_ENDDATE, argc, argv, szLogFileEndFilter, 3);

        ProcessSpecialDateParameter(szLogFileStartFilter,0);
        ProcessSpecialDateParameter(szLogFileEndFilter,0);

        if(iGenerateType == GENERATE_TYPE_SQLITE_DATACOMPRESS)
        {
            char szDatabase[1024]="./";
            CheckForParameter(APP_FLAG_DB, argc, argv,  szDatabase, 4);

            int iInsertedRows = compressDataIntoDailyStats(szLogFileStartFilter,szLogFileEndFilter,szDatabase,
                                                           tBandwidthUsage, tBandwidthUsageInitValue, iGenerateType);

            return iInsertedRows;
        }

        CheckForParameter(APP_FLAG_LOGFILE_PATH, argc, argv, szLogPath, 4);
        CheckForParameter(APP_FLAG_LOGFILE_INCOMING, argc, argv, szIncomingDataLogfilePrefix, 5);
        CheckForParameter(APP_FLAG_LOGFILE_OUTGOING, argc, argv, szOutgoingDataLogfilePrefix, 6);

        if(iGenerateType == GENERATE_TYPE_LOGFILE_CONSOLE || iGenerateType == GENERATE_TYPE_LOGFILE_HTML)
        {
            CheckForParameter(APP_FLAG_IMPORT_LOGFILEDATA, argc, argv, szSpecialFlagsValue, 7);
        }
    }

    if( iGenerateType == GENERATE_TYPE_LOGFILE_CONSOLE      ||
        iGenerateType == GENERATE_TYPE_SQLITE_DATACOMPRESS  ||
        iGenerateType == GENERATE_TYPE_TESTLOG_PARSE)
    {
        printf("Using filter date-range [%s] - [%s]\n",szLogFileStartFilter,szLogFileEndFilter);
    }

    if(iGenerateType == GENERATE_TYPE_TESTLOG_PARSE)
    {
        char szLogFile[50]="";
        time_t now = time ( NULL );
        struct tm *tm_now = localtime ( &now );
		if(!tm_now)
		{
		    char szBuf[100]="";
		    sprintf(szBuf,"#1 [%s] Invalid date conversion value specified, returned NULL for tm_now : [%ld]",__FUNCTION__,now);
		    perror(szBuf);
		}

        strftime (szLogFile, 49, "%Y%m%d%H%M%S", tm_now);

        ProcessLogFile("", szLogFile, tBandwidthUsage,INCOMING_DATA,0,szTestLogFile);
        ProcessLogFile("", szLogFile, tBandwidthUsage,OUTGOING_DATA,0,szTestLogFile);
    }
    else if(iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE || iGenerateType == GENERATE_TYPE_SQLITE_HTML)
    {
        if(iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE)
        {
            printf("Using filter date-range [%s] - [%s]\n",szLogFileStartFilter,szLogFileEndFilter);
        }


        char *customFilter=NULL;
        char szCustomFilter[1024]="";
        if(CheckForParameter(APP_FLAG_FILTERBY, argc, argv, szCustomFilter, -1) >= 0)
        {
            customFilter = &szCustomFilter[0];
        }

        char *customSortOrder=NULL;
        char szCustomSortOrder[1024]="";
        if(CheckForParameter(APP_FLAG_SORTBY, argc, argv, szCustomSortOrder, -1) >= 0)
        {
            customSortOrder = &szCustomSortOrder[0];
        }

        char szDB[1024]="";
        CheckForParameter(APP_FLAG_DB, argc, argv,  szDB, 4);

        mainSelectStatsByCriteria(szDB,szLogFileStartFilter,szLogFileEndFilter,tBandwidthUsage,tBandwidthUsageInitValue,iGenerateType,customSortOrder,customFilter);
    }
    else
    {
        ProcessTrafficByDirectionFlow( szLogPath, szIncomingDataLogfilePrefix, INCOMING_DATA,
                                       szLogFileStartFilter,szLogFileEndFilter, tBandwidthUsage,tBandwidthUsageInitValue);

        ProcessTrafficByDirectionFlow( szLogPath, szOutgoingDataLogfilePrefix, OUTGOING_DATA,
                                       szLogFileStartFilter,szLogFileEndFilter, tBandwidthUsage,tBandwidthUsageInitValue);
    }

    if(iGenerateType == GENERATE_TYPE_LOGFILE_CONSOLE || iGenerateType == GENERATE_TYPE_LOGFILE_HTML)
    {
        if(strncasecmp(szSpecialFlags,APP_FLAG_IMPORT_LOGFILEDATA,strlen(APP_FLAG_IMPORT_LOGFILEDATA)) == 0)
        {
            importLogfileDataIntoSqlite(szSpecialFlagsValue, tBandwidthUsage,tBandwidthUsageInitValue);
        }
    }

    ipt_display_flags tDisplayFlags;
    tDisplayFlags.bShowAdditionalHostInfo         = IsParameterEnabled(APP_FLAG_MOREHOSTINFO, argc, argv);
    tDisplayFlags.bShowAdditionalHostInfoAppend   = IsParameterEnabled(APP_FLAG_MOREHOSTINFO_APPEND, argc, argv);
    tDisplayFlags.bShowHostnameOnly               = IsParameterEnabled(APP_FLAG_HOSTNAME_ONLY, argc, argv);

    char szHTMLTemplate[1024]="";
    if(CheckForParameter(APP_FLAG_USE_HTML_TEMPLATE, argc, argv, szHTMLTemplate, -1) >= 0)
    {
        DisplayResultsUsingTemplateFile(szHTMLTemplate, tBandwidthUsage, tBandwidthUsageInitValue,tStartProcessing,
                        (iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE ||
                         iGenerateType == GENERATE_TYPE_SQLITE_HTML    ||
                         iGenerateType == GENERATE_TYPE_TESTLOG_PARSE  ? szLogFileStartFilter : NULL),
                        (iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE ||
                         iGenerateType == GENERATE_TYPE_SQLITE_HTML    ||
                         iGenerateType == GENERATE_TYPE_TESTLOG_PARSE  ? szLogFileEndFilter : NULL),
                        &tDisplayFlags);
    }
    else
    {
        DisplayResults(iGenerateType, tBandwidthUsage, tBandwidthUsageInitValue,tStartProcessing,
                        (iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE ||
                         iGenerateType == GENERATE_TYPE_SQLITE_HTML    ||
                         iGenerateType == GENERATE_TYPE_TESTLOG_PARSE  ? szLogFileStartFilter : NULL),
                        (iGenerateType == GENERATE_TYPE_SQLITE_CONSOLE ||
                         iGenerateType == GENERATE_TYPE_SQLITE_HTML    ||
                         iGenerateType == GENERATE_TYPE_TESTLOG_PARSE  ? szLogFileEndFilter : NULL),
                        &tDisplayFlags);
    }

    return 0;
}
// ==========================================================================
