import os from PyQt5.QtWidgets import (QMessageBox, QWidget, QRadioButton, QFileDialog, QLabel, QPushButton, QGridLayout, QGroupBox, QHBoxLayout, QVBoxLayout, QLineEdit, QDateTimeEdit) from PyQt5.QtCore import Qt, QDate from logline_leviathan.gui.checkbox_panel import * from logline_leviathan.gui.ui_helper import UIHelper from logline_leviathan.database.database_manager import session_scope from logline_leviathan.database.database_operations import DatabaseOperations from logline_leviathan.exporter.wordlist_export import generate_wordlist from logline_leviathan.gui.checkbox_panel import * import shutil import glob class GenerateWordlistWindow(QWidget): def __init__(self, app): super().__init__() self.app = app self.checkboxPanel = CheckboxPanel() self.database_operations = DatabaseOperations(self, app) self.ui_helper = UIHelper(self) self.WordlistPath = os.path.join(os.getcwd(), 'data', 'wordlist') os.makedirs(self.WordlistPath, exist_ok=True) self.initialize_generate_wordlist_window(app) self.updateCheckboxes() def initialize_generate_wordlist_window(generate_wordlist_window, app): generate_wordlist_window.setWindowTitle('Logline Leviathan - Wordlist-Generator') generate_wordlist_window.mainLayout = QVBoxLayout(generate_wordlist_window) #generate_wordlist_window.extendedLayout = QHBoxLayout(generate_wordlist_window) generate_wordlist_window.db_session = None stylesheet = """ /* Style for the main window */ QWidget { background-color: #282C34; /* Dark grey background */ color: white; /* White text */ } /* Style for buttons */ QPushButton { background-color: #4B5563; /* Dark grey background */ color: white; /* White text */ border-style: outset; border-width: 2px; border-radius: 1px; /* Rounded corners */ border-color: #4A4A4A; padding: 6px; min-width: 50px; min-height: 15px; } QPushButton:hover { background-color: #6E6E6E; /* Slightly lighter grey on hover */ } QPushButton:pressed { background-color: #484848; /* Even darker grey when pressed */ } """ highlited_button_style = """ QPushButton { background-color: #3C8CCE; /* Lighter blue background */ color: white; /* White text */ border-style: outset; border-width: 2px; border-radius: 1px; /* Rounded corners */ border-color: #4A4A4A; padding: 6px; min-width: 50px; min-height: 15px; } QPushButton:hover { background-color: #7EC0EE; /* Even lighter blue on hover */ } QPushButton:pressed { background-color: #4A86E8; /* Slightly darker blue when pressed */ } """ generate_wordlist_window.setStyleSheet(stylesheet) generate_wordlist_window.statusLabel = QLabel(' Erwarte Selektion der Entitätentypen', generate_wordlist_window) generate_wordlist_window.statusLabel.setWordWrap(True) generate_wordlist_window.statusLabel.setMinimumHeight(40) generate_wordlist_window.statusLabel.setStyleSheet("QLabel { background-color: #3C4043; color: white; }") generate_wordlist_window.mainLayout.addWidget(generate_wordlist_window.statusLabel) # Create a GroupBox for the CheckboxPanel exportOptionsGroupBox = QGroupBox("SELEKTION", generate_wordlist_window) exportOptionsLayout = QVBoxLayout(exportOptionsGroupBox) generate_wordlist_window.checkboxPanel = CheckboxPanel() # Create a horizontal layout filterLayout = QHBoxLayout() # Create the "Check All" button checkAllButton = QPushButton("Alle markieren", generate_wordlist_window) checkAllButton.clicked.connect(lambda: generate_wordlist_window.checkboxPanel.checkAllVisible()) # Create the "Uncheck All" button uncheckAllButton = QPushButton("Keine markieren", generate_wordlist_window) uncheckAllButton.clicked.connect(lambda: generate_wordlist_window.checkboxPanel.uncheckAllVisible()) expandAllButton = QPushButton("Expandieren", generate_wordlist_window) expandAllButton.clicked.connect(lambda: generate_wordlist_window.checkboxPanel.expandAllTreeItems()) collapseAllButton = QPushButton("Komprimieren", generate_wordlist_window) collapseAllButton.clicked.connect(lambda: generate_wordlist_window.checkboxPanel.collapseAllTreeItems()) # Add buttons to the filter layout, to the left of the filter label filterLayout.addWidget(checkAllButton) filterLayout.addWidget(uncheckAllButton) filterLayout.addWidget(expandAllButton) filterLayout.addWidget(collapseAllButton) # Create the label for the filter filterLabel = QLabel("Filtern:") filterLayout.addWidget(filterLabel) # Add label to the horizontal layout # Add Text Input for Filtering filterLineEdit = QLineEdit(generate_wordlist_window) filterLineEdit.setPlaceholderText(" nach Typ, Tooltip oder Kurzbezeichnung filtern...") filterLineEdit.setStyleSheet(""" QLineEdit { background-color: #3C4043; /* Background color */ color: white; /* Text color */ min-height: 20px; } """) filterLayout.addWidget(filterLineEdit) # Add line edit to the horizontal layout exportOptionsLayout.addLayout(filterLayout) # Add the horizontal layout to the export options layout # Add CheckboxPanel to the GroupBox's Layout exportOptionsLayout.addWidget(generate_wordlist_window.checkboxPanel) # Connect the textChanged signal of QLineEdit to a new method filterLineEdit.textChanged.connect(generate_wordlist_window.checkboxPanel.filterCheckboxes) generate_wordlist_window.mainLayout.addWidget(exportOptionsGroupBox) copyWordlistToParserDirButton = QPushButton('Soeben generierte Wordlist zur Analyse hinzufügen (kopiert erzeugte Datei)', generate_wordlist_window) copyWordlistToParserDirButton.clicked.connect(generate_wordlist_window.copyWordlistToParserDir) generate_wordlist_window.mainLayout.addWidget(copyWordlistToParserDirButton) purgeWordlistEntriesButton = QPushButton('Alte Wordlist-Eintraege aus Datenbank entfernen (empfohlen, sofern neue Wordlist generiert)', generate_wordlist_window) purgeWordlistEntriesButton.clicked.connect(generate_wordlist_window.purgeWordlistEntries) generate_wordlist_window.mainLayout.addWidget(purgeWordlistEntriesButton) twoWordlistButtonsLayout = QHBoxLayout() openActiveWordlistButton = QPushButton('Bestehende Wordlist oeffnen', generate_wordlist_window) openActiveWordlistButton.clicked.connect(generate_wordlist_window.openActiveWordlist) twoWordlistButtonsLayout.addWidget(openActiveWordlistButton) deleteActiveWordlistButton = QPushButton('Bestehende Wordlist entfernen', generate_wordlist_window) deleteActiveWordlistButton.clicked.connect(generate_wordlist_window.deleteActiveWordlist) twoWordlistButtonsLayout.addWidget(deleteActiveWordlistButton) generate_wordlist_window.mainLayout.addLayout(twoWordlistButtonsLayout) # Exit Button Layout bottomLayout = QGridLayout() generate_wordlist_window.openWordlistPathButton = QPushButton('Wordlist-Dateipfad...', generate_wordlist_window) generate_wordlist_window.openWordlistPathButton.clicked.connect(generate_wordlist_window.openWordlistPath) bottomLayout.addWidget(generate_wordlist_window.openWordlistPathButton, 3, 1) # Start Export Button generate_wordlist_window.startExportButton = QPushButton('Wordlist erstellen', generate_wordlist_window) generate_wordlist_window.startExportButton.clicked.connect(generate_wordlist_window.start_export_process) generate_wordlist_window.startExportButton.setStyleSheet(highlited_button_style) bottomLayout.addWidget(generate_wordlist_window.startExportButton, 3, 2) # Output File Directory generate_wordlist_window.selectOutputFileButton = QPushButton('Wordlist-Ausgabepfad setzen...', generate_wordlist_window) generate_wordlist_window.selectOutputFileButton.clicked.connect(generate_wordlist_window.selectOutputFile) bottomLayout.addWidget(generate_wordlist_window.selectOutputFileButton, 4, 1) # Exit Button generate_wordlist_window.exitButton = QPushButton('Schließen', generate_wordlist_window) generate_wordlist_window.exitButton.clicked.connect(generate_wordlist_window.close) bottomLayout.addWidget(generate_wordlist_window.exitButton, 4, 2) generate_wordlist_window.crossmatchesCheckbox = QCheckBox('Nur Kreuztreffer (Entitäten, die in mehreren Dateien vorkommen)', generate_wordlist_window) bottomLayout.addWidget(generate_wordlist_window.crossmatchesCheckbox, 0, 1) generate_wordlist_window.timestampFilterCheckbox = QCheckBox('Nach Zeitstempel filtern:', generate_wordlist_window) generate_wordlist_window.startDateEdit = QDateTimeEdit(generate_wordlist_window) generate_wordlist_window.startDateEdit.setCalendarPopup(True) generate_wordlist_window.startDateEdit.setDate(QDate.currentDate()) generate_wordlist_window.endDateEdit = QDateTimeEdit(generate_wordlist_window) generate_wordlist_window.endDateEdit.setCalendarPopup(True) generate_wordlist_window.endDateEdit.setDate(QDate.currentDate()) generate_wordlist_window.timestampFilterQHBoxLayout = QHBoxLayout() generate_wordlist_window.timestampFilterQHBoxLayout.addWidget(generate_wordlist_window.timestampFilterCheckbox) generate_wordlist_window.timestampFilterQHBoxLayout.addWidget(generate_wordlist_window.startDateEdit) generate_wordlist_window.timestampFilterQHBoxLayout.addWidget(generate_wordlist_window.endDateEdit) bottomLayout.addLayout(generate_wordlist_window.timestampFilterQHBoxLayout, 1, 1) generate_wordlist_window.flaggedEntriesLayout = QHBoxLayout() generate_wordlist_window.flaggedEntriesCheckbox = QCheckBox('Markierte Einträge berücksichtigen', generate_wordlist_window) generate_wordlist_window.flaggedEntriesLayout.addWidget(generate_wordlist_window.flaggedEntriesCheckbox) generate_wordlist_window.flaggedRadioButtonLayout = QHBoxLayout() generate_wordlist_window.flaggedRadioButton = QRadioButton('Nur markierte Einträge') generate_wordlist_window.notflaggedRadioButton = QRadioButton('Nur nicht markierte Einträge') generate_wordlist_window.flaggedRadioButtonLayout.addWidget(generate_wordlist_window.flaggedRadioButton) generate_wordlist_window.flaggedRadioButtonLayout.addWidget(generate_wordlist_window.notflaggedRadioButton) generate_wordlist_window.flaggedRadioButton.setChecked(True) generate_wordlist_window.flaggedEntriesLayout.addLayout(generate_wordlist_window.flaggedRadioButtonLayout) bottomLayout.addLayout(generate_wordlist_window.flaggedEntriesLayout, 2, 1) # Output File Path Label generate_wordlist_window.WordlistPathLabel = QLabel('', generate_wordlist_window) generate_wordlist_window.updateWordlistPathLabel() # Call this method to set the initial text bottomLayout.addWidget(generate_wordlist_window.WordlistPathLabel, 0, 2) generate_wordlist_window.mainLayout.addLayout(bottomLayout) generate_wordlist_window.setLayout(generate_wordlist_window.mainLayout) def updateCheckboxes(self): with session_scope() as session: self.checkboxPanel.updateCheckboxes(session) def getSelectedCheckboxes(self): selected_checkboxes = [] def traverseTreeItems(treeItem): if treeItem.checkState(0) == Qt.Checked: selected_checkboxes.append(treeItem) for i in range(treeItem.childCount()): traverseTreeItems(treeItem.child(i)) for i in range(self.checkboxPanel.treeWidget.topLevelItemCount()): traverseTreeItems(self.checkboxPanel.treeWidget.topLevelItem(i)) return selected_checkboxes def updateWordlistPathLabel(self): outputDirPath = os.path.dirname(self.WordlistPath) display_text = f'{outputDirPath}/' self.WordlistPathLabel.setText(display_text) def openWordlistPath(self): outputDirPath = os.path.dirname(self.WordlistPath) wordlistPath = os.path.join(outputDirPath, 'wordlist') self.ui_helper.openFile(wordlistPath) def selectOutputFile(self): options = QFileDialog.Options() output_format = self.outputFormatList.currentItem().text().lower() extension_map = {'html': '.html', 'xlsx': '.xlsx'} default_extension = extension_map.get(output_format, '') selected_file, _ = QFileDialog.getSaveFileName( self, "Selektieren des Ziel-Dateipfads", self.WordlistPath, f"{output_format.upper()} Files (*{default_extension});;All Files (*)", options=options ) if selected_file: if not selected_file.endswith(default_extension): selected_file += default_extension self.WordlistPath = selected_file self.outputDir = os.path.dirname(selected_file) self.updateWordlistPathLabel() def get_unique_filename(self, base_path): directory, filename = os.path.split(base_path) name, extension = os.path.splitext(filename) counter = 1 new_path = base_path while os.path.exists(new_path): new_filename = f"{name}_{counter}{extension}" new_path = os.path.join(directory, new_filename) counter += 1 return new_path def copyWordlistToParserDir(self): try: # Path to the parser directory parser_dir = os.path.join(os.getcwd(), 'data', 'parser') # Ensure the parser directory exists os.makedirs(parser_dir, exist_ok=True) # Find the newest .txt file in the WordlistPath directory list_of_files = glob.glob(os.path.join(self.WordlistPath, '*.txt')) if not list_of_files: raise FileNotFoundError("No .txt files found in the WordlistPath directory.") newest_file = max(list_of_files, key=os.path.getctime) # Destination file path destination_file = os.path.join(parser_dir, 'generated_wordlist.txt') # Copy and overwrite the newest file to the destination shutil.copy2(newest_file, destination_file) self.statusLabel.setText(f" Wordlist erfolgreich kopiert nach {destination_file}") except Exception as e: self.message("Fehler beim kopieren", f"Fehler beim kopieren: {str(e)}") def openActiveWordlist(self): try: parser_dir = os.path.join(os.getcwd(), 'data', 'parser') wordlist_file = os.path.join(parser_dir, 'generated_wordlist.txt') if os.path.exists(wordlist_file): self.ui_helper.openFile(wordlist_file) else: raise FileNotFoundError("Wordlist file not found.") except Exception as e: self.message("Fehler beim Öffnen", f"Fehler beim Öffnen: {str(e)}") def deleteActiveWordlist(self): try: parser_dir = os.path.join(os.getcwd(), 'data', 'parser') wordlist_file = os.path.join(parser_dir, 'generated_wordlist.txt') if os.path.exists(wordlist_file): os.remove(wordlist_file) self.statusLabel.setText(" Wordlist erfolgreich gelöscht.") else: raise FileNotFoundError("Wordlist file not found.") except Exception as e: self.message("Fehler beim Löschen", f"Fehler beim Löschen: {str(e)}") def start_export_process(self): # Base filename for the wordlist file base_filename = "wordlist.txt" # Construct the full path with the base filename full_output_path = os.path.join(self.WordlistPath, base_filename) # Generate a unique filename to avoid overwriting existing files unique_output_path = self.get_unique_filename(full_output_path) # Retrieve dates from QDateEdit widgets start_date = self.startDateEdit.date().toPyDate() if self.timestampFilterCheckbox.isChecked() else None end_date = self.endDateEdit.date().toPyDate() if self.timestampFilterCheckbox.isChecked() else None include_flagged = self.flaggedEntriesCheckbox.isChecked() only_flagged = self.flaggedRadioButton.isChecked() only_unflagged = self.notflaggedRadioButton.isChecked() try: with session_scope() as session: selected_checkboxes = self.getSelectedCheckboxes() if not selected_checkboxes: self.message("Generieren nicht möglich", "Keine Typen selektiert. Auswahl vornehmen.") return only_crossmatches = self.crossmatchesCheckbox.isChecked() # Call the generate_wordlist function with timestamp parameters generate_wordlist(unique_output_path, session, selected_checkboxes, only_crossmatches, start_date, end_date, include_flagged, only_flagged, only_unflagged) self.statusLabel.setText(f" Generierte Liste gespeichert unter {unique_output_path}") except Exception as e: self.statusLabel.setText(f" Fehler beim speichern: {str(e)}") logging.error(f"Export Error: {str(e)}") def purgeWordlistEntries(self): self.database_operations.purgeWordlistEntries() self.updateCheckboxes() def message(self, title, text, extra_widget=None): msgBox = QMessageBox() msgBox.setStyleSheet(""" QMessageBox { background-color: #282C34; /* Dark grey background */ } QLabel { color: white; /* White text */ } QPushButton { color: white; /* White text for buttons */ background-color: #4B5563; /* Dark grey background for buttons */ border-style: solid; border-width: 2px; border-radius: 5px; border-color: #4A4A4A; padding: 6px; min-width: 80px; min-height: 30px; } """) msgBox.setIcon(QMessageBox.Warning) msgBox.setWindowTitle(title) msgBox.setText(text) if extra_widget: msgBox.setInformativeText('') msgBox.layout().addWidget(extra_widget, 1, 1) msgBox.exec_()