/* Copyright (c) 2025 Mario Stöckl (mstoeck3@hs-mittweida.de). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #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 simple_time { int day; int month; int year; int hour; int minute; int second; }; 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; }; struct status_filter { int code; filter_mode_t mode; }; struct ip_filter { char ip_address[50]; filter_mode_t mode; }; struct time_filter { struct simple_time start_time; struct simple_time end_time; filter_mode_t mode; }; 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; }; struct log_entry *all_entries = NULL; int max_entries = 0; int total_entries = 0; struct filter_system filters = {0}; int is_space(char c) { return (c == ' ' || c == '\t'); } char* skip_spaces(char* str) { while (is_space(*str)) { str++; } return str; } void copy_until_space(char* destination, char* source, int max_length) { int i = 0; while (source[i] != ' ' && source[i] != '\0' && i < max_length - 1) { destination[i] = source[i]; i++; } destination[i] = '\0'; } int month_name_to_number(char* month_name) { if (strncmp(month_name, "Jan", 3) == 0) return 1; if (strncmp(month_name, "Feb", 3) == 0) return 2; if (strncmp(month_name, "Mar", 3) == 0) return 3; if (strncmp(month_name, "Apr", 3) == 0) return 4; if (strncmp(month_name, "May", 3) == 0) return 5; if (strncmp(month_name, "Jun", 3) == 0) return 6; if (strncmp(month_name, "Jul", 3) == 0) return 7; if (strncmp(month_name, "Aug", 3) == 0) return 8; if (strncmp(month_name, "Sep", 3) == 0) return 9; if (strncmp(month_name, "Oct", 3) == 0) return 10; if (strncmp(month_name, "Nov", 3) == 0) return 11; if (strncmp(month_name, "Dec", 3) == 0) return 12; return 1; } int compare_times(struct simple_time time1, struct simple_time time2) { if (time1.year != time2.year) return (time1.year < time2.year) ? -1 : 1; if (time1.month != time2.month) return (time1.month < time2.month) ? -1 : 1; if (time1.day != time2.day) return (time1.day < time2.day) ? -1 : 1; if (time1.hour != time2.hour) return (time1.hour < time2.hour) ? -1 : 1; if (time1.minute != time2.minute) return (time1.minute < time2.minute) ? -1 : 1; if (time1.second != time2.second) return (time1.second < time2.second) ? -1 : 1; return 0; } void clear_input_buffer() { int c; while ((c = getchar()) != '\n' && c != EOF) { } } int read_safe_integer() { int number; int result = scanf("%d", &number); if (result != 1) { clear_input_buffer(); return -1; } clear_input_buffer(); return number; } void cleanup_memory() { if (all_entries != NULL) { printf("Gebe %zu Bytes Speicher frei...\n", max_entries * sizeof(struct log_entry)); free(all_entries); all_entries = NULL; } max_entries = 0; total_entries = 0; } void cleanup_and_exit() { printf("Programm wird wegen Speicherfehler beendet...\n"); cleanup_memory(); exit(1); } void expand_memory_if_needed() { if (total_entries >= 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; }