diff --git a/.gitignore b/.gitignore index cd531cf..3151d03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # ---> C # Prerequisites *.d - +download # Object files *.o *.ko diff --git a/src/main.c b/src/main.c index a91b685..4b6ee48 100644 --- a/src/main.c +++ b/src/main.c @@ -15,128 +15,1228 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #include #include #include -#include -#include -#include -#include +#include +#include -#define BUF 1024 -#define DEBUG true -#define EXPECTED_PARAMS 2 +#define MAX_LINE 2048 +#define INITIAL_ENTRIES 1000 +#define GROWTH_FACTOR 2 +#define MAX_FILTERS 100 +typedef enum { + FILTER_INCLUDE, + FILTER_EXCLUDE +} filter_mode_t; -struct datetime_t { - int year; - int month; +struct simple_time { int day; + int month; + int year; int hour; int minute; int second; }; -struct errorLogEntry_t { - struct datetime_t datetime; - char level; - int process_id; - int state_id; - char *connection_id_message; +struct log_entry { + char ip_address[50]; + char request_method[10]; + char url_path[200]; + int status_code; + int bytes_sent; + struct simple_time time; }; -// https://nginx.org/en/docs/http/ngx_http_log_module.html -struct access_log_entry_t { - char *remote_addr; - char *remote_user; - struct datetime_t time_local; - char *request; - unsigned int status; - unsigned int body_bytes_sent; - char *http_referer; - char *http_user_agent; +struct status_filter { + int code; + filter_mode_t mode; }; - -void debugmsg(const char *format, ...) { - if(DEBUG == true) { - va_list args; - va_start(args, format); - printf("DEBUG: "); - vprintf(format, args); - printf("\n"); - va_end(args); - } +struct ip_filter { + char ip_address[50]; + filter_mode_t mode; }; -int checkparams(int *argc, char *argv[]) { - if(*argc != EXPECTED_PARAMS) { - printf("Dateipfad anageben! Syntax: ./main \n"); - exit(-1); - }; +struct time_filter { + struct simple_time start_time; + struct simple_time end_time; + filter_mode_t mode; }; -struct access_log_entry_t parse_access_log(char *accessLogLine) { - struct access_log_entry_t logEntry; -// //logEntry.remote_addr = parse_remote_address(accessLogLine); - return logEntry; +struct filter_system { + struct status_filter status_filters[MAX_FILTERS]; + int status_count; + + struct ip_filter ip_filters[MAX_FILTERS]; + int ip_count; + + struct time_filter time_filters[MAX_FILTERS]; + int time_count; + + int combination_mode; }; -//char* parse_remote_address(char *accessLogLine) { -// return 1; -//} +struct log_entry *all_entries = NULL; +int max_entries = 0; +int total_entries = 0; +struct filter_system filters = {0}; -unsigned int count_lines(FILE *filepointer) { - rewind(filepointer); - unsigned int lines = 0; - char c; - for(c=getc(filepointer); c != EOF; c = getc(filepointer)) { - if(c=='\n'){ - lines += 1; - } - } - debugmsg( "Detected %d Lines in filestream\n", lines); - rewind(filepointer); - return lines; +int is_space(char c) { + return (c == ' ' || c == '\t'); } -unsigned int calculate_filesize(FILE *filepointer){ - rewind(filepointer); - fseek(filepointer, 0L, SEEK_END); - int filesize = ftell(filepointer); - rewind(filepointer); - return filesize; +char* skip_spaces(char* str) { + while (is_space(*str)) { + str++; + } + return str; } -int main(int argc, char *argv[]) { - debugmsg("START OF PROGRAM"); +void copy_until_space(char* destination, char* source, int max_length) { int i = 0; - while(i= max_entries) { + int old_max = max_entries; + max_entries = max_entries * GROWTH_FACTOR; + + printf("Speicher wird erweitert: %d -> %d Einträge\n", old_max, max_entries); + + struct log_entry *new_ptr = realloc(all_entries, max_entries * sizeof(struct log_entry)); + + if (new_ptr == NULL) { + printf("KRITISCHER FEHLER: Speicher konnte nicht auf %d Einträge erweitert werden!\n", max_entries); + printf("Benötigter Speicher: %zu Bytes\n", max_entries * sizeof(struct log_entry)); + cleanup_and_exit(); + } + + all_entries = new_ptr; + printf("Speicher erfolgreich erweitert auf %zu Bytes\n", + max_entries * sizeof(struct log_entry)); + } +} + +void allocate_initial_memory() { + max_entries = INITIAL_ENTRIES; + all_entries = malloc(max_entries * sizeof(struct log_entry)); + + if (all_entries == NULL) { + printf("KRITISCHER FEHLER: Konnte %d Einträge nicht allokieren!\n", max_entries); + printf("Benötigter Speicher: %zu Bytes\n", max_entries * sizeof(struct log_entry)); + exit(1); + } + + printf("Speicher allokiert für %d Log-Einträge (%zu Bytes)\n", + max_entries, max_entries * sizeof(struct log_entry)); +} + +int is_directory(char* path) { + struct stat path_stat; + if (stat(path, &path_stat) != 0) { + return 0; + } + return S_ISDIR(path_stat.st_mode); +} + +int is_log_file(char* filename) { + char* log_pos = strstr(filename, ".log"); + if (log_pos == NULL) return 0; + + char* after_log = log_pos + 4; + + if (*after_log == '\0') return 1; + + if (*after_log == '.') { + after_log++; + if (*after_log == '\0') return 0; + + while (*after_log != '\0') { + if (*after_log < '0' || *after_log > '9') return 0; + after_log++; + } + return 1; + } + return 0; +} + +int parse_simple_log_line(char* line, int entry_index) { + char* current_pos = line; + + current_pos = skip_spaces(current_pos); + copy_until_space(all_entries[entry_index].ip_address, current_pos, sizeof(all_entries[entry_index].ip_address)); + + while (*current_pos != ' ' && *current_pos != '\0') current_pos++; + current_pos = skip_spaces(current_pos); + + while (*current_pos != ' ' && *current_pos != '\0') current_pos++; + current_pos = skip_spaces(current_pos); + while (*current_pos != ' ' && *current_pos != '\0') current_pos++; + current_pos = skip_spaces(current_pos); + + if (*current_pos == '[') { + current_pos++; + + all_entries[entry_index].time.day = 0; + while (*current_pos >= '0' && *current_pos <= '9') { + all_entries[entry_index].time.day = all_entries[entry_index].time.day * 10 + (*current_pos - '0'); + current_pos++; + } + + if (*current_pos == '/') current_pos++; + + char month_str[4] = {0}; + int month_pos = 0; + while (*current_pos != '/' && *current_pos != '\0' && month_pos < 3) { + month_str[month_pos] = *current_pos; + month_pos++; + current_pos++; + } + all_entries[entry_index].time.month = month_name_to_number(month_str); + + if (*current_pos == '/') current_pos++; + + all_entries[entry_index].time.year = 0; + while (*current_pos >= '0' && *current_pos <= '9') { + all_entries[entry_index].time.year = all_entries[entry_index].time.year * 10 + (*current_pos - '0'); + current_pos++; + } + + if (*current_pos == ':') current_pos++; + + all_entries[entry_index].time.hour = 0; + while (*current_pos >= '0' && *current_pos <= '9') { + all_entries[entry_index].time.hour = all_entries[entry_index].time.hour * 10 + (*current_pos - '0'); + current_pos++; + } + + if (*current_pos == ':') current_pos++; + + all_entries[entry_index].time.minute = 0; + while (*current_pos >= '0' && *current_pos <= '9') { + all_entries[entry_index].time.minute = all_entries[entry_index].time.minute * 10 + (*current_pos - '0'); + current_pos++; + } + + if (*current_pos == ':') current_pos++; + + all_entries[entry_index].time.second = 0; + while (*current_pos >= '0' && *current_pos <= '9') { + all_entries[entry_index].time.second = all_entries[entry_index].time.second * 10 + (*current_pos - '0'); + current_pos++; + } + + while (*current_pos != ']' && *current_pos != '\0') current_pos++; + if (*current_pos == ']') current_pos++; + } + + current_pos = skip_spaces(current_pos); + + if (*current_pos == '"') { + current_pos++; + + copy_until_space(all_entries[entry_index].request_method, current_pos, sizeof(all_entries[entry_index].request_method)); + while (*current_pos != ' ' && *current_pos != '\0') current_pos++; + current_pos = skip_spaces(current_pos); + + int i = 0; + while (*current_pos != ' ' && *current_pos != '"' && *current_pos != '\0' && i < sizeof(all_entries[entry_index].url_path) - 1) { + all_entries[entry_index].url_path[i] = *current_pos; + i++; + current_pos++; + } + all_entries[entry_index].url_path[i] = '\0'; + + while (*current_pos != '"' && *current_pos != '\0') current_pos++; + if (*current_pos == '"') current_pos++; + } + + current_pos = skip_spaces(current_pos); + + all_entries[entry_index].status_code = 0; + while (*current_pos >= '0' && *current_pos <= '9') { + all_entries[entry_index].status_code = all_entries[entry_index].status_code * 10 + (*current_pos - '0'); + current_pos++; + } + + current_pos = skip_spaces(current_pos); + + all_entries[entry_index].bytes_sent = 0; + while (*current_pos >= '0' && *current_pos <= '9') { + all_entries[entry_index].bytes_sent = all_entries[entry_index].bytes_sent * 10 + (*current_pos - '0'); + current_pos++; + } + + return 1; +} + +void load_regular_file(char* filename) { + FILE* file = fopen(filename, "r"); + if (file == NULL) { + printf("WARNUNG: Kann Datei '%s' nicht öffnen!\n", filename); + return; + } + + printf("Lade Datei: %s\n", filename); + char line[MAX_LINE]; + int loaded_from_this_file = 0; + + while (fgets(line, sizeof(line), file) != NULL) { + expand_memory_if_needed(); + + if (parse_simple_log_line(line, total_entries)) { + total_entries++; + loaded_from_this_file++; + } + } + + fclose(file); + printf(" -> %d Einträge aus dieser Datei geladen.\n", loaded_from_this_file); +} + +void load_log_file(char* path) { + total_entries = 0; + + if (is_directory(path)) { + printf("Verzeichnis erkannt: %s\n", path); + printf("Suche nach .log Dateien...\n"); + + DIR* dir = opendir(path); + if (dir == NULL) { + printf("FEHLER: Kann Verzeichnis '%s' nicht öffnen!\n", path); + return; + } + + struct dirent* entry; + int files_found = 0; + + while ((entry = readdir(dir)) != NULL) { + char* filename = entry->d_name; + + if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) { + continue; + } + + if (is_log_file(filename)) { + char full_path[512]; + snprintf(full_path, sizeof(full_path), "%s/%s", path, filename); + + load_regular_file(full_path); + files_found++; + } else if (strstr(filename, ".gz") != NULL) { + printf("INFO: .gz Datei '%s' übersprungen (nicht unterstützt in dieser Version)\n", filename); + printf(" Tipp: Dekomprimieren Sie mit 'gunzip %s'\n", filename); + } + } + + closedir(dir); + + if (files_found == 0) { + printf("Keine .log Dateien im Verzeichnis gefunden.\n"); + printf("Tipp: Für .gz Dateien verwenden Sie 'gunzip *.gz' zum Dekomprimieren\n"); + } else { + printf("Insgesamt %d .log Dateien verarbeitet.\n", files_found); + } + + } else { + printf("Einzelne Datei erkannt: %s\n", path); + + if (strstr(path, ".gz") != NULL) { + printf("FEHLER: .gz Dateien werden in dieser Version nicht unterstützt!\n"); + printf("Lösung: Dekomprimieren Sie die Datei zuerst:\n"); + printf(" gunzip %s\n", path); + printf(" Dann: %s %.*s\n", "PROGRAMM", (int)(strlen(path)-3), path); + return; + } else { + load_regular_file(path); + } + } + + printf("Erfolgreich %d Einträge insgesamt geladen.\n", total_entries); + printf("Aktueller Speicherverbrauch: %zu Bytes für %d Einträge\n", + max_entries * sizeof(struct log_entry), max_entries); +} + +int status_code_matches(int status_code) { + if (filters.status_count == 0) return 1; + + int has_include_filters = 0; + int include_match = 0; + + for (int i = 0; i < filters.status_count; i++) { + if (filters.status_filters[i].mode == FILTER_INCLUDE) { + has_include_filters = 1; + if (filters.status_filters[i].code == status_code) { + include_match = 1; + } + } else { + if (filters.status_filters[i].code == status_code) { + return 0; + } + } + } + + if (has_include_filters) { + return include_match; + } + + return 1; +} + +int ip_address_matches(char* ip_address) { + if (filters.ip_count == 0) return 1; + + int has_include_filters = 0; + int include_match = 0; + + for (int i = 0; i < filters.ip_count; i++) { + if (filters.ip_filters[i].mode == FILTER_INCLUDE) { + has_include_filters = 1; + if (strcmp(filters.ip_filters[i].ip_address, ip_address) == 0) { + include_match = 1; + } + } else { + if (strcmp(filters.ip_filters[i].ip_address, ip_address) == 0) { + return 0; + } + } + } + + if (has_include_filters) { + return include_match; + } + + return 1; +} + +int time_matches(struct simple_time entry_time) { + if (filters.time_count == 0) return 1; + + int has_include_filters = 0; + int include_match = 0; + + for (int i = 0; i < filters.time_count; i++) { + int in_range = (compare_times(entry_time, filters.time_filters[i].start_time) >= 0 && + compare_times(entry_time, filters.time_filters[i].end_time) <= 0); + + if (filters.time_filters[i].mode == FILTER_INCLUDE) { + has_include_filters = 1; + if (in_range) { + include_match = 1; + } + } else { + if (in_range) { + return 0; + } + } + } + + if (has_include_filters) { + return include_match; + } + + return 1; +} + +int passes_filter(int entry_index) { + int status_match = status_code_matches(all_entries[entry_index].status_code); + int ip_match = ip_address_matches(all_entries[entry_index].ip_address); + int time_match = time_matches(all_entries[entry_index].time); + + if (filters.combination_mode == 0) { + return status_match && ip_match && time_match; + } else { + int total_filters = filters.status_count + filters.ip_count + filters.time_count; + if (total_filters == 0) return 1; + + return status_match || ip_match || time_match; + } +} + +int count_filtered_entries() { + int count = 0; + for (int i = 0; i < total_entries; i++) { + if (passes_filter(i)) { + count++; + } + } + return count; +} + +void show_status() { + printf("\n========== SYSTEM STATUS ==========\n"); + + if (total_entries > 0) { + printf("✅ Log-Daten: %d Einträge geladen\n", total_entries); + printf(" Speicherverbrauch: %zu Bytes (%d Einträge Kapazität)\n", + max_entries * sizeof(struct log_entry), max_entries); + } else { + printf("❌ Keine Log-Daten geladen\n"); + } + + printf("\n🔍 Aktive Filter:\n"); + int total_filters = filters.status_count + filters.ip_count + filters.time_count; + + if (total_filters == 0) { + printf(" Keine Filter gesetzt\n"); + } else { + printf(" Filter-Modus: %s\n", filters.combination_mode == 0 ? "AND (alle müssen zutreffen)" : "OR (einer muss zutreffen)"); + + if (filters.status_count > 0) { + printf(" Status-Codes (%d): ", filters.status_count); + for (int i = 0; i < filters.status_count; i++) { + char mode_char = (filters.status_filters[i].mode == FILTER_EXCLUDE) ? '!' : ' '; + printf("%c%d", mode_char, filters.status_filters[i].code); + if (i < filters.status_count - 1) printf(", "); + } + printf("\n"); + } + + if (filters.ip_count > 0) { + printf(" IP-Adressen (%d): ", filters.ip_count); + for (int i = 0; i < filters.ip_count; i++) { + char mode_char = (filters.ip_filters[i].mode == FILTER_EXCLUDE) ? '!' : ' '; + printf("%c%s", mode_char, filters.ip_filters[i].ip_address); + if (i < filters.ip_count - 1) printf(", "); + } + printf("\n"); + } + + if (filters.time_count > 0) { + printf(" Zeiträume (%d):\n", filters.time_count); + for (int i = 0; i < filters.time_count; i++) { + char mode_char = (filters.time_filters[i].mode == FILTER_EXCLUDE) ? '!' : ' '; + printf(" %c%02d.%02d.%d %02d:%02d:%02d - %02d.%02d.%d %02d:%02d:%02d\n", + mode_char, + filters.time_filters[i].start_time.day, + filters.time_filters[i].start_time.month, + filters.time_filters[i].start_time.year, + filters.time_filters[i].start_time.hour, + filters.time_filters[i].start_time.minute, + filters.time_filters[i].start_time.second, + filters.time_filters[i].end_time.day, + filters.time_filters[i].end_time.month, + filters.time_filters[i].end_time.year, + filters.time_filters[i].end_time.hour, + filters.time_filters[i].end_time.minute, + filters.time_filters[i].end_time.second); + } + } + } + + if (total_entries > 0) { + int filtered_count = count_filtered_entries(); + printf("\n📊 Ergebnis: %d von %d Einträgen entsprechen den Filtern\n", filtered_count, total_entries); + } + + printf("===================================\n"); +} + +long long time_to_unix_microseconds(struct simple_time time) { + int days_since_1970 = (time.year - 1970) * 365 + (time.year - 1970) / 4; + + int days_in_months[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + if (time.month >= 1 && time.month <= 12) { + days_since_1970 += days_in_months[time.month - 1]; + } + + if (time.month > 2 && ((time.year % 4 == 0 && time.year % 100 != 0) || time.year % 400 == 0)) { + days_since_1970 += 1; + } + + days_since_1970 += time.day - 1; + + long long seconds = (long long)days_since_1970 * 24 * 60 * 60; + seconds += time.hour * 60 * 60; + seconds += time.minute * 60; + seconds += time.second; + + return seconds * 1000000LL; +} + +void format_iso8601_time(struct simple_time time, char* buffer, int buffer_size) { + snprintf(buffer, buffer_size, "%04d-%02d-%02dT%02d:%02d:%02d+00:00", + time.year, time.month, time.day, time.hour, time.minute, time.second); +} + +void export_filtered_entries() { + printf("Dateiname für Timesketch Export eingeben (ohne .csv): "); + char filename[95]; + if (scanf("%94s", filename) != 1) { + printf("FEHLER: Ungültiger Dateiname!\n"); + clear_input_buffer(); + return; + } + clear_input_buffer(); + + strcat(filename, ".csv"); + + FILE* file = fopen(filename, "w"); + if (file == NULL) { + printf("FEHLER: Kann Datei '%s' nicht erstellen!\n", filename); + return; + } + + fprintf(file, "message,datetime,timestamp_desc,timestamp,ip_address,method,url_path,status_code,bytes_sent\n"); + + int exported_count = 0; + char iso_datetime[32]; + char message_text[300]; + + for (int i = 0; i < total_entries; i++) { + if (passes_filter(i)) { + format_iso8601_time(all_entries[i].time, iso_datetime, sizeof(iso_datetime)); + + long long unix_timestamp = time_to_unix_microseconds(all_entries[i].time); + + snprintf(message_text, sizeof(message_text), + "%s %s %s - Status: %d, Bytes: %d", + all_entries[i].ip_address, + all_entries[i].request_method, + all_entries[i].url_path, + all_entries[i].status_code, + all_entries[i].bytes_sent); + + fprintf(file, "\"%s\",\"%s\",\"HTTP Access Log\",%lld,\"%s\",\"%s\",\"%s\",%d,%d\n", + message_text, + iso_datetime, + unix_timestamp, + all_entries[i].ip_address, + all_entries[i].request_method, + all_entries[i].url_path, + all_entries[i].status_code, + all_entries[i].bytes_sent); + + exported_count++; + } + } + + fclose(file); + printf("✅ %d Einträge erfolgreich als Timesketch-kompatible CSV nach '%s' exportiert.\n", exported_count, filename); +} + +struct ip_stat { + char ip_address[50]; + int count; +}; + +void show_top_10_ips() { + struct ip_stat ip_stats[1000]; + int unique_ips = 0; + + for (int i = 0; i < total_entries; i++) { + if (!passes_filter(i)) continue; + + char* current_ip = all_entries[i].ip_address; + + int found_index = -1; + for (int j = 0; j < unique_ips; j++) { + if (strcmp(ip_stats[j].ip_address, current_ip) == 0) { + found_index = j; + break; + } + } + + if (found_index >= 0) { + ip_stats[found_index].count++; + } else { + if (unique_ips < 1000) { + strcpy(ip_stats[unique_ips].ip_address, current_ip); + ip_stats[unique_ips].count = 1; + unique_ips++; + } + } + } + + for (int i = 0; i < unique_ips - 1; i++) { + for (int j = 0; j < unique_ips - i - 1; j++) { + if (ip_stats[j].count < ip_stats[j + 1].count) { + struct ip_stat temp = ip_stats[j]; + ip_stats[j] = ip_stats[j + 1]; + ip_stats[j + 1] = temp; + } + } + } + + printf("\n=== TOP 10 IP-ADRESSEN ===\n"); + printf("Rang | IP-Adresse | Anzahl Anfragen\n"); + printf("-----|------------------|----------------\n"); + + int show_count = (unique_ips < 10) ? unique_ips : 10; + for (int i = 0; i < show_count; i++) { + printf("%-4d | %-16s | %d\n", i + 1, ip_stats[i].ip_address, ip_stats[i].count); + } + + if (unique_ips == 0) { + printf("Keine IP-Adressen in den gefilterten Daten gefunden.\n"); + } else { + printf("\nInsgesamt %d verschiedene IP-Adressen gefunden.\n", unique_ips); + } +} + +void show_filtered_entries() { + int shown_count = 0; + + printf("\n=== GEFILTERTE LOG-EINTRÄGE ===\n"); + printf("IP-Adresse | Methode | URL | Status | Bytes | Zeit\n"); + printf("-----------------|---------|------------------------|--------|-------|------------------\n"); + + for (int i = 0; i < total_entries; i++) { + if (passes_filter(i)) { + printf("%-16s | %-7s | %-22s | %-6d | %-5d | %02d.%02d.%d %02d:%02d:%02d\n", + all_entries[i].ip_address, + all_entries[i].request_method, + all_entries[i].url_path, + all_entries[i].status_code, + all_entries[i].bytes_sent, + all_entries[i].time.day, + all_entries[i].time.month, + all_entries[i].time.year, + all_entries[i].time.hour, + all_entries[i].time.minute, + all_entries[i].time.second); + + shown_count++; + } + } + + printf("\nInsgesamt %d Einträge gefunden.\n", shown_count); + + if (shown_count == 0) { + printf("Keine Einträge gefunden, die dem Filter entsprechen.\n"); + } +} + +void show_statistics() { + int count_200 = 0; + int count_404 = 0; + int count_500 = 0; + int total_bytes = 0; + int filtered_count = 0; + + printf("\n=== STATISTIKEN ===\n"); + + for (int i = 0; i < total_entries; i++) { + if (passes_filter(i)) { + filtered_count++; + total_bytes += all_entries[i].bytes_sent; + + if (all_entries[i].status_code == 200) count_200++; + else if (all_entries[i].status_code == 404) count_404++; + else if (all_entries[i].status_code >= 500) count_500++; + } + } + + printf("Gefilterte Einträge: %d von %d\n", filtered_count, total_entries); + printf("Erfolgreiche Anfragen (200): %d\n", count_200); + printf("Nicht gefunden (404): %d\n", count_404); + printf("Server-Fehler (5xx): %d\n", count_500); + printf("Gesamte übertragene Bytes: %d\n", total_bytes); + + if (filtered_count > 0) { + printf("Durchschnittliche Bytes pro Anfrage: %d\n", total_bytes / filtered_count); + } +} + +void menu_set_filters() { + int choice = 0; + while (choice != 4) { + show_status(); + printf("\n=== FILTER SETZEN ===\n"); + printf("1. Status-Code Filter hinzufügen\n"); + printf("2. IP-Adresse Filter hinzufügen\n"); + printf("3. Zeitraum Filter hinzufügen\n"); + printf("4. Zurück zum Filter-Menü\n"); + printf("Auswahl: "); + + choice = read_safe_integer(); + + if (choice == 1) { + if (filters.status_count >= MAX_FILTERS) { + printf("FEHLER: Maximale Anzahl Status-Code Filter erreicht (%d)!\n", MAX_FILTERS); + continue; + } + + printf("Status Code eingeben (z.B. 200, 404, 500): "); + int status = read_safe_integer(); + if (status == -1) { + printf("FEHLER: Ungültiger Status Code!\n"); + } else { + printf("Filter-Typ wählen:\n"); + printf("1. Einschließen (nur Status %d anzeigen)\n", status); + printf("2. Ausschließen (Status %d NICHT anzeigen)\n", status); + printf("Auswahl: "); + int filter_type = read_safe_integer(); + + if (filter_type == 1 || filter_type == 2) { + filters.status_filters[filters.status_count].code = status; + filters.status_filters[filters.status_count].mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; + filters.status_count++; + printf("✅ Status-Code Filter hinzugefügt. Total: %d\n", filters.status_count); + } else { + printf("FEHLER: Ungültiger Filter-Typ!\n"); + } + } + + } else if (choice == 2) { + if (filters.ip_count >= MAX_FILTERS) { + printf("FEHLER: Maximale Anzahl IP-Filter erreicht (%d)!\n", MAX_FILTERS); + continue; + } + + printf("IP-Adresse eingeben: "); + char ip[50]; + if (scanf("%49s", ip) == 1) { + printf("Filter-Typ wählen:\n"); + printf("1. Einschließen (nur IP %s anzeigen)\n", ip); + printf("2. Ausschließen (IP %s NICHT anzeigen)\n", ip); + printf("Auswahl: "); + int filter_type = read_safe_integer(); + + if (filter_type == 1 || filter_type == 2) { + strcpy(filters.ip_filters[filters.ip_count].ip_address, ip); + filters.ip_filters[filters.ip_count].mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; + filters.ip_count++; + printf("✅ IP-Filter hinzugefügt. Total: %d\n", filters.ip_count); + } else { + printf("FEHLER: Ungültiger Filter-Typ!\n"); + } + } else { + printf("FEHLER: Ungültige IP-Adresse!\n"); + clear_input_buffer(); + } + + } else if (choice == 3) { + if (filters.time_count >= MAX_FILTERS) { + printf("FEHLER: Maximale Anzahl Zeitraum-Filter erreicht (%d)!\n", MAX_FILTERS); + continue; + } + + printf("\n=== ZEITRAUM FILTER HINZUFÜGEN ===\n"); + struct time_filter new_time_filter = {0}; + + printf("Startzeit eingeben:\n"); + printf("Jahr (z.B. 2023): "); + int start_year = read_safe_integer(); + if (start_year == -1) { printf("FEHLER: Ungültiges Jahr!\n"); continue; } + + printf("Monat (1-12): "); + int start_month = read_safe_integer(); + if (start_month == -1 || start_month < 1 || start_month > 12) { + printf("FEHLER: Ungültiger Monat!\n"); continue; + } + + printf("Tag (1-31): "); + int start_day = read_safe_integer(); + if (start_day == -1 || start_day < 1 || start_day > 31) { + printf("FEHLER: Ungültiger Tag!\n"); continue; + } + + printf("Stunde (0-23): "); + int start_hour = read_safe_integer(); + if (start_hour == -1 || start_hour < 0 || start_hour > 23) { + printf("FEHLER: Ungültige Stunde!\n"); continue; + } + + printf("Minute (0-59): "); + int start_minute = read_safe_integer(); + if (start_minute == -1 || start_minute < 0 || start_minute > 59) { + printf("FEHLER: Ungültige Minute!\n"); continue; + } + + printf("Sekunde (0-59): "); + int start_second = read_safe_integer(); + if (start_second == -1 || start_second < 0 || start_second > 59) { + printf("FEHLER: Ungültige Sekunde!\n"); continue; + } + + printf("\nEndzeit eingeben:\n"); + printf("Jahr (z.B. 2023): "); + int end_year = read_safe_integer(); + if (end_year == -1) { printf("FEHLER: Ungültiges Jahr!\n"); continue; } + + printf("Monat (1-12): "); + int end_month = read_safe_integer(); + if (end_month == -1 || end_month < 1 || end_month > 12) { + printf("FEHLER: Ungültiger Monat!\n"); continue; + } + + printf("Tag (1-31): "); + int end_day = read_safe_integer(); + if (end_day == -1 || end_day < 1 || end_day > 31) { + printf("FEHLER: Ungültiger Tag!\n"); continue; + } + + printf("Stunde (0-23): "); + int end_hour = read_safe_integer(); + if (end_hour == -1 || end_hour < 0 || end_hour > 23) { + printf("FEHLER: Ungültige Stunde!\n"); continue; + } + + printf("Minute (0-59): "); + int end_minute = read_safe_integer(); + if (end_minute == -1 || end_minute < 0 || end_minute > 59) { + printf("FEHLER: Ungültige Minute!\n"); continue; + } + + printf("Sekunde (0-59): "); + int end_second = read_safe_integer(); + if (end_second == -1 || end_second < 0 || end_second > 59) { + printf("FEHLER: Ungültige Sekunde!\n"); continue; + } + + printf("\nFilter-Typ wählen:\n"); + printf("1. Einschließen (nur Ereignisse IN diesem Zeitraum anzeigen)\n"); + printf("2. Ausschließen (Ereignisse in diesem Zeitraum NICHT anzeigen)\n"); + printf("Auswahl: "); + int filter_type = read_safe_integer(); + + if (filter_type != 1 && filter_type != 2) { + printf("FEHLER: Ungültiger Filter-Typ!\n"); + continue; + } + + new_time_filter.start_time.year = start_year; + new_time_filter.start_time.month = start_month; + new_time_filter.start_time.day = start_day; + new_time_filter.start_time.hour = start_hour; + new_time_filter.start_time.minute = start_minute; + new_time_filter.start_time.second = start_second; + + new_time_filter.end_time.year = end_year; + new_time_filter.end_time.month = end_month; + new_time_filter.end_time.day = end_day; + new_time_filter.end_time.hour = end_hour; + new_time_filter.end_time.minute = end_minute; + new_time_filter.end_time.second = end_second; + + new_time_filter.mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; + + filters.time_filters[filters.time_count] = new_time_filter; + filters.time_count++; + + printf("✅ Zeitraum-Filter hinzugefügt. Total: %d\n", filters.time_count); + + } else if (choice == 4) { + return; + } else if (choice != -1) { + printf("Ungültige Auswahl! Bitte wählen Sie 1-4.\n"); + } + } +} + +void menu_delete_filters() { + int total_filters = filters.status_count + filters.ip_count + filters.time_count; + + if (total_filters == 0) { + printf("Keine Filter gesetzt zum Löschen.\n"); + return; + } + + printf("\n=== FILTER LÖSCHEN ===\n"); + printf("Aktuell gesetzte Filter:\n"); + + int filter_index = 1; + + for (int i = 0; i < filters.status_count; i++) { + char* mode_str = (filters.status_filters[i].mode == FILTER_EXCLUDE) ? "(ausschließen)" : "(einschließen)"; + printf("%d. Status-Code: %d %s\n", filter_index++, filters.status_filters[i].code, mode_str); + } + + for (int i = 0; i < filters.ip_count; i++) { + char* mode_str = (filters.ip_filters[i].mode == FILTER_EXCLUDE) ? "(ausschließen)" : "(einschließen)"; + printf("%d. IP-Adresse: %s %s\n", filter_index++, filters.ip_filters[i].ip_address, mode_str); + } + + for (int i = 0; i < filters.time_count; i++) { + char* mode_str = (filters.time_filters[i].mode == FILTER_EXCLUDE) ? "(ausschließen)" : "(einschließen)"; + printf("%d. Zeitraum: %02d.%02d.%d %02d:%02d:%02d - %02d.%02d.%d %02d:%02d:%02d %s\n", + filter_index++, + filters.time_filters[i].start_time.day, + filters.time_filters[i].start_time.month, + filters.time_filters[i].start_time.year, + filters.time_filters[i].start_time.hour, + filters.time_filters[i].start_time.minute, + filters.time_filters[i].start_time.second, + filters.time_filters[i].end_time.day, + filters.time_filters[i].end_time.month, + filters.time_filters[i].end_time.year, + filters.time_filters[i].end_time.hour, + filters.time_filters[i].end_time.minute, + filters.time_filters[i].end_time.second, + mode_str); + } + + printf("Welchen Filter möchten Sie löschen (1-%d) oder 0 für Abbrechen: ", total_filters); + int choice = read_safe_integer(); + + if (choice == 0) { + return; + } + + if (choice < 1 || choice > total_filters) { + printf("FEHLER: Ungültige Auswahl!\n"); + return; + } + + int current_index = 1; + + for (int i = 0; i < filters.status_count; i++) { + if (current_index == choice) { + for (int j = i; j < filters.status_count - 1; j++) { + filters.status_filters[j] = filters.status_filters[j + 1]; + } + filters.status_count--; + printf("✅ Status-Code Filter gelöscht.\n"); + return; + } + current_index++; + } + + for (int i = 0; i < filters.ip_count; i++) { + if (current_index == choice) { + for (int j = i; j < filters.ip_count - 1; j++) { + filters.ip_filters[j] = filters.ip_filters[j + 1]; + } + filters.ip_count--; + printf("✅ IP-Filter gelöscht.\n"); + return; + } + current_index++; + } + + for (int i = 0; i < filters.time_count; i++) { + if (current_index == choice) { + for (int j = i; j < filters.time_count - 1; j++) { + filters.time_filters[j] = filters.time_filters[j + 1]; + } + filters.time_count--; + printf("✅ Zeitraum-Filter gelöscht.\n"); + return; + } + current_index++; + } +} + +void menu_filter_mode() { + printf("\n=== FILTER-MODUS ===\n"); + printf("Aktueller Modus: %s\n", filters.combination_mode == 0 ? "AND (alle müssen zutreffen)" : "OR (einer muss zutreffen)"); + printf("1. AND-Modus (alle Filter müssen zutreffen)\n"); + printf("2. OR-Modus (mindestens ein Filter muss zutreffen)\n"); + printf("Auswahl: "); + + int choice = read_safe_integer(); + + if (choice == 1) { + filters.combination_mode = 0; + printf("✅ Filter-Modus auf AND gesetzt.\n"); + } else if (choice == 2) { + filters.combination_mode = 1; + printf("✅ Filter-Modus auf OR gesetzt.\n"); + } else if (choice != -1) { + printf("Ungültige Auswahl!\n"); + } +} + +void menu_reset_filters() { + filters.status_count = 0; + filters.ip_count = 0; + filters.time_count = 0; + filters.combination_mode = 0; + + printf("✅ Alle Filter zurückgesetzt.\n"); +} + +void menu_filter_management() { + int choice = 0; + while (choice != 5) { + show_status(); + printf("\n=== FILTER VERWALTEN ===\n"); + printf("1. Filter setzen\n"); + printf("2. Filter löschen\n"); + printf("3. Filter-Modus (AND/OR)\n"); + printf("4. Filter zurücksetzen\n"); + printf("5. Zurück zum Hauptmenü\n"); + printf("Auswahl: "); + + choice = read_safe_integer(); + + if (choice == 1) { + menu_set_filters(); + } else if (choice == 2) { + menu_delete_filters(); + } else if (choice == 3) { + menu_filter_mode(); + } else if (choice == 4) { + menu_reset_filters(); + } else if (choice == 5) { + return; + } else if (choice != -1) { + printf("Ungültige Auswahl! Bitte wählen Sie 1-5.\n"); + } + } +} + +void menu_show_entries() { + int choice = 0; + while (choice != 4) { + show_status(); + printf("\n=== EINTRÄGE ANZEIGEN ===\n"); + int filtered_count = count_filtered_entries(); + printf("Aktuell %d gefilterte Einträge verfügbar.\n", filtered_count); + printf("1. Gefilterte Einträge vollständig anzeigen\n"); + printf("2. Gefilterte Einträge in Datei exportieren\n"); + printf("3. Top 10 IP-Adressen anzeigen\n"); + printf("4. Zurück zum Hauptmenü\n"); + printf("Auswahl: "); + + choice = read_safe_integer(); + + if (choice == 1) { + if (filtered_count > 1000) { + printf("WARNUNG: %d Einträge sind sehr viele für die Anzeige!\n", filtered_count); + printf("Möchten Sie trotzdem fortfahren? (1=Ja, 0=Nein): "); + int confirm = read_safe_integer(); + if (confirm != 1) { + printf("Anzeige abgebrochen. Tipp: Verwenden Sie den Export für große Datenmengen.\n"); + continue; + } + } + show_filtered_entries(); + } else if (choice == 2) { + export_filtered_entries(); + } else if (choice == 3) { + show_top_10_ips(); + } else if (choice == 4) { + return; + } else if (choice != -1) { + printf("Ungültige Auswahl! Bitte wählen Sie 1-4.\n"); + } + } +} + +void show_main_menu() { + printf("\n=== NGINX LOG PARSER ===\n"); + printf("1. Filter verwalten\n"); + printf("2. Gefilterte Einträge anzeigen\n"); + printf("3. Statistiken anzeigen\n"); + printf("4. Beenden\n"); + printf("Auswahl: "); +} + +int main(int argc, char* argv[]) { + if (argc != 2) { + printf("Verwendung: %s \n", argv[0]); + printf("Unterstützte Formate: .log Dateien (KEINE .gz Unterstützung)\n"); + printf("Beispiele:\n"); + printf(" %s /var/log/nginx/access.log (einzelne .log Datei)\n", argv[0]); + printf(" %s /var/log/nginx/ (Verzeichnis mit .log Dateien)\n", argv[0]); + printf("\nFür .gz Dateien:\n"); + printf(" 1. Dekomprimieren: gunzip /var/log/nginx/*.gz\n"); + printf(" 2. Dann ausführen: %s /var/log/nginx/\n", argv[0]); + return 1; + } + + printf("=== NGINX LOG PARSER - VEREINFACHTE VERSION ===\n"); + allocate_initial_memory(); + + load_log_file(argv[1]); + + if (total_entries == 0) { + printf("Keine gültigen Log-Einträge gefunden. Überprüfen Sie den Pfad und die Dateiformate.\n"); + cleanup_memory(); + return 1; + } + + int choice = 0; + while (choice != 4) { + show_status(); + show_main_menu(); + choice = read_safe_integer(); + + if (choice == -1) { + printf("FEHLER: Bitte geben Sie eine gültige Zahl ein!\n"); + continue; + } + + if (choice == 1) { + menu_filter_management(); + } else if (choice == 2) { + menu_show_entries(); + } else if (choice == 3) { + show_statistics(); + } else if (choice == 4) { + printf("Auf Wiedersehen!\n"); + } else { + printf("Ungültige Auswahl! Bitte wählen Sie 1-4.\n"); + } + } + + cleanup_memory(); return 0; } \ No newline at end of file