// Script that process stdout from pcf and creates IRI messages
//
// Copyright (C) 2014 Martin Holkovic
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/un.h>

#define MODULE_ID   0 // v aktualne planovanej podobe SDM, bude mat kazdy modul priradene unikatne ID na zaklade ktoreho sa bude rozpoznavat ktoremu modulu patria data ulozene v SDM
char interface[15];
short int interfaceValid = 0;
short int standalone = 0; // moznost nastavit aby sa spravy neposielali na IRI-Core
short int verboseMode = 0; // urcuje ci sa budu vypisovat spravy IRI na stdout

typedef struct { // struktura pre predavanie informacii rozparsovaneho paketu
    unsigned int srcIp;
    unsigned int dstIp;
    unsigned int srcPort;
    unsigned int dstPort;
    unsigned int seqNum;
    unsigned int ackNum;
    unsigned int flags;
    unsigned int data;
} parser;

typedef struct ack { 
    unsigned int value;
    struct ack *next;
} *ack_ptr;

typedef struct email { 
    unsigned short int length;
    char *emailString;
    unsigned int ackNum;
    char confirmed;
    struct email *next;
} *email_ptr;

typedef struct smtpData { // struktura do ktorej sa budu ukladat data modulu (na strukturu bude odkazovane z SDM)
    char state;
    char clientServer;
    struct email *mail;
    struct email *rcpts;
    struct ack *acks;
} *smtpData_ptr;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// nasledujuce data budu vo finalnej verzii implementovane v SDM, t.j. v tomto programe na ne nie je kladeny doraz na optimalizaciu

typedef struct moduleData { 
    char moduleId;
    void * data;
    struct moduleData *next;
} *moduleData_ptr;

typedef struct connection { 
    char ipType;
    char *clientIp;
    char *serverIp;
    char *clientPort;
    char *serverPort;
    
    char state;
    struct email *mail;
    struct email *rcpts;
    struct ack *acks;
    
    moduleData_ptr modules;
    
    struct connection *next;
} *connection_ptr;

typedef struct {
    connection_ptr first;
    connection_ptr temp;
    unsigned int count;
} connections;

connections db; // jedna sa o obycajny linearny zoznam v ktorom sa zistuje, ci spracovavany paket nie je sucastou uz ulozeneho spojenia
int socket_iri; // socket pre komunikaciu s IRI-Core

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



email_ptr email_create(char * emailString, unsigned short int length, unsigned int ackNum)
{
    email_ptr mail = malloc(sizeof(struct email));
    mail->length = length;
    mail->emailString = emailString;
    mail->ackNum = ackNum;
    mail->next = NULL;
    mail->confirmed = 0;
    return mail;
}

char save_find_ack(smtpData_ptr save, unsigned int ackNum)
{
    ack_ptr tmp = save->acks;
    while(tmp != NULL)
    {
        if(tmp->value == ackNum)
            return 1;
        tmp = tmp->next;
    }
    return 0;
}

void save_set_sender (smtpData_ptr save, char * emailString, unsigned int ackNum)
{
    unsigned short int length = strlen(emailString);
    email_ptr mail = email_create(emailString, length, ackNum);
    if(save_find_ack(save, ackNum) == 1)
        mail->confirmed = 1;
    
    if(save->mail != NULL)
        free(save->mail);
    
    save->mail = mail;
}

void save_add_receiver (smtpData_ptr save, char * emailString, unsigned int ackNum)
{
    unsigned short int length = strlen(emailString);
    if(save->rcpts == NULL)
    {
        email_ptr mail = email_create(emailString, length, ackNum);
        if(save_find_ack(save, ackNum) == 1)
            (save->mail)->confirmed = 1;
        save->rcpts = mail;
    }
    else
    {
        email_ptr tmp = save->rcpts;
        while(tmp != NULL)
        {
            if(strcmp(tmp->emailString, emailString) == 0)
                return;
            if(tmp->next == NULL)
            {
                email_ptr mail = email_create(emailString, length, ackNum);
                if(save_find_ack(save, ackNum) == 1)
                    (save->mail)->confirmed = 1;
                tmp->next = mail;
                return;
            }
            tmp = tmp->next;
        }
    }
}

void save_add_ack (smtpData_ptr save, unsigned int ackNum)
{
    if(save->mail != NULL && (save->mail)->ackNum == ackNum)
    {
        (save->mail)->confirmed = 1;
    }
    else
    {
        email_ptr last; 
        if(save->rcpts != NULL)
        {
            email_ptr tmp = save->rcpts;
            while(tmp != NULL)
            {
                if(tmp->ackNum == ackNum)
                {
                    tmp->confirmed = 1;
                    return;
                }
                last = tmp;
                tmp = tmp->next;
            } 
        }
        
        ack_ptr ackTemp, ackLast;
        ackTemp = save->acks;
        while(ackTemp != NULL)
        {
            if(ackTemp->value == ackNum)
                return;
            ackLast = ackTemp;
            ackTemp = ackTemp->next;
        }
        
        ack_ptr ackStruct = malloc(sizeof(struct ack));
        ackStruct->value = ackNum;
        ackStruct->next = NULL;
        
        if(save->acks == NULL)
            save->acks = ackStruct;
        else
            ackLast->next = ackStruct;
    }
}

void save_delete_inner_structures (smtpData_ptr save, char acksToo)
{
    email_ptr mailTemp;
    
    while(save->rcpts != NULL)
    {
        mailTemp = (save->rcpts)->next;
        free(save->rcpts);
        save->rcpts = mailTemp;
    }
    
    if(save->mail != NULL)
    {
        free(save->mail);
        save->mail = NULL;
    }
    
    if(acksToo == 1)
    {
        ack_ptr ackTemp;
        while(save->acks != NULL)
        {
            ackTemp = (save->acks)->next;
            free(save->acks);
            save->acks = ackTemp;
        }
    }
}

// odstranenie dat modulu z SDM uloziska
void connection_delete_save (connection_ptr connection, smtpData_ptr save)
{
    save_delete_inner_structures(save, 1);
    
    moduleData_ptr moduleData = connection->modules;
    if( (connection->modules)->moduleId == MODULE_ID )
    {
        connection->modules = (connection->modules)->next;
        free(moduleData);
    }
}

// vyhladavnie substringu v stringu
char str_match(const u_char * packet, unsigned int packetLength, short int data, const u_char * string)
{
    short int realLength, length;
    char code;
    
    length = (short int)strlen(string);
    
    if(data + length > packetLength)
        realLength = packetLength - data;
    else
        realLength = length;
    
    code = strcmp(strndup(packet + data, realLength), string);
    return code == 0 ? 1 : 0;
}

// kontrola ci paket obsahuje data v base64 podobe
char base64_data(const u_char * packet, unsigned int packetLength, short int data)
{
    int i;
    char ascii;
    
    packetLength -= 2; // posledne dva znaky su \r \n
    if (packetLength % 3 != 0)
    {
        for (i = data; i < packetLength; i++)
        {
            ascii = packet[i];
            if ( ( ascii < '0' || ascii > '9' )
             && ( ascii < 'a' || ascii > 'z' )
             && ( ascii < 'A' || ascii > 'Z' )
             && ascii != '+' && ascii != '/' && ascii != '=' )
                return 0;
        }
        return 1;
    }
    else
        return 0;
}

unsigned int convert_seqack(const u_char * packet, short int position)
{
    int ackNum = 0;
    short int i;
    
    for(i = 0; i < 4; i++)
    {
        i += (int) packet[position + i];
        i *= 256;
    }
    
    return i;
}
// vypocita sa hodnota ACK (v TCP hlavicke), ktora bude potvrdzovat aktualne prijaty paket
unsigned int calc_ack_from_seq(const u_char * packet, short int positionSeqNum, unsigned int packetLength, unsigned int positionData)
{
    return convert_seqack(packet, positionSeqNum) + (packetLength - positionData);
}

char * convert_binary_to_ipv4(char ip[4])
{
    unsigned int x, i;
    char ipv4[17], ipv4Part[5];
    
    strcpy(ipv4, "");
    
    for(i = 0; i < 4; i++)
    {
        x = ip[i];
        if(x > 4294967040)
            x -= 4294967040;
        
        if(i != 0)
            strcat(ipv4, ".");
        sprintf(ipv4Part, "%u", x);
        strcat(ipv4, ipv4Part);
    }
}

char * convert_binary_to_port(char input[2])
{
    unsigned int x, sum;
    char port[8], port2[8];
    
    strcpy(port, "");
    
    x = input[0];
    if(x > 4294967040)
        x -= 4294967040;
    
    sum = x * 256;
    
    x = input[1];
    if(x > 4294967040)
        x -= 4294967040;
    
    sum += x;
    
    sprintf(port, "%u", sum);
    strcpy(port2, port);
}

// IRI sprava je ukladana do pola, ktoreho velkost sa moze dynamicky zvacsovat
void extend_array_if_necessary(char * array, unsigned int * actualSize, unsigned int * maximumSize, unsigned int newDataLength, unsigned int incrementSize)
{
    *actualSize += newDataLength;
    if(*actualSize > *maximumSize)
    {
        *maximumSize = (*actualSize) + incrementSize;
        array = realloc(array, sizeof(char) * (*maximumSize) );
    }
}

// vytvorenie IRI spravy
void send_to_socket(const u_char * iriType, const u_char * text, connection_ptr connection, smtpData_ptr save, const u_char * aditionalData)
{
    unsigned int bytesAvailable = 50, bytesTaken = 37;
    char * message;
    message = malloc(sizeof(char) * bytesAvailable);
    char timeArray[20];
    struct timeval tv;
    double time_in_micros;
    
    gettimeofday(&tv,NULL);
    time_in_micros = tv.tv_sec + 1000000.0 / tv.tv_usec;
    sprintf(timeArray, "%.2f", time_in_micros);
    
    strcpy(message, "");
    strcat(message, "('smtp', ");
    strcat(message, timeArray);
    strcat(message, ", '");
    strcat(message, iriType);
    strcat(message, "', '");
    
    extend_array_if_necessary(message, &bytesTaken, &bytesAvailable, strlen(text), 400);
    strcat(message, text);
    extend_array_if_necessary(message, &bytesTaken, &bytesAvailable, 205, 220);
    strcat(message, "', [('TCP', ('");
    strcat(message, convert_binary_to_ipv4(connection->clientIp));
    strcat(message, "', ");
    strcat(message, convert_binary_to_port(connection->clientPort));
    strcat(message, ", '");
    strcat(message, convert_binary_to_ipv4(connection->serverIp));
    strcat(message, "', ");
    strcat(message, convert_binary_to_port(connection->serverPort));
    strcat(message, "))");
    
    if(aditionalData != NULL)
    {
        extend_array_if_necessary(message, &bytesTaken, &bytesAvailable, strlen(aditionalData), 10);
        strcat(message, aditionalData);
    }
    
    extend_array_if_necessary(message, &bytesTaken, &bytesAvailable, 3, 1);
    strcat(message, "])\n");
    
    if(verboseMode)
        fprintf(stderr, "IRI-IIF: %s", message);
    
    if(!standalone)
    {
        printf("iriSocket.send(message)\n");
        send(socket_iri, message, strlen(message), 0);
    }
}

// z e-mailovych adries ulozenych v ramci spojenia sa vytvori parameter IRI spravy
char* create_smtp_nid(smtpData_ptr save)
{
    unsigned int bytesAvailable = 200, bytesTaken = 7;
    char * message;
    message = malloc(sizeof(char) * bytesAvailable);
    
    strcpy(message, "");
    
    strcat(message, "('SMTP'");
    
    if(save->mail != NULL)
    {
        extend_array_if_necessary(message, &bytesTaken, &bytesAvailable, 24 + (save->mail)->length, 100);
        strcat(message, ", ('E-mail address', '");
        strcat(message, (save->mail)->emailString );
        strcat(message, "')");
    }
    else
    {
        bytesTaken += 31;
        strcat(message, ", ('E-mail address', 'unknown')");
    }
    extend_array_if_necessary(message, &bytesTaken, &bytesAvailable, 3, 100);
    strcat(message, ", [");
    
    email_ptr tmp = save->rcpts;
    while(tmp != NULL)
    {
        extend_array_if_necessary(message, &bytesTaken, &bytesAvailable, 24 + tmp->length, 100);
        strcat(message, ", ('E-mail address', '");
        strcat(message, tmp->emailString );
        strcat(message, "')");
        tmp = tmp->next;
    }
    
    extend_array_if_necessary(message, &bytesTaken, &bytesAvailable, 36, 100);
    
    strcat(message, "]");
    strcat(message, ", ('E-mail Message-ID', 'unknown')");
    strcat(message, ")");
    
    return strdup(message);
}

// v pakete vyhlada emailovu adresu odosielatela
char * get_mail(const u_char * packet, unsigned int dataStart, unsigned int length, char * result)
{
    char * begin, * end;
    unsigned int beginIndex, endIndex;
    begin = (char*) memchr (packet + dataStart, '<', length - dataStart);
    end   = (char*) memchr (packet + dataStart, '>', length - dataStart);
    
    if(begin == NULL || end == NULL)
        return NULL;
    
    beginIndex = begin - (char *) packet + 1;
    endIndex = end - (char *) packet;
    
    result = malloc( sizeof(char) * ( endIndex - beginIndex ) );
    sprintf(result, "%.*s", endIndex - beginIndex, &packet[beginIndex]);
    
    return result;
}

// v pakete vyhlada emailovu adresu prijimatela
char * get_rcpt(const u_char * packet, unsigned int dataStart, unsigned int length, char * result)
{
    char * begin, * end;
    unsigned int beginIndex, endIndex;
    char beginSymbol;
    
    begin = (char*) memchr (packet + dataStart, '@', length - dataStart);
    beginIndex = begin - (char *) packet + 2;
    begin = (char*) memchr (packet + beginIndex, '@', length - dataStart);
    if(begin != NULL) // packet.count("@") > 1
        beginSymbol = ':';
    else
        beginSymbol = '<';    
    
    begin = (char*) memchr (packet + dataStart, beginSymbol, length - dataStart);
    end   = (char*) memchr (packet + dataStart, '>', length - dataStart);
    
    if(begin == NULL || end == NULL)
        return NULL;
    
    beginIndex = begin - (char *) packet + 1;
    endIndex = end - (char *) packet;
    
    result = malloc( sizeof(char) * ( endIndex - beginIndex ) );
    sprintf(result, "%.*s", endIndex - beginIndex, &packet[beginIndex]);
    
    return result;
}

// ulozenie zaznamu o stave toku do SDM
void save_create(connection_ptr connection, char state, char clientServer)
{
    smtpData_ptr smtpData = malloc(sizeof(struct smtpData));
    smtpData->state = state;
    smtpData->clientServer = clientServer;
    smtpData->mail = NULL;
    smtpData->rcpts = NULL;
    smtpData->acks = NULL;
    
    moduleData_ptr save = malloc(sizeof(struct moduleData));
    save->moduleId = MODULE_ID;
    save->data = smtpData;
    save->next = NULL;
    
    if(connection->modules == NULL)
    {
        connection->modules = save;
    }
    else
    {
        moduleData_ptr lastModuleData = connection->modules;
        while(lastModuleData->next != NULL)
            lastModuleData = lastModuleData->next;
        lastModuleData->next = save;
    }
}

// funkcia obsahuje konecny automat pre analyzu protokolu SMTP
void smtp(unsigned int length, const u_char * packet, parser * parsedData, connection_ptr connection, moduleData_ptr input)
{
    smtpData_ptr save;
    if(input != NULL)
        save = (smtpData_ptr) input->data;
    else
        save = NULL;
    
    char flags = packet[parsedData->flags];
    
    if(save == NULL)
    {
        if(str_match(packet, length, parsedData->data, "220"))
        {
            save_create(connection, 2, 0);
        }
        else if(str_match(packet, length, parsedData->data, "EHLO") || str_match(packet, length, parsedData->data, "HELO"))
        {
            save_create(connection, 3, 1);
        }
    }
    else if(save->state == 2)
    {
        if(str_match(packet, length, parsedData->data, "250"))
        {
            send_to_socket("REPORT", "User has connected to the SMTP server", connection, save, NULL);
            save->state = 4;
        }
        else if(str_match(packet, length, parsedData->data, "EHLO") || str_match(packet, length, parsedData->data, "HELO"))
        {
            save->state = 3;
        }
    }
    else if(save->state == 3)
    {
        if(str_match(packet, length, parsedData->data, "250"))
        {
            send_to_socket("REPORT", "User has connected to the SMTP server", connection, save, NULL);
            save->state = 4;
        }
        else if(str_match(packet, length, parsedData->data, "AUTH"))
        {
            send_to_socket("REPORT", "User has connected to the SMTP server", connection, save, NULL);
            save->state = 5;
        }
        else if(str_match(packet, length, parsedData->data, "MAIL")
         || str_match(packet, length, parsedData->data, "Mail"))
        {
            char * emailAddressString;
            send_to_socket("REPORT", "User has connected to the SMTP server", connection, save, NULL);
            save_set_sender(save, get_mail(packet, parsedData->data, length, emailAddressString), calc_ack_from_seq(packet, parsedData->seqNum, length, parsedData->data));
            save->state = 8;
        }
        else if(str_match(packet, length, parsedData->data, "5"))
        {
            send_to_socket("REPORT", "The user unsuccessfully tried connect to server", connection, save, NULL);
            save->state = 2;
        }
        else if( flags & 1 > 0
         || str_match(packet, length, parsedData->data, "221")
         || str_match(packet, length, parsedData->data, "QUIT") )
        {
            send_to_socket("REPORT", "User has disconnected from the SMTP server", connection, save, NULL);
            connection_delete_save(connection, save);
        }
        else if(str_match(packet, length, parsedData->data, "STARTTLS"))
        {
            save->state = 11;
        }
    }
    else if(save->state == 4)
    {
        if(str_match(packet, length, parsedData->data, "AUTH"))
        {
            save->state = 5;
        }
        else if(str_match(packet, length, parsedData->data, "MAIL")
         || str_match(packet, length, parsedData->data, "Mail"))
        {
            char * emailAddressString;
            send_to_socket("REPORT", "User has connected to the SMTP server", connection, save, NULL);
            save_set_sender(save, get_mail(packet, parsedData->data, length, emailAddressString), calc_ack_from_seq(packet, parsedData->seqNum, length, parsedData->data));
            save->state = 8;
        }
        else if(str_match(packet, length, parsedData->data, "250"))
        {
            save_add_ack (save, convert_seqack(packet, parsedData->ackNum));
            save->state = 8;
        }
        else if( flags & 1 > 0
         || str_match(packet, length, parsedData->data, "221")
         || str_match(packet, length, parsedData->data, "QUIT") )
        {
            send_to_socket("REPORT", "User has disconnected from the SMTP server", connection, save, NULL);
            connection_delete_save(connection, save);
        }
        else if(str_match(packet, length, parsedData->data, "STARTTLS"))
        {
            save->state = 11;
        }
        else if(str_match(packet, length, parsedData->data, "334")
         || base64_data(packet, length, parsedData->data))
        {
            save->state = 6;
        }
    }
    else if(save->state == 5)
    {
        if( flags & 1 > 0
         || str_match(packet, length, parsedData->data, "221")
         || str_match(packet, length, parsedData->data, "QUIT") )
        {
            send_to_socket("REPORT", "User has disconnected from the SMTP server", connection, save, NULL);
            connection_delete_save(connection, save);
        }
        else if(str_match(packet, length, parsedData->data, "334")
         || base64_data(packet, length, parsedData->data))
        {
            save->state = 6;
        }
    }
    else if(save->state == 6)
    {
        if(str_match(packet, length, parsedData->data, "AUTH"))
        {
            send_to_socket("REPORT", "The user was unsuccessful in authentication", connection, save, NULL);
            save->state = 5;
        }
        else if(str_match(packet, length, parsedData->data, "MAIL")
         || str_match(packet, length, parsedData->data, "Mail"))
        {
            char * emailAddressString;
            send_to_socket("BEGIN", "The user was successfully authenticated", connection, save, NULL);
            save_set_sender(save, get_mail(packet, parsedData->data, length, emailAddressString), calc_ack_from_seq(packet, parsedData->seqNum, length, parsedData->data));
            save->state = 8;
        }
        else if(str_match(packet, length, parsedData->data, "235"))
        {
            send_to_socket("BEGIN", "The user was successfully authenticated", connection, save, NULL);
            save->state = 8;
        }
        else if(str_match(packet, length, parsedData->data, "535"))
        {
            send_to_socket("REPORT", "The user was unsuccessful in authentication", connection, save, NULL);
            save->state = 7;
        }
        else if( flags & 1 > 0
         || str_match(packet, length, parsedData->data, "221")
         || str_match(packet, length, parsedData->data, "QUIT") )
        {
            send_to_socket("REPORT", "User has disconnected from the SMTP server", connection, save, NULL);
            connection_delete_save(connection, save);
        }
    }
    else if(save->state == 7)
    {
        if(str_match(packet, length, parsedData->data, "AUTH"))
        {
            save->state = 5;
        }
        else if( flags & 1 > 0
         || str_match(packet, length, parsedData->data, "221")
         || str_match(packet, length, parsedData->data, "QUIT") )
        {
            send_to_socket("REPORT", "User has disconnected from the SMTP server", connection, save, NULL);
            connection_delete_save(connection, save);
        }
        else if(str_match(packet, length, parsedData->data, "334")
         || base64_data(packet, length, parsedData->data))
        {
            save->state = 6;
        }
    }
    else if(save->state == 8)
    {
        if(str_match(packet, length, parsedData->data, "250"))
        {
            save_add_ack (save, convert_seqack(packet, parsedData->ackNum));
        }
        else if(str_match(packet, length, parsedData->data, "MAIL")
         || str_match(packet, length, parsedData->data, "Mail"))
        {
            char * emailAddressString;
            save_set_sender(save, get_mail(packet, parsedData->data, length, emailAddressString), calc_ack_from_seq(packet, parsedData->seqNum, length, parsedData->data));
        }
        else if(str_match(packet, length, parsedData->data, "RCPT")
         || str_match(packet, length, parsedData->data, "Rcpt"))
        {
            char * emailAddressString;
            save_add_receiver (save, get_rcpt(packet, parsedData->data, length, emailAddressString), calc_ack_from_seq(packet, parsedData->seqNum, length, parsedData->data));
            save_add_ack (save, convert_seqack(packet, parsedData->ackNum));
        }
        else if(str_match(packet, length, parsedData->data, "EHLO")
         || str_match(packet, length, parsedData->data, "HELO"))
        {
            save_delete_inner_structures(save, 0);
            save->state = 3;
        }
        else if(str_match(packet, length, parsedData->data, "DATA"))
        {
            save_add_ack (save, convert_seqack(packet, parsedData->ackNum));
            save->state = 9;
        }
        else if(str_match(packet, length, parsedData->data, "354"))
        {
            save->state = 9;
        }
        else if(str_match(packet, length, parsedData->data, "RSET"))
        {
            save_delete_inner_structures(save, 0);
            save->state = 8;
        }
        else if( flags & 1 > 0
         || str_match(packet, length, parsedData->data, "221")
         || str_match(packet, length, parsedData->data, "QUIT") )
        {
            send_to_socket("END", "User has disconnected from the SMTP server", connection, save, NULL);
            connection_delete_save(connection, save);
        }
    }
    else if(save->state == 9)
    {
        if(str_match(packet, length, parsedData->data, "EHLO")
         || str_match(packet, length, parsedData->data, "HELO"))
        {
            save_delete_inner_structures(save, 0);
            save->state = 3;
        }
        else if(str_match(packet, length, parsedData->data, "RSET"))
        {
            save_delete_inner_structures(save, 0);
            save->state = 8;
        }
        else if(str_match(packet, length, parsedData->data, "250"))
        {
            send_to_socket("CONTINUE", "An e-mail of length TODO bytes successfully sent", connection, save, create_smtp_nid(save));
            save->state = 10;
        }
        else if(str_match(packet, length, parsedData->data, "4")
         || str_match(packet, length, parsedData->data, "5"))
        {
            send_to_socket("CONTINUE", "The user unsuccessfully tried send a e-mail", connection, save, create_smtp_nid(save));
            save->state = 10;
        }
        else if( flags & 1 > 0
         || str_match(packet, length, parsedData->data, "221")
         || str_match(packet, length, parsedData->data, "QUIT") )
        {
            send_to_socket("END", "User has disconnected from the SMTP server", connection, save, NULL);
            connection_delete_save(connection, save);
        }
    }
    else if(save->state == 10)
    {
        if(str_match(packet, length, parsedData->data, "EHLO")
         || str_match(packet, length, parsedData->data, "HELO"))
        {
            save_delete_inner_structures(save, 0);
            save->state = 3;
        }
        else if(str_match(packet, length, parsedData->data, "MAIL")
         || str_match(packet, length, parsedData->data, "Mail"))
        {
            char * emailAddressString;
            save_delete_inner_structures(save, 0);
            save_set_sender(save, get_mail(packet, parsedData->data, length, emailAddressString), calc_ack_from_seq(packet, parsedData->seqNum, length, parsedData->data));
            save->state = 8;
        }
        else if(str_match(packet, length, parsedData->data, "RSET"))
        {
            save_delete_inner_structures(save, 0);
            save->state = 8;
        }
        else if( flags & 1 > 0
         || str_match(packet, length, parsedData->data, "221")
         || str_match(packet, length, parsedData->data, "QUIT") )
        {
            send_to_socket("END", "User has disconnected from the SMTP server", connection, save, NULL);
            connection_delete_save(connection, save);
        }
    }
    else if(save->state == 11)
    {
        if(str_match(packet, length, parsedData->data, "220") || packet[parsedData->data] < 25 ) // v pripade zacati TLS je na prvej pozicii asci hodnota 20-24 reprezentujuca typ TLS obsahu
        {
            send_to_socket("CONTINUE", "SSL connection initiated", connection, save, NULL);
            save->state = 10;
        }
        else if(str_match(packet, length, parsedData->data, "4")
         || str_match(packet, length, parsedData->data, "5"))
        {
            save->state = 4;
        }
        else if(str_match(packet, length, parsedData->data, "AUTH"))
        {
            save->state = 5;
        }
    }
    
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// zvysok programu bude opat vo finalnej verzii implementovany v SDM, t.j. v tomto programe na ne neho je kladeny doraz na optimalizaciu

void connections_init (connections *input)	{
    input->first = NULL;
    input->count = 0;
}

connection_ptr connections_add (connections *input, char ipType, char clientIp[16], char serverIp[16], char clientPort[2], char serverPort[2]) {
    connection_ptr newElement = malloc(sizeof(struct connection));
    if(newElement == NULL) exit(21);
    else
    {
		if(input->first == NULL)
		{
			input->first = newElement;
		}
		else
		{
			input->temp = input->first;
			while((*input->temp).next != NULL)
			{
				input->temp = (*input->temp).next;
			}
			(*input->temp).next = newElement;
		}
		
        newElement->next = NULL;
        
        newElement->ipType = ipType;
        newElement->clientIp = clientIp;
        newElement->serverIp = serverIp;
        newElement->clientPort = clientPort;
        newElement->serverPort = serverPort;
        
        newElement->modules = NULL;
		input->count = input->count + 1;
        
        return newElement;
    }
}

connection_ptr connections_find (connections *input, char srcIp[16], char dstIp[16], char srcPort[2], char dstPort[2]) {
    input->temp = input->first;
    
    while(input->temp != NULL)
    {
        if(strcmp( (*input->temp).clientIp, srcIp) == 0 && strcmp( (*input->temp).serverIp, dstIp) == 0 && strcmp( (*input->temp).clientPort, srcPort) == 0 && strcmp( (*input->temp).serverPort, dstPort) == 0)
        {
            return input->temp;
        }
        else if(strcmp( (*input->temp).serverIp, srcIp) == 0 && strcmp( (*input->temp).clientIp, dstIp) == 0 && strcmp( (*input->temp).serverPort, srcPort) == 0 && strcmp( (*input->temp).clientPort, dstPort) == 0)
        {
            return input->temp;
        }
        input->temp = (*input->temp).next;
    }
    
    return NULL;
}

void processPacket(u_char *arg, const struct pcap_pkthdr* pkthdr, const u_char * packet)
{
    // sucastou modulu nie je parser paketov, preto pre testovacie ucely som vybral .pcap subor (ktory je sucastou projektu) a manualne vypocital pozicie jednotlivych dat, ktore pri integrovani do SDM bude riesit SDM
    int *counter = (int *)arg; 
    
    parser parsedData;
    parsedData.srcIp = 26;
    parsedData.dstIp = 30;
    parsedData.srcPort = 34;
    parsedData.dstPort = 36;
    parsedData.seqNum = 38;
    parsedData.ackNum = 42;
    parsedData.flags = 47;
    parsedData.data = 54 + 0; //12
    
    connection_ptr connection;
    connection = connections_find(&db, strndup(packet + parsedData.srcIp, 4), strndup(packet + parsedData.dstIp, 4), strndup(packet + parsedData.srcPort, 2), strndup(packet + parsedData.dstPort, 2));
    if(connection == NULL)
        connection = connections_add(&db, 4, strndup(packet + parsedData.dstIp, 4), strndup(packet + parsedData.srcIp, 4), strndup(packet + parsedData.dstPort, 2), strndup(packet + parsedData.srcPort, 2));
    
    moduleData_ptr moduleData = connection->modules;
    while(moduleData != NULL && moduleData->moduleId != 0)
        moduleData = moduleData->next;
    
    if(packet[23] == 6)
    {
        smtp(pkthdr->len, packet, &parsedData, connection, moduleData);
    }
} 


void checkParams(int argc, char **argv)
{
    int ch;
	while ((ch = getopt(argc, argv, "i:sv")) != -1)
	{
		if(ch == 'i')
		{
            strcpy(interface, optarg);
            interfaceValid = 1;
		}
        else if (ch == 's')
            standalone = 1;
        else if (ch == 'v')
            verboseMode = 1;
        else
			fprintf(stderr, "You specified an unsupported parameter!\n");
	}
}

/*
 * otevre TCP spojeni s MF ktere se budou zasilat vytvorene zpravy IRI
 */
int otevri_spojeni_pro_iri(void)
{
	int len;
	struct sockaddr_un remote;
	if ((socket_iri = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
	{
		return 1;
	}
	
	remote.sun_family = AF_UNIX;
	strcpy(remote.sun_path, "/tmp/iricol");
	len = strlen(remote.sun_path) + sizeof(remote.sun_family);
	if (connect(socket_iri, (struct sockaddr *)&remote, len) == -1)
	{
		perror("Chyba pri pripojovani k socketu");
		return 1;
	}
	
	return 0;
}

 int main(int argc, char *argv[])
 {
    pcap_t *handle;			/* Session handle */
    char errbuf[PCAP_ERRBUF_SIZE];	/* Error string */
    struct pcap_pkthdr header;	/* The header that pcap gives us */
    const u_char *packet;		/* The actual packet */
    int count;
    
    checkParams(argc, argv);
    
    if(!interfaceValid)
        strcpy(interface, "any"); // defaultny interface na ktorom bude prebiehat odposlech
    
    if(!standalone)
        fprintf(stderr, "Sending IRI messages to the IRI-Core is not yet supported.\n");
    
    if(standalone == 0)
        otevri_spojeni_pro_iri();
    
    /* Open the session in promiscuous mode */
    handle = pcap_open_live(interface, BUFSIZ, 1, 1000, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "Couldn't open device %s: %s\n", "lo", errbuf);
        return(2);
    }
    
	connections_init(&db);
    
    /* Loop forever & call processPacket() for every received packet*/ 
    if ( pcap_loop(handle, -1, processPacket, (u_char *)&count) == -1){
        fprintf(stderr, "ERROR: %s\n", pcap_geterr(handle) );
        return(1);
    }
    
    pcap_close(handle);
    return(0);
 }
